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

22/04/2015

Как найти сплайн в AutoCAD между двумя другими сплайнами в .NET

Я получил вопрос в комментариях к блогу:

Как найти «центральную» линию NurbCurve3dмежду двумя другими линиями NurbCurve3d?

Я интерпретирую это следующим образом: с учетом двух объектов NurbCurve3d, создать NurbCurve3d, который находится точно между ними. NurbCurve3d являются AcGe-классом - это означает, что это объект неграфический - так что я решил работать со сплайнами (Spline), поскольку они тесно связаны с классом NurbCurve3d (есть удобные методы для преобразования между этими двумя классами). Вот еще для интересующихся некоторая информация о NURBS.

Я также решил ограничится одним довольно конкретном случаем: сплайны (и эквивалентные им NurbCurve3d) имеют точно такую же степень и период, а также одинаковое количество контрольных пунктов, узлов и весов. Это резко упрощает задачу. Можно было конечно при достаточных усилиях и времени найти способ параметризации двух кривых и создать точки между кривыми и построить кривую по этим точкам. Я решил не заниматься этой более общей проблемой. По крайней мере, не в этой статье.

Так что я сделал на самом деле очень просто: между двумя же подобными объектами NurbCurve3d, мы получаем три коллекции значений - контрольных точек, узлов и весов - и создаем три новые коллекции для новой кривой, которые содержат "средние "значения данных из двух исходных объектов. Я далек от того чтобы быть экспертом NURBS, но я считаю, что такой подход вполне разумен для этой конкретной категории Spline (по крайней мере, кажется, это работает достаточно хорошо).

Имейте в виду, что этот код не будет работать со «сглаженными» (fit) сплайнами. По крайней мере, я не ожидаю, что это будет работать. :-)

 

Вот код на C#:

