Discountinous CurveLoop

Hi All, @Mike.Buttery @c.poupin

In my code below, I created a ‘U’ shape from a selected wall face, which I now want to translate along a line with a given distance. However, it seems that the shape I used has a discontinuous CurveLoop, and I’m struggling with how to sort and reorient this CurveLoop.

Please check my code and the Revit model below:

Discountinous CurveLoop
import clr
import sys
import System
import math

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import specify namespace



#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.GeometryReferences)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

# select all walls within the project
Walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()

rebar_Types = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rebar).WhereElementIsElementType().ToElements()

rebar = [r for r in rebar_Types if r.get_Name() == "HA12 (Fe400)"]
units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
results = DB.IntersectionResultArray()
 
# get walls faces 
Faces = []
for w in Walls:
    face_Ref = list(HostObjectUtils.GetSideFaces(w, ShellLayerType.Interior))[0]
    wall_face = w.GetGeometryObjectFromReference(face_Ref)
    Faces.append(wall_face)
    Thick_wall = w.Width
    
# covers parameters    
c1 = UnitUtils.ConvertToInternalUnits(0.03, units)
c2 = UnitUtils.ConvertToInternalUnits(0.05, units)
c3 = UnitUtils.ConvertToInternalUnits(0.05, units) + Thick_wall

# choose one face end get its edges serving as the base of creating the searched rebar shape

face = Faces[0]

edges = face.GetEdgesAsCurveLoops()[0]

plane = edges.GetPlane()

# create an offset of the previous edge as a rectangle 
new_edges = edges.CreateViaOffset([c2, c1, c2, c1], plane.Normal.Negate())

# translate the rectangle with a normal distance c3
trans = Transform.CreateTranslation(plane.Normal.Negate().Multiply(c3))
new_edges.Transform(trans)

u_plane = new_edges.GetPlane()
new_edges = list(new_edges)

# remove the top horizontal edge of the rectangle to get the desired shape " U "
new_edges.remove(max(new_edges, key=lambda line: line.Evaluate(0.5, True).Z))

def is_horizontal(line):
    return not XYZ.BasisZ.DotProduct(line.Direction)

# get the horizontal line from the shape U and rotate it with 90 degrees to use it as a translation path 
for e in new_edges:
    if is_horizontal(e):
        axis = e.Direction.CrossProduct(u_plane.Normal)
        rot = Transform.CreateRotation(axis, math.pi/2)
        line = e.CreateTransformed(rot)
        result, intersect = line.Intersect(e, results)
        if result != SetComparisonResult.Overlap:
            intersect = "No Overlaping"
        else:
            st_pt = intersect[0].XYZPoint
            print(st_pt)
            offset = line.GetEndPoint(0).DistanceTo(st_pt)
            end_pt = line.Evaluate((line.Length - offset)/line.Length, True)
            path = Line.CreateBound(st_pt, end_pt)
        plane2 = Plane.CreateByNormalAndOrigin(e.Direction, line.GetEndPoint(0))
        
# define the spacing for trnslation
space = UnitUtils.ConvertToInternalUnits(0.15, units)
count = int(math.ceil(path.Length/space))

# function to use to oriente the curveloop
def substract_point(pt1, pt2):
    x_value = pt1.X - pt2.X
    y_value = pt1.Y - pt2.Y
    Z_value = pt1.Z - pt2.Z
    return(x_value + y_value + Z_value)

# define a new curveloop
final_edge = CurveLoop()

# I'm struggling here
for i in range(0, len(new_edges)):
    edge = new_edges[i]
    st_pt = new_edges[i].GetEndPoint(0)
    temp = new_edges[i+1].GetEndPoint(0)
    if substract_point(st_pt, temp) == 0 :
        edge = new_edges[i]
        final_edge.Append(edge)
    else:
        edge = new_edges[i].CreateReversed()
        final_edge.Append(edge)
        


OUT = final_edge

regard.rvt (5.2 MB)

Any help would be appreciated.

Thanks.

Hi,

here a different approach

import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

debug = []
units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
# covers parameters    
c1 = UnitUtils.ConvertToInternalUnits(0.05, units)
c2 = UnitUtils.ConvertToInternalUnits(0.10, units)
# c3 = UnitUtils.ConvertToInternalUnits(0.05, units) + Thick_wall
# select all walls within the project
walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType()

rebar_Type = List[Element](FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rebar)\
                        .WhereElementIsElementType()\
                        .ToElements())\
                        .Find(System.Predicate[System.Object](lambda r : r.get_Name() == "HA12 (Fe400)"))
for w in walls:
    bbx = w.get_BoundingBox(None)
    # height of the wall
    h = bbx.Max.Z - bbx.Min.Z
    #
    # base location line ___
    lineB = w.Location.Curve
    # compute the vector from the height of wall
    vectZ = XYZ(0,0, h - c1)
    #
    #                   |
    #                   |
    # 1st vertical line ↓——→
    #
    lineA = Line.CreateBound(lineB.GetEndPoint(0).Add(vectZ), lineB.GetEndPoint(0))
    #
    #                   |   ↑
    #                   |   |
    # 2nd vertical line ↓——→┘
    lineC = Line.CreateBound(lineB.GetEndPoint(1), lineB.GetEndPoint(1).Add(vectZ))
    # create curveloop with order
    curveLoop = CurveLoop.Create(List[Curve]([lineA, lineB, lineC]))
    # offset the curve 
    offset_curveLoop = CurveLoop.CreateViaOffset(curveLoop, List[System.Double]([c2, c1, c2]), curveLoop.GetPlane().Normal.Negate())
    debug.append([c.ToProtoType() for c in offset_curveLoop])
    # ↓↓↓ rest of your code ↓↓↓↓

OUT = debug

A few comments
Do you want the line to rotate 90 out of the plane? e.Direction.CrossProduct(u_plane.Normal) will create a vertical axis (horizontal cross plane normal = vertical vector).

Why not use the surface normal and multiply by the spacing? Also the Line.Intersect() method takes some time to return a result

Anyhow, I would start with the positive and likely most common case

 if result == SetComparisonResult.Overlap:
    st_pt = intersect[0].XYZPoint
    print(st_pt)
    offset = line.GetEndPoint(0).DistanceTo(st_pt)
    end_pt = line.Evaluate((line.Length - offset)/line.Length, True)
    path = Line.CreateBound(st_pt, end_pt)
 else:
    intersect = "No Overlaping"

math.ceil returns an integer so the int function is not required.
XYZ class has a DistanceTo method

def substract_point(pt1, pt2):
    return pt1.DistanceTo(pt2)

However it looks like you are testing if the points are equal you could use pt1.IsAlmostEqualTo(pt2)

While I have been inspecting your code, I figured out that the main reason why you can’t return the edited curves to a curve loop is that you have removed an edge that is in the middle of the sequence, so 1, 2, 3, 4 is now 1, 3, 4 so the edges do not conform with the requirements of the CurveLoop class. Edges have directions so you will have to reconfigure the loop.

See Jeremy Tammik’s post here which is one way. However we can use graph theory to get this to work.

If we think of our edges as ‘edges’ and their start and end points as ‘nodes’ we can rebuild our edges so that they will be accepted in a CurveLoop.

Start with from collections import Counter as part of your imports

Then create a function that finds the ‘start’ curve and then uses the end points to find the next curve in sequence. I use tuples as they are able to be used and compared where the XYZ class would be a lot more clunky. There are a lot of assumptions here - the curves are continuous, that the start and end points align (no tolerance), there is one ‘U’ shape. It could look something like this

