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

30/07/2018

Создание размеров труб

Сегодня на форуме поступил вопрос о создании размеров труб на 3D-виде. Вопрос не настолько прост, насколько кажется. Изначально проблема автора темы была в том, что он получал ошибку "invalid number of reference", получая reference-ы следующим образом:

Код - C#: [Выделить]
  1.  
  2. LocationCurve lp = pipe.Location as LocationCurve;
  3. ReferenceArray refArray = new ReferenceArray();
  4. Reference refPipe1 = lp.Curve.GetEndPointReference (0);
  5. Reference refPipe2 = lp.Curve.GetEndPointReference(1);
  6.  

На самом деле, метод lp.Curve.GetEndPointReference возвращает в данном случае null

Давайте попробуем разобраться, как, всё-таки, создавать размеры для труб.

Основной код команды будет выглядеть следующим образом. Здесь всё просто: находим все трубы (метод FindPipesOnActiveView), и в транзакции в цикле для всех труб, к которым мы можем создать размеры (проверяем метод IsValidForDimensioning) запускаем метод CreatePipeDimension, ответственный за создание размеров. Все методы будут показаны ниже по тексту статьи, а в самом конце статьи Вы найдете полный код.

Код - C#: [Выделить]
  1.  
  2. public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
  3. {
  4.                 var uiapp = commandData.Application;
  5.                 var uidoc = uiapp.ActiveUIDocument;
  6.                 var doc = uidoc.Document;
  7.  
  8.                 var pipes = FindPipesOnActiveView(uidoc);
  9.  
  10.                 using (var transaction = new Transaction(doc, "Размеры труб на текущем виде"))
  11.                 {
  12.                                transaction.Start();
  13.  
  14.                                foreach (var pipe in pipes.Where(x => IsValidForDimensioning(x, uidoc.ActiveGraphicalView)))
  15.                                                CreatePipeDimension(pipe, uidoc.ActiveGraphicalView);
  16.  
  17.                                transaction.Commit();
  18.                 }
  19.  
  20.                 return Result.Succeeded;
  21. }
  22.  

Метод поиска труб на текущем виде. Здесь опять же всё просто, небольшой нюанс - передаем в метод параметром UIDocument и используем ActiveGraphicalView вместо, скажем, Autodesk.Revit.DB.Document.ActiveView, который может быть, к примеру, ProjectBrowser:

Код - C#: [Выделить]
  1.  
  2. private static IEnumerable<Pipe> FindPipesOnActiveView(UIDocument uiDoc)
  3. {
  4.                 var collector = new FilteredElementCollector(uiDoc.Document, uiDoc.ActiveGraphicalView.Id);
  5.  
  6.                 return collector
  7.                                .OfClass(typeof(Pipe))
  8.                                .OfType<Pipe>();
  9. }
  10.  

Размеры на 3D-виде создаются в плоскостях, параллельных рабочей плоскости вида (view.SketchPlane), т.е. мы не сможем создать размер для труб, перпендикулярных рабочей плоскости. Для труб, у которых задан наклон, всё равно, будем создавать размер в этой плоскости. Соответственно, метод проверки:

Код - C#: [Выделить]
  1.  
  2. private static bool IsValidForDimensioning(Pipe pipe, View view)
  3. {
  4.                 var viewPlane = GetViewPlane(view);
  5.  
  6.                 if (viewPlane == null)
  7.                                return false;
  8.  
  9.                 var locationCurve = (LocationCurve) pipe.Location;
  10.  
  11.                 var pipeDirection = locationCurve.Curve.ComputeDerivatives(0.5, true).BasisX;//это направление трубы
  12.                
  13.                 return !viewPlane.Normal.CrossProduct(pipeDirection).IsAlmostEqualTo(XYZ.Zero, 1E-2); // проверяем, что нормаль плоскости не коллинеарная направлению трубы
  14. }
  15.  

Если Вы не хотите задавать размеры для труб с уклоном замените последнюю строку на:

Код - C#: [Выделить]
  1.  
  2. return Math.Abs(viewPlane.Normal.DotProduct(pipeDirection)) < 1E-4;
  3.  

Метод GetViewPlane:

Код - C#: [Выделить]
  1.  
  2. private static Plane GetViewPlane(View view)
  3. {
  4.                 return view
  5.                                .SketchPlane
  6.                                ?.GetPlane();
  7. }

Метод создания размеров труб CreatePipeDimension использует методы FindPipeEndPointsReference, где осуществляется поиск Reference-ов, необходимых для создания размера и GetPipeDimensionLine - размерная линия, они будут описаны ниже:

