Skip to content

Instantly share code, notes, and snippets.

@ochafik
Last active May 23, 2023 21:23
Show Gist options
  • Select an option

  • Save ochafik/5b622b1075dfb2fbf537805aa925818b to your computer and use it in GitHub Desktop.

Select an option

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 model is one of a few used to test my identifiers branch which precomputes string hashes & caches builtin function lookups.

  • -Dsingle=true gets ~15% faster evaluation (echo output only)

    image
  • -Dsingle=false gets ~30% faster evaluation (echo output only)

    image
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