def continuous_edges(edges):
    # Utility functions
    def point_tuple(p):
        return tuple(p.get_Item(i) for i in [0, 1, 2])

    def edge_tuples(edge):
        return [point_tuple(p) for p in [edge.GetEndPoint(0), edge.GetEndPoint(1)]]

    # Create a graph of edge index and end points (nodes) as tuples
    graph = {i: edge_tuples(e) for i, e in enumerate(edges)}

    # Count the nodes (1 count = start/end node, 2 count = connected)
    point_counter = Counter([point for nodes in graph.values() for point in nodes])

    # Get index of edge with single point at index 0 (this assumes we have one).
    # This is our start curve. TODO handle continuous loops
    idx = next(idx for idx, nodes in graph.items() if point_counter[nodes[0]] == 1)

    # Get a dict of start points and their edges index
    sp_lookup = {t[0]: i for i, t in graph.items()}

    ordered_edges = []

    # Index can be 0 which evaluates to False - so compare to None
    while idx is not None:
        ordered_edges.append(edges[idx])
        # End point is new start point
        start_point = graph[idx][1]
        idx = sp_lookup.get(start_point)

    return ordered_edges

Finally we can build our CurveLoop

final_edges = continuous_edges(new_edges)
new_curveloop = CurveLoop.Create(final_edges)

@c.poupin Your approach is simple and interesting, but I noticed that the CreateViaOffset method doesn’t produce any output, even when I vary the parameters c1 and c2, because the ‘U’ shape is already created beforehand in the CurveLoop by assembling lines A, B, and C. …From what I understand, the CreateViaOffset method offsets the shape only within its plane, whereas I want to perform multiple offsets of the shape perpendicular to its plane with a specified distance. I made changes to your script to get the correct ‘U’ shape I need, but I’m struggling with how to perform the multiple offsets as described in the image below

import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

debug = []
units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
# covers parameters    

#c3 = UnitUtils.ConvertToInternalUnits(0.05, units) + Thick_wall
# select all walls within the project
walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()

floors = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Floors).WhereElementIsNotElementType().ToElements()

floor_width = floors[0].get_Parameter(BuiltInParameter.FLOOR_ATTR_THICKNESS_PARAM).AsDouble()

rebar_Type = List[Element](FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rebar)\
                        .WhereElementIsElementType()\
                        .ToElements())\
                        .Find(System.Predicate[System.Object](lambda r : r.get_Name() == "HA12 (Fe400)"))

width = walls[0].Width
c1 = width/2 + UnitUtils.ConvertToInternalUnits(0.05, units)
c2 = (floor_width - UnitUtils.ConvertToInternalUnits(0.05, units))

plans = []
loops = []

BBX = []
debug = []
for w in walls:
    bbx = w.get_BoundingBox(None)
    # height of the wall
    h = bbx.Max.Z - bbx.Min.Z
    lineB = w.Location.Curve
    vectB = XYZ(0,0, -c2)
    vectZ = XYZ(0,0, h + c2)
    lineB = lineB.CreateTransformed(Transform.CreateTranslation(vectB))
    lineA = Line.CreateBound(lineB.GetEndPoint(0).Add(vectZ), lineB.GetEndPoint(0))
    lineC = Line.CreateBound(lineB.GetEndPoint(1), lineB.GetEndPoint(1).Add(vectZ))
    # create curveloop with order
    curveLoop = CurveLoop.Create(List[Curve]([lineA, lineB, lineC]))
    plane = curveLoop.GetPlane()
    trans = Transform.CreateTranslation(plane.Normal.Negate().Multiply(c1))
    curveLoop.Transform(trans)
    debug.append([c.ToProtoType() for c in curveLoop])
    plane = curveLoop.GetPlane()
    
    plans.append(plane)
    loops.append(list(curveLoop))
    
space = UnitUtils.ConvertToInternalUnits(0.15, units)

# I'm struggling on how to iterate over each curvloop and get the path

Thanks.

CurveLoop.CreateViaOffset() method works in the same plane with the nominated line offsets. To create the new CurveLoop in a different location (rotation, scale, etc.) use the CreateViaTransform() method.
Example code

new_curveloop = [CurveLoop.Create(final_edges)]

o_no = (walls.Width - c3 * 2) // c3
o_dist = (walls.Width - c3 * 2) / o_no
offset = new_curveloop[0].GetPlane().Normal.Multiply(o_dist)
xform = Transform.CreateTranslation(offset)

count = 0
while count < o_no:
    # Take the last CurveLoop in the list and create via transform
    new_curveloop.append(new_curveloop[-1].CreateViaTransform(xform))
    count += 1

