Last active
July 21, 2018 03:21
-
-
Save pgolay/4b0bb678c036346a8efa2596cb8abcdc 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 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() |
Allows planar curves in any plane. The script will attempt to orient text curves to face up in WorldXY as much as possible.
First crack at adding the ability to set a font and accommodate single stroke fonts.
Allow extension of the hash marks.
Added 'Hole=Yes/No' toggle option to offset to the inside of the curve if set to Yes.
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
Added faked subobject flash on selection.