refactor(js): extract shared utilities from MaskRectArea modules

- Move readLinkedNumber, getDrawColor, computeCanvasSize to common.js
- Import shared functions in mask-rect-area.js and mask-rect-area-advanced.js
- Remove duplicate implementations from both MaskRectArea modules
- Clean up unused debug parameter in syncLinkedInputsToProperties
This commit is contained in:
Dr.Lt.Data
2025-12-30 18:30:53 +09:00
parent 4c864fafb0
commit 51b7dcdffa
5 changed files with 154 additions and 232 deletions
+144
View File
@@ -135,3 +135,147 @@ function refreshPreview(event) {
}
api.addEventListener("impact-preview", refreshPreview);
// ============================================================================
// MaskRectArea Shared Utilities
// ============================================================================
/**
* Reads a numeric value from a connected link by inspecting the origin node widget.
* More reliable than getInputData() in ComfyUI's frontend execution model.
*
* @param {LGraphNode} node - LiteGraph node instance
* @param {string} inputName - Name of the input to read
* @returns {number|null} The numeric value or null if not available
*/
export function readLinkedNumber(node, inputName) {
try {
if (!node || !node.graph || !Array.isArray(node.inputs)) {
return null;
}
const inp = node.inputs.find(i => i && i.name === inputName);
if (!inp || inp.link == null) {
return null;
}
const link = node.graph.links && node.graph.links[inp.link];
if (!link) {
return null;
}
const originNode = node.graph.getNodeById
? node.graph.getNodeById(link.origin_id)
: null;
if (!originNode || !Array.isArray(originNode.widgets) || originNode.widgets.length === 0) {
return null;
}
const w = originNode.widgets.find(ww => ww && ww.name === "value")
|| originNode.widgets[0];
const v = w ? w.value : null;
return (typeof v === "number") ? v : null;
} catch (e) {
return null;
}
}
/**
* Generates a color based on percentage using HSL color space.
*
* @param {number} percent - Value between 0 and 1
* @param {string} alpha - Hex alpha value (e.g., "ff", "80")
* @returns {string} Hex color string with alpha (e.g., "#ff8040ff")
*/
export function getDrawColor(percent, alpha) {
let h = 360 * percent;
let s = 50;
let l = 50;
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}${alpha}`;
}
/**
* Computes and adjusts canvas size for preview widgets.
*
* @param {LGraphNode} node - LiteGraph node instance
* @param {[number, number]} size - [width, height] array
* @param {number} minHeight - Minimum canvas height (REQUIRED)
* @param {number} minWidth - Minimum canvas width (REQUIRED)
* @returns {void}
*/
export function computeCanvasSize(node, size, minHeight, minWidth) {
// Validate required parameters
if (typeof minHeight !== 'number' || typeof minWidth !== 'number') {
console.warn('[computeCanvasSize] minHeight and minWidth are required parameters');
return;
}
// Null safety check for widgets array
if (!node.widgets?.length || node.widgets[0].last_y == null) {
return;
}
// LiteGraph global availability check
const NODE_WIDGET_HEIGHT = (typeof LiteGraph !== 'undefined' && LiteGraph.NODE_WIDGET_HEIGHT)
? LiteGraph.NODE_WIDGET_HEIGHT
: 20;
let y = node.widgets[0].last_y + 5;
let freeSpace = size[1] - y;
// Compute the height of all non-customCanvas widgets
let widgetHeight = 0;
for (let i = 0; i < node.widgets.length; i++) {
const w = node.widgets[i];
if (w.type !== "customCanvas") {
if (w.computeSize) {
widgetHeight += w.computeSize()[1] + 4;
} else {
widgetHeight += NODE_WIDGET_HEIGHT + 5;
}
}
}
// Ensure there is enough vertical space
freeSpace -= widgetHeight;
// Clamp minimum canvas height
if (freeSpace < minHeight) {
freeSpace = minHeight;
}
// Allow both grow and shrink to fit content
const targetHeight = y + widgetHeight + freeSpace;
if (node.size[1] !== targetHeight) {
node.size[1] = targetHeight;
node.graph.setDirtyCanvas(true);
}
// Ensure the node width meets the minimum width requirement
if (node.size[0] < minWidth) {
node.size[0] = minWidth;
node.graph.setDirtyCanvas(true);
}
// Position each of the widgets
for (const w of node.widgets) {
w.y = y;
if (w.type === "customCanvas") {
y += freeSpace;
} else if (w.computeSize) {
y += w.computeSize()[1] + 4;
} else {
y += NODE_WIDGET_HEIGHT + 4;
}
}
node.canvasHeight = freeSpace;
}
+3 -108
View File
@@ -1,4 +1,5 @@
import { app } from "../../scripts/app.js";
import { readLinkedNumber, getDrawColor, computeCanvasSize } from "./common.js";
function showPreviewCanvas(node, app) {
const widget = {
@@ -15,7 +16,7 @@ function showPreviewCanvas(node, app) {
// If we are initially offscreen when created we wont have received a resize event
// Calculate it here instead
if (!node.canvasHeight) {
computeCanvasSize(node, node.size);
computeCanvasSize(node, node.size, 220, 240);
}
const visible = true;
@@ -202,7 +203,7 @@ function showPreviewCanvas(node, app) {
};
node.onResize = function (size) {
computeCanvasSize(node, size);
computeCanvasSize(node, size, 220, 240);
};
return {minWidth: 200, minHeight: 200, widget};
@@ -377,112 +378,6 @@ function CUSTOM_INT(node, inputName, val, func, config = {}) {
};
}
function getDrawColor(percent, alpha) {
let h = 360 * percent;
let s = 50;
let l = 50;
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}${alpha}`;
}
function computeCanvasSize(node, size) {
if (node.widgets[0].last_y == null) {
return;
}
const MIN_HEIGHT = 220;
const MIN_WIDTH = 240;
// FIX: use real last_y from layout, not slot count
let y = node.widgets[0].last_y + 5;
let freeSpace = size[1] - y;
// Compute the height of all non-customCanvas widgets
let widgetHeight = 0;
for (let i = 0; i < node.widgets.length; i++) {
const w = node.widgets[i];
if (w.type !== "customCanvas") {
if (w.computeSize) {
widgetHeight += w.computeSize()[1] + 4;
} else {
widgetHeight += LiteGraph.NODE_WIDGET_HEIGHT + 5;
}
}
}
// Ensure there is enough vertical space
freeSpace -= widgetHeight;
// Minimum canvas height clamp
if (freeSpace < MIN_HEIGHT) {
freeSpace = MIN_HEIGHT;
}
// Allow the node height to grow and shrink
const targetHeight = y + widgetHeight + freeSpace;
if (node.size[1] !== targetHeight) {
node.size[1] = targetHeight;
node.graph.setDirtyCanvas(true);
}
// Ensure the node width meets the minimum width requirement
if (node.size[0] < MIN_WIDTH) {
node.size[0] = MIN_WIDTH;
node.graph.setDirtyCanvas(true);
}
// Position each of the widgets
for (const w of node.widgets) {
w.y = y;
if (w.type === "customCanvas") {
y += freeSpace;
} else if (w.computeSize) {
y += w.computeSize()[1] + 4;
} else {
y += LiteGraph.NODE_WIDGET_HEIGHT + 4;
}
}
node.canvasHeight = freeSpace;
}
// Reads a numeric value from a connected link by inspecting the origin node widget.
// This is more reliable than getInputData() in ComfyUI's frontend execution model.
function readLinkedNumber(node, inputName) {
try {
if (!node || !node.graph || !Array.isArray(node.inputs)) {
return null;
}
const inp = node.inputs.find(i => i && i.name === inputName);
if (!inp || inp.link == null) {
return null;
}
const link = node.graph.links && node.graph.links[inp.link];
if (!link) {
return null;
}
const originNode = node.graph.getNodeById ? node.graph.getNodeById(link.origin_id) : null;
if (!originNode || !Array.isArray(originNode.widgets) || originNode.widgets.length === 0) {
return null;
}
// Most "Int" nodes expose the value in the first widget named "value".
const w = originNode.widgets.find(ww => ww && ww.name === "value") || originNode.widgets[0];
const v = w ? w.value : null;
return (typeof v === "number") ? v : null;
} catch (e) {
return null;
}
}
function syncLinkedInputsToPropertiesAdvanced(node) {
let changed = false;
+5 -122
View File
@@ -1,4 +1,5 @@
import { app } from "../../scripts/app.js";
import { readLinkedNumber, getDrawColor, computeCanvasSize } from "./common.js";
function showPreviewCanvas(node, app) {
const widget = {
@@ -15,7 +16,7 @@ function showPreviewCanvas(node, app) {
// If we are initially offscreen when created we wont have received a resize event
// Calculate it here instead
if (!node.canvasHeight) {
computeCanvasSize(node, node.size);
computeCanvasSize(node, node.size, 200, 200);
}
const visible = true;
@@ -65,8 +66,7 @@ function showPreviewCanvas(node, app) {
ctx.fillRect(widgetX, widgetY, backgroundWidth, backgroundHeight);
// Keep preview in sync when inputs are driven by links.
const DEBUG_PREVIEW_SYNC = false;
syncLinkedInputsToProperties(node, DEBUG_PREVIEW_SYNC);
syncLinkedInputsToProperties(node);
// Draw the conditioning zone
let [x, y, w, h] = getDrawArea(node, backgroundWidth, backgroundHeight);
@@ -196,7 +196,7 @@ function showPreviewCanvas(node, app) {
};
node.onResize = function (size) {
computeCanvasSize(node, size);
computeCanvasSize(node, size, 200, 200);
};
return {minWidth: 200, minHeight: 200, widget};
@@ -435,114 +435,7 @@ function CUSTOM_INT(node, inputName, val, func, config = {}) {
};
}
function getDrawColor(percent, alpha) {
let h = 360 * percent;
let s = 50;
let l = 50;
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}${alpha}`;
}
function computeCanvasSize(node, size) {
if (node.widgets[0].last_y == null) {
return;
}
const MIN_HEIGHT = 200;
const MIN_WIDTH = 200;
// Use last_y from LiteGraph layout (fixes excessive node height)
let y = node.widgets[0].last_y + 5;
let freeSpace = size[1] - y;
// Compute the height of all non-customCanvas widgets
let widgetHeight = 0;
for (let i = 0; i < node.widgets.length; i++) {
const w = node.widgets[i];
if (w.type !== "customCanvas") {
if (w.computeSize) {
widgetHeight += w.computeSize()[1] + 4;
} else {
widgetHeight += LiteGraph.NODE_WIDGET_HEIGHT + 5;
}
}
}
// Ensure there is enough vertical space
freeSpace -= widgetHeight;
// Clamp minimum canvas height
if (freeSpace < MIN_HEIGHT) {
freeSpace = MIN_HEIGHT;
}
// Allow both grow and shrink to fit content
const targetHeight = y + widgetHeight + freeSpace;
if (node.size[1] !== targetHeight) {
node.size[1] = targetHeight;
node.graph.setDirtyCanvas(true);
}
// Ensure the node width meets the minimum width requirement
if (node.size[0] < MIN_WIDTH) {
node.size[0] = MIN_WIDTH;
node.graph.setDirtyCanvas(true);
}
// Position each of the widgets
for (const w of node.widgets) {
w.y = y;
if (w.type === "customCanvas") {
y += freeSpace;
} else if (w.computeSize) {
y += w.computeSize()[1] + 4;
} else {
y += LiteGraph.NODE_WIDGET_HEIGHT + 4;
}
}
node.canvasHeight = freeSpace;
}
// Reads a numeric value from a connected link by inspecting the origin node widget.
// This is more reliable than getInputData() in ComfyUI's frontend execution model.
function readLinkedNumber(node, inputName) {
try {
if (!node || !node.graph || !Array.isArray(node.inputs)) {
return null;
}
const inp = node.inputs.find(i => i && i.name === inputName);
if (!inp || inp.link == null) {
return null;
}
const link = node.graph.links && node.graph.links[inp.link];
if (!link) {
return null;
}
const originNode = node.graph.getNodeById ? node.graph.getNodeById(link.origin_id) : null;
if (!originNode || !Array.isArray(originNode.widgets) || originNode.widgets.length === 0) {
return null;
}
// Most "Int" nodes expose the value in the first widget named "value".
const w = originNode.widgets.find(ww => ww && ww.name === "value") || originNode.widgets[0];
const v = w ? w.value : null;
return (typeof v === "number") ? v : null;
} catch (e) {
return null;
}
}
function syncLinkedInputsToProperties(node, debug) {
function syncLinkedInputsToProperties(node) {
let changed = false;
const vx = readLinkedNumber(node, "x");
@@ -590,15 +483,5 @@ function syncLinkedInputsToProperties(node, debug) {
}
}
if (debug && changed) {
console.log("[MaskRectArea] preview sync from links", {
x: node.properties["x"],
y: node.properties["y"],
w: node.properties["w"],
h: node.properties["h"],
blur_radius: node.properties["blur_radius"]
});
}
return changed;
}
+1 -1
View File
@@ -2,7 +2,7 @@ import configparser
import logging
import os
version_code = [8, 28]
version_code = [8, 28, 1]
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
my_path = os.path.dirname(__file__)
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-impact-pack"
description = "This node pack offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
version = "8.28"
version = "8.28.1"
license = { file = "LICENSE.txt" }
dependencies = [
"segment-anything",