Thanks a lot, @c.poupin and @Mike.Buttery , for your guidance on how to create a continuous CurveLoop, either by generating a new one using the BoundingBox method or by reordering an existing one. (Mike’s approach, which employs graph theory to reorder a CurveLoop, is difficult for me at the moment, but I’ll study it later.) I now understand how to use the CurveLoop.CreateViaOffset() and CreateViaTransform() methods, and I finally solved my issue, as you can see in the example below

I do not dismiss your comments, and I would appreciate your guidelines for a well-organized code structure. Also, I wonder if it is possible to obtain both the horizontal and vertical curve-loop transformations within the main for-loop?

final code
import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument


units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()

# select all walls within the project
walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()
width = walls[0].Width

# select all floors within the project
floors = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Floors).WhereElementIsNotElementType().ToElements()

floor_width = floors[0].get_Parameter(BuiltInParameter.FLOOR_ATTR_THICKNESS_PARAM).AsDouble()

rebar_Type = List[Element](FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rebar)\
                        .WhereElementIsElementType()\
                        .ToElements())\
                        .Find(System.Predicate[System.Object](lambda r : r.get_Name() == "HA12 (Fe400)"))

# covers parameters    
c1 = width/2 + UnitUtils.ConvertToInternalUnits(0.05, units)
c2 = (floor_width - UnitUtils.ConvertToInternalUnits(0.05, units))
c3 = UnitUtils.ConvertToInternalUnits(0.05, units)
# spacing parameter
space = UnitUtils.ConvertToInternalUnits(0.15, units)
loops = []
Lines = []

for w in walls:
    bbx = w.get_BoundingBox(None)
    # height of the wall
    h = bbx.Max.Z - bbx.Min.Z
    lineB = w.Location.Curve
    Lines.append(lineB)
    #Create a continuous CurveLoop from the wall location curves as a horizontal rectangle. 
    line1 = Lines[0]
    end_pt1 = line1.GetEndPoint(1)
    L = line1.Length
    norm1 = line1.Direction.Normalize()
    vect1 = (XYZ(end_pt1.X, end_pt1.Y, end_pt1.Z + L)).Subtract(end_pt1)
    line2 = Line.CreateBound(end_pt1, end_pt1.Add((vect1).CrossProduct(norm1)))
    end_pt2 = line2.GetEndPoint(1)
    norm2 = line2.Direction.Normalize()
    vect2 = (XYZ(end_pt2.X, end_pt2.Y, end_pt2.Z + L)).Subtract(end_pt2)
    line3 = Line.CreateBound(end_pt2, end_pt2.Add((vect2).CrossProduct(norm2)))
    end_pt3 = line3.GetEndPoint(1)
    norm3 = line3.Direction.Normalize()
    vect3 = (XYZ(end_pt3.X, end_pt3.Y, end_pt3.Z + L)).Subtract(end_pt3)
    line4 = Line.CreateBound(end_pt3, end_pt3.Add((vect3).CrossProduct(norm3)))
    # get the expected Horizontal curveloop
    H_Loop = CurveLoop.Create(List[Curve]([line1, line2, line3, line4]))
    
    # create the vertical shape " U " for each wall
    vectB = XYZ(0,0, -c2)
    vectZ = XYZ(0,0, h + c2)
    lineB = lineB.CreateTransformed(Transform.CreateTranslation(vectB))
    lineA = Line.CreateBound(lineB.GetEndPoint(0).Add(vectZ), lineB.GetEndPoint(0))
    lineC = Line.CreateBound(lineB.GetEndPoint(1), lineB.GetEndPoint(1).Add(vectZ))
    # create curveloop with order
    curveLoop = CurveLoop.Create(List[Curve]([lineA, lineB, lineC]))
    plane = curveLoop.GetPlane()
    
    # create a translation of the curveloop to the first position
    trans = Transform.CreateTranslation(plane.Normal.Negate().Multiply(c1))
    curveLoop.Transform(trans)
    loops.append(list(curveLoop))

