Skip to content

Commit 8760eed

Browse files
Merge pull request #8 from ScaDS/move-to-near-point-button
Add "Move to Near Point" button
2 parents c671356 + 62170b5 commit 8760eed

3 files changed

Lines changed: 104 additions & 5 deletions

File tree

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,13 @@ test\Image_2562.jpg, 12.263655, 5.8971086, -0.066879705
7070
## Controls
7171

7272
### Camera Movement
73-
- **W / A / S / D** - Move forward, left, backward, right
74-
- **E** - Move up
75-
- **Y** - Move down
76-
- **Mouse** - Look around (click to enable pointer lock)
73+
- **W / A / S / D**: Move forward, left, backward, right
74+
- **E / Y**: Move up / Down
75+
- **I / J / K / L**: Rotate upward, left, downwar, righ
76+
- **U / M**: Rotate counter-clockwise, clockwise
77+
- **O**: Move to Near Point
78+
- **Space**: Add keyframe
79+
- **Mouse**: Look around (click to enable pointer lock)
7780
- **Touch control**
7881
* 1 finger: Rotate view
7982
* 2 fingers: Zoom

vest/static/viewer.js

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ class CameraController {
327327
if (this.keys['k']) euler.x -= this.rotationStep;
328328
if (this.keys['j']) euler.y += this.rotationStep;
329329
if (this.keys['l']) euler.y -= this.rotationStep;
330-
if (this.keys['o']) euler.z -= this.rotationStep;
330+
if (this.keys['u']) euler.z -= this.rotationStep;
331331
if (this.keys['m']) euler.z += this.rotationStep;
332332

333333
// Prevent flipping over the top/bottom
@@ -486,6 +486,9 @@ class ImageViewer {
486486
// Setup keyframe controls
487487
this.initKeyframeControls();
488488

489+
// Setup near-point jump controls
490+
this.initNearPointControls();
491+
489492
// Setup color mode dropdown
490493
this.initColorModeControl();
491494
this.initColorIntensityControls();
@@ -1382,6 +1385,63 @@ class ImageViewer {
13821385
}
13831386
}
13841387

1388+
getNearestSpriteInfo(preferInFront = true) {
1389+
if (this.imageSprites.length === 0) {
1390+
return null;
1391+
}
1392+
1393+
const cameraForward = new THREE.Vector3();
1394+
this.camera.getWorldDirection(cameraForward);
1395+
1396+
const distances = this.imageSprites.map((sprite, index) => {
1397+
const distance = this.camera.position.distanceTo(sprite.position);
1398+
const toSprite = new THREE.Vector3().subVectors(sprite.position, this.camera.position).normalize();
1399+
const dotProduct = cameraForward.dot(toSprite);
1400+
1401+
return {
1402+
sprite,
1403+
distance,
1404+
index,
1405+
inFront: dotProduct > this.fovConeThreshold
1406+
};
1407+
});
1408+
1409+
const inFrontSprites = distances
1410+
.filter(item => item.inFront)
1411+
.sort((a, b) => a.distance - b.distance);
1412+
1413+
if (preferInFront && inFrontSprites.length > 0) {
1414+
return inFrontSprites[0];
1415+
}
1416+
1417+
distances.sort((a, b) => a.distance - b.distance);
1418+
return distances[0] || null;
1419+
}
1420+
1421+
jumpNearNearestPoint() {
1422+
const nearest = this.getNearestSpriteInfo(true);
1423+
if (!nearest?.sprite) {
1424+
return;
1425+
}
1426+
1427+
const nearPointPosition = nearest.sprite.position.clone();
1428+
const offsetDirection = new THREE.Vector3().subVectors(this.camera.position, nearPointPosition);
1429+
1430+
if (offsetDirection.lengthSq() < 1e-10) {
1431+
this.camera.getWorldDirection(offsetDirection);
1432+
offsetDirection.multiplyScalar(-1);
1433+
}
1434+
1435+
offsetDirection.normalize();
1436+
1437+
// Keep the camera extremely close to the near point, but not exactly at the same location.
1438+
const offsetDistance = Math.max(0.1, Math.min(0.001, this.imageSize * 0.1));
1439+
const targetPosition = nearPointPosition.add(offsetDirection.multiplyScalar(offsetDistance));
1440+
1441+
this.camera.position.copy(targetPosition);
1442+
this.camera.lookAt(nearest.sprite.position);
1443+
}
1444+
13851445
updateNearestPoint() {
13861446
if (this.imageSprites.length === 0) return;
13871447

@@ -1738,12 +1798,39 @@ class ImageViewer {
17381798
});
17391799
}
17401800

1801+
initNearPointControls() {
1802+
const nearBtn = document.getElementById('btn-near');
1803+
if (!nearBtn) return;
1804+
1805+
nearBtn.addEventListener('click', (e) => {
1806+
e.preventDefault();
1807+
this.jumpNearNearestPoint();
1808+
});
1809+
1810+
nearBtn.addEventListener('touchstart', (e) => {
1811+
e.preventDefault();
1812+
this.jumpNearNearestPoint();
1813+
}, { passive: false });
1814+
}
1815+
17411816
initGlobalKeyboardShortcuts() {
17421817
document.addEventListener('keydown', (e) => {
17431818
// Space key adds a keyframe
17441819
if (e.key === ' ' || e.code === 'Space') {
17451820
e.preventDefault(); // Prevent default scrolling behavior
17461821
this.addKeyframe();
1822+
return;
1823+
}
1824+
1825+
// Jump very near the current nearest point.
1826+
if (!e.ctrlKey && !e.metaKey && !e.altKey && e.key.toLowerCase() === 'o') {
1827+
e.preventDefault();
1828+
if (this.controller?.keys) {
1829+
this.controller.keys['o'] = false;
1830+
}
1831+
if (!e.repeat) {
1832+
this.jumpNearNearestPoint();
1833+
}
17471834
}
17481835
});
17491836
}

vest/templates/viewer.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,11 @@
654654
grid-column: 3;
655655
grid-row: 3;
656656
}
657+
658+
.arrow-btn:nth-child(7) {
659+
grid-column: 2;
660+
grid-row: 2;
661+
}
657662
</style>
658663
</head>
659664
<body>
@@ -703,6 +708,10 @@ <h3>Controls</h3>
703708
<button class="arrow-btn" id="btn-right" title="Right (D)"></button>
704709
<button class="arrow-btn" id="btn-forward" title="Forward (W)"></button>
705710
<button class="arrow-btn" id="btn-back" title="Backward (S)"></button>
711+
<button class="arrow-btn" id="btn-near" title="Jump to Near Point (O)">O</button>
712+
</div>
713+
<div class="control-section" style="margin-top: 8px;">
714+
<span class="control-key">O</span> Jump to near point
706715
</div>
707716
<button id="reset-view-btn" class="keyframe-btn" style="width: 100%; margin-top: 10px;">Reset View</button>
708717
</div>

0 commit comments

Comments
 (0)