Skip to content

Commit dca75f5

Browse files
authored
Merge pull request #1 from lettucegoblin/fixes
feat: integrate background image as a Cytoscape node for improved ren…
2 parents 7df1f0e + accaec4 commit dca75f5

File tree

9 files changed

+581
-154
lines changed

9 files changed

+581
-154
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.jsx

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -418,12 +418,20 @@ function App() {
418418
setCdnBaseUrl(cdnBaseUrl);
419419
}, [cdnBaseUrl]);
420420

421-
// Save camera state to localStorage (kept as-is)
421+
// Save camera state to localStorage (debounced to prevent excessive writes)
422422
useEffect(() => {
423-
localStorage.setItem("shipLogCamera", JSON.stringify({
424-
zoom: zoomLevel,
425-
position: cameraPosition
426-
}));
423+
const timeoutId = setTimeout(() => {
424+
try {
425+
localStorage.setItem("shipLogCamera", JSON.stringify({
426+
zoom: zoomLevel,
427+
position: cameraPosition
428+
}));
429+
} catch (e) {
430+
printWarn('Failed to save camera to localStorage:', e);
431+
}
432+
}, 1000); // Save at most once per second
433+
434+
return () => clearTimeout(timeoutId);
427435
}, [zoomLevel, cameraPosition]);
428436

429437
// Save mode to localStorage
@@ -1045,6 +1053,18 @@ function App() {
10451053
const memoSelectedEdgeIds = useMemo(() => selectedEdgeIds, [selectedEdgeIds]);
10461054
const memoCameraPosition = useMemo(() => cameraPosition, [cameraPosition]);
10471055
const memoNotes = useMemo(() => graphData.notes, [graphData.notes]);
1056+
1057+
// Memoize background image object to prevent infinite re-renders
1058+
const memoBgImage = useMemo(() => {
1059+
if (!bgImage.imageUrl) return null;
1060+
console.log('🔄 [App] memoBgImage recalculating with calibration:', bgCalibration);
1061+
return {
1062+
imageUrl: bgImage.imageUrl,
1063+
visible: bgImage.visible,
1064+
opacity: bgImage.opacity,
1065+
calibration: bgCalibration
1066+
};
1067+
}, [bgImage.imageUrl, bgImage.visible, bgImage.opacity, bgCalibration]);
10481068

10491069
const handleLoadFromCdnButton = useCallback((cdnBaseUrlArg) => {
10501070
handleLoadFromCdn({
@@ -1124,22 +1144,21 @@ useEffect(() => {
11241144
</div>
11251145
</div>
11261146

1127-
{/* Background image underlay */}
1147+
{/* Background image underlay - DISABLED: Now integrated into Cytoscape canvas */}
1148+
{/* Background now renders as a Cytoscape node for perfect sync with pan/zoom */}
1149+
{/* See CytoscapeGraph bgImage prop and bgNodeAdapter.js */}
1150+
{/*
11281151
{bgImage.imageUrl && bgImage.visible && (
11291152
<BgImageLayer
11301153
url={bgImage.imageUrl}
11311154
visible={bgImage.visible}
11321155
opacity={bgImage.opacity}
1133-
pan={livePan} // 🔴 live pan (every frame)
1134-
zoom={liveZoom} // 🔴 live zoom (every frame)
1135-
calibration={ bgCalibration
1136-
// keep your existing semantics: scale is a percentage
1137-
// tx: bgImage.x, // world offset X (same units as node positions)
1138-
// ty: bgImage.y, // world offset Y
1139-
// s: (bgImage.scale ?? 100) / 100 // world units per image pixel
1140-
}
1156+
pan={livePan}
1157+
zoom={liveZoom}
1158+
calibration={bgCalibration}
11411159
/>
11421160
)}
1161+
*/}
11431162

11441163
{canEdit && (
11451164
<GraphControls
@@ -1346,6 +1365,7 @@ useEffect(() => {
13461365
showNoteCountOverlay={showNoteCountOverlay}
13471366
notes={memoNotes}
13481367
visited={visited} /* pass visited to drive unseen badges */
1368+
bgImage={memoBgImage}
13491369
/>
13501370

13511371
{compassVisible && (

src/bg/useBgImageState.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@
33
/**
44
* useBgImageState — Background image state + helpers
55
*
6-
* Responsibilities
6+
* Responsi // handy derived mapping for BgImageLayer "calibration"
7+
// ⚡ Memoized to prevent infinite render loops
8+
const calibration = useMemo(() => {
9+
console.log('🔄 [useBgImageState] Calibration recalculating:', { x: bgImage.x, y: bgImage.y, scale: bgImage.scale });
10+
return {
11+
tx: bgImage.x,
12+
ty: bgImage.y,
13+
s: (bgImage.scale ?? 100) / 100
14+
};
15+
}, [bgImage.x, bgImage.y, bgImage.scale]);s
716
* - Encapsulates bg image metadata (src, x, y, scale, opacity, included, visible).
817
* - Exposes load/delete/toggle/transform helpers for App to compose.
918
*
@@ -12,7 +21,7 @@
1221
* setIncluded(bool), setTransform({x,y,scale,opacity}) }
1322
*/
1423

15-
import { useCallback, useEffect, useState } from "react";
24+
import { useCallback, useEffect, useState, useMemo } from "react";
1625
import { printDebug } from "../utils/debug";
1726

1827
/** LocalStorage key */
@@ -106,11 +115,12 @@ export function useBgImageState() {
106115
}, []);
107116

108117
// handy derived mapping for BgImageLayer “calibration”
109-
const calibration = {
118+
// ⚡ Memoized to prevent infinite render loops
119+
const calibration = useMemo(() => ({
110120
tx: bgImage.x,
111121
ty: bgImage.y,
112122
s: (bgImage.scale ?? 100) / 100
113-
};
123+
}), [bgImage.x, bgImage.y, bgImage.scale]);
114124

115125
return {
116126
bgImage,

0 commit comments

Comments
 (0)