void REllipse::moveEndPoint(const RVector& pos, bool changeAngleOnly) { if (changeAngleOnly) { endParam = getParamTo(pos); } else { RVector sp = getStartPoint(); double distOri = sp.getDistanceTo(getEndPoint()); double angleOri = sp.getAngleTo(getEndPoint()); if (distOri<RS::PointTolerance) { return; } double distNew = sp.getDistanceTo(pos); double angleNew = sp.getAngleTo(pos); double factor = distNew / distOri; if (factor<RS::PointTolerance) { return; } double angleDelta = angleNew - angleOri; center.scale(factor, sp); center.rotate(angleDelta, sp); majorPoint.scale(factor); majorPoint.rotate(angleDelta); } }
/** * \overload */ RRefPoint RGraphicsView::getClosestReferencePoint(REntity::Id entityId, const RVector& screenPosition) { RRefPoint ret = RVector::invalid; double minDist = RMAXDOUBLE; if (scene == NULL) { return ret; } if (getDocument() == NULL) { return ret; } QSharedPointer<REntity> entity = getDocument()->queryEntity(entityId); if (entity.isNull()) { return ret; } QList<RRefPoint> referencePoints = entity->getReferencePoints(scene->getProjectionRenderingHint()); QList<RRefPoint>::iterator it; for (it=referencePoints.begin(); it!=referencePoints.end(); it++) { RVector rp = mapToView(*it); double dist = screenPosition.getDistanceTo(rp); if (dist<minDist) { minDist = dist; ret = (*it); } } return ret; }
/** * Creates an arc from 3 points. */ RArc RArc::createFrom3Points(const RVector& startPoint, const RVector& point, const RVector& endPoint) { // intersection of two middle lines // middle points between first two points: RVector mp1 = RVector::getAverage(startPoint, point); double a1 = startPoint.getAngleTo(point) + M_PI / 2.0; // direction from middle point to center: RVector dir1 = RVector::createPolar(1.0, a1); // middle points between last two points: RVector mp2 = RVector::getAverage(point, endPoint); double a2 = point.getAngleTo(endPoint) + M_PI / 2.0; // direction from middle point to center: RVector dir2 = RVector::createPolar(1.0, a2); RLine midLine1(mp1, mp1 + dir1); RLine midLine2(mp2, mp2 + dir2); QList<RVector> ips = midLine1.getIntersectionPoints(midLine2, false); if (ips.length()!=1) { //this.error = qsTr("No arc possible"); return RArc(); } RVector center = ips[0]; double radius = center.getDistanceTo(endPoint); double angle1 = center.getAngleTo(startPoint); double angle2 = center.getAngleTo(endPoint); bool reversed = RMath::isAngleBetween(center.getAngleTo(point), angle1, angle2, true); return RArc(center, radius, angle1, angle2, reversed); }
RCircle RCircle::createFrom3Points(const RVector& p1, const RVector& p2, const RVector& p3) { // intersection of two middle lines // middle points between first two points: RVector mp1 = RVector::getAverage(p1, p2); double a1 = p1.getAngleTo(p2) + M_PI / 2.0; // direction from middle point to center: RVector dir1 = RVector::createPolar(1.0, a1); // middle points between last two points: RVector mp2 = RVector::getAverage(p2, p3); double a2 = p2.getAngleTo(p3) + M_PI / 2.0; // direction from middle point to center: RVector dir2 = RVector::createPolar(1.0, a2); RLine midLine1(mp1, mp1 + dir1); RLine midLine2(mp2, mp2 + dir2); QList<RVector> ips = midLine1.getIntersectionPoints(midLine2, false); if (ips.length()!=1) { return RCircle(); } RVector center = ips[0]; double radius = center.getDistanceTo(p3); // double angle1 = center.getAngleTo(p1); // double angle2 = center.getAngleTo(p3); // bool reversed = RMath::isAngleBetween(center.getAngleTo(p2), // angle1, angle2, true); return RCircle(center, radius); }
/** * Creates an arc from its startpoint, endpoint and bulge (= tan(angle/4)). */ RArc RArc::createFrom2PBulge(const RVector& startPoint, const RVector& endPoint, double bulge) { RArc arc; arc.reversed = (bulge < 0.0); double alpha = atan(bulge) * 4.0; RVector middle = (startPoint + endPoint) / 2.0; double dist = startPoint.getDistanceTo(endPoint) / 2.0; // alpha can't be 0.0 at this point arc.radius = fabs(dist / sin(alpha / 2.0)); double wu = fabs(RMath::pow(arc.radius, 2.0) - RMath::pow(dist, 2.0)); double h = sqrt(wu); double angle = startPoint.getAngleTo(endPoint); if (bulge > 0.0) { angle += M_PI / 2.0; } else { angle -= M_PI / 2.0; } if (fabs(alpha) > M_PI) { h *= -1.0; } arc.center.setPolar(h, angle); arc.center += middle; arc.startAngle = arc.center.getAngleTo(startPoint); arc.endAngle = arc.center.getAngleTo(endPoint); return arc; }
bool RSpline::isOnShape(const RVector& point, bool limited, double tolerance) const { if (hasProxy()) { double t = getTAtPoint(point); RVector p = getPointAt(t); return point.getDistanceTo(p) < tolerance; } else { return RShape::isOnShape(point, limited, tolerance); } }
QSharedPointer<RShape> RCircle::getTransformed(const QTransform& transform) const { RVector ct = center.getTransformed2d(transform); RVector sp = center + RVector(radius, 0); RVector spt = sp.getTransformed2d(transform); return QSharedPointer<RShape>( new RCircle( ct, ct.getDistanceTo(spt) ) ); }
bool RDimDiametricData::moveReferencePoint(const RVector& referencePoint, const RVector& targetPoint) { bool ret = false; if (referencePoint.equalsFuzzy(chordPoint)) { RVector c = (definitionPoint + chordPoint)/2.0; double d = c.getDistanceTo(chordPoint); double a = c.getAngleTo(targetPoint); RVector v = RVector::createPolar(d, a); chordPoint = c + v; definitionPoint = c - v; autoTextPos = true; ret = true; } else if (referencePoint.equalsFuzzy(definitionPoint)) { RVector c = (definitionPoint + chordPoint)/2.0; double d = c.getDistanceTo(definitionPoint); double a = c.getAngleTo(targetPoint); RVector v = RVector::createPolar(d, a); definitionPoint = c + v; chordPoint = c - v; autoTextPos = true; ret = true; } if (!ret) { ret = RDimensionData::moveReferencePoint(referencePoint, targetPoint); } if (ret) { update(); } return ret; }
/** * Maps the given model position to the grid. */ RVector ROrthoGrid::snapToGrid(const RVector& positionUcs) { RDocumentInterface* documentInterface = view.getDocumentInterface(); if (documentInterface==NULL) { return RVector::invalid; } RVector sp = spacing; if (!sp.isValid()) { sp = metaSpacing; if (isometric) { sp /= 2; } } int x = RMath::mround(positionUcs.x / sp.x); int y = RMath::mround(positionUcs.y / sp.y); int z = RMath::mround(positionUcs.z / sp.z); // closest grid point is not available in isometric grid, // find closest available grid point: if (isometric && (x+y)%2!=0) { int cx, cy; double minDist = RMAXDOUBLE; double dist; for (int ix=-1; ix<=1; ix++) { for (int iy=-1; iy<=1; iy++) { if (qAbs(ix) + qAbs(iy)!=1) { continue; } cx = RMath::mround(positionUcs.x / sp.x) + ix; cy = RMath::mround(positionUcs.y / sp.y) + iy; dist = positionUcs.getDistanceTo(RVector(cx*sp.x, cy*sp.y)); if (dist<minDist) { x = cx; y = cy; minDist = dist; } } } } RVector gridPositionUcs = RVector( x * sp.x, y * sp.y, z * sp.z ); RUcs ucs = documentInterface->getCurrentUcs(); return ucs.mapFromUcs(gridPositionUcs); }
/** * \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(); 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 RSpline::removeFitPointAt(const RVector& point) { double minDist = RMAXDOUBLE; int index = -1; for (int i=0; i<fitPoints.length(); i++) { double dist = point.getDistanceTo(fitPoints[i]); if (dist<minDist) { minDist = dist; index = i; } } if (index<0 || index>=fitPoints.length()) { return; } fitPoints.removeAt(index); update(); }
/** * Finds the reference point that is the closest to the given screen * coordinate (in pixels). * * \param range Maximum distance in pixels. * * \return The closest referecene point in model coordinates. */ RRefPoint RGraphicsView::getClosestReferencePoint(const RVector& screenPosition, int range) { RRefPoint ret = RVector::invalid; if (scene == NULL) { return ret; } double minDist = (double) range; QMultiMap<REntity::Id, RRefPoint>& referencePoints = scene->getReferencePoints(); QMultiMap<REntity::Id, RRefPoint>::iterator it; for (it = referencePoints.begin(); it != referencePoints.end(); it++) { RVector rp = mapToView(*it); double dist = screenPosition.getDistanceTo(rp); if (dist < minDist) { minDist = dist; ret = *it; } } return ret; }
/** * \todo Not working as expected, fix or disable */ QSharedPointer<RShape> RArc::getTransformed(const QTransform& transform) const { RVector ct = center.getTransformed2d(transform); RVector sp = getStartPoint(); RVector spt = sp.getTransformed2d(transform); RVector ep = getEndPoint(); RVector ept = ep.getTransformed2d(transform); //RVector mp = getMiddlePoint(); //RVector mpt = mp.getTransformed2d(transform); RArc* ret = new RArc( ct, ct.getDistanceTo(spt), ct.getAngleTo(spt), ct.getAngleTo(ept), reversed ); // if (!ret->getMiddlePoint().equalsFuzzy(mpt)) { // ret->setReversed(!reversed); // } return QSharedPointer<RShape>(ret); }
void REventHandler::drawSnapLabel(QPainter* painter, const RVector& pos, const RVector& posRestriction, const QString& text) { RVector p = graphicsView->mapToView(pos); RVector pr = RVector::invalid; if (posRestriction.isValid()) { pr = graphicsView->mapToView(posRestriction); } RColor color = RSettings::getColor("GraphicsViewColors/TextLabelColor", RColor(249,198,31)); painter->setPen(color); QFont font = RSettings::getSnapLabelFont(); font.setPointSizeF(font.pointSizeF()*graphicsView->getDevicePixelRatio()); QFontMetrics fm(font); painter->setFont(font); int offset = 5 * graphicsView->getDevicePixelRatio(); if (!text.isEmpty()) { painter->drawText( p.x + offset, p.y + offset, fm.width(text)+10, fm.height()+10, Qt::AlignHCenter | Qt::AlignVCenter, text, NULL); } painter->drawEllipse(p.x-offset, p.y-offset, offset*2, offset*2); // restriction position: if (pr.isSane()) { painter->drawLine(pr.x, pr.y-offset, pr.x+offset, pr.y); painter->drawLine(pr.x+offset, pr.y, pr.x, pr.y+offset); painter->drawLine(pr.x, pr.y+offset, pr.x-offset, pr.y); painter->drawLine(pr.x-offset, pr.y, pr.x, pr.y-offset); } // display distance/angle: int display = RSettings::getIntValue("DisplaySettings/DisplayDistanceAngle", 0); if (display == 0) { return; } RDocumentInterface* di = graphicsView->getDocumentInterface(); RDocument* doc = graphicsView->getDocument(); RVector relativeZero = di->getRelativeZero(); double dist, angle; if (posRestriction.isSane()) { dist = relativeZero.getDistanceTo(posRestriction); angle = relativeZero.getAngleTo(posRestriction); } else { dist = relativeZero.getDistanceTo(pos); angle = relativeZero.getAngleTo(pos); } int lp = doc->getLinearPrecision(); QString distStr = RUnit::doubleToString(dist, lp); angle = RMath::rad2deg(angle); int ap = doc->getAnglePrecision(); QString angStr = RUnit::doubleToString(angle, ap); QString sep = RSettings::getStringValue("Input/PolarCoordinateSeparator", "<"); color = RSettings::getColor("GraphicsViewColors/MeasurementToolsColor", RColor(155,220,112)); painter->setPen(color); QString displayText; switch (display) { case 0: displayText = ""; break; case 1: displayText = distStr + sep + angStr + QChar(0x00b0); break; case 2: displayText = distStr; break; case 3: displayText = angStr + QChar(0x00b0); break; default: displayText = ""; } if (!displayText.isEmpty()) { painter->drawText( p.x + offset, p.y - 3*offset - fm.height(), fm.width(displayText)+10, fm.height()+10, Qt::AlignHCenter | Qt::AlignVCenter, displayText, NULL); } }
RCircle RCircle::createFrom2Points(const RVector& p1, const RVector& p2) { RVector center = (p1+p2)/2.0; double radius = p1.getDistanceTo(p2)/2.0; return RCircle(center, radius); }
bool RCircle::contains(const RVector& p) const { return p.getDistanceTo(center) < radius; // TODO: + RS::PointTolerance ? }
/** * Creates a dimensioning line (line with one, two or no arrows). */ QList<QSharedPointer<RShape> > RDimensionData::getDimensionLineShapes( const RVector& p1, const RVector& p2, bool arrow1, bool arrow2) const { QList<QSharedPointer<RShape> > ret; // text height (DIMTXT) double dimtxt = getDimtxt(); // text distance to line (DIMGAP) double dimgap = getDimgap(); // length of dimension line: dimLineLength = p1.getDistanceTo(p2); // do we have to put the arrows outside of the line? bool outsideArrows = (dimLineLength < getDimasz()*2.5); // arrow angles: double arrowAngle1, arrowAngle2; // Create dimension line: RLine dimensionLine(p1, p2); if (outsideArrows==false) { arrowAngle1 = dimensionLine.getDirection2(); arrowAngle2 = RMath::getNormalizedAngle(arrowAngle1+M_PI); } else { arrowAngle1 = dimensionLine.getDirection1(); arrowAngle2 = RMath::getNormalizedAngle(arrowAngle1+M_PI); // extend dimension line outside arrows RVector dir; dir.setPolar(getDimasz()*2, arrowAngle2); dimensionLine.setStartPoint(p1 + dir); dimensionLine.setEndPoint(p2 - dir); } ret.append(QSharedPointer<RShape>(new RLine(dimensionLine))); if (arrow1) { QList<QSharedPointer<RShape> > arrow = getArrow(p1, arrowAngle1); ret.append(arrow); } if (arrow2) { QList<QSharedPointer<RShape> > arrow = getArrow(p2, arrowAngle2); ret.append(arrow); } double dimAngle1 = dimensionLine.getDirection1(); bool corrected=false; defaultAngle = RMath::makeAngleReadable(dimAngle1, true, &corrected); if (autoTextPos) { RVector newTextPos = dimensionLine.getMiddlePoint(); RVector distV; // rotate text so it's readable from the bottom or right (ISO) // quadrant 1 & 4 if (corrected) { distV.setPolar(dimgap + dimtxt/2.0, dimAngle1-M_PI/2.0); } else { distV.setPolar(dimgap + dimtxt/2.0, dimAngle1+M_PI/2.0); } // move text away from dimension line: newTextPos+=distV; textPositionCenter = newTextPos; } return ret; }
RVector RSnapIntersection::snap( const RVector& position, RGraphicsView& view, const QMap<REntity::Id, QSet<int> >& candidates, const RBox& queryBox) { RDocument* document = view.getDocument(); if (document==NULL) { return lastSnap; } REntity::Id entityId1 = REntity::INVALID_ID; REntity::Id entityId2 = REntity::INVALID_ID; lastSnap = RVector::invalid; double minDist = RMAXDOUBLE; double dist; QMap<REntity::Id, QSet<int> >::const_iterator it1; for (it1=candidates.begin(); it1!=candidates.end(); it1++) { if (RMouseEvent::hasMouseMoved()) { lastSnap = RVector::invalid; return RVector::invalid; } QSharedPointer<REntity> e1 = document->queryEntityDirect(it1.key()); if (e1.isNull()) { continue; } if (e1->getType()==RS::EntityText || e1->getType()==RS::EntityAttribute || e1->getType()==RS::EntityAttributeDefinition) { continue; } QMap<REntity::Id, QSet<int> >::const_iterator it2; for (it2=it1; it2!=candidates.end(); it2++) { if (RMouseEvent::hasMouseMoved()) { lastSnap = RVector::invalid; return RVector::invalid; } QSharedPointer<REntity> e2 = document->queryEntityDirect(it2.key()); if (e2.isNull()) { continue; } if (e2->getType()==RS::EntityText || e2->getType()==RS::EntityAttribute || e2->getType()==RS::EntityAttributeDefinition) { continue; } QList<RVector> candidates = e1->getIntersectionPoints(*e2, true, queryBox); if (candidates.isEmpty()) { continue; } RVector candidate = position.getClosest(candidates); dist = candidate.getDistanceTo(position); if (dist<minDist) { lastSnap = candidate; minDist = dist; entityId1 = e1->getId(); entityId2 = e2->getId(); } } } if (!lastSnap.isValid()) { lastSnap = position; lastSnap.valid = false; return lastSnap; } else { if (entityId1!=REntity::INVALID_ID) { entityIds.insert(entityId1); } if (entityId2!=REntity::INVALID_ID) { entityIds.insert(entityId2); } return lastSnap; } }
RVector RRestrictOrthogonal::restrictSnap(const RVector& position, const RVector& relativeZero) { RVector ret; RVector retX; RVector retY; if (documentInterface==NULL) { return ret; } RGraphicsView* view = documentInterface->getLastKnownViewWithFocus(); if (view==NULL) { return ret; } ROrthoGrid* grid = dynamic_cast<ROrthoGrid*>(view->getGrid()); if (grid!=NULL && grid->isIsometric()) { double d1, d2; double a1, a2; switch (grid->getProjection()) { default: case RS::IsoTop: a1 = RMath::deg2rad(30); a2 = RMath::deg2rad(150); // d1 = x / cos(30): d1 = (position.x - relativeZero.x) / (sqrt(3.0)/2); d2 = -d1; break; case RS::IsoLeft: a1 = RMath::deg2rad(150); a2 = RMath::deg2rad(90); d1 = (position.x - relativeZero.x) / (-sqrt(3.0)/2); d2 = (position.y - relativeZero.y); break; case RS::IsoRight: a1 = RMath::deg2rad(30); a2 = RMath::deg2rad(90); d1 = (position.x - relativeZero.x) / (sqrt(3.0)/2); d2 = (position.y - relativeZero.y); break; } retX = relativeZero + RVector::createPolar(d1, a1); retY = relativeZero + RVector::createPolar(d2, a2); } else { retX = RVector(relativeZero.x, position.y); retY = RVector(position.x, relativeZero.y); } switch (mode) { case RRestrictOrthogonal::Vertical: ret = retX; break; case RRestrictOrthogonal::Horizonal: ret = retY; break; case RRestrictOrthogonal::Orthogonal: if (retX.getDistanceTo(position) > retY.getDistanceTo(position)) { ret = retY; } else { ret = retX; } break; } lastSnap = ret; return ret; }
QList<RLine> REllipse::getTangents(const RVector& point) const { QList<RLine> ret; if (getDistanceTo(point, false) < RS::PointTolerance) { // point is on ellipse: return ret; } // point is at center (prevents recursion when swapping ellipse minor / major): if (point.getDistanceTo(getCenter())<RS::PointTolerance) { return ret; } // swap ellipse minor / major if point is on minor axis // 20120928: and not also on major axis (prevent recursion): RLine minorAxis(getCenter(), getCenter() + getMinorPoint()); RLine majorAxis(getCenter(), getCenter() + getMajorPoint()); if (minorAxis.isOnShape(point, false) && !majorAxis.isOnShape(point, false)) { REllipse e2 =*this; e2.majorPoint = getMinorPoint(); e2.ratio = 1.0/ratio; return e2.getTangents(point); } double a = getMajorRadius(); // the length of the major axis / 2 double b = getMinorRadius(); // the length of the minor axis / 2 // rotate and move point: RVector point2 = point; point2.move(-getCenter()); point2.rotate(-getAngle()); double xp = point2.x; // coordinates of the given point double yp = point2.y; double xt1; // Tangent point 1 double yt1; double xt2; // Tangent point 2 double yt2; double a2 = a * a; double b2 = b * b; double d = a2 / b2 * yp / xp; double e = a2 / xp; double af = b2 * d * d + a2; double bf = -b2 * d * e * 2.0; double cf = b2 * e * e - a2 * b2; double t = sqrt(bf * bf - af * cf * 4.0); if (RMath::isNaN(t)) { return ret; } yt1 = (t - bf) / (af * 2.0); xt1 = e - d * yt1; yt2 = (-t - bf) / (af * 2.0); xt2 = e - d * yt2; RVector s1(xt1, yt1); s1.rotate(getAngle()); s1.move(getCenter()); RVector s2(xt2, yt2); s2.rotate(getAngle()); s2.move(getCenter()); if (s1.isValid()) { ret.append(RLine(point, s1)); } if (s2.isValid()) { ret.append(RLine(point, s2)); } return ret; }