def scaledipad(x, y, height): """Returns an instance of the iPad scaled to have x, y mm extra size, at height""" xscale = (IPAD_W + x) / IPAD_W yscale = (IPAD_H + y) / IPAD_H print(xscale, yscale) o = linear_extrude(height)(polygon(IPAD_POINTS)) return scale([xscale, yscale, 1])(o)
def test_fillet_2d_add(self): pts = [[0, 5], [5, 5], [5, 0], [10, 0], [10, 10], [0, 10], ] p = polygon(pts) three_points = [euclidify(pts[0:3], Point2)] actual = fillet_2d(three_points, orig_poly=p, fillet_rad=2, remove_material=False) expected = 'union(){polygon(points=[[0,5],[5,5],[5,0],[10,0],[10,10],[0,10]]);translate(v=[3.0000000000,3.0000000000]){difference(){intersection(){rotate(a=359.9000000000){translate(v=[-998,0,0]){square(center=false,size=[1000,1000]);}}rotate(a=450.1000000000){translate(v=[-998,-1000,0]){square(center=false,size=[1000,1000]);}}}circle(r=2);}}}' self.assertEqualOpenScadObject(expected, actual)
def test_fillet_2d_remove(self): pts = list((project_to_2D(p) for p in tri)) poly = polygon(euc_to_arr(pts)) newp = fillet_2d([pts], orig_poly=poly, fillet_rad=2, remove_material=True) expected = 'difference(){polygon(paths=[[0,1,2]],points=[[0,0],[10,0],[0,10]]);translate(v=[5.1715728753,2.0000000000]){difference(){intersection(){rotate(a=-90.1000000000){translate(v=[-998,0,0]){square(center=false,size=[1000,1000]);}}rotate(a=45.1000000000){translate(v=[-998,-1000,0]){square(center=false,size=[1000,1000]);}}}circle(r=2);}}}' actual = scad_render(newp) self.assertEqualNoWhitespace(expected, actual)
def test_imported_scad_arguments(self): include_file = self.expand_scad_path("examples/scad_to_include.scad") mod = import_scad(include_file) points = mod.scad_points(); poly = polygon(points); actual = scad_render(poly); abs_path = points._get_include_path(include_file) expected = f'use <{abs_path}>\n\n\npolygon(points = scad_points());' self.assertEqual(expected, actual)
def test_fillet_2d_remove(self): pts = tri poly = polygon(euc_to_arr(tri)) newp = fillet_2d(tri, orig_poly=poly, fillet_rad=2, remove_material=True) expected = '\n\ndifference() {\n\tpolygon(paths = [[0, 1, 2]], points = [[0, 0, 0], [10, 0, 0], [0, 10, 0]]);\n\ttranslate(v = [5.1715728753, 2.0000000000, 0.0000000000]) {\n\t\tdifference() {\n\t\t\tintersection() {\n\t\t\t\trotate(a = 268.0000000000) {\n\t\t\t\t\ttranslate(v = [-998, 0, 0]) {\n\t\t\t\t\t\tsquare(center = false, size = [1000, 1000]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trotate(a = 407.0000000000) {\n\t\t\t\t\ttranslate(v = [-998, -1000, 0]) {\n\t\t\t\t\t\tsquare(center = false, size = [1000, 1000]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcircle(r = 2);\n\t\t}\n\t}\n}' actual = scad_render(newp) if expected != actual: print(''.join(difflib.unified_diff(expected, actual))) self.assertEqual(expected, actual)
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 test_fillet_2d_add(self): pts = [ [0, 5], [5, 5], [5, 0], [10, 0], [10, 10], [0, 10], ] p = polygon(pts) newp = fillet_2d(euclidify(pts[0:3], Point3), orig_poly=p, fillet_rad=2, remove_material=False) expected = '\n\nunion() {\n\tpolygon(paths = [[0, 1, 2, 3, 4, 5]], points = [[0, 5], [5, 5], [5, 0], [10, 0], [10, 10], [0, 10]]);\n\ttranslate(v = [3.0000000000, 3.0000000000, 0.0000000000]) {\n\t\tdifference() {\n\t\t\tintersection() {\n\t\t\t\trotate(a = 358.0000000000) {\n\t\t\t\t\ttranslate(v = [-998, 0, 0]) {\n\t\t\t\t\t\tsquare(center = false, size = [1000, 1000]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trotate(a = 452.0000000000) {\n\t\t\t\t\ttranslate(v = [-998, -1000, 0]) {\n\t\t\t\t\t\tsquare(center = false, size = [1000, 1000]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcircle(r = 2);\n\t\t}\n\t}\n}' actual = scad_render(newp) self.assertEqual(expected, actual)
def threadedinsert(flip_x, thick, holesize, length): """insert heatpress insert Heatpress can be inserted from the top. The screw should also be inserted from the top. The screw is centered at the origin. The wall thickness are specified by; http://uk.farnell.com/tr-fastenings-brass-inserts-for-plastics The length and hole size should be obtained from the sheet. this threaded is supported by a triangle and can be mirrored in the x-direction :param flip_x: flip insert in, origin shifted back :param length: length of the insert :param holesize: diameter of the hole """ # NOTE: other options # -sliding ; this results in a cable collision # -press insert; more recommended for photopolymer parts, less permanent # -bolt printed inside; requires print pause, not useful in production # -magnet; magnets are dangerous for electronics x_extent = holesize + 2 * thick y_extent = holesize / 2 + thick + SCREW_FIXOFFST base = cube([x_extent, y_extent, length]) triangle = polygon([[0, 0], [y_extent, y_extent], [0, y_extent]]) prism = linear_extrude(x_extent)(triangle) support = translate([x_extent, y_extent, 0])(rotate([90, 0, -90])(prism)) base = support + up(y_extent)(base) base -= translate([holesize / 2 + thick, holesize / 2 + thick, 0])(cylinder(h=length + y_extent, r=holesize / 2, segments=30)) # changed orientation to simplify placement base = down(y_extent + length)(base) # center origin at Z-axis base = translate( [-x_extent / 2, -SCREW_FIXOFFST + thick + holesize / 2, 0])(base) if flip_x: base = rotate([0, 0, 180])(base) return base
def ipadcorner(height=7.2): """Returns a 15x15mm corner of the ipad at mm height""" o = linear_extrude(height)(polygon(IPAD_CORNER)) return o
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 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)