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

18/10/2013

Перемещение соединений воздуховодов

Я всегда рад иметь дело с реальными проблемами в MEP, именно поэтому эта проблем меня зацепила и заставила меня сесть и написать немного кода для решения этой проблемы.

Вопрос: Как можно программно переместить соединение двух воздуховодов?

Я не хочу удалять их, а затем создавать заново, так как другие концы этих воздуховодов соединены как положено.

Ответ: Если говорить в общем, то когда вы работаете с программным изменениями систем воздуховода, у вас есть два способа как достичь желаемого результата:

  • Определить воздуховоды и дать Revit’у самостоятельно вставить требуемые фитинги, когда вы скажете, что эти воздуховоды необходимо соединить.
  • Определить фитинги, затем позволить Revit’у самостоятельно создать воздуховоды, когда вы скажете, что эти фитинги нужно соединить.

Ваша же проблема немного другая. У вас уже есть и фитинги и сами воздуховоды и вам необходимо переместить все компоненты.

Я поразмыслил немного и написал надстройку, которую назвал MoveDuctJoin. Надстройка содержит две отдельные команды, так как они отчетливо демонстрируют проблему перемещения:

  • CmdDisconnected – эта команда просто перемещает один соединитель воздуховода, поэтому связь с соседним воздуховодом разрывается.
  • CmdReconnect – эта команда перемещает фитинг, соединяющий два воздуховода. В этом случае вся система остается неразрывной.

Использование команд

Пользователю необходимо выполнить три действия, чтобы выполнить любую из этих команд:

  • Непосредственно запустить команду
  • Выбрать воздуховод, соединенный с фитингом, который необходимо переместить
  • Выбрать точку, в которую следует переместить воздуховод

Продемонстрируем результат на картинках.

Исходные воздуховоды с соединителями выглядят так:

 

Как я сказал, команда CmdDisconnect просто перемещает соединитель выбранного воздуховода, разрывая с ним связь.

 

CmdReconnect же перемещает сам фитинг, сохраняя при этом связь с воздуховодами.

 

А сейчас пришло время взглянуть на реализацию этих двух команд, а также показать сходства и отличия этих двух подходов.

CmdDisconnect – перемещение соединителя

Различия между двумя командами очень небольшие. Поэтому почти весь код дублируется.

Алгоритм команды CmdDisconnect следующий:

  • Запросить пользователя выбрать воздуховод с соединителем, который нужно переместить
  • Попросить пользователя выбрать точку на этом воздуховоде, куда его следует передвинуть
  • Найти ближайший к первой выбранной точке соединитель
  • Спроецировать вторую выбранную точку на центр воздуховода
  • Переместить соединитель в спроецированную точку

Первые 4 шага повторяются и для второй команды без каких-либо изменений.

Только на последнем шаге делаются изменения в модели.

Код - C#: [Выделить]
  1. /// <summary>
  2. /// External command to move a duct connector
  3. /// away from its original position along the
  4. /// duct centre line, disconnecting from the
  5. /// neighbour element.
  6. /// </summary>
  7. [Transaction( TransactionMode.Manual )]
  8. public class CmdDisconnect : IExternalCommand
  9. {
  10.   public Result Execute(
  11.     ExternalCommandData commandData,
  12.     ref string message,
  13.     ElementSet elements )
  14.   {
  15.     UIApplication uiapp = commandData.Application;
  16.     UIDocument uidoc = uiapp.ActiveUIDocument;
  17.     Document doc = uidoc.Document;
  18.     Selection sel = uidoc.Selection;
  19.     Duct duct = null;
  20.     XYZ pFrom = null;
  21.     XYZ pTo = null;
  22.  
  23.     try
  24.     {
  25.       Reference r = sel.PickObject(
  26.         ObjectType.Element,
  27.         new DuctSelectionFilter(),
  28.         "Please pick a duct at the "
  29.         + "connection to move." );
  30.  
  31.       duct = doc.GetElement( r.ElementId ) as Duct;
  32.       pFrom = r.GlobalPoint;
  33.  
  34.       r = sel.PickObject(
  35.         ObjectType.Element,
  36.         new DuctSelectionFilter(),
  37.         "Please pick a target point on the "
  38.         + "duct to move the connection to." );
  39.  
  40.       pTo = r.GlobalPoint;
  41.     }
  42.     catch( Autodesk.Revit.Exceptions
  43.       .OperationCanceledException )
  44.     {
  45.       return Result.Cancelled;
  46.     }
  47.  
  48.     // Determine connector closest to picked point
  49.  
  50.     ConnectorSet connectors
  51.       = duct.ConnectorManager.Connectors;
  52.  
  53.     Connector con = null;
  54.     double d, dmin = double.MaxValue;
  55.  
  56.     foreach( Connector c in connectors )
  57.     {
  58.       d = pFrom.DistanceTo( c.Origin );
  59.  
  60.       if( d < dmin )
  61.       {
  62.         dmin = d;
  63.         con = c;
  64.       }
  65.     }
  66.  
  67.     // Determine target point to move it to
  68.  
  69.     Transform cs = con.CoordinateSystem;
  70.  
  71.     Debug.Assert(
  72.       con.Origin.IsAlmostEqualTo( cs.Origin ),
  73.       "expected same origin" );
  74.  
  75.     Line line = Line.CreateUnbound( cs.Origin, cs.BasisZ );
  76.  
  77.     IntersectionResult ir = line.Project( pTo );
  78.  
  79.     pTo = ir.XYZPoint;
  80.  
  81.     Debug.Assert( line.Distance( pTo ) < 1e-9,
  82.       "expected projected point on line" );
  83.  
  84.     // Modify document within a transaction
  85.  
  86.     using( Transaction tx = new Transaction( doc ) )
  87.     {
  88.       tx.Start( "Move Duct Connector" );
  89.       con.Origin = pTo;
  90.       tx.Commit();
  91.     }
  92.     return Result.Succeeded;
  93.   }
  94. }

