Last active
July 8, 2020 04:19
-
-
Save pgolay/9d2609388329e9200c3a7b8a9a58b1df to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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