@toolpath/viewer 0.3.1 → 0.4.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.
|
@@ -4,7 +4,7 @@ import { useEffect as useEffect5, useMemo as useMemo6 } from "react";
|
|
|
4
4
|
// src/part-mesh.tsx
|
|
5
5
|
import { useThree as useThree6 } from "@react-three/fiber";
|
|
6
6
|
import { useCallback as useCallback3, useEffect as useEffect4, useLayoutEffect, useMemo as useMemo5, useRef as useRef5 } from "react";
|
|
7
|
-
import { Vector3 as
|
|
7
|
+
import { Vector3 as Vector39 } from "three";
|
|
8
8
|
|
|
9
9
|
// src/model/directions.ts
|
|
10
10
|
var EPSILON = 1e-6;
|
|
@@ -54,34 +54,48 @@ var HIGHLIGHT_COLORS = {
|
|
|
54
54
|
geometryIssue: 16711680
|
|
55
55
|
};
|
|
56
56
|
var DIRECTION_COLORS = [
|
|
57
|
+
/*
|
|
58
|
+
* Nine ways up, told apart at a glance.
|
|
59
|
+
*
|
|
60
|
+
* The palette is an **identity**, not a ranking — the same colour on the
|
|
61
|
+
* face, on the arrow and on the row, which is the whole point of it. Two
|
|
62
|
+
* entries that look alike make it a worse identity than eight that do not,
|
|
63
|
+
* and this list had three problems at once:
|
|
64
|
+
*
|
|
65
|
+
* - teal-500, cyan-500 and emerald-500 were neighbours in hue *and*
|
|
66
|
+
* lightness, so three ways up were one blue-green smear;
|
|
67
|
+
* - blue-500 and indigo-500 were the closest pair of all;
|
|
68
|
+
* - indigo-500 sat 0.06 from the violet an offer is painted in — the very
|
|
69
|
+
* clash that sent a purple out of this list once already, still here under
|
|
70
|
+
* a different name.
|
|
71
|
+
*
|
|
72
|
+
* The warm ramp belongs to difficulty, red to sharp corners, orange to the
|
|
73
|
+
* selection over this cycle, violet to an offer. There is no room to move
|
|
74
|
+
* anything to a fresh hue, so the spread is along **lightness** as much as
|
|
75
|
+
* hue: a pale cyan and a mid teal, a light green and a deep navy.
|
|
76
|
+
*
|
|
77
|
+
* `selection-colors.test.ts` measures every pair in Oklab and holds them all
|
|
78
|
+
* above 0.1 — including against the four colours the part can wear over this
|
|
79
|
+
* wash. The binding pair now is blue against an offer's violet, at 0.12.
|
|
80
|
+
*/
|
|
57
81
|
3900150,
|
|
58
82
|
// blue
|
|
59
|
-
|
|
83
|
+
889992,
|
|
60
84
|
// teal
|
|
61
85
|
14239471,
|
|
62
86
|
// fuchsia
|
|
63
|
-
|
|
64
|
-
// cyan
|
|
65
|
-
/*
|
|
66
|
-
* Olive, where a purple used to be.
|
|
67
|
-
*
|
|
68
|
-
* The purple sat one step from the violet a proposed feature is painted in,
|
|
69
|
-
* so an offer laid over work already assigned to this direction was a clash
|
|
70
|
-
* between "suggested" and "decided" — the two states the part is read for.
|
|
71
|
-
* Olive is the only hue with room: the warm ramp belongs to the difficulty
|
|
72
|
-
* bands, red to sharp corners, orange to faces being picked, and green to
|
|
73
|
-
* whatever is being looked at.
|
|
74
|
-
*/
|
|
87
|
+
6809849,
|
|
88
|
+
// pale cyan
|
|
75
89
|
6660877,
|
|
76
90
|
// olive
|
|
77
91
|
15485081,
|
|
78
92
|
// pink
|
|
79
|
-
|
|
80
|
-
// slate
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
//
|
|
93
|
+
9741240,
|
|
94
|
+
// light slate
|
|
95
|
+
4906624,
|
|
96
|
+
// light green
|
|
97
|
+
1981066
|
|
98
|
+
// deep navy
|
|
85
99
|
];
|
|
86
100
|
var DEFAULT_THEME = {
|
|
87
101
|
background: null,
|
|
@@ -272,6 +286,17 @@ function trackTaps(element) {
|
|
|
272
286
|
dispose: () => element.removeEventListener("pointerdown", down, { capture: true })
|
|
273
287
|
};
|
|
274
288
|
}
|
|
289
|
+
var DOUBLE_TAP_MS = 400;
|
|
290
|
+
function trackDoubleTaps(within = DOUBLE_TAP_MS, slop = TAP_SLOP) {
|
|
291
|
+
let last = null;
|
|
292
|
+
return {
|
|
293
|
+
isDouble: (event) => {
|
|
294
|
+
const paired = last !== null && event.timeStamp - last.timeStamp <= within && !movedFar(last, event, slop);
|
|
295
|
+
last = paired ? null : event;
|
|
296
|
+
return paired;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
275
300
|
|
|
276
301
|
// src/tap.tsx
|
|
277
302
|
function useTapGuard() {
|
|
@@ -391,10 +416,11 @@ function buildRegionAttribute(model, texels, vertexCount) {
|
|
|
391
416
|
function createPart(model, geometry, theme) {
|
|
392
417
|
const position = geometry.getAttribute("position");
|
|
393
418
|
const texels = buildRegionTexels(model);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
419
|
+
const regionAttribute = new Float32BufferAttribute2(
|
|
420
|
+
buildRegionAttribute(model, texels, position.count),
|
|
421
|
+
1
|
|
397
422
|
);
|
|
423
|
+
geometry.setAttribute(REGION_ATTRIBUTE, regionAttribute);
|
|
398
424
|
const width = model.regions.length + 1;
|
|
399
425
|
const state = new Uint8Array(width * 4);
|
|
400
426
|
const stateTexture = new DataTexture(state, width, 1, RGBAFormat, UnsignedByteType);
|
|
@@ -430,7 +456,8 @@ function createPart(model, geometry, theme) {
|
|
|
430
456
|
"#include <color_fragment>",
|
|
431
457
|
`#include <color_fragment>
|
|
432
458
|
regionState = texelFetch(uRegionState, ivec2(int(vRegion + 0.5), 0), 0);
|
|
433
|
-
diffuseColor.rgb = mix(diffuseColor.rgb, regionState.rgb, regionState.a)
|
|
459
|
+
diffuseColor.rgb = mix(diffuseColor.rgb, regionState.rgb, regionState.a);
|
|
460
|
+
`
|
|
434
461
|
).replace(
|
|
435
462
|
"#include <emissivemap_fragment>",
|
|
436
463
|
`#include <emissivemap_fragment>
|
|
@@ -520,7 +547,9 @@ function createPart(model, geometry, theme) {
|
|
|
520
547
|
},
|
|
521
548
|
dispose() {
|
|
522
549
|
object.clear();
|
|
523
|
-
geometry.
|
|
550
|
+
if (geometry.getAttribute(REGION_ATTRIBUTE) === regionAttribute) {
|
|
551
|
+
geometry.deleteAttribute(REGION_ATTRIBUTE);
|
|
552
|
+
}
|
|
524
553
|
material.dispose();
|
|
525
554
|
edgeGeometry.dispose();
|
|
526
555
|
edgeMaterial.dispose();
|
|
@@ -529,6 +558,48 @@ function createPart(model, geometry, theme) {
|
|
|
529
558
|
};
|
|
530
559
|
}
|
|
531
560
|
|
|
561
|
+
// src/render/adjacency.ts
|
|
562
|
+
var GRID = 1e-4;
|
|
563
|
+
var keyFor = (x, y, z) => `${Math.round(x / GRID)},${Math.round(y / GRID)},${Math.round(z / GRID)}`;
|
|
564
|
+
function regionAdjacency(model, geometry) {
|
|
565
|
+
const position = geometry.getAttribute("position");
|
|
566
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
567
|
+
if (!position) return adjacency;
|
|
568
|
+
const owner = new Int32Array(Math.floor(position.count / 3)).fill(-1);
|
|
569
|
+
for (const region of model.regions) {
|
|
570
|
+
for (let at = region.triangles.start; at < region.triangles.end; at++) {
|
|
571
|
+
if (at >= 0 && at < owner.length) owner[at] = region.idx;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const edges = /* @__PURE__ */ new Map();
|
|
575
|
+
const join = (a, b) => {
|
|
576
|
+
if (a === b) return;
|
|
577
|
+
const mine = adjacency.get(a) ?? /* @__PURE__ */ new Set();
|
|
578
|
+
mine.add(b);
|
|
579
|
+
adjacency.set(a, mine);
|
|
580
|
+
const theirs = adjacency.get(b) ?? /* @__PURE__ */ new Set();
|
|
581
|
+
theirs.add(a);
|
|
582
|
+
adjacency.set(b, theirs);
|
|
583
|
+
};
|
|
584
|
+
for (let triangle = 0; triangle < owner.length; triangle++) {
|
|
585
|
+
const region = owner[triangle];
|
|
586
|
+
if (region === -1) continue;
|
|
587
|
+
const corners = [0, 1, 2].map((corner) => {
|
|
588
|
+
const at = triangle * 3 + corner;
|
|
589
|
+
return keyFor(position.getX(at), position.getY(at), position.getZ(at));
|
|
590
|
+
});
|
|
591
|
+
for (let corner = 0; corner < 3; corner++) {
|
|
592
|
+
const from = corners[corner];
|
|
593
|
+
const to = corners[(corner + 1) % 3];
|
|
594
|
+
const edge = from < to ? `${from}|${to}` : `${to}|${from}`;
|
|
595
|
+
const seen = edges.get(edge);
|
|
596
|
+
if (seen === void 0) edges.set(edge, region);
|
|
597
|
+
else join(seen, region);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return adjacency;
|
|
601
|
+
}
|
|
602
|
+
|
|
532
603
|
// src/render/selection.ts
|
|
533
604
|
var FEATURE_TYPE_RANKS = [
|
|
534
605
|
["blind_hole", "through_hole", "filleted_blind_hole"],
|
|
@@ -638,7 +709,7 @@ import {
|
|
|
638
709
|
useMemo as useMemo3,
|
|
639
710
|
useRef as useRef2
|
|
640
711
|
} from "react";
|
|
641
|
-
import { Box3 as Box33, Vector3 as
|
|
712
|
+
import { Box3 as Box33, Vector3 as Vector36 } from "three";
|
|
642
713
|
|
|
643
714
|
// src/camera.tsx
|
|
644
715
|
import { useFrame, useThree as useThree2 } from "@react-three/fiber";
|
|
@@ -990,7 +1061,8 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
990
1061
|
var CadCameraControls = ({
|
|
991
1062
|
controlsRef,
|
|
992
1063
|
scheme = "toolpath",
|
|
993
|
-
freeOrbit = true
|
|
1064
|
+
freeOrbit = true,
|
|
1065
|
+
zoomTo = "cursor"
|
|
994
1066
|
}) => {
|
|
995
1067
|
const camera = useThree2((state) => state.camera);
|
|
996
1068
|
const domElement = useThree2((state) => state.gl.domElement);
|
|
@@ -1029,12 +1101,255 @@ var CadCameraControls = ({
|
|
|
1029
1101
|
useEffect2(() => {
|
|
1030
1102
|
controls.setFreeOrbit(freeOrbit);
|
|
1031
1103
|
}, [controls, freeOrbit]);
|
|
1104
|
+
useEffect2(() => {
|
|
1105
|
+
controls.dollyToCursor = zoomTo === "cursor";
|
|
1106
|
+
}, [controls, zoomTo]);
|
|
1032
1107
|
useFrame((_, delta) => {
|
|
1033
1108
|
controls.update(delta);
|
|
1034
1109
|
});
|
|
1035
1110
|
return null;
|
|
1036
1111
|
};
|
|
1037
1112
|
|
|
1113
|
+
// src/render/view-cube.ts
|
|
1114
|
+
import {
|
|
1115
|
+
BufferGeometry,
|
|
1116
|
+
CanvasTexture,
|
|
1117
|
+
Float32BufferAttribute as Float32BufferAttribute3,
|
|
1118
|
+
SRGBColorSpace,
|
|
1119
|
+
Vector3 as Vector35
|
|
1120
|
+
} from "three";
|
|
1121
|
+
var VIEW_NAMES = [
|
|
1122
|
+
// The six faces.
|
|
1123
|
+
"top",
|
|
1124
|
+
"bottom",
|
|
1125
|
+
"front",
|
|
1126
|
+
"back",
|
|
1127
|
+
"left",
|
|
1128
|
+
"right",
|
|
1129
|
+
// The twelve edges.
|
|
1130
|
+
"top-front",
|
|
1131
|
+
"top-back",
|
|
1132
|
+
"top-left",
|
|
1133
|
+
"top-right",
|
|
1134
|
+
"bottom-front",
|
|
1135
|
+
"bottom-back",
|
|
1136
|
+
"bottom-left",
|
|
1137
|
+
"bottom-right",
|
|
1138
|
+
"front-left",
|
|
1139
|
+
"front-right",
|
|
1140
|
+
"back-left",
|
|
1141
|
+
"back-right",
|
|
1142
|
+
// The eight corners.
|
|
1143
|
+
"top-front-left",
|
|
1144
|
+
"top-front-right",
|
|
1145
|
+
"top-back-left",
|
|
1146
|
+
"top-back-right",
|
|
1147
|
+
"bottom-front-left",
|
|
1148
|
+
"bottom-front-right",
|
|
1149
|
+
"bottom-back-left",
|
|
1150
|
+
"bottom-back-right"
|
|
1151
|
+
];
|
|
1152
|
+
var VIEW_SIGNS = {
|
|
1153
|
+
top: [0, 0, 1],
|
|
1154
|
+
bottom: [0, 0, -1],
|
|
1155
|
+
front: [0, -1, 0],
|
|
1156
|
+
back: [0, 1, 0],
|
|
1157
|
+
left: [-1, 0, 0],
|
|
1158
|
+
right: [1, 0, 0],
|
|
1159
|
+
"top-front": [0, -1, 1],
|
|
1160
|
+
"top-back": [0, 1, 1],
|
|
1161
|
+
"top-left": [-1, 0, 1],
|
|
1162
|
+
"top-right": [1, 0, 1],
|
|
1163
|
+
"bottom-front": [0, -1, -1],
|
|
1164
|
+
"bottom-back": [0, 1, -1],
|
|
1165
|
+
"bottom-left": [-1, 0, -1],
|
|
1166
|
+
"bottom-right": [1, 0, -1],
|
|
1167
|
+
"front-left": [-1, -1, 0],
|
|
1168
|
+
"front-right": [1, -1, 0],
|
|
1169
|
+
"back-left": [-1, 1, 0],
|
|
1170
|
+
"back-right": [1, 1, 0],
|
|
1171
|
+
"top-front-left": [-1, -1, 1],
|
|
1172
|
+
"top-front-right": [1, -1, 1],
|
|
1173
|
+
"top-back-left": [-1, 1, 1],
|
|
1174
|
+
"top-back-right": [1, 1, 1],
|
|
1175
|
+
"bottom-front-left": [-1, -1, -1],
|
|
1176
|
+
"bottom-front-right": [1, -1, -1],
|
|
1177
|
+
"bottom-back-left": [-1, 1, -1],
|
|
1178
|
+
"bottom-back-right": [1, 1, -1]
|
|
1179
|
+
};
|
|
1180
|
+
function viewKind(name) {
|
|
1181
|
+
const off = VIEW_SIGNS[name].filter((sign) => sign !== 0).length;
|
|
1182
|
+
if (off === 1) return "face";
|
|
1183
|
+
if (off === 2) return "edge";
|
|
1184
|
+
return "corner";
|
|
1185
|
+
}
|
|
1186
|
+
var DIRECTIONS = new Map(
|
|
1187
|
+
VIEW_NAMES.map((name) => {
|
|
1188
|
+
const [x, y, z] = VIEW_SIGNS[name];
|
|
1189
|
+
const length = Math.hypot(x, y, z);
|
|
1190
|
+
return [name, { x: x / length, y: y / length, z: z / length }];
|
|
1191
|
+
})
|
|
1192
|
+
);
|
|
1193
|
+
function viewVector(name) {
|
|
1194
|
+
return DIRECTIONS.get(name) ?? { x: 0, y: 0, z: 1 };
|
|
1195
|
+
}
|
|
1196
|
+
function viewUp(direction) {
|
|
1197
|
+
if (direction.x === 0 && direction.y === 0) {
|
|
1198
|
+
return { x: 0, y: direction.z > 0 ? 1 : -1, z: 0 };
|
|
1199
|
+
}
|
|
1200
|
+
return { x: 0, y: 0, z: 1 };
|
|
1201
|
+
}
|
|
1202
|
+
function squaredUp(direction, currentUp) {
|
|
1203
|
+
const forward = new Vector35(direction.x, direction.y, direction.z).normalize();
|
|
1204
|
+
const canonical = viewUp(direction);
|
|
1205
|
+
const zero = new Vector35(canonical.x, canonical.y, canonical.z);
|
|
1206
|
+
zero.addScaledVector(forward, -forward.dot(zero)).normalize();
|
|
1207
|
+
const quarter = new Vector35().crossVectors(forward, zero);
|
|
1208
|
+
const current = new Vector35(currentUp.x, currentUp.y, currentUp.z);
|
|
1209
|
+
const rolls = [zero, quarter, zero.clone().negate(), quarter.clone().negate()];
|
|
1210
|
+
const nearest = rolls.reduce(
|
|
1211
|
+
(best, roll) => roll.dot(current) > best.dot(current) ? roll : best
|
|
1212
|
+
);
|
|
1213
|
+
return vec(nearest);
|
|
1214
|
+
}
|
|
1215
|
+
var CHAMFER = 0.5834;
|
|
1216
|
+
function vec(v) {
|
|
1217
|
+
return { x: v.x, y: v.y, z: v.z };
|
|
1218
|
+
}
|
|
1219
|
+
function cubeZones(chamfer = CHAMFER) {
|
|
1220
|
+
const AXES = [0, 1, 2];
|
|
1221
|
+
return VIEW_NAMES.map((name) => {
|
|
1222
|
+
const kind = viewKind(name);
|
|
1223
|
+
const direction = viewVector(name);
|
|
1224
|
+
const [sx, sy, sz] = VIEW_SIGNS[name];
|
|
1225
|
+
const sign = (axis) => VIEW_SIGNS[name][axis] ?? 0;
|
|
1226
|
+
const normal = new Vector35(direction.x, direction.y, direction.z);
|
|
1227
|
+
let polygon;
|
|
1228
|
+
if (kind === "face") {
|
|
1229
|
+
const up = viewUp(direction);
|
|
1230
|
+
const upVector = new Vector35(up.x, up.y, up.z);
|
|
1231
|
+
const right = normal.clone().negate().cross(upVector).normalize();
|
|
1232
|
+
const square = [
|
|
1233
|
+
[-1, -1],
|
|
1234
|
+
[1, -1],
|
|
1235
|
+
[1, 1],
|
|
1236
|
+
[-1, 1]
|
|
1237
|
+
];
|
|
1238
|
+
polygon = square.map(
|
|
1239
|
+
([u, v]) => normal.clone().addScaledVector(right, u * chamfer).addScaledVector(upVector, v * chamfer)
|
|
1240
|
+
);
|
|
1241
|
+
} else if (kind === "edge") {
|
|
1242
|
+
const a = AXES.find((axis) => sign(axis) !== 0) ?? 0;
|
|
1243
|
+
const b = [...AXES].reverse().find((axis) => sign(axis) !== 0) ?? 0;
|
|
1244
|
+
const c = AXES.find((axis) => sign(axis) === 0) ?? 0;
|
|
1245
|
+
const rectangle = [
|
|
1246
|
+
[1, chamfer, -chamfer],
|
|
1247
|
+
[1, chamfer, chamfer],
|
|
1248
|
+
[chamfer, 1, chamfer],
|
|
1249
|
+
[chamfer, 1, -chamfer]
|
|
1250
|
+
];
|
|
1251
|
+
polygon = rectangle.map(
|
|
1252
|
+
([onA, onB, onC]) => new Vector35().setComponent(a, sign(a) * onA).setComponent(b, sign(b) * onB).setComponent(c, onC)
|
|
1253
|
+
);
|
|
1254
|
+
} else {
|
|
1255
|
+
const triangle = [
|
|
1256
|
+
[1, chamfer, chamfer],
|
|
1257
|
+
[chamfer, 1, chamfer],
|
|
1258
|
+
[chamfer, chamfer, 1]
|
|
1259
|
+
];
|
|
1260
|
+
polygon = triangle.map(([onX, onY, onZ]) => new Vector35(sx * onX, sy * onY, sz * onZ));
|
|
1261
|
+
}
|
|
1262
|
+
const [first, second, third] = polygon;
|
|
1263
|
+
if (first && second && third) {
|
|
1264
|
+
const facing = new Vector35().subVectors(second, first).cross(new Vector35().subVectors(third, first));
|
|
1265
|
+
if (facing.dot(normal) < 0) polygon.reverse();
|
|
1266
|
+
}
|
|
1267
|
+
return { name, kind, direction, polygon: polygon.map(vec) };
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
function panelGeometry(zone) {
|
|
1271
|
+
const positions = [];
|
|
1272
|
+
const normals = [];
|
|
1273
|
+
const [first] = zone.polygon;
|
|
1274
|
+
const geometry = new BufferGeometry();
|
|
1275
|
+
if (!first) return geometry;
|
|
1276
|
+
for (let index = 1; index + 1 < zone.polygon.length; index += 1) {
|
|
1277
|
+
const b = zone.polygon[index];
|
|
1278
|
+
const c = zone.polygon[index + 1];
|
|
1279
|
+
if (!b || !c) continue;
|
|
1280
|
+
positions.push(first.x, first.y, first.z, b.x, b.y, b.z, c.x, c.y, c.z);
|
|
1281
|
+
for (let vertex = 0; vertex < 3; vertex += 1) {
|
|
1282
|
+
normals.push(zone.direction.x, zone.direction.y, zone.direction.z);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
geometry.setAttribute("position", new Float32BufferAttribute3(positions, 3));
|
|
1286
|
+
geometry.setAttribute("normal", new Float32BufferAttribute3(normals, 3));
|
|
1287
|
+
return geometry;
|
|
1288
|
+
}
|
|
1289
|
+
function cubeOutlineGeometry(zones) {
|
|
1290
|
+
const positions = [];
|
|
1291
|
+
for (const zone of zones) {
|
|
1292
|
+
for (let index = 0; index < zone.polygon.length; index += 1) {
|
|
1293
|
+
const from = zone.polygon[index];
|
|
1294
|
+
const to = zone.polygon[(index + 1) % zone.polygon.length];
|
|
1295
|
+
if (!from || !to) continue;
|
|
1296
|
+
positions.push(from.x, from.y, from.z, to.x, to.y, to.z);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const geometry = new BufferGeometry();
|
|
1300
|
+
geometry.setAttribute("position", new Float32BufferAttribute3(positions, 3));
|
|
1301
|
+
return geometry;
|
|
1302
|
+
}
|
|
1303
|
+
function labelGeometry(zone) {
|
|
1304
|
+
const positions = [];
|
|
1305
|
+
const uvs = [];
|
|
1306
|
+
const corners = [0, 1, 2, 0, 2, 3];
|
|
1307
|
+
const corner = [
|
|
1308
|
+
[0, 0],
|
|
1309
|
+
[1, 0],
|
|
1310
|
+
[1, 1],
|
|
1311
|
+
[0, 1]
|
|
1312
|
+
];
|
|
1313
|
+
for (const index of corners) {
|
|
1314
|
+
const point = zone.polygon[index];
|
|
1315
|
+
const uv = corner[index];
|
|
1316
|
+
if (!point || !uv) continue;
|
|
1317
|
+
positions.push(
|
|
1318
|
+
point.x + zone.direction.x * 4e-3,
|
|
1319
|
+
point.y + zone.direction.y * 4e-3,
|
|
1320
|
+
point.z + zone.direction.z * 4e-3
|
|
1321
|
+
);
|
|
1322
|
+
uvs.push(uv[0] ?? 0, uv[1] ?? 0);
|
|
1323
|
+
}
|
|
1324
|
+
const geometry = new BufferGeometry();
|
|
1325
|
+
geometry.setAttribute("position", new Float32BufferAttribute3(positions, 3));
|
|
1326
|
+
geometry.setAttribute("uv", new Float32BufferAttribute3(uvs, 2));
|
|
1327
|
+
return geometry;
|
|
1328
|
+
}
|
|
1329
|
+
function labelTexture(label, color) {
|
|
1330
|
+
const size = 128;
|
|
1331
|
+
const canvas = document.createElement("canvas");
|
|
1332
|
+
canvas.width = size;
|
|
1333
|
+
canvas.height = size;
|
|
1334
|
+
const context = canvas.getContext("2d");
|
|
1335
|
+
if (context) {
|
|
1336
|
+
const text = label.toUpperCase();
|
|
1337
|
+
const hex = `#${color.toString(16).padStart(6, "0")}`;
|
|
1338
|
+
context.font = `600 ${size / 4}px system-ui, sans-serif`;
|
|
1339
|
+
const width = context.measureText("BOTTOM").width || 1;
|
|
1340
|
+
const scale = Math.min(1, size * 0.78 / width);
|
|
1341
|
+
context.font = `600 ${size / 4 * scale}px system-ui, sans-serif`;
|
|
1342
|
+
context.textAlign = "center";
|
|
1343
|
+
context.textBaseline = "middle";
|
|
1344
|
+
context.fillStyle = hex;
|
|
1345
|
+
context.fillText(text, size / 2, size / 2);
|
|
1346
|
+
}
|
|
1347
|
+
const texture = new CanvasTexture(canvas);
|
|
1348
|
+
texture.colorSpace = SRGBColorSpace;
|
|
1349
|
+
texture.needsUpdate = true;
|
|
1350
|
+
return texture;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1038
1353
|
// src/viewer.tsx
|
|
1039
1354
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
1040
1355
|
var ViewerControlsContext = createContext(null);
|
|
@@ -1049,26 +1364,29 @@ var ViewerScene = ({
|
|
|
1049
1364
|
projection,
|
|
1050
1365
|
scheme,
|
|
1051
1366
|
freeOrbit,
|
|
1367
|
+
zoomTo,
|
|
1368
|
+
recentreOnDoubleClick,
|
|
1052
1369
|
theme
|
|
1053
1370
|
}) => {
|
|
1054
1371
|
const camera = useThree3((state) => state.camera);
|
|
1055
1372
|
const size = useThree3((state) => state.size);
|
|
1056
1373
|
const invalidate = useThree3((state) => state.invalidate);
|
|
1374
|
+
const domElement = useThree3((state) => state.gl.domElement);
|
|
1057
1375
|
const controlsRef = useRef2(null);
|
|
1058
1376
|
const contentRef = useRef2(null);
|
|
1059
1377
|
const lightsRef = useRef2(null);
|
|
1060
1378
|
const initialFrameComplete = useRef2(false);
|
|
1061
1379
|
const boundsRef = useRef2(defaultBounds());
|
|
1062
1380
|
const scratchBox = useMemo3(() => new Box33(), []);
|
|
1063
|
-
const scratchDirection = useMemo3(() => new
|
|
1064
|
-
const scratchView = useMemo3(() => new
|
|
1381
|
+
const scratchDirection = useMemo3(() => new Vector36(), []);
|
|
1382
|
+
const scratchView = useMemo3(() => new Vector36(), []);
|
|
1065
1383
|
const measure = useCallback(() => {
|
|
1066
1384
|
const content = contentRef.current;
|
|
1067
1385
|
boundsRef.current = content ? contentBounds(content, scratchBox) : defaultBounds();
|
|
1068
1386
|
return boundsRef.current;
|
|
1069
1387
|
}, [scratchBox]);
|
|
1070
1388
|
const frame = useCallback(
|
|
1071
|
-
(direction, transition = false) => {
|
|
1389
|
+
(direction, transition = false, up) => {
|
|
1072
1390
|
const controls = controlsRef.current;
|
|
1073
1391
|
if (!controls) return false;
|
|
1074
1392
|
const bounds = measure();
|
|
@@ -1076,6 +1394,10 @@ var ViewerScene = ({
|
|
|
1076
1394
|
const distance = fitDistance(projection, size, bounds, DEFAULT_FIT_MARGIN);
|
|
1077
1395
|
const position = scratchDirection.copy(direction).normalize().multiplyScalar(distance);
|
|
1078
1396
|
position.add(bounds.center);
|
|
1397
|
+
if (up) {
|
|
1398
|
+
camera.up.set(up.x, up.y, up.z);
|
|
1399
|
+
controls.updateCameraUp();
|
|
1400
|
+
}
|
|
1079
1401
|
void controls.setLookAt(
|
|
1080
1402
|
position.x,
|
|
1081
1403
|
position.y,
|
|
@@ -1095,8 +1417,8 @@ var ViewerScene = ({
|
|
|
1095
1417
|
(bounds) => {
|
|
1096
1418
|
const controls = controlsRef.current;
|
|
1097
1419
|
if (!controls) return false;
|
|
1098
|
-
const target = controls.getTarget(new
|
|
1099
|
-
const direction = currentViewDirection(camera, target, new
|
|
1420
|
+
const target = controls.getTarget(new Vector36());
|
|
1421
|
+
const direction = currentViewDirection(camera, target, new Vector36());
|
|
1100
1422
|
const distance = fitDistance(projection, size, bounds, DEFAULT_FIT_MARGIN);
|
|
1101
1423
|
const position = direction.multiplyScalar(distance).add(bounds.center);
|
|
1102
1424
|
void controls.setLookAt(
|
|
@@ -1119,14 +1441,24 @@ var ViewerScene = ({
|
|
|
1119
1441
|
const fitContent = useCallback(() => {
|
|
1120
1442
|
const controls = controlsRef.current;
|
|
1121
1443
|
if (!controls) return false;
|
|
1122
|
-
const target = controls.getTarget(new
|
|
1123
|
-
return frame(currentViewDirection(camera, target, new
|
|
1444
|
+
const target = controls.getTarget(new Vector36());
|
|
1445
|
+
return frame(currentViewDirection(camera, target, new Vector36()), true);
|
|
1124
1446
|
}, [camera, frame]);
|
|
1125
1447
|
const resetContent = useCallback(() => {
|
|
1126
1448
|
const bounds = measure();
|
|
1127
1449
|
const start = startPosition(projection, size, bounds).sub(bounds.center);
|
|
1128
1450
|
return frame(start, true);
|
|
1129
1451
|
}, [frame, measure, projection, size]);
|
|
1452
|
+
useEffect3(() => {
|
|
1453
|
+
if (!recentreOnDoubleClick) return void 0;
|
|
1454
|
+
const doubles = trackDoubleTaps();
|
|
1455
|
+
const recentre = (event) => {
|
|
1456
|
+
if (event.button !== 1) return;
|
|
1457
|
+
if (doubles.isDouble(event)) fitContent();
|
|
1458
|
+
};
|
|
1459
|
+
domElement.addEventListener("auxclick", recentre);
|
|
1460
|
+
return () => domElement.removeEventListener("auxclick", recentre);
|
|
1461
|
+
}, [domElement, fitContent, recentreOnDoubleClick]);
|
|
1130
1462
|
useEffect3(() => {
|
|
1131
1463
|
setControls({
|
|
1132
1464
|
fit: () => {
|
|
@@ -1136,16 +1468,20 @@ var ViewerScene = ({
|
|
|
1136
1468
|
resetContent();
|
|
1137
1469
|
},
|
|
1138
1470
|
setView: (view) => {
|
|
1139
|
-
frame(cadViewDirections[view], true);
|
|
1471
|
+
frame(cadViewDirections[view], true, squaredUp(cadViewDirections[view], camera.up));
|
|
1140
1472
|
},
|
|
1141
1473
|
setViewDirection: (direction) => {
|
|
1142
|
-
frame(
|
|
1474
|
+
frame(
|
|
1475
|
+
scratchView.set(direction.x, direction.y, direction.z),
|
|
1476
|
+
true,
|
|
1477
|
+
squaredUp(direction, camera.up)
|
|
1478
|
+
);
|
|
1143
1479
|
},
|
|
1144
1480
|
frameBox: (box) => {
|
|
1145
1481
|
frameBounds(boundsFromBox(box));
|
|
1146
1482
|
}
|
|
1147
1483
|
});
|
|
1148
|
-
}, [fitContent, frame, frameBounds, resetContent, scratchView, setControls]);
|
|
1484
|
+
}, [camera, fitContent, frame, frameBounds, resetContent, scratchView, setControls]);
|
|
1149
1485
|
useEffect3(() => {
|
|
1150
1486
|
if (initialFrameComplete.current) resetContent();
|
|
1151
1487
|
}, [projection]);
|
|
@@ -1176,7 +1512,15 @@ var ViewerScene = ({
|
|
|
1176
1512
|
)
|
|
1177
1513
|
] }),
|
|
1178
1514
|
/* @__PURE__ */ jsx("group", { ref: contentRef, children }),
|
|
1179
|
-
/* @__PURE__ */ jsx(
|
|
1515
|
+
/* @__PURE__ */ jsx(
|
|
1516
|
+
CadCameraControls,
|
|
1517
|
+
{
|
|
1518
|
+
controlsRef,
|
|
1519
|
+
scheme,
|
|
1520
|
+
freeOrbit,
|
|
1521
|
+
zoomTo
|
|
1522
|
+
}
|
|
1523
|
+
)
|
|
1180
1524
|
] });
|
|
1181
1525
|
};
|
|
1182
1526
|
var Viewer = forwardRef(function Viewer2({
|
|
@@ -1186,6 +1530,8 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
1186
1530
|
projection = "perspective",
|
|
1187
1531
|
controls = "toolpath",
|
|
1188
1532
|
freeOrbit = true,
|
|
1533
|
+
zoomTo = "cursor",
|
|
1534
|
+
recentreOnDoubleClick = true,
|
|
1189
1535
|
theme,
|
|
1190
1536
|
onPointerMissed
|
|
1191
1537
|
}, ref) {
|
|
@@ -1210,32 +1556,45 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
1210
1556
|
tracker.current?.dispose();
|
|
1211
1557
|
tracker.current = element ? trackTaps(element) : null;
|
|
1212
1558
|
}, []);
|
|
1213
|
-
return /* @__PURE__ */ jsx(ViewerControlsContext.Provider, { value: proxy, children: /* @__PURE__ */ jsx(
|
|
1214
|
-
|
|
1559
|
+
return /* @__PURE__ */ jsx(ViewerControlsContext.Provider, { value: proxy, children: /* @__PURE__ */ jsx(
|
|
1560
|
+
"div",
|
|
1215
1561
|
{
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
gl: { antialias: true, alpha: true, stencil: true, localClippingEnabled: true },
|
|
1221
|
-
onPointerMissed: (event) => {
|
|
1222
|
-
if (event.button !== 0) return;
|
|
1223
|
-
if (tracker.current?.isTap(event) ?? true) onPointerMissed?.();
|
|
1562
|
+
className,
|
|
1563
|
+
ref: hold,
|
|
1564
|
+
onMouseDown: (event) => {
|
|
1565
|
+
if (event.target instanceof HTMLCanvasElement) event.preventDefault();
|
|
1224
1566
|
},
|
|
1567
|
+
style: { height: "100%", width: "100%", ...style },
|
|
1225
1568
|
children: /* @__PURE__ */ jsx(
|
|
1226
|
-
|
|
1569
|
+
Canvas,
|
|
1227
1570
|
{
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1571
|
+
orthographic: projection === "orthographic",
|
|
1572
|
+
camera: { fov: PERSPECTIVE_FOV, up: [0, 0, 1], position: [1, -1, 1] },
|
|
1573
|
+
dpr: [1, 2],
|
|
1574
|
+
frameloop: "demand",
|
|
1575
|
+
gl: { antialias: true, alpha: true, stencil: true, localClippingEnabled: true },
|
|
1576
|
+
onPointerMissed: (event) => {
|
|
1577
|
+
if (event.button !== 0) return;
|
|
1578
|
+
if (tracker.current?.isTap(event) ?? true) onPointerMissed?.();
|
|
1579
|
+
},
|
|
1580
|
+
children: /* @__PURE__ */ jsx(
|
|
1581
|
+
ViewerScene,
|
|
1582
|
+
{
|
|
1583
|
+
setControls,
|
|
1584
|
+
projection,
|
|
1585
|
+
scheme: controls,
|
|
1586
|
+
freeOrbit,
|
|
1587
|
+
zoomTo,
|
|
1588
|
+
recentreOnDoubleClick,
|
|
1589
|
+
theme: resolved,
|
|
1590
|
+
children
|
|
1591
|
+
}
|
|
1592
|
+
)
|
|
1593
|
+
},
|
|
1594
|
+
projection
|
|
1235
1595
|
)
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
) }) });
|
|
1596
|
+
}
|
|
1597
|
+
) });
|
|
1239
1598
|
});
|
|
1240
1599
|
|
|
1241
1600
|
// src/section-view.tsx
|
|
@@ -1254,22 +1613,22 @@ import {
|
|
|
1254
1613
|
Raycaster as Raycaster2,
|
|
1255
1614
|
ReplaceStencilOp,
|
|
1256
1615
|
Vector2 as Vector22,
|
|
1257
|
-
Vector3 as
|
|
1616
|
+
Vector3 as Vector38
|
|
1258
1617
|
} from "three";
|
|
1259
1618
|
|
|
1260
1619
|
// src/render/directions.ts
|
|
1261
|
-
import { Vector3 as
|
|
1262
|
-
var CONE_AXIS = new
|
|
1620
|
+
import { Vector3 as Vector37 } from "three";
|
|
1621
|
+
var CONE_AXIS = new Vector37(0, 1, 0);
|
|
1263
1622
|
var LENGTH = 0.45;
|
|
1264
1623
|
var HEAD = 0.45;
|
|
1265
1624
|
var HEAD_RADIUS = 0.3;
|
|
1266
1625
|
var SHAFT_RADIUS = 0.09;
|
|
1267
1626
|
var GAP = 0.2;
|
|
1268
1627
|
function arrowPlacement(direction, box) {
|
|
1269
|
-
const center = box.getCenter(new
|
|
1270
|
-
const half = box.getSize(new
|
|
1628
|
+
const center = box.getCenter(new Vector37());
|
|
1629
|
+
const half = box.getSize(new Vector37()).multiplyScalar(0.5);
|
|
1271
1630
|
const radius = half.length() || 1;
|
|
1272
|
-
const axis = new
|
|
1631
|
+
const axis = new Vector37(direction.x, direction.y, direction.z);
|
|
1273
1632
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
1274
1633
|
axis.normalize();
|
|
1275
1634
|
let exit = Number.POSITIVE_INFINITY;
|
|
@@ -1291,7 +1650,7 @@ var FURNITURE = { [EXCLUDE_FROM_FRAME]: true };
|
|
|
1291
1650
|
function resolveSectionPlane(options, box) {
|
|
1292
1651
|
if (!options?.enabled || box.isEmpty()) return null;
|
|
1293
1652
|
const normal = options.plane?.normal ?? options.normal ?? DEFAULT_NORMAL;
|
|
1294
|
-
const axis = new
|
|
1653
|
+
const axis = new Vector38(normal.x, normal.y, normal.z);
|
|
1295
1654
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
1296
1655
|
axis.normalize();
|
|
1297
1656
|
const bounds = sectionBounds(box, normal);
|
|
@@ -1328,15 +1687,15 @@ var SectionView = ({
|
|
|
1328
1687
|
const handleRef = useRef3(null);
|
|
1329
1688
|
const [hovered, setHovered] = useState(false);
|
|
1330
1689
|
const dragging = useRef3(null);
|
|
1331
|
-
const centre = useMemo4(() => box.getCenter(new
|
|
1332
|
-
const span = useMemo4(() => box.getSize(new
|
|
1690
|
+
const centre = useMemo4(() => box.getCenter(new Vector38()), [box]);
|
|
1691
|
+
const span = useMemo4(() => box.getSize(new Vector38()).length(), [box]);
|
|
1333
1692
|
const clip = useMemo4(() => [plane], [plane]);
|
|
1334
1693
|
const capPosition = useMemo4(() => {
|
|
1335
1694
|
const point = centre.clone();
|
|
1336
1695
|
return point.addScaledVector(plane.normal, -(plane.constant + plane.normal.dot(centre)));
|
|
1337
1696
|
}, [centre, plane]);
|
|
1338
1697
|
const capQuaternion = useMemo4(
|
|
1339
|
-
() => new Quaternion2().setFromUnitVectors(new
|
|
1698
|
+
() => new Quaternion2().setFromUnitVectors(new Vector38(0, 0, 1), plane.normal),
|
|
1340
1699
|
[plane]
|
|
1341
1700
|
);
|
|
1342
1701
|
const handleQuaternion = useMemo4(
|
|
@@ -1364,7 +1723,7 @@ var SectionView = ({
|
|
|
1364
1723
|
event.stopPropagation();
|
|
1365
1724
|
const view = camera.position.clone().sub(capPosition).normalize();
|
|
1366
1725
|
const surface = dragPlane(plane.normal.clone(), view, capPosition);
|
|
1367
|
-
const hit = event.ray.intersectPlane(surface, new
|
|
1726
|
+
const hit = event.ray.intersectPlane(surface, new Vector38());
|
|
1368
1727
|
const start = {
|
|
1369
1728
|
plane: surface,
|
|
1370
1729
|
from: hit ? hit.dot(plane.normal) : 0,
|
|
@@ -1381,7 +1740,7 @@ var SectionView = ({
|
|
|
1381
1740
|
-((native.clientY - rect.top) / rect.height) * 2 + 1
|
|
1382
1741
|
);
|
|
1383
1742
|
raycaster.setFromCamera(pointer, camera);
|
|
1384
|
-
const point = raycaster.ray.intersectPlane(start.plane, new
|
|
1743
|
+
const point = raycaster.ray.intersectPlane(start.plane, new Vector38());
|
|
1385
1744
|
if (!point) return;
|
|
1386
1745
|
onDrag(start.constant - (point.dot(plane.normal) - start.from));
|
|
1387
1746
|
invalidate();
|
|
@@ -1528,6 +1887,7 @@ var PartMesh = ({
|
|
|
1528
1887
|
activeDirection = null,
|
|
1529
1888
|
section,
|
|
1530
1889
|
onSectionChange,
|
|
1890
|
+
onAdjacency,
|
|
1531
1891
|
focusFeature = null,
|
|
1532
1892
|
onHover,
|
|
1533
1893
|
onPick,
|
|
@@ -1569,6 +1929,10 @@ var PartMesh = ({
|
|
|
1569
1929
|
invalidate();
|
|
1570
1930
|
}, [invalidate, part]);
|
|
1571
1931
|
useEffect4(() => () => part.dispose(), [part]);
|
|
1932
|
+
const adjacency = useMemo5(() => regionAdjacency(model, geometry), [model, geometry]);
|
|
1933
|
+
useEffect4(() => {
|
|
1934
|
+
onAdjacency?.(adjacency);
|
|
1935
|
+
}, [adjacency, onAdjacency]);
|
|
1572
1936
|
const framed = useRef5(null);
|
|
1573
1937
|
useEffect4(() => {
|
|
1574
1938
|
if (focusFeature === null || focusFeature === framed.current) return;
|
|
@@ -1664,6 +2028,15 @@ var PartMesh = ({
|
|
|
1664
2028
|
if (!isTap(event.nativeEvent)) return;
|
|
1665
2029
|
const pick = pickFor(event);
|
|
1666
2030
|
if (pick) onPick?.(pick);
|
|
2031
|
+
},
|
|
2032
|
+
onPointerUp: (event) => {
|
|
2033
|
+
if (event.nativeEvent.button !== 2) return;
|
|
2034
|
+
if (!isTap(event.nativeEvent)) return;
|
|
2035
|
+
const pick = pickFor(event);
|
|
2036
|
+
if (pick) onPick?.(pick);
|
|
2037
|
+
},
|
|
2038
|
+
onContextMenu: (event) => {
|
|
2039
|
+
if (pickFor(event)) event.nativeEvent.preventDefault();
|
|
1667
2040
|
}
|
|
1668
2041
|
}
|
|
1669
2042
|
),
|
|
@@ -1680,9 +2053,9 @@ var PartMesh = ({
|
|
|
1680
2053
|
) : null
|
|
1681
2054
|
] });
|
|
1682
2055
|
};
|
|
1683
|
-
var ORIGIN = new
|
|
1684
|
-
var UP = new
|
|
1685
|
-
var TARGET = new
|
|
2056
|
+
var ORIGIN = new Vector39();
|
|
2057
|
+
var UP = new Vector39(0, 0, 1);
|
|
2058
|
+
var TARGET = new Vector39();
|
|
1686
2059
|
function readTarget(controls) {
|
|
1687
2060
|
if (controls && typeof controls.getTarget === "function") {
|
|
1688
2061
|
return controls.getTarget(TARGET);
|
|
@@ -1817,7 +2190,7 @@ function disposeMaterials(scene) {
|
|
|
1817
2190
|
}
|
|
1818
2191
|
|
|
1819
2192
|
// src/engine/normals.ts
|
|
1820
|
-
import { Float32BufferAttribute as
|
|
2193
|
+
import { Float32BufferAttribute as Float32BufferAttribute4 } from "three";
|
|
1821
2194
|
function smoothRegionNormals(geometry, regions) {
|
|
1822
2195
|
const position = geometry.getAttribute("position");
|
|
1823
2196
|
if (!position || geometry.index) return;
|
|
@@ -1883,7 +2256,7 @@ function smoothRegionNormals(geometry, regions) {
|
|
|
1883
2256
|
normals[vertex * 3 + 1] = y / length;
|
|
1884
2257
|
normals[vertex * 3 + 2] = z / length;
|
|
1885
2258
|
}
|
|
1886
|
-
geometry.setAttribute("normal", new
|
|
2259
|
+
geometry.setAttribute("normal", new Float32BufferAttribute4(normals, 3));
|
|
1887
2260
|
}
|
|
1888
2261
|
|
|
1889
2262
|
// src/engine/geometry-cache.ts
|
|
@@ -2250,12 +2623,12 @@ function readDirections(value, issues) {
|
|
|
2250
2623
|
}
|
|
2251
2624
|
const directions = [];
|
|
2252
2625
|
for (const [i, raw] of value.entries()) {
|
|
2253
|
-
const
|
|
2254
|
-
if (
|
|
2626
|
+
const vec2 = readVec3(raw);
|
|
2627
|
+
if (vec2 === null) {
|
|
2255
2628
|
issues.push(`candidateDirections[${i}] is not a vector`);
|
|
2256
2629
|
continue;
|
|
2257
2630
|
}
|
|
2258
|
-
directions.push(
|
|
2631
|
+
directions.push(vec2);
|
|
2259
2632
|
}
|
|
2260
2633
|
return directions;
|
|
2261
2634
|
}
|
|
@@ -2355,6 +2728,8 @@ export {
|
|
|
2355
2728
|
TAP_SLOP,
|
|
2356
2729
|
movedFar,
|
|
2357
2730
|
trackTaps,
|
|
2731
|
+
DOUBLE_TAP_MS,
|
|
2732
|
+
trackDoubleTaps,
|
|
2358
2733
|
useTapGuard,
|
|
2359
2734
|
visualSurfaces,
|
|
2360
2735
|
regionEdgesGeometry,
|
|
@@ -2362,6 +2737,7 @@ export {
|
|
|
2362
2737
|
buildRegionTexels,
|
|
2363
2738
|
buildRegionAttribute,
|
|
2364
2739
|
createPart,
|
|
2740
|
+
regionAdjacency,
|
|
2365
2741
|
FEATURE_TYPE_RANKS,
|
|
2366
2742
|
featureTypeRank,
|
|
2367
2743
|
rankOwners,
|
|
@@ -2388,6 +2764,18 @@ export {
|
|
|
2388
2764
|
currentViewDirection,
|
|
2389
2765
|
ExtendedCameraControls,
|
|
2390
2766
|
CadCameraControls,
|
|
2767
|
+
VIEW_NAMES,
|
|
2768
|
+
VIEW_SIGNS,
|
|
2769
|
+
viewKind,
|
|
2770
|
+
viewVector,
|
|
2771
|
+
viewUp,
|
|
2772
|
+
squaredUp,
|
|
2773
|
+
CHAMFER,
|
|
2774
|
+
cubeZones,
|
|
2775
|
+
panelGeometry,
|
|
2776
|
+
cubeOutlineGeometry,
|
|
2777
|
+
labelGeometry,
|
|
2778
|
+
labelTexture,
|
|
2391
2779
|
useViewerControls,
|
|
2392
2780
|
Viewer,
|
|
2393
2781
|
CONE_AXIS,
|