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
68
drawing.scad
68
drawing.scad
|
@ -28,6 +28,7 @@
|
|||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
|
@ -115,6 +116,12 @@
|
|||
// 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)]];
|
||||
// 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
|
||||
// path = [for (a=[0:30:360]) [a-180, 60*sin(a)]];
|
||||
// stroke(path, width=8, joints="dot", endcaps="arrow2");
|
||||
|
@ -238,7 +245,8 @@ module stroke(
|
|||
) * linewidth;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -290,14 +298,18 @@ module stroke(
|
|||
// We want to allow "paths" with length 1, so we can't use the normal path/region checks
|
||||
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.");
|
||||
attachable(){
|
||||
for (path = paths) {
|
||||
pathvalid = is_path(path,[2,3]) || same_shape(path,[[0,0]]) || same_shape(path,[[0,0,0]]);
|
||||
assert(pathvalid,"The path argument must be a list of 2D or 3D points, or a region.");
|
||||
path = deduplicate( closed? list_wrap(path) : 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 = 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_shape2 = _shape_path(endcap2, last(width), endcap_width2, endcap_length2, endcap_extent2);
|
||||
|
@ -350,14 +362,20 @@ module stroke(
|
|||
}
|
||||
} 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 = _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]);
|
||||
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) {
|
||||
if (len(path[0]) == 2) { // Two dimensional case
|
||||
// Straight segments
|
||||
setcolor(color) {
|
||||
for (i = idx(path2,e=-2)) {
|
||||
|
@ -376,9 +394,9 @@ module stroke(
|
|||
for (i = [1:1:len(path2)-2]) {
|
||||
$fn = quantup(segs(widths[i]/2),4);
|
||||
translate(path2[i]) {
|
||||
if (joints != undef && joints != "round") {
|
||||
if (joints != undef && joints != "round" && joints != "square") {
|
||||
joint_shape = _shape_path(
|
||||
joints, width[i],
|
||||
joints, widths[i],
|
||||
joint_width,
|
||||
joint_length,
|
||||
joint_extent
|
||||
|
@ -397,15 +415,33 @@ module stroke(
|
|||
// 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);
|
||||
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]) {
|
||||
|
@ -423,7 +459,8 @@ module stroke(
|
|||
multmatrix(mat) polygon(endcap_shape2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
} else { // Three dimensional case
|
||||
rotmats = cumprod([
|
||||
for (i = idx(path2,e=-2)) let(
|
||||
vec1 = i==0? UP : unit(path2[i]-path2[i-1], UP),
|
||||
|
@ -496,7 +533,7 @@ module stroke(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!closed){
|
||||
// Endcap1
|
||||
setcolor(endcap_color1) {
|
||||
translate(path[0]) {
|
||||
|
@ -544,6 +581,9 @@ module stroke(
|
|||
}
|
||||
}
|
||||
}
|
||||
union();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function&Module: dashed_stroke()
|
||||
|
|
|
@ -359,6 +359,8 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
|
|||
pt1 = point3d(pt1);
|
||||
pt2 = point3d(pt2);
|
||||
rtp = xyz_to_spherical(pt2-pt1);
|
||||
attachable()
|
||||
{
|
||||
translate(pt1) {
|
||||
rotate([0, rtp[2], rtp[1]]) {
|
||||
if (rtp[0] > 0) {
|
||||
|
@ -368,6 +370,8 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
|
|||
}
|
||||
}
|
||||
}
|
||||
union();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1892,6 +1892,7 @@ module convex_offset_extrude(
|
|||
delta[i] == 1 ? above :
|
||||
/* delta[i] == -1 ? */ below];
|
||||
dochamfer = offset=="chamfer";
|
||||
attachable(){
|
||||
for(i=[0:len(r)-2])
|
||||
for(j=[0:$children-1])
|
||||
hull(){
|
||||
|
@ -1912,6 +1913,8 @@ module convex_offset_extrude(
|
|||
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
|
||||
// d1 = Bottom 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
|
||||
// taper1 = Length of taper for bottom thread end
|
||||
// taper2 = Length of taper for top thread end
|
||||
// lead_in = Specify linear length of the lead in section of the threading with blunt start threads
|
||||
// lead_in1 = Specify linear length of the lead in section of the threading at the bottom with blunt start threads
|
||||
// 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`
|
||||
// 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`
|
||||
|
@ -2060,7 +2065,11 @@ module _nutshape(nutwidth, h, shape, bevel1, bevel2)
|
|||
function thread_helix(
|
||||
d, pitch, thread_depth, flank_angle, turns,
|
||||
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
|
||||
) = no_function("thread_helix");
|
||||
module thread_helix(
|
||||
|
|
7
vnf.scad
7
vnf.scad
|
@ -999,15 +999,22 @@ module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="orig
|
|||
// vnf_wireframe(octahedron,width=5);
|
||||
module vnf_wireframe(vnf, width=1)
|
||||
{
|
||||
no_children($children);
|
||||
vertex = vnf[0];
|
||||
edges = unique([for (face=vnf[1], i=idx(face))
|
||||
sort([face[i], select(face,i+1)])
|
||||
]);
|
||||
attachable()
|
||||
{
|
||||
union(){
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Section: Operations on VNFs
|
||||
|
|
|
@ -85,12 +85,15 @@ module wire_bundle(path, wires, wirediam=2, rounding=10, wirenum=0, corner_steps
|
|||
sides = max(segs(wirediam/2), 8);
|
||||
offsets = _hex_offsets(wires, wirediam);
|
||||
rounded_path = round_corners(path, radius=rounding, $fn=(corner_steps+1)*4, closed=false);
|
||||
attachable(){
|
||||
for (i = [0:1:wires-1]) {
|
||||
extpath = move(offsets[i], p=circle(d=wirediam, $fn=sides));
|
||||
color(colors[(i+wirenum)%len(colors)]) {
|
||||
path_sweep(extpath, rounded_path);
|
||||
}
|
||||
}
|
||||
union();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue