def tabs(self): c = math.cos(TAB_ANGLE) s = math.sin(TAB_ANGLE) alpha = 1.0 / (math.sqrt(4.0 - c * c) / (2 * s)) post = self.inner_cylinder() ytabs = scale([2, alpha, 1])(post) xtabs = scale([alpha, 2, 1])(post) return self.tab_cylinder() * (xtabs + ytabs)
def ell_main(): return S.translate([0, 0, pot_w])( S.difference()( S.scale([1, pot_lw_ratio, 1])(S.sphere(pot_w)), # cut out inside S.scale([s2, pot_lw_ratio * s2, s2])(S.sphere(pot_w)), S.translate([-rectl / 2, -rectl / 2, -pot_w + 50])(S.cube([rectl, rectl, rectl])), ))
def scaffold(length, color=[0, 0, 1, 1]): h = solid.translate([diameter / 2.0, 0, length / 2.0])( solid.scale([diameter, diameter, length])( solid.cube(center=True) ) ) + solid.translate([0, 0, length / 2.0])( solid.scale([center_notch + tool_radius * 2, center_notch, length])( solid.cube(center=True) ) ) # scale & move in Z to ensure overlap h = solid.translate([0, 0, -(length * .1)/2.0])(solid.scale([1, 1, 1.1])(h)) return solid.color(color)(h)
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth, frameClearance, enlargeHoles, front): zScale = -1 if front else 1 xRotate = 180 if front else 0 substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf, thickness, frameHeight, frameWidth, frameClearance)) holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles) holes = solid.linear_extrude(height=4*thickness, center=True)( holesOffset(solid.import_dxf(holesDxf))) substrate -= holes for h in extraHoles: substrate -= solid.scale([toMm(1), -toMm(1), 1])( solid.linear_extrude(height=4*thickness, center=True)( solid.polygon(h.exterior.coords))) return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
def slot_peg_with_catch( diameter=DEFAULT_PEG_DIAMETER, thickness=DEFAULT_HOLDER_THICKNESS, clearance=DEFAULT_CLEARANCE, overreach=None, slot_width=None, slot_clearance=DEFAULT_CONTAINER_CLEARANCE ): if overreach is None: overreach = diameter peg = solid_peg(diameter=diameter, thickness=thickness, clearance=clearance, overreach=overreach) if slot_width is None: slot_width = 0.35 * min(diameter, overreach) hole_height = clearance + overreach hole_displacement = slot_clearance + thickness + 0.5 * hole_height round_hole_bottom = back(0.5 * hole_height)( cylinder(r=0.5 * slot_width, h=2 * diameter, center=True, segments=16)) hole_cutout = cube([slot_width, hole_height, 2 * diameter], center=True) hole = up(hole_displacement)( rotate([90.0, 0.0, 0.0])( hole_cutout + round_hole_bottom)) catch_height = clearance + thickness + 0.5 * diameter catch_offset = 0.5 * diameter unscaled_catch = up(catch_height)( rotate([90, 0, 0])( cylinder(r=0.5 * diameter, h=0.5 * slot_width, center=True, segments=4))) catch = scale([0.4, 1.0, 1.0])(unscaled_catch) return peg \ + left(catch_offset)(catch) \ + right(catch_offset)(catch) \ - hole
def key_handle(): radius = HANDLE_DIAMETER / 2 base = scale([1.5, 1.0, 1.0])(cylinder(r=radius, h=KEY_HEIGHT, center=True, segments=32)) return back(radius)(base)
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness, outerBorder, innerBorder, tolerance, topSide): bBox = findBoardBoundingBox(board) centerpoint = rectCenter(bBox) top = jigThickness - fromMm(0.15) pcbBottom = jigThickness - pcbThickness outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder) outerRing = outerPolygon.exterior.coords if topSide: outerRing = mirrorX(outerRing, centerpoint[0]) body = solid.linear_extrude(height=top, convexity=10)(solid.polygon(outerRing)) innerRing = createOffsetPolygon(board, -innerBorder).exterior.coords if topSide: innerRing = mirrorX(innerRing, centerpoint[0]) innerCutout = solid.utils.down(jigThickness)(solid.linear_extrude( height=3 * jigThickness, convexity=10)(solid.polygon(innerRing))) registerRing = createOffsetPolygon(board, tolerance).exterior.coords if topSide: registerRing = mirrorX(registerRing, centerpoint[0]) registerCutout = solid.utils.up(jigThickness - pcbThickness)( solid.linear_extrude(height=jigThickness, convexity=10)(solid.polygon(registerRing))) register = body - innerCutout - registerCutout for hole in holes: register = register - solid.translate([hole[0], hole[1], top])( m2countersink()) return solid.scale(toMm(1))(solid.translate( [-centerpoint[0], -centerpoint[1], 0])(register))
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_same_transformations_nonequivalent_types(self) -> None: self.assertFalse( affinables.Affinable._same_transformations( [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]), solid.scale([31.0, 32.0, 33.0]), ], [ solid.translate([11.0, 12.0, 13.0]), solid.scale([21.0, 22.0, 23.0]), solid.rotate([31.0, 32.0, 33.0]), ], ), msg="Similar transformations with different types are not the same", )
def test_same_transformations_equivalent(self) -> None: self.assertTrue( affinables.Affinable._same_transformations( [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]), solid.scale([31.0, 32.0, 33.0]), ], [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]), solid.scale([31.0, 32.0, 33.0]), ], ), msg="Identical transformations should be considered the same", )
def test_same_transformations_different_order(self) -> None: self.assertFalse( affinables.Affinable._same_transformations( [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]), solid.scale([31.0, 32.0, 33.0]), ], [ solid.scale([31.0, 32.0, 33.0]), solid.translate([21.0, 22.0, 23.0]), solid.rotate([11.0, 12.0, 13.0]), ], ), msg= "Identical transformations in different orders should not be considered the same", )
def create_cutter(addr, left_or_right_dir): pos = switches.get_switch_position(addr) pos = Point3(*pos, 0) angle = switches.get_switch_angle(addr) cutter = switches.create_switch(addr, size=1) cutter = sc.translate(pos)(sc.scale( (4, 4, 0))(sc.translate(pos * -1)(cutter))) offset = utils.unit_point2(angle) * 2.5 * left_or_right_dir cutter = sc.translate((*offset, 0))(cutter) return cutter
def __call__(self, scaling=nscale_inches): table = self.picnic_table_top() + self.picnic_seats() + self.picnic_frame() if self.add_support: table += self.support() return scale(1 * scaling)( up(self.table_length/2) ( rotate([90, 0, 0]) ( table ) ) )
def test_transform_dispatch_scaling(self) -> None: holonomic_transformable = MockHolonomicTransformable() scaling = solid.scale([31.0, 32.0, 33.0]) holonomic_transformable.transform(scaling) self.assertEqual( holonomic_transformable._scalings, [scaling], msg="Scaling should be correctly dispatched", )
def test_transform_equivalent(self) -> None: original_connector = connector.Connector( point=vector.Vector.from_raw([1, 1, 1]), axis=vector.Vector.from_raw([0, 0, 1]), normal=vector.Vector.from_raw([1, 0, 0]), ) self.assertEqual( original_connector.rotate(solid.rotate(a=[90, 90, 90])).translate( solid.translate([1, 1, 1])).scale(solid.scale([2, 2, 2])), original_connector.transform([ solid.rotate(a=90, v=[1, 0, 0]), solid.rotate(a=90, v=[0, 1, 0]), solid.rotate(a=90, v=[0, 0, 1]), solid.translate([1, 1, 1]), solid.scale([2, 2, 2]), ]), msg= "Transform should accept & correctly dispatch multiple transformations", )
def rect2scad(rect, height, z_start = 0.0, mirrored = False): """ Convert a Rectangle into an openscad cube by giving it a height and Z start """ scad_cube = sc.translate([rect.left(), rect.bot(), z_start])( sc.cube([rect.width, rect.height, height]) ) if mirrored: return sc.scale([1,1,-1])(scad_cube) else: return scad_cube
def key_lettering(): messages = ["LeRoy", "Dental"] layout = union()([ forward((index - 0.5) * -LETTER_HEIGHT)(text(message, halign='center', valign='center')) for index, message in enumerate(messages) ]) lettering = up(THICKNESS / 2 - LETTERING_RISE)( linear_extrude(height=2 * LETTERING_RISE)(scale([0.8, 0.8, 1])(layout))) return back(HANDLE_DIAMETER / 2)(lettering)
def test_eq_different_transformations(self) -> None: transformable_1 = MockHistoricalTransformable() transformable_1.transform(solid.rotate([11.0, 12.0, 13.0])) transformable_2 = MockHistoricalTransformable() transformable_2.transform(solid.scale([31.0, 32.0, 33.0])) self.assertNotEqual( transformable_1, transformable_2, msg= "Two historical transformables with different transformations are not equal", )
def shell(obj: OpenSCADObject, wall: float, center_x: float, center_y: float, center_z: float) -> OpenSCADObject: ''' create a shell of a given object centered at [center_x, center_y, center_z] scaled by wall as a percentage. PARAMETERS: center_x, center_y, center_z: center coordinates of the hollow shell wall: thickness of the shell given as a percentage of the given object RETURNS: shell of given OpenSCADObject ''' solution = obj - hole()(translate([ center_x / 2, center_y / 2, center_z / 2 ])(scale([wall, wall, wall])(obj))) return solution
def test_same_transformations_different_lengths(self) -> None: self.assertFalse( affinables.Affinable._same_transformations( [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]), solid.scale([31.0, 32.0, 33.0]), ], [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]) ], ), msg="Transformation lists of different lengths are not the same", )
def create_model(data, rows, columns): hexes = to_base(int.from_bytes(data, 'big'), 720) assert len(hexes) == rows * columns sts = [] for row in range(rows): for col in range(columns): order = permute.permute([1, 2, 3, 4, 5, 6], hexes[row * 9 + col]) heights = [1 + 1.5 * i / len(order) for i in order] st = stump(heights) xdiff = 1.5 * col xdiff *= 1.2 ydiff = (-3**0.5) * row - (3**0.5 / 2) * col ydiff *= 1.2 st = solid.translate([xdiff, ydiff, 0])(st) sts.append(st) base = solid.translate([-3.4, -3.2, 0])(solid.rotate([0, 0, -30])( solid.scale([22, 6.5, 0.3])(solid.cube(1)))) x, y = 0.5, (3**0.5 / 2) / 2 marker = solid.translate([-1.5, -2.5, 0])( solid.polyhedron( points=[ # bottom (-x, -y, 0), (x, -y, 0), (0, y, 0), # top (-x, -y, 1), (x, -y, 1), (0, y, 1) ], faces=[ # bottom (0, 1, 2), # top (5, 4, 3), # sides (0, 3, 4, 1), (1, 4, 5, 2), (2, 5, 3, 0) ])) return solid.union()([base, solid.union()(sts), marker])
def combine_shapes(shapes): """ Transform and combine shapes as in all_shapes below using OpenSCAD generators and functions. Args: shapes = (open_pl, squarcle_pl, star_pl) Retruns: A single PolyLine with transformed and combined geometry """ open_pl, squarcle_pl, star_pl = shapes small_open_pl = solid.scale(0.5)( open_pl.get_generator() ) trans_squarcle_pl = solid.translate([0,50])( squarcle_pl.get_generator() ) trans_rot_star_pl = solid.translate([50,175])( solid.rotate(numpy.pi/2)( star_pl.get_generator() ) ) combined = (small_open_pl + trans_squarcle_pl + trans_rot_star_pl) return PolyLine(generator=combined)
def hold_down(od, wing_thickness, alpha, height, width, angle): half_thickness=to_mm(wing_thickness/2); r=to_mm(od/2); depth = r * sqrt(1-alpha*alpha); h=to_mm(height); w=to_mm(width); hprime = h*w/(w-(1-alpha)*r); basic_wedge = linear_extrude(h, center=True)(polygon([[0,half_thickness], [0,depth], [w, half_thickness]])) basic_cylinder = rotate([angle, 0,0])(cylinder(h=h*1.5, r=r,center=True)) cone_envelope = translate([alpha*r, half_thickness,-h*0.5])( rotate([0,90,0])( scale([hprime/(depth-half_thickness),1,1])( cylinder(r1=depth-half_thickness, r2=0, h=w)))) clipping_cube = cube([10000,r*1.2, 10000],center=True); holder = (translate([alpha*r, 0, 0])(basic_wedge)- basic_cylinder)* cone_envelope *clipping_cube return holder
def test_transformed_object(self) -> None: transformations = [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]), solid.scale([31.0, 32.0, 33.0]), ] transformable = MockHistoricalTransformable() transformable.transform(transformations) transformed_nonaffinable = transformable.cube self.assertTrue( affinables.Affinable._same_transformations( transformations, # Just extract the transformations to compare utils.flatten_openscad_children(transformed_nonaffinable)[1:], ), msg= "The property should be correctly transformed, identically to the parent Affinable", )
def test_transformed_solid_transformation(self) -> None: transformations = [ solid.rotate([11.0, 12.0, 13.0]), solid.translate([21.0, 22.0, 23.0]), solid.scale([31.0, 32.0, 33.0]), ] transformable = MockHistoricalTransformable() transformable.transform(transformations) cube = solid.cube(1) transformed_nonaffinable = transformable.transformed(cube) self.assertTrue( affinables.Affinable._same_transformations( transformations, # Just extract the transformations to compare utils.flatten_openscad_children(transformed_nonaffinable)[1:], ), msg= "Transformation application order should be unchanged, and application shouldn't alter transformations", )
def __post_init__(self): """ create the dish calculates chord_length: y offest """ if self.top_dims is None: self.top_dims = [12.16, 14.16] self.key_x, self.key_y = self.top_dims if self.dish_type == "cylinder": c_length = chord_length(self.key_x, self.depth) radius = rad_chord(self.key_x, self.depth) self.dish = s.cylinder(h=100, r=radius, center=True, segments=self.segments) if self.inverted: self.dish = s.translate([0, -c_length, 0])(self.dish) else: self.dish = s.translate([0, c_length, 0])(self.dish) self.dish = s.rotate([90, 0, 0])(self.dish) elif self.dish_type == "sphere": self.chord = pow((pow(self.key_x, 2) + pow(self.key_y, 2)), .5) c_length = chord_length(self.chord, self.depth) self.radius = rad_chord(self.chord, self.depth) self.dish = s.scale([ self.chord / 2 / self.depth, self.chord / 2 / self.depth ])(s.sphere(r=self.depth, segments=self.segments)) self.dish = s.translate([0, 0, self.key_z])(self.dish)
def test_scaling(self) -> None: original_connector = connector.Connector( point=vector.Vector.from_raw([2, 2, 2])) translated_connector = original_connector.scale(solid.scale([1, 2, 3])) self.assertEqual( translated_connector.point, vector.Vector.from_raw([2, 4, 6]), msg="The connector point should be scaled as expected", ) self.assertEqual( translated_connector.axis, original_connector.axis, msg="The primary alignment axis should be unchanged under scaling", ) self.assertEqual( translated_connector.normal, original_connector.normal, msg= "The secondary alignment axis should be unchanged under scaling", )
def grabbers(): gripper_shape = [CLAMP_GRIP_WIDTH, CLAMP_LENGTH, CLAMP_GRIP_HEIGHT] gripper = grounded_cube(gripper_shape) gripper_offset = (CLAMP_BASE_WIDTH - CLAMP_GRIP_WIDTH) / 2 grippers = left_right_symmetric(gripper_offset, gripper) bumper_shape = [CLAMP_BUMPER_WIDTH, CLAMP_LENGTH, CLAMP_BUMPER_THICKNESS] bumper_elevation = CLAMP_GRIP_HEIGHT - CLAMP_BUMPER_THICKNESS bumper_offset = (CLAMP_BASE_WIDTH - CLAMP_BUMPER_WIDTH) / 2 round_bumper_radius = CLAMP_BUMPER_WIDTH / 2 shrinkage = CLAMP_BUMPER_THICKNESS / round_bumper_radius round_bumper = right(0)( rotate([90, 0, 0])( scale([1.0, shrinkage, 1.0])( cylinder(r=round_bumper_radius, h=CLAMP_LENGTH, segments=4, center=True)))) bumper = up(bumper_elevation)(round_bumper) # grounded_cube(bumper_shape) + ) bumpers = left_right_symmetric(bumper_offset, bumper) clipper_shape = [CLAMP_BASE_WIDTH, CLAMP_LENGTH, CLAMP_TOP_CLIP] clipper = grounded_cube(clipper_shape) return (grippers + bumpers) * clipper
SLIP = 1 # Space around the edges to allow the iPad to slide in WALL_PADDING = 0.4 BUTTON_TO_EDGE = 3.7 SCREEN_BORDER = 3 BUTTON_D = 14 # Oversize to also cover the camera and light sensor START_X = -100.000162 START_Y = -67.209085 POWER_CONNECTOR_LEN = 7 POWER_UNDERHANG = 20 LOCK_BUTTON_LEN = 12 LOCK_BUTTON_TO_EDGE = 9 BASE_IPAD = translate([0, IPAD_H, 0])(rotate([0, 0, -90])(scale( [25.4, 25.4, 25.4])(import_stl("iPadMini.STL")))).set_modifier("#") SCREEN_BOTTOM_EDGE = (IPAD_H - SCREEN_H) / 2 SCREEN_SIDE_EDGE = (IPAD_W - SCREEN_W) / 2 BASE_IPAD -= translate([SCREEN_SIDE_EDGE, SCREEN_BOTTOM_EDGE, IPAD_D - 0.1])(cube([SCREEN_W, SCREEN_H, 0.2]).set_modifier("%")) IPAD_STL = translate([IPAD_W, IPAD_H, 0])(rotate([0, 0, 180])(BASE_IPAD)) def screwthread(): """Returns a screw thread""" if DEV_MODE: return cylinder(d=6, h=15).set_modifier("#") else: return metric_thread(diameter=6, length=15)
# Lets create circles/holes for the joints and position them # c_circle= PolyLine(generator= solid.circle(3.5)).simplified() c_circle_lst=[] for i in range(len(joints_pose_tr)): d = c_circle.clone() trl_mtx = translation_matrix(joints_pose_tr[i]) d *= trl_mtx c_circle_lst.append(d) p_bBox= p.bounding_box().points p_w = numpy.linalg.norm(pts_to_vec(p_bBox[0],p_bBox[3])) p_h = numpy.linalg.norm(pts_to_vec(p_bBox[0],p_bBox[1])) scale_factor_x = ternary_w/p_w scale_factor_y = ternary_h/p_h p_scl = PolyLine(generator= solid.scale([scale_factor_x,scale_factor_y,1])(p.get_generator())).simplified() for i in range(len(a_circle_lst)): p_scl = p_scl | a_circle_lst[i] # Lets create layers # p_scl_layer= Layer(p_scl, color= 'red') circle_layer= Layer(c_circle_lst, color='red') Final_block = Block([p_scl_layer, circle_layer]) a_layout= Layout(blocks= Final_block).save('opt_foot.dxf')
def battery_cup(): height = 39 * mm diameter = 36 * mm squish = 31 * mm / diameter thickness = 2 * mm return scale([1.0, squish, 1.0])(cup(diameter, height, thickness))
ddiam1, ddiam2 = cdiam1+0.07, cdiam2+0.07 # ...flange..... ....center.... ...outer tube... dii = [cdiam1, cdiam1, cdiam1, cdiam2, diam2, diam2] doo = [1.70, 1.70, ddiam1, ddiam2, diam1, diam1] hss = [0.00, baseThik, baseThik, cHi, baseThik, thredHi] topAsm = cylinderAsm(dii, doo, hss) # Make assembly-of-cylinders # Bottom piece: Specify inner and outer diameters, plus heights thredSlop = 0.05 # Oversize to avoid thread binding # The bottom's ID is top's OD plus some slop for clearance diam1, diam2 = thredOD+thredSlop+0.20, thredOD+thredSlop moveTop = (doo[0]+diam1+.1)/2 # Left-shift for top & bottom parts ringHi = 0.35 turns = ringHi/pitch dii = [diam2, diam2] doo = [diam1, diam1-0.10] hss = [0, ringHi] botAsm = cylinderAsm(dii, doo, hss) # Make assembly-of-cylinders botThred = threadAsm(0, diam2, thredThik, pitch, 3, turns, False) # Assemble items and apply scale factor asm, bot, top = None, botAsm + botThred, topThred+topAsm if makeConn: asm = left(moveTop)(top) if makeRing: asm = asm + bot if asm else bot asm = scale((sf, sf, sf))(asm) cylSegments, version = 60, 3 cylSet_fn = '$fn = {};'.format(cylSegments) asmFile = 'flanged-tube{}.scad'.format(version) scad_render_to_file(asm, asmFile, file_header=cylSet_fn, include_orig_code=False) print ('Wrote scad code to {}'.format(asmFile))