Как найти сплайн в AutoCAD между двумя другими сплайнами в .NET
Я получил вопрос в комментариях к блогу:
Как найти «центральную» линию NurbCurve3dмежду двумя другими линиями NurbCurve3d?
Я интерпретирую это следующим образом: с учетом двух объектов NurbCurve3d, создать NurbCurve3d, который находится точно между ними. NurbCurve3d являются AcGe-классом - это означает, что это объект неграфический - так что я решил работать со сплайнами (Spline), поскольку они тесно связаны с классом NurbCurve3d (есть удобные методы для преобразования между этими двумя классами). Вот еще для интересующихся некоторая информация о NURBS.
Я также решил ограничится одним довольно конкретном случаем: сплайны (и эквивалентные им NurbCurve3d) имеют точно такую же степень и период, а также одинаковое количество контрольных пунктов, узлов и весов. Это резко упрощает задачу. Можно было конечно при достаточных усилиях и времени найти способ параметризации двух кривых и создать точки между кривыми и построить кривую по этим точкам. Я решил не заниматься этой более общей проблемой. По крайней мере, не в этой статье.
Так что я сделал на самом деле очень просто: между двумя же подобными объектами NurbCurve3d, мы получаем три коллекции значений - контрольных точек, узлов и весов - и создаем три новые коллекции для новой кривой, которые содержат "средние "значения данных из двух исходных объектов. Я далек от того чтобы быть экспертом NURBS, но я считаю, что такой подход вполне разумен для этой конкретной категории Spline (по крайней мере, кажется, это работает достаточно хорошо).
Имейте в виду, что этот код не будет работать со «сглаженными» (fit) сплайнами. По крайней мере, я не ожидаю, что это будет работать. :-)
Вот код на C#:
- using Autodesk.AutoCAD.ApplicationServices;
- using Autodesk.AutoCAD.DatabaseServices;
- using Autodesk.AutoCAD.EditorInput;
- using Autodesk.AutoCAD.Geometry;
- using Autodesk.AutoCAD.Runtime;
- namespace NurbCurveStuff
- {
- public class Commands
- {
- [CommandMethod("SBS")]
- static public void SplineBetweenSplines()
- {
- var doc = Application.DocumentManager.MdiActiveDocument;
- var db = doc.Database;
- var ed = doc.Editor;
- // Выбираем наши сплайны
- var peo = new PromptEntityOptions("\nВыберите первый сплайн ");
- peo.SetRejectMessage("\nДолжен быть сплайн.");
- peo.AddAllowedClass(typeof(Spline), true);
- var per = ed.GetEntity(peo);
- if (per.Status != PromptStatus.OK)
- return;
- var spId1 = per.ObjectId;
- peo.Message = "\nВыберите второй сплайн ";
- per = ed.GetEntity(peo);
- if (per.Status != PromptStatus.OK)
- return;
- var spId2 = per.ObjectId;
- // Стартуем транзакцию
- using (var tr = doc.TransactionManager.StartTransaction())
- {
- // Открываем сплайны
- var sp1 =
- tr.GetObject(spId1, OpenMode.ForRead) as Spline;
- var sp2 =
- tr.GetObject(spId2, OpenMode.ForRead) as Spline;
- if (sp1 != null && sp2 != null)
- {
- try
- {
- // Получаем GE-эквиваленты сплайнов
- var cur1 = sp1.GetGeCurve() as NurbCurve3d;
- var cur2 = sp2.GetGeCurve() as NurbCurve3d;
- if (cur1 != null && cur2 != null)
- {
- // Находим средний сплайн между двумя
- var cur3 = MiddleCurve(cur1, cur2);
- if (cur3 != null)
- {
- // Создаем сплайн из средней кривой
- var sp = Curve.CreateFromGeCurve(cur3);
- if (sp != null)
- {
- // Добавляем сплайн в базу чертежа
- var btr =
- (BlockTableRecord)tr.GetObject(
- db.CurrentSpaceId,
- OpenMode.ForWrite
- );
- btr.AppendEntity(sp);
- tr.AddNewlyCreatedDBObject(sp, true);
- }
- }
- }
- tr.Commit();
- }
- catch (Autodesk.AutoCAD.Runtime.Exception ex)
- {
- ed.WriteMessage(
- "\nИсключение: {0}", ex.Message
- );
- }
- }
- }
- }
- private static NurbCurve3d MiddleCurve(
- NurbCurve3d cur1, NurbCurve3d cur2
- )
- {
- // Возвращаем NurbCurve3d, которая лежит между переданными
- // Начнем с получения периода обеих кривых
- double per1, per2;
- bool ip1 = cur1.IsPeriodic(out per1);
- bool ip2 = cur2.IsPeriodic(out per2);
- // Убеждаемся, что кривые имеют одинаковый период, степень,
- // число контрольных точек, узлов и весов
- if (
- cur1.Degree != cur2.Degree || ip1 != ip2 || per1 != per2 ||
- cur1.NumberOfControlPoints != cur2.NumberOfControlPoints ||
- cur1.NumberOfKnots != cur2.NumberOfKnots ||
- cur1.NumWeights != cur2.NumWeights
- )
- return null;
- var degree = cur1.Degree;
- var period = ip1;
- // Получаем средние контрольные точки
- int numPoints = cur1.NumberOfControlPoints;
- var pts = new Point3dCollection();
- for (int i = 0; i < numPoints; i++)
- {
- var pt1 = cur1.ControlPointAt(i);
- var pt2 = cur2.ControlPointAt(i);
- pts.Add(pt1 + ((pt2 - pt1) / 2));
- }
- // Получаем средние узлы
- var numKnots = cur1.NumberOfKnots;
- var knots = new KnotCollection();
- for (int i = 0; i < numKnots; i++)
- {
- knots.Add((cur1.KnotAt(i) + cur2.KnotAt(i)) / 2);
- }
- // Получаем средние веса
- var numWeights = cur1.NumWeights;
- var weights = new DoubleCollection();
- for (int i = 0; i < numWeights; i++)
- {
- knots.Add((cur1.GetWeightAt(i) + cur2.GetWeightAt(i)) / 2);
- }
- // Создаем нашу новую GE-кривую на основе вычисленных данных
- return new NurbCurve3d(degree, knots, pts, weights, period);
- }
- }
- }
Мы видим, что она работает достаточно хорошо для идентичных сплайнов с некоторым расстоянием между ними:
Но если мы изменим контрольные точки на одном из сплайнов, то мы видим, что наш подход по-прежнему хорошо работает:
И даже может быть использован для создания того, что выглядит как изолинии между сплайнами:
Так что в ряде случаев этот способ имеет право на жизнь и может представлять интерес.
Обсуждение: http://adn-cis.org/forum/index.php?topic=2652
Опубликовано 22.04.2015Отредактировано 23.04.2015 в 10:58:24