Cluster labels: topmost layer + user-facing size setting
- Draw order: the cluster-label block moves from layer 5 to THE LAST layer of draw() — labels can never be occluded by node labels or hover names (user 2026-09-17; chose "topmost" after reassessing that discs were never actually on top — the white-out over discs is source-over compositing of the 85%-alpha text over near-white node fill, an optical contrast flip, not a draw-order bug). - clusterLabelSize: new persisted setting (default 14 = the old REGION_LABEL_SIZE constant, so rendering is pixel-identical). Screen px at ANY zoom (drawn at size/k — camera-compensated, UI-like, same law as before). Log-slider range [7, 28] puts the default dead-center per the house slider law. Sanitized: old persisted files (no field) fall back to 14. - Panel: the Cluster labels checkbox moves from the Labels section into Regions (user: cluster settings live with regions), joined by the new "Cluster label size" slider. Labels stay decoupled from the Background colors compute switch, as designed on 2026-09-16.
This commit is contained in:
+21
-19
@@ -169,7 +169,6 @@ const COMMIT_DEBOUNCE_MS = 250; // settle → ledger PUT delay (new drags cancel
|
||||
* live-physics frame pays one tiny blit instead of 32 clusters × 4
|
||||
* offset polygons re-rasterized at DPR 2 (the old GPU coil-whine). */
|
||||
const REGION_EVERY = 3; // live-sim frames: rebuild every Nth, blit the rest
|
||||
const REGION_LABEL_SIZE = 14;
|
||||
|
||||
|
||||
|
||||
@@ -1357,24 +1356,6 @@ export class GraphCanvasRenderer {
|
||||
ctx.fillStyle = data.colors.node;
|
||||
ctx.fill(this.nodesPath);
|
||||
|
||||
// 5. Cluster region labels — inverse-gated on THE zoom control.
|
||||
// Viewer-relative by design (UI, not graph content): drawn at the
|
||||
// world centroid with a font of REGION_LABEL_SIZE / k, which is a
|
||||
// constant 18px on screen at any zoom.
|
||||
if (settings.clusterLabels && regionAlpha > 0.01) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = regionAlpha;
|
||||
ctx.font = `${LABEL_WEIGHT} ${REGION_LABEL_SIZE / k}px ${data.fontFamily}`;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
for (const rm of this.regionMeta) {
|
||||
if (!rm.label) continue;
|
||||
ctx.fillStyle = `hsla(${rm.hue}, 55%, 78%, 0.85)`;
|
||||
ctx.fillText(rm.label, rm.cx, rm.cy);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// 6. Node labels — one global gate, faded together. World-scaled
|
||||
// with the graph (they grow as you zoom in, Obsidian-style).
|
||||
if (nodeAlpha > 0.01) {
|
||||
@@ -1413,5 +1394,26 @@ export class GraphCanvasRenderer {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Cluster region labels — THE TOPMOST layer (user 2026-09-16:
|
||||
// node labels and hover names drew over them before; region labels
|
||||
// must never be occluded by content). Inverse-gated on THE zoom
|
||||
// control (visible zoomed-out, fading as node labels come in).
|
||||
// Viewer-relative by design (UI, not graph content): drawn at the
|
||||
// world centroid with a font of clusterLabelSize / k — a constant
|
||||
// size on screen at any zoom (user-tunable since 2026-09-16).
|
||||
if (settings.clusterLabels && regionAlpha > 0.01) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = regionAlpha;
|
||||
ctx.font = `${LABEL_WEIGHT} ${settings.clusterLabelSize / k}px ${data.fontFamily}`;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
for (const rm of this.regionMeta) {
|
||||
if (!rm.label) continue;
|
||||
ctx.fillStyle = `hsla(${rm.hue}, 55%, 78%, 0.85)`;
|
||||
ctx.fillText(rm.label, rm.cx, rm.cy);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user