commenting pass

This commit is contained in:
grimsace
2026-05-07 15:38:41 -05:00
parent f75c8b9d3c
commit 64d0d29a7c
+38
View File
@@ -1,3 +1,6 @@
// Basic Aura Highlighting
// A module for Foundry VTT that highlights tokens within specified aura ranges.
const MODULE_ID = "basic_aura_highlighting"; const MODULE_ID = "basic_aura_highlighting";
const AURAS_TAB = "basic-aura-highlighting-auras"; const AURAS_TAB = "basic-aura-highlighting-auras";
const AURAS_FLAG_PATH = `flags.${MODULE_ID}.auras`; const AURAS_FLAG_PATH = `flags.${MODULE_ID}.auras`;
@@ -7,6 +10,8 @@ const HIGHLIGHT_RING_SPACING = 5;
const HIGHLIGHT_ALPHA_THRESHOLD = 0.2; const HIGHLIGHT_ALPHA_THRESHOLD = 0.2;
const ALPHA_SAMPLE_SIZE = 48; const ALPHA_SAMPLE_SIZE = 48;
const HIGHLIGHT_FILTER_MARKER = Symbol(MODULE_ID); const HIGHLIGHT_FILTER_MARKER = Symbol(MODULE_ID);
// Shader for drawing an outline around the token's non-transparent pixels
const ALPHA_OUTLINE_FRAGMENT_SHADER = ` const ALPHA_OUTLINE_FRAGMENT_SHADER = `
varying vec2 vTextureCoord; varying vec2 vTextureCoord;
uniform sampler2D uSampler; uniform sampler2D uSampler;
@@ -48,6 +53,7 @@ const alphaShapeCache = new Map();
const REFRESH_DURING_MOVE_SETTING = "refreshDuringMove"; const REFRESH_DURING_MOVE_SETTING = "refreshDuringMove";
// Initialization: Register settings and hooks
Hooks.once("init", () => { Hooks.once("init", () => {
game.settings.register(MODULE_ID, REFRESH_DURING_MOVE_SETTING, { game.settings.register(MODULE_ID, REFRESH_DURING_MOVE_SETTING, {
name: localize("settings.refreshDuringMove.name"), name: localize("settings.refreshDuringMove.name"),
@@ -62,12 +68,16 @@ Hooks.once("init", () => {
Hooks.on("renderTokenConfig", injectAurasTab); Hooks.on("renderTokenConfig", injectAurasTab);
Hooks.on("renderPrototypeTokenConfig", injectAurasTab); Hooks.on("renderPrototypeTokenConfig", injectAurasTab);
// Data parsing hooks
Hooks.on("preUpdateToken", (_token, changes) => Hooks.on("preUpdateToken", (_token, changes) =>
parseAurasInChanges(changes, AURAS_FLAG_PATH), parseAurasInChanges(changes, AURAS_FLAG_PATH),
); );
Hooks.on("preUpdateActor", (_actor, changes) => Hooks.on("preUpdateActor", (_actor, changes) =>
parseAurasInChanges(changes, `prototypeToken.${AURAS_FLAG_PATH}`), parseAurasInChanges(changes, `prototypeToken.${AURAS_FLAG_PATH}`),
); );
// Refresh hooks
Hooks.on("canvasReady", scheduleAuraHighlightRefresh); Hooks.on("canvasReady", scheduleAuraHighlightRefresh);
Hooks.on("canvasTearDown", clearAuraHighlights); Hooks.on("canvasTearDown", clearAuraHighlights);
Hooks.on("controlToken", scheduleAuraHighlightRefresh); Hooks.on("controlToken", scheduleAuraHighlightRefresh);
@@ -89,6 +99,7 @@ Hooks.on("refreshToken", (token) => {
Hooks.on("createToken", scheduleAuraHighlightRefresh); Hooks.on("createToken", scheduleAuraHighlightRefresh);
Hooks.on("deleteToken", scheduleAuraHighlightRefresh); Hooks.on("deleteToken", scheduleAuraHighlightRefresh);
// UI Injection: Add the "Auras" tab to Token Configuration
function injectAurasTab(app, element) { function injectAurasTab(app, element) {
const root = getRootElement(element); const root = getRootElement(element);
if (!root || root.querySelector(`[data-tab="${AURAS_TAB}"]`)) return; if (!root || root.querySelector(`[data-tab="${AURAS_TAB}"]`)) return;
@@ -167,6 +178,7 @@ function createAurasTabPanel(group, auras) {
return panel; return panel;
} }
// Event handling for the Auras configuration tab
function activateAurasControls(root, group, panel) { function activateAurasControls(root, group, panel) {
const form = root.querySelector("form"); const form = root.querySelector("form");
const aurasTabButton = root.querySelector( const aurasTabButton = root.querySelector(
@@ -278,6 +290,7 @@ function renderAura(aura, index) {
`; `;
} }
// Data Management: Parsing and normalizing aura flags
function readAuras(panel) { function readAuras(panel) {
return Array.from(panel.querySelectorAll("[data-aura-id]")).map((item) => ({ return Array.from(panel.querySelectorAll("[data-aura-id]")).map((item) => ({
id: item.dataset.auraId, id: item.dataset.auraId,
@@ -351,6 +364,9 @@ function parseAuras(value) {
} }
} }
// Rendering Logic: Highlights and calculations
// Debounce highlight refreshes to avoid excessive calculations
function scheduleAuraHighlightRefresh() { function scheduleAuraHighlightRefresh() {
if (pendingHighlightRefresh !== null) return; if (pendingHighlightRefresh !== null) return;
@@ -422,6 +438,9 @@ function clearHighlightContainer(container) {
} }
} }
// Distance Calculation Logic
// Find which tokens are within range of which auras
function collectAuraHighlights(sourceTokens) { function collectAuraHighlights(sourceTokens) {
const highlights = new Map(); const highlights = new Map();
@@ -454,6 +473,7 @@ function isTokenInAura(source, target, aura) {
return measureAuraDistance(source, target, aura) <= rangePixels; return measureAuraDistance(source, target, aura) <= rangePixels;
} }
// 3D Distance (Euclidean) from center to center
function measureCenterDistance(source, target) { function measureCenterDistance(source, target) {
const dx = source.center.x - target.center.x; const dx = source.center.x - target.center.x;
const dy = source.center.y - target.center.y; const dy = source.center.y - target.center.y;
@@ -461,6 +481,7 @@ function measureCenterDistance(source, target) {
return Math.sqrt(dx * dx + dy * dy + dz * dz); return Math.sqrt(dx * dx + dy * dy + dz * dz);
} }
// Main distance entry point that handles edge vs center modes
function measureAuraDistance(source, target, aura) { function measureAuraDistance(source, target, aura) {
const sourcePoints = getMeasurementPoints(source, aura.from); const sourcePoints = getMeasurementPoints(source, aura.from);
const targetPoints = getMeasurementPoints(target, aura.to); const targetPoints = getMeasurementPoints(target, aura.to);
@@ -470,6 +491,7 @@ function measureAuraDistance(source, target, aura) {
return measurePointSetDistance(sourcePoints, targetPoints, source, target); return measurePointSetDistance(sourcePoints, targetPoints, source, target);
} }
// Fallback distance calculation using token bounds
function measureBoundsDistance(source, target, aura) { function measureBoundsDistance(source, target, aura) {
const dz = getElevationOffset(source, target); const dz = getElevationOffset(source, target);
let d2d = 0; let d2d = 0;
@@ -525,6 +547,7 @@ function getMeasurementPoints(token, mode) {
return []; return [];
} }
// Find the minimum 3D distance between two sets of points
function measurePointSetDistance(sourcePoints, targetPoints, source, target) { function measurePointSetDistance(sourcePoints, targetPoints, source, target) {
let minDistanceSquared = Infinity; let minDistanceSquared = Infinity;
for (const sourcePoint of sourcePoints) { for (const sourcePoint of sourcePoints) {
@@ -541,12 +564,16 @@ function measurePointSetDistance(sourcePoints, targetPoints, source, target) {
return Math.sqrt(minDistanceSquared + dz * dz); return Math.sqrt(minDistanceSquared + dz * dz);
} }
// Convert elevation difference to pixel distance
function getElevationOffset(source, target) { function getElevationOffset(source, target) {
const e1 = source.document?.elevation ?? 0; const e1 = source.document?.elevation ?? 0;
const e2 = target.document?.elevation ?? 0; const e2 = target.document?.elevation ?? 0;
return Math.abs(e1 - e2) * getPixelsPerSceneUnit(); return Math.abs(e1 - e2) * getPixelsPerSceneUnit();
} }
// Alpha Sampling: Pixel-perfect edge detection
// Get points along the visual edge of the token's texture
function getTokenAlphaEdgePoints(token) { function getTokenAlphaEdgePoints(token) {
const displayObject = getTokenFilterTarget(token); const displayObject = getTokenFilterTarget(token);
const normalizedPoints = getTextureAlphaEdgePoints(displayObject); const normalizedPoints = getTextureAlphaEdgePoints(displayObject);
@@ -556,6 +583,7 @@ function getTokenAlphaEdgePoints(token) {
return normalizedPoints.map(mapper); return normalizedPoints.map(mapper);
} }
// Normalize texture-space points to canvas-space coordinates
function getAlphaPointMapper(displayObject, token) { function getAlphaPointMapper(displayObject, token) {
const localBounds = displayObject?.getLocalBounds?.(); const localBounds = displayObject?.getLocalBounds?.();
const transform = displayObject?.worldTransform; const transform = displayObject?.worldTransform;
@@ -567,6 +595,7 @@ function getAlphaPointMapper(displayObject, token) {
x: localBounds.x + point.x * localBounds.width, x: localBounds.x + point.x * localBounds.width,
y: localBounds.y + point.y * localBounds.height, y: localBounds.y + point.y * localBounds.height,
}; };
// Map from local mesh space to world (screen) space, then back to canvas space
const worldPoint = transform.apply(localPoint); const worldPoint = transform.apply(localPoint);
return stageTransform.applyInverse(worldPoint); return stageTransform.applyInverse(worldPoint);
}; };
@@ -579,6 +608,7 @@ function getAlphaPointMapper(displayObject, token) {
}); });
} }
// Extract and cache points representing the non-transparent outline of a texture
function getTextureAlphaEdgePoints(displayObject) { function getTextureAlphaEdgePoints(displayObject) {
const texture = displayObject?.texture; const texture = displayObject?.texture;
const source = getTextureSource(texture); const source = getTextureSource(texture);
@@ -649,6 +679,7 @@ function getAlphaShapeCacheKey(texture, frame) {
return `${textureId}:${frame.x}:${frame.y}:${frame.width}:${frame.height}:${ALPHA_SAMPLE_SIZE}:${HIGHLIGHT_ALPHA_THRESHOLD}`; return `${textureId}:${frame.x}:${frame.y}:${frame.width}:${frame.height}:${ALPHA_SAMPLE_SIZE}:${HIGHLIGHT_ALPHA_THRESHOLD}`;
} }
// Draw the texture to a small canvas to scan its alpha channel
function sampleTextureAlphaEdges(source, frame) { function sampleTextureAlphaEdges(source, frame) {
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.width = ALPHA_SAMPLE_SIZE; canvas.width = ALPHA_SAMPLE_SIZE;
@@ -682,6 +713,7 @@ function sampleTextureAlphaEdges(source, frame) {
} }
} }
// Scan image data for pixels that transition from opaque to transparent
function getAlphaEdgeSamplePoints(imageData) { function getAlphaEdgeSamplePoints(imageData) {
const alphaThreshold = HIGHLIGHT_ALPHA_THRESHOLD * 255; const alphaThreshold = HIGHLIGHT_ALPHA_THRESHOLD * 255;
const { data, width, height } = imageData; const { data, width, height } = imageData;
@@ -735,6 +767,8 @@ function isAlphaEdgePixel(opaquePixels, width, height, x, y) {
return false; return false;
} }
// Filter Management: Drawing the highlights
function highlightToken(token, highlights) { function highlightToken(token, highlights) {
const target = getTokenFilterTarget(token); const target = getTokenFilterTarget(token);
const filters = highlights const filters = highlights
@@ -777,6 +811,7 @@ function removeHighlightFilters(target) {
target.filters = remainingFilters.length > 0 ? remainingFilters : null; target.filters = remainingFilters.length > 0 ? remainingFilters : null;
} }
// Determine which outline filter to use based on available system/module filters
function createOutlineFilter(color) { function createOutlineFilter(color) {
const colorNumber = colorToNumber(color); const colorNumber = colorToNumber(color);
const pixiOutlineFilter = globalThis.PIXI?.filters?.OutlineFilter; const pixiOutlineFilter = globalThis.PIXI?.filters?.OutlineFilter;
@@ -845,6 +880,7 @@ function createAlphaOutlineFilter(color) {
} }
} }
// Fallback graphics when filters are not supported
function drawFallbackTokenHighlights(container, token, highlights) { function drawFallbackTokenHighlights(container, token, highlights) {
highlights.forEach((aura, index) => { highlights.forEach((aura, index) => {
const bounds = token.bounds.clone().pad(index * HIGHLIGHT_RING_SPACING); const bounds = token.bounds.clone().pad(index * HIGHLIGHT_RING_SPACING);
@@ -862,6 +898,8 @@ function drawFallbackTokenHighlights(container, token, highlights) {
}); });
} }
// Utility Functions
function getPixelsPerSceneUnit() { function getPixelsPerSceneUnit() {
const activeCanvas = globalThis.canvas; const activeCanvas = globalThis.canvas;
const gridSize = const gridSize =