mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Allowed half_of() to work on paths, regions and surfaces.
This commit is contained in:
parent
96bd60aceb
commit
16204c6724
3 changed files with 181 additions and 9 deletions
46
common.scad
46
common.scad
|
@ -312,7 +312,53 @@ function get_height(h=undef,l=undef,height=undef,dflt=undef) =
|
|||
assert(num_defined([h,l,height])<=1,"You must specify only one of `l`, `h`, and `height`")
|
||||
first_defined([h,l,height,dflt]);
|
||||
|
||||
// Function: get_named_args(anonymous, named, _undef)
|
||||
// Usage:
|
||||
// function f(anon1=_undef, anon2=_undef,...,
|
||||
// named1=_undef, named2=_undef, ...) =
|
||||
// let(args = get_named_args([anon1, anon2, ...],
|
||||
// [[named1, default1], [named2, default2], ...]))
|
||||
// ...
|
||||
// Description:
|
||||
// given a set of anonymous and named arguments, returns the values of
|
||||
// named arguments, in order.
|
||||
// - All named arguments which were provided by the user take the
|
||||
// value provided.
|
||||
// - All named arguments which were not provided by the user are
|
||||
// affected from anonymous arguments, in order.
|
||||
// - Any remaining named arguments take the provided default values.
|
||||
// Arguments:
|
||||
// anonymous = the list of values of anonymous arguments.
|
||||
// named = the list of [passed-value, default value] of named arguments.
|
||||
// _undef = the default value used by the calling function for all
|
||||
// arguments (this is *not* undef, or any value that the user might
|
||||
// purposely want to use as an argument value).
|
||||
// Examples:
|
||||
// function f(arg1=_undef, arg2=_undef, arg3=_undef,
|
||||
// named1=_undef, named2=_undef, named3=_undef) =
|
||||
// let(named = get_named_args([arg1, arg2, arg3],
|
||||
// [[named1, "default1"], [named2, "default2"], [named3, "default3"]]))
|
||||
// named;
|
||||
// echo(f()); // ["default1", "default2", "default3"]
|
||||
// echo(f("given2", "given3", named1="given1")); // ["given1", "given2", "given3"]
|
||||
// echo(f("given1")); // ["given1", "default2", "default3"]
|
||||
// echo(f(named1="given1", "given2")); // ["given1", "given2", "default3"]
|
||||
// echo(f(undef, named1="given1", undef)); // ["given1", undef, undef]
|
||||
|
||||
// a value that the user should never enter randomly;
|
||||
// result of `dd if=/dev/random bs=32 count=1 |base64` :
|
||||
_undef="LRG+HX7dy89RyHvDlAKvb9Y04OTuaikpx205CTh8BSI";
|
||||
|
||||
function get_named_args(anonymous, named,_undef=_undef) =
|
||||
/* u: set of undefined indices in named arguments */
|
||||
let(from_anon = [for(p=enumerate(named)) if(p[1][0]==_undef) p[0]],
|
||||
n = len(anonymous))
|
||||
echo("from_anon:", from_anon)
|
||||
[ for(e = enumerate(named))
|
||||
// if the value is defined, return it:
|
||||
e[1][0] != _undef ? e[1][0] :
|
||||
let(k = anonymous[search(e[0], from_anon)[0]])
|
||||
k != _undef ? k : e[1][1] ];
|
||||
// Function: scalar_vec3()
|
||||
// Usage:
|
||||
// scalar_vec3(v, <dflt>);
|
||||
|
|
|
@ -118,7 +118,7 @@ module half_of(v=UP, cp, s=1000, planar=false)
|
|||
}
|
||||
}
|
||||
|
||||
function half_of(v, arg1, arg2, cp, p, s=1e4) =
|
||||
function half_of(v, arg1, arg2, s=1e4, cp, p) =
|
||||
/* may be called as either:
|
||||
* p= cp=
|
||||
* 1. (v, p) arg1 0
|
||||
|
@ -158,14 +158,15 @@ function half_of(v, arg1, arg2, cp, p, s=1e4) =
|
|||
// create self-intersection or whiskers:
|
||||
z[i]*z[j] >= 0 ? [] : [(z[j]*p[i]-z[i]*p[j])/(z[j]-z[i])]) ]
|
||||
:
|
||||
assert(is_region(p), str("must provide point, path or region"))
|
||||
assert(len(v) == 2, str("3D vector not compatible with region"))
|
||||
let(u=unit(v), w=[-u[1], u[0]],
|
||||
R=[[cp+s*w, cp+s*(v+v), cp+s*(v-w), cp-s*w]]) // bounding region
|
||||
intersection(R, p);
|
||||
// FIXME: find something intelligent to do if p is a VNF
|
||||
// FIXME: scadlib csg.scad, csg_hspace()
|
||||
|
||||
is_region(p) ?
|
||||
assert(len(v) == 2, str("3D vector not compatible with region"))
|
||||
let(u=unit(v), w=[-u[1], u[0]],
|
||||
R=[[cp+s*w, cp+s*(v+v), cp+s*(v-w), cp-s*w]]) // half-plane
|
||||
intersection(R, p)
|
||||
:
|
||||
is_vnf(p) ?
|
||||
vnf_halfspace(halfspace=concat(v,[-v*cp]), vnf=p) :
|
||||
assert(false, "must pass either a point, a path, a region, or a VNF");
|
||||
|
||||
// Module: left_half()
|
||||
//
|
||||
|
|
125
vnf.scad
125
vnf.scad
|
@ -827,5 +827,130 @@ module vnf_validate(vnf, size=1, show_warns=true, check_isects=false) {
|
|||
color([0.5,0.5,0.5,0.5]) vnf_polyhedron(vnf);
|
||||
}
|
||||
|
||||
// Section: VNF transformations
|
||||
//
|
||||
// Function: vnf_halfspace(halfspace, vnf)
|
||||
// Usage:
|
||||
// vnf_halfspace([a,b,c,d], vnf)
|
||||
// Description:
|
||||
// returns the intersection of the VNF with the given half-space.
|
||||
// Arguments:
|
||||
// halfspace = half-space to intersect with, given as the four
|
||||
// coefficients of the affine inequation a*x+b*y+c*z+d ≥ 0.
|
||||
|
||||
function _vnf_halfspace_pts(halfspace, points, faces,
|
||||
inside=undef, coords=[], map=[]) =
|
||||
/* Recursive function to compute the intersection of points (and edges,
|
||||
* but not faces) with with the half-space.
|
||||
* Parameters:
|
||||
* halfspace a vector(4)
|
||||
* points a list of points3d
|
||||
* faces a list of indexes in points
|
||||
* inside a vector{bool} determining which points belong to the
|
||||
* half-space; if undef, it is initialized at first loop.
|
||||
* coords the coordinates of the points in the intersection
|
||||
* map the logical map (old point) → (new point(s)):
|
||||
* if point i is kept, then map[i] = new-index-for-i;
|
||||
* if point i is dropped, then map[i] = [[j1, k1], [j2, k2], …],
|
||||
* where points j1,… are kept (old index)
|
||||
* and k1,… are the matching intersections (new index).
|
||||
* Returns the triple [coords, map, inside].
|
||||
*
|
||||
*/
|
||||
let(i=len(map), n=len(coords)) // we are currently processing point i
|
||||
// termination test:
|
||||
i >= len(points) ? [ coords, map, inside ] :
|
||||
let(inside = !is_undef(inside) ? inside :
|
||||
[for(x=points) halfspace*concat(x,[1]) >= 0],
|
||||
pi = points[i])
|
||||
// inside half-space: keep the point (and reindex)
|
||||
inside[i] ? _vnf_halfspace_pts(halfspace, points, faces, inside,
|
||||
concat(coords, [pi]), concat(map, [n]))
|
||||
: // else: compute adjacent vertices (adj)
|
||||
let(adj = unique([for(f=faces) let(m=len(f), j=search(i, f)[0])
|
||||
each if(j!=undef) [f[(j+1)%m], f[(j+m-1)%m]] ]),
|
||||
// filter those which lie in half-space:
|
||||
adj2 = [for(x=adj) if(inside[x]) x],
|
||||
zi = halfspace*concat(pi, [1]))
|
||||
_vnf_halfspace_pts(halfspace, points, faces, inside,
|
||||
// new points: we append all these intersection points
|
||||
concat(coords, [for(j=adj2) let(zj=halfspace*concat(points[j],[1]))
|
||||
(zi*points[j]-zj*pi)/(zi-zj)]),
|
||||
// map: we add the info
|
||||
concat(map, [[for(y=enumerate(adj2)) [y[1], n+y[0]]]]));
|
||||
function _vnf_halfspace_face(face, map, inside, i=0,
|
||||
newface=[], newedge=[], exit) =
|
||||
/* Recursive function to intersect a face of the VNF with the half-plane.
|
||||
* Arguments:
|
||||
* face: the list of points of the face (old indices).
|
||||
* map: as produced by _vnf_halfspace_pts
|
||||
* inside: vector{bool} containing half-space info
|
||||
* i: index for iteration
|
||||
* exit: boolean; is first point in newedge an exit or an entrance from
|
||||
* half-space?
|
||||
* newface: list of (new indexes of) points on the face
|
||||
* newedge: list of new points on the plane (even number of points)
|
||||
* Return value: [newface, new-edges], where new-edges is a list of
|
||||
* pairs [entrance-node, exit-node] (new indices).
|
||||
*/
|
||||
// termination condition:
|
||||
(i >= len(face)) ? [ newface,
|
||||
// if exit==true then we return newedge[1,0], newedge[3,2], ...
|
||||
// otherwise newedge[0,1], newedge[2,3], ...;
|
||||
// all edges are oriented (entrance->exit), so that by following the
|
||||
// arrows we obtain a correctly-oriented face:
|
||||
let(k = exit ? 0 : 1)
|
||||
[for(i=[0:2:len(newedge)-2]) [newedge[i+k], newedge[i+1-k]]] ]
|
||||
: // recursion case: p is current point on face, q is next point
|
||||
let(p = face[i], q = face[(i+1)%len(face)],
|
||||
// if p is inside half-plane, keep it in the new face:
|
||||
newface0 = inside[p] ? concat(newface, [map[p]]) : newface)
|
||||
// if the current segment does not intersect, this is all:
|
||||
inside[p] == inside[q] ? _vnf_halfspace_face(face, map, inside, i+1,
|
||||
newface0, newedge, exit)
|
||||
: // otherwise, we must add the intersection point:
|
||||
// rename the two points p,q as inner and outer point:
|
||||
let(in = inside[p] ? p : q, out = p+q-in,
|
||||
inter=[for(a=map[out]) if(a[0]==in) a[1]][0])
|
||||
_vnf_halfspace_face(face, map, inside, i+1,
|
||||
concat(newface0, [inter]),
|
||||
concat(newedge, [inter]),
|
||||
is_undef(exit) ? inside[p] : exit);
|
||||
function _vnf_halfspace_paths(edges, i=0, paths=[]) =
|
||||
/* given a set of oriented edges [x,y],
|
||||
returns all paths [x,y,z,..] that may be formed from these edges.
|
||||
A closed path will be returned with equal first and last point.
|
||||
i: index of currently examined edge
|
||||
*/
|
||||
i >= len(edges) ? paths : // termination condition
|
||||
let(e = edges[i],
|
||||
s = [for(x=enumerate(paths)) if(x[1][len(x[1])-1] == e[0]) x[0]])
|
||||
_vnf_halfspace_paths(edges, i+1,
|
||||
// if cannot attach to previous path: create a new one
|
||||
s == [] ? concat(paths, [e]) :
|
||||
// otherwise, attach to found path
|
||||
[for(x=enumerate(paths)) x[0]==s[0] ? concat(x[1], [e[1]]) : x[1]]);
|
||||
function vnf_halfspace(_arg1=_undef, _arg2=_undef,
|
||||
halfspace=_undef, vnf=_undef) =
|
||||
// here is where we wish that OpenSCAD had array lvalues...
|
||||
let(args=get_named_args([_arg1, _arg2], [[halfspace],[vnf]]),
|
||||
halfspace=args[0], vnf=args[1])
|
||||
assert(is_vector(halfspace, 4),
|
||||
"half-space must be passed as a length 4 affine form")
|
||||
assert(is_vnf(vnf), "must pass a vnf")
|
||||
// read points
|
||||
let(tmp1=_vnf_halfspace_pts(halfspace, vnf[0], vnf[1]),
|
||||
coords=tmp1[0], map=tmp1[1], inside=tmp1[2],
|
||||
// cut faces and generate edges
|
||||
tmp2= [for(f=vnf[1]) _vnf_halfspace_face(f, map, inside)],
|
||||
newfaces=[for(x=tmp2) if(x[0]!=[]) x[0]],
|
||||
newedges=[for(x=tmp2) each x[1]],
|
||||
// generate new faces
|
||||
paths=_vnf_halfspace_paths(newedges),
|
||||
loops=[for(p=paths) if(p[0] == p[len(p)-1]) p])
|
||||
[coords, concat(newfaces, loops)];
|
||||
|
||||
//
|
||||
//
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue