Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Richard Milewski
2025-05-13 18:15:00 -07:00
8 changed files with 132 additions and 63 deletions

View File

@@ -767,6 +767,7 @@ function _make_anchor_legal(anchor,geom) =
// This module differs from {{position()}} and {{align()}} in that it rotates the children to
// the anchor direction, which generally means it places the children on the surface of a parent.
// There are two modes of operation, parent anchor (single argument) and parent-child anchor (double argument).
// In most cases you should use the parent-child (double argument) version of `attach()`.
// .
// The parent-child anchor (double argument) version is usually easier to use, and it is more powerful because it supports
// alignment. You provide an anchor on the parent (`parent`) and an anchor on the child (`child`).
@@ -810,17 +811,6 @@ function _make_anchor_legal(anchor,geom) =
// ignored** with the **double argument** version of `attach()`. As noted above, you can give `spin=` to the
// child but using the `spin=` parameter to `attach()` is more likely to be useful.
// .
// For the single parameter version of `attach()` you give only the `parent` anchor. The `align` direction
// is not permitted. In this case the child is placed at the specified parent anchor point
// and rotated to the anchor direction. For example, `attach(TOP) cuboid(2);` will place a small
// cube **with its center** located at the TOP anchor of the parent, so just half the cube will project
// from the parent. If you want the cube sitting on the parent you need to anchor the cube to its bottom:
// `attach(TOP) cuboid(2,anchor=BOT);`.
// .
// The **single argument** version of `attach()` **respects `anchor=` and `orient=` given to the child.**
// These options will probably be necessary, in fact, to get the child correctly positioned. Note that
// giving `spin=` to `attach()` in this case is the same as applying `zrot()` to the child.
// .
// You can overlap attached children into the parent by giving the `$overlap` value
// which is 0 by default, or by the `overlap=` argument. This is to prevent OpenSCAD
// from making non-manifold objects. You can define `$overlap=` as an argument in a parent
@@ -833,6 +823,17 @@ function _make_anchor_legal(anchor,geom) =
// the parent. For an inside child this is equivalent to giving a positive overlap and negative inset value.
// For a child with `inside=false` it is equivalent to a negative overlap and negative inset.
// .
// The single parameter version of `attach()` is rarely needed; to use it, you give only the `parent` anchor. The `align` direction
// is not permitted. In this case the child is placed at the specified parent anchor point
// and rotated to the anchor direction. For example, `attach(TOP) cuboid(2);` will place a small
// cube **with its center** located at the TOP anchor of the parent, so just half the cube will project
// from the parent. If you want the cube sitting on the parent you need to anchor the cube to its bottom:
// `attach(TOP) cuboid(2,anchor=BOT);`.
// .
// The **single argument** version of `attach()` **respects `anchor=` and `orient=` given to the child.**
// These options will probably be necessary, in fact, to get the child correctly positioned. Note that
// giving `spin=` to `attach()` in this case is the same as applying `zrot()` to the child.
// .
// For a step-by-step explanation of
// attachments, see the [Attachments Tutorial](Tutorial-Attachments).
// Arguments:

View File

@@ -1267,7 +1267,6 @@ function bezier_vnf(patches=[], splinesteps=16, style="default") =
: assert(false,"\nInvalid patch list.")
]
);
// Function: bezier_vnf_degenerate_patch()
@@ -1283,7 +1282,8 @@ function bezier_vnf(patches=[], splinesteps=16, style="default") =
// equal. If the resulting patch has no faces then returns an empty VNF. Note that due to the degeneracy,
// the shape of the surface can be triangular even though the underlying patch is a rectangle.
// If you specify return_edges then the return is a list whose first element is the VNF and whose second
// element lists the edges in the order [left, right, top, bottom], where each list is a list of the actual
// element lists the edges in the order [left (index zero of rows), right (last index of rows), top (first row), bottom (last row)],
// where each list is a list of the actual
// point values, but possibly only a single point if that edge is degenerate.
// The method checks for various types of degeneracy and uses a triangular or partly triangular array of sample points.
// See examples below for the types of degeneracy detected and how the patch is sampled for those cases.
@@ -1292,7 +1292,7 @@ function bezier_vnf(patches=[], splinesteps=16, style="default") =
// patch = Patch to process
// splinesteps = Number of segments to produce on each side. Default: 16
// reverse = reverse direction of faces. Default: false
// return_edges = if true return the points on the four edges: [left, right, top, bottom]. Default: false
// return_edges = if true return the points on the four edges of the array: [left (index zero of rows), right (last index of rows) , top (first row), bottom (last row)]. Default: false
// Example(3D,NoAxes): This quartic patch is degenerate at one corner, where a row of control points are equal. Processing this degenerate patch normally produces excess triangles near the degenerate point.
// splinesteps=8;
// patch=[

