Создание размеров труб
Сегодня на форуме поступил вопрос о создании размеров труб на 3D-виде. Вопрос не настолько прост, насколько кажется. Изначально проблема автора темы была в том, что он получал ошибку "invalid number of reference", получая reference-ы следующим образом:
- LocationCurve lp = pipe.Location as LocationCurve;
- ReferenceArray refArray = new ReferenceArray();
- Reference refPipe1 = lp.Curve.GetEndPointReference (0);
- Reference refPipe2 = lp.Curve.GetEndPointReference(1);
На самом деле, метод lp.Curve.GetEndPointReference возвращает в данном случае null
Давайте попробуем разобраться, как, всё-таки, создавать размеры для труб.
Основной код команды будет выглядеть следующим образом. Здесь всё просто: находим все трубы (метод FindPipesOnActiveView), и в транзакции в цикле для всех труб, к которым мы можем создать размеры (проверяем метод IsValidForDimensioning) запускаем метод CreatePipeDimension, ответственный за создание размеров. Все методы будут показаны ниже по тексту статьи, а в самом конце статьи Вы найдете полный код.
- public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
- {
- var uiapp = commandData.Application;
- var uidoc = uiapp.ActiveUIDocument;
- var doc = uidoc.Document;
- var pipes = FindPipesOnActiveView(uidoc);
- using (var transaction = new Transaction(doc, "Размеры труб на текущем виде"))
- {
- transaction.Start();
- foreach (var pipe in pipes.Where(x => IsValidForDimensioning(x, uidoc.ActiveGraphicalView)))
- CreatePipeDimension(pipe, uidoc.ActiveGraphicalView);
- transaction.Commit();
- }
- return Result.Succeeded;
- }
Метод поиска труб на текущем виде. Здесь опять же всё просто, небольшой нюанс - передаем в метод параметром UIDocument и используем ActiveGraphicalView вместо, скажем, Autodesk.Revit.DB.Document.ActiveView, который может быть, к примеру, ProjectBrowser:
- private static IEnumerable<Pipe> FindPipesOnActiveView(UIDocument uiDoc)
- {
- var collector = new FilteredElementCollector(uiDoc.Document, uiDoc.ActiveGraphicalView.Id);
- return collector
- .OfClass(typeof(Pipe))
- .OfType<Pipe>();
- }
Размеры на 3D-виде создаются в плоскостях, параллельных рабочей плоскости вида (view.SketchPlane), т.е. мы не сможем создать размер для труб, перпендикулярных рабочей плоскости. Для труб, у которых задан наклон, всё равно, будем создавать размер в этой плоскости. Соответственно, метод проверки:
- private static bool IsValidForDimensioning(Pipe pipe, View view)
- {
- var viewPlane = GetViewPlane(view);
- if (viewPlane == null)
- return false;
- var locationCurve = (LocationCurve) pipe.Location;
- var pipeDirection = locationCurve.Curve.ComputeDerivatives(0.5, true).BasisX;//это направление трубы
- return !viewPlane.Normal.CrossProduct(pipeDirection).IsAlmostEqualTo(XYZ.Zero, 1E-2); // проверяем, что нормаль плоскости не коллинеарная направлению трубы
- }
Если Вы не хотите задавать размеры для труб с уклоном замените последнюю строку на:
- return Math.Abs(viewPlane.Normal.DotProduct(pipeDirection)) < 1E-4;
Метод GetViewPlane:
- private static Plane GetViewPlane(View view)
- {
- return view
- .SketchPlane
- ?.GetPlane();
- }
Метод создания размеров труб CreatePipeDimension использует методы FindPipeEndPointsReference, где осуществляется поиск Reference-ов, необходимых для создания размера и GetPipeDimensionLine - размерная линия, они будут описаны ниже:
- private static void CreatePipeDimension(Pipe pipe, View view)
- {
- var references = FindPipeEndPointsReference(pipe, view);
- if (references == null)
- return;
- var dimensionLine = GetPipeDimensionLine(pipe, view);
- view.Document
- .Create
- .NewDimension(view, dimensionLine, references);
- }
Метод поиска Reference-ов для создания размеров.
- private static ReferenceArray FindPipeEndPointsReference(Pipe pipe, View view)
- {
- var document = pipe.Document;
- var options = new Options
- {
- View = view,
- ComputeReferences = true,
- IncludeNonVisibleObjects = true
- };
- var geometry = pipe.get_Geometry(options);
- var pipeLine = geometry
- .OfType<Curve>()
- .FirstOrDefault(x => IsPipeLine(document, x));
- if (pipeLine == null)
- return null;
- var references = new ReferenceArray();
- references.Append(pipeLine.GetEndPointReference(0));
- references.Append(pipeLine.GetEndPointReference(1));
- return references;
- }
- private static bool IsPipeLine(Document doc, Curve curve)
- {
- var graphicsStyle = (GraphicsStyle) doc.GetElement(curve.GraphicsStyleId);
- var category = new ElementId(BuiltInCategory.OST_PipeCurves);
- return graphicsStyle.GraphicsStyleCategory.Id == category;
- }
Фактически здесь мы получаем линию из геометрии объекта. На разных уровнях детализации мы можем получать разный набор геометрии, поэтому нам дополнительно нужна проверка графического типа линии.
Ну и последний фрагмент - получение самой размерной линии. Ставить размер по центру трубы как-то не слишком user-friendly, поэтому я реализовал алгоритм сдвига размерной линии на 10 мм в масштабе текущего вида
- private static Line GetPipeDimensionLine(Pipe pipe, View view)
- {
- const double distanceInMm = 10;
- var locationCurve = (LocationCurve) pipe.Location;
- var derivatives = locationCurve
- .Curve
- .ComputeDerivatives(0.5, true);
- var shiftDirection = derivatives.BasisX.CrossProduct(GetViewPlane(view).Normal).Normalize();
- var shift = UnitUtils.ConvertToInternalUnits(distanceInMm*view.Scale, DisplayUnitType.DUT_MILLIMETERS)*shiftDirection;
- var origin = derivatives.Origin + shift;
- return Line.CreateUnbound(origin, ProjectOnto(GetViewPlane(view), derivatives.BasisX));
- }
- public static XYZ ProjectOnto(Plane plane, XYZ p)
- {
- var d = SignedDistanceTo(plane, p);
- var q = p - d * plane.Normal;
- return q;
- }
- private static double SignedDistanceTo(Plane plane, XYZ p)
- {
- var v = p - plane.Origin;
- return plane.Normal.DotProduct(v);
- }
Полный код построения размеров труб:
- using System.Collections.Generic;
- using System.Linq;
- using Autodesk.Revit.Attributes;
- using Autodesk.Revit.DB;
- using Autodesk.Revit.DB.Plumbing;
- using Autodesk.Revit.UI;
- namespace CreatePipesDimensions
- {
- [Transaction(TransactionMode.Manual)]
- public class CreatePipeDimensionsCommand : IExternalCommand
- {
- public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
- {
- var uiapp = commandData.Application;
- var uidoc = uiapp.ActiveUIDocument;
- var doc = uidoc.Document;
- var pipes = FindPipesOnActiveView(uidoc);
- using (var transaction = new Transaction(doc, "Размеры труб на текущем виде"))
- {
- transaction.Start();
- foreach (var pipe in pipes.Where(x => IsValidForDimensioning(x, uidoc.ActiveGraphicalView)))
- CreatePipeDimension(pipe, uidoc.ActiveGraphicalView);
- transaction.Commit();
- }
- return Result.Succeeded;
- }
- private static IEnumerable<Pipe> FindPipesOnActiveView(UIDocument uiDoc)
- {
- var collector = new FilteredElementCollector(uiDoc.Document, uiDoc.ActiveGraphicalView.Id);
- return collector
- .OfClass(typeof(Pipe))
- .OfType<Pipe>();
- }
- private static bool IsValidForDimensioning(Pipe pipe, View view)
- {
- var viewPlane = GetViewPlane(view);
- if (viewPlane == null)
- return false;
- var locationCurve = (LocationCurve) pipe.Location;
- var pipeDirection = locationCurve.Curve.ComputeDerivatives(0.5, true).BasisX;
- return !viewPlane.Normal.CrossProduct(pipeDirection).IsAlmostEqualTo(XYZ.Zero, 1E-2);
- }
- private static void CreatePipeDimension(Pipe pipe, View view)
- {
- var references = FindPipeEndPointsReference(pipe, view);
- if (references == null)
- return;
- var dimensionLine = GetPipeDimensionLine(pipe, view);
- view.Document
- .Create
- .NewDimension(view, dimensionLine, references);
- }
- private static ReferenceArray FindPipeEndPointsReference(Pipe pipe, View view)
- {
- var document = pipe.Document;
- var options = new Options
- {
- View = view,
- ComputeReferences = true,
- IncludeNonVisibleObjects = true
- };
- var geometry = pipe.get_Geometry(options);
- var pipeLine = geometry
- .OfType<Curve>()
- .FirstOrDefault(x => IsPipeLine(document, x));
- if (pipeLine == null)
- return null;
- var references = new ReferenceArray();
- references.Append(pipeLine.GetEndPointReference(0));
- references.Append(pipeLine.GetEndPointReference(1));
- return references;
- }
- private static bool IsPipeLine(Document doc, Curve curve)
- {
- var graphicsStyle = (GraphicsStyle) doc.GetElement(curve.GraphicsStyleId);
- var category = new ElementId(BuiltInCategory.OST_PipeCurves);
- return graphicsStyle.GraphicsStyleCategory.Id == category;
- }
- private static Line GetPipeDimensionLine(Pipe pipe, View view)
- {
- const double distanceInMm = 10;
- var locationCurve = (LocationCurve) pipe.Location;
- var derivatives = locationCurve
- .Curve
- .ComputeDerivatives(0.5, true);
- var shiftDirection = derivatives.BasisX.CrossProduct(GetViewPlane(view).Normal).Normalize();
- var shift = UnitUtils.ConvertToInternalUnits(distanceInMm*view.Scale, DisplayUnitType.DUT_MILLIMETERS)*shiftDirection;
- var origin = derivatives.Origin + shift;
- return Line.CreateUnbound(origin, ProjectOnto(GetViewPlane(view), derivatives.BasisX));
- }
- public static XYZ ProjectOnto(Plane plane, XYZ p)
- {
- var d = SignedDistanceTo(plane, p);
- var q = p - d * plane.Normal;
- return q;
- }
- private static double SignedDistanceTo(Plane plane, XYZ p)
- {
- var v = p - plane.Origin;
- return plane.Normal.DotProduct(v);
- }
- private static Plane GetViewPlane(View view)
- {
- return view
- .SketchPlane
- ?.GetPlane();
- }
- }
- }
Обсуждение: http://adn-cis.org/forum/index.php?topic=
Опубликовано 30.07.2018