From 14ae1795bbd73e6264cb63971ce634d007db79bb Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Thu, 9 Sep 2021 18:32:58 -0400 Subject: [PATCH] fix permutation docs consolidate "line/segment/ray" functions to just "line" with bounded option add RAY, LINE and SEGMENT constants --- arrays.scad | 15 +- attachments.scad | 4 +- bottlecaps.scad | 2 +- constants.scad | 33 ++++ geometry.scad | 423 ++++++++++++++--------------------------------- paths.scad | 2 +- rounding.scad | 10 +- vnf.scad | 2 +- 8 files changed, 177 insertions(+), 314 deletions(-) diff --git a/arrays.scad b/arrays.scad index c5ccc96b..c09fd951 100644 --- a/arrays.scad +++ b/arrays.scad @@ -1369,11 +1369,10 @@ function triplet(list, wrap=false) = // Function: combinations() // Usage: // list = combinations(l, [n]); -// for (p = combinations(l, [n])) ... // Topics: List Handling, Iteration // See Also: idx(), enumerate(), pair(), triplet(), permutations() // Description: -// Returns an ordered list of every unique permutation of `n` items out of the given list `l`. +// Returns a list of all of the (unordered) combinations of `n` items out of the given list `l`. // For the list `[1,2,3,4]`, with `n=2`, this will return `[[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]]`. // For the list `[1,2,3,4]`, with `n=3`, this will return `[[1,2,3], [1,2,4], [1,3,4], [2,3,4]]`. // Arguments: @@ -1395,21 +1394,17 @@ function combinations(l,n=2,_s=0) = // Function: permutations() // Usage: // list = permutations(l, [n]); -// for (p = permutations(l, [n])) ... // Topics: List Handling, Iteration // See Also: idx(), enumerate(), pair(), triplet(), combinations() // Description: -// Returns an ordered list of every unique permutation of `n` items out of the given list `l`. -// For the list `[1,2,3,4]`, with `n=2`, this will return `[[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]]`. -// For the list `[1,2,3,4]`, with `n=3`, this will return `[[1,2,3], [1,2,4], [1,3,4], [2,3,4]]`. +// Returns a list of all of the (ordered) permutation `n` items out of the given list `l`. +// For the list `[1,2,3]`, with `n=2`, this will return `[[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]` +// For the list `[1,2,3]`, with `n=3`, this will return `[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]` // Arguments: // l = The list to provide permutations for. // n = The number of items in each permutation. Default: 2 // Example: -// pairs = permutations([3,4,5,6]); // Returns: [[3,4],[3,5],[3,6],[4,5],[4,6],[5,6]] -// triplets = permutations([3,4,5,6],n=3); // Returns: [[3,4,5],[3,4,6],[3,5,6],[4,5,6]] -// Example(2D): -// for (p=permutations(regular_ngon(n=7,d=100))) stroke(p); +// pairs = permutations([3,4,5,6]); // // Returns: [[3,4],[3,5],[3,6],[4,3],[4,5],[4,6],[5,3],[5,4],[5,6],[6,3],[6,4],[6,5]] function permutations(l,n=2) = assert(is_list(l), "Invalid list." ) assert( is_finite(n) && n>=1 && n<=len(l), "Invalid number `n`." ) diff --git a/attachments.scad b/attachments.scad index 1300fbbd..0b3c9101 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1673,7 +1673,7 @@ function _find_anchor(anchor, geom) = for (t=triplet(path,true)) let( seg1 = [t[0],t[1]], seg2 = [t[1],t[2]], - isect = ray_segment_intersection([[0,0],anchor], seg1), + isect = line_intersection([[0,0],anchor], seg1,RAY,SEGMENT), n = is_undef(isect)? [0,1] : !approx(isect, t[1])? line_normal(seg1) : unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]), @@ -1708,7 +1708,7 @@ function _find_anchor(anchor, geom) = for (t=triplet(path,true)) let( seg1 = [t[0],t[1]], seg2 = [t[1],t[2]], - isect = ray_segment_intersection([[0,0],xyanch], seg1), + isect = line_intersection([[0,0],xyanch], seg1, RAY, SEGMENT), n = is_undef(isect)? [0,1] : !approx(isect, t[1])? line_normal(seg1) : unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]), diff --git a/bottlecaps.scad b/bottlecaps.scad index 6a79ca2d..35aa2421 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -1119,7 +1119,7 @@ module sp_neck(diam,type,wall,id,style="L",bead=false, anchor, spin, orient) [0,W/2] ]; - isect400 = [for(seg=pair(beadpts)) let(segisect = line_segment_intersection([[T/2,0],[T/2,1]] , seg)) if (is_def(segisect)) segisect.y]; + isect400 = [for(seg=pair(beadpts)) let(segisect = line_intersection([[T/2,0],[T/2,1]] , seg, LINE, SEGMENT)) if (is_def(segisect)) segisect.y]; extra_bot = type==400 && bead ? -min(subindex(beadpts,1))+max(isect400) : 0; bead_shift = type==400 ? H+max(isect400) : entry[5]+W/2; // entry[5] is L diff --git a/constants.scad b/constants.scad index 8815aa96..0813d604 100644 --- a/constants.scad +++ b/constants.scad @@ -190,4 +190,37 @@ CTR = CENTER; +// Constant: SEGMENT +// Topics: Constants, Lines +// See Also: RAY, LINE +// Description: Treat a line as a segment. [true, true] +// Example: Usage with line_intersection: +// line1 = 10*[[9, 4], [5, 7]]; +// line2 = 10*[[2, 3], [6, 5]]; +// isect = line_intersection(line1, line2, SEGMENT, SEGMENT); +SEGMENT = [true,true]; + + +// Constant: RAY +// Topics: Constants, Lines +// See Also: SEGMENT, LINE +// Description: Treat a line as a ray, based at the first point. [true, false] +// Example: Usage with line_intersection: +// line = [[-30,0],[30,30]]; +// pt = [40,25]; +// closest = line_closest_point(line,pt,RAY); +RAY = [true, false]; + + +// Constant: LINE +// Topics: Constants, Lines +// See Also: RAY, SEGMENT +// Description: Treat a line as an unbounded line. [false, false] +// Example: Usage with line_intersection: +// line1 = 10*[[9, 4], [5, 7]]; +// line2 = 10*[[2, 3], [6, 5]]; +// isect = line_intersection(line1, line2, LINE, SEGMENT); +LINE = [false, false]; + + // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/geometry.scad b/geometry.scad index 055d7966..30e1d9da 100644 --- a/geometry.scad +++ b/geometry.scad @@ -8,9 +8,9 @@ // Section: Lines, Rays, and Segments -// Function: point_on_segment2d() +// Function: point_on_segment() // Usage: -// pt = point_on_segment2d(point, edge); +// pt = point_on_segment(point, edge); // Topics: Geometry, Points, Segments // Description: // Determine if the point is on the line segment between two points. @@ -19,9 +19,9 @@ // point = The point to test. // edge = Array of two points forming the line segment to test against. // eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9) -function point_on_segment2d(point, edge, eps=EPSILON) = +function point_on_segment(point, edge, eps=EPSILON) = assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." ) - point_segment_distance(point, edge)=0, "The tolerance should be a positive number." ) - assert( _valid_line(l1,dim=2,eps=eps) &&_valid_line(l2,dim=2,eps=eps), "Invalid line(s)." ) +// line1 = List of two points in 2D defining the first line, segment or ray +// line2 = List of two points in 2D defining the second line, segment or ray +// bounded1 = boolean or list of two booleans defining which ends are bounded for line1. Default: [false,false] +// bounded2 = boolean or list of two booleans defining which ends are bounded for line2. Default: [false,false] +// --- +// bounded = boolean or list of two booleans defining which ends are bounded for both lines. The bounded1 and bounded2 parameters override this if both are given. +// eps = tolerance for geometric comparisons. Default: `EPSILON` (1e-9) +// Example(2D): The segments do not intersect but the lines do in this example. +// line1 = 10*[[9, 4], [5, 7]]; +// line2 = 10*[[2, 3], [6, 5]]; +// stroke(line1, endcaps="arrow2"); +// stroke(line2, endcaps="arrow2"); +// isect = line_intersection(line1, line2); +// color("red") translate(isect) circle(r=1,$fn=12); +// Example(2D): Specifying a ray and segment using the shorthand variables. +// line1 = 10*[[0, 2], [4, 7]]; +// line2 = 10*[[10, 4], [3, 4]]; +// stroke(line1); +// stroke(line2, endcap2="arrow2"); +// isect = line_intersection(line1, line2, SEGMENT, RAY); +// color("red") translate(isect) circle(r=1,$fn=12); +// Example(2D): Here we use the same example as above, but specify two segments using the bounded argument. +// line1 = 10*[[0, 2], [4, 7]]; +// line2 = 10*[[10, 4], [3, 4]]; +// stroke(line1); +// stroke(line2); +// isect = line_intersection(line1, line2, bounded=true); // Returns undef +function line_intersection(line1, line2, bounded1, bounded2, bounded, eps=EPSILON) = assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." ) - let(isect = _general_line_intersection(l1,l2,eps=eps)) - isect[0]; - - -// Function: line_ray_intersection() -// Usage: -// pt = line_ray_intersection(line, ray); -// Topics: Geometry, Lines, Rays, Intersections -// Description: -// Returns the 2D intersection point of an unbounded 2D line, and a half-bounded 2D ray. -// Returns `undef` if they do not intersect. -// Arguments: -// line = The unbounded 2D line, defined by two 2D points on the line. -// ray = The 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray. -// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9) -function line_ray_intersection(line,ray,eps=EPSILON) = - assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." ) - assert( _valid_line(line,dim=2,eps=eps) && _valid_line(ray,dim=2,eps=eps), "Invalid line or ray." ) - let( isect = _general_line_intersection(line,ray,eps=eps) ) + assert( _valid_line(line1,dim=2,eps=eps), "First line invalid") + assert( _valid_line(line2,dim=2,eps=eps), "Second line invalid") + assert( is_undef(bounded) || is_bool(bounded) || is_bool_list(bounded,2), "Invalid value for \"bounded\"") + assert( is_undef(bounded1) || is_bool(bounded1) || is_bool_list(bounded1,2), "Invalid value for \"bounded1\"") + assert( is_undef(bounded2) || is_bool(bounded2) || is_bool_list(bounded2,2), "Invalid value for \"bounded2\"") + let(isect = _general_line_intersection(line1,line2,eps=eps)) is_undef(isect[0]) ? undef : - (isect[2]<0-eps) ? undef : - isect[0]; - - -// Function: line_segment_intersection() -// Usage: -// pt = line_segment_intersection(line, segment); -// Topics: Geometry, Lines, Segments, Intersections -// Description: -// Returns the 2D intersection point of an unbounded 2D line, and a bounded 2D line segment. -// Returns `undef` if they do not intersect. -// Arguments: -// line = The unbounded 2D line, defined by two 2D points on the line. -// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment. -// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9) -function line_segment_intersection(line,segment,eps=EPSILON) = - assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." ) - assert( _valid_line(line, dim=2,eps=eps) &&_valid_line(segment,dim=2,eps=eps), "Invalid line or segment." ) - let( isect = _general_line_intersection(line,segment,eps=eps) ) - is_undef(isect[0]) ? undef : - isect[2]<0-eps || isect[2]>1+eps ? undef : - isect[0]; - - -// Function: ray_intersection() -// Usage: -// pt = ray_intersection(s1, s2); -// Topics: Geometry, Lines, Rays, Intersections -// Description: -// Returns the 2D intersection point of two 2D line rays. -// Returns `undef` if they do not intersect. -// Arguments: -// r1 = First 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray. -// r2 = Second 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray. -// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9) -function ray_intersection(r1,r2,eps=EPSILON) = - assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." ) - assert( _valid_line(r1,dim=2,eps=eps) && _valid_line(r2,dim=2,eps=eps), "Invalid ray(s)." ) - let( isect = _general_line_intersection(r1,r2,eps=eps) ) - is_undef(isect[0]) ? undef : - isect[1]<0-eps || isect[2]<0-eps ? undef : - isect[0]; - - -// Function: ray_segment_intersection() -// Usage: -// pt = ray_segment_intersection(ray, segment); -// Topics: Geometry, Rays, Segments, Intersections -// Description: -// Returns the 2D intersection point of a half-bounded 2D ray, and a bounded 2D line segment. -// Returns `undef` if they do not intersect. -// Arguments: -// ray = The 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray. -// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment. -// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9) -function ray_segment_intersection(ray,segment,eps=EPSILON) = - assert( _valid_line(ray,dim=2,eps=eps) && _valid_line(segment,dim=2,eps=eps), "Invalid ray or segment." ) - assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." ) - let( isect = _general_line_intersection(ray,segment,eps=eps) ) - is_undef(isect[0]) ? undef : - isect[1]<0-eps || isect[2]<0-eps || isect[2]>1+eps ? undef : - isect[0]; - - -// Function: segment_intersection() -// Usage: -// pt = segment_intersection(s1, s2); -// Topics: Geometry, Segments, Intersections -// Description: -// Returns the 2D intersection point of two 2D line segments. -// Returns `undef` if they do not intersect. -// Arguments: -// s1 = First 2D segment, given as a list of the two 2D endpoints of the line segment. -// s2 = Second 2D segment, given as a list of the two 2D endpoints of the line segment. -// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9) -function segment_intersection(s1,s2,eps=EPSILON) = - assert( _valid_line(s1,dim=2,eps=eps) && _valid_line(s2,dim=2,eps=eps), "Invalid segment(s)." ) - assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." ) - let( isect = _general_line_intersection(s1,s2,eps=eps) ) - is_undef(isect[0]) ? undef : - isect[1]<0-eps || isect[1]>1+eps || isect[2]<0-eps || isect[2]>1+eps ? undef : - isect[0]; - + let( + bounded1 = force_list(first_defined([bounded1,bounded,false]),2), + bounded2 = force_list(first_defined([bounded2,bounded,false]),2), + good = (!bounded1[0] || isect[1]>=0-eps) + && (!bounded1[1] || isect[1]<=1+eps) + && (!bounded2[0] || isect[2]>=0-eps) + && (!bounded2[1] || isect[2]<=1+eps) + ) + good ? isect[0] : undef; + // Function: line_closest_point() // Usage: -// pt = line_closest_point(line,pt); +// pt = line_closest_point(line, pt, [bounded]); // Topics: Geometry, Lines, Distance // Description: -// Returns the point on the given 2D or 3D `line` that is closest to the given point `pt`. -// The `line` and `pt` args should either both be 2D or both 3D. +// Returns the point on the given 2D or 3D line, segment or ray that is closest to the given point `pt`. +// The inputs `line` and `pt` args should either both be 2D or both 3D. The parameter bounded indicates +// whether the points of `line` should be treated as endpoints. // Arguments: // line = A list of two points that are on the unbounded line. // pt = The point to find the closest point on the line to. +// bounded = boolean or list of two booleans indicating that the line is bounded at that end. Default: [false,false] // Example(2D): // line = [[-30,0],[30,30]]; // pt = [-32,-10]; @@ -314,169 +246,70 @@ function segment_intersection(s1,s2,eps=EPSILON) = // stroke(line, endcaps="arrow2"); // color("blue") translate(pt) circle(r=1,$fn=12); // color("red") translate(p2) circle(r=1,$fn=12); -// Example(2D): +// Example(2D): If the line is bounded on the left you get the endpoint instead // line = [[-30,0],[30,30]]; -// pt = [-5,0]; -// p2 = line_closest_point(line,pt); -// stroke(line, endcaps="arrow2"); -// color("blue") translate(pt) circle(r=1,$fn=12); -// color("red") translate(p2) circle(r=1,$fn=12); -// Example(2D): -// line = [[-30,0],[30,30]]; -// pt = [40,25]; -// p2 = line_closest_point(line,pt); -// stroke(line, endcaps="arrow2"); -// color("blue") translate(pt) circle(r=1,$fn=12); -// color("red") translate(p2) circle(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// line = [[-30,-15,0],[30,15,30]]; -// pt = [5,5,5]; -// p2 = line_closest_point(line,pt); -// stroke(line, endcaps="arrow2"); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// line = [[-30,-15,0],[30,15,30]]; -// pt = [-35,-15,0]; -// p2 = line_closest_point(line,pt); -// stroke(line, endcaps="arrow2"); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// line = [[-30,-15,0],[30,15,30]]; -// pt = [40,15,25]; -// p2 = line_closest_point(line,pt); -// stroke(line, endcaps="arrow2"); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -function line_closest_point(line,pt) = - assert(_valid_line(line), "Invalid line." ) - assert( is_vector(pt,len(line[0])), "Invalid point or incompatible dimensions." ) - let( n = unit( line[0]- line[1]) ) - line[1] + ((pt- line[1]) * n) * n; - - -// Function: ray_closest_point() -// Usage: -// pt = ray_closest_point(seg,pt); -// Topics: Geometry, Rays, Distance -// Description: -// Returns the point on the given 2D or 3D ray `ray` that is closest to the given point `pt`. -// The `ray` and `pt` args should either both be 2D or both 3D. -// Arguments: -// ray = The ray, given as a list `[START,POINT]` of the start-point START, and a point POINT on the ray. -// pt = The point to find the closest point on the ray to. -// Example(2D): -// ray = [[-30,0],[30,30]]; // pt = [-32,-10]; -// p2 = ray_closest_point(ray,pt); -// stroke(ray, endcap2="arrow2"); +// p2 = line_closest_point(line,pt,bounded=[true,false]); +// stroke(line, endcap2="arrow2"); // color("blue") translate(pt) circle(r=1,$fn=12); // color("red") translate(p2) circle(r=1,$fn=12); -// Example(2D): -// ray = [[-30,0],[30,30]]; +// Example(2D): In this case it doesn't matter how bounded is set. Using SEGMENT is the most restrictive option. +// line = [[-30,0],[30,30]]; // pt = [-5,0]; -// p2 = ray_closest_point(ray,pt); -// stroke(ray, endcap2="arrow2"); +// p2 = line_closest_point(line,pt,SEGMENT); +// stroke(line); // color("blue") translate(pt) circle(r=1,$fn=12); // color("red") translate(p2) circle(r=1,$fn=12); -// Example(2D): -// ray = [[-30,0],[30,30]]; +// Example(2D): The result here is the same for a line or a ray. +// line = [[-30,0],[30,30]]; // pt = [40,25]; -// p2 = ray_closest_point(ray,pt); -// stroke(ray, endcap2="arrow2"); +// p2 = line_closest_point(line,pt,RAY); +// stroke(line, endcap2="arrow2"); // color("blue") translate(pt) circle(r=1,$fn=12); // color("red") translate(p2) circle(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// ray = [[-30,-15,0],[30,15,30]]; +// Example(2D): But with a segment we get a different result +// line = [[-30,0],[30,30]]; +// pt = [40,25]; +// p2 = line_closest_point(line,pt,SEGMENT); +// stroke(line); +// color("blue") translate(pt) circle(r=1,$fn=12); +// color("red") translate(p2) circle(r=1,$fn=12); +// Example(2D): The shorthand RAY uses the first point as the base of the ray. But you can specify a reversed ray directly, and in this case the result is the same as the result above for the segment. +// line = [[-30,0],[30,30]]; +// pt = [40,25]; +// p2 = line_closest_point(line,pt,[false,true]); +// stroke(line,endcap1="arrow2"); +// color("blue") translate(pt) circle(r=1,$fn=12); +// color("red") translate(p2) circle(r=1,$fn=12); +// Example(FlatSpin,VPD=200,VPT=[0,0,15]): A 3D example +// line = [[-30,-15,0],[30,15,30]]; // pt = [5,5,5]; -// p2 = ray_closest_point(ray,pt); -// stroke(ray, endcap2="arrow2"); +// p2 = line_closest_point(line,pt); +// stroke(line, endcaps="arrow2"); // color("blue") translate(pt) sphere(r=1,$fn=12); // color("red") translate(p2) sphere(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// ray = [[-30,-15,0],[30,15,30]]; -// pt = [-35,-15,0]; -// p2 = ray_closest_point(ray,pt); -// stroke(ray, endcap2="arrow2"); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// ray = [[-30,-15,0],[30,15,30]]; -// pt = [40,15,25]; -// p2 = ray_closest_point(ray,pt); -// stroke(ray, endcap2="arrow2"); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -function ray_closest_point(ray,pt) = - assert( _valid_line(ray), "Invalid ray." ) - assert(is_vector(pt,len(ray[0])), "Invalid point or incompatible dimensions." ) +function line_closest_point(line, pt, bounded=false) = + assert(_valid_line(line), "Invalid line") + assert(is_vector(pt, len(line[0])), "Invalid point or incompatible dimensions.") + assert(is_bool(bounded) || is_bool_list(bounded,2), "Invalid value for \"bounded\"") let( - seglen = norm(ray[1]-ray[0]), - segvec = (ray[1]-ray[0])/seglen, - projection = (pt-ray[0]) * segvec + bounded = force_list(bounded,2) ) - projection<=0 ? ray[0] : - ray[0] + projection*segvec; - - -// Function: segment_closest_point() -// Usage: -// pt = segment_closest_point(seg,pt); -// Topics: Geometry, Segments, Distance -// Description: -// Returns the point on the given 2D or 3D line segment `seg` that is closest to the given point `pt`. -// The `seg` and `pt` args should either both be 2D or both 3D. -// Arguments: -// seg = A list of two points that are the endpoints of the bounded line segment. -// pt = The point to find the closest point on the segment to. -// Example(2D): -// seg = [[-30,0],[30,30]]; -// pt = [-32,-10]; -// p2 = segment_closest_point(seg,pt); -// stroke(seg); -// color("blue") translate(pt) circle(r=1,$fn=12); -// color("red") translate(p2) circle(r=1,$fn=12); -// Example(2D): -// seg = [[-30,0],[30,30]]; -// pt = [-5,0]; -// p2 = segment_closest_point(seg,pt); -// stroke(seg); -// color("blue") translate(pt) circle(r=1,$fn=12); -// color("red") translate(p2) circle(r=1,$fn=12); -// Example(2D): -// seg = [[-30,0],[30,30]]; -// pt = [40,25]; -// p2 = segment_closest_point(seg,pt); -// stroke(seg); -// color("blue") translate(pt) circle(r=1,$fn=12); -// color("red") translate(p2) circle(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// seg = [[-30,-15,0],[30,15,30]]; -// pt = [5,5,5]; -// p2 = segment_closest_point(seg,pt); -// stroke(seg); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// seg = [[-30,-15,0],[30,15,30]]; -// pt = [-35,-15,0]; -// p2 = segment_closest_point(seg,pt); -// stroke(seg); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -// Example(FlatSpin,VPD=200,VPT=[0,0,15]): -// seg = [[-30,-15,0],[30,15,30]]; -// pt = [40,15,25]; -// p2 = segment_closest_point(seg,pt); -// stroke(seg); -// color("blue") translate(pt) sphere(r=1,$fn=12); -// color("red") translate(p2) sphere(r=1,$fn=12); -function segment_closest_point(seg,pt) = - assert( is_matrix(concat([pt],seg),3) , - "Invalid point or segment or incompatible dimensions." ) - pt + _closest_s1([seg[0]-pt, seg[1]-pt])[0]; - + bounded==[false,false] ? + let( n = unit( line[0]- line[1]) ) + line[1] + ((pt- line[1]) * n) * n + : bounded == [true,true] ? + pt + _closest_s1([line[0]-pt, line[1]-pt])[0] + : + let( + ray = bounded==[true,false] ? line : reverse(line), + seglen = norm(ray[1]-ray[0]), + segvec = (ray[1]-ray[0])/seglen, + projection = (pt-ray[0]) * segvec + ) + projection<=0 ? ray[0] : + ray[0] + projection*segvec; + // Function: line_from_points() // Usage: @@ -2071,7 +1904,7 @@ function point_in_polygon(point, poly, nonzero=true, eps=EPSILON) = for (i = [0:1:len(poly)-1]) let( seg = select(poly,i,i+1) ) if (!approx(seg[0],seg[1],eps) ) - point_on_segment2d(point, seg, eps=eps)? 1:0 + point_on_segment(point, seg, eps=eps)? 1:0 ] ) sum(on_brd) > 0? 0 : @@ -2577,4 +2410,4 @@ function _support_diff(p1,p2,d) = -// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap \ No newline at end of file +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/paths.scad b/paths.scad index a3f9b08f..35c1f243 100644 --- a/paths.scad +++ b/paths.scad @@ -311,7 +311,7 @@ function path_trim_end(path,trim,_d=0,_i=undef) = // color("red") translate(closest[1]) circle(d=3, $fn=12); function path_closest_point(path, pt) = let( - pts = [for (seg=idx(path)) segment_closest_point(select(path,seg,seg+1),pt)], + pts = [for (seg=idx(path)) line_closest_point(select(path,seg,seg+1),pt,SEGMENT)], dists = [for (p=pts) norm(p-pt)], min_seg = min_index(dists) ) [min_seg, pts[min_seg]]; diff --git a/rounding.scad b/rounding.scad index 8ea25bf3..52fe7c43 100644 --- a/rounding.scad +++ b/rounding.scad @@ -656,7 +656,7 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false) let( first_dir=firstcut[2], next_dir=nextcut[2], - corner = ray_intersection([firstcut[0], firstcut[0]-first_dir], [nextcut[0], nextcut[0]-next_dir]) + corner = line_intersection([firstcut[0], firstcut[0]-first_dir], [nextcut[0], nextcut[0]-next_dir],RAY,RAY) ) assert(is_def(corner), str("Curve directions at cut points don't intersect in a corner when ", loop?"closing the path":str("adding path ",i+1))) @@ -1641,7 +1641,7 @@ function _stroke_end(width,left, right, spec) = // returns [intersection_pt, index of first point in path after the intersection] function _path_line_intersection(path, line, ind=0) = ind==len(path)-1 ? undef : - let(intersect=line_segment_intersection(line, select(path,ind,ind+1))) + let(intersect=line_intersection(line, select(path,ind,ind+1),LINE,SEGMENT)) // If it intersects the segment excluding it's final point, then we're done // The final point is treated as part of the next segment is_def(intersect) && intersect != path[ind+1]? @@ -1694,8 +1694,10 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) = let( prev_corner = prev_offset + abs(rtop_in)*in_prev, next_corner = next_offset + abs(rtop_in)*in_next, - prev_degenerate = is_undef(ray_intersection(path2d([far_corner, far_corner+prev]), path2d([prev_offset, prev_offset+in_prev]))), - next_degenerate = is_undef(ray_intersection(path2d([far_corner, far_corner+next]), path2d([next_offset, next_offset+in_next]))) + prev_degenerate = is_undef(line_intersection(path2d([far_corner, far_corner+prev]), + path2d([prev_offset, prev_offset+in_prev]),RAY,RAY)), + next_degenerate = is_undef(line_intersection(path2d([far_corner, far_corner+next]), + path2d([next_offset, next_offset+in_next]),RAY,RAY)) ) [ prev_degenerate ? far_corner : prev_corner, far_corner, diff --git a/vnf.scad b/vnf.scad index 77e08e79..f10fc0cc 100644 --- a/vnf.scad +++ b/vnf.scad @@ -899,7 +899,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) = c = varr[ic] ) if (!approx(a,b) && !approx(b,c) && !approx(a,c)) let( - pt = segment_closest_point([a,c],b) + pt = line_closest_point([a,c],b,SEGMENT) ) if (approx(pt,b)) _vnf_validate_err("T_JUNCTION", [b])