View File

@@ -17,10 +17,10 @@
// Section: Utility Functions
// Definitions:
// Point|Points = A numeric vector of length 2 or 3 that represents either a 2D or 3D vertex.
// Pointlist|Pointlists|Point List|Point Lists = An unordered list of points.
// Path|Paths = A list of two or more 2D {{point}} coordinates that specify a route on the XY plane.
// Polygon|Polygons = A {{path}} where the first and last {{point}} coordinates are considered to be connected.
// Point|Points = A list of numbers, also called a vector. Usually has length 2 or 3 to represent points in the place on points in space.
// Pointlist|Pointlists|Point List|Point Lists = An unordered list of {{points}}.
// Path|Paths = An ordered list of two or more {{points}} specifying a path through space. Usually points are 2D.
// Polygon|Polygons = A {{path}}, usually 2D, that describes a polygon by asuming that the first and last point are connected.
// Function: is_path()
// Synopsis: Returns True if 'list' is a {{path}}.

View File

@@ -62,9 +62,9 @@
// ];
// region(rgn);
//
// Definitions:
// Region|Regions = A list of zero or more non-intersecting {{polygons}}, representing possibly disjointed shape perimeters with enclosed holes.
// Definitions:
// Region|Regions = A list of one or more non-intersecting {{polygons}} representing a union of one or more disconnected polygons that may have internal holes.
// Function: is_region()
// Synopsis: Returns true if the input appears to be a {{region}}.

View File

@@ -7,11 +7,9 @@
// two prisms together with a rounded fillet at the joint.
// Includes:
// include <BOSL2/std.scad>
// include <BOSL2/rounding.scad>
// FileGroup: Advanced Modeling
// FileSummary: Round path corners, rounded prisms, rounded cutouts in tubes, filleted prism joints
//////////////////////////////////////////////////////////////////////
include <structs.scad>
// Section: Types of Roundovers
// The functions and modules in this file support two different types of roundovers and some different mechanisms for specifying

123
skin.scad
View File