Код - C#: [Выделить]
  1.  
  2. private static void CreatePipeDimension(Pipe pipe, View view)
  3. {
  4.                 var references = FindPipeEndPointsReference(pipe, view);
  5.  
  6.                 if (references == null)
  7.                                return;
  8.  
  9.                 var dimensionLine = GetPipeDimensionLine(pipe, view);
  10.  
  11.                 view.Document
  12.                                .Create
  13.                                .NewDimension(view, dimensionLine, references);
  14. }

Метод поиска Reference-ов для создания размеров.

Код - C#: [Выделить]
  1.  
  2. private static ReferenceArray FindPipeEndPointsReference(Pipe pipe, View view)
  3. {
  4.                 var document = pipe.Document;
  5.  
  6.                 var options = new Options
  7.                                {
  8.                                                View = view,
  9.                                                ComputeReferences = true,
  10.                                                IncludeNonVisibleObjects = true
  11.                                };
  12.  
  13.                 var geometry = pipe.get_Geometry(options);
  14.  
  15.                 var pipeLine = geometry
  16.                                .OfType<Curve>()
  17.                                .FirstOrDefault(x => IsPipeLine(document, x));
  18.  
  19.                 if (pipeLine == null)
  20.                                return null;
  21.  
  22.                 var references = new ReferenceArray();
  23.  
  24.                 references.Append(pipeLine.GetEndPointReference(0));
  25.                 references.Append(pipeLine.GetEndPointReference(1));
  26.  
  27.                 return references;
  28. }
  29.  
  30. private static bool IsPipeLine(Document doc, Curve curve)
  31. {
  32.                 var graphicsStyle = (GraphicsStyle) doc.GetElement(curve.GraphicsStyleId);
  33.  
  34.                 var category = new ElementId(BuiltInCategory.OST_PipeCurves);
  35.  
  36.                 return graphicsStyle.GraphicsStyleCategory.Id == category;
  37. }
  38.  

Фактически здесь мы получаем линию из геометрии объекта. На разных уровнях детализации мы можем получать разный набор геометрии, поэтому нам дополнительно нужна проверка графического типа линии.

Ну и последний фрагмент - получение самой размерной линии. Ставить размер по центру трубы как-то не слишком user-friendly, поэтому я реализовал алгоритм сдвига размерной линии на 10 мм в масштабе текущего вида

Код - C#: [Выделить]
  1.  
  2. private static Line GetPipeDimensionLine(Pipe pipe, View view)
  3. {
  4.                 const double distanceInMm = 10;
  5.  
  6.                 var locationCurve = (LocationCurve) pipe.Location;
  7.  
  8.                 var derivatives = locationCurve
  9.                                .Curve
  10.                                .ComputeDerivatives(0.5, true);
  11.  
  12.                 var shiftDirection = derivatives.BasisX.CrossProduct(GetViewPlane(view).Normal).Normalize();
  13.  
  14.                 var shift = UnitUtils.ConvertToInternalUnits(distanceInMm*view.Scale, DisplayUnitType.DUT_MILLIMETERS)*shiftDirection;
  15.                
  16.                 var origin = derivatives.Origin + shift;
  17.  
  18.                 return Line.CreateUnbound(origin, ProjectOnto(GetViewPlane(view), derivatives.BasisX));
  19. }
  20.  
  21. public static XYZ ProjectOnto(Plane plane, XYZ p)
  22. {
  23.                 var d = SignedDistanceTo(plane, p);
  24.  
  25.                 var q = p - d * plane.Normal;
  26.                
  27.                 return q;
  28. }
  29.  
  30. private static double SignedDistanceTo(Plane plane, XYZ p)
  31. {
  32.                 var v = p - plane.Origin;
  33.  
  34.                 return plane.Normal.DotProduct(v);
  35. }
  36.  

 

Полный код построения размеров труб:

 

