texture fixes, relax checks, and added docs

This commit is contained in:
Adrian Mariano 2022-12-17 13:51:52 -05:00
parent 963fdead80
commit 732033196a

180
skin.scad
View file

@ -507,7 +507,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
// Function&Module: linear_sweep() // Function&Module: linear_sweep()
// Usage: // Usage:
// linear_sweep(region, [height], [center=], [slices=], [twist=], [scale=], [style=], [convexity=]) [ATTACHMENTS]; // linear_sweep(region, [height], [center=], [slices=], [twist=], [scale=], [style=], [caps=], [convexity=]) [ATTACHMENTS];
// Usage: With Texturing // Usage: With Texturing
// linear_sweep(region, [height], [center=], texture=, [tex_size=]|[tex_counts=], [tex_scale=], [style=], [tex_samples=], ...) [ATTACHMENTS]; // linear_sweep(region, [height], [center=], texture=, [tex_size=]|[tex_counts=], [tex_scale=], [style=], [tex_samples=], ...) [ATTACHMENTS];
// Description: // Description:
@ -534,6 +534,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
// tex_scale = Scaling multiplier for the texture depth. // tex_scale = Scaling multiplier for the texture depth.
// tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 // tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8
// style = The style to use when triangulating the surface of the object. Valid values are `"default"`, `"alt"`, or `"quincunx"`. // style = The style to use when triangulating the surface of the object. Valid values are `"default"`, `"alt"`, or `"quincunx"`.
// caps = If false do not create end caps. Can be a boolean vector. Default: true
// convexity = Max number of surfaces any single ray could pass through. Module use only. // convexity = Max number of surfaces any single ray could pass through. Module use only.
// cp = 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"` // cp = 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"`
// atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" // atype = Set to "hull" or "intersect" to select anchor type. Default: "hull"
@ -624,6 +625,22 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
// ] // ]
// ]; // ];
// linear_sweep(path, texture=tex, tex_size=[5,5], h=40); // linear_sweep(path, texture=tex, tex_size=[5,5], h=40);
// Example: VNF tile that has no top/bottom edges and produces a disconnected result
// shape = skin([
// rect(2/5),
// rect(2/3),
// rect(2/5)
// ],
// z=[0,1/2,1],
// slices=0,
// caps=false);
// tile = move([0,1/2,2/3],yrot(90,shape));
// linear_sweep(
// circle(20), texture=tile,
// tex_size=[10,10],tex_scale=5,
// h=40,convexity=4);
// Example: As Function // Example: As Function
// path = glued_circles(r=15, spread=40, tangent=45); // path = glued_circles(r=15, spread=40, tangent=45);
// vnf = linear_sweep( // vnf = linear_sweep(
@ -634,7 +651,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
module linear_sweep( module linear_sweep(
region, height, center, region, height, center,
twist=0, scale=1, shift=[0,0], twist=0, scale=1, shift=[0,0],
slices, maxseg, style="default", convexity, slices, maxseg, style="default", convexity, caps=true,
texture, tex_size=[5,5], tex_counts, texture, tex_size=[5,5], tex_counts,
tex_inset=false, tex_rot=false, tex_inset=false, tex_rot=false,
tex_scale=1, tex_samples, tex_scale=1, tex_samples,
@ -648,7 +665,7 @@ module linear_sweep(
center == false? "original_base" : center == false? "original_base" :
default(anchor, "original_base"); default(anchor, "original_base");
vnf = linear_sweep( vnf = linear_sweep(
region, height=h, style=style, region, height=h, style=style, caps=caps,
twist=twist, scale=scale, shift=shift, twist=twist, scale=scale, shift=shift,
texture=texture, texture=texture,
tex_size=tex_size, tex_size=tex_size,
@ -685,7 +702,7 @@ module linear_sweep(
function linear_sweep( function linear_sweep(
region, height, center, region, height, center,
twist=0, scale=1, shift=[0,0], twist=0, scale=1, shift=[0,0],
slices, maxseg, style="default", slices, maxseg, style="default", caps=true,
cp, atype="hull", h, cp, atype="hull", h,
texture, tex_size=[5,5], tex_counts, texture, tex_size=[5,5], tex_counts,
tex_inset=false, tex_rot=false, tex_inset=false, tex_rot=false,
@ -696,11 +713,12 @@ function linear_sweep(
assert(is_region(region), "Input is not a region or polygon.") assert(is_region(region), "Input is not a region or polygon.")
assert(is_num(scale) || is_vector(scale)) assert(is_num(scale) || is_vector(scale))
assert(is_vector(shift, 2), str(shift)) assert(is_vector(shift, 2), str(shift))
assert(is_bool(caps) || is_bool_list(caps,2), "caps must be boolean or a list of two booleans")
let( let(
h = first_defined([h, height, 1]) h = first_defined([h, height, 1])
) )
!is_undef(texture)? _textured_linear_sweep( !is_undef(texture)? _textured_linear_sweep(
region, h=h, region, h=h, caps=caps,
texture=texture, tex_size=tex_size, texture=texture, tex_size=tex_size,
counts=tex_counts, inset=tex_inset, counts=tex_counts, inset=tex_inset,
rot=tex_rot, tex_scale=tex_scale, rot=tex_rot, tex_scale=tex_scale,
@ -709,6 +727,7 @@ function linear_sweep(
anchor=anchor, spin=spin, orient=orient anchor=anchor, spin=spin, orient=orient
) : ) :
let( let(
caps = is_bool(caps) ? [caps,caps] : caps,
anchor = center==true? "origin" : anchor = center==true? "origin" :
center == false? "original_base" : center == false? "original_base" :
default(anchor, "original_base"), default(anchor, "original_base"),
@ -747,8 +766,8 @@ function linear_sweep(
) apply(m, path3d(path)) ) apply(m, path3d(path))
] ]
) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style), ) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style),
for (rgn = regions) vnf_from_region(rgn, down(h/2), reverse=true), if (caps[0]) for (rgn = regions) vnf_from_region(rgn, down(h/2), reverse=true),
for (rgn = trgns) vnf_from_region(rgn, up(h/2), reverse=false) if (caps[1]) for (rgn = trgns) vnf_from_region(rgn, up(h/2), reverse=false)
]), ]),
anchors = [ anchors = [
named_anchor("original_base", [0,0,-h/2], UP) named_anchor("original_base", [0,0,-h/2], UP)
@ -1087,7 +1106,6 @@ function spiral_sweep(poly, h, r, turns=1, taper, center, r1, r2, d, d1, d2, tap
endpoint=false), endpoint=false),
last(anglist) last(anglist)
], ],
e=echo(lenlist=len(interp_ang)),
skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h)), skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h)),
points = [ points = [
for (a = interp_ang) let ( for (a = interp_ang) let (
@ -2494,6 +2512,110 @@ function associate_vertices(polygons, split, curpoly=0) =
// DefineHeader(Table;Headers=Texture Name|Type|Description): Texture Values // DefineHeader(Table;Headers=Texture Name|Type|Description): Texture Values
// Section: Texturing // Section: Texturing
// Some operations are able to add texture to the objects they create. A texture can be any regularly repeated variation in the height of the surface.
// To define a texture you need to specify how the height should vary over a rectangular block that will be repeated to tile the object. Because textures
// are based on rectangular tiling, this means adding textures to curved shapes may result in distortion of the basic texture unit. For example, if you
// texture a cone, the scale of the texture will be larger at the wide end of the cone and smaller at the narrower end of the cone.
// .
// You can specify a texture using to method: a height field or a VNF. For each method you also must specify the scale of the texture, which
// gives the size of the rectangular unit in your object that will correspond to one texture tile. Note that this scale does not preserve
// aspect ratio: you can stretch the texture as desired.
// Subsection: Height Field Texture Maps
// The simplest way to specify a texture map is to give a 2d array of
// height values which specify the height of the texture on a grid.
// Values in the height field should range from 0 to 1. A zero height
// in the height field corresponds to the height of the surface and 1
// the heighest point in the texture.
// .
// Below is an example of a 2d texture described by a "grid" that just contains a single row. Such a texture can be used to create ribbing.
// The texture is [[0, 1, 1, 0]], and the fixture shows three repetitions of the basic texture unit.
// Figure(2D,Big,NoScales):
// ftex1 = [0,1,1,0,0];
// stroke( transpose([count(5),ftex1]), dots=true, dots_width=3,width=.05);
// right(4)stroke( transpose([count(5),ftex1]), dots=true, width=.05,dots_color="red",color="blue",dots_width=3);
// right(8)stroke( transpose([count(5),ftex1]), dots=true, dots_width=3,width=.05);
// stroke([[4,-.3],[8,-.3]],width=.05,endcaps="arrow2",color="black");
// move([6,-.4])color("black")text("Texture Size", size=0.3,anchor=BACK);
// Continues:
// Line segments connect the dots within the texture and also the dots between adjacent texture tiles.
// The size of the texture (specified with `tex_size`) includes the segment that connects the tile to the next one.
// Note that the grid is always uniformly spaced. If you want to keep the texture the same size but make the slope
// steeper you need to add more points.
// Figure(2D,Big,NoScales):
// ftex2 = xscale(4/11,transpose([count(12),[0,1,1,1,1,1,1,1,1,1,0,0]]));
// stroke( ftex2, dots=true, dots_width=3,width=.05);
// right(4)stroke( ftex2, dots=true, width=.05,dots_color="red",color="blue",dots_width=3);
// right(8)stroke( ftex2, dots=true, dots_width=3,width=.05);
// stroke([[4,-.3],[8,-.3]],width=.05,endcaps="arrow2",color="black");
// move([6,-.4])color("black")text("Texture Size", size=0.3,anchor=BACK);
// Continues:
// A more serious limitation of height field textures is that some shapes, such as hexagons or circles, cannot be accurately represented because
// their points don't fall on a grid. Trying to create such shapes is difficult and will require many points to approximate the
// true point positions for the desired shape. This will make the texture slow to compute.
// Another serious limitation is more subtle. In the 2D examples above, it is obvious how to connect the
// dots together. But in 3D example we need to triangulate the points on a grid, and this triangulation is not unique.
// The `style` argument lets you specify how the points are triangulated using the styles supported by {{vnf_vertex_array()}}.
// In the example below we have expanded the 2D example into 3D: [[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]], and we show the
// 3D triangulations produced by the different styles:
// Figure(Big, NoAxes, VPR = [39.2, 0, 13.3], VPT = [3.76242, -5.50969, 4.51854], VPD = 32.0275):
// tex = [
// [0,0,0,0,0],
// [0,1,1,0,0],
// [0,1,1,0,0],
// [0,0,0,0,0],
// [0,0,0,0,0]
// ];
// hm = [for(i=[0:4]) [for(j=[0:4]) [i,-j,tex[i][j]]]];
// types = ["quincunx", "convex", "concave","default","alt","min_edge"];
// grid2d(spacing=5, n=[3,2]){
// let(s = types[$row*3+$col]){
// vnf_polyhedron(vnf_vertex_array(hm,style=s));
// if ($row==1)
// back(.8)right(2)rotate($vpr)color("black")text(s,size=.5,anchor=CENTER);
// else
// fwd(4.7)right(2)rotate($vpr)color("black")text(s,size=.5,anchor=CENTER);
// }
// }
// Continues:
// Note that of the six available styles, five produce a different result. You might think that simply identifying the
// correct style will enable you to get the result you want. This is true if you apply the texture to a convex object, but for concave
// objects, the triangulation rules for the styles may result in different triangulations in different parts of the surface.
// For some cases, none of the six styles produces the correct result in every case.
// [Can this actually happen? We need an example of this!]
// Subsection: VNF Textures
// VNF textures overcome all of the limitations of height field textures, but with two costs. They can be more difficult to construct than
// a simple array of height values, and they are significantly slower to compute for a tile with the same number of points. Note, however, for
// textures that don't neatly lie on a grid, a VNF tile will be more efficient than a finely sampled height field. With VNF textures you can create
// textures that have disconnected components, or concavities that cannot be expressed with a single valued height map. However, you can also
// create invalid textures that fail to close at the ends, so care is required to ensure that your resulting shape is valid.
// .
// A VNF texture is defined by defining the texture tile with a VNF whose projection onto the XY plane is contained in the unit square [0,1] x [0,1] so
// that the VNF can be tiled. The VNF is tiled without a gap, matching the edges, so the vertices along corresponding edges must match to make a
// consistent triangulation possible. The VNF cannot have any X or Y values outside the interval [0,1]. If you want a valid polyhedron
// that OpenSCAD will render then you need to take care with edges of the tiles that correspond to endcap faces in the textured object.
// So for example, in a linear sweep, the top and bottom edges of tiles end abruptly to form the end cap of the object. You can make a valid object
// in two ways. One way is to create a tile with a single, complete edge along Y=0, and of course a corresponding edges along Y=1. The second way
// to make a valid object is to have no points at all on the Y=0 line, and of course none on Y=1. In this case, the resulting texture produces
// a collection of disconnected objects. Note that the Z coordinates of your tile can be anything, but for the dimensional settings on textures
// to work intuitively, you should construct your tile so that Z ranges from 0 to 1.
// Figure: This is the "hexgrid" VNF tile, which creates a hexagonal grid texture, something which doesn't work well with a height field because the edges of the hexagon don't align with the grid. Note how the tile ranges between 0 and 1 in both X, Y and Z.
// tex = texture("hex_grid");
// vnf_polyhedron(tex);
// Figure: This is an example of a tile that has no edges at the top or bottom, so it creates disconnected rings. See below for examples showing this tile in use.
// shape = skin([
// rect(2/5),
// rect(2/3),
// rect(2/5)
// ],
// z=[0,1/2,1],
// slices=0,
// caps=false);
// tile = move([0,1/2,2/3],yrot(90,shape));
// vnf_polyhedron(tile);
// Function: texture() // Function: texture()
// Usage: // Usage:
@ -2503,31 +2625,32 @@ function associate_vertices(polygons, split, curpoly=0) =
// Given a texture name, returns a texture. Textures can come in two varieties: // Given a texture name, returns a texture. Textures can come in two varieties:
// - Heightfield textures which are 2D arrays of scalars. These are faster to render, but are less precise and prone to triangulation errors. // - Heightfield textures which are 2D arrays of scalars. These are faster to render, but are less precise and prone to triangulation errors.
// - VNF Tile textures, which are VNFs that completely tile the rectangle `[0,0]` to `[1,1]`. These tend to be slower to render, but are more precise. // - VNF Tile textures, which are VNFs that completely tile the rectangle `[0,0]` to `[1,1]`. These tend to be slower to render, but are more precise.
// Sometimes the geometry of a shape to be textured will cause a heightfield texture to be badly triangulated. // Sometimes the geometry of a shape to be textured will cause a heightfield texture to be badly triangulated. The table below gives the recommended
// Switching to a similar VNF tile texture can solve this problem. Usually just by adding the prefix "vnf_". // style for the best triangulation of height field textures, but if results are still incorrect, switch to the similar VNF tile by adding the "_vnf" suffix
// to the texture name.
// Texture Values: // Texture Values:
// "bricks" = Heightfield = A brick-wall pattern. Giving `n=` sets the number of heightfield samples to `n` by `n`. Giving `roughness=` adds a level of height randomization to add roughness to the texture. // "bricks" = Heightfield = A brick-wall pattern. Giving `n=` sets the number of heightfield samples to `n` by `n`. Giving `roughness=` adds a level of height randomization to add roughness to the texture. Use `style="convex"`.
// "bricks_vnf" = VNF Tile = Like "bricks", but slower and more consistent in triangulation. Giving `gap=` sets the mortar gap between bricks. Giving `inset=` specifies the inset of the brick tops, relative to their bottoms. // "bricks_vnf" = VNF Tile = Like "bricks", but slower and more consistent in triangulation. Giving `gap=` sets the mortar gap between bricks. Giving `inset=` specifies the inset of the brick tops, relative to their bottoms.
// "checkers" = VNF Tile = A pattern of alternating checkerboard squares. Giving `inset=` specifies the inset of the raised checker tile tops, relative to the lowered tiles. // "checkers" = VNF Tile = A pattern of alternating checkerboard squares. Giving `inset=` specifies the inset of the raised checker tile tops, relative to the lowered tiles.
// "cones" = VNF Tile = Raised conical spikes. Giving `n=` sets the number of sides to the cone. Giving `inset=` specifies the inset of the base of the cone, relative to the tile edges. // "cones" = VNF Tile = Raised conical spikes. Giving `n=` sets the number of sides to the cone. Giving `inset=` specifies the inset of the base of the cone, relative to the tile edges.
// "cubes" = VNF Tile = Cornercubes texture. // "cubes" = VNF Tile = Cornercubes texture.
// "diamonds" = Heightfield = Diamond shapes with tips aligned with the axes. Useful for knurling. Giving `n=` sets the number of heightfield samples to `n` by `n`. // "diamonds" = Heightfield = Diamond shapes with tips aligned with the axes. Useful for knurling. Giving `n=` sets the number of heightfield samples to `n` by `n`. Use `style="concave"` for pointed bumps, or `style="default"` or `style="alt"` for diagonal ribs.
// "diamonds_vnf" = VNF Tile = Like "diamonds", but slower and more consistent in triangulation. // "diamonds_vnf" = VNF Tile = Like "diamonds", but slower and more consistent in triangulation.
// "dimples" = VNF Tile = Small round divots. Giving `n=` sets the resolution of the divot curve. Giving `inset=` specifies the inset of the dimples, relative to the edge of the tile. // "dimples" = VNF Tile = Small round divots. Giving `n=` sets the resolution of the divot curve. Giving `inset=` specifies the inset of the dimples, relative to the edge of the tile.
// "dots" = VNF Tile = Raised small round bumps. Giving `n=` sets the resolution of the bump curve. Giving `inset=` specifies the inset of the dots, relative to the edge of the tile. // "dots" = VNF Tile = Raised small round bumps. Giving `n=` sets the resolution of the bump curve. Giving `inset=` specifies the inset of the dots, relative to the edge of the tile.
// "hex_grid" = VNF Tile = A hexagonal grid of thin lines. Giving `inset=` specifies the inset of the hex tops, relative to their bottoms. // "hex_grid" = VNF Tile = A hexagonal grid of thin lines. Giving `inset=` specifies the inset of the hex tops, relative to their bottoms.
// "hills" = Heightfield = Wavy sine-wave hills and valleys, Giving `n=` sets the number of heightfield samples to `n` by `n`. // "hills" = Heightfield = Wavy sine-wave hills and valleys, Giving `n=` sets the number of heightfield samples to `n` by `n`. Set `style="quincunx"`.
// "pyramids" = Heightfield = Pyramids shapes with flat sides aligned with the axes. Also useful for knurling. Giving `n=` sets the number of heightfield samples to `n` by `n`. // "pyramids" = Heightfield = Pyramids shapes with flat sides aligned with the axes. Also useful for knurling. Giving `n=` sets the number of heightfield samples to `n` by `n`.
// "pyramids_vnf" = VNF Tile = Like "pyramids", but slower and more consistent in triangulation. // "pyramids_vnf" = VNF Tile = Like "pyramids", but slower and more consistent in triangulation.
// "ribs" = Heightfield = Vertically aligned triangular ribs. Giving `n=` sets the number of heightfield samples to `n` by `1`. // "ribs" = Heightfield = Vertically aligned triangular ribs. Giving `n=` sets the number of heightfield samples to `n` by `1`. The choice of style does not matter.
// "rough" = Heightfield = A pseudo-randomized rough surace texture. Giving `n=` sets the number of heightfield samples to `n` by `n`. Giving `roughness=` adds a level of height randomization to add roughness to the texture. // "rough" = Heightfield = A pseudo-randomized rough surace texture. Giving `n=` sets the number of heightfield samples to `n` by `n`. Giving `roughness=` adds a level of height randomization to add roughness to the texture.
// "tri_grid" = VNF Tile = A triangular grid of thin lines. Giving `inset=` specifies the inset of the triangle tops, relative to their bottoms. // "tri_grid" = VNF Tile = A triangular grid of thin lines. Giving `inset=` specifies the inset of the triangle tops, relative to their bottoms.
// "trunc_diamonds" = VNF Tile = Truncated diamonds. A grid of thin lines at 45º angles. Giving `inset=` specifies the inset of the truncated diamond tops, relative to their bottoms. // "trunc_diamonds" = VNF Tile = Truncated diamonds. A grid of thin lines at 45º angles. Giving `inset=` specifies the inset of the truncated diamond tops, relative to their bottoms.
// "trunc_pyramids" = Heightfield = Truncated pyramids. Like "pyramids" but with flattened tips. Giving `n=` sets the number of heightfield samples to `n` by `n`. // "trunc_pyramids" = Heightfield = Truncated pyramids. Like "pyramids" but with flattened tips. Giving `n=` sets the number of heightfield samples to `n` by `n`. Set `style="convex"`.
// "trunc_pyramids_vnf" = VNF Tile = Like "trunc_pyramids", but slower and more consistent in triangulation. Giving `inset=` specifies the inset of the truncated pyramid tops, relative to their bottoms. // "trunc_pyramids_vnf" = VNF Tile = Like "trunc_pyramids", but slower and more consistent in triangulation. Giving `inset=` specifies the inset of the truncated pyramid tops, relative to their bottoms.
// "trunc_ribs" = Heightfield = Truncated ribs. Like "ribs" but with flat rib tips. Giving `n=` sets the number of heightfield samples to `n` by `1`. // "trunc_ribs" = Heightfield = Truncated ribs. Like "ribs" but with flat rib tips. Giving `n=` sets the number of heightfield samples to `n` by `1`. The style does not matter.
// "trunc_ribs_vnf" = VNF Tile = Like "trunc_ribs", but slower and more adjustable. Giving `gap=` sets the bottom gap between ribs. Giving `inset=` specifies the inset of the rib tops, relative to their bottoms. // "trunc_ribs_vnf" = VNF Tile = Like "trunc_ribs", but slower and more adjustable. Giving `gap=` sets the bottom gap between ribs. Giving `inset=` specifies the inset of the rib tops, relative to their bottoms.
// "wave_ribs" = Heightfield = Vertically aligned wavy ribs. Giving `n=` sets the number of heightfield samples to `n` by `1`. // "wave_ribs" = Heightfield = Vertically aligned wavy ribs. Giving `n=` sets the number of heightfield samples to `n` by `1`. The style does not matter.
// Arguments: // Arguments:
// tex = The name of the texture to get. // tex = The name of the texture to get.
// --- // ---
@ -3072,15 +3195,16 @@ function _validate_texture(texture) =
min_xy = point2d(bounds[0]), min_xy = point2d(bounds[0]),
max_xy = point2d(bounds[1]) max_xy = point2d(bounds[1])
) )
assert(min_xy==[0,0] && max_xy==[1,1], "VNF tiles must span exactly from [0,0] to [1,1] in the X and Y components.") //assert(min_xy==[0,0] && max_xy==[1,1],"VNF tiles must span exactly from [0,0] to [1,1] in the X and Y components."))
assert(all_nonnegative(concat(min_xy,[1,1]-max_xy)), "VNF tile X and Y components must be between 0 and 1.")
let( let(
verts = texture[0], verts = texture[0],
uedges = _get_vnf_tile_edges(texture), uedges = _get_vnf_tile_edges(texture),
edge_verts = [for (i = unique(flatten(uedges))) verts[i] ], edge_verts = [for (i = unique(flatten(uedges))) verts[i] ],
hverts = [for(v = edge_verts) if(v.x==0 || v.x==1) v], hverts = [for(v = edge_verts) if(v.x==0 || v.x==1) v],
vverts = [for(v = edge_verts) if(v.y==0 || v.y==1) v], vverts = [for(v = edge_verts) if(v.y==0 || v.y==1) v],
allgoodx = all(hverts, function(v) any(hverts, function(w) w==[1-v.x, v.y, v.z])), allgoodx = all(hverts, function(v) any(hverts, function(w) approx(w,[1-v.x, v.y, v.z]))),
allgoody = all(vverts, function(v) any(vverts, function(w) w==[v.x, 1-v.y, v.z])) allgoody = all(vverts, function(v) any(vverts, function(w) approx(w,[v.x, 1-v.y, v.z])))
) )
assert(allgoodx && allgoody, "All VNF tile edge vertices must line up with a vertex on the opposite side of the tile.") assert(allgoodx && allgoody, "All VNF tile edge vertices must line up with a vertex on the opposite side of the tile.")
true true
@ -3096,7 +3220,7 @@ function _textured_linear_sweep(
region, texture, tex_size=[5,5], region, texture, tex_size=[5,5],
h, counts, inset=false, rot=false, h, counts, inset=false, rot=false,
tex_scale=1, twist, scale, shift, tex_scale=1, twist, scale, shift,
style="min_edge", l, style="min_edge", l, caps=true,
height, length, samples, height, length, samples,
anchor=CENTER, spin=0, orient=UP anchor=CENTER, spin=0, orient=UP
) = ) =
@ -3105,7 +3229,9 @@ function _textured_linear_sweep(
assert(counts==undef || is_vector(counts,2)) assert(counts==undef || is_vector(counts,2))
assert(tex_size==undef || is_vector(tex_size,2)) assert(tex_size==undef || is_vector(tex_size,2))
assert(is_bool(rot) || in_list(rot,[0,90,180,270])) assert(is_bool(rot) || in_list(rot,[0,90,180,270]))
assert(is_bool(caps) || is_bool_list(caps,2))
let( let(
caps = is_bool(caps) ? [caps,caps] : caps,
regions = is_path(region,2)? [[region]] : region_parts(region), regions = is_path(region,2)? [[region]] : region_parts(region),
tex = is_string(texture)? texture(texture) : texture, tex = is_string(texture)? texture(texture) : texture,
texture = !rot? tex : texture = !rot? tex :
@ -3263,8 +3389,8 @@ function _textured_linear_sweep(
] ]
) nupath ) nupath
], ],
bot_vnf = vnf_from_region(brgn, down(h/2), reverse=true), bot_vnf = !caps[0] || brgn==[[]] ? EMPTY_VNF:vnf_from_region(brgn, down(h/2), reverse=true),
top_vnf = vnf_from_region(brgn, tmat, reverse=false) top_vnf = !caps[1] || brgn==[[]] ? EMPTY_VNF:vnf_from_region(brgn, tmat, reverse=false)
) vnf_join([walls_vnf, bot_vnf, top_vnf]) ) vnf_join([walls_vnf, bot_vnf, top_vnf])
]), ]),
skmat = down(h/2) * skew(sxz=shift.x/h, syz=shift.y/h) * up(h/2), skmat = down(h/2) * skew(sxz=shift.x/h, syz=shift.y/h) * up(h/2),
@ -3281,7 +3407,7 @@ function _textured_linear_sweep(
module _textured_linear_sweep( module _textured_linear_sweep(
path, texture, tex_size=[5,5], h, path, texture, tex_size=[5,5], h,
inset=false, rot=false, tex_scale=1, inset=false, rot=false, tex_scale=1,
twist, scale, shift, samples, twist, scale, shift, samples, caps=true,
style="min_edge", l, style="min_edge", l,
height, length, counts, height, length, counts,
anchor=CENTER, spin=0, orient=UP, anchor=CENTER, spin=0, orient=UP,
@ -3289,7 +3415,7 @@ module _textured_linear_sweep(
) { ) {
h = first_defined([h, l, height, length, 1]); h = first_defined([h, l, height, length, 1]);
vnf = _textured_linear_sweep( vnf = _textured_linear_sweep(
path, texture, h=h, path, texture, h=h, caps=caps,
tex_size=tex_size, counts=counts, tex_size=tex_size, counts=counts,
inset=inset, rot=rot, tex_scale=tex_scale, inset=inset, rot=rot, tex_scale=tex_scale,
twist=twist, scale=scale, shift=shift, twist=twist, scale=scale, shift=shift,
@ -3320,7 +3446,9 @@ function _find_vnf_tile_edge_path(vnf, val) =
], ],
sfrags = sort(fragments, idx=[0,1]), sfrags = sort(fragments, idx=[0,1]),
rpath = _assemble_a_path_from_fragments(sfrags)[0], rpath = _assemble_a_path_from_fragments(sfrags)[0],
opath = rpath[0].x > last(rpath).x? reverse(rpath) : rpath opath = rpath==[]? []
: rpath[0].x > last(rpath).x ? reverse(rpath)
: rpath
) opath; ) opath;