@project-skymap/library 0.6.0 → 0.7.0
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/index.cjs +194 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +194 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -746,10 +746,14 @@ var init_projections = __esm({
|
|
|
746
746
|
maxFov = 165;
|
|
747
747
|
glslProjectionType = 2;
|
|
748
748
|
/** FOV thresholds for blend transition (degrees) */
|
|
749
|
-
blendStart
|
|
750
|
-
blendEnd
|
|
749
|
+
blendStart;
|
|
750
|
+
blendEnd;
|
|
751
751
|
/** Current blend factor, updated via setFov() */
|
|
752
752
|
blend = 0;
|
|
753
|
+
constructor(blendStart = 40, blendEnd = 100) {
|
|
754
|
+
this.blendStart = blendStart;
|
|
755
|
+
this.blendEnd = blendEnd;
|
|
756
|
+
}
|
|
753
757
|
/** Call this each frame / when FOV changes so forward/inverse stay in sync */
|
|
754
758
|
setFov(fovDeg) {
|
|
755
759
|
if (fovDeg <= this.blendStart) {
|
|
@@ -802,8 +806,7 @@ var init_projections = __esm({
|
|
|
802
806
|
};
|
|
803
807
|
exports.PROJECTIONS = {
|
|
804
808
|
perspective: () => new PerspectiveProjection(),
|
|
805
|
-
stereographic: () => new StereographicProjection()
|
|
806
|
-
blended: () => new BlendedProjection()
|
|
809
|
+
stereographic: () => new StereographicProjection()
|
|
807
810
|
};
|
|
808
811
|
}
|
|
809
812
|
});
|
|
@@ -906,10 +909,21 @@ function createEngine({
|
|
|
906
909
|
draggedStarIndex: -1,
|
|
907
910
|
draggedDist: 2e3,
|
|
908
911
|
draggedGroup: null,
|
|
909
|
-
tempArrangement: {}
|
|
912
|
+
tempArrangement: {},
|
|
913
|
+
// Touch state
|
|
914
|
+
touchCount: 0,
|
|
915
|
+
touchStartTime: 0,
|
|
916
|
+
touchStartX: 0,
|
|
917
|
+
touchStartY: 0,
|
|
918
|
+
touchMoved: false,
|
|
919
|
+
pinchStartDistance: 0,
|
|
920
|
+
pinchStartFov: ENGINE_CONFIG.defaultFov,
|
|
921
|
+
pinchCenterX: 0,
|
|
922
|
+
pinchCenterY: 0
|
|
910
923
|
};
|
|
911
924
|
const mouseNDC = new THREE5__namespace.Vector2();
|
|
912
925
|
let isMouseInWindow = false;
|
|
926
|
+
let isTouchDevice = false;
|
|
913
927
|
let edgeHoverStart = 0;
|
|
914
928
|
let handlers = { onSelect, onHover, onArrangementChange, onFovChange };
|
|
915
929
|
let currentConfig;
|
|
@@ -917,7 +931,7 @@ function createEngine({
|
|
|
917
931
|
function mix(a, b, t) {
|
|
918
932
|
return a * (1 - t) + b * t;
|
|
919
933
|
}
|
|
920
|
-
let currentProjection =
|
|
934
|
+
let currentProjection = new BlendedProjection(ENGINE_CONFIG.blendStart, ENGINE_CONFIG.blendEnd);
|
|
921
935
|
function syncProjectionState() {
|
|
922
936
|
if (currentProjection instanceof BlendedProjection) {
|
|
923
937
|
currentProjection.setFov(state.fov);
|
|
@@ -1955,9 +1969,13 @@ function createEngine({
|
|
|
1955
1969
|
let lastAppliedLat = void 0;
|
|
1956
1970
|
let lastBackdropCount = void 0;
|
|
1957
1971
|
function setProjection(id) {
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1972
|
+
if (id === "blended") {
|
|
1973
|
+
currentProjection = new BlendedProjection(ENGINE_CONFIG.blendStart, ENGINE_CONFIG.blendEnd);
|
|
1974
|
+
} else {
|
|
1975
|
+
const factory = exports.PROJECTIONS[id];
|
|
1976
|
+
if (!factory) return;
|
|
1977
|
+
currentProjection = factory();
|
|
1978
|
+
}
|
|
1961
1979
|
updateUniforms();
|
|
1962
1980
|
}
|
|
1963
1981
|
function setConfig(cfg) {
|
|
@@ -2072,7 +2090,8 @@ function createEngine({
|
|
|
2072
2090
|
const w = rect.width;
|
|
2073
2091
|
const h = rect.height;
|
|
2074
2092
|
let closestLabel = null;
|
|
2075
|
-
|
|
2093
|
+
const LABEL_THRESHOLD = isTouchDevice ? 48 : 40;
|
|
2094
|
+
let minLabelDist = LABEL_THRESHOLD;
|
|
2076
2095
|
for (const item of dynamicLabels) {
|
|
2077
2096
|
if (!item.obj.visible) continue;
|
|
2078
2097
|
if (isNodeFiltered(item.node)) continue;
|
|
@@ -2385,6 +2404,144 @@ function createEngine({
|
|
|
2385
2404
|
state.targetLat = state.lat;
|
|
2386
2405
|
state.targetLon = state.lon;
|
|
2387
2406
|
}
|
|
2407
|
+
function getTouchDistance(t1, t2) {
|
|
2408
|
+
const dx = t1.clientX - t2.clientX;
|
|
2409
|
+
const dy = t1.clientY - t2.clientY;
|
|
2410
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
2411
|
+
}
|
|
2412
|
+
function getTouchCenter(t1, t2) {
|
|
2413
|
+
return {
|
|
2414
|
+
x: (t1.clientX + t2.clientX) / 2,
|
|
2415
|
+
y: (t1.clientY + t2.clientY) / 2
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
function onTouchStart(e) {
|
|
2419
|
+
e.preventDefault();
|
|
2420
|
+
isTouchDevice = true;
|
|
2421
|
+
const touches = e.touches;
|
|
2422
|
+
state.touchCount = touches.length;
|
|
2423
|
+
if (touches.length === 1) {
|
|
2424
|
+
const touch = touches[0];
|
|
2425
|
+
state.touchStartTime = performance.now();
|
|
2426
|
+
state.touchStartX = touch.clientX;
|
|
2427
|
+
state.touchStartY = touch.clientY;
|
|
2428
|
+
state.touchMoved = false;
|
|
2429
|
+
state.lastMouseX = touch.clientX;
|
|
2430
|
+
state.lastMouseY = touch.clientY;
|
|
2431
|
+
flyToActive = false;
|
|
2432
|
+
state.dragMode = "camera";
|
|
2433
|
+
state.isDragging = true;
|
|
2434
|
+
state.velocityX = 0;
|
|
2435
|
+
state.velocityY = 0;
|
|
2436
|
+
} else if (touches.length === 2) {
|
|
2437
|
+
const t0 = touches[0];
|
|
2438
|
+
const t1 = touches[1];
|
|
2439
|
+
state.pinchStartDistance = getTouchDistance(t0, t1);
|
|
2440
|
+
state.pinchStartFov = state.fov;
|
|
2441
|
+
const center = getTouchCenter(t0, t1);
|
|
2442
|
+
state.pinchCenterX = center.x;
|
|
2443
|
+
state.pinchCenterY = center.y;
|
|
2444
|
+
state.lastMouseX = center.x;
|
|
2445
|
+
state.lastMouseY = center.y;
|
|
2446
|
+
state.touchMoved = true;
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
function onTouchMove(e) {
|
|
2450
|
+
e.preventDefault();
|
|
2451
|
+
const touches = e.touches;
|
|
2452
|
+
if (touches.length === 1 && state.dragMode === "camera") {
|
|
2453
|
+
const touch = touches[0];
|
|
2454
|
+
const deltaX = touch.clientX - state.lastMouseX;
|
|
2455
|
+
const deltaY = touch.clientY - state.lastMouseY;
|
|
2456
|
+
state.lastMouseX = touch.clientX;
|
|
2457
|
+
state.lastMouseY = touch.clientY;
|
|
2458
|
+
const totalDx = touch.clientX - state.touchStartX;
|
|
2459
|
+
const totalDy = touch.clientY - state.touchStartY;
|
|
2460
|
+
if (Math.sqrt(totalDx * totalDx + totalDy * totalDy) > ENGINE_CONFIG.tapMaxDistance) {
|
|
2461
|
+
state.touchMoved = true;
|
|
2462
|
+
}
|
|
2463
|
+
const speedScale = state.fov / ENGINE_CONFIG.defaultFov;
|
|
2464
|
+
const rotLock = Math.max(0, Math.min(1, (state.fov - 100) / (ENGINE_CONFIG.maxFov - 100)));
|
|
2465
|
+
const latFactor = 1 - rotLock * rotLock;
|
|
2466
|
+
state.targetLon += deltaX * ENGINE_CONFIG.dragSpeed * speedScale;
|
|
2467
|
+
state.targetLat += deltaY * ENGINE_CONFIG.dragSpeed * speedScale * latFactor;
|
|
2468
|
+
state.targetLat = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, state.targetLat));
|
|
2469
|
+
state.velocityX = deltaX * ENGINE_CONFIG.dragSpeed * speedScale;
|
|
2470
|
+
state.velocityY = deltaY * ENGINE_CONFIG.dragSpeed * speedScale * latFactor;
|
|
2471
|
+
state.lon = state.targetLon;
|
|
2472
|
+
state.lat = state.targetLat;
|
|
2473
|
+
} else if (touches.length === 2) {
|
|
2474
|
+
const t0 = touches[0];
|
|
2475
|
+
const t1 = touches[1];
|
|
2476
|
+
const newDistance = getTouchDistance(t0, t1);
|
|
2477
|
+
const scale = newDistance / state.pinchStartDistance;
|
|
2478
|
+
state.fov = state.pinchStartFov / scale;
|
|
2479
|
+
state.fov = Math.max(ENGINE_CONFIG.minFov, Math.min(ENGINE_CONFIG.maxFov, state.fov));
|
|
2480
|
+
handlers.onFovChange?.(state.fov);
|
|
2481
|
+
const center = getTouchCenter(t0, t1);
|
|
2482
|
+
const deltaX = center.x - state.lastMouseX;
|
|
2483
|
+
const deltaY = center.y - state.lastMouseY;
|
|
2484
|
+
state.lastMouseX = center.x;
|
|
2485
|
+
state.lastMouseY = center.y;
|
|
2486
|
+
const speedScale = state.fov / ENGINE_CONFIG.defaultFov;
|
|
2487
|
+
state.targetLon += deltaX * ENGINE_CONFIG.dragSpeed * speedScale * 0.5;
|
|
2488
|
+
state.targetLat += deltaY * ENGINE_CONFIG.dragSpeed * speedScale * 0.5;
|
|
2489
|
+
state.targetLat = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, state.targetLat));
|
|
2490
|
+
state.lon = state.targetLon;
|
|
2491
|
+
state.lat = state.targetLat;
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
function onTouchEnd(e) {
|
|
2495
|
+
e.preventDefault();
|
|
2496
|
+
const remainingTouches = e.touches.length;
|
|
2497
|
+
if (remainingTouches === 0) {
|
|
2498
|
+
const duration = performance.now() - state.touchStartTime;
|
|
2499
|
+
const wasTap = !state.touchMoved && duration < ENGINE_CONFIG.tapMaxDuration;
|
|
2500
|
+
if (wasTap) {
|
|
2501
|
+
const rect = renderer.domElement.getBoundingClientRect();
|
|
2502
|
+
const mX = state.touchStartX - rect.left;
|
|
2503
|
+
const mY = state.touchStartY - rect.top;
|
|
2504
|
+
mouseNDC.x = mX / rect.width * 2 - 1;
|
|
2505
|
+
mouseNDC.y = -(mY / rect.height) * 2 + 1;
|
|
2506
|
+
const syntheticEvent = {
|
|
2507
|
+
clientX: state.touchStartX,
|
|
2508
|
+
clientY: state.touchStartY
|
|
2509
|
+
};
|
|
2510
|
+
const hit = pick(syntheticEvent);
|
|
2511
|
+
if (hit) {
|
|
2512
|
+
handlers.onSelect?.(hit.node);
|
|
2513
|
+
constellationLayer.setFocused(hit.node.id);
|
|
2514
|
+
if (hit.node.level === 2) setFocusedBook(hit.node.id);
|
|
2515
|
+
else if (hit.node.level === 3 && hit.node.parent) setFocusedBook(hit.node.parent);
|
|
2516
|
+
} else {
|
|
2517
|
+
setFocusedBook(null);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
state.isDragging = false;
|
|
2521
|
+
state.dragMode = "none";
|
|
2522
|
+
state.touchCount = 0;
|
|
2523
|
+
} else if (remainingTouches === 1) {
|
|
2524
|
+
const touch = e.touches[0];
|
|
2525
|
+
state.lastMouseX = touch.clientX;
|
|
2526
|
+
state.lastMouseY = touch.clientY;
|
|
2527
|
+
state.touchCount = 1;
|
|
2528
|
+
state.dragMode = "camera";
|
|
2529
|
+
state.isDragging = true;
|
|
2530
|
+
state.velocityX = 0;
|
|
2531
|
+
state.velocityY = 0;
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
function onTouchCancel(e) {
|
|
2535
|
+
e.preventDefault();
|
|
2536
|
+
state.isDragging = false;
|
|
2537
|
+
state.dragMode = "none";
|
|
2538
|
+
state.touchCount = 0;
|
|
2539
|
+
state.velocityX = 0;
|
|
2540
|
+
state.velocityY = 0;
|
|
2541
|
+
}
|
|
2542
|
+
function onGesturePrevent(e) {
|
|
2543
|
+
e.preventDefault();
|
|
2544
|
+
}
|
|
2388
2545
|
function resize() {
|
|
2389
2546
|
const w = container.clientWidth || 1;
|
|
2390
2547
|
const h = container.clientHeight || 1;
|
|
@@ -2407,6 +2564,13 @@ function createEngine({
|
|
|
2407
2564
|
});
|
|
2408
2565
|
el.addEventListener("mouseleave", onWindowBlur);
|
|
2409
2566
|
window.addEventListener("blur", onWindowBlur);
|
|
2567
|
+
el.addEventListener("touchstart", onTouchStart, { passive: false });
|
|
2568
|
+
el.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
2569
|
+
el.addEventListener("touchend", onTouchEnd, { passive: false });
|
|
2570
|
+
el.addEventListener("touchcancel", onTouchCancel, { passive: false });
|
|
2571
|
+
el.addEventListener("gesturestart", onGesturePrevent, { passive: false });
|
|
2572
|
+
el.addEventListener("gesturechange", onGesturePrevent, { passive: false });
|
|
2573
|
+
el.addEventListener("gestureend", onGesturePrevent, { passive: false });
|
|
2410
2574
|
raf = requestAnimationFrame(tick);
|
|
2411
2575
|
}
|
|
2412
2576
|
function tick() {
|
|
@@ -2448,7 +2612,7 @@ function createEngine({
|
|
|
2448
2612
|
}
|
|
2449
2613
|
let panX = 0;
|
|
2450
2614
|
let panY = 0;
|
|
2451
|
-
if (!state.isDragging && isMouseInWindow && !currentConfig?.editable) {
|
|
2615
|
+
if (!state.isDragging && isMouseInWindow && !currentConfig?.editable && !isTouchDevice) {
|
|
2452
2616
|
const t = ENGINE_CONFIG.edgePanThreshold;
|
|
2453
2617
|
const inZoneX = mouseNDC.x < -1 + t || mouseNDC.x > 1 - t;
|
|
2454
2618
|
const inZoneY = mouseNDC.y < -1 + t || mouseNDC.y > 1 - t;
|
|
@@ -2501,8 +2665,9 @@ function createEngine({
|
|
|
2501
2665
|
} else if (!state.isDragging && !flyToActive) {
|
|
2502
2666
|
state.lon += state.velocityX;
|
|
2503
2667
|
state.lat += state.velocityY;
|
|
2504
|
-
|
|
2505
|
-
state.
|
|
2668
|
+
const damping = isTouchDevice ? ENGINE_CONFIG.touchInertiaDamping : ENGINE_CONFIG.inertiaDamping;
|
|
2669
|
+
state.velocityX *= damping;
|
|
2670
|
+
state.velocityY *= damping;
|
|
2506
2671
|
if (Math.abs(state.velocityX) < 1e-6) state.velocityX = 0;
|
|
2507
2672
|
if (Math.abs(state.velocityY) < 1e-6) state.velocityY = 0;
|
|
2508
2673
|
}
|
|
@@ -2674,6 +2839,13 @@ function createEngine({
|
|
|
2674
2839
|
el.removeEventListener("wheel", onWheel);
|
|
2675
2840
|
el.removeEventListener("mouseleave", onWindowBlur);
|
|
2676
2841
|
window.removeEventListener("blur", onWindowBlur);
|
|
2842
|
+
el.removeEventListener("touchstart", onTouchStart);
|
|
2843
|
+
el.removeEventListener("touchmove", onTouchMove);
|
|
2844
|
+
el.removeEventListener("touchend", onTouchEnd);
|
|
2845
|
+
el.removeEventListener("touchcancel", onTouchCancel);
|
|
2846
|
+
el.removeEventListener("gesturestart", onGesturePrevent);
|
|
2847
|
+
el.removeEventListener("gesturechange", onGesturePrevent);
|
|
2848
|
+
el.removeEventListener("gestureend", onGesturePrevent);
|
|
2677
2849
|
}
|
|
2678
2850
|
function dispose() {
|
|
2679
2851
|
stop();
|
|
@@ -2728,7 +2900,7 @@ var init_createEngine = __esm({
|
|
|
2728
2900
|
init_projections();
|
|
2729
2901
|
init_fader();
|
|
2730
2902
|
ENGINE_CONFIG = {
|
|
2731
|
-
minFov:
|
|
2903
|
+
minFov: 1,
|
|
2732
2904
|
maxFov: 135,
|
|
2733
2905
|
defaultFov: 50,
|
|
2734
2906
|
dragSpeed: 125e-5,
|
|
@@ -2740,7 +2912,14 @@ var init_createEngine = __esm({
|
|
|
2740
2912
|
horizonLockStrength: 0.05,
|
|
2741
2913
|
edgePanThreshold: 0.15,
|
|
2742
2914
|
edgePanMaxSpeed: 0.02,
|
|
2743
|
-
edgePanDelay: 250
|
|
2915
|
+
edgePanDelay: 250,
|
|
2916
|
+
// Touch-specific
|
|
2917
|
+
touchInertiaDamping: 0.85,
|
|
2918
|
+
// Snappier than mouse (0.92)
|
|
2919
|
+
tapMaxDuration: 300,
|
|
2920
|
+
// ms
|
|
2921
|
+
tapMaxDistance: 10
|
|
2922
|
+
// px
|
|
2744
2923
|
};
|
|
2745
2924
|
ORDER_REVEAL_CONFIG = {
|
|
2746
2925
|
globalDim: 0.85,
|