@jwc/jscad-utils 5.2.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -5
- package/dist/compat.js +641 -100
- package/dist/examples/bisect.jscad +471 -93
- package/dist/examples/bisect2.jscad +2614 -0
- package/dist/examples/boxes.jscad +481 -102
- package/dist/examples/chamfer.jscad +471 -93
- package/dist/examples/fillet.jscad +471 -93
- package/dist/examples/fit.jscad +471 -93
- package/dist/examples/groups.jscad +471 -93
- package/dist/examples/midlineTo.jscad +471 -93
- package/dist/examples/parts-hexagon.jscad +471 -93
- package/dist/examples/rabett-tb.jscad +471 -93
- package/dist/examples/rabett.jscad +471 -93
- package/dist/examples/rabett2.jscad +471 -93
- package/dist/examples/rabett3.jscad +2614 -0
- package/dist/examples/retraction-test.jscad +471 -93
- package/dist/examples/size.jscad +471 -93
- package/dist/examples/snap.jscad +471 -93
- package/dist/examples/text.jscad +471 -93
- package/dist/examples/wedge.jscad +471 -93
- package/dist/index.js +638 -100
- package/package.json +8 -4
- package/src/add-prototype.js +5 -1
- package/src/boxes.js +11 -3
- package/src/compat.js +3 -0
- package/src/group.js +13 -11
- package/src/parts.js +25 -23
- package/src/util.js +239 -72
- package/src/validate.js +335 -0
package/src/util.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a detailed documentation comment for the function.
|
|
3
|
+
* @param {any} o - The object to convert to a string representation
|
|
4
|
+
* @returns {string} A string representation of the object, including polygon count and properties for CSG-like objects, or the object's toString() result
|
|
5
|
+
* @function jscadToString
|
|
6
|
+
*/
|
|
1
7
|
/** @typedef { import('@jscad/csg').CSG } CSG */
|
|
2
8
|
|
|
9
|
+
/** @typedef {"x"|"y"|"z"|"xy"|"yz"|"xz"|"xyz"} AxisStrings Composed axis strings*/
|
|
10
|
+
/** @typedef {"x"|"y"|"z"} AxisString Individual axis strings */
|
|
11
|
+
|
|
3
12
|
import * as array from './array';
|
|
4
13
|
import { Debug } from './debug';
|
|
5
14
|
// import jsCadCSG from '@jscad/csg';
|
|
@@ -16,7 +25,10 @@ import {
|
|
|
16
25
|
vector_char,
|
|
17
26
|
vector_text
|
|
18
27
|
} from './jscad';
|
|
28
|
+
import { AssertValidCSG } from './validate';
|
|
19
29
|
const debug = Debug('jscadUtils:util');
|
|
30
|
+
const assertValidCSG = AssertValidCSG('util');
|
|
31
|
+
|
|
20
32
|
// import utilInit from '../src/add-prototype';
|
|
21
33
|
// utilInit(CSG);
|
|
22
34
|
// console.trace('CSG', CSG.prototype);
|
|
@@ -193,7 +205,7 @@ export function label(text, x, y, width, height) {
|
|
|
193
205
|
// console.trace('label', Object.getPrototypeOf(union(o)));
|
|
194
206
|
// var foo = union(o);
|
|
195
207
|
// console.trace('typeof', typeof foo);
|
|
196
|
-
return center(union(o));
|
|
208
|
+
return assertValidCSG(center(union(o)), 'label');
|
|
197
209
|
}
|
|
198
210
|
|
|
199
211
|
export function text(text) {
|
|
@@ -209,10 +221,10 @@ export function text(text) {
|
|
|
209
221
|
|
|
210
222
|
export function unitCube(length, radius) {
|
|
211
223
|
radius = radius || 0.5;
|
|
212
|
-
return CSG.cube({
|
|
224
|
+
return assertValidCSG(CSG.cube({
|
|
213
225
|
center: [0, 0, 0],
|
|
214
226
|
radius: [radius, radius, length || 0.5]
|
|
215
|
-
});
|
|
227
|
+
}), 'unitCube');
|
|
216
228
|
}
|
|
217
229
|
|
|
218
230
|
export function unitAxis(length, radius, centroid) {
|
|
@@ -230,7 +242,7 @@ export function unitAxis(length, radius, centroid) {
|
|
|
230
242
|
[1, 0, 0],
|
|
231
243
|
[0, 1, 0]
|
|
232
244
|
);
|
|
233
|
-
return unitaxis.translate(centroid);
|
|
245
|
+
return assertValidCSG(unitaxis.translate(centroid), 'unitAxis');
|
|
234
246
|
}
|
|
235
247
|
|
|
236
248
|
export function toArray(a) {
|
|
@@ -372,17 +384,17 @@ export function scale(size, value) {
|
|
|
372
384
|
|
|
373
385
|
export function center(object, objectSize) {
|
|
374
386
|
objectSize = objectSize || size(object.getBounds());
|
|
375
|
-
return centerY(centerX(object, objectSize), objectSize);
|
|
387
|
+
return assertValidCSG(centerY(centerX(object, objectSize), objectSize), 'center');
|
|
376
388
|
}
|
|
377
389
|
|
|
378
390
|
export function centerY(object, objectSize) {
|
|
379
391
|
objectSize = objectSize || size(object.getBounds());
|
|
380
|
-
return object.translate([0, -objectSize.y / 2, 0]);
|
|
392
|
+
return assertValidCSG(object.translate([0, -objectSize.y / 2, 0]), 'centerY');
|
|
381
393
|
}
|
|
382
394
|
|
|
383
395
|
export function centerX(object, objectSize) {
|
|
384
396
|
objectSize = objectSize || size(object.getBounds());
|
|
385
|
-
return object.translate([-objectSize.x / 2, 0, 0]);
|
|
397
|
+
return assertValidCSG(object.translate([-objectSize.x / 2, 0, 0]), 'centerX');
|
|
386
398
|
}
|
|
387
399
|
|
|
388
400
|
/**
|
|
@@ -419,7 +431,7 @@ export function enlarge(object, x, y, z) {
|
|
|
419
431
|
/// Calculate the difference between the original centroid and the new
|
|
420
432
|
var delta = new_centroid.minus(objectCentroid).times(-1);
|
|
421
433
|
|
|
422
|
-
return new_object.translate(delta);
|
|
434
|
+
return assertValidCSG(new_object.translate(delta), 'enlarge');
|
|
423
435
|
}
|
|
424
436
|
|
|
425
437
|
/**
|
|
@@ -458,7 +470,7 @@ export function fit(object, x, y, z, keep_aspect_ratio) {
|
|
|
458
470
|
scale(objectSize.z, z)
|
|
459
471
|
];
|
|
460
472
|
var min = array.min(s);
|
|
461
|
-
return centerWith(
|
|
473
|
+
return assertValidCSG(centerWith(
|
|
462
474
|
object.scale(
|
|
463
475
|
s.map(function (d, i) {
|
|
464
476
|
if (a[i] === 0) return 1; // don't scale when value is zero
|
|
@@ -467,7 +479,7 @@ export function fit(object, x, y, z, keep_aspect_ratio) {
|
|
|
467
479
|
),
|
|
468
480
|
'xyz',
|
|
469
481
|
object
|
|
470
|
-
);
|
|
482
|
+
), 'fit');
|
|
471
483
|
}
|
|
472
484
|
|
|
473
485
|
export function shift(object, x, y, z) {
|
|
@@ -477,15 +489,15 @@ export function shift(object, x, y, z) {
|
|
|
477
489
|
|
|
478
490
|
export function zero(object) {
|
|
479
491
|
var bounds = object.getBounds();
|
|
480
|
-
return object.translate([0, 0, -bounds[0].z]);
|
|
492
|
+
return assertValidCSG(object.translate([0, 0, -bounds[0].z]), 'zero');
|
|
481
493
|
}
|
|
482
494
|
|
|
483
495
|
export function mirrored4(x) {
|
|
484
|
-
return x.union([
|
|
496
|
+
return assertValidCSG(x.union([
|
|
485
497
|
x.mirroredY(90),
|
|
486
498
|
x.mirroredX(90),
|
|
487
499
|
x.mirroredY(90).mirroredX(90)
|
|
488
|
-
]);
|
|
500
|
+
]), 'mirrored4');
|
|
489
501
|
}
|
|
490
502
|
|
|
491
503
|
export const flushSide = {
|
|
@@ -570,7 +582,7 @@ export function calcSnap(moveobj, withobj, axes, orientation, delta = 0) {
|
|
|
570
582
|
export function snap(moveobj, withobj, axis, orientation, delta) {
|
|
571
583
|
debug('snap', moveobj, withobj, axis, orientation, delta);
|
|
572
584
|
var t = calcSnap(moveobj, withobj, axis, orientation, delta);
|
|
573
|
-
return moveobj.translate(t);
|
|
585
|
+
return assertValidCSG(moveobj.translate(t), 'snap');
|
|
574
586
|
}
|
|
575
587
|
|
|
576
588
|
/**
|
|
@@ -583,26 +595,40 @@ export function snap(moveobj, withobj, axis, orientation, delta) {
|
|
|
583
595
|
* @return {CSG} [description]
|
|
584
596
|
*/
|
|
585
597
|
export function flush(moveobj, withobj, axis, mside, wside) {
|
|
586
|
-
return moveobj.translate(calcFlush(moveobj, withobj, axis, mside, wside));
|
|
598
|
+
return assertValidCSG(moveobj.translate(calcFlush(moveobj, withobj, axis, mside, wside)), 'flush');
|
|
587
599
|
}
|
|
588
600
|
|
|
601
|
+
/**
|
|
602
|
+
*
|
|
603
|
+
* @param {AxisStrings} axes
|
|
604
|
+
* @param {function(number, string): number} valfun
|
|
605
|
+
* @param {Array<number>} [a] In initial array to apply the values to, if not provided a new array will be created.
|
|
606
|
+
* @returns {Array<number>} The resulting array after applying the function to the specified axes.
|
|
607
|
+
*/
|
|
589
608
|
export function axisApply(axes, valfun, a) {
|
|
590
609
|
debug('axisApply', axes, valfun, a);
|
|
610
|
+
|
|
611
|
+
/** @type {Array<number>} */
|
|
591
612
|
var retval = a || [0, 0, 0];
|
|
613
|
+
|
|
614
|
+
/** @type {Record<AxisString, number>} */
|
|
592
615
|
var lookup = {
|
|
593
616
|
x: 0,
|
|
594
617
|
y: 1,
|
|
595
618
|
z: 2
|
|
596
619
|
};
|
|
597
620
|
axes.split('').forEach(function (axis) {
|
|
598
|
-
retval[lookup[axis]] = valfun(
|
|
621
|
+
retval[lookup[/** @type {AxisString} */ (axis)]] = valfun(
|
|
622
|
+
lookup[/** @type {AxisString} */ (axis)],
|
|
623
|
+
axis
|
|
624
|
+
);
|
|
599
625
|
});
|
|
600
626
|
|
|
601
627
|
return retval;
|
|
602
628
|
}
|
|
603
629
|
|
|
604
630
|
export function axis2array(axes, valfun) {
|
|
605
|
-
depreciated('axis2array');
|
|
631
|
+
depreciated('axis2array', false, 'Use axisApply instead.');
|
|
606
632
|
var a = [0, 0, 0];
|
|
607
633
|
var lookup = {
|
|
608
634
|
x: 0,
|
|
@@ -652,7 +678,7 @@ export function calcmidlineTo(o, axis, to) {
|
|
|
652
678
|
}
|
|
653
679
|
|
|
654
680
|
export function midlineTo(o, axis, to) {
|
|
655
|
-
return o.translate(calcmidlineTo(o, axis, to));
|
|
681
|
+
return assertValidCSG(o.translate(calcmidlineTo(o, axis, to)), 'midlineTo');
|
|
656
682
|
}
|
|
657
683
|
|
|
658
684
|
export function translator(o, axis, withObj) {
|
|
@@ -678,7 +704,7 @@ export function calcCenterWith(o, axes, withObj, delta = 0) {
|
|
|
678
704
|
}
|
|
679
705
|
|
|
680
706
|
export function centerWith(o, axis, withObj) {
|
|
681
|
-
return o.translate(calcCenterWith(o, axis, withObj));
|
|
707
|
+
return assertValidCSG(o.translate(calcCenterWith(o, axis, withObj)), 'centerWith');
|
|
682
708
|
}
|
|
683
709
|
|
|
684
710
|
/**
|
|
@@ -709,6 +735,118 @@ export function getDelta(size, bounds, axis, offset, nonzero) {
|
|
|
709
735
|
});
|
|
710
736
|
}
|
|
711
737
|
|
|
738
|
+
|
|
739
|
+
var EPS = 1e-5;
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Split a CSG object into two halves along a plane by directly
|
|
743
|
+
* splitting polygons. This avoids BSP-tree-based boolean operations
|
|
744
|
+
* which can fail on geometry produced by stretchAtPlane.
|
|
745
|
+
* @param {CSG} csg The object to split
|
|
746
|
+
* @param {CSG.Plane} plane The splitting plane
|
|
747
|
+
* @return {{ front: CSG, back: CSG }} front (positive normal side) and back (negative normal side)
|
|
748
|
+
*/
|
|
749
|
+
function splitCSGByPlane(csg, plane) {
|
|
750
|
+
var frontPolys = [];
|
|
751
|
+
var backPolys = [];
|
|
752
|
+
|
|
753
|
+
csg.polygons.forEach(function (poly) {
|
|
754
|
+
var vertices = poly.vertices;
|
|
755
|
+
var numVerts = vertices.length;
|
|
756
|
+
var hasfront = false;
|
|
757
|
+
var hasback = false;
|
|
758
|
+
var vertexIsBack = [];
|
|
759
|
+
|
|
760
|
+
for (var i = 0; i < numVerts; i++) {
|
|
761
|
+
var t = plane.normal.dot(vertices[i].pos) - plane.w;
|
|
762
|
+
vertexIsBack.push(t < 0);
|
|
763
|
+
if (t > EPS) hasfront = true;
|
|
764
|
+
if (t < -EPS) hasback = true;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (!hasfront && !hasback) {
|
|
768
|
+
// coplanar — assign based on normal alignment
|
|
769
|
+
var d = plane.normal.dot(poly.plane.normal);
|
|
770
|
+
if (d >= 0) {
|
|
771
|
+
frontPolys.push(poly);
|
|
772
|
+
} else {
|
|
773
|
+
backPolys.push(poly);
|
|
774
|
+
}
|
|
775
|
+
} else if (!hasback) {
|
|
776
|
+
frontPolys.push(poly);
|
|
777
|
+
} else if (!hasfront) {
|
|
778
|
+
backPolys.push(poly);
|
|
779
|
+
} else {
|
|
780
|
+
// spanning — split the polygon
|
|
781
|
+
var fv = [];
|
|
782
|
+
var bv = [];
|
|
783
|
+
for (var vi = 0; vi < numVerts; vi++) {
|
|
784
|
+
var vertex = vertices[vi];
|
|
785
|
+
var nextVi = (vi + 1) % numVerts;
|
|
786
|
+
var isback = vertexIsBack[vi];
|
|
787
|
+
var nextisback = vertexIsBack[nextVi];
|
|
788
|
+
|
|
789
|
+
if (isback === nextisback) {
|
|
790
|
+
if (isback) {
|
|
791
|
+
bv.push(vertex);
|
|
792
|
+
} else {
|
|
793
|
+
fv.push(vertex);
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
var point = vertex.pos;
|
|
797
|
+
var nextpoint = vertices[nextVi].pos;
|
|
798
|
+
var ip = plane.splitLineBetweenPoints(point, nextpoint);
|
|
799
|
+
var iv = new CSG.Vertex(ip);
|
|
800
|
+
if (isback) {
|
|
801
|
+
bv.push(vertex);
|
|
802
|
+
bv.push(iv);
|
|
803
|
+
fv.push(iv);
|
|
804
|
+
} else {
|
|
805
|
+
fv.push(vertex);
|
|
806
|
+
fv.push(iv);
|
|
807
|
+
bv.push(iv);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
// Remove degenerate (near-duplicate) adjacent vertices that arise
|
|
812
|
+
// when the cut plane passes very close to existing vertices.
|
|
813
|
+
// This matches the cleanup done by the BSP-tree splitter.
|
|
814
|
+
var EPSEPS = EPS * EPS;
|
|
815
|
+
if (fv.length >= 3) {
|
|
816
|
+
var prev = fv[fv.length - 1];
|
|
817
|
+
for (var fi = 0; fi < fv.length; fi++) {
|
|
818
|
+
var curr = fv[fi];
|
|
819
|
+
if (curr.pos.distanceToSquared(prev.pos) < EPSEPS) {
|
|
820
|
+
fv.splice(fi, 1);
|
|
821
|
+
fi--;
|
|
822
|
+
}
|
|
823
|
+
prev = curr;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
if (bv.length >= 3) {
|
|
827
|
+
var prev = bv[bv.length - 1];
|
|
828
|
+
for (var bi = 0; bi < bv.length; bi++) {
|
|
829
|
+
var curr = bv[bi];
|
|
830
|
+
if (curr.pos.distanceToSquared(prev.pos) < EPSEPS) {
|
|
831
|
+
bv.splice(bi, 1);
|
|
832
|
+
bi--;
|
|
833
|
+
}
|
|
834
|
+
prev = curr;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (fv.length >= 3)
|
|
838
|
+
frontPolys.push(new CSG.Polygon(fv, poly.shared, poly.plane));
|
|
839
|
+
if (bv.length >= 3)
|
|
840
|
+
backPolys.push(new CSG.Polygon(bv, poly.shared, poly.plane));
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
return {
|
|
845
|
+
front: CSG.fromPolygons(frontPolys),
|
|
846
|
+
back: CSG.fromPolygons(backPolys)
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
712
850
|
/**
|
|
713
851
|
* Cut an object into two pieces, along a given axis. The offset
|
|
714
852
|
* allows you to move the cut plane along the cut axis. For example,
|
|
@@ -718,7 +856,7 @@ export function getDelta(size, bounds, axis, offset, nonzero) {
|
|
|
718
856
|
*
|
|
719
857
|
* You can angle the cut plane and position the rotation point.
|
|
720
858
|
*
|
|
721
|
-
* 
|
|
722
860
|
* @param {CSG} object object to bisect
|
|
723
861
|
* @param {string} axis axis to cut along
|
|
724
862
|
* @param {number} [offset] offset to cut at
|
|
@@ -841,9 +979,33 @@ export function bisect(...args) {
|
|
|
841
979
|
}
|
|
842
980
|
);
|
|
843
981
|
|
|
982
|
+
var negative = object.cutByPlane(cutplane.plane);
|
|
983
|
+
var positive = object.cutByPlane(cutplane.plane.flipped());
|
|
984
|
+
|
|
985
|
+
// Detect cutByPlane failure: if a half's bounding box in the cut axis
|
|
986
|
+
// is not smaller than the original, the BSP-tree-based cut failed.
|
|
987
|
+
// Fall back to direct polygon splitting which is more robust, then
|
|
988
|
+
// apply cutByPlane to the simpler half to add cap faces.
|
|
989
|
+
var negSize = size(negative);
|
|
990
|
+
var posSize = size(positive);
|
|
991
|
+
if (negSize[axis] >= objectSize[axis] - EPS ||
|
|
992
|
+
posSize[axis] >= objectSize[axis] - EPS) {
|
|
993
|
+
var halves = splitCSGByPlane(object, cutplane.plane);
|
|
994
|
+
if (negSize[axis] >= objectSize[axis] - EPS) {
|
|
995
|
+
negative = halves.back;
|
|
996
|
+
// Cap the open cut face
|
|
997
|
+
try { negative = negative.cutByPlane(cutplane.plane); } catch (e) { /* keep uncapped */ }
|
|
998
|
+
}
|
|
999
|
+
if (posSize[axis] >= objectSize[axis] - EPS) {
|
|
1000
|
+
positive = halves.front;
|
|
1001
|
+
// Cap the open cut face
|
|
1002
|
+
try { positive = positive.cutByPlane(cutplane.plane.flipped()); } catch (e) { /* keep uncapped */ }
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
844
1006
|
var g = Group('negative,positive', [
|
|
845
|
-
|
|
846
|
-
|
|
1007
|
+
negative.color(options.color && 'red'),
|
|
1008
|
+
positive.color(options.color && 'blue')
|
|
847
1009
|
]);
|
|
848
1010
|
|
|
849
1011
|
if (options.addRotationCenter)
|
|
@@ -905,7 +1067,7 @@ export function slice(
|
|
|
905
1067
|
* Creates a `JsCadUtilsGroup` object that has `body` and `wedge` objects. The `wedge` object
|
|
906
1068
|
* is created by radially cutting the object from the `start` to the `end` angle.
|
|
907
1069
|
*
|
|
908
|
-
* 
|
|
909
1071
|
*
|
|
910
1072
|
*
|
|
911
1073
|
* @example
|
|
@@ -961,7 +1123,7 @@ export function stretch(object, axis, distance, offset) {
|
|
|
961
1123
|
var objectSize = size(object);
|
|
962
1124
|
var cutDelta = getDelta(objectSize, bounds, axis, offset, true);
|
|
963
1125
|
// debug('stretch.cutDelta', cutDelta, normal[axis]);
|
|
964
|
-
return object.stretchAtPlane(normal[axis], cutDelta, distance);
|
|
1126
|
+
return assertValidCSG(object.stretchAtPlane(normal[axis], cutDelta, distance), 'stretch');
|
|
965
1127
|
}
|
|
966
1128
|
|
|
967
1129
|
/**
|
|
@@ -1018,7 +1180,7 @@ export function poly2solid(top, bottom, height) {
|
|
|
1018
1180
|
);
|
|
1019
1181
|
// }
|
|
1020
1182
|
|
|
1021
|
-
return CSG.fromPolygons(polygons);
|
|
1183
|
+
return assertValidCSG(CSG.fromPolygons(polygons), 'poly2solid');
|
|
1022
1184
|
}
|
|
1023
1185
|
|
|
1024
1186
|
export function slices2poly(slices, options, axis) {
|
|
@@ -1102,7 +1264,7 @@ export function slices2poly(slices, options, axis) {
|
|
|
1102
1264
|
}
|
|
1103
1265
|
});
|
|
1104
1266
|
|
|
1105
|
-
return CSG.fromPolygons(polygons);
|
|
1267
|
+
return assertValidCSG(CSG.fromPolygons(polygons), 'slices2poly');
|
|
1106
1268
|
}
|
|
1107
1269
|
|
|
1108
1270
|
export function normalVector(axis) {
|
|
@@ -1116,7 +1278,7 @@ export function normalVector(axis) {
|
|
|
1116
1278
|
normalVector: CSG.Vector3D.Create(0, 0, 1)
|
|
1117
1279
|
},
|
|
1118
1280
|
y: {
|
|
1119
|
-
orthoNormalCartesian: ['
|
|
1281
|
+
orthoNormalCartesian: ['Z', 'X'],
|
|
1120
1282
|
normalVector: CSG.Vector3D.Create(0, 0, 1)
|
|
1121
1283
|
}
|
|
1122
1284
|
};
|
|
@@ -1170,6 +1332,7 @@ export function sliceParams(orientation, radius, bounds) {
|
|
|
1170
1332
|
// };
|
|
1171
1333
|
// },
|
|
1172
1334
|
|
|
1335
|
+
|
|
1173
1336
|
export function reShape(object, radius, orientation, options, slicer) {
|
|
1174
1337
|
options = options || {};
|
|
1175
1338
|
var b = object.getBounds();
|
|
@@ -1211,57 +1374,61 @@ export function reShape(object, radius, orientation, options, slicer) {
|
|
|
1211
1374
|
).color(options.color);
|
|
1212
1375
|
|
|
1213
1376
|
var remainder = object.cutByPlane(plane);
|
|
1214
|
-
return union([
|
|
1377
|
+
return assertValidCSG(union([
|
|
1215
1378
|
options.unionOriginal ? object : remainder,
|
|
1216
1379
|
delta.translate(si.moveDelta)
|
|
1217
|
-
]);
|
|
1380
|
+
]), 'reShape');
|
|
1218
1381
|
}
|
|
1219
1382
|
|
|
1220
1383
|
export function chamfer(object, radius, orientation, options) {
|
|
1221
|
-
return reShape(
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1384
|
+
return assertValidCSG(reShape(
|
|
1385
|
+
object,
|
|
1386
|
+
radius,
|
|
1387
|
+
orientation,
|
|
1388
|
+
options,
|
|
1389
|
+
function (first, last, slice) {
|
|
1390
|
+
return [
|
|
1391
|
+
{
|
|
1392
|
+
poly: slice,
|
|
1393
|
+
offset: new CSG.Vector3D(first)
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
poly: enlarge(slice, [-radius * 2, -radius * 2]),
|
|
1397
|
+
offset: new CSG.Vector3D(last)
|
|
1398
|
+
}
|
|
1399
|
+
];
|
|
1400
|
+
}
|
|
1401
|
+
), 'chamfer');
|
|
1237
1402
|
}
|
|
1238
1403
|
|
|
1239
1404
|
export function fillet(object, radius, orientation, options) {
|
|
1240
1405
|
options = options || {};
|
|
1241
|
-
return reShape(
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
var
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1406
|
+
return assertValidCSG(reShape(
|
|
1407
|
+
object,
|
|
1408
|
+
radius,
|
|
1409
|
+
orientation,
|
|
1410
|
+
options,
|
|
1411
|
+
function (first, last, slice) {
|
|
1412
|
+
var v1 = new CSG.Vector3D(first);
|
|
1413
|
+
var v2 = new CSG.Vector3D(last);
|
|
1414
|
+
|
|
1415
|
+
var res = options.resolution || CSG.defaultResolution3D;
|
|
1416
|
+
|
|
1417
|
+
var slices = array.range(0, res).map(function (i) {
|
|
1418
|
+
var p = i > 0 ? i / (res - 1) : 0;
|
|
1419
|
+
var v = v1.lerp(v2, p);
|
|
1420
|
+
|
|
1421
|
+
var size = -radius * 2 - Math.cos(Math.asin(p)) * (-radius * 2);
|
|
1422
|
+
|
|
1423
|
+
return {
|
|
1424
|
+
poly: enlarge(slice, [size, size]),
|
|
1425
|
+
offset: v
|
|
1426
|
+
};
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
return slices;
|
|
1430
|
+
}
|
|
1431
|
+
), 'fillet');
|
|
1265
1432
|
}
|
|
1266
1433
|
|
|
1267
1434
|
export function calcRotate(part, solid, axis /* , angle */) {
|
|
@@ -1278,7 +1445,7 @@ export function calcRotate(part, solid, axis /* , angle */) {
|
|
|
1278
1445
|
export function rotateAround(part, solid, axis, angle) {
|
|
1279
1446
|
var { rotationCenter, rotationAxis } = calcRotate(part, solid, axis, angle);
|
|
1280
1447
|
|
|
1281
|
-
return part.rotate(rotationCenter, rotationAxis, angle);
|
|
1448
|
+
return assertValidCSG('rotateAround')(part.rotate(rotationCenter, rotationAxis, angle));
|
|
1282
1449
|
}
|
|
1283
1450
|
function cloneProperties(from, to, depth = 0) {
|
|
1284
1451
|
return Object.entries(from).reduce((props, [key, value]) => {
|
|
@@ -1293,7 +1460,7 @@ export function clone(o) {
|
|
|
1293
1460
|
cloneProperties(o, c);
|
|
1294
1461
|
|
|
1295
1462
|
debug('clone', o, c, CSG);
|
|
1296
|
-
return c;
|
|
1463
|
+
return assertValidCSG(c, 'clone');
|
|
1297
1464
|
}
|
|
1298
1465
|
|
|
1299
1466
|
/**
|
|
@@ -1313,5 +1480,5 @@ export function addConnector(
|
|
|
1313
1480
|
normal = [0, 0, 1]
|
|
1314
1481
|
) {
|
|
1315
1482
|
object.properties[name] = new CSG.Connector(point, axis, normal);
|
|
1316
|
-
return object;
|
|
1483
|
+
return assertValidCSG('addConnector')(object);
|
|
1317
1484
|
}
|