@toolpath/viewer 2.2.1 → 2.3.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 +110 -43
- package/dist/{chunk-7S6SIYHL.js → chunk-N54FHMTG.js} +753 -178
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +7 -205
- package/dist/index.js +49 -28
- package/dist/{normalize--FiXUhO4.d.ts → normalize-C4Wj4IGK.d.ts} +333 -3
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useRef as useRef6,
|
|
12
12
|
useSyncExternalStore
|
|
13
13
|
} from "react";
|
|
14
|
-
import { Vector3 as
|
|
14
|
+
import { Vector3 as Vector312 } from "three";
|
|
15
15
|
|
|
16
16
|
// src/part-context.tsx
|
|
17
17
|
import { createContext, useContext } from "react";
|
|
@@ -136,6 +136,8 @@ var DEFAULT_THEME = {
|
|
|
136
136
|
picked: 16347926,
|
|
137
137
|
edge: 0,
|
|
138
138
|
edgeOpacity: 0.5,
|
|
139
|
+
// Red, which the direction palette's note keeps free for sharp corners.
|
|
140
|
+
sharpCorner: 14427686,
|
|
139
141
|
sectionCap: 14672618,
|
|
140
142
|
sectionHatch: 9080998,
|
|
141
143
|
sectionOutline: 7057587,
|
|
@@ -157,7 +159,7 @@ function resolveTheme(overrides) {
|
|
|
157
159
|
return { ...DEFAULT_THEME, ...overrides };
|
|
158
160
|
}
|
|
159
161
|
function themesEqual(a, b) {
|
|
160
|
-
return a.background === b.background && a.hemisphereSky === b.hemisphereSky && a.hemisphereGround === b.hemisphereGround && a.hemisphereIntensity === b.hemisphereIntensity && a.ambient === b.ambient && a.ambientIntensity === b.ambientIntensity && a.part === b.part && a.partEmissive === b.partEmissive && a.hover === b.hover && a.highlight === b.highlight && a.picked === b.picked && a.edge === b.edge && a.edgeOpacity === b.edgeOpacity && a.sectionCap === b.sectionCap && a.sectionHatch === b.sectionHatch && a.sectionOutline === b.sectionOutline && a.sectionHandle === b.sectionHandle && a.sectionHandleOutline === b.sectionHandleOutline && a.measure === b.measure && a.measureSnap === b.measureSnap && a.cube === b.cube && a.cubeEdge === b.cubeEdge && a.cubeLabel === b.cubeLabel;
|
|
162
|
+
return a.background === b.background && a.hemisphereSky === b.hemisphereSky && a.hemisphereGround === b.hemisphereGround && a.hemisphereIntensity === b.hemisphereIntensity && a.ambient === b.ambient && a.ambientIntensity === b.ambientIntensity && a.part === b.part && a.partEmissive === b.partEmissive && a.hover === b.hover && a.highlight === b.highlight && a.picked === b.picked && a.edge === b.edge && a.edgeOpacity === b.edgeOpacity && a.sharpCorner === b.sharpCorner && a.sectionCap === b.sectionCap && a.sectionHatch === b.sectionHatch && a.sectionOutline === b.sectionOutline && a.sectionHandle === b.sectionHandle && a.sectionHandleOutline === b.sectionHandleOutline && a.measure === b.measure && a.measureSnap === b.measureSnap && a.cube === b.cube && a.cubeEdge === b.cubeEdge && a.cubeLabel === b.cubeLabel;
|
|
161
163
|
}
|
|
162
164
|
function directionColor(index) {
|
|
163
165
|
const wrapped = index % DIRECTION_COLORS.length;
|
|
@@ -857,19 +859,19 @@ function useTapGuard() {
|
|
|
857
859
|
// src/render/part.ts
|
|
858
860
|
import {
|
|
859
861
|
Box3,
|
|
860
|
-
Color,
|
|
862
|
+
Color as Color2,
|
|
861
863
|
DataTexture,
|
|
862
|
-
DoubleSide,
|
|
863
|
-
Float32BufferAttribute as
|
|
864
|
-
Group,
|
|
864
|
+
DoubleSide as DoubleSide2,
|
|
865
|
+
Float32BufferAttribute as Float32BufferAttribute3,
|
|
866
|
+
Group as Group2,
|
|
865
867
|
LineBasicMaterial,
|
|
866
868
|
LineSegments,
|
|
867
|
-
Mesh,
|
|
869
|
+
Mesh as Mesh2,
|
|
868
870
|
MeshLambertMaterial,
|
|
869
871
|
RedFormat,
|
|
870
872
|
RGBAFormat,
|
|
871
873
|
UnsignedByteType,
|
|
872
|
-
Vector3 as
|
|
874
|
+
Vector3 as Vector35
|
|
873
875
|
} from "three";
|
|
874
876
|
|
|
875
877
|
// src/render/edges.ts
|
|
@@ -937,6 +939,555 @@ function regionEdgesGeometry(geometry, model) {
|
|
|
937
939
|
return edges;
|
|
938
940
|
}
|
|
939
941
|
|
|
942
|
+
// src/render/sharp-corners.ts
|
|
943
|
+
import {
|
|
944
|
+
BufferAttribute,
|
|
945
|
+
BufferGeometry,
|
|
946
|
+
Color,
|
|
947
|
+
DoubleSide,
|
|
948
|
+
Float32BufferAttribute as Float32BufferAttribute2,
|
|
949
|
+
GreaterDepth,
|
|
950
|
+
Group,
|
|
951
|
+
KeepStencilOp,
|
|
952
|
+
LessEqualDepth,
|
|
953
|
+
Mesh,
|
|
954
|
+
MeshBasicMaterial,
|
|
955
|
+
NotEqualStencilFunc,
|
|
956
|
+
ReplaceStencilOp,
|
|
957
|
+
ShaderMaterial,
|
|
958
|
+
Vector2 as Vector22,
|
|
959
|
+
Vector3 as Vector34,
|
|
960
|
+
Vector4
|
|
961
|
+
} from "three";
|
|
962
|
+
|
|
963
|
+
// src/render/adjacency.ts
|
|
964
|
+
var GRID = 1e-4;
|
|
965
|
+
var keyFor = (x, y, z) => `${Math.round(x / GRID)},${Math.round(y / GRID)},${Math.round(z / GRID)}`;
|
|
966
|
+
function regionAdjacency(model, geometry) {
|
|
967
|
+
const position = geometry.getAttribute("position");
|
|
968
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
969
|
+
if (!position) return adjacency;
|
|
970
|
+
const owner = new Int32Array(Math.floor(position.count / 3)).fill(-1);
|
|
971
|
+
for (const region of model.regions) {
|
|
972
|
+
for (let at = region.triangles.start; at < region.triangles.end; at++) {
|
|
973
|
+
if (at >= 0 && at < owner.length) owner[at] = region.idx;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
const edges = /* @__PURE__ */ new Map();
|
|
977
|
+
const join = (a, b) => {
|
|
978
|
+
if (a === b) return;
|
|
979
|
+
const mine = adjacency.get(a) ?? /* @__PURE__ */ new Set();
|
|
980
|
+
mine.add(b);
|
|
981
|
+
adjacency.set(a, mine);
|
|
982
|
+
const theirs = adjacency.get(b) ?? /* @__PURE__ */ new Set();
|
|
983
|
+
theirs.add(a);
|
|
984
|
+
adjacency.set(b, theirs);
|
|
985
|
+
};
|
|
986
|
+
for (let triangle = 0; triangle < owner.length; triangle++) {
|
|
987
|
+
const region = owner[triangle];
|
|
988
|
+
if (region === -1) continue;
|
|
989
|
+
const corners = [0, 1, 2].map((corner) => {
|
|
990
|
+
const at = triangle * 3 + corner;
|
|
991
|
+
return keyFor(position.getX(at), position.getY(at), position.getZ(at));
|
|
992
|
+
});
|
|
993
|
+
for (let corner = 0; corner < 3; corner++) {
|
|
994
|
+
const from = corners[corner];
|
|
995
|
+
const to = corners[(corner + 1) % 3];
|
|
996
|
+
const edge = from < to ? `${from}|${to}` : `${to}|${from}`;
|
|
997
|
+
const seen = edges.get(edge);
|
|
998
|
+
if (seen === void 0) edges.set(edge, region);
|
|
999
|
+
else join(seen, region);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return adjacency;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/render/sharp-corners.ts
|
|
1006
|
+
var MIN_FOLD_DEGREES = 20;
|
|
1007
|
+
var ALONG_TOLERANCE_DEGREES = 15;
|
|
1008
|
+
var BAND_WIDTH = 6;
|
|
1009
|
+
var NEAR_OPACITY = 1;
|
|
1010
|
+
var FAR_OPACITY = 0.35;
|
|
1011
|
+
var DEGREES = 180 / Math.PI;
|
|
1012
|
+
function surfaceCreases(geometry, model) {
|
|
1013
|
+
const position = geometry.getAttribute("position");
|
|
1014
|
+
if (!position || geometry.index) return emptyCreases();
|
|
1015
|
+
const triangleCount = Math.floor(position.count / 3);
|
|
1016
|
+
const surfaces = visualSurfaces(model.regions);
|
|
1017
|
+
const surfaceOf = new Int32Array(triangleCount).fill(-1);
|
|
1018
|
+
const regionOf = new Int32Array(triangleCount).fill(-1);
|
|
1019
|
+
for (const region of model.regions) {
|
|
1020
|
+
const end = Math.min(region.triangles.end, triangleCount);
|
|
1021
|
+
for (let triangle = Math.max(region.triangles.start, 0); triangle < end; triangle += 1) {
|
|
1022
|
+
surfaceOf[triangle] = surfaces.get(region.idx) ?? region.idx;
|
|
1023
|
+
regionOf[triangle] = region.idx;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
const normals = new Float32Array(triangleCount * 3);
|
|
1027
|
+
for (let triangle = 0; triangle < triangleCount; triangle += 1) {
|
|
1028
|
+
const at = triangle * 3;
|
|
1029
|
+
const ax = position.getX(at);
|
|
1030
|
+
const ay = position.getY(at);
|
|
1031
|
+
const az = position.getZ(at);
|
|
1032
|
+
const ux = position.getX(at + 1) - ax;
|
|
1033
|
+
const uy = position.getY(at + 1) - ay;
|
|
1034
|
+
const uz = position.getZ(at + 1) - az;
|
|
1035
|
+
const vx = position.getX(at + 2) - ax;
|
|
1036
|
+
const vy = position.getY(at + 2) - ay;
|
|
1037
|
+
const vz = position.getZ(at + 2) - az;
|
|
1038
|
+
const nx = uy * vz - uz * vy;
|
|
1039
|
+
const ny = uz * vx - ux * vz;
|
|
1040
|
+
const nz = ux * vy - uy * vx;
|
|
1041
|
+
const length = Math.hypot(nx, ny, nz);
|
|
1042
|
+
if (length === 0) continue;
|
|
1043
|
+
normals[at] = nx / length;
|
|
1044
|
+
normals[at + 1] = ny / length;
|
|
1045
|
+
normals[at + 2] = nz / length;
|
|
1046
|
+
}
|
|
1047
|
+
const keyOf = (vertex) => keyFor(position.getX(vertex), position.getY(vertex), position.getZ(vertex));
|
|
1048
|
+
const open = /* @__PURE__ */ new Map();
|
|
1049
|
+
const positions = [];
|
|
1050
|
+
const angles = [];
|
|
1051
|
+
const concave = [];
|
|
1052
|
+
const regions = [];
|
|
1053
|
+
const fars = [];
|
|
1054
|
+
const inFront = (triangle, vertex, on) => normals[triangle * 3] * (position.getX(vertex) - position.getX(on)) + normals[triangle * 3 + 1] * (position.getY(vertex) - position.getY(on)) + normals[triangle * 3 + 2] * (position.getZ(vertex) - position.getZ(on));
|
|
1055
|
+
const measure = (first, second) => {
|
|
1056
|
+
const t1 = Math.floor(first / 3);
|
|
1057
|
+
const t2 = Math.floor(second / 3);
|
|
1058
|
+
if (surfaceOf[t1] === surfaceOf[t2]) return;
|
|
1059
|
+
if (normals[t1 * 3] === 0 && normals[t1 * 3 + 1] === 0 && normals[t1 * 3 + 2] === 0) return;
|
|
1060
|
+
if (normals[t2 * 3] === 0 && normals[t2 * 3 + 1] === 0 && normals[t2 * 3 + 2] === 0) return;
|
|
1061
|
+
const dot2 = normals[t1 * 3] * normals[t2 * 3] + normals[t1 * 3 + 1] * normals[t2 * 3 + 1] + normals[t1 * 3 + 2] * normals[t2 * 3 + 2];
|
|
1062
|
+
const a = t1 * 3 + first % 3;
|
|
1063
|
+
const b = t1 * 3 + (first + 1) % 3;
|
|
1064
|
+
const far1 = t1 * 3 + (first + 2) % 3;
|
|
1065
|
+
const far2 = t2 * 3 + (second + 2) % 3;
|
|
1066
|
+
const fold = inFront(t1, far2, a) + inFront(t2, far1, a);
|
|
1067
|
+
positions.push(
|
|
1068
|
+
position.getX(a),
|
|
1069
|
+
position.getY(a),
|
|
1070
|
+
position.getZ(a),
|
|
1071
|
+
position.getX(b),
|
|
1072
|
+
position.getY(b),
|
|
1073
|
+
position.getZ(b)
|
|
1074
|
+
);
|
|
1075
|
+
angles.push(Math.acos(Math.min(1, Math.max(-1, dot2))) * DEGREES);
|
|
1076
|
+
concave.push(fold > 0 ? 1 : 0);
|
|
1077
|
+
regions.push(regionOf[t1], regionOf[t2]);
|
|
1078
|
+
fars.push(
|
|
1079
|
+
position.getX(far1),
|
|
1080
|
+
position.getY(far1),
|
|
1081
|
+
position.getZ(far1),
|
|
1082
|
+
position.getX(far2),
|
|
1083
|
+
position.getY(far2),
|
|
1084
|
+
position.getZ(far2)
|
|
1085
|
+
);
|
|
1086
|
+
};
|
|
1087
|
+
for (let triangle = 0; triangle < triangleCount; triangle += 1) {
|
|
1088
|
+
const keys = [keyOf(triangle * 3), keyOf(triangle * 3 + 1), keyOf(triangle * 3 + 2)];
|
|
1089
|
+
for (let corner = 0; corner < 3; corner += 1) {
|
|
1090
|
+
const from = keys[corner];
|
|
1091
|
+
const to = keys[(corner + 1) % 3];
|
|
1092
|
+
if (from === to) continue;
|
|
1093
|
+
const edge = from < to ? `${from}|${to}` : `${to}|${from}`;
|
|
1094
|
+
const seen = open.get(edge);
|
|
1095
|
+
if (seen === void 0) open.set(edge, triangle * 3 + corner);
|
|
1096
|
+
else if (seen !== -1) {
|
|
1097
|
+
measure(seen, triangle * 3 + corner);
|
|
1098
|
+
open.set(edge, -1);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return {
|
|
1103
|
+
count: angles.length,
|
|
1104
|
+
positions: new Float32Array(positions),
|
|
1105
|
+
angles: new Float32Array(angles),
|
|
1106
|
+
concave: new Uint8Array(concave),
|
|
1107
|
+
regions: new Int32Array(regions),
|
|
1108
|
+
fars: new Float32Array(fars)
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
function sharpCornerKey(options) {
|
|
1112
|
+
return options.features.map((tag) => tag.toLowerCase()).sort().join(",");
|
|
1113
|
+
}
|
|
1114
|
+
function findSharpCorners(creases, model, options) {
|
|
1115
|
+
const alongCos = Math.cos(ALONG_TOLERANCE_DEGREES / DEGREES);
|
|
1116
|
+
const flagged = new Set(options.features.map((tag) => tag.toLowerCase()));
|
|
1117
|
+
const owners = /* @__PURE__ */ new Map();
|
|
1118
|
+
for (const feature of model.features) {
|
|
1119
|
+
if (!flagged.has(feature.tag.toLowerCase())) continue;
|
|
1120
|
+
const axis = unit2(feature.machiningDirection);
|
|
1121
|
+
if (!axis) continue;
|
|
1122
|
+
for (const region of model.regionIndex.regionsForFeature(feature.tag)) {
|
|
1123
|
+
const held = owners.get(region);
|
|
1124
|
+
if (held) held.push({ tag: feature.tag, axis });
|
|
1125
|
+
else owners.set(region, [{ tag: feature.tag, axis }]);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (owners.size === 0) return [];
|
|
1129
|
+
const { positions } = creases;
|
|
1130
|
+
const kept = [];
|
|
1131
|
+
for (let segment = 0; segment < creases.count; segment += 1) {
|
|
1132
|
+
if (creases.concave[segment] !== 1 || creases.angles[segment] < MIN_FOLD_DEGREES) continue;
|
|
1133
|
+
const candidates = [
|
|
1134
|
+
...owners.get(creases.regions[segment * 2]) ?? [],
|
|
1135
|
+
...owners.get(creases.regions[segment * 2 + 1]) ?? []
|
|
1136
|
+
];
|
|
1137
|
+
if (candidates.length === 0) continue;
|
|
1138
|
+
const at = segment * 6;
|
|
1139
|
+
const dx = positions[at + 3] - positions[at];
|
|
1140
|
+
const dy = positions[at + 4] - positions[at + 1];
|
|
1141
|
+
const dz = positions[at + 5] - positions[at + 2];
|
|
1142
|
+
const length = Math.hypot(dx, dy, dz);
|
|
1143
|
+
if (length === 0) continue;
|
|
1144
|
+
const owner = candidates.find(
|
|
1145
|
+
({ axis }) => Math.abs(dx * axis.x + dy * axis.y + dz * axis.z) / length >= alongCos
|
|
1146
|
+
);
|
|
1147
|
+
if (owner) kept.push({ segment, feature: owner.tag });
|
|
1148
|
+
}
|
|
1149
|
+
const keyOf = (at) => keyFor(positions[at], positions[at + 1], positions[at + 2]);
|
|
1150
|
+
const parent = kept.map((_, at) => at);
|
|
1151
|
+
const root = (at) => {
|
|
1152
|
+
while (parent[at] !== at) at = parent[at] = parent[parent[at]];
|
|
1153
|
+
return at;
|
|
1154
|
+
};
|
|
1155
|
+
const byEnd = /* @__PURE__ */ new Map();
|
|
1156
|
+
kept.forEach(({ segment, feature }, at) => {
|
|
1157
|
+
for (const end of [segment * 6, segment * 6 + 3]) {
|
|
1158
|
+
const key = `${feature.toLowerCase()}|${keyOf(end)}`;
|
|
1159
|
+
const other = byEnd.get(key);
|
|
1160
|
+
if (other === void 0) byEnd.set(key, at);
|
|
1161
|
+
else parent[root(at)] = root(other);
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1165
|
+
kept.forEach((_, at) => {
|
|
1166
|
+
const group = groups.get(root(at));
|
|
1167
|
+
if (group) group.push(at);
|
|
1168
|
+
else groups.set(root(at), [at]);
|
|
1169
|
+
});
|
|
1170
|
+
return [...groups.values()].map((members, index) => {
|
|
1171
|
+
const feature = kept[members[0]].feature;
|
|
1172
|
+
const segments = members.map((at) => kept[at].segment);
|
|
1173
|
+
const first = segments[0] * 6;
|
|
1174
|
+
const axis = unit2({
|
|
1175
|
+
x: positions[first + 3] - positions[first],
|
|
1176
|
+
y: positions[first + 4] - positions[first + 1],
|
|
1177
|
+
z: positions[first + 5] - positions[first + 2]
|
|
1178
|
+
});
|
|
1179
|
+
let start = null;
|
|
1180
|
+
let end = null;
|
|
1181
|
+
let low = Infinity;
|
|
1182
|
+
let high = -Infinity;
|
|
1183
|
+
let length = 0;
|
|
1184
|
+
let folded = 0;
|
|
1185
|
+
const regions = /* @__PURE__ */ new Set();
|
|
1186
|
+
for (const segment of segments) {
|
|
1187
|
+
const at = segment * 6;
|
|
1188
|
+
for (const point of [at, at + 3]) {
|
|
1189
|
+
const p = { x: positions[point], y: positions[point + 1], z: positions[point + 2] };
|
|
1190
|
+
const along = p.x * axis.x + p.y * axis.y + p.z * axis.z;
|
|
1191
|
+
if (along < low) [low, start] = [along, p];
|
|
1192
|
+
if (along > high) [high, end] = [along, p];
|
|
1193
|
+
}
|
|
1194
|
+
const run = Math.hypot(
|
|
1195
|
+
positions[at + 3] - positions[at],
|
|
1196
|
+
positions[at + 4] - positions[at + 1],
|
|
1197
|
+
positions[at + 5] - positions[at + 2]
|
|
1198
|
+
);
|
|
1199
|
+
length += run;
|
|
1200
|
+
folded += run * creases.angles[segment];
|
|
1201
|
+
regions.add(creases.regions[segment * 2]);
|
|
1202
|
+
regions.add(creases.regions[segment * 2 + 1]);
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
index,
|
|
1206
|
+
feature,
|
|
1207
|
+
start,
|
|
1208
|
+
end,
|
|
1209
|
+
length,
|
|
1210
|
+
// The normals turn by the fold; the faces themselves open by what is left of 180°.
|
|
1211
|
+
angle: 180 - folded / length,
|
|
1212
|
+
regions: [...regions],
|
|
1213
|
+
segments
|
|
1214
|
+
};
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
function sharpCornerPositions(creases, model, options) {
|
|
1218
|
+
const segments = findSharpCorners(creases, model, options).flatMap((corner) => corner.segments);
|
|
1219
|
+
const positions = new Float32Array(segments.length * 6);
|
|
1220
|
+
segments.forEach(
|
|
1221
|
+
(segment, at) => positions.set(creases.positions.subarray(segment * 6, segment * 6 + 6), at * 6)
|
|
1222
|
+
);
|
|
1223
|
+
return positions;
|
|
1224
|
+
}
|
|
1225
|
+
var SHARP_CORNER_HOVER_PIXELS = 6;
|
|
1226
|
+
function sharpCornerAt(corners, creases, hit, camera, viewport, matrixWorld) {
|
|
1227
|
+
const pointer = screenPoint(camera, hit.point, viewport);
|
|
1228
|
+
const a = new Vector34();
|
|
1229
|
+
const b = new Vector34();
|
|
1230
|
+
const from = new Vector22();
|
|
1231
|
+
const to = new Vector22();
|
|
1232
|
+
let nearest = null;
|
|
1233
|
+
let reach = SHARP_CORNER_HOVER_PIXELS;
|
|
1234
|
+
for (const corner of corners) {
|
|
1235
|
+
if (!corner.regions.includes(hit.region)) continue;
|
|
1236
|
+
for (const segment of corner.segments) {
|
|
1237
|
+
a.fromArray(creases.positions, segment * 6).applyMatrix4(matrixWorld);
|
|
1238
|
+
b.fromArray(creases.positions, segment * 6 + 3).applyMatrix4(matrixWorld);
|
|
1239
|
+
screenPoint(camera, a, viewport, from);
|
|
1240
|
+
screenPoint(camera, b, viewport, to);
|
|
1241
|
+
const distance = distanceToSegment(pointer, from, to);
|
|
1242
|
+
if (distance <= reach) [reach, nearest] = [distance, corner];
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return nearest;
|
|
1246
|
+
}
|
|
1247
|
+
function distanceToSegment(point, from, to) {
|
|
1248
|
+
const dx = to.x - from.x;
|
|
1249
|
+
const dy = to.y - from.y;
|
|
1250
|
+
const span = dx * dx + dy * dy;
|
|
1251
|
+
const t = span === 0 ? 0 : Math.min(1, Math.max(0, ((point.x - from.x) * dx + (point.y - from.y) * dy) / span));
|
|
1252
|
+
return Math.hypot(point.x - (from.x + t * dx), point.y - (from.y + t * dy));
|
|
1253
|
+
}
|
|
1254
|
+
function sharpCornerBands(creases, corners) {
|
|
1255
|
+
const position = [];
|
|
1256
|
+
const perp = [];
|
|
1257
|
+
const side = [];
|
|
1258
|
+
const reach = [];
|
|
1259
|
+
const owner = [];
|
|
1260
|
+
const index = [];
|
|
1261
|
+
for (const { index: corner, segments } of corners) {
|
|
1262
|
+
for (const segment of segments) {
|
|
1263
|
+
const at = segment * 6;
|
|
1264
|
+
const a = [creases.positions[at], creases.positions[at + 1], creases.positions[at + 2]];
|
|
1265
|
+
const b = [creases.positions[at + 3], creases.positions[at + 4], creases.positions[at + 5]];
|
|
1266
|
+
const edge = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
|
|
1267
|
+
const length = Math.hypot(edge[0], edge[1], edge[2]);
|
|
1268
|
+
if (length === 0) continue;
|
|
1269
|
+
const along = edge.map((value) => value / length);
|
|
1270
|
+
for (let face = 0; face < 2; face += 1) {
|
|
1271
|
+
const far = at + face * 3;
|
|
1272
|
+
const v = [
|
|
1273
|
+
creases.fars[far] - a[0],
|
|
1274
|
+
creases.fars[far + 1] - a[1],
|
|
1275
|
+
creases.fars[far + 2] - a[2]
|
|
1276
|
+
];
|
|
1277
|
+
const t = v[0] * along[0] + v[1] * along[1] + v[2] * along[2];
|
|
1278
|
+
const across = [v[0] - t * along[0], v[1] - t * along[1], v[2] - t * along[2]];
|
|
1279
|
+
const extent = Math.hypot(across[0], across[1], across[2]);
|
|
1280
|
+
if (extent === 0) continue;
|
|
1281
|
+
const first = position.length / 3;
|
|
1282
|
+
for (const [point, out] of [
|
|
1283
|
+
[a, 0],
|
|
1284
|
+
[b, 0],
|
|
1285
|
+
[a, 1],
|
|
1286
|
+
[b, 1]
|
|
1287
|
+
]) {
|
|
1288
|
+
position.push(point[0], point[1], point[2]);
|
|
1289
|
+
perp.push(across[0] / extent, across[1] / extent, across[2] / extent);
|
|
1290
|
+
side.push(out);
|
|
1291
|
+
reach.push(extent);
|
|
1292
|
+
owner.push(corner);
|
|
1293
|
+
}
|
|
1294
|
+
index.push(first, first + 1, first + 3, first, first + 3, first + 2);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
const geometry = new BufferGeometry();
|
|
1299
|
+
geometry.setAttribute("position", new Float32BufferAttribute2(position, 3));
|
|
1300
|
+
geometry.setAttribute("aPerp", new Float32BufferAttribute2(perp, 3));
|
|
1301
|
+
geometry.setAttribute("aSide", new Float32BufferAttribute2(side, 1));
|
|
1302
|
+
geometry.setAttribute("aReach", new Float32BufferAttribute2(reach, 1));
|
|
1303
|
+
geometry.setAttribute("aCorner", new Float32BufferAttribute2(owner, 1));
|
|
1304
|
+
geometry.setIndex(new BufferAttribute(new Uint32Array(index), 1));
|
|
1305
|
+
return geometry;
|
|
1306
|
+
}
|
|
1307
|
+
var SHARP_CORNER_RENDER_ORDER = { depth: 6.25, near: 6.5, far: 6.75 };
|
|
1308
|
+
var SHARP_CORNER_STENCIL_BIT = 128;
|
|
1309
|
+
var BAND_VERTEX = (
|
|
1310
|
+
/* glsl */
|
|
1311
|
+
`
|
|
1312
|
+
#include <common>
|
|
1313
|
+
#include <clipping_planes_pars_vertex>
|
|
1314
|
+
|
|
1315
|
+
attribute vec3 aPerp;
|
|
1316
|
+
attribute float aSide;
|
|
1317
|
+
attribute float aReach;
|
|
1318
|
+
attribute float aCorner;
|
|
1319
|
+
uniform float uHalfWidth;
|
|
1320
|
+
uniform float uHovered;
|
|
1321
|
+
uniform vec2 uResolution;
|
|
1322
|
+
varying float vHovered;
|
|
1323
|
+
|
|
1324
|
+
void main() {
|
|
1325
|
+
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
1326
|
+
vec3 across = mat3(modelViewMatrix) * aPerp;
|
|
1327
|
+
float reach = aReach * length(across);
|
|
1328
|
+
across = normalize(across);
|
|
1329
|
+
|
|
1330
|
+
bool perspective = projectionMatrix[2][3] == -1.0;
|
|
1331
|
+
float pixel = 2.0 / (projectionMatrix[1][1] * uResolution.y);
|
|
1332
|
+
if (perspective) pixel *= -mvPosition.z;
|
|
1333
|
+
vec3 sight = perspective ? normalize(-mvPosition.xyz) : vec3(0.0, 0.0, 1.0);
|
|
1334
|
+
float shown = length(across - sight * dot(across, sight));
|
|
1335
|
+
vHovered = abs(aCorner - uHovered) < 0.5 ? 1.0 : 0.0;
|
|
1336
|
+
float offset = min(uHalfWidth * pixel / max(shown, 0.25), reach);
|
|
1337
|
+
|
|
1338
|
+
mvPosition.xyz += across * offset * aSide;
|
|
1339
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
1340
|
+
#include <clipping_planes_vertex>
|
|
1341
|
+
}
|
|
1342
|
+
`
|
|
1343
|
+
);
|
|
1344
|
+
var BAND_FRAGMENT = (
|
|
1345
|
+
/* glsl */
|
|
1346
|
+
`
|
|
1347
|
+
#include <common>
|
|
1348
|
+
#include <clipping_planes_pars_fragment>
|
|
1349
|
+
|
|
1350
|
+
uniform vec3 diffuse;
|
|
1351
|
+
uniform vec3 hoverColor;
|
|
1352
|
+
uniform float opacity;
|
|
1353
|
+
varying float vHovered;
|
|
1354
|
+
|
|
1355
|
+
void main() {
|
|
1356
|
+
#include <clipping_planes_fragment>
|
|
1357
|
+
// The corner under the pointer in the hover colour, as the part's faces are, not wider.
|
|
1358
|
+
vec3 color = mix(diffuse, hoverColor, vHovered);
|
|
1359
|
+
// And twice as strong: plain even where the part hides it.
|
|
1360
|
+
gl_FragColor = vec4(color, min(1.0, opacity * mix(1.0, 2.0, vHovered)));
|
|
1361
|
+
#include <colorspace_fragment>
|
|
1362
|
+
}
|
|
1363
|
+
`
|
|
1364
|
+
);
|
|
1365
|
+
function createSharpCornerDrawing(theme, part) {
|
|
1366
|
+
let geometry = new BufferGeometry();
|
|
1367
|
+
const resolution = { value: new Vector22(1, 1) };
|
|
1368
|
+
const hovered = { value: -1 };
|
|
1369
|
+
const viewport = new Vector4();
|
|
1370
|
+
const material = (opacity, depthFunc) => {
|
|
1371
|
+
const band = new ShaderMaterial({
|
|
1372
|
+
uniforms: {
|
|
1373
|
+
diffuse: { value: new Color(theme.sharpCorner) },
|
|
1374
|
+
hoverColor: { value: new Color(theme.hover) },
|
|
1375
|
+
opacity: { value: opacity },
|
|
1376
|
+
uHalfWidth: { value: BAND_WIDTH / 2 },
|
|
1377
|
+
uHovered: hovered,
|
|
1378
|
+
uResolution: resolution
|
|
1379
|
+
},
|
|
1380
|
+
vertexShader: BAND_VERTEX,
|
|
1381
|
+
fragmentShader: BAND_FRAGMENT,
|
|
1382
|
+
transparent: true,
|
|
1383
|
+
depthWrite: false,
|
|
1384
|
+
side: DoubleSide,
|
|
1385
|
+
clipping: true
|
|
1386
|
+
});
|
|
1387
|
+
band.depthFunc = depthFunc;
|
|
1388
|
+
band.polygonOffset = true;
|
|
1389
|
+
band.polygonOffsetFactor = -1;
|
|
1390
|
+
band.polygonOffsetUnits = -1;
|
|
1391
|
+
band.stencilWrite = true;
|
|
1392
|
+
band.stencilRef = SHARP_CORNER_STENCIL_BIT;
|
|
1393
|
+
band.stencilFuncMask = SHARP_CORNER_STENCIL_BIT;
|
|
1394
|
+
band.stencilWriteMask = SHARP_CORNER_STENCIL_BIT;
|
|
1395
|
+
band.stencilFunc = NotEqualStencilFunc;
|
|
1396
|
+
band.stencilFail = KeepStencilOp;
|
|
1397
|
+
band.stencilZFail = KeepStencilOp;
|
|
1398
|
+
band.stencilZPass = ReplaceStencilOp;
|
|
1399
|
+
return band;
|
|
1400
|
+
};
|
|
1401
|
+
const nearMaterial = material(NEAR_OPACITY, LessEqualDepth);
|
|
1402
|
+
const farMaterial = material(FAR_OPACITY, GreaterDepth);
|
|
1403
|
+
const near = new Mesh(geometry, nearMaterial);
|
|
1404
|
+
const far = new Mesh(geometry, farMaterial);
|
|
1405
|
+
near.name = "sharpCornersNear";
|
|
1406
|
+
far.name = "sharpCornersFar";
|
|
1407
|
+
near.renderOrder = SHARP_CORNER_RENDER_ORDER.near;
|
|
1408
|
+
far.renderOrder = SHARP_CORNER_RENDER_ORDER.far;
|
|
1409
|
+
near.raycast = () => {
|
|
1410
|
+
};
|
|
1411
|
+
far.raycast = () => {
|
|
1412
|
+
};
|
|
1413
|
+
const depthMaterial = new MeshBasicMaterial({
|
|
1414
|
+
colorWrite: false,
|
|
1415
|
+
depthWrite: true,
|
|
1416
|
+
transparent: true,
|
|
1417
|
+
polygonOffset: true,
|
|
1418
|
+
polygonOffsetFactor: 1,
|
|
1419
|
+
polygonOffsetUnits: 1
|
|
1420
|
+
});
|
|
1421
|
+
const depth = new Mesh(part, depthMaterial);
|
|
1422
|
+
depth.name = "sharpCornersDepth";
|
|
1423
|
+
depth.renderOrder = SHARP_CORNER_RENDER_ORDER.depth;
|
|
1424
|
+
depth.visible = false;
|
|
1425
|
+
depth.raycast = () => {
|
|
1426
|
+
};
|
|
1427
|
+
near.onBeforeRender = (renderer) => {
|
|
1428
|
+
renderer.getViewport(viewport);
|
|
1429
|
+
resolution.value.set(viewport.z, viewport.w);
|
|
1430
|
+
};
|
|
1431
|
+
const object = new Group();
|
|
1432
|
+
object.add(depth, far, near);
|
|
1433
|
+
object.visible = false;
|
|
1434
|
+
return {
|
|
1435
|
+
object,
|
|
1436
|
+
clear() {
|
|
1437
|
+
object.visible = false;
|
|
1438
|
+
},
|
|
1439
|
+
setHovered(index) {
|
|
1440
|
+
hovered.value = index ?? -1;
|
|
1441
|
+
},
|
|
1442
|
+
setCorners(creases, corners) {
|
|
1443
|
+
hovered.value = -1;
|
|
1444
|
+
object.visible = corners.length > 0;
|
|
1445
|
+
if (corners.length === 0) return;
|
|
1446
|
+
const next = sharpCornerBands(creases, corners);
|
|
1447
|
+
near.geometry = next;
|
|
1448
|
+
far.geometry = next;
|
|
1449
|
+
geometry.dispose();
|
|
1450
|
+
geometry = next;
|
|
1451
|
+
},
|
|
1452
|
+
setTheme(next) {
|
|
1453
|
+
for (const band of [nearMaterial, farMaterial]) {
|
|
1454
|
+
;
|
|
1455
|
+
band.uniforms["diffuse"].value.setHex(next.sharpCorner);
|
|
1456
|
+
band.uniforms["hoverColor"].value.setHex(next.hover);
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
setPartDepth(on) {
|
|
1460
|
+
depth.visible = on;
|
|
1461
|
+
},
|
|
1462
|
+
setClippingPlanes(planes) {
|
|
1463
|
+
nearMaterial.clippingPlanes = planes;
|
|
1464
|
+
farMaterial.clippingPlanes = planes;
|
|
1465
|
+
depthMaterial.clippingPlanes = planes;
|
|
1466
|
+
},
|
|
1467
|
+
dispose() {
|
|
1468
|
+
object.clear();
|
|
1469
|
+
geometry.dispose();
|
|
1470
|
+
nearMaterial.dispose();
|
|
1471
|
+
farMaterial.dispose();
|
|
1472
|
+
depthMaterial.dispose();
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
function unit2(vector) {
|
|
1477
|
+
const length = Math.hypot(vector.x, vector.y, vector.z);
|
|
1478
|
+
return length === 0 ? null : { x: vector.x / length, y: vector.y / length, z: vector.z / length };
|
|
1479
|
+
}
|
|
1480
|
+
function emptyCreases() {
|
|
1481
|
+
return {
|
|
1482
|
+
count: 0,
|
|
1483
|
+
positions: new Float32Array(0),
|
|
1484
|
+
angles: new Float32Array(0),
|
|
1485
|
+
concave: new Uint8Array(0),
|
|
1486
|
+
regions: new Int32Array(0),
|
|
1487
|
+
fars: new Float32Array(0)
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
|
|
940
1491
|
// src/render/part.ts
|
|
941
1492
|
var REGION_ATTRIBUTE = "aRegion";
|
|
942
1493
|
var EMISSIVE_MIX = 0.4;
|
|
@@ -959,7 +1510,7 @@ function buildRegionAttribute(model, texels, vertexCount) {
|
|
|
959
1510
|
function createPart(model, geometry, theme) {
|
|
960
1511
|
const position = geometry.getAttribute("position");
|
|
961
1512
|
const texels = buildRegionTexels(model);
|
|
962
|
-
const regionAttribute = new
|
|
1513
|
+
const regionAttribute = new Float32BufferAttribute3(
|
|
963
1514
|
buildRegionAttribute(model, texels, position.count),
|
|
964
1515
|
1
|
|
965
1516
|
);
|
|
@@ -975,6 +1526,11 @@ function createPart(model, geometry, theme) {
|
|
|
975
1526
|
const wireframe = { value: false };
|
|
976
1527
|
let focused = false;
|
|
977
1528
|
let currentTheme = theme;
|
|
1529
|
+
let clipping = null;
|
|
1530
|
+
let creases = null;
|
|
1531
|
+
let sharpLines = null;
|
|
1532
|
+
let sharpKey = "";
|
|
1533
|
+
let corners = [];
|
|
978
1534
|
const material = new MeshLambertMaterial({
|
|
979
1535
|
color: theme.part,
|
|
980
1536
|
emissive: theme.partEmissive,
|
|
@@ -1038,11 +1594,11 @@ function createPart(model, geometry, theme) {
|
|
|
1038
1594
|
emissive: theme.partEmissive,
|
|
1039
1595
|
// A selected face can be viewed through the part from the side its normal
|
|
1040
1596
|
// faces away from.
|
|
1041
|
-
side:
|
|
1597
|
+
side: DoubleSide2
|
|
1042
1598
|
});
|
|
1043
1599
|
configureShader(focusMaterial, true);
|
|
1044
1600
|
configureShader(material, false);
|
|
1045
|
-
const mesh = new
|
|
1601
|
+
const mesh = new Mesh2(geometry, material);
|
|
1046
1602
|
mesh.renderOrder = 3;
|
|
1047
1603
|
const edgeGeometry = regionEdgesGeometry(geometry, model);
|
|
1048
1604
|
const edgeMaterial = new LineBasicMaterial({
|
|
@@ -1054,7 +1610,7 @@ function createPart(model, geometry, theme) {
|
|
|
1054
1610
|
edges.renderOrder = 4;
|
|
1055
1611
|
edges.raycast = () => {
|
|
1056
1612
|
};
|
|
1057
|
-
const focusMesh = new
|
|
1613
|
+
const focusMesh = new Mesh2(geometry, focusMaterial);
|
|
1058
1614
|
focusMesh.renderOrder = 3;
|
|
1059
1615
|
focusMesh.visible = false;
|
|
1060
1616
|
focusMesh.raycast = () => {
|
|
@@ -1068,11 +1624,12 @@ function createPart(model, geometry, theme) {
|
|
|
1068
1624
|
}
|
|
1069
1625
|
focusState.value = focused;
|
|
1070
1626
|
focusMesh.visible = focused;
|
|
1627
|
+
sharpLines?.setPartDepth(transparent);
|
|
1071
1628
|
};
|
|
1072
|
-
const object = new
|
|
1629
|
+
const object = new Group2();
|
|
1073
1630
|
object.add(focusMesh, mesh, edges);
|
|
1074
|
-
const scratchColor = new
|
|
1075
|
-
const scratchVector = new
|
|
1631
|
+
const scratchColor = new Color2();
|
|
1632
|
+
const scratchVector = new Vector35();
|
|
1076
1633
|
const paintRegion = (region, color, weight) => {
|
|
1077
1634
|
const column = texels.get(region);
|
|
1078
1635
|
if (column === void 0) return;
|
|
@@ -1146,9 +1703,11 @@ function createPart(model, geometry, theme) {
|
|
|
1146
1703
|
},
|
|
1147
1704
|
setClippingPlanes(planes) {
|
|
1148
1705
|
const value = planes === null ? null : [...planes];
|
|
1706
|
+
clipping = value;
|
|
1149
1707
|
material.clippingPlanes = value;
|
|
1150
1708
|
focusMaterial.clippingPlanes = value;
|
|
1151
1709
|
edgeMaterial.clippingPlanes = value;
|
|
1710
|
+
sharpLines?.setClippingPlanes(value);
|
|
1152
1711
|
},
|
|
1153
1712
|
setTheme(next) {
|
|
1154
1713
|
currentTheme = next;
|
|
@@ -1158,6 +1717,7 @@ function createPart(model, geometry, theme) {
|
|
|
1158
1717
|
focusMaterial.emissive.setHex(next.partEmissive);
|
|
1159
1718
|
edgeMaterial.color.setHex(wireframe.value ? next.part : next.edge);
|
|
1160
1719
|
edgeMaterial.opacity = wireframe.value ? 1 : next.edgeOpacity;
|
|
1720
|
+
sharpLines?.setTheme(next);
|
|
1161
1721
|
},
|
|
1162
1722
|
setDisplay(display, showEdges = true) {
|
|
1163
1723
|
const enabled = display === "wireframe";
|
|
@@ -1169,6 +1729,35 @@ function createPart(model, geometry, theme) {
|
|
|
1169
1729
|
edgeMaterial.color.setHex(enabled ? currentTheme.part : currentTheme.edge);
|
|
1170
1730
|
edgeMaterial.opacity = enabled ? 1 : currentTheme.edgeOpacity;
|
|
1171
1731
|
},
|
|
1732
|
+
setSharpCorners(options) {
|
|
1733
|
+
const key = options === null ? "" : sharpCornerKey(options);
|
|
1734
|
+
if (key === sharpKey) return;
|
|
1735
|
+
sharpKey = key;
|
|
1736
|
+
if (options === null || options.features.length === 0) {
|
|
1737
|
+
corners = [];
|
|
1738
|
+
sharpLines?.clear();
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
creases ??= surfaceCreases(geometry, model);
|
|
1742
|
+
if (!sharpLines) {
|
|
1743
|
+
sharpLines = createSharpCornerDrawing(currentTheme, geometry);
|
|
1744
|
+
sharpLines.setClippingPlanes(clipping);
|
|
1745
|
+
sharpLines.setPartDepth(wireframe.value || focused);
|
|
1746
|
+
object.add(sharpLines.object);
|
|
1747
|
+
}
|
|
1748
|
+
corners = findSharpCorners(creases, model, options);
|
|
1749
|
+
sharpLines.setCorners(creases, corners);
|
|
1750
|
+
},
|
|
1751
|
+
sharpCorners() {
|
|
1752
|
+
return corners;
|
|
1753
|
+
},
|
|
1754
|
+
sharpCornerAt(hit, camera, viewport) {
|
|
1755
|
+
if (!creases || corners.length === 0) return null;
|
|
1756
|
+
return sharpCornerAt(corners, creases, hit, camera, viewport, mesh.matrixWorld);
|
|
1757
|
+
},
|
|
1758
|
+
setHoveredSharpCorner(index) {
|
|
1759
|
+
sharpLines?.setHovered(index);
|
|
1760
|
+
},
|
|
1172
1761
|
dispose() {
|
|
1173
1762
|
object.clear();
|
|
1174
1763
|
if (geometry.getAttribute(REGION_ATTRIBUTE) === regionAttribute) {
|
|
@@ -1178,54 +1767,13 @@ function createPart(model, geometry, theme) {
|
|
|
1178
1767
|
focusMaterial.dispose();
|
|
1179
1768
|
edgeGeometry.dispose();
|
|
1180
1769
|
edgeMaterial.dispose();
|
|
1770
|
+
sharpLines?.dispose();
|
|
1181
1771
|
stateTexture.dispose();
|
|
1182
1772
|
opacityTexture.dispose();
|
|
1183
1773
|
}
|
|
1184
1774
|
};
|
|
1185
1775
|
}
|
|
1186
1776
|
|
|
1187
|
-
// src/render/adjacency.ts
|
|
1188
|
-
var GRID = 1e-4;
|
|
1189
|
-
var keyFor = (x, y, z) => `${Math.round(x / GRID)},${Math.round(y / GRID)},${Math.round(z / GRID)}`;
|
|
1190
|
-
function regionAdjacency(model, geometry) {
|
|
1191
|
-
const position = geometry.getAttribute("position");
|
|
1192
|
-
const adjacency = /* @__PURE__ */ new Map();
|
|
1193
|
-
if (!position) return adjacency;
|
|
1194
|
-
const owner = new Int32Array(Math.floor(position.count / 3)).fill(-1);
|
|
1195
|
-
for (const region of model.regions) {
|
|
1196
|
-
for (let at = region.triangles.start; at < region.triangles.end; at++) {
|
|
1197
|
-
if (at >= 0 && at < owner.length) owner[at] = region.idx;
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
const edges = /* @__PURE__ */ new Map();
|
|
1201
|
-
const join = (a, b) => {
|
|
1202
|
-
if (a === b) return;
|
|
1203
|
-
const mine = adjacency.get(a) ?? /* @__PURE__ */ new Set();
|
|
1204
|
-
mine.add(b);
|
|
1205
|
-
adjacency.set(a, mine);
|
|
1206
|
-
const theirs = adjacency.get(b) ?? /* @__PURE__ */ new Set();
|
|
1207
|
-
theirs.add(a);
|
|
1208
|
-
adjacency.set(b, theirs);
|
|
1209
|
-
};
|
|
1210
|
-
for (let triangle = 0; triangle < owner.length; triangle++) {
|
|
1211
|
-
const region = owner[triangle];
|
|
1212
|
-
if (region === -1) continue;
|
|
1213
|
-
const corners = [0, 1, 2].map((corner) => {
|
|
1214
|
-
const at = triangle * 3 + corner;
|
|
1215
|
-
return keyFor(position.getX(at), position.getY(at), position.getZ(at));
|
|
1216
|
-
});
|
|
1217
|
-
for (let corner = 0; corner < 3; corner++) {
|
|
1218
|
-
const from = corners[corner];
|
|
1219
|
-
const to = corners[(corner + 1) % 3];
|
|
1220
|
-
const edge = from < to ? `${from}|${to}` : `${to}|${from}`;
|
|
1221
|
-
const seen = edges.get(edge);
|
|
1222
|
-
if (seen === void 0) edges.set(edge, region);
|
|
1223
|
-
else join(seen, region);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
return adjacency;
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
1777
|
// src/render/selection.ts
|
|
1230
1778
|
var FEATURE_TYPE_RANKS = [
|
|
1231
1779
|
["blind_hole", "through_hole", "filleted_blind_hole"],
|
|
@@ -1337,7 +1885,7 @@ import {
|
|
|
1337
1885
|
useMemo as useMemo4,
|
|
1338
1886
|
useRef as useRef3
|
|
1339
1887
|
} from "react";
|
|
1340
|
-
import { Box3 as Box33, Vector3 as
|
|
1888
|
+
import { Box3 as Box33, Vector3 as Vector39 } from "three";
|
|
1341
1889
|
|
|
1342
1890
|
// src/camera.tsx
|
|
1343
1891
|
import { useFrame, useThree as useThree2 } from "@react-three/fiber";
|
|
@@ -1353,9 +1901,9 @@ import {
|
|
|
1353
1901
|
Raycaster,
|
|
1354
1902
|
Sphere,
|
|
1355
1903
|
Spherical,
|
|
1356
|
-
Vector2 as
|
|
1357
|
-
Vector3 as
|
|
1358
|
-
Vector4
|
|
1904
|
+
Vector2 as Vector23,
|
|
1905
|
+
Vector3 as Vector36,
|
|
1906
|
+
Vector4 as Vector42
|
|
1359
1907
|
} from "three";
|
|
1360
1908
|
|
|
1361
1909
|
// src/render/control-schemes.ts
|
|
@@ -1451,9 +1999,9 @@ CameraControls.install({
|
|
|
1451
1999
|
Raycaster,
|
|
1452
2000
|
Sphere,
|
|
1453
2001
|
Spherical,
|
|
1454
|
-
Vector2:
|
|
1455
|
-
Vector3:
|
|
1456
|
-
Vector4
|
|
2002
|
+
Vector2: Vector23,
|
|
2003
|
+
Vector3: Vector36,
|
|
2004
|
+
Vector4: Vector42
|
|
1457
2005
|
}
|
|
1458
2006
|
});
|
|
1459
2007
|
var EPSILON3 = 1e-6;
|
|
@@ -1475,9 +2023,9 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
1475
2023
|
// Scratch objects — `#onPointerMove` and `#adaptUpVector` run at pointer and
|
|
1476
2024
|
// frame rate respectively, so neither may allocate.
|
|
1477
2025
|
#spherical = new Spherical();
|
|
1478
|
-
#scratchA = new
|
|
1479
|
-
#scratchB = new
|
|
1480
|
-
#scratchC = new
|
|
2026
|
+
#scratchA = new Vector36();
|
|
2027
|
+
#scratchB = new Vector36();
|
|
2028
|
+
#scratchC = new Vector36();
|
|
1481
2029
|
constructor(camera, domElement, options = {}) {
|
|
1482
2030
|
super(camera, domElement);
|
|
1483
2031
|
this.#domElement = domElement;
|
|
@@ -1819,10 +2367,10 @@ function counted(notify) {
|
|
|
1819
2367
|
import { useFrame as useFrame2, useThree as useThree3 } from "@react-three/fiber";
|
|
1820
2368
|
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
|
|
1821
2369
|
import {
|
|
1822
|
-
DoubleSide as
|
|
2370
|
+
DoubleSide as DoubleSide3,
|
|
1823
2371
|
RingGeometry,
|
|
1824
2372
|
SphereGeometry,
|
|
1825
|
-
Vector3 as
|
|
2373
|
+
Vector3 as Vector37
|
|
1826
2374
|
} from "three";
|
|
1827
2375
|
|
|
1828
2376
|
// src/render/target.ts
|
|
@@ -1865,8 +2413,8 @@ var TargetMarker = ({ controlsRef }) => {
|
|
|
1865
2413
|
const heldUntil = useRef2(0);
|
|
1866
2414
|
const painted = useRef2(-1);
|
|
1867
2415
|
const placed = useRef2(false);
|
|
1868
|
-
const lastTarget = useMemo3(() => new
|
|
1869
|
-
const scratch = useMemo3(() => new
|
|
2416
|
+
const lastTarget = useMemo3(() => new Vector37(), []);
|
|
2417
|
+
const scratch = useMemo3(() => new Vector37(), []);
|
|
1870
2418
|
useEffect3(() => {
|
|
1871
2419
|
const controls = controlsRef.current;
|
|
1872
2420
|
if (!controls) return void 0;
|
|
@@ -1932,7 +2480,7 @@ var TargetMarker = ({ controlsRef }) => {
|
|
|
1932
2480
|
opacity: 0,
|
|
1933
2481
|
depthTest: false,
|
|
1934
2482
|
depthWrite: false,
|
|
1935
|
-
side:
|
|
2483
|
+
side: DoubleSide3
|
|
1936
2484
|
}
|
|
1937
2485
|
)
|
|
1938
2486
|
}
|
|
@@ -1962,11 +2510,11 @@ var TargetMarker = ({ controlsRef }) => {
|
|
|
1962
2510
|
|
|
1963
2511
|
// src/render/view-cube.ts
|
|
1964
2512
|
import {
|
|
1965
|
-
BufferGeometry,
|
|
2513
|
+
BufferGeometry as BufferGeometry2,
|
|
1966
2514
|
CanvasTexture,
|
|
1967
|
-
Float32BufferAttribute as
|
|
2515
|
+
Float32BufferAttribute as Float32BufferAttribute4,
|
|
1968
2516
|
SRGBColorSpace,
|
|
1969
|
-
Vector3 as
|
|
2517
|
+
Vector3 as Vector38
|
|
1970
2518
|
} from "three";
|
|
1971
2519
|
var VIEW_NAMES = [
|
|
1972
2520
|
// The six faces.
|
|
@@ -2050,12 +2598,12 @@ function viewUp(direction) {
|
|
|
2050
2598
|
return { x: 0, y: 0, z: 1 };
|
|
2051
2599
|
}
|
|
2052
2600
|
function squaredUp(direction, currentUp) {
|
|
2053
|
-
const forward = new
|
|
2601
|
+
const forward = new Vector38(direction.x, direction.y, direction.z).normalize();
|
|
2054
2602
|
const canonical = viewUp(direction);
|
|
2055
|
-
const zero = new
|
|
2603
|
+
const zero = new Vector38(canonical.x, canonical.y, canonical.z);
|
|
2056
2604
|
zero.addScaledVector(forward, -forward.dot(zero)).normalize();
|
|
2057
|
-
const quarter = new
|
|
2058
|
-
const current = new
|
|
2605
|
+
const quarter = new Vector38().crossVectors(forward, zero);
|
|
2606
|
+
const current = new Vector38(currentUp.x, currentUp.y, currentUp.z);
|
|
2059
2607
|
const rolls = [zero, quarter, zero.clone().negate(), quarter.clone().negate()];
|
|
2060
2608
|
const nearest = rolls.reduce(
|
|
2061
2609
|
(best, roll) => roll.dot(current) > best.dot(current) ? roll : best
|
|
@@ -2073,11 +2621,11 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
2073
2621
|
const direction = viewVector(name);
|
|
2074
2622
|
const [sx, sy, sz] = VIEW_SIGNS[name];
|
|
2075
2623
|
const sign = (axis) => VIEW_SIGNS[name][axis] ?? 0;
|
|
2076
|
-
const normal = new
|
|
2624
|
+
const normal = new Vector38(direction.x, direction.y, direction.z);
|
|
2077
2625
|
let polygon;
|
|
2078
2626
|
if (kind === "face") {
|
|
2079
2627
|
const up = viewUp(direction);
|
|
2080
|
-
const upVector = new
|
|
2628
|
+
const upVector = new Vector38(up.x, up.y, up.z);
|
|
2081
2629
|
const right = normal.clone().negate().cross(upVector).normalize();
|
|
2082
2630
|
const square = [
|
|
2083
2631
|
[-1, -1],
|
|
@@ -2099,7 +2647,7 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
2099
2647
|
[chamfer, 1, -chamfer]
|
|
2100
2648
|
];
|
|
2101
2649
|
polygon = rectangle.map(
|
|
2102
|
-
([onA, onB, onC]) => new
|
|
2650
|
+
([onA, onB, onC]) => new Vector38().setComponent(a, sign(a) * onA).setComponent(b, sign(b) * onB).setComponent(c, onC)
|
|
2103
2651
|
);
|
|
2104
2652
|
} else {
|
|
2105
2653
|
const triangle = [
|
|
@@ -2107,11 +2655,11 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
2107
2655
|
[chamfer, 1, chamfer],
|
|
2108
2656
|
[chamfer, chamfer, 1]
|
|
2109
2657
|
];
|
|
2110
|
-
polygon = triangle.map(([onX, onY, onZ]) => new
|
|
2658
|
+
polygon = triangle.map(([onX, onY, onZ]) => new Vector38(sx * onX, sy * onY, sz * onZ));
|
|
2111
2659
|
}
|
|
2112
2660
|
const [first, second, third] = polygon;
|
|
2113
2661
|
if (first && second && third) {
|
|
2114
|
-
const facing = new
|
|
2662
|
+
const facing = new Vector38().subVectors(second, first).cross(new Vector38().subVectors(third, first));
|
|
2115
2663
|
if (facing.dot(normal) < 0) polygon.reverse();
|
|
2116
2664
|
}
|
|
2117
2665
|
return { name, kind, direction, polygon: polygon.map(vec) };
|
|
@@ -2121,7 +2669,7 @@ function panelGeometry(zone) {
|
|
|
2121
2669
|
const positions = [];
|
|
2122
2670
|
const normals = [];
|
|
2123
2671
|
const [first] = zone.polygon;
|
|
2124
|
-
const geometry = new
|
|
2672
|
+
const geometry = new BufferGeometry2();
|
|
2125
2673
|
if (!first) return geometry;
|
|
2126
2674
|
for (let index = 1; index + 1 < zone.polygon.length; index += 1) {
|
|
2127
2675
|
const b = zone.polygon[index];
|
|
@@ -2132,8 +2680,8 @@ function panelGeometry(zone) {
|
|
|
2132
2680
|
normals.push(zone.direction.x, zone.direction.y, zone.direction.z);
|
|
2133
2681
|
}
|
|
2134
2682
|
}
|
|
2135
|
-
geometry.setAttribute("position", new
|
|
2136
|
-
geometry.setAttribute("normal", new
|
|
2683
|
+
geometry.setAttribute("position", new Float32BufferAttribute4(positions, 3));
|
|
2684
|
+
geometry.setAttribute("normal", new Float32BufferAttribute4(normals, 3));
|
|
2137
2685
|
return geometry;
|
|
2138
2686
|
}
|
|
2139
2687
|
function cubeOutlineGeometry(zones) {
|
|
@@ -2146,8 +2694,8 @@ function cubeOutlineGeometry(zones) {
|
|
|
2146
2694
|
positions.push(from.x, from.y, from.z, to.x, to.y, to.z);
|
|
2147
2695
|
}
|
|
2148
2696
|
}
|
|
2149
|
-
const geometry = new
|
|
2150
|
-
geometry.setAttribute("position", new
|
|
2697
|
+
const geometry = new BufferGeometry2();
|
|
2698
|
+
geometry.setAttribute("position", new Float32BufferAttribute4(positions, 3));
|
|
2151
2699
|
return geometry;
|
|
2152
2700
|
}
|
|
2153
2701
|
function labelGeometry(zone) {
|
|
@@ -2171,9 +2719,9 @@ function labelGeometry(zone) {
|
|
|
2171
2719
|
);
|
|
2172
2720
|
uvs.push(uv[0] ?? 0, uv[1] ?? 0);
|
|
2173
2721
|
}
|
|
2174
|
-
const geometry = new
|
|
2175
|
-
geometry.setAttribute("position", new
|
|
2176
|
-
geometry.setAttribute("uv", new
|
|
2722
|
+
const geometry = new BufferGeometry2();
|
|
2723
|
+
geometry.setAttribute("position", new Float32BufferAttribute4(positions, 3));
|
|
2724
|
+
geometry.setAttribute("uv", new Float32BufferAttribute4(uvs, 2));
|
|
2177
2725
|
return geometry;
|
|
2178
2726
|
}
|
|
2179
2727
|
function labelTexture(label, color) {
|
|
@@ -2241,10 +2789,10 @@ var ViewerScene = ({
|
|
|
2241
2789
|
const framedRef = useRef3(null);
|
|
2242
2790
|
const scratchBox = useMemo4(() => new Box33(), []);
|
|
2243
2791
|
const scratchBoundary = useMemo4(() => new Box33(), []);
|
|
2244
|
-
const scratchDirection = useMemo4(() => new
|
|
2245
|
-
const scratchView = useMemo4(() => new
|
|
2246
|
-
const scratchTarget = useMemo4(() => new
|
|
2247
|
-
const scratchPosition = useMemo4(() => new
|
|
2792
|
+
const scratchDirection = useMemo4(() => new Vector39(), []);
|
|
2793
|
+
const scratchView = useMemo4(() => new Vector39(), []);
|
|
2794
|
+
const scratchTarget = useMemo4(() => new Vector39(), []);
|
|
2795
|
+
const scratchPosition = useMemo4(() => new Vector39(), []);
|
|
2248
2796
|
const applyLimits = useCallback(
|
|
2249
2797
|
(bounds, framed) => {
|
|
2250
2798
|
controlsRef.current?.applyLimits(
|
|
@@ -2293,8 +2841,8 @@ var ViewerScene = ({
|
|
|
2293
2841
|
(bounds) => {
|
|
2294
2842
|
const controls = controlsRef.current;
|
|
2295
2843
|
if (!controls) return false;
|
|
2296
|
-
const target = controls.getTarget(new
|
|
2297
|
-
const direction = currentViewDirection(camera, target, new
|
|
2844
|
+
const target = controls.getTarget(new Vector39());
|
|
2845
|
+
const direction = currentViewDirection(camera, target, new Vector39());
|
|
2298
2846
|
const distance = fitDistance(projection, size, bounds, DEFAULT_FIT_MARGIN);
|
|
2299
2847
|
const position = direction.multiplyScalar(distance).add(bounds.center);
|
|
2300
2848
|
void controls.setLookAt(
|
|
@@ -2319,8 +2867,8 @@ var ViewerScene = ({
|
|
|
2319
2867
|
const fitContent = useCallback(() => {
|
|
2320
2868
|
const controls = controlsRef.current;
|
|
2321
2869
|
if (!controls) return false;
|
|
2322
|
-
const target = controls.getTarget(new
|
|
2323
|
-
return frame(currentViewDirection(camera, target, new
|
|
2870
|
+
const target = controls.getTarget(new Vector39());
|
|
2871
|
+
return frame(currentViewDirection(camera, target, new Vector39()), true);
|
|
2324
2872
|
}, [camera, frame]);
|
|
2325
2873
|
const resetContent = useCallback(() => {
|
|
2326
2874
|
const bounds = measure();
|
|
@@ -2529,17 +3077,17 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
2529
3077
|
import { useFrame as useFrame4, useThree as useThree6 } from "@react-three/fiber";
|
|
2530
3078
|
import { useCallback as useCallback2, useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useMemo as useMemo6, useRef as useRef4, useState } from "react";
|
|
2531
3079
|
import {
|
|
2532
|
-
Color as
|
|
2533
|
-
DoubleSide as
|
|
3080
|
+
Color as Color5,
|
|
3081
|
+
DoubleSide as DoubleSide5,
|
|
2534
3082
|
EdgesGeometry,
|
|
2535
|
-
Mesh as
|
|
3083
|
+
Mesh as Mesh4,
|
|
2536
3084
|
Plane as Plane2,
|
|
2537
3085
|
PlaneGeometry,
|
|
2538
3086
|
Quaternion as Quaternion2,
|
|
2539
3087
|
Raycaster as Raycaster2,
|
|
2540
3088
|
Scene,
|
|
2541
|
-
Vector2 as
|
|
2542
|
-
Vector3 as
|
|
3089
|
+
Vector2 as Vector26,
|
|
3090
|
+
Vector3 as Vector311
|
|
2543
3091
|
} from "three";
|
|
2544
3092
|
|
|
2545
3093
|
// src/arrow-mesh.tsx
|
|
@@ -2547,7 +3095,7 @@ import { useThree as useThree5 } from "@react-three/fiber";
|
|
|
2547
3095
|
import { useEffect as useEffect5, useLayoutEffect, useMemo as useMemo5 } from "react";
|
|
2548
3096
|
|
|
2549
3097
|
// src/render/arrow-material.ts
|
|
2550
|
-
import { AlwaysDepth, Color as
|
|
3098
|
+
import { AlwaysDepth, Color as Color3, ShaderMaterial as ShaderMaterial2 } from "three";
|
|
2551
3099
|
var VERTEX = (
|
|
2552
3100
|
/* glsl */
|
|
2553
3101
|
`
|
|
@@ -2582,10 +3130,10 @@ void main() {
|
|
|
2582
3130
|
);
|
|
2583
3131
|
function createArrowMaterial() {
|
|
2584
3132
|
const uniforms = {
|
|
2585
|
-
uColor: { value: new
|
|
3133
|
+
uColor: { value: new Color3() },
|
|
2586
3134
|
uOpacity: { value: 1 }
|
|
2587
3135
|
};
|
|
2588
|
-
return new
|
|
3136
|
+
return new ShaderMaterial2({
|
|
2589
3137
|
vertexShader: VERTEX,
|
|
2590
3138
|
fragmentShader: FRAGMENT,
|
|
2591
3139
|
uniforms,
|
|
@@ -2620,7 +3168,7 @@ void main() {
|
|
|
2620
3168
|
`
|
|
2621
3169
|
);
|
|
2622
3170
|
function createDepthResetMaterial() {
|
|
2623
|
-
return new
|
|
3171
|
+
return new ShaderMaterial2({
|
|
2624
3172
|
vertexShader: RESET_VERTEX,
|
|
2625
3173
|
fragmentShader: RESET_FRAGMENT,
|
|
2626
3174
|
transparent: true,
|
|
@@ -2651,8 +3199,8 @@ var ArrowMesh = ({ geometry, color, opacity, onTop = false, events }) => {
|
|
|
2651
3199
|
};
|
|
2652
3200
|
|
|
2653
3201
|
// src/render/arrow.ts
|
|
2654
|
-
import { LatheGeometry, Vector2 as
|
|
2655
|
-
var ARROW_AXIS2 = new
|
|
3202
|
+
import { LatheGeometry, Vector2 as Vector24, Vector3 as Vector310 } from "three";
|
|
3203
|
+
var ARROW_AXIS2 = new Vector310(0, 1, 0);
|
|
2656
3204
|
var ARROW_ROUND = 32;
|
|
2657
3205
|
var HEAD_LENGTH = 0.415;
|
|
2658
3206
|
var HEAD_RADIUS = 0.153;
|
|
@@ -2669,9 +3217,9 @@ var ROUND_STEPS = 4;
|
|
|
2669
3217
|
var CAP_STEPS = 6;
|
|
2670
3218
|
var BEAD_STEPS = 12;
|
|
2671
3219
|
var head = ({ length, radius, shaft }) => [
|
|
2672
|
-
{ at: new
|
|
2673
|
-
{ at: new
|
|
2674
|
-
{ at: new
|
|
3220
|
+
{ at: new Vector24(0, 0) },
|
|
3221
|
+
{ at: new Vector24(radius, length), round: RIM_ROUND },
|
|
3222
|
+
{ at: new Vector24(shaft, length), round: NECK_ROUND }
|
|
2675
3223
|
];
|
|
2676
3224
|
var rounded = (corners) => corners.flatMap(({ at, round = 0 }, index) => {
|
|
2677
3225
|
const before = corners[index - 1]?.at;
|
|
@@ -2696,17 +3244,17 @@ var rounded = (corners) => corners.flatMap(({ at, round = 0 }, index) => {
|
|
|
2696
3244
|
if (sweep < -Math.PI) sweep += Math.PI * 2;
|
|
2697
3245
|
return Array.from({ length: ROUND_STEPS + 1 }, (_, step) => {
|
|
2698
3246
|
const turn = start + sweep * step / ROUND_STEPS;
|
|
2699
|
-
return new
|
|
3247
|
+
return new Vector24(centre.x + Math.cos(turn) * radius, centre.y + Math.sin(turn) * radius);
|
|
2700
3248
|
});
|
|
2701
3249
|
});
|
|
2702
3250
|
function arrowProfile() {
|
|
2703
3251
|
const body = rounded([
|
|
2704
3252
|
...head({ length: HEAD_LENGTH, radius: HEAD_RADIUS, shaft: SHAFT_RADIUS }),
|
|
2705
|
-
{ at: new
|
|
3253
|
+
{ at: new Vector24(SHAFT_RADIUS, 1 - SHAFT_RADIUS) }
|
|
2706
3254
|
]);
|
|
2707
3255
|
const tail = Array.from({ length: CAP_STEPS }, (_, step) => {
|
|
2708
3256
|
const turn = (step + 1) / CAP_STEPS * (Math.PI / 2);
|
|
2709
|
-
return new
|
|
3257
|
+
return new Vector24(
|
|
2710
3258
|
step + 1 === CAP_STEPS ? 0 : Math.cos(turn) * SHAFT_RADIUS,
|
|
2711
3259
|
1 - SHAFT_RADIUS + Math.sin(turn) * SHAFT_RADIUS
|
|
2712
3260
|
);
|
|
@@ -2718,12 +3266,12 @@ function handleProfile() {
|
|
|
2718
3266
|
length: HANDLE_HEAD_LENGTH,
|
|
2719
3267
|
radius: HANDLE_HEAD_RADIUS,
|
|
2720
3268
|
shaft: HANDLE_SHAFT_RADIUS
|
|
2721
|
-
}).map(({ at, round }) => ({ at: new
|
|
2722
|
-
const upper = lower.slice().reverse().map(({ at, round }) => ({ at: new
|
|
3269
|
+
}).map(({ at, round }) => ({ at: new Vector24(at.x, at.y - HANDLE_TIP), round }));
|
|
3270
|
+
const upper = lower.slice().reverse().map(({ at, round }) => ({ at: new Vector24(at.x, -at.y), round }));
|
|
2723
3271
|
const meet = Math.acos(HANDLE_SHAFT_RADIUS / HANDLE_BEAD_RADIUS);
|
|
2724
3272
|
const bead = Array.from({ length: BEAD_STEPS + 1 }, (_, step) => {
|
|
2725
3273
|
const turn = -meet + 2 * meet * step / BEAD_STEPS;
|
|
2726
|
-
const at = new
|
|
3274
|
+
const at = new Vector24(Math.cos(turn), Math.sin(turn)).multiplyScalar(HANDLE_BEAD_RADIUS);
|
|
2727
3275
|
return { at, round: step === 0 || step === BEAD_STEPS ? BEAD_ROUND : 0 };
|
|
2728
3276
|
});
|
|
2729
3277
|
return rounded([...lower, ...bead, ...upper]);
|
|
@@ -2740,17 +3288,17 @@ var handleGeometry = () => turned(handleProfile());
|
|
|
2740
3288
|
import {
|
|
2741
3289
|
AlwaysStencilFunc,
|
|
2742
3290
|
BackSide,
|
|
2743
|
-
Color as
|
|
3291
|
+
Color as Color4,
|
|
2744
3292
|
DecrementWrapStencilOp,
|
|
2745
|
-
DoubleSide as
|
|
3293
|
+
DoubleSide as DoubleSide4,
|
|
2746
3294
|
FrontSide,
|
|
2747
3295
|
IncrementWrapStencilOp,
|
|
2748
3296
|
LinearFilter,
|
|
2749
|
-
MeshBasicMaterial as
|
|
2750
|
-
NotEqualStencilFunc,
|
|
2751
|
-
ReplaceStencilOp,
|
|
2752
|
-
ShaderMaterial as
|
|
2753
|
-
Vector2 as
|
|
3297
|
+
MeshBasicMaterial as MeshBasicMaterial3,
|
|
3298
|
+
NotEqualStencilFunc as NotEqualStencilFunc2,
|
|
3299
|
+
ReplaceStencilOp as ReplaceStencilOp2,
|
|
3300
|
+
ShaderMaterial as ShaderMaterial3,
|
|
3301
|
+
Vector2 as Vector25,
|
|
2754
3302
|
WebGLRenderTarget
|
|
2755
3303
|
} from "three";
|
|
2756
3304
|
var VERTEX2 = (
|
|
@@ -2809,25 +3357,25 @@ function createMaskTarget() {
|
|
|
2809
3357
|
function createCapMaterial(mask) {
|
|
2810
3358
|
const uniforms = {
|
|
2811
3359
|
uMask: { value: mask },
|
|
2812
|
-
uResolution: { value: new
|
|
2813
|
-
uFill: { value: new
|
|
2814
|
-
uHatch: { value: new
|
|
2815
|
-
uOutline: { value: new
|
|
3360
|
+
uResolution: { value: new Vector25(1, 1) },
|
|
3361
|
+
uFill: { value: new Color4() },
|
|
3362
|
+
uHatch: { value: new Color4() },
|
|
3363
|
+
uOutline: { value: new Color4() },
|
|
2816
3364
|
uPitch: { value: CAP_HATCH.pitch },
|
|
2817
3365
|
uLine: { value: CAP_HATCH.line },
|
|
2818
3366
|
uThickness: { value: CAP_HATCH.outline }
|
|
2819
3367
|
};
|
|
2820
|
-
return new
|
|
3368
|
+
return new ShaderMaterial3({
|
|
2821
3369
|
vertexShader: VERTEX2,
|
|
2822
3370
|
fragmentShader: FRAGMENT2,
|
|
2823
3371
|
uniforms,
|
|
2824
|
-
side:
|
|
3372
|
+
side: DoubleSide4,
|
|
2825
3373
|
stencilWrite: true,
|
|
2826
3374
|
stencilRef: 0,
|
|
2827
|
-
stencilFunc:
|
|
2828
|
-
stencilFail:
|
|
2829
|
-
stencilZFail:
|
|
2830
|
-
stencilZPass:
|
|
3375
|
+
stencilFunc: NotEqualStencilFunc2,
|
|
3376
|
+
stencilFail: ReplaceStencilOp2,
|
|
3377
|
+
stencilZFail: ReplaceStencilOp2,
|
|
3378
|
+
stencilZPass: ReplaceStencilOp2
|
|
2831
3379
|
});
|
|
2832
3380
|
}
|
|
2833
3381
|
function applyCapTheme(material, theme, dpr) {
|
|
@@ -2848,14 +3396,14 @@ function createStencilMaterials(clip) {
|
|
|
2848
3396
|
clippingPlanes: clip
|
|
2849
3397
|
};
|
|
2850
3398
|
return [
|
|
2851
|
-
new
|
|
3399
|
+
new MeshBasicMaterial3({
|
|
2852
3400
|
...shared,
|
|
2853
3401
|
side: BackSide,
|
|
2854
3402
|
stencilFail: IncrementWrapStencilOp,
|
|
2855
3403
|
stencilZFail: IncrementWrapStencilOp,
|
|
2856
3404
|
stencilZPass: IncrementWrapStencilOp
|
|
2857
3405
|
}),
|
|
2858
|
-
new
|
|
3406
|
+
new MeshBasicMaterial3({
|
|
2859
3407
|
...shared,
|
|
2860
3408
|
side: FrontSide,
|
|
2861
3409
|
stencilFail: DecrementWrapStencilOp,
|
|
@@ -2865,15 +3413,15 @@ function createStencilMaterials(clip) {
|
|
|
2865
3413
|
];
|
|
2866
3414
|
}
|
|
2867
3415
|
function createMaskMaterial() {
|
|
2868
|
-
return new
|
|
3416
|
+
return new MeshBasicMaterial3({
|
|
2869
3417
|
color: 16777215,
|
|
2870
|
-
side:
|
|
3418
|
+
side: DoubleSide4,
|
|
2871
3419
|
stencilWrite: true,
|
|
2872
3420
|
stencilRef: 0,
|
|
2873
|
-
stencilFunc:
|
|
2874
|
-
stencilFail:
|
|
2875
|
-
stencilZFail:
|
|
2876
|
-
stencilZPass:
|
|
3421
|
+
stencilFunc: NotEqualStencilFunc2,
|
|
3422
|
+
stencilFail: ReplaceStencilOp2,
|
|
3423
|
+
stencilZFail: ReplaceStencilOp2,
|
|
3424
|
+
stencilZPass: ReplaceStencilOp2
|
|
2877
3425
|
});
|
|
2878
3426
|
}
|
|
2879
3427
|
|
|
@@ -2883,7 +3431,7 @@ var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
|
2883
3431
|
function resolveSectionPlane(options, box) {
|
|
2884
3432
|
if (!options?.enabled || box.isEmpty()) return null;
|
|
2885
3433
|
const normal = options.plane?.normal ?? options.normal ?? DEFAULT_SECTION_NORMAL;
|
|
2886
|
-
const axis = new
|
|
3434
|
+
const axis = new Vector311(normal.x, normal.y, normal.z);
|
|
2887
3435
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
2888
3436
|
axis.normalize();
|
|
2889
3437
|
const bounds = sectionBounds(box, normal);
|
|
@@ -2904,7 +3452,7 @@ function resolveSectionPlane(options, box) {
|
|
|
2904
3452
|
}
|
|
2905
3453
|
};
|
|
2906
3454
|
}
|
|
2907
|
-
var Z = new
|
|
3455
|
+
var Z = new Vector311(0, 0, 1);
|
|
2908
3456
|
var SectionView = ({
|
|
2909
3457
|
geometry,
|
|
2910
3458
|
box,
|
|
@@ -2924,7 +3472,7 @@ var SectionView = ({
|
|
|
2924
3472
|
const handleRef = useRef4(null);
|
|
2925
3473
|
const [hovered, setHovered] = useState(false);
|
|
2926
3474
|
const dragging = useRef4(null);
|
|
2927
|
-
const span = useMemo6(() => box.getSize(new
|
|
3475
|
+
const span = useMemo6(() => box.getSize(new Vector311()).length(), [box]);
|
|
2928
3476
|
const frameSize = span * SECTION_GIZMO_FRAME_SCALE;
|
|
2929
3477
|
const directionColor2 = useMemo6(() => sectionDirectionColor(plane.normal), [plane]);
|
|
2930
3478
|
const resolvedHandleColor = sectionHandleColor(handleColor, directionColor2);
|
|
@@ -2955,11 +3503,11 @@ var SectionView = ({
|
|
|
2955
3503
|
const maskScene = useMemo6(() => {
|
|
2956
3504
|
const scene = new Scene();
|
|
2957
3505
|
const [back, front] = stencils;
|
|
2958
|
-
const backMesh = new
|
|
3506
|
+
const backMesh = new Mesh4(geometry, back);
|
|
2959
3507
|
backMesh.renderOrder = SECTION_RENDER_ORDER.stencil;
|
|
2960
|
-
const frontMesh = new
|
|
3508
|
+
const frontMesh = new Mesh4(geometry, front);
|
|
2961
3509
|
frontMesh.renderOrder = SECTION_RENDER_ORDER.stencil;
|
|
2962
|
-
const maskMesh = new
|
|
3510
|
+
const maskMesh = new Mesh4(quad, createMaskMaterial());
|
|
2963
3511
|
maskMesh.renderOrder = SECTION_RENDER_ORDER.cap;
|
|
2964
3512
|
scene.add(backMesh, frontMesh, maskMesh);
|
|
2965
3513
|
return { scene, maskMesh };
|
|
@@ -2970,13 +3518,13 @@ var SectionView = ({
|
|
|
2970
3518
|
maskScene.maskMesh.quaternion.copy(capQuaternion);
|
|
2971
3519
|
}, [capPosition, capQuaternion, maskScene]);
|
|
2972
3520
|
useLayoutEffect2(() => {
|
|
2973
|
-
const buffer = gl.getDrawingBufferSize(new
|
|
3521
|
+
const buffer = gl.getDrawingBufferSize(new Vector26());
|
|
2974
3522
|
mask.setSize(buffer.x, buffer.y);
|
|
2975
3523
|
cap.uniforms.uResolution.value.copy(buffer);
|
|
2976
3524
|
applyCapTheme(cap, theme, dpr);
|
|
2977
3525
|
invalidate();
|
|
2978
3526
|
}, [cap, dpr, gl, invalidate, mask, size, theme]);
|
|
2979
|
-
const clearColor = useMemo6(() => new
|
|
3527
|
+
const clearColor = useMemo6(() => new Color5(), []);
|
|
2980
3528
|
useFrame4(({ gl: renderer, camera: eye }) => {
|
|
2981
3529
|
const previousAlpha = renderer.getClearAlpha();
|
|
2982
3530
|
renderer.getClearColor(clearColor);
|
|
@@ -3008,7 +3556,7 @@ var SectionView = ({
|
|
|
3008
3556
|
event.stopPropagation();
|
|
3009
3557
|
const view = camera.position.clone().sub(capPosition).normalize();
|
|
3010
3558
|
const surface = dragPlane(plane.normal.clone(), view, capPosition);
|
|
3011
|
-
const hit = event.ray.intersectPlane(surface, new
|
|
3559
|
+
const hit = event.ray.intersectPlane(surface, new Vector311());
|
|
3012
3560
|
const start = {
|
|
3013
3561
|
plane: surface,
|
|
3014
3562
|
from: hit ? hit.dot(plane.normal) : 0,
|
|
@@ -3017,7 +3565,7 @@ var SectionView = ({
|
|
|
3017
3565
|
dragging.current = start;
|
|
3018
3566
|
setControlsEnabled(false);
|
|
3019
3567
|
const raycaster = new Raycaster2();
|
|
3020
|
-
const pointer = new
|
|
3568
|
+
const pointer = new Vector26();
|
|
3021
3569
|
const move = (native) => {
|
|
3022
3570
|
const rect = domElement.getBoundingClientRect();
|
|
3023
3571
|
pointer.set(
|
|
@@ -3025,7 +3573,7 @@ var SectionView = ({
|
|
|
3025
3573
|
-((native.clientY - rect.top) / rect.height) * 2 + 1
|
|
3026
3574
|
);
|
|
3027
3575
|
raycaster.setFromCamera(pointer, camera);
|
|
3028
|
-
const point = raycaster.ray.intersectPlane(start.plane, new
|
|
3576
|
+
const point = raycaster.ray.intersectPlane(start.plane, new Vector311());
|
|
3029
3577
|
if (!point) return;
|
|
3030
3578
|
onDrag(start.constant - (point.dot(plane.normal) - start.from));
|
|
3031
3579
|
invalidate();
|
|
@@ -3101,7 +3649,7 @@ var SectionView = ({
|
|
|
3101
3649
|
color: directionColor2,
|
|
3102
3650
|
transparent: true,
|
|
3103
3651
|
opacity: 0.18,
|
|
3104
|
-
side:
|
|
3652
|
+
side: DoubleSide5,
|
|
3105
3653
|
depthTest: false,
|
|
3106
3654
|
depthWrite: false
|
|
3107
3655
|
}
|
|
@@ -3175,10 +3723,11 @@ var PartMesh = ({
|
|
|
3175
3723
|
onPick,
|
|
3176
3724
|
theme,
|
|
3177
3725
|
showEdges = true,
|
|
3726
|
+
sharpCorners = null,
|
|
3178
3727
|
display = "solid",
|
|
3179
3728
|
children
|
|
3180
3729
|
}) => {
|
|
3181
|
-
const { camera, controls, invalidate } = useThree8();
|
|
3730
|
+
const { camera, controls, invalidate, size } = useThree8();
|
|
3182
3731
|
const context = useMemo7(() => ({ model, geometry }), [model, geometry]);
|
|
3183
3732
|
const viewerControls = useViewerControls();
|
|
3184
3733
|
const resolved = useStableTheme(theme);
|
|
@@ -3186,6 +3735,7 @@ var PartMesh = ({
|
|
|
3186
3735
|
currentTheme.current = resolved;
|
|
3187
3736
|
const part = useMemo7(() => createPart(model, geometry, currentTheme.current), [geometry, model]);
|
|
3188
3737
|
const hoverRegion = useRef6(null);
|
|
3738
|
+
const hoverCorner = useRef6(null);
|
|
3189
3739
|
const onHoverRef = useRef6(onHover);
|
|
3190
3740
|
onHoverRef.current = onHover;
|
|
3191
3741
|
const box = useContentBox();
|
|
@@ -3216,11 +3766,24 @@ var PartMesh = ({
|
|
|
3216
3766
|
const { hoveredFeatureIds: hoveredFeatures, ...rest } = layers.current;
|
|
3217
3767
|
applyHighlightLayers(
|
|
3218
3768
|
part,
|
|
3219
|
-
{
|
|
3769
|
+
{
|
|
3770
|
+
...rest,
|
|
3771
|
+
hoveredFeatures,
|
|
3772
|
+
// On a corner the corner is what is hovered, in the hover colour; the
|
|
3773
|
+
// face it lies on is not painted as if the pointer meant it.
|
|
3774
|
+
hoverRegion: hoverCorner.current === null ? hoverRegion.current : null
|
|
3775
|
+
},
|
|
3220
3776
|
currentTheme.current
|
|
3221
3777
|
);
|
|
3222
3778
|
invalidate();
|
|
3223
3779
|
}, [invalidate, part]);
|
|
3780
|
+
const dropHover = useCallback3(() => {
|
|
3781
|
+
hoverRegion.current = null;
|
|
3782
|
+
hoverCorner.current = null;
|
|
3783
|
+
part.setHoveredSharpCorner(null);
|
|
3784
|
+
repaint();
|
|
3785
|
+
onHoverRef.current?.(null);
|
|
3786
|
+
}, [part, repaint]);
|
|
3224
3787
|
useEffect7(() => () => part.dispose(), [part]);
|
|
3225
3788
|
const adjacency = useMemo7(() => regionAdjacency(model, geometry), [model, geometry]);
|
|
3226
3789
|
useEffect7(() => {
|
|
@@ -3254,6 +3817,14 @@ var PartMesh = ({
|
|
|
3254
3817
|
part.setClippingPlanes(cut ? [cut.plane] : null);
|
|
3255
3818
|
invalidate();
|
|
3256
3819
|
}, [cut, invalidate, part]);
|
|
3820
|
+
const sharpCornersRef = useRef6(sharpCorners);
|
|
3821
|
+
sharpCornersRef.current = sharpCorners;
|
|
3822
|
+
const sharpKey = sharpCorners ? sharpCornerKey(sharpCorners) : "";
|
|
3823
|
+
useLayoutEffect3(() => {
|
|
3824
|
+
part.setSharpCorners(sharpCornersRef.current);
|
|
3825
|
+
if (hoverCorner.current !== null) dropHover();
|
|
3826
|
+
invalidate();
|
|
3827
|
+
}, [dropHover, invalidate, part, sharpKey]);
|
|
3257
3828
|
const reportedSection = useRef6("");
|
|
3258
3829
|
useLayoutEffect3(() => {
|
|
3259
3830
|
const state = cut?.state ?? null;
|
|
@@ -3286,7 +3857,7 @@ var PartMesh = ({
|
|
|
3286
3857
|
const target = readTarget(controls);
|
|
3287
3858
|
const normal = event.face?.normal ?? UP;
|
|
3288
3859
|
const source = event.nativeEvent;
|
|
3289
|
-
|
|
3860
|
+
const pick = buildPick({
|
|
3290
3861
|
model,
|
|
3291
3862
|
region,
|
|
3292
3863
|
triangleIndex,
|
|
@@ -3304,26 +3875,27 @@ var PartMesh = ({
|
|
|
3304
3875
|
secondary: "button" in source && source.button === 2
|
|
3305
3876
|
}
|
|
3306
3877
|
});
|
|
3878
|
+
const corner = part.sharpCornerAt({ point: event.point, region }, camera, size);
|
|
3879
|
+
return corner ? { ...pick, sharpCorner: corner } : pick;
|
|
3307
3880
|
};
|
|
3308
3881
|
const emitHover = (next) => {
|
|
3309
3882
|
const region = next?.region ?? null;
|
|
3310
|
-
|
|
3883
|
+
const corner = next?.sharpCorner?.index ?? null;
|
|
3884
|
+
if (hoverRegion.current === region && hoverCorner.current === corner) return;
|
|
3311
3885
|
hoverRegion.current = region;
|
|
3886
|
+
hoverCorner.current = corner;
|
|
3887
|
+
part.setHoveredSharpCorner(corner);
|
|
3312
3888
|
repaint();
|
|
3313
3889
|
onHover?.(next);
|
|
3314
3890
|
};
|
|
3315
3891
|
useLayoutEffect3(() => {
|
|
3316
3892
|
if (hover || hoverRegion.current === null) return;
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
onHoverRef.current?.(null);
|
|
3320
|
-
}, [hover, repaint]);
|
|
3893
|
+
dropHover();
|
|
3894
|
+
}, [dropHover, hover]);
|
|
3321
3895
|
useLayoutEffect3(() => {
|
|
3322
3896
|
if (!engaged || hoverRegion.current === null) return;
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
onHoverRef.current?.(null);
|
|
3326
|
-
}, [engaged, repaint]);
|
|
3897
|
+
dropHover();
|
|
3898
|
+
}, [dropHover, engaged]);
|
|
3327
3899
|
const dragSection = useCallback3(
|
|
3328
3900
|
(constant) => {
|
|
3329
3901
|
if (!cut) return;
|
|
@@ -3395,9 +3967,9 @@ var PartMesh = ({
|
|
|
3395
3967
|
children ? /* @__PURE__ */ jsx6(PartContext.Provider, { value: context, children }) : null
|
|
3396
3968
|
] });
|
|
3397
3969
|
};
|
|
3398
|
-
var ORIGIN = new
|
|
3399
|
-
var UP = new
|
|
3400
|
-
var TARGET = new
|
|
3970
|
+
var ORIGIN = new Vector312();
|
|
3971
|
+
var UP = new Vector312(0, 0, 1);
|
|
3972
|
+
var TARGET = new Vector312();
|
|
3401
3973
|
function readTarget(controls) {
|
|
3402
3974
|
if (controls && typeof controls.getTarget === "function") {
|
|
3403
3975
|
return controls.getTarget(TARGET);
|
|
@@ -3413,7 +3985,7 @@ function useStableTheme(theme) {
|
|
|
3413
3985
|
}
|
|
3414
3986
|
|
|
3415
3987
|
// src/engine/geometry.ts
|
|
3416
|
-
import { Matrix4 as Matrix42, Mesh as
|
|
3988
|
+
import { Matrix4 as Matrix42, Mesh as Mesh5 } from "three";
|
|
3417
3989
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3418
3990
|
import { STLLoader } from "three/examples/jsm/loaders/STLLoader.js";
|
|
3419
3991
|
var PartMeshError = class extends Error {
|
|
@@ -3488,7 +4060,7 @@ function parseStl(data, mesh) {
|
|
|
3488
4060
|
function singleMesh(scene) {
|
|
3489
4061
|
const meshes = [];
|
|
3490
4062
|
scene.traverse((object) => {
|
|
3491
|
-
if (object instanceof
|
|
4063
|
+
if (object instanceof Mesh5) meshes.push(object);
|
|
3492
4064
|
});
|
|
3493
4065
|
const [mesh] = meshes;
|
|
3494
4066
|
if (mesh === void 0 || meshes.length !== 1) {
|
|
@@ -3524,7 +4096,7 @@ function assertCounts(geometry, expected) {
|
|
|
3524
4096
|
}
|
|
3525
4097
|
function disposeMaterials(scene) {
|
|
3526
4098
|
scene.traverse((object) => {
|
|
3527
|
-
if (!(object instanceof
|
|
4099
|
+
if (!(object instanceof Mesh5)) return;
|
|
3528
4100
|
for (const material of Array.isArray(object.material) ? object.material : [object.material]) {
|
|
3529
4101
|
material.dispose();
|
|
3530
4102
|
}
|
|
@@ -3532,7 +4104,7 @@ function disposeMaterials(scene) {
|
|
|
3532
4104
|
}
|
|
3533
4105
|
|
|
3534
4106
|
// src/engine/normals.ts
|
|
3535
|
-
import { Float32BufferAttribute as
|
|
4107
|
+
import { Float32BufferAttribute as Float32BufferAttribute5 } from "three";
|
|
3536
4108
|
function smoothRegionNormals(geometry, regions) {
|
|
3537
4109
|
const position = geometry.getAttribute("position");
|
|
3538
4110
|
if (!position || geometry.index) return;
|
|
@@ -3598,7 +4170,7 @@ function smoothRegionNormals(geometry, regions) {
|
|
|
3598
4170
|
normals[vertex * 3 + 1] = y / length;
|
|
3599
4171
|
normals[vertex * 3 + 2] = z / length;
|
|
3600
4172
|
}
|
|
3601
|
-
geometry.setAttribute("normal", new
|
|
4173
|
+
geometry.setAttribute("normal", new Float32BufferAttribute5(normals, 3));
|
|
3602
4174
|
}
|
|
3603
4175
|
|
|
3604
4176
|
// src/engine/geometry-cache.ts
|
|
@@ -4138,11 +4710,14 @@ export {
|
|
|
4138
4710
|
useTapGuard,
|
|
4139
4711
|
visualSurfaces,
|
|
4140
4712
|
regionEdgesGeometry,
|
|
4713
|
+
regionAdjacency,
|
|
4714
|
+
surfaceCreases,
|
|
4715
|
+
findSharpCorners,
|
|
4716
|
+
sharpCornerPositions,
|
|
4141
4717
|
REGION_ATTRIBUTE,
|
|
4142
4718
|
buildRegionTexels,
|
|
4143
4719
|
buildRegionAttribute,
|
|
4144
4720
|
createPart,
|
|
4145
|
-
regionAdjacency,
|
|
4146
4721
|
FEATURE_TYPE_RANKS,
|
|
4147
4722
|
featureTypeRank,
|
|
4148
4723
|
rankOwners,
|