# create a translation of the horizontal curveloop to the first position
H_plane = H_Loop.GetPlane()
H_trans = Transform.CreateTranslation(H_plane.Normal.Multiply(c3))
H_Loop.Transform(H_trans)

# create a multiple translation for the horizontal curveloop
H_Loop = [H_Loop]

h1 = h - UnitUtils.ConvertToInternalUnits(0.10, units)
n1 = h1 // space
H_offset = H_Loop[0].GetPlane().Normal.Multiply(space)
H_xform = Transform.CreateTranslation(H_offset)

count1 = 0
while count1 < n1:
    # Take the last CurveLoop in the list and create via transform
    H_Loop.append(H_Loop[-1].CreateViaTransform(H_xform))
    count1 += 1

# Grouping each pair of vertical CurveLoops to perform multiple translations
loop1 = []
loop2 = []
loop1.append(loops[0])
loop1.append(loops[1])
loop2.append(loops[2])
loop2.append(loops[3])

for i,j in zip(loop1, loop2):
    curveloop1 = [CurveLoop.Create(loops[0])]
    curveloop2 = [CurveLoop.Create(loops[1])]
    st = i[1].GetEndPoint(0)
    end = j[1].GetEndPoint(1)
    line = Line.CreateBound(st, end)
    n = line.Length // space
    offset1 = curveloop1[0].GetPlane().Normal.Negate().Multiply(space)
    xform1 = Transform.CreateTranslation(offset1)
    offset2 = curveloop2[0].GetPlane().Normal.Negate().Multiply(space)
    xform2 = Transform.CreateTranslation(offset2)
    count = 0
    while count < n:
        # Take the last CurveLoop in the list and create via transform
        curveloop1.append(curveloop1[-1].CreateViaTransform(xform1))
        curveloop2.append(curveloop2[-1].CreateViaTransform(xform2))
        count += 1


OUT = [i.ToProtoType() for j in curveloop1 for i in j], [i.ToProtoType() for j in curveloop2 for i in j], [i.ToProtoType() for j in H_Loop for i in j]

Thanks.

Sorry @c.poupin for unchecking your solution. I just didn’t want to mark it as solved until I tested the final code for rebar creation (which is the purpose of this topic). After running the test, I encountered the following error related to rebar creation that I’m unsure how to solve?

Here the code:

rebars creation
import clr
import sys
import System

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import *
import Autodesk.Revit.DB as DB

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

import math
doc = DocumentManager.Instance.CurrentDBDocument


units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()

# select all walls within the project
walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()
width = walls[0].Width

# select all floors within the project
floors = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Floors).WhereElementIsNotElementType().ToElements()

floor_width = floors[0].get_Parameter(BuiltInParameter.FLOOR_ATTR_THICKNESS_PARAM).AsDouble()

rebar_Type = List[Element](FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rebar)\
                        .WhereElementIsElementType()\
                        .ToElements())\
                        .Find(System.Predicate[System.Object](lambda r : r.get_Name() == "HA12 (Fe400)"))

# covers parameters    
c1 = width/2 + UnitUtils.ConvertToInternalUnits(0.05, units)
c2 = (floor_width - UnitUtils.ConvertToInternalUnits(0.05, units))
c3 = UnitUtils.ConvertToInternalUnits(0.05, units)
# spacing parameter
space = UnitUtils.ConvertToInternalUnits(0.15, units)
loops = []
Lines = []

