void RMemoryStorage::clear() { RStorage::clear(); maxLineweight = RLineweight::Weight000; inTransaction = false; boundingBoxChanged = true; boundingBox[0][0] = RBox(); boundingBox[0][1] = RBox(); boundingBox[1][0] = RBox(); boundingBox[1][1] = RBox(); objectMap.clear(); objectHandleMap.clear(); entityMap.clear(); blockEntityMap.clear(); blockMap.clear(); layerMap.clear(); linetypeMap.clear(); transactionMap.clear(); documentVariables.clear(); variables.clear(); variableCaseMap.clear(); if (!documentVariables.isNull()) { documentVariables->clear(); } //linetypeScale = 1.0; setLastTransactionId(-1); }
RBox RArc::getBoundingBox() const { RVector minV; RVector maxV; double minX = qMin(getStartPoint().x, getEndPoint().x); double minY = qMin(getStartPoint().y, getEndPoint().y); double maxX = qMax(getStartPoint().x, getEndPoint().x); double maxY = qMax(getStartPoint().y, getEndPoint().y); if (getStartPoint().getDistanceTo(getEndPoint()) < 1.0e-6 && getRadius() > 1.0e5) { minV = RVector(minX, minY); maxV = RVector(maxX, maxY); return RBox(minV, maxV); } double a1 = RMath::getNormalizedAngle(!isReversed() ? startAngle : endAngle); double a2 = RMath::getNormalizedAngle(!isReversed() ? endAngle : startAngle); // check for left limit: if ((a1<M_PI && a2>M_PI) || (a1>a2-1.0e-12 && a2>M_PI) || (a1>a2-1.0e-12 && a1<M_PI) ) { minX = qMin(center.x - radius, minX); } // check for right limit: if (a1 > a2-1.0e-12) { maxX = qMax(center.x + radius, maxX); } // check for bottom limit: if ((a1<(M_PI_2*3) && a2>(M_PI_2*3)) || (a1>a2-1.0e-12 && a2>(M_PI_2*3)) || (a1>a2-1.0e-12 && a1<(M_PI_2*3)) ) { minY = qMin(center.y - radius, minY); } // check for top limit: if ((a1<M_PI_2 && a2>M_PI_2) || (a1>a2-1.0e-12 && a2>M_PI_2) || (a1>a2-1.0e-12 && a1<M_PI_2) ) { maxY = qMax(center.y + radius, maxY); } minV = RVector(minX, minY); maxV = RVector(maxX, maxY); return RBox(minV, maxV); }
void RSpline::updateInternal() const { if (!dirty || updateInProgress) { return; } dirty = false; updateInProgress = true; if (degree<1) { invalidate(); qWarning() << "RSpline::updateInternal: invalid degree: " << degree; updateInProgress = false; return; } exploded.clear(); // if fit points are known, update from fit points, otherwise from // control points: // TODO: use fitpoints from DXF/DWG file if possible (fit points might not correspond to control points): if (fitPoints.size()==0) { updateFromControlPoints(); } else { updateFromFitPoints(); } //updateBoundingBox(); boundingBox = RBox(); //getExploded(); updateInProgress = false; }
RBox RSpline::getBoundingBox() const { if (!isValid()) { return RBox(); } if (!boundingBox.isValid()) { updateBoundingBox(); } return boundingBox; //ON_3dPoint onmin; //ON_3dPoint onmax; //curve.GetBoundingBox(onmin, onmax); // double min[3]; // double max[3]; // curve.GetBBox(min, max); //ON_BoundingBox bb; //curve.GetTightBoundingBox(bb); //return RBox(RVector(bb.Min().x, bb.Min().y), RVector(bb.Max().x, bb.Max().y)); //return RBox(RVector(min[0], min[1]), RVector(max[0], max[1])); }
RBox REllipse::getBoundingBox() const { double radius1 = getMajorRadius(); double radius2 = getMinorRadius(); double angle = getAngle(); double a1 = ((!isReversed()) ? startParam : endParam); double a2 = ((!isReversed()) ? endParam : startParam); RVector startPoint = getStartPoint(); RVector endPoint = getEndPoint(); double minX = qMin(startPoint.x, endPoint.x); double minY = qMin(startPoint.y, endPoint.y); double maxX = qMax(startPoint.x, endPoint.x); double maxY = qMax(startPoint.y, endPoint.y); // kind of a brute force. TODO: exact calculation RVector vp; double a = a1; do { vp.set(center.x + radius1 * cos(a), center.y + radius2 * sin(a)); vp.rotate(angle, center); minX = qMin(minX, vp.x); minY = qMin(minY, vp.y); maxX = qMax(maxX, vp.x); maxY = qMax(maxY, vp.y); a += 0.03; } while (RMath::isAngleBetween(a, a1, a2, false) && a<4*M_PI); return RBox(RVector(minX,minY), RVector(maxX,maxY)); }
void RSpline::updateInternal() const { if (!dirty || updateInProgress) { return; } dirty = false; updateInProgress = true; if (degree<2 || degree>3) { invalidate(); qWarning() << "RSpline::updateInternal: invalid degree: " << degree; updateInProgress = false; return; } exploded.clear(); // if fit points are known, update from fit points, otherwise from // control points: if (fitPoints.size()==0) { updateFromControlPoints(); } else { updateFromFitPoints(); } //updateBoundingBox(); boundingBox = RBox(); getExploded(); updateInProgress = false; }
void RDimensionData::update() const { dirty = true; textData.update(); boundingBox = RBox(); if (!autoUpdatesBlocked) { //qDebug() << "clear dim block name"; dimBlockName = ""; } }
void RSpatialIndexSimple::addToIndex( int id, int pos, double x1, double y1, double z1, double x2, double y2, double z2) { Q_ASSERT(si[id].size()==pos); si[id].insert(pos, RBox(RVector(x1,y1,z1), RVector(x2,y2,z2))); //si.insert(id, RBox(RVector(x1,y1,z1), RVector(x2,y2,z2))); }
RBox RGraphicsView::getBox() const { QList<RVector> corners = mapCornersFromView(); return RBox( RVector::getMinimum( RVector::getMinimum(corners[0], corners[1]), RVector::getMinimum(corners[2], corners[3]) ), RVector::getMaximum( RVector::getMaximum(corners[0], corners[1]), RVector::getMaximum(corners[2], corners[3]) ) ); }
RBox RTextBasedData::getBoundingBox(bool ignoreEmpty) const { if (!boundingBox.isValid() || dirty) { getPainterPaths(gotDraft); } // if (!ignoreEmpty && boundingBox.getWidth()<RS::PointTolerance && boundingBox.getHeight()<RS::PointTolerance) { // RDebug::printBacktrace(); // } if (ignoreEmpty && boundingBox.getWidth()<RS::PointTolerance && boundingBox.getHeight()<RS::PointTolerance) { return RBox(); } return boundingBox; }
/** * Maps the given \c box (e.g. a 3d bounding box) to a 2d box * in view coordinates (pixels). */ RBox RGraphicsView::mapToView(const RBox& box) const { QList<RVector> boxCorners = box.getCorners(); RVector minView(RMAXDOUBLE, RMAXDOUBLE, RMAXDOUBLE); RVector maxView(RMINDOUBLE, RMINDOUBLE, RMINDOUBLE); RVector corner; for (int i=0; i<8; i++) { corner = mapToView(boxCorners[i]); minView = RVector::getMinimum(corner, minView); maxView = RVector::getMaximum(corner, maxView); } return RBox(minView, maxView); }
RBox RPolyline::getBoundingBox() const { RBox ret; if (countVertices()==1) { ret = RBox(vertices.at(0), vertices.at(0)); } QList<QSharedPointer<RShape> > sub = getExploded(); QList<QSharedPointer<RShape> >::iterator it; for (it=sub.begin(); it!=sub.end(); ++it) { RBox bb = (*it)->getBoundingBox(); ret.growToInclude(bb); } return ret; }
/** * \return The one shape that is part of this entity which is the * closest to the given position. */ QSharedPointer<RShape> REntityData::getClosestShape(const RVector& pos, double range) const { QSharedPointer<RShape> ret; QList<QSharedPointer<RShape> > shapes; if (RMath::isNaN(range)) { shapes = getShapes(); } else { shapes = getShapes(RBox(pos, range)); } // entity not based on shape: if (shapes.size()==0) { return ret; } // entity based on one or more shapes, find closest: double minDistance = RMAXDOUBLE; QList<QSharedPointer<RShape> >::const_iterator it; for (it=shapes.constBegin(); it!=shapes.constEnd(); ++it) { QSharedPointer<RShape> shape = (*it); // explode shape if possible: QList<QSharedPointer<RShape> > subShapes; QSharedPointer<RExplodable> explodable = shape.dynamicCast<RExplodable>(); // 20110916: interpolated shapes are not exploded at this point (e.g. // for perpendicular snap to spline inside block): if (explodable.isNull() || shape->isInterpolated()) { subShapes.append(QSharedPointer<RShape>(shape->clone())); } else { subShapes = explodable->getExploded(); } QList<QSharedPointer<RShape> >::iterator it2; for (it2=subShapes.begin(); it2!=subShapes.end(); ++it2) { QSharedPointer<RShape> shape2 = (*it2); double distance = shape2->getDistanceTo(pos); if (distance<minDistance) { minDistance = distance; ret = shape2; } } } return ret; }
RBox RMemoryStorage::getBoundingBox(bool includeHiddenLayer) { if (!boundingBoxChanged) { return boundingBox; } RBlock::Id currentBlockId = getCurrentBlockId(); boundingBox = RBox(); maxLineweight = RLineweight::Weight000; QHash<RObject::Id, QSharedPointer<REntity> >::iterator it; for (it = entityMap.begin(); it != entityMap.end(); ++it) { QSharedPointer<REntity> e = *it; if (e.isNull() || e->isUndone()) { continue; } if (includeHiddenLayer) { QSharedPointer<RLayer> layer = queryLayerDirect(e->getLayerId()); if (layer.isNull() || layer->isFrozen()) { continue; } } if (e->getBlockId() == currentBlockId) { boundingBox.growToInclude(e->getBoundingBox()); } // resolve line width ByLayer: //QSharedPointer<RLayer> layer = // queryLayerDirect(e->getLayerId()); // don't resolve block references, if line weight is ByBlock, // the maxLinewidth will be adjusted when the block reference // is encountered: QStack<RBlockReferenceEntity*> blockRefStack; RLineweight::Lineweight lw = e->getLineweight(true, blockRefStack); if (!boundingBox.isValid()) { maxLineweight = lw; } else { maxLineweight = qMax(lw, maxLineweight); } } boundingBoxChanged = false; return boundingBox; }
/** * \return Closest point to \c point on this entity. Used for snap to * points on entity. */ RVector REntityData::getClosestPointOnEntity(const RVector& point, double range, bool limited) const { Q_UNUSED(range) RVector ret = RVector::invalid; double minDist = RMAXDOUBLE; QList<QSharedPointer<RShape> > shapes = getShapes(RBox(), true); for (int i=0; i<shapes.size(); i++) { RVector r = shapes.at(i)->getClosestPointOnShape(point, limited); double dist = r.getDistanceTo(point); if (!ret.isValid() || dist<minDist) { ret = r; minDist = dist; } } return ret; }
void RMemoryStorage::clear() { RStorage::clear(); maxLineweight = RLineweight::Weight000; inTransaction = false; boundingBoxChanged = true; boundingBox = RBox(); objectMap.clear(); entityMap.clear(); blockEntityMap.clear(); blockMap.clear(); layerMap.clear(); transactionMap.clear(); variables.clear(); variableCaseMap.clear(); knownVariables.clear(); linetypeScale = 1.0; setLastTransactionId(-1); }
RBox RMemoryStorage::getBoundingBox(bool ignoreHiddenLayers, bool ignoreEmpty) const { if (!boundingBoxChanged) { return boundingBox[ignoreHiddenLayers][ignoreEmpty]; } RBlock::Id currentBlockId = getCurrentBlockId(); boundingBox[0][0] = RBox(); boundingBox[0][1] = RBox(); boundingBox[1][0] = RBox(); boundingBox[1][1] = RBox(); maxLineweight = RLineweight::Weight000; QHash<RObject::Id, QSharedPointer<REntity> >::const_iterator it; for (it = entityMap.constBegin(); it != entityMap.constEnd(); ++it) { QSharedPointer<REntity> e = *it; if (e.isNull() || e->isUndone()) { continue; } //if (ignoreHiddenLayers) { bool layerHidden = false; QSharedPointer<RLayer> layer = queryLayerDirect(e->getLayerId()); if (layer.isNull() || layer->isFrozen()) { layerHidden = true; } //} if (e->getBlockId() == currentBlockId) { //bb.growToInclude(e->getBoundingBox(ignoreEmpty)); RBox bb = e->getBoundingBox(false); RBox bbIgnoreEmpty = e->getBoundingBox(true); if (!bb.isSane()) { continue; } boundingBox[0][0].growToInclude(bb); boundingBox[0][1].growToInclude(bbIgnoreEmpty); if (!layerHidden) { boundingBox[1][0].growToInclude(bb); boundingBox[1][1].growToInclude(bbIgnoreEmpty); } } // don't resolve block references, if line weight is ByBlock, // the maxLinewidth will be adjusted when the block reference // is encountered: QStack<REntity*> blockRefStack; RLineweight::Lineweight lw = e->getLineweight(true, blockRefStack); maxLineweight = qMax(lw, maxLineweight); } boundingBoxChanged = false; // qDebug() << "\n\nbb: " << boundingBox[0][0]; // qDebug() << "bb ignoreEmpty: " << boundingBox[0][1]; // qDebug() << "bb ignoreHiddenLayers: " << boundingBox[1][0]; // qDebug() << "bb ignoreHiddenLayers, ignoreEmpty: " << boundingBox[1][1]; return boundingBox[ignoreHiddenLayers][ignoreEmpty]; }
RBox RViewportData::getBoundingBox(bool ignoreEmpty) const { Q_UNUSED(ignoreEmpty) RVector v(width/2, height/2); return RBox(center - v, center + v); }
/** * Renders the text data into painter paths. */ void RTextRenderer::renderSimple() { boundingBox = RBox(); painterPaths.clear(); richText = ""; RVector pos = textData.getAlignmentPoint(); QString text = textData.getEscapedText(); double textHeight = textData.getTextHeight(); RS::VAlign verticalAlignment = textData.getVAlign(); RS::HAlign horizontalAlignment = textData.getHAlign(); QString fontName = textData.getFontName(); bool bold = textData.isBold(); bool italic = textData.isItalic(); double angle = textData.getAngle(); // degree: text.replace(QRegExp(RTextRenderer::rxDegree), RTextRenderer::chDegree); // plus minus: text.replace(QRegExp(RTextRenderer::rxPlusMinus), RTextRenderer::chPlusMinus); // diameter: text.replace(QRegExp(RTextRenderer::rxDiameter), RTextRenderer::chDiameter); bool leadingSpaces = false; bool trailingSpaces = false; if (!text.isEmpty()) { if (text.at(0).isSpace()) { leadingSpaces = true; } if (text.at(text.length()-1).isSpace()) { trailingSpaces = true; } } // implicit top level format block: QTextCharFormat f; f.setForeground(QBrush(QColor())); currentFormat.push(f); QTextLayout::FormatRange fr; fr.start = 0; fr.length = text.length(); fr.format = currentFormat.top(); QList<QTextLayout::FormatRange> formats; formats.append(fr); blockHeight.push(textHeight); blockFont.push(fontName); blockBold.push(bold); blockItalic.push(italic); useCadFont.push(RFontList::isCadFont(blockFont.top())); openTags.push(QStringList()); double horizontalAdvance = 0.0; double horizontalAdvanceNoSpacing = 0.0; double ascent = 0.0; double descent = 0.0; // get painter paths for text at height 1.0: painterPaths = getPainterPathsForBlock( text, formats, horizontalAdvance, horizontalAdvanceNoSpacing, ascent, descent); width = horizontalAdvanceNoSpacing * textHeight; // transform to scale text from 1.0 to current text height: QTransform sizeTransform; sizeTransform.scale(textHeight, textHeight); // transform paths of text: boundingBox = RBox(); for (int i=0; i<painterPaths.size(); ++i) { painterPaths[i].transform(sizeTransform); boundingBox.growToInclude(painterPaths[i].getBoundingBox()); } // feature size of a text is its height // determines if text is displayed or only bounding box double featureSize = boundingBox.getHeight(); QPen pen; for (int i=0; i<painterPaths.size(); ++i) { if (i==0) { pen = painterPaths[i].getPen(); if (pen.style()==Qt::NoPen) { pen = QPen(painterPaths[i].getBrush().color()); } } painterPaths[i].setFeatureSize(featureSize); } RPainterPath bbPath; bbPath.addBox(boundingBox); bbPath.setFeatureSize(-featureSize); bbPath.setPen(pen); painterPaths.append(bbPath); //height = boundingBox.getHeight(); double topLine = textHeight; double baseLine = 0.0; double bottomLine = descent * textHeight; // at this point, the text is at 0/0 with the base line of the // first text line at y==0 double xOffset = 0.0; double yOffset = 0.0; switch (verticalAlignment) { case RS::VAlignTop: yOffset = -topLine; break; case RS::VAlignMiddle: yOffset = -textHeight/2.0; break; case RS::VAlignBase: yOffset = -baseLine; break; case RS::VAlignBottom: yOffset = -bottomLine; break; } switch (horizontalAlignment) { default: case RS::HAlignAlign: case RS::HAlignFit: case RS::HAlignLeft: if (!leadingSpaces) { // move completely to the left (left border is 0.0): xOffset = -boundingBox.getMinimum().x; } else { xOffset = 0.0; } break; case RS::HAlignMid: case RS::HAlignCenter: if (!leadingSpaces && !trailingSpaces) { xOffset = -(boundingBox.getMinimum().x + boundingBox.getMaximum().x)/2.0; } else { xOffset = -horizontalAdvance/2.0*textHeight; } break; case RS::HAlignRight: if (!trailingSpaces) { xOffset = -boundingBox.getMaximum().x; } else { xOffset = -horizontalAdvance*textHeight; } break; } height = boundingBox.getHeight(); QTransform globalTransform; globalTransform.translate(pos.x, pos.y); globalTransform.rotate(RMath::rad2deg(angle)); globalTransform.translate(xOffset, yOffset); // apply global transform for position, angle and vertical alignment: boundingBox = RBox(); for (int i=0; i<painterPaths.size(); ++i) { painterPaths[i].transform(globalTransform); boundingBox.growToInclude(painterPaths[i].getBoundingBox()); } }
/** * Updates the grid information, in particular the grid spacing and * grid region to the current view port. */ void ROrthoGrid::update(bool force) { if (!force && viewBox==view.getBox()) { return; } viewBox = view.getBox(); int viewportNumber = view.getViewportNumber(); RDocument* doc = view.getDocument(); if (doc == NULL) { qWarning() << "ROrthoGrid::update: document is NULL"; return; } RGraphicsScene* scene = view.getScene(); if (scene==NULL) { qWarning() << "ROrthoGrid::update: scene is NULL"; return; } RS::ProjectionRenderingHint hint = scene->getProjectionRenderingHint(); // for 3d views, we have no convenient way to calculate the grid dimensions: if (hint == RS::RenderThreeD) { gridBox = RBox(RVector(-1000, -1000), RVector(1000, 1000)); spacing = RVector(10.0, 10.0); return; } QString key; key = QString("Grid/IsometricGrid0%1").arg(viewportNumber); isometric = doc->getVariable(key, false, true).toBool(); RS::Unit unit = doc->getUnit(); RS::LinearFormat linearFormat = doc->getLinearFormat(); // default values for missing configurations and 'auto' settings: minSpacing.valid = true; minMetaSpacing.valid = true; if (isFractionalFormat(linearFormat) && !RUnit::isMetric(unit)) { //minSpacing.x = minSpacing.y = RNANDOUBLE; //minMetaSpacing.x = minMetaSpacing.y = RNANDOUBLE; minSpacing.x = minSpacing.y = RUnit::convert(1.0, RS::Inch, unit) / 1024; minMetaSpacing.x = minMetaSpacing.y = RUnit::convert(1.0, RS::Inch, unit) / 1024; } else { minSpacing.x = minSpacing.y = 1.0e-6; minMetaSpacing.x = minMetaSpacing.y = RNANDOUBLE; //minSpacing.x = minSpacing.y = RNANDOUBLE; //minMetaSpacing.x = minMetaSpacing.y = 1.0e-6; } key = QString("Grid/GridSpacingX0%1").arg(viewportNumber); QVariant strSx = doc->getVariable(key, QVariant(), true); key = QString("Grid/GridSpacingY0%1").arg(viewportNumber); QVariant strSy = doc->getVariable(key, QVariant(), true); spacing.valid = true; bool autoX = !strSx.isValid() || strSx.toString()=="auto"; bool autoY = !strSy.isValid() || strSy.toString()=="auto"; // grid spacing x: if (!autoX) { // fixed: double d = RMath::eval(strSx.toString()); if (!RMath::hasError() && d>RS::PointTolerance) { minSpacing.x = spacing.x = d; } } // grid spacing y: if (!autoY) { double d = RMath::eval(strSy.toString()); if (!RMath::hasError() && d>RS::PointTolerance) { minSpacing.y = spacing.y = d; } } // meta grid: key = QString("Grid/MetaGridSpacingX0%1").arg(viewportNumber); QVariant strMsx = doc->getVariable(key, QVariant(), true); key = QString("Grid/MetaGridSpacingY0%1").arg(viewportNumber); QVariant strMsy = doc->getVariable(key, QVariant(), true); metaSpacing.valid = true; bool metaAutoX = !strMsx.isValid() || strMsx.toString()=="auto"; bool metaAutoY = !strMsy.isValid() || strMsy.toString()=="auto"; // meta grid spacing x: if (!metaAutoX) { // fixed: double d = RMath::eval(strMsx.toString()); if (d>RS::PointTolerance) { minMetaSpacing.x = metaSpacing.x = d; } } // meta grid spacing y: if (!metaAutoY) { // fixed: double d = RMath::eval(strMsy.toString()); if (d>RS::PointTolerance) { minMetaSpacing.y = metaSpacing.y = d; } } // auto scale grid: QList<RVector> s = getIdealSpacing(minPixelSpacing, minSpacing, minMetaSpacing); if (RSettings::getAutoScaleGrid()) { autoSpacing = spacing = s.at(0); } if (RSettings::getAutoScaleMetaGrid()) { autoMetaSpacing = metaSpacing = s.at(1); } // switch grid off below given pixel limit: if (view.mapDistanceToView(spacing.x) < minPixelSpacing) { spacing = RVector::invalid; } if (view.mapDistanceToView(metaSpacing.x) < minPixelSpacing) { metaSpacing = RVector::invalid; } if (view.mapDistanceToView(spacing.y) < minPixelSpacing) { spacing = RVector::invalid; } if (view.mapDistanceToView(metaSpacing.y) < minPixelSpacing) { metaSpacing = RVector::invalid; } // qDebug() << "spacing: " << spacing; // qDebug() << "minSpacing: " << minSpacing; // qDebug() << "metaSpacing: " << metaSpacing; // if (scaleGrid) { // QList<RVector> s = ROrthoGrid::getIdealSpacing(view, minPixelSpacing, minSpacing); // spacing = s.at(0); // metaSpacing = s.at(1); // } else { // spacing = minSpacing; // } RVector minGridPoint; RVector maxGridPoint; spacing.z = 1; if (isometric) { spacing.x = spacing.y * 2.0 * sin(M_PI/3.0); metaSpacing.x = metaSpacing.y * 2.0 * sin(M_PI/3.0); spacing = spacing / 2; //metaSpacing = metaSpacing / 2; } minGridPoint = viewBox.getCorner1(); minGridPoint = minGridPoint.getDividedComponents(spacing).getFloor(); minGridPoint = minGridPoint.getMultipliedComponents(spacing); maxGridPoint = viewBox.getCorner2(); maxGridPoint = maxGridPoint.getDividedComponents(spacing).getCeil(); maxGridPoint = maxGridPoint.getMultipliedComponents(spacing); minGridPoint.z = viewBox.getCorner1().z; maxGridPoint.z = viewBox.getCorner2().z; gridBox = RBox(minGridPoint, maxGridPoint); minGridPoint = viewBox.getCorner1(); minGridPoint = minGridPoint.getDividedComponents(metaSpacing).getFloor(); minGridPoint = minGridPoint.getMultipliedComponents(metaSpacing); maxGridPoint = viewBox.getCorner2(); maxGridPoint = maxGridPoint.getDividedComponents(metaSpacing).getCeil(); maxGridPoint = maxGridPoint.getMultipliedComponents(metaSpacing); minGridPoint.z = viewBox.getCorner1().z; maxGridPoint.z = viewBox.getCorner2().z; metaGridBox = RBox(minGridPoint, maxGridPoint); if (isometric) { QString i1 = RUnit::getLabel(spacing.x / cos(M_PI/6), *doc, true, true); QString i2 = RUnit::getLabel(metaSpacing.x / cos(M_PI/6) / 2, *doc, true, true); infoText = QString("%1 < %2").arg(i1).arg(i2); } else { QString i1 = RUnit::getLabel(spacing.x, *doc, true, true); QString i2 = RUnit::getLabel(metaSpacing.x, *doc, true, true); infoText = QString("%1 < %2").arg(i1).arg(i2); } }
void RDimensionData::update() const { dirty = true; textData.update(); boundingBox = RBox(); }
/** * Renders the text data into painter paths. */ void RTextRenderer::render() { boundingBox = RBox(); painterPaths.clear(); richText = ""; QString text = textData.getEscapedText(); //RVector position = textData.getPosition(); RVector position = textData.getAlignmentPoint(); double textHeight = textData.getTextHeight(); RS::VAlign verticalAlignment = textData.getVAlign(); RS::HAlign horizontalAlignment = textData.getHAlign(); double lineSpacingFactor = textData.getLineSpacingFactor(); QString fontName = textData.getFontName(); bool bold = textData.isBold(); bool italic = textData.isItalic(); double angle = textData.getAngle(); //RColor color = textData.getColor(true); // split up text where separation is required due to line feed, // or any type of height change (\P, \H[0-9]*.?[0-9]+;, \S*/*;) // split up text at every formatting change: QRegExp regExpAll(rxAll); QStringList literals = text.split(regExpAll); // collect formatting change information for every literal: QStringList formattings; int pos = 0; while ((pos = regExpAll.indexIn(text, pos)) != -1) { formattings << regExpAll.cap(1); pos += regExpAll.matchedLength(); } // x position in real units: double xCursor = 0.0; // y position for next text line: double yCursor = 0.0; // painter paths for current text line: QList<RPainterPath> linePaths; // max ascent of current text line: double maxAscent = 0.0; // max descent of current line: double minDescent = 0.0; // max descent of _previous_ line: double minDescentPrevious = 0.0; // true for the first block of the current line: bool firstBlockInLine = true; // current line has leading spaces: bool leadingSpaces = false; // current line has trailing spaces: bool trailingSpaces = false; // text has leading empty lines: bool leadingEmptyLines = false; // text has trailing empty lines: bool trailingEmptyLines = false; int lineCounter = 0; QString textBlock; QList<QTextLayout::FormatRange> formats; bool blockChangedHeightOrFont = false; // implicit top level format block: QTextCharFormat f; f.setForeground(QBrush(QColor())); currentFormat.push(f); QTextLayout::FormatRange fr; fr.start = 0; fr.length = 0; fr.format = currentFormat.top(); formats.append(fr); blockHeight.push(textHeight); blockFont.push(fontName); blockBold.push(bold); blockItalic.push(italic); useCadFont.push(RFontList::isCadFont(fontName)); openTags.push(QStringList()); width = 0.0; height = 0.0; // iterate through all text blocks: for (int i=0; i<literals.size(); ++i) { // the literal text, e.g. "ABC": QString literal = literals.at(i); // the formatting _after_ the text, e.g. "\C1;" QString formatting; if (i<formattings.size()) { formatting = formattings.at(i); } // qDebug() << "block: " << i; // qDebug() << " literal: " << literal; // qDebug() << " literal length: " << literal.length(); // qDebug() << " formatting: " << formatting; // qDebug() << " font: " << blockFont.top(); // qDebug() << " height: " << blockHeight.top(); // qDebug() << " cad font: " << useCadFont.top(); // qDebug() << " xCursor: " << xCursor; // handle literal: textBlock.append(literal); if (firstBlockInLine) { if (!literal.isEmpty()) { if (literal.at(0).isSpace()) { leadingSpaces = true; } } else { if (formatting=="\\~") { leadingSpaces = true; } } firstBlockInLine = false; } bool lineFeed = false; bool paragraphFeed = false; bool heightChange = false; bool stackedText = false; bool fontChange = false; bool colorChange = false; bool blockEnd = false; // detect formatting that ends the current text block: if (!formatting.isEmpty()) { fontChange = QRegExp(rxFontChangeTtf).exactMatch(formatting) || QRegExp(rxFontChangeCad).exactMatch(formatting); colorChange = QRegExp(rxColorChangeCustom).exactMatch(formatting) || QRegExp(rxColorChangeIndex).exactMatch(formatting); heightChange = QRegExp(rxHeightChange).exactMatch(formatting); stackedText = QRegExp(rxStackedText).exactMatch(formatting); lineFeed = QRegExp(rxLineFeed).exactMatch(formatting); paragraphFeed = QRegExp(rxParagraphFeed).exactMatch(formatting); blockEnd = QRegExp(rxEndBlock).exactMatch(formatting); } bool start = (i==0); bool end = (i==literals.size()-1); // first line is empty: if (textBlock.trimmed().isEmpty() && (lineFeed || paragraphFeed || end)) { if (start) { leadingEmptyLines = true; } else if (end) { trailingEmptyLines = true; } } // reached a new text block that needs to be rendered separately // due to line feed, height change, font change, ...: if (target==RichText || lineFeed || paragraphFeed || heightChange || stackedText || fontChange || colorChange || end || (blockEnd && blockChangedHeightOrFont)) { // render block _before_ format change that requires new block: if (textBlock!="") { // fix format lengths to match up. each format stops where // the next one starts. formatting that is carried over is set // again for the new format. for (int i=0; i<formats.size(); i++) { int nextStart; if (i<formats.size()-1) { nextStart = formats[i+1].start; } else { nextStart = textBlock.length(); } formats[i].length = nextStart - formats[i].start; } if (target==RichText) { richText += getRichTextForBlock( textBlock, formats); } if (target==PainterPaths) { double horizontalAdvance = 0.0; double horizontalAdvanceNoSpacing = 0.0; double ascent = 0.0; double descent = 0.0; QList<RPainterPath> paths; // get painter paths for current text block at height 1.0: paths = getPainterPathsForBlock( textBlock, formats, horizontalAdvance, horizontalAdvanceNoSpacing, ascent, descent); // transform to scale text from 1.0 to current text height: QTransform sizeTransform; sizeTransform.scale(blockHeight.top(), blockHeight.top()); // transform for current block due to xCursor position: QTransform blockTransform; blockTransform.translate(xCursor, 0); // combine transforms for current text block: QTransform allTransforms = sizeTransform; allTransforms *= blockTransform; maxAscent = qMax(maxAscent, ascent * blockHeight.top()); minDescent = qMin(minDescent, descent * blockHeight.top()); // transform paths of current block and append to paths // of current text line: for (int i=0; i<paths.size(); ++i) { RPainterPath p = paths.at(i); p.transform(allTransforms); linePaths.append(p); } xCursor += horizontalAdvance * blockHeight.top(); } } // empty text, might be line feed, we need ascent, descent anyway: else if (lineFeed || paragraphFeed || end) { if (target==PainterPaths) { double horizontalAdvance = 0.0; double horizontalAdvanceNoSpacing = 0.0; double ascent = 0.0; double descent = 0.0; // get painter paths for current text block at height 1.0: getPainterPathsForBlock( "A", QList<QTextLayout::FormatRange>(), horizontalAdvance, horizontalAdvanceNoSpacing, ascent, descent); maxAscent = qMax(maxAscent, ascent * blockHeight.top()); minDescent = qMin(minDescent, descent * blockHeight.top()); } } // handle stacked text: if (stackedText) { QRegExp reg; reg.setPattern(rxStackedText); reg.exactMatch(formatting); if (target==PainterPaths) { double horizontalAdvance[2]; horizontalAdvance[0] = 0.0; horizontalAdvance[1] = 0.0; double horizontalAdvanceNoSpacing[2]; horizontalAdvanceNoSpacing[0] = 0.0; horizontalAdvanceNoSpacing[1] = 0.0; double heightFactor = 0.4; // s==0: superscript, s==1: subscript: for (int s=0; s<=1; ++s) { QString script = reg.cap(s+1); QList<RPainterPath> paths; double ascent = 0.0; double descent = 0.0; QList<QTextLayout::FormatRange> localFormats; QTextLayout::FormatRange fr; fr.start = 0; fr.length = script.length(); fr.format = currentFormat.top(); localFormats.append(fr); // get painter paths for superscript at height 1.0: paths = getPainterPathsForBlock( script, localFormats, horizontalAdvance[s], horizontalAdvanceNoSpacing[s], ascent, descent); if (s==0) { maxAscent = qMax(maxAscent, ascent * blockHeight.top() * heightFactor + blockHeight.top()*(1.0-heightFactor)); } else { minDescent = qMin(minDescent, descent * blockHeight.top() * heightFactor); } // transform to scale text from 1.0 to current text height * 0.4: QTransform sizeTransform; sizeTransform.scale(blockHeight.top()*heightFactor, blockHeight.top()*heightFactor); // move top text more to the right for italic texts: double xOffset = 0.0; if (s==0 && blockItalic.top()==true) { double y = blockHeight.top()*(1.0-heightFactor); // assume italic means roughly 12 degrees: xOffset = tan(RMath::deg2rad(12)) * y; } // transform for current block due to xCursor position // and top or bottom text position: QTransform blockTransform; blockTransform.translate(xCursor + xOffset, s==0 ? blockHeight.top()*(1.0-heightFactor) : 0.0); horizontalAdvance[s] += xOffset / (blockHeight.top() * heightFactor); // combine transforms for current text block: QTransform allTransforms = sizeTransform; allTransforms *= blockTransform; // transform paths of current block and append to paths // of current text line: for (int i=0; i<paths.size(); ++i) { RPainterPath p = paths.at(i); p.transform(allTransforms); linePaths.append(p); } } xCursor += qMax(horizontalAdvance[0], horizontalAdvance[1]) * blockHeight.top() * heightFactor; } if (target==RichText) { QString super = reg.cap(1); QString sub = reg.cap(2); if (!super.isEmpty()) { richText += QString("<span style=\"vertical-align:super;\">%1</span>").arg(super); } if (!sub.isEmpty()) { richText += QString("<span style=\"vertical-align:sub;\">%1</span>").arg(sub); } } } // prepare for next text block: if (lineFeed || paragraphFeed || end) { if (!textBlock.isEmpty()) { if (textBlock.at(textBlock.length()-1).isSpace()) { trailingSpaces = true; } } // else { // if (formatting=="\\~") { // trailingSpaces = true; // } // } } textBlock = ""; formats.clear(); QTextLayout::FormatRange fr; fr.start = 0; fr.length = 0; fr.format = currentFormat.top(); formats.append(fr); // handle text line. // add all painter paths of the current line to result set of // painter paths. apply line feed transformations. if (lineFeed || paragraphFeed || end) { // qDebug() << "lineFeed: adding text line:"; // qDebug() << " maxAscent: " << maxAscent; // qDebug() << " minDescent: " << minDescent; // qDebug() << " minDescentPrevious: " << minDescentPrevious; // qDebug() << "got line feed or end"; // qDebug() << " trailingSpaces: " << trailingSpaces; if (target==RichText) { if (lineFeed || paragraphFeed) { richText += "<br/>"; } } if (lineCounter!=0) { yCursor += (minDescentPrevious - maxAscent) * lineSpacingFactor; } // calculate line bounding box for alignment without spaces at borders: RBox lineBoundingBox; for (int i=0; i<linePaths.size(); ++i) { RPainterPath p = linePaths.at(i); lineBoundingBox.growToInclude(p.getBoundingBox()); } double featureSize = lineBoundingBox.getHeight(); QTransform lineTransform; switch (horizontalAlignment) { case RS::HAlignAlign: case RS::HAlignFit: case RS::HAlignLeft: if (!leadingSpaces) { // move completely to the left (left border is 0.0): lineTransform.translate( -lineBoundingBox.getMinimum().x, yCursor); } else { lineTransform.translate(0, yCursor); } break; case RS::HAlignMid: case RS::HAlignCenter: if (!leadingSpaces && !trailingSpaces) { lineTransform.translate( -(lineBoundingBox.getMinimum().x + lineBoundingBox.getMaximum().x)/2.0, yCursor); } else { lineTransform.translate(-xCursor/2.0, yCursor); } break; case RS::HAlignRight: if (!trailingSpaces) { lineTransform.translate(-lineBoundingBox.getMaximum().x, yCursor); } else { lineTransform.translate(-xCursor, yCursor); } break; } width = qMax(width, lineBoundingBox.getMaximum().x - qMin(0.0, lineBoundingBox.getMinimum().x)); // add current text line to result set: QPen pen; for (int i=0; i<linePaths.size(); ++i) { RPainterPath p = linePaths[i]; if (i==0) { pen = p.getPen(); if (pen.style()==Qt::NoPen) { pen = QPen(p.getBrush().color()); } } p.transform(lineTransform); p.setFeatureSize(featureSize); painterPaths.append(p); boundingBox.growToInclude(p.getBoundingBox()); } RPainterPath bbPath; bbPath.addBox(lineBoundingBox); bbPath.transform(lineTransform); bbPath.setFeatureSize(-featureSize); bbPath.setPen(pen); painterPaths.append(bbPath); lineCounter++; xCursor = 0.0; maxAscent = 0.0; minDescentPrevious = minDescent; minDescent = 0.0; linePaths.clear(); firstBlockInLine = true; leadingSpaces = false; trailingSpaces = false; } } // handle formatting after text block, that applies either // to the next text block(s) or the rest of the current text block: if (formatting.isEmpty()) { continue; } QRegExp reg; // unicode: reg.setPattern(rxUnicode); if (reg.exactMatch(formatting)) { textBlock += QChar(reg.cap(1).toInt(0, 16)); continue; } // curly braket open: reg.setPattern(rxCurlyOpen); if (reg.exactMatch(formatting)) { textBlock += "{"; continue; } // curly braket close: reg.setPattern(rxCurlyClose); if (reg.exactMatch(formatting)) { textBlock += "}"; continue; } // degree: reg.setPattern(rxDegree); if (reg.exactMatch(formatting)) { textBlock += chDegree; continue; } // plus/minus: reg.setPattern(rxPlusMinus); if (reg.exactMatch(formatting)) { textBlock += chPlusMinus; continue; } // diameter: reg.setPattern(rxDiameter); if (reg.exactMatch(formatting)) { textBlock += chDiameter; continue; } // backslash: reg.setPattern(rxBackslash); if (reg.exactMatch(formatting)) { textBlock += "\\"; continue; } // non-breaking space: reg.setPattern(rxNonBreakingSpace); if (reg.exactMatch(formatting)) { if (target==PainterPaths) { textBlock += QChar(Qt::Key_nobreakspace); } if (target==RichText) { textBlock += " "; } continue; } // font change (TTF): reg.setPattern(rxFontChangeTtf); if (reg.exactMatch(formatting)) { blockFont.top() = reg.cap(1); blockBold.top() = (reg.cap(2).toInt()!=0); blockItalic.top() = (reg.cap(3).toInt()!=0); useCadFont.top() = false; blockChangedHeightOrFont = true; if (target==RichText) { QString style; style += QString("font-family:%1;").arg(blockFont.top()); style += QString("font-weight:%1;").arg(blockBold.top() ? "bold" : "normal"); style += QString("font-style:%1;").arg(blockItalic.top() ? "italic" : "normal"); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } // font change (CAD): reg.setPattern(rxFontChangeCad); if (reg.exactMatch(formatting)) { blockFont.top() = reg.cap(1); useCadFont.top() = true; if (xCursor>RS::PointTolerance) { RFont* f = RFontList::get(blockFont.top()); if (f!=NULL && f->isValid()) { xCursor += f->getLetterSpacing() / 9.0; } } blockChangedHeightOrFont = true; if (target==RichText) { QString style; style += QString("font-family:%1;").arg(blockFont.top()); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } // height change: reg.setPattern(rxHeightChange); if (reg.exactMatch(formatting)) { bool factor = reg.cap(2)=="x"; if (factor) { blockHeight.top() *= reg.cap(1).toDouble(); } else { blockHeight.top() = reg.cap(1).toDouble(); } blockChangedHeightOrFont = true; if (target==RichText) { QString style; style += QString("font-size:%1pt;").arg(blockHeight.top() * fontHeightFactor); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } QTextLayout::FormatRange fr; fr.start = textBlock.length(); fr.length = 0; // start format block: reg.setPattern(rxBeginBlock); if (reg.exactMatch(formatting)) { currentFormat.push(currentFormat.top()); blockFont.push(blockFont.top()); blockBold.push(blockBold.top()); blockItalic.push(blockItalic.top()); blockHeight.push(blockHeight.top()); useCadFont.push(useCadFont.top()); if (target==RichText) { openTags.push(QStringList()); } blockChangedHeightOrFont = false; continue; } // end format block: reg.setPattern(rxEndBlock); if (reg.exactMatch(formatting)) { currentFormat.pop(); fr.format = currentFormat.top(); formats.append(fr); blockFont.pop(); blockBold.pop(); blockItalic.pop(); blockHeight.pop(); useCadFont.pop(); blockChangedHeightOrFont = false; if (target==RichText) { // close all tags that were opened in this block: for (int i=openTags.top().size()-1; i>=0; --i) { QString tag = openTags.top().at(i); richText += QString("</%1>").arg(tag); } openTags.pop(); } continue; } // color change (indexed): reg.setPattern(rxColorChangeIndex); if (reg.exactMatch(formatting)) { RColor blockColor = RColor::createFromCadIndex(reg.cap(1)); if (blockColor.isByLayer()) { currentFormat.top().setForeground(RColor::CompatByLayer); } else if (blockColor.isByBlock()) { currentFormat.top().setForeground(RColor::CompatByBlock); } else { currentFormat.top().setForeground(blockColor); } fr.format = currentFormat.top(); formats.append(fr); if (target==RichText) { QString style; style += QString("color:%1;").arg(blockColor.name()); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } // color change (custom): reg.setPattern(rxColorChangeCustom); if (reg.exactMatch(formatting)) { RColor blockColor = RColor::createFromCadCustom(reg.cap(1)); currentFormat.top().setForeground(blockColor); fr.format = currentFormat.top(); formats.append(fr); if (target==RichText) { QString style; style += QString("color:%1;").arg(blockColor.name()); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } } if (target==PainterPaths) { // at this point, the text is at 0/0 with the base line of the // first text line at y==0 // vertical alignment: double topLine = qMax(textHeight, boundingBox.getMaximum().y); double bottomLine = qMin(0.0, boundingBox.getMinimum().y); QTransform globalTransform; globalTransform.translate(position.x, position.y); globalTransform.rotate(RMath::rad2deg(angle)); switch (verticalAlignment) { case RS::VAlignTop: //if (!leadingEmptyLines) { globalTransform.translate(0.0, -topLine); //} break; case RS::VAlignMiddle: if (leadingEmptyLines || trailingEmptyLines) { globalTransform.translate(0.0, -(yCursor+textHeight)/2.0); } else { globalTransform.translate(0.0, -(bottomLine + topLine) / 2.0); } break; case RS::VAlignBottom: case RS::VAlignBase: if (trailingEmptyLines) { globalTransform.translate(0.0, -yCursor); } else { globalTransform.translate(0.0, -bottomLine); } break; } height = boundingBox.getHeight(); // apply global transform for position, angle and vertical alignment: boundingBox = RBox(); for (int i=0; i<painterPaths.size(); ++i) { painterPaths[i].transform(globalTransform); boundingBox.growToInclude(painterPaths[i].getBoundingBox()); } } if (target==RichText) { // close all tags that were opened: for (int i=openTags.top().size()-1; i>=0; --i) { QString tag = openTags.top().at(i); richText += QString("</%1>").arg(tag); } } }
RBox RTriangle::getBoundingBox() const { return RBox(RVector::getMinimum(RVector::getMinimum(corner[0], corner[1]), corner[2]), RVector::getMaximum(RVector::getMaximum(corner[0], corner[1]), corner[2])); }
void RSpline::update() const { dirty = true; boundingBox = RBox(); exploded.clear(); }
RBox RPoint::getBoundingBox() const { return RBox(position, position); }
RBox RLine::getBoundingBox() const { return RBox(RVector::getMinimum(startPoint, endPoint), RVector::getMaximum( startPoint, endPoint)); }
RBox RCircle::getBoundingBox() const { return RBox(center - RVector(radius, radius), center + RVector(radius, radius)); }
RBox RXLine::getBoundingBox() const { return RBox( RVector::getMinimum(basePoint, getSecondPoint()), RVector::getMaximum(basePoint, getSecondPoint()) ); }