@@ -1731,7 +1731,15 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// You can also apply scaling to the profile along the path. You can give a list of scalar scale factors or a list of 2-vector scale.
// In the latter scale the x and y scales of the profile are scaled separately before the profile is placed onto the path. For non-closed
// paths you can also give a single scale value or a 2-vector, which is treated as the final scale. The intermediate sections
// are then scaled by linear interpolation either relative to length (if scale_by_length is true) or by point count otherwise.
// are then scaled by linear interpolation either relative to length (if scale_by_length is true) or by point count otherwise.
// .
// The `caps` parameter controls what happens at the ends of the polyhedron. If `closed=true` the shape links to itself and has no
// ends, but when `closed` is false, the two ends are, by default capped with flat faces. If you set `caps=false` then the ends
// receive no faces and the resulting non-manifold polyhedron has exposed edges. You can also set caps to a number, which adds a
// rounded cap with the specified radius, or you can set caps to an {{offset_sweep()}} end treatment, and the specified sweep will
// be attached as a cap. Note that you are **adding** a rounded cap, not rounding the specified shape as is common for many other
// library modules. The rounded cap is attached to the end face and may not blend neatly with the swept shape unless the sides of
// the swept shape are perpendicular to the end cap.
// .
// You can use set `transforms` to true to return a list of transformation matrices instead of the swept shape. In this case, you can
// often omit shape entirely. The exception is when `closed=true` and you are using the "incremental" method. In this case, `path_sweep`
@@ -1764,7 +1772,7 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// uniform = if set to false then compute tangents using the uniform=false argument, which may give better results when your path is non-uniformly sampled. This argument is passed to {{path_tangents()}}. Default: true
// tangent = a list of tangent vectors in case you need more accuracy (particularly at the end points of your curve)
// relaxed = set to true with the "manual" method to relax the orthogonality requirement of cross sections to the path tangent. Default: false
// caps = Can be a boolean or vector of two booleans. Set to false to disable caps at the two ends. Default: true
// caps = if closed is false, set caps to false to leave the ends open. Other values are true to create a flat cap, a number a rounded cap, or an {{offset_sweep()}} end treatment to create the specified offset sweep. Can be a single value or pair of values to control the caps independently at each end. Default: true
// style = vnf_vertex_array style. Default: "min_edge"
// profiles = if true then display all the cross section profiles instead of the solid shape. Can help debug a sweep. (module only) Default: false
// width = the width of lines used for profile display. (module only) Default: 1
@@ -2101,6 +2109,15 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// closed=true, twist=360*2/5,symmetry=5,
// texture="bricks_vnf",tex_reps=[10,40],
// tex_depth=.1);
// Example(NoScales): Applying rounded end caps to a sweep
// $fs=1;$fa=1;
// path_sweep(circle(r=5), arc(r=15, angle=[0,230]),caps=2.5);
// Example(NoScales): Using a small `$fn` creates a chamfer on the endcap
// $fs=1;$fa=1;
// path_sweep(circle(r=5), arc(r=15, angle=[0,230]),caps=1, $fn=4);
// Example(NoScales): One flat endcap and one rounding with a negative radius
// $fs=1;$fa=1;
// path_sweep(circle(r=5), arc(r=15, angle=[180,330]),caps=[true, -3]);
module path_sweep(shape, path, method="incremental", normal, closed, twist=0, twist_by_length=true, scale=1, scale_by_length=true,
@@ -2113,9 +2130,6 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
trans_scale = path_sweep(shape, path, method, normal, closed, twist, twist_by_length, scale, scale_by_length,
symmetry, last_normal, tangent, uniform, relaxed, caps, style, transforms=true,_return_scales=true);
caps = is_def(caps) ? caps :
closed ? false : true;
fullcaps = is_bool(caps) ? [caps,caps] : caps;
transforms = trans_scale[0];
scales = trans_scale[1];
firstscale = is_num(scales[0]) ? 1/scales[0] : [1/scales[0].x, 1/scales[0].y];
@@ -2125,7 +2139,7 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw
shape_normals = -path3d(path_normals(clockwise_polygon(shape), closed=true))
)
[for(T=transforms) apply(_force_rot(T),shape_normals)];
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, _closed_for_normals=closed, caps=fullcaps,style=style,
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, _closed_for_normals=closed, caps=caps,style=style,
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples, normals=tex_normals,
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip);
shapecent = point3d(centroid(shape));
@@ -2181,10 +2195,6 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
assert((is_region(shape) || is_path(shape,2)) || (transforms && !(closed && method=="incremental")),"shape must be a 2d path or region")
let(
path = path3d(path),
caps = is_def(caps) ? caps :
closed ? false : true,
capsOK = is_bool(caps) || is_bool_list(caps,2),
fullcaps = is_bool(caps) ? [caps,caps] : caps,
normalOK = is_undef(normal) || (method!="natural" && is_vector(normal,3))
|| (method=="manual" && same_shape(normal,path)),
scaleOK = scale==1 || ((is_num(scale) || is_vector(scale,2)) && !closed) || is_vector(scale,len(path)) || is_matrix(scale,len(path),2)
@@ -2193,8 +2203,6 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
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")
assert(is_undef(tangent) || (is_path(tangent) && len(tangent)==len(path) && len(tangent[0])==3), "Invalid tangent specified")
assert(scaleOK,str("Incompatible or invalid scale",closed?" for closed path":"",": must be ", closed?"":"a scalar, a 2-vector, ",
@@ -2330,7 +2338,7 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
transforms && _return_scales
? [transform_list,scale]
: transforms ? transform_list
: sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=fullcaps,style=style,
: sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=caps,style=style,
anchor=anchor,cp=cp,spin=spin,orient=orient,atype=atype,
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples,
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip,
@@ -2485,9 +2493,16 @@ function _ofs_face_edge(face,firstlen,second=false) =
// is a list of 4x4 transformation matrices. The sweep algorithm applies each transformation in sequence
// to the shape input and links the resulting polygons together to form a polyhedron.
// If `closed=true` then the first and last transformation are linked together.
// The `caps` parameter controls whether the ends of the shape are closed.
// As a function, returns the VNF for the polyhedron. As a module, computes the polyhedron.
// .
// The `caps` parameter controls what happens at the ends of the polyhedron. If `closed=true` the shape links to itself and has no
// ends, but when `closed` is false, the two ends are, by default capped with flat faces. If you set `caps=false` then the ends
// receive no faces and the resulting non-manifold polyhedron has exposed edges. You can also set caps to a number, which adds a
// rounded cap with the specified radius, or you can set caps to an {{offset_sweep()}} end treatment, and the specified sweep will
// be attached as a cap. Note that you are **adding** a rounded cap, not rounding the specified shape as is common for many other
// library modules. The rounded cap is attached to the end face and may not blend neatly with the swept shape unless the sides of
// the swept shape are perpendicular to the end cap.
// .
// This is a powerful, general framework for producing polyhedra. It is important
// to ensure that your resulting polyhedron does not include any self-intersections, or it will
// be invalid and generate CGAL errors. If you get such errors, most likely you have an
@@ -2500,11 +2515,13 @@ function _ofs_face_edge(face,firstlen,second=false) =
// This works by passing through to {{vnf_vertex_array()}}, which also has more details on
// texturing. Note that textures work only when the shape is a path; you cannot apply a texture to a region.
// The texture tiles are oriented on the path sweep so that the Y axis of the tile is aligned with the sweep direction.
// .
//
// Arguments:
// shape = 2d path or region, describing the shape to be swept.
// transforms = list of 4x4 matrices to apply
// closed = set to true to form a closed (torus) model. Default: false
// caps = true to create endcap faces when closed is false. Can be a singe boolean to specify endcaps at both ends, or a length 2 boolean array. Default is true if closed is false.
// caps = if closed is false, set caps to false to leave the ends open. Other values are true to create a flat cap, a number a rounded cap, or an {{offset_sweep()}} end treatment to create the specified offset sweep. Can be a single value or pair of values to control the caps independently at each end. Default: true
// style = vnf_vertex_array style. Default: "min_edge"
// ---
// convexity = convexity setting for use with polyhedron. (module only) Default: 10
@@ -2570,15 +2587,24 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
assert(is_consistent(transforms, ident(4)), "Input transforms must be a list of numeric 4x4 matrices in sweep")
assert(is_path(shape,2) || is_region(shape), "Input shape must be a 2d path or a region.")
let(
caps = is_def(caps) ? caps :
closed ? false : true,
capsOK = is_bool(caps) || is_bool_list(caps,2),
fullcaps = is_bool(caps) ? [caps,caps] : caps
caps = is_list(caps) && select(caps,0,1)==["for","offset_sweep"] ? [caps,caps]
: is_bool(caps) || is_num(caps) ? [caps,caps]
: is_undef(caps) ? closed ? [false,false] : [true,true]
: caps,
capsOK = is_list(caps) && len(caps)==2
&&
[] == [for(cap=caps)
if (!(is_bool(cap) || is_num(cap) || select(cap,0,1)==["for","offset_sweep"])) 1],
flatcaps = [for(cap=caps) is_bool(cap) ? cap : false],
fancycaps = [for(cap=caps) is_bool(cap) ? false
: is_num(cap) ? os_circle(r=cap,steps=ceil(segs(cap)/4))
: cap]
)
assert(len(transforms)>=2, "transformation must be length 2 or more")
assert(capsOK, "caps must be boolean or a list of two booleans")
assert(!closed || !caps, "Cannot make closed shape with caps")
assert(capsOK, "caps must be boolean, number, an offset_sweep specification, or a list of two of those")
assert(!closed || caps==[false,false], "Cannot make closed shape with caps")
is_region(shape)?
assert(fancycaps==[false,false], "rounded caps are not supported for regions")
assert(is_undef(texture), "textures are not supported for regions, only paths")
let(
regions = region_parts(shape),
@@ -2587,8 +2613,8 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
for (rgn=regions) each [
for (path=rgn)
sweep(path, transforms, closed=closed, caps=false, style=style),
if (fullcaps[0]) vnf_from_region(rgn, transform=transforms[0], reverse=true),
if (fullcaps[1]) vnf_from_region(rgn, transform=last(transforms)),
if (flatcaps[0]) vnf_from_region(rgn, transform=transforms[0], reverse=true),
if (flatcaps[1]) vnf_from_region(rgn, transform=last(transforms)),
],
],
vnf = vnf_join(vnfs)
@@ -2603,12 +2629,22 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
: let(
n = surface_normals(select(points,0,-2), col_wrap=true, row_wrap=true)
)
[each n, n[0]]
)
vnf_vertex_array(points, normals=normals,
cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style,
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples,
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip);
[each n, n[0]],
vva_result = vnf_vertex_array(points, normals=normals,
cap1=flatcaps[0],cap2=flatcaps[1],col_wrap=true,style=style, return_edges=fancycaps!=[false,false],
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples,
tex_inset=tex_inset, tex_rot=tex_rot, tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip),
vnf = fancycaps==[false,false] ? vva_result
: vnf_join(
[ vva_result[0],
for(ind=[0,1])
if (fancycaps[ind]) let(
polygon = vva_result[1][ind+2],
plane = plane_from_polygon(ind==0? reverse(polygon) : polygon)
)
apply(lift_plane(plane),offset_sweep(project_plane(plane, polygon), top=fancycaps[ind], caps=[false,true]))
])
) vnf;
module sweep(shape, transforms, closed=false, caps, style="min_edge", convexity=10,
@@ -4952,7 +4988,7 @@ module _textured_revolution(
}
function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0, triangulate=false, tex_scaling="default",
function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0, triangulate=false, tex_scaling="default",return_edges=false,
col_wrap=false, tex_depth=1, row_wrap=false, caps, cap1, cap2, reverse=false, style="min_edge", tex_extra, tex_skip, sidecaps,sidecap1,sidecap2,normals) =
assert(tex_reps==undef || is_int(tex_reps) || (all_integer(tex_reps) && len(tex_reps)==2), "tex_reps must be an integer or list of two integers")
assert(tex_size==undef || is_num(tex_size) || is_vector(tex_size,2), "tex_size must be a scalar or 2-vector")
@@ -5016,7 +5052,8 @@ function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples,
]
]
)
vnf_vertex_array(tex_surf, row_wrap=row_wrap, col_wrap=col_wrap, reverse=reverse,style=style, caps=caps, cap1=cap1, cap2=cap2, triangulate=triangulate)
vnf_vertex_array(tex_surf, row_wrap=row_wrap, col_wrap=col_wrap, reverse=reverse,style=style,
caps=caps, cap1=cap1, cap2=cap2, triangulate=triangulate, return_edges=return_edges)
: // VNF case
let(
local_scale = [for(y=[-1:1:ptsize.y])
@@ -5068,7 +5105,7 @@ function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples,
)
base + _tex_height(tex_depth,tex_inset,pt.z) * normal*(reverse?-1:1) * scale,
fullvnf = vnf_join([
for(y=[0:1:tex_reps.y-1], x=[0:1:tex_reps.x-1])
for(y=[0:1:tex_reps.y-1], x=[0:1:tex_reps.x-1]) // Main body of the textured shape
[
[for(pt=vnf[0]) trans_pt(x,y,pt)],
vnf[1]
@@ -5092,9 +5129,27 @@ function _textured_point_array(points, texture, tex_reps, tex_size, tex_samples,
[for(pt = closed_path) trans_pt(x,y,[x?1:0,pt.y,pt.z])]]
)
for(path=cap_paths) [path, [count(path,reverse=x!=0)]]
])
]),
edgepaths = !return_edges ? undef
: [
if (!col_wrap)
for(x=[0, tex_reps.x-1])
[for(y=[0:1:tex_reps.y-1],pt=xedge_paths[0][0])
trans_pt(x,y,[x?1:0,pt.y,pt.z])]
else each [[],[]],
if (!row_wrap && len(yedge_paths[0])>0)
for(ind=[0,1])
if ([cap1,cap2][ind]) []
else let(y=[0,tex_reps.y-1][ind])
[for(x=[0:1:tex_reps.x-1], pt=yedge_paths[0][0])
trans_pt(x,y,[pt.x,y?0:1,pt.z])]
else each [[],[]]
],
revvnf = reverse ? vnf_reverse_faces(fullvnf) : fullvnf
)
reverse ? vnf_reverse_faces(fullvnf) : fullvnf;
!return_edges ? revvnf : [revvnf, edgepaths];
// Resamples a point array to the specified size.

