def xulaconnector(): screw_xuout = 2.5 screw_xuin = 1.5 screw_toph = 1 offset = 5 length = offset + THICK_WALL # XULA2 is attached to the top with two screws screw_xula = hscrew(screw_xuout, screw_toph, screw_xuin, length) screws_xula = screw_xula + right(58)(screw_xula) screws_xula += up(48.8)(screws_xula) xula_base = screws_xula # Raspberry connector xula2 rsp_cnctr = cube([58 - 2 * (screw_xuin + THICK_WALL * 0.5), length, 6]) xula_base += translate([screw_xuin + THICK_WALL * 0.5, -length, -3 + 48.8])(hole()(rsp_cnctr)) # add stickit connector top_height = 4 # top height screw in mm top_r = 3.5 # top r screw in mm shaft_r = 2 screw_stick = hscrew(top_r, top_height, shaft_r, length) screws_stick = screw_stick + up(15)(screw_stick) # stickit length 49.6-1.28-2-2 # 15.5+1.5+1.2-1.5 xula_base += translate( [-(49.6 - 1.28 - 2 - 2) - 8, 0, 15.5 + 1.5 + 1.2 - 1.5 - 2.7])(screws_stick) xula_base += translate([-60, -2, -8])(cube([123, 2, 61])) xula_base = up(THICK_WALL + 11)(xula_base) # add down connector base_exit = cube([58 + 2 * screw_xuin + THICK_WALL, 16, THICK_WALL]) base_exit += hole()(translate( [screw_xuin + THICK_WALL * 0.5, THICK_WALL, 0])(cube([58 - 2 * (screw_xuin + THICK_WALL * 0.5), 12, THICK_WALL]))) xula_base += back(16)(base_exit) return xula_base
def createlogo(): """createlogo Openscad cannot handle the Storm font. To mitigate, a vector image of the storm font is converted to PNG via Inkscape. The PNG image is linearly extruded and converted to a STL. This STL is imported by this function, to create the logo. """ # TODO: move Python converter for logo to here # LOGO bounding box x = 234 , y = 26, z = 1 # scaled to x = 120, y = 13 x_bound = 120 + THICK_WALL * 2 y_bound = 13 + THICK_WALL * 2 # TODO: should throw error !! you removed logo logo = scale([0.5, 0.5, 1])(import_stl('hexastorm.stl')) logo = None # openscad cannot handle minkowski on hexastorm logo # logo_mink = up(1)(minkowski()(cylinder(r=0.5, h=1), logo)) result = translate([-0.5 * x_bound, -0.5 * y_bound, 0])(cube( [x_bound, y_bound, 1])) - hole()(mirror([0, 1, 0])(logo)) result = scale([1, 1, HEIGHT_TOP - THICK_WALL ])(translate([0.5 * x_bound, 0.5 * y_bound, 0])(result)) result = up(HEIGHT_TOP - THICK_WALL)(cube([x_bound, y_bound, 1])) # TODO: Openscad can create a preview but does not render the logo, # at the moment we resort to # modiefs in blender return result
def pulley_arms(height=40, through_screw='m4', arm_width=15, arm_thickness=7.5, pully_width=10, base_width=30, base_thickness=10, spin_clearance=0.5): arm_base = so.translate((0,-arm_width/2.0,0))(so.cube((arm_thickness, arm_width, 1))) arm = so.hull()(so.translate((0,0,height-arm_width/2.0))(so.rotate((0,90,0))(so.cylinder(r=arm_width/2.0, h=arm_thickness))) + arm_base) def arms_xf_left(obj): return so.translate(((pully_width + spin_clearance)/2.0,0,0))(obj) def arms_xf_right(obj): return so.translate((-(pully_width + spin_clearance)/2.0,0,0))(so.rotate((0,0,180))(obj)) arms = arms_xf_left(arm) + arms_xf_right(arm) plate_orig = so.translate((-base_width/2.0,-base_width/2.0,-base_thickness))(so.cube((base_width, base_width, base_thickness))) plate = so.hull()(plate_orig, so.translate((0,0,base_thickness/2.0))(arms_xf_left(arm_base))) plate += so.hull()(plate_orig, so.translate((0,0,base_thickness/2.0))(arms_xf_right(arm_base))) # add mount holes spacing = arm_thickness*2.0+pully_width bolt_hole = so.rotate((0,90,0))(so.translate((0,0,-spacing))(so.cylinder(r=screw_clearance[through_screw]/2.0, h=spacing*2.0))) nut_recess = so.translate((-spacing/2.0,0,0))(so.rotate((0,90,0))(hex(screw_nut[through_screw]['width'], screw_nut[through_screw]['depth']))) bolt_hole += nut_recess head_recess = so.translate((spacing/2.0,0,0))(so.rotate((0,-90,0))(so.cylinder(r=screw_head_sink[through_screw]['diameter']/2.0, h=screw_head_sink[through_screw]['h']))) bolt_hole += head_recess arms -= so.translate((0,0,height-arm_width/2.0))(bolt_hole) # add base mount hole head_recess = so.translate((0,0,-5))(so.cylinder(r=screw_head_sink[through_screw]['diameter']/2.0, h=screw_head_sink[through_screw]['h']*base_thickness)) bolt_hole = so.translate((0,0,-base_thickness))(so.cylinder(r=screw_clearance[through_screw]/2.0, h=base_thickness*2.0)) bolt_hole += head_recess return arms + plate - bolt_hole
def stopper(screw='m4'): body = so.cube((40,20,10), center=True) nut_recess = hex(screw_nut[screw]['width'], screw_nut[screw]['depth']) bolt_hole = so.translate((0,0,-10))(so.cylinder(r=screw_clearance[screw]/2.0, h=20)) nut_slide = so.translate((0,-screw_nut[screw]['width']/2.0))(so.cube((20, screw_nut[screw]['width'], screw_nut[screw]['depth']))) nut_attachment = so.rotate((0,-90,-90))(so.translate((0,0,-screw_nut[screw]['depth']/2.0))(nut_slide + nut_recess) + bolt_hole) return body - so.rotate((0,0,180))(so.translate((0,-13,-10))(expand_for_fit(0.3)(rail_section(20)))) - so.translate((0,-5,0))(nut_attachment)
def fibcube(): outside = cube([m.fibcube_side, m.fibcube_side, m.fibcube_height], center=False) top = translate( [ m.fibcube_wall + m.fibcube_extra / 2, m.fibcube_wall + m.fibcube_extra / 2, m.fibcube_height, ] )( cube( [ m.fibcube_plopp - m.fibcube_extra, m.fibcube_plopp - m.fibcube_extra, m.fibcube_top_margin, ], center=False, ) ) inside = translate([m.fibcube_wall, m.fibcube_wall, -m.fibcube_wall])( cube([m.fibcube_plopp, m.fibcube_plopp, m.fibcube_height], center=False) ) fc = outside + top fc = fc + hole()(inside) return fc
def basic_geometry(): # SolidPython code can look a lot like OpenSCAD code. It also has # some syntactic sugar built in that can make it look more pythonic. # Here are two identical pieces of geometry, one left and one right. # left_piece uses standard OpenSCAD grammar (note the commas between # block elements; OpenSCAD doesn't require this) left_piece = union()(translate((-15, 0, 0))(cube([10, 5, 3], center=True)), translate( (-10, 0, 0))(difference()(cylinder(r=5, h=15, center=True), cylinder(r=4, h=16, center=True)))) # Right piece uses a more Pythonic grammar. + (plus) is equivalent to union(), # - (minus) is equivalent to difference() and * (star) is equivalent to intersection # solid.utils also defines up(), down(), left(), right(), forward(), and back() # for common transforms. right_piece = right(15)(cube([10, 5, 3], center=True)) cyl = cylinder(r=5, h=15, center=True) - cylinder(r=4, h=16, center=True) right_piece += right(10)(cyl) return union()(left_piece, right_piece)
def base_bracket(mount_screw_hole, through_screw='m4', chamfer=1, clearance=0.25, base_flange_width = 20, base_flange_thickness=25, bottom_thickness=10, height=50, holes_offset=10): # bolt holes at 20-bottom_thickness and 40-bottom_thickness offsets for rail body = bracket(bottom_thickness=bottom_thickness, chamfer=chamfer, height=height, clearance=clearance, through_offsets=[20,40]) bracket_bottom = so.translate((-15-base_flange_width,-20-base_flange_width,0))(so.cube((30+base_flange_width*2,40+base_flange_width*2,base_flange_thickness))) bracket_chamfer = so.translate((-15-chamfer,-20-chamfer,-chamfer))(so.cube((30+2*chamfer,40+2*chamfer,chamfer))) body += chamfer_hull(x=True, y=True, z=[1])(bracket_bottom) for x in [1,-1]: for y in [1,-1]: body -= so.translate((x*(15+base_flange_width - holes_offset),y*(20+base_flange_width - holes_offset),base_flange_thickness+chamfer+0.001))(mount_screw_hole) return body
def laserbase(laserheight): """laserabase creates the basis for the laser with ventilation wall The laserbase is in the XY plane at quadrant 1. One corner is at the origin. The width is parallel to the x-axis. The laser was provided by Odic Force, productid OFL510-1. The padheight is laser height- 16.5 The laserbundle travels in the +x direction and departs from the center, that is 15 mm. param: laserheight: the desired height of the laser """ # The laser tube is at 8 mm from bottom. # The laser tube has a diameter of 17 mm # The laser is at 8 + 17 * 0.5 - 1 = 16.5 mm (shim of 1 mm needed) # The laser base is 30x60 mm, which was made # 30x75 mm to make room for the ventilator # PARAMETERS height = laserheight - 15.5 # [mm], xdisp = 48.5 # [mm], x-displacement screws ydisp = 16 # [mm], y-displacement screws r_shaft = 2 # [mm], shaft radius screws h_head = 5 # [mm], height shaft head r_head = 3.5 # [mm], top radius screws tspile = 4 # [mm], y-thickness ventilation spile hspile = 25 # [mm], height ventilation spile length = 75 # [mm], x-direction length laser width = 30 # [mm], y-direction width laser screw_offst = 7 # [mm], screw offset +x-edge # MINIMAL MATERIAL BASE screws = screw(r_head, h_head, r_shaft, height) + right(xdisp)(screw( r_head, h_head, r_shaft, height)) spiegel = forward(ydisp / 2)(mirror([0, 1, 0])(back(ydisp / 2)(screws))) screws += spiegel base = translate([length - xdisp - screw_offst, (width - ydisp) / 2, 0])(screws) # ventilation wall # spile spile = up(height)(cube([THICK_WALL, tspile, hspile])) nofspiles = ceil((width) / (tspile * 2)) # shift base base = right(THICK_WALL)(base) # add wall base += cube([THICK_WALL, width, HEIGHT_WALL]) # create pockets for i in range(0, nofspiles): base -= hole()(forward(i * 2 * tspile + THICK_WALL)(spile)) return base
def ueyeholder(): """ueyeholder creates a holder for an ueye camera A holder for an uEye camera to view upward. The uEye camera cannot look upward due its connector on the back. This design solves this problem via a cubical enclosure. """ #### UEYE cubical dimension margin = 0.3 size_x = 34 # mm size_y = 32 # mm size_z = 34.6 # mm ### UEYE screw screw_d = 3 # mm screw_z = 30.4 # sep z-direction screw_z -= screw_d screw_ytop = 19.8 # mm screw_ytop -= screw_d screw_ybottom = 21.8 screw_ybottom -= screw_d screw_zoff = 1.3 + screw_d / 2 connector_z = 18 # mm connector_x = 16.2 # mm #### # box # camera is pushed in from bottom holder = cube([ size_x + 2 * THCKW + margin, size_y + 2 * THCKW + margin, size_z + THCKW + connector_z + margin ]) holder -= translate([THCKW, THCKW, THCKW])(cube([ size_x + margin, size_y + margin, size_z + THCKW + connector_z + margin ])) # 4 screw holes socket = cylinder(h=size_x + 2 * THCKW, r=screw_d / 2, segments=SGM) socket = rotate([0, 90, 0])(socket) sockets = socket + forward(screw_ybottom)(socket) temp = (screw_ytop - screw_ybottom) * 0.5 sockets += up(screw_z)(back(temp)(socket) + forward(screw_ytop - temp)(socket)) # substraction prep sockets holder -= translate([ 0, (size_y - screw_ybottom) * 0.5 + THCKW + 0.5 * margin, screw_zoff + THCKW + connector_z + 0.5 * margin ])(sockets) # space connector holder -= translate([THCKW + 0.5 * (size_x - connector_x), 0, THCKW])(cube( [connector_x, THCKW, size_z + THCKW + connector_z])) return holder
def servo_mount(): body = so.translate((0, -19.9/2, 0))(so.cube((thickness+2.0, 19.9, 40.5))) body = so.minkowski()(body, so.cube((0.5,0.5,0.5))) insert = so.rotate((0,90,0))(m3_heatset_insert_hole) for x in [-5, 5]: for y in [48.7/2.0, -48.7/2.0]: body += so.translate((0, x, y+40.5/2))(insert) # tips span 55.35, body spans 40.4 # holes are 1cm apart by 48.7mm apart # shaft is in the middle about 9.37mm off of the bottom # also don't forget the cable route body += so.translate((thickness/2.0+1,0,-0.5))(so.cube((thickness+2,7,1), center=True)) body += so.translate((thickness/2.0+1+5.5,0,-2))(so.cube((12,7,4), center=True)) return body
def panelmountmini(): """panelmount panel mount for panel Mount cable -B to Mini-B cable http://adafru.it/936 """ base = cube([40, 20, THICK_WALL], center=True) # screw hole screw_hole = cylinder(r=1.75, h=THICK_WALL * 2, center=True, segments=30) # create two holes + hole usb cube base -= hole()(left(14)(screw_hole) + right(14)(screw_hole) + cube([17.5, 12, THICK_WALL * 2], center=True)) # change orientation base = right(0.5 * THICK_WALL)(rotate([0, 90, 0])(rotate([0, 0, 90])(base))) return base
def doohickey(): hole_cyl = translate( (0, 0, -EPSILON))(cylinder(r=m3_rad, h=doohickey_h + 2 * EPSILON)) d = difference()(cube([30, 10, doohickey_h], center=True), translate((-10, 0, 0))(hole_cyl), hole_cyl, translate((10, 0, 0))(hole_cyl)) return d
def sidewall(): body = chamfer_hull(z=True, y=True)(so.cube((depth, thickness, height))) insert = so.rotate((0,90,0))(m3_heatset_insert_hole) for i in range(1,4): body -= so.translate((0,thickness/2.0,height/4.0*i))(insert) body -= so.translate((depth,thickness/2.0,height/4.0*i))(so.rotate((0,0,180))(insert)) return body
def multipart_hole(): # It's good to be able to keep holes empty, but often we want to put # things (bolts, etc.) in them. The way to do this is to declare the # object containing the hole a "part". Then, the hole will remain # empty no matter what you add to the 'part'. But if you put an object # that is NOT part of the 'part' into the hole, it will still appear. # On the left (not_part), here's what happens if we try to put an object # into an explicit hole: the object gets erased by the hole. # On the right (is_part), we mark the cube-with-hole as a "part", # and then insert the same 'bolt' cylinder into it. The entire # bolt rematins. b = cube(10, center=True) c = cylinder(r=2, h=12, center=True) # A cube with an explicit hole not_part = b - hole()(c) # Mark this cube-with-hole as a separate part from the cylinder is_part = part()(not_part.copy()) # This fits in the holes bolt = cylinder(r=1.5, h=14, center=True) + up(8)(cylinder( r=2.5, h=2.5, center=True)) # The section of the bolt inside not_part disappears. The section # of the bolt inside is_part is still there. return not_part + bolt + right(45)(is_part + bolt)
def lasershim(height): """lasershim This is a shim which can be used to pad. The base of the shim is in the XY plane at quadrant 1. One corner is at the origin. The width is parallel to the x-axis. The shim can be used if the laserbase is not correctly alligned. The laser was provided by Odic Force, productid OFL510-1. param: height: defines height shim [mm] """ # PARAMETER xdisp = 48.5 # [mm], x-displacement screw ydisp = 16 # [mm], y-displacement screws r_shaft = 2 + 0.5 # [mm], shaft radius screws length = 75 # [mm], x-direction length laser width = 30 # [mm], y-direction width laser screw_offst = 7 # [mm], screw offset +x-edge # MAXIMAL MATERIAL BASE base = cube([length, width, height]) # screw holes screws = cylinder(h=height, r=r_shaft) + right(xdisp)(cylinder(h=height, r=r_shaft)) spiegel = forward(ydisp / 2)(mirror([0, 1, 0])(back(ydisp / 2)(screws))) screws += spiegel # create holes base -= translate([length - xdisp - screw_offst, (width - ydisp) / 2, 0])(screws) return base
def topbox(down, logo): """topbox constructs the top part of the box :param down: if true downward ray box created :param logo: if true logo is generated, logo slows rendering """ top = cube([LENGTH_TOP, WIDTH_TOP, THICK_WALL]) # 4 screws used, 2 was insufficient screw_fixout = 3.5 # mm (radius) screw_fixin = 2 # TODO: connect to holesize threaded inserti screw_toph = 5 cyl = screw(screw_fixout, screw_toph, screw_fixin, HEIGHT_TOP) top += translate([SCREW_FIXOFFST, SCREW_FIXOFFST, 0])(cyl) top += translate([LENGTH_TOP - SCREW_FIXOFFST, SCREW_FIXOFFST, 0])(cyl) top += translate( [LENGTH_TOP - SCREW_FIXOFFST, WIDTH_TOP - SCREW_FIXOFFST, 0])(cyl) top += translate([SCREW_FIXOFFST, WIDTH_TOP - SCREW_FIXOFFST, 0])(cyl) # sliding should be prevented with 4 protrusion, # 1 is logo and 3 other are knobs x_knob = cube([THICK_WALL, THICK_WALL * 3, HEIGHT_TOP - THICK_WALL]) x_knobs = translate( [THICK_WALL, WIDTH_TOP / 2 - 1, THICK_WALL])(x_knob) + translate([ LENGTH_TOP - 2 * THICK_WALL, WIDTH_TOP / 2 - 1, THICK_WALL ])(x_knob) y_knob = cube([THICK_WALL * 3, THICK_WALL, HEIGHT_TOP - THICK_WALL]) y_knobs = translate([LENGTH_TOP * 0.25, 0, THICK_WALL ])(forward(THICK_WALL)(y_knob) + forward(WIDTH_TOP - 2 * THICK_WALL)(y_knob)) top += y_knobs + x_knobs # LOGO slows down render, should be turned off when developing if logo: top += translate([ 0.5 * (LENGTH_TOP - (120 + THICK_WALL * 2)), WIDTH_TOP - THICK_WALL - (13 + THICK_WALL * 2), 0 ])(createlogo()) if not down: laser_y = 24 + 2 * THICK_WALL top -= translate( [75 + 10 + 48 + THICK_WALL + 10, laser_y - 0.5 * 8, 0])(cube([20, 8, THICK_WALL])) # FIX FOR BOX orientation top = rotate([0, 0, 180])(mirror([0, 1, 0])(rotate([0, 180, 0])(top))) return top
def split_lock(diameter, thickness=3, depth=40, lip=10, chamfer=1, gap=2, screw='m4', shape='circle'): lip_part = so.translate((diameter/2.0,-thickness/2.0,0))(so.cube((lip,thickness,depth))) if shape == 'circle': hole = so.cylinder(r=diameter/2.0, h=depth*2) brace = so.cylinder(r=diameter/2.0+thickness, h=depth) elif shape == 'square': hole = so.rotate((0,0,45))(so.translate((-diameter/2.0,-diameter/2.0,0))(so.cube((diameter,diameter,depth*2)))) brace = so.rotate((0,0,45))(so.translate((-diameter/2.0-thickness,-diameter/2.0-thickness,0))(so.cube((2*thickness+diameter,2*thickness+diameter,depth)))) holder = so.translate((0,depth/2.0,0))(chamfer_hull(x=True,y=True)(so.rotate((90,0,0))(brace + lip_part)) - so.hole()(so.translate((0,depth/2.0,0))(so.rotate((90,0,0))(hole)))) split = so.translate((0, -depth/2.0-chamfer, -gap/2.0))(so.cube((thickness + diameter + lip,depth+chamfer*2,gap))) split_nut_recess = so.translate((0,0,chamfer))(hex(screw_nut[screw]['width'], screw_nut[screw]['depth'])) split_nut_slide = so.translate((0,-screw_nut[screw]['width']/2, chamfer))(so.cube((thickness + diameter, screw_nut[screw]['width'], screw_nut[screw]['depth']))) split_bolt_hole = so.translate((0,0,-thickness*2-chamfer))(so.cylinder(r=screw_clearance[screw]/2.0, h=100)) split_head_recess = so.translate((0,0,-diameter-thickness*2.5))(so.cylinder(r=screw_head_sink[screw]['diameter']/2.0, h=diameter+thickness)) split_tensioner = so.translate(((diameter+lip)/2.0,0,thickness/2.0+chamfer))(split_nut_recess + split_bolt_hole + split_nut_slide + split_head_recess) return holder - split - so.hole()(split_tensioner)
def test_numpy_type(self): try: import numpy numpy_cube = cube(size=numpy.array([1, 2, 3])) expected = '\n\ncube(size = [1,2,3]);' actual = scad_render(numpy_cube) self.assertEqual(expected, actual, 'Numpy SolidPython not rendered correctly') except ImportError: pass
def boxmount(): base = cube([30, 50, 2]) circ = cylinder(h=10, r=1.6, segments=30) circs = right(5)(circ) + right(25)(circ) base -= forward(5)(circs) base -= forward(25)(circs) base -= forward(45)(circs) base = rotate([90, 0, 90])(base) return base
def polygonshim(height): """polygonshim The polygon shim is located in first quadrant of the XY plane. A corner is at the origin. The width is parallel to the y-axis. The shim can be used to align the polygon. The shim was designed for polygon mirror Motor aficio 1018 G029-196. :param height: height shim """ # BASE: length = 68 # mm [y-direction] width = 48 # mm [x-direction] r_shaft = 2 # mm shaft radius slot_width = 2 # width slot base = cube([width / 2, length, height]) def slot(radius, height, width): """slot openscad styled vertically oriented printable slot origin formed by the center of the left circle :param radius: the radius of the top of the screw :param height: the height of the slot :param width: the width of the slot, i.e. distance between radii """ cyl = cylinder(h=height, r=radius) outer = hull()(cyl, right(width)(cyl)) return outer # create 2 screw slots simple_slot = slot(r_shaft, height, slot_width) base -= translate([3.1, length - 4, 0])(simple_slot) base -= translate([3.2, 4 + 1.29, 0])(rotate([0, 0, -50])(simple_slot)) # create hole for polygon rotation axis base -= translate([24, 24, 0])(cylinder(h=height, r=10)) # create hole for polygon lock base -= translate([24 - 7.5, 0, 0])(cube([15, 10, height])) # mirror and add to original spiegelold = right(width)(mirror([1, 0, 0])(base)) base += spiegelold return base
def matrix2render(threeDimensionalMatrix): distance = 15 cube_size = 1 figure = cube([0.1, 0.1, 0.1], center=True) for matrix_i in range(len(threeDimensionalMatrix)): for row_i in range(len(threeDimensionalMatrix[matrix_i])): for cube_i in range(len(threeDimensionalMatrix[matrix_i][row_i])): if threeDimensionalMatrix[matrix_i][row_i][cube_i] == 1: new_cube = right(matrix_i * cube_size)(cube( [cube_size, cube_size, cube_size], center=True)) new_cube = up(row_i * cube_size)(new_cube) new_cube = forward(cube_i * cube_size)(new_cube) figure += new_cube SEGMENTS = 48 file_out = scad_render_to_file(figure) print(f"{__file__}: SCAD file written to: \n{file_out}")
def square_pipe(width: float, depth: float, height: float, wall_thickness: float) -> SolidBuilder: g = empty() pipe_hole = (node( cube([width - wall_thickness * 2, depth - wall_thickness * 2, height])).right(wall_thickness).forward(wall_thickness)) g.add(box(width, depth, height).hole(pipe_hole)) g.set_center(x=width / 2, y=depth / 2, z=height / 2) return g.part()
def sidewall_clamp(): # my wood is 19.5mm wide and 38.6mm tall body = chamfer_hull(z=True, y=True)(so.cube((20, thickness, height))) body += chamfer_hull(z=True, y=True, x=[1])(so.translate((19,0,0))(so.cube((1, thickness, height))) + so.translate((50,-thickness*0.25,0))(so.cube((10, thickness*1.5, 45)))) wood_cavity = so.translate((20,0,5))(so.cube((100,19.5,38.6))) body -= wood_cavity insert = so.rotate((0,90,0))(m3_heatset_insert_hole) for i in range(1,4): body -= so.translate((0,thickness/2.0,height/4.0*i))(insert) body -= so.translate((depth,thickness/2.0,height/4.0*i))(so.rotate((0,0,180))(insert)) nut_recess = so.translate((54,-5,15))(so.rotate((90,30,0))(hex(screw_nut['m3']['width'], screw_nut['m3']['depth']))) screw_recess = so.translate((54,22.4+5,15))(so.rotate((90,0,0))(so.cylinder(screw_head_sink['m3']['diameter']/2.0, screw_nut['m3']['depth']))) screw_hole = so.translate((54,22.4+10,15))(so.rotate((90,0,0))(so.cylinder(screw_clearance['m3']/2.0, 100))) screw_capture = nut_recess + screw_recess + screw_hole def add_screw(x,y): nonlocal body body -= so.translate((-x,0,y))(screw_capture) add_screw(0,0) add_screw(0,17) add_screw(17/2.0,17/2.0) return body
def enclosure(): enc_outer = cube( size=[m.enclosure_width, m.enclosure_depth, m.enclosure_height], center=True ) enc_hole = up(m.wall_thickness)( cube( size=[ m.enclosure_width - m.wall_thickness, m.enclosure_depth - m.wall_thickness, m.enclosure_height, ], center=True, ) ) enclosure = enc_outer + hole()(enc_hole) enclosure = part()(enclosure) enclosure = up(m.enclosure_height / 2)(enclosure) return enclosure
def double_side_rail(h, bottom_thickness=10, holes=None): section = so.translate((0,5,0))(rail_section(h)) stack = section + so.rotate((0,0,180))(section) + so.translate((-8,-5,0))(so.cube((16,10,h))) if holes is not None: bolt_hole = so.rotate((0,90,0))(so.translate((0,0,-40))(so.cylinder(r=screw_clearance[holes]/2.0, h=80))) def mkhole(offset): nonlocal stack stack -= so.translate((0,0,offset))(bolt_hole) mkhole(20-bottom_thickness) mkhole(40-bottom_thickness) mkhole(h-10) return stack
def test_hole_transform_propagation(self): # earlier versions of holes had problems where a hole # that was used a couple places wouldn't propagate correctly. # Confirm that's still happening as it's supposed to h = hole()(rotate(a=90, v=[0, 1, 0])(cylinder(2, 20, center=True))) h_vert = rotate(a=-90, v=[0, 1, 0])(h) a = cube(10, center=True) + h + h_vert expected = '\n\ndifference(){\n\tunion() {\n\t\tcube(center = true, size = 10);\n\t\trotate(a = -90, v = [0, 1, 0]) {\n\t\t}\n\t}\n\t/* Holes Below*/\n\tunion(){\n\t\trotate(a = 90, v = [0, 1, 0]) {\n\t\t\tcylinder(center = true, h = 20, r = 2);\n\t\t}\n\t\trotate(a = -90, v = [0, 1, 0]){\n\t\t\trotate(a = 90, v = [0, 1, 0]) {\n\t\t\t\tcylinder(center = true, h = 20, r = 2);\n\t\t\t}\n\t\t}\n\t} /* End Holes */ \n}' actual = scad_render(a) self.assertEqual(expected, actual)
def cable_fasten(height, width, thick, x_orient): """cable_fasten fastener for the cable used is tie wrap 150x2.5 mm with h = 3 and w = 4 mm param hoog: height cable fastener param width: width cable fastener maximum for FDM is 10 mm link http://www.futureengineers.org/Pdfs/startrek/DesignGuidelines.pdf param thick: walll thickness param x_orient: if true oriented in x-direction otherwise y-direction """ # Altenatives considered: # cable fastener: cable clip; (http://www.thingiverse.com/thing:643160) # might require support and post-processing # cable clamp; i.e. two pillars, fixture via friction # might need to be tailored to thickess of cable if x_orient: base = cube([2 * thick + width, thick, height + thick]) - right(thick)( cube([width, thick, height])) else: base = cube([thick, 2 * thick + width, height + thick ]) - forward(thick)(cube([thick, width, height])) return base
def iron_holder(thickness=20, depth=40, length=45, iron_diameter=20.5, chamfer=1, iron_holder_thickness=5,arm_screw='m4', arm_mount_dist=20, gap=5, split_screw='m4', cup_diameter=30, cup_thickness=5): arm = chamfer_hull(x=True,y=True,z=[1])(so.translate((-thickness/2.0, -depth/2.0, 0))(so.cube((thickness,depth,length + cup_diameter)))) counterweight_cup = so.translate((0,depth/2.0,length-cup_thickness))(chamfer_hull(x=True,y=True)(so.rotate((90,0,0))(so.cylinder(r=cup_diameter/2.0+cup_thickness, h=depth))) - so.hole()(so.translate((0,depth/2.0-cup_thickness,0))(so.rotate((90,0,0))(so.cylinder(r=cup_diameter/2.0, h=depth))))) holder = so.translate((0,0,cup_diameter+length))(so.rotate((0,-90,0))(split_lock(iron_diameter, thickness=iron_holder_thickness, depth=depth, lip=10, chamfer=chamfer, gap=gap, screw=split_screw))) rope_tie = so.translate((0,depth/2.0+chamfer,length/2.0-iron_holder_thickness))(so.rotate((-90,90,0))(arch())) nut_recess = hex(screw_nut[arm_screw]['width'], screw_nut[arm_screw]['depth']) bolt_hole = so.cylinder(r=screw_clearance[arm_screw]/2.0, h=10) nut_slide = so.translate((0,-screw_nut[arm_screw]['width']/2.0))(so.cube((thickness, screw_nut[arm_screw]['width'], screw_nut[arm_screw]['depth']))) nut_attachment = so.translate((0,0,5))(nut_slide + nut_recess) + bolt_hole plate_install_holes = so.translate((0,0,-0.1))(carraige_plate_install_holes(diameter=4.95)) return counterweight_cup + arm + holder - so.translate((0,arm_mount_dist/2.0,0))(nut_attachment) - so.translate((0,-arm_mount_dist/2.0,0))(nut_attachment) + rope_tie - plate_install_holes
def board_pegs(): arduino_measurements = dict(holes=[[0, 0], [0, 48.2], [50.8, 36.0], [50.8, 5.1]]) rpi_measurements = dict(holes=[[0, 0], [0, 49.0], [58.0, 49.0], [58.0, 0.0]]) os = [arduino_measurements, rpi_measurements] g = empty() for i, o in enumerate(os): for x, y in o["holes"]: c = node(cube()).right(x + i * 5).forward(y) g.add(c) g = g.set_translation([0, 0, 30]) return g.render()
def board_cylinder(): group = cube() # FIXME: look into empties/children for x in [-m.rpi_hole_x_dist / 2, m.rpi_hole_x_dist / 2]: for y in [-m.rpi_hole_y_dist / 2, m.rpi_hole_y_dist / 2]: c = cylinder(r=m.rpi_cylinder / 2, h=5) c2 = up(m.rpi_board_height)( cylinder(r=m.rpi_hole / 2 - m.diameter_margin, h=m.rpi_board_height) ) c = c + c2 group += forward(y)(left(x)(c)) return group