Skip to content

Instantly share code, notes, and snippets.

@pgolay
Last active July 8, 2020 04:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pgolay/9d2609388329e9200c3a7b8a9a58b1df to your computer and use it in GitHub Desktop.
Save pgolay/9d2609388329e9200c3a7b8a9a58b1df to your computer and use it in GitHub Desktop.
import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
import System
import math
class DrawDeviationConduit(Rhino.Display.DisplayConduit):
def __init__(self, id1, id2, idx):
self.textSize = 24
self.curveColor = Rhino.ApplicationSettings.AppearanceSettings.FeedbackColor
self.hairScale = 0
self.intSamples = 32
self.interpCrv = None
self.id1 = id1
self.id2 = id2
self.edgeIdx = idx
self.blnDot = True
self.lineColor = None
self.posColor = System.Drawing.Color.Green
self.negColor = System.Drawing.Color.Red
self.m_dirty = False
self.bbox = Rhino.Geometry.BoundingBox()
self.Start()
def Calculate(self):
pi = math.pi
tol = sc.doc.ModelAbsoluteTolerance
x = self.edgeIdx
if self.edgeIdx == -1:
crv = rs.coercecurve (self.id1)
else:
tempBrep = rs.coercebrep(self.id1)
edge = tempBrep.Edges[self.edgeIdx]
crv = edge.ToNurbsCurve()
brepFace = rs.coercesurface (self.id2)
pc = Rhino.Geometry.Curve.PullToBrepFace(crv, brepFace, tol)
pc = pc[0]
# sc.doc.Objects.AddCurve(pc)
# return
brep = brepFace.Brep
srf = brepFace.UnderlyingSurface()
info = Rhino.Geometry.Curve.GetDistancesBetweenCurves(crv, pc, tol)
"""
Checking for partial overlaps -
rc 0
out double maxDistance, 1
out double maxDistanceParameterA, 2
out double maxDistanceParameterB, 3
out double minDistance, 4
out double minDistanceParameterA, 5
out double minDistanceParameterB 6
If the max or min deviation is on the end point of one of the curves, AND the vector between the ends
is not parallel to the surface normal there, then
find the closest point from that end back to the other curve and split off a subcrv to use instead.
"""
if info[0]:
end1 = False
end2 = False
rc,u,v = srf.ClosestPoint(crv.PointAtEnd)
if rc:
srfNorm = srf.NormalAt(u,v)
ptBase = srf.PointAt(u,v)
vec = crv.PointAtEnd-ptBase
if vec.IsParallelTo(srfNorm) == 0:
x = vec.IsParallelTo(srfNorm)
end2=True
# here, checking to see if the max deviation is on a curve end point
# and if so, is the closest point on a surface normal
# if it is not on a normal, find the sub curve for which the end is on a normal.
rc,u,v = srf.ClosestPoint(crv.PointAtStart)
if rc:
srfNorm = srf.NormalAt(u,v)
ptBase = srf.PointAt(u,v)
vec = crv.PointAtStart-ptBase
if vec.IsParallelTo(srfNorm) == 0:
y = vec.IsParallelTo(srfNorm)
end1=True
dom1 = crv.Domain
dom2 = pc.Domain
if end1:
crv2A = pc.ClosestPoint(crv.PointAt(dom1.Min))[1]
else:
crv2A = dom2.Min
if end2:
crv2B = pc.ClosestPoint(crv.PointAt(dom1.Max))[1]
else:
crv2B = dom2.Max
domList = sorted([crv2A,crv2B])
dom2Ex = Rhino.Geometry.Interval(domList[0], domList[1])
if end1:
crv1A = crv.ClosestPoint(pc.PointAt(dom2.Min))[1]
else:
crv1A = dom1.Min
if end2:
crv1B = crv.ClosestPoint(pc.PointAt(dom2.Max))[1]
else:
crv1B = dom1.Max
domList = sorted([crv1A,crv1B])
dom1Ex = Rhino.Geometry.Interval(domList[0], domList[1])
exCrv = crv.Trim(dom1Ex)
if exCrv is not None:
crv = exCrv
exCrvPC = pc.Trim(dom2Ex)
if exCrvPC is not None:
pc = exCrvPC
pars = crv.DivideByCount(self.intSamples,True)
self.pts = [crv.PointAt(par) for par in pars]
self.srfPts = [brep.ClosestPoint(pt) for pt in self.pts]
srfNorms = []
for pt in self.pts:
rc,u,v =srf.ClosestPoint(pt)
if rc:
srfNorms.append(srf.NormalAt(u,v))
else:
srfNorms.append (None)
self.Distances = []
self.Lines = []
info = Rhino.Geometry.Curve.GetDistancesBetweenCurves(crv, pc, tol)
pass
if info[0]:
if info[3] == pc.Domain.Max or info[3] == pc.Domain.Min:
maxPar = crv.ClosestPoint(pc.PointAt(info[3]))[1]
self.maxDist = crv.PointAt(maxPar).DistanceTo(pc.PointAt(info[3]))
else:
self.maxDist = info[1]
self.maxLoc = pc.PointAt(info[3])
rc, u, v = srf.ClosestPoint(self.maxLoc)
maxNorm = srf.NormalAt(u,v)
maxEndPt = crv.PointAt(info[2])
vecTest = maxEndPt-self.maxLoc
if vecTest.IsParallelTo(maxNorm) == -1:
self.maxDist = self.maxDist * -1
if info[6] == pc.Domain.Max or info[6] == pc.Domain.Min:
minPar = crv.ClosestPoint(pc.PointAt(info[6]))[1]
self.minDist = crv.PointAt(minPar).DistanceTo(pc.PointAt(info[6]))
else:
self.minDist = info[4]
self.minLoc = pc.PointAt(info[6])
rc, u, v = srf.ClosestPoint(self.minLoc)
minNorm = srf.NormalAt(u,v)
minEndPt = crv.PointAt(info[5])
vecTest = minEndPt-self.minLoc
ang = Rhino.Geometry.Vector3d.VectorAngle(vecTest,minNorm)
if ang > pi/2:
self.minDist = self.minDist * -1
targs = []
for i in range(len(self.pts)):
dist = True
vecTest = self.pts[i]- self.srfPts[i]
temp = vecTest.Length
testVec = vecTest.IsParallelTo(srfNorms[i])
if testVec== -1:
self.Distances.append(-1*temp)
#print "NEG"
elif testVec == 1:
#print "POS"
self.Distances.append(temp)
else:
self.Distances.append(None)
dist = False
crntDist = temp + (self.hairScale * temp)
vecTest.Unitize()
targ = self.srfPts[i] + vecTest*crntDist
if dist: targs.append(targ)
self.Lines.append(Rhino.Geometry.Line(self.srfPts[i], targ))
for pt in self.pts:
self.bbox.Union(pt)
for pt in self.srfPts:
self.bbox.Union(pt)
if self.hairScale != 0:
vUS = Rhino.Geometry.Vector3d.Unset
self.interpCrv = Rhino.Geometry.Curve.CreateInterpolatedCurve(targs, 3, Rhino.Geometry.CurveKnotStyle.Uniform, vUS, vUS)
self.maxLine = Rhino.Geometry.Line(self.maxLoc, maxEndPt)
self.minLine = Rhino.Geometry.Line(self.minLoc, minEndPt)
# a = self.maxLoc
# b = self.minLoc
# c = self.maxDist
# d = self.minDist
# e = self.minLine
# f = self.maxLine
# g = self.bbox
# h = self.Lines
# i = self.Distances
# j = self.interpCrv
return self.maxLoc, self.minLoc, self.maxDist, self.minDist, self.minLine, self.maxLine, self.bbox, self.Lines, self.Distances, self.interpCrv
def Start(self):
# Create event handlers
self.CreateEvents()
if sc.sticky.has_key('CRVDEVIATION_SAMPLES'):
self.intSamples = sc.sticky['CRVDEVIATION_SAMPLES']
if sc.sticky.has_key('CRVDEVIATION_HAIRSCALE'):
intScale = sc.sticky['CRVDEVIATION_HAIRSCALE']
self.hairScale = pow(2, intScale)/100
if sc.sticky.has_key('CRVDEVIATION_TEXT_USAGE'):
self.blnDot = sc.sticky['CRVDEVIATION_TEXT_USAGE']
# Enable conduit
self.Enabled = True
# Stop the conduit
def Stop(self):
# Remove event handlers
self.RemoveEvents()
# Disable conduit
self.Enabled = False
def CreateEvents(self):
Rhino.RhinoDoc.CloseDocument += self.OnCloseDocument
Rhino.RhinoDoc.ReplaceRhinoObject += self.OnReplaceObject
Rhino.RhinoDoc.DeleteRhinoObject += self.OnDeleteObject
Rhino.RhinoApp.Idle += self.OnIdle
# Remove Rhino event handlers
def RemoveEvents(self):
Rhino.RhinoDoc.CloseDocument -= self.OnCloseDocument
Rhino.RhinoDoc.ReplaceRhinoObject -= self.OnReplaceObject
Rhino.RhinoDoc.DeleteRhinoObject -= self.OnDeleteObject
Rhino.RhinoApp.Idle -= self.OnIdle
# RhinoDoc.ReplaceRhinoObject event handler
def OnReplaceObject(self, sender, e):
if e.ObjectId == self.id1 or e.ObjectId == self.id2 :
self.hairScale = 0
self.intSamples = 32
self.blnDot = True
if sc.sticky.has_key('CRVDEVIATION_SAMPLES'):
self.intSamples = sc.sticky['CRVDEVIATION_SAMPLES']
if sc.sticky.has_key('CRVDEVIATION_HAIRSCALE'):
intScale = sc.sticky['CRVDEVIATION_HAIRSCALE']
self.hairScale = pow(2, intScale)/100
if sc.sticky.has_key('CRVDEVIATION_TEXT_USAGE'):
self.blnDot = sc.sticky['CRVDEVIATION_TEXT_USAGE']
self.m_dirty = True
def OnDeleteObject(self, sender, e):
if e.ObjectId == self.id1 or e.ObjectId == self.id2 :
if not self.m_dirty:
self.Stop()
self.id1 = None
self.id2 = None
print "Curve deviation conduit has been turned off."
if sc.sticky.has_key("CurveDeviationConduit"):
sc.sticky.Remove("CurveDeviationConduit")
def OnIdle(self, sender, e):
if self.m_dirty == True:
if e.ObjectId == self.id1 or e.ObjectId == self.id2 :
self.calculate()
self.m_dirty = False
def OnCloseDocument(self, sender, e):
self.Stop()
self.id1 = None
self.id2 = None
def CalculateBoundingBox(self, e):
e.IncludeBoundingBox(self.bbox)
"""
str = "Max " + str(round((self.maxDist),4))
rect = e.Display.Measure2dText(str, self.maxLoc, False, 0.0, 12, "Arial");
if (rect.Width + (2*X_GAP) < width || rect.Height + (2*Y_GAP) < height)
{
// Cook up text location (lower right corner of viewport)
Point2d point = new Point2d(right - rect.Width - X_GAP, bottom - Y_GAP);
//e.Display.Draw2dRectangle(rect, Color.White, 1, Color.White);
e.Display.Draw2dText(str, Color.Black, point, false, 12, "Arial");
"""
def DrawForeground(self, e):
self.Calculate()
#str = "Max " + str(round((self.maxDist),4))
#rect = e.Display.Measure2dText(str, self.maxLoc, False, 0.0, 12, "Arial")
pass
for b in range(len(self.Lines)):
if self.Distances[b] is not None:
if self.Distances[b] >=0:
self.lineColor = self.posColor
else:
self.lineColor = self.negColor
e.Display.DrawLine(self.Lines[b], self.lineColor)
e.Display.DrawLine(self.maxLine, System.Drawing.Color.Aqua)
e.Display.DrawLine(self.minLine, System.Drawing.Color.Aqua)
if self.interpCrv !=None:
e.Display.DrawCurve(self.interpCrv, self.curveColor, 2)
def DrawOverlay(self, e):
self.Calculate()
if not self.blnDot:
e.Display.Draw2dText("Max " + str(round((self.maxDist),4)), System.Drawing.Color.White, self.maxLoc, True, self.textSize)
e.Display.Draw2dText("Min " + str(round((self.minDist),4)), System.Drawing.Color.White, self.minLoc, True, self.textSize)
else:
e.Display.DrawDot(self.maxLoc, "Max " + str(round((self.maxDist),4)), System.Drawing.Color.White, System.Drawing.Color.Black)
e.Display.DrawDot(self.minLoc, "Min " + str(round((self.minDist),4)), System.Drawing.Color.White, System.Drawing.Color.Black)
def DrawCurveSurfaceDeviation():
intSamples = 32
if sc.sticky.has_key('CRVDEVIATION_SAMPLES'):
intSamples = sc.sticky['CRVDEVIATION_SAMPLES']
blnDot = True
if sc.sticky.has_key('CRVDEVIATION_TEXT_USAGE'):
blnDot = sc.sticky['CRVDEVIATION_TEXT_USAGE']
intScale = 0
if sc.sticky.has_key('CRVDEVIATION_HAIRSCALE'):
intScale = sc.sticky['CRVDEVIATION_HAIRSCALE']
if sc.sticky.has_key("CurveDeviationConduit"):
conduit = sc.sticky["CurveDeviationConduit"]
conduit.Stop()
conduit = None
sc.sticky.Remove("CurveDeviationConduit")
sc.doc.Views.Redraw()
print "Curve deviation display has been turned off."
return
id1 = None
id2 = None
idx = None
ids = rs.SelectedObjects()
if ids:
if len(ids) < 3:
selCrv = False
selSrf = False
for id in ids:
if rs.IsCurve(id):
if selCrv:
selCrv = false
id1 = None
idx = None
rs.UnselectAllObjects()
else:
selCrv = True
id1 = id
idx = -1
if rs.IsSurface(id):
if selSrf:
selSrf = False
id2 = None
rs.UnselectAllObjects()
else:
selSrf = True
id2 = id
if not id1:
while True:
intSamples = 32
if sc.sticky.has_key('CRVDEVIATION_SAMPLES'):
intSamples = sc.sticky['CRVDEVIATION_SAMPLES']
blnDot = True
if sc.sticky.has_key('CRVDEVIATION_TEXT_USAGE'):
blnDot = sc.sticky['CRVDEVIATION_TEXT_USAGE']
intScale = 0
if sc.sticky.has_key('CRVDEVIATION_HAIRSCALE'):
intScale = sc.sticky['CRVDEVIATION_HAIRSCALE']
go = Rhino.Input.Custom.GetObject()
go.GeometryFilter = Rhino.DocObjects.ObjectType.Curve|Rhino.DocObjects.ObjectType.EdgeFilter
go.SubObjectSelect=True
go.SetCommandPrompt("Select the curve to test.")
opDot = Rhino.Input.Custom.OptionToggle(blnDot,"Text", "Dot")
go.AddOptionToggle("TextMode", opDot)
opScale = Rhino.Input.Custom.OptionInteger(intScale, 0, 10)
go.AddOptionInteger("HairScale", opScale)
opSamples = Rhino.Input.Custom.OptionInteger(intSamples, True, 8)
go.AddOptionInteger("Samples", opSamples)
go.AcceptNumber(False, True)
crvRC = go.Get()
if ( go.CommandResult() != Rhino.Commands.Result.Success ):
return
if crvRC == Rhino.Input.GetResult.Option:
blnDot = opDot.CurrentValue
sc.sticky['CRVDEVIATION_TEXT_USAGE'] = blnDot
intScale = opScale.CurrentValue
sc.sticky['CRVDEVIATION_HAIRSCALE'] = intScale
intSamples = opSamples.CurrentValue
sc.sticky['CRVDEVIATION_SAMPLES'] = intSamples
continue
if crvRC == Rhino.Input.GetResult.Number:
intScale = go.Number()
sc.sticky['CRVDEVIATION_HAIRSCALE'] = intScale
if crvRC == Rhino.Input.GetResult.Object:
obj = go.Object(0)
idx = obj.GeometryComponentIndex.Index
id1 = obj.ObjectId
break
if not id1: return
if not id2:
id2 = rs.GetObject("Select the target surface",filter=8)
if not id2: return
conduit = None
conduit = DrawDeviationConduit( id1, id2, idx)
sc.sticky["CurveDeviationConduit"] = conduit
sc.sticky['CRVSRFDEVIATION_INPUT'] = id2
conduit.Enabled = True
sc.doc.Views.Redraw()
print "Curve-surface deviation display is now on."
if __name__ == '__main__':DrawCurveSurfaceDeviation()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment