mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Added skin.scad
This commit is contained in:
parent
a947ec2476
commit
785befb1c1
3 changed files with 163 additions and 1 deletions
|
@ -17,7 +17,7 @@ done
|
|||
if [[ "$FILES" != "" ]]; then
|
||||
PREVIEW_LIBS="$FILES"
|
||||
else
|
||||
PREVIEW_LIBS="common errors attachments math arrays vectors affine coords geometry triangulation quaternions strings stacks queues structs vnf hull constants edges transforms primitives shapes masks shapes2d paths beziers rounding walls cubetruss metric_screws threading partitions involute_gears sliders joiners linear_bearings nema_steppers wiring phillips_drive torx_drive polyhedra knurling cubetruss debug"
|
||||
PREVIEW_LIBS="common errors attachments math arrays vectors affine coords geometry triangulation quaternions strings stacks queues structs vnf skin hull constants edges transforms primitives shapes masks shapes2d paths beziers rounding walls cubetruss metric_screws threading partitions involute_gears sliders joiners linear_bearings nema_steppers wiring phillips_drive torx_drive polyhedra knurling cubetruss debug"
|
||||
fi
|
||||
|
||||
dir="$(basename $PWD)"
|
||||
|
|
159
skin.scad
Normal file
159
skin.scad
Normal file
|
@ -0,0 +1,159 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: skin.scad
|
||||
// Functions to skin arbitrary 2D profiles/paths in 3-space.
|
||||
// To use, add the following line to the beginning of your file:
|
||||
// ```
|
||||
// include <BOSL2/std.scad>
|
||||
// include <BOSL2/skin.scad>
|
||||
// ```
|
||||
// Derived from list-comprehension-demos skin():
|
||||
// - https://github.com/openscad/list-comprehension-demos/blob/master/skin.scad
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
include <vnf.scad>
|
||||
|
||||
|
||||
// Section: Skinning
|
||||
|
||||
|
||||
// Function&Module: skin()
|
||||
// Usage: As Module
|
||||
// skin(profiles, [closed], [matching]);
|
||||
// Usage: As Function
|
||||
// vnf = skin(profiles, [closed], [caps], [matching]);
|
||||
// Description
|
||||
// Given a list of two or more 2D path `profiles` that have been moved and/or rotated into 3D-space,
|
||||
// produces faces to skin a surface between consecutive profiles. Optionally, the first and last
|
||||
// profiles can have endcaps, or the last and first profiles can be skinned together.
|
||||
// The user is responsible for making sure the orientation of the first vertex of each profile are relatively aligned.
|
||||
// If called as a function, returns a VNF structure like `[VERTICES, FACES]`. See [VNF](vnf.scad).
|
||||
// If called as a module, creates a polyhedron of the skinned profiles.
|
||||
// The vertex matching algorithms are as follows:
|
||||
// - `"distance"`: Vertices between profiles are matched based on closest next position, relative to the center of each profile.
|
||||
// - `"angle"`: Vertices between profiles are matched based on closest next polar angle, relative to the center of each profile.
|
||||
// - `"evenly"`: Vertices are evenly matched between profiles, such that a point 30% of the way through one profile, will be matched to a vertex 30% of the way through the other profile, based on vertex count.
|
||||
// Arguments:
|
||||
// profiles = A list of 2D paths that have been moved and/or rotated into 3D-space.
|
||||
// closed = If true, the last profile is skinned to the first profile, to allow for making a closed loop. Assumes `caps=false`. Default: false
|
||||
// caps = If true, endcap faces are created. Assumes `closed=false`. Default: true
|
||||
// matching = Specifies the algorithm used to match up vertices between profiles, to create faces. Given as a string, one of `"distance"`, `"angle"`, or `"evenly"`. If given as a list of strings, equal in number to the number of profile transitions, lets you specify the algorithm used for each transition. Default: "distance"
|
||||
// Example(FlatSpin):
|
||||
// skin([
|
||||
// move([0,0, 0], p=scale([2,1,1], p=path3d(circle(d=100,$fn=48)))),
|
||||
// move([0,0,100], p=path3d(circle(d=100,$fn=4))),
|
||||
// move([0,0,200], p=path3d(circle(d=100,$fn=12))),
|
||||
// ]);
|
||||
// Example(FlatSpin):
|
||||
// skin([
|
||||
// for (ang = [0:10:90])
|
||||
// rot([0,ang,0], cp=[200,0,0], p=path3d(circle(d=100,$fn=3+(ang/10))))
|
||||
// ]);
|
||||
// Example(FlatSpin): Möbius Strip
|
||||
// skin([
|
||||
// for (ang = [0:10:360])
|
||||
// rot([0,ang,0], cp=[100,0,0], p=rot(ang/2, p=path3d(square([1,30],center=true))))
|
||||
// ], caps=false);
|
||||
// Example(FlatSpin): Closed Loop
|
||||
// skin([
|
||||
// for (i = [0:5])
|
||||
// rot([0,i*60,0], cp=[100,0,0], p=path3d(circle(d=30,$fn=3+i%3)))
|
||||
// ], closed=true, caps=false);
|
||||
// Example: Distance Matching
|
||||
// skin([
|
||||
// move([0,0, 0], p=scale([1,2,1],p=path3d(circle(d=50,$fn=36)))),
|
||||
// move([0,0,100], p=scale([2,1,1],p=path3d(circle(d=50,$fn=36))))
|
||||
// ], matching="distance");
|
||||
// Example: Angle Matching
|
||||
// skin([
|
||||
// move([0,0, 0], p=scale([1,2,1],p=path3d(circle(d=50,$fn=36)))),
|
||||
// move([0,0,100], p=scale([2,1,1],p=path3d(circle(d=50,$fn=36))))
|
||||
// ], matching="angle");
|
||||
// Example: Evenly Matching
|
||||
// skin([
|
||||
// move([0,0, 0], p=scale([1,2,1],p=path3d(circle(d=50,$fn=36)))),
|
||||
// move([0,0,100], p=scale([2,1,1],p=path3d(circle(d=50,$fn=36))))
|
||||
// ], matching="evenly");
|
||||
module skin(profiles, closed=false, caps=true, matching="distance") {
|
||||
vnf_polyhedron(skin(profiles, caps=!closed, closed=closed, matching=matching));
|
||||
}
|
||||
|
||||
|
||||
function skin(profiles, closed=false, caps=true, matching="distance") =
|
||||
assert(is_list(profiles))
|
||||
assert(is_bool(closed))
|
||||
assert(is_bool(caps))
|
||||
assert(!closed||!caps)
|
||||
assert(is_string(matching)||is_list(matching))
|
||||
let( matching = is_list(matching)? matching : [for (pidx=idx(profiles,end=closed?-1:-2)) matching] )
|
||||
assert(len(matching) == len(profiles)-closed?0:1)
|
||||
vnf_triangulate(
|
||||
concat([
|
||||
for(pidx=idx(profiles,end=closed? -1 : -2))
|
||||
let(
|
||||
prof1 = profiles[pidx%len(profiles)],
|
||||
prof2 = profiles[(pidx+1)%len(profiles)],
|
||||
cp1 = mean(prof1),
|
||||
cp2 = mean(prof2),
|
||||
midpt = (cp1+cp2)/2,
|
||||
n = normalize(cp2-cp1),
|
||||
n1 = plane_normal(plane_from_pointslist(prof1)),
|
||||
n2 = plane_normal(plane_from_pointslist(prof2)),
|
||||
perp = vector_angle(n1,n2)>0.01? vector_axis(n1,n2) : vector_angle(n,UP)>44? vector_axis(n,UP) : vector_axis(n,LEFT),
|
||||
perp1 = vector_axis(n1,perp),
|
||||
perp2 = vector_axis(n2,perp),
|
||||
poly1 = ccw_polygon(project_plane(prof1, cp1, cp1+perp, cp1+perp1)),
|
||||
poly2 = ccw_polygon(project_plane(prof2, cp2, cp2+perp, cp2+perp2)),
|
||||
match = matching[pidx],
|
||||
faces = [
|
||||
for(
|
||||
first = true,
|
||||
finishing = false,
|
||||
finished = false,
|
||||
plen1 = len(poly1),
|
||||
plen2 = len(poly2),
|
||||
i=0, j=0, side=0;
|
||||
|
||||
!finished;
|
||||
|
||||
dang1 = abs(xy_to_polar(poly1[i%plen1]).y - xy_to_polar(poly2[(j+1)%plen2]).y),
|
||||
dang2 = abs(xy_to_polar(poly2[j%plen2]).y - xy_to_polar(poly1[(i+1)%plen1]).y),
|
||||
dist1 = norm(poly1[i%plen1] - poly2[(j+1)%plen2]),
|
||||
dist2 = norm(poly2[j%plen2] - poly1[(i+1)%plen1]),
|
||||
side = i>=plen1? 0 :
|
||||
j>=plen2? 1 :
|
||||
match=="angle"? (dang1>dang2? 1 : 0) :
|
||||
match=="distance"? (dist1>dist2? 1 : 0) :
|
||||
match=="evenly"? (i/plen1 > j/plen2? 0 : 1) :
|
||||
assert(in_list(matching[i],["angle","distance","evenly"]),str("Got `",matching,"'")),
|
||||
p1 = lift_plane(poly1[i%plen1], cp1, cp1+perp, cp1+perp1),
|
||||
p2 = lift_plane(poly2[j%plen2], cp2, cp2+perp, cp2+perp2),
|
||||
p3 = side?
|
||||
lift_plane(poly1[(i+1)%plen1], cp1, cp1+perp, cp1+perp1) :
|
||||
lift_plane(poly2[(j+1)%plen2], cp2, cp2+perp, cp2+perp2),
|
||||
face = [p1, p3, p2],
|
||||
i = i + (side? 1 : 0),
|
||||
j = j + (side? 0 : 1),
|
||||
first = false,
|
||||
finished = finishing,
|
||||
finishing = i>=plen1 && j>=plen2
|
||||
) if (!first) face
|
||||
]
|
||||
) vnf_add_faces(faces=faces)
|
||||
], closed||!caps? [] : let(
|
||||
prof1 = profiles[0],
|
||||
prof2 = select(profiles,-1),
|
||||
ncl1 = sort(find_noncollinear_points(prof1)),
|
||||
ncl2 = sort(find_noncollinear_points(prof2)),
|
||||
pa1=prof1[ncl1.x], pa2=prof1[ncl1.y], pa3=prof1[ncl1.z],
|
||||
pb1=prof2[ncl2.x], pb2=prof2[ncl2.y], pb3=prof2[ncl2.z],
|
||||
poly1 = ccw_polygon(project_plane(prof1, pa1, pa2, pa3)),
|
||||
poly2 = clockwise_polygon(project_plane(prof2, pb1, pb2, pb3))
|
||||
) [
|
||||
vnf_add_face(pts=lift_plane(poly1, pa1, pa2, pa3)),
|
||||
vnf_add_face(pts=lift_plane(poly2, pb1, pb2, pb3))
|
||||
])
|
||||
);
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
3
vnf.scad
3
vnf.scad
|
@ -9,6 +9,9 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
include <triangulation.scad>
|
||||
|
||||
|
||||
// Section: Creating Polyhedrons with VNF Structures
|
||||
// VNF stands for "Vertices'N'Faces". VNF structures are 2-item lists, `[VERTICES,FACES]` where the
|
||||
// first item is a list of vertex points, and the second is a list of face indices into the vertex
|
||||
|
|
Loading…
Reference in a new issue