def arc_inverted(rad: float, start_degrees: float, end_degrees: float, segments: int = None) -> OpenSCADObject: # Return the segment of an arc *outside* the circle of radius rad, # bounded by two tangents to the circle. This is the shape # needed for fillets. # Note: the circle that this arc is drawn from gets segments, # not the arc itself. That means a quarter-circle arc will # have segments/4 segments. # Leave the portion of a circumscribed square of sides # 2*rad that is NOT in the arc behind. This is most useful for 90-degree # segments, since it's what you'll add to create fillets and take away # to create rounds. # NOTE: an inverted arc is only valid for end_degrees-start_degrees <= 180. # If this isn't true, end_degrees and start_degrees will be swapped so # that an acute angle can be found. end_degrees-start_degrees == 180 # will yield a long rectangle of width 2*radius, since the tangent lines # will be parallel and never meet. # Fix start/end degrees as needed; find a way to make an acute angle if end_degrees < start_degrees: end_degrees += 360 if end_degrees - start_degrees >= 180: start_degrees, end_degrees = end_degrees, start_degrees # We want the area bounded by: # -- the circle from start_degrees to end_degrees # -- line tangent to the circle at start_degrees # -- line tangent to the circle at end_degrees # Note that this shape is only valid if end_degrees - start_degrees < 180, # since if the two angles differ by more than 180 degrees, # the tangent lines don't converge if end_degrees - start_degrees == 180: raise ValueError("Unable to draw inverted arc over 180 or more " "degrees. start_degrees: %s end_degrees: %s" % (start_degrees, end_degrees)) wide = 1000 high = 1000 top_half_square = translate((-(wide - rad), 0, 0))(square([wide, high], center=False)) bottom_half_square = translate( (-(wide - rad), -high, 0))(square([wide, high], center=False)) a = rotate(start_degrees)(top_half_square) b = rotate(end_degrees)(bottom_half_square) ret = (a * b) - circle(rad, segments=segments) return ret
def projection(): d = motor.body_diameter + 10. outer = sp.hull()( sp.square((plate_width, d), center=True), spu.back(lower_extent - 5.)(sp.square((plate_width, 10.), center=True)), ) mounting_holes = spu.back(mount_holes_offset)(place_at_centres( (mount_holes_distance, 0.), mounting_slot())) return outer - motor.mountable_face() - mounting_holes
def __init__(self, retainer_diameter, height, depth, threads_per_inch, cap_diameter, hole_diameter, cap_thickness,**kwargs): r_r = to_mm(retainer_diameter/2.) height = to_mm(height) h_r = to_mm(hole_diameter/2.) c_r = to_mm(cap_diameter/2.) c_t = to_mm(cap_thickness) threads_per_inch = threads_per_inch o_d = kwargs.get('outer_diameter') i_d = kwargs.get('inner_diameter') if 'bodytube' in kwargs: o_d = kwargs['bodytube'].outer_diameter i_d = kwargs['bodytube'].inner_diameter print i_d i_r = to_mm(i_d/2.) o_r = to_mm(o_d/2.) print retainer_diameter flange_d = to_mm(kwargs.get('flange_diameter'), safe=True) flange_t = to_mm(kwargs.get('flange_thickness'), safe=True) spine_diameter = to_mm(kwargs.get('spine_diameter'), safe=True) round_radius = to_mm(kwargs.get('round_radius',0)) if flange_d and flange_t: flange = cylinder(h=flange_t, r=flange_d/2.) else: flange = cylinder(h=height, r=o_r/2.) # HACK because this will be removed self.retainer = difference() (self.threaded_male_column(height, to_mm(retainer_diameter), threads_per_inch) + flange, cylinder(h=height, r=o_r), up(height-depth), cylinder(h=depth, r=i_r)) self.cap = difference()(cylinder(h=height,r=c_r), self.threaded_female_column(height-c_t, to_mm(retainer_diameter), threads_per_inch), cylinder(h=height, r=h_r)) if spine_diameter: no_spines = kwargs.get('spines',0) spines = [] for idx in xrange(0, no_spines): spines.append(rotate([0,0,360/no_spines*idx])(right(c_r)(cylinder(h=height-c_t-spine_diameter/2, r=spine_diameter/2)+ up(height-c_t-spine_diameter/2)(sphere(r=spine_diameter/2.))))) self.cap+=union()(*spines) self.round = rotate_extrude()(right(c_r-round_radius)(square([round_radius,round_radius])*circle(round_radius))) self.cap -= up(height-round_radius)(rotate_extrude()(right(c_r-round_radius)(square([round_radius,round_radius])))) self.cap += up(height-round_radius)(self.round)
def slice_mesh(fn='LOGOROBOT.stl', t_m=3.0): """ Return list of slices from stl modifed with through holes for alignment. Args: filename (str): STL file with 3D solid to turn into slices t_m (float): material thickness Returns: list of PolyLines that are the 2D profiles of the slices """ poly = PolyMesh(filename=fn) #custom rotation for LOGOROBOT rot = sl.rotate(-90, [1, 0, 0])(poly.get_generator()) poly = PolyMesh(generator=rot) slices = [] plane = sl.square(1000, True) axis = np.array([0, 0, 1]) # slice in Z direction (alpha_min, alpha_max) = mesh_extremum(poly, axis) for alpha in frange(alpha_min, alpha_max, t_m): norm = list(axis * alpha) slice_polyline = PolyLine(generator=plane) slice_args = (slice_polyline, norm) slice = planar_slice(poly, slice_args, t_m) slices.append(slice) return slices
def slice_mesh(fn="LOGOROBOT.stl", t_m=3.0): """ Return list of slices from stl modifed with through holes for alignment. Args: filename (str): STL file with 3D solid to turn into slices t_m (float): material thickness Returns: list of PolyLines that are the 2D profiles of the slices """ poly = PolyMesh(filename=fn) # custom rotation for LOGOROBOT rot = sl.rotate(-90, [1, 0, 0])(poly.get_generator()) poly = PolyMesh(generator=rot) slices = [] plane = sl.square(1000, True) axis = np.array([0, 0, 1]) # slice in Z direction (alpha_min, alpha_max) = mesh_extremum(poly, axis) for alpha in frange(alpha_min, alpha_max, t_m): norm = list(axis * alpha) slice_polyline = PolyLine(generator=plane) slice_args = (slice_polyline, norm) slice = planar_slice(poly, slice_args, t_m) slices.append(slice) return slices
def flat(): body = square(size=[length, height]) holes = [] num_cutouts = int(ceil(length / step)) for i in xrange(num_cutouts + 1): holes.append(right((i + 1) * step)(slit())) body -= union()(holes) return body
def projection(): p = outer_projection(3.1) horn_button = sp.translate((-18, 15))(sp.square(cherry_mx_cutout, center=True)) headlight_button = sp.translate((-18, -15))(sp.square(cherry_mx_cutout, center=True)) light_switch = sp.translate((0, -15))(sp.circle(d=switch_diameter, segments=32)) gear_select_switch = sp.translate((18, 15))(sp.circle(d=switch_diameter, segments=32)) display_button = sp.translate((18, -15))(sp.square(cherry_mx_cutout, center=True)) return p - horn_button - headlight_button - light_switch - gear_select_switch - display_button
def square_key(size, top_delta, progress): """ square key """ if isinstance(size, list): dims = [size[i] - top_delta[i] * progress for i in [0, 1]] else: dims = size - (top_delta * progress) return s.square(dims, center=True)
def create_left_buttom_circle(): p0 = switches.get_cap_corner((0, 1), Point2(0.5, -0.5)) p1 = switches.get_cap_corner((1, 1), Point2(0.5, -0.5)) p2 = switches.get_cap_corner((3, 1), Point2(0, -0.62)) circle = utils.three_point_cicle(p0, p1, p2, radius_adjustment=1.5 * mm, segments=1000) right_edge = switches.get_cap_corner(Point2(0, 0), Point2(-0.5, 0.5)).x return circle * sc.square(right_edge * 2, center=True)
def joints_bb_coord(mechanism): joints_coord = [] bb_coord = [] origin = numpy.array([0, 0, 0]) ternary_body = filter_three_joints(mechanism) ternary_joints = ternary_body.joints for i in range(len(ternary_joints)): joints_coord.append(ternary_joints[i].pose[0]) BBox = ternary_body.bounding_box() BBox_points = BBox.points BBox_point0 = numpy.append(BBox_points[0], [0]) #numpy append to add a z coord width = numpy.linalg.norm(pts_to_vec(BBox_points[0], BBox_points[2])) height = numpy.linalg.norm(pts_to_vec(BBox_points[0], BBox_points[1])) if width == height: bb_square = BBox elif width > height: bb_square = PolyLine(generator=solid.square(width)).simplified() tr_vec = translation_matrix(pts_to_vec(origin, BBox_point0)) bb_square *= tr_vec else: bb_square = PolyLine(generator=solid.square(height)).simplified() tr_vec = translation_matrix(pts_to_vec(origin, BBox_point0)) bb_square *= tr_vec bb_square_points = [ numpy.append(bb_square.points[0], [0]), numpy.append(bb_square.points[2], [0]) ] #numpy append to add a z coord - consistency # For the sake of clarity transforming the arrays of bb_square_points to tuplles # # In this way the output of the function would be always lists of tupples # bb_pts_tpl = map(tuple, bb_square_points) return joints_coord, bb_pts_tpl
def projection(): panel = sp.square(tray_dimensions, center=True) frame_mounting_holes = place_at_centres( mounting_hole_centres, sp.circle(d=mounting_hole_diameter, segments=32)) return panel - frame_mounting_holes - placements.vesc( vesc.holes()) - placements.lighting_control_board( lighting_control_board.holes()) - placements.relay_board( relay_board.holes()) - placements.bec_module( bec_module.holes()) - placements.logic_board( logic_board.holes()) - placements.vesc_fan( shroud.mounting_holes(32))
def joints_bb_coord (mechanism): joints_coord=[] bb_coord=[] origin = numpy.array([0,0,0]) ternary_body = filter_three_joints(mechanism) ternary_joints = ternary_body.joints for i in range(len(ternary_joints)): joints_coord.append(ternary_joints[i].pose[0]) BBox = ternary_body.bounding_box() BBox_points= BBox.points BBox_point0 = numpy.append(BBox_points[0], [0]) #numpy append to add a z coord width = numpy.linalg.norm(pts_to_vec(BBox_points[0],BBox_points[2])) height = numpy.linalg.norm(pts_to_vec(BBox_points[0],BBox_points[1])) if width == height: bb_square = BBox elif width > height: bb_square = PolyLine(generator = solid.square(width)).simplified() tr_vec = translation_matrix(pts_to_vec(origin, BBox_point0)) bb_square *= tr_vec else: bb_square = PolyLine(generator = solid.square(height)).simplified() tr_vec = translation_matrix(pts_to_vec(origin, BBox_point0)) bb_square *= tr_vec bb_square_points = [numpy.append(bb_square.points[0], [0]), numpy.append(bb_square.points[2], [0])] #numpy append to add a z coord - consistency # For the sake of clarity transforming the arrays of bb_square_points to tuplles # # In this way the output of the function would be always lists of tupples # bb_pts_tpl = map (tuple, bb_square_points) return joints_coord, bb_pts_tpl
def main(params: Params): profile = utils.profile_with_screws(params) cutout = None if params.cutout_depth > 0: cutout = s.linear_extrude(params.cutout_depth)( s.square([params.interface_width, params.height]) ) if cutout is not None: cutout_x = params.screw_spacing * 2 + params.screw_head_radius * 2 cutout_z = params.depth - params.cutout_depth profile = s.difference()(profile, s.translate([cutout_x, 0, cutout_z])(cutout)) return profile
def generalized_pot(extrude_func, prof_n1, prof_p1, pot_l, pot_w, holes=True, base_th=3, emboss_text=""): base_prof = S.difference()( # main profile S.difference()( prof_p1(), prof_n1(), ), # cut off everything above the base height S.translate([-pot_l * 5, base_th])(S.square(pot_l * 10), ), ) base = S.hull()(extrude_func(base_prof)) # bottom holes if holes: base = S.difference()( base, S.translate([0, 0, -INC])(S.linear_extrude(base_th * 2)(hole_pattern( pot_l, pot_w)), ), ) # embossed text emboss_depth = 0.4 font_size = 6 if len(emboss_text): base = S.difference()( base, # text S.translate([0, 10, base_th - emboss_depth])(S.linear_extrude( emboss_depth * 2)(S.text( EMBOSS_TEXT, halign="center", valign="center", size=font_size)), )) return S.difference()( S.union()(extrude_func(prof_p1), base), extrude_func(prof_n1), )
def projection(): cu = 30. cd = -30. def arm(a, b): return sp.hull()( sp.translate(a)(sp.circle(d=20., segments=32)), sp.translate(b)(sp.circle(d=25., segments=32)), ) # Drill these holes in the existing steering wheel box_mounting_holes = instrument_panel.place_mounting_holes( drilled_hole.projection(diameter=4.)) return sp.union()( sp.square((75., 75.), center=True), arm((-xu, yu), (0, cu)), arm((xu, yu), (0, cu)), arm((-xd, yd), (0, cd)), arm((xd, yd), (0, cd)), ) - place_mounting_holes( sp.circle(d=mounting_hole_diameter)) - box_mounting_holes
def example(): """Run lab_0 example. Creates all_shapes.scad and all_shapes.dxf files. Returns: open, squarcle, and star PolyLines """ # Make an open PolyLine defined with points open_pl = PolyLine([[0,0],[0,60],[100,0],[200,0]]) # Make an OpenSCAD generator squarcle_gen = ( solid.square(50) + solid.translate([50,25])(solid.circle(25)) - solid.translate([20,15])(solid.text('S',size=20)) ) # Use the OpenSCAD generator to make a PolyLine squarcle_pl = PolyLine(generator=squarcle_gen).simplified() # Create star.dxf by saving a PolyLine star().save('star.dxf') # Load PolyLine from DXF file star_pl = PolyLine(filename='star.dxf').simplified() # Scale, translate and rotate PolyLines small_open_pl = 0.5 * open_pl trans_squarcle_pl = (0,50,0) * squarcle_pl trans_rot_star_pl = (50,175,numpy.pi/2) * star_pl # Combine the geometries and save them all_shapes = small_open_pl + trans_squarcle_pl + trans_rot_star_pl all_shapes.save('all_shapes.scad') all_shapes.save('all_shapes.dxf') return (open_pl, squarcle_pl, star_pl)
def plane_from_normal(norm): plane = sl.square(1000, True) plane_poly = PolyLine(generator=plane) plane_axis = np.array([0, 0, 1]) if norm == plane_axis: return plane rot_axis = np.cross(norm, plane_axis) cos_theta = np.dot(norm, plane_axis) theta = math.acos(cos_theta) quaternion = quaternion_about_axis(theta, rot_axis) z = np.array([0, 0, 1]) x = np.array([1, 0, 0]) rot_axis = np.cross(x, z) cos_theta = np.dot(z, x) theta = math.acos(cos_theta) quat = quaternion_about_axis(theta, rot_axis) quat_mat = quaternion_matrix(quat) x * quat I = identity_matrix() plane_poly.points plane_poly * e e = euler_from_quaternion(quat) np.dot(quat, np.array(z))
import solid as sc import typing import math from euclid3 import Point2 import utils import z3 circle_center = Point2(15, 15) circle_radius = 10 circle = sc.translate((*circle_center, 0))(sc.circle(circle_radius, segments=100)) square = sc.square(20, center=True) def dist_square(a, b): return (a[0] - b[0])**2 + (a[1] - b[1])**2 PRECISION = 6 def assert_tangent_to_circle(fillet_center, fillet_radius: float, circle_radius: Point2, circle_center: Point2): dist_squared = (circle_center.x - fillet_center[0])**2 + ( circle_center.y - fillet_center[1])**2 return dist_squared == (circle_radius + fillet_radius)**2 def assert_tangent_to_line(
def projection(): outer = sp.square(size, center=True) return outer - main_vent() - mounting_holes()
def main_vent(): return sp.intersection()( sp.square([d - 2. for d in size], center=True), sp.circle(d=64.), )
def make_hole(self): body = square([self.width, self.height]) layer_width = (phone_width + 2 * radius) moved_right = right(layer_width / 2 - self.width / 2)(body) moved_down = back(self.height / 2 + self.y_adjust)(moved_right) return moved_down
def projection(size, radius=3., center=True): return sp.minkowski()( sp.square([a - 2. * radius for a in size], center=center), sp.circle(r=radius, segments=16), )
def roundrect(vector: Vector2, radius: float): mod_vec = [v - radius * 2 for v in vector] return s.minkowski()( s.square(mod_vec), s.translate([radius, radius, 0])(s.circle(r=radius)) )
from solid import linear_extrude, scad_render_to_file from solid.utils import color, down, left, Green from math import sqrt, pi, sin, cos, atan2 #--------------------------------------------- if __name__ == '__main__': from solid import include, use, scad_render, rotate, square from sys import argv from re import sub arn = 0 arn += 1 gThik = float(argv[arn]) if len(argv) > arn else 6 arn += 1 circPitch = float(argv[arn]) if len(argv) > arn else 10 use('GFgear.scad') tRateHold, toothHold, stickHold = 98765, 76543, 54321 gearAsm = gear(toothHold, circPitch) + square([stickHold, 1]) hs = linear_extrude(gThik, True)(rotate(tRateHold)(gearAsm)) # Substitute variable names & formulae in place of various holders # Note, scad_render returns a string with a `use <...>` statement # and CSG .scad code for gear and square outline and its extrusion. hs = sub(str(toothHold), 'toothCount', scad_render(hs)) hs = sub(str(tRateHold), '(0.5-abs($t-0.5))*tAngle*2', hs) hs = sub(str(stickHold), 'toothCount*{:0.4f}'.format(circPitch / 4), hs) hs = '/* [Params:] */\n// Number of Teeth\ntoothCount=11; // [3:300]\n// Total sweep angle; 1-360\ntAngle=90; // [1:360]\n$fn=20;\n{}'.format( hs) fo = open('tooth.scad', 'w+') fo.write(hs) fo.close()
a_circle = PolyLine(generator = solid.circle(r = 7/2.0)) right_circle = a_circle.clone() left_circle = a_circle.clone() right_circle *= translation_matrix([40,0,0]) left_circle *= translation_matrix([-40,0,0]) right_down_circ = right_circle.clone() right_down_circ *= tr_matrix left_down_circle = left_circle.clone() left_down_circle *= tr_matrix_inv circles = right_circle + right_down_circ + left_circle + left_down_circle rec = PolyLine(generator = solid.square(size = [175,175], center = True)) base = Block(Layer(geometries = circles + rec, color= 'red'), name = 'base_gripper') # TO PRODUCE LAYOUT CUTS # gen_laser_cuts(bot_leg, name = 'robot_leg') grip_l_layout= gen_laser_cuts(grip_r, name = 'gripper_l_arm', plot_to_file = False) # Because it is just a mirror of grip_r gen_laser_cuts(grip_r, name = 'gripper_r_arm', sheet = (1200,600), plot_to_file = False).augmented(base).augmented(grip_l_layout).solved(margin = 3.0)[0].save('gripper.dxf') # NOW CUT AND ASSEMBLE YOU LASY BASTARD # """ TEST FOR GRIPPER ARMS - this was a nightmare, I wish I was a more math guy
def rcylinder(r, h, rnd=0, center=False): ''' primitive for a cylinder with rounded edges''' if rnd == 0: return solid.cylinder(r=r, h=h, center=center) mod_cyl = solid.translate((0, 0, -h/2 if center else 0))( \ solid.rotate_extrude(convexity=1)(solid.offset(r=rnd)(solid.offset(delta=-rnd)(solid.square((r, h)) + solid.square((rnd, h)))))) return mod_cyl