Efektywne łączenie elementów w Dynamo

[Podobny problem w C# prezentowaliśmy w innym poście]

Masowe łączenie elementów w Revicie może być bardzo pracochłonną czynnością. Jest to na pewno coś co większość z nas chętnie zoptymalizowałaby przy pomocy Dynamo. Szczęśliwie bez problemu możemy znaleźć dodatkowe węzły Dynamo pozwalające na łączenie elementów. Możemy też skorzystać z funkcji Revit API w Pythonie:

JoinGeometryUtils.JoinGeometry(doc,a,b)

gdzie doc to aktywny dokument a a i b to elementy do połączenia.

Problemy (z wydajnością) zaczynają się jednak przy większych i bardziej skomplikowanych modelach. Jeśli stworzymy skrypt który będzie próbował połączyć np. każdą ścianę z każdym możliwym stropem, to nie tylko jego wykonanie zajmie dużo czasu, ale także zwróci wiele błędów. Stanie się tak ponieważ Dynamo będzie próbowało połączyć także te elementy, które w ogóle się nie stykają, a wręcz znacznie od siebie oddalone. To nie może być efektywne podejście 🙂

Przed próbą połączenia elementów warto więc sprawdzić czy ich geometrie się przenikają. Często jednak obiekty mają skomplikowane kształty i takie sprawdzenie również może być dość pracochłonne (dla skryptu). Dlatego w poniższym rozwiązaniu proponuję dla zadanych elementów wykonywać dodatkowe, wstępne sprawdzenie, które pozwoli określić czy elementy te w ogóle znajdują się w pobliżu i mają szansę przenikać.

Każdy element modelowy w Revicie posiada tzw. BoundingBox – przestrzenną obwiednie, która de facto jest najmniejszym pudełkiem mogącym pomieścić ten element. W dość prosty (i szybki) sposób jesteśmy w stanie sprawdzić czy takie pudełka, zawierające dwa różne elementy, się przenikają lub stykają. Wykonanie takiej operacji dla prostopadłościanów jest dużo efektywniejsze niż dla dokładnej geometrii.

Poniższy kod jako parametry przyjmuje dwie listy elementów. Następnie testuje każdy element z pierwszej listy z każdym elementem z drugiej – czy ich pudełka się przenikają. Zwracanym rezultatem jest lista par przenikających się elementów.

import clr

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

def overlap1D(a1, a2, b1, b2):
  if a2>=b1 and b2>=a1:
    return True
  return False
  
def bbIntersect(bbA, bbB):
  return overlap1D(bbA.Min.X,bbA.Max.X,bbB.Min.X,bbB.Max.X) and overlap1D(bbA.Min.Y,bbA.Max.Y,bbB.Min.Y,bbB.Max.Y) and overlap1D(bbA.Min.Z,bbA.Max.Z,bbB.Min.Z,bbB.Max.Z)

#The inputs to this node will be stored as a list in the IN variables.
listA = UnwrapElement(IN[0])
listB = UnwrapElement(IN[1])

output = []

for a in listA:
  bbA = a.get_BoundingBox(None)
  if not bbA is None:
    for b in listB:
      bbB = b.get_BoundingBox(None)
      if not bbB is None:
        if bbIntersect(bbA,bbB):
          output.append([a,b])

#Assign your output to the OUT variable.
OUT = output

W ten sposób otrzymujemy listę par elementów, co do których mamy już dość poważne podejrzenie że mogą się przenikać lub stykać.

Ten przykładowy skrypt (do pobrania na końcu posta) połączy nam wszystkie ściany i posadzki w projekcie:

Ostatni węzeł Join, również zawiera kod w Pythonie łączący dwie listy elementów:

import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#The inputs to this node will be stored as a list in the IN variables.
list_a = UnwrapElement(IN[0])
list_b = UnwrapElement(IN[1])

doc = DocumentManager.Instance.CurrentDBDocument
TransactionManager.Instance.EnsureInTransaction(doc)

output = []

for a,b in zip(list_a,list_b):
	try:
		JoinGeometryUtils.JoinGeometry(doc,a,b)
		output.append("OK")
	except Exception,e:
		output.append(str(e))


TransactionManager.Instance.TransactionTaskDone()

#Assign your output to the OUT variable.
OUT = output
Posts created 32

Dodaj komentarz

Twój adres email nie zostanie opublikowany.

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top