addBoltv0.2.py (11.5 KB) (sticky settings)
addBolt_v0.3.py (11.7 KB) (multiple bolts)
addBolt_v0.4.py (15.5 KB) (dynamic preview)
addBolt_v0.51.py (16.5 KB) (now works in all unit systems)
addBolt_v0.6.py (16.7 KB)
addBolt_v0.7.py (16.8 KB)
Attached is a working script that adds metric bolts (hex, flat head, socket allen, M3 - M8). I wrote this mainly as a coding exercise.
Would like to get some feedback from more experienced coders if the way i did this with classes is done properly or not. Suggestions for optimizing the structure of the code highly appreciated…
edit: would like to record last selected bolt options to change the defaults at next run…how?
edit: uploaded version 0.4 which that allows to add multiple bolts of the same type to (multiple) surfaces and added preview of the to be placed geometry
If you have additional suggestions for improvement of the functionality that would be nice too.
show the code (or download the above .py file)
from __future__ import division
import rhinoscriptsyntax as rs
from Rhino.ApplicationSettings import *
import Rhino
import math
import scriptcontext as sc
from System.Drawing import Color
import Rhino.RhinoMath as rm
"""
addBolt version 0.7
this script will add a bolt (named block) in Metric size to the rhino document
Bolt must be oriented on a surface and can be aligned with a center point
needed user input : metric size, length, bolt type, (center)point on (poly)surface
to do:
-rewrite Bolt creation in Rhino Common so that redraw can stay on for showing selected center point
changes in v0.2:
- added sticky values to size, length and type to make insertion of multiple bolts of the same type faster
changes in v0.3:
-add multiple bolts of same type to one or multiple surfaces
changes in v0.4:
-added preview during placement
-replaced most rhinoscriptsyntax with Rhino common calls to avoid adding objects to scene too early
changes in v0.5:
-now works in all unit systems
-fixed bug in socket_allen
changes in v0.6:
-refactored spiral creation and made it generic with extra bolt property 'lenght_with_thread'
which should make it applicable to all kinds of bolts that might get added later
-split user options from main function
changes in v.0.7:
-all options in command line at once (code by Willem Derks)
script written by Gijs de Zwart
www.studiogijs.nl
"""
class Bolt():
def __init__(self,msize,length):
diameter = { 'M3':3.0, 'M4':4.0, 'M5':5.0, 'M6':6.0, 'M8':8.0 }
pitch = { 'M3':0.5, 'M4':0.7, 'M5':1.0, 'M6':1.0, 'M8':1.25 }
self.diameter = diameter[msize]
self.length = length
self.pitch = pitch[msize]
self.name = msize+"x"+str(length)
self.breps = []
self.curves = []
self.msize = msize
self.length_with_head = False
def __repr__(self):
cls = self.__class__.__name__
return '{}({}x{})'.format(cls, self.msize, self.length)
def __str__(self):
return self.name
class hexBolt(Bolt):
def __init__(self, msize, length):
Bolt.__init__(self, msize, length)
head_width = { 'M3':5.5, 'M4':7.0, 'M5':8.0, 'M6':10, 'M8':13.0 }
head_height = { 'M3':2.2, 'M4':3.0, 'M5':3.2, 'M6':4.0, 'M8':5.3 }
self.head_width = head_width[msize]
self.head_height = head_height[msize]
self.name += "(DIN993)"
class flatBolt(Bolt):
def __init__(self,msize,length):
Bolt.__init__(self, msize, length)
head_width = { 'M3':6.0, 'M4':8.0, 'M5':10.0, 'M6':12.0, 'M8':16.0 }
head_height = { 'M3':1.7, 'M4':2.3, 'M5':2.8, 'M6':3.3, 'M8':4.4 }
hex_socket = { 'M3':2.0, 'M4':2.5, 'M5':3.0, 'M6':4.0, 'M8':5.0 }
self.head_width = head_width[msize]
self.head_height = head_height[msize]
self.head_dheight= self.head_height-(self.head_width-self.diameter)/2
self.hex_socket = hex_socket[msize]
self.name += "(DIN7991)"
class DIN912Bolt(Bolt):
def __init__(self,msize,length):
Bolt.__init__(self, msize, length)
head_width = { 'M3':5.5, 'M4':7.0, 'M5':8.5, 'M6':10.0, 'M8':13.0 }
head_height = { 'M3':3.0, 'M4':4.0, 'M5':5.0, 'M6':6.0, 'M8':8.0 }
hex_socket = { 'M3':2.5, 'M4':3.0, 'M5':4.0, 'M6':5.0, 'M8':6.0 }
hex_socket_depth = { 'M3':1.3, 'M4':2.0, 'M5':2.5, 'M6':3.0, 'M8':3.5 }
self.head_width = head_width[msize]
self.head_height = head_height[msize]
self.hex_socket = hex_socket[msize]
self.hex_socket_depth = hex_socket_depth[msize]
self.name += "(DIN912)"
def getBoltProperties():
#collect previous settings
msize = sc.sticky["msize"] if sc.sticky.has_key("msize") else 0
boltlength= sc.sticky["boltlength"] if sc.sticky.has_key("boltlength") else 0
bolttype= sc.sticky["bolttype"] if sc.sticky.has_key("bolttype") else 0
get_o = Rhino.Input.Custom.GetOption()
get_o.SetCommandPrompt("Set Bolt Parameters")
msizes = ['M3','M4','M5','M6','M8']
msizelist_index = get_o.AddOptionList("Metric_size", msizes, msize)
lengths=['12mm','16mm','20mm','25mm','30mm','40mm','50mm','60mm','70mm','80mm']
lengthlist_index = get_o.AddOptionList("Bolt_Length", lengths, boltlength)
headstyles=['hex_head','flat_head','socket_allen']
headstylelist_index = get_o.AddOptionList("Bolt_Type", headstyles, bolttype)
#accept Enter as an option
get_o.AcceptNothing(True)
while True:
# perform the get operation. This will prompt the user to
# input a point, but also allow for command line options
# defined above
get_rc = get_o.Get()
if get_o.CommandResult()!= Rhino.Commands.Result.Success:
return None,None,None
if get_rc==Rhino.Input.GetResult.Nothing:
pass
elif get_rc==Rhino.Input.GetResult.Option:
if get_o.OptionIndex() == msizelist_index:
msize = get_o.Option().CurrentListOptionIndex
#print 'set ',msizes[msize]
if get_o.OptionIndex() == lengthlist_index:
boltlength = get_o.Option().CurrentListOptionIndex
#print 'set ',lengths[boltlength]
if get_o.OptionIndex() == headstylelist_index:
bolttype = get_o.Option().CurrentListOptionIndex
#print 'set ',headstyles[bolttype]
continue
break
sc.sticky["msize"] = msize
sc.sticky["boltlength"] = boltlength
sc.sticky["bolttype"] = bolttype
size = msizes[msize]
length = lengths[boltlength]
length = int(length[:-2])
headstyle = headstyles[bolttype]
return size, length, headstyle
def createBolt():
# *************************************************
# *********** CREATE THE BOLT GEOMETRY ************
# *************************************************
size, length, headstyle = getBoltProperties()
if headstyle=='hex_head':
#create a hex bolt object
bolt = hexBolt(size, length)
#create the head
radius = bolt.head_width/2/math.cos(math.pi/6)#calculate radius of circle that circumscribes the hexagon
hexagon = Rhino.Geometry.Circle(Rhino.Geometry.Plane.WorldXY, radius)
hexagon = hexagon.ToNurbsCurve(1,6)
head = Rhino.Geometry.Extrusion
vec = Rhino.Geometry.Vector3d(0,0,bolt.head_height)
head = head.CreateExtrusion(hexagon, vec)
head = head.ToBrep()
head = head.CapPlanarHoles(sc.doc.ModelAbsoluteTolerance)
#create threaded part
circle = Rhino.Geometry.Circle(Rhino.Geometry.Plane.WorldXY, bolt.diameter/2)
thread = Rhino.Geometry.Cylinder(circle,-bolt.length).ToBrep(True, True)
#add objects to bolt
bolt.breps.append(head)
bolt.breps.append(thread)
#add cosmetic thread
addCosmeticThread(bolt)
#scale objects
bolt = setScale(bolt)
addBolt(bolt)
if headstyle=='flat_head':
#create a flat head bolt object and some helper points
bolt = flatBolt(size,length)
bolt.length_with_head = True
point1 = Rhino.Geometry.Point3d(0,0,0)
point2 = Rhino.Geometry.Point3d(0,0,-bolt.head_height)#position of head end
plane2 = Rhino.Geometry.Plane(point2, Rhino.Geometry.Plane.WorldXY.ZAxis)
point3 = Rhino.Geometry.Point3d(0,0,-bolt.head_dheight)#position of edge start
plane3 = Rhino.Geometry.Plane(point3, Rhino.Geometry.Plane.WorldXY.ZAxis)
#create the hexagon socket
radius = bolt.hex_socket/2/math.cos(math.pi/6)#calculate radius of circle that circumscribes the hexagon
hexagon = Rhino.Geometry.Circle(Rhino.Geometry.Plane.WorldXY, radius)
hexagon = hexagon.ToNurbsCurve(1,6)
socket = Rhino.Geometry.Extrusion
vec = Rhino.Geometry.Vector3d(0,0,-bolt.head_height)
socket = socket.CreateExtrusion(hexagon, vec)
socket = socket.ToBrep()
#create the head
circle1 = Rhino.Geometry.Circle(plane2,bolt.diameter/2)
circle2 = Rhino.Geometry.Circle(plane3,bolt.head_width/2)
circle3 = Rhino.Geometry.Circle(Rhino.Geometry.Plane.WorldXY, bolt.head_width/2)
edge = Rhino.Geometry.Cylinder(circle2, bolt.head_dheight).ToBrep(False,False)
#create top face
curvelist = Rhino.Collections.CurveList()
curvelist.Add(hexagon)
curvelist.Add(circle3)
topface = Rhino.Geometry.Brep.CreatePlanarBreps(curvelist)[0]
no_pt=Rhino.Geometry.Point3d.Unset
straight_loft=Rhino.Geometry.LoftType.Straight
head = Rhino.Geometry.Brep.CreateFromLoft([circle1.ToNurbsCurve(),circle2.ToNurbsCurve()],no_pt,no_pt,straight_loft,False)[0]
circle = Rhino.Geometry.Circle(plane2, bolt.diameter/2)
thread = Rhino.Geometry.Cylinder(circle,-bolt.length+bolt.head_height).ToBrep(True, False)
tol = sc.doc.ModelAbsoluteTolerance
#stitch everything together
head.Join(edge, tol, False)
head.Join(topface, tol, False)
head.Join(thread,tol, False)
head.Join(socket, tol, True)
head = head.CapPlanarHoles(sc.doc.ModelAbsoluteTolerance)
bolt.breps.append(head)
#add cosmetic thread
addCosmeticThread(bolt)
#scale objects
bolt = setScale(bolt)
addBolt(bolt)
if headstyle=='socket_allen':
#create a flat head bolt object and some helper points
bolt = DIN912Bolt(size,length)
point1 = Rhino.Geometry.Point3d(0,0,0)
point2 = Rhino.Geometry.Point3d(0,0,bolt.head_height)#position of head end
plane2 = Rhino.Geometry.Plane(point2, Rhino.Geometry.Plane.WorldXY.ZAxis)
point3 = Rhino.Geometry.Point3d(0,0,(bolt.head_height-bolt.hex_socket_depth))
plane3 = Rhino.Geometry.Plane(point3, Rhino.Geometry.Plane.WorldXY.ZAxis)
#create the hexagon socket
radius = bolt.hex_socket/2/math.cos(math.pi/6)#calculate radius of circle that circumscribes the hexagon
hexagon = Rhino.Geometry.Circle(plane2, radius)
hexagon = hexagon.ToNurbsCurve(1,6)
socket = Rhino.Geometry.Extrusion
vec = Rhino.Geometry.Vector3d(0,0,-bolt.hex_socket_depth)
socket = socket.CreateExtrusion(hexagon, vec)
socket = socket.ToBrep()
#create the head
circle = Rhino.Geometry.Circle(plane2, bolt.head_width/2)
head = Rhino.Geometry.Cylinder(circle, -bolt.head_height).ToBrep(False,False)
#create threaded part
circle1 = Rhino.Geometry.Circle(Rhino.Geometry.Plane.WorldXY, bolt.diameter/2)
thread = Rhino.Geometry.Cylinder(circle1, -bolt.length).ToBrep(True, False)
#create top face
curvelist = Rhino.Collections.CurveList()
curvelist.Add(hexagon)
curvelist.Add(circle)
topface = Rhino.Geometry.Brep.CreatePlanarBreps(curvelist)[0]
circle3 = Rhino.Geometry.Circle(Rhino.Geometry.Plane.WorldXY,bolt.head_width/2)
curvelist1= Rhino.Collections.CurveList()
curvelist1.Add(circle1)
curvelist1.Add(circle3)
bottomface = Rhino.Geometry.Brep.CreatePlanarBreps(curvelist1)[0]
tol=sc.doc.ModelAbsoluteTolerance
#stitch everything together:
head.Join(topface, tol, False)
head.Join(bottomface, tol, False)
head.Join(socket, tol, False)
head.Join(thread, tol, True)
head = head.CapPlanarHoles(tol)
bolt.breps.append(head)
#add cosmetic thread
addCosmeticThread(bolt)
#scale objects
bolt = setScale(bolt)
addBolt(bolt)
def addBolt(bolt):
# ***********************************************
# ******** ADDING THE BOLT TO THE SCENE *********
# ***********************************************
old_osnap_state = ModelAidSettings.OsnapModes #record Osnap state to reset later
bolts=0
while True:
Rhino.UI.MouseCursor.SetToolTip("select surface or face")
# this function ask the user to select a point on a surface to insert the bolt on
# Surface to orient on
gs = Rhino.Input.Custom.GetObject()
gs.SetCommandPrompt("Surface to orient on")
gs.GeometryFilter = Rhino.DocObjects.ObjectType.Surface
gs.Get()
if gs.CommandResult()!=Rhino.Commands.Result.Success:
ModelAidSettings.OsnapModes = old_osnap_state #reset to previous Osnap state
print str(bolts) + " "+bolt.__repr__() + " bolt(s) added to the document"
Rhino.UI.MouseCursor.SetToolTip("")
return
objref = gs.Object(0)
# get selected surface object
obj = objref.Object()
if not obj:
ModelAidSettings.OsnapModes = old_osnap_state #reset to previous Osnap state
print str(bolts) + " "+bolt.__repr__() + " bolt(s) added to the document"
return
# get selected surface (face)
global surface
surface = objref.Surface()
if not surface: return Rhino.Commands.Result.Failure
# Unselect surface
obj.Select(False)
# Point on surface to orient to / activate center Osnap
cen()
gp=Rhino.Input.Custom.GetPoint()
gp.SetCommandPrompt("Point on surface to orient to")
gp.Constrain(surface, False)
#display the geometry to be created
gp.DynamicDraw+=drawbreps
gp.Get()
if gp.CommandResult()!=Rhino.Commands.Result.Success:
ModelAidSettings.OsnapModes = old_osnap_state #reset to previous Osnap state
print str(bolts) + " "+bolt.__repr__() + " bolt(s) added to the document"
return
getrc, u, v = surface.ClosestPoint(gp.Point())
if getrc:
getrc, target_plane = surface.FrameAt(u,v)
if getrc:
# Build transformation
source_plane = Rhino.Geometry.Plane.WorldXY
xform = Rhino.Geometry.Transform.PlaneToPlane(source_plane, target_plane)
# Do the transformation
objs=bolt.breps
rhobj=[]
for brep in objs :
rhobj.append(sc.doc.Objects.AddBrep(brep))
for curve in bolt.curves:
rhobj.append(sc.doc.Objects.AddCurve(curve))
rs.AddBlock(rhobj,[0,0,0],bolt.name, True)
newbolt = rs.InsertBlock2(bolt.name, xform)
bolts +=1
def addCosmeticThread(bolt):
normal = Rhino.Geometry.Point3d(0,1,0)
if not bolt.length_with_head:
start = Rhino.Geometry.Point3d(0,0,0)
turns=bolt.length/bolt.pitch
lengthvec = Rhino.Geometry.Vector3d(0,0,-bolt.length)
else:
start = Rhino.Geometry.Point3d(0,0,-bolt.head_height)
turns = (bolt.length-bolt.head_height)/bolt.pitch
lengthvec = Rhino.Geometry.Vector3d(0,0,-bolt.length+bolt.head_height)
radius = bolt.diameter/2
spiral = Rhino.Geometry.NurbsCurve.CreateSpiral(start, lengthvec,normal,bolt.pitch,turns,radius, radius)
bolt.curves.append(spiral)
def setScale(bolt):
scale = rm.UnitScale(sc.doc.ModelUnitSystem.Millimeters, sc.doc.ModelUnitSystem)
xf=Rhino.Geometry.Transform.Scale(Rhino.Geometry.Plane.WorldXY, scale,scale,scale)
for brep in bolt.breps:
brep.Transform(xf)
for curve in bolt.curves:
curve.Transform(xf)
#copy to displaybreps for preview
global displaybreps
displaybreps = bolt.breps
return bolt
def drawbreps(gp, args ):
getrc, u, v = surface.ClosestPoint(args.CurrentPoint)
if getrc:
getrc, target_plane = surface.FrameAt(u,v)
xf = Rhino.Geometry.Transform.PlaneToPlane(Rhino.Geometry.Plane.WorldXY,target_plane)
for brep in displaybreps:
new = brep.Duplicate()
new.Transform(xf)
args.Display.DrawBrepWires(new, Color.Aquamarine,2)
def cen():
#this function turns off all Osnaps except center Osnap
cen = Rhino.ApplicationSettings.OsnapModes.Center
Rhino.ApplicationSettings.ModelAidSettings.Osnap = True
Rhino.ApplicationSettings.ModelAidSettings.OsnapModes = cen
if __name__ == "__main__":
createBolt()