@omrips/viewstl 2.0.0-rc.6 → 2.0.0-rc.7
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 +3 -3
- package/build/chunks/scene-export-7P6ZJOG3.js +1 -0
- package/build/stl_viewer.min.js +6 -5
- package/build/viewstl.cjs +118 -17
- package/build/viewstl.js +119 -17
- package/build/viewstl.min.js +6 -5
- package/build/viewstl.worker.js +1 -1
- package/docs/releases/2.0.0-rc.7.md +16 -0
- package/docs/v2-usage.md +15 -0
- package/docs/v2-verification.md +43 -0
- package/package.json +2 -2
- package/build/chunks/scene-export-FPPL466I.js +0 -1
- package/docs/releases/2.0.0-rc.6.md +0 -7
package/build/viewstl.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//=========== Stl Viewer v2.0.0-rc.7, by Omri Rips, Viewstl.com, November 2026 ; admin@viewstl.com ===========
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -2369,7 +2370,7 @@ function modelOptions(input) {
|
|
|
2369
2370
|
return out;
|
|
2370
2371
|
}
|
|
2371
2372
|
var StlViewer = class _StlViewer {
|
|
2372
|
-
static version = "2.0.0-rc.
|
|
2373
|
+
static version = "2.0.0-rc.7";
|
|
2373
2374
|
static workerURL = null;
|
|
2374
2375
|
constructor(parent, options = {}) {
|
|
2375
2376
|
if (!parent?.appendChild)
|
|
@@ -3823,7 +3824,7 @@ var StlViewer = class _StlViewer {
|
|
|
3823
3824
|
null,
|
|
3824
3825
|
scene.files.map((f) => f.bin)
|
|
3825
3826
|
);
|
|
3826
|
-
return new Blob([await job], { type: "application/
|
|
3827
|
+
return new Blob([await job], { type: "application/octet-stream" });
|
|
3827
3828
|
}
|
|
3828
3829
|
download_vsb(filename = "1.vsb") {
|
|
3829
3830
|
return this.get_vsb_blob().then(
|
|
@@ -5593,7 +5594,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5593
5594
|
let animationUI;
|
|
5594
5595
|
let toolsUI;
|
|
5595
5596
|
let sceneImport;
|
|
5596
|
-
let draining = false, busy = false, gridEnabled = false, gridHeight = 0, gridSpacing = 1, moveMode = false, transformTool = "move", modelMover, dragArrangement, background = "transparent";
|
|
5597
|
+
let draining = false, busy = false, gridEnabled = false, gridHeight = 0, gridSpacing = 1, moveMode = false, selectionMode = false, selectionPointer = null, transformTool = "move", modelMover, dragArrangement, background = "transparent";
|
|
5597
5598
|
const viewNames = {
|
|
5598
5599
|
flat: "Solid",
|
|
5599
5600
|
edges: "Solid + edges",
|
|
@@ -5604,6 +5605,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5604
5605
|
orientation: "Face orientation"
|
|
5605
5606
|
};
|
|
5606
5607
|
const modeInputs = [...root.querySelectorAll('[name="view-mode"]')];
|
|
5608
|
+
const shortcutButtons = [...root.querySelectorAll("[data-viewstl-shortcut]")];
|
|
5607
5609
|
if (options.id_prefix)
|
|
5608
5610
|
modeInputs.forEach(
|
|
5609
5611
|
(input) => input.name = options.id_prefix + "view-mode"
|
|
@@ -5623,7 +5625,44 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5623
5625
|
}
|
|
5624
5626
|
function syncSelection() {
|
|
5625
5627
|
viewer?.set_selection(
|
|
5626
|
-
!busy && records.has(modelID) && (moveMode || !$("scene-panel").hidden) ? modelID : null
|
|
5628
|
+
!busy && records.has(modelID) && (selectionMode || moveMode || !$("scene-panel").hidden) ? modelID : null
|
|
5629
|
+
);
|
|
5630
|
+
}
|
|
5631
|
+
function syncShortcuts() {
|
|
5632
|
+
for (const button of shortcutButtons) {
|
|
5633
|
+
const action = button.dataset.viewstlShortcut;
|
|
5634
|
+
button.disabled = busy || !records.size || action === "save" && !exportUI;
|
|
5635
|
+
if (action === "select")
|
|
5636
|
+
button.setAttribute("aria-pressed", String(selectionMode));
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
function setSelectionMode(enabled) {
|
|
5640
|
+
if (enabled) setMoveMode(false);
|
|
5641
|
+
selectionMode = !!enabled && !busy && records.size > 0;
|
|
5642
|
+
selectionPointer = null;
|
|
5643
|
+
if (viewer) {
|
|
5644
|
+
viewer.controls.enabled = !selectionMode;
|
|
5645
|
+
viewer.canvas.setAttribute(
|
|
5646
|
+
"aria-label",
|
|
5647
|
+
selectionMode ? "Select a model with a click. Escape returns to viewing." : "3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."
|
|
5648
|
+
);
|
|
5649
|
+
}
|
|
5650
|
+
$("workspace").classList.toggle("selecting-model-mode", selectionMode);
|
|
5651
|
+
syncShortcuts();
|
|
5652
|
+
syncSelection();
|
|
5653
|
+
}
|
|
5654
|
+
function editModelAt(event) {
|
|
5655
|
+
const hit = viewer.pick_model(event.clientX, event.clientY);
|
|
5656
|
+
if (!hit || !records.has(hit.id)) return;
|
|
5657
|
+
event.preventDefault();
|
|
5658
|
+
selectModel(hit.id);
|
|
5659
|
+
closePanels();
|
|
5660
|
+
$("scene-panel").hidden = false;
|
|
5661
|
+
$("scene-button").setAttribute("aria-expanded", "true");
|
|
5662
|
+
$("position-details").open = true;
|
|
5663
|
+
syncSelection();
|
|
5664
|
+
announce(
|
|
5665
|
+
records.get(hit.id).name + " selected. Choose Move, Rotate or Depth."
|
|
5627
5666
|
);
|
|
5628
5667
|
}
|
|
5629
5668
|
function closePanel(name) {
|
|
@@ -5699,6 +5738,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5699
5738
|
printUI?.sync();
|
|
5700
5739
|
animationUI?.sync();
|
|
5701
5740
|
toolsUI?.sync();
|
|
5741
|
+
syncShortcuts();
|
|
5702
5742
|
}
|
|
5703
5743
|
function populateInfo() {
|
|
5704
5744
|
if (modelID === null) return;
|
|
@@ -5739,6 +5779,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5739
5779
|
}
|
|
5740
5780
|
function renderScene() {
|
|
5741
5781
|
const has = records.size > 0;
|
|
5782
|
+
if (!has && selectionMode) setSelectionMode(false);
|
|
5742
5783
|
$("workspace").classList.toggle("has-models", has);
|
|
5743
5784
|
$("viewer").hidden = !has && !busy;
|
|
5744
5785
|
$("empty-state").hidden = has || busy;
|
|
@@ -5757,6 +5798,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5757
5798
|
function setBusy(value) {
|
|
5758
5799
|
if (value) exportUI?.close();
|
|
5759
5800
|
if (disposed) return;
|
|
5801
|
+
if (value && selectionMode) setSelectionMode(false);
|
|
5760
5802
|
if (value) setMoveMode(false);
|
|
5761
5803
|
busy = value;
|
|
5762
5804
|
$("loading-state").hidden = !value;
|
|
@@ -5894,6 +5936,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5894
5936
|
$("move-gesture-message").textContent = toolHints[transformTool] + " \xB7 Scroll to adjust depth";
|
|
5895
5937
|
}
|
|
5896
5938
|
function setMoveMode(enabled, tool = transformTool, retainPaused = false) {
|
|
5939
|
+
if (enabled && selectionMode) setSelectionMode(false);
|
|
5897
5940
|
modelMover?.setEnabled(false);
|
|
5898
5941
|
transformTool = tool;
|
|
5899
5942
|
moveMode = !!enabled && modelID !== null && !busy;
|
|
@@ -5906,7 +5949,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5906
5949
|
if (viewer?.canvas)
|
|
5907
5950
|
viewer.canvas.setAttribute(
|
|
5908
5951
|
"aria-label",
|
|
5909
|
-
moveMode ? toolHints[transformTool] + ". Scroll changes only its depth. Escape exits." : "3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."
|
|
5952
|
+
moveMode ? toolHints[transformTool] + ". Scroll changes only its depth. Escape exits." : selectionMode ? "Select a model with a click. Escape returns to viewing." : "3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."
|
|
5910
5953
|
);
|
|
5911
5954
|
if (moveMode) {
|
|
5912
5955
|
closePanels();
|
|
@@ -6144,20 +6187,39 @@ function mountViewstlApp(root, options = {}) {
|
|
|
6144
6187
|
}
|
|
6145
6188
|
});
|
|
6146
6189
|
listen(viewer.canvas, "dblclick", (event) => {
|
|
6147
|
-
if (busy || event.button !== 0) return;
|
|
6148
|
-
|
|
6149
|
-
|
|
6190
|
+
if (busy || selectionMode || event.button !== 0) return;
|
|
6191
|
+
editModelAt(event);
|
|
6192
|
+
});
|
|
6193
|
+
listen(viewer.canvas, "pointerdown", (event) => {
|
|
6194
|
+
if (!selectionMode || busy || event.button !== 0 || !event.isPrimary)
|
|
6195
|
+
return;
|
|
6196
|
+
selectionPointer = {
|
|
6197
|
+
id: event.pointerId,
|
|
6198
|
+
x: event.clientX,
|
|
6199
|
+
y: event.clientY,
|
|
6200
|
+
moved: false
|
|
6201
|
+
};
|
|
6202
|
+
viewer.canvas.focus({ preventScroll: true });
|
|
6203
|
+
viewer.canvas.setPointerCapture(event.pointerId);
|
|
6150
6204
|
event.preventDefault();
|
|
6151
|
-
selectModel(hit.id);
|
|
6152
|
-
closePanels();
|
|
6153
|
-
$("scene-panel").hidden = false;
|
|
6154
|
-
$("scene-button").setAttribute("aria-expanded", "true");
|
|
6155
|
-
$("position-details").open = true;
|
|
6156
|
-
syncSelection();
|
|
6157
|
-
announce(
|
|
6158
|
-
records.get(hit.id).name + " selected. Choose Move, Rotate or Depth."
|
|
6159
|
-
);
|
|
6160
6205
|
});
|
|
6206
|
+
listen(viewer.canvas, "pointermove", (event) => {
|
|
6207
|
+
if (selectionPointer?.id === event.pointerId && Math.hypot(
|
|
6208
|
+
event.clientX - selectionPointer.x,
|
|
6209
|
+
event.clientY - selectionPointer.y
|
|
6210
|
+
) > 6)
|
|
6211
|
+
selectionPointer.moved = true;
|
|
6212
|
+
});
|
|
6213
|
+
listen(viewer.canvas, "pointerup", (event) => {
|
|
6214
|
+
const pointer = selectionPointer;
|
|
6215
|
+
selectionPointer = null;
|
|
6216
|
+
if (selectionMode && !busy && pointer?.id === event.pointerId && !pointer.moved)
|
|
6217
|
+
editModelAt(event);
|
|
6218
|
+
});
|
|
6219
|
+
for (const event of ["pointercancel", "lostpointercapture"])
|
|
6220
|
+
listen(viewer.canvas, event, () => {
|
|
6221
|
+
selectionPointer = null;
|
|
6222
|
+
});
|
|
6161
6223
|
return viewer;
|
|
6162
6224
|
}).catch((error) => {
|
|
6163
6225
|
modelMover?.dispose();
|
|
@@ -6422,6 +6484,39 @@ function mountViewstlApp(root, options = {}) {
|
|
|
6422
6484
|
}
|
|
6423
6485
|
}
|
|
6424
6486
|
root.querySelectorAll("[data-open-file]").forEach((b) => listen(b, "click", () => $("file-input").click()));
|
|
6487
|
+
for (const button of shortcutButtons)
|
|
6488
|
+
listen(button, "click", () => {
|
|
6489
|
+
if (button.disabled || busy || !records.size) return;
|
|
6490
|
+
const action = button.dataset.viewstlShortcut;
|
|
6491
|
+
if (action === "save") $("save-button")?.click();
|
|
6492
|
+
else if (action === "select") {
|
|
6493
|
+
setSelectionMode(!selectionMode);
|
|
6494
|
+
closePanels();
|
|
6495
|
+
announce(
|
|
6496
|
+
selectionMode ? "Selection mode. Click a model; Escape returns to viewing." : "Viewing mode. Drag to rotate the scene."
|
|
6497
|
+
);
|
|
6498
|
+
} else if ([
|
|
6499
|
+
"front",
|
|
6500
|
+
"back",
|
|
6501
|
+
"left",
|
|
6502
|
+
"right",
|
|
6503
|
+
"top",
|
|
6504
|
+
"bottom",
|
|
6505
|
+
"isometric"
|
|
6506
|
+
].includes(action)) {
|
|
6507
|
+
setMoveMode(false);
|
|
6508
|
+
setSelectionMode(false);
|
|
6509
|
+
viewer.set_camera_view(action);
|
|
6510
|
+
toolsUI?.sync();
|
|
6511
|
+
announce(button.title || action + " view");
|
|
6512
|
+
}
|
|
6513
|
+
});
|
|
6514
|
+
listen(window2, "resize", () => {
|
|
6515
|
+
if (selectionMode && !shortcutButtons.some(
|
|
6516
|
+
(button) => button.dataset.viewstlShortcut === "select" && button.getClientRects().length
|
|
6517
|
+
))
|
|
6518
|
+
setSelectionMode(false);
|
|
6519
|
+
});
|
|
6425
6520
|
listen($("file-input"), "change", () => {
|
|
6426
6521
|
const files = [...$("file-input").files];
|
|
6427
6522
|
$("file-input").value = "";
|
|
@@ -6633,6 +6728,12 @@ function mountViewstlApp(root, options = {}) {
|
|
|
6633
6728
|
listen(root, "keydown", (event) => {
|
|
6634
6729
|
if (event.key !== "Escape") return;
|
|
6635
6730
|
if ($("save-dialog")?.open) return;
|
|
6731
|
+
if (selectionMode) {
|
|
6732
|
+
setSelectionMode(false);
|
|
6733
|
+
closePanels();
|
|
6734
|
+
announce("Viewing mode. Drag to rotate the scene.");
|
|
6735
|
+
return;
|
|
6736
|
+
}
|
|
6636
6737
|
if (moveMode) {
|
|
6637
6738
|
setMoveMode(false);
|
|
6638
6739
|
announce("Move tool closed.");
|
package/build/viewstl.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
//=========== Stl Viewer v2.0.0-rc.7, by Omri Rips, Viewstl.com, November 2026 ; admin@viewstl.com ===========
|
|
2
|
+
|
|
1
3
|
// src/math.js
|
|
2
4
|
var add = (a, b) => a.map((v, i) => v + b[i]);
|
|
3
5
|
var sub = (a, b) => a.map((v, i) => v - b[i]);
|
|
@@ -2342,7 +2344,7 @@ function modelOptions(input) {
|
|
|
2342
2344
|
return out;
|
|
2343
2345
|
}
|
|
2344
2346
|
var StlViewer = class _StlViewer {
|
|
2345
|
-
static version = "2.0.0-rc.
|
|
2347
|
+
static version = "2.0.0-rc.7";
|
|
2346
2348
|
static workerURL = null;
|
|
2347
2349
|
constructor(parent, options = {}) {
|
|
2348
2350
|
if (!parent?.appendChild)
|
|
@@ -3796,7 +3798,7 @@ var StlViewer = class _StlViewer {
|
|
|
3796
3798
|
null,
|
|
3797
3799
|
scene.files.map((f) => f.bin)
|
|
3798
3800
|
);
|
|
3799
|
-
return new Blob([await job], { type: "application/
|
|
3801
|
+
return new Blob([await job], { type: "application/octet-stream" });
|
|
3800
3802
|
}
|
|
3801
3803
|
download_vsb(filename = "1.vsb") {
|
|
3802
3804
|
return this.get_vsb_blob().then(
|
|
@@ -5565,7 +5567,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5565
5567
|
let animationUI;
|
|
5566
5568
|
let toolsUI;
|
|
5567
5569
|
let sceneImport;
|
|
5568
|
-
let draining = false, busy = false, gridEnabled = false, gridHeight = 0, gridSpacing = 1, moveMode = false, transformTool = "move", modelMover, dragArrangement, background = "transparent";
|
|
5570
|
+
let draining = false, busy = false, gridEnabled = false, gridHeight = 0, gridSpacing = 1, moveMode = false, selectionMode = false, selectionPointer = null, transformTool = "move", modelMover, dragArrangement, background = "transparent";
|
|
5569
5571
|
const viewNames = {
|
|
5570
5572
|
flat: "Solid",
|
|
5571
5573
|
edges: "Solid + edges",
|
|
@@ -5576,6 +5578,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5576
5578
|
orientation: "Face orientation"
|
|
5577
5579
|
};
|
|
5578
5580
|
const modeInputs = [...root.querySelectorAll('[name="view-mode"]')];
|
|
5581
|
+
const shortcutButtons = [...root.querySelectorAll("[data-viewstl-shortcut]")];
|
|
5579
5582
|
if (options.id_prefix)
|
|
5580
5583
|
modeInputs.forEach(
|
|
5581
5584
|
(input) => input.name = options.id_prefix + "view-mode"
|
|
@@ -5595,7 +5598,44 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5595
5598
|
}
|
|
5596
5599
|
function syncSelection() {
|
|
5597
5600
|
viewer?.set_selection(
|
|
5598
|
-
!busy && records.has(modelID) && (moveMode || !$("scene-panel").hidden) ? modelID : null
|
|
5601
|
+
!busy && records.has(modelID) && (selectionMode || moveMode || !$("scene-panel").hidden) ? modelID : null
|
|
5602
|
+
);
|
|
5603
|
+
}
|
|
5604
|
+
function syncShortcuts() {
|
|
5605
|
+
for (const button of shortcutButtons) {
|
|
5606
|
+
const action = button.dataset.viewstlShortcut;
|
|
5607
|
+
button.disabled = busy || !records.size || action === "save" && !exportUI;
|
|
5608
|
+
if (action === "select")
|
|
5609
|
+
button.setAttribute("aria-pressed", String(selectionMode));
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5612
|
+
function setSelectionMode(enabled) {
|
|
5613
|
+
if (enabled) setMoveMode(false);
|
|
5614
|
+
selectionMode = !!enabled && !busy && records.size > 0;
|
|
5615
|
+
selectionPointer = null;
|
|
5616
|
+
if (viewer) {
|
|
5617
|
+
viewer.controls.enabled = !selectionMode;
|
|
5618
|
+
viewer.canvas.setAttribute(
|
|
5619
|
+
"aria-label",
|
|
5620
|
+
selectionMode ? "Select a model with a click. Escape returns to viewing." : "3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."
|
|
5621
|
+
);
|
|
5622
|
+
}
|
|
5623
|
+
$("workspace").classList.toggle("selecting-model-mode", selectionMode);
|
|
5624
|
+
syncShortcuts();
|
|
5625
|
+
syncSelection();
|
|
5626
|
+
}
|
|
5627
|
+
function editModelAt(event) {
|
|
5628
|
+
const hit = viewer.pick_model(event.clientX, event.clientY);
|
|
5629
|
+
if (!hit || !records.has(hit.id)) return;
|
|
5630
|
+
event.preventDefault();
|
|
5631
|
+
selectModel(hit.id);
|
|
5632
|
+
closePanels();
|
|
5633
|
+
$("scene-panel").hidden = false;
|
|
5634
|
+
$("scene-button").setAttribute("aria-expanded", "true");
|
|
5635
|
+
$("position-details").open = true;
|
|
5636
|
+
syncSelection();
|
|
5637
|
+
announce(
|
|
5638
|
+
records.get(hit.id).name + " selected. Choose Move, Rotate or Depth."
|
|
5599
5639
|
);
|
|
5600
5640
|
}
|
|
5601
5641
|
function closePanel(name) {
|
|
@@ -5671,6 +5711,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5671
5711
|
printUI?.sync();
|
|
5672
5712
|
animationUI?.sync();
|
|
5673
5713
|
toolsUI?.sync();
|
|
5714
|
+
syncShortcuts();
|
|
5674
5715
|
}
|
|
5675
5716
|
function populateInfo() {
|
|
5676
5717
|
if (modelID === null) return;
|
|
@@ -5711,6 +5752,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5711
5752
|
}
|
|
5712
5753
|
function renderScene() {
|
|
5713
5754
|
const has = records.size > 0;
|
|
5755
|
+
if (!has && selectionMode) setSelectionMode(false);
|
|
5714
5756
|
$("workspace").classList.toggle("has-models", has);
|
|
5715
5757
|
$("viewer").hidden = !has && !busy;
|
|
5716
5758
|
$("empty-state").hidden = has || busy;
|
|
@@ -5729,6 +5771,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5729
5771
|
function setBusy(value) {
|
|
5730
5772
|
if (value) exportUI?.close();
|
|
5731
5773
|
if (disposed) return;
|
|
5774
|
+
if (value && selectionMode) setSelectionMode(false);
|
|
5732
5775
|
if (value) setMoveMode(false);
|
|
5733
5776
|
busy = value;
|
|
5734
5777
|
$("loading-state").hidden = !value;
|
|
@@ -5866,6 +5909,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5866
5909
|
$("move-gesture-message").textContent = toolHints[transformTool] + " \xB7 Scroll to adjust depth";
|
|
5867
5910
|
}
|
|
5868
5911
|
function setMoveMode(enabled, tool = transformTool, retainPaused = false) {
|
|
5912
|
+
if (enabled && selectionMode) setSelectionMode(false);
|
|
5869
5913
|
modelMover?.setEnabled(false);
|
|
5870
5914
|
transformTool = tool;
|
|
5871
5915
|
moveMode = !!enabled && modelID !== null && !busy;
|
|
@@ -5878,7 +5922,7 @@ function mountViewstlApp(root, options = {}) {
|
|
|
5878
5922
|
if (viewer?.canvas)
|
|
5879
5923
|
viewer.canvas.setAttribute(
|
|
5880
5924
|
"aria-label",
|
|
5881
|
-
moveMode ? toolHints[transformTool] + ". Scroll changes only its depth. Escape exits." : "3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."
|
|
5925
|
+
moveMode ? toolHints[transformTool] + ". Scroll changes only its depth. Escape exits." : selectionMode ? "Select a model with a click. Escape returns to viewing." : "3D model viewer. Drag to rotate, shift-drag to pan, scroll to zoom."
|
|
5882
5926
|
);
|
|
5883
5927
|
if (moveMode) {
|
|
5884
5928
|
closePanels();
|
|
@@ -6116,20 +6160,39 @@ function mountViewstlApp(root, options = {}) {
|
|
|
6116
6160
|
}
|
|
6117
6161
|
});
|
|
6118
6162
|
listen(viewer.canvas, "dblclick", (event) => {
|
|
6119
|
-
if (busy || event.button !== 0) return;
|
|
6120
|
-
|
|
6121
|
-
|
|
6163
|
+
if (busy || selectionMode || event.button !== 0) return;
|
|
6164
|
+
editModelAt(event);
|
|
6165
|
+
});
|
|
6166
|
+
listen(viewer.canvas, "pointerdown", (event) => {
|
|
6167
|
+
if (!selectionMode || busy || event.button !== 0 || !event.isPrimary)
|
|
6168
|
+
return;
|
|
6169
|
+
selectionPointer = {
|
|
6170
|
+
id: event.pointerId,
|
|
6171
|
+
x: event.clientX,
|
|
6172
|
+
y: event.clientY,
|
|
6173
|
+
moved: false
|
|
6174
|
+
};
|
|
6175
|
+
viewer.canvas.focus({ preventScroll: true });
|
|
6176
|
+
viewer.canvas.setPointerCapture(event.pointerId);
|
|
6122
6177
|
event.preventDefault();
|
|
6123
|
-
selectModel(hit.id);
|
|
6124
|
-
closePanels();
|
|
6125
|
-
$("scene-panel").hidden = false;
|
|
6126
|
-
$("scene-button").setAttribute("aria-expanded", "true");
|
|
6127
|
-
$("position-details").open = true;
|
|
6128
|
-
syncSelection();
|
|
6129
|
-
announce(
|
|
6130
|
-
records.get(hit.id).name + " selected. Choose Move, Rotate or Depth."
|
|
6131
|
-
);
|
|
6132
6178
|
});
|
|
6179
|
+
listen(viewer.canvas, "pointermove", (event) => {
|
|
6180
|
+
if (selectionPointer?.id === event.pointerId && Math.hypot(
|
|
6181
|
+
event.clientX - selectionPointer.x,
|
|
6182
|
+
event.clientY - selectionPointer.y
|
|
6183
|
+
) > 6)
|
|
6184
|
+
selectionPointer.moved = true;
|
|
6185
|
+
});
|
|
6186
|
+
listen(viewer.canvas, "pointerup", (event) => {
|
|
6187
|
+
const pointer = selectionPointer;
|
|
6188
|
+
selectionPointer = null;
|
|
6189
|
+
if (selectionMode && !busy && pointer?.id === event.pointerId && !pointer.moved)
|
|
6190
|
+
editModelAt(event);
|
|
6191
|
+
});
|
|
6192
|
+
for (const event of ["pointercancel", "lostpointercapture"])
|
|
6193
|
+
listen(viewer.canvas, event, () => {
|
|
6194
|
+
selectionPointer = null;
|
|
6195
|
+
});
|
|
6133
6196
|
return viewer;
|
|
6134
6197
|
}).catch((error) => {
|
|
6135
6198
|
modelMover?.dispose();
|
|
@@ -6394,6 +6457,39 @@ function mountViewstlApp(root, options = {}) {
|
|
|
6394
6457
|
}
|
|
6395
6458
|
}
|
|
6396
6459
|
root.querySelectorAll("[data-open-file]").forEach((b) => listen(b, "click", () => $("file-input").click()));
|
|
6460
|
+
for (const button of shortcutButtons)
|
|
6461
|
+
listen(button, "click", () => {
|
|
6462
|
+
if (button.disabled || busy || !records.size) return;
|
|
6463
|
+
const action = button.dataset.viewstlShortcut;
|
|
6464
|
+
if (action === "save") $("save-button")?.click();
|
|
6465
|
+
else if (action === "select") {
|
|
6466
|
+
setSelectionMode(!selectionMode);
|
|
6467
|
+
closePanels();
|
|
6468
|
+
announce(
|
|
6469
|
+
selectionMode ? "Selection mode. Click a model; Escape returns to viewing." : "Viewing mode. Drag to rotate the scene."
|
|
6470
|
+
);
|
|
6471
|
+
} else if ([
|
|
6472
|
+
"front",
|
|
6473
|
+
"back",
|
|
6474
|
+
"left",
|
|
6475
|
+
"right",
|
|
6476
|
+
"top",
|
|
6477
|
+
"bottom",
|
|
6478
|
+
"isometric"
|
|
6479
|
+
].includes(action)) {
|
|
6480
|
+
setMoveMode(false);
|
|
6481
|
+
setSelectionMode(false);
|
|
6482
|
+
viewer.set_camera_view(action);
|
|
6483
|
+
toolsUI?.sync();
|
|
6484
|
+
announce(button.title || action + " view");
|
|
6485
|
+
}
|
|
6486
|
+
});
|
|
6487
|
+
listen(window2, "resize", () => {
|
|
6488
|
+
if (selectionMode && !shortcutButtons.some(
|
|
6489
|
+
(button) => button.dataset.viewstlShortcut === "select" && button.getClientRects().length
|
|
6490
|
+
))
|
|
6491
|
+
setSelectionMode(false);
|
|
6492
|
+
});
|
|
6397
6493
|
listen($("file-input"), "change", () => {
|
|
6398
6494
|
const files = [...$("file-input").files];
|
|
6399
6495
|
$("file-input").value = "";
|
|
@@ -6605,6 +6701,12 @@ function mountViewstlApp(root, options = {}) {
|
|
|
6605
6701
|
listen(root, "keydown", (event) => {
|
|
6606
6702
|
if (event.key !== "Escape") return;
|
|
6607
6703
|
if ($("save-dialog")?.open) return;
|
|
6704
|
+
if (selectionMode) {
|
|
6705
|
+
setSelectionMode(false);
|
|
6706
|
+
closePanels();
|
|
6707
|
+
announce("Viewing mode. Drag to rotate the scene.");
|
|
6708
|
+
return;
|
|
6709
|
+
}
|
|
6608
6710
|
if (moveMode) {
|
|
6609
6711
|
setMoveMode(false);
|
|
6610
6712
|
announce("Move tool closed.");
|