Код - C#: [Выделить]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
  6.  
  7. namespace NurbCurveStuff
  8. {
  9.   public class Commands
  10.   {
  11.     [CommandMethod("SBS")]
  12.     static public void SplineBetweenSplines()
  13.     {
  14.       var doc = Application.DocumentManager.MdiActiveDocument;
  15.       var db = doc.Database;
  16.       var ed = doc.Editor;
  17.  
  18.       // Выбираем наши сплайны
  19.  
  20.       var peo = new PromptEntityOptions("\nВыберите первый сплайн ");
  21.       peo.SetRejectMessage("\nДолжен быть сплайн.");
  22.       peo.AddAllowedClass(typeof(Spline), true);
  23.  
  24.       var per = ed.GetEntity(peo);
  25.       if (per.Status != PromptStatus.OK)
  26.         return;
  27.  
  28.       var spId1 = per.ObjectId;
  29.  
  30.       peo.Message = "\nВыберите второй сплайн ";
  31.  
  32.       per = ed.GetEntity(peo);
  33.       if (per.Status != PromptStatus.OK)
  34.         return;
  35.  
  36.       var spId2 = per.ObjectId;
  37.  
  38.       // Стартуем транзакцию
  39.  
  40.       using (var tr = doc.TransactionManager.StartTransaction())
  41.       {
  42.         // Открываем сплайны
  43.  
  44.         var sp1 =
  45.           tr.GetObject(spId1, OpenMode.ForRead) as Spline;
  46.  
  47.         var sp2 =
  48.           tr.GetObject(spId2, OpenMode.ForRead) as Spline;
  49.  
  50.         if (sp1 != null && sp2 != null)
  51.         {
  52.           try
  53.           {
  54.             // Получаем GE-эквиваленты сплайнов
  55.  
  56.             var cur1 = sp1.GetGeCurve() as NurbCurve3d;
  57.             var cur2 = sp2.GetGeCurve() as NurbCurve3d;
  58.  
  59.             if (cur1 != null && cur2 != null)
  60.             {
  61.               // Находим средний сплайн между двумя
  62.  
  63.               var cur3 = MiddleCurve(cur1, cur2);
  64.               if (cur3 != null)
  65.               {
  66.                 // Создаем сплайн из средней кривой
  67.                 var sp = Curve.CreateFromGeCurve(cur3);
  68.                 if (sp != null)
  69.                 {
  70.                   // Добавляем сплайн в базу чертежа
  71.  
  72.                   var btr =
  73.                     (BlockTableRecord)tr.GetObject(
  74.                       db.CurrentSpaceId,
  75.                       OpenMode.ForWrite
  76.                     );
  77.  
  78.                   btr.AppendEntity(sp);
  79.                   tr.AddNewlyCreatedDBObject(sp, true);
  80.                 }
  81.               }
  82.             }
  83.             tr.Commit();
  84.           }
  85.           catch (Autodesk.AutoCAD.Runtime.Exception ex)
  86.           {
  87.             ed.WriteMessage(
  88.               "\nИсключение: {0}", ex.Message
  89.             );
  90.           }
  91.         }
  92.       }
  93.     }
  94.  
  95.     private static NurbCurve3d MiddleCurve(
  96.       NurbCurve3d cur1, NurbCurve3d cur2
  97.     )
  98.     {
  99.       // Возвращаем NurbCurve3d, которая лежит между переданными
  100.  
  101.       // Начнем с получения периода обеих кривых
  102.  
  103.       double per1, per2;
  104.       bool ip1 = cur1.IsPeriodic(out per1);
  105.       bool ip2 = cur2.IsPeriodic(out per2);
  106.  
  107.       // Убеждаемся, что кривые имеют одинаковый период, степень,
  108.       // число контрольных точек, узлов и весов
  109.  
  110.       if (
  111.         cur1.Degree != cur2.Degree || ip1 != ip2 || per1 != per2 ||
  112.         cur1.NumberOfControlPoints != cur2.NumberOfControlPoints ||
  113.         cur1.NumberOfKnots != cur2.NumberOfKnots ||
  114.         cur1.NumWeights != cur2.NumWeights
  115.       )
  116.         return null;
  117.  
  118.       var degree = cur1.Degree;
  119.       var period = ip1;
  120.  
  121.       // Получаем средние контрольные точки
  122.  
  123.       int numPoints = cur1.NumberOfControlPoints;
  124.       var pts = new Point3dCollection();
  125.  
  126.       for (int i = 0; i < numPoints; i++)
  127.       {
  128.         var pt1 = cur1.ControlPointAt(i);
  129.         var pt2 = cur2.ControlPointAt(i);
  130.  
  131.         pts.Add(pt1 + ((pt2 - pt1) / 2));
  132.       }
  133.  
  134.       // Получаем средние узлы
  135.  
  136.       var numKnots = cur1.NumberOfKnots;
  137.       var knots = new KnotCollection();
  138.  
  139.       for (int i = 0; i < numKnots; i++)
  140.       {
  141.         knots.Add((cur1.KnotAt(i) + cur2.KnotAt(i)) / 2);
  142.       }
  143.  
  144.       // Получаем средние веса
  145.  
  146.       var numWeights = cur1.NumWeights;
  147.       var weights = new DoubleCollection();
  148.  
  149.       for (int i = 0; i < numWeights; i++)
  150.       {
  151.         knots.Add((cur1.GetWeightAt(i) + cur2.GetWeightAt(i)) / 2);
  152.       }
  153.  
  154.       // Создаем нашу новую GE-кривую на основе вычисленных данных
  155.  
  156.       return new NurbCurve3d(degree, knots, pts, weights, period);
  157.     }
  158.   }
  159. }

Мы видим, что она работает достаточно хорошо для идентичных сплайнов с некоторым расстоянием между ними:

 

Но если мы изменим контрольные точки на одном из сплайнов, то мы видим, что наш подход по-прежнему хорошо работает:

И даже может быть использован для создания того, что выглядит как изолинии между сплайнами:

 

Так что в ряде случаев этот способ имеет право на жизнь и может представлять интерес.

Источник: http://through-the-interface.typepad.com/through_the_interface/2013/07/finding-an-autocad-spline-between-two-others-using-net.html

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

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

Опубликовано 22.04.2015
Отредактировано 23.04.2015 в 10:58:24