Error encountered in Dynamo rotation script for aligning FamilyInstance orientations

Hey, I’m getting an error when trying to rotate multiple elements based on a reference element.
The issue seems to happen in the part where the script handles the XYZ vectors. (Line 116)

Does anyone know what I’m doing wrong?

import clr
import math

# Revit API
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")

from Autodesk.Revit.DB import (
    FamilyInstance,
    XYZ,
    Line,
    ElementTransformUtils
)
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

# ---------------------------------------------------------
# INPUT
# IN[0] : Select Model Elements  (meerdere elementen)
# IN[1] : Select Model Element   (referentie-element)
# ---------------------------------------------------------

sel_in = IN[0]
ref_in = IN[1]

# Altijd werken met een lijst
if isinstance(sel_in, list):
    elements_in = sel_in
else:
    elements_in = [sel_in]

def to_db(e):
    """Dynamo wrapper -> Revit DB element."""
    return getattr(e, "InternalElement", e)

elements = [to_db(e) for e in elements_in]
ref_element = to_db(ref_in)

# Referentie moet een FamilyInstance zijn met FacingOrientation
if not isinstance(ref_element, FamilyInstance):
    raise Exception("Referentie-element is geen FamilyInstance. Kies een loadable / host-based family instance.")

ref_vec = ref_element.FacingOrientation  # XYZ

# ---------------------------------------------------------
# Helper: signed angle tussen twee Revit XYZ vectors in XY-vlak
# - gebruikt AngleTo (0..PI)
# - gebruikt CrossProduct.Z om links/rechts te bepalen
# ---------------------------------------------------------
def signed_angle_xy(from_vec, to_vec):
    """
    Geeft signed rotatiehoek om Z-as om 'from_vec' naar 'to_vec' te draaien.
    Return in radians, bereik ongeveer [-PI, PI].
    """
    if from_vec is None or to_vec is None:
        return None

    # Hoekgrootte (0..PI)
    angle = from_vec.AngleTo(to_vec)  # radians

    # Kruisproduct: richting van rotatie-as
    cross = from_vec.CrossProduct(to_vec)
    z = cross.Z

    # Als bijna geen Z-component: hoek is 0 of PI, teken maakt niet uit
    if abs(z) < 1e-9:
        return angle

    sign = 1.0 if z > 0.0 else -1.0
    return angle * sign

rotated = []

# ---------------------------------------------------------
# TRANSACTIE
# ---------------------------------------------------------
TransactionManager.Instance.EnsureInTransaction(doc)

for e in elements:

    # Alleen FamilyInstance verwerken
    if not isinstance(e, FamilyInstance):
        rotated.append(e)
        continue

    # Referentie zelf overslaan
    if e.Id == ref_element.Id:
        rotated.append(e)
        continue

    # FacingOrientation van current element
    try:
        cur_vec = e.FacingOrientation  # XYZ
    except:
        rotated.append(e)
        continue

    angle = signed_angle_xy(cur_vec, ref_vec)

    # Geen bruikbare hoek -> skip
    if angle is None or abs(angle) < 1e-6:
        rotated.append(e)
        continue

    # Locatie moet een LocationPoint hebben
    loc = e.Location
    if not hasattr(loc, "Point"):
        rotated.append(e)
        continue

    pt = loc.Point  # XYZ

    # Rotatie-as: lijn door insertiepunt, in Z-richting
    axis = Line.CreateBound(pt, pt + XYZ(0, 0, 1))

    # Element roteren
    ElementTransformUtils.RotateElement(doc, e.Id, axis, angle)

    rotated.append(e)

TransactionManager.Instance.TransactionTaskDone()

OUT = rotated

The + opperator doesn’t translate to Python, so you’ll need to use the method Add instead.

Something like axis = Line.CreateBound(pt, pt.Add(XYZ(0,0,1))) should work.

1 Like

Note that the script only works for family instances that are hosted on a horizontal face or plane (FaceNormal aligned with the Z vector).

Thanks, this worked!

Correct. It sometimes happens that fixtures in the model are not rotated correctly.
The purpose of this script is to give multiple elements the same Z-rotation as a reference element.
Sometimes you don’t notice that elements are rotated, for example with round fixtures.
ps. we use non-host families.