Skip to content

Instantly share code, notes, and snippets.

@pgolay
Last active July 21, 2018 03:21
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/4b0bb678c036346a8efa2596cb8abcdc to your computer and use it in GitHub Desktop.
Save pgolay/4b0bb678c036346a8efa2596cb8abcdc to your computer and use it in GitHub Desktop.
import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc
import System.Guid
import math
def GetMarkerOptions(crvLength):
tol = sc.doc.ModelAbsoluteTolerance
lFonts = [item.lower() for item in sorted(list(set(Rhino.DocObjects.Font.AvailableFontFaceNames())))]
fonts = [item.title() for item in lFonts] #to use in a list box if needed
for item in fonts:
if Rhino.DocObjects.Font(item).IsSingleStrokeFont:
fonts.insert(0, fonts.pop(fonts.index(item)))
while True:
num = 0
if sc.sticky.has_key("SEW_START_NUMBER"):
num = sc.sticky["SEW_START_NUMBER"]
prefix = None
if sc.sticky.has_key("SEW_NUMBER_PREFIX"):
prefix = sc.sticky["SEW_NUMBER_PREFIX"]
addNum = True
if sc.sticky.has_key("ADD_SEW_NUMBERS"):
addNum = sc.sticky["ADD_SEW_NUMBERS"]
dblSize = 1
if sc.sticky.has_key("SEAM_MARKER_SIZE"):
dblSize = sc.sticky["SEAM_MARKER_SIZE"]
divLen = 1
if sc.sticky.has_key("SEW_DIV_LENGTH"):
divLen = sc.sticky["SEW_DIV_LENGTH"]
divNum = 5
if sc.sticky.has_key("SEW_DIV_NUM"):
divNum = sc.sticky["SEW_DIV_NUM"]
mode = False
if sc.sticky.has_key("SEW_DIV_MODE"):
mode = sc.sticky["SEW_DIV_MODE"]
extension = 0
if sc.sticky.has_key("SEW_HASH_EXTENSION"):
extension = sc.sticky["SEW_HASH_EXTENSION"]
fontName= "Arial"
if sc.sticky.has_key("SEW_FONTNAME"):
fontName = sc.sticky ["SEW_FONTNAME"].title()
hole = False
if sc.sticky.has_key("SEW_HOLE_STATUS"):
hole = sc.sticky ["SEW_HOLE_STATUS"]
go = Rhino.Input.Custom.GetOption()
go.AcceptNothing(True)
strGo = "Curve segment length is " + str(crvLength) + " Press Enter to accept the current settings."
go.SetCommandPrompt(strGo)
go.AcceptNumber(True, True)
opSize = Rhino.Input.Custom.OptionDouble(dblSize)
opNum = Rhino.Input.Custom.OptionInteger(num)
opDivLen = Rhino.Input.Custom.OptionDouble(divLen)
opDivNum = Rhino.Input.Custom.OptionInteger(divNum)
opMode = Rhino.Input.Custom.OptionToggle(mode, "Number", "Length")
opAddNum = Rhino.Input.Custom.OptionToggle(addNum, "No", "Yes")
opHashExtension = Rhino.Input.Custom.OptionDouble(extension, True, tol*10)
opFont = go.AddOption("Font", fontName)
opHole = Rhino.Input.Custom.OptionToggle(hole, "No", "Yes")
go.AddOptionToggle("Mode",opMode)
go.AddOptionInteger("CurrentNumber", opNum)
go.AddOptionDouble("MarkerSize", opSize)
#Decide what division option to add
if mode:
go.AddOptionDouble("DivisionLength", opDivLen)
else:
go.AddOptionInteger("DivisionNumber", opDivNum)
go.AddOptionDouble("HashMarkExtension", opHashExtension)
go.AddOptionToggle("Numbers", opAddNum)
go.AddOptionToggle("Hole", opHole)
result = go.Get()
if( go.CommandResult() != Rhino.Commands.Result.Success ):
return False, mode,dblSize,num,divLen,divNum, addNum, fontName, extension, hole
if result == Rhino.Input.GetResult.Number:
divNum = int(go.Number())
sc.sticky["SEW_DIV_NUM"] = divNum
continue
if result == Rhino.Input.GetResult.Nothing:
sc.sticky["SEW_START_NUMBER"]= num
return True, mode,dblSize,num,divLen,divNum, addNum, fontName, extension, hole
if result == Rhino.Input.GetResult.Option:
opIdx = go.OptionIndex()
if opIdx == opFont:
#fontRC, tempFont = Rhino.Input.RhinoGet.GetString("Font name", True, fontName)
gf = Rhino.Input.Custom.GetString()
gf.SetCommandPrompt("Set new marker font.")
gf.AddOption("List")
fontRC = gf.Get()
if fontRC == Rhino.Input.GetResult.Option:
tempFont = rs.ListBox(fonts, "Set marker font", fonts[fonts.index(fontName)])
else:
tempFont == gf.String()
if tempFont.lower() in lFonts:
fontName = tempFont
#sc.doc.Fonts.FindOrCreate(fontName,False, False)
sc.sticky["SEW_FONTNAME"]= fontName.title()
mode = opMode.CurrentValue
dblSize = opSize.CurrentValue
num = opNum.CurrentValue
divLen = opDivLen.CurrentValue
divNum = opDivNum.CurrentValue
addNum = opAddNum.CurrentValue
extension = opHashExtension.CurrentValue
hole = opHole.CurrentValue
sc.sticky["ADD_SEW_NUMBERS"] = addNum
sc.sticky["SEW_NUMBER"] = num
sc.sticky["SEW_START_NUMBER"]= num
sc.sticky["SEAM_MARKER_SIZE"] = dblSize
sc.sticky["SEW_DIV_LENGTH"] = divLen
sc.sticky["SEW_DIV_NUM"] = divNum
sc.sticky["SEW_DIV_MODE"] = mode
sc.sticky["SEW_HASH_EXTENSION"] = extension
sc.sticky["SEW_HOLE_STATUS"] = hole
continue
def EdgeDivider():
pi = math.pi
style = Rhino.Geometry.CurveOffsetCornerStyle.None
count = 0
startNum = 0
if sc.sticky.has_key("SEW_START_NUMBER"):
startNum = sc.sticky["SEW_START_NUMBER"]
def PlanarClosedCurveFilter(rhObject, geometry, componentIndex):
pcc = False
if geometry.IsPlanar and geometry.IsClosed:
pcc = True
return pcc
while count < 2:
count +=1
grp = rs.AddGroup()
edgeStr = "Select a curve segment to mark near starting end."
if count > 1:
edgeStr = "Select the second curve segment near the starting end."
#Get the curve to divide
ge = Rhino.Input.Custom.GetObject()
ge.GeometryFilter = Rhino.DocObjects.ObjectType.Curve
ge.SetCustomGeometryFilter(PlanarClosedCurveFilter)
ge.EnableHighlight(False)
ge.DisablePreSelect()
ge.SetCommandPrompt(edgeStr)
rc = ge.Get()
if( ge.CommandResult() != Rhino.Commands.Result.Success ):
return
if rc == Rhino.Input.GetResult.Object:
objref = ge.Object(0)
tempCrv = objref.Curve()
full_crv, curve_parameter = objref.CurveParameter()
tempPt = full_crv.PointAt(curve_parameter)
if not isinstance(tempCrv, Rhino.Geometry.PolyCurve):
if isinstance(tempCrv, Rhino.Geometry.PolylineCurve):
full_crv = Rhino.Geometry.PolyCurve()
for i in range(tempCrv.PointCount-1):
full_crv.Append(Rhino.Geometry.LineCurve(tempCrv.Point(i),tempCrv.Point(i+1)))
if not full_crv.IsClosed:
print "Could not make a closed curve"
return
curve_parameter = full_crv.ClosestPoint(tempPt)[1]
else:
simStyle = Rhino.Geometry.CurveSimplifyOptions.RebuildLines|Rhino.Geometry.CurveSimplifyOptions.RebuildArcs
tempCrv = tempCrv.Simplify(simStyle, .001, Rhino.RhinoMath.ToRadians(1))
if not isinstance(full_crv, Rhino.Geometry.NurbsCurve):
seg_par = full_crv.SegmentCurveParameter(curve_parameter)
idx = full_crv.SegmentIndex(curve_parameter)
e = full_crv.SegmentCurve(idx)
eCrv = e.ToNurbsCurve()
else:
eCrv = full_crv
#Fake flashing on selection
xx = sc.doc.Objects.AddCurve(eCrv)
tObj = sc.doc.Objects.Find(xx)
sc.doc.Views.FlashObjects([tObj], True)
sc.doc.Objects.Delete(tObj)
pPt = objref.SelectionPoint()
if count == 1: crvLength = round(eCrv.GetLength(), 3)
#Get the options for dividing and marking the edges
if count == 1:
RC, mode,dblSize,num,divLen,divNum,addNum, fontName, extension, hole = GetMarkerOptions(crvLength)
if not RC: return
if count > 1:
if sc.sticky.has_key("SEW_START_NUMBER"):
num = sc.sticky["SEW_START_NUMBER"]
if not eCrv: return
#start counting from the picked end of the curve
eDom = eCrv.Domain
x = eCrv.ClosestPoint(pPt)
if eCrv.ClosestPoint(pPt)[1]>eDom.Mid:
eCrv.Reverse()
#print "Reversed"
#Set the right kind of curve division
if mode:#Use length of division
pars = eCrv.DivideByLength(divLen, True)
else: #Use number of divisions
pars = eCrv.DivideByCount(divNum, True)
WXY = Rhino.Geometry.Plane.WorldXY
WXY.Origin = eCrv.PointAtStart
planeRC,crvPlane = full_crv.TryGetPlane()
if planeRC:
planeAngle = Rhino.Geometry.Vector3d.VectorAngle(WXY.ZAxis, crvPlane.ZAxis)
if planeAngle > pi/2: crvPlane.Flip()
else:
crvPlane = WXY
vecPerp = eCrv.TangentAt(pars[1])
vecPerp.Rotate(pi/2,crvPlane.ZAxis)
pt = eCrv.PointAt(pars[1])
if full_crv.Contains(pt+vecPerp) == Rhino.Geometry.PointContainment.Inside:
if hole: vecPerp = vecPerp*-1
offsetCrv = eCrv.Offset(pt + -1*vecPerp, crvPlane.ZAxis ,dblSize,.001, style)[0]
else:
if hole: vecPerp = vecPerp*-1
offsetCrv = eCrv.Offset(pt + vecPerp, crvPlane.ZAxis ,dblSize,.001, style)[0]
sc.doc.Objects.AddCurve(offsetCrv)
for i in range(len(pars)):
offset=True
pt = eCrv.PointAt(pars[i])
#rs.AddPoint(pt)
rc, plane = eCrv.PerpendicularFrameAt(pars[i])
circle = Rhino.Geometry.Circle(plane, dblSize)
#Intersect a circle with the World XY plane to get two points marking the ends of the hash mark
ppp = Rhino.Geometry.Intersect.Intersection.CurvePlane(circle.ToNurbsCurve(), crvPlane, .001)
if ppp is None:
print "The curve plane may not be parallel to the World XY plane."
return
p1 = ppp[0].PointA
p2 = ppp[1].PointA
if offsetCrv.PointAt(offsetCrv.ClosestPoint(pt)[1]).DistanceTo(p1) > offsetCrv.PointAt(offsetCrv.ClosestPoint(pt)[1]).DistanceTo(p2):
vecY = pt-p1
else:
vecY = pt-p2
hashMark = Rhino.Geometry.LineCurve(pt, pt+vecY)
vecY.Unitize
if extension > 0:
hashMark = hashMark.Extend(Rhino.Geometry.CurveEnd.Start, extension, Rhino.Geometry.CurveExtensionStyle.Line)
#if numbering is asked for by the user, get the correct plane etc, and add number-shaped curves.
if addNum: #Numbering enabled
directionsMatch=False
vecX= Rhino.Geometry.Vector3d(vecY)
vecX.Rotate( -pi/2,crvPlane.ZAxis)
tPlane = Rhino.Geometry.Plane(pt,vecX,vecY)
if eCrv.FrameAt(pars[i])[1].XAxis.IsParallelTo( tPlane.XAxis) == 1:
directionsMatch=True
blnClose = True
if Rhino.DocObjects.Font(fontName).IsSingleStrokeFont: blnClose = False
text = Rhino.Geometry.Curve.CreateTextOutlines(str(num),fontName, .5*dblSize, 0, blnClose, tPlane,.8*dblSize, tolerance=.001)
#TEXT = Rhino.Geometry.TextEntity()
#TEXT.Plane = tPlane
#TEXT.Text = str(num)
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomLeft
#TEXT.FontIndex = sc.doc.Fonts.FindOrCreate("Arial",False, False)
#sc.doc.Objects.AddText(TEXT)
if text:
textBB = Rhino.Geometry.BoundingBox()
for txtCrv in text:
textBB.Union(txtCrv.GetBoundingBox(tPlane))
pass
corners = textBB.GetCorners()
pToP = Rhino.Geometry.Transform.PlaneToPlane(Rhino.Geometry.Plane.WorldXY,tPlane)
for u in range(8):
corners[u] = pToP*corners[u]
tempPt = corners[1]
if directionsMatch:
if count > 1:
if i > 0:
xXform = (pt-corners[1])*1.1
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomRight
else:
xXform = None
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomLeft
else:
if i > 0:
xXform = (pt-corners[1])*1.1
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomRight
else:
xXform = None
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomLeft
else:
if count > 1:
if i < len(pars)-1:
xXform = (pt-corners[1])*1.1
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomRight
else:
xXform = None
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomLeft
else:
if 0< i < len(pars)-1:
xXform = None
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomLeft
elif i == len(pars)-1 :
xXform = None
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomLeft
elif i == 0:
xXform = (pt-corners[1]).Length*eCrv.TangentAtStart*1.1
#TEXT.Justification= Rhino.Geometry.TextJustification.BottomRight
#xXform.Reverse()
#Adjust the Y location
yXform = tPlane.YAxis*.2*dblSize
#yXform.Reverse()
tempIds = []
#tPlane.Rotate( pi, tPlane.ZAxis)
#TEXT.Plane = tPlane
#TEXT.Justification= Rhino.Geometry.TextJustification.TopRight
#sc.doc.Objects.AddText(TEXT)
for txtCrv in text:
if xXform is not None: txtCrv.Translate(xXform)
txtCrv.Translate(yXform)
tempIds.append(sc.doc.Objects.AddCurve(txtCrv))
rs.AddObjectsToGroup(tempIds, rs.AddGroup())
rs.AddObjectsToGroup(tempIds, grp)
addId = System.Guid.NewGuid()
crvId = sc.doc.Objects.AddCurve(hashMark)
rs.AddObjectToGroup(crvId, grp)
rs.SetUserText(crvId, "SeamMarkerId", str(addId))
addId = None
if addNum: num += 1
sc.sticky["SEW_NUMBER"] = num
sc.doc.Objects.UnselectAll()
sc.doc.Views.Redraw()
sc.sticky["SEW_START_NUMBER"] = num
if __name__ == "__main__": EdgeDivider()
@pgolay
Copy link
Author

pgolay commented Jul 11, 2018

Added faked subobject flash on selection.

@pgolay
Copy link
Author

pgolay commented Jul 19, 2018

Allows planar curves in any plane. The script will attempt to orient text curves to face up in WorldXY as much as possible.

@pgolay
Copy link
Author

pgolay commented Jul 20, 2018

First crack at adding the ability to set a font and accommodate single stroke fonts.

@pgolay
Copy link
Author

pgolay commented Jul 20, 2018

Allow extension of the hash marks.

@pgolay
Copy link
Author

pgolay commented Jul 21, 2018

Added 'Hole=Yes/No' toggle option to offset to the inside of the curve if set to Yes.

@pgolay
Copy link
Author

pgolay commented Jul 21, 2018

Filter single stroke fonts to the top of the list in the Font option > List

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment