mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-01-16 21:58:27 +01:00
Added polygon_shift_to_closest_point(), clockwise_polygon(), ccw_polygon()
This commit is contained in:
parent
be6575b5fd
commit
700acae000
105
geometry.scad
105
geometry.scad
@ -417,16 +417,6 @@ function plane3pt(p1, p2, p3) =
|
|||||||
) concat(normal, [normal*p1]);
|
) concat(normal, [normal*p1]);
|
||||||
|
|
||||||
|
|
||||||
function plane_from_pointslist(points) =
|
|
||||||
let(
|
|
||||||
points = deduplicate(points),
|
|
||||||
indices = find_noncollinear_points(points),
|
|
||||||
p1 = points[indices[0]],
|
|
||||||
p2 = points[indices[1]],
|
|
||||||
p3 = points[indices[2]]
|
|
||||||
) plane3pt(p1,p2,p3);
|
|
||||||
|
|
||||||
|
|
||||||
// Function: plane3pt_indexed()
|
// Function: plane3pt_indexed()
|
||||||
// Usage:
|
// Usage:
|
||||||
// plane3pt_indexed(points, i1, i2, i3);
|
// plane3pt_indexed(points, i1, i2, i3);
|
||||||
@ -448,6 +438,22 @@ function plane3pt_indexed(points, i1, i2, i3) =
|
|||||||
) plane3pt(p1,p2,p3);
|
) plane3pt(p1,p2,p3);
|
||||||
|
|
||||||
|
|
||||||
|
// Function: plane_from_pointslist()
|
||||||
|
// Usage:
|
||||||
|
// plane_from_pointslist(points);
|
||||||
|
// Description:
|
||||||
|
// Given a list of coplanar points, returns the cartesian equation of a plane.
|
||||||
|
// Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of the plane.
|
||||||
|
function plane_from_pointslist(points) =
|
||||||
|
let(
|
||||||
|
points = deduplicate(points),
|
||||||
|
indices = find_noncollinear_points(points),
|
||||||
|
p1 = points[indices[0]],
|
||||||
|
p2 = points[indices[1]],
|
||||||
|
p3 = points[indices[2]]
|
||||||
|
) plane3pt(p1,p2,p3);
|
||||||
|
|
||||||
|
|
||||||
// Function: plane_normal()
|
// Function: plane_normal()
|
||||||
// Usage:
|
// Usage:
|
||||||
// plane_normal(plane);
|
// plane_normal(plane);
|
||||||
@ -574,6 +580,21 @@ function polygon_area(vertices) =
|
|||||||
0.5*sum([for(i=[0:len(vertices)-1]) det2(select(vertices,i,i+1))]);
|
0.5*sum([for(i=[0:len(vertices)-1]) det2(select(vertices,i,i+1))]);
|
||||||
|
|
||||||
|
|
||||||
|
// Function: polygon_shift_to_closest_point()
|
||||||
|
// Usage:
|
||||||
|
// polygon_shift_to_closest_point(path, pt);
|
||||||
|
// Description:
|
||||||
|
// Given a polygon `path`, rotates the point ordering so that the first point in the path is the one closest to the given point `pt`.
|
||||||
|
function polygon_shift_to_closest_point(path, pt) =
|
||||||
|
let(
|
||||||
|
path = cleanup_path(path),
|
||||||
|
closest = path_closest_point(path,pt),
|
||||||
|
seg = select(path,closest[0],closest[0]+1),
|
||||||
|
u = norm(closest[1]-seg[0]) / norm(seg[1]-seg[0]),
|
||||||
|
segnum = closest[0] + (u>0.5? 1 : 0)
|
||||||
|
) select(path,segnum,segnum+len(path)-1);
|
||||||
|
|
||||||
|
|
||||||
// Function: first_noncollinear()
|
// Function: first_noncollinear()
|
||||||
// Usage:
|
// Usage:
|
||||||
// first_noncollinear(i1, i2, points);
|
// first_noncollinear(i1, i2, points);
|
||||||
@ -740,24 +761,6 @@ function point_in_polygon(point, path, eps=EPSILON) =
|
|||||||
sum([for(i=[0:1:len(path)-1]) let(seg=select(path,i,i+1)) if(!approx(seg[0],seg[1],eps=eps)) _point_above_below_segment(point, seg)]) != 0? 1 : -1;
|
sum([for(i=[0:1:len(path)-1]) let(seg=select(path,i,i+1)) if(!approx(seg[0],seg[1],eps=eps)) _point_above_below_segment(point, seg)]) != 0? 1 : -1;
|
||||||
|
|
||||||
|
|
||||||
// Function: point_in_region()
|
|
||||||
// Usage:
|
|
||||||
// point_in_region(point, region);
|
|
||||||
// Description:
|
|
||||||
// Tests if a point is inside, outside, or on the border of a region.
|
|
||||||
// Returns -1 if the point is outside the region.
|
|
||||||
// Returns 0 if the point is on the boundary.
|
|
||||||
// Returns 1 if the point lies inside the region.
|
|
||||||
// Arguments:
|
|
||||||
// point = The point to test.
|
|
||||||
// region = The region to test against. Given as a list of polygon paths.
|
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
|
||||||
function point_in_region(point, region, eps=EPSILON, _i=0, _cnt=0) =
|
|
||||||
(_i >= len(region))? ((_cnt%2==1)? 1 : -1) : let(
|
|
||||||
pip = point_in_polygon(point, region[_i], eps=eps)
|
|
||||||
) pip==0? 0 : point_in_region(point, region, eps=eps, _i=_i+1, _cnt = _cnt + (pip>0? 1 : 0));
|
|
||||||
|
|
||||||
|
|
||||||
// Function: pointlist_bounds()
|
// Function: pointlist_bounds()
|
||||||
// Usage:
|
// Usage:
|
||||||
// pointlist_bounds(pts);
|
// pointlist_bounds(pts);
|
||||||
@ -800,15 +803,15 @@ function furthest_point(pt, points) =
|
|||||||
) i;
|
) i;
|
||||||
|
|
||||||
|
|
||||||
// Function: polygon_clockwise()
|
// Function: polygon_is_clockwise()
|
||||||
// Usage:
|
// Usage:
|
||||||
// polygon_clockwise(path);
|
// polygon_is_clockwise(path);
|
||||||
// Description:
|
// Description:
|
||||||
// Return true if the given 2D simple polygon is in clockwise order, false otherwise.
|
// Return true if the given 2D simple polygon is in clockwise order, false otherwise.
|
||||||
// Results for complex (self-intersecting) polygon are indeterminate.
|
// Results for complex (self-intersecting) polygon are indeterminate.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = The list of 2D path points for the perimeter of the polygon.
|
// path = The list of 2D path points for the perimeter of the polygon.
|
||||||
function polygon_clockwise(path) =
|
function polygon_is_clockwise(path) =
|
||||||
let(
|
let(
|
||||||
minx = min(subindex(path,0)),
|
minx = min(subindex(path,0)),
|
||||||
lowind = search(minx, path, 0, 0),
|
lowind = search(minx, path, 0, 0),
|
||||||
@ -819,6 +822,24 @@ function polygon_clockwise(path) =
|
|||||||
) det2([select(path,extreme+1)-path[extreme], select(path, extreme-1)-path[extreme]])<0;
|
) det2([select(path,extreme+1)-path[extreme], select(path, extreme-1)-path[extreme]])<0;
|
||||||
|
|
||||||
|
|
||||||
|
// Function: clockwise_polygon()
|
||||||
|
// Usage:
|
||||||
|
// clockwise_polygon(path);
|
||||||
|
// Description:
|
||||||
|
// Given a polygon path, returns the clockwise winding version of that path.
|
||||||
|
function clockwise_polygon(path) =
|
||||||
|
polygon_is_clockwise(path)? path : reverse(path);
|
||||||
|
|
||||||
|
|
||||||
|
// Function: ccw_polygon()
|
||||||
|
// Usage:
|
||||||
|
// ccw_polygon(path);
|
||||||
|
// Description:
|
||||||
|
// Given a polygon path, returns the counter-clockwise winding version of that path.
|
||||||
|
function ccw_polygon(path) =
|
||||||
|
polygon_is_clockwise(path)? reverse(path) : path;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Section: Regions and Boolean 2D Geometry
|
// Section: Regions and Boolean 2D Geometry
|
||||||
|
|
||||||
@ -872,6 +893,24 @@ function check_and_fix_path(path,valid_dim=undef,closed=false) =
|
|||||||
function cleanup_region(region, eps=EPSILON) = [for (path=region) cleanup_path(path, eps=eps)];
|
function cleanup_region(region, eps=EPSILON) = [for (path=region) cleanup_path(path, eps=eps)];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: point_in_region()
|
||||||
|
// Usage:
|
||||||
|
// point_in_region(point, region);
|
||||||
|
// Description:
|
||||||
|
// Tests if a point is inside, outside, or on the border of a region.
|
||||||
|
// Returns -1 if the point is outside the region.
|
||||||
|
// Returns 0 if the point is on the boundary.
|
||||||
|
// Returns 1 if the point lies inside the region.
|
||||||
|
// Arguments:
|
||||||
|
// point = The point to test.
|
||||||
|
// region = The region to test against. Given as a list of polygon paths.
|
||||||
|
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||||
|
function point_in_region(point, region, eps=EPSILON, _i=0, _cnt=0) =
|
||||||
|
(_i >= len(region))? ((_cnt%2==1)? 1 : -1) : let(
|
||||||
|
pip = point_in_polygon(point, region[_i], eps=eps)
|
||||||
|
) pip==0? 0 : point_in_region(point, region, eps=eps, _i=_i+1, _cnt = _cnt + (pip>0? 1 : 0));
|
||||||
|
|
||||||
|
|
||||||
// Function: region_path_crossings()
|
// Function: region_path_crossings()
|
||||||
// Usage:
|
// Usage:
|
||||||
// region_path_crossings(path, region);
|
// region_path_crossings(path, region);
|
||||||
@ -1142,7 +1181,7 @@ function offset(
|
|||||||
is_region(path)? (
|
is_region(path)? (
|
||||||
assert(!return_faces, "return_faces not supported for regions.")
|
assert(!return_faces, "return_faces not supported for regions.")
|
||||||
let(
|
let(
|
||||||
path = [for (p=path) polygon_clockwise(p)? p : reverse(p)],
|
path = [for (p=path) polygon_is_clockwise(p)? p : reverse(p)],
|
||||||
rgn = exclusive_or([for (p = path) [p]]),
|
rgn = exclusive_or([for (p = path) [p]]),
|
||||||
pathlist = sort(idx=0,[
|
pathlist = sort(idx=0,[
|
||||||
for (i=[0:1:len(rgn)-1]) [
|
for (i=[0:1:len(rgn)-1]) [
|
||||||
@ -1164,7 +1203,7 @@ function offset(
|
|||||||
let(
|
let(
|
||||||
chamfer = is_def(r) ? false : chamfer,
|
chamfer = is_def(r) ? false : chamfer,
|
||||||
quality = max(0,round(quality)),
|
quality = max(0,round(quality)),
|
||||||
flip_dir = closed && !polygon_clockwise(path) ? -1 : 1,
|
flip_dir = closed && !polygon_is_clockwise(path) ? -1 : 1,
|
||||||
d = flip_dir * (is_def(r) ? r : delta),
|
d = flip_dir * (is_def(r) ? r : delta),
|
||||||
shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)],
|
shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)],
|
||||||
// good segments are ones where no point on the segment is less than distance d from any point on the path
|
// good segments are ones where no point on the segment is less than distance d from any point on the path
|
||||||
|
@ -636,7 +636,7 @@ module rounded_sweep(
|
|||||||
top = struct_set(argspec, top, grow=false);
|
top = struct_set(argspec, top, grow=false);
|
||||||
bottom = struct_set(argspec, bottom, grow=false);
|
bottom = struct_set(argspec, bottom, grow=false);
|
||||||
|
|
||||||
clockwise = polygon_clockwise(path);
|
clockwise = polygon_is_clockwise(path);
|
||||||
|
|
||||||
assert(height>=0, "Height must be nonnegative");
|
assert(height>=0, "Height must be nonnegative");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user