fix align_polygon & examples

This commit is contained in:
Adrian Mariano 2021-10-15 16:01:01 -04:00
parent 315a1021df
commit 544bb5883e
2 changed files with 49 additions and 66 deletions

View file

@ -1888,26 +1888,7 @@ function polygon_shift(poly, i) =
// move_copies(concat(circ,pent)) circle(r=.1,$fn=32);
// color("red") move_copies([pent[0],circ[0]]) circle(r=.1,$fn=32);
// color("blue") translate(reindexed[0])circle(r=.1,$fn=32);
function old_reindex_polygon(reference, poly, return_error=false) =
assert(is_path(reference) && is_path(poly,dim=len(reference[0])),
"Invalid polygon(s) or incompatible dimensions. " )
assert(len(reference)==len(poly), "The polygons must have the same length.")
let(
dim = len(reference[0]),
N = len(reference),
fixpoly = dim != 2? poly :
is_polygon_clockwise(reference)
? clockwise_polygon(poly)
: ccw_polygon(poly),
I = [for(i=reference) 1],
val = [ for(k=[0:N-1])
[for(i=[0:N-1])
(reference[i]*poly[(i+k)%N]) ] ]*I,
optimal_poly = polygon_shift(fixpoly, max_index(val))
)
return_error? [optimal_poly, min(poly*(I*poly)-2*val)] :
optimal_poly;
function reindex_polygon(reference, poly, return_error=false) =
function reindex_polygon(reference, poly, return_error=false) =
assert(is_path(reference) && is_path(poly,dim=len(reference[0])),
"Invalid polygon(s) or incompatible dimensions. " )
assert(len(reference)==len(poly), "The polygons must have the same length.")
@ -1929,67 +1910,69 @@ function reindex_polygon(reference, poly, return_error=false) =
optimal_poly;
// Function: align_polygon()
// Usage:
// newpoly = align_polygon(reference, poly, angles, [cp]);
// newpoly = align_polygon(reference, poly, [angles], [cp], [tran], [return_ind]);
// Topics: Geometry, Polygons
// Description:
// Tries the list or range of angles to find a rotation of the specified 2D polygon that best aligns
// with the reference 2D polygon. For each angle, the polygon is reindexed, which is a costly operation
// so if run time is a problem, use a smaller sampling of angles. Returns the rotated and reindexed
// polygon.
// Find the best alignment of a specified 2D polygon with a reference 2D polygon over a set of
// transformations. You can specify a list or range of angles and a centerpoint or you can
// give a list of arbitrary 2d transformation matrices. For each transformation or angle, the polygon is
// reindexed, which is a costly operation so if run time is a problem, use a smaller sampling of angles or
// transformations. By default returns the rotated and reindexed polygon. You can also request that
// the best angle or the index into the transformation list be returned. When you specify an angle
// Arguments:
// reference = reference polygon
// poly = polygon to rotate into alignment with the reference
// angles = list or range of angles to test
// cp = centerpoint for rotations
// Example(2D): The original hexagon in yellow is not well aligned with the pentagon. Turning it so the faces line up gives an optimal alignment, shown in red.
// $fn=32;
// pentagon = subdivide_path(pentagon(side=2),60);
// hexagon = subdivide_path(hexagon(side=2.7),60);
// color("red") move_copies(scale(1.4,p=align_polygon(pentagon,hexagon,[0:10:359]))) circle(r=.1);
// move_copies(concat(pentagon,hexagon))circle(r=.1);
function old_align_polygon(reference, poly, angles, cp) =
assert(is_path(reference,dim=2) && is_path(poly,dim=2),
"Invalid polygon(s). " )
assert(len(reference)==len(poly), "The polygons must have the same length.")
// ---
// tran = list of 2D transformation matrices to optimize over
// return_ind = if true, return the best angle (if you specified angles) or the index into tran otherwise of best alignment
// Example(2D): Rotating the poorly aligned light gray triangle by 105 degrees produces the best alignment, shown in blue:
// ellipse = yscale(3,circle(r=10, $fn=32));
// tri = move([-50/3,-9],
// subdivide_path([[0,0], [50,0], [0,27]], 32));
// aligned = align_polygon(ellipse,tri, [0:5:180]);
// color("white")stroke(tri,width=.5,closed=true);
// stroke(ellipse, width=.5, closed=true);
// color("blue")stroke(aligned,width=.5,closed=true);
// Example(2D,NoAxes): Translating a triangle (light gray) to the best alignment (blue)
// ellipse = yscale(2,circle(r=10, $fn=32));
// tri = subdivide_path([[0,0], [27,0], [-7,50]], 32);
// T = [for(x=[-10:0], y=[-30:-15]) move([x,y])];
// aligned = align_polygon(ellipse,tri, trans=T);
// color("white")stroke(tri,width=.5,closed=true);
// stroke(ellipse, width=.5, closed=true);
// color("blue")stroke(aligned,width=.5,closed=true);
function align_polygon(reference, poly, angles, cp, trans, return_ind=false) =
assert(is_undef(trans) || (is_undef(angles) && is_undef(cp)), "Cannot give both angles/cp and trans as input")
let(
trans = is_def(trans) ? trans :
assert( (is_vector(angles) && len(angles)>0) || valid_range(angles),
"The `angle` parameter must be a range or a non void list of numbers.")
let( // alignments is a vector of entries of the form: [polygon, error]
alignments = [
for(angle=angles)
reindex_polygon(
reference,
zrot(angle,p=poly,cp=cp),
return_error=true
[for(angle=angles) zrot(angle,cp=cp)]
)
],
best = min_index(subindex(alignments,1))
) alignments[best][0];
function align_polygon(reference, poly, angles, cp) =
assert(is_path(reference,dim=2) && is_path(poly,dim=2),
"Invalid polygon(s). " )
assert(len(reference)==len(poly), "The polygons must have the same length.")
assert( (is_vector(angles) && len(angles)>0) || valid_range(angles),
"The `angle` parameter must be a range or a non void list of numbers.")
let( // alignments is a vector of entries of the form: [polygon, error]
alignments = [
for(angle=angles)
for(T=trans)
reindex_polygon(
reference,
zrot(angle,p=poly,cp=cp),
apply(T,poly),
return_error=true
)
],
scores = subindex(alignments,1),
minscore = min(scores),
minind = [for(i=idx(scores)) if (scores[i]<minscore+EPSILON) i],
f=echo(best_angles = select(list(angles), minind)),
dummy = is_def(angles) ? echo(best_angles = select(list(angles), minind)):0,
best = minind[0]
) alignments[best][0];
)
return_ind ? (is_def(angles) ? list(angles)[best] : best)
: alignments[best][0];
// Function: are_polygons_equal()

View file

@ -287,9 +287,9 @@ function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) =
// 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
// eps - the tolerance in finding duplicates when cleanup=true. Default: EPSILON
// vnfs = a list of the VNFs to merge in one VNF.
// cleanup = when true, consolidates the duplicate vertices of the merge. Default: false
// eps = the tolerance in finding duplicates when cleanup=true. Default: EPSILON
function vnf_merge(vnfs, cleanup=false, eps=EPSILON) =
is_vnf(vnfs) ? vnf_merge([vnfs], cleanup, eps) :
assert( is_vnf_list(vnfs) , "Improper vnf or vnf list")