3dtiles-inspector 0.2.0 → 0.2.2

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/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ The format is based on Keep a Changelog and this project follows Semantic Versio
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.2] - 2026-05-03
10
+
11
+ ### Fixed
12
+
13
+ - Declared the server-side crop dependencies as runtime dependencies so installed packages can load `three` and `@sparkjsdev/spark`.
14
+
15
+ ## [0.2.1] - 2026-05-03
16
+
17
+ ### Changed
18
+
19
+ - Renamed the crop selection action to `Select Region` and show pending or newest crop regions first in the list.
20
+ - Kept the Save/status controls fixed at the bottom of the sidebar, moved `Reset` into the Transform controls, and tightened sidebar behavior on narrow screens.
21
+
9
22
  ## [0.2.0] - 2026-05-03
10
23
 
11
24
  ### Added
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  </div>
12
12
 
13
- `3dtiles-inspector` is a Node.js package and CLI for opening a local 3D Tiles tileset in a browser inspector, adjusting the root transform, tuning the effective geometric error scale, and saving the result back to disk.
13
+ `3dtiles-inspector` is a Node.js package and CLI for opening a local 3D Tiles tileset in a browser inspector, adjusting the root transform, tuning geometric-error scaling, cropping supported 3D Gaussian Splat tilesets, and saving the result back to disk.
14
14
 
15
15
  Requires Node.js 18 or newer.
16
16
 
