Is there a way to access the LINEWORKSHRINKWRAP command within Dynamo? I haven’t been able to find a corresponding method for it in the Civil 2026 API—if it’s even exposed as a method.
Any guidance or suggestions would be greatly appreciated. Thanks!
There is no node for this command, as it’s not a specific API method. Rather, it’s an undocumented command that makes use of other API calls.
If you’re trying to call this from within a dynamo graph, you’ll want to use either a python node to write a small bit of code that calls the command for you or the Camber node:
I’d be interested in seeing if this was possible in Dynamo.
So what the LINEWORKSHRINKWRAP command does is creates a polyline around the exterior of the selected geometry. You can see in the screenshot below the white line generated by the command on the blue objects.
I can conceptualize how to intersect the geometry, and I guess then you can then split the geometry and discard any pieces contained by the original objects.
Where the command struggles (and I guess I would too) is around objects like these polylines below, where it creates two separate boundaries rather than one single one. Or it has inconsistent results, in the example on the left it technically should have created three different boundaries (or one big one that didn’t strictly adhere to the geometry like the first two did).
For this one it’s fairly easy in both concepts and execution:
I would patch the curves into a surface, union them, and pull the perimeter curves.
Something like this: Surface.Patch > PolySurface.ByJoinedSurfaces > Surface.PerimeterCurves
For this it’s a bit harder as I can’t tell which you started with and which you ended with:
I think you are providing the curves on the right, extruding and the tool is giving you the outline on the left, but there are two instead of a single one?
If that’s the case, building a convex hull might suffice, but you’re not going to get a lot more of the space. You’d need to document the logic you’d like to progress though (the hardest part of automation) to get progress here beyond this:
########################################
############## Properties ##############
########################################
__author__ = 'Jacob Small'
__version__ = '0.1.0'
__description__ = "Attempts to giftwrap a set of oints from the minimum point "
__DynamoBuilds__ = "3.5 but likely good in all builds from 2.13 to 3.6"
__ReleaseNotes__ = "POC only - not fit for production"
__Dependancies__ = "none"
__Copyright__ = "2025, Autodesk Inc."
__license__ = "Apache 2"
# Load the Python Standard and DesignScript Libraries
import clr #import the common language runtime (CLR) to the Python environment so we can use .NET libraries
clr.AddReference('ProtoGeometry') #add the protogeoemtry library to the clr
from Autodesk.DesignScript.Geometry import * #import the geometry class to the Python environment
pnts = IN[0] #the original list of points from IN[0] of the Dynamo environment
perimPoints = [] #an empty list to hold the perimer points in the order they'll return
activeVector = Vector.YAxis() #the initial vector for validation
pnts.sort(key = lambda x: x.X) #sort the points by their X value
perimPoints.append(pnts[0]) #append the first point (now with the lowest X value) to the perimeter points
def PopPointByMinimumAngle(basePoint, prevVector, pointSet): #function to get the next point by the minimum vector angle about the Z axis between the previous vector and the vector from the base point to the test point
validPoints = [i for i in pointSet if not i.DistanceTo(basePoint) ==0] #remove any points which are atop the base point to prevent zero length vector issues
angles = [Vector.AngleAboutAxis(prevVector, Vector.ByTwoPoints(basePoint,i), Vector.ZAxis()) for i in validPoints] #get the angle about the Z axis between the previous vector and the vector formed by the base point and each valid point
minVal = min(angles) #get the minimum angle from the list of angles
indx = angles.index(minVal) #get the index of the minimum angle
nextPoint = validPoints.pop(indx) #pop the point at the minimum angle index
return nextPoint, pointSet #return the next point and the point set (which no longer has the next point in it)
maxAttempts = len(pnts) #set the maximum number of attempts for teh wrapping algorithm
attempt = 1 #set teh attempt to 1
unclosed = True #set unclosed to true
while attempt < maxAttempts and unclosed: #while the attempt is less then the maximum number of attempts and unclosed is true
attempt+=1 #increase the attempt number
wrapSegment = PopPointByMinimumAngle(perimPoints[-1], activeVector, pnts) #get the minimum point by angle from the active vector, and the list of other points
pnts = wrapSegment[1] #set pnts to teh list of points without the next point in it
perimPoints.append(wrapSegment[0]) #append the next point to the list of perimeter points
activeVector = Vector.ByTwoPoints(perimPoints[-2],perimPoints[-1]) #set teh active vector to the vector formed between the last two points in the list
if perimPoints[-1].DistanceTo(perimPoints[0]) == 0: unclosed = False #if the distance from the last point to the first point is 0 set unclosed to false to end the loop
hull = Polygon.ByPoints(perimPoints) #build a polygon by the points
OUT = hull #return the hull
Thanks! I’ll check out the surface nodes you recommended. I would have never have thought to create the surface and extract exterior. That is definitely a lot easier than the route I was headed down with intersecting curve geometry.
On the second example you are correct on your assumption, a little more context on my part would have gone a long way. The right side is the original geometry, the middle is the LINEWORKSHRINKWRAP command results on top of the original geometry, and the left side is just LINEWORKSHRINKWRAP results.
The python sample you came up with is great (and really well documented). I will definitely be taking a look at that as a starting point, I like the approach you have looking the minimum point and the angle. I was trying to figure out what the current commands approach is and I think it is something similar; but there also seems to be some logic about not reaching past points with some length criteria, and then something about avoiding pinching out by creating extra points if needed.
Definitely a fun puzzle to take a look at. Thanks again for the push in the right direction, if I can figure something out I will definitely post it up.
Edit:
Here is the drawing I was using in case anyone else wants to take a look