CmdReconnect – перемещение всех фиттингов

Алгоритм команды CmdReconnect повторяет первые 4 шага предыдущей команды

  • Запросить пользователя выбрать воздуховод с соединителем, который нужно переместить
  • Попросить пользователя выбрать точку на этом воздуховоде, куда его следует передвинуть
  • Найти ближайший к первой выбранной точке соединитель
  • Спроецировать вторую выбранную точку на центр воздуховода
  • Определить вектор перемещения из исходной точки в точку перемещения
  • Определить соседний фитинги
  • Переместить фитинг в выбранную точку

Соседний фитинг мы определяем с помощью такого кода, который я взял из примера, поставляемого с Revit SDK

Код - C#: [Выделить]
  1.  /// <summary>
  2.   /// Return the connector
  3.   /// connected to the one given.
  4.   /// </summary>
  5.   static Connector GetConnectedConnector(
  6.     Connector con )
  7.   {
  8.     Connector neighbour = null;
  9.  
  10.     int ownerId = con.Owner.Id.IntegerValue;
  11.  
  12.     ConnectorSet refs = con.AllRefs;
  13.  
  14.     foreach( Connector c in refs )
  15.     {
  16.       // Ignore non-End connectors and 
  17.       // connectors on the same element
  18.  
  19.       if( c.ConnectorType == ConnectorType.End
  20.         && !ownerId.Equals(
  21.           c.Owner.Id.IntegerValue ) )
  22.       {
  23.         neighbour = c;
  24.         break;
  25.       }
  26.     }
  27.     return neighbour;
  28.   }

Остальная часть команды выглядит вот так:

Код - C#: [Выделить]
  1. /// <summary>
  2. /// External command to move a duct connector
  3. /// away from its original position along the
  4. /// duct centre line, disconnecting from the
  5. /// neighbour element.
  6. /// </summary>
  7. [Transaction( TransactionMode.Manual )]
  8. public class CmdReconnect : IExternalCommand
  9. {
  10.   public Result Execute(
  11.     ExternalCommandData commandData,
  12.     ref string message,
  13.     ElementSet elements )
  14.   {
  15.     UIApplication uiapp = commandData.Application;
  16.     UIDocument uidoc = uiapp.ActiveUIDocument;
  17.     Document doc = uidoc.Document;
  18.     Selection sel = uidoc.Selection;
  19.     Duct duct = null;
  20.     XYZ pFrom = null;
  21.     XYZ pTo = null;
  22.  
  23.     try
  24.     {
  25.       Reference r = sel.PickObject(
  26.         ObjectType.Element,
  27.         new DuctSelectionFilter(),
  28.         "Please pick a duct at the "
  29.         + "connection to move." );
  30.  
  31.       duct = doc.GetElement( r.ElementId ) as Duct;
  32.       pFrom = r.GlobalPoint;
  33.  
  34.       r = sel.PickObject(
  35.         ObjectType.Element,
  36.         new DuctSelectionFilter(),
  37.         "Please pick a target point on the "
  38.         + "duct to move the connection to." );
  39.  
  40.       pTo = r.GlobalPoint;
  41.     }
  42.     catch( Autodesk.Revit.Exceptions
  43.       .OperationCanceledException )
  44.     {
  45.       return Result.Cancelled;
  46.     }
  47.  
  48.     // Determine connector closest to picked point
  49.  
  50.     ConnectorSet connectors
  51.       = duct.ConnectorManager.Connectors;
  52.  
  53.     Connector con = null;
  54.     double d, dmin = double.MaxValue;
  55.  
  56.     foreach( Connector c in connectors )
  57.     {
  58.       d = pFrom.DistanceTo( c.Origin );
  59.  
  60.       if( d < dmin )
  61.       {
  62.         dmin = d;
  63.         con = c;
  64.       }
  65.     }
  66.  
  67.     // Determine target point to move it to
  68.  
  69.     Transform cs = con.CoordinateSystem;
  70.  
  71.     Debug.Assert(
  72.       con.Origin.IsAlmostEqualTo( cs.Origin ),
  73.       "expected same origin" );
  74.  
  75.     Line line = Line.CreateUnbound( cs.Origin, cs.BasisZ );
  76.  
  77.     IntersectionResult ir = line.Project( pTo );
  78.  
  79.     pTo = ir.XYZPoint;
  80.  
  81.     Debug.Assert( line.Distance( pTo ) < 1e-9,
  82.       "expected projected point on line" );
  83.  
  84.     // Determine translation vector
  85.  
  86.     XYZ v = pTo - pFrom;
  87.  
  88.     // Determine neighbouring fitting connector
  89.  
  90.     Connector neighbour
  91.       = GetConnectedConnector( con );
  92.  
  93.     // Modify document within a transaction
  94.  
  95.     using( Transaction tx = new Transaction( doc ) )
  96.     {
  97.       tx.Start( "Move Fitting" );
  98.  
  99.       ElementTransformUtils.MoveElement(
  100.         doc, neighbour.Owner.Id, v );
  101.  
  102.       tx.Commit();
  103.     }
  104.     return Result.Succeeded;
  105.   }
  106. }

Обратите внимание на сходства и различия двух команд и какой они в итоге дают эффект.

Исходный код с проектом для Visual Studio можно скачать на GitHub.

Источник: http://thebuildingcoder.typepad.com/blog/2013/10/move-duct-join-add-in-with-video-and-github-support.html

 

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

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