/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// /************************************************************************/ /* This class is an implementation of the OdDbGripPointsPE class for */ /* OdDbEllipse entities. */ /************************************************************************/ #include "StdAfx.h" #include "RxObjectImpl.h" #include "DbEllipseGripPoints.h" #include "Ge/GeEllipArc3d.h" #include "Ge/GePointOnCurve3d.h" #include "OdGripPointsModule.h" #include #include // Returns 5 Points: center + 4 points on Circle OdResult OdDbEllipseGripPointsPE::getGripPoints(const OdDbEntity* ent, OdGePoint3dArray& gripPoints)const { OdDbEllipsePtr ellipse = OdDbEllipse::cast(ent); // For closed ellipse - set the same points as for circle (center + 4 poles) // For elliptical arc - same as circular arc (center, start, end, middle) int nPoints = ellipse->isClosed() ? 5 : 4; gripPoints.resize((unsigned int)nPoints); gripPoints[0] = ellipse->center(); if (ellipse->isClosed()) { ellipse->getPointAtParam(0, gripPoints[1]); // 1 - right (0) ellipse->getPointAtParam(OdaPI, gripPoints[2]); // 2 - left (pi) ellipse->getPointAtParam(OdaPI2, gripPoints[3]); // 3 - top (pi/2) ellipse->getPointAtParam(OdaPI + OdaPI2, gripPoints[4]); // 4 - bottom (pi + pi/2) } else { ellipse->getStartPoint(gripPoints[1]); // 1 start ellipse->getEndPoint(gripPoints[2]); // 2 end double start, end; ellipse->getStartParam(start); ellipse->getEndParam(end); ellipse->getPointAtParam((start + end) / 2, gripPoints[3]); // 3 middle } return eOk; } // Move the ellipse OdResult OdDbEllipseGripPointsPE::moveGripPointsAt(OdDbEntity* pEnt, const OdIntArray& indices, const OdGeVector3d& vOffset) { if (indices.size() == 0) // indices[0] - defines for what point we pull: return eOk; // center or other OdDbEllipsePtr ellipse = pEnt; bool bCenter = true; OdGeVector3d offset(vOffset); if (!checkIndices(indices, ellipse->isClosed())) return eInvalidInput; if (!projectOffset(ellipse->database(), ellipse->normal(), offset)) // Project offset on entity's plane in view direction { ellipse->setCenter(ellipse->center() + offset); // View direction is perpendicular to normal return eOk; // Move the ellipse } int nPoints = ellipse->isClosed() ? 5 : 4; for (unsigned i = 0; i < indices.size(); i++) { if (indices[i] % nPoints == 0) // center point - move the ellipse { if (bCenter) // move center only once { ellipse->setCenter(ellipse->center() + offset); bCenter = false; } } else // change radius { OdGePoint3dArray gripPoints; ellipse->getGripPoints(gripPoints); OdGePoint3d point0 = gripPoints[indices[i] % nPoints]; OdGePoint3d point = point0 + offset; OdGeVector3d normal = ellipse->normal(); OdGePoint3d center = ellipse->center(); double newDist = ellipse->center().distanceTo(point); newDist = (newDist < 1.e-10) ? 1.e-10 : newDist; OdGeVector3d major = ellipse->majorAxis(); OdGeVector3d minor = ellipse->minorAxis(); double radiusRatio = ellipse->radiusRatio(); if (ellipse->isClosed()) { if (indices[i] < 3) { // major axis radiusRatio = minor.length() / newDist; if (radiusRatio < 1.e-8) radiusRatio = 1.e-8; // Teigha throws "invalid data" exception if radius ratio < 1.e-12 else if (radiusRatio > 1) { // stop when reduced to circle radiusRatio = 1.0; newDist = minor.length(); } major.setLength(newDist); } else { radiusRatio = newDist / major.length(); if (radiusRatio < 1.e-8) radiusRatio = 1.e-8; else if (radiusRatio > 1) radiusRatio = 1.0; } try { ellipse->set(center, normal, major, radiusRatio); } catch (...) { } } else { // elliptical arc - just scale it double oldDist = ellipse->center().distanceTo(point0); if (oldDist < 1.e-10) oldDist = 1.e-10; double scale = newDist / oldDist; if (scale < 1.e-3) scale = 1.e-3; else if (scale > 1.e3) scale = 1.e3; try { ellipse->transformBy(OdGeMatrix3d::scaling(OdGeScale3d(scale), center)); } catch (...) { } } } } return eOk; } // Arc can be stretched moving start / end; ellipse can only be moved OdResult OdDbEllipseGripPointsPE::getStretchPoints(const OdDbEntity* pEnt, OdGePoint3dArray& stretchPoints) const { OdDbEllipsePtr ellipse = pEnt; if (ellipse->isClosed()) stretchPoints.append(ellipse->center()); // center else { OdGePoint3dArray gripPoints; ellipse->getGripPoints(gripPoints); // start & end stretchPoints.append(gripPoints[1]); stretchPoints.append(gripPoints[2]); } return eOk; } OdResult OdDbEllipseGripPointsPE::moveStretchPointsAt(OdDbEntity* pEnt, const OdIntArray& indices, const OdGeVector3d& vOffset) { OdDbEllipsePtr ellipse = pEnt; OdGeVector3d offset(vOffset); // Project offset on entity's plane in view direction if (!projectOffset(ellipse->database(), ellipse->normal(), offset)) { // View direction is perpendicular to normal // Do nothing return eOk; } if (ellipse->isClosed() || (indices.size() >= 2)) { ellipse->setCenter(ellipse->center() + offset); } else { return handleStartEndPoint(indices, ellipse, offset); } return eOk; } OdResult OdDbEllipseGripPointsPE::handleStartEndPoint(const OdIntArray& indices, OdDbEllipsePtr& ellipse, OdGeVector3d& offset) { try { OdGePoint3d pt; switch (indices[0]) { case 0: { ellipse->getStartPoint(pt); OdGeVector3d v0 = pt - ellipse->center(); pt += offset; OdGeVector3d v1 = pt - ellipse->center(); double angle = v0.angleTo(v1, ellipse->normal()); ellipse->setStartAngle(ellipse->startAngle() + angle); } break; case 1: { ellipse->getEndPoint(pt); OdGeVector3d v0 = pt - ellipse->center(); pt += offset; OdGeVector3d v1 = pt - ellipse->center(); double angle = v0.angleTo(v1, ellipse->normal()); ellipse->setEndAngle(ellipse->endAngle() + angle); } break; } } catch (const OdError& e) { return e.code(); } return eOk; } // Return snap Points into snapPoints, depending on snap Mode OdResult OdDbEllipseGripPointsPE::getOsnapPoints(const OdDbEntity* ent, OdDb::OsnapMode osnapMode, OdGsMarker /*gsSelectionMark*/, const OdGePoint3d& pickPoint_, // Point, which moves const OdGePoint3d& lastPoint_, // Point, from which draw line const OdGeMatrix3d& /*xWorldToEye*/, OdGePoint3dArray& snapPoints) const { OdGePoint3dArray gripPoints; OdResult res = getGripPoints(ent, gripPoints); if (res != eOk) return res; OdDbEllipsePtr ellipse = ent; OdGePoint3d pickPointProj = getPlanePoint(ellipse, pickPoint_), // recalculated pickPoint and lastPoint lastPointProj = getPlanePoint(ellipse, lastPoint_); // in plane of circle OdGePoint3d center = ellipse->center(); //double radius = 0;// circle->radius(); OdGePoint3d ptPick = pickPointProj - center.asVector(); //double rdPick = pickPointProj.distanceTo(center); OdGePoint3d ptLast = lastPointProj - center.asVector(); //double rdLast = lastPointProj.distanceTo(center); //bool bThickness = false;//OdZero(circle->thickness()); OdGeVector3d vThickness;//= circle->normal()*circle->thickness(); OdGeVector3d vMajorAxis = ellipse->majorAxis(); OdGeVector3d vMinorAxis = ellipse->minorAxis(); bool bCircle = fabs(ellipse->radiusRatio() - 1.0) <= OdGeContext::gTol.equalPoint(); switch (osnapMode) { case OdDb::kOsModeEnd: // Endpoint: arc only if (ellipse->isClosed() == false) { snapPoints.append(gripPoints[1]); snapPoints.append(gripPoints[2]); } break; case OdDb::kOsModeMid: // Middle: arc only if (ellipse->isClosed() == false) { snapPoints.append(gripPoints[3]); } break; case OdDb::kOsModeCen: snapPoints.append(gripPoints[0]); break; case OdDb::kOsModeQuad: // Quadrant - entire ellipse only if (ellipse->isClosed()) { for (unsigned i = 1; i < gripPoints.size(); i++) { if (i == 5) continue; snapPoints.append(gripPoints[i]); } } break; case OdDb::kOsModePerp: // Perpendicular - implemented only for degraded ellipse (circle) if (bCircle == true) { double circRadius = vMajorAxis.length(); OdGePoint3d circPtLast = lastPointProj - center.asVector(); double circRdLast = lastPointProj.distanceTo(center); OdGePoint3d Point = circPtLast * circRadius / circRdLast; snapPoints.append(center + Point.asVector()); snapPoints.append(center - Point.asVector()); } break; case OdDb::kOsModeTan: // Tangent { OdGeEllipArc3d ellipArc3d; ellipArc3d.set(center, vMajorAxis, vMinorAxis, vMajorAxis.length(), vMinorAxis.length(), ellipse->startAngle(), ellipse->endAngle()); OdGeVector3d tan = pickPointProj - lastPointProj; if (tan.length() > 1.e-6) { OdGeDoubleArray params; if ((ellipArc3d.isInside(pickPointProj) == false) && (ellipArc3d.isInside(lastPointProj) == false) && (ellipArc3d.inverseTangent(tan, params) == eOk) && (params.size() > 0)) { OdGePoint3d ptTan; for (unsigned i = 0; i < params.size(); i++) { ellipse->getPointAtParam(params[i], ptTan); snapPoints.append(ptTan); } } } } break; case OdDb::kOsModeNear: // Nearest: cursor ~ hourglasses { OdGeCurve3d* curve = nullptr; if (ellipse->getOdGeCurve(curve) == eOk) { std::unique_ptr geCurve(curve); OdGePointOnCurve3d closestPoint; geCurve->getClosestPointTo(ptPick, closestPoint, OdGeTol()); snapPoints.append(closestPoint.point()); } } break; case OdDb::kOsModeNode: // Node: cursor as cross in a square case OdDb::kOsModeIntersec: // Intersection: cursor as intersection in a square case OdDb::kOsModeIns: // Insertion: -/- case OdDb::kOsModePar: // Parallel: case OdDb::kOsModeApint: // Apparent intersection: break; // isn't necessary to do default: break; } return eOk; } bool OdDbEllipseGripPointsPE::checkIndices(const OdIntArray& indices, bool isClosed) { // Check if all indices in proper range int minAvailableValue = 0; int maxAvailableValue = isClosed ? 5 : 4; bool availableValues = std::all_of(indices.begin(), indices.end(), [&](int idx)->bool { return (idx >= minAvailableValue) && (idx <= maxAvailableValue); }); if (!availableValues) { ODA_ASSERT_ONCE(("One of index is out of range", availableValues)); return availableValues; } // Check for duplicates in indices array bool noDuplicates = true; for(int idx : indices) { std::ptrdiff_t numItems = std::count(indices.begin(), indices.end(), idx); if (numItems > 1) { noDuplicates = false; ODA_ASSERT_ONCE(("Indices array has duplicates", noDuplicates)); break; } } return noDuplicates; }