QList<RVector> RShape::getIntersectionPointsLT(const RLine& line1, const RTriangle& triangle2, bool limited) { QList<RVector> res; RVector normal = triangle2.getNormal(); if (normal.getMagnitude() < 1.0e-12) { return res; } if (line1.getLength() < 1.0e-12) { return res; } double t = RVector::getDotProduct(normal, triangle2.getCorner(2) - line1.getStartPoint()) / RVector::getDotProduct(normal, (line1.getEndPoint() - line1.getStartPoint())); // check if intersection point is on the line: if (limited && (t < 0.0 || t > 1.0)) { return res; } // intersection point: RVector ip = line1.getStartPoint() + (line1.getEndPoint() - line1.getStartPoint()) * t; // check if intersection point is inside the triangle: if (!limited || triangle2.isPointInTriangle(ip)) { res.push_back(ip); } return res; }
void RPainterPathExporter::exportLineSegment(const RLine& line, double angle) { if (line.getLength()<RS::PointTolerance) { if (exportZeroLinesAsPoints) { path.addPoint(line.getStartPoint()); } else { // Qt won't export a zero length line as point: // e.g. dot in a dash/dot line: RVector startPoint = line.startPoint - RVector::createPolar(0.01, angle); RVector endPoint = line.endPoint + RVector::createPolar(0.01, angle); path.moveTo(startPoint); path.lineTo(endPoint); // path.moveTo(line.getStartPoint()-RVector(0.01,0)); // path.lineTo(line.getEndPoint()+RVector(0.01, 0)); // path.moveTo(line.getStartPoint()-RVector(0,0.01)); // path.lineTo(line.getEndPoint()+RVector(0, 0.01)); } } else { if (!path.isAtPosition(line.getStartPoint())) { path.moveTo(line.getStartPoint()); } path.lineTo(line.getEndPoint()); } }
void RGraphicsSceneQt::exportRay(const RRay& ray) { bool created = beginPath(); Q_ASSERT(currentPainterPath.isValid()); // find largest view box over all attached views: RBox box; QList<RGraphicsView*>::iterator it; for (it=views.begin(); it!=views.end(); it++) { RBox b = (*it)->getBox(); box.growToIncludeBox(b); } // trim line to view box: RLine clippedLine = ray.getClippedLine(box); double offs = clippedLine.getStartPoint().getDistanceTo(ray.getBasePoint()); if (RMath::isSameDirection(ray.getBasePoint().getAngleTo(clippedLine.getStartPoint()), ray.getDirection1())) { offs *= -1; } exportLine(clippedLine, offs); currentPainterPath.setAlwaysRegen(true); if (created) { endPath(); } }
void RPainterPathExporter::exportLineSegment(const RLine& line) { if (line.getLength()<RS::PointTolerance) { path.addPoint(line.getStartPoint()); } else { path.moveTo(line.getStartPoint()); path.lineTo(line.getEndPoint()); } }
RVector RVector::rotate3D(const RLine& axis, double rotation) { RVector off = -axis.getStartPoint(); RVector ret = *this; ret.move(off); RVector ax = axis.getStartPoint() - axis.getEndPoint(); QQuaternion quat = QQuaternion::fromAxisAndAngle(ax.x, ax.y, ax.z, RMath::rad2deg(rotation)); QVector3D qv = quat.rotatedVector(QVector3D(ret.x, ret.y, ret.z)); ret = RVector(qv.x(), qv.y(), qv.z()); ret.move(-off); *this = ret; return *this; }
bool RDimRotatedData::mirror(const RLine& axis) { RDimLinearData::mirror(axis); //extensionPoint1.mirror(axis); //extensionPoint2.mirror(axis); RLine neutralAxis = axis; neutralAxis.move(-neutralAxis.getStartPoint()); RVector vec = RVector::createPolar(1.0, rotation); vec.mirror(neutralAxis); rotation = vec.getAngle(); update(); return true; }
void RExporter::exportLine(const RLine& line, double offset) { if (!line.isValid()) { return; } double length = line.getLength(); if (length>1e100 || length<RS::PointTolerance) { return; } RLinetypePattern p = getLinetypePattern(); // continuous line or // we are in draft mode or // QCAD is configured to show screen based line patterns if (!p.isValid() || p.getNumDashes() == 1 || draftMode || screenBasedLinetypes) { exportLineSegment(line); return; } p.scale(getPatternFactor()); double patternLength = p.getPatternLength(); // avoid huge number of small segments due to very fine // pattern or long lines: if (patternLength<RS::PointTolerance || length / patternLength > 5000) { exportLineSegment(line); return; } double angle = line.getAngle(); RVector* vp = NULL; vp = new RVector[p.getNumDashes()]; for (int i = 0; i < p.getNumDashes(); ++i) { vp[i] = RVector(cos(angle) * fabs(p.getDashLengthAt(i)), sin(angle) * fabs(p.getDashLengthAt(i))); } bool optimizeEnds = false; if (RMath::isNaN(offset)) { offset = getPatternOffset(length, p); optimizeEnds = true; } else { double num = ceil(offset / patternLength); offset -= num * patternLength; } bool done = false; int i = 0; RVector cursor(line.getStartPoint() + RVector::createPolar(offset, angle)); double total = offset; bool dashFound = false; bool gapFound = false; RVector p1 = line.getStartPoint(); RVector p2 = p1; do { if (dashFound && !gapFound) { // don't shoot over end of line: if (total + fabs(p.getDashLengthAt(i)) >= length - 1.0e-6) { if (optimizeEnds) { exportLineSegment(RLine(p1, line.endPoint)); } else { exportLineSegment(RLine(p1, p2)); } break; } exportLineSegment(RLine(p1, p2)); } // dash, no gap. note that a dash can have a length of 0.0 (point): if (p.getDashLengthAt(i) > -RS::PointTolerance) { // check if we're on the line already: if (total + p.getDashLengthAt(i) > 0) { p1 = cursor; // no gap at the beginning of the line: if (total < 0 || (!dashFound && optimizeEnds)) { p1 = line.startPoint; } p2 = cursor + vp[i]; if (!p2.equalsFuzzy(line.startPoint, 1.0e-6)) { dashFound = true; } } gapFound = false; } // gap: else { gapFound = true; } cursor += vp[i]; total += fabs(p.getDashLengthAt(i)); done = total > length; ++i; if (i >= p.getNumDashes()) { i = 0; } } while (!done); if (!gapFound || !dashFound) { if (total + fabs(p.getDashLengthAt(i)) >= length - 1.0e-6) { if (optimizeEnds || (total>length && !gapFound)) { exportLineSegment(RLine(p1, line.endPoint)); } else { exportLineSegment(RLine(p1, p2)); } } else { exportLineSegment(RLine(p1, p2)); } } delete[] vp; }
double RExporter::exportLine(const RLine& line, double offset) { double ret = RNANDOUBLE; if (!line.isValid()) { return ret; } double length = line.getLength(); if (length>1e100 || length<RS::PointTolerance) { return ret; } double angle = line.getAngle(); // continuous line or // we are in draft mode or // QCAD is configured to show screen based line patterns if (draftMode || screenBasedLinetypes || twoColorSelectedMode) { exportLineSegment(line, angle); return ret; } RLinetypePattern p = getLinetypePattern(); if (!p.isValid() || p.getNumDashes() <= 1) { exportLineSegment(line, angle); return ret; } p.scale(getLineTypePatternScale(p)); double patternLength = p.getPatternLength(); // avoid huge number of small segments due to very fine // pattern or long lines: if (patternLength<RS::PointTolerance || length / patternLength > RSettings::getDashThreshold()) { exportLineSegment(line, angle); return ret; } RVector* vp = NULL; vp = new RVector[p.getNumDashes()]; for (int i = 0; i < p.getNumDashes(); ++i) { vp[i] = RVector(cos(angle) * fabs(p.getDashLengthAt(i)), sin(angle) * fabs(p.getDashLengthAt(i))); } if (RMath::isNaN(offset)) { offset = p.getPatternOffset(length); } else { double num = ceil(offset / patternLength); offset -= num * patternLength; } bool done = false; int i = 0; RVector cursor(line.getStartPoint() + RVector::createPolar(offset, angle)); double total = offset; double nextTotal; bool isGap = false; RLine dash; do { double dashLength = p.getDashLengthAt(i); nextTotal = total + fabs(dashLength); //qDebug() << "total: " << total; //qDebug() << "nextTotal: " << nextTotal; // dash, no gap. note that a dash can have a length of 0.0 (point): if (dashLength > -RS::PointTolerance) { isGap = false; } // gap: else { isGap = true; } // check if we're on the line already // (since we might start before the line due to pattern offset): if (nextTotal > 0.0) { dash = RLine(cursor, cursor + vp[i]); if (!isGap) { // fist part is gap, then dash ret = -nextTotal; } else { // fist part is dash, then gap ret = nextTotal; } // shorten at start of line: if (total < 0.0 /*&& nextTotal > 0.0*/) { dash.startPoint = line.startPoint; ret = RNANDOUBLE; } // shorten at end of line: if (/*total < length &&*/ nextTotal >= length - 1.0e-6) { dash.endPoint = line.endPoint; ret = RINFDOUBLE; } if (!isGap) { exportLineSegment(dash, angle); ret = nextTotal; } } cursor += vp[i]; total = nextTotal; done = total > length; // export shape (zigzag, text, etc.) at end of dash / gap: if (p.hasShapeAt(i)) { QList<RPainterPath> pps = p.getShapeAt(i); exportLinetypeShape(pps, line, total, length, angle, cursor); } ++i; if (i >= p.getNumDashes()) { i = 0; } } while (!done); delete[] vp; return ret; }
QList<RVector> RShape::getIntersectionPointsLE(const RLine& line1, const REllipse& ellipse2, bool limited) { QList<RVector> res; // find out if line1 is (almost) a tangent: QList<RLine> tangents = ellipse2.getTangents(line1.getMiddlePoint()); for (int i=0; i<tangents.length(); i++) { double a = tangents[i].getAngle(); double ad1 = fabs(RMath::getAngleDifference180(a, line1.getDirection1())); double ad2 = fabs(RMath::getAngleDifference180(a, line1.getDirection2())); if (ad1 < 1.0e-2 || ad2 < 1.0e-2) { res.append(tangents[i].getEndPoint()); // no need to continue: max. one tangent possible: return res; } } // rotate into normal position: double ang = ellipse2.getAngle(); double rx = ellipse2.getMajorRadius(); double ry = ellipse2.getMinorRadius(); RVector center = ellipse2.getCenter(); RVector a1 = line1.getStartPoint(); a1.rotate(-ang, center); RVector a2 = line1.getEndPoint(); a2.rotate(-ang, center); RVector origin = a1; RVector dir = a2-a1; RVector diff = origin - center; RVector mDir = RVector(dir.x/(rx*rx), dir.y/(ry*ry)); RVector mDiff = RVector(diff.x/(rx*rx), diff.y/(ry*ry)); double a = RVector::getDotProduct(dir, mDir); double b = RVector::getDotProduct(dir, mDiff); double c = RVector::getDotProduct(diff, mDiff) - 1.0; double d = b*b - a*c; RVector res1 = RVector::invalid; RVector res2 = RVector::invalid; if (d < 0) { // no solution } else if ( d > 0 ) { double root = sqrt(d); double t_a = (-b - root) / a; double t_b = (-b + root) / a; res1 = a1.getLerp(a2, t_a).rotate(ang, center); res2 = a1.getLerp(a2, t_b).rotate(ang, center); } else { double t = -b/a; if ( 0 <= t && t <= 1 ) { // one solution: res1 = a1.getLerp(a2, t).rotate(ang, center); } else { // no solution } } if (res1.isValid()) { if (!limited || (line1.isOnShape(res1) && ellipse2.isOnShape(res1))) { res.append(res1); } } if (res2.isValid()) { if (!limited || (line1.isOnShape(res2) && ellipse2.isOnShape(res2))) { res.append(res2); } } return res; }
QList<RVector> RShape::getIntersectionPointsLC(const RLine& line1, const RCircle& circle2, bool limited) { QList<RVector> res; RVector vLineCenter = line1.getVectorTo(circle2.getCenter(), false); double dist = vLineCenter.getMagnitude(); // special case: arc touches line (tangent): if (fabs(dist - circle2.getRadius()) < 1.0e-4) { res.append(circle2.getCenter() - vLineCenter); // ret.setTangent(true); return res; } RVector p = line1.getStartPoint(); RVector d = line1.getEndPoint() - line1.getStartPoint(); if (d.getMagnitude() < 1.0e-6) { return res; } RVector delta = p - circle2.getCenter(); // root term: double term = RMath::pow(RVector::getDotProduct(d, delta), 2.0) - RMath::pow(d.getMagnitude(), 2.0) * (RMath::pow(delta.getMagnitude(), 2.0) - RMath::pow(circle2.getRadius(), 2.0)); // no intersection: if (term<0.0) { return res; } // one or two intersections: double t1 = (- RVector::getDotProduct(d, delta) + sqrt(term)) / RMath::pow(d.getMagnitude(), 2.0); double t2; bool tangent = false; // only one intersection: if (fabs(term) < RS::PointTolerance) { t2 = t1; tangent = true; } // two intersections else { t2 = (-RVector::getDotProduct(d, delta) - sqrt(term)) / RMath::pow(d.getMagnitude(), 2.0); } RVector sol1; RVector sol2 = RVector::invalid; sol1 = p + d * t1; if (!tangent) { sol2 = p + d * t2; } if (!limited || line1.isOnShape(sol1)) { res.append(sol1); } if (sol2.isValid()) { if (!limited || line1.isOnShape(sol2)) { res.append(sol2); } } // ret.setTangent(tangent); return res; }
RXLine::RXLine(const RLine& line) : basePoint(line.getStartPoint()), directionVector(line.getEndPoint()-line.getStartPoint()) { }