mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
fix various stroke() bugs, add attachable() to some modules
This commit is contained in:
parent
bfa5fbc81a
commit
168ae7968e
6 changed files with 333 additions and 267 deletions
492
drawing.scad
492
drawing.scad
|
@ -28,6 +28,7 @@
|
||||||
// various marker shapes, and can be assigned different colors. If passed a region instead of
|
// various marker shapes, and can be assigned different colors. If passed a region instead of
|
||||||
// a path, draws each path in the region as a closed polygon by default. If `closed=false` is
|
// a path, draws each path in the region as a closed polygon by default. If `closed=false` is
|
||||||
// given with a region or list of paths, then each path is drawn without the closing line segment.
|
// given with a region or list of paths, then each path is drawn without the closing line segment.
|
||||||
|
// When drawing a closed path or region, there are no endcaps, so you cannot give the endcap parameters.
|
||||||
// To facilitate debugging, stroke() accepts "paths" that have a single point. These are drawn with
|
// To facilitate debugging, stroke() accepts "paths" that have a single point. These are drawn with
|
||||||
// the style of endcap1, but have their own scale parameter, `singleton_scale`, which defaults to 2
|
// the style of endcap1, but have their own scale parameter, `singleton_scale`, which defaults to 2
|
||||||
// so that singleton dots with endcap "round" are clearly visible.
|
// so that singleton dots with endcap "round" are clearly visible.
|
||||||
|
@ -115,6 +116,12 @@
|
||||||
// Example(2D): Plotting Points. Setting endcap_angle to zero results in the weird arrow orientation.
|
// Example(2D): Plotting Points. Setting endcap_angle to zero results in the weird arrow orientation.
|
||||||
// path = [for (a=[0:30:360]) [a-180, 60*sin(a)]];
|
// path = [for (a=[0:30:360]) [a-180, 60*sin(a)]];
|
||||||
// stroke(path, width=3, joints="diamond", endcaps="arrow2", endcap_angle=0, endcap_width=5, joint_angle=0, joint_width=5);
|
// stroke(path, width=3, joints="diamond", endcaps="arrow2", endcap_angle=0, endcap_width=5, joint_angle=0, joint_width=5);
|
||||||
|
// Example(2D): Default joint gives curves along outside corners of the path:
|
||||||
|
// stroke([square(40)], width=18);
|
||||||
|
// Example(2D): Setting `joints="square"` gives flat outside corners
|
||||||
|
// stroke([square(40)], width=18, joints="square");
|
||||||
|
// Example(2D): Setting `joints="butt"` does not draw any transitions, just rectangular strokes for each segment, meeting at their centers:
|
||||||
|
// stroke([square(40)], width=18, joints="butt");
|
||||||
// Example(2D): Joints and Endcaps
|
// Example(2D): Joints and Endcaps
|
||||||
// path = [for (a=[0:30:360]) [a-180, 60*sin(a)]];
|
// path = [for (a=[0:30:360]) [a-180, 60*sin(a)]];
|
||||||
// stroke(path, width=8, joints="dot", endcaps="arrow2");
|
// stroke(path, width=8, joints="dot", endcaps="arrow2");
|
||||||
|
@ -238,14 +245,15 @@ module stroke(
|
||||||
) * linewidth;
|
) * linewidth;
|
||||||
|
|
||||||
closed = default(closed, is_region(path));
|
closed = default(closed, is_region(path));
|
||||||
check1 = assert(is_bool(closed));
|
check1 = assert(is_bool(closed))
|
||||||
|
assert(!closed || num_defined([endcaps,endcap1,endcap2])==0, "Cannot give endcap parameter(s) with closed path or region");
|
||||||
|
|
||||||
dots = dots==true? "dot" : dots;
|
dots = dots==true? "dot" : dots;
|
||||||
|
|
||||||
endcap1 = first_defined([endcap1, endcaps, dots, "round"]);
|
endcap1 = first_defined([endcap1, endcaps, dots, "round"]);
|
||||||
endcap2 = first_defined([endcap2, endcaps, if (!closed) dots, "round"]);
|
endcap2 = first_defined([endcap2, endcaps, if (!closed) dots, "round"]);
|
||||||
joints = first_defined([joints, dots, "round"]);
|
joints = first_defined([joints, dots, "round"]);
|
||||||
check2 =
|
check2 =
|
||||||
assert(is_bool(endcap1) || is_string(endcap1) || is_path(endcap1))
|
assert(is_bool(endcap1) || is_string(endcap1) || is_path(endcap1))
|
||||||
assert(is_bool(endcap2) || is_string(endcap2) || is_path(endcap2))
|
assert(is_bool(endcap2) || is_string(endcap2) || is_path(endcap2))
|
||||||
assert(is_bool(joints) || is_string(joints) || is_path(joints));
|
assert(is_bool(joints) || is_string(joints) || is_path(joints));
|
||||||
|
@ -290,258 +298,290 @@ module stroke(
|
||||||
// We want to allow "paths" with length 1, so we can't use the normal path/region checks
|
// We want to allow "paths" with length 1, so we can't use the normal path/region checks
|
||||||
paths = is_matrix(path) ? [path] : path;
|
paths = is_matrix(path) ? [path] : path;
|
||||||
assert(is_list(paths),"The path argument must be a list of 2D or 3D points, or a region.");
|
assert(is_list(paths),"The path argument must be a list of 2D or 3D points, or a region.");
|
||||||
for (path = paths) {
|
attachable(){
|
||||||
pathvalid = is_path(path,[2,3]) || same_shape(path,[[0,0]]) || same_shape(path,[[0,0,0]]);
|
for (path = paths) {
|
||||||
assert(pathvalid,"The path argument must be a list of 2D or 3D points, or a region.");
|
pathvalid = is_path(path,[2,3]) || same_shape(path,[[0,0]]) || same_shape(path,[[0,0,0]]);
|
||||||
path = deduplicate( closed? list_wrap(path) : path );
|
assert(pathvalid,"The path argument must be a list of 2D or 3D points, or a region.");
|
||||||
|
|
||||||
check4 = assert(is_num(width) || len(width)==len(path),
|
check4 = assert(is_num(width) || len(width)==len(path),
|
||||||
"width must be a number or a vector the same length as the path (or all components of a region)");
|
"width must be a number or a vector the same length as the path (or all components of a region)");
|
||||||
width = is_num(width)? [for (x=path) width] : width;
|
path = deduplicate( closed? list_wrap(path) : path );
|
||||||
|
width = is_num(width)? [for (x=path) width]
|
||||||
|
: closed? list_wrap(width)
|
||||||
|
: width;
|
||||||
|
check4a=assert(len(width)==len(path), "path had duplicated points and width was given as a list: this is not allowd");
|
||||||
|
|
||||||
endcap_shape1 = _shape_path(endcap1, width[0], endcap_width1, endcap_length1, endcap_extent1);
|
endcap_shape1 = _shape_path(endcap1, width[0], endcap_width1, endcap_length1, endcap_extent1);
|
||||||
endcap_shape2 = _shape_path(endcap2, last(width), endcap_width2, endcap_length2, endcap_extent2);
|
endcap_shape2 = _shape_path(endcap2, last(width), endcap_width2, endcap_length2, endcap_extent2);
|
||||||
|
|
||||||
trim1 = width[0] * first_defined([
|
trim1 = width[0] * first_defined([
|
||||||
trim1, trim,
|
trim1, trim,
|
||||||
(endcap1=="arrow")? endcap_length1-0.01 :
|
(endcap1=="arrow")? endcap_length1-0.01 :
|
||||||
(endcap1=="arrow2")? endcap_length1*3/4 :
|
(endcap1=="arrow2")? endcap_length1*3/4 :
|
||||||
0
|
0
|
||||||
]);
|
]);
|
||||||
|
|
||||||
trim2 = last(width) * first_defined([
|
trim2 = last(width) * first_defined([
|
||||||
trim2, trim,
|
trim2, trim,
|
||||||
(endcap2=="arrow")? endcap_length2-0.01 :
|
(endcap2=="arrow")? endcap_length2-0.01 :
|
||||||
(endcap2=="arrow2")? endcap_length2*3/4 :
|
(endcap2=="arrow2")? endcap_length2*3/4 :
|
||||||
0
|
0
|
||||||
]);
|
]);
|
||||||
check10 = assert(is_finite(trim1))
|
check10 = assert(is_finite(trim1))
|
||||||
assert(is_finite(trim2));
|
assert(is_finite(trim2));
|
||||||
|
|
||||||
if (len(path) == 1) {
|
if (len(path) == 1) {
|
||||||
if (len(path[0]) == 2) {
|
if (len(path[0]) == 2) {
|
||||||
// Endcap1
|
// Endcap1
|
||||||
setcolor(endcap_color1) {
|
setcolor(endcap_color1) {
|
||||||
translate(path[0]) {
|
translate(path[0]) {
|
||||||
mat = is_undef(endcap_angle1)? ident(3) : zrot(endcap_angle1);
|
mat = is_undef(endcap_angle1)? ident(3) : zrot(endcap_angle1);
|
||||||
multmatrix(mat) polygon(scale(singleton_scale,endcap_shape1));
|
multmatrix(mat) polygon(scale(singleton_scale,endcap_shape1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Endcap1
|
// Endcap1
|
||||||
setcolor(endcap_color1) {
|
setcolor(endcap_color1) {
|
||||||
translate(path[0]) {
|
translate(path[0]) {
|
||||||
$fn = segs(width[0]/2);
|
$fn = segs(width[0]/2);
|
||||||
if (is_undef(endcap_angle1)) {
|
if (is_undef(endcap_angle1)) {
|
||||||
rotate_extrude(convexity=convexity) {
|
rotate_extrude(convexity=convexity) {
|
||||||
right_half(planar=true) {
|
right_half(planar=true) {
|
||||||
polygon(endcap_shape1);
|
polygon(endcap_shape1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rotate([90,0,endcap_angle1]) {
|
rotate([90,0,endcap_angle1]) {
|
||||||
linear_extrude(height=max(widths[0],0.001), center=true, convexity=convexity) {
|
linear_extrude(height=max(widths[0],0.001), center=true, convexity=convexity) {
|
||||||
polygon(endcap_shape1);
|
polygon(endcap_shape1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dummy=assert(trim1<path_length(path)-trim2, "Path is too short for endcap(s). Try a smaller width, or set endcap_length to a smaller value.");
|
||||||
|
// This section shortens the path to allow room for the specified endcaps. Note that if
|
||||||
|
// the path is closed, there are not endcaps, so we don't shorten the path, but in that case we
|
||||||
|
// duplicate entry 1 so that the path wraps around a little more and we can correctly create all the joints.
|
||||||
|
// (Why entry 1? Because entry 0 was already duplicated by a list_wrap() call.)
|
||||||
|
pathcut = path_cut_points(path, [trim1, path_length(path)-trim2], closed=false);
|
||||||
|
pathcut_su = _cut_to_seg_u_form(pathcut,path);
|
||||||
|
path2 = closed ? [each path, path[1]]
|
||||||
|
: _path_cut_getpaths(path, pathcut, closed=false)[1];
|
||||||
|
widths = closed ? [each width, width[1]]
|
||||||
|
: _path_select(width, pathcut_su[0][0], pathcut_su[0][1], pathcut_su[1][0], pathcut_su[1][1]);
|
||||||
|
start_vec = path[0] - path[1];
|
||||||
|
end_vec = last(path) - select(path,-2);
|
||||||
|
|
||||||
|
if (len(path[0]) == 2) { // Two dimensional case
|
||||||
|
// Straight segments
|
||||||
|
setcolor(color) {
|
||||||
|
for (i = idx(path2,e=-2)) {
|
||||||
|
seg = select(path2,i,i+1);
|
||||||
|
delt = seg[1] - seg[0];
|
||||||
|
translate(seg[0]) {
|
||||||
|
rot(from=BACK,to=delt) {
|
||||||
|
trapezoid(w1=widths[i], w2=widths[i+1], h=norm(delt), anchor=FRONT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joints
|
||||||
|
setcolor(joint_color) {
|
||||||
|
for (i = [1:1:len(path2)-2]) {
|
||||||
|
$fn = quantup(segs(widths[i]/2),4);
|
||||||
|
translate(path2[i]) {
|
||||||
|
if (joints != undef && joints != "round" && joints != "square") {
|
||||||
|
joint_shape = _shape_path(
|
||||||
|
joints, widths[i],
|
||||||
|
joint_width,
|
||||||
|
joint_length,
|
||||||
|
joint_extent
|
||||||
|
);
|
||||||
|
v1 = unit(path2[i] - path2[i-1]);
|
||||||
|
v2 = unit(path2[i+1] - path2[i]);
|
||||||
|
mat = is_undef(joint_angle)
|
||||||
|
? rot(from=BACK,to=v1)
|
||||||
|
: zrot(joint_angle);
|
||||||
|
multmatrix(mat) polygon(joint_shape);
|
||||||
|
} else {
|
||||||
|
// These are parallel to the path
|
||||||
|
v1 = path2[i] - path2[i-1];
|
||||||
|
v2 = path2[i+1] - path2[i];
|
||||||
|
ang = modang(v_theta(v2) - v_theta(v1));
|
||||||
|
// Need 90 deg offset to make wedge perpendicular to path, and the wedge
|
||||||
|
// position depends on whether we turn left (ang<0) or right (ang>0)
|
||||||
|
theta = v_theta(v1) - sign(ang)*90;
|
||||||
|
|
||||||
|
if (!approx(ang,0)){
|
||||||
|
// This section creates a rounded wedge to fill in gaps. The wedge needs to be oversized for overlap
|
||||||
|
// in all directions, including its apex, but not big enough to create artifacts.
|
||||||
|
// The core of the wedge is the proper arc we need to create. We then add side points based
|
||||||
|
// on firstang and secondang, where we try 1 degree, but if that appears too big we based it
|
||||||
|
// on the segment length. We pick the radius based on the smaller of the width at this point
|
||||||
|
// and the adjacent width, which could be much smaller---meaning that we need a much smaller radius.
|
||||||
|
// The apex offset we pick to be simply based on the width at this point.
|
||||||
|
firstang = sign(ang)*min(1,0.5*norm(v1)/PI/widths[i]*360);
|
||||||
|
secondang = sign(ang)*min(1,0.5*norm(v2)/PI/widths[i]*360);
|
||||||
|
firstR = 0.5*min(widths[i], lerp(widths[i],widths[i-1], abs(firstang)*PI*widths[i]/360/norm(v1)));
|
||||||
|
secondR = 0.5*min(widths[i], lerp(widths[i],widths[i+1], abs(secondang)*PI*widths[i]/360/norm(v2)));
|
||||||
|
apex_offset = widths[i]/10;
|
||||||
|
arcpath = [
|
||||||
|
firstR*[cos(theta-firstang), sin(theta-firstang)],
|
||||||
|
each arc(d=widths[i], angle=[theta, theta+ang],n=joints=="square"?2:undef),
|
||||||
|
secondR*[cos(theta+ang+secondang), sin(theta+ang+secondang)],
|
||||||
|
-apex_offset*[cos(theta+ang/2), sin(theta+ang/2)]
|
||||||
|
];
|
||||||
|
polygon(arcpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!closed){
|
||||||
|
// Endcap1
|
||||||
|
setcolor(endcap_color1) {
|
||||||
|
translate(path[0]) {
|
||||||
|
mat = is_undef(endcap_angle1)? rot(from=BACK,to=start_vec) :
|
||||||
|
zrot(endcap_angle1);
|
||||||
|
multmatrix(mat) polygon(endcap_shape1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dummy=assert(trim1<path_length(path)-trim2, "Path is too short for endcap(s). Try a smaller width, or set endcap_length to a smaller value.");
|
|
||||||
pathcut = path_cut_points(path, [trim1, path_length(path)-trim2], closed=false);
|
|
||||||
pathcut_su = _cut_to_seg_u_form(pathcut,path);
|
|
||||||
path2 = _path_cut_getpaths(path, pathcut, closed=false)[1];
|
|
||||||
widths = _path_select(width, pathcut_su[0][0], pathcut_su[0][1], pathcut_su[1][0], pathcut_su[1][1]);
|
|
||||||
start_vec = path[0] - path[1];
|
|
||||||
end_vec = last(path) - select(path,-2);
|
|
||||||
|
|
||||||
if (len(path[0]) == 2) {
|
// Endcap2
|
||||||
// Straight segments
|
setcolor(endcap_color2) {
|
||||||
setcolor(color) {
|
translate(last(path)) {
|
||||||
for (i = idx(path2,e=-2)) {
|
mat = is_undef(endcap_angle2)? rot(from=BACK,to=end_vec) :
|
||||||
seg = select(path2,i,i+1);
|
zrot(endcap_angle2);
|
||||||
delt = seg[1] - seg[0];
|
multmatrix(mat) polygon(endcap_shape2);
|
||||||
translate(seg[0]) {
|
|
||||||
rot(from=BACK,to=delt) {
|
|
||||||
trapezoid(w1=widths[i], w2=widths[i+1], h=norm(delt), anchor=FRONT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else { // Three dimensional case
|
||||||
|
rotmats = cumprod([
|
||||||
|
for (i = idx(path2,e=-2)) let(
|
||||||
|
vec1 = i==0? UP : unit(path2[i]-path2[i-1], UP),
|
||||||
|
vec2 = unit(path2[i+1]-path2[i], UP)
|
||||||
|
) rot(from=vec1,to=vec2)
|
||||||
|
]);
|
||||||
|
|
||||||
// Joints
|
sides = [
|
||||||
setcolor(joint_color) {
|
for (i = idx(path2,e=-2))
|
||||||
for (i = [1:1:len(path2)-2]) {
|
quantup(segs(max(widths[i],widths[i+1])/2),4)
|
||||||
$fn = quantup(segs(widths[i]/2),4);
|
];
|
||||||
translate(path2[i]) {
|
|
||||||
if (joints != undef && joints != "round") {
|
|
||||||
joint_shape = _shape_path(
|
|
||||||
joints, width[i],
|
|
||||||
joint_width,
|
|
||||||
joint_length,
|
|
||||||
joint_extent
|
|
||||||
);
|
|
||||||
v1 = unit(path2[i] - path2[i-1]);
|
|
||||||
v2 = unit(path2[i+1] - path2[i]);
|
|
||||||
mat = is_undef(joint_angle)
|
|
||||||
? rot(from=BACK,to=v1)
|
|
||||||
: zrot(joint_angle);
|
|
||||||
multmatrix(mat) polygon(joint_shape);
|
|
||||||
} else {
|
|
||||||
// These are parallel to the path
|
|
||||||
v1 = path2[i] - path2[i-1];
|
|
||||||
v2 = path2[i+1] - path2[i];
|
|
||||||
ang = modang(v_theta(v2) - v_theta(v1));
|
|
||||||
// Need 90 deg offset to make wedge perpendicular to path, and the wedge
|
|
||||||
// position depends on whether we turn left (ang<0) or right (ang>0)
|
|
||||||
theta = v_theta(v1) - sign(ang)*90;
|
|
||||||
ang_eps = sign(ang)/10;
|
|
||||||
|
|
||||||
if (!approx(ang,0))
|
|
||||||
arc(d=widths[i], angle=[theta-ang_eps, theta+ang+ang_eps], wedge=true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Endcap1
|
// Straight segments
|
||||||
setcolor(endcap_color1) {
|
setcolor(color) {
|
||||||
translate(path[0]) {
|
for (i = idx(path2,e=-2)) {
|
||||||
mat = is_undef(endcap_angle1)? rot(from=BACK,to=start_vec) :
|
dist = norm(path2[i+1] - path2[i]);
|
||||||
zrot(endcap_angle1);
|
w1 = widths[i]/2;
|
||||||
multmatrix(mat) polygon(endcap_shape1);
|
w2 = widths[i+1]/2;
|
||||||
}
|
$fn = sides[i];
|
||||||
}
|
translate(path2[i]) {
|
||||||
|
multmatrix(rotmats[i]) {
|
||||||
|
cylinder(r1=w1, r2=w2, h=dist, center=false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Endcap2
|
// Joints
|
||||||
setcolor(endcap_color2) {
|
setcolor(joint_color) {
|
||||||
translate(last(path)) {
|
for (i = [1:1:len(path2)-2]) {
|
||||||
mat = is_undef(endcap_angle2)? rot(from=BACK,to=end_vec) :
|
$fn = sides[i];
|
||||||
zrot(endcap_angle2);
|
translate(path2[i]) {
|
||||||
multmatrix(mat) polygon(endcap_shape2);
|
if (joints != undef && joints != "round") {
|
||||||
}
|
joint_shape = _shape_path(
|
||||||
}
|
joints, width[i],
|
||||||
} else {
|
joint_width,
|
||||||
rotmats = cumprod([
|
joint_length,
|
||||||
for (i = idx(path2,e=-2)) let(
|
joint_extent
|
||||||
vec1 = i==0? UP : unit(path2[i]-path2[i-1], UP),
|
);
|
||||||
vec2 = unit(path2[i+1]-path2[i], UP)
|
multmatrix(rotmats[i] * xrot(180)) {
|
||||||
) rot(from=vec1,to=vec2)
|
$fn = sides[i];
|
||||||
]);
|
if (is_undef(joint_angle)) {
|
||||||
|
rotate_extrude(convexity=convexity) {
|
||||||
sides = [
|
right_half(planar=true) {
|
||||||
for (i = idx(path2,e=-2))
|
polygon(joint_shape);
|
||||||
quantup(segs(max(widths[i],widths[i+1])/2),4)
|
}
|
||||||
];
|
}
|
||||||
|
} else {
|
||||||
// Straight segments
|
rotate([90,0,joint_angle]) {
|
||||||
setcolor(color) {
|
linear_extrude(height=max(widths[i],0.001), center=true, convexity=convexity) {
|
||||||
for (i = idx(path2,e=-2)) {
|
polygon(joint_shape);
|
||||||
dist = norm(path2[i+1] - path2[i]);
|
}
|
||||||
w1 = widths[i]/2;
|
}
|
||||||
w2 = widths[i+1]/2;
|
}
|
||||||
$fn = sides[i];
|
}
|
||||||
translate(path2[i]) {
|
} else {
|
||||||
multmatrix(rotmats[i]) {
|
corner = select(path2,i-1,i+1);
|
||||||
cylinder(r1=w1, r2=w2, h=dist, center=false);
|
axis = vector_axis(corner);
|
||||||
}
|
ang = vector_angle(corner);
|
||||||
}
|
if (!approx(ang,0)) {
|
||||||
}
|
frame_map(x=path2[i-1]-path2[i], z=-axis) {
|
||||||
}
|
zrot(90-0.5) {
|
||||||
|
rotate_extrude(angle=180-ang+1) {
|
||||||
// Joints
|
arc(d=widths[i], start=-90, angle=180);
|
||||||
setcolor(joint_color) {
|
}
|
||||||
for (i = [1:1:len(path2)-2]) {
|
}
|
||||||
$fn = sides[i];
|
}
|
||||||
translate(path2[i]) {
|
}
|
||||||
if (joints != undef && joints != "round") {
|
}
|
||||||
joint_shape = _shape_path(
|
}
|
||||||
joints, width[i],
|
}
|
||||||
joint_width,
|
}
|
||||||
joint_length,
|
if (!closed){
|
||||||
joint_extent
|
// Endcap1
|
||||||
);
|
setcolor(endcap_color1) {
|
||||||
multmatrix(rotmats[i] * xrot(180)) {
|
translate(path[0]) {
|
||||||
$fn = sides[i];
|
multmatrix(rotmats[0] * xrot(180)) {
|
||||||
if (is_undef(joint_angle)) {
|
$fn = sides[0];
|
||||||
rotate_extrude(convexity=convexity) {
|
if (is_undef(endcap_angle1)) {
|
||||||
right_half(planar=true) {
|
rotate_extrude(convexity=convexity) {
|
||||||
polygon(joint_shape);
|
right_half(planar=true) {
|
||||||
}
|
polygon(endcap_shape1);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rotate([90,0,joint_angle]) {
|
|
||||||
linear_extrude(height=max(widths[i],0.001), center=true, convexity=convexity) {
|
|
||||||
polygon(joint_shape);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
rotate([90,0,endcap_angle1]) {
|
||||||
corner = select(path2,i-1,i+1);
|
linear_extrude(height=max(widths[0],0.001), center=true, convexity=convexity) {
|
||||||
axis = vector_axis(corner);
|
polygon(endcap_shape1);
|
||||||
ang = vector_angle(corner);
|
|
||||||
if (!approx(ang,0)) {
|
|
||||||
frame_map(x=path2[i-1]-path2[i], z=-axis) {
|
|
||||||
zrot(90-0.5) {
|
|
||||||
rotate_extrude(angle=180-ang+1) {
|
|
||||||
arc(d=widths[i], start=-90, angle=180);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Endcap1
|
// Endcap2
|
||||||
setcolor(endcap_color1) {
|
setcolor(endcap_color2) {
|
||||||
translate(path[0]) {
|
translate(last(path)) {
|
||||||
multmatrix(rotmats[0] * xrot(180)) {
|
multmatrix(last(rotmats)) {
|
||||||
$fn = sides[0];
|
$fn = last(sides);
|
||||||
if (is_undef(endcap_angle1)) {
|
if (is_undef(endcap_angle2)) {
|
||||||
rotate_extrude(convexity=convexity) {
|
rotate_extrude(convexity=convexity) {
|
||||||
right_half(planar=true) {
|
right_half(planar=true) {
|
||||||
polygon(endcap_shape1);
|
polygon(endcap_shape2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
rotate([90,0,endcap_angle2]) {
|
||||||
rotate([90,0,endcap_angle1]) {
|
linear_extrude(height=max(last(widths),0.001), center=true, convexity=convexity) {
|
||||||
linear_extrude(height=max(widths[0],0.001), center=true, convexity=convexity) {
|
polygon(endcap_shape2);
|
||||||
polygon(endcap_shape1);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Endcap2
|
}
|
||||||
setcolor(endcap_color2) {
|
}
|
||||||
translate(last(path)) {
|
union();
|
||||||
multmatrix(last(rotmats)) {
|
|
||||||
$fn = last(sides);
|
|
||||||
if (is_undef(endcap_angle2)) {
|
|
||||||
rotate_extrude(convexity=convexity) {
|
|
||||||
right_half(planar=true) {
|
|
||||||
polygon(endcap_shape2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rotate([90,0,endcap_angle2]) {
|
|
||||||
linear_extrude(height=max(last(widths),0.001), center=true, convexity=convexity) {
|
|
||||||
polygon(endcap_shape2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -359,14 +359,18 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
|
||||||
pt1 = point3d(pt1);
|
pt1 = point3d(pt1);
|
||||||
pt2 = point3d(pt2);
|
pt2 = point3d(pt2);
|
||||||
rtp = xyz_to_spherical(pt2-pt1);
|
rtp = xyz_to_spherical(pt2-pt1);
|
||||||
translate(pt1) {
|
attachable()
|
||||||
rotate([0, rtp[2], rtp[1]]) {
|
{
|
||||||
if (rtp[0] > 0) {
|
translate(pt1) {
|
||||||
linear_extrude(height=rtp[0], convexity=convexity, center=false, slices=slices, twist=twist, scale=scale) {
|
rotate([0, rtp[2], rtp[1]]) {
|
||||||
children();
|
if (rtp[0] > 0) {
|
||||||
}
|
linear_extrude(height=rtp[0], convexity=convexity, center=false, slices=slices, twist=twist, scale=scale) {
|
||||||
}
|
children();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
union();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1892,26 +1892,29 @@ module convex_offset_extrude(
|
||||||
delta[i] == 1 ? above :
|
delta[i] == 1 ? above :
|
||||||
/* delta[i] == -1 ? */ below];
|
/* delta[i] == -1 ? */ below];
|
||||||
dochamfer = offset=="chamfer";
|
dochamfer = offset=="chamfer";
|
||||||
for(i=[0:len(r)-2])
|
attachable(){
|
||||||
for(j=[0:$children-1])
|
for(i=[0:len(r)-2])
|
||||||
hull(){
|
for(j=[0:$children-1])
|
||||||
up(r[i][1]+layers[i][0])
|
hull(){
|
||||||
linear_extrude(convexity=convexity,height=layers[i][1]-layers[i][0])
|
up(r[i][1]+layers[i][0])
|
||||||
if (offset=="round")
|
linear_extrude(convexity=convexity,height=layers[i][1]-layers[i][0])
|
||||||
offset(r=r[i][0])
|
if (offset=="round")
|
||||||
children(j);
|
offset(r=r[i][0])
|
||||||
else
|
children(j);
|
||||||
offset(delta=r[i][0],chamfer = dochamfer)
|
else
|
||||||
children(j);
|
offset(delta=r[i][0],chamfer = dochamfer)
|
||||||
up(r[i+1][1]+layers[i+1][0])
|
children(j);
|
||||||
linear_extrude(convexity=convexity,height=layers[i+1][1]-layers[i+1][0])
|
up(r[i+1][1]+layers[i+1][0])
|
||||||
if (offset=="round")
|
linear_extrude(convexity=convexity,height=layers[i+1][1]-layers[i+1][0])
|
||||||
offset(r=r[i+1][0])
|
if (offset=="round")
|
||||||
children(j);
|
offset(r=r[i+1][0])
|
||||||
else
|
children(j);
|
||||||
offset(delta=r[i+1][0],chamfer=dochamfer)
|
else
|
||||||
children(j);
|
offset(delta=r[i+1][0],chamfer=dochamfer)
|
||||||
}
|
children(j);
|
||||||
|
}
|
||||||
|
union();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2012,9 +2012,14 @@ module _nutshape(nutwidth, h, shape, bevel1, bevel2)
|
||||||
// internal = if true make internal threads. The only effect this has is to change how the threads taper if tapering is selected. When true, threads taper towards the outside; when false, they taper towards the inside. Default: false
|
// internal = if true make internal threads. The only effect this has is to change how the threads taper if tapering is selected. When true, threads taper towards the outside; when false, they taper towards the inside. Default: false
|
||||||
// d1 = Bottom inside base diameter of threads.
|
// d1 = Bottom inside base diameter of threads.
|
||||||
// d2 = Top inside base diameter of threads.
|
// d2 = Top inside base diameter of threads.
|
||||||
// taper = Length of tapers for thread ends. Positive to add taper to threads, negative to taper within specified length. Default: 0
|
// lead_in = Specify linear length of the lead in section of the threading with blunt start threads
|
||||||
// taper1 = Length of taper for bottom thread end
|
// lead_in1 = Specify linear length of the lead in section of the threading at the bottom with blunt start threads
|
||||||
// taper2 = Length of taper for top thread end
|
// lead_in2 = Specify linear length of the lead in section of the threading at the top with blunt start threads
|
||||||
|
// lead_in_ang = Specify angular length in degrees of the lead in section of the threading with blunt start threads
|
||||||
|
// lead_in_ang1 = Specify angular length in degrees of the lead in section of the threading at the bottom with blunt start threads
|
||||||
|
// lead_in_ang2 = Specify angular length in degrees of the lead in section of the threading at the top with blunt start threads
|
||||||
|
// lead_in_shape = Specify the shape of the thread lead in by giving a text string or function. Default: "default"
|
||||||
|
// lead_in_sample = Factor to increase sample rate in the lead-in section. Default: 10
|
||||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
|
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
|
||||||
|
@ -2060,7 +2065,11 @@ module _nutshape(nutwidth, h, shape, bevel1, bevel2)
|
||||||
function thread_helix(
|
function thread_helix(
|
||||||
d, pitch, thread_depth, flank_angle, turns,
|
d, pitch, thread_depth, flank_angle, turns,
|
||||||
profile, starts=1, left_handed=false, internal=false,
|
profile, starts=1, left_handed=false, internal=false,
|
||||||
d1, d2, taper, taper1, taper2,
|
d1, d2,
|
||||||
|
lead_in_shape,
|
||||||
|
lead_in, lead_in1, lead_in2,
|
||||||
|
lead_in_ang, lead_in_ang1, lead_in_ang2,
|
||||||
|
lead_in_sample=10,
|
||||||
anchor, spin, orient
|
anchor, spin, orient
|
||||||
) = no_function("thread_helix");
|
) = no_function("thread_helix");
|
||||||
module thread_helix(
|
module thread_helix(
|
||||||
|
|
15
vnf.scad
15
vnf.scad
|
@ -999,14 +999,21 @@ module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="orig
|
||||||
// vnf_wireframe(octahedron,width=5);
|
// vnf_wireframe(octahedron,width=5);
|
||||||
module vnf_wireframe(vnf, width=1)
|
module vnf_wireframe(vnf, width=1)
|
||||||
{
|
{
|
||||||
|
no_children($children);
|
||||||
vertex = vnf[0];
|
vertex = vnf[0];
|
||||||
edges = unique([for (face=vnf[1], i=idx(face))
|
edges = unique([for (face=vnf[1], i=idx(face))
|
||||||
sort([face[i], select(face,i+1)])
|
sort([face[i], select(face,i+1)])
|
||||||
]);
|
]);
|
||||||
for (e=edges) extrude_from_to(vertex[e[0]],vertex[e[1]]) circle(d=width);
|
attachable()
|
||||||
// Identify vertices actually used and draw them
|
{
|
||||||
vertused = search(count(len(vertex)), flatten(edges), 1);
|
union(){
|
||||||
for(i=idx(vertex)) if(vertused[i]!=[]) move(vertex[i]) sphere(d=width);
|
for (e=edges) extrude_from_to(vertex[e[0]],vertex[e[1]]) circle(d=width);
|
||||||
|
// Identify vertices actually used and draw them
|
||||||
|
vertused = search(count(len(vertex)), flatten(edges), 1);
|
||||||
|
for(i=idx(vertex)) if(vertused[i]!=[]) move(vertex[i]) sphere(d=width);
|
||||||
|
}
|
||||||
|
union();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
13
wiring.scad
13
wiring.scad
|
@ -85,11 +85,14 @@ module wire_bundle(path, wires, wirediam=2, rounding=10, wirenum=0, corner_steps
|
||||||
sides = max(segs(wirediam/2), 8);
|
sides = max(segs(wirediam/2), 8);
|
||||||
offsets = _hex_offsets(wires, wirediam);
|
offsets = _hex_offsets(wires, wirediam);
|
||||||
rounded_path = round_corners(path, radius=rounding, $fn=(corner_steps+1)*4, closed=false);
|
rounded_path = round_corners(path, radius=rounding, $fn=(corner_steps+1)*4, closed=false);
|
||||||
for (i = [0:1:wires-1]) {
|
attachable(){
|
||||||
extpath = move(offsets[i], p=circle(d=wirediam, $fn=sides));
|
for (i = [0:1:wires-1]) {
|
||||||
color(colors[(i+wirenum)%len(colors)]) {
|
extpath = move(offsets[i], p=circle(d=wirediam, $fn=sides));
|
||||||
path_sweep(extpath, rounded_path);
|
color(colors[(i+wirenum)%len(colors)]) {
|
||||||
}
|
path_sweep(extpath, rounded_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
union();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue