Skip to content

Instantly share code, notes, and snippets.

@pgolay
Created June 14, 2019 03:44
Show Gist options
  • Save pgolay/b34fb43926dacef61a5b05276b9eb8b5 to your computer and use it in GitHub Desktop.
Save pgolay/b34fb43926dacef61a5b05276b9eb8b5 to your computer and use it in GitHub Desktop.
import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
import math
class GO_FilterPrevious(Rhino.Input.Custom.GetObject):
#Make sure not to allow selection of previously
#dogboned curves in the same run of the script
def __init__(self, ids):
self.m_ids = ids
self.SubObjectSelect = False
def CustomGeometryFilter(self, rhino_object, geometry, component_index):
if not geometry.IsClosed:
return False
if not geometry.TryGetPlane()[0]:
return False
if rhino_object.Id in self.m_ids:
return False
return True
def DogBone():
pi = math.pi
#make a .001" offset
inch = Rhino.UnitSystem.Inches
units = sc.doc.ModelUnitSystem
offset = .001*Rhino.RhinoMath.UnitScale(inch, units)
#Get radius, side (hole/outer) and depth factor defsults
rad = 1
if sc.sticky.has_key("DOGBONE_RAD"):
rad = sc.sticky["DOGBONE_RAD"]
outer=False
if sc.sticky.has_key('DOGBONE_SIDE'):
outer = sc.sticky['DOGBONE_SIDE']
factor = .05
if sc.sticky.has_key('DOGBONE_FACTOR'):
factor = sc.sticky['DOGBONE_FACTOR']
tol = sc.doc.ModelAbsoluteTolerance
aTol = sc.doc.ModelAngleToleranceRadians
#keep a list of ids that have already been dogboned
ids = []
#loop to allow any number of curves to be dogboned
while True:
#Loop to allow settings changes per curve
while True:
go = Rhino.Input.Custom.GetObject()
go = GO_FilterPrevious(ids)
#go.GeometryAttributeFilter=Rhino.Input.Custom.GeometryAttributeFilter.Sub
go.GeometryFilter = Rhino.DocObjects.ObjectType.Curve
go.DisablePreSelect()
go.SubObjectSelect = False
oprad = Rhino.Input.Custom.OptionDouble(rad,True, tol*10)
go.AddOptionDouble("CutterRadius", oprad)
opSide = Rhino.Input.Custom.OptionToggle(outer, "Yes", "No")
go.AddOptionToggle("Hole", opSide)
opFactor = Rhino.Input.Custom.OptionDouble(factor,0, 1 )
go.AddOptionDouble("DepthFactor", opFactor)
go.AcceptNumber(True, False)
#go.SetCustomGeometryFilter(is_new_id)
ret = go.Get()
if( go.CommandResult() != Rhino.Commands.Result.Success ):
return
if ret == Rhino.Input.GetResult.Option:
rad = oprad.CurrentValue
sc.sticky["DOGBONE_RAD"] = rad
outer = opSide.CurrentValue
sc.sticky['DOGBONE_SIDE'] = outer
factor = opFactor.CurrentValue
sc.sticky['DOGBONE_FACTOR'] = factor
continue
if ret == Rhino.Input.GetResult.Number:
rad = go.Number()
sc.sticky["DOGBONE_RAD"] = rad
continue
if ret == Rhino.Input.GetResult.Object:
objRef = go.Object(0)
point = objRef.SelectionPoint()
crv, par = objRef.CurveParameter()
obj = go.Object(0).Object()
Id = objRef.ObjectId
break
simOp = Rhino.Geometry.CurveSimplifyOptions.All
simpleCrv = crv.Simplify(Rhino.Geometry.CurveSimplifyOptions.All, tol, aTol)
if simpleCrv is not None:crv = simpleCrv
# the kink discontinuity
discoType= Rhino.Geometry.Continuity.G1_locus_continuous
disc = [crv.PointAtStart]
crv = crv.ToNurbsCurve()
dom = crv.Domain
t0 = dom.Min
t1 = dom.Max
#a list of kink parameters
discpar = []
get_next = True
planeRC, crvPlane = crv.TryGetPlane()
arcInt = Rhino.Geometry.Interval(pi/2, (3*pi)/2)
boolCrvs = []
#boolCrvs.append(crv)
#Set containment of the test point according to
#whether the cut is a hole or outer
pContainment = Rhino.Geometry.PointContainment.Outside
if outer:
pContainment = Rhino.Geometry.PointContainment.Inside
while get_next == True:
get_next, t = crv.GetNextDiscontinuity(discoType, t0, t1)
if get_next:
pt = crv.PointAt(t)
disc.append(crv.PointAt(t))
discpar.append(t)
t0 = t
circ = Rhino.Geometry.Circle(pt, rad+offset).ToNurbsCurve()
#Iteresect the circle with the input curve
iInfo = Rhino.Geometry.Intersect.Intersection.CurveCurve(crv,circ, tol, tol)
#Set the test point between the two intersection points.
testPt = ( iInfo[0].PointA + iInfo[1].PointA)/2
#Make a direction vector bisecting the kink
vecX = testPt-pt
vecX.Unitize()
#check if the test point is inside or outside the curve and skip
#or not, accordingly.
if crv.Contains(testPt) == pContainment:
continue
#set a plane at the point using the direction vector
vecY = Rhino.Geometry.Vector3d(vecX)
vecY.Rotate(pi/2, crvPlane.ZAxis)
plane = Rhino.Geometry.Plane(pt, vecX, vecY)
#move the plane inward in the corner to avoid too much cut.
#User sets the amount
plane.Origin = pt + vecX*(1-factor)*rad
#make a new circle at the plane origin such that the seam is on the direction vector
circle = Rhino.Geometry.Circle(plane, rad+offset)
#find the quads where the tangent is parallel to the direction vector
quad1 = circle.PointAt(pi/2)
quad2 = circle.PointAt((3*pi)/2)
#make lines from the quads in vector direction
line1 = Rhino.Geometry.Line(quad1,quad1+vecX)
line2 = Rhino.Geometry.Line(quad2,quad2+vecX)
# extend the lines to the curve.
info1 = Rhino.Geometry.Intersect.Intersection.CurveLine(crv, line1, tol, tol)
iPts = [item.PointA for item in info1]
iP1 = iPts[rs.PointArrayClosestPoint(iPts, quad1)]
info2 = Rhino.Geometry.Intersect.Intersection.CurveLine(crv, line2, tol, tol)
iPts = [item.PointA for item in info2]
iP2 = iPts[rs.PointArrayClosestPoint(iPts, quad2)]
#make new curves bounding the region to be cut and add them to a list
lines = []
lines.append(Rhino.Geometry.LineCurve(Rhino.Geometry.Line(quad1, iP1)))
lines.append(Rhino.Geometry.LineCurve(Rhino.Geometry.Line(quad2, iP2)))
lines.append(Rhino.Geometry.LineCurve(Rhino.Geometry.Line(iP1, iP2)))
lines.append(Rhino.Geometry.ArcCurve(Rhino.Geometry.Arc(circle,arcInt)))
x = Rhino.Geometry.Curve.JoinCurves(lines)
if len(Rhino.Geometry.Intersect.Intersection.CurveSelf(x[0], tol)) == 0 :
boolCrvs.append(x[0])
else:
break
pass
#BooleanUnion or Difference the curve regions with the input curve.
if not outer:
res = Rhino.Geometry.Curve.CreateBooleanUnion(boolCrvs + [crv])
else:
res = Rhino.Geometry.Curve.CreateBooleanDifference( crv, boolCrvs)
if len(res) > 0:
db = res[0]
#make sure the curve was actually changed before replacing it.
if db.GetLength() != crv.GetLength:
db = db.Simplify(Rhino.Geometry.CurveSimplifyOptions.All, tol, aTol)
sc.doc.Objects.Replace(Id, db )
ids.append(Id)
sc.doc.Views.Redraw()
if __name__ == "__main__":
DogBone()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment