From 6c78fac451ea114e896f81509064800fcf3db9be Mon Sep 17 00:00:00 2001 From: AlekPet Date: Sat, 9 May 2026 23:44:06 +0300 Subject: [PATCH] update PainterNode, PoseNode + add support clone nodes + fix PainterNode not working in Nodes 2.0 (enablePointerEvents in Fabricjs not support correct) and node Painter in Nodes 2.0 used my old widget type 'painter' + update version --- pyproject.toml | 2 +- web_alekpet_nodes/lib/painternode/brushes.js | 3 + web_alekpet_nodes/painter_node.js | 150 ++++++++++++------- web_alekpet_nodes/pose_node.js | 137 ++++++++++------- 4 files changed, 190 insertions(+), 102 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 10bf2fe..2c5c4f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_custom_nodes_alekpet" description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, ChatGLM4TranslateCLIPTextEncodeNode, ChatGLM4TranslateTextNode, ChatGLM4InstructNode, ChatGLM4InstructMediaNode, ChatGLMImageGenerateNode, ChatGLMVideoGenerateNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode." -version = "1.1.6" +version = "1.1.7" license = { file = "LICENSE" } [project.urls] diff --git a/web_alekpet_nodes/lib/painternode/brushes.js b/web_alekpet_nodes/lib/painternode/brushes.js index 5e7522a..c6c0ca7 100644 --- a/web_alekpet_nodes/lib/painternode/brushes.js +++ b/web_alekpet_nodes/lib/painternode/brushes.js @@ -241,6 +241,9 @@ fabric.MyBrushPaintSymmetry = fabric.util.createClass(fabric.SymmetryBrush, { } } + if (!pressure || pressure === 0) + pressure = parseFloat(this.range_brush_pressure.value); + this.range_brush_pressure.nextElementSibling.textContent = pressure.toFixed(2); this.range_brush_pressure.style.background = rangeGradient( diff --git a/web_alekpet_nodes/painter_node.js b/web_alekpet_nodes/painter_node.js index 2904687..f4f52a2 100644 --- a/web_alekpet_nodes/painter_node.js +++ b/web_alekpet_nodes/painter_node.js @@ -34,6 +34,7 @@ import { MyPaintManager } from "./lib/painternode/manager_mypaint.js"; const DEBUG = false; const extensionName = "alekpet.PainterNode"; +const CLONE_CACHE = new Map(); // Save settings in JSON file on the extension folder [big data settings includes images] if true else localStorage let painters_settings_json = JSON.parse( @@ -183,6 +184,16 @@ class Painter { } initCanvas(canvasEl) { + // Check Node 2.0 enabled/disabled + const nodeValue2_0 = app.ui.settings.getSettingValue( + "Comfy.VueNodes.Enabled" + ); + const isNode2_0 = nodeValue2_0 != null && nodeValue2_0 ? true : false; + isNode2_0 && + console.log( + `🎨 [PainterNode] Node 2.0 is ${isNode2_0 ? "enabled! Brushes MyPaint may not support brush pressure correctly." : "disabled."}` + ); + this.canvas = new fabric.Canvas(canvasEl, { isDrawingMode: true, backgroundColor: "transparent", @@ -190,7 +201,7 @@ class Painter { this.storageCls.settings_painter_node.settings.currentCanvasSize.width, height: this.storageCls.settings_painter_node.settings.currentCanvasSize.height, - enablePointerEvents: true, + enablePointerEvents: !isNode2_0, containerClass: "canvas-container-painter", }); @@ -204,6 +215,17 @@ class Painter { } ); + // Check Node 2.0 enabled/disabled + app.ui.settings.addEventListener( + "Comfy.VueNodes.Enabled.change", + (event) => { + this.canvas.enablePointerEvents = !event.detail.value; + console.log( + `🎨 [PainterNode] Node 2.0 is ${event.detail.value ? "enabled! Brushes MyPaint may not support brush pressure correctly." : "disabled."}` + ); + } + ); + return this.canvas; } @@ -307,7 +329,7 @@ class Painter {
StrokeAlpha - +
@@ -325,7 +347,7 @@ class Painter { -
+
`, @@ -740,8 +762,8 @@ class Painter { addTypeCheck = o.hasOwnProperty("mypaintlib") ? "mypaint" : o.hasOwnProperty("cropimage") - ? "cropimage" - : type, + ? "cropimage" + : type, text_value = addTypeCheck + `_${countType}`; obEl.setAttribute("painter_object", text_value); @@ -2711,6 +2733,56 @@ class Painter { // - end } + + async loadCanvasData(data, painter_idx) { + if (painters_settings_json && data === null) { + data = await this.storageCls.getData(); + + if (!data) { + data = JSON.parse( + JSON.stringify(this.storageCls.settings_painter_node_default) + ); + } + + this.storageCls.settings_painter_node = data; + } + + Object.assign(this.node.widgets[painter_idx].value, data); + + if (data) { + if (data?.settings) { + const { + currentCanvasSize: { width, height }, + } = data.settings; + + // -- Settings piping + this.setValueElementsLS(); + + // -- Settings size + if (width && height) { + this.storageCls.settings_painter_node.settings.currentCanvasSize = { + width, + height, + }; + + this.setCanvasSize(width, height); + } + } + + // Loading canvas data + if (data?.canvas_settings) { + this.canvasLoadSettingPainter(data).then((result) => { + if (result) { + this.canvas.renderAll(); + this.uploadPaintFile(this.name); + } + }); + } + + this.node.setSize(this.node.size); + app.graph.setDirtyCanvas(true, false); + } + } } // ================= END CLASS PAINTER ================ @@ -2739,7 +2811,7 @@ function PainterWidget(node, inputName, inputData, app) { const widget = node.addDOMWidget( "painter_widget", - "painter", + "painter_node_alekpet", wrapperPainter, { setValue(v) { @@ -3075,6 +3147,18 @@ app.registerExtension({ return r; }; + const origClone = nodeType.prototype.clone; + nodeType.prototype.clone = function () { + const cloneNode = origClone?.apply(this, arguments); + if (!cloneNode) return origClone; + + CLONE_CACHE.set( + this.id, + structuredClone(this.painter.storageCls.settings_painter_node) + ); + return cloneNode; + }; + const onConfigure = nodeType.prototype.onConfigure; nodeType.prototype.onConfigure = async function (widget) { onConfigure?.apply(this, arguments); @@ -3113,61 +3197,25 @@ app.registerExtension({ console.log(`🔧 Configure PainterNode: ${this.name}`); const painter_idx = this.widgets.findIndex( - (w) => w.type === "painter" + (w) => w.type === "painter_node_alekpet" ); if (painter_idx < 0) return; let data = widget.widgets_values[painter_idx]; - if (painters_settings_json && data === null) { - data = await this.painter.storageCls.getData(); - - if (!data) { - data = JSON.parse( - JSON.stringify( - this.painter.storageCls.settings_painter_node_default - ) + if (!data) { + for (const [originalId, cachedSettings] of CLONE_CACHE.entries()) { + console.log( + `♻️ Recovering cloned data from node ${originalId} to node ${this.id}` ); + data = cachedSettings; + CLONE_CACHE.delete(originalId); + break; } - - this.painter.storageCls.settings_painter_node = data; } - Object.assign(this.widgets[painter_idx].value, data); - if (data) { - if (data?.settings) { - const { - currentCanvasSize: { width, height }, - } = data.settings; - - // -- Settings piping - this.painter.setValueElementsLS(); - - // -- Settings size - if (width && height) { - this.painter.storageCls.settings_painter_node.settings.currentCanvasSize = - { - width, - height, - }; - - this.painter.setCanvasSize(width, height); - } - } - - // Loading canvas data - if (data?.canvas_settings) { - this.painter.canvasLoadSettingPainter(data).then((result) => { - if (result) { - this.painter.canvas.renderAll(); - this.painter.uploadPaintFile(this.name); - } - }); - } - - this.setSize(arguments[0].size); - app.graph.setDirtyCanvas(true, false); + this.painter.loadCanvasData(data, painter_idx); } }); }; diff --git a/web_alekpet_nodes/pose_node.js b/web_alekpet_nodes/pose_node.js index 7e8e740..12502d6 100644 --- a/web_alekpet_nodes/pose_node.js +++ b/web_alekpet_nodes/pose_node.js @@ -83,6 +83,8 @@ const default_keypoints = [ [260, 72], ]; +const CLONE_CACHE = new Map(); + function resizeCanvas(node, sizes) { const { width, height } = sizes ?? node.openPose.settings.currentCanvasSize; @@ -635,6 +637,60 @@ class OpenPose { console.error(e); } } + + updateCanvasData(data) { + if (!data) return; + + const { + currentCanvasSize, + background, + backgroundColor, + undo_history, + redo_history, + } = data; + + if (currentCanvasSize) { + const { width, height } = currentCanvasSize; + this.setCanvasSize(width, height); + } + + this.loadPreset( + undo_history.length + ? undo_history[undo_history.length - 1] + : { keypoints: default_keypoints } + ); + + if (backgroundColor) { + this.canvas.backgroundColor = backgroundColor; + this.node.openPoseWrapper.querySelector(".pose_SetBackgroundBg").value = + backgroundColor; + } + + if (background) { + fabric.Image.fromURL(background, (img) => { + img.set({ + originX: "left", + originY: "top", + opacity: 0.5, + }); + + this.canvas.setBackgroundImage( + img, + this.canvas.renderAll.bind(this.canvas), + { + scaleX: this.canvas.width / img.width, + scaleY: this.canvas.height / img.height, + } + ); + }); + } + + this.canvas.renderAll(); + this.uploadPoseFile(this.node.name); + + this.node.setSize(this.node.size); + app.graph.setDirtyCanvas(true, false); + } } // Create OpenPose widget @@ -906,63 +962,44 @@ app.registerExtension({ return r; }; + const origClone = nodeType.prototype.clone; + nodeType.prototype.clone = function () { + const cloneNode = origClone?.apply(this, arguments); + if (!cloneNode) return origClone; + + this.openPose.settings.undo_history = [this.openPose.getJSON()]; + CLONE_CACHE.set(this.id, structuredClone(this.openPose.settings)); + return cloneNode; + }; + const onConfigure = nodeType.prototype.onConfigure; nodeType.prototype.onConfigure = async function (widget) { onConfigure?.apply(this, arguments); setTimeout(() => { - const data = widget.widgets_values[3]; + const poseWidgetIdx = this.widgets.findIndex( + (w) => w.type === "openpose" + ); + let data = + poseWidgetIdx !== -1 ? widget.widgets_values[poseWidgetIdx] : null; + + if (!data && widget?.widgets_values) { + data = widget.widgets_values[3]; + } + + if (!data) { + for (const [originalId, cachedSettings] of CLONE_CACHE.entries()) { + console.log( + `♻️ Recovering cloned data from node ${originalId} to node ${this.id}` + ); + data = cachedSettings; + CLONE_CACHE.delete(originalId); + break; + } + } if (data) { - const { - currentCanvasSize, - background, - backgroundColor, - undo_history, - redo_history, - } = data; - if (currentCanvasSize) { - const { width, height } = currentCanvasSize; - this.openPose.setCanvasSize(width, height); - } - - this.openPose.loadPreset( - undo_history.length - ? undo_history[undo_history.length - 1] - : { keypoints: default_keypoints } - ); - - if (backgroundColor) { - this.openPose.canvas.backgroundColor = backgroundColor; - this.openPoseWrapper.querySelector( - ".pose_SetBackgroundBg" - ).value = backgroundColor; - } - - if (background) { - fabric.Image.fromURL(background, (img) => { - img.set({ - originX: "left", - originY: "top", - opacity: 0.5, - }); - - this.openPose.canvas.setBackgroundImage( - img, - this.openPose.canvas.renderAll.bind(this.openPose.canvas), - { - scaleX: this.openPose.canvas.width / img.width, - scaleY: this.openPose.canvas.height / img.height, - } - ); - }); - } - - this.openPose.canvas.renderAll(); - this.openPose.uploadPoseFile(this.name); - - this.setSize(arguments[0].size); - app.graph.setDirtyCanvas(true, false); + this.openPose.updateCanvasData(data); } }, 0); };