Reading the docs, the Curve.Offstet method should return an Array with more than one curve when the curve offset results in self intersections: " If the original curve had kinks or the offset curve had self intersections, you will get multiple segments in the output array.", but this is not happening. The output is just the overlaping portion of the resulting offset, I think its a bug:
from scriptcontext import doc
import Rhino
import rhinoscriptsyntax as rs
from Rhino import Geometry as rg
plane = rg.Plane.WorldXY
tol = doc.ModelAbsoluteTolerance
off = x.Offset(plane, 6, tol, rg.CurveOffsetCornerStyle(2))
print off
a = off
I tried the 3 offset methods already (with vector and point), with the same result. How can I get the complete output array, or the offset without trim?
I made a (very messy) workaround code in python that works in R7, if anyone interested I can clean the code and pass it to a C# component. It works with nurbs but needs aditional code for almost-intersecting curves.
The steps:
Detect if the curve self intersects, and then trim it
Find if the resultant curves can be closed
Obtain the segments of the open lines and offset it
The code (I plan to clean it up when I have time):
from scriptcontext import doc
import Rhino
import rhinoscriptsyntax as rs
from Rhino import Geometry as rg
from ghpythonlib.parallel import run
plane = rg.Plane.WorldXY
tol = doc.ModelAbsoluteTolerance
c = []
d = []
def shatterAll(crvs):
if len(crvs) == 1:
return crvs
else:
params = []
for i in range(len(crvs)):
params.append([])
for i in range(len(crvs)):
for j in range(i+1, len(crvs)):
try:
events = rg.Intersect.Intersection.CurveCurve(crvs[i], crvs[j], tol, tol)
for event in events:
params[i].append(event.ParameterA)
params[j].append(event.ParameterB)
except:
pass
sub_curves = []
for i in range(len(crvs)):
if params[i]:
split = crvs[i].Split(params[i])
for split in split:
sub_curves.append(split)
else:
sub_curves.append(crvs[i])
return sub_curves
def offsetCrvs(x):
#the nasty mess is needed?
defaultOffset = x.Offset(plane, distance, tol, rg.CurveOffsetCornerStyle(2))
if defaultOffset[0].IsClosed:
b = []
#verify if curve interscets itself:
int = rg.Intersect.Intersection.CurveSelf(x, tol)
intCount = int.Count
if intCount > 0:
params = []
for event in int:
params.append(event.ParameterA)
params.append(event.ParameterB)
preSplit = rg.Curve.Split(x, params)
for i, crvSplit in enumerate(preSplit):
if crvSplit.IsClosed:
if str(crvSplit.ClosedCurveOrientation()) == 'Clockwise':
preSplit[i].Reverse()
else:
preSplit = [x]
a = preSplit
valid = []
#TODO: verify if bug will occur
for crvS in preSplit:
crvS = crvS.ToNurbsCurve()
#get curve segments
if isinstance(crvS, rg.NurbsCurve):
SpanCount = crvS.SpanCount
spanParams = []
for i in range(SpanCount):
spanParams.append(crvS.SpanDomain(i)[0])
spanParams.append(crvS.SpanDomain(i)[1])
splitAtSpans = crvS.Split(spanParams)
toSegments = splitAtSpans
else:
toSegments = crvS
try:
segments = toSegments.DuplicateSegments()
except:
segments = toSegments
#print segments
for crv in segments:
#verify if is closed
offset = crv.Offset(plane, distance, tol, rg.CurveOffsetCornerStyle(2))
if offset != None:
b.append(offset[0])
#join reusults
joined = rg.Curve.JoinCurves(b)
#verify self intersections
for crv in joined:
selfInt = rg.Intersect.Intersection.CurveSelf(crv, tol)
intersectionCount = selfInt.Count
#split the damn crv:
if intersectionCount > 0:
params = []
for event in selfInt:
params.append(event.ParameterA)
params.append(event.ParameterB)
split = crv.Split(params)
#filter the thingy things
for crvs in split:
#verify the distance from the input crv
minDist = crvs.ClosestPoints(x)
minDist = minDist[1].DistanceTo(minDist[2])
if minDist >= (distance-tol):
valid.append(crvs)
else:
valid.append(crv)
#need to cleanup the mess:
dist_ = distance
shatter = shatterAll(valid)
if distance < 0:
dist_ = abs(dist_)
for crvs in shatter:
cps = x.ClosestPoints(crvs)
dist = cps[1].DistanceTo(cps[2])
if dist > (dist_-tol):
if crvs.IsValid:
if crvs.GetLength() > tol:
d.append(crvs.ToNurbsCurve())
join = rg.Curve.JoinCurves(d, dist_)
for crv in join:
c.append(crv.ToNurbsCurve())
else:
c.append(defaultOffset[0])
return c
run(offsetCrvs, x, True)
if bothSides:
distance = -distance
run(offsetCrvs, x, True)```
inputs: x[curve], distance[float], bothSides[bool]
output: c