mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-19 19:09:36 +00:00
Merge pull request #659 from adrianVmariano/master
screws bugfix & paths doc changes
This commit is contained in:
commit
2911110bd0
18 changed files with 1117 additions and 993 deletions
|
@ -50,7 +50,7 @@ function ident(n) = [
|
|||
// Arguments:
|
||||
// x = The value to test for being an affine matrix.
|
||||
// dim = The number of dimensions the given affine is required to be for. Generally 2 for 2D or 3 for 3D. If given as a list of integers, allows any of the given dimensions. Default: `[2,3]`
|
||||
// Examples:
|
||||
// Example:
|
||||
// bool = is_affine(affine2d_scale([2,3])); // Returns true
|
||||
// bool = is_affine(affine3d_scale([2,3,4])); // Returns true
|
||||
// bool = is_affine(affine3d_scale([2,3,4]),2); // Returns false
|
||||
|
@ -74,7 +74,7 @@ function is_affine(x,dim=[2,3]) =
|
|||
// for a simple scaling of z. Note that an input which is only a zscale returns false.
|
||||
// Arguments:
|
||||
// t = The transformation matrix to check.
|
||||
// Examples:
|
||||
// Example:
|
||||
// b = is_2d_transform(zrot(45)); // Returns: true
|
||||
// b = is_2d_transform(yrot(45)); // Returns: false
|
||||
// b = is_2d_transform(xrot(45)); // Returns: false
|
||||
|
|
|
@ -141,12 +141,10 @@ function reduce(func, list, init=0) =
|
|||
// list = The input list.
|
||||
// init = The starting value for the accumulator. Default: 0
|
||||
// See Also: map(), filter(), reduce(), while(), for_n()
|
||||
// Examples: Reimplement cumsum()
|
||||
// echo(accumulate(function (a,b) a+b, [3,4,5],0));
|
||||
// // ECHO: [3,7,12]
|
||||
// Examples: Reimplement cumprod()
|
||||
// echo(accumulate(f_mul(),[3,4,5],1));
|
||||
// // ECHO: [3,12,60,360]
|
||||
// Example: Reimplement cumsum()
|
||||
// echo(accumulate(function (a,b) a+b, [3,4,5],0)); // ECHO: [3,7,12]
|
||||
// Example: Reimplement cumprod()
|
||||
// echo(accumulate(f_mul(),[3,4,5],1)); // ECHO: [3,12,60,360]
|
||||
function accumulate(func, list, init=0) =
|
||||
assert(is_function(func))
|
||||
assert(is_list(list))
|
||||
|
@ -313,7 +311,7 @@ function binsearch(key, list, idx, cmp=f_cmp()) =
|
|||
// Arguments:
|
||||
// x = The value to get the simple hash value of.
|
||||
// See Also: hashmap()
|
||||
// Examples:
|
||||
// Example:
|
||||
// x = simple_hash("Foobar");
|
||||
// x = simple_hash([[10,20],[-5,3]]);
|
||||
function simple_hash(x) =
|
||||
|
|
20
gears.scad
20
gears.scad
|
@ -37,7 +37,7 @@
|
|||
// Arguments:
|
||||
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// circp = circular_pitch(pitch=5);
|
||||
// circp = circular_pitch(mod=2);
|
||||
function circular_pitch(pitch=5, mod) =
|
||||
|
@ -54,7 +54,7 @@ function circular_pitch(pitch=5, mod) =
|
|||
// Arguments:
|
||||
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// dp = diametral_pitch(pitch=5);
|
||||
// dp = diametral_pitch(mod=2);
|
||||
function diametral_pitch(pitch=5, mod) =
|
||||
|
@ -96,7 +96,7 @@ function module_value(pitch=5) = pitch / PI;
|
|||
// Arguments:
|
||||
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// ad = adendum(pitch=5);
|
||||
// ad = adendum(mod=2);
|
||||
// Example(2D):
|
||||
|
@ -123,7 +123,7 @@ function adendum(pitch=5, mod) =
|
|||
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
|
||||
// clearance = If given, sets the clearance between meshing teeth.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// ddn = dedendum(pitch=5);
|
||||
// ddn = dedendum(mod=2);
|
||||
// Example(2D):
|
||||
|
@ -152,7 +152,7 @@ function dedendum(pitch=5, clearance, mod) =
|
|||
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
|
||||
// teeth = The number of teeth on the gear.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// pr = pitch_radius(pitch=5, teeth=11);
|
||||
// pr = pitch_radius(mod=2, teeth=20);
|
||||
// Example(2D):
|
||||
|
@ -177,7 +177,7 @@ function pitch_radius(pitch=5, teeth=11, mod) =
|
|||
// clearance = If given, sets the clearance between meshing teeth.
|
||||
// interior = If true, calculate for an interior gear.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// or = outer_radius(pitch=5, teeth=20);
|
||||
// or = outer_radius(mod=2, teeth=16);
|
||||
// Example(2D):
|
||||
|
@ -203,7 +203,7 @@ function outer_radius(pitch=5, teeth=11, clearance, interior=false, mod) =
|
|||
// clearance = If given, sets the clearance between meshing teeth.
|
||||
// interior = If true, calculate for an interior gear.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// rr = root_radius(pitch=5, teeth=11);
|
||||
// rr = root_radius(mod=2, teeth=16);
|
||||
// Example(2D):
|
||||
|
@ -228,7 +228,7 @@ function root_radius(pitch=5, teeth=11, clearance, interior=false, mod) =
|
|||
// teeth = The number of teeth on the gear.
|
||||
// pressure_angle = Pressure angle in degrees. Controls how straight or bulged the tooth sides are.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// br = base_radius(pitch=5, teeth=20, pressure_angle=20);
|
||||
// br = base_radius(mod=2, teeth=18, pressure_angle=20);
|
||||
// Example(2D):
|
||||
|
@ -253,7 +253,7 @@ function base_radius(pitch=5, teeth=11, pressure_angle=28, mod) =
|
|||
// teeth = Number of teeth that this gear has.
|
||||
// mate_teeth = Number of teeth that the matching gear has.
|
||||
// drive_angle = Angle between the drive shafts of each gear. Default: 90º.
|
||||
// Examples:
|
||||
// Example:
|
||||
// ang = bevel_pitch_angle(teeth=18, mate_teeth=30);
|
||||
// Example(2D):
|
||||
// t1 = 13; t2 = 19; pitch=5;
|
||||
|
@ -287,7 +287,7 @@ function bevel_pitch_angle(teeth, mate_teeth, drive_angle=90) =
|
|||
// crowning = The amount to oversize the virtual hobbing cutter used to make the teeth, to add a slight crowning to the teeth to make them fir the work easier. Default: 1
|
||||
// clearance = Clearance gap at the bottom of the inter-tooth valleys.
|
||||
// mod = The metric module/modulus of the gear.
|
||||
// Examples:
|
||||
// Example:
|
||||
// thick = worm_gear_thickness(pitch=5, teeth=36, worm_diam=30);
|
||||
// thick = worm_gear_thickness(mod=2, teeth=28, worm_diam=25);
|
||||
// Example(2D):
|
||||
|
|
|
@ -113,7 +113,7 @@ function point_line_distance(pt, line, bounded=false) =
|
|||
|
||||
// Function: segment_distance()
|
||||
// Usage:
|
||||
// dist = segment_distance(seg1, seg2);
|
||||
// dist = segment_distance(seg1, seg2, [eps]);
|
||||
// Topics: Geometry, Segments, Distance
|
||||
// See Also: convex_collision(), convex_distance()
|
||||
// Description:
|
||||
|
@ -121,12 +121,13 @@ function point_line_distance(pt, line, bounded=false) =
|
|||
// Arguments:
|
||||
// seg1 = The list of two points representing the first line segment to check the distance of.
|
||||
// seg2 = The list of two points representing the second line segment to check the distance of.
|
||||
// eps = tolerance for point comparisons
|
||||
// Example:
|
||||
// dist = segment_distance([[-14,3], [-15,9]], [[-10,0], [10,0]]); // Returns: 5
|
||||
// dist2 = segment_distance([[-5,5], [5,-5]], [[-10,3], [10,-3]]); // Returns: 0
|
||||
function segment_distance(seg1, seg2) =
|
||||
function segment_distance(seg1, seg2,eps=EPSILON) =
|
||||
assert( is_matrix(concat(seg1,seg2),4), "Inputs should be two valid segments." )
|
||||
convex_distance(seg1,seg2);
|
||||
convex_distance(seg1,seg2,eps);
|
||||
|
||||
|
||||
// Function: line_normal()
|
||||
|
@ -1476,7 +1477,7 @@ function polygon_normal(poly) =
|
|||
// color("red")back(28/(2/3))text("Even-Odd", size=5/(2/3), halign="center");
|
||||
// }
|
||||
// right(40){
|
||||
// dp = decompose_path(path,closed=true);
|
||||
// dp = polygon_parts(path,closed=true);
|
||||
// region(dp);
|
||||
// color("red"){stroke(path,width=1,closed=true);
|
||||
// back(28/(2/3))text("Nonzero", size=5/(2/3), halign="center");
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
|
||||
include <threading.scad>
|
||||
include <phillips_drive.scad>
|
||||
include <torx_drive.scad>
|
||||
include <screw_drive.scad>
|
||||
|
||||
|
||||
// Section: Functions
|
||||
|
@ -607,13 +606,13 @@ module metric_bolt(
|
|||
// Phillips drive hole
|
||||
if (headtype != "socket" && phillips != undef) {
|
||||
down(headtype != "hex"? H/6 : 0) {
|
||||
phillips_drive(size=phillips, shaft=D);
|
||||
phillips_mask(size=phillips); //, shaft=D);
|
||||
}
|
||||
}
|
||||
|
||||
// Torx drive hole
|
||||
if (headtype != "socket" && torx != undef) {
|
||||
up(1) torx_drive(size=torx, l=H+0.1, center=false);
|
||||
up(1) torx_mask(size=torx, l=H+0.1, center=false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1036,6 +1036,7 @@ module HSV(h,s=1,v=1,a=1) color(HSV(h,s,v),a) children();
|
|||
// Arguments:
|
||||
// list = The list of items to iterate through.
|
||||
// stride = Consecutive colors stride around the color wheel divided into this many parts.
|
||||
// maxhues = max number of hues to use (to prevent lots of indistinguishable hues)
|
||||
// Side Effects:
|
||||
// Sets the color to progressive values along the ROYGBIV spectrum for each item.
|
||||
// Sets `$idx` to the index of the current item in `list` that we want to show.
|
||||
|
@ -1045,11 +1046,14 @@ module HSV(h,s=1,v=1,a=1) color(HSV(h,s,v),a) children();
|
|||
// Example(2D):
|
||||
// rgn = [circle(d=45,$fn=3), circle(d=75,$fn=4), circle(d=50)];
|
||||
// rainbow(rgn) stroke($item, closed=true);
|
||||
module rainbow(list, stride=1)
|
||||
module rainbow(list, stride=1, maxhues)
|
||||
{
|
||||
ll = len(list);
|
||||
huestep = 360 / ll;
|
||||
maxhues = first_defined([maxhues,ll]);
|
||||
huestep = 360 / maxhues;
|
||||
hues = [for (i=[0:1:ll-1]) posmod(i*huestep+i*360/stride,360)];
|
||||
echo(hues=hues);
|
||||
s = [for (i=[0:1:ll-1]) [.5,.7,1][posmod(i,3)]];
|
||||
for($idx=idx(list)) {
|
||||
$item = list[$idx];
|
||||
HSV(h=hues[$idx]) children();
|
||||
|
|
971
paths.scad
971
paths.scad
File diff suppressed because it is too large
Load diff
|
@ -1,77 +0,0 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: phillips_drive.scad
|
||||
// Phillips driver bits
|
||||
// Includes:
|
||||
// include <BOSL2/std.scad>
|
||||
// include <BOSL2/phillips_drive.scad>
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Section: Modules
|
||||
|
||||
|
||||
// Module: phillips_drive()
|
||||
// Description: Creates a model of a phillips driver bit of a given named size.
|
||||
// Arguments:
|
||||
// size = The size of the bit as a string. "#0", "#1", "#2", "#3", or "#4"
|
||||
// shaft = The diameter of the drive bit's shaft.
|
||||
// l = The length of the drive bit.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Example:
|
||||
// xdistribute(10) {
|
||||
// phillips_drive(size="#1", shaft=4, l=20);
|
||||
// phillips_drive(size="#2", shaft=6, l=20);
|
||||
// phillips_drive(size="#3", shaft=6, l=20);
|
||||
// }
|
||||
module phillips_drive(size="#2", shaft=6, l=20, $fn=36, anchor=BOTTOM, spin=0, orient=UP) {
|
||||
assert(is_string(size));
|
||||
assert(in_list(size,["#0","#1","#2","#3","#4"]));
|
||||
|
||||
num = ord(size[1]) - ord("0");
|
||||
b = [0.61, 0.97, 1.47, 2.41, 3.48][num];
|
||||
e = [0.31, 0.43, 0.81, 2.00, 2.41][num];
|
||||
g = [0.81, 1.27, 2.29, 3.81, 5.08][num];
|
||||
//f = [0.33, 0.53, 0.70, 0.82, 1.23][num];
|
||||
//r = [0.30, 0.50, 0.60, 0.80, 1.00][num];
|
||||
alpha = [ 136, 138, 140, 146, 153][num];
|
||||
beta = [7.00, 7.00, 5.75, 5.75, 7.00][num];
|
||||
gamma = 92.0;
|
||||
ang1 = 28.0;
|
||||
ang2 = 26.5;
|
||||
h1 = adj_ang_to_opp(g/2, ang1);
|
||||
h2 = adj_ang_to_opp((shaft-g)/2, 90-ang2);
|
||||
h3 = adj_ang_to_opp(b/2, ang1);
|
||||
p0 = [0,0];
|
||||
p1 = [e/2, adj_ang_to_opp(e/2, 90-alpha/2)];
|
||||
p2 = p1 + [(shaft-e)/2, adj_ang_to_hyp((shaft-e)/2, 90-gamma/2)];
|
||||
attachable(anchor,spin,orient, d=shaft, l=l) {
|
||||
down(l/2) {
|
||||
difference() {
|
||||
union() {
|
||||
cyl(d1=0, d2=g, h=h1, anchor=BOT);
|
||||
up(h1) {
|
||||
cyl(d1=g, d2=shaft, h=h2, anchor=BOT);
|
||||
up(h2) cyl(d=shaft, h=l-h1-h2, anchor=BOT);
|
||||
}
|
||||
}
|
||||
zrot(45)
|
||||
zrot_copies(n=4, r=b/2/cos(90-alpha/2), sa=90) {
|
||||
up(h3) {
|
||||
xrot(-beta) {
|
||||
linear_extrude(height=(h1+h2)*20, convexity=4, center=true) {
|
||||
path = [p0, p1, p2, [-p2.x,p2.y], [-p1.x,p1.y]];
|
||||
polygon(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
126
regions.scad
126
regions.scad
|
@ -29,31 +29,17 @@ function is_region(x) = is_list(x) && is_path(x.x);
|
|||
function close_region(region, eps=EPSILON) = [for (path=region) close_path(path, eps=eps)];
|
||||
|
||||
|
||||
// Module: region()
|
||||
// Function: cleanup_region()
|
||||
// Usage:
|
||||
// region(r);
|
||||
// cleanup_region(region);
|
||||
// Description:
|
||||
// Creates 2D polygons for the given region. The region given is a list of closed 2D paths.
|
||||
// Each path will be effectively exclusive-ORed from all other paths in the region, so if a
|
||||
// path is inside another path, it will be effectively subtracted from it.
|
||||
// Example(2D):
|
||||
// region([circle(d=50), square(25,center=true)]);
|
||||
// Example(2D):
|
||||
// rgn = concat(
|
||||
// [for (d=[50:-10:10]) circle(d=d-5)],
|
||||
// [square([60,10], center=true)]
|
||||
// );
|
||||
// region(rgn);
|
||||
module region(r)
|
||||
{
|
||||
points = flatten(r);
|
||||
paths = [
|
||||
for (i=[0:1:len(r)-1]) let(
|
||||
start = default(sum([for (j=[0:1:i-1]) len(r[j])]),0)
|
||||
) [for (k=[0:1:len(r[i])-1]) start+k]
|
||||
];
|
||||
polygon(points=points, paths=paths);
|
||||
}
|
||||
// For all paths in the given region, if the last point coincides with the first point, removes the last point.
|
||||
// Arguments:
|
||||
// region = The region to clean up. Given as a list of polygon paths.
|
||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||
function cleanup_region(region, eps=EPSILON) =
|
||||
[for (path=region) cleanup_path(path, eps=eps)];
|
||||
|
||||
|
||||
|
||||
// Function: check_and_fix_path()
|
||||
|
@ -91,16 +77,34 @@ function check_and_fix_path(path, valid_dim=undef, closed=false, name="path") =
|
|||
closed && approx(path[0], last(path))? list_head(path) : path;
|
||||
|
||||
|
||||
// Function: cleanup_region()
|
||||
|
||||
|
||||
// Module: region()
|
||||
// Usage:
|
||||
// cleanup_region(region);
|
||||
// region(r);
|
||||
// Description:
|
||||
// For all paths in the given region, if the last point coincides with the first point, removes the last point.
|
||||
// Arguments:
|
||||
// region = The region to clean up. Given as a list of polygon paths.
|
||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||
function cleanup_region(region, eps=EPSILON) =
|
||||
[for (path=region) cleanup_path(path, eps=eps)];
|
||||
// Creates 2D polygons for the given region. The region given is a list of closed 2D paths.
|
||||
// Each path will be effectively exclusive-ORed from all other paths in the region, so if a
|
||||
// path is inside another path, it will be effectively subtracted from it.
|
||||
// Example(2D):
|
||||
// region([circle(d=50), square(25,center=true)]);
|
||||
// Example(2D):
|
||||
// rgn = concat(
|
||||
// [for (d=[50:-10:10]) circle(d=d-5)],
|
||||
// [square([60,10], center=true)]
|
||||
// );
|
||||
// region(rgn);
|
||||
module region(r)
|
||||
{
|
||||
points = flatten(r);
|
||||
paths = [
|
||||
for (i=[0:1:len(r)-1]) let(
|
||||
start = default(sum([for (j=[0:1:i-1]) len(r[j])]),0)
|
||||
) [for (k=[0:1:len(r[i])-1]) start+k]
|
||||
];
|
||||
polygon(points=points, paths=paths);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function: point_in_region()
|
||||
|
@ -121,11 +125,12 @@ function point_in_region(point, region, eps=EPSILON, _i=0, _cnt=0) =
|
|||
) pip==0? 0 : point_in_region(point, region, eps=eps, _i=_i+1, _cnt = _cnt + (pip>0? 1 : 0));
|
||||
|
||||
|
||||
|
||||
// Function: polygons_equal()
|
||||
// Usage:
|
||||
// b = polygons_equal(poly1, poly2, [eps])
|
||||
// Description:
|
||||
// Returns true if the components of region1 and region2 are the same polygons
|
||||
// Returns true if poly1 and poly2 are the same polongs
|
||||
// within given epsilon tolerance.
|
||||
// Arguments:
|
||||
// poly1 = first polygon
|
||||
|
@ -151,34 +156,34 @@ function __polygons_equal(poly1, poly2, eps, st) =
|
|||
max([for(d=poly1-select(poly2,st,st-1)) d*d])<eps*eps;
|
||||
|
||||
|
||||
// Function: poly_in_polygons()
|
||||
// Function: is_polygon_in_list()
|
||||
// Topics: Polygons, Comparators
|
||||
// See Also: polygons_equal(), regions_equal()
|
||||
// Usage:
|
||||
// bool = poly_in_polygons(poly, polys);
|
||||
// bool = is_polygon_in_list(poly, polys);
|
||||
// Description:
|
||||
// Returns true if one of the polygons in `polys` is equivalent to the polygon `poly`.
|
||||
// Arguments:
|
||||
// poly = The polygon to search for.
|
||||
// polys = The list of polygons to look for the polygon in.
|
||||
function poly_in_polygons(poly, polys) =
|
||||
__poly_in_polygons(poly, polys, 0);
|
||||
function is_polygon_in_list(poly, polys) =
|
||||
__is_polygon_in_list(poly, polys, 0);
|
||||
|
||||
function __poly_in_polygons(poly, polys, i) =
|
||||
function __is_polygon_in_list(poly, polys, i) =
|
||||
i >= len(polys)? false :
|
||||
polygons_equal(poly, polys[i])? true :
|
||||
__poly_in_polygons(poly, polys, i+1);
|
||||
__is_polygon_in_list(poly, polys, i+1);
|
||||
|
||||
|
||||
// Function: regions_equal()
|
||||
// Usage:
|
||||
// b = regions_equal(region1, region2, [eps])
|
||||
// Description:
|
||||
// Returns true if the components of region1 and region2 are the same polygons
|
||||
// Returns true if the components of region1 and region2 are the same polygons (in any order)
|
||||
// within given epsilon tolerance.
|
||||
// Arguments:
|
||||
// poly1 = first polygon
|
||||
// poly2 = second polygon
|
||||
// region1 = first region
|
||||
// region2 = second region
|
||||
// eps = tolerance for comparison
|
||||
function regions_equal(region1, region2) =
|
||||
assert(is_region(region1) && is_region(region2))
|
||||
|
@ -187,21 +192,21 @@ function regions_equal(region1, region2) =
|
|||
|
||||
function __regions_equal(region1, region2, i) =
|
||||
i >= len(region1)? true :
|
||||
!poly_in_polygons(region1[i], region2)? false :
|
||||
!is_polygon_in_list(region1[i], region2)? false :
|
||||
__regions_equal(region1, region2, i+1);
|
||||
|
||||
|
||||
// Function: region_path_crossings()
|
||||
// Usage:
|
||||
// region_path_crossings(path, region);
|
||||
// Description:
|
||||
// Returns a sorted list of [SEGMENT, U] that describe where a given path is crossed by a second path.
|
||||
// Arguments:
|
||||
// path = The path to find crossings on.
|
||||
// region = Region to test for crossings of.
|
||||
// closed = If true, treat path as a closed polygon. Default: true
|
||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||
function region_path_crossings(path, region, closed=true, eps=EPSILON) = sort([
|
||||
/// Internal Function: _region_path_crossings()
|
||||
/// Usage:
|
||||
/// _region_path_crossings(path, region);
|
||||
/// Description:
|
||||
/// Returns a sorted list of [SEGMENT, U] that describe where a given path is crossed by a second path.
|
||||
/// Arguments:
|
||||
/// path = The path to find crossings on.
|
||||
/// region = Region to test for crossings of.
|
||||
/// closed = If true, treat path as a closed polygon. Default: true
|
||||
/// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
||||
function _region_path_crossings(path, region, closed=true, eps=EPSILON) = sort([
|
||||
let(
|
||||
segs = pair(closed? close_path(path) : cleanup_path(path))
|
||||
) for (
|
||||
|
@ -240,7 +245,7 @@ function split_path_at_region_crossings(path, region, closed=true, eps=EPSILON)
|
|||
let(
|
||||
path = deduplicate(path, eps=eps),
|
||||
region = [for (path=region) deduplicate(path, eps=eps)],
|
||||
xings = region_path_crossings(path, region, closed=closed, eps=eps),
|
||||
xings = _region_path_crossings(path, region, closed=closed, eps=eps),
|
||||
crossings = deduplicate(
|
||||
concat([[0,0]], xings, [[len(path)-1,1]]),
|
||||
eps=eps
|
||||
|
@ -489,7 +494,7 @@ function _shift_segment(segment, d) =
|
|||
// Extend to segments to their intersection point. First check if the segments already have a point in common,
|
||||
// which can happen if two colinear segments are input to the path variant of `offset()`
|
||||
function _segment_extension(s1,s2) =
|
||||
norm(s1[1]-s2[0])<1e-6 ? s1[1] : line_intersection(s1,s2);
|
||||
norm(s1[1]-s2[0])<1e-6 ? s1[1] : line_intersection(s1,s2,LINE,LINE);
|
||||
|
||||
|
||||
function _makefaces(direction, startind, good, pointcount, closed) =
|
||||
|
@ -745,7 +750,11 @@ function offset(
|
|||
quality = max(0,round(quality)),
|
||||
flip_dir = closed && !is_polygon_clockwise(path)? -1 : 1,
|
||||
d = flip_dir * (is_def(r) ? r : delta),
|
||||
shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)],
|
||||
// shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)],
|
||||
shiftsegs = [for(i=[0:len(path)-2]) _shift_segment([path[i],path[i+1]], d),
|
||||
if (closed) _shift_segment([last(path),path[0]],d)
|
||||
else [path[0],path[1]] // dummy segment, not used
|
||||
],
|
||||
// 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) : repeat(true,len(shiftsegs)),
|
||||
goodsegs = bselect(shiftsegs, good),
|
||||
|
@ -759,7 +768,8 @@ function offset(
|
|||
// Note if !closed the last corner doesn't matter, so exclude it
|
||||
parallelcheck =
|
||||
(len(sharpcorners)==2 && !closed) ||
|
||||
all_defined(closed? sharpcorners : list_tail(sharpcorners))
|
||||
all_defined(closed? sharpcorners : select(sharpcorners, 1,-2)),
|
||||
f=echo(sharpcorners=sharpcorners)
|
||||
)
|
||||
assert(parallelcheck, "Path contains sequential parallel segments (either 180 deg turn or 0 deg turn")
|
||||
let(
|
||||
|
@ -771,7 +781,7 @@ function offset(
|
|||
:
|
||||
[for(i=[0:len(goodsegs)-1])
|
||||
let(prevseg=select(goodsegs,i-1))
|
||||
i==0 && !closed ? false // In open case first entry is bogus
|
||||
(i==0 || i==len(goodsegs)-1) && !closed ? false // In open case first entry is bogus
|
||||
:
|
||||
(goodsegs[i][1]-goodsegs[i][0]) * (goodsegs[i][0]-sharpcorners[i]) > 0
|
||||
&& (prevseg[1]-prevseg[0]) * (sharpcorners[i]-prevseg[1]) > 0
|
||||
|
|
|
@ -1931,8 +1931,8 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
|
|||
top_patch[i][4][4]
|
||||
]
|
||||
],
|
||||
top_intersections = path_self_intersections(faces[0]),
|
||||
bot_intersections = path_self_intersections(faces[1]),
|
||||
top_simple = is_path_simple(faces[0],closed=true),
|
||||
bot_simple = is_path_simple(faces[1],closed=true),
|
||||
// verify vertical edges
|
||||
verify_vert =
|
||||
[for(i=[0:N-1],j=[0:4])
|
||||
|
@ -1949,9 +1949,9 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
|
|||
)
|
||||
if (!is_collinear(hline_top) || !is_collinear(hline_bot)) [i,j]]
|
||||
)
|
||||
assert(debug || top_intersections==[],
|
||||
assert(debug || top_simple,
|
||||
"Roundovers interfere with each other on top face: either input is self intersecting or top joint length is too large")
|
||||
assert(debug || bot_intersections==[],
|
||||
assert(debug || bot_simple,
|
||||
"Roundovers interfere with each other on bottom face: either input is self intersecting or top joint length is too large")
|
||||
assert(debug || (verify_vert==[] && verify_horiz==[]), "Curvature continuity failed")
|
||||
let(
|
||||
|
|
312
screw_drive.scad
Normal file
312
screw_drive.scad
Normal file
|
@ -0,0 +1,312 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: screw_drive.scad
|
||||
// Recess masks for screw heads
|
||||
// Includes:
|
||||
// include <BOSL2/std.scad>
|
||||
// include <BOSL2/screw_drive.scad>
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Section: Phillips Drive
|
||||
|
||||
// Module: phillips_mask()
|
||||
// Description:
|
||||
// Creates a mask for creating a Phillips drive recess given the Phillips size. Each mask can
|
||||
// be lowered to different depths to create different sizes of recess.
|
||||
// Arguments:
|
||||
// size = The size of the bit as a number or string. "#0", "#1", "#2", "#3", or "#4"
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Example:
|
||||
// xdistribute(10) {
|
||||
// phillips_mask(size="#1");
|
||||
// phillips_mask(size="#2");
|
||||
// phillips_mask(size=3);
|
||||
// phillips_mask(size=4);
|
||||
// }
|
||||
|
||||
// Specs for phillips recess here:
|
||||
// https://www.fasteners.eu/tech-info/ISO/4757/
|
||||
|
||||
_phillips_shaft = [3,4.5,6,8,10];
|
||||
_ph_bot_angle = 28.0;
|
||||
_ph_side_angle = 26.5;
|
||||
|
||||
module phillips_mask(size="#2", $fn=36, anchor=BOTTOM, spin=0, orient=UP) {
|
||||
assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]));
|
||||
num = is_num(size) ? size : ord(size[1]) - ord("0");
|
||||
shaft = _phillips_shaft[num];
|
||||
b = [0.61, 0.97, 1.47, 2.41, 3.48][num];
|
||||
e = [0.31, 0.435, 0.815, 2.005, 2.415][num];
|
||||
g = [0.81, 1.27, 2.29, 3.81, 5.08][num];
|
||||
//f = [0.33, 0.53, 0.70, 0.82, 1.23][num];
|
||||
//r = [0.30, 0.50, 0.60, 0.80, 1.00][num];
|
||||
alpha = [ 136, 138, 140, 146, 153][num];
|
||||
beta = [7.00, 7.00, 5.75, 5.75, 7.00][num];
|
||||
gamma = 92.0;
|
||||
h1 = adj_ang_to_opp(g/2, _ph_bot_angle); // height of the small conical tip
|
||||
h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle); // height of larger cone
|
||||
l = h1+h2;
|
||||
h3 = adj_ang_to_opp(b/2, _ph_bot_angle); // height where cutout starts
|
||||
p0 = [0,0];
|
||||
p1 = [adj_ang_to_opp(e/2, 90-alpha/2), -e/2];
|
||||
p2 = p1 + [adj_ang_to_opp((shaft-e)/2, 90-gamma/2),-(shaft-e)/2];
|
||||
attachable(anchor,spin,orient, d=shaft, l=l) {
|
||||
down(l/2) {
|
||||
difference() {
|
||||
rotate_extrude()
|
||||
polygon([[0,0],[g/2,h1],[shaft/2,l],[0,l]]);
|
||||
zrot(45)
|
||||
zrot_copies(n=4, r=b/2) {
|
||||
up(h3) {
|
||||
yrot(beta) {
|
||||
down(1)
|
||||
linear_extrude(height=l+2, convexity=4, center=false) {
|
||||
path = [p0, p1, p2, [p2.x,-p2.y], [p1.x,-p1.y]];
|
||||
polygon(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function: phillips_depth()
|
||||
// Usage:
|
||||
// depth = phillips_depth(size, d);
|
||||
// Description:
|
||||
// Returns the depth of the Phillips recess required to produce the specified diameter, or
|
||||
// undef if not possible.
|
||||
// Arguments:
|
||||
// size = size as a number or text string like "#2"
|
||||
// d = desired diameter
|
||||
function phillips_depth(size, d) =
|
||||
assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
|
||||
let(
|
||||
num = is_num(size) ? size : ord(size[1]) - ord("0"),
|
||||
shaft = [3,4.5,6,8,10][num],
|
||||
g = [0.81, 1.27, 2.29, 3.81, 5.08][num],
|
||||
_ph_bot_angle = 28.0,
|
||||
_ph_side_angle = 26.5,
|
||||
h1 = adj_ang_to_opp(g/2, _ph_bot_angle), // height of the small conical tip
|
||||
h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle) // height of larger cone
|
||||
)
|
||||
d>=shaft || d<g ? undef :
|
||||
(d-g) / 2 / tan(_ph_side_angle) + h1;
|
||||
|
||||
|
||||
// Function: phillips_diam()
|
||||
// Usage:
|
||||
// diam = phillips_diam(size, depth);
|
||||
// Description:
|
||||
// Returns the diameter at the top of the Phillips recess when constructed at the specified depth,
|
||||
// or undef if that depth is not valid.
|
||||
// Arguments:
|
||||
// size = size as number or text string like "#2"
|
||||
// depth = depth of recess to find the diameter of
|
||||
function phillips_diam(size, depth) =
|
||||
assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
|
||||
let(
|
||||
num = is_num(size) ? size : ord(size[1]) - ord("0"),
|
||||
shaft = _phillips_shaft[num],
|
||||
g = [0.81, 1.27, 2.29, 3.81, 5.08][num],
|
||||
h1 = adj_ang_to_opp(g/2, _ph_bot_angle), // height of the small conical tip
|
||||
h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle) // height of larger cone
|
||||
)
|
||||
depth<h1 || depth>= h1+h2 ? undef :
|
||||
2 * tan(_ph_side_angle)*(depth-h1) + g;
|
||||
|
||||
|
||||
|
||||
// Section: Torx Drive
|
||||
|
||||
|
||||
// Function: torx_outer_diam()
|
||||
// Description: Get the typical outer diameter of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_outer_diam(size) = lookup(size, [
|
||||
[ 6, 1.75],
|
||||
[ 8, 2.40],
|
||||
[ 10, 2.80],
|
||||
[ 15, 3.35],
|
||||
[ 20, 3.95],
|
||||
[ 25, 4.50],
|
||||
[ 30, 5.60],
|
||||
[ 40, 6.75],
|
||||
[ 45, 7.93],
|
||||
[ 50, 8.95],
|
||||
[ 55, 11.35],
|
||||
[ 60, 13.45],
|
||||
[ 70, 15.70],
|
||||
[ 80, 17.75],
|
||||
[ 90, 20.20],
|
||||
[100, 22.40]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_inner_diam()
|
||||
// Description: Get typical inner diameter of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_inner_diam(size) = lookup(size, [
|
||||
[ 6, 1.27],
|
||||
[ 8, 1.75],
|
||||
[ 10, 2.05],
|
||||
[ 15, 2.40],
|
||||
[ 20, 2.85],
|
||||
[ 25, 3.25],
|
||||
[ 30, 4.05],
|
||||
[ 40, 4.85],
|
||||
[ 45, 5.64],
|
||||
[ 50, 6.45],
|
||||
[ 55, 8.05],
|
||||
[ 60, 9.60],
|
||||
[ 70, 11.20],
|
||||
[ 80, 12.80],
|
||||
[ 90, 14.40],
|
||||
[100, 16.00]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_depth()
|
||||
// Description: Gets typical drive hole depth.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_depth(size) = lookup(size, [
|
||||
[ 6, 1.82],
|
||||
[ 8, 3.05],
|
||||
[ 10, 3.56],
|
||||
[ 15, 3.81],
|
||||
[ 20, 4.07],
|
||||
[ 25, 4.45],
|
||||
[ 30, 4.95],
|
||||
[ 40, 5.59],
|
||||
[ 45, 6.22],
|
||||
[ 50, 6.48],
|
||||
[ 55, 6.73],
|
||||
[ 60, 8.17],
|
||||
[ 70, 8.96],
|
||||
[ 80, 9.90],
|
||||
[ 90, 10.56],
|
||||
[100, 11.35]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_tip_radius()
|
||||
// Description: Gets minor rounding radius of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_tip_radius(size) = lookup(size, [
|
||||
[ 6, 0.132],
|
||||
[ 8, 0.190],
|
||||
[ 10, 0.229],
|
||||
[ 15, 0.267],
|
||||
[ 20, 0.305],
|
||||
[ 25, 0.375],
|
||||
[ 30, 0.451],
|
||||
[ 40, 0.546],
|
||||
[ 45, 0.574],
|
||||
[ 50, 0.775],
|
||||
[ 55, 0.867],
|
||||
[ 60, 1.067],
|
||||
[ 70, 1.194],
|
||||
[ 80, 1.526],
|
||||
[ 90, 1.530],
|
||||
[100, 1.720]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_rounding_radius()
|
||||
// Description: Gets major rounding radius of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_rounding_radius(size) = lookup(size, [
|
||||
[ 6, 0.383],
|
||||
[ 8, 0.510],
|
||||
[ 10, 0.598],
|
||||
[ 15, 0.716],
|
||||
[ 20, 0.859],
|
||||
[ 25, 0.920],
|
||||
[ 30, 1.194],
|
||||
[ 40, 1.428],
|
||||
[ 45, 1.796],
|
||||
[ 50, 1.816],
|
||||
[ 55, 2.667],
|
||||
[ 60, 2.883],
|
||||
[ 70, 3.477],
|
||||
[ 80, 3.627],
|
||||
[ 90, 4.468],
|
||||
[100, 4.925]
|
||||
]);
|
||||
|
||||
|
||||
|
||||
// Module: torx_mask2d()
|
||||
// Description: Creates a torx bit 2D profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
// Example(2D):
|
||||
// torx_mask2d(size=30, $fa=1, $fs=1);
|
||||
module torx_mask2d(size) {
|
||||
od = torx_outer_diam(size);
|
||||
id = torx_inner_diam(size);
|
||||
tip = torx_tip_radius(size);
|
||||
rounding = torx_rounding_radius(size);
|
||||
base = od - 2*tip;
|
||||
$fn = quantup(segs(od/2),12);
|
||||
difference() {
|
||||
union() {
|
||||
circle(d=base);
|
||||
zrot_copies(n=2) {
|
||||
hull() {
|
||||
zrot_copies(n=3) {
|
||||
translate([base/2,0,0]) {
|
||||
circle(r=tip, $fn=$fn/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
zrot_copies(n=6) {
|
||||
zrot(180/6) {
|
||||
translate([id/2+rounding,0,0]) {
|
||||
circle(r=rounding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Module: torx_mask()
|
||||
// Description: Creates a torx bit tip.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
// l = Length of bit.
|
||||
// center = If true, centers bit vertically.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Examples:
|
||||
// torx_mask(size=30, l=10, $fa=1, $fs=1);
|
||||
module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
|
||||
anchor = get_anchor(anchor, center, BOT, BOT);
|
||||
od = torx_outer_diam(size);
|
||||
attachable(anchor,spin,orient, d=od, l=l) {
|
||||
linear_extrude(height=l, convexity=4, center=true) {
|
||||
torx_mask2d(size);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
44
screws.scad
44
screws.scad
|
@ -8,8 +8,8 @@
|
|||
|
||||
include <structs.scad>
|
||||
include <threading.scad>
|
||||
include <phillips_drive.scad>
|
||||
include <torx_drive.scad>
|
||||
include <screw_drive.scad>
|
||||
|
||||
|
||||
// Section: Generic Screw Creation
|
||||
|
||||
|
@ -837,18 +837,18 @@ module screw_head(screw_info,details=false) {
|
|||
// anchor = anchor relative to the shaft of the screw
|
||||
// anchor_head = anchor relative to the screw head
|
||||
// Example(Med): Selected UTS (English) screws
|
||||
$fn=32;
|
||||
xdistribute(spacing=8){
|
||||
screw("#6", length=12);
|
||||
screw("#6-32", head="button", drive="torx",length=12);
|
||||
screw("#6-32,3/4", head="hex");
|
||||
screw("#6", thread="fine", head="fillister",length=12, drive="phillips");
|
||||
screw("#6", head="flat small",length=12,drive="slot");
|
||||
screw("#6-32", head="flat large", length=12, drive="torx");
|
||||
screw("#6-32", head="flat undercut",length=12);
|
||||
screw("#6-24", head="socket",length=12); // Non-standard threading
|
||||
screw("#6-32", drive="hex", drive_size=1.5, length=12);
|
||||
}
|
||||
// $fn=32;
|
||||
// xdistribute(spacing=8){
|
||||
// screw("#6", length=12);
|
||||
// screw("#6-32", head="button", drive="torx",length=12);
|
||||
// screw("#6-32,3/4", head="hex");
|
||||
// screw("#6", thread="fine", head="fillister",length=12, drive="phillips");
|
||||
// screw("#6", head="flat small",length=12,drive="slot");
|
||||
// screw("#6-32", head="flat large", length=12, drive="torx");
|
||||
// screw("#6-32", head="flat undercut",length=12);
|
||||
// screw("#6-24", head="socket",length=12); // Non-standard threading
|
||||
// screw("#6-32", drive="hex", drive_size=1.5, length=12);
|
||||
// }
|
||||
// Example(Med): A few examples of ISO (metric) screws
|
||||
// $fn=32;
|
||||
// xdistribute(spacing=8){
|
||||
|
@ -1037,8 +1037,8 @@ module _driver(spec)
|
|||
echo(drive_size=drive_size);
|
||||
up(head_top-drive_depth){
|
||||
// recess should be positioned with its bottom center at (0,0) and the correct recess depth given above
|
||||
if (drive=="phillips") phillips_drive(size=str("#",drive_size), shaft=diameter,anchor=BOTTOM);
|
||||
if (drive=="torx") torx_drive(size=drive_size, l=drive_depth+1, center=false);
|
||||
if (drive=="phillips") phillips_mask(drive_size,anchor=BOTTOM);
|
||||
if (drive=="torx") torx_mask(size=drive_size, l=drive_depth+1, center=false);
|
||||
if (drive=="hex") linear_extrude(height=drive_depth+1) hexagon(id=drive_size);
|
||||
if (drive=="slot") cuboid([2*struct_val(spec,"head_size"), drive_width, drive_depth+1],anchor=BOTTOM);
|
||||
}
|
||||
|
@ -1415,9 +1415,21 @@ http://files.engineering.com/getfile.aspx?folder=76fb0d5e-1fff-4c49-87a5-0597947
|
|||
//
|
||||
// Torx drive depth for UTS and ISO (at least missing for "flat small", which means you can't select torx for this head type)
|
||||
// Handle generic phillips (e.g. ph2) or remove it?
|
||||
|
||||
// https://www.fasteners.eu/tech-info/ISO/7721-2/
|
||||
//
|
||||
// How do you insert a threaded hole into a model?
|
||||
// Default nut thickness
|
||||
//
|
||||
// JIS
|
||||
//https://www.garagejournal.com/forum/media/jis-b-4633-vs-iso-8764-1-din-5260-ph.84492/
|
||||
|
||||
//square:
|
||||
//https://www.aspenfasteners.com/content/pdf/square_drive_specification.pdf
|
||||
//http://www.globalfastener.com/standards/index.php?narr58=149
|
||||
//https://patents.google.com/patent/US1003657
|
||||
|
||||
// thread standards:
|
||||
// https://www.gewinde-normen.de/en/index.html
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -810,9 +810,45 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit,
|
|||
|
||||
|
||||
|
||||
/// Internal Function: _path_add_jitter()
|
||||
/// Topics: Paths
|
||||
/// See Also: jittered_poly(), subdivide_long_segments()
|
||||
/// Usage:
|
||||
/// jpath = _path_add_jitter(path, [dist], [closed=]);
|
||||
/// Description:
|
||||
/// Adds tiny jitter offsets to collinear points in the given path so that they
|
||||
/// are no longer collinear. This is useful for preserving subdivision on long
|
||||
/// straight segments, when making geometry with `polygon()`, for use with
|
||||
/// `linear_exrtrude()` with a `twist()`.
|
||||
/// Arguments:
|
||||
/// path = The path to add jitter to.
|
||||
/// dist = The amount to jitter points by. Default: 1/512 (0.00195)
|
||||
/// ---
|
||||
/// closed = If true, treat path like a closed polygon. Default: true
|
||||
/// Example(3D):
|
||||
/// d = 100; h = 75; quadsize = 5;
|
||||
/// path = pentagon(d=d);
|
||||
/// spath = subdivide_long_segments(path, quadsize, closed=true);
|
||||
/// jpath = _path_add_jitter(spath, closed=true);
|
||||
/// linear_extrude(height=h, twist=72, slices=h/quadsize)
|
||||
/// polygon(jpath);
|
||||
function _path_add_jitter(path, dist=1/512, closed=true) =
|
||||
assert(is_path(path))
|
||||
assert(is_finite(dist))
|
||||
assert(is_bool(closed))
|
||||
[
|
||||
path[0],
|
||||
for (i=idx(path,s=1,e=closed?-1:-2)) let(
|
||||
n = line_normal([path[i-1],path[i]])
|
||||
) path[i] + n * (is_collinear(select(path,i-1,i+1))? (dist * ((i%2)*2-1)) : 0),
|
||||
if (!closed) last(path)
|
||||
];
|
||||
|
||||
|
||||
|
||||
// Module: jittered_poly()
|
||||
// Topics: Extrusions
|
||||
// See Also: path_add_jitter(), subdivide_long_segments()
|
||||
// See Also: _path_add_jitter(), subdivide_long_segments()
|
||||
// Usage:
|
||||
// jittered_poly(path, [dist]);
|
||||
// Description:
|
||||
|
@ -829,7 +865,7 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit,
|
|||
// linear_extrude(height=h, twist=72, slices=h/quadsize)
|
||||
// jittered_poly(spath);
|
||||
module jittered_poly(path, dist=1/512) {
|
||||
polygon(path_add_jitter(path, dist, closed=true));
|
||||
polygon(_path_add_jitter(path, dist, closed=true));
|
||||
}
|
||||
|
||||
|
||||
|
|
114
shapes3d.scad
114
shapes3d.scad
|
@ -1,6 +1,10 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: shapes3d.scad
|
||||
// Common useful shapes and structured objects.
|
||||
// Some standard modules for making 3d shapes with attachment support, and function forms
|
||||
// that produce a VNF. Also included are shortcuts cylinders in each orientation and extended versions of
|
||||
// the standard modules that provide roundovers and chamfers. The sphereoid() module provides
|
||||
// several different ways to make a sphere, and the text modules let you write text on a path
|
||||
// so you can place it on a curved object.
|
||||
// Includes:
|
||||
// include <BOSL2/std.scad>
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -1396,6 +1400,61 @@ module tube(
|
|||
|
||||
|
||||
|
||||
// Module: pie_slice()
|
||||
//
|
||||
// Description:
|
||||
// Creates a pie slice shape.
|
||||
//
|
||||
// Usage: Typical
|
||||
// pie_slice(l|h, r, ang, [center]);
|
||||
// pie_slice(l|h, d=, ang=, ...);
|
||||
// pie_slice(l|h, r1=|d1=, r2=|d2=, ang=, ...);
|
||||
// Usage: Attaching Children
|
||||
// pie_slice(l|h, r, ang, ...) [attachments];
|
||||
//
|
||||
// Arguments:
|
||||
// h / l = height of pie slice.
|
||||
// r = radius of pie slice.
|
||||
// ang = pie slice angle in degrees.
|
||||
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
|
||||
// ---
|
||||
// r1 = bottom radius of pie slice.
|
||||
// r2 = top radius of pie slice.
|
||||
// d = diameter of pie slice.
|
||||
// d1 = bottom diameter of pie slice.
|
||||
// d2 = top diameter of pie slice.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
//
|
||||
// Example: Cylindrical Pie Slice
|
||||
// pie_slice(ang=45, l=20, r=30);
|
||||
// Example: Conical Pie Slice
|
||||
// pie_slice(ang=60, l=20, d1=50, d2=70);
|
||||
module pie_slice(
|
||||
h, r, ang=30, center,
|
||||
r1, r2, d, d1, d2, l,
|
||||
anchor, spin=0, orient=UP
|
||||
) {
|
||||
l = first_defined([l, h, 1]);
|
||||
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10);
|
||||
r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10);
|
||||
maxd = max(r1,r2)+0.1;
|
||||
anchor = get_anchor(anchor, center, BOT, BOT);
|
||||
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
|
||||
difference() {
|
||||
cyl(r1=r1, r2=r2, h=l);
|
||||
if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
|
||||
difference() {
|
||||
fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true);
|
||||
if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Section: Other Round Objects
|
||||
|
||||
|
@ -2204,59 +2263,6 @@ module nil() union(){}
|
|||
module noop(spin=0, orient=UP) attachable(CENTER,spin,orient, d=0.01) {nil(); children();}
|
||||
|
||||
|
||||
// Module: pie_slice()
|
||||
//
|
||||
// Description:
|
||||
// Creates a pie slice shape.
|
||||
//
|
||||
// Usage: Typical
|
||||
// pie_slice(l|h, r, ang, [center]);
|
||||
// pie_slice(l|h, d=, ang=, ...);
|
||||
// pie_slice(l|h, r1=|d1=, r2=|d2=, ang=, ...);
|
||||
// Usage: Attaching Children
|
||||
// pie_slice(l|h, r, ang, ...) [attachments];
|
||||
//
|
||||
// Arguments:
|
||||
// h / l = height of pie slice.
|
||||
// r = radius of pie slice.
|
||||
// ang = pie slice angle in degrees.
|
||||
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
|
||||
// ---
|
||||
// r1 = bottom radius of pie slice.
|
||||
// r2 = top radius of pie slice.
|
||||
// d = diameter of pie slice.
|
||||
// d1 = bottom diameter of pie slice.
|
||||
// d2 = top diameter of pie slice.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
//
|
||||
// Example: Cylindrical Pie Slice
|
||||
// pie_slice(ang=45, l=20, r=30);
|
||||
// Example: Conical Pie Slice
|
||||
// pie_slice(ang=60, l=20, d1=50, d2=70);
|
||||
module pie_slice(
|
||||
h, r, ang=30, center,
|
||||
r1, r2, d, d1, d2, l,
|
||||
anchor, spin=0, orient=UP
|
||||
) {
|
||||
l = first_defined([l, h, 1]);
|
||||
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10);
|
||||
r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10);
|
||||
maxd = max(r1,r2)+0.1;
|
||||
anchor = get_anchor(anchor, center, BOT, BOT);
|
||||
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
|
||||
difference() {
|
||||
cyl(r1=r1, r2=r2, h=l);
|
||||
if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
|
||||
difference() {
|
||||
fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true);
|
||||
if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: interior_fillet()
|
||||
|
|
38
skin.scad
38
skin.scad
|
@ -832,8 +832,13 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
|
|||
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
|
||||
fullcaps = is_bool(caps) ? [caps,caps] : caps,
|
||||
normalOK = is_undef(normal) || (method!="natural" && is_vector(normal,3))
|
||||
|| (method=="manual" && same_shape(normal,path))
|
||||
)
|
||||
assert(normalOK, method=="natural" ? "Cannot specify normal with the \"natural\" method"
|
||||
: method=="incremental" ? "Normal with \"incremental\" method must be a 3-vector"
|
||||
: str("Incompatible normal given. Must be a 3-vector or a list of ",len(path)," 3-vectors"))
|
||||
assert(capsOK, "caps must be boolean or a list of two booleans")
|
||||
assert(!closed || !caps, "Cannot make closed shape with caps")
|
||||
assert(is_undef(normal) || (is_vector(normal) && len(normal)==3) || (is_path(normal) && len(normal)==len(path) && len(normal[0])==3), "Invalid normal specified")
|
||||
|
@ -1158,37 +1163,6 @@ function subdivide_and_slice(profiles, slices, numpoints, method="length", close
|
|||
|
||||
|
||||
|
||||
// Function: subdivide_long_segments()
|
||||
// Topics: Paths, Path Subdivision
|
||||
// See Also: subdivide_path(), subdivide_and_slice(), path_add_jitter(), jittered_poly()
|
||||
// Usage:
|
||||
// spath = subdivide_long_segments(path, maxlen, [closed=]);
|
||||
// Description:
|
||||
// Evenly subdivides long `path` segments until they are all shorter than `maxlen`.
|
||||
// Arguments:
|
||||
// path = The path to subdivide.
|
||||
// maxlen = The maximum allowed path segment length.
|
||||
// ---
|
||||
// closed = If true, treat path like a closed polygon. Default: true
|
||||
// Example:
|
||||
// path = pentagon(d=100);
|
||||
// spath = subdivide_long_segments(path, 10, closed=true);
|
||||
// stroke(path);
|
||||
// color("lightgreen") move_copies(path) circle(d=5,$fn=12);
|
||||
// color("blue") move_copies(spath) circle(d=3,$fn=12);
|
||||
function subdivide_long_segments(path, maxlen, closed=false) =
|
||||
assert(is_path(path))
|
||||
assert(is_finite(maxlen))
|
||||
assert(is_bool(closed))
|
||||
[
|
||||
for (p=pair(path,closed)) let(
|
||||
steps = ceil(norm(p[1]-p[0])/maxlen)
|
||||
) each lerpn(p[0], p[1], steps, false),
|
||||
if (!closed) last(path)
|
||||
];
|
||||
|
||||
|
||||
|
||||
// Function: slice_profiles()
|
||||
// Topics: Paths, Path Subdivision
|
||||
// Usage:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
include <../std.scad>
|
||||
include <../torx_drive.scad>
|
||||
include <../screw_drive.scad>
|
||||
|
||||
|
||||
module test_torx_outer_diam() {
|
199
torx_drive.scad
199
torx_drive.scad
|
@ -1,199 +0,0 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: torx_drive.scad
|
||||
// Torx driver bits
|
||||
// Includes:
|
||||
// include <BOSL2/std.scad>
|
||||
// include <BOSL2/torx_drive.scad>
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Section: Functions
|
||||
|
||||
|
||||
// Function: torx_outer_diam()
|
||||
// Description: Get the typical outer diameter of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_outer_diam(size) = lookup(size, [
|
||||
[ 6, 1.75],
|
||||
[ 8, 2.40],
|
||||
[ 10, 2.80],
|
||||
[ 15, 3.35],
|
||||
[ 20, 3.95],
|
||||
[ 25, 4.50],
|
||||
[ 30, 5.60],
|
||||
[ 40, 6.75],
|
||||
[ 45, 7.93],
|
||||
[ 50, 8.95],
|
||||
[ 55, 11.35],
|
||||
[ 60, 13.45],
|
||||
[ 70, 15.70],
|
||||
[ 80, 17.75],
|
||||
[ 90, 20.20],
|
||||
[100, 22.40]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_inner_diam()
|
||||
// Description: Get typical inner diameter of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_inner_diam(size) = lookup(size, [
|
||||
[ 6, 1.27],
|
||||
[ 8, 1.75],
|
||||
[ 10, 2.05],
|
||||
[ 15, 2.40],
|
||||
[ 20, 2.85],
|
||||
[ 25, 3.25],
|
||||
[ 30, 4.05],
|
||||
[ 40, 4.85],
|
||||
[ 45, 5.64],
|
||||
[ 50, 6.45],
|
||||
[ 55, 8.05],
|
||||
[ 60, 9.60],
|
||||
[ 70, 11.20],
|
||||
[ 80, 12.80],
|
||||
[ 90, 14.40],
|
||||
[100, 16.00]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_depth()
|
||||
// Description: Gets typical drive hole depth.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_depth(size) = lookup(size, [
|
||||
[ 6, 1.82],
|
||||
[ 8, 3.05],
|
||||
[ 10, 3.56],
|
||||
[ 15, 3.81],
|
||||
[ 20, 4.07],
|
||||
[ 25, 4.45],
|
||||
[ 30, 4.95],
|
||||
[ 40, 5.59],
|
||||
[ 45, 6.22],
|
||||
[ 50, 6.48],
|
||||
[ 55, 6.73],
|
||||
[ 60, 8.17],
|
||||
[ 70, 8.96],
|
||||
[ 80, 9.90],
|
||||
[ 90, 10.56],
|
||||
[100, 11.35]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_tip_radius()
|
||||
// Description: Gets minor rounding radius of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_tip_radius(size) = lookup(size, [
|
||||
[ 6, 0.132],
|
||||
[ 8, 0.190],
|
||||
[ 10, 0.229],
|
||||
[ 15, 0.267],
|
||||
[ 20, 0.305],
|
||||
[ 25, 0.375],
|
||||
[ 30, 0.451],
|
||||
[ 40, 0.546],
|
||||
[ 45, 0.574],
|
||||
[ 50, 0.775],
|
||||
[ 55, 0.867],
|
||||
[ 60, 1.067],
|
||||
[ 70, 1.194],
|
||||
[ 80, 1.526],
|
||||
[ 90, 1.530],
|
||||
[100, 1.720]
|
||||
]);
|
||||
|
||||
|
||||
// Function: torx_rounding_radius()
|
||||
// Description: Gets major rounding radius of Torx profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
function torx_rounding_radius(size) = lookup(size, [
|
||||
[ 6, 0.383],
|
||||
[ 8, 0.510],
|
||||
[ 10, 0.598],
|
||||
[ 15, 0.716],
|
||||
[ 20, 0.859],
|
||||
[ 25, 0.920],
|
||||
[ 30, 1.194],
|
||||
[ 40, 1.428],
|
||||
[ 45, 1.796],
|
||||
[ 50, 1.816],
|
||||
[ 55, 2.667],
|
||||
[ 60, 2.883],
|
||||
[ 70, 3.477],
|
||||
[ 80, 3.627],
|
||||
[ 90, 4.468],
|
||||
[100, 4.925]
|
||||
]);
|
||||
|
||||
|
||||
// Section: Modules
|
||||
|
||||
|
||||
// Module: torx_drive2d()
|
||||
// Description: Creates a torx bit 2D profile.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
// Example(2D):
|
||||
// torx_drive2d(size=30, $fa=1, $fs=1);
|
||||
module torx_drive2d(size) {
|
||||
od = torx_outer_diam(size);
|
||||
id = torx_inner_diam(size);
|
||||
tip = torx_tip_radius(size);
|
||||
rounding = torx_rounding_radius(size);
|
||||
base = od - 2*tip;
|
||||
$fn = quantup(segs(od/2),12);
|
||||
difference() {
|
||||
union() {
|
||||
circle(d=base);
|
||||
zrot_copies(n=2) {
|
||||
hull() {
|
||||
zrot_copies(n=3) {
|
||||
translate([base/2,0,0]) {
|
||||
circle(r=tip, $fn=$fn/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
zrot_copies(n=6) {
|
||||
zrot(180/6) {
|
||||
translate([id/2+rounding,0,0]) {
|
||||
circle(r=rounding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Module: torx_drive()
|
||||
// Description: Creates a torx bit tip.
|
||||
// Arguments:
|
||||
// size = Torx size.
|
||||
// l = Length of bit.
|
||||
// center = If true, centers bit vertically.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Examples:
|
||||
// torx_drive(size=30, l=10, $fa=1, $fs=1);
|
||||
module torx_drive(size, l=5, center, anchor, spin=0, orient=UP) {
|
||||
anchor = get_anchor(anchor, center, BOT, BOT);
|
||||
od = torx_outer_diam(size);
|
||||
attachable(anchor,spin,orient, d=od, l=l) {
|
||||
linear_extrude(height=l, convexity=4, center=true) {
|
||||
torx_drive2d(size);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
119
vnf.scad
119
vnf.scad
|
@ -9,9 +9,8 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Creating Polyhedrons with VNF Structures
|
||||
// Section: Creating Polyhedrons with VNF Structures
|
||||
|
||||
// Section: VNF Testing and Access
|
||||
// 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
|
||||
// list. Each VNF is self contained, with face indices referring only to its own vertex list.
|
||||
|
@ -21,8 +20,6 @@
|
|||
EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
||||
|
||||
|
||||
// Section: Constructing VNFs
|
||||
|
||||
// Function: vnf_vertex_array()
|
||||
// Usage:
|
||||
// vnf = vnf_vertex_array(points, [caps], [cap1], [cap2], [style], [reverse], [col_wrap], [row_wrap], [vnf]);
|
||||
|
@ -207,12 +204,12 @@ function vnf_vertex_array(
|
|||
// points = List of point lists for each row
|
||||
// row_wrap = If true then add faces connecting the first row and last row. These rows must differ by at most 2 in length.
|
||||
// reverse = Set this to reverse the direction of the faces
|
||||
// Examples: Each row has one more point than the preceeding one.
|
||||
// Example: Each row has one more point than the preceeding one.
|
||||
// pts = [for(y=[1:1:10]) [for(x=[0:y-1]) [x,y,y]]];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
// vnf_wireframe(vnf,d=.1);
|
||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||
// Examples: Each row has one more point than the preceeding one.
|
||||
// Example: Each row has one more point than the preceeding one.
|
||||
// pts = [for(y=[0:2:10]) [for(x=[-y/2:y/2]) [x,y,y]]];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
// vnf_wireframe(vnf,d=.1);
|
||||
|
@ -277,58 +274,6 @@ function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) =
|
|||
vnf_merge(cleanup=true, [vnf, [flatten(points), faces]]);
|
||||
|
||||
|
||||
// Function: vnf_add_face()
|
||||
// Usage:
|
||||
// vnf_add_face(vnf, pts);
|
||||
// Description:
|
||||
// Given a VNF structure and a list of face vertex points, adds the face to the VNF structure.
|
||||
// Returns the modified VNF structure `[VERTICES, FACES]`. It is up to the caller to make
|
||||
// sure that the points are in the correct order to make the face normal point outwards.
|
||||
// Arguments:
|
||||
// vnf = The VNF structure to add a face to.
|
||||
// pts = The vertex points for the face.
|
||||
function vnf_add_face(vnf=EMPTY_VNF, pts) =
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_path(pts))
|
||||
let(
|
||||
res = set_union(vnf[0], pts, get_indices=true),
|
||||
face = deduplicate(res[0], closed=true)
|
||||
) [
|
||||
res[1],
|
||||
concat(vnf[1], len(face)>2? [face] : [])
|
||||
];
|
||||
|
||||
|
||||
// Function: vnf_add_faces()
|
||||
// Usage:
|
||||
// vnf_add_faces(vnf, faces);
|
||||
// Description:
|
||||
// Given a VNF structure and a list of faces, where each face is given as a list of vertex points,
|
||||
// adds the faces to the VNF structure. Returns the modified VNF structure `[VERTICES, FACES]`.
|
||||
// It is up to the caller to make sure that the points are in the correct order to make the face
|
||||
// normals point outwards.
|
||||
// Arguments:
|
||||
// vnf = The VNF structure to add a face to.
|
||||
// faces = The list of faces, where each face is given as a list of vertex points.
|
||||
function vnf_add_faces(vnf=EMPTY_VNF, faces) =
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_list(faces))
|
||||
let(
|
||||
res = set_union(vnf[0], flatten(faces), get_indices=true),
|
||||
idxs = res[0],
|
||||
nverts = res[1],
|
||||
offs = cumsum([0, for (face=faces) len(face)]),
|
||||
ifaces = [
|
||||
for (i=idx(faces)) [
|
||||
for (j=idx(faces[i]))
|
||||
idxs[offs[i]+j]
|
||||
]
|
||||
]
|
||||
) [
|
||||
nverts,
|
||||
concat(vnf[1],ifaces)
|
||||
];
|
||||
|
||||
|
||||
// Function: vnf_merge()
|
||||
// Usage:
|
||||
|
@ -381,6 +326,64 @@ function vnf_merge(vnfs, cleanup=false, eps=EPSILON) =
|
|||
[nverts, nfaces];
|
||||
|
||||
|
||||
|
||||
// Function: vnf_add_face()
|
||||
// Usage:
|
||||
// vnf_add_face(vnf, pts);
|
||||
// Description:
|
||||
// Given a VNF structure and a list of face vertex points, adds the face to the VNF structure.
|
||||
// Returns the modified VNF structure `[VERTICES, FACES]`. It is up to the caller to make
|
||||
// sure that the points are in the correct order to make the face normal point outwards.
|
||||
// Arguments:
|
||||
// vnf = The VNF structure to add a face to.
|
||||
// pts = The vertex points for the face.
|
||||
function vnf_add_face(vnf=EMPTY_VNF, pts) =
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_path(pts))
|
||||
let(
|
||||
res = set_union(vnf[0], pts, get_indices=true),
|
||||
face = deduplicate(res[0], closed=true)
|
||||
) [
|
||||
res[1],
|
||||
concat(vnf[1], len(face)>2? [face] : [])
|
||||
];
|
||||
|
||||
|
||||
|
||||
// Function: vnf_add_faces()
|
||||
// Usage:
|
||||
// vnf_add_faces(vnf, faces);
|
||||
// Description:
|
||||
// Given a VNF structure and a list of faces, where each face is given as a list of vertex points,
|
||||
// adds the faces to the VNF structure. Returns the modified VNF structure `[VERTICES, FACES]`.
|
||||
// It is up to the caller to make sure that the points are in the correct order to make the face
|
||||
// normals point outwards.
|
||||
// Arguments:
|
||||
// vnf = The VNF structure to add a face to.
|
||||
// faces = The list of faces, where each face is given as a list of vertex points.
|
||||
function vnf_add_faces(vnf=EMPTY_VNF, faces) =
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_list(faces))
|
||||
let(
|
||||
res = set_union(vnf[0], flatten(faces), get_indices=true),
|
||||
idxs = res[0],
|
||||
nverts = res[1],
|
||||
offs = cumsum([0, for (face=faces) len(face)]),
|
||||
ifaces = [
|
||||
for (i=idx(faces)) [
|
||||
for (j=idx(faces[i]))
|
||||
idxs[offs[i]+j]
|
||||
]
|
||||
]
|
||||
) [
|
||||
nverts,
|
||||
concat(vnf[1],ifaces)
|
||||
];
|
||||
|
||||
|
||||
// Section: VNF Testing and Access
|
||||
|
||||
|
||||
// Function: is_vnf()
|
||||
// Usage:
|
||||
// bool = is_vnf(x);
|
||||
|
|
Loading…
Reference in a new issue