#include "OdaCommon.h" #include "DbUserIO.h" #include "DbCommandContext.h" #include "DbHostAppServices.h" #include "DbBlockTableRecord.h" #include "DbPolyline.h" #include "DbHatch.h" #include "Ge/GeCircArc2d.h" #include "Ge/GeLineSeg2d.h" #include "DbLine.h" namespace CreateHatchArea { namespace Util { inline OdGePoint3d to3d(const OdGePoint2d& pnt) { return { pnt.x, pnt.y, 0 }; } inline double calcBulge(const OdGeCircArc2d& arc) { double ang = arc.endAng() - arc.startAng(); if (ang < 0.) ang += Oda2PI; return tan(ang * 0.25); } inline void addPoint(const OdGePoint2d& pnt, const double bulge, OdDbPolyline* pPoly, OdGePoint2dArray& vertexPts, OdGeDoubleArray& vertexBulges) { if (bulge != 0.) { pPoly->setBulgeAt(pPoly->numVerts() - 1, bulge); vertexBulges.last() = bulge; } pPoly->addVertexAt(pPoly->numVerts(), pnt, 0.); vertexPts.append(pnt); vertexBulges.append(0.); } inline void removeLastPoint(OdDbPolyline* pPoly, OdGePoint2dArray& vertexPts, OdGeDoubleArray& vertexBulges) { pPoly->removeVertexAt(pPoly->numVerts() - 1); vertexBulges.removeLast(); vertexPts.removeLast(); } inline void resetHatch(OdDbHatch* pHatch) { if (pHatch->numLoops()) pHatch->removeLoopAt(0); } } class BasicPolyTracker : public OdStaticRxObject { protected: BasicPolyTracker() = delete; BasicPolyTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges) : m_pHatch(pHatch->clone()), m_pPoly(pPoly->clone()), m_vertexPts(vertexPts), m_vertexBulges(vertexBulges), m_bulge(0.), m_intersect(false) { reloadHatch(); m_pPoly->addVertexAt(m_pPoly->numVerts(), vertexPts.last(), 0.); m_vertexPts.append(vertexPts.last()); m_vertexBulges.append(0.); } public: double getBulge() const { return m_bulge; } bool isHasIntersect() { return m_intersect; } virtual int addDrawables(OdGsView* pView) { pView->add(m_pHatch, 0); pView->add(m_pPoly, 0); return 1; } virtual void removeDrawables(OdGsView* pView) { pView->erase(m_pHatch); pView->erase(m_pPoly); } protected: void reloadHatch() { Util::resetHatch(m_pHatch); m_pHatch->appendLoop(OdDbHatch::kPolyline, m_vertexPts, m_vertexBulges); m_pHatch->evaluateHatch(); } bool checkIntersect() const { OdGePoint3dArray arr; m_pPoly->intersectWith(m_pPoly, OdDb::kExtendThis, arr); if (arr.size() > 1) // restore if intersect prev point which work return true; else return false; } void setLastPnt(const unsigned prevIndex, const OdGePoint2d pnt, const double bulge) { m_vertexPts.last() = pnt; m_vertexBulges[prevIndex] = bulge; m_pPoly->setPointAt(m_pPoly->numVerts() - 1, pnt); m_pPoly->setBulgeAt(prevIndex, bulge); } void setLastPnt(const OdGePoint2d pnt) { m_vertexPts.last() = pnt; m_pPoly->setPointAt(m_pPoly->numVerts() - 1, pnt); } protected: OdDbHatchPtr m_pHatch; OdDbPolylinePtr m_pPoly; OdGePoint2dArray m_vertexPts; OdGeDoubleArray m_vertexBulges; double m_bulge; bool m_intersect; }; class PolyDrawTracker : public OdStaticRxObject, public OdStaticRxObject { public: PolyDrawTracker() = delete; PolyDrawTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, const OdGePoint2dArray& vertexPts, const OdGeDoubleArray& vertexBulges) : m_pHatch(pHatch->clone()), m_pPoly(pPoly) { m_pHatch->appendLoop(OdDbHatch::kPolyline, vertexPts, vertexBulges); m_pHatch->evaluateHatch(); } virtual void setValue(double) {} virtual void setValue(const OdGePoint3d&) {} virtual int addDrawables(OdGsView* pView) { pView->add(m_pHatch, 0); pView->add(m_pPoly, 0); return 1; } virtual void removeDrawables(OdGsView* pView) { pView->erase(m_pHatch); pView->erase(m_pPoly); } private: OdDbHatchPtr m_pHatch; OdDbPolylinePtr m_pPoly; }; class BasicFigureTracker : public OdStaticRxObject { protected: BasicFigureTracker() = delete; BasicFigureTracker(OdDbHatchPtr pHatch) : m_pHatch(pHatch) { } public: virtual int addDrawables(OdGsView* pView) { pView->add(m_pHatch, 0); return 1; } virtual void removeDrawables(OdGsView* pView) { pView->erase(m_pHatch); } protected: OdDbHatchPtr m_pHatch; }; namespace PolyAreaFuncs { bool AreaSecondPoint(OdDbUserIO* pUserIO, OdDbHatchPtr pHatch, OdDbPolyline* pPoly, OdGePoint2dArray& vertexPts, OdGeDoubleArray& vertexBulges) { class PolySecondPtTracker : public BasicPolyTracker { public: PolySecondPtTracker() = delete; PolySecondPtTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges, const OdGePoint2d& secondPnt) : BasicPolyTracker(pHatch, pPoly, vertexPts, vertexBulges), m_secondPnt(secondPnt) { } virtual void setValue(const OdGePoint3d& value) { const OdGePoint2d curPnt = value.convert2d(); const OdGePoint2d lastPnt = m_vertexPts.last(); const unsigned prevIndex = m_pPoly->numVerts() - 2; double curBulge = 0.; OdGeCircArc2d arc(curPnt, m_secondPnt, m_vertexPts[prevIndex]); curBulge = Util::calcBulge(arc); if (!arc.isClockWise()) curBulge = -curBulge; setLastPnt(prevIndex, curPnt, curBulge); if (m_pPoly->numVerts() <= 2) return; m_intersect = checkIntersect(); if (m_intersect) { setLastPnt(prevIndex, lastPnt, m_bulge); return; } reloadHatch(); m_bulge = curBulge; } private: OdGePoint2d m_secondPnt; }; OdString keywords; PolyDrawTracker draw(pHatch, pPoly, vertexPts, vertexBulges); try { OdGePoint3d pnt = pUserIO->getPoint(OD_T("Specify second point on arc:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &draw); PolySecondPtTracker tracker(pHatch, pPoly, vertexPts, vertexBulges, pnt.convert2d()); pnt = pUserIO->getPoint(OD_T("Specify the next point:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, keywords, &tracker); if (tracker.isHasIntersect()) return false; else Util::addPoint(pnt.convert2d(), tracker.getBulge(), pPoly, vertexPts, vertexBulges); return true; } catch (OdEdCancel&) { return false; } } bool AreaRadius(OdDbUserIO* pUserIO, OdDbHatchPtr pHatch, OdDbPolyline* pPoly, OdGePoint2dArray& vertexPts, OdGeDoubleArray& vertexBulges) { class PolyRadiusTracker : public BasicPolyTracker { public: PolyRadiusTracker() = delete; PolyRadiusTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges, const double radius) : BasicPolyTracker(pHatch, pPoly, vertexPts, vertexBulges), m_radius(radius) { } virtual void setValue(const OdGePoint3d& value) { const OdGePoint2d curPnt = value.convert2d(); const OdGePoint2d lastPnt = m_vertexPts.last(); const unsigned prevIndex = m_pPoly->numVerts() - 2; const OdGePoint2d prevPnt = m_vertexPts[prevIndex]; double curBulge = 0.; const OdGeVector2d v = curPnt - prevPnt; const double dist = curPnt.distanceTo(prevPnt); if (m_radius * 2. < dist) return; const OdGePoint2d mid((curPnt.x + prevPnt.x) / 2, (curPnt.y + prevPnt.y) / 2); const double h = sqrt(pow(m_radius, 2.) - pow(dist / 2, 2.)); const OdGePoint2d c1( mid.x + h * (-(curPnt.y - prevPnt.y) / dist), mid.y + h * ((curPnt.x - prevPnt.x) / dist)); const double startAng = atan2(prevPnt.y - c1.y, prevPnt.x - c1.x); const double endAng = atan2(curPnt.y - c1.y, curPnt.x - c1.x); OdGeCircArc2d arc; arc.set(c1, m_radius, startAng, endAng); curBulge = Util::calcBulge(arc); setLastPnt(prevIndex, curPnt, curBulge); if (m_pPoly->numVerts() <= 2) return; m_intersect = checkIntersect(); if (m_intersect) { setLastPnt(prevIndex, lastPnt, m_bulge); return; } reloadHatch(); m_bulge = curBulge; } OdGePoint2d getLastPoint() const { return m_vertexPts.last(); } private: const double m_radius; }; OdString keywords; PolyDrawTracker draw(pHatch, pPoly, vertexPts, vertexBulges); try { OdGePoint3d p1 = pUserIO->getPoint("Specify start point:", OdEd::kInpThrowEmpty, 0, OdString::kEmpty, &draw); OdGePoint3d p2 = pUserIO->getPoint("Specify end point:", OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, &p1, OdString::kEmpty, &draw); const double radius = p1.distanceTo(p2); PolyRadiusTracker tracker(pHatch, pPoly, vertexPts, vertexBulges, radius); pUserIO->setLASTPOINT(Util::to3d(tracker.getLastPoint())); pUserIO->getPoint(OD_T("Specify the next point:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, keywords, &tracker); if (tracker.isHasIntersect()) return false; else Util::addPoint(tracker.getLastPoint(), tracker.getBulge(), pPoly, vertexPts, vertexBulges); return true; } catch (OdEdCancel&) { return false; } } bool AreaCenter(OdDbUserIO* pUserIO, OdDbHatchPtr pHatch, OdDbPolyline* pPoly, OdGePoint2dArray& vertexPts, OdGeDoubleArray& vertexBulges) { class PolyCenterTracker : public BasicPolyTracker { public: PolyCenterTracker() = delete; PolyCenterTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges, const OdGePoint2d& centerPnt) : BasicPolyTracker(pHatch, pPoly, vertexPts, vertexBulges), m_centerPnt(centerPnt) { } virtual void setValue(const OdGePoint3d& value) { const OdGePoint2d curPnt = value.convert2d(); const OdGePoint2d lastPnt = m_vertexPts.last(); const unsigned prevIndex = m_pPoly->numVerts() - 2; const OdGePoint2d prevPnt = m_vertexPts[prevIndex]; const double radius = m_centerPnt.distanceTo(prevPnt); const double startAng = atan2(prevPnt.y - m_centerPnt.y, prevPnt.x - m_centerPnt.x); const double endAng = atan2(curPnt.y - m_centerPnt.y, curPnt.x - m_centerPnt.x); OdGeCircArc2d arc(m_centerPnt, radius, startAng, endAng); double curBulge = Util::calcBulge(arc); curBulge *= arc.isClockWise() ? -1. : 1.; setLastPnt(prevIndex, arc.endPoint(), curBulge); if (m_pPoly->numVerts() <= 1) return; m_intersect = checkIntersect(); if (m_intersect) { setLastPnt(prevIndex, lastPnt, m_bulge); return; } reloadHatch(); m_bulge = curBulge; } OdGePoint2d getLastPoint() const { return m_vertexPts.last(); } private: OdGePoint2d m_centerPnt; }; OdString keywords; PolyDrawTracker draw(pHatch, pPoly, vertexPts, vertexBulges); try { OdGePoint3d pnt = pUserIO->getPoint(OD_T("Specify center point on arc:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &draw); PolyCenterTracker tracker(pHatch, pPoly, vertexPts, vertexBulges, pnt.convert2d()); pUserIO->getPoint(OD_T("Specify the next point :"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &tracker); if (tracker.isHasIntersect()) return false; else Util::addPoint(tracker.getLastPoint(), tracker.getBulge(), pPoly, vertexPts, vertexBulges); return true; } catch (OdEdCancel&) { return false; } } bool AreaLength(OdDbUserIO* pUserIO, OdDbHatchPtr pHatch, OdDbPolyline* pPoly, OdGePoint2dArray& vertexPts, OdGeDoubleArray& vertexBulges, const OdGeVector2d& dir) { class PolyLengthenTracker : public BasicPolyTracker { public: PolyLengthenTracker() = delete; PolyLengthenTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges, OdGeVector2d dir) : BasicPolyTracker(pHatch, pPoly, vertexPts, vertexBulges), m_dir(dir) { } virtual void setValue(const OdGePoint3d& value) { const OdGePoint2d curPnt = value.convert2d(); const OdGePoint2d lastPnt = m_vertexPts.last(); const unsigned prevIndex = m_pPoly->numVerts() - 2; const OdGePoint2d prevPnt = m_vertexPts[prevIndex]; setLastPnt(calcPntProj(curPnt, prevPnt)); if (m_pPoly->numVerts() <= 2) return; m_intersect = checkIntersect(); if (m_intersect) { setLastPnt(prevIndex, lastPnt, m_bulge); return; } reloadHatch(); } OdGePoint2d getLastPoint() const { return m_vertexPts.last(); } private: OdGePoint2d calcPntProj(const OdGePoint2d& pnt, const OdGePoint2d& sidePnt) const { const double len = m_dir.lengthSqrd(); const OdGeVector2d vec = pnt - sidePnt; const double scalar = abs(vec.dotProduct(m_dir)); const OdGeVector2d vecProj = (scalar / len) * m_dir; return sidePnt + vecProj; } private: OdGeVector2d m_dir; }; try { PolyLengthenTracker tracker(pHatch, pPoly, vertexPts, vertexBulges, dir); pUserIO->getPoint(OD_T("Specify length of line:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &tracker); if (tracker.isHasIntersect()) return false; else Util::addPoint(tracker.getLastPoint(), 0., pPoly, vertexPts, vertexBulges); return true; } catch (OdEdCancel&) { return false; } } bool AreaAngle(OdDbUserIO* pUserIO, OdDbHatchPtr pHatch, OdDbPolyline* pPoly, OdGePoint2dArray& vertexPts, OdGeDoubleArray& vertexBulges) { class PolyAngleTracker : public BasicPolyTracker { public: PolyAngleTracker() = delete; PolyAngleTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges, const double angle) : BasicPolyTracker(pHatch, pPoly, vertexPts, vertexBulges) { m_bulge = tan(angle * 0.25); } virtual void setValue(const OdGePoint3d& value) { const OdGePoint2d curPnt = value.convert2d(); const OdGePoint2d lastPnt = m_vertexPts.last(); const unsigned prevIndex = m_pPoly->numVerts() - 2; const OdGePoint2d prevPnt = m_vertexPts[prevIndex]; setLastPnt(prevIndex, curPnt, m_bulge); if (m_pPoly->numVerts() <= 2) return; m_intersect = checkIntersect(); if (m_intersect) { setLastPnt(prevIndex, lastPnt, m_bulge); return; } reloadHatch(); } OdGePoint2d getLastPoint() const { return m_vertexPts.last(); } }; PolyDrawTracker draw(pHatch, pPoly, vertexPts, vertexBulges); try { double custom_angle = pUserIO->getAngle("Specify included angle: ", OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &draw); PolyAngleTracker tracker(pHatch, pPoly, vertexPts, vertexBulges, custom_angle); pUserIO->setLASTPOINT(Util::to3d(tracker.getLastPoint())); pUserIO->getPoint("Specify end point of arc:", OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &tracker); if (tracker.isHasIntersect()) return false; else Util::addPoint(tracker.getLastPoint(), tracker.getBulge(), pPoly, vertexPts, vertexBulges); return true; } catch (OdEdCancel&) { return false; } } } void PolylineArea(OdDbHatchPtr& pHatch, OdDbUserIOPtr& pUserIO, const OdGePoint2d& startPnt) { enum eAreaMode { Arc = 0, Length, Undo, Exit, Angle, Center, Close, Direction, Line, Radius, SecondPt }; class PolylineAreaTracker : public BasicPolyTracker { public: PolylineAreaTracker() = delete; PolylineAreaTracker(OdDbHatchPtr pHatch, OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges) : BasicPolyTracker(pHatch, pPoly, vertexPts, vertexBulges), m_dir(1., 0.), m_useBulge(false) { calculateDirection(); } virtual void setValue(const OdGePoint3d& value) { const OdGePoint2d curPnt = value.convert2d(); const OdGePoint2d lastPnt = m_vertexPts.last(); const unsigned prevIndex = m_pPoly->numVerts() - 2; const OdGePoint2d prevPnt = m_vertexPts[prevIndex]; double curBulge = 0.; if (m_useBulge) { const OdGeVector2d v = curPnt - prevPnt; curBulge = tan(v.angleTo(m_dir) / 2.); curBulge *= m_dir.crossProduct(v) >= 0 ? 1. : -1.; } setLastPnt(prevIndex, curPnt, curBulge); if (m_pPoly->numVerts() <= 1) return; m_intersect = checkIntersect(); if (m_intersect) { setLastPnt(prevIndex, lastPnt, m_bulge); return; } reloadHatch(); m_bulge = curBulge; } void setUseBulge(bool useBulge) { m_useBulge = useBulge; } void setDirectionPoint(const OdGePoint2d& pnt) { m_dir = pnt - m_vertexPts[m_vertexPts.size() - 2]; m_dir.normalize(); } OdGeVector2d getDirection() const { return m_dir; } void updatePolyData(OdDbPolylinePtr pPoly, OdGePoint2dArray vertexPts, OdGeDoubleArray vertexBulges) { m_pPoly = pPoly->clone(); m_vertexPts = vertexPts; m_vertexBulges = vertexBulges; m_pPoly->addVertexAt(m_pPoly->numVerts(), vertexPts.last(), 0.); m_vertexPts.append(vertexPts.last()); m_vertexBulges.append(0.); reloadHatch(); calculateDirection(); } private: void calculateDirection() { const unsigned indexSeg = indexLastSeg(); switch (m_pPoly->segType(indexSeg)) { case OdDbPolyline::SegType::kArc: { OdGeCircArc2d circ; m_pPoly->getArcSegAt(indexSeg, circ); const OdGeVector2d vec = circ.endPoint() - circ.center(); const double endAng = atan2(vec.y, vec.x); if (circ.isClockWise()) { m_dir.x = sin(endAng); m_dir.y = -cos(endAng); } else { m_dir.x = -sin(endAng); m_dir.y = cos(endAng); } break; } case OdDbPolyline::SegType::kLine: { OdGeLineSeg2d line; m_pPoly->getLineSegAt(indexSeg, line); m_dir = line.direction(); break; } default: m_dir = OdGeVector2d(1., 0.); break; } m_dir.normalize(); } unsigned indexLastSeg() const { return m_pPoly->numVerts() - 3; } private: OdGeVector2d m_dir; bool m_useBulge; }; OdDbPolylinePtr pPoly = OdDbPolyline::createObject(); pPoly->setColorIndex(2); pPoly->setClosed(true); OdGePoint2dArray vertexPts; OdGeDoubleArray vertexBulges; const OdString keywords(OD_T("Arc Length Undo Exit Angle Center Close Direction Line Radius SecondPt")); const OdString keywordLine(OD_T("ARc/LEngth/Undo/Exit")); const OdString keywordArc(OD_T("ANgle/CEnter/CLose/Direction/LIne/Radius/Second pt/Undo")); OdString currentKeyword = keywordLine; OdGePoint3d pnt; Util::addPoint(startPnt, 0., pPoly, vertexPts, vertexBulges);; bool bDone = true; PolylineAreaTracker tracker(pHatch, pPoly, vertexPts, vertexBulges); int nKeyword = 0; bool bKeyword = false; while (bDone) { nKeyword = 0; bKeyword = false; try { pUserIO->setLASTPOINT(Util::to3d(vertexPts.last())); pnt = pUserIO->getPoint(OD_T("Specify the next point or [" + currentKeyword + "]:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, keywords, &tracker); if (tracker.isHasIntersect()) continue; else { Util::addPoint(pnt.convert2d(), tracker.getBulge(), pPoly, vertexPts, vertexBulges); tracker.updatePolyData(pPoly, vertexPts, vertexBulges); } } catch (const OdEdCancel&) { break; } catch (const OdEdKeyword& kwd) { bKeyword = true; nKeyword = kwd.keywordIndex(); } if (bKeyword) { switch (nKeyword) { case eAreaMode::Arc: { currentKeyword = keywordArc; tracker.setUseBulge(true); break; } case eAreaMode::Length: { if (PolyAreaFuncs::AreaLength(pUserIO, pHatch, pPoly, vertexPts, vertexBulges, tracker.getDirection())) tracker.updatePolyData(pPoly, vertexPts, vertexBulges); break; } case eAreaMode::Undo: { switch (pPoly->segType(pPoly->numVerts() - 2)) { case OdDbPolyline::SegType::kArc: tracker.setUseBulge(true); currentKeyword = keywordArc; Util::removeLastPoint(pPoly, vertexPts, vertexBulges); tracker.updatePolyData(pPoly, vertexPts, vertexBulges); break; case OdDbPolyline::SegType::kLine: tracker.setUseBulge(false); currentKeyword = keywordLine; Util::removeLastPoint(pPoly, vertexPts, vertexBulges); tracker.updatePolyData(pPoly, vertexPts, vertexBulges); break; } break; } case eAreaMode::Exit: { bDone = false; break; } case eAreaMode::Angle: { if (PolyAreaFuncs::AreaAngle(pUserIO, pHatch, pPoly, vertexPts, vertexBulges)) tracker.updatePolyData(pPoly, vertexPts, vertexBulges); break; } case eAreaMode::Center: { if (PolyAreaFuncs::AreaCenter(pUserIO, pHatch, pPoly, vertexPts, vertexBulges)) tracker.updatePolyData(pPoly, vertexPts, vertexBulges); break; } case eAreaMode::Close: { bDone = false; break; } case eAreaMode::Direction: { PolyDrawTracker trackern(pHatch, pPoly, vertexPts, vertexBulges); OdGePoint3d dirPnt = pUserIO->getPoint(OD_T("Specify the tangent direction for the start point of arc:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &trackern); tracker.setDirectionPoint(dirPnt.convert2d()); break; } case eAreaMode::Line: { currentKeyword = keywordLine; tracker.setUseBulge(false); break; } case eAreaMode::Radius: { if (PolyAreaFuncs::AreaRadius(pUserIO, pHatch, pPoly, vertexPts, vertexBulges)) tracker.updatePolyData(pPoly, vertexPts, vertexBulges); break; } case eAreaMode::SecondPt: { if (PolyAreaFuncs::AreaSecondPoint(pUserIO, pHatch, pPoly, vertexPts, vertexBulges)) tracker.updatePolyData(pPoly, vertexPts, vertexBulges); break; } } } } pHatch->appendLoop(OdDbHatch::kPolyline, vertexPts, vertexBulges); pHatch->evaluateHatch(); } namespace CircleFuncs { bool Circle3P(OdDbUserIO* pUserIO, OdDbHatchPtr pHatch) { class Circle3PTracker : public BasicFigureTracker { public: Circle3PTracker() = delete; Circle3PTracker(OdDbHatchPtr& pHatch, const OdGePoint3d& start, const OdGePoint3d& end) : BasicFigureTracker(pHatch), m_firstPnt(start.convert2d()), m_endPnt(end.convert2d()) { } virtual void setValue(const OdGePoint3d& value) { EdgeArray edgePtrs; OdGeCircArc2d* circ = new OdGeCircArc2d(m_firstPnt, value.convert2d(), m_endPnt); circ->set(circ->center(), circ->radius()); // arc to circle edgePtrs.append(circ); Util::resetHatch(m_pHatch); m_pHatch->appendLoop(OdDbHatch::kExternal, edgePtrs); m_pHatch->evaluateHatch(); } private: const OdGePoint2d m_firstPnt; const OdGePoint2d m_endPnt; }; try { OdGePoint3d first = pUserIO->getPoint(OD_T("Specify first point on circle:")); OdGePoint3d second = pUserIO->getPoint(OD_T("Specify second point on circle:")); Circle3PTracker tracker(pHatch, first, second); OdGePoint3d third = pUserIO->getPoint(OD_T("Specify third point on circle:"), OdEd::kGptRubberBand, &second, OdString::kEmpty, &tracker); } catch (const OdEdCancel&) { return false; } return true; } bool Circle2P(OdDbUserIO* pUserIO, OdDbHatchPtr pHatch) { class Circle2PTracker : public BasicFigureTracker { public: Circle2PTracker() = delete; Circle2PTracker(OdDbHatchPtr& pHatch, const OdGePoint3d& start) : BasicFigureTracker(pHatch), m_firstPnt(start.convert2d()) { } virtual void setValue(const OdGePoint3d& value) { EdgeArray edgePtrs; OdGePoint2d mid((m_firstPnt.x + value.x) / 2, (m_firstPnt.y + value.y) / 2); OdGeCircArc2d* circ = new OdGeCircArc2d(mid, m_firstPnt.distanceTo(value.convert2d()) / 2.); edgePtrs.append(circ); Util::resetHatch(m_pHatch); m_pHatch->appendLoop(OdDbHatch::kExternal, edgePtrs); m_pHatch->evaluateHatch(); } private: const OdGePoint2d m_firstPnt; const OdGePoint2d m_endPnt; }; try { OdGePoint3d first = pUserIO->getPoint(OD_T("Specify first end point of circle's diameter:")); Circle2PTracker tracker(pHatch, first); OdGePoint3d second = pUserIO->getPoint(OD_T("Specify fsecond end point of circle's diameter:"), OdEd::kGptRubberBand, &first, OdString::kEmpty, &tracker); } catch (const OdEdCancel&) { return false; } return true; } } void CircleArea(OdDbHatchPtr& pHatch, OdDbUserIOPtr& pUserIO) { class CircleTracker : public BasicFigureTracker { public: CircleTracker() = delete; CircleTracker(OdDbHatchPtr& pHatch, const OdGePoint3d& pnt) : BasicFigureTracker(pHatch), m_firstPnt(pnt.convert2d()) { } virtual void setValue(const OdGePoint3d& value) { EdgeArray edgePtrs; OdGeCircArc2d* circ = new OdGeCircArc2d(m_firstPnt, m_firstPnt.distanceTo(value.convert2d())); edgePtrs.append(circ); Util::resetHatch(m_pHatch); m_pHatch->appendLoop(OdDbHatch::kExternal, edgePtrs); m_pHatch->evaluateHatch(); } private: const OdGePoint2d m_firstPnt; }; OdGePoint3d pnt; int nKeyword = 0; bool bKeyword = false; try { pnt = pUserIO->getPoint(OD_T("Specify center point for circle or [3P/2P]:"), OdEd::kInpThrowEmpty, 0, OD_T("3P 2P")); } catch (const OdEdCancel&) { return; } catch (const OdEdKeyword& kwd) { bKeyword = true; nKeyword = kwd.keywordIndex(); } if (bKeyword) { switch (nKeyword) { case 0: // 3P CircleFuncs::Circle3P(pUserIO, pHatch); break; case 1: // 2P CircleFuncs::Circle2P(pUserIO, pHatch); break; } } else { try { CircleTracker tracker(pHatch, pnt); pUserIO->getPoint(OD_T("Specify other corner point:"), OdEd::kInpThrowEmpty | OdEd::kGptRubberBand, 0, OdString::kEmpty, &tracker); } catch (const OdEdCancel&) { return; } } } void RectangleArea(OdDbHatchPtr& pHatch, OdDbUserIOPtr& pUserIO) { class RectangleTracker : public BasicFigureTracker { public: RectangleTracker() = delete; RectangleTracker(OdDbHatchPtr pHatch, const OdGePoint3d& pnt) : BasicFigureTracker(pHatch), m_firstPnt(pnt.convert2d()) { } virtual void setValue(const OdGePoint3d& value) { const OdGePoint2d rt = m_firstPnt; const OdGePoint2d lb = value.convert2d(); const OdGePoint2d lt = { lb.x, m_firstPnt.y }; const OdGePoint2d rb = { m_firstPnt.x, value.y }; if (rt == lb || rt == lt || rt == rb || lb == lt || lb == rb || lt == rb) return; EdgeArray edgePtrs; OdGeLineSeg2d* top = new OdGeLineSeg2d(lt, rt); OdGeLineSeg2d* left = new OdGeLineSeg2d(lt, lb); OdGeLineSeg2d* bottom = new OdGeLineSeg2d(lb, rb); OdGeLineSeg2d* right = new OdGeLineSeg2d(rt, rb); edgePtrs.append(top); edgePtrs.append(left); edgePtrs.append(bottom); edgePtrs.append(right); Util::resetHatch(m_pHatch); m_pHatch->appendLoop(OdDbHatch::kExternal, edgePtrs); m_pHatch->evaluateHatch(); } private: const OdGePoint2d m_firstPnt; }; OdGePoint3d pnt; try { pnt = pUserIO->getPoint(OD_T("Specify first corner point:"), OdEd::kInpThrowEmpty); } catch (const OdEdCancel&) { return; } try { RectangleTracker tracker(pHatch, pnt); pUserIO->getPoint(OD_T("Specify other corner point:"), 0, 0, OdString::kEmpty, &tracker); } catch (const OdEdCancel&) { return; } } } namespace hatchByPath { bool getParallelLines(const double width, const OdGeLineSeg2d& line, OdGeLineSeg2d& lower, OdGeLineSeg2d& upper) { if (OdZero(line.length() || OdLess(0., line.length()))) return false; const OdGePoint2d p1 = line.startPoint(); const OdGePoint2d p2 = line.endPoint(); const OdGeVector2d dir = line.direction(); OdGeVector2d perp = dir.perpVector(); perp.normalize(); const double halfWidth = 0.5 * width; perp *= halfWidth; OdGePoint2d lowerP1; OdGePoint2d upperP1; const OdGePoint2d lowerP2 = p2 - perp; const OdGePoint2d upperP2 = p2 + perp; lowerP1 = p1 - perp; upperP1 = p1 + perp; lower.set(lowerP1, lowerP2); upper.set(upperP1, upperP2); return true; } void connectLines(OdArray& lines) { OdGeLineSeg2d* arr = lines.asArrayPtr(); OdDbLinePtr line1 = OdDbLine::createObject(); OdDbLinePtr line2 = OdDbLine::createObject(); OdGePoint3dArray points; for (OdUInt32 i = 1; i < lines.size(); ++i) { OdGePoint2d cross; // convert to OdDbLine to use OdDb::kExtendBoth flag const OdGePoint2d prevStart = arr[i - 1].startPoint(); const OdGePoint2d prevEnd = arr[i - 1].endPoint(); const OdGePoint2d curStart = arr[i].startPoint(); const OdGePoint2d curEnd = arr[i].endPoint(); line1->setEndPoint(OdGePoint3d(prevEnd.x, prevEnd.y, 0.)); line1->setStartPoint(OdGePoint3d(prevStart.x, prevStart.y, 0.)); line2->setEndPoint(OdGePoint3d(curEnd.x, curEnd.y, 0.)); line2->setStartPoint(OdGePoint3d(curStart.x, curStart.y, 0.)); if (line1->intersectWith(line2, OdDb::kExtendBoth, points) == eOk && points.size() > 0) { cross = points[0].convert2d(); arr[i - 1].set(prevStart, cross); arr[i].set(cross, curEnd); points.clear(); } } } void getParallelForEachSeg(const double width, const OdDbPolyline* polyline, OdArray& lowerAr, OdArray& upperAr) { const OdUInt32 verts = polyline->numVerts(); OdGeLineSeg2d seg; for (OdUInt32 i = 0; i < verts - 1; ++i) { polyline->getLineSegAt(i, seg); OdGeLineSeg2d lower, upper; if (getParallelLines(width, seg, lower, upper)) { lowerAr.append(lower); upperAr.append(upper); } } connectLines(lowerAr); connectLines(upperAr); } void removeVertsFromPolyline(OdDbPolylinePtr& polyline) { OdGePoint2d first; polyline->getPointAt(0, first); polyline->erase(); polyline = OdDbPolyline::createObject(); polyline->addVertexAt(0, first); } void createHatch(OdDbHatch* pHatch, const OdDbPolyline* polyline, const OdString& patName = L"SOLID") { if (pHatch->numLoops() > 0) pHatch->removeLoopAt(0); OdGePoint2dArray vertices; OdGeDoubleArray bulges; const OdUInt32 size = polyline->numVerts(); if (size < 2) { return; } for (OdUInt32 i = 0; i < size; ++i) { OdGePoint2d point; polyline->getPointAt(i, point); vertices.append(point); const double bulge = polyline->getBulgeAt(i); bulges.append(bulge); } pHatch->appendLoop(OdDbHatch::kPolyline, vertices, bulges); try { pHatch->setPattern(OdDbHatch::HatchPatternType::kPreDefined, patName); } // if the pattern is invalid catch (const OdError&) { pHatch->setPattern(OdDbHatch::HatchPatternType::kPreDefined, L"SOLID"); } } std::pair setArcs(const OdGeCircArc2d& arc, const double width) { OdGeCircArc2d lower(arc), upper(arc); lower.setRadius(arc.radius() - width * 0.5); upper.setRadius(arc.radius() + width * 0.5); return { lower, upper }; } #define DRAW_HATCH struct MyTracker { double m_width = 0.; OdDbPolylinePtr m_pPline, m_shape; OdDbHatchPtr m_hatch; OdString m_patName; }; struct PolylineTracker : public OdEdPointTracker, MyTracker { OdGePoint2d m_end; bool m_isArc = false; void createArc(OdUInt32 arcSegIndex, OdUInt32 shapePointIndex, bool isLower, double bulge) { if (!OdZero(bulge)) { OdGeCircArc2d arc; m_pPline->getArcSegAt(arcSegIndex, arc); std::pair arcs = setArcs(arc, m_width); auto setShape = [&](const OdGePoint2d& p1, const OdGePoint2d& p2) { m_shape->setPointAt(shapePointIndex, p1); if (shapePointIndex + 1 < m_shape->numVerts()) m_shape->setPointAt(shapePointIndex + 1, p2); }; if (isLower) { arc = !arc.isClockWise() ? arcs.second : arcs.first; setShape(arc.startPoint(), arc.endPoint()); } else { arc = !arc.isClockWise() ? arcs.first : arcs.second; setShape(arc.endPoint(), arc.startPoint()); } if (!OdZero(m_pPline->getBulgeAt(0))) { // connect if (arc == arcs.first) m_shape->setPointAt(m_shape->numVerts() - 1, arcs.second.startPoint()); else m_shape->setPointAt(m_shape->numVerts() - 1, arcs.first.startPoint()); } } } void rebuildShape() { OdArray lower, upper; getParallelForEachSeg(m_width, m_pPline, lower, upper); removeVertsFromPolyline(m_shape); if (!lower.isEmpty() && !upper.isEmpty()) { for (const OdGeLineSeg2d& seg : lower) { m_shape->addVertexAt(m_shape->numVerts(), seg.startPoint()); } m_shape->addVertexAt(m_shape->numVerts(), lower[lower.size() - 1].endPoint()); for (auto it = upper.rbegin(); it != upper.rend(); ++it) { m_shape->addVertexAt(m_shape->numVerts(), it->endPoint()); } m_shape->addVertexAt(m_shape->numVerts(), upper[0].startPoint()); m_shape->addVertexAt(m_shape->numVerts(), lower[0].startPoint()); // get bulges const OdUInt32 linesN = m_pPline->numVerts() - 1; OdUInt32 i = 0; for (; i < linesN; ++i) { const double bulge = m_pPline->getBulgeAt(i); if (i + 1 < m_shape->numVerts()) { m_shape->setBulgeAt(i + 1, bulge); createArc(i, i + 1, true, bulge); } } i++; // skip lower last end for (int j = linesN; j >= 0; --j, ++i) { const double bulge = m_pPline->getBulgeAt(j); m_shape->setBulgeAt(i, -bulge); createArc(j, i, false, bulge); } } } virtual void setValue(const OdGePoint3d& value) { if (m_pPline->numVerts() > 1) { m_pPline->removeVertexAt(m_pPline->numVerts() - 1); } m_end = value.convert2d(); m_pPline->addVertexAt(m_pPline->numVerts(), m_end); if (m_pPline->numVerts() > 1) { rebuildShape(); if (m_isArc) { OdGeLineSeg2d seg; OdGeCircArc2d segArc; OdGeVector2d vec(OdGeVector2d::kXAxis); if (m_pPline->numVerts() > 2) { const bool isZero = OdZero(m_pPline->getBulgeAt(m_pPline->numVerts() - 3)); if (isZero) { m_pPline->getLineSegAt(m_pPline->numVerts() - 3, seg); vec = seg.endPoint() - seg.startPoint(); } else { m_pPline->getArcSegAt(m_pPline->numVerts() - 3, segArc); int n = 60; const double arcAngle = abs(segArc.endAng() - segArc.startAng()); n *= static_cast(arcAngle); if (n == 0) n += 2; OdGePoint2dArray ar; segArc.appendSamplePoints(n, ar); vec = ar[ar.size() - 1] - ar[ar.size() - 2]; } } m_pPline->getLineSegAt(m_pPline->numVerts() - 2, seg); double angle = (seg.endPoint() - seg.startPoint()).angleTo(vec); if (vec.perpVector().dotProduct((seg.endPoint() - seg.startPoint())) < 0) angle = -angle; const double tol = OdGeContext::gTol.equalPoint(); const double bulge = 2 * angle / OdaPI + tol; m_pPline->setBulgeAt(m_pPline->numVerts() - 2, bulge); } } OdGePoint3dArray points; m_shape->intersectWith(m_shape, OdDb::Intersect::kOnBothOperands, points); if (points.size() < 1) createHatch(m_hatch, m_shape, m_patName); } virtual void removeDrawables(OdGsView* pView) { #ifdef DRAW_BOUNDS pView->erase(m_shape); pView->erase(m_pPline); #endif #ifdef DRAW_HATCH pView->erase(m_hatch); #endif } virtual int addDrawables(OdGsView* pView) { m_pPline->addVertexAt(m_pPline->numVerts(), m_end); #ifdef DRAW_BOUNDS pView->add(m_pPline, 0); pView->add(m_shape, 0); #endif #ifdef DRAW_HATCH pView->add(m_hatch, 0); #endif return 0; } }; struct RadiusTracker : OdStaticRxObject, MyTracker { OdGePoint2d m_center; bool m_diameter = false; void createHatch(EdgeArray& edgeArray1, EdgeArray& edgeArray2, const OdString& patName = L"SOLID") { if (m_hatch->numLoops() > 1) m_hatch->removeLoopAt(1); if (m_hatch->numLoops() > 0) m_hatch->removeLoopAt(0); m_hatch->setPattern(OdDbHatch::HatchPatternType::kPreDefined, L"SOLID"); m_hatch->appendLoop(OdDbHatch::kDefault, edgeArray1); m_hatch->appendLoop(OdDbHatch::kDefault, edgeArray2); try { m_hatch->setPattern(OdDbHatch::HatchPatternType::kPreDefined, patName); } // if the pattern is invalid catch (const OdError&) { m_hatch->setPattern(OdDbHatch::HatchPatternType::kPreDefined, L"SOLID"); } } virtual void setValue(double radius) { if (m_diameter) radius /= 2; OdGeCircArc2d arc(m_center, radius); std::pair arcs = setArcs(arc, m_width); EdgeArray edgeArray1, edgeArray2; edgeArray1.append(arcs.first.convertTo3d()->convertTo2d()); edgeArray2.append(arcs.second.convertTo3d()->convertTo2d()); createHatch(edgeArray2, edgeArray1, m_patName); } virtual int addDrawables(OdGsView* pView) { pView->add(m_hatch, 0); return 0; } virtual void removeDrawables(OdGsView* pView) { pView->erase(m_hatch); } }; void hatchByPathMode(OdDbHatch* pHatch, OdDbDatabase* pDb, OdDbUserIO* pIO, const OdGePoint3d& start) { OdInt32 ans = pIO->getInt("Line - 0 / Circle - 1"); const double width = pDb->getHPPATHWIDTH(); OdString patName; //\u0332 make letter before as bold OdString key = pIO->getString(L"S\u0332olid or P\u0332attern:", 0, "s"); if (!key.isEmpty() && key.makeLower() == "p") { key = pIO->getString(L"patternName:", 0, "angle"); patName = key; } else patName = L"SOLID"; if (ans == 1) { RadiusTracker circTracker; circTracker.m_center = start.convert2d(); circTracker.m_hatch = pHatch; circTracker.m_hatch->setDatabaseDefaults(pDb); circTracker.m_width = width; circTracker.m_patName = patName; try { pIO->getDist(L"Specify radius of circle or [Diameter]: ", // allows drawing the dragged circle and radius OdEd::kGdsFromLastPoint | OdEd::kGptRubberBand | OdEd::kInpThrowEmpty, 1., L"Diameter", &circTracker); } catch (const OdEdKeyword& kwd) { if (kwd.keywordIndex() != 0) { ODA_FAIL(); } circTracker.m_diameter = true; pIO->getDist(L"Specify diameter of circle: ", OdEd::kGdsFromLastPoint | OdEd::kGptRubberBand | OdEd::kInpThrowEmpty, 1., OdString::kEmpty, &circTracker); } OdDbBlockTableRecordPtr model = pDb->getModelSpaceId().safeOpenObject(OdDb::kForWrite); model->appendOdDbEntity(circTracker.m_hatch); } else { OdStaticRxObject tracker; tracker.m_width = width; tracker.m_pPline = OdDbPolyline::createObject(); tracker.m_shape = OdDbPolyline::createObject(); tracker.m_pPline->addVertexAt(0, start.convert2d()); tracker.m_shape->addVertexAt(0, start.convert2d()); tracker.m_hatch = pHatch; tracker.m_hatch->setDatabaseDefaults(pDb); tracker.m_patName = patName; OdGePoint3dArray points; while (1) { try { OdGePoint3d endPoint; while (1) { endPoint = pIO->getPoint("Arc/Line", OdEd::kInpThrowEmpty, NULL, L"", &tracker); // this will throw tracker.m_shape->intersectWith(tracker.m_shape, OdDb::Intersect::kOnBothOperands, points); if (points.size() >= 1) { tracker.m_pPline->removeVertexAt(tracker.m_pPline->numVerts() - 1); points.clear(); } } } catch (const OdEdOtherInput& err) { if (err.string() == L"a" || err.string() == L"A") { tracker.m_pPline->removeVertexAt(tracker.m_pPline->numVerts() - 1); tracker.m_isArc = true; } else if (err.string() == L"l" || err.string() == L"L") { tracker.m_pPline->removeVertexAt(tracker.m_pPline->numVerts() - 1); tracker.m_pPline->setBulgeAt(tracker.m_pPline->numVerts() - 1, 0); tracker.m_isArc = false; } } catch (const OdEdCancel&) { // remove current segment tracker.m_pPline->removeVertexAt(tracker.m_pPline->numVerts() - 1); if (tracker.m_isArc) { tracker.m_pPline->setBulgeAt(tracker.m_pPline->numVerts() - 1, 0); } tracker.rebuildShape(); createHatch(tracker.m_hatch, tracker.m_shape, patName); // save hatch tracker.m_hatch->setDatabaseDefaults(pDb); OdDbBlockTableRecordPtr model = pDb->getModelSpaceId().safeOpenObject(OdDb::kForWrite); model->appendOdDbEntity(tracker.m_hatch); break; } } } } }; enum eCmdMode { RECTANGLE = 0, CIRCLE = 1, ALIGNMENT = 2, WIDTH = 3, MODE = 4 }; void SelectMode(OdDbDatabase* pDb, OdDbUserIO* pUserIO, OdUInt8& hpdrawmode) { bool bKeyword = 0; int nKeyword = 0; while (true) { try { pUserIO->getPoint(OD_T("Specify draw hatch mode [Area/Path]:"), OdEd::kInpDefault, 0, OD_T("Area Path")); } catch (const OdEdCancel&) { return; } catch (const OdEdKeyword& kwd) { bKeyword = true; nKeyword = kwd.keywordIndex(); } if (bKeyword) { switch (nKeyword) { case 0: // AREA { hpdrawmode = 0; pDb->appServices()->setHPDRAWMODE(0); return; } case 1: // PATH { hpdrawmode = 1; pDb->appServices()->setHPDRAWMODE(1); return; } } } } return; } void selectWidth(OdDbDatabase* pDb, OdDbUserIO* pUserIO, double& hpwidth) { const double width = pUserIO->getReal("Specify the width of the hatch"); hpwidth = width; pDb->setHPPATHWIDTH(width); } void CreateHatchMode(OdEdCommandContext* pCmdCtx, OdDbHatchPtr pOrgHatch) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIOPtr pUserIO = pDbCmdCtx->dbUserIO(); OdDbDatabasePtr pDb = pDbCmdCtx->database(); OdDbBlockTableRecordPtr pBTR = pDb->getActiveLayoutBTRId().safeOpenObject(OdDb::kForWrite); const OdString keywords(OD_T("Rectangle Circle Alignment Width Mode")); int nKeyword = 0; bool bKeyword = false; OdGePoint3d pnt; OdUInt8 hpdrawmode = pDb->appServices()->getHPDRAWMODE(); double hpwidth = pDb->getHPPATHWIDTH(); bool bDone = true; while (bDone) { OdDbHatchPtr pHatch = pOrgHatch->clone(); bKeyword = false; try { OdString prompt; if (hpdrawmode == 0) prompt = OD_T("Specify start point [Rectangle/Circle/Mode]:"); else if (hpdrawmode == 1) prompt = OD_T("Specify start point [Rectangle/Circle/ALignment/Width/Mode]:"); pnt = pUserIO->getPoint(prompt, OdEd::kInpDefault, 0, keywords); } catch (const OdEdCancel&) { break; } catch (const OdEdKeyword& kwd) { bKeyword = true; nKeyword = kwd.keywordIndex(); } if (bKeyword) { switch (nKeyword) { case eCmdMode::RECTANGLE: if (hpdrawmode == 0) CreateHatchArea::RectangleArea(pHatch, pUserIO); break; case eCmdMode::CIRCLE: if (hpdrawmode == 0) CreateHatchArea::CircleArea(pHatch, pUserIO); break; case eCmdMode::ALIGNMENT: // TODO Select alignment break; case eCmdMode::WIDTH: selectWidth(pDb, pUserIO, hpwidth); break; case eCmdMode::MODE: SelectMode(pDb, pUserIO, hpdrawmode); break; } } else { if (hpdrawmode == 0) CreateHatchArea::PolylineArea(pHatch, pUserIO, pnt.convert2d()); else if (hpdrawmode == 1) { hatchByPath::hatchByPathMode(pHatch, pDb, pUserIO, pnt); break; } } pBTR->appendOdDbEntity(pHatch); } }