Handflipped doors

Hello,

I have an issue with rotating doors in my Revit project using a Python script. When the door family has both facing flip and hand flip controls, I can successfully rotate the door as needed. However, when the family has only a single flip control (e.g., vertical flip only), the door only rotates 180° along the “ghost” wall, and I cannot achieve the desired orientation.

Here’s my situation:

  1. I have walls with doors placed on them.
  2. I need to delete these walls, which also removes the doors.
  3. Before deletion, I create new walls and place new doors at the exact same locations as the original ones.
  4. However, depending on the original door orientation, sometimes the new doors are placed correctly, and sometimes they are not.

To solve this, I compare the HandFlipped and FacingFlipped parameters before and after placement. However, due to the single 180° flip control, I am unable to properly adjust the orientation of some doors.

:small_blue_diamond: Is there a way to bypass this limitation and ensure the new doors have the correct orientation?
Any help would be greatly appreciated!

We need to see your graph in action (with all node preview bubbles pinned) to see what you’re dealing with. If you’re not specifying the initial orientation of the door it may be defaulting to the normal of the host. There should be a simple fix, we just need to understand your exact situation first.

Here’s the code group to perform the action. For the list, first, it’s the new door and family instace facing orientation, the base doors.
Here, I compare it with respect to the vector, and finally, the Python node returns based on true or false. But this only works if there’s a double controller in the door family. The single one rotates 180°. Even though I set it to true for the Python handflip, it doesn’t do anything. The node outputs the information as if the Python node would have worked correctly for the rotation.

code :slight_smile:

import clr

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

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

doc = DocumentManager.Instance.CurrentDBDocument

# Unwrap inputs
doors = UnwrapElement(IN[0])
hands = IN[1]  # Liste des états HandFlipped attendus
facings = IN[2]  # Liste des états FacingFlipped attendus

# Assurez-vous que les entrées sont bien des listes
if not isinstance(doors, list):
    doors = [doors]
if not isinstance(hands, list):
    hands = [hands] * len(doors)  # Étendre à la taille des portes
if not isinstance(facings, list):
    facings = [facings] * len(doors)  # Étendre à la taille des portes

# Démarrer la transaction
TransactionManager.Instance.EnsureInTransaction(doc)

# Listes de portes traitées et ignorées
updated_doors = []
ignored_doors = []

for door, hand_target, facing_target in zip(doors, hands, facings):
    if not door.IsValidObject:
        ignored_doors.append(door)  # Porte supprimée ou invalide
        continue  

    modified = False  # Pour suivre si la porte a été modifiée

    # Vérifier si la porte a la propriété HandFlipped et peut être retournée
    if hasattr(door, 'HandFlipped') and hasattr(door, 'flipHand'):
        if door.HandFlipped != hand_target:
            door.flipHand()
            modified = True  # On a modifié la porte

    # Vérifier si la porte a la propriété FacingFlipped et peut être retournée
    if hasattr(door, 'FacingFlipped') and hasattr(door, 'flipFacing'):
        if door.FacingFlipped != facing_target:
            door.flipFacing()
            modified = True  # On a modifié la porte

    if modified:
        updated_doors.append(door)  # Ajoutée à la liste des modifiées
    else:
        ignored_doors.append(door)  # Ajoutée à la liste des ignorées

# Valider la transaction
TransactionManager.Instance.TransactionTaskDone()

# Sortie Dynamo : [portes modifiées, portes ignorées]
OUT = [updated_doors, ignored_doors]

We need to see the node previews to understand what your data looks like. Without them, we’re basically blind. Hover over each of your nodes and click the pin icon.

Also, to make things simpler, just get the flipping properties from the original doors first. That way you can create new instances and just apply the same property values to match. No need to check a bunch of vectors and properties for matching values.

The normal way to do this would be to copy/cut the doors to memory. Delete the walls. Make your new wall and then paste the doors back into the same place. Assuming the new wall is in the same location as the old.

But - If the wall is in the same place - why aren’t you just updating the existing wall to the new wall type and parameters? (Faster. Simpler.) ???

…you could go another route and get tricky with phasing or design options.
Don’t delete the doors and wall. Move them to a new design option. Add the new wall and then move the doors to the new wall. Then delete the design option with the old wall.

…you could do a similar thing by grouping the wall and doors. Saving that group out to a file, then reading the doors back in from that file.

A bigger picture of what you are ding would help.

1 Like

It’s true that I didn’t think about deleting the doors, but simply replacing them on the host. I’ll try right away, but there’s no root node to modify the host. You can place an instance on a host.

Are you not just modifying the wall?

If the action is simply to change the host of the pot to put the new walls. And it works great!!!

I don’t know if the code is correct because I don’t know Python.

import clr
clr.AddReference(“RevitServices”)
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

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

Récupérer le document actif
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
doc = uidoc.Document

Entrées Dynamo
doors_input = IN[0] # Liste des portes sélectionnées dans Dynamo
walls_input = IN[1] # Liste des murs sélectionnés dans Dynamo (ou None)

Déballer les éléments
doors = [UnwrapElement(door) for door in doors_input]
walls = [UnwrapElement(wall) for wall in walls_input] if walls_input else
FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()

Fonction pour trouver le mur le plus proche
def find_closest_wall(door, walls):
door_location = door.Location.Point
min_distance = float(“inf”)
closest_wall = None

for wall in walls:
    wall_location = wall.Location.Curve
    if wall_location is not None:
        wall_point = wall_location.Evaluate(0.5, True)  # Point central du mur
        distance = door_location.DistanceTo(wall_point)
        if distance < min_distance:
            min_distance = distance
            closest_wall = wall

return closest_wall

Fonction pour rehéberger la porte sur un nouveau mur
def rehost_door(door, new_wall):
try:
TransactionManager.Instance.EnsureInTransaction(doc)

    # Récupérer la position et l'orientation
    door_location = door.Location.Point
    facing_orientation = door.FacingOrientation
    hand_flipped = door.HandFlipped
    facing_flipped = door.FacingFlipped

    # Créer une nouvelle porte sur le nouveau mur
    new_door = doc.Create.NewFamilyInstance(door_location, door.Symbol, new_wall, Structure.StructuralType.NonStructural)

    # Appliquer les mêmes paramètres d'orientation
    if new_door:
        if hand_flipped:
            new_door.flipHand()
        if facing_flipped:
            new_door.flipFacing()

    # Supprimer l'ancienne porte
    doc.Delete(door.Id)

    TransactionManager.Instance.TransactionTaskDone()
    return new_door  # Retourner la nouvelle porte créée

except Exception as e:
    return f"Erreur: {str(e)}"

Appliquer la modification à toutes les portes sélectionnées
new_doors =
errors =

for door in doors:
closest_wall = find_closest_wall(door, walls)
if closest_wall:
result = rehost_door(door, closest_wall)
if isinstance(result, str): # Si c’est une erreur, on l’ajoute à la liste d’erreurs
errors.append(result)
else:
new_doors.append(result)
else:
errors.append(f"Aucun mur trouvé pour la porte {door.Id.IntegerValue}.")

Sortie Dynamo
OUT = (new_doors, errors) # Retourne la liste des nouvelles portes + erreurs éventuelles