diff --git a/algorithm/cgal/Pack.h b/algorithm/cgal/Pack.h index bfb6b1c22..1ce7a0ba1 100644 --- a/algorithm/cgal/Pack.h +++ b/algorithm/cgal/Pack.h @@ -172,8 +172,7 @@ static int Pack(Geometry* geometry, size_t count, auto display_pwhs = [&](const std::vector>& pwhs, - const std::string& tag) { - }; + const std::string& tag) {}; auto cut_part_from_sheet = [&](Sheet& sheet, const Part& part, diff --git a/api/shape/size.js b/api/shape/size.js index fa2954b26..9d39d71ff 100644 --- a/api/shape/size.js +++ b/api/shape/size.js @@ -22,9 +22,17 @@ export const size = Shape.registerMethod3( [ 'inputGeometry', 'function', - 'strings:empty,max,min,right,left,front,back,top,bottom,length,width,height,center', + 'strings:empty,max,min,maxX,maxY,maxZ,minX,minY,minZ,right,left,front,back,top,bottom,length,width,height,center', + 'options', ], - async (geometry, _op, modes) => { + async (geometry, _op, modes, { resolution = 0.01 } = {}) => { + const round = (value) => { + if (resolution === 0) { + return value; + } else { + return Math.round(value / resolution) * resolution; + } + }; const bounds = measureBoundingBox(geometry); const args = []; if (bounds === undefined) { @@ -43,39 +51,48 @@ export const size = Shape.registerMethod3( args.push(false); break; case 'max': - args.push(max); + // CHECK: This is return a vector rather than a scalar. + args.push(round(max)); break; case 'min': - args.push(min); + // CHECK: This is return a vector rather than a scalar. + args.push(round(min)); break; + case 'maxX': case 'right': - args.push(max[X]); + args.push(round(max[X])); break; + case 'minX': case 'left': - args.push(min[X]); + args.push(round(min[X])); break; + case 'minY': case 'front': - args.push(min[Y]); + args.push(round(min[Y])); break; + case 'maxY': case 'back': - args.push(max[Y]); + args.push(round(max[Y])); break; + case 'maxZ': case 'top': - args.push(max[Z]); + args.push(round(max[Z])); break; + case 'minZ': case 'bottom': - args.push(min[Z]); + args.push(round(min[Z])); break; case 'length': - args.push(max[X] - min[X]); + args.push(round(max[X] - min[X])); break; case 'width': - args.push(max[Y] - min[Y]); + args.push(round(max[Y] - min[Y])); break; case 'height': - args.push(max[Z] - min[Z]); + args.push(round(max[Z] - min[Z])); break; case 'center': + // CHECK: This is return a vector rather than a scalar. args.push(scale(0.5, add(min, max))); break; } diff --git a/api/shape/sort.js b/api/shape/sort.js index 4294d88c1..b7c3f0c2f 100644 --- a/api/shape/sort.js +++ b/api/shape/sort.js @@ -1,78 +1,43 @@ -import { Group, getLeafs, measureBoundingBox } from '@jsxcad/geometry'; +import { Group, getLeafs } from '@jsxcad/geometry'; import Shape from './Shape.js'; -const X = 0; -const Y = 1; -const Z = 2; - -const round = (values, resolution) => - values.map((value) => Math.round(value / resolution) * resolution); - export const sort = Shape.registerMethod3( 'sort', - ['inputGeometry', 'string', 'number'], - (geometry, spec = 'z { - let leafs = []; + ['inputGeometry', 'function', 'modes:min,max', 'numbers'], + async (geometry, rankOp = () => 0, mode, tiersToKeep = []) => { + let predicate = (a, b) => a - b; + if (mode.max) { + // Start from the max tier. + predicate = (a, b) => b - a; + } + const leafs = []; for (const leaf of getLeafs(geometry)) { - console.log(JSON.stringify(leaf)); - const bounds = measureBoundingBox(leaf); - if (bounds === undefined) { - continue; - } - const [min, max] = bounds; + const rank = await rankOp(Shape.fromGeometry(leaf)); leafs.push({ - min: round(min, resolution), - max: round(max, resolution), + rank, leaf, }); } - const ops = []; - while (spec) { - const found = spec.match(/([xyz])([<>])([0-9.])?(.*)/); - if (found === null) { - throw Error(`Bad sort spec ${spec}`); + leafs.sort((a, b) => predicate(a.rank, b.rank)); + let lastLeaf; + let tier; + const tiers = []; + for (const thisLeaf of leafs) { + if (!lastLeaf || lastLeaf.rank !== thisLeaf.rank) { + tier = []; + tiers.push(tier); } - const [, dimension, order, limit, rest] = found; - // We apply the sorting ops in reverse. - ops.unshift({ dimension, order, limit }); - spec = rest; + tier.push(thisLeaf); } - for (const { dimension, order, limit } of ops) { - let axis; - switch (dimension) { - case 'x': - axis = X; - break; - case 'y': - axis = Y; - break; - case 'z': - axis = Z; - break; - } - if (limit !== undefined) { - switch (order) { - case '>': - leafs = leafs.filter(({ min }) => min[axis] > limit); - break; - case '<': - leafs = leafs.filter(({ max }) => max[axis] < limit); - break; - } - } - let compare; - switch (order) { - case '>': - compare = (a, b) => b.min[axis] - a.min[axis]; - break; - case '<': - compare = (a, b) => a.max[axis] - b.max[axis]; - break; + // Structure the results by rank tiers. + const keptTiers = []; + for (let nth = 0; nth < tiers.length; nth++) { + if (tiersToKeep.length === 0 || tiersToKeep.includes(nth + 1)) { + keptTiers.push(tiers[nth]); } - leafs.sort(compare); } - return Group(leafs.map(({ leaf }) => leaf)); + return Group(keptTiers.map((tier) => Group(tier.map(({ leaf }) => leaf)))); } ); diff --git a/api/shape/view.js b/api/shape/view.js index 26e2bd42c..7be548cb0 100644 --- a/api/shape/view.js +++ b/api/shape/view.js @@ -115,6 +115,7 @@ const topViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -127,6 +128,7 @@ const topViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, @@ -150,6 +152,7 @@ const gridViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -162,6 +165,7 @@ const gridViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, @@ -185,6 +189,7 @@ const frontViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -197,6 +202,7 @@ const frontViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, @@ -220,6 +226,7 @@ const sideViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -232,6 +239,7 @@ const sideViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, diff --git a/doc/updateNotebook.js b/doc/updateNotebook.js index b09b39b34..c20f9c688 100644 --- a/doc/updateNotebook.js +++ b/doc/updateNotebook.js @@ -87,7 +87,11 @@ const writeMarkdown = async ( } const observedPath = `${modulePath}.observed.${filename}`; const expectedPath = `${modulePath}.${filename}`; - if (!filename.endsWith('stl') && !filename.endsWith('pdf') && !filename.endsWith('png')) { + if ( + !filename.endsWith('stl') && + !filename.endsWith('pdf') && + !filename.endsWith('png') + ) { // STL output has become unstable; skip for now. try { const observed = new TextDecoder('utf8').decode(data); diff --git a/es6/jsxcad-api-shape.js b/es6/jsxcad-api-shape.js index 28e878172..87e89929e 100644 --- a/es6/jsxcad-api-shape.js +++ b/es6/jsxcad-api-shape.js @@ -1133,9 +1133,9 @@ const md = (strings, ...placeholders) => { return md; }; -const X$2 = Shape.registerMethod3('X', ['numbers'], g$1.X); -const Y$2 = Shape.registerMethod3('Y', ['numbers'], g$1.Y); -const Z$2 = Shape.registerMethod3('Z', ['numbers'], g$1.Z); +const X$1 = Shape.registerMethod3('X', ['numbers'], g$1.X); +const Y$1 = Shape.registerMethod3('Y', ['numbers'], g$1.Y); +const Z$1 = Shape.registerMethod3('Z', ['numbers'], g$1.Z); const XY = Shape.registerMethod3('XY', ['numbers'], g$1.XY); const YX = Shape.registerMethod3('YX', ['numbers'], g$1.YX); const XZ = Shape.registerMethod3('XZ', ['numbers'], g$1.XZ); @@ -1617,6 +1617,7 @@ const topViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -1629,6 +1630,7 @@ const topViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, @@ -1652,6 +1654,7 @@ const gridViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -1664,6 +1667,7 @@ const gridViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, @@ -1687,6 +1691,7 @@ const frontViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -1699,6 +1704,7 @@ const frontViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, @@ -1722,6 +1728,7 @@ const sideViewOp = ( op = (_x) => (s) => s, { download, + downloadOp, size = 256, skin = true, outline = true, @@ -1734,6 +1741,7 @@ const sideViewOp = ( ) => { const options = { download, + downloadOp, size, skin, outline, @@ -3317,18 +3325,26 @@ const scale = (amount, [x = 0, y = 0, z = 0]) => [ z * amount, ]; -const X$1 = 0; -const Y$1 = 1; -const Z$1 = 2; +const X = 0; +const Y = 1; +const Z = 2; const size = Shape.registerMethod3( 'size', [ 'inputGeometry', 'function', - 'strings:empty,max,min,right,left,front,back,top,bottom,length,width,height,center', + 'strings:empty,max,min,maxX,maxY,maxZ,minX,minY,minZ,right,left,front,back,top,bottom,length,width,height,center', + 'options', ], - async (geometry, _op, modes) => { + async (geometry, _op, modes, { resolution = 0.01 } = {}) => { + const round = (value) => { + if (resolution === 0) { + return value; + } else { + return Math.round(value / resolution) * resolution; + } + }; const bounds = measureBoundingBox(geometry); const args = []; if (bounds === undefined) { @@ -3347,39 +3363,48 @@ const size = Shape.registerMethod3( args.push(false); break; case 'max': - args.push(max); + // CHECK: This is return a vector rather than a scalar. + args.push(round(max)); break; case 'min': - args.push(min); + // CHECK: This is return a vector rather than a scalar. + args.push(round(min)); break; + case 'maxX': case 'right': - args.push(max[X$1]); + args.push(round(max[X])); break; + case 'minX': case 'left': - args.push(min[X$1]); + args.push(round(min[X])); break; + case 'minY': case 'front': - args.push(min[Y$1]); + args.push(round(min[Y])); break; + case 'maxY': case 'back': - args.push(max[Y$1]); + args.push(round(max[Y])); break; + case 'maxZ': case 'top': - args.push(max[Z$1]); + args.push(round(max[Z])); break; + case 'minZ': case 'bottom': - args.push(min[Z$1]); + args.push(round(min[Z])); break; case 'length': - args.push(max[X$1] - min[X$1]); + args.push(round(max[X] - min[X])); break; case 'width': - args.push(max[Y$1] - min[Y$1]); + args.push(round(max[Y] - min[Y])); break; case 'height': - args.push(max[Z$1] - min[Z$1]); + args.push(round(max[Z] - min[Z])); break; case 'center': + // CHECK: This is return a vector rather than a scalar. args.push(scale(0.5, add(min, max))); break; } @@ -3436,77 +3461,41 @@ const smooth = Shape.registerMethod3( ) ); -const X = 0; -const Y = 1; -const Z = 2; - -const round = (values, resolution) => - values.map((value) => Math.round(value / resolution) * resolution); - const sort = Shape.registerMethod3( 'sort', - ['inputGeometry', 'string', 'number'], - (geometry, spec = 'z { - let leafs = []; + ['inputGeometry', 'function', 'modes:min,max', 'numbers'], + async (geometry, rankOp = () => 0, mode, tiersToKeep = []) => { + let predicate = (a, b) => a - b; + if (mode.max) { + // Start from the max tier. + predicate = (a, b) => b - a; + } + const leafs = []; for (const leaf of getLeafs(geometry)) { - console.log(JSON.stringify(leaf)); - const bounds = measureBoundingBox(leaf); - if (bounds === undefined) { - continue; - } - const [min, max] = bounds; + const rank = await rankOp(Shape.fromGeometry(leaf)); leafs.push({ - min: round(min, resolution), - max: round(max, resolution), + rank, leaf, }); } - const ops = []; - while (spec) { - const found = spec.match(/([xyz])([<>])([0-9.])?(.*)/); - if (found === null) { - throw Error(`Bad sort spec ${spec}`); + leafs.sort((a, b) => predicate(a.rank, b.rank)); + let tier; + const tiers = []; + for (const thisLeaf of leafs) { + { + tier = []; + tiers.push(tier); } - const [, dimension, order, limit, rest] = found; - // We apply the sorting ops in reverse. - ops.unshift({ dimension, order, limit }); - spec = rest; + tier.push(thisLeaf); } - for (const { dimension, order, limit } of ops) { - let axis; - switch (dimension) { - case 'x': - axis = X; - break; - case 'y': - axis = Y; - break; - case 'z': - axis = Z; - break; - } - if (limit !== undefined) { - switch (order) { - case '>': - leafs = leafs.filter(({ min }) => min[axis] > limit); - break; - case '<': - leafs = leafs.filter(({ max }) => max[axis] < limit); - break; - } - } - let compare; - switch (order) { - case '>': - compare = (a, b) => b.min[axis] - a.min[axis]; - break; - case '<': - compare = (a, b) => a.max[axis] - b.max[axis]; - break; + // Structure the results by rank tiers. + const keptTiers = []; + for (let nth = 0; nth < tiers.length; nth++) { + if (tiersToKeep.length === 0 || tiersToKeep.includes(nth + 1)) { + keptTiers.push(tiers[nth]); } - leafs.sort(compare); } - return Group$1(leafs.map(({ leaf }) => leaf)); + return Group$1(keptTiers.map((tier) => Group$1(tier.map(({ leaf }) => leaf)))); } ); @@ -4194,4 +4183,4 @@ const Wave = Shape.registerMethod3( } ); -export { And, Arc, ArcX, ArcY, ArcZ, As, AsPart, Assembly, Box, Cached, ChainHull, Clip, Cloud, Curve, Cut, Edge, Empty, Face, Fuse, Geometry, GrblConstantLaser, GrblDynamicLaser, GrblPlotter, GrblSpindle, Group, Grow, Hershey, Hexagon, Hull, Icosahedron, Implicit, Iron, Join, LDraw, LDrawPart, Label, Line, LineX, LineY, LineZ, Link, List, LoadDxf, LoadLDraw, LoadPng, LoadPngAsRelief, LoadStl, LoadSvg, Loft, Loop, MaskedBy, Note, Octagon, Off, Orb, Pentagon, Point, Points, Polygon, Polyhedron, RX, RY, RZ, Ref, Route, Segments, Seq, Shape, Skeleton, Spiral, Stl, SurfaceMesh, Svg, To, Triangle, Voxels, Wave, Wrap, X$2 as X, XY, XZ, Y$2 as Y, YX, YZ, Z$2 as Z, ZX, ZY, absolute, abstract, acos, addTo, align, alignment, and, approximate, area, as, asPart, at, base, bb, bend, billOfMaterials, by, centroid, chainHull, clean, clip, clipFrom, cloud, color, commonVolume, copy, cos, curve, cut, cutFrom, cutOut, defRgbColor, defThreejsMaterial, defTool, define, deform, demesh, diameter, dilateXY, disjoint, drop, dxf, e, each, eachEdge, eachPoint, eachSegment, eagerTransform, edges, ex, exterior, extrudeAlong, extrudeX, extrudeY, extrudeZ, ey, ez, faces, fair, fill, fit, fitTo, fix, flat, fuse, g, gap, gauge, gcode, get, getAll, getNot, getTag, ghost, gn, gridView, grow, hold, holes, hull, image, inFn, input, inset, involute, iron, join, lerp, link, list, load, loadGeometry, loft, log, loop, lowerEnvelope, m, mark, maskedBy, masking, material, max, md, min, minimizeOverhang, move, moveAlong, n, noGap, noHoles, noOp, noVoid, normal, note, nth, o, obb, offset, on, op, orient, origin, outline, overlay, pack, pdf, plus, png, points, put, random, raycastPng, reconstruct, ref, refine, remesh, repair, rotateX, rotateY, rotateZ, route, runLength, rx, ry, rz, s, save, saveGeometry, scale$1 as scale, scaleToFit, scaleX, scaleY, scaleZ, seam, section, self, separate, seq, serialize, setTag, setTags, shadow, shell, simplify, sin, size, skeleton, sketch, smooth, sort, split, sqrt, stl, svg, sx, sy, sz, table, tag, tags, times, tint, to, toCoordinates, toDisplayGeometry, toGeometry, tool, toolpath, transform, trim, turnX, turnY, turnZ, twist, tx, ty, tz, unfold, untag, upperEnvelope, v, validate, version, view, voidFn, volume, voxels, wrap, x, xyz, y, z, zagSides, zagSteps }; +export { And, Arc, ArcX, ArcY, ArcZ, As, AsPart, Assembly, Box, Cached, ChainHull, Clip, Cloud, Curve, Cut, Edge, Empty, Face, Fuse, Geometry, GrblConstantLaser, GrblDynamicLaser, GrblPlotter, GrblSpindle, Group, Grow, Hershey, Hexagon, Hull, Icosahedron, Implicit, Iron, Join, LDraw, LDrawPart, Label, Line, LineX, LineY, LineZ, Link, List, LoadDxf, LoadLDraw, LoadPng, LoadPngAsRelief, LoadStl, LoadSvg, Loft, Loop, MaskedBy, Note, Octagon, Off, Orb, Pentagon, Point, Points, Polygon, Polyhedron, RX, RY, RZ, Ref, Route, Segments, Seq, Shape, Skeleton, Spiral, Stl, SurfaceMesh, Svg, To, Triangle, Voxels, Wave, Wrap, X$1 as X, XY, XZ, Y$1 as Y, YX, YZ, Z$1 as Z, ZX, ZY, absolute, abstract, acos, addTo, align, alignment, and, approximate, area, as, asPart, at, base, bb, bend, billOfMaterials, by, centroid, chainHull, clean, clip, clipFrom, cloud, color, commonVolume, copy, cos, curve, cut, cutFrom, cutOut, defRgbColor, defThreejsMaterial, defTool, define, deform, demesh, diameter, dilateXY, disjoint, drop, dxf, e, each, eachEdge, eachPoint, eachSegment, eagerTransform, edges, ex, exterior, extrudeAlong, extrudeX, extrudeY, extrudeZ, ey, ez, faces, fair, fill, fit, fitTo, fix, flat, fuse, g, gap, gauge, gcode, get, getAll, getNot, getTag, ghost, gn, gridView, grow, hold, holes, hull, image, inFn, input, inset, involute, iron, join, lerp, link, list, load, loadGeometry, loft, log, loop, lowerEnvelope, m, mark, maskedBy, masking, material, max, md, min, minimizeOverhang, move, moveAlong, n, noGap, noHoles, noOp, noVoid, normal, note, nth, o, obb, offset, on, op, orient, origin, outline, overlay, pack, pdf, plus, png, points, put, random, raycastPng, reconstruct, ref, refine, remesh, repair, rotateX, rotateY, rotateZ, route, runLength, rx, ry, rz, s, save, saveGeometry, scale$1 as scale, scaleToFit, scaleX, scaleY, scaleZ, seam, section, self, separate, seq, serialize, setTag, setTags, shadow, shell, simplify, sin, size, skeleton, sketch, smooth, sort, split, sqrt, stl, svg, sx, sy, sz, table, tag, tags, times, tint, to, toCoordinates, toDisplayGeometry, toGeometry, tool, toolpath, transform, trim, turnX, turnY, turnZ, twist, tx, ty, tz, unfold, untag, upperEnvelope, v, validate, version, view, voidFn, volume, voxels, wrap, x, xyz, y, z, zagSides, zagSteps }; diff --git a/nb/api/sort.md b/nb/api/sort.md index 8df4be9f0..cb745abd7 100644 --- a/nb/api/sort.md +++ b/nb/api/sort.md @@ -2,45 +2,43 @@ ### sort() Parameter|Default|Type ---|---|--- -spec|'z 0|Maps each shape to a rank. +'min'|true|Start with the minimum rank. +'max'|false|Start with the maximum rank. +keep|[]|Which ranks to keep, indexed from 1. -This orders a group of shapes by their extremal bounding box corner. +Seq({ to: 10 }, Arc).sort(area(), 'min').disjoint() -e.g., if ordering by 'z>' the leaf with the greatest minimum z value is selected. +![Image](sort.md.$2.png) -![Image](sort.md.$2_1.png) - -Box(4, 4, 4).faces().n(0) selects an arbitrary face +```JavaScript +Seq({ to: 10 }, Arc) + .sort(area(), 'min') + .disjoint() + .note(`Seq({ to: 10 }, Arc).sort(area(), 'min').disjoint()`) + .view(); +``` -![Image](sort.md.$2_2.png) +Seq({ to: 10 }, Arc).sort(area(), 'max').disjoint() -Box(4, 4, 4).faces().sort().n(0) selects the top-most face +![Image](sort.md.$3.png) ```JavaScript -Box(4, 4, 4) - .faces() - .op( - n(0).view(1).note('Box(4, 4, 4).faces().n(0) selects an arbitrary face'), - sort('z>') - .n(0) - .view(2) - .note('Box(4, 4, 4).faces().sort().n(0) selects the top-most face') - ); +Seq({ to: 10 }, Arc) + .sort(area(), 'max') + .disjoint() + .note(`Seq({ to: 10 }, Arc).sort(area(), 'max').disjoint()`) + .view(); ``` -![Image](sort.md.$3.png) +Seq({ to: 10 }, Arc).sort(area(), 'max', 1, 3, 5).disjoint() -_Check this one_. +![Image](sort.md.$4.png) ```JavaScript -Box(4, 4, 4) - .cut( - eachEdge() - .sort('z>y') - .n(0, 1) - .op((e) => Box(2, 2, [0, 4]).to(e)) - ) - .view() - .note('_Check this one_.'); +Seq({ to: 10 }, Arc) + .sort(area(), 'max', 1, 3, 5) + .disjoint() + .note(`Seq({ to: 10 }, Arc).sort(area(), 'max', 1, 3, 5).disjoint()`) + .view(); ``` diff --git a/nb/api/sort.md.$2.png b/nb/api/sort.md.$2.png new file mode 100644 index 000000000..25cfdb70b Binary files /dev/null and b/nb/api/sort.md.$2.png differ diff --git a/nb/api/sort.md.$3.png b/nb/api/sort.md.$3.png index a80559f0d..0221c3e75 100644 Binary files a/nb/api/sort.md.$3.png and b/nb/api/sort.md.$3.png differ diff --git a/nb/api/sort.md.$4.png b/nb/api/sort.md.$4.png new file mode 100644 index 000000000..10ce19e75 Binary files /dev/null and b/nb/api/sort.md.$4.png differ diff --git a/nb/api/sort.nb b/nb/api/sort.nb index e314b0d87..8c9340bec 100644 --- a/nb/api/sort.nb +++ b/nb/api/sort.nb @@ -3,30 +3,26 @@ md` ### sort() Parameter|Default|Type ---|---|--- -spec|'z' the leaf with the greatest minimum z value is selected. +rank|() => 0|Maps each shape to a rank. +'min'|true|Start with the minimum rank. +'max'|false|Start with the maximum rank. +keep|[]|Which ranks to keep, indexed from 1. `; -Box(4, 4, 4) - .faces() - .op( - n(0).view(1).note('Box(4, 4, 4).faces().n(0) selects an arbitrary face'), - sort('z>') - .n(0) - .view(2) - .note('Box(4, 4, 4).faces().sort().n(0) selects the top-most face') - ); +Seq({ to: 10 }, Arc) + .sort(area(), 'min') + .disjoint() + .note(`Seq({ to: 10 }, Arc).sort(area(), 'min').disjoint()`) + .view(); + +Seq({ to: 10 }, Arc) + .sort(area(), 'max') + .disjoint() + .note(`Seq({ to: 10 }, Arc).sort(area(), 'max').disjoint()`) + .view(); -Box(4, 4, 4) - .cut( - eachEdge() - .sort('z>y') - .n(0, 1) - .op((e) => Box(2, 2, [0, 4]).to(e)) - ) - .view() - .note('_Check this one_.'); +Seq({ to: 10 }, Arc) + .sort(area(), 'max', 1, 3, 5) + .disjoint() + .note(`Seq({ to: 10 }, Arc).sort(area(), 'max', 1, 3, 5).disjoint()`) + .view(); diff --git a/nb/projects/pentacular/desk/desk.md b/nb/projects/pentacular/desk/desk.md new file mode 100644 index 000000000..904f424e4 --- /dev/null +++ b/nb/projects/pentacular/desk/desk.md @@ -0,0 +1,225 @@ +```JavaScript +const Beam = (length) => + Box(20) + .cut( + Box(5, 5) + .x(7.5) + .rz({ by: 1 / 4 }) + ) + .ez([length]) + .material('aluminium') + .x(10) + .y(10) + .maskedBy(Box([20], [20], [length])) + .rx(1 / 4) + .asPart(`Beam${length}`) + .rx(-1 / 4) + .clean(); +``` + +```JavaScript +const DeskBase = ({ width = 700, height = 660, depth = 400 } = {}) => + Assembly( + Plywood({ width, depth, height: 10 }).z(height), + Beam(height - 40) + .x(0, width - 20) + .y(0, depth - 20) + .z(20) + .as('uprights'), + Beam(width - 20 * 2) + .ry(1 / 4) + .x(width - 20) + .y(0, depth - 20) + .z(0, height - 20) + .as('beams'), + Beam(depth) + .rx(1 / 4) + .y(depth) + .x(0, width - 20) + .z(0, height - 20) + .as('rafters') + ); +``` + +```JavaScript +const DeskTop = ({ + width = 700, + height = 640, + depth = 400, + shelves = [], +} = {}) => + Assembly( + Plywood({ width, depth, height: 10 }).z(height), + Plywood({ width, depth: depth / 2 + 20, height: 10 }) + .y(depth / 2 - 20) + .z(...shelves), + Plywood({ width, depth: height + 20 + 10 }) + .rx(1 / 4) + .y(400 + 10) + .z(-20), + Beam(height - 20) + .x(0, width - 20) + .y(depth / 2 - 20, depth - 20), + Beam(depth) + .rx(1 / 4) + .y(depth) + .x(0, width - 20) + .z(height - 20), + Beam(width - 20 * 2) + .ry(1 / 4) + .x(width - 20) + .y(0, depth - 20) + .z(height - 20) + ); +``` + +```JavaScript +const Desk = ({ width = 700, height = 660, depth = 400, shelves = [] } = {}) => + Assembly( + DeskBase({ width, height, depth }), + DeskTop({ width, height, depth, shelves }).z(height) + ); +``` + +```JavaScript +const Plywood = ({ width = 700, height = 10, depth = 400 } = {}) => + Box([width], [depth], [height]) + .material('wood') + .at( + align('xyz<').origin(), + and(Hershey(`${width}x${depth}`, 50).align('xyz>').z(1)) + ) + .asPart('Plywood', `Plywood ${width}x${depth}x${height}`); +``` + +![Image](desk.md.desk.png) + +```JavaScript +const desk = Desk({ width: 700, shelves: [350, 450, 550] }).view(); +``` + +![Image](desk.md.parts.png) + +![Image](desk.md.parts_parts.png) + +[parts.png](desk.parts.png) + +```JavaScript +const parts = cornerDesign3 + .get('part:Plywood') + .view() + .each(flat()) + .sort(area(), 'max') + .pack(Box(2440, 1200).copy(10), 0, 1 / 4, { margin: 5 }) + .pack({ margin: 100 }) + .align('xy') + .png('parts', 'top', { size: 1024 }) + .v(18); +``` + +![Image](desk.md.room.png) + +```JavaScript +const room = Box([870 + 1580], [3800]) + .cut(Arc(870 * 2).x(1580 + 875)) + .clean() + .view(); +``` + +![Image](desk.md.desk1400.png) + +```JavaScript +const desk1400 = Desk({ width: 1400, shelves: [350, 450, 550] }).view(); +``` + +![Image](desk.md.cornerDesign3.png) + +```JavaScript +const cornerDesign3 = And( + desk700b + .rz(-1 / 4) + .align('x>y>') + .y(400), + desk1400b.rz(1 / 2).align('x>y>'), + desk1400b + .x(280 + 25) + .y(870 + 700 * 3 + 400) + .ghost(), + desk + .rz(1 / 4) + .align('x>y>') + .x(870 + 1580 - 400) + .y(870 + 700 * 0, 870 + 700 * 1, 870 + 700 * 2) + .ghost(), + Box([1000], [700], [2000]) + .x(870 + 1580 - 1000 + 280) + .y(870 + 700 * 3), + Box([280], [1200], [2500]).y(400 + 700 + 100, 400 + 700 + 1200 + 200), + room +).view('top'); +``` + +```JavaScript +const DeskHalfBase = ({ + width = 700, + height = 660, + depth = 400, + shelves = [], +} = {}) => + Assembly( + Plywood({ width, depth, height: 10 }).z(height), + Plywood({ width, depth, height: 10 }).z(20), + Plywood({ width, depth: depth / 2 + 20, height: 10 }) + .y(depth / 2 - 20) + .z(...shelves), + Beam(height - 40) + .x(0, width - 20) + .y(depth - 20, depth / 2 - 20) + .z(20) + .as('uprights'), + Beam(width - 20 * 2) + .ry(1 / 4) + .x(width - 20) + .y(0, depth - 20) + .z(0, height - 20) + .as('beams'), + Beam(depth) + .rx(1 / 4) + .y(depth) + .x(0, width - 20) + .z(0, height - 20) + .as('rafters') + ); +``` + +![Image](desk.md.deskHalfBase.png) + +```JavaScript +const deskHalfBase = DeskHalfBase({ + shelves: [20, 120, 220, 320, 420, 520], +}).view(); +``` + +![Image](desk.md.desk1400b.png) + +```JavaScript +const desk1400b = Assembly( + DeskHalfBase({ + width: 1400, + shelves: [/*120, */ 220, 320, 420, 520], + }), + DeskTop({ width: 1400, shelves: [420, 520] }).z(660) +).view(); +``` + +![Image](desk.md.desk700b.png) + +```JavaScript +const desk700b = Assembly( + DeskHalfBase({ + width: 700, + shelves: [120, 220, 320, 420, 520], + }), + DeskTop({ width: 700, shelves: [220, 320, 420, 520] }).z(660) +).view(); +``` diff --git a/nb/projects/pentacular/desk/desk.md.cornerDesign3.png b/nb/projects/pentacular/desk/desk.md.cornerDesign3.png new file mode 100644 index 000000000..8091ad5d0 Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.cornerDesign3.png differ diff --git a/nb/projects/pentacular/desk/desk.md.desk.png b/nb/projects/pentacular/desk/desk.md.desk.png new file mode 100644 index 000000000..82b9465db Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.desk.png differ diff --git a/nb/projects/pentacular/desk/desk.md.desk1400.png b/nb/projects/pentacular/desk/desk.md.desk1400.png new file mode 100644 index 000000000..84b5f1bf0 Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.desk1400.png differ diff --git a/nb/projects/pentacular/desk/desk.md.desk1400b.png b/nb/projects/pentacular/desk/desk.md.desk1400b.png new file mode 100644 index 000000000..f51f7b248 Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.desk1400b.png differ diff --git a/nb/projects/pentacular/desk/desk.md.desk700b.png b/nb/projects/pentacular/desk/desk.md.desk700b.png new file mode 100644 index 000000000..3d61760fb Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.desk700b.png differ diff --git a/nb/projects/pentacular/desk/desk.md.deskHalfBase.png b/nb/projects/pentacular/desk/desk.md.deskHalfBase.png new file mode 100644 index 000000000..4932b7761 Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.deskHalfBase.png differ diff --git a/nb/projects/pentacular/desk/desk.md.parts.png b/nb/projects/pentacular/desk/desk.md.parts.png new file mode 100644 index 000000000..db2abcc80 Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.parts.png differ diff --git a/nb/projects/pentacular/desk/desk.md.parts_parts.png b/nb/projects/pentacular/desk/desk.md.parts_parts.png new file mode 100644 index 000000000..4305eced3 Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.parts_parts.png differ diff --git a/nb/projects/pentacular/desk/desk.md.room.png b/nb/projects/pentacular/desk/desk.md.room.png new file mode 100644 index 000000000..1067d917d Binary files /dev/null and b/nb/projects/pentacular/desk/desk.md.room.png differ diff --git a/nb/projects/pentacular/desk/desk.nb b/nb/projects/pentacular/desk/desk.nb new file mode 100644 index 000000000..d5c459fc5 --- /dev/null +++ b/nb/projects/pentacular/desk/desk.nb @@ -0,0 +1,177 @@ +const Beam = (length) => + Box(20) + .cut( + Box(5, 5) + .x(7.5) + .rz({ by: 1 / 4 }) + ) + .ez([length]) + .material('aluminium') + .x(10) + .y(10) + .maskedBy(Box([20], [20], [length])) + .rx(1 / 4) + .asPart(`Beam${length}`) + .rx(-1 / 4) + .clean(); + +const DeskBase = ({ width = 700, height = 660, depth = 400 } = {}) => + Assembly( + Plywood({ width, depth, height: 10 }).z(height), + Beam(height - 40) + .x(0, width - 20) + .y(0, depth - 20) + .z(20) + .as('uprights'), + Beam(width - 20 * 2) + .ry(1 / 4) + .x(width - 20) + .y(0, depth - 20) + .z(0, height - 20) + .as('beams'), + Beam(depth) + .rx(1 / 4) + .y(depth) + .x(0, width - 20) + .z(0, height - 20) + .as('rafters') + ); + +const DeskTop = ({ + width = 700, + height = 640, + depth = 400, + shelves = [], +} = {}) => + Assembly( + Plywood({ width, depth, height: 10 }).z(height), + Plywood({ width, depth: depth / 2 + 20, height: 10 }) + .y(depth / 2 - 20) + .z(...shelves), + Plywood({ width, depth: height + 20 + 10 }) + .rx(1 / 4) + .y(400 + 10) + .z(-20), + Beam(height - 20) + .x(0, width - 20) + .y(depth / 2 - 20, depth - 20), + Beam(depth) + .rx(1 / 4) + .y(depth) + .x(0, width - 20) + .z(height - 20), + Beam(width - 20 * 2) + .ry(1 / 4) + .x(width - 20) + .y(0, depth - 20) + .z(height - 20) + ); + +const Desk = ({ width = 700, height = 660, depth = 400, shelves = [] } = {}) => + Assembly( + DeskBase({ width, height, depth }), + DeskTop({ width, height, depth, shelves }).z(height) + ); + +const Plywood = ({ width = 700, height = 10, depth = 400 } = {}) => + Box([width], [depth], [height]) + .material('wood') + .at( + align('xyz<').origin(), + and(Hershey(`${width}x${depth}`, 50).align('xyz>').z(1)) + ) + .asPart('Plywood', `Plywood ${width}x${depth}x${height}`); + +const desk = Desk({ width: 700, shelves: [350, 450, 550] }).view(); + +const parts = cornerDesign3 + .get('part:Plywood') + .view() + .each(flat()) + .sort(area(), 'max') + .pack(Box(2440, 1200).copy(10), 0, 1 / 4, { margin: 5 }) + .pack({ margin: 100 }) + .align('xy') + .png('parts', 'top', { size: 1024 }) + .v(18); + +const room = Box([870 + 1580], [3800]) + .cut(Arc(870 * 2).x(1580 + 875)) + .clean() + .view(); + +const desk1400 = Desk({ width: 1400, shelves: [350, 450, 550] }).view(); + +const cornerDesign3 = And( + desk700b + .rz(-1 / 4) + .align('x>y>') + .y(400), + desk1400b.rz(1 / 2).align('x>y>'), + desk1400b + .x(280 + 25) + .y(870 + 700 * 3 + 400) + .ghost(), + desk + .rz(1 / 4) + .align('x>y>') + .x(870 + 1580 - 400) + .y(870 + 700 * 0, 870 + 700 * 1, 870 + 700 * 2) + .ghost(), + Box([1000], [700], [2000]) + .x(870 + 1580 - 1000 + 280) + .y(870 + 700 * 3), + Box([280], [1200], [2500]).y(400 + 700 + 100, 400 + 700 + 1200 + 200), + room +).view('top'); + +const DeskHalfBase = ({ + width = 700, + height = 660, + depth = 400, + shelves = [], +} = {}) => + Assembly( + Plywood({ width, depth, height: 10 }).z(height), + Plywood({ width, depth, height: 10 }).z(20), + Plywood({ width, depth: depth / 2 + 20, height: 10 }) + .y(depth / 2 - 20) + .z(...shelves), + Beam(height - 40) + .x(0, width - 20) + .y(depth - 20, depth / 2 - 20) + .z(20) + .as('uprights'), + Beam(width - 20 * 2) + .ry(1 / 4) + .x(width - 20) + .y(0, depth - 20) + .z(0, height - 20) + .as('beams'), + Beam(depth) + .rx(1 / 4) + .y(depth) + .x(0, width - 20) + .z(0, height - 20) + .as('rafters') + ); + +const deskHalfBase = DeskHalfBase({ + shelves: [20, 120, 220, 320, 420, 520], +}).view(); + +const desk1400b = Assembly( + DeskHalfBase({ + width: 1400, + shelves: [/*120, */ 220, 320, 420, 520], + }), + DeskTop({ width: 1400, shelves: [420, 520] }).z(660) +).view(); + +const desk700b = Assembly( + DeskHalfBase({ + width: 700, + shelves: [120, 220, 320, 420, 520], + }), + DeskTop({ width: 700, shelves: [220, 320, 420, 520] }).z(660) +).view(); diff --git a/nb/projects/pentacular/lego/lego.md b/nb/projects/pentacular/lego/lego.md index 51ada7e5a..538022f4e 100644 --- a/nb/projects/pentacular/lego/lego.md +++ b/nb/projects/pentacular/lego/lego.md @@ -68,8 +68,8 @@ const legoify = () => (shape) => shape .and( faces().op( - sort('z>').n(0).put(Stud().x(-4, 4).y(-4, 4)), - sort('z<').n(0).put(Socket().x(-4, 4).y(-4, 4)) + sort(size('top'), 'max', 1).put(Stud().x(-4, 4).y(-4, 4)), + sort(size('bottom'), 'min', 1).put(Socket().x(-4, 4).y(-4, 4)) ) ) .disjoint() diff --git a/nb/projects/pentacular/lego/lego.nb b/nb/projects/pentacular/lego/lego.nb index 4b88b3d62..4a0218af6 100644 --- a/nb/projects/pentacular/lego/lego.nb +++ b/nb/projects/pentacular/lego/lego.nb @@ -61,8 +61,8 @@ const legoify = () => (shape) => shape .and( faces().op( - sort('z>').n(0).put(Stud().x(-4, 4).y(-4, 4)), - sort('z<').n(0).put(Socket().x(-4, 4).y(-4, 4)) + sort(size('top'), 'max', 1).put(Stud().x(-4, 4).y(-4, 4)), + sort(size('bottom'), 'min', 1).put(Socket().x(-4, 4).y(-4, 4)) ) ) .disjoint() diff --git a/nb/regression/shape/shape.md b/nb/regression/shape/shape.md index 8fbaaf7d7..10e14ed3c 100644 --- a/nb/regression/shape/shape.md +++ b/nb/regression/shape/shape.md @@ -335,13 +335,13 @@ const b = Box(5) ![Image](shape.md.$47.png) ```JavaScript -b.at(eachEdge().sort('x').n(0).origin(), cut(Box(3, 3, 11))).view(); +b.at(eachEdge().sort(size('maxZ'), 'max', 1).sort(size('minY'), 'min', 1).sort(size('minX'), 'min', 1).n(0).origin(), cut(Box(3, 3, 11))).view(); ``` ![Image](shape.md.$48.png) ```JavaScript -b.by(eachEdge().sort('x').n(0).origin()).cut(Box(3, 3, 11)).view(); +b.by(eachEdge().sort(size('maxZ'), 'max', 1).sort(size('minY'), 'min', 1).sort(size('minX'), 'min', 1).n(0).origin()).cut(Box(3, 3, 11)).view(); ``` ![Image](shape.md.$49.png) @@ -502,13 +502,13 @@ Triangle(4) ![Image](shape.md.$65.png) ```JavaScript -Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort('x<3').view(); +Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort(size('left')).view(); ``` ![Image](shape.md.$66.png) ```JavaScript -Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort('x>1').view(); +Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort(size('right')).view(); ``` ![Image](shape.md.$67.png) diff --git a/nb/regression/shape/shape.md.$47.png b/nb/regression/shape/shape.md.$47.png index 4df2a517e..8645ae1d0 100644 Binary files a/nb/regression/shape/shape.md.$47.png and b/nb/regression/shape/shape.md.$47.png differ diff --git a/nb/regression/shape/shape.md.$48.png b/nb/regression/shape/shape.md.$48.png index 8b880df62..f29b4565c 100644 Binary files a/nb/regression/shape/shape.md.$48.png and b/nb/regression/shape/shape.md.$48.png differ diff --git a/nb/regression/shape/shape.nb b/nb/regression/shape/shape.nb index afecab425..fcbafa956 100644 --- a/nb/regression/shape/shape.nb +++ b/nb/regression/shape/shape.nb @@ -39,7 +39,7 @@ Box(10).to(Point(1, 2, 3)).gridView(); Box(10).move(1, 2, 3).to(centroid()).gridView(); // I don't know what this example is supposed to do. -// Box(10, 10, 10).move(faces().sort('z>').n(0)).view(); +// Box(10, 10, 10).move(faces().sort(size('top')).n(0)).view(); // Let's simplify it for now. Box(10, 10, 10).view(); @@ -142,9 +142,11 @@ const b = Box(5) .material('glass') .y(10); -b.at(eachEdge().sort('x').n(0).origin(), cut(Box(3, 3, 11))).view(); +// b.at(eachEdge().sort('x').n(0).origin(), cut(Box(3, 3, 11))).view(); +b.at(eachEdge().sort(size('maxZ'), 'max', 1).sort(size('minY'), 'min', 1).sort(size('minX'), 'min', 1).n(0).origin(), cut(Box(3, 3, 11))).view(); -b.by(eachEdge().sort('x').n(0).origin()).cut(Box(3, 3, 11)).view(); +// b.by(eachEdge().sort('x').n(0).origin()).cut(Box(3, 3, 11)).view(); +b.by(eachEdge().sort(size('maxZ'), 'max', 1).sort(size('minY'), 'min', 1).sort(size('minX'), 'min', 1).n(0).origin()).cut(Box(3, 3, 11)).view(); Voxels([0, 0, 0], [0, 0, 1], [1, 0, 1], [2, 0, 1]).view(); @@ -225,9 +227,11 @@ Triangle(4) .at(origin(), rz(1 / 16)) .view(); -Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort('x<3').view(); +// Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort('x<3').view(); +Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort(size('left')).view(); -Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort('x>1').view(); +// Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort('x>1').view(); +Group(Box(), Triangle(1).x(2), Hexagon(1).x(4)).sort(size('right')).view(); Box(5, 5, 20) .rx(1 / 4, 1 / 32)