0xa85e2099…e34asent to0x78f6609c…ce19·#25,413,114·view on Etherscan
form);
}
svgDrawArtifactFrame();
}
function svgDrawArtifactFrame() {
if (!artifactFrame || !artifactFrameLayer) return;
artifactFrameLayer.textContent = "";
const color = SIGNAL_RITUAL_ENGINE.visualRenderer.background === "#f7f7f2" ? "#08090b" : "#f3f0e8";
const corner = artifactFrame.borderGlyphs?.corner || "+";
const horizontal = artifactFrame.borderGlyphs?.horizontal || "-";
const vertical = artifactFrame.borderGlyphs?.vertical || "|";
function addText(text, column, row) {
const node = document.createElementNS(svgNs, "text");
node.setAttribute("class", "cell");
node.setAttribute("x", column * cell + cell / 2);
node.setAttribute("y", row * cell + cell / 2);
node.setAttribute("font-size", Math.round(cell * 1.18));
node.setAttribute("font-weight", "700");
node.setAttribute("fill", color);
node.setAttribute("opacity", "0.92");
node.textContent = text;
artifactFrameLayer.appendChild(node);
}
svgDrawBorderLine(0, color, corner, horizontal, [
{ text: artifactFrame.collectionName || "Signal Rituals", align: "left", max: Math.floor(artifactFrameColumns * 0.38) },
{ text: artifactFrame.artworkName || "Untitled", align: "right", max: Math.floor(artifactFrameColumns * 0.48) },
]);
svgDrawBorderLine(artifactFrameRows - 1, color, corner, horizontal, [
{ text: "Seed: " + (artifactFrame.seed || "-"), align: "right", max: Math.floor(artifactFrameColumns * 0.44) },
]);
for (let row = 1; row < artifactFrameRows - 1; row += 1) {
addText(vertical, 0, row);
addText(vertical, artifactFrameColumns - 1, row);
}
}
function svgFrameText(text, maxLength) {
text = String(text || "").replace(/[^\x20-\x7E]/g, "").replace(/\s+/g, " ").trim();
if (text.length <= maxLength) return text;
return maxLength <= 3 ? text.slice(0, maxLength) : text.slice(0, maxLength - 3) + "...";
}
function svgDrawBorderLine(row, color, corner, horizontal, segments = []) {
const line = new Array(artifactFrameColumns).fill(horizontal);
for (let column = 0; column < artifactFrameColumns; column += 1) {
if (column === 0 || column === artifactFrameColumns - 1) line[column] = corner;
}
const occupied = new Array(artifactFrameColumns).fill(false);
occupied[0] = true;
occupied[artifactFrameColumns - 1] = true;
for (const segment of segments) {
const text = " " + svgFrameText(segment.text, Math.max(0, segment.max || artifactFrameColumns - 4)) + " ";
let start = 2;
if (segment.align === "center") start = Math.floor((artifactFrameColumns - text.length) / 2);
if (segment.align === "right") start = artifactFrameColumns - text.length - 2;
start = Math.max(1, Math.min(artifactFrameColumns - text.length - 1, start));
for (let index = 0; index < text.length; index += 1) {
const column = start + index;
if (column <= 0 || column >= artifactFrameColumns - 1 || occupied[column]) continue;
line[column] = text[index];
occupied[column] = true;
}
}
for (let column = 0; column < artifactFrameColumns; column += 1) {
const node = document.createElementNS(svgNs, "text");
node.setAttribute("class", "cell");
node.setAttribute("x", column * cell + cell / 2);
node.setAttribute("y", row * cell + cell / 2);
node.setAttribute("font-size", Math.round(cell * 1.18));
node.setAttribute("font-weight", "700");
node.setAttribute("fill", color);
node.setAttribute("opacity", "0.92");
node.textContent = line[column];
artifactFrameLayer.appendChild(node);
}
}
function createCellNodes(parent, output) {
for (let row = 0; row < rows; row += 1) {
for (let column = 0; column < columns; column += 1) {
output.push(svgCreateCellNode(parent, column, row));
}
}
}
function svgCreateCellNode(parent, column, row) {
const tuning = SIGNAL_RITUAL_ENGINE.visualRenderer.rendererTuning || {};
const glyphScale = tuning.glyphScale ?? 1.25;
const text = document.createElementNS(svgNs, "text");
text.setAttribute("class", "cell");
text.setAttribute("x", column * cell + cell / 2);
text.setAttribute("y", row * cell + cell / 2);
text.setAttribute("font-size", Math.round(cell * glyphScale));
text.textContent = " ";
parent.appendChild(text);
return text;
}
function svgRowGlitchOffset(row, frame) {
const effects = grEffectSettings();
const passes = grActiveGlitchPasses(frame);
const fullWidth = columns * cell;
const rowY = row * cell;
let offset = 0;
for (const pass of passes) {
const seed = frame * 4099 + pass.index * 10007 + 101;
const time = frame * (0.016 + pass.speed * 0.3);
if (pass.index === 0) {
const bandCount = Math.max(1, Math.round(3 + pass.strength * 14));
for (let band = 0; band < bandCount; band += 1) {
const bandSeed = seed + band * 977;
const y = Math.floor(grSmoothRandom(bandSeed, time * 0.82) * rows * cell);
const bandHeight = Math.max(1, Math.floor((grSmoothRandom(bandSeed + 37, time * 0.45) * 10 + 2) * pass.strength));
if (rowY < y || rowY > y + bandHeight) continue;
offset += Math.round((grSmoothRandom(bandSeed + 73, time) - 0.5) * fullWidth * 0.16 * pass.strength);
}
} else {
const slices = pass.index === 1 ? 5 : 10;
const sliceHeight = Math.max(1, Math.ceil(rows / slices));
const slice = Math.floor(row / sliceHeight);
const sliceSeed = seed + slice * 557;
const direction = grSmoothRandom(sliceSeed, time * 0.5) * 2 - 1;
const travel = 0.025 + grSmoothRandom(sliceSeed + 89, time * 0.42) * (slices === 5 ? 0.18 : 0.26);
offset += Math.round(direction * travel * fullWidth * pass.strength);
}
}
const brokenRows = (effects.brokenRows || 0) / 100;
const misregistration = (effects.misregistration || 0) / 100;
if (brokenRows > 0 || misregistration > 0) {
const tears = Math.round(1 + brokenRows * 10 + misregistration * 5);
for (let i = 0; i < tears; i += 1) {
const active = grSmoothRandom(i * 379 + 73, frame * 0.12) < brokenRows * 0.85 + misregistration * 0.35;
if (!active) continue;
const y = Math.floor(grSmoothRandom(i * 701 + 17, frame * 0.1) * rows);
const bandHeight = Math.max(1, Math.round(1 + grSmoothRandom(i * 313 + 29, frame * 0.07) * (2 + brokenRows * 18)));
if (row < y || row > y + bandHeight) continue;
offset += Math.round((grSmoothRandom(i * 997 + 41, frame * 0.16) - 0.5) * fullWidth * (brokenRows * 0.14 + misregistration * 0.08));
}
}
return offset;
}
function svgComputeRowOffsets(frame) {
for (let row = 0; row < rows; row += 1) rowOffsets[row] = svgRowGlitchOffset(row, frame);
}
function svgClearCell(node) {
if (node._grText !== "") {
node.textContent = "";
node._grText = "";
}
node._grTone = 0;
}
function grToneAt(toneLines, column, row) {
const line = toneLines[Math.max(0, Math.min(rows - 1, row))] || "";
return toneValueMap[line[Math.max(0, Math.min(columns - 1, column))] || "0"] ?? 0;
}
function grAdaptiveCellScale(toneLines, column, row) {
const effects = grEffectSettings();
if (!effects.asciiAdaptiveSize) return 1;
const blockColumn = column - (column % 2);
const blockRow = row - (row % 2);
const a = grToneAt(toneLines, blockColumn, blockRow);
const b = grToneAt(toneLines, blockColumn + 1, blockRow);
const c = grToneAt(toneLines, blockColumn, blockRow + 1);
const d = grToneAt(toneLines, blockColumn + 1, blockRow + 1);
const maxTone = Math.max(a, b, c, d);
const minTone = Math.min(a, b, c, d);
if (maxTone > 0.68) return 1;
if (maxTone - minTone > 0.24) return 1;
return Math.max(1, Math.min(1.18, 1 + ((effects.asciiLargeScale || 2) - 1) * 0.12));
}
function grAdaptiveClearedNeighborCount(styleLines, column, row) {
let clearedNeighbors = 0;
for (let y = row - 1; y <= row + 1; y += 1) {
const line = styleLines[Math.max(0, Math.min(rows - 1, y))] || "";
for (let x = column - 1; x <= column + 1; x += 1) {
if (x === column && y === row) continue;
if ((line[Math.max(0, Math.min(columns - 1, x))] || "0") === "1") clearedNeighbors += 1;
}
}
return clearedNeighbors;
}
function grRecoveredAdaptiveDenseAnchor(styleLines, column, row, style) {
return grIsDenseHighlightStyle(style) && grAdaptiveClearedNeighborCount(styleLines, column, row) >= 2;
}
function grStoredAnchorScale(styleLines, column, row, style) {
const canBeAdaptiveAnchor = style === "2" || style === "3" || grIsDenseHighlightStyle(style);
if (!canBeAdaptiveAnchor) return 1;
const clearedNeighbors = grAdaptiveClearedNeighborCount(styleLines, column, row);
if (style !== "2" && clearedNeighbors < 2) return 1;
const tuning = SIGNAL_RITUAL_ENGINE.visualRenderer.rendererTuning || {};
return Math.min(3, Math.max(1.35, (grEffectSettings().asciiLargeScale || 2) * (tuning.largeAnchorScaleMultiplier ?? 0.95)));
}
function grIsAdaptiveAnchorStyle(style) {
return style === "2" || style === "3" || style === "4" || grIsDenseHighlightStyle(style) || grIsEdgeStyle(style);
}
function grAdaptiveBlockSpec(styleLines, column, row, style) {
const effects = grEffectSettings();
if (!effects.asciiAdaptiveSize || !grIsAdaptiveAnchorStyle(style)) return null;
const maxBlockSize = Math.max(2, Math.min(4, Math.round(effects.asciiLargeScale || 2)));
for (let blockSize = maxBlockSize; blockSize >= 2; blockSize -= 1) {
const startColumn = column - Math.floor(blockSize / 2);
const startRow = row - Math.floor(blockSize / 2);
const endColumn = startColumn + blockSize;
const endRow = startRow + blockSize;
if (startColumn < 0 || startRow < 0 || endColumn > columns || endRow > rows) continue;
let helperCells = 0;
let valid = true;
for (let y = startRow; y < endRow && valid; y += 1) {
const line = styleLines[y] || "";
for (let x = startColumn; x < endColumn; x += 1) {
if (x === column && y === row) continue;
if ((line[x] || "0") !== "1") {
valid = false;
break;
}
helperCells += 1;
}
}
if (!valid || helperCells === 0) continue;
const blockCenterX = (startColumn + blockSize / 2) * cell;
const blockCenterY = (startRow + blockSize / 2) * cell;
const anchorCenterX = (column + 0.5) * cell;
const anchorCenterY = (row + 0.5) * cell;
return {
scale: blockSize,
offsetX: blockCenterX - anchorCenterX,
offsetY: blockCenterY - anchorCenterY,
recoveredDenseAnchor: grIsDenseHighlightStyle(style),
};
}
return null;
}
function grAdaptiveAnchorOffset(styleLines, column, row, style) {
const canBeAdaptiveAnchor = style === "2" || style === "3" || grIsDenseHighlightStyle(style);
if (!canBeAdaptiveAnchor) return [0, 0];
let minX = 0;
let maxX = 0;
let minY = 0;
let maxY = 0;
let hasClear = false;
for (let y = row - 1; y <= row + 1; y += 1) {
const line = styleLines[Math.max(0, Math.min(rows - 1, y))] || "";
for (let x = column - 1; x <= column + 1; x += 1) {
if ((line[Math.max(0, Math.min(columns - 1, x))] || "0") !== "1") continue;
const dx = x - column;
const dy = y - row;
minX = Math.min(minX, dx);
maxX = Math.max(maxX, dx);
minY = Math.min(minY, dy);
maxY = Math.max(maxY, dy);
hasClear = true;
}
}
if (!hasClear) return [0, 0];
return [((minX + maxX) / 2) * cell, ((minY + maxY) / 2) * cell];
}
function grShouldGlowCell(tone, style, highlightFill, column, row, frame) {
if (!RENDER_GLOW_ENABLED) return false;
const isLargeAnchor = style === "2";
const isHighlightMark = style === "3";
const isHeavyInk = style === "4" || grIsDenseHighlightStyle(style);
const highlightThreshold = (grEffectSettings().asciiHighlightThreshold || 65) / 100;
const glowToneFloor = Math.max(0.42, highlightThreshold - 0.24);
const eligible = isLargeAnchor
|| isHighlightMark
|| isHeavyInk
|| highlightFill > 0.08
|| tone >= glowToneFloor;
if (!eligible) return false;
const strong = Math