@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.d.cts
CHANGED
|
@@ -29243,7 +29243,7 @@ interface Projection {
|
|
|
29243
29243
|
*/
|
|
29244
29244
|
isClipped(dirZ: number): boolean;
|
|
29245
29245
|
}
|
|
29246
|
-
type ProjectionId = "perspective" | "stereographic"
|
|
29246
|
+
type ProjectionId = "perspective" | "stereographic";
|
|
29247
29247
|
declare const PROJECTIONS: Record<ProjectionId, () => Projection>;
|
|
29248
29248
|
|
|
29249
29249
|
export { type BibleJSON, type ConstellationConfig, type GenerateOptions, type HierarchyFilter, PROJECTIONS, type Projection, type ProjectionId, type SceneLink, type SceneModel, type SceneNode, type StarArrangement, StarMap, type StarMapConfig, type StarMapHandle, type StarMapProps, bibleToSceneModel, defaultGenerateOptions, defaultStars, generateArrangement };
|
package/dist/index.d.ts
CHANGED
|
@@ -29243,7 +29243,7 @@ interface Projection {
|
|
|
29243
29243
|
*/
|
|
29244
29244
|
isClipped(dirZ: number): boolean;
|
|
29245
29245
|
}
|
|
29246
|
-
type ProjectionId = "perspective" | "stereographic"
|
|
29246
|
+
type ProjectionId = "perspective" | "stereographic";
|
|
29247
29247
|
declare const PROJECTIONS: Record<ProjectionId, () => Projection>;
|
|
29248
29248
|
|
|
29249
29249
|
export { type BibleJSON, type ConstellationConfig, type GenerateOptions, type HierarchyFilter, PROJECTIONS, type Projection, type ProjectionId, type SceneLink, type SceneModel, type SceneNode, type StarArrangement, StarMap, type StarMapConfig, type StarMapHandle, type StarMapProps, bibleToSceneModel, defaultGenerateOptions, defaultStars, generateArrangement };
|
package/dist/index.js
CHANGED
|
@@ -724,10 +724,14 @@ var init_projections = __esm({
|
|
|
724
724
|
maxFov = 165;
|
|
725
725
|
glslProjectionType = 2;
|
|
726
726
|
/** FOV thresholds for blend transition (degrees) */
|
|
727
|
-
blendStart
|
|
728
|
-
blendEnd
|
|
727
|
+
blendStart;
|
|
728
|
+
blendEnd;
|
|
729
729
|
/** Current blend factor, updated via setFov() */
|
|
730
730
|
blend = 0;
|
|
731
|
+
constructor(blendStart = 40, blendEnd = 100) {
|
|
732
|
+
this.blendStart = blendStart;
|
|
733
|
+
this.blendEnd = blendEnd;
|
|
734
|
+
}
|
|
731
735
|
/** Call this each frame / when FOV changes so forward/inverse stay in sync */
|
|
732
736
|
setFov(fovDeg) {
|
|
733
737
|
if (fovDeg <= this.blendStart) {
|
|
@@ -780,8 +784,7 @@ var init_projections = __esm({
|
|
|
780
784
|
};
|
|
781
785
|
PROJECTIONS = {
|
|
782
786
|
perspective: () => new PerspectiveProjection(),
|
|
783
|
-
stereographic: () => new StereographicProjection()
|
|
784
|
-
blended: () => new BlendedProjection()
|
|
787
|
+
stereographic: () => new StereographicProjection()
|
|
785
788
|
};
|
|
786
789
|
}
|
|
787
790
|
});
|
|
@@ -884,10 +887,21 @@ function createEngine({
|
|
|
884
887
|
draggedStarIndex: -1,
|
|
885
888
|
draggedDist: 2e3,
|
|
886
889
|
draggedGroup: null,
|
|
887
|
-
tempArrangement: {}
|
|
890
|
+
tempArrangement: {},
|
|
891
|
+
// Touch state
|
|
892
|
+
touchCount: 0,
|
|
893
|
+
touchStartTime: 0,
|
|
894
|
+
touchStartX: 0,
|
|
895
|
+
touchStartY: 0,
|
|
896
|
+
touchMoved: false,
|
|
897
|
+
pinchStartDistance: 0,
|
|
898
|
+
pinchStartFov: ENGINE_CONFIG.defaultFov,
|
|
899
|
+
pinchCenterX: 0,
|
|
900
|
+
pinchCenterY: 0
|
|
888
901
|
};
|
|
889
902
|
const mouseNDC = new THREE5.Vector2();
|
|
890
903
|
let isMouseInWindow = false;
|
|
904
|
+
let isTouchDevice = false;
|
|
891
905
|
let edgeHoverStart = 0;
|
|
892
906
|
let handlers = { onSelect, onHover, onArrangementChange, onFovChange };
|
|
893
907
|
let currentConfig;
|
|
@@ -895,7 +909,7 @@ function createEngine({
|
|
|
895
909
|
function mix(a, b, t) {
|
|
896
910
|
return a * (1 - t) + b * t;
|
|
897
911
|
}
|
|
898
|
-
let currentProjection =
|
|
912
|
+
let currentProjection = new BlendedProjection(ENGINE_CONFIG.blendStart, ENGINE_CONFIG.blendEnd);
|
|
899
913
|
function syncProjectionState() {
|
|
900
914
|
if (currentProjection instanceof BlendedProjection) {
|
|
901
915
|
currentProjection.setFov(state.fov);
|
|
@@ -1933,9 +1947,13 @@ function createEngine({
|
|
|
1933
1947
|
let lastAppliedLat = void 0;
|
|
1934
1948
|
let lastBackdropCount = void 0;
|
|
1935
1949
|
function setProjection(id) {
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1950
|
+
if (id === "blended") {
|
|
1951
|
+
currentProjection = new BlendedProjection(ENGINE_CONFIG.blendStart, ENGINE_CONFIG.blendEnd);
|
|
1952
|
+
} else {
|
|
1953
|
+
const factory = PROJECTIONS[id];
|
|
1954
|
+
if (!factory) return;
|
|
1955
|
+
currentProjection = factory();
|
|
1956
|
+
}
|
|
1939
1957
|
updateUniforms();
|
|
1940
1958
|
}
|
|
1941
1959
|
function setConfig(cfg) {
|
|
@@ -2050,7 +2068,8 @@ function createEngine({
|
|
|
2050
2068
|
const w = rect.width;
|
|
2051
2069
|
const h = rect.height;
|
|
2052
2070
|
let closestLabel = null;
|
|
2053
|
-
|
|
2071
|
+
const LABEL_THRESHOLD = isTouchDevice ? 48 : 40;
|
|
2072
|
+
let minLabelDist = LABEL_THRESHOLD;
|
|
2054
2073
|
for (const item of dynamicLabels) {
|
|
2055
2074
|
if (!item.obj.visible) continue;
|
|
2056
2075
|
if (isNodeFiltered(item.node)) continue;
|
|
@@ -2363,6 +2382,144 @@ function createEngine({
|
|
|
2363
2382
|
state.targetLat = state.lat;
|
|
2364
2383
|
state.targetLon = state.lon;
|
|
2365
2384
|
}
|
|
2385
|
+
function getTouchDistance(t1, t2) {
|
|
2386
|
+
const dx = t1.clientX - t2.clientX;
|
|
2387
|
+
const dy = t1.clientY - t2.clientY;
|
|
2388
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
2389
|
+
}
|
|
2390
|
+
function getTouchCenter(t1, t2) {
|
|
2391
|
+
return {
|
|
2392
|
+
x: (t1.clientX + t2.clientX) / 2,
|
|
2393
|
+
y: (t1.clientY + t2.clientY) / 2
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
function onTouchStart(e) {
|
|
2397
|
+
e.preventDefault();
|
|
2398
|
+
isTouchDevice = true;
|
|
2399
|
+
const touches = e.touches;
|
|
2400
|
+
state.touchCount = touches.length;
|
|
2401
|
+
if (touches.length === 1) {
|
|
2402
|
+
const touch = touches[0];
|
|
2403
|
+
state.touchStartTime = performance.now();
|
|
2404
|
+
state.touchStartX = touch.clientX;
|
|
2405
|
+
state.touchStartY = touch.clientY;
|
|
2406
|
+
state.touchMoved = false;
|
|
2407
|
+
state.lastMouseX = touch.clientX;
|
|
2408
|
+
state.lastMouseY = touch.clientY;
|
|
2409
|
+
flyToActive = false;
|
|
2410
|
+
state.dragMode = "camera";
|
|
2411
|
+
state.isDragging = true;
|
|
2412
|
+
state.velocityX = 0;
|
|
2413
|
+
state.velocityY = 0;
|
|
2414
|
+
} else if (touches.length === 2) {
|
|
2415
|
+
const t0 = touches[0];
|
|
2416
|
+
const t1 = touches[1];
|
|
2417
|
+
state.pinchStartDistance = getTouchDistance(t0, t1);
|
|
2418
|
+
state.pinchStartFov = state.fov;
|
|
2419
|
+
const center = getTouchCenter(t0, t1);
|
|
2420
|
+
state.pinchCenterX = center.x;
|
|
2421
|
+
state.pinchCenterY = center.y;
|
|
2422
|
+
state.lastMouseX = center.x;
|
|
2423
|
+
state.lastMouseY = center.y;
|
|
2424
|
+
state.touchMoved = true;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
function onTouchMove(e) {
|
|
2428
|
+
e.preventDefault();
|
|
2429
|
+
const touches = e.touches;
|
|
2430
|
+
if (touches.length === 1 && state.dragMode === "camera") {
|
|
2431
|
+
const touch = touches[0];
|
|
2432
|
+
const deltaX = touch.clientX - state.lastMouseX;
|
|
2433
|
+
const deltaY = touch.clientY - state.lastMouseY;
|
|
2434
|
+
state.lastMouseX = touch.clientX;
|
|
2435
|
+
state.lastMouseY = touch.clientY;
|
|
2436
|
+
const totalDx = touch.clientX - state.touchStartX;
|
|
2437
|
+
const totalDy = touch.clientY - state.touchStartY;
|
|
2438
|
+
if (Math.sqrt(totalDx * totalDx + totalDy * totalDy) > ENGINE_CONFIG.tapMaxDistance) {
|
|
2439
|
+
state.touchMoved = true;
|
|
2440
|
+
}
|
|
2441
|
+
const speedScale = state.fov / ENGINE_CONFIG.defaultFov;
|
|
2442
|
+
const rotLock = Math.max(0, Math.min(1, (state.fov - 100) / (ENGINE_CONFIG.maxFov - 100)));
|
|
2443
|
+
const latFactor = 1 - rotLock * rotLock;
|
|
2444
|
+
state.targetLon += deltaX * ENGINE_CONFIG.dragSpeed * speedScale;
|
|
2445
|
+
state.targetLat += deltaY * ENGINE_CONFIG.dragSpeed * speedScale * latFactor;
|
|
2446
|
+
state.targetLat = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, state.targetLat));
|
|
2447
|
+
state.velocityX = deltaX * ENGINE_CONFIG.dragSpeed * speedScale;
|
|
2448
|
+
state.velocityY = deltaY * ENGINE_CONFIG.dragSpeed * speedScale * latFactor;
|
|
2449
|
+
state.lon = state.targetLon;
|
|
2450
|
+
state.lat = state.targetLat;
|
|
2451
|
+
} else if (touches.length === 2) {
|
|
2452
|
+
const t0 = touches[0];
|
|
2453
|
+
const t1 = touches[1];
|
|
2454
|
+
const newDistance = getTouchDistance(t0, t1);
|
|
2455
|
+
const scale = newDistance / state.pinchStartDistance;
|
|
2456
|
+
state.fov = state.pinchStartFov / scale;
|
|
2457
|
+
state.fov = Math.max(ENGINE_CONFIG.minFov, Math.min(ENGINE_CONFIG.maxFov, state.fov));
|
|
2458
|
+
handlers.onFovChange?.(state.fov);
|
|
2459
|
+
const center = getTouchCenter(t0, t1);
|
|
2460
|
+
const deltaX = center.x - state.lastMouseX;
|
|
2461
|
+
const deltaY = center.y - state.lastMouseY;
|
|
2462
|
+
state.lastMouseX = center.x;
|
|
2463
|
+
state.lastMouseY = center.y;
|
|
2464
|
+
const speedScale = state.fov / ENGINE_CONFIG.defaultFov;
|
|
2465
|
+
state.targetLon += deltaX * ENGINE_CONFIG.dragSpeed * speedScale * 0.5;
|
|
2466
|
+
state.targetLat += deltaY * ENGINE_CONFIG.dragSpeed * speedScale * 0.5;
|
|
2467
|
+
state.targetLat = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, state.targetLat));
|
|
2468
|
+
state.lon = state.targetLon;
|
|
2469
|
+
state.lat = state.targetLat;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
function onTouchEnd(e) {
|
|
2473
|
+
e.preventDefault();
|
|
2474
|
+
const remainingTouches = e.touches.length;
|
|
2475
|
+
if (remainingTouches === 0) {
|
|
2476
|
+
const duration = performance.now() - state.touchStartTime;
|
|
2477
|
+
const wasTap = !state.touchMoved && duration < ENGINE_CONFIG.tapMaxDuration;
|
|
2478
|
+
if (wasTap) {
|
|
2479
|
+
const rect = renderer.domElement.getBoundingClientRect();
|
|
2480
|
+
const mX = state.touchStartX - rect.left;
|
|
2481
|
+
const mY = state.touchStartY - rect.top;
|
|
2482
|
+
mouseNDC.x = mX / rect.width * 2 - 1;
|
|
2483
|
+
mouseNDC.y = -(mY / rect.height) * 2 + 1;
|
|
2484
|
+
const syntheticEvent = {
|
|
2485
|
+
clientX: state.touchStartX,
|
|
2486
|
+
clientY: state.touchStartY
|
|
2487
|
+
};
|
|
2488
|
+
const hit = pick(syntheticEvent);
|
|
2489
|
+
if (hit) {
|
|
2490
|
+
handlers.onSelect?.(hit.node);
|
|
2491
|
+
constellationLayer.setFocused(hit.node.id);
|
|
2492
|
+
if (hit.node.level === 2) setFocusedBook(hit.node.id);
|
|
2493
|
+
else if (hit.node.level === 3 && hit.node.parent) setFocusedBook(hit.node.parent);
|
|
2494
|
+
} else {
|
|
2495
|
+
setFocusedBook(null);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
state.isDragging = false;
|
|
2499
|
+
state.dragMode = "none";
|
|
2500
|
+
state.touchCount = 0;
|
|
2501
|
+
} else if (remainingTouches === 1) {
|
|
2502
|
+
const touch = e.touches[0];
|
|
2503
|
+
state.lastMouseX = touch.clientX;
|
|
2504
|
+
state.lastMouseY = touch.clientY;
|
|
2505
|
+
state.touchCount = 1;
|
|
2506
|
+
state.dragMode = "camera";
|
|
2507
|
+
state.isDragging = true;
|
|
2508
|
+
state.velocityX = 0;
|
|
2509
|
+
state.velocityY = 0;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
function onTouchCancel(e) {
|
|
2513
|
+
e.preventDefault();
|
|
2514
|
+
state.isDragging = false;
|
|
2515
|
+
state.dragMode = "none";
|
|
2516
|
+
state.touchCount = 0;
|
|
2517
|
+
state.velocityX = 0;
|
|
2518
|
+
state.velocityY = 0;
|
|
2519
|
+
}
|
|
2520
|
+
function onGesturePrevent(e) {
|
|
2521
|
+
e.preventDefault();
|
|
2522
|
+
}
|
|
2366
2523
|
function resize() {
|
|
2367
2524
|
const w = container.clientWidth || 1;
|
|
2368
2525
|
const h = container.clientHeight || 1;
|
|
@@ -2385,6 +2542,13 @@ function createEngine({
|
|
|
2385
2542
|
});
|
|
2386
2543
|
el.addEventListener("mouseleave", onWindowBlur);
|
|
2387
2544
|
window.addEventListener("blur", onWindowBlur);
|
|
2545
|
+
el.addEventListener("touchstart", onTouchStart, { passive: false });
|
|
2546
|
+
el.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
2547
|
+
el.addEventListener("touchend", onTouchEnd, { passive: false });
|
|
2548
|
+
el.addEventListener("touchcancel", onTouchCancel, { passive: false });
|
|
2549
|
+
el.addEventListener("gesturestart", onGesturePrevent, { passive: false });
|
|
2550
|
+
el.addEventListener("gesturechange", onGesturePrevent, { passive: false });
|
|
2551
|
+
el.addEventListener("gestureend", onGesturePrevent, { passive: false });
|
|
2388
2552
|
raf = requestAnimationFrame(tick);
|
|
2389
2553
|
}
|
|
2390
2554
|
function tick() {
|
|
@@ -2426,7 +2590,7 @@ function createEngine({
|
|
|
2426
2590
|
}
|
|
2427
2591
|
let panX = 0;
|
|
2428
2592
|
let panY = 0;
|
|
2429
|
-
if (!state.isDragging && isMouseInWindow && !currentConfig?.editable) {
|
|
2593
|
+
if (!state.isDragging && isMouseInWindow && !currentConfig?.editable && !isTouchDevice) {
|
|
2430
2594
|
const t = ENGINE_CONFIG.edgePanThreshold;
|
|
2431
2595
|
const inZoneX = mouseNDC.x < -1 + t || mouseNDC.x > 1 - t;
|
|
2432
2596
|
const inZoneY = mouseNDC.y < -1 + t || mouseNDC.y > 1 - t;
|
|
@@ -2479,8 +2643,9 @@ function createEngine({
|
|
|
2479
2643
|
} else if (!state.isDragging && !flyToActive) {
|
|
2480
2644
|
state.lon += state.velocityX;
|
|
2481
2645
|
state.lat += state.velocityY;
|
|
2482
|
-
|
|
2483
|
-
state.
|
|
2646
|
+
const damping = isTouchDevice ? ENGINE_CONFIG.touchInertiaDamping : ENGINE_CONFIG.inertiaDamping;
|
|
2647
|
+
state.velocityX *= damping;
|
|
2648
|
+
state.velocityY *= damping;
|
|
2484
2649
|
if (Math.abs(state.velocityX) < 1e-6) state.velocityX = 0;
|
|
2485
2650
|
if (Math.abs(state.velocityY) < 1e-6) state.velocityY = 0;
|
|
2486
2651
|
}
|
|
@@ -2652,6 +2817,13 @@ function createEngine({
|
|
|
2652
2817
|
el.removeEventListener("wheel", onWheel);
|
|
2653
2818
|
el.removeEventListener("mouseleave", onWindowBlur);
|
|
2654
2819
|
window.removeEventListener("blur", onWindowBlur);
|
|
2820
|
+
el.removeEventListener("touchstart", onTouchStart);
|
|
2821
|
+
el.removeEventListener("touchmove", onTouchMove);
|
|
2822
|
+
el.removeEventListener("touchend", onTouchEnd);
|
|
2823
|
+
el.removeEventListener("touchcancel", onTouchCancel);
|
|
2824
|
+
el.removeEventListener("gesturestart", onGesturePrevent);
|
|
2825
|
+
el.removeEventListener("gesturechange", onGesturePrevent);
|
|
2826
|
+
el.removeEventListener("gestureend", onGesturePrevent);
|
|
2655
2827
|
}
|
|
2656
2828
|
function dispose() {
|
|
2657
2829
|
stop();
|
|
@@ -2706,7 +2878,7 @@ var init_createEngine = __esm({
|
|
|
2706
2878
|
init_projections();
|
|
2707
2879
|
init_fader();
|
|
2708
2880
|
ENGINE_CONFIG = {
|
|
2709
|
-
minFov:
|
|
2881
|
+
minFov: 1,
|
|
2710
2882
|
maxFov: 135,
|
|
2711
2883
|
defaultFov: 50,
|
|
2712
2884
|
dragSpeed: 125e-5,
|
|
@@ -2718,7 +2890,14 @@ var init_createEngine = __esm({
|
|
|
2718
2890
|
horizonLockStrength: 0.05,
|
|
2719
2891
|
edgePanThreshold: 0.15,
|
|
2720
2892
|
edgePanMaxSpeed: 0.02,
|
|
2721
|
-
edgePanDelay: 250
|
|
2893
|
+
edgePanDelay: 250,
|
|
2894
|
+
// Touch-specific
|
|
2895
|
+
touchInertiaDamping: 0.85,
|
|
2896
|
+
// Snappier than mouse (0.92)
|
|
2897
|
+
tapMaxDuration: 300,
|
|
2898
|
+
// ms
|
|
2899
|
+
tapMaxDistance: 10
|
|
2900
|
+
// px
|
|
2722
2901
|
};
|
|
2723
2902
|
ORDER_REVEAL_CONFIG = {
|
|
2724
2903
|
globalDim: 0.85,
|