@d5techs/3dgs-lib 1.4.8 → 1.4.10

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/dist/3dgs-lib.cjs CHANGED
@@ -1003,514 +1003,224 @@ class OrbitControls {
1003
1003
  this.velocityPanZ = 0;
1004
1004
  }
1005
1005
  }
1006
- const gizmoShaderCode = (
1007
- /* wgsl */
1008
- `
1009
- struct Uniforms {
1010
- viewMatrix: mat4x4<f32>,
1011
- projMatrix: mat4x4<f32>,
1012
- }
1013
-
1014
- @group(0) @binding(0) var<uniform> uniforms: Uniforms;
1015
-
1016
- struct VertexInput {
1017
- @location(0) position: vec3<f32>,
1018
- @location(1) color: vec3<f32>,
1019
- }
1020
-
1021
- struct VertexOutput {
1022
- @builtin(position) position: vec4<f32>,
1023
- @location(0) color: vec3<f32>,
1024
- }
1025
-
1026
- @vertex
1027
- fn vs_main(input: VertexInput) -> VertexOutput {
1028
- var output: VertexOutput;
1029
- let worldPos = vec4<f32>(input.position, 1.0);
1030
- output.position = uniforms.projMatrix * uniforms.viewMatrix * worldPos;
1031
- output.color = input.color;
1032
- return output;
1033
- }
1034
-
1035
- @fragment
1036
- fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
1037
- return vec4<f32>(input.color, 1.0);
1038
- }
1039
- `
1040
- );
1041
1006
  class ViewportGizmo {
1042
- constructor(renderer, camera, canvas) {
1043
- __publicField(this, "renderer");
1007
+ constructor(_renderer, camera, canvas) {
1044
1008
  __publicField(this, "camera");
1045
1009
  __publicField(this, "canvas");
1046
- // 渲染资源
1047
- __publicField(this, "pipeline");
1048
- __publicField(this, "uniformBuffer");
1049
- __publicField(this, "bindGroup");
1050
- __publicField(this, "vertexBuffer");
1051
- __publicField(this, "indexBuffer");
1052
- __publicField(this, "vertexCount", 0);
1053
- __publicField(this, "indexCount", 0);
1054
- // Gizmo 配置
1055
- __publicField(this, "size", 200);
1056
- // Gizmo 尺寸(像素)
1057
- __publicField(this, "margin", 20);
1058
- // 边距
1059
- // Gizmo 投影矩阵
1060
- __publicField(this, "projMatrix", new Float32Array(16));
1061
- __publicField(this, "viewMatrix", new Float32Array(16));
1062
- // 轴配置
1063
- __publicField(this, "axes", [
1064
- { direction: [1, 0, 0], color: [0.9, 0.2, 0.2], label: "X" },
1065
- // 红色 X
1066
- { direction: [0, 1, 0], color: [0.2, 0.9, 0.2], label: "Y" },
1067
- // 绿色 Y
1068
- { direction: [0, 0, 1], color: [0.2, 0.4, 0.9], label: "Z" }
1069
- // 蓝色 Z
1070
- ]);
1071
- // 交互回调
1010
+ __publicField(this, "container");
1011
+ __publicField(this, "svg");
1012
+ __publicField(this, "group");
1013
+ __publicField(this, "shapes");
1014
+ __publicField(this, "size", 140);
1015
+ __publicField(this, "margin", 10);
1016
+ __publicField(this, "scale", 40);
1017
+ __publicField(this, "_visible", false);
1072
1018
  __publicField(this, "onAxisClick");
1073
- this.renderer = renderer;
1019
+ __publicField(this, "resizeObserver");
1074
1020
  this.camera = camera;
1075
1021
  this.canvas = canvas;
1076
- this.createPipeline();
1077
- this.createGeometry();
1078
- this.createUniformBuffer();
1079
- this.setupOrthoProjection();
1022
+ this.createSVG();
1023
+ this.setupResizeObserver();
1080
1024
  }
1081
- /**
1082
- * 设置轴点击回调
1083
- */
1084
1025
  setOnAxisClick(callback) {
1085
1026
  this.onAxisClick = callback;
1086
1027
  }
1087
- /**
1088
- * 创建渲染管线
1089
- */
1090
- createPipeline() {
1091
- const device = this.renderer.device;
1092
- const shaderModule = device.createShaderModule({
1093
- code: gizmoShaderCode
1028
+ createSVG() {
1029
+ const ns = "http://www.w3.org/2000/svg";
1030
+ this.container = document.createElement("div");
1031
+ Object.assign(this.container.style, {
1032
+ position: "absolute",
1033
+ width: `${this.size}px`,
1034
+ height: `${this.size}px`,
1035
+ pointerEvents: "none",
1036
+ zIndex: "10",
1037
+ display: this._visible ? "" : "none"
1094
1038
  });
1095
- const bindGroupLayout = device.createBindGroupLayout({
1096
- entries: [
1097
- {
1098
- binding: 0,
1099
- visibility: GPUShaderStage.VERTEX,
1100
- buffer: { type: "uniform" }
1101
- }
1102
- ]
1103
- });
1104
- const pipelineLayout = device.createPipelineLayout({
1105
- bindGroupLayouts: [bindGroupLayout]
1106
- });
1107
- const vertexBufferLayout = {
1108
- arrayStride: 24,
1109
- attributes: [
1110
- { shaderLocation: 0, offset: 0, format: "float32x3" },
1111
- { shaderLocation: 1, offset: 12, format: "float32x3" }
1112
- ]
1039
+ this.positionContainer();
1040
+ this.svg = document.createElementNS(ns, "svg");
1041
+ this.svg.setAttribute("width", this.size.toString());
1042
+ this.svg.setAttribute("height", this.size.toString());
1043
+ this.group = document.createElementNS(ns, "g");
1044
+ this.group.setAttribute(
1045
+ "transform",
1046
+ `translate(${this.size / 2}, ${this.size / 2})`
1047
+ );
1048
+ this.svg.appendChild(this.group);
1049
+ const r = "#f44";
1050
+ const g = "#4f4";
1051
+ const b = "#77f";
1052
+ this.shapes = {
1053
+ nx: this.createCircle(r, false),
1054
+ ny: this.createCircle(g, false),
1055
+ nz: this.createCircle(b, false),
1056
+ xaxis: this.createLine(r),
1057
+ yaxis: this.createLine(g),
1058
+ zaxis: this.createLine(b),
1059
+ px: this.createCircle(r, true, "X"),
1060
+ py: this.createCircle(g, true, "Y"),
1061
+ pz: this.createCircle(b, true, "Z")
1113
1062
  };
1114
- this.pipeline = device.createRenderPipeline({
1115
- layout: pipelineLayout,
1116
- vertex: {
1117
- module: shaderModule,
1118
- entryPoint: "vs_main",
1119
- buffers: [vertexBufferLayout]
1120
- },
1121
- fragment: {
1122
- module: shaderModule,
1123
- entryPoint: "fs_main",
1124
- targets: [{ format: this.renderer.format }]
1125
- },
1126
- primitive: {
1127
- topology: "triangle-list",
1128
- cullMode: "none"
1129
- },
1130
- depthStencil: {
1131
- format: this.renderer.depthFormat,
1132
- depthWriteEnabled: false,
1133
- depthCompare: "always"
1063
+ this.bindClickEvents();
1064
+ this.container.appendChild(this.svg);
1065
+ const parent = this.canvas.parentElement;
1066
+ if (parent) {
1067
+ const pos = getComputedStyle(parent).position;
1068
+ if (pos === "static") {
1069
+ parent.style.position = "relative";
1134
1070
  }
1135
- });
1071
+ parent.appendChild(this.container);
1072
+ }
1073
+ }
1074
+ createCircle(color, fill, text) {
1075
+ const ns = this.svg.namespaceURI;
1076
+ const g = document.createElementNS(ns, "g");
1077
+ const circle = document.createElementNS(ns, "circle");
1078
+ circle.setAttribute("fill", fill ? color : "#222");
1079
+ circle.setAttribute("stroke", color);
1080
+ circle.setAttribute("stroke-width", "2");
1081
+ circle.setAttribute("r", "10");
1082
+ circle.setAttribute("cx", "0");
1083
+ circle.setAttribute("cy", "0");
1084
+ circle.setAttribute("pointer-events", "all");
1085
+ g.appendChild(circle);
1086
+ g.setAttribute("cursor", "pointer");
1087
+ if (text) {
1088
+ const t = document.createElementNS(ns, "text");
1089
+ t.setAttribute("font-size", "10");
1090
+ t.setAttribute("font-family", "Arial");
1091
+ t.setAttribute("font-weight", "bold");
1092
+ t.setAttribute("text-anchor", "middle");
1093
+ t.setAttribute("alignment-baseline", "central");
1094
+ t.setAttribute("fill", "#fff");
1095
+ t.setAttribute("pointer-events", "none");
1096
+ t.textContent = text;
1097
+ g.appendChild(t);
1098
+ }
1099
+ this.group.appendChild(g);
1100
+ return g;
1101
+ }
1102
+ createLine(color) {
1103
+ const ns = this.svg.namespaceURI;
1104
+ const line = document.createElementNS(ns, "line");
1105
+ line.setAttribute("stroke", color);
1106
+ line.setAttribute("stroke-width", "2");
1107
+ this.group.appendChild(line);
1108
+ return line;
1109
+ }
1110
+ bindClickEvents() {
1111
+ const bind = (shape, axis, positive) => {
1112
+ const circle = shape.children[0];
1113
+ circle.addEventListener("pointerdown", (e) => {
1114
+ e.stopPropagation();
1115
+ if (this.onAxisClick) {
1116
+ this.onAxisClick(axis, positive);
1117
+ }
1118
+ });
1119
+ };
1120
+ bind(this.shapes.px, "X", true);
1121
+ bind(this.shapes.py, "Y", true);
1122
+ bind(this.shapes.pz, "Z", true);
1123
+ bind(this.shapes.nx, "X", false);
1124
+ bind(this.shapes.ny, "Y", false);
1125
+ bind(this.shapes.nz, "Z", false);
1126
+ }
1127
+ positionContainer() {
1128
+ const parent = this.canvas.parentElement;
1129
+ if (!parent) return;
1130
+ const canvasRect = this.canvas.getBoundingClientRect();
1131
+ const parentRect = parent.getBoundingClientRect();
1132
+ const right = parentRect.right - canvasRect.right + this.margin;
1133
+ const top = canvasRect.top - parentRect.top + this.margin;
1134
+ this.container.style.right = `${right}px`;
1135
+ this.container.style.top = `${top}px`;
1136
1136
  }
1137
- /**
1138
- * 创建 Gizmo 几何体(三个轴 + 箭头)
1139
- */
1140
- createGeometry() {
1141
- const vertices = [];
1142
- const indices = [];
1143
- let vertexOffset = 0;
1144
- const axisLength = 0.8;
1145
- const axisRadius = 0.04;
1146
- const coneLength = 0.25;
1147
- const coneRadius = 0.1;
1148
- const segments = 12;
1149
- for (const axis of this.axes) {
1150
- const [dx, dy, dz] = axis.direction;
1151
- const [r, g, b] = axis.color;
1152
- const cylResult = this.createCylinder(
1153
- [0, 0, 0],
1154
- [dx * axisLength, dy * axisLength, dz * axisLength],
1155
- axisRadius,
1156
- segments,
1157
- [r, g, b],
1158
- vertexOffset
1159
- );
1160
- vertices.push(...cylResult.vertices);
1161
- indices.push(...cylResult.indices);
1162
- vertexOffset += cylResult.vertexCount;
1163
- const coneStart = [
1164
- dx * axisLength,
1165
- dy * axisLength,
1166
- dz * axisLength
1167
- ];
1168
- const coneEnd = [
1169
- dx * (axisLength + coneLength),
1170
- dy * (axisLength + coneLength),
1171
- dz * (axisLength + coneLength)
1172
- ];
1173
- const coneResult = this.createCone(
1174
- coneStart,
1175
- coneEnd,
1176
- coneRadius,
1177
- segments,
1178
- [r, g, b],
1179
- vertexOffset
1180
- );
1181
- vertices.push(...coneResult.vertices);
1182
- indices.push(...coneResult.indices);
1183
- vertexOffset += coneResult.vertexCount;
1184
- const sphereResult = this.createSphere(
1185
- [-dx * 0.15, -dy * 0.15, -dz * 0.15],
1186
- 0.08,
1187
- 8,
1188
- [r * 0.6, g * 0.6, b * 0.6],
1189
- vertexOffset
1190
- );
1191
- vertices.push(...sphereResult.vertices);
1192
- indices.push(...sphereResult.indices);
1193
- vertexOffset += sphereResult.vertexCount;
1194
- }
1195
- const centerResult = this.createSphere(
1196
- [0, 0, 0],
1197
- 0.1,
1198
- 12,
1199
- [0.5, 0.5, 0.5],
1200
- vertexOffset
1201
- );
1202
- vertices.push(...centerResult.vertices);
1203
- indices.push(...centerResult.indices);
1204
- this.vertexCount = vertices.length / 6;
1205
- this.indexCount = indices.length;
1206
- const vertexData = new Float32Array(vertices);
1207
- const indexData = new Uint16Array(indices);
1208
- const device = this.renderer.device;
1209
- this.vertexBuffer = device.createBuffer({
1210
- size: vertexData.byteLength,
1211
- usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
1212
- });
1213
- device.queue.writeBuffer(this.vertexBuffer, 0, vertexData);
1214
- this.indexBuffer = device.createBuffer({
1215
- size: indexData.byteLength,
1216
- usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST
1137
+ setupResizeObserver() {
1138
+ this.resizeObserver = new ResizeObserver(() => {
1139
+ this.positionContainer();
1217
1140
  });
1218
- device.queue.writeBuffer(this.indexBuffer, 0, indexData);
1141
+ this.resizeObserver.observe(this.canvas);
1219
1142
  }
1220
- /**
1221
- * 创建圆柱体几何
1222
- */
1223
- createCylinder(start, end, radius, segments, color, indexOffset) {
1224
- const vertices = [];
1225
- const indices = [];
1226
- const dx = end[0] - start[0];
1227
- const dy = end[1] - start[1];
1228
- const dz = end[2] - start[2];
1229
- const length = Math.sqrt(dx * dx + dy * dy + dz * dz);
1230
- const dir = [dx / length, dy / length, dz / length];
1231
- const up = Math.abs(dir[1]) < 0.99 ? [0, 1, 0] : [1, 0, 0];
1232
- const right = this.cross(
1233
- up,
1234
- dir
1235
- );
1236
- this.normalize(right);
1237
- const actualUp = this.cross(dir, right);
1238
- for (let i = 0; i <= segments; i++) {
1239
- const angle = i / segments * Math.PI * 2;
1240
- const cos = Math.cos(angle);
1241
- const sin = Math.sin(angle);
1242
- const nx0 = right[0] * cos + actualUp[0] * sin;
1243
- const ny0 = right[1] * cos + actualUp[1] * sin;
1244
- const nz0 = right[2] * cos + actualUp[2] * sin;
1245
- vertices.push(
1246
- start[0] + nx0 * radius,
1247
- start[1] + ny0 * radius,
1248
- start[2] + nz0 * radius,
1249
- color[0],
1250
- color[1],
1251
- color[2]
1252
- );
1253
- vertices.push(
1254
- end[0] + nx0 * radius,
1255
- end[1] + ny0 * radius,
1256
- end[2] + nz0 * radius,
1257
- color[0],
1258
- color[1],
1259
- color[2]
1260
- );
1261
- }
1262
- for (let i = 0; i < segments; i++) {
1263
- const i0 = indexOffset + i * 2;
1264
- const i1 = indexOffset + i * 2 + 1;
1265
- const i2 = indexOffset + (i + 1) * 2;
1266
- const i3 = indexOffset + (i + 1) * 2 + 1;
1267
- indices.push(i0, i1, i2, i2, i1, i3);
1268
- }
1269
- return { vertices, indices, vertexCount: (segments + 1) * 2 };
1143
+ get visible() {
1144
+ return this._visible;
1270
1145
  }
1271
- /**
1272
- * 创建圆锥几何
1273
- */
1274
- createCone(base, tip, radius, segments, color, indexOffset) {
1275
- const vertices = [];
1276
- const indices = [];
1277
- const dx = tip[0] - base[0];
1278
- const dy = tip[1] - base[1];
1279
- const dz = tip[2] - base[2];
1280
- const length = Math.sqrt(dx * dx + dy * dy + dz * dz);
1281
- const dir = [dx / length, dy / length, dz / length];
1282
- const up = Math.abs(dir[1]) < 0.99 ? [0, 1, 0] : [1, 0, 0];
1283
- const right = this.cross(
1284
- up,
1285
- dir
1286
- );
1287
- this.normalize(right);
1288
- const actualUp = this.cross(dir, right);
1289
- vertices.push(tip[0], tip[1], tip[2], color[0], color[1], color[2]);
1290
- for (let i = 0; i <= segments; i++) {
1291
- const angle = i / segments * Math.PI * 2;
1292
- const cos = Math.cos(angle);
1293
- const sin = Math.sin(angle);
1294
- const nx = right[0] * cos + actualUp[0] * sin;
1295
- const ny = right[1] * cos + actualUp[1] * sin;
1296
- const nz = right[2] * cos + actualUp[2] * sin;
1297
- vertices.push(
1298
- base[0] + nx * radius,
1299
- base[1] + ny * radius,
1300
- base[2] + nz * radius,
1301
- color[0],
1302
- color[1],
1303
- color[2]
1304
- );
1305
- }
1306
- for (let i = 0; i < segments; i++) {
1307
- indices.push(indexOffset, indexOffset + i + 1, indexOffset + i + 2);
1308
- }
1309
- const baseCenterIdx = indexOffset + segments + 2;
1310
- vertices.push(
1311
- base[0],
1312
- base[1],
1313
- base[2],
1314
- color[0] * 0.7,
1315
- color[1] * 0.7,
1316
- color[2] * 0.7
1317
- );
1318
- for (let i = 0; i < segments; i++) {
1319
- indices.push(baseCenterIdx, indexOffset + i + 2, indexOffset + i + 1);
1146
+ set visible(v) {
1147
+ this._visible = v;
1148
+ if (this.container) {
1149
+ this.container.style.display = v ? "" : "none";
1320
1150
  }
1321
- return { vertices, indices, vertexCount: segments + 3 };
1322
1151
  }
1323
1152
  /**
1324
- * 创建球体几何
1153
+ * 每帧更新 SVG 位置(从相机视图矩阵中提取轴投影)
1325
1154
  */
1326
- createSphere(center, radius, segments, color, indexOffset) {
1327
- const vertices = [];
1328
- const indices = [];
1329
- const rings = segments / 2;
1330
- for (let ring = 0; ring <= rings; ring++) {
1331
- const phi = ring / rings * Math.PI;
1332
- const sinPhi = Math.sin(phi);
1333
- const cosPhi = Math.cos(phi);
1334
- for (let seg = 0; seg <= segments; seg++) {
1335
- const theta = seg / segments * Math.PI * 2;
1336
- const x = center[0] + radius * sinPhi * Math.cos(theta);
1337
- const y = center[1] + radius * cosPhi;
1338
- const z = center[2] + radius * sinPhi * Math.sin(theta);
1339
- vertices.push(x, y, z, color[0], color[1], color[2]);
1340
- }
1341
- }
1342
- for (let ring = 0; ring < rings; ring++) {
1343
- for (let seg = 0; seg < segments; seg++) {
1344
- const current = indexOffset + ring * (segments + 1) + seg;
1345
- const next = current + segments + 1;
1346
- indices.push(current, next, current + 1);
1347
- indices.push(current + 1, next, next + 1);
1155
+ render(_pass) {
1156
+ if (!this._visible) return;
1157
+ const vm = this.camera.viewMatrix;
1158
+ const vecx = { x: vm[0], y: vm[1], z: vm[2] };
1159
+ const vecy = { x: vm[4], y: vm[5], z: vm[6] };
1160
+ const vecz = { x: vm[8], y: vm[9], z: vm[10] };
1161
+ const s = this.scale;
1162
+ const setTransform = (el, x, y) => {
1163
+ el.setAttribute("transform", `translate(${x * s}, ${y * s})`);
1164
+ };
1165
+ const setLine = (line, x, y) => {
1166
+ line.setAttribute("x2", (x * s).toString());
1167
+ line.setAttribute("y2", (y * s).toString());
1168
+ };
1169
+ setTransform(this.shapes.px, vecx.x, -vecx.y);
1170
+ setTransform(this.shapes.nx, -vecx.x, vecx.y);
1171
+ setTransform(this.shapes.py, vecy.x, -vecy.y);
1172
+ setTransform(this.shapes.ny, -vecy.x, vecy.y);
1173
+ setTransform(this.shapes.pz, vecz.x, -vecz.y);
1174
+ setTransform(this.shapes.nz, -vecz.x, vecz.y);
1175
+ setLine(this.shapes.xaxis, vecx.x, -vecx.y);
1176
+ setLine(this.shapes.yaxis, vecy.x, -vecy.y);
1177
+ setLine(this.shapes.zaxis, vecz.x, -vecz.y);
1178
+ const order = [
1179
+ { keys: ["xaxis", "px"], depth: vecx.z },
1180
+ { keys: ["yaxis", "py"], depth: vecy.z },
1181
+ { keys: ["zaxis", "pz"], depth: vecz.z },
1182
+ { keys: ["nx"], depth: -vecx.z },
1183
+ { keys: ["ny"], depth: -vecy.z },
1184
+ { keys: ["nz"], depth: -vecz.z }
1185
+ ].sort((a, b) => a.depth - b.depth);
1186
+ const fragment = document.createDocumentFragment();
1187
+ for (const item of order) {
1188
+ for (const key of item.keys) {
1189
+ fragment.appendChild(this.shapes[key]);
1348
1190
  }
1349
1191
  }
1350
- return { vertices, indices, vertexCount: (rings + 1) * (segments + 1) };
1192
+ this.group.appendChild(fragment);
1351
1193
  }
1352
1194
  /**
1353
- * 创建 uniform buffer
1195
+ * 点击检测 SVG 版本通过 DOM 事件处理,此方法仅保留接口兼容
1354
1196
  */
1355
- createUniformBuffer() {
1356
- const device = this.renderer.device;
1357
- this.uniformBuffer = device.createBuffer({
1358
- size: 128,
1359
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
1360
- });
1361
- const bindGroupLayout = this.pipeline.getBindGroupLayout(0);
1362
- this.bindGroup = device.createBindGroup({
1363
- layout: bindGroupLayout,
1364
- entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }]
1365
- });
1366
- }
1367
- /**
1368
- * 设置正交投影矩阵
1369
- */
1370
- setupOrthoProjection() {
1371
- const s = 1.5;
1372
- this.projMatrix[0] = 1 / s;
1373
- this.projMatrix[5] = 1 / s;
1374
- this.projMatrix[10] = -1 / 10;
1375
- this.projMatrix[14] = 0;
1376
- this.projMatrix[15] = 1;
1377
- }
1378
- /**
1379
- * 更新 Gizmo 视图矩阵(从相机提取旋转部分)
1380
- */
1381
- updateViewMatrix() {
1382
- const camView = this.camera.viewMatrix;
1383
- this.viewMatrix[0] = camView[0];
1384
- this.viewMatrix[1] = camView[1];
1385
- this.viewMatrix[2] = camView[2];
1386
- this.viewMatrix[3] = 0;
1387
- this.viewMatrix[4] = camView[4];
1388
- this.viewMatrix[5] = camView[5];
1389
- this.viewMatrix[6] = camView[6];
1390
- this.viewMatrix[7] = 0;
1391
- this.viewMatrix[8] = camView[8];
1392
- this.viewMatrix[9] = camView[9];
1393
- this.viewMatrix[10] = camView[10];
1394
- this.viewMatrix[11] = 0;
1395
- this.viewMatrix[12] = 0;
1396
- this.viewMatrix[13] = 0;
1397
- this.viewMatrix[14] = -3;
1398
- this.viewMatrix[15] = 1;
1399
- }
1400
- /**
1401
- * 渲染 Gizmo
1402
- */
1403
- render(pass) {
1404
- this.updateViewMatrix();
1405
- const dpr = window.devicePixelRatio || 1;
1406
- let gizmoSize = Math.floor(this.size * dpr);
1407
- const marginX = Math.floor(this.margin * dpr);
1408
- const marginY = Math.floor(this.margin * dpr);
1409
- const maxSize = Math.min(
1410
- this.canvas.width - marginX * 2,
1411
- this.canvas.height - marginY * 2
1412
- );
1413
- if (maxSize < 50) {
1414
- return;
1415
- }
1416
- gizmoSize = Math.min(gizmoSize, maxSize);
1417
- const x = Math.max(0, this.canvas.width - gizmoSize - marginX);
1418
- const y = marginY;
1419
- pass.setViewport(x, y, gizmoSize, gizmoSize, 0, 1);
1420
- pass.setScissorRect(x, y, gizmoSize, gizmoSize);
1421
- this.renderer.device.queue.writeBuffer(
1422
- this.uniformBuffer,
1423
- 0,
1424
- new Float32Array(this.viewMatrix)
1425
- );
1426
- this.renderer.device.queue.writeBuffer(
1427
- this.uniformBuffer,
1428
- 64,
1429
- new Float32Array(this.projMatrix)
1430
- );
1431
- pass.setPipeline(this.pipeline);
1432
- pass.setBindGroup(0, this.bindGroup);
1433
- pass.setVertexBuffer(0, this.vertexBuffer);
1434
- pass.setIndexBuffer(this.indexBuffer, "uint16");
1435
- pass.drawIndexed(this.indexCount);
1436
- pass.setViewport(0, 0, this.canvas.width, this.canvas.height, 0, 1);
1437
- pass.setScissorRect(0, 0, this.canvas.width, this.canvas.height);
1438
- }
1439
- /**
1440
- * 处理点击事件,检测是否点击了某个轴
1441
- */
1442
- handleClick(clientX, clientY) {
1443
- const rect = this.canvas.getBoundingClientRect();
1444
- const gizmoSize = this.size;
1445
- const marginX = this.margin;
1446
- const marginY = this.margin;
1447
- const gizmoLeft = rect.right - gizmoSize - marginX;
1448
- const gizmoTop = rect.top + marginY;
1449
- const gizmoRight = gizmoLeft + gizmoSize;
1450
- const gizmoBottom = gizmoTop + gizmoSize;
1451
- if (clientX < gizmoLeft || clientX > gizmoRight || clientY < gizmoTop || clientY > gizmoBottom) {
1452
- return false;
1453
- }
1454
- const relX = (clientX - gizmoLeft) / gizmoSize * 2 - 1;
1455
- const relY = -((clientY - gizmoTop) / gizmoSize * 2 - 1);
1456
- const clickedAxis = this.detectClickedAxis(relX, relY);
1457
- if (clickedAxis && this.onAxisClick) {
1458
- this.onAxisClick(clickedAxis.axis, clickedAxis.positive);
1459
- return true;
1460
- }
1197
+ handleClick(_clientX, _clientY) {
1461
1198
  return false;
1462
1199
  }
1463
- /**
1464
- * 检测点击了哪个轴
1465
- */
1466
- detectClickedAxis(relX, relY) {
1467
- const threshold = 0.4;
1468
- for (const axis of this.axes) {
1469
- const [dx, dy, dz] = axis.direction;
1470
- const posX = this.viewMatrix[0] * dx + this.viewMatrix[4] * dy + this.viewMatrix[8] * dz;
1471
- const posY = this.viewMatrix[1] * dx + this.viewMatrix[5] * dy + this.viewMatrix[9] * dz;
1472
- const distPos = Math.sqrt(
1473
- (relX - posX * 0.5) ** 2 + (relY - posY * 0.5) ** 2
1474
- );
1475
- if (distPos < threshold) {
1476
- return { axis: axis.label, positive: true };
1477
- }
1478
- const distNeg = Math.sqrt(
1479
- (relX + posX * 0.15) ** 2 + (relY + posY * 0.15) ** 2
1480
- );
1481
- if (distNeg < threshold * 0.5) {
1482
- return { axis: axis.label, positive: false };
1483
- }
1484
- }
1485
- return null;
1486
- }
1487
- // 向量工具函数
1488
- cross(a, b) {
1489
- return [
1490
- a[1] * b[2] - a[2] * b[1],
1491
- a[2] * b[0] - a[0] * b[2],
1492
- a[0] * b[1] - a[1] * b[0]
1493
- ];
1494
- }
1495
- normalize(v) {
1496
- const len = Math.sqrt(v[0] ** 2 + v[1] ** 2 + v[2] ** 2);
1497
- if (len > 0) {
1498
- v[0] /= len;
1499
- v[1] /= len;
1500
- v[2] /= len;
1501
- }
1502
- }
1503
- /**
1504
- * 设置 Gizmo 大小
1505
- */
1506
1200
  setSize(size) {
1507
1201
  this.size = size;
1202
+ if (this.container) {
1203
+ this.container.style.width = `${size}px`;
1204
+ this.container.style.height = `${size}px`;
1205
+ this.svg.setAttribute("width", size.toString());
1206
+ this.svg.setAttribute("height", size.toString());
1207
+ this.group.setAttribute(
1208
+ "transform",
1209
+ `translate(${size / 2}, ${size / 2})`
1210
+ );
1211
+ this.positionContainer();
1212
+ }
1508
1213
  }
1509
- /**
1510
- * 设置边距
1511
- */
1512
1214
  setMargin(margin) {
1513
1215
  this.margin = margin;
1216
+ this.positionContainer();
1217
+ }
1218
+ destroy() {
1219
+ var _a2;
1220
+ if (this.resizeObserver) {
1221
+ this.resizeObserver.disconnect();
1222
+ }
1223
+ (_a2 = this.container) == null ? void 0 : _a2.remove();
1514
1224
  }
1515
1225
  }
1516
1226
  class BoundingBoxRenderer {
@@ -13533,6 +13243,9 @@ class GizmoManager {
13533
13243
  getViewportGizmo() {
13534
13244
  return this.viewportGizmo;
13535
13245
  }
13246
+ setViewportGizmoVisible(visible) {
13247
+ this.viewportGizmo.visible = visible;
13248
+ }
13536
13249
  // ============================================
13537
13250
  // Bounding Box
13538
13251
  // ============================================
@@ -13592,6 +13305,7 @@ class GizmoManager {
13592
13305
  this.canvas.removeEventListener("pointermove", this.boundOnPointerMove);
13593
13306
  this.canvas.removeEventListener("pointerdown", this.boundOnPointerDown);
13594
13307
  this.canvas.removeEventListener("pointerup", this.boundOnPointerUp);
13308
+ this.viewportGizmo.destroy();
13595
13309
  this.transformGizmo.destroy();
13596
13310
  this.boundingBoxRenderer.destroy();
13597
13311
  }
@@ -16833,6 +16547,9 @@ class App {
16833
16547
  getViewportGizmo() {
16834
16548
  return this.gizmoManager.getViewportGizmo();
16835
16549
  }
16550
+ setViewportGizmoVisible(visible) {
16551
+ this.gizmoManager.setViewportGizmoVisible(visible);
16552
+ }
16836
16553
  getBoundingBoxRenderer() {
16837
16554
  return this.gizmoManager.getBoundingBoxRenderer();
16838
16555
  }