diff --git a/web/visual_area_prompt.js b/web/visual_area_prompt.js index 856d663..389cf49 100644 --- a/web/visual_area_prompt.js +++ b/web/visual_area_prompt.js @@ -1,17 +1,7 @@ import { app } from "../../scripts/app.js"; -// Type of the canvas widget -const WIDGET_CANVAS_TYPE = 'areaCondCanvas'; -// Default height for all widgets -const WIDGET_BASE_HEIGHT = LiteGraph.NODE_WIDGET_HEIGHT; -// Default size for canvas widget -const CANVAS_MIN_SIZE = 200; -// Margin of the canvas -const CANVAS_MARGIN = 6; -// Border size of the canvas -const CANVAS_BORDER = 2; -// Border color of the canvas -const CANVAS_BORDER_COLOR = "#000000"; +import { addAreaGraphWidget } from "./widgets/graph_widget.js" + // Id of the node const _ID = "VisualAreaPrompt"; // Prefix of the input to add @@ -23,118 +13,6 @@ const _AREA_DEFAULTS = [0.0, 0.0, 1.0, 1.0, 1.0]; const TypeSlot = { Input: 1, Output: 2 }; -// Function to generate an hsl color, based on a value and a maximum range -function generateHslColor(value, max, alpha) { - if (max <= 0) { - return `hsl(0, 0%, 0%, 1.0)`; - } - const hue = Math.round(((value % max) / max) * 360); - return `hsl(${hue}, 100%, 50%, ${alpha})`; -} - -function computeCanvasSize(node, size) { - if (node.widgets[3].last_y == null) { - return; - } - const yBase = WIDGET_BASE_HEIGHT * Math.max(node.inputs.length, node.outputs.length) + 5; - let remainingHeight = size[1] - yBase; - // Calculate total height of non-canvas widgets - const widgetTotalHeight = node.widgets.reduce((totalHeight, widget) => { - if (widget.type !== WIDGET_CANVAS_TYPE) { - totalHeight += (widget.computeSize ? widget.computeSize()[1] : WIDGET_BASE_HEIGHT) + 5; - } - return totalHeight; - }, 0); - // Calculate canvas height - remainingHeight = Math.max(remainingHeight - widgetTotalHeight, CANVAS_MIN_SIZE); - node.size[1] = yBase + widgetTotalHeight + remainingHeight; - node.graph.setDirtyCanvas(true); - // Position each widget within the canvas - let currentY = yBase; - node.widgets.forEach(widget => { - widget.y = currentY; - currentY += (widget.type === WIDGET_CANVAS_TYPE ? remainingHeight : (widget.computeSize ? widget.computeSize()[1] : WIDGET_BASE_HEIGHT)) + 4; - }); - node.canvasHeight = remainingHeight; -} - -function addAreaGraphWidget(node) { - const widget = { - type: WIDGET_CANVAS_TYPE, - name: "AreaConditioningCanvas", - draw: function(ctx, node, widgetWidth, widgetY) { - if (!node.canvasHeight) { - computeCanvasSize(node, node.size) - } - // Canvas variables - const transform = ctx.getTransform(); - const widgetHeight = node.canvasHeight; - const imageWidth = node.properties["image_width"] || 512; - const imageHeight = node.properties["image_height"] || 512; - const scale = Math.min((widgetWidth - CANVAS_MARGIN * 2) / imageWidth, (widgetHeight - CANVAS_MARGIN * 2) / imageHeight); - // Get values from node - const values = node.properties["area_values"]; - // Set canvas position and size in DOM - Object.assign(this.canvas.style, { - left: `${transform.e}px`, - top: `${transform.f + (widgetY * transform.d)}px`, - width: `${widgetWidth * transform.a}px`, - height: `${widgetHeight * transform.d}px`, - position: "absolute", - zIndex: 1, - pointerEvents: "none", - }); - // Calculate canvas draw dimensions - const backgroundWidth = imageWidth * scale; - const backgroundHeight = imageHeight * scale; - const xOffset = CANVAS_MARGIN + (backgroundWidth < widgetWidth ? (widgetWidth - backgroundWidth) / 2 - CANVAS_MARGIN : 0); - const yOffset = CANVAS_MARGIN + (backgroundHeight < widgetHeight ? (widgetHeight - backgroundHeight) / 2 - CANVAS_MARGIN : 0); - // Transforms the node's area values to canvas pixel dimensions - const getDrawArea = (arr) => { - if (!arr || arr.length < 4) { - return [0, 0, 0, 0]; - } - return [ - Math.min(arr[0] * backgroundWidth, backgroundWidth), - Math.min(arr[1] * backgroundHeight, backgroundHeight), - Math.max(0, Math.min(arr[2] * backgroundWidth, backgroundWidth - arr[0] * backgroundWidth)), - Math.max(0, Math.min(arr[3] * backgroundHeight, backgroundHeight - arr[1] * backgroundHeight)), - ]; - } - // Draws a rectangle on the canvas - const drawRect = (x, y, w, h, color) => { - ctx.fillStyle = color; - ctx.fillRect(x, y, w, h); - }; - // Calculate widget positions - const widgetX = xOffset; - const widgetYOffset = widgetY + yOffset; - // Draw the canvas's background and border - drawRect(widgetX - CANVAS_BORDER, widgetYOffset - CANVAS_BORDER, backgroundWidth + CANVAS_BORDER * 2, backgroundHeight + CANVAS_BORDER * 2, CANVAS_BORDER_COLOR); - drawRect(widgetX, widgetYOffset, backgroundWidth, backgroundHeight, globalThis.LiteGraph.NODE_DEFAULT_BGCOLOR); - // Draw all conditioning areas - values.forEach((v, k) => { - // Skip selected area to draw later on top - if (k === node.index) { - return; - } - const [x, y, w, h] = getDrawArea(v); - drawRect(widgetX + x, widgetYOffset + y, w, h, generateHslColor(k + 1, values.length, 0.3)); - }); - // Draw selected area - const [x, y, w, h] = getDrawArea(values[node.index]); - drawRect(widgetX + x, widgetYOffset + y, w, h, generateHslColor(node.index + 1, values.length, 0.8)); - } - } - widget.canvas = document.createElement("canvas"); - widget.canvas.className = "area-cond-canvas"; - widget.parent = node; - document.body.appendChild(widget.canvas); - node.addCustomWidget(widget); - node.onResize = size => computeCanvasSize(node, size); - return { widget }; -} - // Adds a numerical widget to the node function addNumberInput(node, inputName, startValue, updateFunc, settings = { min: 0, max: 1, step: 0.1, precision: 2 }) { node.addWidget("number", inputName, startValue, updateFunc, settings); diff --git a/web/widgets/graph_widget.js b/web/widgets/graph_widget.js new file mode 100644 index 0000000..cf41c3d --- /dev/null +++ b/web/widgets/graph_widget.js @@ -0,0 +1,124 @@ +// Type of the canvas widget +const WIDGET_CANVAS_TYPE = 'fuwuffyAreaCanvas'; +// Default height for all widgets +const WIDGET_BASE_HEIGHT = LiteGraph.NODE_WIDGET_HEIGHT; +// Default size for canvas widget +const CANVAS_MIN_SIZE = 200; +// Margin of the canvas +const CANVAS_MARGIN = 6; +// Border size of the canvas +const CANVAS_BORDER = 2; +// Border color of the canvas +const CANVAS_BORDER_COLOR = "#000000"; + +// Function to generate an hsl color, based on a value and a maximum range +function generateHslColor(value, max, alpha) { + if (max <= 0) { + return `hsl(0, 0%, 0%, 1.0)`; + } + const hue = Math.round(((value % max) / max) * 360); + return `hsl(${hue}, 100%, 50%, ${alpha})`; +} + +function computeCanvasSize(node, size) { + if (node.widgets[3].last_y == null) { + return; + } + const yBase = WIDGET_BASE_HEIGHT * Math.max(node.inputs.length, node.outputs.length) + 5; + let remainingHeight = size[1] - yBase; + // Calculate total height of non-canvas widgets + const widgetTotalHeight = node.widgets.reduce((totalHeight, widget) => { + if (widget.type !== WIDGET_CANVAS_TYPE) { + totalHeight += (widget.computeSize ? widget.computeSize()[1] : WIDGET_BASE_HEIGHT) + 5; + } + return totalHeight; + }, 0); + // Calculate canvas height + remainingHeight = Math.max(remainingHeight - widgetTotalHeight, CANVAS_MIN_SIZE); + node.size[1] = yBase + widgetTotalHeight + remainingHeight; + node.graph.setDirtyCanvas(true); + // Position each widget within the canvas + let currentY = yBase; + node.widgets.forEach(widget => { + widget.y = currentY; + currentY += (widget.type === WIDGET_CANVAS_TYPE ? remainingHeight : (widget.computeSize ? widget.computeSize()[1] : WIDGET_BASE_HEIGHT)) + 4; + }); + node.canvasHeight = remainingHeight; +} + +export function addAreaGraphWidget(node) { + const widget = { + type: WIDGET_CANVAS_TYPE, + name: "AreaConditioningCanvas", + draw: function(ctx, node, widgetWidth, widgetY) { + if (!node.canvasHeight) { + computeCanvasSize(node, node.size) + } + // Canvas variables + const transform = ctx.getTransform(); + const widgetHeight = node.canvasHeight; + const imageWidth = node.properties["image_width"] || 512; + const imageHeight = node.properties["image_height"] || 512; + const scale = Math.min((widgetWidth - CANVAS_MARGIN * 2) / imageWidth, (widgetHeight - CANVAS_MARGIN * 2) / imageHeight); + // Get values from node + const values = node.properties["area_values"]; + // Set canvas position and size in DOM + Object.assign(this.canvas.style, { + left: `${transform.e}px`, + top: `${transform.f + (widgetY * transform.d)}px`, + width: `${widgetWidth * transform.a}px`, + height: `${widgetHeight * transform.d}px`, + position: "absolute", + zIndex: 1, + pointerEvents: "none", + }); + // Calculate canvas draw dimensions + const backgroundWidth = imageWidth * scale; + const backgroundHeight = imageHeight * scale; + const xOffset = CANVAS_MARGIN + (backgroundWidth < widgetWidth ? (widgetWidth - backgroundWidth) / 2 - CANVAS_MARGIN : 0); + const yOffset = CANVAS_MARGIN + (backgroundHeight < widgetHeight ? (widgetHeight - backgroundHeight) / 2 - CANVAS_MARGIN : 0); + // Transforms the node's area values to canvas pixel dimensions + const getDrawArea = (arr) => { + if (!arr || arr.length < 4) { + return [0, 0, 0, 0]; + } + return [ + Math.min(arr[0] * backgroundWidth, backgroundWidth), + Math.min(arr[1] * backgroundHeight, backgroundHeight), + Math.max(0, Math.min(arr[2] * backgroundWidth, backgroundWidth - arr[0] * backgroundWidth)), + Math.max(0, Math.min(arr[3] * backgroundHeight, backgroundHeight - arr[1] * backgroundHeight)), + ]; + } + // Draws a rectangle on the canvas + const drawRect = (x, y, w, h, color) => { + ctx.fillStyle = color; + ctx.fillRect(x, y, w, h); + }; + // Calculate widget positions + const widgetX = xOffset; + const widgetYOffset = widgetY + yOffset; + // Draw the canvas's background and border + drawRect(widgetX - CANVAS_BORDER, widgetYOffset - CANVAS_BORDER, backgroundWidth + CANVAS_BORDER * 2, backgroundHeight + CANVAS_BORDER * 2, CANVAS_BORDER_COLOR); + drawRect(widgetX, widgetYOffset, backgroundWidth, backgroundHeight, globalThis.LiteGraph.NODE_DEFAULT_BGCOLOR); + // Draw all conditioning areas + values.forEach((v, k) => { + // Skip selected area to draw later on top + if (k === node.index) { + return; + } + const [x, y, w, h] = getDrawArea(v); + drawRect(widgetX + x, widgetYOffset + y, w, h, generateHslColor(k + 1, values.length, 0.3)); + }); + // Draw selected area + const [x, y, w, h] = getDrawArea(values[node.index]); + drawRect(widgetX + x, widgetYOffset + y, w, h, generateHslColor(node.index + 1, values.length, 0.8)); + } + } + widget.canvas = document.createElement("canvas"); + widget.canvas.className = "fuwuffy-area-canvas"; + widget.parent = node; + document.body.appendChild(widget.canvas); + node.addCustomWidget(widget); + node.onResize = size => computeCanvasSize(node, size); + return { widget }; +}