Skip to content

Commit 9c83448

Browse files
committed
refactor use node for bg img underlay
1 parent 659f83d commit 9c83448

5 files changed

Lines changed: 380 additions & 20 deletions

File tree

src/App.jsx

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ import { appStateReducer, initialAppState, ACTION_TYPES } from "./appStateReduce
6464
import { ZOOM_TO_SELECTION, DEBUG_LOGGING, DEV_MODE, GRAYSCALE_IMAGES, CAMERA_INFO_HIDDEN } from "./config/features.js";
6565
import { handleLoadFromCdn, setCdnBaseUrl, getCdnBaseUrl } from "./utils/cdnHelpers.js"; // getMapUrlFromQuery, clearQueryParams
6666
import BgImageModal from "./components/BgImageModal.jsx";
67-
import BgImageLayer from "./bg/BgImageLayer";
67+
// import BgImageLayer from "./bg/BgImageLayer";
6868
import { useBgImageState } from "./bg/useBgImageState";
6969
// import { loadImageWithFallback } from "./utils/imageLoader.js";
7070
// import { dataUrlOrBlobToWebpDataUrl } from "./utils/imageUtils.js"
@@ -1124,22 +1124,6 @@ useEffect(() => {
11241124
</div>
11251125
</div>
11261126

1127-
{/* Background image underlay */}
1128-
{bgImage.imageUrl && bgImage.visible && (
1129-
<BgImageLayer
1130-
url={bgImage.imageUrl}
1131-
visible={bgImage.visible}
1132-
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-
}
1141-
/>
1142-
)}
11431127

11441128
{canEdit && (
11451129
<GraphControls
@@ -1346,6 +1330,14 @@ useEffect(() => {
13461330
showNoteCountOverlay={showNoteCountOverlay}
13471331
notes={memoNotes}
13481332
visited={visited} /* pass visited to drive unseen badges */
1333+
// Background node integration
1334+
bgNodeProps={{
1335+
imageUrl: bgImage?.imageUrl || '',
1336+
visible: !!bgImage?.visible,
1337+
opacity: Number.isFinite(bgImage?.opacity) ? bgImage.opacity : 100,
1338+
calibration: bgCalibration || { tx: 0, ty: 0, s: 1 }
1339+
}}
1340+
13491341
/>
13501342

13511343
{compassVisible && (

src/components/CytoscapeGraph.jsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { setNoteCountsVisible, refreshPositions as refreshOverlayPositions } fro
3333
import { printDebug, printError, printWarn } from "../utils/debug.js";
3434
import { TEST_ICON_SVG } from "../constants/testAssets.js";
3535
import { GRAYSCALE_IMAGES } from "../config/features.js";
36+
import { ensureBgNode, removeBgNode } from "../graph/bgNodeAdapter.js";
3637

3738
function CytoscapeGraph({
3839
nodes = [],
@@ -65,7 +66,8 @@ function CytoscapeGraph({
6566
showNoteCountOverlay = false,
6667
notes = {},
6768
visited = { nodes: new Set(), edges: new Set() },
68-
onCytoscapeInstanceReady
69+
onCytoscapeInstanceReady,
70+
bgNodeProps = { imageUrl: '', visible: false, opacity: 100, calibration: { tx: 0, ty: 0, s: 1 } }
6971
}) {
7072

7173
const containerRef = useRef(null);
@@ -212,6 +214,7 @@ function CytoscapeGraph({
212214
if (cy._eventCleanup) cy._eventCleanup();
213215
if (cy._edgeCountCleanup) cy._edgeCountCleanup();
214216
if (cy._viewportCleanup) cy._viewportCleanup();
217+
try { removeBgNode(cy); } catch { /* noop */ }
215218

216219
cy.destroy();
217220
} catch {
@@ -222,6 +225,34 @@ function CytoscapeGraph({
222225
// eslint-disable-next-line react-hooks/exhaustive-deps
223226
}, []); // mount once
224227

228+
// ------------------- Background image node sync -------------------
229+
useEffect(() => {
230+
const cy = cyRef.current;
231+
if (!cy) return;
232+
const { imageUrl, visible, opacity, calibration } = bgNodeProps || {};
233+
234+
// Create/update or remove the background node
235+
ensureBgNode(cy, {
236+
imageUrl,
237+
visible,
238+
opacity,
239+
calibration
240+
});
241+
242+
// Nothing special required on cleanup; node is removed when invisible.
243+
// Still, if component unmounts while visible, clean it up.
244+
return () => {
245+
// Only remove on unmount (don’t fight with other effects)
246+
if (cy?.destroyed && !cy.destroyed()) {
247+
// noop (we remove on visibility false in ensureBgNode)
248+
}
249+
};
250+
// Include a stable calibration dep (stringify small object is fine here)
251+
}, [
252+
cyRef,
253+
bgNodeProps
254+
]);
255+
225256
// ------------------- Re-wire events when handlers/mode change -------------------
226257
useEffect(() => {
227258
printDebug(`🔌 [CytoscapeGraph] Event handlers changed, re-wiring events`);
@@ -373,6 +404,7 @@ function CytoscapeGraph({
373404

374405
// Optional: refresh layout on large changes
375406
if (updatedCount > 0 && majorChange) {
407+
376408
syncElements(cy, { nodes, edges, mapName, cdnBaseUrl }, { mode });
377409
try { updateOverlays(cy, notesRef.current, showNoteCountOverlay, visitedRef.current, mode); } catch {
378410
printWarn('Failed to update edge count node positions after major position-only update');

src/cytoscapeStyles.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,32 @@ const badgeRules = [
383383
...unseenBadgeRules
384384
];
385385

386+
// Background image node - giant locked node that renders behind everything
387+
const backgroundImageNodeRule = {
388+
selector: 'node.background-image-node',
389+
style: {
390+
'shape': 'rectangle',
391+
'background-image': 'data(imageUrl)',
392+
'background-fit': 'contain',
393+
// Draw ONLY the image; no solid fill behind it
394+
'background-opacity': 0,
395+
// Keep element fully opaque; control alpha via image
396+
'opacity': 1,
397+
// Single source of truth for alpha (0..1)
398+
'background-image-opacity': 'data(bgAlpha)',
399+
'border-width': 0,
400+
'label': '',
401+
'z-index-compare': 'manual',
402+
'z-index': -1, // Below everything
403+
'events': 'no', // Not interactive
404+
'overlay-opacity': 0 // No selection overlay
405+
}
406+
};
407+
386408
const cytoscapeStyles = [
387409
{ selector: 'node.entry-parent', style: entryParentBase },
388410
{ selector: 'node.entry', style: entryNodeBase },
411+
backgroundImageNodeRule,
389412
...sizeRules,
390413
...colorRules,
391414
...parentSelectionRules,

0 commit comments

Comments
 (0)