@jwc/jscad-utils 5.1.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 -7
- 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/dist/compat.js
CHANGED
|
@@ -38,6 +38,9 @@ function initJscadutils(_CSG, options = {}) {
|
|
|
38
38
|
{ enabled: [], disabled: [] }
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
+
var jscadUtilsAssertValidCSGWarnings = options.assertValidCSGWarnings || false;
|
|
42
|
+
var jscadUtilsAssertValidCSG = options.assertValidCSG || false;
|
|
43
|
+
|
|
41
44
|
// include:compat
|
|
42
45
|
// ../dist/index.js
|
|
43
46
|
var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
@@ -131,6 +134,54 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
131
134
|
function _arrayWithHoles(r) {
|
|
132
135
|
if (Array.isArray(r)) return r;
|
|
133
136
|
}
|
|
137
|
+
function _createForOfIteratorHelper(r, e) {
|
|
138
|
+
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
139
|
+
if (!t) {
|
|
140
|
+
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
|
|
141
|
+
t && (r = t);
|
|
142
|
+
var n = 0,
|
|
143
|
+
F = function () {};
|
|
144
|
+
return {
|
|
145
|
+
s: F,
|
|
146
|
+
n: function () {
|
|
147
|
+
return n >= r.length ? {
|
|
148
|
+
done: !0
|
|
149
|
+
} : {
|
|
150
|
+
done: !1,
|
|
151
|
+
value: r[n++]
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
e: function (r) {
|
|
155
|
+
throw r;
|
|
156
|
+
},
|
|
157
|
+
f: F
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
161
|
+
}
|
|
162
|
+
var o,
|
|
163
|
+
a = !0,
|
|
164
|
+
u = !1;
|
|
165
|
+
return {
|
|
166
|
+
s: function () {
|
|
167
|
+
t = t.call(r);
|
|
168
|
+
},
|
|
169
|
+
n: function () {
|
|
170
|
+
var r = t.next();
|
|
171
|
+
return a = r.done, r;
|
|
172
|
+
},
|
|
173
|
+
e: function (r) {
|
|
174
|
+
u = !0, o = r;
|
|
175
|
+
},
|
|
176
|
+
f: function () {
|
|
177
|
+
try {
|
|
178
|
+
a || null == t.return || t.return();
|
|
179
|
+
} finally {
|
|
180
|
+
if (u) throw o;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
134
185
|
function _defineProperty(e, r, t) {
|
|
135
186
|
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
136
187
|
value: t,
|
|
@@ -803,6 +854,344 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
803
854
|
return o.setColor(c);
|
|
804
855
|
}
|
|
805
856
|
|
|
857
|
+
/* globals jscadUtilsAssertValidCSG jscadUtilsAssertValidCSGWarnings */
|
|
858
|
+
/**
|
|
859
|
+
* @typedef {Object} CSG
|
|
860
|
+
* @property {Array<{vertices: Array<{pos: any}>}>} polygons
|
|
861
|
+
* @property {function(): CSG} [canonicalized]
|
|
862
|
+
* @property {function(): CSG} [reTesselated]
|
|
863
|
+
* @property {function(): CSG} [fixTJunctions]
|
|
864
|
+
* @property {function(): Array} [getBounds]
|
|
865
|
+
* @property {Object} [properties]
|
|
866
|
+
*/
|
|
867
|
+
/**
|
|
868
|
+
* Validate that a CSG object represents a solid, watertight mesh
|
|
869
|
+
* without degenerate faces. Returns an object with an `ok` boolean
|
|
870
|
+
* and an `errors` array describing any problems found.
|
|
871
|
+
*
|
|
872
|
+
* Checks performed:
|
|
873
|
+
* - **No empty mesh** – the object must contain at least one polygon.
|
|
874
|
+
* - **No degenerate polygons** – every polygon must have ≥ 3 vertices
|
|
875
|
+
* and a computable area greater than `EPS²`.
|
|
876
|
+
* - **Watertight / manifold edges** – every directed edge A→B in the
|
|
877
|
+
* mesh must be matched by exactly one reverse edge B→A in another
|
|
878
|
+
* polygon. Unmatched edges indicate holes; edges shared more than
|
|
879
|
+
* twice indicate non-manifold geometry.
|
|
880
|
+
*
|
|
881
|
+
* By default, the mesh is canonicalized and T-junctions are repaired
|
|
882
|
+
* before validation so that results from boolean operations (union,
|
|
883
|
+
* subtract, intersect) can be validated successfully. Pass
|
|
884
|
+
* `{ fixTJunctions: false }` to skip this step and validate the raw
|
|
885
|
+
* mesh.
|
|
886
|
+
*
|
|
887
|
+
* @param {CSG} csg The CSG object to validate.
|
|
888
|
+
* @param {object} [options] Validation options.
|
|
889
|
+
* @param {boolean} [options.fixTJunctions=true] Whether to canonicalize and fix T-junctions before validation.
|
|
890
|
+
* @return {{ ok: boolean, errors: string[], warnings: string[] }} Validation result.
|
|
891
|
+
* @function validateCSG
|
|
892
|
+
*/
|
|
893
|
+
function validateCSG(csg, options) {
|
|
894
|
+
/** @type {string[]} */
|
|
895
|
+
var errors = [];
|
|
896
|
+
/** @type {string[]} */
|
|
897
|
+
var warnings = [];
|
|
898
|
+
if (!csg || !csg.polygons || csg.polygons.length === 0) {
|
|
899
|
+
errors.push('Empty mesh: no polygons');
|
|
900
|
+
return {
|
|
901
|
+
ok: false,
|
|
902
|
+
errors: errors,
|
|
903
|
+
warnings: warnings
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
var opts = _objectSpread2({
|
|
907
|
+
fixTJunctions: true
|
|
908
|
+
}, options);
|
|
909
|
+
|
|
910
|
+
// Optionally canonicalize and fix T-junctions so that boolean-op
|
|
911
|
+
// output can pass the watertight check.
|
|
912
|
+
if (opts.fixTJunctions && typeof csg.canonicalized === 'function') {
|
|
913
|
+
csg = csg.canonicalized();
|
|
914
|
+
if (typeof csg.reTesselated === 'function') {
|
|
915
|
+
csg = csg.reTesselated();
|
|
916
|
+
}
|
|
917
|
+
if (typeof csg.fixTJunctions === 'function') {
|
|
918
|
+
csg = csg.fixTJunctions();
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
var AREA_EPS = 1e-10;
|
|
922
|
+
var KEY_EPS = 1e-5;
|
|
923
|
+
var degenerateCount = 0;
|
|
924
|
+
var invalidVertexCount = 0;
|
|
925
|
+
|
|
926
|
+
// Check for NaN/Infinity vertex coordinates which cause WebGL errors
|
|
927
|
+
// (GL_INVALID_VALUE: glVertexAttribPointer: Vertex attribute size must be 1, 2, 3, or 4)
|
|
928
|
+
var _iterator = _createForOfIteratorHelper(csg.polygons),
|
|
929
|
+
_step;
|
|
930
|
+
try {
|
|
931
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
932
|
+
var npoly = _step.value;
|
|
933
|
+
var _iterator4 = _createForOfIteratorHelper(npoly.vertices),
|
|
934
|
+
_step4;
|
|
935
|
+
try {
|
|
936
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
937
|
+
var nvert = _step4.value;
|
|
938
|
+
var np = nvert.pos;
|
|
939
|
+
if (!Number.isFinite(np.x) || !Number.isFinite(np.y) || !Number.isFinite(np.z) || Number.isNaN(np.x) || Number.isNaN(np.y) || Number.isNaN(np.z)) {
|
|
940
|
+
invalidVertexCount++;
|
|
941
|
+
break;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
} catch (err) {
|
|
945
|
+
_iterator4.e(err);
|
|
946
|
+
} finally {
|
|
947
|
+
_iterator4.f();
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
} catch (err) {
|
|
951
|
+
_iterator.e(err);
|
|
952
|
+
} finally {
|
|
953
|
+
_iterator.f();
|
|
954
|
+
}
|
|
955
|
+
if (invalidVertexCount > 0) {
|
|
956
|
+
errors.push(invalidVertexCount + ' polygon(s) with invalid vertex coordinates (NaN or Infinity)');
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// Position-based vertex key (shared vertices across polygons have different
|
|
960
|
+
// object tags but the same position, so we round coordinates to match them).
|
|
961
|
+
/** @param {{ pos: { x: number, y: number, z: number } }} v */
|
|
962
|
+
function vtxKey(v) {
|
|
963
|
+
var p = v.pos;
|
|
964
|
+
return Math.round(p.x / KEY_EPS) + ',' + Math.round(p.y / KEY_EPS) + ',' + Math.round(p.z / KEY_EPS);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// First pass: identify degenerate polygons
|
|
968
|
+
var validPolygons = [];
|
|
969
|
+
var _iterator2 = _createForOfIteratorHelper(csg.polygons),
|
|
970
|
+
_step2;
|
|
971
|
+
try {
|
|
972
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
973
|
+
var poly = _step2.value;
|
|
974
|
+
var verts = poly.vertices;
|
|
975
|
+
var nv = verts.length;
|
|
976
|
+
if (nv < 3) {
|
|
977
|
+
degenerateCount++;
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// Skip polygons with invalid vertex coordinates
|
|
982
|
+
var hasInvalid = false;
|
|
983
|
+
var _iterator5 = _createForOfIteratorHelper(verts),
|
|
984
|
+
_step5;
|
|
985
|
+
try {
|
|
986
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
987
|
+
var vert = _step5.value;
|
|
988
|
+
var ip = vert.pos;
|
|
989
|
+
if (!Number.isFinite(ip.x) || !Number.isFinite(ip.y) || !Number.isFinite(ip.z)) {
|
|
990
|
+
hasInvalid = true;
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
} catch (err) {
|
|
995
|
+
_iterator5.e(err);
|
|
996
|
+
} finally {
|
|
997
|
+
_iterator5.f();
|
|
998
|
+
}
|
|
999
|
+
if (hasInvalid) continue;
|
|
1000
|
+
|
|
1001
|
+
// Check degenerate area using cross-product summation
|
|
1002
|
+
var area = 0;
|
|
1003
|
+
for (var ai = 0; ai < nv - 2; ai++) {
|
|
1004
|
+
area += verts[ai + 1].pos.minus(verts[0].pos).cross(verts[ai + 2].pos.minus(verts[ai + 1].pos)).length();
|
|
1005
|
+
}
|
|
1006
|
+
area *= 0.5;
|
|
1007
|
+
if (area < AREA_EPS) {
|
|
1008
|
+
degenerateCount++;
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
validPolygons.push(poly);
|
|
1012
|
+
}
|
|
1013
|
+
} catch (err) {
|
|
1014
|
+
_iterator2.e(err);
|
|
1015
|
+
} finally {
|
|
1016
|
+
_iterator2.f();
|
|
1017
|
+
}
|
|
1018
|
+
if (degenerateCount > 0) {
|
|
1019
|
+
warnings.push(degenerateCount + ' degenerate polygon(s) (fewer than 3 vertices or near-zero area)');
|
|
1020
|
+
|
|
1021
|
+
// Rebuild the CSG from valid polygons only and re-run the repair
|
|
1022
|
+
// pipeline so that fixTJunctions can close gaps left by the removed
|
|
1023
|
+
// degenerate faces.
|
|
1024
|
+
/* eslint-disable no-undef */
|
|
1025
|
+
// @ts-ignore — CSG is a runtime global injected by the JSCAD compat layer
|
|
1026
|
+
if (opts.fixTJunctions && typeof CSG !== 'undefined') {
|
|
1027
|
+
// @ts-ignore
|
|
1028
|
+
var cleaned = CSG.fromPolygons(validPolygons);
|
|
1029
|
+
/* eslint-enable no-undef */
|
|
1030
|
+
cleaned = cleaned.canonicalized();
|
|
1031
|
+
if (typeof cleaned.reTesselated === 'function') {
|
|
1032
|
+
cleaned = cleaned.reTesselated();
|
|
1033
|
+
}
|
|
1034
|
+
if (typeof cleaned.fixTJunctions === 'function') {
|
|
1035
|
+
cleaned = cleaned.fixTJunctions();
|
|
1036
|
+
}
|
|
1037
|
+
// Re-scan for valid polygons after second repair pass
|
|
1038
|
+
validPolygons = [];
|
|
1039
|
+
var _iterator3 = _createForOfIteratorHelper(cleaned.polygons),
|
|
1040
|
+
_step3;
|
|
1041
|
+
try {
|
|
1042
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
1043
|
+
var cpoly = _step3.value;
|
|
1044
|
+
var cverts = cpoly.vertices;
|
|
1045
|
+
var cnv = cverts.length;
|
|
1046
|
+
if (cnv < 3) continue;
|
|
1047
|
+
var carea = 0;
|
|
1048
|
+
for (var cai = 0; cai < cnv - 2; cai++) {
|
|
1049
|
+
carea += cverts[cai + 1].pos.minus(cverts[0].pos).cross(cverts[cai + 2].pos.minus(cverts[cai + 1].pos)).length();
|
|
1050
|
+
}
|
|
1051
|
+
carea *= 0.5;
|
|
1052
|
+
if (carea < AREA_EPS) continue;
|
|
1053
|
+
validPolygons.push(cpoly);
|
|
1054
|
+
}
|
|
1055
|
+
} catch (err) {
|
|
1056
|
+
_iterator3.e(err);
|
|
1057
|
+
} finally {
|
|
1058
|
+
_iterator3.f();
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// Edge map: key = "vtxKeyA/vtxKeyB", value = count
|
|
1064
|
+
/** @type {Record<string, number>} */
|
|
1065
|
+
var edgeCounts = {};
|
|
1066
|
+
|
|
1067
|
+
// Accumulate directed edges from valid polygons only
|
|
1068
|
+
for (var _i = 0, _validPolygons = validPolygons; _i < _validPolygons.length; _i++) {
|
|
1069
|
+
var vpoly = _validPolygons[_i];
|
|
1070
|
+
var vverts = vpoly.vertices;
|
|
1071
|
+
var vnv = vverts.length;
|
|
1072
|
+
for (var ei = 0; ei < vnv; ei++) {
|
|
1073
|
+
var v0 = vverts[ei];
|
|
1074
|
+
var v1 = vverts[(ei + 1) % vnv];
|
|
1075
|
+
var edgeKey = vtxKey(v0) + '/' + vtxKey(v1);
|
|
1076
|
+
edgeCounts[edgeKey] = (edgeCounts[edgeKey] || 0) + 1;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Check edge manifoldness: every edge A→B should be cancelled by B→A
|
|
1081
|
+
var unmatchedEdges = 0;
|
|
1082
|
+
var nonManifoldEdges = 0;
|
|
1083
|
+
/** @type {Record<string, boolean>} */
|
|
1084
|
+
var checked = {};
|
|
1085
|
+
for (var _i2 = 0, _Object$keys = Object.keys(edgeCounts); _i2 < _Object$keys.length; _i2++) {
|
|
1086
|
+
var _edgeKey = _Object$keys[_i2];
|
|
1087
|
+
if (checked[_edgeKey]) continue;
|
|
1088
|
+
var parts = _edgeKey.split('/');
|
|
1089
|
+
var reverseKey = parts[1] + '/' + parts[0];
|
|
1090
|
+
var forwardCount = edgeCounts[_edgeKey] || 0;
|
|
1091
|
+
var reverseCount = edgeCounts[reverseKey] || 0;
|
|
1092
|
+
checked[_edgeKey] = true;
|
|
1093
|
+
checked[reverseKey] = true;
|
|
1094
|
+
if (forwardCount !== reverseCount) {
|
|
1095
|
+
unmatchedEdges += Math.abs(forwardCount - reverseCount);
|
|
1096
|
+
}
|
|
1097
|
+
if (forwardCount > 1 || reverseCount > 1) {
|
|
1098
|
+
nonManifoldEdges++;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
if (unmatchedEdges > 0) {
|
|
1102
|
+
errors.push(unmatchedEdges + ' unmatched edge(s): mesh is not watertight');
|
|
1103
|
+
}
|
|
1104
|
+
if (nonManifoldEdges > 0) {
|
|
1105
|
+
errors.push(nonManifoldEdges + ' non-manifold edge(s): edge shared by more than 2 polygons');
|
|
1106
|
+
}
|
|
1107
|
+
return {
|
|
1108
|
+
ok: errors.length === 0,
|
|
1109
|
+
errors: errors,
|
|
1110
|
+
warnings: warnings
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/** @param {any} csg @returns {any} */
|
|
1115
|
+
function _noOp(csg) {
|
|
1116
|
+
return csg;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/**
|
|
1120
|
+
* @param {boolean} warnEnabled
|
|
1121
|
+
* @returns {function(*, string=, string=): *}
|
|
1122
|
+
*/
|
|
1123
|
+
function _makeAssertFn(warnEnabled) {
|
|
1124
|
+
return function _assert(csg) {
|
|
1125
|
+
var functionName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'unknown';
|
|
1126
|
+
var moduleName = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'unknown';
|
|
1127
|
+
// Only validate CSG-like objects (they have a polygons array).
|
|
1128
|
+
// CAG objects (2D cross-sections) have `sides` instead and are passed through.
|
|
1129
|
+
if (!csg || csg.polygons === undefined) return csg;
|
|
1130
|
+
var result = validateCSG(csg);
|
|
1131
|
+
if (!result.ok) {
|
|
1132
|
+
throw new Error(moduleName + ':' + functionName + ': ' + 'invalid CSG: ' + result.errors.join(', '));
|
|
1133
|
+
}
|
|
1134
|
+
if (warnEnabled && result.warnings.length > 0) {
|
|
1135
|
+
throw new Error(moduleName + ':' + functionName + ': ' + 'CSG warnings: ' + result.warnings.join(', '));
|
|
1136
|
+
}
|
|
1137
|
+
return csg;
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// Live pointer that all returned closures call through — swap this and all
|
|
1142
|
+
// existing closures immediately pick up the change.
|
|
1143
|
+
/** @type {function(*, string=, string=): *} */
|
|
1144
|
+
var _assertFn = _noOp;
|
|
1145
|
+
|
|
1146
|
+
// Read compat globals set by initJscadutils — mirrors the Debug() pattern
|
|
1147
|
+
// in debug.js. Returns _noOp when globals are absent (ESM / test context).
|
|
1148
|
+
function _resolveFromGlobals() {
|
|
1149
|
+
/* eslint-disable no-undef */
|
|
1150
|
+
// @ts-ignore — globals set by the JSCAD compat layer before bundle injection
|
|
1151
|
+
var enabled = typeof jscadUtilsAssertValidCSG !== 'undefined' && !!jscadUtilsAssertValidCSG;
|
|
1152
|
+
// @ts-ignore
|
|
1153
|
+
var warnEnabled = typeof jscadUtilsAssertValidCSGWarnings !== 'undefined' && !!jscadUtilsAssertValidCSGWarnings;
|
|
1154
|
+
/* eslint-enable no-undef */
|
|
1155
|
+
return enabled ? _makeAssertFn(warnEnabled) : _noOp;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Returns an asserter function bound to `moduleName`. Call the returned
|
|
1160
|
+
* function with a CSG object and the calling function's name to validate it
|
|
1161
|
+
* (when enabled) or pass it through unchanged (when disabled).
|
|
1162
|
+
*
|
|
1163
|
+
* Best practice is to call `AssertValidCSG` once per module at load time and
|
|
1164
|
+
* capture the result as a module-level constant so that `moduleName` appears
|
|
1165
|
+
* consistently in every error message thrown from that module.
|
|
1166
|
+
*
|
|
1167
|
+
* On creation, reads compat globals (jscadUtilsAssertValidCSG /
|
|
1168
|
+
* jscadUtilsAssertValidCSGWarnings) if setValidationEnabled() has not been
|
|
1169
|
+
* called explicitly — identical to how Debug('name') reads jscadUtilsDebug.
|
|
1170
|
+
*
|
|
1171
|
+
* Error message format: `moduleName:functionName: invalid CSG: <errors>`
|
|
1172
|
+
*
|
|
1173
|
+
* @example
|
|
1174
|
+
* // Once at the top of your module:
|
|
1175
|
+
* const assertValidCSG = AssertValidCSG('myModule');
|
|
1176
|
+
*
|
|
1177
|
+
* export function enlarge(object, ...) {
|
|
1178
|
+
* // ...
|
|
1179
|
+
* return assertValidCSG(new_object.translate(delta), 'enlarge');
|
|
1180
|
+
* }
|
|
1181
|
+
*
|
|
1182
|
+
* @param {string} [moduleName='unknown'] Module name, included in error messages.
|
|
1183
|
+
* @return {function(CSG, string=): CSG}
|
|
1184
|
+
*/
|
|
1185
|
+
function AssertValidCSG() {
|
|
1186
|
+
var moduleName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'unknown';
|
|
1187
|
+
{
|
|
1188
|
+
_assertFn = _resolveFromGlobals();
|
|
1189
|
+
}
|
|
1190
|
+
return function (csg, name) {
|
|
1191
|
+
return _assertFn(csg, name, moduleName);
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
|
|
806
1195
|
/** @typedef {object} ExtendedCSG
|
|
807
1196
|
* @property {object} prototype
|
|
808
1197
|
* @property {function} prototype.color
|
|
@@ -875,7 +1264,6 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
875
1264
|
* @property {function} stackTrace
|
|
876
1265
|
* @property {function} getConnector
|
|
877
1266
|
*/
|
|
878
|
-
|
|
879
1267
|
/**
|
|
880
1268
|
* Initialize `jscad-utils` and add utilities to the `proto` object.
|
|
881
1269
|
* @param {CSG} proto The global `proto` object
|
|
@@ -985,6 +1373,9 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
985
1373
|
proto.prototype.subtractIf = function subtractIf(object, condition) {
|
|
986
1374
|
return condition ? this.subtract(result(this, object)) : this;
|
|
987
1375
|
};
|
|
1376
|
+
proto.prototype.validate = function validate(options) {
|
|
1377
|
+
return validateCSG(this, options);
|
|
1378
|
+
};
|
|
988
1379
|
proto.prototype._translate = proto.prototype.translate;
|
|
989
1380
|
|
|
990
1381
|
/**
|
|
@@ -1036,16 +1427,17 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1036
1427
|
'default': init
|
|
1037
1428
|
});
|
|
1038
1429
|
|
|
1039
|
-
var CSG = jsCadCSG__default["default"].CSG,
|
|
1430
|
+
var CSG$1 = jsCadCSG__default["default"].CSG,
|
|
1040
1431
|
CAG = jsCadCSG__default["default"].CAG;
|
|
1041
1432
|
var rectangular_extrude = scadApi__default["default"].extrusions.rectangular_extrude;
|
|
1042
1433
|
var _scadApi$text = scadApi__default["default"].text,
|
|
1043
1434
|
vector_text = _scadApi$text.vector_text,
|
|
1044
1435
|
vector_char = _scadApi$text.vector_char;
|
|
1045
1436
|
var union = scadApi__default["default"].booleanOps.union;
|
|
1046
|
-
init(CSG);
|
|
1437
|
+
init(CSG$1);
|
|
1047
1438
|
|
|
1048
1439
|
var debug$3 = Debug('jscadUtils:group');
|
|
1440
|
+
var assertValidCSG$2 = AssertValidCSG('group');
|
|
1049
1441
|
|
|
1050
1442
|
/**
|
|
1051
1443
|
* @function JsCadUtilsGroup
|
|
@@ -1124,7 +1516,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1124
1516
|
debug$3('combine mapPick', value, key, object);
|
|
1125
1517
|
return map ? map(value, key, index, object) : identity(value);
|
|
1126
1518
|
}, self.name));
|
|
1127
|
-
return g.subtractIf(self.holes && Array.isArray(self.holes) ? union(self.holes) : self.holes, self.holes && !options.noholes);
|
|
1519
|
+
return assertValidCSG$2(g.subtractIf(self.holes && Array.isArray(self.holes) ? union(self.holes) : self.holes, self.holes && !options.noholes), 'combine');
|
|
1128
1520
|
} catch (err) {
|
|
1129
1521
|
debug$3('combine error', this, pieces, options, err);
|
|
1130
1522
|
throw error("group::combine error \"".concat(err.message || err.toString(), "\"\nthis: ").concat(this, "\npieces: \"").concat(pieces, "\"\noptions: ").concat(JSON.stringify(options, null, 2), "\nstack: ").concat(err.stack, "\n"), 'JSCAD_UTILS_GROUP_ERROR');
|
|
@@ -1186,7 +1578,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1186
1578
|
});
|
|
1187
1579
|
if (self.holes) {
|
|
1188
1580
|
group.holes = toArray(self.holes).map(function (part) {
|
|
1189
|
-
return map(CSG.fromPolygons(part.toPolygons()), 'holes');
|
|
1581
|
+
return assertValidCSG$2(map(CSG$1.fromPolygons(part.toPolygons()), 'holes'), 'clone');
|
|
1190
1582
|
});
|
|
1191
1583
|
}
|
|
1192
1584
|
return group;
|
|
@@ -1214,7 +1606,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1214
1606
|
var rotationCenter = solid.centroid();
|
|
1215
1607
|
var rotationAxis = axes[axis];
|
|
1216
1608
|
self.map(function (part) {
|
|
1217
|
-
return part.rotate(rotationCenter, rotationAxis, angle);
|
|
1609
|
+
return assertValidCSG$2(part.rotate(rotationCenter, rotationAxis, angle), 'rotate');
|
|
1218
1610
|
});
|
|
1219
1611
|
return self;
|
|
1220
1612
|
};
|
|
@@ -1248,7 +1640,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1248
1640
|
// debug(', self);
|
|
1249
1641
|
var t = calcSnap(self.combine(part), to, axis, orientation, delta);
|
|
1250
1642
|
self.map(function (part) {
|
|
1251
|
-
return part.translate(t);
|
|
1643
|
+
return assertValidCSG$2(part.translate(t), 'snap');
|
|
1252
1644
|
});
|
|
1253
1645
|
return self;
|
|
1254
1646
|
} catch (err) {
|
|
@@ -1274,7 +1666,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1274
1666
|
noholes: true
|
|
1275
1667
|
}), axis, to, delta);
|
|
1276
1668
|
self.map(function (part /*, name */) {
|
|
1277
|
-
return part.translate(t);
|
|
1669
|
+
return assertValidCSG$2(part.translate(t), 'align');
|
|
1278
1670
|
});
|
|
1279
1671
|
|
|
1280
1672
|
// if (self.holes)
|
|
@@ -1312,14 +1704,14 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1312
1704
|
var myConnector = connectorName.split('.').reduce(function (a, v) {
|
|
1313
1705
|
return a[v];
|
|
1314
1706
|
}, self.parts[partName].properties);
|
|
1315
|
-
debug$3('toConnector', to instanceof CSG.Connector);
|
|
1707
|
+
debug$3('toConnector', to instanceof CSG$1.Connector);
|
|
1316
1708
|
var toConnector = toConnectorName.split('.').reduce(function (a, v) {
|
|
1317
1709
|
return a[v];
|
|
1318
1710
|
}, to.properties);
|
|
1319
1711
|
var matrix = myConnector.getTransformationTo(toConnector, mirror, normalrotation);
|
|
1320
1712
|
debug$3('connectTo', matrix);
|
|
1321
1713
|
self.map(function (part) {
|
|
1322
|
-
return part.transform(matrix);
|
|
1714
|
+
return assertValidCSG$2(part.transform(matrix), 'connectTo');
|
|
1323
1715
|
});
|
|
1324
1716
|
return self;
|
|
1325
1717
|
};
|
|
@@ -1340,7 +1732,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1340
1732
|
// debug(' part, t);
|
|
1341
1733
|
// var t = util.calcCenterWith(self.combine(part), axis, to, delta);
|
|
1342
1734
|
self.map(function (part) {
|
|
1343
|
-
return part.translate(t);
|
|
1735
|
+
return assertValidCSG$2(part.translate(t), 'midlineTo');
|
|
1344
1736
|
});
|
|
1345
1737
|
|
|
1346
1738
|
// if (self.holes)
|
|
@@ -1364,7 +1756,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1364
1756
|
var t = Array.isArray(x) ? x : [x, y, z];
|
|
1365
1757
|
debug$3('translate', t);
|
|
1366
1758
|
self.map(function (part) {
|
|
1367
|
-
return part.translate(t);
|
|
1759
|
+
return assertValidCSG$2(part.translate(t), 'translate');
|
|
1368
1760
|
});
|
|
1369
1761
|
|
|
1370
1762
|
// if (self.holes)
|
|
@@ -1388,7 +1780,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1388
1780
|
if (!map) map = identity;
|
|
1389
1781
|
var g = Group();
|
|
1390
1782
|
p.forEach(function (name) {
|
|
1391
|
-
g.add(map(CSG.fromPolygons(self.parts[name].toPolygons()), name), name);
|
|
1783
|
+
g.add(assertValidCSG$2(map(CSG$1.fromPolygons(self.parts[name].toPolygons()), name), 'pick'), name);
|
|
1392
1784
|
});
|
|
1393
1785
|
return g;
|
|
1394
1786
|
};
|
|
@@ -1412,7 +1804,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1412
1804
|
debug$3('array error', _this, parts);
|
|
1413
1805
|
throw error("group::array error \"".concat(name, "\" not found.\nthis: ").concat(_this, "\nparts: \"").concat(parts, "\"\n"), 'JSCAD_UTILS_GROUP_ERROR');
|
|
1414
1806
|
}
|
|
1415
|
-
a.push(map(CSG.fromPolygons(self.parts[name].toPolygons()), name));
|
|
1807
|
+
a.push(assertValidCSG$2(map(CSG$1.fromPolygons(self.parts[name].toPolygons()), name), 'array'));
|
|
1416
1808
|
});
|
|
1417
1809
|
return a;
|
|
1418
1810
|
// } catch (err) {
|
|
@@ -1481,7 +1873,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1481
1873
|
self.names = names && names.length > 0 && names.split(',') || [];
|
|
1482
1874
|
if (Array.isArray(objects)) {
|
|
1483
1875
|
self.parts = zipObject(self.names, objects);
|
|
1484
|
-
} else if (objects instanceof CSG) {
|
|
1876
|
+
} else if (objects instanceof CSG$1) {
|
|
1485
1877
|
self.parts = zipObject(self.names, [objects]);
|
|
1486
1878
|
} else {
|
|
1487
1879
|
self.parts = objects || {};
|
|
@@ -1506,6 +1898,8 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1506
1898
|
}
|
|
1507
1899
|
|
|
1508
1900
|
var debug$2 = Debug('jscadUtils:util');
|
|
1901
|
+
var assertValidCSG$1 = AssertValidCSG('util');
|
|
1902
|
+
|
|
1509
1903
|
// import utilInit from '../src/add-prototype';
|
|
1510
1904
|
// utilInit(CSG);
|
|
1511
1905
|
// console.trace('CSG', CSG.prototype);
|
|
@@ -1669,12 +2063,12 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1669
2063
|
// console.trace('label', Object.getPrototypeOf(union(o)));
|
|
1670
2064
|
// var foo = union(o);
|
|
1671
2065
|
// console.trace('typeof', typeof foo);
|
|
1672
|
-
return center(union(o));
|
|
2066
|
+
return assertValidCSG$1(center(union(o)), 'label');
|
|
1673
2067
|
}
|
|
1674
2068
|
function text(text) {
|
|
1675
2069
|
var l = vector_char(0, 0, text); // l contains a list of polylines to draw
|
|
1676
2070
|
var _char = l.segments.reduce(function (result, segment) {
|
|
1677
|
-
var path = new CSG.Path2D(segment);
|
|
2071
|
+
var path = new CSG$1.Path2D(segment);
|
|
1678
2072
|
var cag = path.expandToCAG(2);
|
|
1679
2073
|
// debug('reduce', result, segment, path, cag);
|
|
1680
2074
|
return result ? result.union(cag) : cag;
|
|
@@ -1683,17 +2077,17 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1683
2077
|
}
|
|
1684
2078
|
function unitCube(length, radius) {
|
|
1685
2079
|
radius = radius || 0.5;
|
|
1686
|
-
return CSG.cube({
|
|
2080
|
+
return assertValidCSG$1(CSG$1.cube({
|
|
1687
2081
|
center: [0, 0, 0],
|
|
1688
2082
|
radius: [radius, radius, length || 0.5]
|
|
1689
|
-
});
|
|
2083
|
+
}), 'unitCube');
|
|
1690
2084
|
}
|
|
1691
2085
|
function unitAxis(length, radius, centroid) {
|
|
1692
2086
|
debug$2('unitAxis', length, radius, centroid);
|
|
1693
2087
|
centroid = centroid || [0, 0, 0];
|
|
1694
2088
|
var unitaxis = unitCube(length, radius).setColor(1, 0, 0).union([unitCube(length, radius).rotateY(90).setColor(0, 1, 0), unitCube(length, radius).rotateX(90).setColor(0, 0, 1)]);
|
|
1695
|
-
unitaxis.properties.origin = new CSG.Connector([0, 0, 0], [1, 0, 0], [0, 1, 0]);
|
|
1696
|
-
return unitaxis.translate(centroid);
|
|
2089
|
+
unitaxis.properties.origin = new CSG$1.Connector([0, 0, 0], [1, 0, 0], [0, 1, 0]);
|
|
2090
|
+
return assertValidCSG$1(unitaxis.translate(centroid), 'unitAxis');
|
|
1697
2091
|
}
|
|
1698
2092
|
function toArray(a) {
|
|
1699
2093
|
return Array.isArray(a) ? a : [a];
|
|
@@ -1817,15 +2211,15 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1817
2211
|
}
|
|
1818
2212
|
function center(object, objectSize) {
|
|
1819
2213
|
objectSize = objectSize || size(object.getBounds());
|
|
1820
|
-
return centerY(centerX(object, objectSize), objectSize);
|
|
2214
|
+
return assertValidCSG$1(centerY(centerX(object, objectSize), objectSize), 'center');
|
|
1821
2215
|
}
|
|
1822
2216
|
function centerY(object, objectSize) {
|
|
1823
2217
|
objectSize = objectSize || size(object.getBounds());
|
|
1824
|
-
return object.translate([0, -objectSize.y / 2, 0]);
|
|
2218
|
+
return assertValidCSG$1(object.translate([0, -objectSize.y / 2, 0]), 'centerY');
|
|
1825
2219
|
}
|
|
1826
2220
|
function centerX(object, objectSize) {
|
|
1827
2221
|
objectSize = objectSize || size(object.getBounds());
|
|
1828
|
-
return object.translate([-objectSize.x / 2, 0, 0]);
|
|
2222
|
+
return assertValidCSG$1(object.translate([-objectSize.x / 2, 0, 0]), 'centerX');
|
|
1829
2223
|
}
|
|
1830
2224
|
|
|
1831
2225
|
/**
|
|
@@ -1857,7 +2251,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1857
2251
|
|
|
1858
2252
|
/// Calculate the difference between the original centroid and the new
|
|
1859
2253
|
var delta = new_centroid.minus(objectCentroid).times(-1);
|
|
1860
|
-
return new_object.translate(delta);
|
|
2254
|
+
return assertValidCSG$1(new_object.translate(delta), 'enlarge');
|
|
1861
2255
|
}
|
|
1862
2256
|
|
|
1863
2257
|
/**
|
|
@@ -1889,10 +2283,10 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1889
2283
|
}
|
|
1890
2284
|
var s = [scale(objectSize.x, x), scale(objectSize.y, y), scale(objectSize.z, z)];
|
|
1891
2285
|
var min$1 = min(s);
|
|
1892
|
-
return centerWith(object.scale(s.map(function (d, i) {
|
|
2286
|
+
return assertValidCSG$1(centerWith(object.scale(s.map(function (d, i) {
|
|
1893
2287
|
if (a[i] === 0) return 1; // don't scale when value is zero
|
|
1894
2288
|
return keep_aspect_ratio ? min$1 : d;
|
|
1895
|
-
})), 'xyz', object);
|
|
2289
|
+
})), 'xyz', object), 'fit');
|
|
1896
2290
|
}
|
|
1897
2291
|
function shift(object, x, y, z) {
|
|
1898
2292
|
var hsize = this.div(this.size(object.getBounds()), 2);
|
|
@@ -1900,10 +2294,10 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1900
2294
|
}
|
|
1901
2295
|
function zero(object) {
|
|
1902
2296
|
var bounds = object.getBounds();
|
|
1903
|
-
return object.translate([0, 0, -bounds[0].z]);
|
|
2297
|
+
return assertValidCSG$1(object.translate([0, 0, -bounds[0].z]), 'zero');
|
|
1904
2298
|
}
|
|
1905
2299
|
function mirrored4(x) {
|
|
1906
|
-
return x.union([x.mirroredY(90), x.mirroredX(90), x.mirroredY(90).mirroredX(90)]);
|
|
2300
|
+
return assertValidCSG$1(x.union([x.mirroredY(90), x.mirroredX(90), x.mirroredY(90).mirroredX(90)]), 'mirrored4');
|
|
1907
2301
|
}
|
|
1908
2302
|
var flushSide = {
|
|
1909
2303
|
'above-outside': [1, 0],
|
|
@@ -1969,7 +2363,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1969
2363
|
function snap(moveobj, withobj, axis, orientation, delta) {
|
|
1970
2364
|
debug$2('snap', moveobj, withobj, axis, orientation, delta);
|
|
1971
2365
|
var t = calcSnap(moveobj, withobj, axis, orientation, delta);
|
|
1972
|
-
return moveobj.translate(t);
|
|
2366
|
+
return assertValidCSG$1(moveobj.translate(t), 'snap');
|
|
1973
2367
|
}
|
|
1974
2368
|
|
|
1975
2369
|
/**
|
|
@@ -1982,23 +2376,35 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
1982
2376
|
* @return {CSG} [description]
|
|
1983
2377
|
*/
|
|
1984
2378
|
function flush(moveobj, withobj, axis, mside, wside) {
|
|
1985
|
-
return moveobj.translate(calcFlush(moveobj, withobj, axis, mside, wside));
|
|
2379
|
+
return assertValidCSG$1(moveobj.translate(calcFlush(moveobj, withobj, axis, mside, wside)), 'flush');
|
|
1986
2380
|
}
|
|
2381
|
+
|
|
2382
|
+
/**
|
|
2383
|
+
*
|
|
2384
|
+
* @param {AxisStrings} axes
|
|
2385
|
+
* @param {function(number, string): number} valfun
|
|
2386
|
+
* @param {Array<number>} [a] In initial array to apply the values to, if not provided a new array will be created.
|
|
2387
|
+
* @returns {Array<number>} The resulting array after applying the function to the specified axes.
|
|
2388
|
+
*/
|
|
1987
2389
|
function axisApply(axes, valfun, a) {
|
|
1988
2390
|
debug$2('axisApply', axes, valfun, a);
|
|
2391
|
+
|
|
2392
|
+
/** @type {Array<number>} */
|
|
1989
2393
|
var retval = a || [0, 0, 0];
|
|
2394
|
+
|
|
2395
|
+
/** @type {Record<AxisString, number>} */
|
|
1990
2396
|
var lookup = {
|
|
1991
2397
|
x: 0,
|
|
1992
2398
|
y: 1,
|
|
1993
2399
|
z: 2
|
|
1994
2400
|
};
|
|
1995
2401
|
axes.split('').forEach(function (axis) {
|
|
1996
|
-
retval[lookup[axis]] = valfun(lookup[axis], axis);
|
|
2402
|
+
retval[lookup[(/** @type {AxisString} */axis)]] = valfun(lookup[(/** @type {AxisString} */axis)], axis);
|
|
1997
2403
|
});
|
|
1998
2404
|
return retval;
|
|
1999
2405
|
}
|
|
2000
2406
|
function axis2array(axes, valfun) {
|
|
2001
|
-
depreciated('axis2array');
|
|
2407
|
+
depreciated('axis2array', false, 'Use axisApply instead.');
|
|
2002
2408
|
var a = [0, 0, 0];
|
|
2003
2409
|
var lookup = {
|
|
2004
2410
|
x: 0,
|
|
@@ -2039,7 +2445,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2039
2445
|
});
|
|
2040
2446
|
}
|
|
2041
2447
|
function midlineTo(o, axis, to) {
|
|
2042
|
-
return o.translate(calcmidlineTo(o, axis, to));
|
|
2448
|
+
return assertValidCSG$1(o.translate(calcmidlineTo(o, axis, to)), 'midlineTo');
|
|
2043
2449
|
}
|
|
2044
2450
|
function translator(o, axis, withObj) {
|
|
2045
2451
|
var objectCentroid = centroid(o);
|
|
@@ -2060,7 +2466,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2060
2466
|
return delta ? add(t, delta) : t;
|
|
2061
2467
|
}
|
|
2062
2468
|
function centerWith(o, axis, withObj) {
|
|
2063
|
-
return o.translate(calcCenterWith(o, axis, withObj));
|
|
2469
|
+
return assertValidCSG$1(o.translate(calcCenterWith(o, axis, withObj)), 'centerWith');
|
|
2064
2470
|
}
|
|
2065
2471
|
|
|
2066
2472
|
/**
|
|
@@ -2090,6 +2496,109 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2090
2496
|
return bounds[0][a] + (isEmpty(dist) ? size[axis] / 2 : dist);
|
|
2091
2497
|
});
|
|
2092
2498
|
}
|
|
2499
|
+
var EPS = 1e-5;
|
|
2500
|
+
|
|
2501
|
+
/**
|
|
2502
|
+
* Split a CSG object into two halves along a plane by directly
|
|
2503
|
+
* splitting polygons. This avoids BSP-tree-based boolean operations
|
|
2504
|
+
* which can fail on geometry produced by stretchAtPlane.
|
|
2505
|
+
* @param {CSG} csg The object to split
|
|
2506
|
+
* @param {CSG.Plane} plane The splitting plane
|
|
2507
|
+
* @return {{ front: CSG, back: CSG }} front (positive normal side) and back (negative normal side)
|
|
2508
|
+
*/
|
|
2509
|
+
function splitCSGByPlane(csg, plane) {
|
|
2510
|
+
var frontPolys = [];
|
|
2511
|
+
var backPolys = [];
|
|
2512
|
+
csg.polygons.forEach(function (poly) {
|
|
2513
|
+
var vertices = poly.vertices;
|
|
2514
|
+
var numVerts = vertices.length;
|
|
2515
|
+
var hasfront = false;
|
|
2516
|
+
var hasback = false;
|
|
2517
|
+
var vertexIsBack = [];
|
|
2518
|
+
for (var i = 0; i < numVerts; i++) {
|
|
2519
|
+
var t = plane.normal.dot(vertices[i].pos) - plane.w;
|
|
2520
|
+
vertexIsBack.push(t < 0);
|
|
2521
|
+
if (t > EPS) hasfront = true;
|
|
2522
|
+
if (t < -EPS) hasback = true;
|
|
2523
|
+
}
|
|
2524
|
+
if (!hasfront && !hasback) {
|
|
2525
|
+
// coplanar — assign based on normal alignment
|
|
2526
|
+
var d = plane.normal.dot(poly.plane.normal);
|
|
2527
|
+
if (d >= 0) {
|
|
2528
|
+
frontPolys.push(poly);
|
|
2529
|
+
} else {
|
|
2530
|
+
backPolys.push(poly);
|
|
2531
|
+
}
|
|
2532
|
+
} else if (!hasback) {
|
|
2533
|
+
frontPolys.push(poly);
|
|
2534
|
+
} else if (!hasfront) {
|
|
2535
|
+
backPolys.push(poly);
|
|
2536
|
+
} else {
|
|
2537
|
+
// spanning — split the polygon
|
|
2538
|
+
var fv = [];
|
|
2539
|
+
var bv = [];
|
|
2540
|
+
for (var vi = 0; vi < numVerts; vi++) {
|
|
2541
|
+
var vertex = vertices[vi];
|
|
2542
|
+
var nextVi = (vi + 1) % numVerts;
|
|
2543
|
+
var isback = vertexIsBack[vi];
|
|
2544
|
+
var nextisback = vertexIsBack[nextVi];
|
|
2545
|
+
if (isback === nextisback) {
|
|
2546
|
+
if (isback) {
|
|
2547
|
+
bv.push(vertex);
|
|
2548
|
+
} else {
|
|
2549
|
+
fv.push(vertex);
|
|
2550
|
+
}
|
|
2551
|
+
} else {
|
|
2552
|
+
var point = vertex.pos;
|
|
2553
|
+
var nextpoint = vertices[nextVi].pos;
|
|
2554
|
+
var ip = plane.splitLineBetweenPoints(point, nextpoint);
|
|
2555
|
+
var iv = new CSG$1.Vertex(ip);
|
|
2556
|
+
if (isback) {
|
|
2557
|
+
bv.push(vertex);
|
|
2558
|
+
bv.push(iv);
|
|
2559
|
+
fv.push(iv);
|
|
2560
|
+
} else {
|
|
2561
|
+
fv.push(vertex);
|
|
2562
|
+
fv.push(iv);
|
|
2563
|
+
bv.push(iv);
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
// Remove degenerate (near-duplicate) adjacent vertices that arise
|
|
2568
|
+
// when the cut plane passes very close to existing vertices.
|
|
2569
|
+
// This matches the cleanup done by the BSP-tree splitter.
|
|
2570
|
+
var EPSEPS = EPS * EPS;
|
|
2571
|
+
if (fv.length >= 3) {
|
|
2572
|
+
var prev = fv[fv.length - 1];
|
|
2573
|
+
for (var fi = 0; fi < fv.length; fi++) {
|
|
2574
|
+
var curr = fv[fi];
|
|
2575
|
+
if (curr.pos.distanceToSquared(prev.pos) < EPSEPS) {
|
|
2576
|
+
fv.splice(fi, 1);
|
|
2577
|
+
fi--;
|
|
2578
|
+
}
|
|
2579
|
+
prev = curr;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
if (bv.length >= 3) {
|
|
2583
|
+
var prev = bv[bv.length - 1];
|
|
2584
|
+
for (var bi = 0; bi < bv.length; bi++) {
|
|
2585
|
+
var curr = bv[bi];
|
|
2586
|
+
if (curr.pos.distanceToSquared(prev.pos) < EPSEPS) {
|
|
2587
|
+
bv.splice(bi, 1);
|
|
2588
|
+
bi--;
|
|
2589
|
+
}
|
|
2590
|
+
prev = curr;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
if (fv.length >= 3) frontPolys.push(new CSG$1.Polygon(fv, poly.shared, poly.plane));
|
|
2594
|
+
if (bv.length >= 3) backPolys.push(new CSG$1.Polygon(bv, poly.shared, poly.plane));
|
|
2595
|
+
}
|
|
2596
|
+
});
|
|
2597
|
+
return {
|
|
2598
|
+
front: CSG$1.fromPolygons(frontPolys),
|
|
2599
|
+
back: CSG$1.fromPolygons(backPolys)
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2093
2602
|
|
|
2094
2603
|
/**
|
|
2095
2604
|
* Cut an object into two pieces, along a given axis. The offset
|
|
@@ -2100,7 +2609,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2100
2609
|
*
|
|
2101
2610
|
* You can angle the cut plane and position the rotation point.
|
|
2102
2611
|
*
|
|
2103
|
-
* 
|
|
2104
2613
|
* @param {CSG} object object to bisect
|
|
2105
2614
|
* @param {string} axis axis to cut along
|
|
2106
2615
|
* @param {number} [offset] offset to cut at
|
|
@@ -2178,13 +2687,13 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2178
2687
|
}[[axis, rotateaxis].sort().join('')];
|
|
2179
2688
|
var centroid = object.centroid();
|
|
2180
2689
|
var rotateDelta = getDelta(objectSize, bounds, rotateOffsetAxis, rotateoffset);
|
|
2181
|
-
var rotationCenter = options.rotationCenter || new CSG.Vector3D(axisApply('xyz', function (i, a) {
|
|
2690
|
+
var rotationCenter = options.rotationCenter || new CSG$1.Vector3D(axisApply('xyz', function (i, a) {
|
|
2182
2691
|
if (a == axis) return cutDelta[i];
|
|
2183
2692
|
if (a == rotateOffsetAxis) return rotateDelta[i];
|
|
2184
2693
|
return centroid[a];
|
|
2185
2694
|
}));
|
|
2186
2695
|
var theRotationAxis = rotationAxes[rotateaxis];
|
|
2187
|
-
var cutplane = CSG.OrthoNormalBasis.GetCartesian(info.orthoNormalCartesian[0], info.orthoNormalCartesian[1]).translate(cutDelta).rotate(rotationCenter, theRotationAxis, angle);
|
|
2696
|
+
var cutplane = CSG$1.OrthoNormalBasis.GetCartesian(info.orthoNormalCartesian[0], info.orthoNormalCartesian[1]).translate(cutDelta).rotate(rotationCenter, theRotationAxis, angle);
|
|
2188
2697
|
debug$2('bisect', debug$2.enabled && {
|
|
2189
2698
|
axis: axis,
|
|
2190
2699
|
offset: offset,
|
|
@@ -2197,7 +2706,33 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2197
2706
|
cutplane: cutplane,
|
|
2198
2707
|
options: options
|
|
2199
2708
|
});
|
|
2200
|
-
var
|
|
2709
|
+
var negative = object.cutByPlane(cutplane.plane);
|
|
2710
|
+
var positive = object.cutByPlane(cutplane.plane.flipped());
|
|
2711
|
+
|
|
2712
|
+
// Detect cutByPlane failure: if a half's bounding box in the cut axis
|
|
2713
|
+
// is not smaller than the original, the BSP-tree-based cut failed.
|
|
2714
|
+
// Fall back to direct polygon splitting which is more robust, then
|
|
2715
|
+
// apply cutByPlane to the simpler half to add cap faces.
|
|
2716
|
+
var negSize = size(negative);
|
|
2717
|
+
var posSize = size(positive);
|
|
2718
|
+
if (negSize[axis] >= objectSize[axis] - EPS || posSize[axis] >= objectSize[axis] - EPS) {
|
|
2719
|
+
var halves = splitCSGByPlane(object, cutplane.plane);
|
|
2720
|
+
if (negSize[axis] >= objectSize[axis] - EPS) {
|
|
2721
|
+
negative = halves.back;
|
|
2722
|
+
// Cap the open cut face
|
|
2723
|
+
try {
|
|
2724
|
+
negative = negative.cutByPlane(cutplane.plane);
|
|
2725
|
+
} catch (e) {/* keep uncapped */}
|
|
2726
|
+
}
|
|
2727
|
+
if (posSize[axis] >= objectSize[axis] - EPS) {
|
|
2728
|
+
positive = halves.front;
|
|
2729
|
+
// Cap the open cut face
|
|
2730
|
+
try {
|
|
2731
|
+
positive = positive.cutByPlane(cutplane.plane.flipped());
|
|
2732
|
+
} catch (e) {/* keep uncapped */}
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
var g = Group('negative,positive', [negative.color(options.color && 'red'), positive.color(options.color && 'blue')]);
|
|
2201
2736
|
if (options.addRotationCenter) g.add(unitAxis(objectSize.length() + 10, 0.1, rotationCenter), 'rotationCenter');
|
|
2202
2737
|
return g;
|
|
2203
2738
|
}
|
|
@@ -2222,9 +2757,9 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2222
2757
|
addRotationCenter: true
|
|
2223
2758
|
};
|
|
2224
2759
|
var info = normalVector(axis);
|
|
2225
|
-
var rotationCenter = options.rotationCenter || new CSG.Vector3D(0, 0, 0);
|
|
2760
|
+
var rotationCenter = options.rotationCenter || new CSG$1.Vector3D(0, 0, 0);
|
|
2226
2761
|
var theRotationAxis = rotationAxes[rotateaxis];
|
|
2227
|
-
var cutplane = CSG.OrthoNormalBasis.GetCartesian(info.orthoNormalCartesian[0], info.orthoNormalCartesian[1])
|
|
2762
|
+
var cutplane = CSG$1.OrthoNormalBasis.GetCartesian(info.orthoNormalCartesian[0], info.orthoNormalCartesian[1])
|
|
2228
2763
|
// .translate(cutDelta)
|
|
2229
2764
|
.rotate(rotationCenter, theRotationAxis, angle);
|
|
2230
2765
|
var g = Group('negative,positive', [object.cutByPlane(cutplane.plane).color(options.color && 'red'), object.cutByPlane(cutplane.plane.flipped()).color(options.color && 'blue')]);
|
|
@@ -2239,7 +2774,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2239
2774
|
* Creates a `JsCadUtilsGroup` object that has `body` and `wedge` objects. The `wedge` object
|
|
2240
2775
|
* is created by radially cutting the object from the `start` to the `end` angle.
|
|
2241
2776
|
*
|
|
2242
|
-
* 
|
|
2243
2778
|
*
|
|
2244
2779
|
*
|
|
2245
2780
|
* @example
|
|
@@ -2295,7 +2830,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2295
2830
|
var objectSize = size(object);
|
|
2296
2831
|
var cutDelta = getDelta(objectSize, bounds, axis, offset, true);
|
|
2297
2832
|
// debug('stretch.cutDelta', cutDelta, normal[axis]);
|
|
2298
|
-
return object.stretchAtPlane(normal[axis], cutDelta, distance);
|
|
2833
|
+
return assertValidCSG$1(object.stretchAtPlane(normal[axis], cutDelta, distance), 'stretch');
|
|
2299
2834
|
}
|
|
2300
2835
|
|
|
2301
2836
|
/**
|
|
@@ -2310,11 +2845,11 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2310
2845
|
function poly2solid(top, bottom, height) {
|
|
2311
2846
|
if (top.sides.length == 0) {
|
|
2312
2847
|
// empty!
|
|
2313
|
-
return new CSG();
|
|
2848
|
+
return new CSG$1();
|
|
2314
2849
|
}
|
|
2315
2850
|
// var offsetVector = CSG.parseOptionAs3DVector(options, "offset", [0, 0, 10]);
|
|
2316
|
-
var offsetVector = CSG.Vector3D.Create(0, 0, height);
|
|
2317
|
-
var normalVector = CSG.Vector3D.Create(0, 1, 0);
|
|
2851
|
+
var offsetVector = CSG$1.Vector3D.Create(0, 0, height);
|
|
2852
|
+
var normalVector = CSG$1.Vector3D.Create(0, 1, 0);
|
|
2318
2853
|
var polygons = [];
|
|
2319
2854
|
// bottom and top
|
|
2320
2855
|
polygons = polygons.concat(bottom._toPlanePolygons({
|
|
@@ -2328,8 +2863,8 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2328
2863
|
flipped: offsetVector.z < 0
|
|
2329
2864
|
}));
|
|
2330
2865
|
// walls
|
|
2331
|
-
var c1 = new CSG.Connector(offsetVector.times(0), [0, 0, offsetVector.z], normalVector);
|
|
2332
|
-
var c2 = new CSG.Connector(offsetVector, [0, 0, offsetVector.z], normalVector);
|
|
2866
|
+
var c1 = new CSG$1.Connector(offsetVector.times(0), [0, 0, offsetVector.z], normalVector);
|
|
2867
|
+
var c2 = new CSG$1.Connector(offsetVector, [0, 0, offsetVector.z], normalVector);
|
|
2333
2868
|
polygons = polygons.concat(bottom._toWallPolygons({
|
|
2334
2869
|
cag: top,
|
|
2335
2870
|
toConnector1: c1,
|
|
@@ -2337,7 +2872,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2337
2872
|
}));
|
|
2338
2873
|
// }
|
|
2339
2874
|
|
|
2340
|
-
return CSG.fromPolygons(polygons);
|
|
2875
|
+
return assertValidCSG$1(CSG$1.fromPolygons(polygons), 'poly2solid');
|
|
2341
2876
|
}
|
|
2342
2877
|
function slices2poly(slices, options, axis) {
|
|
2343
2878
|
debug$2('slices2poly', slices, options, axis);
|
|
@@ -2346,7 +2881,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2346
2881
|
twiststeps: 0
|
|
2347
2882
|
}, options);
|
|
2348
2883
|
var twistangle = options && parseFloat(options.twistangle) || 0;
|
|
2349
|
-
options && parseInt(options.twiststeps) || CSG.defaultResolution3D;
|
|
2884
|
+
options && parseInt(options.twiststeps) || CSG$1.defaultResolution3D;
|
|
2350
2885
|
var normalVector = options.si.normalVector;
|
|
2351
2886
|
var polygons = [];
|
|
2352
2887
|
|
|
@@ -2385,8 +2920,8 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2385
2920
|
var nextidx = idx + 1;
|
|
2386
2921
|
var top = !up ? slices[nextidx] : slice;
|
|
2387
2922
|
var bottom = up ? slices[nextidx] : slice;
|
|
2388
|
-
var c1 = new CSG.Connector(bottom.offset, connectorAxis, rotate(normalVector, twistangle, idx / slices.length));
|
|
2389
|
-
var c2 = new CSG.Connector(top.offset, connectorAxis, rotate(normalVector, twistangle, nextidx / slices.length));
|
|
2923
|
+
var c1 = new CSG$1.Connector(bottom.offset, connectorAxis, rotate(normalVector, twistangle, idx / slices.length));
|
|
2924
|
+
var c2 = new CSG$1.Connector(top.offset, connectorAxis, rotate(normalVector, twistangle, nextidx / slices.length));
|
|
2390
2925
|
|
|
2391
2926
|
// debug('slices2poly.slices', c1.point, c2.point);
|
|
2392
2927
|
polygons = polygons.concat(bottom.poly._toWallPolygons({
|
|
@@ -2396,21 +2931,21 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2396
2931
|
}));
|
|
2397
2932
|
}
|
|
2398
2933
|
});
|
|
2399
|
-
return CSG.fromPolygons(polygons);
|
|
2934
|
+
return assertValidCSG$1(CSG$1.fromPolygons(polygons), 'slices2poly');
|
|
2400
2935
|
}
|
|
2401
2936
|
function normalVector(axis) {
|
|
2402
2937
|
var axisInfo = {
|
|
2403
2938
|
z: {
|
|
2404
2939
|
orthoNormalCartesian: ['X', 'Y'],
|
|
2405
|
-
normalVector: CSG.Vector3D.Create(0, 1, 0)
|
|
2940
|
+
normalVector: CSG$1.Vector3D.Create(0, 1, 0)
|
|
2406
2941
|
},
|
|
2407
2942
|
x: {
|
|
2408
2943
|
orthoNormalCartesian: ['Y', 'Z'],
|
|
2409
|
-
normalVector: CSG.Vector3D.Create(0, 0, 1)
|
|
2944
|
+
normalVector: CSG$1.Vector3D.Create(0, 0, 1)
|
|
2410
2945
|
},
|
|
2411
2946
|
y: {
|
|
2412
|
-
orthoNormalCartesian: ['
|
|
2413
|
-
normalVector: CSG.Vector3D.Create(0, 0, 1)
|
|
2947
|
+
orthoNormalCartesian: ['Z', 'X'],
|
|
2948
|
+
normalVector: CSG$1.Vector3D.Create(0, 0, 1)
|
|
2414
2949
|
}
|
|
2415
2950
|
};
|
|
2416
2951
|
if (!axisInfo[axis]) error('normalVector: invalid axis ' + axis);
|
|
@@ -2462,7 +2997,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2462
2997
|
var si = sliceParams(orientation, radius, b);
|
|
2463
2998
|
debug$2('reShape', absoluteRadius, si);
|
|
2464
2999
|
if (si.axis !== 'z') throw new Error('reShape error: CAG._toPlanePolygons only uses the "z" axis. You must use the "z" axis for now.');
|
|
2465
|
-
var cutplane = CSG.OrthoNormalBasis.GetCartesian(si.orthoNormalCartesian[0], si.orthoNormalCartesian[1]).translate(si.cutDelta);
|
|
3000
|
+
var cutplane = CSG$1.OrthoNormalBasis.GetCartesian(si.orthoNormalCartesian[0], si.orthoNormalCartesian[1]).translate(si.cutDelta);
|
|
2466
3001
|
var slice = object.sectionCut(cutplane);
|
|
2467
3002
|
var first = axisApply(si.axis, function () {
|
|
2468
3003
|
return si.positive ? 0 : absoluteRadius;
|
|
@@ -2477,25 +3012,25 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2477
3012
|
si: si
|
|
2478
3013
|
}), si.axis).color(options.color);
|
|
2479
3014
|
var remainder = object.cutByPlane(plane);
|
|
2480
|
-
return union([options.unionOriginal ? object : remainder, delta.translate(si.moveDelta)]);
|
|
3015
|
+
return assertValidCSG$1(union([options.unionOriginal ? object : remainder, delta.translate(si.moveDelta)]), 'reShape');
|
|
2481
3016
|
}
|
|
2482
3017
|
function chamfer(object, radius, orientation, options) {
|
|
2483
|
-
return reShape(object, radius, orientation, options, function (first, last, slice) {
|
|
3018
|
+
return assertValidCSG$1(reShape(object, radius, orientation, options, function (first, last, slice) {
|
|
2484
3019
|
return [{
|
|
2485
3020
|
poly: slice,
|
|
2486
|
-
offset: new CSG.Vector3D(first)
|
|
3021
|
+
offset: new CSG$1.Vector3D(first)
|
|
2487
3022
|
}, {
|
|
2488
3023
|
poly: enlarge(slice, [-radius * 2, -radius * 2]),
|
|
2489
|
-
offset: new CSG.Vector3D(last)
|
|
3024
|
+
offset: new CSG$1.Vector3D(last)
|
|
2490
3025
|
}];
|
|
2491
|
-
});
|
|
3026
|
+
}), 'chamfer');
|
|
2492
3027
|
}
|
|
2493
3028
|
function fillet(object, radius, orientation, options) {
|
|
2494
3029
|
options = options || {};
|
|
2495
|
-
return reShape(object, radius, orientation, options, function (first, last, slice) {
|
|
2496
|
-
var v1 = new CSG.Vector3D(first);
|
|
2497
|
-
var v2 = new CSG.Vector3D(last);
|
|
2498
|
-
var res = options.resolution || CSG.defaultResolution3D;
|
|
3030
|
+
return assertValidCSG$1(reShape(object, radius, orientation, options, function (first, last, slice) {
|
|
3031
|
+
var v1 = new CSG$1.Vector3D(first);
|
|
3032
|
+
var v2 = new CSG$1.Vector3D(last);
|
|
3033
|
+
var res = options.resolution || CSG$1.defaultResolution3D;
|
|
2499
3034
|
var slices = range(0, res).map(function (i) {
|
|
2500
3035
|
var p = i > 0 ? i / (res - 1) : 0;
|
|
2501
3036
|
var v = v1.lerp(v2, p);
|
|
@@ -2506,7 +3041,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2506
3041
|
};
|
|
2507
3042
|
});
|
|
2508
3043
|
return slices;
|
|
2509
|
-
});
|
|
3044
|
+
}), 'fillet');
|
|
2510
3045
|
}
|
|
2511
3046
|
function calcRotate(part, solid, axis /* , angle */) {
|
|
2512
3047
|
var axes = {
|
|
@@ -2525,7 +3060,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2525
3060
|
var _calcRotate = calcRotate(part, solid, axis),
|
|
2526
3061
|
rotationCenter = _calcRotate.rotationCenter,
|
|
2527
3062
|
rotationAxis = _calcRotate.rotationAxis;
|
|
2528
|
-
return part.rotate(rotationCenter, rotationAxis, angle);
|
|
3063
|
+
return assertValidCSG$1('rotateAround')(part.rotate(rotationCenter, rotationAxis, angle));
|
|
2529
3064
|
}
|
|
2530
3065
|
function cloneProperties(from, to) {
|
|
2531
3066
|
return Object.entries(from).reduce(function (props, _ref) {
|
|
@@ -2537,10 +3072,10 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2537
3072
|
}, to);
|
|
2538
3073
|
}
|
|
2539
3074
|
function clone(o) {
|
|
2540
|
-
var c = CSG.fromPolygons(o.toPolygons());
|
|
3075
|
+
var c = CSG$1.fromPolygons(o.toPolygons());
|
|
2541
3076
|
cloneProperties(o, c);
|
|
2542
|
-
debug$2('clone', o, c, CSG);
|
|
2543
|
-
return c;
|
|
3077
|
+
debug$2('clone', o, c, CSG$1);
|
|
3078
|
+
return assertValidCSG$1(c, 'clone');
|
|
2544
3079
|
}
|
|
2545
3080
|
|
|
2546
3081
|
/**
|
|
@@ -2556,11 +3091,12 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2556
3091
|
var point = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [0, 0, 0];
|
|
2557
3092
|
var axis = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [1, 0, 0];
|
|
2558
3093
|
var normal = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [0, 0, 1];
|
|
2559
|
-
object.properties[name] = new CSG.Connector(point, axis, normal);
|
|
2560
|
-
return object;
|
|
3094
|
+
object.properties[name] = new CSG$1.Connector(point, axis, normal);
|
|
3095
|
+
return assertValidCSG$1('addConnector')(object);
|
|
2561
3096
|
}
|
|
2562
3097
|
|
|
2563
3098
|
var debug$1 = Debug('jscadUtils:parts');
|
|
3099
|
+
var assertValidCSG = AssertValidCSG('parts');
|
|
2564
3100
|
var parts = {
|
|
2565
3101
|
BBox: BBox$1,
|
|
2566
3102
|
Cube: Cube,
|
|
@@ -2578,7 +3114,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2578
3114
|
*/
|
|
2579
3115
|
function BBox$1() {
|
|
2580
3116
|
function box(object) {
|
|
2581
|
-
return CSG.cube({
|
|
3117
|
+
return CSG$1.cube({
|
|
2582
3118
|
center: object.centroid(),
|
|
2583
3119
|
radius: object.size().dividedBy(2)
|
|
2584
3120
|
});
|
|
@@ -2586,17 +3122,17 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2586
3122
|
for (var _len = arguments.length, objects = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
2587
3123
|
objects[_key] = arguments[_key];
|
|
2588
3124
|
}
|
|
2589
|
-
return objects.reduce(function (bbox, part) {
|
|
3125
|
+
return assertValidCSG(objects.reduce(function (bbox, part) {
|
|
2590
3126
|
var object = bbox ? union([bbox, box(part)]) : part;
|
|
2591
3127
|
return box(object);
|
|
2592
|
-
}, undefined);
|
|
3128
|
+
}, undefined), 'BBox');
|
|
2593
3129
|
}
|
|
2594
3130
|
function Cube(width) {
|
|
2595
3131
|
var r = div$1(fromxyz(width), 2);
|
|
2596
|
-
return CSG.cube({
|
|
3132
|
+
return assertValidCSG(CSG$1.cube({
|
|
2597
3133
|
center: r,
|
|
2598
3134
|
radius: r
|
|
2599
|
-
});
|
|
3135
|
+
}), 'Cube');
|
|
2600
3136
|
}
|
|
2601
3137
|
|
|
2602
3138
|
// export function Sphere(diameter) {
|
|
@@ -2630,11 +3166,11 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2630
3166
|
center: [r[0], r[1], 0],
|
|
2631
3167
|
radius: r,
|
|
2632
3168
|
roundradius: corner_radius,
|
|
2633
|
-
resolution: CSG.defaultResolution2D
|
|
3169
|
+
resolution: CSG$1.defaultResolution2D
|
|
2634
3170
|
}).extrude({
|
|
2635
3171
|
offset: [0, 0, thickness || 1.62]
|
|
2636
3172
|
});
|
|
2637
|
-
return roundedcube;
|
|
3173
|
+
return assertValidCSG(roundedcube, 'RoundedCube');
|
|
2638
3174
|
}
|
|
2639
3175
|
|
|
2640
3176
|
/**
|
|
@@ -2652,9 +3188,9 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2652
3188
|
start: [0, 0, 0],
|
|
2653
3189
|
end: [0, 0, height],
|
|
2654
3190
|
radius: diameter / 2,
|
|
2655
|
-
resolution: CSG.defaultResolution2D
|
|
3191
|
+
resolution: CSG$1.defaultResolution2D
|
|
2656
3192
|
}, options);
|
|
2657
|
-
return CSG.cylinder(options);
|
|
3193
|
+
return assertValidCSG(CSG$1.cylinder(options), 'Cylinder');
|
|
2658
3194
|
}
|
|
2659
3195
|
|
|
2660
3196
|
/**
|
|
@@ -2669,13 +3205,13 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2669
3205
|
function Cone(diameter1, diameter2, height) {
|
|
2670
3206
|
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
2671
3207
|
debug$1('parts.Cone', diameter1, diameter2, height, options);
|
|
2672
|
-
return CSG.cylinder(Object.assign({
|
|
3208
|
+
return assertValidCSG(CSG$1.cylinder(Object.assign({
|
|
2673
3209
|
start: [0, 0, 0],
|
|
2674
3210
|
end: [0, 0, height],
|
|
2675
3211
|
radiusStart: diameter1 / 2,
|
|
2676
3212
|
radiusEnd: diameter2 / 2,
|
|
2677
|
-
resolution: CSG.defaultResolution2D
|
|
2678
|
-
}, options));
|
|
3213
|
+
resolution: CSG$1.defaultResolution2D
|
|
3214
|
+
}, options)), 'Cone');
|
|
2679
3215
|
}
|
|
2680
3216
|
|
|
2681
3217
|
/**
|
|
@@ -2688,9 +3224,9 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2688
3224
|
var radius = diameter / 2;
|
|
2689
3225
|
var sqrt3 = Math.sqrt(3) / 2;
|
|
2690
3226
|
var hex = CAG.fromPoints([[radius, 0], [radius / 2, radius * sqrt3], [-radius / 2, radius * sqrt3], [-radius, 0], [-radius / 2, -radius * sqrt3], [radius / 2, -radius * sqrt3]]);
|
|
2691
|
-
return hex.extrude({
|
|
3227
|
+
return assertValidCSG(hex.extrude({
|
|
2692
3228
|
offset: [0, 0, height]
|
|
2693
|
-
});
|
|
3229
|
+
}), 'Hexagon');
|
|
2694
3230
|
}
|
|
2695
3231
|
|
|
2696
3232
|
/**
|
|
@@ -2703,9 +3239,9 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2703
3239
|
function Triangle(base, height) {
|
|
2704
3240
|
var radius = base / 2;
|
|
2705
3241
|
var tri = CAG.fromPoints([[-radius, 0], [radius, 0], [0, Math.sin(30) * radius]]);
|
|
2706
|
-
return tri.extrude({
|
|
3242
|
+
return assertValidCSG(tri.extrude({
|
|
2707
3243
|
offset: [0, 0, height]
|
|
2708
|
-
});
|
|
3244
|
+
}), 'Triangle');
|
|
2709
3245
|
}
|
|
2710
3246
|
|
|
2711
3247
|
/**
|
|
@@ -2720,7 +3256,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2720
3256
|
* @returns {CSG} A CSG Tube
|
|
2721
3257
|
*/
|
|
2722
3258
|
function Tube(outsideDiameter, insideDiameter, height, outsideOptions, insideOptions) {
|
|
2723
|
-
return Cylinder(outsideDiameter, height, outsideOptions).subtract(Cylinder(insideDiameter, height, insideOptions || outsideOptions));
|
|
3259
|
+
return assertValidCSG(Cylinder(outsideDiameter, height, outsideOptions).subtract(Cylinder(insideDiameter, height, insideOptions || outsideOptions)), 'Tube');
|
|
2724
3260
|
}
|
|
2725
3261
|
|
|
2726
3262
|
/**
|
|
@@ -2744,11 +3280,11 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2744
3280
|
center: [r[0], r[1], 0],
|
|
2745
3281
|
radius: r,
|
|
2746
3282
|
roundradius: corner_radius,
|
|
2747
|
-
resolution: CSG.defaultResolution2D
|
|
3283
|
+
resolution: CSG$1.defaultResolution2D
|
|
2748
3284
|
}).extrude({
|
|
2749
3285
|
offset: [0, 0, thickness || 1.62]
|
|
2750
3286
|
});
|
|
2751
|
-
return board;
|
|
3287
|
+
return assertValidCSG(board, 'Board');
|
|
2752
3288
|
}
|
|
2753
3289
|
var Hardware = {
|
|
2754
3290
|
Orientation: {
|
|
@@ -2898,7 +3434,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2898
3434
|
* This will bisect an object using a rabett join. Returns a
|
|
2899
3435
|
* `group` object with `positive` and `negative` parts.
|
|
2900
3436
|
*
|
|
2901
|
-
*
|
|
3437
|
+
* 
|
|
2902
3438
|
* @example
|
|
2903
3439
|
*include('dist/jscad-utils.jscad');
|
|
2904
3440
|
*
|
|
@@ -2928,6 +3464,11 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2928
3464
|
gap = gap || 0.25;
|
|
2929
3465
|
var inside = thickness - gap;
|
|
2930
3466
|
var outside = -thickness + gap;
|
|
3467
|
+
var boxHeight = box.size().z;
|
|
3468
|
+
if (Math.abs(height) >= boxHeight) {
|
|
3469
|
+
throw new Error("Rabett: height (".concat(height, ") must be less than the object height (").concat(boxHeight, ")"));
|
|
3470
|
+
}
|
|
3471
|
+
|
|
2931
3472
|
// options.color = true;
|
|
2932
3473
|
debug('inside', inside, 'outside', outside);
|
|
2933
3474
|
var group = Group();
|
|
@@ -2957,7 +3498,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
2957
3498
|
* Used on a hollow object, this will rabett out the top and/or
|
|
2958
3499
|
* bottom of the object.
|
|
2959
3500
|
*
|
|
2960
|
-
* 
|
|
2961
3502
|
*
|
|
2962
3503
|
* @example
|
|
2963
3504
|
*include('dist/jscad-utils.jscad');
|
|
@@ -3044,10 +3585,10 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
3044
3585
|
thickness = thickness || 2;
|
|
3045
3586
|
var s = div$1(xyz2array(size), 2);
|
|
3046
3587
|
var r = add(s, thickness);
|
|
3047
|
-
var box = CSG.cube({
|
|
3588
|
+
var box = CSG$1.cube({
|
|
3048
3589
|
center: r,
|
|
3049
3590
|
radius: r
|
|
3050
|
-
}).subtract(CSG.cube({
|
|
3591
|
+
}).subtract(CSG$1.cube({
|
|
3051
3592
|
center: r,
|
|
3052
3593
|
radius: s
|
|
3053
3594
|
}));
|
|
@@ -3062,7 +3603,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
3062
3603
|
* wall thickness. This is done by reducing the object by half the
|
|
3063
3604
|
* thickness and subtracting the reduced version from the original object.
|
|
3064
3605
|
*
|
|
3065
|
-
* 
|
|
3066
3607
|
*
|
|
3067
3608
|
* @param {CSG} object A CSG object
|
|
3068
3609
|
* @param {Number} [thickness=2] The thickness of the walls.
|
|
@@ -3090,7 +3631,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
3090
3631
|
var BBox = function BBox(o) {
|
|
3091
3632
|
depreciated('BBox', true, "Use 'parts.BBox' instead");
|
|
3092
3633
|
var s = div$1(xyz2array(o.size()), 2);
|
|
3093
|
-
return CSG.cube({
|
|
3634
|
+
return CSG$1.cube({
|
|
3094
3635
|
center: s,
|
|
3095
3636
|
radius: s
|
|
3096
3637
|
}).align(o, 'xyz');
|
|
@@ -3102,7 +3643,7 @@ var jscadUtils = (function (exports, jsCadCSG, scadApi) {
|
|
|
3102
3643
|
var gap = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.25;
|
|
3103
3644
|
var r = add(getRadius(box), -thickness / 2);
|
|
3104
3645
|
r[2] = thickness / 2;
|
|
3105
|
-
var cutter = CSG.cube({
|
|
3646
|
+
var cutter = CSG$1.cube({
|
|
3106
3647
|
center: r,
|
|
3107
3648
|
radius: r
|
|
3108
3649
|
}).align(box, 'xy').color('green');
|