for w in walls:
    bbx = w.get_BoundingBox(None)
    # height of the wall
    h = bbx.Max.Z - bbx.Min.Z
    lineB = w.Location.Curve
    Lines.append(lineB)
    #Create a continuous CurveLoop from the wall location curves as a horizontal rectangle. 
    line1 = Lines[0]
    end_pt1 = line1.GetEndPoint(1)
    L = line1.Length
    norm1 = line1.Direction.Normalize()
    vect1 = (XYZ(end_pt1.X, end_pt1.Y, end_pt1.Z + L)).Subtract(end_pt1)
    line2 = Line.CreateBound(end_pt1, end_pt1.Add((vect1).CrossProduct(norm1)))
    end_pt2 = line2.GetEndPoint(1)
    norm2 = line2.Direction.Normalize()
    vect2 = (XYZ(end_pt2.X, end_pt2.Y, end_pt2.Z + L)).Subtract(end_pt2)
    line3 = Line.CreateBound(end_pt2, end_pt2.Add((vect2).CrossProduct(norm2)))
    end_pt3 = line3.GetEndPoint(1)
    norm3 = line3.Direction.Normalize()
    vect3 = (XYZ(end_pt3.X, end_pt3.Y, end_pt3.Z + L)).Subtract(end_pt3)
    line4 = Line.CreateBound(end_pt3, end_pt3.Add((vect3).CrossProduct(norm3)))
    # get the expected Horizontal curveloop
    H_Loop = CurveLoop.Create(List[Curve]([line1, line2, line3, line4]))
    
    # create the vertical shape " U " for each wall
    vectB = XYZ(0,0, -c2)
    vectZ = XYZ(0,0, h + c2)
    lineB = lineB.CreateTransformed(Transform.CreateTranslation(vectB))
    lineA = Line.CreateBound(lineB.GetEndPoint(0).Add(vectZ), lineB.GetEndPoint(0))
    lineC = Line.CreateBound(lineB.GetEndPoint(1), lineB.GetEndPoint(1).Add(vectZ))
    # create curveloop with order
    curveLoop = CurveLoop.Create(List[Curve]([lineA, lineB, lineC]))
    plane = curveLoop.GetPlane()
    # create a translation of the curveloop to the first position
    trans = Transform.CreateTranslation(plane.Normal.Negate().Multiply(c1))
    curveLoop.Transform(trans)
    loops.append(list(curveLoop))

# create a translation of the horizontal curveloop to the first position
H_plane = H_Loop.GetPlane()
H_trans = Transform.CreateTranslation(H_plane.Normal.Multiply(c3))
H_Loop.Transform(H_trans)
H_plane = H_Loop.GetPlane()
H_vector = H_Loop.GetPlane().Normal
# create a multiple translation for the horizontal curveloop
H_Loop = [H_Loop]

h1 = h - UnitUtils.ConvertToInternalUnits(0.10, units)
n1 = h1 // space
H_offset = H_Loop[0].GetPlane().Normal.Multiply(space)
H_xform = Transform.CreateTranslation(H_offset)

count1 = 0
while count1 < n1:
    # Take the last CurveLoop in the list and create via transform
    H_Loop.append(H_Loop[-1].CreateViaTransform(H_xform))
    count1 += 1

# Grouping each pair of vertical CurveLoops to perform multiple translations
loop1 = []
loop2 = []
loop1.append(loops[0])
loop1.append(loops[1])
loop2.append(loops[2])
loop2.append(loops[3])

for i,j in zip(loop1, loop2):
    curveloop1 = [CurveLoop.Create(loops[0])]
    curveloop2 = [CurveLoop.Create(loops[1])]
    st = i[1].GetEndPoint(0)
    end = j[1].GetEndPoint(1)
    line = Line.CreateBound(st, end)
    n = line.Length // space
    offset1 = curveloop1[0].GetPlane().Normal.Negate().Multiply(space)
    xform1 = Transform.CreateTranslation(offset1)
    offset2 = curveloop2[0].GetPlane().Normal.Negate().Multiply(space)
    xform2 = Transform.CreateTranslation(offset2)
    count = 0
    while count < n:
        # Take the last CurveLoop in the list and create via transform
        curveloop1.append(curveloop1[-1].CreateViaTransform(xform1))
        curveloop2.append(curveloop2[-1].CreateViaTransform(xform2))
        count += 1

  
H_arrayIlist = List[CurveLoop]()
V_arrayIlist1 = List[CurveLoop]()
V_arrayIlist2 = List[CurveLoop]()

for i in H_Loop:
    H_arrayIlist.Add(CurveLoop.Create([j for j in i]))

for i in curveloop1:
   V_arrayIlist1.Add(CurveLoop.Create([j for j in i]))

