/** * \return List of splines which approximate the given arc. */ QList<RSpline> RSpline::createSplinesFromArc(const RArc& arc) { RArc a = arc; if (a.isReversed()) { a.reverse(); } double startAngle = RMath::getNormalizedAngle(a.getStartAngle()); double endAngle = RMath::getNormalizedAngle(a.getEndAngle()); if (a.isFullCircle()) { startAngle = 0.0; endAngle = 2*M_PI; } // normalize startAngle, endAngle to [-2PI, 2PI] double twoPI = M_PI * 2; //double startAngle = RMath::getNormalizedAngle(a.getStartAngle()); //double endAngle = RMath::getNormalizedAngle(a.getEndAngle()); if (startAngle>endAngle) { startAngle-=2*M_PI; } double radius = a.getRadius(); double EPSILON = 0.00001; // Compute the sequence of arc curves, up to PI/2 at a time. Total arc angle // is less than 2PI. QList<RSpline> curves; double piOverTwo = M_PI_2; double segmentationAngle = piOverTwo/4; //double segmentationAngle = M_PI/8; double sgn = (startAngle < endAngle) ? +1 : -1; double a1 = startAngle; for (double totalAngle = qMin(twoPI, qAbs(endAngle - startAngle)); totalAngle > EPSILON; ) { double a2 = a1 + sgn * qMin(totalAngle, segmentationAngle); RSpline sp = RSpline::createBezierFromSmallArc(radius, a1, a2); sp.move(a.getCenter()); curves.append(sp); totalAngle -= qAbs(a2 - a1); a1 = a2; } return curves; }
void RExporter::exportArc(const RArc& arc, double offset) { if (!arc.isValid()) { return; } RLinetypePattern p = getLinetypePattern(); if (getEntity() == NULL || !p.isValid() || p.getNumDashes() == 1 || draftMode || screenBasedLinetypes) { exportArcSegment(arc); return; } RArc normalArc = arc; if (arc.isReversed()) { normalArc.reverse(); } if (normalArc.radius < 1.0e-12) { return; } p.scale(getPatternFactor()); double length = normalArc.getLength(); 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) { exportArcSegment(arc); return; } double* vp = NULL; vp = new double[p.getNumDashes()]; for (int i = 0; i < p.getNumDashes(); ++i) { vp[i] = fabs(p.getDashLengthAt(i)) / normalArc.radius; } if (RMath::isNaN(offset)) { offset = getPatternOffset(length, p); } QList<RArc> arcSegments; bool done = false; int i = 0; double cursor = normalArc.getStartAngle() + offset / normalArc.radius; double total = offset; bool dashFound = false; bool gapFound = false; double a1 = normalArc.getStartAngle(); double a2; do { if (dashFound && !gapFound) { if (total + fabs(p.getDashLengthAt(i)) >= length - 1.0e-6) { arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, normalArc.getEndAngle())); break; } arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, a2)); } if (p.getDashLengthAt(i) > 0) { // dash, no gap if (total + p.getDashLengthAt(i) > 0) { a1 = cursor; if (total < 0 || !dashFound) { a1 = normalArc.startAngle; } a2 = cursor + vp[i]; if (fabs(a2 - normalArc.getStartAngle()) > 1.0e-6) { dashFound = true; } } gapFound = false; } 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) { arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, normalArc.getEndAngle())); } else { arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, a2)); } } if (arc.isReversed()) { for (int i=arcSegments.length()-1; i>=0; i--) { arcSegments[i].reverse(); exportArcSegment(arcSegments[i]); } } else { for (int i=0; i<arcSegments.length(); i++) { exportArcSegment(arcSegments[i]); } } delete[] vp; }
void RExporter::exportArc(const RArc& arc, double offset) { if (!arc.isValid()) { return; } if (getEntity() == NULL || draftMode || screenBasedLinetypes || twoColorSelectedMode) { exportArcSegment(arc); return; } RLinetypePattern p = getLinetypePattern(); if (!p.isValid() || p.getNumDashes() <= 1) { exportArcSegment(arc); return; } p.scale(getLineTypePatternScale(p)); double patternLength = p.getPatternLength(); if (patternLength<RS::PointTolerance || arc.getLength() / patternLength > RSettings::getDashThreshold()) { exportArcSegment(arc); return; } RArc normalArc = arc; if (arc.isReversed()) { normalArc.reverse(); } if (normalArc.radius < 1.0e-12) { return; } RArcExporter(*this, arc, offset); /* p.scale(getLineTypePatternScale(p)); double length = normalArc.getLength(); 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) { exportArcSegment(arc); return; } double* vp = NULL; vp = new double[p.getNumDashes()]; for (int i = 0; i < p.getNumDashes(); ++i) { vp[i] = fabs(p.getDashLengthAt(i)) / normalArc.radius; } //bool optimizeEnds = false; if (RMath::isNaN(offset)) { offset = p.getPatternOffset(length); //optimizeEnds = true; } QList<RArc> arcSegments; bool done = false; int i = 0; double cursor = normalArc.getStartAngle() + offset / normalArc.radius; double total = offset; bool dashFound = false; bool gapFound = false; double a1 = normalArc.getStartAngle(); double a2 = 0.0; do { if (dashFound && !gapFound) { if (total + fabs(p.getDashLengthAt(i)) >= length - 1.0e-6) { arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, normalArc.getEndAngle())); break; } arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, a2)); } if (p.getDashLengthAt(i) >= 0.0) { // dash, no gap if (total + p.getDashLengthAt(i) >= 0.0) { a1 = cursor; if (total < 0.0 || !dashFound) { a1 = normalArc.startAngle; } a2 = cursor + vp[i]; if (fabs(a2 - normalArc.getStartAngle()) > 1.0e-6) { dashFound = true; } } gapFound = false; } else { gapFound = true; } cursor += vp[i]; total += fabs(p.getDashLengthAt(i)); done = total > length; if (!done && total>0.0) { // handle shape at end of dash / gap: if (p.hasShapeAt(i)) { QList<RPainterPath> pps = p.getShapeAt(i); // RVector min = RPainterPath::getMinList(pps); // RVector max = RPainterPath::getMaxList(pps); RPainterPath::rotateList(pps, cursor+M_PI/2); RPainterPath::translateList(pps, normalArc.getPointAtAngle(cursor)); exportPainterPaths(pps); } } ++i; if (i >= p.getNumDashes()) { i = 0; } } while (!done); if (!gapFound || !dashFound) { if (total + fabs(p.getDashLengthAt(i)) >= length - 1.0e-6) { arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, normalArc.getEndAngle())); } else { arcSegments.append(RArc(normalArc.getCenter(), normalArc.getRadius(), a1, a2)); } } if (arc.isReversed()) { for (int i=arcSegments.length()-1; i>=0; i--) { arcSegments[i].reverse(); exportArcSegment(arcSegments[i], true); } } else { for (int i=0; i<arcSegments.length(); i++) { exportArcSegment(arcSegments[i], true); } } delete[] vp; */ }