mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-01 09:49:45 +00:00
Merge branch 'master' of https://github.com/revarbat/BOSL2
This commit is contained in:
commit
0cfa56d6a7
15 changed files with 245 additions and 192 deletions
3
.nodocsfiles
Normal file
3
.nodocsfiles
Normal file
|
@ -0,0 +1,3 @@
|
|||
bosl1compat.scad
|
||||
std.scad
|
||||
version.scad
|
|
@ -12,8 +12,8 @@ A library for OpenSCAD, filled with useful tools, shapes, masks, math and manipu
|
|||
## Installation
|
||||
|
||||
1. Download the .zip or .tar.gz release file for this library.
|
||||
2. Unpack it. It should create a `BOSL-v2.0` directory with the library files within it.
|
||||
3. Rename the directory to `BOSL2`.
|
||||
2. Unpack it. Make sure that you unpack the whole file structure. Some zipfile unpackers call this option "Use folder names". It should create either a `BOSL-v2.0` or `BOSL2-master` directory with the library files within it. You should see "examples", "scripts", "tests", and other subdirectories.
|
||||
3. Rename the unpacked main directory to `BOSL2`.
|
||||
4. Move the `BOSL2` directory into the apropriate OpenSCAD library directory for your platform:
|
||||
- Windows: `My Documents\OpenSCAD\libraries\`
|
||||
- Linux: `$HOME/.local/share/OpenSCAD/libraries/`
|
||||
|
|
11
arrays.scad
11
arrays.scad
|
@ -101,6 +101,15 @@ function select(list, start, end=undef) =
|
|||
// last(l); // Returns 9.
|
||||
function last(list) = list[len(list)-1];
|
||||
|
||||
// Function: delete_last()
|
||||
// Description:
|
||||
// Returns a list of all but the last entry. If input is empty, returns empty list.
|
||||
// Usage:
|
||||
// delete_last(list)
|
||||
function delete_last(list) =
|
||||
assert(is_list(list))
|
||||
list==[] ? [] : slice(list,0,-2);
|
||||
|
||||
// Function: slice()
|
||||
// Description:
|
||||
// Returns a slice of a list. The first item is index 0.
|
||||
|
@ -281,7 +290,7 @@ function list_range(n=undef, s=0, e=undef, step=undef) =
|
|||
// Example:
|
||||
// reverse([3,4,5,6]); // Returns [6,5,4,3]
|
||||
function reverse(x) =
|
||||
assert(is_list(x)||is_string(x))
|
||||
assert(is_list(x)||is_string(x), "Input to reverse must be a list or string")
|
||||
let (elems = [ for (i = [len(x)-1 : -1 : 0]) x[i] ])
|
||||
is_string(x)? str_join(elems) : elems;
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ function get_anchor(anchor,center,uncentered=BOT,dflt=CENTER) =
|
|||
// r = Most general radius.
|
||||
// d = Most general diameter.
|
||||
// dflt = Value to return if all other values given are `undef`.
|
||||
function get_radius(r1=undef, r2=undef, r=undef, d1=undef, d2=undef, d=undef, dflt=undef) =
|
||||
function get_radius(r1, r2, r, d1, d2, d, dflt) =
|
||||
assert(num_defined([r1,d1,r2,d2])<2, "Conflicting or redundant radius/diameter arguments given.")
|
||||
!is_undef(r1) ? assert(is_finite(r1), "Invalid radius r1." ) r1
|
||||
: !is_undef(r2) ? assert(is_finite(r2), "Invalid radius r2." ) r2
|
||||
|
|
|
@ -1678,12 +1678,21 @@ function furthest_point(pt, points) =
|
|||
function polygon_area(poly, signed=false) =
|
||||
assert(is_path(poly), "Invalid polygon." )
|
||||
len(poly)<3 ? 0 :
|
||||
let( cpoly = close_path(simplify_path(poly)) )
|
||||
len(poly[0])==2
|
||||
? sum([for(i=[1:1:len(poly)-2]) cross(poly[i]-poly[0],poly[i+1]-poly[0]) ])/2
|
||||
: let( plane = plane_from_points(poly) )
|
||||
plane==undef? undef :
|
||||
let( n = unit(plane_normal(plane)),
|
||||
total = sum([for(i=[1:1:len(poly)-1]) cross(poly[i]-poly[0],poly[i+1]-poly[0])*n ])/2
|
||||
let(
|
||||
n = unit(plane_normal(plane)),
|
||||
total = sum([
|
||||
for(i=[1:1:len(cpoly)-2])
|
||||
let(
|
||||
v1 = cpoly[i] - cpoly[0],
|
||||
v2 = cpoly[i+1] - cpoly[0]
|
||||
)
|
||||
cross(v1,v2) * n
|
||||
])/2
|
||||
)
|
||||
signed ? total : abs(total);
|
||||
|
||||
|
@ -2082,9 +2091,8 @@ function _split_polygon_at_z(poly, z) =
|
|||
// polys = A list of 3D polygons to split.
|
||||
// xs = A list of scalar X values to split at.
|
||||
function split_polygons_at_each_x(polys, xs, _i=0) =
|
||||
assert( is_consistent(polys) && is_path(poly[0],dim=3) ,
|
||||
"The input list should contains only 3D polygons." )
|
||||
assert( is_finite(xs), "The split value list should contain only numbers." )
|
||||
assert( [for (poly=polys) if (!is_path(poly,3)) 1] == [], "Expects list of 3D paths.")
|
||||
assert( is_vector(xs), "The split value list should contain only numbers." )
|
||||
_i>=len(xs)? polys :
|
||||
split_polygons_at_each_x(
|
||||
[
|
||||
|
@ -2103,9 +2111,8 @@ function split_polygons_at_each_x(polys, xs, _i=0) =
|
|||
// polys = A list of 3D polygons to split.
|
||||
// ys = A list of scalar Y values to split at.
|
||||
function split_polygons_at_each_y(polys, ys, _i=0) =
|
||||
// assert( is_consistent(polys) && is_path(polys[0],dim=3) , // not all polygons should have the same length!!!
|
||||
// "The input list should contains only 3D polygons." )
|
||||
assert( is_finite(ys) || is_vector(ys), "The split value list should contain only numbers." ) //***
|
||||
assert( [for (poly=polys) if (!is_path(poly,3)) 1] == [], "Expects list of 3D paths.")
|
||||
assert( is_vector(ys), "The split value list should contain only numbers." )
|
||||
_i>=len(ys)? polys :
|
||||
split_polygons_at_each_y(
|
||||
[
|
||||
|
@ -2124,9 +2131,8 @@ function split_polygons_at_each_y(polys, ys, _i=0) =
|
|||
// polys = A list of 3D polygons to split.
|
||||
// zs = A list of scalar Z values to split at.
|
||||
function split_polygons_at_each_z(polys, zs, _i=0) =
|
||||
assert( is_consistent(polys) && is_path(poly[0],dim=3) ,
|
||||
"The input list should contains only 3D polygons." )
|
||||
assert( is_finite(zs), "The split value list should contain only numbers." )
|
||||
assert( [for (poly=polys) if (!is_path(poly,3)) 1] == [], "Expects list of 3D paths.")
|
||||
assert( is_vector(zs), "The split value list should contain only numbers." )
|
||||
_i>=len(zs)? polys :
|
||||
split_polygons_at_each_z(
|
||||
[
|
||||
|
|
|
@ -349,11 +349,16 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
chamf_angle = struct_val(edgespec, "angle"),
|
||||
cheight = struct_val(edgespec, "chamfer_height"),
|
||||
cwidth = struct_val(edgespec, "chamfer_width"),
|
||||
chamf_width = first_defined([cut/cos(chamf_angle), cwidth, cheight*tan(chamf_angle)]),
|
||||
chamf_height = first_defined([cut/sin(chamf_angle),cheight, cwidth/tan(chamf_angle)]),
|
||||
chamf_width = first_defined([!all_defined([cut,chamf_angle]) ? undef : cut/cos(chamf_angle),
|
||||
cwidth,
|
||||
!all_defined([cheight,chamf_angle]) ? undef : cheight*tan(chamf_angle)]),
|
||||
chamf_height = first_defined([
|
||||
!all_defined([cut,chamf_angle]) ? undef : cut/sin(chamf_angle),
|
||||
cheight,
|
||||
!all_defined([cwidth, chamf_angle]) ? undef : cwidth/tan(chamf_angle)]),
|
||||
joint = first_defined([
|
||||
struct_val(edgespec,"joint"),
|
||||
16*cut/sqrt(2)/(1+4*k)
|
||||
all_defined([cut,k]) ? 16*cut/sqrt(2)/(1+4*k) : undef
|
||||
]),
|
||||
points = struct_val(edgespec, "points"),
|
||||
argsOK = in_list(edgetype,["circle","teardrop"])? is_def(radius) :
|
||||
|
@ -365,7 +370,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
assert(argsOK,str("Invalid specification with type ",edgetype))
|
||||
let(
|
||||
offsets =
|
||||
edgetype == "profile"? scale([-1,z_dir], slice(points,1,-1)) :
|
||||
edgetype == "profile"? scale([-1,z_dir], p=slice(points,1,-1)) :
|
||||
edgetype == "chamfer"? chamf_width==0 && chamf_height==0? [] : [[-chamf_width,z_dir*abs(chamf_height)]] :
|
||||
edgetype == "teardrop"? (
|
||||
radius==0? [] : concat(
|
||||
|
@ -380,6 +385,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
|||
1, -1
|
||||
)
|
||||
)
|
||||
|
||||
quant(extra > 0? concat(offsets, [select(offsets,-1)+[0,z_dir*extra]]) : offsets, 1/1024);
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ done
|
|||
if [[ "$FILES" != "" ]]; then
|
||||
PREVIEW_LIBS="$FILES"
|
||||
else
|
||||
PREVIEW_LIBS="affine arrays attachments beziers bottlecaps common constants coords cubetruss debug distributors edges geometry hingesnaps hull involute_gears joiners knurling linear_bearings masks math metric_screws mutators nema_steppers partitions paths phillips_drive polyhedra primitives quaternions queues regions rounding screws shapes shapes2d skin sliders stacks strings structs threading torx_drive transforms triangulation vectors version vnf walls wiring"
|
||||
PREVIEW_LIBS=$(git ls-files | grep '\.scad$' | grep -v / | grep -v -f .nodocsfiles)
|
||||
fi
|
||||
|
||||
dir="$(basename $PWD)"
|
||||
|
|
|
@ -18,8 +18,9 @@ for testscript in $INFILES ; do
|
|||
testfile="tests/test_$repname"
|
||||
if [ -f "$testfile" ] ; then
|
||||
${OPENSCAD} -o out.echo --hardwarnings --check-parameters true --check-parameter-ranges true $testfile 2>&1
|
||||
retcode=$?
|
||||
res=$(cat out.echo)
|
||||
if [ "$res" = "" ] ; then
|
||||
if [ $retcode -eq 0 ] && [ "$res" = "" ] ; then
|
||||
echo "$repname: PASS"
|
||||
else
|
||||
echo "$repname: FAIL!"
|
||||
|
|
|
@ -795,13 +795,13 @@ module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h
|
|||
// ycyl(l=35, d=20);
|
||||
// ycyl(l=35, d1=30, d2=10);
|
||||
// }
|
||||
module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, anchor=CENTER)
|
||||
module ycyl(l, r, d, r1, r2, d1, d2, h, anchor=CENTER)
|
||||
{
|
||||
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1);
|
||||
r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1);
|
||||
l = first_defined([l, h, 1]);
|
||||
attachable(anchor,0,UP, r1=r1, r2=r2, l=l, axis=BACK) {
|
||||
cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=BACK, anchor=CENTER);
|
||||
cyl(l=l, h=h, r1=r1, r2=r2, orient=BACK, anchor=CENTER);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -350,7 +350,7 @@ module stroke(
|
|||
// N = Number of vertices to form the arc curve from.
|
||||
// r = Radius of the arc.
|
||||
// d = Diameter of the arc.
|
||||
// angle = If a scalar, specifies the end angle in degrees. If a vector of two scalars, specifies start and end angles.
|
||||
// angle = If a scalar, specifies the end angle in degrees (relative to start parameter). If a vector of two scalars, specifies start and end angles.
|
||||
// cp = Centerpoint of arc.
|
||||
// points = Points on the arc.
|
||||
// long = if given with cp and points takes the long arc instead of the default short arc. Default: false
|
||||
|
@ -360,6 +360,7 @@ module stroke(
|
|||
// thickness = If given with `width`, arc starts and ends on X axis, to make a circle segment.
|
||||
// start = Start angle of arc.
|
||||
// wedge = If true, include centerpoint `cp` in output to form pie slice shape.
|
||||
// endpoint = If false exclude the last point (function only). Default: true
|
||||
// Examples(2D):
|
||||
// arc(N=4, r=30, angle=30, wedge=true);
|
||||
// arc(r=30, angle=30, wedge=true);
|
||||
|
@ -378,7 +379,10 @@ module stroke(
|
|||
// Example(FlatSpin):
|
||||
// path = arc(points=[[0,30,0],[0,0,30],[30,0,0]]);
|
||||
// trace_path(path, showpts=true, color="cyan");
|
||||
function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, long=false, cw=false, ccw=false) =
|
||||
function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, long=false, cw=false, ccw=false, endpoint=true) =
|
||||
assert(is_bool(endpoint))
|
||||
!endpoint ? assert(!wedge, "endpoint cannot be false if wedge is true")
|
||||
slice(arc(N,r,angle,d,cp,points,width,thickness,start,wedge,long,cw,ccw,true),0,-2) :
|
||||
// First try for 2D arc specified by width and thickness
|
||||
is_def(width) && is_def(thickness)? (
|
||||
assert(!any_defined([r,cp,points]) && !any([cw,ccw,long]),"Conflicting or invalid parameters to arc")
|
||||
|
@ -472,7 +476,7 @@ function _normal_segment(p1,p2) =
|
|||
|
||||
// Function: turtle()
|
||||
// Usage:
|
||||
// turtle(commands, [state], [full_state], [repeat])
|
||||
// turtle(commands, [state], [full_state], [repeat], [endpoint])
|
||||
// Description:
|
||||
// Use a sequence of turtle graphics commands to generate a path. The parameter `commands` is a list of
|
||||
// turtle commands and optional parameters for each command. The turtle state has a position, movement direction,
|
||||
|
|
|
@ -27,6 +27,21 @@ module test_select() {
|
|||
}
|
||||
test_select();
|
||||
|
||||
module test_last() {
|
||||
list = [1,2,3,4];
|
||||
assert(last(list)==4);
|
||||
assert(last([])==undef);
|
||||
}
|
||||
test_last();
|
||||
|
||||
module test_delete_last() {
|
||||
list = [1,2,3,4];
|
||||
assert(delete_last(list) == [1,2,3]);
|
||||
assert(delete_last([1]) == []);
|
||||
assert(delete_last([]) == []);
|
||||
}
|
||||
test_delete_last();
|
||||
|
||||
|
||||
module test_slice() {
|
||||
assert(slice([3,4,5,6,7,8,9], 3, 5) == [6,7]);
|
||||
|
|
|
@ -40,6 +40,7 @@ test_turtle();
|
|||
|
||||
module test_arc() {
|
||||
assert_approx(arc(N=8, d=100, angle=135, cp=[10,10]), [[60,10],[57.1941665154,26.5139530978],[49.0915741234,41.1744900929],[36.6016038258,52.3362099614],[21.1260466978,58.7463956091],[4.40177619483,59.6856104947],[-11.6941869559,55.0484433951],[-25.3553390593,45.3553390593]]);
|
||||
assert_approx(arc(N=8, d=100, angle=135, cp=[10,10],endpoint=false), [[60,10],[57.1941665154,26.5139530978],[49.0915741234,41.1744900929],[36.6016038258,52.3362099614],[21.1260466978,58.7463956091],[4.40177619483,59.6856104947],[-11.6941869559,55.0484433951]]);
|
||||
assert_approx(arc(N=8, d=100, angle=[45,225], cp=[10,10]), [[45.3553390593,45.3553390593],[26.5139530978,57.1941665154],[4.40177619483,59.6856104947],[-16.6016038258,52.3362099614],[-32.3362099614,36.6016038258],[-39.6856104947,15.5982238052],[-37.1941665154,-6.51395309776],[-25.3553390593,-25.3553390593]]);
|
||||
assert_approx(arc(N=8, d=100, start=45, angle=135, cp=[10,10]), [[45.3553390593,45.3553390593],[31.6941869559,55.0484433951],[15.5982238052,59.6856104947],[-1.12604669782,58.7463956091],[-16.6016038258,52.3362099614],[-29.0915741234,41.1744900929],[-37.1941665154,26.5139530978],[-40,10]]);
|
||||
assert_approx(arc(N=8, d=100, start=45, angle=-90, cp=[10,10]), [[45.3553390593,45.3553390593],[52.3362099614,36.6016038258],[57.1941665154,26.5139530978],[59.6856104947,15.5982238052],[59.6856104947,4.40177619483],[57.1941665154,-6.51395309776],[52.3362099614,-16.6016038258],[45.3553390593,-25.3553390593]]);
|
||||
|
|
|
@ -116,10 +116,13 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "arcrot" |x | radius, rotation | Draw an arc turning by the specified absolute rotation with given radius
|
||||
// "arctodir" |x | radius, vector | Draw an arc turning to point in the (absolute) direction of given vector
|
||||
// "arcsteps" |x | count | Specifies the number of segments to use for drawing arcs. If you set it to zero then the standard `$fn`, `$fa` and `$fs` variables define the number of segments.
|
||||
//
|
||||
// Compound commands are lists that group multiple commands to be applied simultaneously during a turtle movement. Example: `["move", 5, "shrink", 2]`. The subcommands that may appear are listed below. Each command command
|
||||
// must begin with either "move" or "arc". The order of subcommands is not important. Left/right turning is applied before up/down. You cannot combine "rot" or "todir" with any other turning commands.
|
||||
//
|
||||
// .
|
||||
// Compound commands are lists that group multiple commands to be applied simultaneously during a
|
||||
// turtle movement. Example: `["move", 5, "shrink", 2]`. The subcommands that may appear are
|
||||
// listed below. Each compound command must begin with either "move" or "arc". The order of
|
||||
// subcommands is not important. Left/right turning is applied before up/down. You cannot combine
|
||||
// "rot" or "todir" with any other turning commands.
|
||||
// .
|
||||
// Subcommands | Arguments | What it does
|
||||
// ------------ | ------------------ | -------------------------------
|
||||
// "move" | dist | Compound command is a forward movement operation
|
||||
|
@ -146,40 +149,39 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "down", "left" or "right" alone then you can give any angle, but if you combine "up"/"down" with "left"/"right" then the specified
|
||||
// angles must be smaller than 180 degrees. (This is because the algorithm decodes the rotation into an angle smaller than 180, so
|
||||
// the results are very strange if larger angles are permitted.)
|
||||
// .
|
||||
// Arguments:
|
||||
// commands = List of turtle3d commands
|
||||
// state = Starting turtle direction or full turtle state (from a previous call). Default: RIGHT
|
||||
// transforms = If true teturn list of transformations instead of points. Default: false
|
||||
// full_state = If true return full turtle state for continuing the path in subsequent turtle calls. Default: false
|
||||
// repeat = Number of times to repeat the command list. Default: 1
|
||||
// Example: Angled rectangle
|
||||
// Example(3D): Angled rectangle
|
||||
// path = turtle3d(["up",25,"move","left","move",3,"left","move"]);
|
||||
// stroke(path,closed=true, width=.2);
|
||||
// Example: Path with rounded corners. Note first and last point of the path are duplicates.
|
||||
// Example(3D): Path with rounded corners. Note first and last point of the path are duplicates.
|
||||
// r = 0.25;
|
||||
// path = turtle3d(["up",25,"move","arcleft",r,"move",3,"arcleft",r,"move","arcleft",r,"move",3,"arcleft",r]);
|
||||
// stroke(path,closed=true, width=.2);
|
||||
// Example: Non-coplanar figure
|
||||
// Example(3D): Non-coplanar figure
|
||||
// path = turtle3d(["up",25,"move","left","move",3,"up","left",0,"move"]);
|
||||
// stroke(path,closed=true, width=.2);
|
||||
// Example: Square spiral. Note that the core twists because the "up" and "left" turns are relative to the previous turns.
|
||||
// Example(3D): Square spiral. Note that the core twists because the "up" and "left" turns are relative to the previous turns.
|
||||
// include<BOSL2/skin.scad>
|
||||
// path = turtle3d(["move",10,"left","up",15],repeat=50);
|
||||
// path_sweep(circle(d=1, $fn=12), path);
|
||||
// Example: Square spiral, second try. Use roll to create the spiral instead of turning up. It still twists because the left turns are inclined.
|
||||
// Example(3D): Square spiral, second try. Use roll to create the spiral instead of turning up. It still twists because the left turns are inclined.
|
||||
// include<BOSL2/skin.scad>
|
||||
// path = turtle3d(["move",10,"left","roll",10],repeat=50);
|
||||
// path_sweep(circle(d=1, $fn=12), path);
|
||||
// Example: Square spiral, third try. One way to avoid the core twisting in the spiral is to use absolute turns. Note that the vertical rise is controlled by the starting upward angle of the turtle, which is preserved as we rotate around the z axis.
|
||||
// Example(3D): Square spiral, third try. One way to avoid the core twisting in the spiral is to use absolute turns. Note that the vertical rise is controlled by the starting upward angle of the turtle, which is preserved as we rotate around the z axis.
|
||||
// include<BOSL2/skin.scad>
|
||||
// path = turtle3d(["up", 5, "repeat", 12, ["move",10,"zrot"]]);
|
||||
// path_sweep(circle(d=1, $fn=12), path);
|
||||
// Example: Square spiral, rounded corners. Careful use of rotations can work for sweep, but it may be better to round the corners. Here we return a list of transforms and use sweep instead of path_sweep:
|
||||
// Example(3D): Square spiral, rounded corners. Careful use of rotations can work for sweep, but it may be better to round the corners. Here we return a list of transforms and use sweep instead of path_sweep:
|
||||
// include<BOSL2/skin.scad>
|
||||
// path = turtle3d(["up", 5, "repeat", 12, ["move",10,"arczrot",4]],transforms=true);
|
||||
// sweep(circle(d=1, $fn=12), path);
|
||||
// Example: Mixing relative and absolute commands
|
||||
// Example(3D): Mixing relative and absolute commands
|
||||
// include<BOSL2/skin.scad>
|
||||
// path = turtle3d(["repeat", 4, ["move",80,"arczrot",40],
|
||||
// "arcyrot",40,-90,
|
||||
|
@ -196,7 +198,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// state=[1,0,.2],transforms=true);
|
||||
// ushape = rot(90,p=[[-10, 0],[-10, 10],[ -7, 10],[ -7, 2],[ 7, 2],[ 7, 7],[ 10, 7],[ 10, 0]]);
|
||||
// sweep(ushape, path);
|
||||
// Example: Generic helix, constructed by a sequence of movements and then rotations
|
||||
// Example(3D): Generic helix, constructed by a sequence of movements and then rotations
|
||||
// include<BOSL2/skin.scad>
|
||||
// radius=14; // Helix radius
|
||||
// pitch=20; // Distance from one turn to the next
|
||||
|
@ -215,7 +217,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ],
|
||||
// ], transforms=true);
|
||||
// sweep(subdivide_path(square([5,1]),20), helix);
|
||||
// Example: Helix generated by a single command. Note this only works for x, y, or z aligned helixes because the generic rot cannot handle multi-turn angles.
|
||||
// Example(3D): Helix generated by a single command. Note this only works for x, y, or z aligned helixes because the generic rot cannot handle multi-turn angles.
|
||||
// include<BOSL2/skin.scad>
|
||||
// pitch=20; // Distance from one turn to the next
|
||||
// radius=14; // Helix radius
|
||||
|
@ -231,11 +233,11 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ]
|
||||
// ], transforms=true);
|
||||
// sweep(subdivide_path(square([5,1]),80), helix);
|
||||
// Example: Expanding helix
|
||||
// Example(3D): Expanding helix
|
||||
// include<BOSL2/skin.scad>
|
||||
// path = turtle3d(["length",.2,"angle",360/20,"up",5,"repeat",50,["move","zrot","addlength",0.05]]);
|
||||
// path_sweep(circle(d=1, $fn=12), path);
|
||||
// Example: Adding some twist to the model
|
||||
// Example(3D): Adding some twist to the model
|
||||
// include<BOSL2/skin.scad>
|
||||
// r = 2.5;
|
||||
// trans = turtle3d(["move",10,
|
||||
|
@ -248,7 +250,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "arcleft",r],
|
||||
// state=yrot(25,p=RIGHT),transforms=true);
|
||||
// sweep(supershape(m1=4,n1=4,n2=16,n3=1.5,a=.9,b=9,step=5),trans);
|
||||
// Example: Twist does not change the turtle orientation, but roll does. The only change from the previous example is twist was changed to roll.
|
||||
// Example(3D): Twist does not change the turtle orientation, but roll does. The only change from the previous example is twist was changed to roll.
|
||||
// include<BOSL2/skin.scad>
|
||||
// r = 2;
|
||||
// trans = turtle3d(["move",10,
|
||||
|
@ -261,7 +263,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "arcleft",r],
|
||||
// state=yrot(25,p=RIGHT),transforms=true);
|
||||
// sweep(supershape(m1=4,n1=4,n2=16,n3=1.5,a=.9,b=9,step=5),trans);
|
||||
// Example: Use of shrink and grow
|
||||
// Example(3D): Use of shrink and grow
|
||||
// include<BOSL2/skin.scad>
|
||||
// $fn=32;
|
||||
// T = turtle3d([
|
||||
|
@ -276,7 +278,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "untily", -1,
|
||||
// ],state=RIGHT, transforms=true);
|
||||
// sweep(square(2,center=true),T);
|
||||
// Example: After several moves you may not understand the turtle orientation. An absolute reorientation with "arctodir" is helpful to head in a known direction
|
||||
// Example(3D): After several moves you may not understand the turtle orientation. An absolute reorientation with "arctodir" is helpful to head in a known direction
|
||||
// include<BOSL2/skin.scad>
|
||||
// trans = turtle3d([
|
||||
// "move",5,
|
||||
|
@ -293,7 +295,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "untilz",0
|
||||
// ],transforms=true);
|
||||
// sweep(square(1,center=true),trans);
|
||||
// Example: The "grow" and "shrink" commands can take a vector giving x and y scaling
|
||||
// Example(3D): The "grow" and "shrink" commands can take a vector giving x and y scaling
|
||||
// include<BOSL2/skin.scad>
|
||||
// tr = turtle3d([
|
||||
// "move", 1.5,
|
||||
|
@ -301,7 +303,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ["move", 5, "grow", [2,0.5],"steps", 10]
|
||||
// ], transforms=true);
|
||||
// sweep(circle($fn=32,r=1), tr);
|
||||
// Example: With "twist" added the anisotropic "grow" interacts with "twist", producing a complex form
|
||||
// Example(3D): With "twist" added the anisotropic "grow" interacts with "twist", producing a complex form
|
||||
// include<BOSL2/skin.scad>
|
||||
// tr = turtle3d([
|
||||
// "move", 1.5,
|
||||
|
@ -309,7 +311,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ["move", 5, "grow", [0.5,2],"steps", 20, "twist",90]
|
||||
// ], transforms=true);
|
||||
// sweep(circle($fn=64,r=1), tr);
|
||||
// Example: Making a tube with "reverse". Note that the move direction is the same even though the direction is reversed.
|
||||
// Example(3D): Making a tube with "reverse". Note that the move direction is the same even though the direction is reversed.
|
||||
// include<BOSL2/skin.scad>
|
||||
// tr = turtle3d([ "move", 4,
|
||||
// ["move",0, "grow", .8, "reverse"],
|
||||
|
@ -317,7 +319,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ], transforms=true);
|
||||
// back_half(s=10)
|
||||
// sweep(circle(r=1,$fn=16), tr, closed=true);
|
||||
// Example: To close the tube at one end we set closed to false in sweep.
|
||||
// Example(3D): To close the tube at one end we set closed to false in sweep.
|
||||
// include<BOSL2/skin.scad>
|
||||
// tr = turtle3d([ "move", 4,
|
||||
// ["move",0, "grow", .8, "reverse"],
|
||||
|
@ -325,7 +327,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ], transforms=true);
|
||||
// back_half(s=10)
|
||||
// sweep(circle(r=1,$fn=16), tr, closed=false);
|
||||
// Example: Cookie cutter using "reverse"
|
||||
// Example(3D): Cookie cutter using "reverse"
|
||||
// include<BOSL2/skin.scad>
|
||||
// cutter = turtle3d( [
|
||||
// ["move", 10, "shrink", 1.3, ],
|
||||
|
@ -334,7 +336,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ], transforms=true,state=UP);
|
||||
// cookie_shape = star(5, r=10, ir=5);
|
||||
// sweep(cookie_shape, cutter, closed=true);
|
||||
// Example: angled shopvac adapter. Shopvac tubing wedges together because the tubes are slightly tapered. We can make this part without using any difference() operations by using "reverse" to trace out the interior portion of the part. Note that it's "arcright" even when reversed.
|
||||
// Example(3D): angled shopvac adapter. Shopvac tubing wedges together because the tubes are slightly tapered. We can make this part without using any difference() operations by using "reverse" to trace out the interior portion of the part. Note that it's "arcright" even when reversed.
|
||||
// include<BOSL2/skin.scad>
|
||||
// inch = 25.4;
|
||||
// insert_ID = 2.3*inch; // Size of shopvac tube at larger end of taper
|
||||
|
@ -360,9 +362,9 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// ["move", seg1_len, "grow", seg1_bot_ID/seg2_bot_ID]
|
||||
// ],
|
||||
// state=UP, transforms=true);
|
||||
// zrot(90)back_half() // Remove this to get a usable part
|
||||
// back_half() // Remove this to get a usable part
|
||||
// sweep(circle(d=seg1_bot_OD, $fn=128), trans, closed=true);
|
||||
// Example: Closed spiral
|
||||
// Example(3D): Closed spiral
|
||||
// include<BOSL2/skin.scad>
|
||||
// steps = 500;
|
||||
// spiral = turtle3d([
|
||||
|
@ -382,11 +384,11 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "grow",1.5],
|
||||
// ], transforms=true);
|
||||
// sweep(fwd(25,p=circle(r=2,$fn=24)), spiral, caps=false);
|
||||
// Example: Mobius strip (square)
|
||||
// Example(3D): Mobius strip (square)
|
||||
// include<BOSL2/skin.scad>
|
||||
// mobius = turtle3d([["arc", 20, "zrot", 360,"steps",100,"twist",180]], transforms=true);
|
||||
// sweep(subdivide_path(square(8,center=true),16), mobius, closed=false);
|
||||
// Example: Torus knot
|
||||
// Example(3D): Torus knot
|
||||
// include<BOSL2/skin.scad>
|
||||
// p = 3; // (number of turns)*gcd(p,q)
|
||||
// q = 10; // (number of dives)*gcd(p,q)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,476];
|
||||
BOSL_VERSION = [2,0,480];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
14
vnf.scad
14
vnf.scad
|
@ -672,8 +672,11 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
],
|
||||
nonplanars = unique([
|
||||
for (face = faces) let(
|
||||
faceverts = [for (k=face) varr[k]]
|
||||
) if (!coplanar(faceverts)) [
|
||||
faceverts = [for (k=face) varr[k]],
|
||||
area = polygon_area(faceverts)
|
||||
)
|
||||
if (is_num(area) && abs(area) > EPSILON)
|
||||
if (!coplanar(faceverts)) [
|
||||
"ERROR",
|
||||
"NONPLANAR",
|
||||
"Face vertices are not coplanar",
|
||||
|
@ -712,9 +715,12 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
if (v!=edge[0] && v!=edge[1]) let(
|
||||
a = varr[edge[0]],
|
||||
b = varr[v],
|
||||
c = varr[edge[1]],
|
||||
c = varr[edge[1]]
|
||||
)
|
||||
if (a != b && b != c && a != c) let(
|
||||
pt = segment_closest_point([a,c],b)
|
||||
) if (pt == b) [
|
||||
)
|
||||
if (pt == b) [
|
||||
"ERROR",
|
||||
"T_JUNCTION",
|
||||
"Vertex is mid-edge on another Face",
|
||||
|
|
Loading…
Reference in a new issue