for i in curveloop2:
   V_arrayIlist2.Add(CurveLoop.Create([j for j in i]))
    
# create rebars
with Transaction(doc, 'create rebars') as t :
    t.Start()
    validationResult = clr.Reference[RebarFreeFormValidationResult]()
    rebar1 = Rebar.CreateFreeForm(doc, rebar_Type, floors[0], H_arrayIlist, validationResult)
    rebar2 = Rebar.CreateFreeForm(doc, rebar_Type, floors[0], V_arrayIlist1, validationResult)
    rebar3 = Rebar.CreateFreeForm(doc, rebar_Type, floors[0], V_arrayIlist2, validationResult)
    t.Commit()

OUT = rebar1, rebar2, rebar3

Thanks.

your code has a syntax that is only compatible with IronPython try switching the Python engine

@c.poupin

I switched to the Python engine as you recommended, and my code finally worked as you can see below

Question: From what I understand, none of the API methods for creating rebars work with the CPython3 engine?

Thanks.

I believe they do, but you need to write and manage the code quite a bit differently, and this particular method might not be CPython compliant in your particular build.

@jacob.small

Do you have an example of creating rebars using CPython 3?

Thanks.

This will work in CPython3 - The validation result is an ‘out’ parameter so you just need to pass a dummy of the same type. In this instance RebarFreeFormValidationResult.Undefined

with Transaction(doc, 'create rebars') as t:
    t.Start()
    
    val_result = RebarFreeFormValidationResult.Undefined
    rebar1 = Rebar.CreateFreeForm(doc, rebar_Type, floors[0], H_arrayIlist, val_result)
    rebar2 = Rebar.CreateFreeForm(doc, rebar_Type, floors[0], V_arrayIlist1, val_result)
    rebar3 = Rebar.CreateFreeForm(doc, rebar_Type, floors[0], V_arrayIlist2, val_result)

    t.Commit()

Other general comments
You will need to test, however .NET collection types are usually not required - there are exceptions.
I prefer to use python builtins before .NET methods

rebar_Type = next(
    iter(
        rb
        for rb in FilteredElementCollector(doc)
        .OfCategory(BuiltInCategory.OST_Rebar)
        .WhereElementIsElementType()
        .ToElements()
        if rb.get_Name() == "HA12 (Fe400)"
    ),
    None,
)

Also shoudn’t vectZ = XYZ(0, 0, h + c2) just be vectZ = XYZ(0, 0, h)?

for info .ToElements() method is not necessarily mandatory FilteredElementCollector supports the IEnumerable interface for Elements

here a small comparison of methods on the FilteredElementCollector


# Load the Python Standard and DesignScript Libraries
import sys
import clr
import System
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
import Autodesk.Revit.DB as DB

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary, IEnumerable

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
from System.Linq import Enumerable

import timeit

def first_by_comprehension_with_toElements():
    result = next((x for x in DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType().ToElements() if "a" in x.Name), None)
    return result

def first_by_comprehension():
    result = next((x for x in DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType() if "a" in x.Name), None)
    return result
    

def first_by_ListFind():
    filterName = System.Predicate[System.Object](lambda e : "a" in e.Name)
    result = List[DB.Element](DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType()).Find(filterName)
    return result
    
def first_by_Linq():
    filterFunc = System.Func[DB.Element, System.Boolean](lambda e : "a" in e.Name)
    result = Enumerable.Where[DB.Element](DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType(), filterFunc)
    return Enumerable.FirstOrDefault[DB.Element](result)
    
    
def findAll_by_comprehension_with_toElements():
    result = [x for x in DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType().ToElements() if "a" in x.Name]
    return result

def findAll_by_comprehension():
    result = [x for x in DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType() if "a" in x.Name]
    return result
    

def findAll_by_ListFind():
    filterName = System.Predicate[System.Object](lambda e : "a" in e.Name)
    result = List[DB.Element](DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType()).FindAll(filterName)
    return result
    
def findAll_by_Linq():
    filterFunc = System.Func[DB.Element, System.Boolean](lambda e : "a" in e.Name)
    result = Enumerable.Where[DB.Element](DB.FilteredElementCollector(doc).OfClass(DB.Wall).WhereElementIsNotElementType(), filterFunc)
    return result
    
    
