scale(Scale) {
spath=smooth(Path,Depth);
if (Construction=="poly") {
knot= path_knot(spath,R,Sides,Kscale,Phase,Base,Open_ended=="on");
// echo(knot);
show_solid(knot);
}
else if (Construction=="hull") {
$fn=Sides;
hulled_path(spath,R,Open_ended);
}
else if (Construction=="tile") {
polygon(3d_to_2d(spath));
}
else if (Construction=="2D") {
$fn=Sides;
2D_hulled_path(spath,R,Open_ended);
}
else echo("no Construction");
}
// polyhedron constructor
function poly(name,vertices,faces,debug=[],partial=false) =
[name,vertices,faces,debug,partial];
function p_name(obj) = obj[0];
function p_vertices(obj) = obj[1];
function p_faces(obj) = obj[2];
module show_solid(obj) {
polyhedron(p_vertices(obj),p_faces(obj),convexity=10);
};
// utility functions
function m_translate(v) = [ [1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[v.x, v.y, v.z, 1 ] ];
function m_rotate(v) = [ [1, 0, 0, 0],
[0, cos(v.x), sin(v.x), 0],
[0, -sin(v.x), cos(v.x), 0],
[0, 0, 0, 1] ]
* [ [ cos(v.y), 0, -sin(v.y), 0],
[0, 1, 0, 0],
[ sin(v.y), 0, cos(v.y), 0],
[0, 0, 0, 1] ]
* [ [ cos(v.z), sin(v.z), 0, 0],
[-sin(v.z), cos(v.z), 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0, 1] ];
function vec3(v) = [v.x, v.y, v.z];
function transform(v, m) = vec3([v.x, v.y, v.z, 1] * m);
function orient_to(centre,normal, p) = m_rotate([0, atan2(sqrt(pow(normal.x, 2) + pow(normal.y, 2)), normal.z), 0])
* m_rotate([0, 0, atan2(normal[1], normal[0])])
* m_translate(centre);
function 3d_to_2d(path) = [for (p=path) [p.x,p.y]];
// solid from path
function circle_points(r, sides,phase=45) =
let (delta = 360/sides)
[for (i=[0:sides-1]) [r * sin(i*delta + phase), r * cos(i*delta+phase), 0]];
function loop_points(step,min=0,max=360) =
[for (t=[min:step:max-step]) f(t)];
function transform_points(list, matrix, i = 0) =
[for (p = list)
transform(p, matrix)
];
function tube_pointsx(loop, circle_points, i = 0) =
(i < len(loop) - 1)
? concat(transform_points(circle_points, orient_to(loop[i], loop[i + 1] - loop[i] )),
tube_points(loop, circle_points, i + 1))
: transform_points(circle_points, orient_to(loop[i], loop[0] - loop[i] )) ;
function flatten(l) = [ for (a = l) for (b = a) b ] ;
function tube_points(loop, circle_points) =
flatten([for (i=[0:len(loop)-1])
transform_points(
circle_points,
orient_to(loop[i], loop[(i + 1)%len(loop)] - loop[i] ))
]);
function loop_faces(segs, sides, open=false) =
open
? concat(
[[for (j=[sides - 1:-1:0]) j ]],
[for (i=[0:segs-3])
for (j=[0:sides -1])
[ i * sides + j,
i * sides + (j + 1) % sides,
(i + 1) * sides + (j + 1) % sides,
(i + 1) * sides + j
]
] ,
[[for (j=[0:1:sides - 1]) (segs-2)*sides + j]]
)
: [for (i=[0:segs-1])
for (j=[0:sides -1])
[ i * sides + j,
i * sides + (j + 1) % sides,
((i + 1) % segs) * sides + (j + 1) % sides,
((i + 1) % segs) * sides + j
]
]
;
// path with hulls
module hulled_path(path,r,open) {
last = open=="on" ? len(path) - 2 :len(path) -1;
for (i = [0 : 1 : last ]) {
hull() {
translate(path[i]) sphere(r);
translate(path[(i + 1) % len(path)]) sphere(r);
}
}
};
module 2D_hulled_path(path,r,open) {
last = open=="on" ? len(path) - 2 :len(path) -1;
for (i = [0 : 1 : last ]) {
hull() {
translate(path[i]) circle(r);
translate(path[(i + 1) % len(path)]) circle(r);
}
}
};
// smoothed path by interpolate between points
weight = [-1, 9, 9, -1] / 16;
function interpolate(path,n,i) =
path[(i + n - 1) %n] * weight[0] +
path[i] * weight[1] +
path[(i + 1) %n] * weight[2] +
path[(i + 2) %n] * weight[3] ;
function subdivide(path,i=0) =
i < len(path)
? concat([path[i]],
[interpolate(path,len(path),i)],
subdivide(path, i+1))
: [];
function smooth(path,n) =
n == 0
? path
: smooth(subdivide(path),n-1);
function path_segment(path,start,end) =
let (l = len(path))
let (s = max(floor(start * 360 / l),0),
e = min(ceil(end * 360 / l),l - 1))
[for (i=[s:e]) path[i]];
function scale(path,scale,i=0) =
[for (p =path)
[p[0]*scale[0],p[1]*scale[1],p[2]*scale[2]]
];
function curve_length(step,t=0) =
t < 360
? norm(f(t+step) - f(t)) + curve_length(step,t+step)
: 0;
function map(t, min, max) =
min + t* (max-min)/360;
function limit_point(p,l) = [p.x,p.y,p.z < l ? l : p.z];
function limit_points(points,l) =
[for (p = points) limit_point(p,l)];
// create a knot from a path
function path_knot(path,r,sides,kscale,phase=45,base=-10000,open=false) =
let(loop_points = scale(path,kscale))
let(circle_points = circle_points(r,sides,phase))
let(tube_points = tube_points(loop_points,circle_points))
let(base_tube_points = limit_points(tube_points,base))
let(loop_faces = loop_faces(len(loop_points),sides,open))
poly(name="Knot",
vertices = base_tube_points,
faces = loop_faces);