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