test_first_A = timeit.Timer(lambda: first_by_comprehension_with_toElements())      
test_first_B = timeit.Timer(lambda: first_by_comprehension())      
test_first_C = timeit.Timer(lambda: first_by_ListFind())      
test_first_D = timeit.Timer(lambda: first_by_Linq())

test_findAll_A = timeit.Timer(lambda: findAll_by_comprehension_with_toElements())      
test_findAll_B = timeit.Timer(lambda: findAll_by_comprehension())      
test_findAll_C = timeit.Timer(lambda: findAll_by_ListFind())      
test_findAll_D = timeit.Timer(lambda: findAll_by_Linq())

OUT = [ 
        f"findFirst_with_py_comprehension_with_toElements : {test_first_A.timeit(50):.4f} seconds" , 
        f"findFirst_with_py_comprehension : {test_first_B.timeit(50):.4f} seconds" , 
        f"findFirst_with_.Net_Find : {test_first_C.timeit(50):.4f} seconds",  
        f"findFirst_with_.Net_Linq : {test_first_D.timeit(50):.4f} seconds",
        "-----------------------------------------------",
        f"findAll_with_py_comprehension_with_toElements : {test_findAll_A.timeit(50):.4f} seconds" , 
        f"findAll_with_py_comprehension : {test_findAll_B.timeit(50):.4f} seconds" , 
        f"findAll_with_.Net_Find : {test_findAll_C.timeit(50):.4f} seconds",  
        f"findAll_with_.Net_Linq : {test_findAll_D.timeit(50):.4f} seconds",
      ]

Since Linq methods give the best results, I’m keeping my fingers crossed that one day Linq method extensions will be available on PythonNet3. :crossed_fingers:

@Mike.Buttery

I tried your syntax, and it worked perfectly.

Because I want the rebar base to be anchored in the floor, as you can see below.

The logic I used in the following part of the code for creating the horizontal rebars seems a little long to me. (I couldn’t build a CurveLoop directly in the main for loop by selecting lineB inside the iteration for each wall because I encountered the same error as before, specifically the “discontinuous CurveLoop” error. Also, I didn’t want to use your solution above, which uses graph theory to reorder the CurveLoop, since I need to study it carefully first.)… Do you have a simpler code logic than mine to suggest?

#Create a continuous CurveLoop from the wall location curves as a horizontal rectangle. 
    lineB = w.Location.Curve
    Lines.append(lineB)
    line1 = Lines[0]
    end_pt1 = line1.GetEndPoint(1)
    L = line1.Length
    norm1 = line1.Direction.Normalize()
    vect1 = (XYZ(end_pt1.X, end_pt1.Y, end_pt1.Z + L)).Subtract(end_pt1)
    line2 = Line.CreateBound(end_pt1, end_pt1.Add((vect1).CrossProduct(norm1)))
    end_pt2 = line2.GetEndPoint(1)
    norm2 = line2.Direction.Normalize()
    vect2 = (XYZ(end_pt2.X, end_pt2.Y, end_pt2.Z + L)).Subtract(end_pt2)
    line3 = Line.CreateBound(end_pt2, end_pt2.Add((vect2).CrossProduct(norm2)))
    end_pt3 = line3.GetEndPoint(1)
    norm3 = line3.Direction.Normalize()
    vect3 = (XYZ(end_pt3.X, end_pt3.Y, end_pt3.Z + L)).Subtract(end_pt3)
    line4 = Line.CreateBound(end_pt3, end_pt3.Add((vect3).CrossProduct(norm3)))
    # get the expected Horizontal curveloop
    H_Loop = CurveLoop.Create(List[Curve]([line1, line2, line3, line4]))

Thanks.

hi,
I had to add this to get them

OUT = findAll_by_Linq(),[a for a in findAll_by_Linq()]

thank you @you for sharing knowledge
Sincerely
christian.stan

@christian.stan

do you have an idea for my previous question?

Thanks.