mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 00:09:41 +00:00
assorted doc fixes
eliminate trace_path _path_self_intersections fix
This commit is contained in:
parent
1147e181c5
commit
bb77faa0c9
8 changed files with 142 additions and 135 deletions
|
@ -194,16 +194,16 @@ module orient(dir, anchor, spin) {
|
|||
// See Also: attachable(), position(), face_profile(), edge_profile(), corner_profile()
|
||||
// Description:
|
||||
// Attaches children to a parent object at an anchor point and orientation. Attached objects will
|
||||
// be overlapped into the parent object by a little bit, as specified by the default `$overlap`
|
||||
// value (0.01 by default), or by the overriding `overlap=` argument. This is to prevent OpenSCAD
|
||||
// from making non-manifold objects. You can also define `$overlap=` as an argument in a parent
|
||||
// be overlapped into the parent object by a little bit, as specified by the `$overlap`
|
||||
// value (0 by default), or by the overriding `overlap=` argument. This is to prevent OpenSCAD
|
||||
// from making non-manifold objects. You can define `$overlap=` as an argument in a parent
|
||||
// module to set the default for all attachments to it. For a more step-by-step explanation of
|
||||
// attachments, see the [[Attachments Tutorial|Tutorial-Attachments]].
|
||||
// Arguments:
|
||||
// from = The vector, or name of the parent anchor point to attach to.
|
||||
// to = Optional name of the child anchor point. If given, orients the child such that the named anchors align together rotationally.
|
||||
// ---
|
||||
// overlap = Amount to sink child into the parent. Equivalent to `down(X)` after the attach. This defaults to the value in `$overlap`, which is `0.01` by default.
|
||||
// overlap = Amount to sink child into the parent. Equivalent to `down(X)` after the attach. This defaults to the value in `$overlap`, which is `0` by default.
|
||||
// norot = If true, don't rotate children when attaching to the anchor point. Only translate to the anchor point.
|
||||
// Example:
|
||||
// spheroid(d=20) {
|
||||
|
|
49
beziers.scad
49
beziers.scad
|
@ -573,9 +573,9 @@ function bezier_line_intersection(curve, line) =
|
|||
// p0 = [40, 0];
|
||||
// p1 = [0, 0];
|
||||
// p2 = [30, 30];
|
||||
// trace_path([p0,p1,p2], showpts=true, size=0.5, color="green");
|
||||
// stroke([p0,p1,p2], dots=true, color="green", dots_color="blue", width=0.5);
|
||||
// fbez = fillet3pts(p0,p1,p2, 10);
|
||||
// trace_bezier(slice(fbez, 1, -2), size=1);
|
||||
// trace_bezier(slice(fbez, 1, -2));
|
||||
function fillet3pts(p0, p1, p2, r, d, maxerr=0.1, w=0.5, dw=0.25) = let(
|
||||
r = get_radius(r=r,d=d),
|
||||
v0 = unit(p0-p1),
|
||||
|
@ -708,8 +708,7 @@ function bezier_path_length(path, N=3, max_deflect=0.001) =
|
|||
// [60,25], [70,0], [80,-25],
|
||||
// [80,-50], [50,-50]
|
||||
// ];
|
||||
// trace_path(bez, size=1, N=3, showpts=true);
|
||||
// trace_path(bezier_path(bez, N=3), size=3);
|
||||
// trace_bezier(bez, N=3, width=2);
|
||||
function bezier_path(bezier, splinesteps=16, N=3, endpoint=true) =
|
||||
assert(is_path(bezier))
|
||||
assert(is_int(N))
|
||||
|
@ -822,8 +821,8 @@ function path_to_bezier(path, closed=false, tangents, uniform=false, size, relsi
|
|||
// Example(2D):
|
||||
// pline = [[40,0], [0,0], [35,35], [0,70], [-10,60], [-5,55], [0,60]];
|
||||
// bez = fillet_path(pline, 10);
|
||||
// trace_path(pline, showpts=true, size=0.5, color="green");
|
||||
// trace_bezier(bez, size=1);
|
||||
// stroke(pline, dots=true, width=0.5, color="green", dots_color="blue");
|
||||
// trace_bezier(bez);
|
||||
function fillet_path(pts, fillet, maxerr=0.1) = concat(
|
||||
[pts[0], pts[0]],
|
||||
(len(pts) < 3)? [] : [
|
||||
|
@ -851,11 +850,11 @@ function fillet_path(pts, fillet, maxerr=0.1) = concat(
|
|||
// Example(2D):
|
||||
// bez = [[50,30], [40,10], [10,50], [0,30], [-10, 10], [-30,10], [-50,20]];
|
||||
// closed = bezier_close_to_axis(bez);
|
||||
// trace_bezier(closed, size=1);
|
||||
// trace_bezier(closed);
|
||||
// Example(2D):
|
||||
// bez = [[30,50], [10,40], [50,10], [30,0], [10, -10], [10,-30], [20,-50]];
|
||||
// closed = bezier_close_to_axis(bez, axis="Y");
|
||||
// trace_bezier(closed, size=1);
|
||||
// trace_bezier(closed);
|
||||
function bezier_close_to_axis(bezier, axis="X", N=3) =
|
||||
assert(is_path(bezier,2), "bezier_close_to_axis() can only work on 2D bezier paths.")
|
||||
assert(is_int(N))
|
||||
|
@ -892,11 +891,11 @@ function bezier_close_to_axis(bezier, axis="X", N=3) =
|
|||
// Example(2D):
|
||||
// bez = [[50,30], [40,10], [10,50], [0,30], [-10, 10], [-30,10], [-50,20]];
|
||||
// closed = bezier_offset([0,-5], bez);
|
||||
// trace_bezier(closed, size=1);
|
||||
// trace_bezier(closed);
|
||||
// Example(2D):
|
||||
// bez = [[30,50], [10,40], [50,10], [30,0], [10, -10], [10,-30], [20,-50]];
|
||||
// closed = bezier_offset([-5,0], bez);
|
||||
// trace_bezier(closed, size=1);
|
||||
// trace_bezier(closed);
|
||||
function bezier_offset(offset, bezier, N=3) =
|
||||
assert(is_vector(offset,2))
|
||||
assert(is_path(bezier,2), "bezier_offset() can only work on 2D bezier paths.")
|
||||
|
@ -936,7 +935,7 @@ function bezier_offset(offset, bezier, N=3) =
|
|||
// [80,-50], [50,-50], [30,-50],
|
||||
// [5,-30], [0,0]
|
||||
// ];
|
||||
// trace_bezier(bez, N=3, size=3);
|
||||
// trace_bezier(bez, N=3, width=3);
|
||||
// linear_extrude(height=0.1) bezier_polygon(bez, N=3);
|
||||
module bezier_polygon(bezier, splinesteps=16, N=3) {
|
||||
assert(is_path(bezier,2), "bezier_polygon() can only work on 2D bezier paths.");
|
||||
|
@ -969,17 +968,35 @@ module bezier_polygon(bezier, splinesteps=16, N=3) {
|
|||
// [ 14, -5], [ 15, 0], [16, 5],
|
||||
// [ 5, 10], [ 0, 10]
|
||||
// ];
|
||||
// trace_bezier(bez, N=3, size=0.5);
|
||||
module trace_bezier(bez, size=1, N=3) {
|
||||
// trace_bezier(bez, N=3, width=0.5);
|
||||
module trace_bezier(bez, width=1, N=3) {
|
||||
assert(is_path(bez));
|
||||
assert(is_int(N));
|
||||
assert(len(bez)%N == 1, str("A degree ",N," bezier path shound have a multiple of ",N," points in it, plus 1."));
|
||||
trace_path(bez, N=N, showpts=true, size=size, color="green");
|
||||
trace_path(bezier_path(bez, N=N), size=size, color="cyan");
|
||||
$fn=8;
|
||||
stroke(bezier_path(bez, N=N), width=width, color="cyan");
|
||||
color("green")
|
||||
if (N!=3)
|
||||
stroke(path3d(path), width=size);
|
||||
else
|
||||
for(i=[1:3:len(bez)]) stroke(select(bez,max(0,i-2), min(len(bez)-1,i)), width=width);
|
||||
twodim = len(bez[0])==2;
|
||||
move_copies(bez)
|
||||
if ($idx % N ==0)
|
||||
color("blue") if (twodim) circle(d=width*2.5); else sphere(d=width*2.5);
|
||||
else
|
||||
color("red")
|
||||
if (twodim){
|
||||
rect([width/2, width*3],center=true);
|
||||
rect([width*3, width/2],center=true);
|
||||
} else {
|
||||
zcyl(d=width/2, h=width*3);
|
||||
xcyl(d=width/2, h=width*3);
|
||||
ycyl(d=width/2, h=width*3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Section: Patch Functions
|
||||
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ module move_copies(a=[[0,0,0]])
|
|||
// cube(size=[1,3,1],center=true);
|
||||
// cube(size=[3,1,1],center=true);
|
||||
// }
|
||||
// Example(2D):
|
||||
// Example(2D): The functional form of line_of() returns a list of points.
|
||||
// pts = line_of([10,5],n=5);
|
||||
// move_copies(pts) circle(d=2);
|
||||
module line_of(spacing, n, l, p1, p2)
|
||||
|
|
54
drawing.scad
54
drawing.scad
|
@ -561,54 +561,6 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) {
|
|||
|
||||
|
||||
|
||||
// Module: trace_path()
|
||||
// Usage:
|
||||
// trace_path(path, [closed=], [showpts=], [N=], [size=], [color=]);
|
||||
// Description:
|
||||
// Renders lines between each point of a path.
|
||||
// Can also optionally show the individual vertex points.
|
||||
// Arguments:
|
||||
// path = The list of points in the path.
|
||||
// ---
|
||||
// closed = If true, draw the segment from the last vertex to the first. Default: false
|
||||
// showpts = If true, draw vertices and control points.
|
||||
// N = Mark the first and every Nth vertex after in a different color and shape.
|
||||
// size = Diameter of the lines drawn.
|
||||
// color = Color to draw the lines (but not vertices) in.
|
||||
// Example(FlatSpin,VPD=44.4):
|
||||
// path = [for (a=[0:30:210]) 10*[cos(a), sin(a), sin(a)]];
|
||||
// trace_path(path, showpts=true, size=0.5, color="lightgreen");
|
||||
module trace_path(path, closed=false, showpts=false, N=1, size=1, color="yellow") {
|
||||
assert(is_path(path),"Invalid path argument");
|
||||
sides = segs(size/2);
|
||||
path = closed? close_path(path) : path;
|
||||
if (showpts) {
|
||||
for (i = [0:1:len(path)-1]) {
|
||||
translate(path[i]) {
|
||||
if (i % N == 0) {
|
||||
color("blue") sphere(d=size*2.5, $fn=8);
|
||||
} else {
|
||||
color("red") {
|
||||
cylinder(d=size/2, h=size*3, center=true, $fn=8);
|
||||
xrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8);
|
||||
yrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (N!=3) {
|
||||
color(color) stroke(path3d(path), width=size, $fn=8);
|
||||
} else {
|
||||
for (i = [0:1:len(path)-2]) {
|
||||
if (N != 3 || (i % N) != 1) {
|
||||
color(color) extrude_from_to(path[i], path[i+1]) circle(d=size, $fn=sides);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Section: Computing paths
|
||||
|
||||
// Function&Module: arc()
|
||||
|
@ -661,7 +613,7 @@ module trace_path(path, closed=false, showpts=false, N=1, size=1, color="yellow"
|
|||
// stroke(closed=true, path);
|
||||
// Example(FlatSpin,VPD=175):
|
||||
// path = arc(points=[[0,30,0],[0,0,30],[30,0,0]]);
|
||||
// trace_path(path, showpts=true, color="cyan");
|
||||
// stroke(path, dots=true, dots_color="blue");
|
||||
function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, long=false, cw=false, ccw=false, endpoint=true) =
|
||||
assert(is_bool(endpoint))
|
||||
!endpoint ? assert(!wedge, "endpoint cannot be false if wedge is true")
|
||||
|
@ -774,9 +726,9 @@ module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false)
|
|||
// d1 = Diameter of bottom of helix
|
||||
// d2 = Diameter of top of helix
|
||||
// Example(3D):
|
||||
// trace_path(helix(turns=2.5, h=100, r=50), N=1, showpts=true);
|
||||
// stroke(helix(turns=2.5, h=100, r=50), dots=true, dots_color="blue");
|
||||
// Example(3D): Helix that turns the other way
|
||||
// trace_path(helix(turns=-2.5, h=100, r=50), N=1, showpts=true);
|
||||
// stroke(helix(turns=-2.5, h=100, r=50), dots=true, dots_color="blue");
|
||||
// Example(3D): Flat helix (note points are still 3d)
|
||||
// stroke(helix(h=0,r1=50,r2=25,l=0, turns=4));
|
||||
function helix(l,h,turns,angle, r, r1, r2, d, d1, d2)=
|
||||
|
|
63
paths.scad
63
paths.scad
|
@ -168,6 +168,9 @@ function path_segment_lengths(path, closed=false) =
|
|||
// point is zero and the final point is 1. If the path is closed the length of the output
|
||||
// will have one extra point because of the final connecting segment that connects the last
|
||||
// point of the path to the first point.
|
||||
// Arguments:
|
||||
// path = path to operate on
|
||||
// closed = set to true if path is closed. Default: false
|
||||
function path_length_fractions(path, closed=false) =
|
||||
assert(is_path(path))
|
||||
assert(is_bool(closed))
|
||||
|
@ -208,33 +211,39 @@ function path_length_fractions(path, closed=false) =
|
|||
/// // isects == [[[-33.3333, 0], 0, 0.666667, 4, 0.333333], [[33.3333, 0], 1, 0.333333, 3, 0.666667]]
|
||||
/// stroke(path, closed=true, width=1);
|
||||
/// for (isect=isects) translate(isect[0]) color("blue") sphere(d=10);
|
||||
function _path_self_intersections(path, closed=true, eps=EPSILON) =
|
||||
function _path_self_intersections5(path, closed=true, eps=EPSILON) =
|
||||
let(
|
||||
path = closed ? close_path(path,eps=eps) : path,
|
||||
plen = len(path)
|
||||
)
|
||||
[ for (i = [0:1:plen-3]) let(
|
||||
a1 = path[i],
|
||||
a2 = path[i+1],
|
||||
// The sign of signals is positive if the segment is one one side of
|
||||
// the line defined by [a1,a2] and negative on the other side.
|
||||
seg_normal = unit([-(a2-a1).y, (a2-a1).x]),
|
||||
signals = [for(j=[i+2:1:plen-(i==0 && closed? 2: 1)]) path[j]-a1 ]*seg_normal
|
||||
a2 = path[i+1],
|
||||
seg_normal = unit([-(a2-a1).y, (a2-a1).x],[0,0]),
|
||||
vals = path*seg_normal,
|
||||
ref = a1*seg_normal,
|
||||
// The value of vals[j]-ref is positive if vertex j is one one side of the
|
||||
// line [a1,a2] and negative on the other side. Only a segment with opposite
|
||||
// signs at its two vertices can have an intersection with segment
|
||||
// [a1,a2]. The variable signals is zero when abs(vals[j]-ref) is less than
|
||||
// eps and the sign of vals[j]-ref otherwise.
|
||||
signals = [for(j=[i+2:1:plen-(i==0 && closed? 2: 1)]) vals[j]-ref > eps ? 1
|
||||
: vals[j]-ref < -eps ? -1
|
||||
: 0]
|
||||
)
|
||||
if(max(signals)>=0 && min(signals)<=0 ) // some remaining edge intersects line [a1,a2]
|
||||
for(j=[i+2:1:plen-(i==0 && closed? 3: 2)])
|
||||
// The signals test requires the two signals to have different signs,
|
||||
// otherwise b1 and b2 are on the same side of the line defined by [a1,a2]
|
||||
// and hence intersection is impossible
|
||||
if( signals[j-i-2]*signals[j-i-1] <= 0 )
|
||||
let(
|
||||
b1 = path[j],
|
||||
b2 = path[j+1]
|
||||
)
|
||||
// This test checks that a1 and a2 are on opposite sides of the
|
||||
// line defined by [b1,b2].
|
||||
if( cross(b2-b1, a1-b1)*cross(b2-b1, a2-b1) <= 0 )
|
||||
let(isect = _general_line_intersection([a1,a2],[b1,b2],eps=eps))
|
||||
if (isect) [isect[0], i, isect[1], j, isect[2]]
|
||||
if( signals[j-i-2]*signals[j-i-1]<=0 ) let( // segm [b1,b2] intersects line [a1,a2]
|
||||
b1 = path[j],
|
||||
b2 = path[j+1],
|
||||
isect = _general_line_intersection([a1,a2],[b1,b2],eps=eps)
|
||||
)
|
||||
if (isect
|
||||
&& isect[1]> (i==0 && !closed? -eps: 0)
|
||||
&& isect[1]<= 1+eps
|
||||
&& isect[2]> 0
|
||||
&& isect[2]<= 1+eps)
|
||||
[isect[0], i, isect[1], j, isect[2]]
|
||||
];
|
||||
|
||||
|
||||
|
@ -368,7 +377,7 @@ function subdivide_path(path, N, refine, closed=true, exact=true, method="length
|
|||
// maxlen = The maximum allowed path segment length.
|
||||
// ---
|
||||
// closed = If true, treat path like a closed polygon. Default: true
|
||||
// Example:
|
||||
// Example(2D):
|
||||
// path = pentagon(d=100);
|
||||
// spath = subdivide_long_segments(path, 10, closed=true);
|
||||
// stroke(path);
|
||||
|
@ -487,14 +496,14 @@ function path_closest_point(path, pt) =
|
|||
// path = path to find the tagent vectors for
|
||||
// closed = set to true of the path is closed. Default: false
|
||||
// uniform = set to false to correct for non-uniform sampling. Default: true
|
||||
// Example(3D): A shape with non-uniform sampling gives distorted derivatives that may be undesirable
|
||||
// Example(3D): A shape with non-uniform sampling gives distorted derivatives that may be undesirable. Note that derivatives tilt towards the long edges of the rectangle.
|
||||
// rect = square([10,3]);
|
||||
// tangents = path_tangents(rect,closed=true);
|
||||
// stroke(rect,closed=true, width=0.1);
|
||||
// color("purple")
|
||||
// for(i=[0:len(tangents)-1])
|
||||
// stroke([rect[i]-tangents[i], rect[i]+tangents[i]],width=.1, endcap2="arrow2");
|
||||
// Example(3D): A shape with non-uniform sampling gives distorted derivatives that may be undesirable
|
||||
// Example(3D): Setting uniform to false corrects the distorted derivatives for this example:
|
||||
// rect = square([10,3]);
|
||||
// tangents = path_tangents(rect,closed=true,uniform=false);
|
||||
// stroke(rect,closed=true, width=0.1);
|
||||
|
@ -1020,7 +1029,7 @@ function _tag_self_crossing_subpaths(path, nonzero, closed=true, eps=EPSILON) =
|
|||
// left(100)region(outside);
|
||||
// rainbow(outside)
|
||||
// stroke($item,closed=true);
|
||||
// Example:
|
||||
// Example(2D):
|
||||
// N=12;
|
||||
// ang=360/N;
|
||||
// sr=10;
|
||||
|
@ -1033,19 +1042,19 @@ function _tag_self_crossing_subpaths(path, nonzero, closed=true, eps=EPSILON) =
|
|||
// "move", sr]);
|
||||
// stroke(path, width=.3);
|
||||
// right(20)rainbow(polygon_parts(path)) polygon($item);
|
||||
// Example: overlapping path segments disappear
|
||||
// Example(2D): overlapping path segments disappear
|
||||
// path = [[0,0], [10,0], [10,10], [0,10],[0,20], [20,10],[10,10], [0,10],[0,0]];
|
||||
// stroke(path,width=0.3);
|
||||
// right(22)stroke(polygon_parts(path)[0], width=0.3, closed=true);
|
||||
// Example: Path segments disappear outside as well
|
||||
// Example(2D): Path segments disappear outside as well
|
||||
// path = turtle(["repeat", 3, ["move", 17, "left", "move", 10, "left", "move", 7, "left", "move", 10, "left"]]);
|
||||
// back(2)stroke(path,width=.3);
|
||||
// fwd(12)rainbow(polygon_parts(path)) polygon($item);
|
||||
// Example: This shape has six components
|
||||
// Example(2D): This shape has six components
|
||||
// path = turtle(["repeat", 3, ["move", 15, "left", "move", 7, "left", "move", 10, "left", "move", 17, "left"]]);
|
||||
// polygon(path);
|
||||
// right(22)rainbow(polygon_parts(path)) polygon($item);
|
||||
// Example: when the loops of the shape overlap then nonzero gives a different result than the even-odd method.
|
||||
// Example(2D): when the loops of the shape overlap then nonzero gives a different result than the even-odd method.
|
||||
// path = turtle(["repeat", 3, ["move", 15, "left", "move", 7, "left", "move", 10, "left", "move", 10, "left"]]);
|
||||
// polygon(path);
|
||||
// right(27)rainbow(polygon_parts(path)) polygon($item);
|
||||
|
|
64
regions.scad
64
regions.scad
|
@ -113,7 +113,7 @@ module region(r)
|
|||
|
||||
// Function: point_in_region()
|
||||
// Usage:
|
||||
// check = point_in_region(point, region);
|
||||
// check = point_in_region(point, region, [eps]);
|
||||
// Description:
|
||||
// Tests if a point is inside, outside, or on the border of a region.
|
||||
// Returns -1 if the point is outside the region.
|
||||
|
@ -256,14 +256,26 @@ function split_path_at_region_crossings(path, region, closed=true, eps=EPSILON)
|
|||
[for(s=subpaths) if (len(s)>1) s];
|
||||
|
||||
|
||||
// Function: split_nested_region()
|
||||
// Function: region_parts()
|
||||
// Usage:
|
||||
// rgns = split_nested_region(region);
|
||||
// rgns = region_parts(region);
|
||||
// Description:
|
||||
// Separates the distinct (possibly nested) positive subregions of a larger compound region.
|
||||
// Returns a list of regions, such that each returned region has exactly one positive outline
|
||||
// and zero or more void outlines.
|
||||
function split_nested_region(region) =
|
||||
// Divides a region into a list of connected regions. Each connected region has exactly one outside boundary
|
||||
// and zero or more outlines defining internal holes. Note that behavior is undefined on invalid regions whose
|
||||
// components intersect each other.
|
||||
// Example(2D,NoAxes):
|
||||
// R = [for(i=[1:7]) square(i,center=true)];
|
||||
// region_list = split_nested_region(R);
|
||||
// rainbow(region_list) region($item);
|
||||
// Example(2D,NoAxes):
|
||||
// R = [back(7,square(3,center=true)),
|
||||
// square([20,10],center=true),
|
||||
// left(5,square(8,center=true)),
|
||||
// for(i=[4:2:8])
|
||||
// right(5,square(i,center=true))];
|
||||
// region_list = split_nested_region(R);
|
||||
// rainbow(region_list) region($item);
|
||||
function region_parts(region) =
|
||||
let(
|
||||
paths = sort(idx=0, [
|
||||
for(i = idx(region)) let(
|
||||
|
@ -360,7 +372,7 @@ function _cleave_connected_region(region) =
|
|||
// vnf = If given, the faces are added to this VNF. Default: `EMPTY_VNF`
|
||||
function region_faces(region, transform, reverse=false, vnf=EMPTY_VNF) =
|
||||
let (
|
||||
regions = split_nested_region(region),
|
||||
regions = region_parts(region),
|
||||
vnfs = [
|
||||
if (vnf != EMPTY_VNF) vnf,
|
||||
for (rgn = regions) let(
|
||||
|
@ -434,12 +446,13 @@ module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg,
|
|||
}
|
||||
|
||||
|
||||
function linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", anchor_isect=false, anchor, spin=0, orient=UP) =
|
||||
function linear_sweep(region, height=1, center, twist=0, scale=1, slices,
|
||||
maxseg, style="default", anchor_isect=false, anchor, spin=0, orient=UP) =
|
||||
let(
|
||||
anchor = get_anchor(anchor,center,BOT,BOT),
|
||||
region = is_path(region)? [region] : region,
|
||||
cp = mean(pointlist_bounds(flatten(region))),
|
||||
regions = split_nested_region(region),
|
||||
regions = region_parts(region),
|
||||
slices = default(slices, floor(twist/5+1)),
|
||||
step = twist/slices,
|
||||
hstep = height/slices,
|
||||
|
@ -939,7 +952,8 @@ function union(regions=[],b=undef,c=undef,eps=EPSILON) =
|
|||
// color("green") region(difference(shape1,shape2));
|
||||
function difference(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||
b!=undef? difference(concat([regions],[b],c==undef?[]:[c]), eps=eps) :
|
||||
len(regions)<=1? regions[0] :
|
||||
len(regions)==0? [] :
|
||||
len(regions)==1? regions[0] :
|
||||
difference(
|
||||
let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)])
|
||||
concat(
|
||||
|
@ -968,16 +982,17 @@ function difference(regions=[],b=undef,c=undef,eps=EPSILON) =
|
|||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true);
|
||||
// color("green") region(intersection(shape1,shape2));
|
||||
function intersection(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||
b!=undef? intersection(concat([regions],[b],c==undef?[]:[c]),eps=eps) :
|
||||
len(regions)<=1? regions[0] :
|
||||
intersection(
|
||||
let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)])
|
||||
concat(
|
||||
[_tagged_region(regions[0],regions[1],["I","S"],["I"],eps=eps)],
|
||||
[for (i=[2:1:len(regions)-1]) regions[i]]
|
||||
),
|
||||
eps=eps
|
||||
);
|
||||
b!=undef? intersection(concat([regions],[b],c==undef?[]:[c]),eps=eps)
|
||||
: len(regions)==0 ? []
|
||||
: len(regions)==1? regions[0]
|
||||
: let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)])
|
||||
intersection([
|
||||
_tagged_region(regions[0],regions[1],["I","S"],["I"],eps=eps),
|
||||
for (i=[2:1:len(regions)-1]) regions[i]
|
||||
],
|
||||
eps=eps
|
||||
);
|
||||
|
||||
|
||||
|
||||
// Function&Module: exclusive_or()
|
||||
|
@ -989,16 +1004,17 @@ function intersection(regions=[],b=undef,c=undef,eps=EPSILON) =
|
|||
// Description:
|
||||
// When called as a function and given a list of regions, where each region is a list of closed
|
||||
// 2D paths, returns the boolean exclusive_or of all given regions. Result is a single region.
|
||||
// When called as a module, performs a boolean exclusive-or of up to 10 children.
|
||||
// When called as a module, performs a boolean exclusive-or of up to 10 children. Note that the
|
||||
// xor operator tends to produce shapes that meet at corners, which do not render in CGAL.
|
||||
// Arguments:
|
||||
// regions = List of regions to exclusive_or. Each region is a list of closed paths.
|
||||
// Example(2D): As Function
|
||||
// Example(2D): As Function. A linear_sweep of this shape fails to render in CGAL.
|
||||
// shape1 = move([-8,-8,0], p=circle(d=50));
|
||||
// shape2 = move([ 8, 8,0], p=circle(d=50));
|
||||
// for (shape = [shape1,shape2])
|
||||
// color("red") stroke(shape, width=0.5, closed=true);
|
||||
// color("green") region(exclusive_or(shape1,shape2));
|
||||
// Example(2D): As Module
|
||||
// Example(2D): As Module. A linear_extrude() of the resulting geometry fails to render in CGAL.
|
||||
// exclusive_or() {
|
||||
// square(40,center=false);
|
||||
// circle(d=40);
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
// of the shortcuts can return a matrix representing the operation
|
||||
// the shortcut performs. The rotation and scaling shortcuts accept
|
||||
// an optional centerpoint for the rotation or scaling operation.
|
||||
// .
|
||||
// Almost all of the transformation functions take a point, a point
|
||||
// list, bezier patch, or VNF as a second positional argument to
|
||||
// operate on. The exceptions are rot(), frame_map() and skew().
|
||||
// Includes:
|
||||
// include <BOSL2/std.scad>
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -378,6 +382,7 @@ function up(z=0, p) = move([0,0,z],p=p);
|
|||
// * Called as a function with a [VNF structure](vnf.scad) in the `p` argument, returns the rotated VNF.
|
||||
// * Called as a function without a `p` argument, and `planar` is true, returns the affine2d rotational matrix. The angle `a` must be a scalar.
|
||||
// * Called as a function without a `p` argument, and `planar` is false, returns the affine3d rotational matrix.
|
||||
// Note that unlike almost all the other transformations, the `p` argument must be given as a named argument.
|
||||
//
|
||||
// Arguments:
|
||||
// a = Scalar angle or vector of XYZ rotation angles to rotate by, in degrees. If `planar` is true or if `p` holds 2d data, or if you use the `from` and `to` arguments then `a` must be a scalar. Default: `0`
|
||||
|
@ -824,8 +829,8 @@ function yscale(y=1, p, cp=0, planar=false) =
|
|||
//
|
||||
// Example: Scaling Points
|
||||
// path = xrot(90,p=path3d(circle(d=50,$fn=12)));
|
||||
// #trace_path(path);
|
||||
// trace_path(zscale(2,p=path));
|
||||
// #stroke(path,closed=true);
|
||||
// stroke(zscale(2,path),closed=true);
|
||||
module zscale(z=1, p, cp=0) {
|
||||
assert(is_undef(p), "Module form `zscale()` does not accept p= argument.");
|
||||
cp = is_num(cp)? [0,0,cp] : cp;
|
||||
|
@ -1119,15 +1124,23 @@ function zflip(p, z=0) =
|
|||
// x = Destination 3D vector for x axis.
|
||||
// y = Destination 3D vector for y axis.
|
||||
// z = Destination 3D vector for z axis.
|
||||
// p = If given, the point, path, patch, or VNF to operate on. Function use only.
|
||||
// reverse = reverse direction of the map for orthogonal inputs. Default: false
|
||||
// Example: Remap axes after linear extrusion
|
||||
// frame_map(x=[0,1,0], y=[0,0,1]) linear_extrude(height=10) square(3);
|
||||
// Example: This map is just a rotation around the z axis
|
||||
// mat = frame_map(x=[1,1,0], y=[-1,1,0]);
|
||||
// mat = frame_map(x=[1,1,0], y=[-1,1,0]);
|
||||
// multmatrix(mat) frame_ref();
|
||||
// Example: This map is not a rotation because x and y aren't orthogonal
|
||||
// mat = frame_map(x=[1,0,0], y=[1,1,0]);
|
||||
// Example: This sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1]
|
||||
// frame_map(x=[1,0,0], y=[1,1,0]) cube(10);
|
||||
// Example: This sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1]. (Original directions shown in light shade, final directions shown dark.)
|
||||
// mat = frame_map(x=[0,1,1], y=[0,-1,1]) * frame_map(x=[1,1,0], y=[-1,1,0],reverse=true);
|
||||
// color("purple",alpha=.2) stroke([[0,0,0],10*[1,1,0]]);
|
||||
// color("green",alpha=.2) stroke([[0,0,0],10*[-1,1,0]]);
|
||||
// multmatrix(mat) {
|
||||
// color("purple") stroke([[0,0,0],10*[1,1,0]]);
|
||||
// color("green") stroke([[0,0,0],10*[-1,1,0]]);
|
||||
// }
|
||||
function frame_map(x,y,z, p, reverse=false) =
|
||||
is_def(p)
|
||||
? apply(frame_map(x,y,z,reverse=reverse), p)
|
||||
|
@ -1223,7 +1236,7 @@ module frame_map(x,y,z,p,reverse=false)
|
|||
// color("blue") move_copies(pts) circle(d=3, $fn=8);
|
||||
// Example(FlatSpin,VPD=175): Calling as a 3D Function
|
||||
// pts = skew(p=path3d(square(40,center=true)), szx=0.5, szy=0.3);
|
||||
// trace_path(close_path(pts), showpts=true);
|
||||
// stroke(pts,closed=true,dots=true,dots_color="blue");
|
||||
module skew(p, sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0)
|
||||
{
|
||||
assert(is_undef(p), "Module form `skew()` does not accept p= argument.")
|
||||
|
|
12
vnf.scad
12
vnf.scad
|
@ -202,16 +202,17 @@ function vnf_vertex_array(
|
|||
// Description:
|
||||
// Produces a vnf from an array of points where each row length can differ from the adjacent rows by up to 2 in length. This enables
|
||||
// the construction of triangular VNF patches. The resulting VNF can be wrapped along the rows by setting `row_wrap` to true.
|
||||
// You cannot wrap columns: if you need to do that you'll need to combine two VNF arrays that share edges.
|
||||
// Arguments:
|
||||
// points = List of point lists for each row
|
||||
// row_wrap = If true then add faces connecting the first row and last row. These rows must differ by at most 2 in length.
|
||||
// reverse = Set this to reverse the direction of the faces
|
||||
// Example(3D): Each row has one more point than the preceeding one.
|
||||
// Example(3D,NoAxes): Each row has one more point than the preceeding one.
|
||||
// pts = [for(y=[1:1:10]) [for(x=[0:y-1]) [x,y,y]]];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
// vnf_wireframe(vnf,width=0.1);
|
||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||
// Example(3D): Each row has one more point than the preceeding one.
|
||||
// Example(3D,NoAxes): Each row has two more points than the preceeding one.
|
||||
// pts = [for(y=[0:2:10]) [for(x=[-y/2:y/2]) [x,y,y]]];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
// vnf_wireframe(vnf,width=0.1);
|
||||
|
@ -230,7 +231,7 @@ function vnf_vertex_array(
|
|||
// vnf=vnf_tri_array(pts2));
|
||||
// color("green")vnf_wireframe(vnf,width=0.1);
|
||||
// vnf_polyhedron(vnf);
|
||||
// Example(3D): Point count can change irregularly
|
||||
// Example(3D,NoAxes): Point count can change irregularly
|
||||
// lens = [10,9,7,5,6,8,8,10];
|
||||
// pts = [for(y=idx(lens)) lerpn([-lens[y],y,y],[lens[y],y,y],lens[y])];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
|
@ -283,9 +284,8 @@ function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) =
|
|||
// Description:
|
||||
// Given a list of VNF structures, merges them all into a single VNF structure.
|
||||
// When cleanup=true, it consolidates all duplicate vertices with a tolerance `eps`,
|
||||
// drops unreferenced vertices and any final face with less than 3 vertices.
|
||||
// Unreferenced vertices of the input VNFs that doesn't duplicate any other vertex
|
||||
// are not dropped.
|
||||
// and eliminates any faces with fewer than 3 vertices.
|
||||
// (Unreferenced vertices of the input VNFs are not dropped.)
|
||||
// Arguments:
|
||||
// vnfs - a list of the VNFs to merge in one VNF.
|
||||
// cleanup - when true, consolidates the duplicate vertices of the merge. Default: false
|
||||
|
|
Loading…
Reference in a new issue