Create elevations based on MULTIPLE scope boxes with Python

I came across this charm.

But i try to get it to work for multiple Scope Boxes. I think something in the Python needs to be change for that (?). I already fiddled with Levels and such, but it wasn’t that simple :sweat_smile:.

Script for Revit 2024_2.dyn (76.7 KB)

@c.poupin are you able to help?

Best to share a screenshot with what you have and what you’re seeing. If it’s just a matter of going from one scope box to many then you should just need another for loop.

1 Like

Elevation markers are not “Views Dependent”, so I don’t really understand the request.

Who’s request? @c.poupin

I will post screenshots and the original Python :snake: tomorrow @Nick_Boyts

this question ↓↓

@c.poupin Sorry, that was a typo. I meant multiple Scope Boxes. Fixed that typo :sweat_smile:.

try to use zip() function,

Example: adapt this to fit the structure of your input lists.

toggle = IN[0]
lst_points = UnwrapElement(IN[1])
lst_modelPoints = UnwrapElement(IN[2])
lst_cropCurves = UnwrapElement(IN[3])
viewType = UnwrapElement(IN[4])

lst = []

if toggle == True:

    TransactionManager.Instance.EnsureInTransaction(doc)
    for points, modelPoints, cropCurves in zip(lst_points, lst_modelPoints, lst_cropCurves):
        for ind, point in enumerate(points):
            # rest of code

Thank you. Will have a look at that tomorrow.

1 Like

@Nick_Boyts

Hereby the screenshot

Die orginal Python you can find in this topic.

When i use the original code i get this error

Node Name: Python Script
Package: Core.Scripting
Dynamo Version: 2.19.3.6394
Host: Dynamo Revit
Messages: AttributeError : '0, Culture=neutral, PublicKeyToken=b77a5c561934e08' object has no attribute 'ToXyz' ['  File "<string>", line 34, in <module>\n']
State: Warning

I am not sure if i made the right changes to your code @c.poupin

import clr
import System
from System.Collections.Generic import List
clr.AddReference('RevitAPI')
clr.AddReference("RevitServices")
clr.AddReference("RevitNodes")
import RevitServices
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

import Autodesk
import  Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

from math import atan2

doc = DocumentManager.Instance.CurrentDBDocument

toggle = IN[0]
lst_points = UnwrapElement(IN[1])
lst_modelPoints = UnwrapElement(IN[2])
lst_cropCurves = UnwrapElement(IN[3])
viewType = UnwrapElement(IN[4])

lst = []

if toggle == True:

    TransactionManager.Instance.EnsureInTransaction(doc)
    for points, modelPoints, cropCurves in zip(lst_points, lst_modelPoints, lst_cropCurves):
        for ind, point in enumerate(points):
    
        modelMP = modelPoints[ind].ToXyz()
        modelMPX = modelMP.X
        modelMPY = modelMP.Y
        
        cropLines = cropCurves[ind]
        l1 = cropLines[0].ToRevitType()
        l2 = cropLines[1].ToRevitType()
        l3 = cropLines[2].ToRevitType()
        l4 = cropLines[3].ToRevitType()
        
        elevationPT = point.ToXyz()
        elptRotate = XYZ(elevationPT.X, elevationPT.Y, elevationPT.Z+100)
        ln = Line.CreateBound(elevationPT, elptRotate)
        
        elevationPTY = elevationPT.Y
        elevationPTX = elevationPT.X
        combY = elevationPTY-modelMPY
        combX = elevationPTX-modelMPX
        ang = atan2(combY, combX)
        
        eleMarker = ElevationMarker.CreateElevationMarker(doc, viewType.Id, elevationPT, 100)
        ele = eleMarker.CreateElevation(doc, doc.ActiveView.Id , 0)
        
        ElementTransformUtils.RotateElement(doc, eleMarker.Id, ln, ang)
        
        crManager = ele.GetCropRegionShapeManager()
        
        newCurveLoop = List[DB.Curve]()
        newCurveLoop.Add(l1)
        newCurveLoop.Add(l2)
        newCurveLoop.Add(l3)
        newCurveLoop.Add(l4)
        cLoop = CurveLoop.Create(newCurveLoop)
        
        lst.append(ele)

    TransactionManager.Instance.TransactionTaskDone()
    OUT = lst
    
else:
    OUT = lst

Because i get the following warning

Node Name: Python Script
Package: Core.Scripting
Dynamo Version: 2.19.3.6394
Host: Dynamo Revit
Messages: PythonEvaluator.Evaluate operation failed. 
IndentationError : ('expected an indented block', ('<string>', 35, 9, '        modelMP = modelPoints[ind].ToXyz()\n'))
State: Warning

I hate doing it, but was fiddling with ChatGPT a bit (also out of curiousity) :see_no_evil_monkey:

The Elevation Markers got placed but face the wrong direction.

import clr
import System
from System.Collections.Generic import List

clr.AddReference('RevitAPI')
clr.AddReference("RevitServices")
clr.AddReference("RevitNodes")

import RevitServices
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

import Autodesk
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

from math import atan2

doc = DocumentManager.Instance.CurrentDBDocument

# Inputs
toggle = IN[0]
points = [UnwrapElement(pt) for pt in IN[1]]
modelPoints = [UnwrapElement(mp) for mp in IN[2]]
viewType = UnwrapElement(IN[4])

# Normalize input format
if not isinstance(points[0], list):
    points = [points]
    modelPoints = [modelPoints]

lst = []
log = []

if toggle:
    TransactionManager.Instance.EnsureInTransaction(doc)

    for i in range(len(points)):

        pts = points[i]
        mps = modelPoints[i]

        for ind, point in enumerate(pts):
            try:
                modelMP = mps[ind].ToXyz()
                modelMPX = modelMP.X
                modelMPY = modelMP.Y

                elevationPT = point.ToXyz()
                elptRotate = XYZ(elevationPT.X, elevationPT.Y, elevationPT.Z + 100)
                ln = Line.CreateBound(elevationPT, elptRotate)

                combY = elevationPT.Y - modelMPY
                combX = elevationPT.X - modelMPX
                ang = atan2(combY, combX)

                # Create elevation marker and view
                eleMarker = ElevationMarker.CreateElevationMarker(doc, viewType.Id, elevationPT, 100)
                ele = eleMarker.CreateElevation(doc, doc.ActiveView.Id , 0)

                ElementTransformUtils.RotateElement(doc, eleMarker.Id, ln, ang)

                # Define local rectangle in view plane (centered at origin)
                size = 100.0
                half = size / 2.0
                localPts = [
                    XYZ(-half, -half, 0),
                    XYZ(half, -half, 0),
                    XYZ(half, half, 0),
                    XYZ(-half, half, 0)
                ]

                # Get view direction and up vectors
                viewDir = ele.ViewDirection.Normalize()
                upDir = ele.UpDirection.Normalize()
                rightDir = viewDir.CrossProduct(upDir).Normalize()

                # Build transform from local (view space) to world
                basisX = rightDir
                basisY = upDir
                basisZ = viewDir
                origin = ele.Origin
                transform = Transform.Identity
                transform.BasisX = basisX
                transform.BasisY = basisY
                transform.BasisZ = basisZ
                transform.Origin = origin

                # Transform each point to world space
                worldPts = [transform.OfPoint(pt) for pt in localPts]

                # Build lines and curve loop
                lines = [
                    Line.CreateBound(worldPts[0], worldPts[1]),
                    Line.CreateBound(worldPts[1], worldPts[2]),
                    Line.CreateBound(worldPts[2], worldPts[3]),
                    Line.CreateBound(worldPts[3], worldPts[0])
                ]

                newCurveLoop = List[DB.Curve]()
                for line in lines:
                    newCurveLoop.Add(line)

                cLoop = CurveLoop.Create(newCurveLoop)
                crManager = ele.GetCropRegionShapeManager()
                crManager.SetCropShape(cLoop)

                lst.append(ele)

            except Exception as e:
                log.append(f"Failed at index {ind} in group {i}: {str(e)}")
                continue

    TransactionManager.Instance.TransactionTaskDone()

# Output
OUT = (lst, log) if toggle else ([], ["Toggle is off."])

When i do this they all face the same direction.

Getting there.


1 Like

Only running into this problem and i am not sure what i am missing.
It works when i use the script for a single Scope Box.
They Elevation Markers Scope Boxes get greyed out on placement.
Dunno why. I guess something in de Python code?

Success!

import clr
import System
from System.Collections.Generic import List

# Add Revit API References
clr.AddReference('RevitAPI')
clr.AddReference("RevitServices")
clr.AddReference("RevitNodes")

import RevitServices
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

import Autodesk
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

from math import atan2

# Document reference
doc = DocumentManager.Instance.CurrentDBDocument

# ------------------------------
# Inputs
# ------------------------------
toggle = IN[0]                                  # Boolean toggle to run
points = [UnwrapElement(pt) for pt in IN[1]]    # Elevation marker positions
modelPoints = [UnwrapElement(mp) for mp in IN[2]] # Model reference points
viewType = UnwrapElement(IN[3])                 # ViewFamilyType for elevation

# Normalize input format for multi-point runs
if not isinstance(points[0], list):
    points = [points]
    modelPoints = [modelPoints]

# ------------------------------
# Execution
# ------------------------------
created_views = [[] for _ in range(len(points))]  # One list per group/scope box

if toggle:
    TransactionManager.Instance.EnsureInTransaction(doc)

    for i in range(len(points)):

        pts = points[i]
        mps = modelPoints[i]

        for ind, point in enumerate(pts):
            try:
                # Get XYZ positions
                modelMP = mps[ind].ToXyz()
                elevationPT = point.ToXyz()
                
                # Vector direction for rotation
                elptRotate = XYZ(elevationPT.X, elevationPT.Y, elevationPT.Z + 100)
                axis = Line.CreateBound(elevationPT, elptRotate)
                angle = atan2(elevationPT.Y - modelMP.Y, elevationPT.X - modelMP.X)

                # Create elevation marker and view
                eleMarker = ElevationMarker.CreateElevationMarker(doc, viewType.Id, elevationPT, 100)
                eleView = eleMarker.CreateElevation(doc, doc.ActiveView.Id, 0)

                # Rotate the marker to face the model point
                ElementTransformUtils.RotateElement(doc, eleMarker.Id, axis, angle)

                # Add the view to the correct group
                created_views[i].append(eleView)

            except Exception:
                continue

    TransactionManager.Instance.TransactionTaskDone()

# ------------------------------
# Outputs
# ------------------------------
OUT = created_views if toggle else []
3 Likes

I really didn’t want to resort to ChatGPT, but it got me to a solution.

Also learned you really need to ask ChatGPT the right questions and i really need
to learn Python still.

Great work @bvs1982!

Been popping in every hour or so to watch your progress. Kudos for sticking with it and showing us the process!

2 Likes

I couldn’t stand it that i was soooo close.
I really wanted it resolved so i could get it out of my head :sweat_smile:.

2 Likes