Код - C#: [Выделить]
  1.  
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Autodesk.Revit.Attributes;
  5. using Autodesk.Revit.DB;
  6. using Autodesk.Revit.DB.Plumbing;
  7. using Autodesk.Revit.UI;
  8.  
  9. namespace CreatePipesDimensions
  10. {
  11.     [Transaction(TransactionMode.Manual)]
  12.     public class CreatePipeDimensionsCommand : IExternalCommand
  13.     {
  14.         public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
  15.         {
  16.             var uiapp = commandData.Application;
  17.             var uidoc = uiapp.ActiveUIDocument;
  18.             var doc = uidoc.Document;
  19.  
  20.             var pipes = FindPipesOnActiveView(uidoc);
  21.  
  22.             using (var transaction = new Transaction(doc, "Размеры труб на текущем виде"))
  23.             {
  24.                 transaction.Start();
  25.  
  26.                 foreach (var pipe in pipes.Where(x => IsValidForDimensioning(x, uidoc.ActiveGraphicalView)))
  27.                     CreatePipeDimension(pipe, uidoc.ActiveGraphicalView);
  28.  
  29.                 transaction.Commit();
  30.             }
  31.  
  32.             return Result.Succeeded;
  33.         }
  34.  
  35.         private static IEnumerable<Pipe> FindPipesOnActiveView(UIDocument uiDoc)
  36.         {
  37.             var collector = new FilteredElementCollector(uiDoc.Document, uiDoc.ActiveGraphicalView.Id);
  38.  
  39.             return collector
  40.                 .OfClass(typeof(Pipe))
  41.                 .OfType<Pipe>();
  42.         }
  43.  
  44.         private static bool IsValidForDimensioning(Pipe pipe, View view)
  45.         {
  46.             var viewPlane = GetViewPlane(view);
  47.  
  48.             if (viewPlane == null)
  49.                 return false;
  50.  
  51.             var locationCurve = (LocationCurve) pipe.Location;
  52.  
  53.             var pipeDirection = locationCurve.Curve.ComputeDerivatives(0.5, true).BasisX;
  54.            
  55.             return !viewPlane.Normal.CrossProduct(pipeDirection).IsAlmostEqualTo(XYZ.Zero, 1E-2);
  56.         }
  57.        
  58.         private static void CreatePipeDimension(Pipe pipe, View view)
  59.         {
  60.             var references = FindPipeEndPointsReference(pipe, view);
  61.  
  62.             if (references == null)
  63.                 return;
  64.  
  65.             var dimensionLine = GetPipeDimensionLine(pipe, view);
  66.  
  67.             view.Document
  68.                 .Create
  69.                 .NewDimension(view, dimensionLine, references);
  70.         }
  71.  
  72.         private static ReferenceArray FindPipeEndPointsReference(Pipe pipe, View view)
  73.         {
  74.             var document = pipe.Document;
  75.  
  76.             var options = new Options
  77.                 {
  78.                     View = view,
  79.                     ComputeReferences = true,
  80.                     IncludeNonVisibleObjects = true
  81.                 };
  82.  
  83.             var geometry = pipe.get_Geometry(options);
  84.  
  85.             var pipeLine = geometry
  86.                 .OfType<Curve>()
  87.                 .FirstOrDefault(x => IsPipeLine(document, x));
  88.  
  89.             if (pipeLine == null)
  90.                 return null;
  91.  
  92.             var references = new ReferenceArray();
  93.  
  94.             references.Append(pipeLine.GetEndPointReference(0));
  95.             references.Append(pipeLine.GetEndPointReference(1));
  96.  
  97.             return references;
  98.         }
  99.  
  100.         private static bool IsPipeLine(Document doc, Curve curve)
  101.         {
  102.             var graphicsStyle = (GraphicsStyle) doc.GetElement(curve.GraphicsStyleId);
  103.  
  104.             var category = new ElementId(BuiltInCategory.OST_PipeCurves);
  105.  
  106.             return graphicsStyle.GraphicsStyleCategory.Id == category;
  107.         }
  108.  
  109.         private static Line GetPipeDimensionLine(Pipe pipe, View view)
  110.         {
  111.             const double distanceInMm = 10;
  112.  
  113.             var locationCurve = (LocationCurve) pipe.Location;
  114.  
  115.             var derivatives = locationCurve
  116.                 .Curve
  117.                 .ComputeDerivatives(0.5, true);
  118.  
  119.             var shiftDirection = derivatives.BasisX.CrossProduct(GetViewPlane(view).Normal).Normalize();
  120.  
  121.             var shift = UnitUtils.ConvertToInternalUnits(distanceInMm*view.Scale, DisplayUnitType.DUT_MILLIMETERS)*shiftDirection;
  122.            
  123.             var origin = derivatives.Origin + shift;
  124.  
  125.             return Line.CreateUnbound(origin, ProjectOnto(GetViewPlane(view), derivatives.BasisX));
  126.         }
  127.  
  128.         public static XYZ ProjectOnto(Plane plane, XYZ p)
  129.         {
  130.             var d = SignedDistanceTo(plane, p);
  131.  
  132.             var q = p - d * plane.Normal;
  133.            
  134.             return q;
  135.         }
  136.  
  137.         private static double SignedDistanceTo(Plane plane, XYZ p)
  138.         {
  139.             var v = p - plane.Origin;
  140.  
  141.             return plane.Normal.DotProduct(v);
  142.         }
  143.  
  144.         private static Plane GetViewPlane(View view)
  145.         {
  146.             return view
  147.                 .SketchPlane
  148.                 ?.GetPlane();
  149.         }
  150.     }
  151. }
  152.  

 

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

Опубликовано 30.07.2018