converting Arc to Polyline
https://forums.autodesk.com/t5/objectarx/converting-arc-to-polyline/td-p/6985016
//----------------------------------------------------------------------------- //----- acrxEntryPoint.cpp //----------------------------------------------------------------------------- #include "StdAfx.h" #include "resource.h" //----------------------------------------------------------------------------- #define szRDS _RXST("") //----------------------------------------------------------------------------- //----- ObjectARX EntryPoint class CJoinToPlineApp : public AcRxArxApp { public: CJoinToPlineApp () : AcRxArxApp () {} virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt) { AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg (pkt) ; return (retCode) ; } virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt) { AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg (pkt) ; return (retCode) ; } virtual void RegisterServerComponents () { } static void JoinToPlineJoinToPline () { // Фильтр для выбора отрезков (LINE) и дуг (ARC) ACHAR* promptPtrs[] = { _T("\nSelect lines and arcs for join in poyline (ENTER - exit): "), _T("\nRemove lines and arcs from selection set: ") }; ads_name ss; resbuf *rbFilter = acutBuildList(RTDXF0,_T("LINE,ARC"),RTNONE); int rc = acedSSGet(_T(":$"),promptPtrs,NULL,rbFilter,ss); acutRelRb(rbFilter); if (rc != RTNORM) return; AcDbObjectIdArray ids; if (ObjectIdArrayFromSelSet(ss, ids) != Acad::eOk) return; while (ids.length() > 0) { AcDbEntity *p = MakeJonedPoly(ids); if (p) { AcDbBlockTableRecordPointer pSpace(acdbCurDwg()->currentSpaceId(), AcDb::kForWrite); if (pSpace.openStatus() != Acad::eOk) return; pSpace->appendAcDbEntity(p); p->close(); } else { acutPrintf(_T("\nError in data!")); return; } } } /// <summary> /// AcDbObjectIdArray from SelectionSet /// </summary> /// <param name="sset">SelectionSet</param> /// <param name="ids">AcDbObjectIdArray</param> /// <returns></returns> static Acad::ErrorStatus ObjectIdArrayFromSelSet(ads_name sset, AcDbObjectIdArray &ids) { Acad::ErrorStatus es = Acad::eOk; long nset = -1; if (acedSSLength(sset,&nset) != RTNORM) return Acad::eAmbiguousInput; ids.setLogicalLength(nset); ads_name en; AcDbObjectId id; for (long i=0; i < nset; i++) { if (acedSSName(sset,i,en) == RTNORM) { if ((es = acdbGetObjectId(id,en)) != Acad::eOk) return es; ids[i] = id; } } return Acad::eOk; } /// <summary> /// Create polyline from lines and arcs. /// Those ids will remove from input array /// </summary> /// <param name="ids">Array of id's</param> /// <param name="FUZZ">The accuracy of determining the distance between points</param> /// <returns></returns> static AcDbPolyline* MakeJonedPoly( AcDbObjectIdArray &ids, double FUZZ = AcGeContext::gTol.equalPoint()) { AcDbPolyline *p = new AcDbPolyline(); p->setDatabaseDefaults(); AcDbObjectId idFirst = ids[0]; AcGePoint3d nextPt = AcGePoint3d::kOrigin; AcGePoint3d prevPt = AcGePoint3d::kOrigin; AcDbObjectPointer<AcDbCurve> c(idFirst,AcDb::kForRead); if (c.openStatus() == Acad::eOk) { AcGePoint3d ptStart, ptEnd; c->getStartPoint(ptStart); c->getEndPoint(ptEnd); p->addVertexAt(0, asPnt2d(asDblArray(ptStart)), BulgeFromArc(c, false), 0, 0); p->addVertexAt(1, asPnt2d(asDblArray(ptEnd)), 0, 0, 0); nextPt = ptEnd; prevPt = ptStart; } ids.remove(idFirst); int prevCnt = ids.length() + 1; while (ids.length() > 0 && ids.length() < prevCnt) { prevCnt = ids.length(); for (int i = 0; i < ids.length(); i++) { AcDbObjectId id = ids[i]; AcDbObjectPointer<AcDbCurve> cv(id,AcDb::kForRead); if (cv.openStatus() == Acad::eOk) { AcGePoint3d ptStart, ptEnd; cv->getStartPoint(ptStart); cv->getEndPoint(ptEnd); if (ptStart.distanceTo(nextPt) < FUZZ || ptEnd.distanceTo(nextPt) < FUZZ) { double bulge = BulgeFromArc(cv, ptEnd.distanceTo(nextPt) < FUZZ); p->setBulgeAt(p->numVerts() - 1, bulge); if (ptStart.distanceTo(nextPt) < FUZZ) nextPt = ptEnd; else nextPt = ptStart; p->addVertexAt(p->numVerts(), asPnt2d(asDblArray(nextPt)), 0, 0, 0); ids.remove(id); break; } else if (ptStart.distanceTo(prevPt) < FUZZ || ptEnd.distanceTo(prevPt) < FUZZ) { double bulge = BulgeFromArc(cv, ptStart.distanceTo(prevPt) < FUZZ); if (ptStart.distanceTo(prevPt) < FUZZ) prevPt = ptEnd; else prevPt = ptStart; p->addVertexAt(0, asPnt2d(asDblArray(prevPt)), bulge, 0, 0); ids.remove(id); break; } } } } if (p->numVerts() == 0) { delete p; return NULL; } else { return p; } } /// <summary> /// Getting bulge of curve /// </summary> /// <param name="c">AcDbCurve pointer</param> /// <param name="clockwise">direction.</param> /// <returns></returns> static double BulgeFromArc(AcDbCurve *c, bool clockwise) { double bulge = 0.0; AcDbArc *a = AcDbArc::cast(c); if (a == NULL) return bulge; double newStart = (a->startAngle() > a->endAngle()) ? (a->startAngle() - 8 * atan(1.0)) : (a->startAngle()); bulge = tan((a->endAngle() - newStart) / 4.0); if (clockwise) bulge = -bulge; return bulge; } } ; //----------------------------------------------------------------------------- IMPLEMENT_ARX_ENTRYPOINT(CJoinToPlineApp) ACED_ARXCOMMAND_ENTRY_AUTO(CJoinToPlineApp, JoinToPline, JoinToPline, JoinToPline, ACRX_CMD_MODAL, NULL)
Do you want to merge segments only using the _PEDIT command and do not like my code for joining? OK.
//----------------------------------------------------------------------------- //----- acrxEntryPoint.cpp //----------------------------------------------------------------------------- #include "StdAfx.h" #include "acedCmdNF.h" #include "resource.h" //----------------------------------------------------------------------------- #define szRDS _RXST("") //----------------------------------------------------------------------------- //----- ObjectARX EntryPoint class CMyJoinApp : public AcRxArxApp { public: CMyJoinApp() : AcRxArxApp() {} virtual AcRx::AppRetCode On_kInitAppMsg(void *pkt) { AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg(pkt); return (retCode); } virtual AcRx::AppRetCode On_kUnloadAppMsg(void *pkt) { AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg(pkt); return (retCode); } virtual void RegisterServerComponents() {} static void RivilisMyJoin() { ads_name en1; ads_point p; while (acedEntSel(_T("\nSelect first segment: "), en1, p) == RTNORM) { bool bConvertToPoly = false; AcDbObjectId id1; acdbGetObjectId(id1, en1); if (id1.objectClass() == AcDbLine::desc() || id1.objectClass() == AcDbArc::desc()) { bConvertToPoly = true; } ads_name en2; if (acedEntSel(_T("\nSelect next segment: "), en2, p) == RTNORM) { if (bConvertToPoly) acedCommandS(RTSTR, _T("_PEDIT"), RTENAME, en1, RTSTR, _T("_YES"), RTSTR, _T("_Join"), RTENAME, en2, RTSTR, _T(""), RTSTR, _T(""), RTNONE); else acedCommandS(RTSTR, _T("_PEDIT"), RTENAME, en1, RTSTR, _T("_Join"), RTENAME, en2, RTSTR, _T(""), RTSTR, _T(""), RTNONE); } } } }; //----------------------------------------------------------------------------- IMPLEMENT_ARX_ENTRYPOINT(CMyJoinApp) ACED_ARXCOMMAND_ENTRY_AUTO(CMyJoinApp, Rivilis, MyJoin, MyJoin, ACRX_CMD_MODAL, NULL)
I've just remembered that the command _PEDIT is affected by the system variable PEDITACCEPT. So more accurate code is:
//----------------------------------------------------------------------------- //----- acrxEntryPoint.cpp //----------------------------------------------------------------------------- #include "StdAfx.h" #include "acedCmdNF.h" #include "resource.h" //----------------------------------------------------------------------------- #define szRDS _RXST("") //----------------------------------------------------------------------------- //----- ObjectARX EntryPoint class CMyJoinApp : public AcRxArxApp { public: CMyJoinApp() : AcRxArxApp() {} virtual AcRx::AppRetCode On_kInitAppMsg(void *pkt) { AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg(pkt); return (retCode); } virtual AcRx::AppRetCode On_kUnloadAppMsg(void *pkt) { AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg(pkt); return (retCode); } virtual void RegisterServerComponents() {} static void RivilisMyJoin() { // Check PEDITACCEPT sysvar resbuf rb = { 0, RTSHORT, 0 }; acedGetVar(_T("PEDITACCEPT"), &rb); bool bPeditAccept = (rb.resval.rint == 1) ? true : false; ads_name en1; ads_point p; while (acedEntSel(_T("\nSelect first segment: "), en1, p) == RTNORM) { // if PEDITACCEPT == 1 then PEDIT no ask about converting bool bConvertToPoly = false; if (!bPeditAccept) { AcDbObjectId id1; acdbGetObjectId(id1, en1); if (id1.objectClass() == AcDbLine::desc() || id1.objectClass() == AcDbArc::desc()) { bConvertToPoly = true; } } ads_name en2; if (acedEntSel(_T("\nSelect next segment: "), en2, p) == RTNORM) { if (bConvertToPoly) acedCommandS(RTSTR, _T("_PEDIT"), RTENAME, en1, RTSTR, _T("_YES"), RTSTR, _T("_Join"), RTENAME, en2, RTSTR, _T(""), RTSTR, _T(""), RTNONE); else acedCommandS(RTSTR, _T("_PEDIT"), RTENAME, en1, RTSTR, _T("_Join"), RTENAME, en2, RTSTR, _T(""), RTSTR, _T(""), RTNONE); } } } }; //----------------------------------------------------------------------------- IMPLEMENT_ARX_ENTRYPOINT(CMyJoinApp) ACED_ARXCOMMAND_ENTRY_AUTO(CMyJoinApp, Rivilis, MyJoin, MyJoin, ACRX_CMD_MODAL, NULL)
Bulge Conversion Functions
The curvature of a Polyline Arc segment is defined using a quantity known as bulge. This unit measures the deviation of the curve from the straight line (chord) joining the two vertices of the segment. It is defined as the ratio of the arc sagitta (versine) to half the length of the chord between the two vertices; this ratio is equal to the tangent of a quarter of the included arc angle between the two polyline vertices.
In this way, a negative bulge indicates that the arc follows a clockwise direction from the first vertex to the next, with a positive bulge describing an anticlockwise oriented arc. A bulge of 0 indicates a straight segment, and a bulge of 1 is a semicircle.
An AutoCAD Arc Entity is defined by a center, radius and start & end angle. The arc is always defined to be anticlockwise oriented, that is, following an anticlockwise direction from the start angle to the end angle.
Here I demonstrate various methods to convert between the quantities used to define a Polyline Arc segment and those used to define an Arc Entity, with a brief explanation of calculations used in each method.
Contents
This function will convert the quantities used to define an AutoCAD Arc Entity (center, start/end angle, radius) to those used to define a Polyline Arc segment (vertices, bulge).
The function first normalises the included arc angle to lie within the range 0 ≤ θ < 2π and then utilises the definition that the bulge of a Polyline Arc is the tangent of a quarter of the included arc angle.
;; Arc to Bulge - Lee Mac;; c - center;; a1,a2 - start, end angle;; r - radius;; Returns: (<vertex> <bulge> <vertex>)(defun LM:arc->bulge ( c a1 a2 r ) (list (polar c a1 r) ( (lambda ( a ) (/ (sin a) (cos a))) (/ (rem (+ pi pi (- a2 a1)) (+ pi pi)) 4.0) ) (polar c a2 r) ))
Test Function
The following test function will prompt for selection of an Arc Entity and proceed to create a (green) Polyline Arc segment to match the selected arc.
(defun c:test ( / e ) (if (setq e (ssget "_+.:E:S" '((0 . "ARC")))) (progn (setq e (entget (ssname e 0))) (entmake (append '( (000 . "LWPOLYLINE") (100 . "AcDbEntity") (100 . "AcDbPolyline") (090 . 2) (070 . 0) ) (mapcar 'cons '(10 42 10) (LM:arc->bulge (cdr (assoc 10 e)) (cdr (assoc 50 e)) (cdr (assoc 51 e)) (cdr (assoc 40 e)) ) ) ) ) ) ) (princ))
This function will return the bulge value describing an arc which starts at the first supplied point pt1, passes through the second supplied point pt2, and terminates at the third supplied point pt3.
The returned bulge value may be positive or negative, depending upon whether the arc passing through the three points traces a clockwise or counter-clockwise path.
;; 3-Points to Bulge - Lee Mac(defun LM:3p->bulge ( pt1 pt2 pt3 ) ((lambda ( a ) (/ (sin a) (cos a))) (/ (+ (- pi (angle pt2 pt1)) (angle pt2 pt3)) 2)))
Test Function
The following test function will prompt the user for specification of three points, and, given a valid response of three non-collinear points, will then construct a polyline arc segment passing through the three points.
(defun c:3ppolyarc ( / ocs pt1 pt2 pt3 ) (if (and (setq pt1 (getpoint "\nSpecify 1st point: ")) (setq pt2 (getpoint "\nSpecify 2nd point: " pt1)) (setq pt3 (getpoint "\nSpecify 3rd point: " pt2)) (setq ocs (trans '(0 0 1) 1 0 t)) ) (entmake (list '(000 . "LWPOLYLINE") '(100 . "AcDbEntity") '(100 . "AcDbPolyline") '(090 . 2) '(070 . 0) (cons 038 (caddr (trans pt1 1 ocs))) (cons 010 (trans pt1 1 ocs)) (cons 042 (LM:3p->bulge pt1 pt2 pt3)) (cons 010 (trans pt3 1 ocs)) (cons 210 ocs) ) ) ) (princ))
This function will convert the quantities used to define a Polyline Arc segment (vertices, bulge) to those used to define an AutoCAD Arc Entity (center, start/end angle, radius).
Version 1
This version uses the relationship between the arc chord length and included angle, as illustrated by the following diagram:
;; Bulge to Arc - Lee Mac;; p1 - start vertex;; p2 - end vertex;; b - bulge;; Returns: (<center> <start angle> <end angle> <radius>)(defun LM:Bulge->Arc ( p1 p2 b / a c r ) (setq a (* 2 (atan b)) r (/ (distance p1 p2) 2 (sin a)) c (polar p1 (+ (- (/ pi 2) a) (angle p1 p2)) r) ) (if (minusp b) (list c (angle c p2) (angle c p1) (abs r)) (list c (angle c p1) (angle c p2) (abs r)) ))
Version 2
This version uses the relationship between the arc sagitta and bulge factor, illustrated by the following diagram:
;; Bulge to Arc - Lee Mac;; p1 - start vertex;; p2 - end vertex;; b - bulge;; Returns: (<center> <start angle> <end angle> <radius>)(defun LM:Bulge->Arc ( p1 p2 b / c r ) (setq r (/ (* (distance p1 p2) (1+ (* b b))) 4 b) c (polar p1 (+ (angle p1 p2) (- (/ pi 2) (* 2 (atan b)))) r) ) (if (minusp b) (list c (angle c p2) (angle c p1) (abs r)) (list c (angle c p1) (angle c p2) (abs r)) ))
Test Function
The following test function will prompt for selection of an LWPolyline with 2 vertices and non-zero bulge, and proceed to create a (green) Arc Entity to match the selected Polyline Arc segment.
(defun c:test ( / e ) (if (setq e (ssget "_+.:E:S" '((0 . "LWPOLYLINE") (90 . 2) (-4 . "<>") (42 . 0.0)))) (progn (setq e (entget (ssname e 0))) (entmake (cons '(0 . "ARC") (mapcar 'cons '(10 50 51 40) (LM:Bulge->Arc (cdr (assoc 10 e)) (cdr (assoc 10 (reverse e))) (cdr (assoc 42 e)) ) ) ) ) ) ) (princ))
Additional Functions
The following functions are derived from the above Bulge to Arc functions, and will return the individual arc properties of the polyline arc segment.
The required parameters & values returned by each function are detailed in the respective code headers.
Bulge Center
;; Bulge Center - Lee Mac;; p1 - start vertex;; p2 - end vertex;; b - bulge;; Returns the center of the arc described by the given bulge and vertices (defun LM:BulgeCenter ( p1 p2 b ) (polar p1 (+ (angle p1 p2) (- (/ pi 2) (* 2 (atan b)))) (/ (* (distance p1 p2) (1+ (* b b))) 4 b) ))
Bulge Radius
;; Bulge Radius - Lee Mac;; p1 - start vertex;; p2 - end vertex;; b - bulge;; Returns the radius of the arc described by the given bulge and vertices (defun LM:BulgeRadius ( p1 p2 b ) (/ (* (distance p1 p2) (1+ (* b b))) 4 (abs b)))
Further Information
More information about the derivation of the bulge quantity can be found in this article by Stig Madsen. The article also explains in greater detail many of the properties and relationships of an arc used by the above functions.
文章评论