View File

@@ -35,10 +35,11 @@ include <geometry.scad>
include <regions.scad>
include <strings.scad>
include <vnf.scad>
include <structs.scad>
include <rounding.scad>
include <skin.scad>
include <utility.scad>
include <partitions.scad>
include <structs.scad>
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View File

@@ -73,6 +73,12 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
// For creating the texture, `vnf_vertex_array()` uses normals to the surface that it estimates from the surface data itself.
// If you have more accurate normals or need the normals to take particular values, you can pass an array of normals
// using the `normals` parameter.
// .
// You can set `return_edges=true` to return the paths of the four edges of the output. In this case the return value
// is `[vnf,edgelist]` where edgelist is [left (column 0 of points), right (last column of points), top (points[0]), bottom (last(points)]. If a given
// edge does not exist then it will be the empty list in the output. An edge only exists it is not capped and not wrapped. The main
// need for this feature is when you have added a texture and need a way to interface the shape with something else. In this case you cannot
// easily determine the edges yourself from the input point list. edges are not easily
// Arguments:
// points = A list of vertices to divide into columns and rows.
// ---
@@ -99,6 +105,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
// sidecap2 = set sidecap only for the `points[][max]` edge of the output
// tex_scaling = set to "const" to disable grid size vertical scaling of the texture. Default: "default"
// normals = array of normal vectors to each point in the point array for more accurate texture height calculation
// return_edges = if true return [vnf,edgelist] where edgelist is the paths of four edges, [left (column 0 of points), right (last column of points), top (points[0]), bottom (last(points)]. Default: false
// cp = (module) Centerpoint for determining intersection anchors or centering the shape. Determines the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
// anchor = (module) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"`
// spin = (module) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
@@ -326,7 +333,7 @@ function vnf_vertex_array(
row_wrap=false,
reverse=false,
style="default",
triangulate = false,
triangulate = false, return_edges=false,
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0, tex_scaling="default",
tex_depth=1, tex_extra, tex_skip, sidecaps,sidecap1,sidecap2, normals
) =
@@ -336,7 +343,7 @@ function vnf_vertex_array(
assert(is_bool(triangulate))
is_def(texture) ?
_textured_point_array(points=points, texture=texture, tex_reps=tex_reps, tex_size=tex_size,
tex_inset=tex_inset, tex_samples=tex_samples, tex_rot=tex_rot, tex_scaling=tex_scaling,
tex_inset=tex_inset, tex_samples=tex_samples, tex_rot=tex_rot, tex_scaling=tex_scaling, return_edges=return_edges,
col_wrap=col_wrap, row_wrap=row_wrap, tex_depth=tex_depth, caps=caps, cap1=cap1, cap2=cap2, reverse=reverse,
style=style, tex_extra=tex_extra, tex_skip=tex_skip, sidecaps=sidecaps, sidecap1=sidecap1, sidecap2=sidecap2,normals=normals,triangulate=triangulate)
:
@@ -431,10 +438,17 @@ function vnf_vertex_array(
)
rfaces,
],
vnf = [verts, allfaces]
) triangulate? vnf_triangulate(vnf) : vnf;
vnf = [verts, allfaces],
tvnf = triangulate? vnf_triangulate(vnf) : vnf
)
!return_edges ? tvnf
: [tvnf, [
if (!col_wrap) deduplicate(column(points,0)) else [],
if (!col_wrap) deduplicate(column(points, len(points[0])-1)) else [],
if (!cap1 && !row_wrap) deduplicate(points[0]) else [],
if (!cap2 && !row_wrap) deduplicate(last(points)) else []
]
];
// Function&Module: vnf_tri_array()
// Synopsis: Returns a VNF from an array of points. The array need not be rectangular.