Thanks for the response Dale,
Here’s a test script I generated to demonstrate this:
#! python3
import time
import System
import System.Collections.Generic
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
import math
import Rhino.Geometry as rg
import System.Drawing
import Rhino.DocObjects as OT
def SetupDocument(unitSystem):
if rs.UnitSystem != unitSystem:
rs.UnitSystem(unitSystem, True)
print("Units converted to " + rs.UnitSystemName(False, False, True, True))
objectsTable = sc.doc.Objects
layerTable = sc.doc.Layers
absoluteDimensionTolerance = rs.UnitAbsoluteTolerance()
relativeDimensionTolerance = rs.UnitRelativeTolerance()
#angleTolerance = rs.UnitAngleTolerance()
angleTolerance = 0.05 #Tested best angle tolerance for most stuff
return objectsTable, layerTable, absoluteDimensionTolerance, relativeDimensionTolerance, angleTolerance
def SelectSingleGeometry(prompt, geometryFilters, doPreselect, doSelect):
selectedID = rs.GetObject(prompt, geometryFilters, doPreselect, doSelect)
selectedObject = sc.doc.Objects.FindId(selectedID)
selectedGeometry = selectedObject.Geometry
return selectedID, selectedObject, selectedGeometry
def ProcessGeometry(brep, doRepair, doForceOutwards, doFindNull):
### REPAIR
if doRepair:
print ("Attempting to repair geometry...")
brep.Repair(absoluteDimensionTolerance)
print("Brep repaired")
brep.Standardize()
print("Brep standardized")
brep.Compact()
print("Brep compacted")
### FORCE OUTWARDS
if doForceOutwards:
if brepSelectedGeometry.IsSolid == True:
print("Brep is solid")
if brepSelectedGeometry.SolidOrientation == rg.BrepSolidOrientation.Inward:
brepSelectedGeometry.Flip
print("Polysurface flipped, now it is facing" + brepSelectedGeometry.SolidOrientation.ToString())
### FIND NULL
if doFindNull:
if brepSelectedGeometry.GetArea() == None:
print("Null surfaces detected.")
# Do something about them or not?
# Find the face IDs, save them, and prompt the user later whether he wants to extract them or not
else:
print ("No null surfaces detected.")
print("Repair finished!")
def CreateEdgeLoopsFromNakedEdges(allNakedEdges):
groupContainer = list()
workNakedEdges = allNakedEdges
# As long as there are edges that haven't been tested:
while workNakedEdges:
# Initialize the chain
chain = list()
chain.append(workNakedEdges[0])
chainStart = chain[0].StartVertex.VertexIndex
chainEnd = chain[0].EndVertex.VertexIndex
workNakedEdges.pop(0)
number = len(allNakedEdges) * 4
# As long as the end vertex isn't equal to the Start Vertex:
while chainEnd != chainStart:
if workNakedEdges:
for edge in workNakedEdges:
if (chainEnd == edge.StartVertex.VertexIndex):
#print ("End to start " + str(chainEnd) + " -> " + str(edge.StartVertex.VertexIndex))
chain.append(edge)
chainEnd = edge.EndVertex.VertexIndex
number = len(allNakedEdges)*4
workNakedEdges.remove(edge)
elif (chainEnd == edge.EndVertex.VertexIndex):
#print ("End to end " + str(chainEnd) + " -> " + str(edge.EndVertex.VertexIndex))
chain.append(edge)
chainEnd = edge.StartVertex.VertexIndex
number = len(allNakedEdges)*4
workNakedEdges.remove(edge)
elif (chainStart == edge.StartVertex.VertexIndex):
#print ("Start to start " + str(chainStart) + " -> " + str(edge.StartVertex.VertexIndex))
chain.insert(0,edge)
chainStart = edge.EndVertex.VertexIndex
number = len(allNakedEdges)*4
workNakedEdges.remove(edge)
elif (chainStart == edge.EndVertex.VertexIndex):
#print ("Start to end " + str(chainStart) + " -> " + str(edge.EndVertex.VertexIndex))
chain.insert(0,edge)
chainStart = edge.StartVertex.VertexIndex
number = len(allNakedEdges)*4
workNakedEdges.remove(edge)
elif (chainStart == chainEnd):
#print ("Chain is closed!")
break
else:
if (chainStart == chainEnd):
#print ("Chain is closed!")
break
# Crash prevention...
number -= 1
if number <= 0:
#print ("Loop timeout! "+str(chainStart)+ " -> " + str(chainEnd) + " can't find match.")
print (str(edge.StartVertex.VertexIndex) + " " + str(edge.EndVertex.VertexIndex))
break
'''
chainedges = list()
for edge in chain:
chainedges.append(edge.EdgeIndex)
print(chainedges)
'''
groupContainer.append(chain)
#print ("Container made")
#print (len(groupContainer))
return groupContainer
def CreatePartEdges(brepGeometry):
workEdges = brepGeometry.Edges
edgeCurves = list()
nakedEdges = list()
for edge in workEdges:
if edge.Valence == Rhino.Geometry.EdgeAdjacency.Naked:
edgeCurves.append(edge.EdgeCurve)
nakedEdges.append(edge)
edgeLoops = CreateEdgeLoopsFromNakedEdges(nakedEdges)
curvesV2 = list()
for group in edgeLoops:
curveLoop = list()
for edge in group:
curveLoop.append(edge.EdgeCurve)
curvesV2.append(curveLoop)
# Join edge curves in loops
curvesV3 = list()
planarEdgeCurves = list()
nonPlanarEdgeCurves = list()
circleEdgeCurves = list()
for curveLoop in curvesV2:
loopedEdges = Rhino.Geometry.Curve.JoinCurves(curveLoop)
if loopedEdges:
for loop in loopedEdges:
curvesV3.append(loopedEdges[0])
if loop.IsCircle(relativeDimensionTolerance):
circleEdgeCurves.append(loop)
elif loop.IsEllipse(relativeDimensionTolerance):
circleEdgeCurves.append(loop)
elif loop.IsPlanar(relativeDimensionTolerance):
planarEdgeCurves.append(loop)
else:
nonPlanarEdgeCurves.append(loop)
print (len(planarEdgeCurves), len(nonPlanarEdgeCurves), len(circleEdgeCurves))
return planarEdgeCurves, nonPlanarEdgeCurves, circleEdgeCurves, curvesV3
def ExtrudeEdgesNormalToSurface(brepGeometry):
nakedEdges = list()
extrusions = list()
workEdges = brepGeometry.Edges
for edge in workEdges:
if edge.Valence == Rhino.Geometry.EdgeAdjacency.Naked:
nakedEdges.append(edge)
for nakedEdge in nakedEdges:
adjacentFace = nakedEdge.AdjacentFaces()[0]
edgeCurve = nakedEdge.EdgeCurve
extrudedCurve = edgeCurve.OffsetTangentToSurface(brepGeometry.Faces[adjacentFace], System.Double(3.0))
extrudedSurface = Rhino.Geometry.Brep.CreateFromLoft([edgeCurve, extrudedCurve], Rhino.Geometry.Point3d.Unset, Rhino.Geometry.Point3d.Unset, Rhino.Geometry.LoftType.Straight, False)
extrusions.append(extrudedSurface)
return extrusions
# Setup
objectsTable, layerTable, absoluteDimensionTolerance, relativeDimensionTolerance, angleTolerance = SetupDocument(2)
# Select the brep to process
brepSelectedID, brepSelectedObject, brepSelectedGeometry = SelectSingleGeometry("Select a polysurface", rs.filter.polysurface, True, True)
allFaces = brepSelectedGeometry.Faces
allEdges = brepSelectedGeometry.Edges
# Process geometry
ProcessGeometry(brepSelectedGeometry, True, True, True)
planarEdgeCurves, nonPlanarEdgeCurves, circleEdgeCurves, allEdgeCurves = CreatePartEdges(brepSelectedGeometry)
extrusions = ''
for curveSet in allEdgeCurves:
extrusions = ExtrudeEdgesNormalToSurface(brepSelectedGeometry)
for extrusion in extrusions:
GUID = objectsTable.AddBrep(extrusion[0])
And the file I used for the testing. The result (computed in my machine) is in a different layer to the main surface. Used Rhino 8.4.24044.150001.
Reference3dm.3dm (1.4 MB)
The goal of the script is to generate a constant height surface that is tangent (G1) to the geometry, which then will be closed, sewn, and used as a base for more operations involving normal, tangent, and linear extrusions from the edges.
The difficulty I found is that, since I need to perform this edge by edge, some edges do not extrude in the same direction as the neighbour’s, within the same base surface. I’m guessing that it has to do with the orientation of the trim, but I would really appreciate your insight; and any suggestions on how to make this extrusion direction predictable.
Lastly, what I’m doing is similar to a Ribbon as in Curve.RibbonOffset, but it’s abstracted to 3 dimensions using custom logic, and has to discriminate between offsetting tangent or normal.
Thanks in advance,