Fix wall click painting and gap rendering
This commit is contained in:
parent
57423a1d88
commit
242a9f1ab0
144
wall.js
144
wall.js
@ -41,6 +41,8 @@
|
||||
const wallPaintLinksBtn = document.getElementById('wall-paint-links');
|
||||
const wallPaintSmallBtn = document.getElementById('wall-paint-small');
|
||||
const wallPaintGapsBtn = document.getElementById('wall-paint-gaps');
|
||||
const wallActiveChip = document.getElementById('wall-active-color-chip');
|
||||
const wallActiveLabel = document.getElementById('wall-active-color-label');
|
||||
|
||||
const patternKey = () => (wallState.pattern === 'x' ? 'x' : 'grid');
|
||||
|
||||
@ -182,9 +184,13 @@
|
||||
else if ((type === 'c' || type.startsWith('l') || type === 'f') && (rVal >= r - 1 || cVal >= c - 1)) delete wallState.customColors[k];
|
||||
});
|
||||
}
|
||||
const wallColorMeta = (idx) => (Number.isInteger(idx) && idx >= 0 && FLAT_COLORS[idx]) ? FLAT_COLORS[idx] : { hex: WALL_FALLBACK_COLOR };
|
||||
const wallColorMeta = (idx) => {
|
||||
const meta = (Number.isInteger(idx) && idx >= 0 && FLAT_COLORS[idx]) ? FLAT_COLORS[idx] : { hex: WALL_FALLBACK_COLOR };
|
||||
return meta;
|
||||
};
|
||||
|
||||
async function buildWallSvgPayload(forExport = false, customColorsOverride = null) {
|
||||
ensureFlatColors();
|
||||
const customColors = customColorsOverride || wallState.customColors;
|
||||
|
||||
if (!ensureShared()) throw new Error('Wall designer shared helpers missing.');
|
||||
@ -323,7 +329,7 @@
|
||||
const shineRy = ry * 0.28;
|
||||
const stroke = shine.stroke ? `stroke="${shine.stroke}" stroke-width="1"` : '';
|
||||
const shineFilter = shineShadow ? `filter="url(#${shineShadow})"` : '';
|
||||
return `<ellipse cx="${sx_relative}" cy="${sy_relative}" rx="${shineRx}" ry="${shineRy}" fill="${shine.fill}" opacity="${shine.opacity ?? 1}" transform="rotate(${rot} ${sx_relative} ${sy_relative})"${stroke}${shineFilter} />`;
|
||||
return `<ellipse cx="${sx_relative}" cy="${sy_relative}" rx="${shineRx}" ry="${shineRy}" fill="${shine.fill}" opacity="${shine.opacity ?? 1}" transform="rotate(${rot} ${sx_relative} ${sy_relative})" ${stroke}${shineFilter} />`;
|
||||
};
|
||||
|
||||
if (isGrid) {
|
||||
@ -369,6 +375,7 @@
|
||||
const meta = wallColorMeta(customIdx);
|
||||
const patId = ensurePattern(meta);
|
||||
const fill = invisible ? hitFill : (isEmpty ? hitFill : (patId ? `url(#${patId})` : meta.hex));
|
||||
console.log(`h-r-c: keyId: ${keyId}, customIdx: ${customIdx}, isEmpty: ${isEmpty}, invisible: ${invisible}, fill: ${fill}, meta:`, meta);
|
||||
const stroke = invisible ? 'none' : (isEmpty ? '#cbd5e1' : (showOutline ? '#111827' : 'none'));
|
||||
const strokeW = invisible ? 0 : (isEmpty ? 1.4 : (showOutline ? 0.6 : 0));
|
||||
const filter = invisible || isEmpty ? '' : `filter="url(#${bigShadow})"`;
|
||||
@ -420,9 +427,9 @@
|
||||
? override.idx
|
||||
: (override.mode === 'empty' ? null : (showGaps ? autoGapColorIdx() : null));
|
||||
const isEmpty = gapIdx === null;
|
||||
const invisible = isEmpty && !showWireframes;
|
||||
const meta = wallColorMeta(gapIdx);
|
||||
const patId = ensurePattern(meta);
|
||||
const invisible = isEmpty && !showGaps;
|
||||
const fill = invisible ? 'rgba(0,0,0,0.001)' : (isEmpty ? 'none' : (patId ? `url(#${patId})` : meta.hex));
|
||||
const stroke = invisible ? 'none' : (isEmpty ? '#cbd5e1' : (showOutline ? '#111827' : 'none'));
|
||||
const strokeW = invisible ? 0 : (isEmpty ? 1.4 : (showOutline ? 0.6 : 0));
|
||||
@ -453,6 +460,7 @@
|
||||
const meta = wallColorMeta(centerCustomIdx);
|
||||
const patId = ensurePattern(meta);
|
||||
const fill = invisible ? 'rgba(0,0,0,0.001)' : (centerIsEmpty ? 'none' : (patId ? `url(#${patId})` : meta.hex));
|
||||
console.log(`c-r-c: keyId: ${centerKey}, customIdx: ${centerCustomIdx}, isEmpty: ${centerIsEmpty}, invisible: ${invisible}, fill: ${fill}, meta:`, meta);
|
||||
const stroke = invisible ? 'none' : (centerIsEmpty ? '#cbd5e1' : (showOutline ? '#111827' : 'none'));
|
||||
const strokeW = invisible ? 0 : (centerIsEmpty ? 1.4 : (showOutline ? 0.6 : 0));
|
||||
const filter = centerIsEmpty || invisible ? '' : `filter="url(#${smallShadow})"`;
|
||||
@ -481,6 +489,7 @@
|
||||
const meta = wallColorMeta(linkCustomIdx);
|
||||
const patId = ensurePattern(meta);
|
||||
const fill = invisibleLink ? 'rgba(0,0,0,0.001)' : (linkIsEmpty ? 'none' : (patId ? `url(#${patId})` : meta.hex));
|
||||
console.log(`l#-r-c: keyId: ${linkKey}, customIdx: ${linkCustomIdx}, isEmpty: ${linkIsEmpty}, invisible: ${invisibleLink}, fill: ${fill}, meta:`, meta);
|
||||
// Always outline X-pattern link ovals; thicken when outline toggle is on.
|
||||
const stroke = invisibleLink ? 'none' : (showOutline ? '#111827' : '#cbd5e1');
|
||||
const strokeW = invisibleLink ? 0 : (showOutline ? 0.8 : 0.6);
|
||||
@ -680,6 +689,7 @@
|
||||
function setActiveColor(idx) {
|
||||
selectedColorIdx = normalizeColorIdx(idx);
|
||||
wallState.activeColorIdx = selectedColorIdx;
|
||||
updateWallActiveChip(selectedColorIdx);
|
||||
if (window.organic?.setColor) {
|
||||
window.organic.setColor(selectedColorIdx);
|
||||
} else if (window.organic?.updateCurrentColorChip) {
|
||||
@ -698,6 +708,7 @@
|
||||
if (wallState) wallState.activeColorIdx = normalized;
|
||||
saveWallState();
|
||||
renderWallPalette();
|
||||
updateWallActiveChip(normalized);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
@ -725,6 +736,25 @@
|
||||
return val;
|
||||
}
|
||||
|
||||
function updateWallActiveChip(idx) {
|
||||
if (!wallActiveChip || !wallActiveLabel) return;
|
||||
ensureFlatColors();
|
||||
const meta = wallColorMeta(idx);
|
||||
if (meta.image) {
|
||||
wallActiveChip.style.backgroundImage = `url("${meta.image}")`;
|
||||
const zoom = Math.max(1, meta.imageZoom ?? 2.5);
|
||||
wallActiveChip.style.backgroundSize = `${100 * zoom}%`;
|
||||
wallActiveChip.style.backgroundPosition = `${(meta.imageFocus?.x ?? 0.5) * 100}% ${(meta.imageFocus?.y ?? 0.5) * 100}%`;
|
||||
wallActiveChip.style.backgroundColor = '#fff';
|
||||
} else {
|
||||
wallActiveChip.style.backgroundImage = 'none';
|
||||
wallActiveChip.style.backgroundSize = '';
|
||||
wallActiveChip.style.backgroundPosition = '';
|
||||
wallActiveChip.style.backgroundColor = meta.hex || WALL_FALLBACK_COLOR;
|
||||
}
|
||||
wallActiveLabel.textContent = meta.name || meta.hex || '';
|
||||
}
|
||||
|
||||
// Paint a specific group of nodes with the active color.
|
||||
function paintWallGroup(group) {
|
||||
ensureWallGridSize(wallState.rows, wallState.cols);
|
||||
@ -795,62 +825,22 @@
|
||||
renderWall();
|
||||
}
|
||||
|
||||
// Apply an immediate visual change to the clicked element so users see feedback even before a full re-render.
|
||||
function applyImmediateFill(el, colorIdx) {
|
||||
if (!el) return;
|
||||
const shape = el.querySelector('circle,ellipse');
|
||||
if (!shape) return;
|
||||
const isEmpty = !Number.isInteger(colorIdx);
|
||||
const meta = wallColorMeta(isEmpty ? null : colorIdx);
|
||||
const emptyHitFill = wallState.showWireframes ? 'none' : 'rgba(0,0,0,0.001)';
|
||||
const fill = isEmpty ? emptyHitFill : (meta.hex || WALL_FALLBACK_COLOR);
|
||||
const stroke = isEmpty
|
||||
? (wallState.showWireframes ? '#cbd5e1' : 'none')
|
||||
: (wallState.outline ? '#111827' : 'none');
|
||||
const strokeW = isEmpty
|
||||
? (wallState.showWireframes ? 1.2 : 0)
|
||||
: (wallState.outline ? 0.6 : 0);
|
||||
shape.setAttribute('fill', fill);
|
||||
shape.setAttribute('stroke', stroke);
|
||||
shape.setAttribute('stroke-width', strokeW);
|
||||
}
|
||||
|
||||
// After rendering the SVG, enforce fill/stroke based on current state to avoid any template mismatch.
|
||||
function paintDomFromState() {
|
||||
if (!wallDisplay) return;
|
||||
const nodes = wallDisplay.querySelectorAll('[data-wall-key]');
|
||||
nodes.forEach(node => {
|
||||
const key = node.dataset?.wallKey;
|
||||
const idx = getStoredColorForKey(key);
|
||||
const shape = node.querySelector('circle,ellipse');
|
||||
if (!shape) return;
|
||||
const isEmpty = !Number.isInteger(idx);
|
||||
const meta = wallColorMeta(isEmpty ? null : idx);
|
||||
const emptyHitFill = wallState.showWireframes ? 'none' : 'rgba(0,0,0,0.001)';
|
||||
const fill = isEmpty ? emptyHitFill : (meta.hex || WALL_FALLBACK_COLOR);
|
||||
const stroke = isEmpty
|
||||
? (wallState.showWireframes ? '#cbd5e1' : 'none')
|
||||
: (wallState.outline ? '#111827' : 'none');
|
||||
const strokeW = isEmpty
|
||||
? (wallState.showWireframes ? 1.2 : 0)
|
||||
: (wallState.outline ? 0.6 : 0);
|
||||
shape.setAttribute('fill', fill);
|
||||
shape.setAttribute('stroke', stroke);
|
||||
shape.setAttribute('stroke-width', strokeW);
|
||||
});
|
||||
}
|
||||
|
||||
async function renderWall() {
|
||||
if (!wallDisplay) return;
|
||||
ensureWallGridSize(wallState.rows, wallState.cols);
|
||||
if (wallGridLabel) wallGridLabel.textContent = `${wallState.cols} × ${wallState.rows}`;
|
||||
try {
|
||||
console.info('[Wall] render start');
|
||||
const { svgString } = await buildWallSvgPayload(false);
|
||||
|
||||
wallDisplay.innerHTML = svgString;
|
||||
|
||||
// Force a reflow to ensure the browser repaints the new SVG.
|
||||
void wallDisplay.offsetWidth;
|
||||
renderWallUsedPalette();
|
||||
// paintDomFromState();
|
||||
console.info('[Wall] render done');
|
||||
} catch (err) {
|
||||
console.error('[Wall] render failed', err);
|
||||
console.error('[Wall] render failed', err?.stack || err);
|
||||
wallDisplay.innerHTML = `<div class="p-4 text-sm text-red-600">Could not render wall.</div>`;
|
||||
}
|
||||
}
|
||||
@ -901,6 +891,7 @@
|
||||
wallPaletteEl.appendChild(row);
|
||||
});
|
||||
renderWallUsedPalette();
|
||||
updateWallActiveChip(getActiveWallColorIdx());
|
||||
}
|
||||
|
||||
function syncWallInputs() {
|
||||
@ -982,27 +973,6 @@
|
||||
return null;
|
||||
};
|
||||
|
||||
wallDisplay?.addEventListener('click', (e) => {
|
||||
const hit = findWallNode(e.target);
|
||||
const key = hit?.dataset?.wallKey;
|
||||
if (!key) return;
|
||||
|
||||
const activeColorIdx = getActiveWallColorIdx();
|
||||
|
||||
// Blindly set the color. This removes the toggle logic for diagnostics.
|
||||
const newCustomColors = { ...wallState.customColors, [key]: activeColorIdx };
|
||||
|
||||
// Update the global state.
|
||||
wallState.customColors = newCustomColors;
|
||||
|
||||
// Persist the new state.
|
||||
saveWallState();
|
||||
saveActivePatternState();
|
||||
|
||||
// Explicitly pass the new state to the render function.
|
||||
renderWall(newCustomColors);
|
||||
});
|
||||
|
||||
const setHoverCursor = (e) => {
|
||||
const hit = findWallNode(e.target);
|
||||
wallDisplay.style.cursor = hit ? 'crosshair' : 'auto';
|
||||
@ -1010,6 +980,38 @@
|
||||
wallDisplay?.addEventListener('pointermove', setHoverCursor);
|
||||
wallDisplay?.addEventListener('pointerleave', () => { wallDisplay.style.cursor = 'auto'; });
|
||||
|
||||
wallDisplay.addEventListener('click', (e) => {
|
||||
const hit = findWallNode(e.target);
|
||||
if (!hit) return;
|
||||
|
||||
const key = hit.dataset.wallKey;
|
||||
if (!key) return;
|
||||
|
||||
const activeColor = getActiveWallColorIdx();
|
||||
if (!Number.isInteger(activeColor)) return;
|
||||
const rawStored = wallState.customColors?.[key];
|
||||
const parsedStored = Number.isInteger(rawStored) ? rawStored : Number.parseInt(rawStored, 10);
|
||||
const storedColor = Number.isInteger(parsedStored) && parsedStored >= 0 ? normalizeColorIdx(parsedStored) : null;
|
||||
const hasStoredColor = Number.isInteger(storedColor) && storedColor >= 0;
|
||||
|
||||
if (e.altKey) {
|
||||
if (Number.isInteger(storedColor)) {
|
||||
setActiveColor(storedColor);
|
||||
renderWallPalette();
|
||||
renderWallUsedPalette();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Only erase when a modifier is held (shift/ctrl/cmd). A plain click always paints.
|
||||
const isEraseClick = e.shiftKey || e.metaKey || e.ctrlKey;
|
||||
wallState.customColors[key] = isEraseClick ? -1 : activeColor;
|
||||
|
||||
saveActivePatternState();
|
||||
saveWallState();
|
||||
renderWall();
|
||||
});
|
||||
|
||||
wallClearBtn?.addEventListener('click', () => {
|
||||
ensureWallGridSize(wallState.rows, wallState.cols);
|
||||
wallState.colors = wallState.colors.map(row => row.map(() => -1));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user