Перемещение соединений воздуховодов
Я всегда рад иметь дело с реальными проблемами в MEP, именно поэтому эта проблем меня зацепила и заставила меня сесть и написать немного кода для решения этой проблемы.
Вопрос: Как можно программно переместить соединение двух воздуховодов?
Я не хочу удалять их, а затем создавать заново, так как другие концы этих воздуховодов соединены как положено.
Ответ: Если говорить в общем, то когда вы работаете с программным изменениями систем воздуховода, у вас есть два способа как достичь желаемого результата:
- Определить воздуховоды и дать Revit’у самостоятельно вставить требуемые фитинги, когда вы скажете, что эти воздуховоды необходимо соединить.
- Определить фитинги, затем позволить Revit’у самостоятельно создать воздуховоды, когда вы скажете, что эти фитинги нужно соединить.
Ваша же проблема немного другая. У вас уже есть и фитинги и сами воздуховоды и вам необходимо переместить все компоненты.
Я поразмыслил немного и написал надстройку, которую назвал MoveDuctJoin. Надстройка содержит две отдельные команды, так как они отчетливо демонстрируют проблему перемещения:
- CmdDisconnected – эта команда просто перемещает один соединитель воздуховода, поэтому связь с соседним воздуховодом разрывается.
- CmdReconnect – эта команда перемещает фитинг, соединяющий два воздуховода. В этом случае вся система остается неразрывной.
Использование команд
Пользователю необходимо выполнить три действия, чтобы выполнить любую из этих команд:
- Непосредственно запустить команду
- Выбрать воздуховод, соединенный с фитингом, который необходимо переместить
- Выбрать точку, в которую следует переместить воздуховод
Продемонстрируем результат на картинках.
Исходные воздуховоды с соединителями выглядят так:
Как я сказал, команда CmdDisconnect просто перемещает соединитель выбранного воздуховода, разрывая с ним связь.
CmdReconnect же перемещает сам фитинг, сохраняя при этом связь с воздуховодами.
А сейчас пришло время взглянуть на реализацию этих двух команд, а также показать сходства и отличия этих двух подходов.
CmdDisconnect – перемещение соединителя
Различия между двумя командами очень небольшие. Поэтому почти весь код дублируется.
Алгоритм команды CmdDisconnect следующий:
- Запросить пользователя выбрать воздуховод с соединителем, который нужно переместить
- Попросить пользователя выбрать точку на этом воздуховоде, куда его следует передвинуть
- Найти ближайший к первой выбранной точке соединитель
- Спроецировать вторую выбранную точку на центр воздуховода
- Переместить соединитель в спроецированную точку
Первые 4 шага повторяются и для второй команды без каких-либо изменений.
Только на последнем шаге делаются изменения в модели.
- /// <summary>
- /// External command to move a duct connector
- /// away from its original position along the
- /// duct centre line, disconnecting from the
- /// neighbour element.
- /// </summary>
- [Transaction( TransactionMode.Manual )]
- public class CmdDisconnect : IExternalCommand
- {
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements )
- {
- UIApplication uiapp = commandData.Application;
- UIDocument uidoc = uiapp.ActiveUIDocument;
- Document doc = uidoc.Document;
- Selection sel = uidoc.Selection;
- Duct duct = null;
- XYZ pFrom = null;
- XYZ pTo = null;
- try
- {
- Reference r = sel.PickObject(
- ObjectType.Element,
- new DuctSelectionFilter(),
- "Please pick a duct at the "
- + "connection to move." );
- duct = doc.GetElement( r.ElementId ) as Duct;
- pFrom = r.GlobalPoint;
- r = sel.PickObject(
- ObjectType.Element,
- new DuctSelectionFilter(),
- "Please pick a target point on the "
- + "duct to move the connection to." );
- pTo = r.GlobalPoint;
- }
- catch( Autodesk.Revit.Exceptions
- .OperationCanceledException )
- {
- return Result.Cancelled;
- }
- // Determine connector closest to picked point
- ConnectorSet connectors
- = duct.ConnectorManager.Connectors;
- Connector con = null;
- double d, dmin = double.MaxValue;
- foreach( Connector c in connectors )
- {
- d = pFrom.DistanceTo( c.Origin );
- if( d < dmin )
- {
- dmin = d;
- con = c;
- }
- }
- // Determine target point to move it to
- Transform cs = con.CoordinateSystem;
- Debug.Assert(
- con.Origin.IsAlmostEqualTo( cs.Origin ),
- "expected same origin" );
- Line line = Line.CreateUnbound( cs.Origin, cs.BasisZ );
- IntersectionResult ir = line.Project( pTo );
- pTo = ir.XYZPoint;
- Debug.Assert( line.Distance( pTo ) < 1e-9,
- "expected projected point on line" );
- // Modify document within a transaction
- using( Transaction tx = new Transaction( doc ) )
- {
- tx.Start( "Move Duct Connector" );
- con.Origin = pTo;
- tx.Commit();
- }
- return Result.Succeeded;
- }
- }
CmdReconnect – перемещение всех фиттингов
Алгоритм команды CmdReconnect повторяет первые 4 шага предыдущей команды
- Запросить пользователя выбрать воздуховод с соединителем, который нужно переместить
- Попросить пользователя выбрать точку на этом воздуховоде, куда его следует передвинуть
- Найти ближайший к первой выбранной точке соединитель
- Спроецировать вторую выбранную точку на центр воздуховода
- Определить вектор перемещения из исходной точки в точку перемещения
- Определить соседний фитинги
- Переместить фитинг в выбранную точку
Соседний фитинг мы определяем с помощью такого кода, который я взял из примера, поставляемого с Revit SDK
- /// <summary>
- /// Return the connector
- /// connected to the one given.
- /// </summary>
- static Connector GetConnectedConnector(
- Connector con )
- {
- Connector neighbour = null;
- int ownerId = con.Owner.Id.IntegerValue;
- ConnectorSet refs = con.AllRefs;
- foreach( Connector c in refs )
- {
- // Ignore non-End connectors and
- // connectors on the same element
- if( c.ConnectorType == ConnectorType.End
- && !ownerId.Equals(
- c.Owner.Id.IntegerValue ) )
- {
- neighbour = c;
- break;
- }
- }
- return neighbour;
- }
Остальная часть команды выглядит вот так:
- /// <summary>
- /// External command to move a duct connector
- /// away from its original position along the
- /// duct centre line, disconnecting from the
- /// neighbour element.
- /// </summary>
- [Transaction( TransactionMode.Manual )]
- public class CmdReconnect : IExternalCommand
- {
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements )
- {
- UIApplication uiapp = commandData.Application;
- UIDocument uidoc = uiapp.ActiveUIDocument;
- Document doc = uidoc.Document;
- Selection sel = uidoc.Selection;
- Duct duct = null;
- XYZ pFrom = null;
- XYZ pTo = null;
- try
- {
- Reference r = sel.PickObject(
- ObjectType.Element,
- new DuctSelectionFilter(),
- "Please pick a duct at the "
- + "connection to move." );
- duct = doc.GetElement( r.ElementId ) as Duct;
- pFrom = r.GlobalPoint;
- r = sel.PickObject(
- ObjectType.Element,
- new DuctSelectionFilter(),
- "Please pick a target point on the "
- + "duct to move the connection to." );
- pTo = r.GlobalPoint;
- }
- catch( Autodesk.Revit.Exceptions
- .OperationCanceledException )
- {
- return Result.Cancelled;
- }
- // Determine connector closest to picked point
- ConnectorSet connectors
- = duct.ConnectorManager.Connectors;
- Connector con = null;
- double d, dmin = double.MaxValue;
- foreach( Connector c in connectors )
- {
- d = pFrom.DistanceTo( c.Origin );
- if( d < dmin )
- {
- dmin = d;
- con = c;
- }
- }
- // Determine target point to move it to
- Transform cs = con.CoordinateSystem;
- Debug.Assert(
- con.Origin.IsAlmostEqualTo( cs.Origin ),
- "expected same origin" );
- Line line = Line.CreateUnbound( cs.Origin, cs.BasisZ );
- IntersectionResult ir = line.Project( pTo );
- pTo = ir.XYZPoint;
- Debug.Assert( line.Distance( pTo ) < 1e-9,
- "expected projected point on line" );
- // Determine translation vector
- XYZ v = pTo - pFrom;
- // Determine neighbouring fitting connector
- Connector neighbour
- = GetConnectedConnector( con );
- // Modify document within a transaction
- using( Transaction tx = new Transaction( doc ) )
- {
- tx.Start( "Move Fitting" );
- ElementTransformUtils.MoveElement(
- doc, neighbour.Owner.Id, v );
- tx.Commit();
- }
- return Result.Succeeded;
- }
- }
Обратите внимание на сходства и различия двух команд и какой они в итоге дают эффект.
Исходный код с проектом для Visual Studio можно скачать на GitHub.
Обсуждение: http://adn-cis.org/forum/index.php?topic=269
Опубликовано 18.10.2013