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 m3_12(): bolt_height = 12 m = union()( head(), translate((0, 0, -bolt_height))( cylinder(r=m3_rad, h=bolt_height) ) ) return m
def makestack(dia): stack = union()(cylinder(d=dia + WALL * 2, h=TRAY_H - FLOOR) - up(FLOOR)(cylinder(d=dia, h=TRAY_H))) hole_pcg = 0.7 stack -= hole()(down(1)(cylinder(d=dia * hole_pcg, h=TRAY_H))) stack -= hole()(translate([-dia / 2 - WALL, -dia / 2 * hole_pcg, -1])(cube( [dia / 2 + WALL, dia * hole_pcg, TRAY_H * 3]))) return stack
def assembly(): return union()( doohickey(c='blue'), translate((-10, 0, doohickey_h / 2))(m3_12()), translate((0, 0, doohickey_h / 2))(m3_16()), translate((10, 0, doohickey_h / 2))(m3_12()), # Nuts translate((-10, 0, -nut_height - doohickey_h / 2))(m3_nut()), translate((0, 0, -nut_height - doohickey_h / 2))(m3_nut()), translate((10, 0, -nut_height - doohickey_h / 2))(m3_nut()), )
def bottom_part(): top = difference() u = union() u2 = union() top.add(u) d = difference() d.add(cylinder(r=innerR + wall + gap, h=toph)) d.add(translate((0, 0, baseH)).add(cylinder(r=innerR + gap, h=toph))) u.add(d) top.add(u2) for i in range(0, 3): a = i * 2 * pi / 3 r = innerR + gap + wall / 2 u.add( translate(((r - 0.3) * cos(a), (r - 0.3) * sin(a), toph - 6)).add(sphere(r=2.4))) u2.add( translate(((r + wall - 0.3) * cos(a), (r + wall - 0.3) * sin(a), toph - 6)).add(sphere(r=2.4))) return top
def screws(): """The screw holes that need to be applied to both halves""" threads = [] for x in [0, IPAD_W + WALL * 1 + SLIP * 2 + INNER_WALL * 1 + WALL_PADDING]: for y in [ 30, IPAD_H + WALL * 2 + SLIP * 2 + INNER_WALL + WALL_PADDING - 30 ]: threads.append( translate([x, y, BACK + IPAD_D / 2])(rotate([0, 90, 0])(screwthread()))) return union()(*threads)
def main(out_dir): # Parameters height_ratio = 0.25 left_loc = ONE_THIRD midpoint_loc = 0.5 right_loc = 2 * ONE_THIRD gens = 5 # Results all_polys = union() # setup ax, ay = 0, 0 bx, by = 100, 0 cx, cy = 50, 86.6 base_seg1 = LineSegment2(Point2(ax, ay), Point2(cx, cy)) base_seg2 = LineSegment2(Point2(cx, cy), Point2(bx, by)) base_seg3 = LineSegment2(Point2(bx, by), Point2(ax, ay)) generations = [[base_seg1, base_seg2, base_seg3]] # Recursively generate snowflake segments for g in range(1, gens): generations.append([]) for seg in generations[g - 1]: generations[g].extend( kochify(seg, height_ratio, left_loc, midpoint_loc, right_loc)) # # Put all generations into SCAD orig_length = abs(generations[0][0]) for g, a_gen in enumerate(generations): points = [s.p1 for s in a_gen] # Just use arrays for points so SCAD understands points = [[p.x, p.y] for p in points] # Move each generation up in y so it doesn't overlap the others h = orig_length * 1.5 * g # Do the SCAD edges = [list(range(len(points)))] all_polys.add(forward(h)(polygon(points=points, paths=edges))) file_out = scad_render_to_file(all_polys, out_dir=out_dir, include_orig_code=True) print(f"{__file__}: SCAD file written to: {file_out}")
def top_part(): maze_path = os.path.join(os.path.dirname(__file__), 'maze7.png') depth_map = build_depth_map(maze_path) d = difference() u = union() u.add(bumpMapCylinder(depth_map, innerR, hn, 0, 255)) u.add(cylinder(r=innerR + wall + gap, h=gripH)) d.add(u) d.add(intersection().add( bumpMapCylinder(depth_map, innerR, hn + 2, wall, 0).set_modifier("")).add( translate((0, 0, baseH)).add( cylinder(r=innerR + 2 * wall, h=h * 1.1).set_modifier("")))) return d
def ipadminiholderfront(): # Back (wall mount) # o = sizedipad(IPAD_W + WALL * 2 + SLIP * 2, IPAD_H + WALL * 2 + SLIP * 2, BACK) o = union() # Front supports o += sizedipad( TOTAL_W, IPAD_H + WALL * 2 + SLIP * 2 + INNER_WALL + WALL_PADDING, IPAD_D + WALL + SLIP + BACK, ) sides = WALL + INNER_WALL + WALL_PADDING + SLIP # Cutouts for buttons, round curves around the button o -= translate([ IPAD_W + WALL + SLIP * 2 + INNER_WALL + WALL_PADDING - BUTTON_D / 2 - BUTTON_TO_EDGE, (IPAD_H + WALL * 2 + SLIP * 2) / 2 + INNER_WALL + WALL_PADDING, BACK, ])(cylinder(d=BUTTON_D, h=50)) o -= translate([ sides + SCREEN_SIDE_EDGE - SCREEN_BORDER + IPAD_W - (SCREEN_SIDE_EDGE - SCREEN_BORDER) * 2 + BUTTON_D / 2, (IPAD_H + WALL * 2 + SLIP * 2) / 2 + INNER_WALL + WALL_PADDING + BUTTON_D, BACK, ])(difference()( translate([-BUTTON_D, -BUTTON_D, 0])(cube([BUTTON_D, BUTTON_D, 50])), cylinder(d=BUTTON_D, h=50), )) o -= translate([ sides + SCREEN_SIDE_EDGE - SCREEN_BORDER + IPAD_W - (SCREEN_SIDE_EDGE - SCREEN_BORDER) * 2 + BUTTON_D / 2, (IPAD_H + WALL * 2 + SLIP * 2) / 2 + INNER_WALL + WALL_PADDING - BUTTON_D, BACK, ])(difference()( translate([-BUTTON_D, 0, 0])(cube([BUTTON_D, BUTTON_D, 50])), cylinder(d=BUTTON_D, h=50), )) # Camera o -= translate([ sides + BUTTON_TO_EDGE + BUTTON_D / 2, (IPAD_H + WALL * 2 + SLIP * 2) / 2 + INNER_WALL + WALL_PADDING, BACK, ])(cylinder(d=BUTTON_D, h=50)) o -= translate([ sides + SCREEN_SIDE_EDGE - SCREEN_BORDER - BUTTON_D / 2, (IPAD_H + WALL * 2 + SLIP * 2) / 2 + INNER_WALL + WALL_PADDING + BUTTON_D, BACK, ])(difference()( translate([0, -BUTTON_D, 0])(cube([BUTTON_D, BUTTON_D, 50])), cylinder(d=BUTTON_D, h=50), )) o -= translate([ sides + SCREEN_SIDE_EDGE - SCREEN_BORDER - BUTTON_D / 2, (IPAD_H + WALL * 2 + SLIP * 2) / 2 + INNER_WALL + WALL_PADDING - BUTTON_D, BACK, ])(difference()( translate([0, 0, 0])(cube([BUTTON_D, BUTTON_D, 50])), cylinder(d=BUTTON_D, h=50), )) # # Camera # o -= translate([WALL + SIDE_EDGE / 2, (IPAD_H + WALL * 2 + SLIP * 2) / 2, BACK])( # cylinder(d=BUTTON_D, h=50) # ) # Cutout for actual iPad, made very tall to allow sliding in/out if ALLOW_IPAD_SLIDE_IN: height = IPAD_H * 2 + INNER_WALL + WALL_PADDING else: height = IPAD_H + INNER_WALL * 2 + WALL_PADDING o -= translate([WALL, WALL, -1])(sizedipad( IPAD_W + SLIP * 2 + INNER_WALL * 2 + WALL_PADDING * 2, height, IPAD_D + SLIP + BACK + 1, )) # Cutout for remaining material around where the extra overhang is for the power # connector o -= translate([WALL, WALL, -1])(sizedipad( TOTAL_W - WALL * 2, IPAD_H + INNER_WALL * 2 + WALL_PADDING, IPAD_D + SLIP + BACK + 1, )) # Cutout for screen o -= translate([ sides + SCREEN_SIDE_EDGE - SCREEN_BORDER, sides + SCREEN_BOTTOM_EDGE - SCREEN_BORDER, BACK, ])(cube([ IPAD_W - (SCREEN_SIDE_EDGE - SCREEN_BORDER) * 2, IPAD_H - (SCREEN_BOTTOM_EDGE - SCREEN_BORDER) * 2, IPAD_D * 3, ])) # Side "hooks" o += translate([WALL, (IPAD_H - 15 * 2) + 15, 0])(rotate([90, 0, 0])( linear_extrude(IPAD_H - 15 * 2)(polygon([[0, 0], [1, 0], [0, 1]])))) o += translate([TOTAL_W - WALL * 2 + WALL, 15, 0])(rotate([-90, 180, 0])( linear_extrude(IPAD_H - 15 * 2)(polygon([[0, 0], [1, 0], [0, 1]])))) o += translate([ (TOTAL_W - 15 * 2) + 15, WALL, 0 ])(rotate([0, -90, 0])(linear_extrude(TOTAL_W - 15 * 2)(polygon([[0, 0], [1, 0], [0, 1]])))) # o += translate([15, WALL, 0])(cube([IPAD_W - 15 * 2, WALL, WALL])) # Power connector if POWER_CONNECTOR_ACCESSIBLE: o -= translate([ IPAD_W / 2, (IPAD_H + WALL * 2 + SLIP * 2) / 2 + INNER_WALL + WALL_PADDING - POWER_UNDERHANG - BUTTON_D / 2, -1, ])(cube([IPAD_W, BUTTON_D + POWER_UNDERHANG, IPAD_D + SLIP + BACK + 1])) # Lock Button if ALLOW_IPAD_SLIDE_IN: extra = 100 else: extra = 0 o -= translate([ -1, (IPAD_H + WALL * 2 + SLIP * 2) - LOCK_BUTTON_TO_EDGE - LOCK_BUTTON_LEN + INNER_WALL + WALL_PADDING, 0, ])(cube([50, LOCK_BUTTON_LEN + extra, IPAD_D + SLIP + BACK])) # Screws o -= screws() if SHOW_IPAD: o += translate([ WALL + INNER_WALL + SLIP + WALL_PADDING, WALL + INNER_WALL + SLIP + WALL_PADDING, BACK, ])(IPAD_STL).set_modifier("") return o
def assembly(): # Your code here! a = union() return a
def main_3d(out_dir): gens = 4 # Parameters ab_weight = 0.5 bc_weight = 0.5 ca_weight = 0.5 pyr_a_weight = ONE_THIRD pyr_b_weight = ONE_THIRD pyr_c_weight = ONE_THIRD pyr_height_weight = ONE_THIRD all_polys = union() # setup ax, ay, az = 100, -100, 100 bx, by, bz = 100, 100, -100 cx, cy, cz = -100, 100, 100 dx, dy, dz = -100, -100, -100 generations = [[ [Point3(ax, ay, az), Point3(bx, by, bz), Point3(cx, cy, cz)], [Point3(bx, by, bz), Point3(ax, ay, az), Point3(dx, dy, dz)], [Point3(ax, ay, az), Point3(cx, cy, cz), Point3(dx, dy, dz)], [Point3(cx, cy, cz), Point3(bx, by, bz), Point3(dx, dy, dz)], ]] # Recursively generate snowflake segments for g in range(1, gens): generations.append([]) for a, b, c in generations[g - 1]: new_tris = kochify_3d(a, b, c, ab_weight, bc_weight, ca_weight, pyr_a_weight, pyr_b_weight, pyr_c_weight, pyr_height_weight) generations[g].extend(new_tris) # Put all generations into SCAD orig_length = abs(generations[0][0][1] - generations[0][0][0]) for g, a_gen in enumerate(generations): # Move each generation up in y so it doesn't overlap the others h = orig_length * 1.5 * g # Build the points and triangles arrays that SCAD needs faces = [] points = [] for a, b, c in a_gen: points.extend([[a.x, a.y, a.z], [b.x, b.y, b.z], [c.x, c.y, c.z]]) t = len(points) faces.append([t - 3, t - 2, t - 1]) # Do the SCAD edges = [list(range(len(points)))] all_polys.add(up(h)(polyhedron(points=points, faces=faces))) file_out = Path(out_dir) / 'koch_3d.scad' file_out = scad_render_to_file(all_polys, file_out, include_orig_code=True) print(f"{__file__}: SCAD file written to: {file_out}")
all_tets = [subtet for tet in all_tets for subtet in tet.next_gen(midpoint_weight, jitter_range_vec)] if scale != 1: for tet in all_tets: tet.scale(scale) return all_tets if __name__ == '__main__': out_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path.cwd() generations = 3 midpoint_weight = 0.5 # jitter_range_vec adds some randomness to the generated shape, # making it more interesting. Try: # jitter_range_vec = [0.5,0, 0] jitter_range_vec = None all_tets = sierpinski_3d(generations, scale=100, midpoint_weight=midpoint_weight, jitter_range_vec=jitter_range_vec) t = union() for tet in all_tets: # Create the scad code for all tetrahedra t.add(tet.scad_code()) # Draw cubes at all intersections to make the shape manifold. for p in tet.points: t.add(translate(p).add(cube(5, center=True))) file_out = out_dir / f'gasket_{generations}_gen.scad' file_out = scad_render_to_file(t, file_out) print(f"{__file__}: SCAD file written to: \n{file_out}")
def main(params: Params): # face face_profile = roundrect( [LGX_FACE_WIDTH, LGX_FACE_HEIGHT + params.lgx_face_extra_height], params.face_radius, ) cutout_profile = roundrect([params.poe_face_width, params.poe_face_height], params.poe_face_radius) cutout_x = LGX_FACE_WIDTH / 2 - params.poe_face_width / 2 cutout_y = params.poe_face_lift + params.platform_thickness cutout = translate([cutout_x, cutout_y, 0])(cutout_profile) cutout_extrude = debug( down(params.lgx_rack_thickness)( linear_extrude(params.face_thickness + params.lgx_rack_thickness)(cutout))) post = circle(d=params.face_post_d) posts = post + right(params.face_post_separation)(post) posts = translate([ LGX_FACE_WIDTH / 2 - params.face_post_separation / 2, LGX_FACE_HEIGHT / 2, 0 ])(posts) face = difference()(face_profile, posts) face_extrusion = linear_extrude(height=params.face_thickness)(face) tray_depth = (params.platform_thickness + params.poe_depth + params.lgx_rack_thickness) # tray tray = linear_extrude(height=params.platform_thickness)( square(size=[LGX_PLATFORM_WIDTH, tray_depth])) poe_holder_outer_params = [ params.poe_width + params.platform_thickness * 2, tray_depth, ] poe_holder_profile = difference()( square(poe_holder_outer_params), right(params.platform_thickness)(forward(params.lgx_rack_thickness)( square([ params.poe_width, params.poe_depth, ]))), ) poe_holder_extrude = linear_extrude( height=params.poe_retainer_height)(poe_holder_profile) poe_holder = translate([ LGX_PLATFORM_WIDTH / 2 - poe_holder_outer_params[0] / 2, 0, params.platform_thickness, ])(poe_holder_extrude) tray = union()(poe_holder, tray) tray = rotate([270, 0, 0])(translate( [LGX_FACE_WIDTH / 2 - LGX_PLATFORM_WIDTH / 2, 0, 0])(tray)) # support support_start_inset = (params.platform_thickness + LGX_FACE_WIDTH / 2 - LGX_PLATFORM_WIDTH / 2) point_extent = LGX_FACE_HEIGHT - params.platform_thickness support_profile = polygon([[0, 0], [0, point_extent], [point_extent, 0]]) support = linear_extrude(height=params.platform_thickness)(support_profile) support = rotate([270, 0, 90])(support) support_left = translate([support_start_inset, 0, 0])(support) support_right = translate([ support_start_inset + LGX_PLATFORM_WIDTH - params.platform_thickness, 0, 0 ])(support) return difference()(union()(face_extrusion, tray, support_left, support_right), cutout_extrude)
top = difference() u = union() u2 = union() top.add(u) d = difference() d.add(cylinder(r=innerR + wall + gap, h=toph)) d.add(translate((0, 0, baseH)).add(cylinder(r=innerR + gap, h=toph))) u.add(d) top.add(u2) for i in range(0, 3): a = i * 2 * pi / 3 r = innerR + gap + wall / 2 u.add( translate(((r - 0.3) * cos(a), (r - 0.3) * sin(a), toph - 6)).add(sphere(r=2.4))) u2.add( translate(((r + wall - 0.3) * cos(a), (r + wall - 0.3) * sin(a), toph - 6)).add(sphere(r=2.4))) return top if __name__ == '__main__': out_dir = sys.argv[1] if len(sys.argv) > 1 else None file_out = os.path.join(out_dir, 'mazebox.scad') assm = union()(top_part(), translate((3 * innerR, 0, 0))(bottom_part())) print(f"{__file__}: SCAD file written to: \n{file_out}") scad_render_to_file(assm, file_out, file_header=f'$fn = {SEGMENTS};')