void RExporter::exportSpline(const RSpline& spline, double offset) { RLinetypePattern p = getLinetypePattern(); bool continuous = false; if (getEntity() == NULL || !p.isValid() || p.getNumDashes() == 1 || draftMode || screenBasedLinetypes) { continuous = true; } if (!continuous) { p.scale(getPatternFactor()); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = getPatternOffset(length, p); } exportExplodable(spline, offset); } else { // version <= 3.0.0 was (line interpolation): //exportExplodable(spline, offset); // performance improvement (using real splines): RPainterPath pp; pp.setPen(QPen(Qt::SolidLine)); pp.addSpline(spline); exportPainterPaths(QList<RPainterPath>() << pp); } /* RLinetypePattern p = getLinetypePattern(); p.scale(getPatternFactor()); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = getPatternOffset(length, p); } double currentOffset = offset; QList<QSharedPointer<RShape> > sub = spline.getExploded(); QList<QSharedPointer<RShape> >::iterator it; for (it=sub.begin(); it!=sub.end(); ++it) { QSharedPointer<RLine> line = (*it).dynamicCast<RLine>(); if (!line.isNull()) { exportLine(*line.data(), currentOffset); currentOffset -= line->getLength(); } QSharedPointer<RArc> arc = (*it).dynamicCast<RArc>(); if (!arc.isNull()) { exportArc(*arc.data(), currentOffset); currentOffset -= arc->getLength(); } } */ }
QScriptValue REcmaLinetypePattern::getNumDashes (QScriptContext* context, QScriptEngine* engine) { //REcmaHelper::functionStart("REcmaLinetypePattern::getNumDashes", context, engine); //qDebug() << "ECMAScript WRAPPER: REcmaLinetypePattern::getNumDashes"; //QCoreApplication::processEvents(); QScriptValue result = engine->undefinedValue(); // public function: can be called from ECMA wrapper of ECMA shell: RLinetypePattern* self = getSelf("getNumDashes", context); //Q_ASSERT(self!=NULL); if (self==NULL) { return REcmaHelper::throwError("self is NULL", context); } if( context->argumentCount() == 0 ){ // prepare arguments: // end of arguments // call C++ function: // return type 'int' int cppResult = self->getNumDashes(); // return type: int // standard Type result = QScriptValue(cppResult); } else { return REcmaHelper::throwError("Wrong number/types of arguments for RLinetypePattern.getNumDashes().", context); } //REcmaHelper::functionEnd("REcmaLinetypePattern::getNumDashes", context, engine); return result; }
void RExporter::exportExplodable(const RExplodable& explodable, double offset) { QList<QSharedPointer<RShape> > sub = explodable.getExploded(); RLinetypePattern p = getLinetypePattern(); if (!p.isValid() || p.getNumDashes() <= 1 || draftMode || screenBasedLinetypes || twoColorSelectedMode) { for (int i=0; i<sub.length(); i++) { QSharedPointer<RLine> lineP = sub[i].dynamicCast<RLine>(); if (!lineP.isNull()) { exportLine(*lineP.data()); continue; } QSharedPointer<RArc> arc = sub[i].dynamicCast<RArc>(); if (!arc.isNull()) { exportArc(*arc.data()); continue; } } return; } if (getEntity()!=NULL && (getEntity()->getType()!=RS::EntitySpline || RSpline::hasProxy())) { // all explodable entities including splines if we have a spline proxy: RShapesExporter(*this, sub, offset); return; } // use alternative algorithm for splines if we don't have a spline proxy: double dist; for (int i=0; i<sub.length(); i++) { QSharedPointer<RLine> lineP = sub[i].dynamicCast<RLine>(); if (!lineP.isNull()) { RLine line = *lineP.data(); dist = exportLine(line, offset); offset -= lineP->getLength(); continue; } QSharedPointer<RArc> arc = sub[i].dynamicCast<RArc>(); if (!arc.isNull()) { exportArc(*arc.data(), offset); offset -= arc->getLength(); continue; } } }
/** * \return Offset to use to apply the given pattern to an entity of the * given length that the pattern is symmetrical. */ double RExporter::getPatternOffset(double length, const RLinetypePattern& pattern) { double optOffset = 0.0; double gap = 0.0; double maxGap = RMINDOUBLE; for (int i = 0; i < pattern.getNumDashes(); ++i) { if (!pattern.isSymmetrical(i)) { continue; } double offset = getPatternOffset(length, pattern, i, &gap); if (gap > maxGap) { maxGap = gap; optOffset = offset; // qDebug(QString("RExporter::getPatternOffset: i=%1").arg(i)); } } return optOffset; }
void RExporter::exportPolyline(const RPolyline& polyline, double offset) { RLinetypePattern p = getLinetypePattern(); bool continuous = false; if (getEntity() == NULL || !p.isValid() || p.getNumDashes() == 1 || draftMode || screenBasedLinetypes) { continuous = true; } if (!continuous) { p.scale(getPatternFactor()); if (RMath::isNaN(offset)) { double length = polyline.getLength(); offset = getPatternOffset(length, p); } } exportExplodable(polyline, offset); }
/** * \param polylineGen True: use pattern along whole polyline, False: pattern per segment. */ void RExporter::exportPolyline(const RPolyline& polyline, bool polylineGen, double offset) { RLinetypePattern p = getLinetypePattern(); bool continuous = false; if (getEntity() == NULL || !p.isValid() || p.getNumDashes() <= 1 || draftMode || screenBasedLinetypes || twoColorSelectedMode) { continuous = true; } if (!continuous) { p.scale(getLineTypePatternScale(p)); if (RMath::isNaN(offset)) { double length = polyline.getLength(); offset = p.getPatternOffset(length); } } if (polylineGen) { // pattern along whole polyline: exportExplodable(polyline, offset); } else { // pattern for each individual segment: for (int i=0; i<polyline.countSegments(); i++) { QSharedPointer<RShape> shape = polyline.getSegmentAt(i); QSharedPointer<RLine> line = shape.dynamicCast<RLine>(); if (!line.isNull()) { RExporter::exportLine(*line); } QSharedPointer<RArc> arc = shape.dynamicCast<RArc>(); if (!arc.isNull()) { RExporter::exportArc(*arc); } } } }
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::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; }
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; */ }
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; }
void RExporter::exportSpline(const RSpline& spline, double offset) { RLinetypePattern p = getLinetypePattern(); bool continuous = false; if (getEntity() == NULL || !p.isValid() || p.getNumDashes() <= 1 || draftMode || screenBasedLinetypes || twoColorSelectedMode) { continuous = true; } p.scale(getLineTypePatternScale(p)); double patternLength = p.getPatternLength(); if (patternLength<RS::PointTolerance || spline.getLength() / patternLength > RSettings::getDashThreshold()) { continuous = true; } if (!continuous) { if (getEntity()!=NULL && (getEntity()->getType()!=RS::EntitySpline || RSpline::hasProxy())) { // we have a spline proxy: RShapesExporter(*this, QList<QSharedPointer<RShape> >() << QSharedPointer<RShape>(spline.clone()), offset); } else { // fallback if we don't have a spline proxy: p.scale(getLineTypePatternScale(p)); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = p.getPatternOffset(length); } exportExplodable(spline, offset); } } else { // version <= 3.0.0 was (line interpolation): //exportExplodable(spline, offset); // performance improvement (using real splines): RPainterPath pp; pp.setPen(currentPen); pp.setInheritPen(true); pp.addSpline(spline); exportPainterPaths(QList<RPainterPath>() << pp); } /* RLinetypePattern p = getLinetypePattern(); p.scale(getLineTypePatternScale(p)); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = p.getPatternOffset(length); } double currentOffset = offset; QList<QSharedPointer<RShape> > sub = spline.getExploded(); QList<QSharedPointer<RShape> >::iterator it; for (it=sub.begin(); it!=sub.end(); ++it) { QSharedPointer<RLine> line = (*it).dynamicCast<RLine>(); if (!line.isNull()) { exportLine(*line.data(), currentOffset); currentOffset -= line->getLength(); } QSharedPointer<RArc> arc = (*it).dynamicCast<RArc>(); if (!arc.isNull()) { exportArc(*arc.data(), currentOffset); currentOffset -= arc->getLength(); } } */ }