mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-08-28 12:49:54 +02:00
Added convex_hull()
This commit is contained in:
264
math.scad
264
math.scad
@@ -44,6 +44,8 @@ include <compat.scad>
|
||||
|
||||
PHI = (1+sqrt(5))/2; // The golden ratio phi.
|
||||
|
||||
EPSILON = 1e-9; // A really small value useful in comparing FP numbers. ie: abs(a-b)<EPSILON
|
||||
|
||||
|
||||
|
||||
// Function: Cpi()
|
||||
@@ -94,6 +96,36 @@ function quantup(x,y) = ceil(x/y)*y;
|
||||
function constrain(v, minval, maxval) = min(maxval, max(minval, v));
|
||||
|
||||
|
||||
// Function: min_index()
|
||||
// Usage:
|
||||
// min_index(vals);
|
||||
// Description:
|
||||
// Returns the index of the minimal value in the given list.
|
||||
function min_index(vals, _minval, _minidx, _i=0) =
|
||||
_i>=len(vals)? _minidx :
|
||||
min_index(
|
||||
vals,
|
||||
((_minval == undef || vals[_i] < _minval)? vals[_i] : _minval),
|
||||
((_minval == undef || vals[_i] < _minval)? _i : _minidx),
|
||||
_i+1
|
||||
);
|
||||
|
||||
|
||||
// Function: max_index()
|
||||
// Usage:
|
||||
// max_index(vals);
|
||||
// Description:
|
||||
// Returns the index of the maximum value in the given list.
|
||||
function max_index(vals, _maxval, _maxidx, _i=0) =
|
||||
_i>=len(vals)? _maxidx :
|
||||
max_index(
|
||||
vals,
|
||||
((_maxval == undef || vals[_i] > _maxval)? vals[_i] : _maxval),
|
||||
((_maxval == undef || vals[_i] > _maxval)? _i : _maxidx),
|
||||
_i+1
|
||||
);
|
||||
|
||||
|
||||
// Function: posmod()
|
||||
// Usage:
|
||||
// posmod(x,m)
|
||||
@@ -788,6 +820,20 @@ function unique(arr) =
|
||||
|
||||
|
||||
|
||||
// Function: list_remove()
|
||||
// Usage:
|
||||
// list_remove(list, elements)
|
||||
// Description:
|
||||
// Remove all items from `list` whose indexes are in `elements`.
|
||||
// Arguments:
|
||||
// list = The list to remove items from.
|
||||
// elements = The list of indexes of items to remove.
|
||||
function list_remove(list, elements) = [
|
||||
for (i = [0:len(list)-1]) if (!search(i, elements)) list[i]
|
||||
];
|
||||
|
||||
|
||||
|
||||
// Internal. Not exposed.
|
||||
function _array_dim_recurse(v) =
|
||||
!is_list(v[0])? (
|
||||
@@ -1114,6 +1160,42 @@ function xy_to_polar(x,y=undef) = let(
|
||||
) [norm([xx,yy]), atan2(yy,xx)];
|
||||
|
||||
|
||||
// Function: xyz_to_planar()
|
||||
// Usage:
|
||||
// xyz_to_planar(point, a, b, c);
|
||||
// Description:
|
||||
// Given three points defining a plane, returns the projected planar
|
||||
// [X,Y] coordinates of the closest point to a 3D `point`. The origin
|
||||
// of the planar coordinate system [0,0] will be at point `a`, and the
|
||||
// Y+ axis direction will be towards point `b`. This coordinate system
|
||||
// can be useful in taking a set of nearly coplanar points, and converting
|
||||
// them to a pure XY set of coordinates for manipulation, before convering
|
||||
// them back to the original 3D plane.
|
||||
function xyz_to_planar(point, a, b, c) = let(
|
||||
u = normalize(b-a),
|
||||
v = normalize(c-a),
|
||||
n = normalize(cross(u,v)),
|
||||
w = normalize(cross(n,u)),
|
||||
relpoint = point-a
|
||||
) [relpoint * w, relpoint * u];
|
||||
|
||||
|
||||
// Function: planar_to_xyz()
|
||||
// Usage:
|
||||
// planar_to_xyz(point, a, b, c);
|
||||
// Description:
|
||||
// Given three points defining a plane, converts a planar [X,Y]
|
||||
// coordinate to the actual corresponding 3D point on the plane.
|
||||
// The origin of the planar coordinate system [0,0] will be at point
|
||||
// `a`, and the Y+ axis direction will be towards point `b`.
|
||||
function planar_to_xyz(point, a, b, c) = let(
|
||||
u = normalize(b-a),
|
||||
v = normalize(c-a),
|
||||
n = normalize(cross(u,v)),
|
||||
w = normalize(cross(n,u))
|
||||
) a + point.x * w + point.y * u;
|
||||
|
||||
|
||||
// Function: cylindrical_to_xyz()
|
||||
// Usage:
|
||||
// cylindrical_to_xyz(r, theta, z)
|
||||
@@ -1618,4 +1700,186 @@ function pointlist_bounds(pts) = [
|
||||
];
|
||||
|
||||
|
||||
// Function: triangle_area2d()
|
||||
// Usage:
|
||||
// triangle_area2d(a,b,c);
|
||||
// Description:
|
||||
// Returns the area of a triangle formed between three vertices.
|
||||
// Result will be negative if the points are in clockwise order.
|
||||
// Examples:
|
||||
// triangle_area2d([0,0], [5,10], [10,0]); // Returns -50
|
||||
// triangle_area2d([10,0], [5,10], [0,0]); // Returns 50
|
||||
function triangle_area2d(a,b,c) =
|
||||
(
|
||||
a.x * (b.y - c.y) +
|
||||
b.x * (c.y - a.y) +
|
||||
c.x * (a.y - b.y)
|
||||
) / 2;
|
||||
|
||||
|
||||
// Function: right_of_line2d()
|
||||
// Usage:
|
||||
// right_of_line2d(line, pt)
|
||||
// Description:
|
||||
// Returns true if the given point is to the left of the given line.
|
||||
// Arguments:
|
||||
// line = A list of two points.
|
||||
// pt = The point to test.
|
||||
function right_of_line2d(line, pt) =
|
||||
triangle_area2d(line[0], line[1], pt) < 0;
|
||||
|
||||
|
||||
// Function: collinear()
|
||||
// Usage:
|
||||
// collinear(a, b, c, [eps]);
|
||||
// Description:
|
||||
// Returns true if three points are co-linear.
|
||||
// Arguments:
|
||||
// a = First point.
|
||||
// b = Second point.
|
||||
// c = Third point.
|
||||
// eps = Acceptable max angle variance. Default: EPSILON (1e-9) degrees.
|
||||
function collinear(a, b, c, eps=EPSILON) =
|
||||
abs(vector_angle(b-a,c-a)) < eps;
|
||||
|
||||
|
||||
// Function: collinear_indexed()
|
||||
// Usage:
|
||||
// collinear_indexed(points, a, b, c, [eps]);
|
||||
// Description:
|
||||
// Returns true if three points are co-linear.
|
||||
// Arguments:
|
||||
// points = A list of points.
|
||||
// a = Index in `points` of first point.
|
||||
// b = Index in `points` of second point.
|
||||
// c = Index in `points` of third point.
|
||||
// eps = Acceptable max angle variance. Default: EPSILON (1e-9) degrees.
|
||||
function collinear_indexed(points, a, b, c, eps=EPSILON) =
|
||||
let(
|
||||
p1=points[a],
|
||||
p2=points[b],
|
||||
p3=points[c]
|
||||
) abs(vector_angle(p2-p1,p3-p1)) < eps;
|
||||
|
||||
|
||||
// Function: plane3pt()
|
||||
// Usage:
|
||||
// plane3pt(p1, p2, p3);
|
||||
// Description:
|
||||
// Generates the cartesian equation of a plane from three non-colinear points on the plane.
|
||||
// Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of a plane.
|
||||
// Arguments:
|
||||
// p1 = The first point on the plane.
|
||||
// p2 = The second point on the plane.
|
||||
// p3 = The third point on the plane.
|
||||
function plane3pt(p1, p2, p3) =
|
||||
let(normal = normalize(cross(p3-p1, p2-p1))) concat(normal, [normal*p1]);
|
||||
|
||||
|
||||
// Function: plane3pt_indexed()
|
||||
// Usage:
|
||||
// plane3pt_indexed(points, i1, i2, i3);
|
||||
// Description:
|
||||
// Given a list of points, and the indexes of three of those points,
|
||||
// generates the cartesian equation of a plane that those points all
|
||||
// lie on. Requires that the three indexed points be non-collinear.
|
||||
// Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of a plane.
|
||||
// Arguments:
|
||||
// points = A list of points.
|
||||
// i1 = The index into `points` of the first point on the plane.
|
||||
// i2 = The index into `points` of the second point on the plane.
|
||||
// i3 = The index into `points` of the third point on the plane.
|
||||
function plane3pt_indexed(points, i1, i2, i3) =
|
||||
let(
|
||||
p1 = points[i1],
|
||||
p2 = points[i2],
|
||||
p3 = points[i3],
|
||||
normal = normalize(cross(p3-p1, p2-p1))
|
||||
) concat(normal, [normal*p1]);
|
||||
|
||||
|
||||
// Function: distance_from_plane()
|
||||
// Usage:
|
||||
// distance_from_plane(plane, point)
|
||||
// Description:
|
||||
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
|
||||
// is Ax+By+Cz+D=0, determines how far from that plane the given point is.
|
||||
// The returned distance will be positive if the point is in front of the
|
||||
// plane; on the same side of the plane as the normal of that plane points
|
||||
// towards. If the point is behind the plane, then the distance returned
|
||||
// will be negative. The normal of the plane is the same as [A,B,C].
|
||||
// Arguments:
|
||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// point = The point to test.
|
||||
function distance_from_plane(plane, point) =
|
||||
[plane.x, plane.y, plane.z] * point - plane[3];
|
||||
|
||||
|
||||
// Function: coplanar()
|
||||
// Usage:
|
||||
// coplanar(plane, point);
|
||||
// Description:
|
||||
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
|
||||
// is Ax+By+Cz+D=0, determines if the given point is on that plane.
|
||||
// Returns true if the point is on that plane.
|
||||
// Arguments:
|
||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// point = The point to test.
|
||||
function coplanar(plane, point) =
|
||||
abs(distance_from_plane(plane, point)) <= EPSILON;
|
||||
|
||||
|
||||
// Function: in_front_of_plane()
|
||||
// Usage:
|
||||
// in_front_of_plane(plane, point);
|
||||
// Description:
|
||||
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
|
||||
// is Ax+By+Cz+D=0, determines if the given point is on the side of that
|
||||
// plane that the normal points towards. The normal of the plane is the
|
||||
// same as [A,B,C].
|
||||
// Arguments:
|
||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// point = The point to test.
|
||||
function in_front_of_plane(plane, point) =
|
||||
distance_from_plane(plane, point) > EPSILON;
|
||||
|
||||
|
||||
// Function: simplify_path()
|
||||
// Description:
|
||||
// Takes a path and removes unnecessary collinear points.
|
||||
// Usage:
|
||||
// simplify_path(path, [eps])
|
||||
// Arguments:
|
||||
// path = A list of 2D path points.
|
||||
// eps = Largest angle variance allowed. Default: EPSILON (1-e9) degrees.
|
||||
function simplify_path(path, eps=EPSILON, _a=0, _b=2, _acc=[]) =
|
||||
(_b >= len(path))? concat([path[0]], _acc, [path[len(path)-1]]) :
|
||||
simplify_path(
|
||||
path, eps,
|
||||
(collinear_indexed(path, _a, _b-1, _b, eps=eps)? _a : _b-1),
|
||||
_b+1,
|
||||
(collinear_indexed(path, _a, _b-1, _b, eps=eps)? _acc : concat(_acc, [path[_b-1]]))
|
||||
);
|
||||
|
||||
|
||||
// Function: simplify_path_indexed()
|
||||
// Description:
|
||||
// Takes a list of points, and a path as a list of indexes into `points`,
|
||||
// and removes all path points that are unecessarily collinear.
|
||||
// Usage:
|
||||
// simplify_path_indexed(path, eps)
|
||||
// Arguments:
|
||||
// points = A list of points.
|
||||
// path = A list of indexes into `points` that forms a path.
|
||||
// eps = Largest angle variance allowed. Default: EPSILON (1-e9) degrees.
|
||||
function simplify_path_indexed(points, path, eps=EPSILON, _a=0, _b=2, _acc=[]) =
|
||||
(_b >= len(path))? concat([path[0]], _acc, [path[len(path)-1]]) :
|
||||
simplify_path_indexed(
|
||||
points, path, eps,
|
||||
(collinear_indexed(points, path[_a], path[_b-1], path[_b], eps=eps)? _a : _b-1),
|
||||
_b+1,
|
||||
(collinear_indexed(points, path[_a], path[_b-1], path[_b], eps=eps)? _acc : concat(_acc, [path[_b-1]]))
|
||||
);
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
Reference in New Issue
Block a user