@@ -93,7 +93,7 @@ const {
93
93
  - `Geometric Error` scaling from `1/16x` to `16x`
94
94
  - `Layer Multiplier` scaling from `1/8x` to `8x` for each tile's geometric-error difference from the tileset's global leaf baseline
95
95
  - `Crop Regions` for drawing screen-space exclude regions on 3D Gaussian Splat tilesets
96
- - `Save` to persist the updated root transform and geometric-error scale back to disk
96
+ - `Save` to persist root transform edits, geometric-error scaling, and confirmed crop regions back to disk
97
97
 
98
98
  ### Crop Regions
99
99
 
@@ -101,7 +101,7 @@ const {
101
101
 
102
102
  The basic workflow is:
103
103
 
104
- 1. Click `Screen Select` and drag a rectangle over the splats to remove.
104
+ 1. Click `Select Region` and drag a rectangle over the splats to remove.
105
105
  2. Drag the far-plane handle to set how deep the crop region extends into the scene.
106
106
  3. Click `Confirm` to add the region to the save list, or `Cancel` to discard the pending rectangle.
107
107
  4. Select a confirmed region row if you need to adjust its 3D far plane with the transform handle.
@@ -68903,22 +68903,22 @@ function updateCropControls({
68903
68903
  screenSelections.length + pendingScreenSelections.length
68904
68904
  );
68905
68905
  cropListEl.replaceChildren();
68906
- screenSelections.forEach((selection, index) => {
68906
+ pendingScreenSelections.forEach((selection) => {
68907
68907
  cropListEl.appendChild(
68908
68908
  createSelectionControl({
68909
68909
  active: selection.id === activeScreenSelectionId,
68910
- label: `Region ${index + 1}`,
68910
+ label: "Pending",
68911
68911
  onScreenSelectionRemove,
68912
68912
  onScreenSelectionSelect,
68913
68913
  selection
68914
68914
  })
68915
68915
  );
68916
68916
  });
68917
- pendingScreenSelections.forEach((selection) => {
68917
+ screenSelections.map((selection, index) => ({ label: `Region ${index + 1}`, selection })).reverse().forEach(({ label, selection }) => {
68918
68918
  cropListEl.appendChild(
68919
68919
  createSelectionControl({
68920
68920
  active: selection.id === activeScreenSelectionId,
68921
- label: "Pending",
68921
+ label,
68922
68922
  onScreenSelectionRemove,
68923
68923
  onScreenSelectionSelect,
68924
68924
  selection
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "3dtiles-inspector",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Inspect, align, and save local 3D Tiles root transforms in an interactive browser session.",
5
5
  "author": "William Liu <lyz15972107087@gmail.com>",
6
6
  "license": "Apache-2.0",
@@ -54,11 +54,13 @@
54
54
  "url": "https://github.com/WilliamLiu-1997/3DTiles-Inspector/issues"
55
55
  },
56
56
  "devDependencies": {
57
- "@sparkjsdev/spark": "2.0.0",
58
57
  "3d-tiles-renderer": "0.4.24",
59
58
  "3d-tiles-rendererjs-3dgs-plugin": "0.1.5",
60
59
  "cesium": "1.140.0",
61
- "esbuild": "^0.25.11",
60
+ "esbuild": "^0.25.11"
61
+ },
62
+ "dependencies": {
63
+ "@sparkjsdev/spark": "2.0.0",
62
64
  "three": "0.180.0"
63
65
  }
64
66
  }
@@ -167,8 +167,8 @@ function buildViewerHtml(viewerConfig) {
167
167
 
168
168
  .toolbar {
169
169
  display: grid;
170
- align-content: start;
171
- gap: 8px;
170
+ grid-template-rows: minmax(0, 1fr) auto;
171
+ gap: 0;
172
172
  padding: 10px 14px;
173
173
  border: 1px solid rgba(22, 50, 79, 0.12);
174
174
  border-top: 0;
@@ -177,8 +177,7 @@ function buildViewerHtml(viewerConfig) {
177
177
  box-shadow: 0 18px 44px rgba(33, 52, 73, 0.16);
178
178
  backdrop-filter: blur(14px);
179
179
  min-height: 0;
180
- overflow-y: auto;
181
- overscroll-behavior: contain;
180
+ overflow: hidden;
182
181
  transition:
183
182
  opacity 160ms ease,
184
183
  transform 160ms ease;
@@ -215,8 +214,8 @@ function buildViewerHtml(viewerConfig) {
215
214
  .toolbar-dock.collapsed .toolbar-toggle {
216
215
  justify-self: start;
217
216
  width: auto;
218
- min-height: 36px;
219
- padding: 6px 12px 7px;
217
+ min-height: 32px;
218
+ padding: 4px 12px 5px;
220
219
  border-radius: 999px;
221
220
  color: #506377;
222
221
  background: rgba(255, 255, 255, 0.94);
@@ -249,6 +248,35 @@ function buildViewerHtml(viewerConfig) {
249
248
  linear-gradient(180deg, rgba(255, 255, 255, 0.78), rgba(243, 247, 251, 0.9));
250
249
  }
251
250
 
251
+ .toolbar-scroll {
252
+ display: grid;
253
+ align-content: start;
254
+ gap: 8px;
255
+ min-height: 0;
256
+ overflow-y: auto;
257
+ overscroll-behavior: contain;
258
+ padding-bottom: 8px;
259
+ scrollbar-color: rgba(93, 115, 139, 0.45) transparent;
260
+ scrollbar-width: thin;
261
+ }
262
+
263
+ .toolbar-scroll::-webkit-scrollbar {
264
+ width: 6px;
265
+ }
266
+
267
+ .toolbar-scroll::-webkit-scrollbar-track {
268
+ background: transparent;
269
+ }
270
+
271
+ .toolbar-scroll::-webkit-scrollbar-thumb {
272
+ border-radius: 999px;
273
+ background: rgba(93, 115, 139, 0.32);
274
+ }
275
+
276
+ .toolbar-scroll::-webkit-scrollbar-thumb:hover {
277
+ background: rgba(93, 115, 139, 0.48);
278
+ }
279
+
252
280
  .toolbar-section-header {
253
281
  display: flex;
254
282
  align-items: flex-start;
@@ -521,6 +549,11 @@ function buildViewerHtml(viewerConfig) {
521
549
  .status-panel {
522
550
  display: grid;
523
551
  gap: 10px;
552
+ margin-top: 8px;
553
+ }
554
+
555
+ .status-panel .status-actions {
556
+ grid-template-columns: 1fr;
524
557
  }
525
558
 
526
559
  .status-actions {
@@ -544,12 +577,14 @@ function buildViewerHtml(viewerConfig) {
544
577
  }
545
578
 
546
579
  .tile-runtime-stats {
547
- right: 16px;
548
- bottom: 16px;
549
- left: 16px;
580
+ right: 50%;
581
+ bottom: 5px;
582
+ left: auto;
550
583
  flex-wrap: wrap;
551
- justify-content: stretch;
552
- max-width: none;
584
+ justify-content: center;
585
+ width: max-content;
586
+ max-width: calc(100vw - 32px);
587
+ transform: translateX(50%);
553
588
  }
554
589
 
555
590
  .runtime-stat {
@@ -564,15 +599,21 @@ function buildViewerHtml(viewerConfig) {
564
599
 
565
600
  .toolbar-dock {
566
601
  top: auto;
567
- bottom: 16px;
602
+ bottom: 32px;
568
603
  right: 16px;
569
604
  left: 16px;
570
605
  width: auto;
571
- max-height: min(78vh, 640px);
606
+ max-height: min(calc(78vh - 26px), 614px);
572
607
  }
573
608
 
574
609
  .toolbar {
575
- max-height: min(calc(78vh - 44px), 596px);
610
+ max-height: min(calc(78vh - 70px), 570px);
611
+ }
612
+
613
+ .toolbar-dock.collapsed .toolbar-toggle {
614
+ justify-self: center;
615
+ min-height: 28px;
616
+ padding: 3px 12px 4px;
576
617
  }
577
618
 
578
619
  .coordinate-actions button,
@@ -627,90 +668,92 @@ function buildViewerHtml(viewerConfig) {
627
668
  Hide Sidebar
628
669
  </button>
629
670
  <div id="toolbar" class="toolbar">
630
- <div class="toolbar-section" data-save-lock-exempt>
631
- <div class="toolbar-section-header">
632
- <p class="toolbar-section-title">Canvas</p>
633
- </div>
634
- <div class="coordinate-actions">
635
- <button id="terrain" class="wide" type="button">Terrain</button>
636
- <button id="bounding-volume" class="wide" type="button">Bounding Volume</button>
637
- <button id="move-to-tiles" type="button">Move To Tiles</button>
638
- </div>
639
- </div>
640
- <div class="toolbar-section">
641
- <div class="toolbar-section-header">
642
- <p class="toolbar-section-title">Transform</p>
643
- </div>
644
- <div class="transform-actions">
645
- <button id="translate" type="button">Translate</button>
646
- <button id="rotate" type="button">Rotate</button>
647
- <button id="set-position" class="full-span" type="button">Set Position</button>
648
- </div>
649
- </div>
650
- <div class="toolbar-section">
651
- <div class="toolbar-section-header">
652
- <p class="toolbar-section-title">Coordinate</p>
653
- </div>
654
- <div class="coordinate-grid">
655
- <label><span>Latitude</span><input id="latitude" type="number" step="any" value="0" /></label>
656
- <label><span>Longitude</span><input id="longitude" type="number" step="any" value="0" /></label>
657
- <label><span>Height</span><input id="height" type="number" step="any" value="0" /></label>
658
- </div>
659
- <div class="coordinate-actions">
660
- <button id="move-camera-to-coordinate" class="wide" type="button">Move Camera</button>
661
- <button id="move-tiles-to-coordinate" class="wide" type="button">Move Tiles</button>
671
+ <div class="toolbar-scroll">
672
+ <div class="toolbar-section" data-save-lock-exempt>
673
+ <div class="toolbar-section-header">
674
+ <p class="toolbar-section-title">Canvas</p>
675
+ </div>
676
+ <div class="coordinate-actions">
677
+ <button id="terrain" class="wide" type="button">Terrain</button>
678
+ <button id="bounding-volume" class="wide" type="button">Bounding Volume</button>
679
+ <button id="move-to-tiles" type="button">Move To Tiles</button>
680
+ </div>
662
681
  </div>
663
- </div>
664
- <div class="toolbar-section">
665
- <div class="toolbar-section-header">
666
- <p class="toolbar-section-title">LOD</p>
682
+ <div class="toolbar-section">
683
+ <div class="toolbar-section-header">
684
+ <p class="toolbar-section-title">Transform</p>
685
+ </div>
686
+ <div class="transform-actions">
687
+ <button id="translate" type="button">Translate</button>
688
+ <button id="rotate" type="button">Rotate</button>
689
+ <button id="set-position" type="button">Set Position</button>
690
+ <button id="reset" type="button">Reset</button>
691
+ </div>
667
692
  </div>
668
- <label class="range-field">
669
- <div class="range-field-header">
670
- <span>Geometric Error</span>
671
- <p id="geometric-error-value" class="toolbar-value">x1.00</p>
693
+ <div class="toolbar-section">
694
+ <div class="toolbar-section-header">
695
+ <p class="toolbar-section-title">Coordinate</p>
672
696
  </div>
673
- <input
674
- id="geometric-error-scale"
675
- type="range"
676
- min="-4"
677
- max="4"
678
- step="0.1"
679
- value="0"
680
- />
681
- </label>
682
- <label class="range-field">
683
- <div class="range-field-header">
684
- <span>Layer Multiplier</span>
685
- <p id="geometric-error-layer-value" class="toolbar-value">x1.00</p>
697
+ <div class="coordinate-grid">
698
+ <label><span>Latitude</span><input id="latitude" type="number" step="any" value="0" /></label>
699
+ <label><span>Longitude</span><input id="longitude" type="number" step="any" value="0" /></label>
700
+ <label><span>Height</span><input id="height" type="number" step="any" value="0" /></label>
701
+ </div>
702
+ <div class="coordinate-actions">
703
+ <button id="move-camera-to-coordinate" class="wide" type="button">Move Camera</button>
704
+ <button id="move-tiles-to-coordinate" class="wide" type="button">Move Tiles</button>
686
705
  </div>
687
- <input
688
- id="geometric-error-layer-scale"
689
- type="range"
690
- min="-3"
691
- max="3"
692
- step="0.1"
693
- value="0"
694
- />
695
- </label>
696
- </div>
697
- <div id="crop-section" class="toolbar-section" hidden>
698
- <div class="toolbar-section-header">
699
- <p class="toolbar-section-title">Crop Regions</p>
700
- <p id="crop-count-value" class="toolbar-value">0</p>
701
706
  </div>
702
- <div class="coordinate-actions">
703
- <button id="crop-screen-select" class="wide" type="button">Screen Select</button>
707
+ <div class="toolbar-section">
708
+ <div class="toolbar-section-header">
709
+ <p class="toolbar-section-title">LOD</p>
710
+ </div>
711
+ <label class="range-field">
712
+ <div class="range-field-header">
713
+ <span>Geometric Error</span>
714
+ <p id="geometric-error-value" class="toolbar-value">x1.00</p>
715
+ </div>
716
+ <input
717
+ id="geometric-error-scale"
718
+ type="range"
719
+ min="-4"
720
+ max="4"
721
+ step="0.1"
722
+ value="0"
723
+ />
724
+ </label>
725
+ <label class="range-field">
726
+ <div class="range-field-header">
727
+ <span>Layer Multiplier</span>
728
+ <p id="geometric-error-layer-value" class="toolbar-value">x1.00</p>
729
+ </div>
730
+ <input
731
+ id="geometric-error-layer-scale"
732
+ type="range"
733
+ min="-3"
734
+ max="3"
735
+ step="0.1"
736
+ value="0"
737
+ />
738
+ </label>
704
739
  </div>
705
- <div id="crop-list" class="crop-list"></div>
706
- <div class="status-actions">
707
- <button id="crop-screen-confirm" type="button">Confirm</button>
708
- <button id="crop-screen-cancel" type="button">Cancel</button>
740
+ <div id="crop-section" class="toolbar-section" hidden>
741
+ <div class="toolbar-section-header">
742
+ <p class="toolbar-section-title">Crop Regions</p>
743
+ <p id="crop-count-value" class="toolbar-value">0</p>
744
+ </div>
745
+ <div class="coordinate-actions">
746
+ <button id="crop-screen-select" class="wide" type="button">Select Region</button>
747
+ </div>
748
+ <div id="crop-list" class="crop-list"></div>
749
+ <div class="status-actions">
750
+ <button id="crop-screen-confirm" type="button">Confirm</button>
751
+ <button id="crop-screen-cancel" type="button">Cancel</button>
752
+ </div>
709
753
  </div>
710
754
  </div>
711
755
  <div class="toolbar-section status-panel">
712
756
  <div class="status-actions">
713
- <button id="reset" type="button">Reset</button>
714
757
  <button id="save" class="save" type="button">Save</button>
715
758
  </div>
716
759
  <progress id="save-progress" class="save-progress" max="100" value="0" hidden></progress>
@@ -30,17 +30,6 @@ export function updateCropControls({
30
30
  );
31
31
 
32
32
  cropListEl.replaceChildren();
33
- screenSelections.forEach((selection, index) => {
34
- cropListEl.appendChild(
35
- createSelectionControl({
36
- active: selection.id === activeScreenSelectionId,
37
- label: `Region ${index + 1}`,
38
- onScreenSelectionRemove,
39
- onScreenSelectionSelect,
40
- selection,
41
- }),
42
- );
43
- });
44
33
  pendingScreenSelections.forEach((selection) => {
45
34
  cropListEl.appendChild(
46
35
  createSelectionControl({
@@ -52,6 +41,20 @@ export function updateCropControls({
52
41
  }),
53
42
  );
54
43
  });
44
+ screenSelections
45
+ .map((selection, index) => ({ label: `Region ${index + 1}`, selection }))
46
+ .reverse()
47
+ .forEach(({ label, selection }) => {
48
+ cropListEl.appendChild(
49
+ createSelectionControl({
50
+ active: selection.id === activeScreenSelectionId,
51
+ label,
52
+ onScreenSelectionRemove,
53
+ onScreenSelectionSelect,
54
+ selection,
55
+ }),
56
+ );
57
+ });
55
58
  }
56
59
 
57
60
  function createSelectionControl({