This model is one of a few used to test my identifiers branch which precomputes string hashes & caches builtin function lookups.
-
-
Save ochafik/5b622b1075dfb2fbf537805aa925818b to your computer and use it in GitHub Desktop.
An OpenSCAD model that's heavy to evaluate (uses BOSL2 intensively), but quick to render
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| include <BOSL2/std.scad> | |
| include <BOSL2/turtle3d.scad> | |
| include <BOSL2/rounding.scad> | |
| include <BOSL2/std.scad> | |
| include <BOSL2/transforms.scad> | |
| single=true; | |
| grid=false; | |
| debug=true; | |
| N=2; | |
| slices=10; | |
| //slices=20; | |
| previewEpsilon = 0.1;//$render ? 0 : 0.01; | |
| smooth_steps=5; | |
| skin_slices=10; | |
| id=16; | |
| od=24; | |
| or=od/2; | |
| leg_length=30; | |
| thickness=8; | |
| hole_round_r=1; | |
| center=[or, leg_length - or]; | |
| holefront=center - [0, id/2]; | |
| wiggle=4; | |
| pole_d=id - wiggle; | |
| pole_len=16;//1.6*thickness; | |
| function slowFast(x) = | |
| 1 - sqrt(1 - pow(x, 2)); | |
| function fastSlow(x) = | |
| sqrt(1 - pow(x - 1, 2)); | |
| function fastFast(x) = | |
| (x < 0.5) | |
| ? sqrt(0.25 - pow(x - 0.5, 2)) | |
| : 1 - sqrt(0.25 - pow(x - 0.5, 2)); | |
| function slowSlow(x) = | |
| (x < 0.5) | |
| ? 0.5 - sqrt(0.25 - pow(x, 2)) | |
| : 0.5 + sqrt(0.25 - pow(x - 1, 2)); | |
| function transitionFactor(name, u) = | |
| name=="slowslow" ? slowSlow(u) | |
| : name=="fastfast" ? fastFast(u) | |
| : name=="fastslow" ? fastSlow(u) | |
| : name=="slowfast" ? slowFast(u) | |
| : u; | |
| function smooth_profiles(profiles, transforms, transitions="slowslow", steps=10) = [ | |
| for (i=[0:len(transforms)-2]) | |
| let (steps=is_list(steps) ? steps[i] : steps, | |
| transition=is_list(transitions) ? transitions[i] : transitions, | |
| t1=transforms[i], | |
| t2=transforms[i+1], | |
| p1=profiles[i], | |
| p2=profiles[i+1]) | |
| for (i=[0:steps]) | |
| let (f=i/steps, | |
| u=transitionFactor(transition, f)) | |
| apply( | |
| lerp(t1, t2, f), | |
| lerp(p1, p2, u)) | |
| ]; | |
| function hull_polys(pts) = | |
| [for (i=hull(pts)) pts[i]]; | |
| function plate(insets=0, hole=false) = | |
| let (radius=or - insets) | |
| hole | |
| ? apply( | |
| translate(center), | |
| // circle(id / 2 + insets) | |
| teardrop2d(id / 2 + insets) | |
| ) | |
| : | |
| // struct_set([], [ | |
| // "path", | |
| round_corners( | |
| hull_polys(concat( | |
| apply(translate(center), teardrop2d(d=od - 2*insets)), | |
| //teardrop2d(d=od - 2*insets), | |
| //apply(translate([od/2, 0]), arc(10, width=od, thickness=od/2)), | |
| //square([od, or/10]), | |
| apply(translate([insets, 0, 0]), square([od - 2 * insets, leg_length - or])), | |
| )) | |
| , radius=2 | |
| ) | |
| ; | |
| // turtle([ | |
| // "move", od - insets, | |
| // "left", | |
| // "move", center.y, | |
| // "arcsteps", 20, | |
| // "arcleft", radius, 180, | |
| // "move", center.y, | |
| // ]), | |
| // ]); | |
| //function geom_get_path(geom) = | |
| // struct_val(geom, "path"); | |
| //function geom_get_attachment(geom, name) = | |
| // struct_val(struct_val(geom, "attachments"), name); | |
| //translate([0, 0, 0]) | |
| module hole(top_shape) | |
| //back_half() | |
| top_half() | |
| //mirror([0, 1, 0]) | |
| zrot(180) | |
| { | |
| *skin( | |
| smooth_profiles( | |
| subdivide_and_slice([ | |
| //? path3d(top_shape), | |
| path3d(apply(translate([-od/2, -thickness]), square([od, thickness]))), | |
| path3d(apply(translate([-od, -3*thickness]), square([2*od, 3*thickness]))), | |
| ], 0), | |
| steps=smooth_steps, | |
| transforms=[ | |
| xrot(-45), | |
| up(0), | |
| ], | |
| transitions=[ | |
| "slowfast", | |
| ], | |
| ), | |
| method="distance", | |
| slices=skin_slices | |
| ); | |
| xrot(45) translate(-holefront) | |
| // translate(-geom_get_attachment(plate(0), "holefront")) | |
| difference() { | |
| skin( | |
| smooth_profiles( | |
| subdivide_and_slice([ | |
| path3d(plate(2)), | |
| path3d(plate(0)), | |
| path3d(plate(2)), | |
| ], 0), | |
| steps=smooth_steps, | |
| transforms=[ | |
| up(0 - previewEpsilon), | |
| up(thickness / 2), | |
| up(thickness + previewEpsilon), | |
| ], | |
| transitions=[ | |
| "fastslow", | |
| //"linear", | |
| "slowfast", | |
| ], | |
| ), | |
| method="distance", | |
| slices=skin_slices | |
| ); | |
| skin( | |
| smooth_profiles( | |
| subdivide_and_slice([ | |
| path3d(plate(2, hole=true)), | |
| path3d(plate(0, hole=true)), | |
| path3d(plate(0, hole=true)), | |
| path3d(plate(2, hole=true)), | |
| ], 0), | |
| steps=smooth_steps, | |
| transforms=[ | |
| up(0 - previewEpsilon), | |
| up(hole_round_r), | |
| up(thickness - hole_round_r), | |
| up(thickness + previewEpsilon), | |
| ], | |
| transitions=[ | |
| "fastslow", | |
| "linear", | |
| "slowfast", | |
| ], | |
| ), | |
| method="distance", | |
| slices=skin_slices | |
| ); | |
| *skin( | |
| smooth_profiles( | |
| subdivide_and_slice([ | |
| path3d(circle(r=30)), | |
| path3d(circle(r=20)), | |
| path3d(circle(r=20)), | |
| path3d(circle(r=30)), | |
| //path3d(circle(5, $fn=3)), | |
| ], 0), | |
| steps=smooth_steps, | |
| transforms=[ | |
| up(0 - previewEpsilon), | |
| up(5), | |
| up(35), | |
| up(40 + previewEpsilon), | |
| ], | |
| transitions=[ | |
| "fastslow", | |
| "linear", | |
| "slowfast", | |
| "fastfast", | |
| // "slowfast", | |
| ], | |
| ), | |
| method="fast_distance", | |
| slices=skin_slices | |
| ); | |
| } | |
| } | |
| function rot_align(pts) = | |
| let (bary=sum(pts)/len(pts), | |
| raw_vecs=[for (p=pts) p-bary], | |
| norm_vecs=[for (v=raw_vecs) v/norm(v)], | |
| angles=[for (v=norm_vecs) atan2(v.y, v.x)], | |
| i=min_index(angles)) | |
| list_rotate(pts, i); | |
| function prepare_profiles(profiles) = | |
| // subdivide_and_slice(profiles, 0); | |
| subdivide_and_slice([for (p=profiles) rot_align(p)], 0); | |
| module pole(top_shape, hat_growth=1.5) { | |
| //mirror([0, 1, 0]) | |
| zrot(180) { | |
| profiles= | |
| prepare_profiles([ | |
| // subdivide_and_slice([ | |
| apply(zrot(180), path3d(top_shape)), | |
| //path3d(apply(translate([-plate_size/2, -plate_size]), square([plate_size, plate_size]))), | |
| path3d(apply(translate([0, -pole_d/2]), circle(d=pole_d))), | |
| path3d(apply(translate([0, -pole_d/2]), circle(d=pole_d))), | |
| path3d(apply(translate([0, -pole_d*hat_growth/2]), circle(d=hat_growth*pole_d))), | |
| path3d(apply(translate([0, -pole_d]), circle(d=pole_d))), | |
| ]); | |
| transforms=[ | |
| up(0), | |
| xrot(-45), | |
| xrot(-45) * translate([0, 0, pole_len]), | |
| xrot(-45) * translate([0, 0, pole_len + thickness / 2]), | |
| xrot(-45) * translate([0, 0, pole_len + thickness]), | |
| ]; | |
| // for (i=[0, 2]) | |
| // color("red") | |
| // stroke( | |
| // apply(transforms[i], path3d(profiles[i])), | |
| // width=0.5, endcap1="dot", endcap2="arrow", closed=true); | |
| skin( | |
| smooth_profiles( | |
| profiles, | |
| steps=10, | |
| transforms=transforms, | |
| transitions=[ | |
| "fastslow", | |
| "linear", | |
| "slowslow", | |
| "slowfast", | |
| ], | |
| ), | |
| method="fast_distance", | |
| slices=10 | |
| ); | |
| } | |
| } | |
| module gate(is_male, top_shape, h) { | |
| let (s=is_undef(h) ? 1 : h/(2*id)) | |
| scale(s) | |
| if (is_male) | |
| pole(top_shape=top_shape/s); | |
| else | |
| hole(top_shape=top_shape/s); | |
| } | |
| function makePlate(pts, insets=0, round=0) = | |
| round_corners(offset(pts, delta=insets, closed=true), radius=round); | |
| // echo("turtle3d", turtle3d(["move"], transforms=true)); | |
| // gate(); | |
| // function path_2d_to_3d(pts) = [for (pt=pts) pt.xy] | |
| module drawPolygon(raw_pts, scale=3, insets=0.1, round_radius=0.2, plate_thickness=0.3, bottom_chamfer=0.2, h=1) { | |
| let (pts = raw_pts * scale, | |
| edges = deltas(pts, wrap=true), | |
| center = sum(pts) / len(pts), | |
| crossed = cross(edges[0], edges[1]), | |
| normal = crossed / norm(crossed), | |
| inset_pts=offset(path2d(pts), delta=-insets, closed=true)) | |
| { | |
| top_plate=makePlate(path2d(pts), insets=-insets, round=round_radius); | |
| // offset(path2d(pts), delta=-insets, closed=true); | |
| //color("green") stroke([[0, 0, 0], normal], width=0.05, endcap1="dot", endcap2="arrow"); | |
| // echo(inset_pts); | |
| // color("gray") stroke(pts, width=0.05, endcap1="dot", endcap2="arrow", closed=true); | |
| //color("red") stroke(path3d(inset_pts), width=0.05, endcap1="dot", endcap2="arrow", closed=true); | |
| for (i=[0:len(pts)-1]) | |
| let( | |
| // prevPt = pts[(i + len(pts) - 1) % len(pts)], | |
| pt = pts[i], | |
| // nextPt = pts[(i + 1) % len(pts)], | |
| edge=edges[i], | |
| mid=pt + edge / 2, | |
| edge_angle=(atan2(edge.y, edge.x) + 360) % 360, | |
| // towardsPrev = normalize(prevPt - pt), | |
| // towardsNext = normalize(nextPt - pt), | |
| // angle = acos(towardsPrev * towardsNext), | |
| // goal = pt + (towardsPrev + towardsNext) / 2, | |
| is_male=edge_angle < 180, | |
| raw_external = cross(edge, normal), | |
| external=raw_external / norm(raw_external)) | |
| { | |
| // echo("edge_angle", edge_angle, "is_male", is_male, | |
| // "floor(edge_angle / 10) % 10", floor(edge_angle / 10) % 10); | |
| // color("blue") stroke([mid, mid + external], width=0.1, endcap1="dot", endcap2="arrow"); | |
| // echo(["move", mid, "setdir", external]); | |
| // echo(turtle3d(["jump", mid, "setdir", external])); | |
| gate_translation = mid - insets*external; | |
| gate_transform = translate(gate_translation) * frame_map(y=-external, z=-normal); | |
| //inverse_gate_transform = rot_inverse(gate_transform); | |
| inverse_gate_transform = frame_map(y=-external, z=-normal, reverse=true) * translate(-gate_translation);// * mirror([0, -1, 0]); | |
| echo("det4(gate_transform)", det4(gate_transform)); | |
| echo("det4(inverse_gate_transform)", det4(inverse_gate_transform)); | |
| // echo("i", i); | |
| // translate([0, 0, i*1]) | |
| // // multmatrix(inverse_gate_transform) | |
| // color("black") stroke(path3d(list_rotate(inset_pts, i)), width=0.05, closed=true); | |
| //if (i == 2) { | |
| multmatrix(gate_transform) { | |
| // translate([0, 0, i*1]) | |
| // multmatrix(inverse_gate_transform) | |
| // color("black") stroke(path3d(list_rotate(inset_pts, i)), width=0.05, closed=true); | |
| gate(is_male, top_shape=apply(inverse_gate_transform, top_plate), h=h*scale); | |
| } | |
| //} | |
| } | |
| skin( | |
| [ | |
| path3d(top_plate, -plate_thickness), | |
| path3d(top_plate), | |
| ], | |
| method="fast_distance", | |
| slices=slices | |
| ); | |
| *skin( | |
| smooth_profiles( | |
| subdivide_and_slice([ | |
| path3d(makePlate(inset_pts, insets=-bottom_chamfer, round=0)), | |
| path3d(makePlate(inset_pts, insets=-round_radius, round=round_radius)), | |
| path3d(top_plate), | |
| ], 0), | |
| steps=20, | |
| transforms=[ | |
| down(plate_thickness), | |
| down(plate_thickness - bottom_chamfer), | |
| up(0), | |
| ], | |
| transitions=[ | |
| "linear", | |
| "slowfast", | |
| ], | |
| ), | |
| method="fast_distance", | |
| slices=slices | |
| ); | |
| //normalize(goal - pt) / sin(angle / 2) | |
| } | |
| // color("green") stroke(square(4), width=0.2, endcap1="tail", endcap2="arrow"); | |
| } | |
| if (single) { | |
| drawPolygon([ | |
| [1,-1,0], | |
| [-1,-1,0], | |
| [-1,1,0], | |
| [0,2,0], | |
| [1, 1,0], | |
| ]); | |
| // drawPolygon([ | |
| // drawPolygon([for (p=[ | |
| // [1,-1,0], | |
| // [-1,-1,0], | |
| // [-1,1,0], | |
| // [1, 1,0], | |
| // ]) p+[2, 2, 0]]); | |
| } else if (grid) { | |
| for (i=[0:N-1]) translate([i * 2, 0, 0]) | |
| for (j=[0:N-1]) translate([0, j * 2, 0]) | |
| drawPolygon([ | |
| [1,-1,0], | |
| [-1,-1,0], | |
| [-1,1,0], | |
| [1, 1,0], | |
| ]); | |
| } else { | |
| drawPolygon([ | |
| [1,-1,0], | |
| [-1,-1,0], | |
| [-1,1,0], | |
| [0,2,0], | |
| [1, 1,0], | |
| ]); | |
| drawPolygon([ | |
| [3,1,0], | |
| [1,-1,0], | |
| [1, 1,0], | |
| ]); | |
| drawPolygon([ | |
| [3,-1,0], | |
| [1, -1,0], | |
| [3,1,0], | |
| ]); | |
| drawPolygon([ | |
| [3,1,0], | |
| [5,1,0], | |
| [5, -1,0], | |
| [3,-1,0], | |
| ]); | |
| drawPolygon([ | |
| [3,3,0], | |
| [5,3,0], | |
| [5,1,0], | |
| [3,1,0], | |
| ]); | |
| drawPolygon([ | |
| [3,3,0], | |
| [3,1,0], | |
| [1,1,0], | |
| [0,2,0], | |
| [0,3,0], | |
| ]); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment

