Create a surface

Hey,

I am trying to translate my dynamo scripts to Pyhton, and i get stuck on this thing.

How can i create a surface with python.
In Dynamo i use the node “Surface.ByPatch”
It uses a polycurve to create a surface.

My goal is to create multiple surfaces.
Most overlap, and when they do i want them to merge in one surface.
At the end i get the perimiter curve and create a filledregion it.

In dynamo it looks like this:

@MelleH ,

Geometry in PyRevit is my weakness :wink: hope someone has mor input.

1 Like

Thanks for your reply!

I can’t see tho how these can be used to create a surface from a few lines and arcs.
im trying to create surfaces like shown below so i can merge them and get that perimeter so have 1 big curveloop.

Hi @MelleH,

Take a look at the DirectShape documentation.
In there you can find various ways to create geometries, via GeometryCreationUtilities or ShapeBuilders.

2 Likes

Thank you so much!!!

This was what i needed.
I was able to make solids of the curveloops.
Then i merge them and after that i get the curveloop from the top face.

Solids = []
for CurveLoop in CurveLoops:
    Solids.append(GeometryCreationUtilities.CreateExtrusionGeometry([CurveLoop], XYZ(0, 0, 1), MM_to_FEET(1000)))

SolidsNew = Solids[1:]
SolidsBase = Solids[0]
for Solid in SolidsNew:
    SolidsBase = BooleanOperationsUtils.ExecuteBooleanOperation(SolidsBase, Solid, BooleanOperationsType.Union)

TempCurves = []
VarLoop = []
VarZ = []
for SolFace in SolidsBase.Faces:
    for Loop in SolFace.GetEdgesAsCurveLoops():
        PointZ = []
        for Curve in Loop:
            PointZ.append(FEET_to_MM(Line_GetStartPoint(Curve).Z))
            PointZ.append(FEET_to_MM(Line_GetEndPoint(Curve).Z))
        if len(list(set(PointZ))) == 1:
            VarLoop.append(Loop)
            VarZ.append(PointZ[0])
CurveLoop =  GetItemAtIndex(VarLoop, (max(range(len(VarZ)), key=VarZ.__getitem__)))

for Curve in CurveLoop:
    TempCurves.append(Curve)
Create_DetailLineByLine(Flatten(TempCurves))
2 Likes

I’m glad you solved it!
Now that you broke free from Dynamo, there’s no need to always use lists for everything :wink:

The solid creation and union could be simplified by extracting out the core operations into functions and use the reduce function:

def extrude(curve):
    GeometryCreationUtilities.CreateExtrusionGeometry([curve], XYZ(0, 0, 1), MM_to_FEET(1000)))

def union(first, second):
    return BooleanOperationsUtils.ExecuteBooleanOperation(first, second, BooleanOperationsType.Union)

solid = extrude(CurveLoops[0])
solid = reduce(CurveLoops[1:], lambda p, n: union(p, extrude(n)), solid)

In the next block you’re extracting the topmost face; assuming the curveloops are closed, you only need to check one point for each curve.
Also, instead of using yet another list, you could directly use a set to hold the Z values.
No need to turn the set into a list to know how many items it holds!

def get_horizontal_face_z(curve_loop):
    zs = {FEET_to_MM(Line_GetStartPoint(c).Z) for c in curve_loop}
    return next(iter(zs)) if len(zs) == 1 else None

to retrieve all the horizontal curveloops with their z coordinate, you can create a generator:

def get_horizontal_faces_loops(solid):
    for face in solid.Faces:
        for loop in face.GetEdgesAsCurveLoops():
            z = get_horizontal_face_z(loop)
            if z is not None:
                yield loop, z

now you can just pick the topmost face by using max with a key function that reads the second item of the tuple, that is, the z coordinate

topmost_face_loop = max(get_horizontal_faces_loops(solid), key=lambda x: x[1])[0]

the TempCurves is unneeded, since you have your curves in topmost_face_loop (your CurveLoop);
If it’s not a list you could just turn it into one with

temp_curves = list(topmost_face_loop)
2 Likes

Thanks for the tips!
My python and dynamo knowlege is mostly self-taught using google, the forums and trial and error.
I’ll try to write scripts a bit more compact using these tricks.
It’s a bit hard to “read” for me now.
I usualy turn code snippets I think i’ll use in the future in to definitions and paste them in my template file.
But that’s mostly because i’m not very familiar with the lambda function and having a bunch of code in 1 row instead of spread out over multiple.
Thanks again!

Well, lambdas can be rewritten as functions for better readability:

def unite_extrusion(solid, curve):
    return union(solid, extrude(curve))

# ...
solid = reduce(CurveLoops[1:], unite_extrusion, solid) 

and for the key parameter of max, you could use operator.itemgetter:

from operator import itemgetter
# ...
topmost_face_loop = max(get_horizontal_faces_loops(solid), key=itemgetter(1))[0]

I agree that this can be tough if you are used to imperative, line-by-line scripting, but learning about functional programming, especially when dealing with transformations on collections (lists, sets, and so on) would make your developer time much, much easier in the long run!

2 Likes