Создание геометрии форм перехода (loft geometry) в Revit API
Сегодня мы немного поговорим о создании геометрических форм с использованием Revit API, а конкретнее о формах перехода (loft geometry), которые можно создать в редакторе семейств с помощью команд "Переход" и "Переход по траектории". Мы создадим геометрию путем перемещения выбранного профиля вдоль кривой, заданной функцией y=sin(x).
Для начала зададим профиль, лежащий в плоскости Y0Z. Метод создает CurveLoop, формирующий наш профиль с возможностью его масштабирования:
- private static CurveLoop CreateProfile(double scale)
- {
- var a = new XYZ(0, -1, -1);
- var b = new XYZ(0, 1, -1);
- var c = new XYZ(0, 1, 0);
- var d = new XYZ(0, 0, 1);
- var e = new XYZ(0, -1, 1);
- var curvesList = new List<Curve>
- {
- Line.CreateBound(scale*a, scale*b),
- Line.CreateBound(scale*b, scale*c),
- Line.CreateBound(scale*c, scale*d),
- Line.CreateBound(scale*d, scale*e),
- Line.CreateBound(scale*e, scale*a)
- };
- return CurveLoop.Create(curvesList);
- }
Наш профиль выглядит вот так:
Чтобы создать геометрию перехода по траектории синусоиды, нам нужно создать перечень профилей вдоль кривой:
На рисунке зеленым показана плоскость профиля, красным - её нормаль. Для решения задачи, на проще всего будет работать с аффинными преобразованиями, которые в Revit API инкапсулирует класс Transform. Требуемое нам преобразование T будет представлять собой произведение T=Tm*Tr, где Tm - перемещение в заданную точку, а Tr - поворот на заданный угол.
Пусть наша синусоида располагается в плоскости X0Y. Зададим функцию, возвращающую требуемый нам T для заданного X. Очевидно, что перемещение в нужную нам точку Tm будет переносом в точку (x, sin(x), 0). С поворотом немного сложнее. Фактически, нам нужно знать направление нормали профиля, которое совпадает с направлением движения по траектории, а направление движения по траектории задается производной функции. Тогда искомый угол a=cos(x). Итого метод для получения нужного нам преобразования будет выглядеть следующим образом:
- private static Transform CreateSinusTransform(double point)
- {
- var translation = Transform.CreateTranslation(new XYZ(point, Math.Sin(point), 0));
- var rotation = Transform.CreateRotation(XYZ.BasisZ, Math.Cos(point));
- return translation*rotation;
- }
Тогда мы сможем получить перечень профилей следующим образом:
- const double scale = 0.2;
- const double length = 4.0*Math.PI;
- const int steps = 16;
- var profile = CreateProfile(scale);
- var profiles = Enumerable
- .Range(0, steps + 1)
- .Select(x => x*length/steps)
- .Select(CreateSinusTransform)
- .Select(x => profile.CreateTransformed(x))
- .ToList();
Здесь CreateTransformed - это Extension-метод, его реализация достаточно тривиальна:
- public static class CurveLoopExtensions
- {
- public static CurveLoop CreateTransformed(this CurveLoop curveLoop, Transform transform)
- {
- var curves = curveLoop
- .Select(x => x.CreateTransformed(transform))
- .ToList();
- return CurveLoop.Create(curves);
- }
- }
В 3D это будет выглядеть следующим образом (где красным опять же показана нормаль профиля):
Теперь, имея перечень профилей, мы можем создать нужную нам геометрию (Solid):
- var solid = GeometryCreationUtilities.CreateLoftGeometry(profiles,
- new SolidOptions(ElementId.InvalidElementId, ElementId.InvalidElementId));
При работе в редакторе семейств, можно создать FreeFormElement, но я делаю это в проекте, а для создания произвольной геометрии в проекте предназначены механизмы DirectShape:
- var directShape = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
- directShape.AppendShape(new GeometryObject[] {solid});
Результат работы:
Полный код доступен по ссылке: https://github.com/CADBIMDeveloper/Just-for-fun/tree/master/src/CreateLoftWithSinusProfile
Обсуждение: http://adn-cis.org/forum/index.php?topic=
Опубликовано 30.05.2018