mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-01 09:49:45 +00:00
Merge pull request #133 from adrianVmariano/master
Added snap pins to joiners.scad
This commit is contained in:
commit
0571425ce7
10 changed files with 408 additions and 60 deletions
35
arrays.scad
35
arrays.scad
|
@ -153,9 +153,9 @@ function list_decreasing(list) =
|
|||
// Section: Basic List Generation
|
||||
|
||||
|
||||
// Function: replist()
|
||||
// Function: repeat()
|
||||
// Usage:
|
||||
// replist(val, n)
|
||||
// repeat(val, n)
|
||||
// Description:
|
||||
// Generates a list or array of `n` copies of the given `list`.
|
||||
// If the count `n` is given as a list of counts, then this creates a
|
||||
|
@ -164,14 +164,14 @@ function list_decreasing(list) =
|
|||
// val = The value to repeat to make the list or array.
|
||||
// n = The number of copies to make of `val`.
|
||||
// Example:
|
||||
// replist(1, 4); // Returns [1,1,1,1]
|
||||
// replist(8, [2,3]); // Returns [[8,8,8], [8,8,8]]
|
||||
// replist(0, [2,2,3]); // Returns [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]
|
||||
// replist([1,2,3],3); // Returns [[1,2,3], [1,2,3], [1,2,3]]
|
||||
function replist(val, n, i=0) =
|
||||
// repeat(1, 4); // Returns [1,1,1,1]
|
||||
// repeat(8, [2,3]); // Returns [[8,8,8], [8,8,8]]
|
||||
// repeat(0, [2,2,3]); // Returns [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]
|
||||
// repeat([1,2,3],3); // Returns [[1,2,3], [1,2,3], [1,2,3]]
|
||||
function repeat(val, n, i=0) =
|
||||
is_num(n)? [for(j=[1:1:n]) val] :
|
||||
(i>=len(n))? val :
|
||||
[for (j=[1:1:n[i]]) replist(val, n, i+1)];
|
||||
[for (j=[1:1:n[i]]) repeat(val, n, i+1)];
|
||||
|
||||
|
||||
// Function: list_range()
|
||||
|
@ -308,11 +308,11 @@ function repeat_entries(list, N, exact = true) =
|
|||
length = len(list),
|
||||
reps_guess = is_list(N)?
|
||||
assert(len(N)==len(list), "Vector parameter N to repeat_entries has the wrong length")
|
||||
N : replist(N/length,length),
|
||||
N : repeat(N/length,length),
|
||||
reps = exact? _sum_preserving_round(reps_guess) :
|
||||
[for (val=reps_guess) round(val)]
|
||||
)
|
||||
[for(i=[0:length-1]) each replist(list[i],reps[i])];
|
||||
[for(i=[0:length-1]) each repeat(list[i],reps[i])];
|
||||
|
||||
|
||||
// Function: list_set()
|
||||
|
@ -357,7 +357,7 @@ function list_set(list=[],indices,values,dflt=0,minlen=0) =
|
|||
)
|
||||
],
|
||||
slice(list,1+lastind, len(list)),
|
||||
replist(dflt, minlen-lastind-1)
|
||||
repeat(dflt, minlen-lastind-1)
|
||||
);
|
||||
|
||||
|
||||
|
@ -487,7 +487,7 @@ function list_bset(indexset, valuelist, dflt=0) =
|
|||
trueind = search([true], indexset,0)[0]
|
||||
) concat(
|
||||
list_set([],trueind, valuelist, dflt=dflt), // Fill in all of the values
|
||||
replist(dflt,len(indexset)-max(trueind)-1) // Add trailing values so length matches indexset
|
||||
repeat(dflt,len(indexset)-max(trueind)-1) // Add trailing values so length matches indexset
|
||||
);
|
||||
|
||||
|
||||
|
@ -523,7 +523,7 @@ function list_longest(vecs) =
|
|||
// fill = The value to pad the list with.
|
||||
function list_pad(v, minlen, fill=undef) =
|
||||
assert(is_list(v)||is_string(list))
|
||||
concat(v,replist(fill,minlen-len(v)));
|
||||
concat(v,repeat(fill,minlen-len(v)));
|
||||
|
||||
|
||||
// Function: list_trim()
|
||||
|
@ -851,6 +851,15 @@ function enumerate(l,idx=undef) =
|
|||
[for (i=[0:1:len(l)-1]) concat([i], [for (j=idx) l[i][j]])];
|
||||
|
||||
|
||||
// Function: force_list()
|
||||
// Usage:
|
||||
// list = force_list(value)
|
||||
// Description:
|
||||
// If value is a list returns value, otherwise returns [value]. Makes it easy to
|
||||
// treat a scalar input consistently as a singleton list along with list inputs.
|
||||
function force_list(value) = is_list(value) ? value : [value];
|
||||
|
||||
|
||||
// Function: pair()
|
||||
// Usage:
|
||||
// pair(v)
|
||||
|
|
26
beziers.scad
26
beziers.scad
|
@ -9,8 +9,8 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
include <BOSL2/vnf.scad>
|
||||
|
||||
include <vnf.scad>
|
||||
include <skin.scad>
|
||||
|
||||
// Section: Terminology
|
||||
// **Polyline**: A series of points joined by straight line segements.
|
||||
|
@ -318,6 +318,28 @@ function bezier_polyline(bezier, splinesteps=16, N=3) = let(
|
|||
);
|
||||
|
||||
|
||||
// Function: path_to_bezier()
|
||||
// Usage:
|
||||
// path_to_bezier(path,[tangent],[closed]);
|
||||
// Description:
|
||||
// Given an input path and optional path of tangent vectors, computes a cubic (degree 3) bezier path that passes
|
||||
// through every point on the input path and matches the tangent vectors. If you do not supply
|
||||
// the tangent it will be computed using path_tangents. If the path is closed specify this
|
||||
// by setting closed=true.
|
||||
// Arguments:
|
||||
// path = path of points to define the bezier
|
||||
// tangents = optional list of tangent vectors at every point
|
||||
// closed = set to true for a closed path. Default: false
|
||||
function path_to_bezier(path, tangents, closed=false) =
|
||||
assert(is_path(path,dim=undef),"Input path is not a valid path")
|
||||
assert(is_undef(tangents) || is_path(tangents,dim=len(path[0])),"Tangents must be a path of the same dimension as the input path")
|
||||
let(
|
||||
tangents = is_def(tangents)? tangents : deriv(path, closed=closed),
|
||||
lastpt = len(path) - (closed?0:1)
|
||||
)
|
||||
[for(i=[0:lastpt-1]) each [path[i], path[i]+tangents[i]/3, select(path,i+1)-select(tangents,i+1)/3],
|
||||
select(path,lastpt)];
|
||||
|
||||
|
||||
// Function: fillet_path()
|
||||
// Usage:
|
||||
|
|
12
coords.scad
12
coords.scad
|
@ -31,7 +31,7 @@ function point2d(p, fill=0) = [for (i=[0:1]) (p[i]==undef)? fill : p[i]];
|
|||
// fill = Value to fill missing values in vectors with.
|
||||
function path2d(points) =
|
||||
assert(is_path(points,dim=undef,fast=true),"Input to path2d is not a path")
|
||||
let (result = points * concat(ident(2), replist([0,0], len(points[0])-2)))
|
||||
let (result = points * concat(ident(2), repeat([0,0], len(points[0])-2)))
|
||||
assert(is_def(result), "Invalid input to path2d")
|
||||
result;
|
||||
|
||||
|
@ -58,11 +58,11 @@ function path3d(points, fill=0) =
|
|||
let (
|
||||
change = len(points[0])-3,
|
||||
M = change < 0? [[1,0,0],[0,1,0]] :
|
||||
concat(ident(3), replist([0,0,0],change)),
|
||||
concat(ident(3), repeat([0,0,0],change)),
|
||||
result = points*M
|
||||
)
|
||||
assert(is_def(result), "Input to path3d is invalid")
|
||||
fill == 0 || change>=0 ? result : result + replist([0,0,fill], len(result));
|
||||
fill == 0 || change>=0 ? result : result + repeat([0,0,fill], len(result));
|
||||
|
||||
|
||||
// Function: point4d()
|
||||
|
@ -87,17 +87,17 @@ function path4d(points, fill=0) =
|
|||
let (
|
||||
change = len(points[0])-4,
|
||||
M = change < 0 ? select(ident(4), 0, len(points[0])-1) :
|
||||
concat(ident(4), replist([0,0,0,0],change)),
|
||||
concat(ident(4), repeat([0,0,0,0],change)),
|
||||
result = points*M
|
||||
)
|
||||
assert(is_def(result), "Input to path4d is invalid")
|
||||
fill == 0 || change >= 0 ? result :
|
||||
let(
|
||||
addition = is_list(fill) ? concat(0*points[0],fill) :
|
||||
concat(0*points[0],replist(fill,-change))
|
||||
concat(0*points[0],repeat(fill,-change))
|
||||
)
|
||||
assert(len(addition) == 4, "Fill is the wrong length")
|
||||
result + replist(addition, len(result));
|
||||
result + repeat(addition, len(result));
|
||||
|
||||
|
||||
// Function: translate_points()
|
||||
|
|
216
joiners.scad
216
joiners.scad
|
@ -565,4 +565,220 @@ module dovetail(gender, length, l, width, w, height, h, angle, slope, taper, bac
|
|||
|
||||
|
||||
|
||||
// h is total height above 0 of the nub
|
||||
// nub extends below xy plane by distance nub/2
|
||||
module _pin_nub(r, nub, h)
|
||||
{
|
||||
L = h / 4;
|
||||
rotate_extrude(){
|
||||
polygon(
|
||||
[[ 0,-nub/2],
|
||||
[-r,-nub/2],
|
||||
[-r-nub, nub/2],
|
||||
[-r-nub, nub/2+L],
|
||||
[-r, h],
|
||||
[0, h]]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module _pin_slot(l, r, t, d, nub, depth, stretch) {
|
||||
yscale(4)
|
||||
intersection() {
|
||||
translate([t, 0, d + t / 4])
|
||||
_pin_nub(r = r + t, nub = nub, h = l - (d + t / 4));
|
||||
translate([-t, 0, d + t / 4])
|
||||
_pin_nub(r = r + t, nub = nub, h = l - (d + t / 4));
|
||||
}
|
||||
cube([2 * r, depth, 2 * l], center = true);
|
||||
up(l)
|
||||
zscale(stretch)
|
||||
ycyl(r = r, h = depth);
|
||||
}
|
||||
|
||||
|
||||
module _pin_shaft(r, lStraight, nub, nubscale, stretch, d, pointed)
|
||||
{
|
||||
extra = 0.02;
|
||||
rPoint = r / sqrt(2);
|
||||
down(extra) cylinder(r = r, h = lStraight + extra);
|
||||
up(lStraight) {
|
||||
zscale(stretch) {
|
||||
sphere(r = r);
|
||||
if (pointed) up(rPoint) cylinder(r1 = rPoint, r2 = 0, h = rPoint);
|
||||
}
|
||||
}
|
||||
up(d) yscale(nubscale) _pin_nub(r = r, nub = nub, h = lStraight - d);
|
||||
}
|
||||
|
||||
function _pin_size(size) =
|
||||
is_undef(size) ? [] :
|
||||
let(sizeok = in_list(size,["tiny", "small","medium", "large", "standard"]))
|
||||
assert(sizeok,"Pin size must be one of \"tiny\", \"small\", or \"standard\"")
|
||||
size=="standard" || size=="large" ?
|
||||
struct_set([], ["length", 10.8,
|
||||
"diameter", 7,
|
||||
"snap", 0.5,
|
||||
"nub_depth", 1.8,
|
||||
"thickness", 1.8,
|
||||
"preload", 0.2]):
|
||||
size=="medium" ?
|
||||
struct_set([], ["length", 8,
|
||||
"diameter", 4.6,
|
||||
"snap", 0.45,
|
||||
"nub_depth", 1.5,
|
||||
"thickness", 1.4,
|
||||
"preload", 0.2]) :
|
||||
size=="small" ?
|
||||
struct_set([], ["length", 6,
|
||||
"diameter", 3.2,
|
||||
"snap", 0.4,
|
||||
"nub_depth", 1.2,
|
||||
"thickness", 1.0,
|
||||
"preload", 0.16]) :
|
||||
size=="tiny" ?
|
||||
struct_set([], ["length", 4,
|
||||
"diameter", 2.5,
|
||||
"snap", 0.25,
|
||||
"nub_depth", 0.9,
|
||||
"thickness", 0.8,
|
||||
"preload", 0.1]):
|
||||
undef;
|
||||
|
||||
|
||||
// Module: snap_pin()
|
||||
// Usage:
|
||||
// snap_pin(size, [pointed], [anchor], [spin], [orient])
|
||||
// snap_pin(r|radius|d|diameter, l|length, nub_depth, snap, thickness, [clearance], [preload], [pointed], [anchor], [spin], [orient])
|
||||
// Description:
|
||||
// Creates a snap pin that can be inserted into an appropriate socket to connect two objects together. You can choose from some standard
|
||||
// pin dimensions by giving a size, or you can specify all the pin geometry parameters yourself. If you use a standard size you can
|
||||
// override the standard parameters by specifying other ones. The pins have flat sides so they can
|
||||
// be printed. When oriented UP the shaft of the pin runs in the Z direction and the flat sides are the front and back. The default
|
||||
// orientation (FRONT) and anchor (FRONT) places the pin in a printable configuration, flat side down on the xy plane.
|
||||
// The tightness of fit is determined by `preload` and `clearance`. To make pins tighter increase `preload` and/or decrease `clearance`.
|
||||
//
|
||||
// The "large" or "standard" size pin has a length of 10.8 and diameter of 7. The "medium" pin has a length of 8 and diameter of 4.6. The "small" pin
|
||||
// has a length of 6 and diameter of 3.2. The "tiny" pin has a length of 4 and a diameter of 2.5.
|
||||
//
|
||||
// This pin is based on https://www.thingiverse.com/thing:213310 by Emmett Lalishe
|
||||
// and a modified version at https://www.thingiverse.com/thing:3218332 by acwest
|
||||
// and distributed under the Creative Commons - Attribution - Share Alike License
|
||||
// Arguments:
|
||||
// size = text string to select from a list of predefined sizes, one of "standard", "small", or "tiny".
|
||||
// pointed = set to true to get a pointed pin, false to get one with a rounded end. Default: true
|
||||
// r|radius = radius of the pin
|
||||
// d|diameter = diameter of the pin
|
||||
// l|length = length of the pin
|
||||
// nub_depth = the distance of the nub from the base of the pin
|
||||
// snap = how much snap the pin provides (the nub projection)
|
||||
// thickness = thickness of the pin walls
|
||||
// pointed = if true the pin is pointed, otherwise it has a rounded tip. Default: true
|
||||
// clearance = how far to shrink the pin away from the socket walls. Default: 0.2
|
||||
// preload = amount to move the nub towards the pin base, which can create tension from the misalignment with the socket. Default: 0.2
|
||||
// Example: Pin in native orientation
|
||||
// snap_pin("standard", anchor=CENTER, orient=UP, thickness = 1, $fn=40);
|
||||
// Example: Pins oriented for printing
|
||||
// xspread(spacing=10, n=4) snap_pin("standard", $fn=40);
|
||||
module snap_pin(size,r,radius,d,diameter, l,length, nub_depth, snap, thickness, clearance=0.2, preload, pointed=true, anchor=FRONT, spin=0, orient=FRONT, center) {
|
||||
preload_default = 0.2;
|
||||
sizedat = _pin_size(size);
|
||||
radius = get_radius(r1=r,r2=radius,d1=d,d2=diameter,dflt=struct_val(sizedat,"diameter")/2);
|
||||
length = first_defined([l,length,struct_val(sizedat,"length")]);
|
||||
snap = first_defined([snap, struct_val(sizedat,"snap")]);
|
||||
thickness = first_defined([thickness, struct_val(sizedat,"thickness")]);
|
||||
nub_depth = first_defined([nub_depth, struct_val(sizedat,"nub_depth")]);
|
||||
preload = first_defined([first_defined([preload, struct_val(sizedat, "preload")]),preload_default]);
|
||||
|
||||
nubscale = 0.9; // Mysterious arbitrary parameter
|
||||
|
||||
// The basic pin assumes a rounded cap of length sqrt(2)*r, which defines lStraight.
|
||||
// If the point is enabled the cap length is instead 2*r
|
||||
// preload shrinks the length, bringing the nubs closer together
|
||||
|
||||
rInner = radius - clearance;
|
||||
stretch = sqrt(2)*radius/rInner; // extra stretch factor to make cap have proper length even though r is reduced.
|
||||
lStraight = length - sqrt(2) * radius - clearance;
|
||||
lPin = lStraight + (pointed ? 2*radius : sqrt(2)*radius);
|
||||
attachable(anchor=anchor,spin=spin, orient=orient,
|
||||
size=[nubscale*(2*rInner+2*snap + clearance),radius*sqrt(2)-2*clearance,2*lPin]){
|
||||
zflip_copy()
|
||||
difference() {
|
||||
intersection() {
|
||||
cube([3 * (radius + snap), radius * sqrt(2) - 2 * clearance, 2 * length + 3 * radius], center = true);
|
||||
_pin_shaft(rInner, lStraight, snap+clearance/2, nubscale, stretch, nub_depth-preload, pointed);
|
||||
}
|
||||
_pin_slot(l = lStraight, r = rInner - thickness, t = thickness, d = nub_depth - preload, nub = snap, depth = 2 * radius + 0.02, stretch = stretch);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
// Module: snap_pin_socket()
|
||||
// Usage:
|
||||
// snap_pin_socket(size, [fixed], [fins], [pointed], [anchor], [spin], [orient]);
|
||||
// snap_pin_socket(r|radius|d|diameter, l|length, nub_depth, snap, [fixed], [pointed], [fins], [anchor], [spin], [orient])
|
||||
// Description:
|
||||
// Constructs a socket suitable for a snap_pin with the same parameters. If `fixed` is true then the socket has flat walls and the
|
||||
// pin will not rotate in the socket. If `fixed` is false then the socket is round and the pin will rotate, particularly well
|
||||
// if you add a lubricant. If `pointed` is true the socket is pointed to receive a pointed pin, otherwise it has a rounded and and
|
||||
// will be shorter. If `fins` is set to true then two fins are included inside the socket to act as supports (which may help when printing tip up,
|
||||
// especially when `pointed=false`). The default orientation is DOWN with anchor BOTTOM so that you can difference() the socket away from an object.
|
||||
//
|
||||
// The "large" or "standard" size pin has a length of 10.8 and diameter of 7. The "medium" pin has a length of 8 and diameter of 4.6. The "small" pin
|
||||
// has a length of 6 and diameter of 3.2. The "tiny" pin has a length of 4 and a diameter of 2.5.
|
||||
// Arguments:
|
||||
// size = text string to select from a list of predefined sizes, one of "standard", "small", or "tiny".
|
||||
// pointed = set to true to get a pointed pin, false to get one with a rounded end. Default: true
|
||||
// r|radius = radius of the pin
|
||||
// d|diameter = diameter of the pin
|
||||
// l|length = length of the pin
|
||||
// nub_depth = the distance of the nub from the base of the pin
|
||||
// snap = how much snap the pin provides (the nub projection)
|
||||
// fixed = if true the pin cannot rotate, if false it can. Default: true
|
||||
// pointed = if true the socket has a pointed tip. Default: true
|
||||
// fins = if true supporting fins are included. Default: false
|
||||
// Example: The socket shape itself in native orientation.
|
||||
// snap_pin_socket("standard", anchor=CENTER, orient=UP, fins=true, $fn=40);
|
||||
// Example: A spinning socket with fins:
|
||||
// snap_pin_socket("standard", anchor=CENTER, orient=UP, fins=true, fixed=false, $fn=40);
|
||||
// Example: A cube with a socket in the middle and one half-way off the front edge so you can see inside:
|
||||
// $fn=40;
|
||||
// diff("socket") cuboid([20,20,20]) {
|
||||
// attach(TOP) snap_pin_socket("standard", $tags="socket");
|
||||
// position(TOP+FRONT)snap_pin_socket("standard", $tags="socket");
|
||||
// }
|
||||
module snap_pin_socket(size, r, radius, l,length, d,diameter,nub_depth, snap, fixed=true, pointed=true, fins=false, anchor=BOTTOM, spin=0, orient=DOWN) {
|
||||
sizedat = _pin_size(size);
|
||||
radius = get_radius(r1=r,r2=radius,d1=d,d2=diameter,dflt=struct_val(sizedat,"diameter")/2);
|
||||
length = first_defined([l,length,struct_val(sizedat,"length")]);
|
||||
snap = first_defined([snap, struct_val(sizedat,"snap")]);
|
||||
nub_depth = first_defined([nub_depth, struct_val(sizedat,"nub_depth")]);
|
||||
|
||||
tip = pointed ? sqrt(2) * radius : radius;
|
||||
lPin = length + (pointed?(2-sqrt(2))*radius:0);
|
||||
lStraight = lPin - (pointed?sqrt(2)*radius:radius);
|
||||
attachable(anchor=anchor,spin=spin,orient=orient,
|
||||
size=[2*(radius+snap),radius*sqrt(2),lPin])
|
||||
{
|
||||
down(lPin/2)
|
||||
intersection() {
|
||||
if (fixed)
|
||||
cube([3 * (radius + snap), radius * sqrt(2), 3 * lPin + 3 * radius], center = true);
|
||||
union() {
|
||||
_pin_shaft(radius,lStraight,snap,1,1,nub_depth,pointed);
|
||||
if (fins)
|
||||
up(lStraight){
|
||||
cube([2 * radius, 0.01, 2 * tip], center = true);
|
||||
cube([0.01, 2 * radius, 2 * tip], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -412,7 +412,7 @@ function _lcmlist(a) =
|
|||
function lcm(a,b=[]) =
|
||||
!is_list(a) && !is_list(b) ? _lcm(a,b) :
|
||||
let(
|
||||
arglist = concat((is_list(a)?a:[a]), (is_list(b)?b:[b]))
|
||||
arglist = concat(force_list(a),force_list(b))
|
||||
)
|
||||
assert(len(arglist)>0,"invalid call to lcm with empty list(s)")
|
||||
_lcmlist(arglist);
|
||||
|
@ -590,7 +590,7 @@ function _qr_factor(A,Q, column, m, n) =
|
|||
let(
|
||||
x = [for(i=[column:1:m-1]) A[i][column]],
|
||||
alpha = (x[0]<=0 ? 1 : -1) * norm(x),
|
||||
u = x - concat([alpha],replist(0,m-1)),
|
||||
u = x - concat([alpha],repeat(0,m-1)),
|
||||
v = u / norm(u),
|
||||
Qc = ident(len(x)) - 2*transpose([v])*[v],
|
||||
Qf = [for(i=[0:m-1]) [for(j=[0:m-1]) i<column || j<column ? (i==j ? 1 : 0) : Qc[i-column][j-column]]]
|
||||
|
|
11
paths.scad
11
paths.scad
|
@ -46,8 +46,8 @@ function is_path(list, dim=[2,3], fast=false) =
|
|||
fast? is_list(list) && is_vector(list[0],fast=true) :
|
||||
is_list(list) && is_list(list[0]) && len(list)>1 &&
|
||||
let( d = len(list[0]) )
|
||||
(is_undef(dim) || in_list(d, is_list(dim)?dim:[dim]) ) &&
|
||||
is_list_of(list, replist(0,d));
|
||||
(is_undef(dim) || in_list(d, force_list(dim))) &&
|
||||
is_list_of(list, repeat(0,d));
|
||||
|
||||
|
||||
// Function: is_closed_path()
|
||||
|
@ -287,7 +287,7 @@ function path_closest_point(path, pt) =
|
|||
// The returns vectors will be normalized to length 1.
|
||||
function path_tangents(path, closed=false) =
|
||||
assert(is_path(path))
|
||||
[for(t=deriv(path)) unit(t)];
|
||||
[for(t=deriv(path,closed=closed)) unit(t)];
|
||||
|
||||
|
||||
// Function: path_normals()
|
||||
|
@ -862,6 +862,7 @@ module path_extrude(path, convexity=10, clipsize=100) {
|
|||
// polyline = [for (a=[0:30:210]) 10*[cos(a), sin(a), sin(a)]];
|
||||
// trace_polyline(polyline, showpts=true, size=0.5, color="lightgreen");
|
||||
module trace_polyline(pline, closed=false, showpts=false, N=1, size=1, color="yellow") {
|
||||
assert(is_path(pline),"Input pline is not a path");
|
||||
sides = segs(size/2);
|
||||
pline = closed? close_path(pline) : pline;
|
||||
if (showpts) {
|
||||
|
@ -1125,7 +1126,7 @@ function _path_cut(path, dists, closed=false, pind=0, dtotal=0, dind=0, result=[
|
|||
[lerp(lastpt,path[pind], (dists[dind]-dtotal)/dpartial),pind] :
|
||||
_path_cut_single(path, dists[dind]-dtotal-dpartial, closed, pind)
|
||||
) is_undef(nextpoint)?
|
||||
concat(result, replist(undef,len(dists)-dind)) :
|
||||
concat(result, repeat(undef,len(dists)-dind)) :
|
||||
_path_cut(path, dists, closed, nextpoint[1], dists[dind],dind+1, concat(result, [nextpoint]));
|
||||
|
||||
// Search for a single cut point in the path
|
||||
|
@ -1259,7 +1260,7 @@ function subdivide_path(path, N, closed=true, exact=true, method="length") =
|
|||
is_list(N)? (
|
||||
assert(len(N)==count,"Vector parameter N to subdivide_path has the wrong length")
|
||||
add_scalar(N,-1)
|
||||
) : replist((N-len(path)) / count, count)
|
||||
) : repeat((N-len(path)) / count, count)
|
||||
) : // method=="length"
|
||||
assert(is_num(N),"Parameter N to subdivide path must be a number when method=\"length\"")
|
||||
let(
|
||||
|
|
|
@ -499,7 +499,7 @@ function offset(
|
|||
d = flip_dir * (is_def(r) ? r : delta),
|
||||
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 = check_valid ? _good_segments(path, abs(d), shiftsegs, closed, quality) : replist(true,len(shiftsegs)),
|
||||
good = check_valid ? _good_segments(path, abs(d), shiftsegs, closed, quality) : repeat(true,len(shiftsegs)),
|
||||
goodsegs = bselect(shiftsegs, good),
|
||||
goodpath = bselect(path,good)
|
||||
)
|
||||
|
@ -569,7 +569,7 @@ function offset(
|
|||
)
|
||||
],
|
||||
pointcount = (is_def(delta) && !chamfer)?
|
||||
replist(1,len(sharpcorners)) :
|
||||
repeat(1,len(sharpcorners)) :
|
||||
[for(i=[0:len(goodsegs)-1]) len(newcorners[i])],
|
||||
start = [goodsegs[0][0]],
|
||||
end = [goodsegs[len(goodsegs)-2][1]],
|
||||
|
|
|
@ -161,7 +161,7 @@ include <BOSL2/skin.scad>
|
|||
// $fs=.25;
|
||||
// $fa=1;
|
||||
// zigzagx = [-10, 0, 10, 20, 29, 38, 46, 52, 59, 66, 72, 78, 83, 88, 92, 96, 99, 102, 112];
|
||||
// zigzagy = concat([0], flatten(replist([-10,10],8)), [-10,0]);
|
||||
// zigzagy = concat([0], flatten(repeat([-10,10],8)), [-10,0]);
|
||||
// zig = zip(zigzagx,zigzagy);
|
||||
// stroke(zig,width=1); // Original shape
|
||||
// fwd(20) // Smooth size corners with a cut of 4 and curvature parameter 0.6
|
||||
|
@ -194,7 +194,7 @@ include <BOSL2/skin.scad>
|
|||
// Example(FlatSpin): Rounding a spiral with increased rounding along the length
|
||||
// // Construct a square spiral path in 3D
|
||||
// square = [[0,0],[1,0],[1,1],[0,1]];
|
||||
// spiral = flatten(replist(concat(square,reverse(square)),5)); // Squares repeat 10 times, forward and backward
|
||||
// spiral = flatten(repeat(concat(square,reverse(square)),5)); // Squares repeat 10 times, forward and backward
|
||||
// squareind = [for(i=[0:9]) each [i,i,i,i]]; // Index of the square for each point
|
||||
// z = list_range(40)*.2+squareind;
|
||||
// path3d = zip(spiral,z); // 3D spiral
|
||||
|
@ -246,7 +246,7 @@ function round_corners(path, curve="circle", measure="cut", size=undef, k=0.5,
|
|||
dim = pathdim - 1 + have_size,
|
||||
points = have_size ? path : subindex(path, [0:dim-1]),
|
||||
parm = have_size && is_list(size) && len(size)>2? size :
|
||||
have_size? replist(size, len(path)) :
|
||||
have_size? repeat(size, len(path)) :
|
||||
subindex(path, dim),
|
||||
// dk will be a list of parameters, for the "smooth" curve the distance and curvature parameter pair,
|
||||
// and for the "circle" curve, distance and radius.
|
||||
|
@ -407,6 +407,41 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
|
||||
|
||||
|
||||
// Function: smooth_path()
|
||||
// Usage:
|
||||
// smooth_path(path, [tangents], [splinesteps], [closed]
|
||||
// Description:
|
||||
// Smooths the input path using a cubic spline. Every segment of the path will be replaced by a cubic curve
|
||||
// with `splinesteps` points. The cubic interpolation will pass through every input point on the path
|
||||
// and will match the tangents at every point. If you do not specify tangents they will be computed using
|
||||
// deriv(). Note that the magnitude of the tangents affects the result. See also path_to_bezier().
|
||||
// Arguments:
|
||||
// path = path to smooth
|
||||
// tangents = tangent vectors of the path
|
||||
// splinesteps = number of points to insert between the path points. Default: 10
|
||||
// closed = set to true for a closed path. Default: false
|
||||
// Example(2D): Original path in green, smoothed path in yellow:
|
||||
// color("green")stroke(square(4), width=0.1);
|
||||
// stroke(smooth_path(square(4)), width=0.1);
|
||||
// Example(2D): Closing the path changes the end tangents
|
||||
// polygon(smooth_path(square(4), closed=true));
|
||||
// Example(2D): A more interesting shape:
|
||||
// path = [[0,0], [4,0], [7,14], [-3,12]];
|
||||
// polygon(smooth_path(path,closed=true));
|
||||
// Example(2D): Scaling the tangent data can decrease or increase the amount of smoothing:
|
||||
// shape = square(4);
|
||||
// polygon(smooth_path(shape, tangents=0.5*deriv(shape, closed=true),closed=true));
|
||||
// Example(2D): Or you can specify your own tangent values to alter the shape of the curve
|
||||
// polygon(smooth_path(square(4),tangents=1.25*[[-2,-1], [-2,1], [1,2], [2,-1]],closed=true));
|
||||
// Example(FlatSpin): Works on 3d paths as well
|
||||
// path = [[0,0,0],[3,3,2],[6,0,1],[9,9,0]];
|
||||
// trace_polyline(smooth_path(path),size=.3);
|
||||
function smooth_path(path, tangents, splinesteps=10, closed=false) =
|
||||
let(
|
||||
bez = path_to_bezier(path, tangents=tangents, closed=closed)
|
||||
)
|
||||
bezier_polyline(bez,splinesteps=splinesteps);
|
||||
|
||||
|
||||
// Module: offset_sweep()
|
||||
//
|
||||
|
@ -497,11 +532,11 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
//
|
||||
// Example: Rounding a star shaped prism with postive radius values
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// rounded_star = round_corners(zip(star, flatten(repeat([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// offset_sweep(rounded_star, height=20, bottom=os_circle(r=4), top=os_circle(r=1), steps=15);
|
||||
// Example: Rounding a star shaped prism with negative radius values
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// rounded_star = round_corners(zip(star, flatten(repeat([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// offset_sweep(rounded_star, height=20, bottom=os_circle(r=-4), top=os_circle(r=-1), steps=15);
|
||||
// Example: Unexpected corners in the result even with `offset="round"` (the default), even with offset_maxstep set small.
|
||||
// triangle = [[0,0],[10,0],[5,10]];
|
||||
|
@ -514,7 +549,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
// offset_sweep(triangle, height=6, bottom = os_circle(r=-2),steps=16,offset_maxstep=0.01);
|
||||
// Example: Here is the star chamfered at the top with a teardrop rounding at the bottom. Check out the rounded corners on the chamfer. Note that a very small value of `offset_maxstep` is needed to keep these round. Observe how the rounded star points vanish at the bottom in the teardrop: the number of vertices does not remain constant from layer to layer.
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// rounded_star = round_corners(zip(star, flatten(repeat([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// offset_sweep(rounded_star, height=20, bottom=os_teardrop(r=4), top=os_chamfer(width=4,offset_maxstep=.1));
|
||||
// Example: We round a cube using the continous curvature rounding profile. But note that the corners are not smooth because the curved square collapses into a square with corners. When a collapse like this occurs, we cannot turn `check_valid` off.
|
||||
// square = [[0,0],[1,0],[1,1],[0,1]];
|
||||
|
@ -565,7 +600,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
// }
|
||||
// Example: Star shaped box
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// rounded_star = round_corners(zip(star, flatten(repeat([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// thickness = 2;
|
||||
// ht=20;
|
||||
// difference(){
|
||||
|
@ -577,12 +612,12 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
// }
|
||||
// Example: A profile defined by an arbitrary sequence of points.
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// rounded_star = round_corners(zip(star, flatten(repeat([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// profile = os_profile(points=[[0,0],[.3,.1],[.6,.3],[.9,.9], [1.2, 2.7],[.8,2.7],[.8,3]]);
|
||||
// offset_sweep(reverse(rounded_star), height=20, top=profile, bottom=profile);
|
||||
// Example: Parabolic rounding
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// rounded_star = round_corners(zip(star, flatten(repeat([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
// offset_sweep(rounded_star, height=20, top=os_profile(points=[for(r=[0:.1:2])[sqr(r),r]]),
|
||||
// bottom=os_profile(points=[for(r=[0:.2:5])[-sqrt(r),r]]));
|
||||
// Example: This example uses a sine wave offset profile. Note that because the offsets occur sequentially and the path grows incrementally the offset needs a very fine resolution to produce the proper result. Note that we give no specification for the bottom, so it is straight.
|
||||
|
@ -642,7 +677,7 @@ module offset_sweep(
|
|||
offsetind+1, vertexcount+len(path),
|
||||
vertices=concat(
|
||||
vertices,
|
||||
zip(vertices_faces[0],replist(offsets[offsetind][1],len(vertices_faces[0])))
|
||||
zip(vertices_faces[0],repeat(offsets[offsetind][1],len(vertices_faces[0])))
|
||||
),
|
||||
faces=concat(faces, vertices_faces[1])
|
||||
)
|
||||
|
@ -711,7 +746,7 @@ module offset_sweep(
|
|||
);
|
||||
|
||||
top_start_ind = len(vertices_faces_bot[0]);
|
||||
initial_vertices_top = zip(path, replist(middle,len(path)));
|
||||
initial_vertices_top = zip(path, repeat(middle,len(path)));
|
||||
vertices_faces_top = make_polyhedron(
|
||||
path, translate_points(offsets_top,[0,middle]),
|
||||
struct_val(top,"offset"), !clockwise,
|
||||
|
|
|
@ -406,7 +406,7 @@ function _normal_segment(p1,p2) =
|
|||
// path = turtle(["move","left",360/5,"addlength",1],repeat=50);
|
||||
// stroke(path,width=.2);
|
||||
// Example(2DMed): yet another spiral, without using `repeat`
|
||||
// path = turtle(concat(["angle",71],flatten(replist(["move","left","addlength",1],50))));
|
||||
// path = turtle(concat(["angle",71],flatten(repeat(["move","left","addlength",1],50))));
|
||||
// stroke(path,width=.2);
|
||||
// Example(2DMed): The previous spiral grows linearly and eventually intersects itself. This one grows geometrically and does not.
|
||||
// path = turtle(["move","left",71,"scale",1.05],repeat=50);
|
||||
|
|
101
skin.scad
101
skin.scad
|
@ -211,7 +211,7 @@ include <vnf.scad>
|
|||
// skin( shapes, slices=0);
|
||||
// Example: You can fix it by specifying "tangent" for the first method, but you still need "direct" for the rest.
|
||||
// shapes = [for(i=[0:.2:1]) path3d(regular_ngon(n=4, side=4, rounding=i, $fn=32),i*5)];
|
||||
// skin( shapes, slices=0, method=concat(["tangent"],replist("direct",len(shapes)-2)));
|
||||
// skin( shapes, slices=0, method=concat(["tangent"],repeat("direct",len(shapes)-2)));
|
||||
// Example(FlatSpin): Connecting square to pentagon using "direct" method.
|
||||
// skin([regular_ngon(n=4, r=4), regular_ngon(n=5,r=5)], z=[0,4], refine=10, slices=10);
|
||||
// Example(FlatSpin): Connecting square to shifted pentagon using "direct" method.
|
||||
|
@ -324,6 +324,7 @@ module skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=
|
|||
|
||||
|
||||
function skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z) =
|
||||
assert(is_def(slices),"The slices argument must be specified.")
|
||||
assert(is_list(profiles) && len(profiles)>1, "Must provide at least two profiles")
|
||||
let( bad = [for(i=idx(profiles)) if (!(is_path(profiles[i]) && len(profiles[i])>2)) i])
|
||||
assert(len(bad)==0, str("Profiles ",bad," are not a paths or have length less than 3"))
|
||||
|
@ -334,14 +335,14 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
|
|||
closed ? false : true,
|
||||
capsOK = is_bool(caps) || (is_list(caps) && len(caps)==2 && is_bool(caps[0]) && is_bool(caps[1])),
|
||||
fullcaps = is_bool(caps) ? [caps,caps] : caps,
|
||||
refine = is_list(refine) ? refine : replist(refine, len(profiles)),
|
||||
slices = is_list(slices) ? slices : replist(slices, profcount),
|
||||
refine = is_list(refine) ? refine : repeat(refine, len(profiles)),
|
||||
slices = is_list(slices) ? slices : repeat(slices, profcount),
|
||||
refineOK = [for(i=idx(refine)) if (refine[i]<=0 || !is_integer(refine[i])) i],
|
||||
slicesOK = [for(i=idx(slices)) if (!is_integer(slices[i]) || slices[i]<0) i],
|
||||
maxsize = list_longest(profiles),
|
||||
methodok = is_list(method) || in_list(method, legal_methods),
|
||||
methodlistok = is_list(method) ? [for(i=idx(method)) if (!in_list(method[i], legal_methods)) i] : [],
|
||||
method = is_string(method) ? replist(method, profcount) : method,
|
||||
method = is_string(method) ? repeat(method, profcount) : method,
|
||||
// Define to be zero where a resampling method is used and 1 where a vertex duplicator is used
|
||||
RESAMPLING = 0,
|
||||
DUPLICATOR = 1,
|
||||
|
@ -381,7 +382,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
|
|||
method_type[i] * method_type[i-1])],
|
||||
parts = search(1,[1,for(i=[0:1:len(profile_resampled)-2]) profile_resampled[i]!=profile_resampled[i+1] ? 1 : 0],0),
|
||||
plen = [for(i=idx(parts)) (i== len(parts)-1? len(refined_len) : parts[i+1]) - parts[i]],
|
||||
max_list = [for(i=idx(parts)) each replist(max(select(refined_len, parts[i], parts[i]+plen[i]-1)), plen[i])],
|
||||
max_list = [for(i=idx(parts)) each repeat(max(select(refined_len, parts[i], parts[i]+plen[i]-1)), plen[i])],
|
||||
transition_profiles = [for(i=[(closed?0:1):1:profcount-1]) if (select(method_type,i-1) != method_type[i]) i],
|
||||
badind = [for(tranprof=transition_profiles) if (refined_len[tranprof] != max_list[tranprof]) tranprof]
|
||||
)
|
||||
|
@ -514,7 +515,7 @@ function slice_profiles(profiles,slices,closed=false) =
|
|||
let(listok = !is_list(slices) || len(slices)==len(profiles)-(closed?0:1))
|
||||
assert(listok, "Input slices to slice_profiles is a list with the wrong length")
|
||||
let(
|
||||
count = is_num(slices) ? replist(slices,len(profiles)-(closed?0:1)) : slices,
|
||||
count = is_num(slices) ? repeat(slices,len(profiles)-(closed?0:1)) : slices,
|
||||
slicelist = [for (i=[0:len(profiles)-(closed?1:2)])
|
||||
each [for(j = [0:count[i]]) lerp(profiles[i],select(profiles,i+1),j/(count[i]+1))]
|
||||
]
|
||||
|
@ -590,7 +591,7 @@ function _dp_distance_array(small, big, abort_thresh=1/0) =
|
|||
|
||||
function _dp_distance_row(small, big, small_ind, tdist) =
|
||||
// Top left corner is zero because it gets counted at the end in bottom right corner
|
||||
small_ind == 0 ? [cumsum([0,for(i=[1:len(big)]) norm(big[i%len(big)]-small[0])]), replist(_MAP_LEFT,len(big)+1)] :
|
||||
small_ind == 0 ? [cumsum([0,for(i=[1:len(big)]) norm(big[i%len(big)]-small[0])]), repeat(_MAP_LEFT,len(big)+1)] :
|
||||
[for(big_ind=1,
|
||||
newrow=[ norm(big[0] - small[small_ind%len(small)]) + tdist[small_ind-1][0] ],
|
||||
newmap = [_MAP_UP]
|
||||
|
@ -715,6 +716,71 @@ function _find_one_tangent(curve, edge, curve_offset=[0,0,0], closed=true) =
|
|||
zero_cross[min_index(d)];
|
||||
|
||||
|
||||
// Function: associate_vertices()
|
||||
// Usage:
|
||||
// associate_vertices(polygons, split)
|
||||
// Description:
|
||||
// Takes as input a list of polygons and duplicates specified vertices in each polygon in the list through the series so
|
||||
// that the input can be passed to `skin()`. This allows you to decide how the vertices are linked up rather than accepting
|
||||
// the automatically computed minimal distance linkage. However, the number of vertices in the polygons must not decrease in the list.
|
||||
// The output is a list of polygons that all have the same number of vertices with some duplicates. You specify the vertix splitting
|
||||
// using the `split` which is a list where each entry corresponds to a polygon: split[i] is a value or list specfying which vertices in polygon i to split.
|
||||
// Give the empty list if you don't want a split for a particular polygon. If you list a vertex once then it will be split and mapped to
|
||||
// two vertices in the next polygon. If you list it N times then N copies will be created to map to N+1 vertices in the next polygon.
|
||||
// You must ensure that each mapping produces the correct number of vertices to exactly map onto every vertex of the next polygon.
|
||||
// Note that if you split (only) vertex i of a polygon that means it will map to vertices i and i+1 of the next polygon. Vertex 0 will always
|
||||
// map to vertex 0 and the last vertices will always map to each other, so if you want something different than that you'll need to reindex
|
||||
// your polygons.
|
||||
// Arguments:
|
||||
// polygons = list of polygons to split
|
||||
// split = list of lists of split vertices
|
||||
// Example(FlatSpin): If you skin together a square and hexagon using the optimal distance method you get two triangular faces on opposite sides:
|
||||
// sq = regular_ngon(4,side=2);
|
||||
// hex = apply(rot(15),hexagon(side=2));
|
||||
// skin([sq,hex], slices=10, refine=10, method="distance", z=[0,4]);
|
||||
// Example(FlatSpin): Using associate_vertices you can change the location of the triangular faces. Here they are connect to two adjacent vertices of the square:
|
||||
// sq = regular_ngon(4,side=2);
|
||||
// hex = apply(rot(15),hexagon(side=2));
|
||||
// skin(associate_vertices([sq,hex],[[1,2]]), slices=10, refine=10, sampling="segment", z=[0,4]);
|
||||
// Example(FlatSpin): Here the two triangular faces connect to a single vertex on the square. Note that we had to rotate the hexagon to line them up because the vertices match counting forward, so in this case vertex 0 of the square matches to vertices 0, 1, and 2 of the hexagon.
|
||||
// sq = regular_ngon(4,side=2);
|
||||
// hex = apply(rot(60),hexagon(side=2));
|
||||
// skin(associate_vertices([sq,hex],[[0,0]]), slices=10, refine=10, sampling="segment", z=[0,4]);
|
||||
// Example: This example shows several polygons, with only a single vertex split at each step:
|
||||
// sq = regular_ngon(4,side=2);
|
||||
// pent = pentagon(side=2);
|
||||
// hex = hexagon(side=2);
|
||||
// sep = regular_ngon(7,side=2);
|
||||
// skin(associate_vertices([sq,pent,hex,sep], [1,3,4]) ,slices=10, refine=10, method="distance", z=[0,2,4,6]);
|
||||
// Example: The polygons cannot shrink, so if you want to have decreasing polygons you'll need to concatenate multiple results. Note that it is perfectly ok to duplicate a profile as shown here, where the pentagon is duplicated:
|
||||
// sq = regular_ngon(4,side=2);
|
||||
// pent = pentagon(side=2);
|
||||
// grow = associate_vertices([sq,pent],[1]);
|
||||
// shrink = associate_vertices([sq,pent],[2]);
|
||||
// skin(concat(grow, reverse(shrink)), slices=10, refine=10, method="distance", z=[0,2,2,4]);
|
||||
function associate_vertices(polygons, split, curpoly=0) =
|
||||
curpoly==len(polygons)-1 ? polygons :
|
||||
let(
|
||||
polylen = len(polygons[curpoly]),
|
||||
cursplit = force_list(split[curpoly]),
|
||||
fdsa= echo(cursplit=cursplit)
|
||||
)
|
||||
assert(len(split)==len(polygons)-1,str(split,"Split list length mismatch: it has length ", len(split)," but must have length ",len(polygons)-1))
|
||||
assert(polylen<=len(polygons[curpoly+1]),str("Polygon ",curpoly," has more vertices than the next one."))
|
||||
assert(len(cursplit)+polylen == len(polygons[curpoly+1]),
|
||||
str("Polygon ", curpoly, " has ", polylen, " vertices. Next polygon has ", len(polygons[curpoly+1]),
|
||||
" vertices. Split list has length ", len(cursplit), " but must have length ", len(polygons[curpoly+1])-polylen))
|
||||
assert(max(cursplit)<polylen && min(curpoly)>=0,
|
||||
str("Split ",cursplit," at polygon ",curpoly," has invalid vertices. Must be in [0:",polylen-1,"]"))
|
||||
len(cursplit)==0 ? associate_vertices(polygons,split,curpoly+1) :
|
||||
let(
|
||||
splitindex = sort(concat(list_range(polylen), cursplit)),
|
||||
newpoly = [for(i=[0:len(polygons)-1]) i<=curpoly ? select(polygons[i],splitindex) : polygons[i]]
|
||||
)
|
||||
associate_vertices(newpoly, split, curpoly+1);
|
||||
|
||||
|
||||
|
||||
// Function&Module: sweep()
|
||||
// Usage: sweep(shape, transformations, [closed], [caps])
|
||||
// Description:
|
||||
|
@ -759,18 +825,16 @@ function _find_one_tangent(curve, edge, curve_offset=[0,0,0], closed=true) =
|
|||
// sweep(shape, concat(outside,inside));
|
||||
|
||||
function sweep(shape, transformations, closed=false, caps) =
|
||||
assert(is_list_of(transformations, ident(4)), "Input transformations must be a list of numeric 4x4 matrices in sweep")
|
||||
assert(is_path(shape,2), "Input shape must be a 2d path")
|
||||
let(
|
||||
tdim = array_dim(transformations),
|
||||
shapedim = array_dim(shape),
|
||||
caps = is_def(caps) ? caps :
|
||||
closed ? false : true,
|
||||
capsOK = is_bool(caps) || (is_list(caps) && len(caps)==2 && is_bool(caps[0]) && is_bool(caps[1])),
|
||||
fullcaps = is_bool(caps) ? [caps,caps] : caps
|
||||
)
|
||||
assert(len(tdim)==3 && tdim[1]==4 && tdim[2]==4, "transformations must be a list of 4x4 matrices in sweep")
|
||||
assert(tdim[0]>1, "transformation must be length 2 or more")
|
||||
assert(len(shapedim)==2 && shapedim[0]>2, "shape must be a path of at least 3 points")
|
||||
assert(shapedim[1]==2, "shape must be a path in 2-dimensions")
|
||||
assert(len(transformations), "transformation must be length 2 or more")
|
||||
assert(len(shape)>=3, "shape must be a path of at least 3 points")
|
||||
assert(capsOK, "caps must be boolean or a list of two booleans")
|
||||
assert(!closed || !caps, "Cannot make closed shape with caps")
|
||||
_skin_core([for(i=[0:len(transformations)-(closed?0:1)]) apply(transformations[i%len(transformations)],path3d(shape))],caps=fullcaps);
|
||||
|
@ -783,7 +847,7 @@ module sweep(shape, transformations, closed=false, caps, convexity=10) {
|
|||
// Function&Module: path_sweep()
|
||||
// Usage: path_sweep(shape, path, [method], [normal], [closed], [twist], [twist_by_length], [symmetry], [last_normal], [tangent], [relaxed], [caps], [convexity], [transforms])
|
||||
// Description:
|
||||
// Takes as input a 2d shape (specified as a point list) and a 3d path and constructs a polyhedron by sweeping the shape along the path.
|
||||
// Takes as input a 2d shape (specified as a point list) and a 2d or 3d path and constructs a polyhedron by sweeping the shape along the path.
|
||||
// When run as a module returns the polyhedron geometry. When run as a function returns a VNF by default or if you set `transforms=true` then
|
||||
// it returns a list of transformations suitable as input to `sweep`.
|
||||
//
|
||||
|
@ -858,7 +922,7 @@ module sweep(shape, transformations, closed=false, caps, convexity=10) {
|
|||
// Example: Sweep along a clockwise elliptical arc, using "natural" method, which lines up the X axis of the shape with the direction of curvature. This means the X axis will point inward, so a counterclockwise arc gives:
|
||||
// ushape = [[-10, 0],[-10, 10],[ -7, 10],[ -7, 2],[ 7, 2],[ 7, 7],[ 10, 7],[ 10, 0]];
|
||||
// elliptic_arc = xscale(2, p=arc($fn=64,angle=[0,180], r=30)); // Counter-clockwise
|
||||
// path_sweep(ushape, path3d(elliptic_arc), method="natural");
|
||||
// path_sweep(ushape, elliptic_arc, method="natural");
|
||||
// Example: Sweep along a clockwise elliptical arc, using "natural" method. If the curve is clockwise than the shape flips upside-down to align the X axis.
|
||||
// ushape = [[-10, 0],[-10, 10],[ -7, 10],[ -7, 2],[ 7, 2],[ 7, 7],[ 10, 7],[ 10, 0]];
|
||||
// elliptic_arc = xscale(2, p=arc($fn=64,angle=[180,0], r=30)); // Clockwise
|
||||
|
@ -1069,9 +1133,10 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
|
|||
assert(!closed || twist % (360/symmetry)==0, str("For a closed sweep, twist must be a multiple of 360/symmetry = ",360/symmetry))
|
||||
assert(closed || symmetry==1, "symmetry must be 1 when closed is false")
|
||||
assert(is_integer(symmetry) && symmetry>0, "symmetry must be a positive integer")
|
||||
assert(is_path(shape) && len(shape[0])==2, "shape must be a 2d path")
|
||||
assert(is_path(path) && len(path[0])==3, "path must be a 3d path")
|
||||
assert(is_path(shape,2), "shape must be a 2d path")
|
||||
assert(is_path(path), "input path is not a path")
|
||||
let(
|
||||
path = path3d(path),
|
||||
caps = is_def(caps) ? caps :
|
||||
closed ? false : true,
|
||||
capsOK = is_bool(caps) || (is_list(caps) && len(caps)==2 && is_bool(caps[0]) && is_bool(caps[1])),
|
||||
|
@ -1086,7 +1151,7 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
|
|||
normal = is_path(normal) ? [for(n=normal) unit(n)] :
|
||||
is_def(normal) ? unit(normal) :
|
||||
method =="incremental" && abs(tangents[0].z) > 1/sqrt(2) ? BACK : UP,
|
||||
normals = is_path(normal) ? normal : replist(normal,len(path)),
|
||||
normals = is_path(normal) ? normal : repeat(normal,len(path)),
|
||||
pathfrac = twist_by_length ? path_length_fractions(path, closed) : [for(i=[0:1:len(path)]) i / (len(path)-(closed?0:1))],
|
||||
L = len(path),
|
||||
transform_list =
|
||||
|
|
Loading…
Reference in a new issue