ADN Open CIS
Сообщество программистов Autodesk в СНГ

21/10/2015

Как объединить отрезки и дуги в полилинию?

Эта статья навеяна темой на форуме Полилинии и дуги в одну полилинию

Начиная с AutoCAD 2011 для этой цели можно воспользоваться методами Entity.JoinEntity и Entity.JoinEntities из AutoCAD .NET API, а также AcDbJoinEntityPE::joinEntity и AcDbJoinEntityPE::joinEntities из ObjectARX. Интереснее показать как можно это реализовать самостоятельно в .NET:

Код - C#: [Выделить]
  1. using System;
  2. using System.Collections;
  3. using Autodesk.AutoCAD.Runtime;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.ApplicationServices;
  6. using Autodesk.AutoCAD.EditorInput;
  7. using Autodesk.AutoCAD.Geometry;
  8.  
  9. #pragma warning disable 0618
  10.  
  11. [assembly: CommandClass(typeof(Rivilis.Contour))]
  12.  
  13. namespace Rivilis
  14. {
  15.   public class Contour
  16.   {
  17.     [CommandMethod("MakePline")]
  18.     public void MakePline()
  19.     {
  20.       Document doc = Application.DocumentManager.MdiActiveDocument;
  21.       Editor ed = doc.Editor;
  22.       Database db = doc.Database;
  23.       // Отбираем только отрезки и дуги
  24.       SelectionFilter sf =
  25.         new SelectionFilter(new TypedValue[] { new TypedValue((int)DxfCode.Start,"LINE,ARC") });
  26.       PromptSelectionResult rs = ed.GetSelection(sf);
  27.       if (rs.Status != PromptStatus.OK) return;
  28.       ObjectIdCollection ids = new ObjectIdCollection(rs.Value.GetObjectIds());
  29.       while (ids.Count > 0) {
  30.         using (Polyline p = MakeJoinedPoly(ref ids)) {
  31.           if (p != null) {
  32.             using (BlockTableRecord btr = db.CurrentSpaceId.Open(OpenMode.ForWrite) as BlockTableRecord) {
  33.               btr.AppendEntity(p);
  34.             }
  35.           }
  36.           else {
  37.             ed.WriteMessage("\nОшибка во входных данных!");
  38.             return;
  39.           }
  40.         }
  41.       }
  42.     }
  43.     public static Polyline MakeJoinedPoly(ref ObjectIdCollection ids)
  44.     {
  45.       if (ids.Count == 0) return null;
  46.       // Создаём полилинию
  47.       Polyline p = new Polyline();
  48.         p.SetDatabaseDefaults();
  49.         ObjectId idOwn = ObjectId.Null;
  50.         ObjectId idFirst = ids[0];
  51.         Point3d nextPt = Point3d.Origin;
  52.         Point3d prevPt = Point3d.Origin;
  53.         // Добавляем первые две вершины из первого выбранного примитива
  54.         using (Curve c = idFirst.Open(OpenMode.ForRead) as Curve) {
  55.           p.AddVertexAt(0, ToPoint2d(c.StartPoint), BulgeFromArc(c, false), 0, 0);
  56.           p.AddVertexAt(1, ToPoint2d(c.EndPoint), 0, 0, 0);
  57.           nextPt = c.EndPoint;
  58.           prevPt = c.StartPoint;
  59.           idOwn = c.OwnerId;
  60.         }
  61.        
  62.         ids.Remove(idFirst);
  63.  
  64.         int prevCnt = ids.Count + 1;
  65.  
  66.         while (ids.Count > 0 && ids.Count < prevCnt)
  67.         {
  68.           prevCnt = ids.Count;
  69.           foreach (ObjectId id in ids) {
  70.             using (Curve cv = id.Open(OpenMode.ForRead) as Curve) {
  71.               if (cv.StartPoint == nextPt || cv.EndPoint == nextPt) {
  72.                 double bulge = BulgeFromArc(cv, cv.EndPoint == nextPt);
  73.                 p.SetBulgeAt(p.NumberOfVertices - 1, bulge);
  74.                 if (cv.StartPoint == nextPt)
  75.                   nextPt = cv.EndPoint;
  76.                 else
  77.                   nextPt = cv.StartPoint;
  78.                 p.AddVertexAt(p.NumberOfVertices, ToPoint2d(nextPt), 0, 0, 0);
  79.                 ids.Remove(id);
  80.                 break;
  81.               } else if (cv.StartPoint == prevPt || cv.EndPoint == prevPt) {
  82.                 double bulge = BulgeFromArc(cv, cv.StartPoint == prevPt);
  83.                 if (cv.StartPoint == prevPt)
  84.                   prevPt = cv.EndPoint;
  85.                 else
  86.                   prevPt = cv.StartPoint;
  87.                 p.AddVertexAt(0, ToPoint2d(prevPt), bulge, 0, 0);
  88.                 ids.Remove(id);
  89.                 break;
  90.               }
  91.             }
  92.           }
  93.         }
  94.         if (p.NumberOfVertices == 0)
  95.           return null;
  96.         else 
  97.           return p;
  98.     }
  99.     // Функция возвращает кривизну дуги (bulge) или 0.0
  100.     static double BulgeFromArc(Curve c, bool clockwise)
  101.     {
  102.       double bulge = 0.0;
  103.       Arc a = c as Arc;
  104.       if (a == null) return bulge;
  105.       double newStart = (a.StartAngle > a.EndAngle) ?
  106.         (a.StartAngle - 8 * Math.Atan(1)) : (a.StartAngle);
  107.       bulge = Math.Tan((a.EndAngle - newStart) / 4);
  108.       if (clockwise) bulge = -bulge;
  109.       return bulge;
  110.     }
  111.     // Функция преобразует Point3d в Point2d (отбрасывает Z)
  112.     static Point2d ToPoint2d(Point3d p) {
  113.       return new Point2d(p.X, p.Y);
  114.     }
  115.   }
  116. }

В ObjectARX:

Код - C++: [Выделить]
  1.  
  2. //-----------------------------------------------------------------------------
  3. //----- acrxEntryPoint.cpp
  4. //-----------------------------------------------------------------------------
  5. #include "StdAfx.h"
  6. #include "resource.h"
  7.  
  8. //-----------------------------------------------------------------------------
  9. #define szRDS _RXST("")
  10.  
  11. //-----------------------------------------------------------------------------
  12. //----- ObjectARX EntryPoint
  13. class CJoinToPlineApp : public AcRxArxApp {
  14.  
  15. public:
  16.   CJoinToPlineApp () : AcRxArxApp () {}
  17.  
  18.   virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt) {
  19.     AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg (pkt) ;
  20.     return (retCode) ;
  21.   }
  22.  
  23.   virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt) {
  24.     AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg (pkt) ;
  25.     return (retCode) ;
  26.   }
  27.  
  28.   virtual void RegisterServerComponents () {
  29.   }
  30.  
  31.   static void JoinToPlineJoinToPline () {
  32.     // Фильтр для выбора отрезков (LINE) и дуг (ARC)
  33.     ACHAR* promptPtrs[] = {
  34.       _T("\nВыберите отрезки и дуги для объединения в полилинии (ENTER - завершение): "),
  35.       _T("\nУдалите линии из набора: ")
  36.     };
  37.  
  38.     ads_name ss;
  39.     resbuf *rbFilter = acutBuildList(RTDXF0,_T("LINE,ARC"),RTNONE);
  40.     int rc = acedSSGet(_T(":$"),promptPtrs,NULL,rbFilter,ss);
  41.     acutRelRb(rbFilter);
  42.  
  43.     if (rc != RTNORM) return;
  44.  
  45.     AcDbObjectIdArray ids;
  46.     if (ObjectIdArrayFromSelSet(ss, ids) != Acad::eOk) return;
  47.     while (ids.length() > 0)
  48.     {
  49.       AcDbEntity *p = MakeJonedPoly(ids);
  50.       if (p) {
  51.         AcDbBlockTableRecordPointer pSpace(acdbCurDwg()->currentSpaceId(), AcDb::kForWrite);
  52.         if (pSpace.openStatus() != Acad::eOk) return;
  53.         pSpace->appendAcDbEntity(p);
  54.         p->close();
  55.       } else {
  56.         acutPrintf(_T("\nОшибка во входных данных!"));
  57.         return;
  58.       }
  59.     }
  60.  
  61.   }
  62.  
  63.   /// <summary>
  64.   /// Получение AcDbObjectIdArray из SelectionSet
  65.   /// </summary>
  66.   /// <param name="sset">SelectionSet</param>
  67.   /// <param name="ids">AcDbObjectIdArray</param>
  68.   /// <returns></returns>
  69.   static Acad::ErrorStatus ObjectIdArrayFromSelSet(ads_name sset, AcDbObjectIdArray &ids)
  70.   {
  71.     Acad::ErrorStatus es = Acad::eOk;
  72.     long nset = -1;
  73.     if (acedSSLength(sset,&nset) != RTNORM) return Acad::eAmbiguousInput;
  74.     ids.setLogicalLength(nset);
  75.     ads_name en;
  76.     AcDbObjectId id;
  77.     for (long i=0; i < nset; i++) {
  78.       if (acedSSName(sset,i,en) == RTNORM) {
  79.         if ((es = acdbGetObjectId(id,en)) != Acad::eOk) return es;
  80.         ids[i] = id;
  81.       }
  82.     }
  83.     return Acad::eOk;
  84.   }
  85.  
  86.   /// <summary>
  87.   /// Создаём полилинию из переданных отрезков и дуг.
  88.   /// Их идентификаторы удаляются из переданного массива
  89.   /// </summary>
  90.   /// <param name="ids">Массив идентификаторов</param>
  91.   /// <param name="FUZZ">Точность определения расстояния между точками</param>
  92.   /// <returns></returns>
  93.   static AcDbPolyline* MakeJonedPoly(
  94.     AcDbObjectIdArray &ids,
  95.     double FUZZ = AcGeContext::gTol.equalPoint())
  96.   {
  97.     AcDbPolyline *p = new AcDbPolyline();
  98.     p->setDatabaseDefaults();
  99.     AcDbObjectId idFirst = ids[0];
  100.     AcGePoint3d nextPt = AcGePoint3d::kOrigin;
  101.     AcGePoint3d prevPt = AcGePoint3d::kOrigin;
  102.  
  103.     AcDbObjectPointer<AcDbCurve> c(idFirst,AcDb::kForRead);
  104.     if (c.openStatus() == Acad::eOk) {
  105.       AcGePoint3d ptStart, ptEnd;
  106.       c->getStartPoint(ptStart); c->getEndPoint(ptEnd);
  107.       p->addVertexAt(0, asPnt2d(asDblArray(ptStart)), BulgeFromArc(c, false), 0, 0);
  108.       p->addVertexAt(1, asPnt2d(asDblArray(ptEnd)), 0, 0, 0);
  109.       nextPt = ptEnd;
  110.       prevPt = ptStart;
  111.     }
  112.  
  113.     ids.remove(idFirst);
  114.     int prevCnt = ids.length() + 1;
  115.  
  116.     while (ids.length() > 0 && ids.length() < prevCnt)
  117.     {
  118.       prevCnt = ids.length();
  119.       for (int i = 0; i < ids.length(); i++) {
  120.         AcDbObjectId id = ids[i];
  121.         AcDbObjectPointer<AcDbCurve> cv(id,AcDb::kForRead);
  122.         if (cv.openStatus() == Acad::eOk) {
  123.           AcGePoint3d ptStart, ptEnd;
  124.           cv->getStartPoint(ptStart); cv->getEndPoint(ptEnd);
  125.           if (ptStart.distanceTo(nextPt) < FUZZ || ptEnd.distanceTo(nextPt) < FUZZ) {
  126.             double bulge = BulgeFromArc(cv, ptEnd.distanceTo(nextPt) < FUZZ);
  127.             p->setBulgeAt(p->numVerts() - 1, bulge);
  128.             if (ptStart.distanceTo(nextPt) < FUZZ)
  129.               nextPt = ptEnd;
  130.             else
  131.               nextPt = ptStart;
  132.             p->addVertexAt(p->numVerts(), asPnt2d(asDblArray(nextPt)), 0, 0, 0);
  133.             ids.remove(id);
  134.             break;
  135.           } else if (ptStart.distanceTo(prevPt) < FUZZ || ptEnd.distanceTo(prevPt) < FUZZ) {
  136.             double bulge = BulgeFromArc(cv, ptStart.distanceTo(prevPt) < FUZZ);
  137.             if (ptStart.distanceTo(prevPt) < FUZZ)
  138.               prevPt = ptEnd;
  139.             else
  140.               prevPt = ptStart;
  141.             p->addVertexAt(0, asPnt2d(asDblArray(prevPt)), bulge, 0, 0);
  142.             ids.remove(id);
  143.             break;
  144.           }
  145.         }
  146.       }
  147.     }
  148.     if (p->numVerts() == 0) {
  149.       delete p;  return NULL;
  150.     }  else  {
  151.       return p;
  152.     }
  153.   }
  154.  
  155.   /// <summary>
  156.   /// Получение кривизны (bulge) для кривой
  157.   /// </summary>
  158.   /// <param name="c">Указатель на кривую</param>
  159.   /// <param name="clockwise">по часовой ли стрелке.</param>
  160.   /// <returns></returns>
  161.   static double BulgeFromArc(AcDbCurve *c, bool clockwise)
  162.   {
  163.     double bulge = 0.0;
  164.     AcDbArc *a = AcDbArc::cast(c);
  165.     if (a == NULL) return bulge;
  166.  
  167.     double newStart =
  168.       (a->startAngle() > a->endAngle()) ?
  169.       (a->startAngle() - 8 * atan(1.0)) :
  170.       (a->startAngle());
  171.  
  172.     bulge = tan((a->endAngle() - newStart) / 4.0);
  173.     if (clockwise) bulge = -bulge;
  174.     return bulge;
  175.   }
  176. } ;
  177.  
  178. //-----------------------------------------------------------------------------
  179. IMPLEMENT_ARX_ENTRYPOINT(CJoinToPlineApp)
  180. ACED_ARXCOMMAND_ENTRY_AUTO(CJoinToPlineApp, JoinToPline, JoinToPline, JoinToPline, ACRX_CMD_MODAL, NULL)

 

Автор: Александр Ривилис

Обсуждение: http://adn-cis.org/forum/index.php?topic=3122

Опубликовано 21.10.2015
Отредактировано 21.10.2015 в 22:57:02