diff --git a/__init__.py b/__init__.py index 428c047..9a62b42 100644 --- a/__init__.py +++ b/__init__.py @@ -4,16 +4,16 @@ import os # Add the custom node's directory to the Python path sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) -from .canvas_node import CanvasNode +from .canvas_node import LayerForgeNode -CanvasNode.setup_routes() +LayerForgeNode.setup_routes() NODE_CLASS_MAPPINGS = { - "CanvasNode": CanvasNode + "LayerForgeNode": LayerForgeNode } NODE_DISPLAY_NAME_MAPPINGS = { - "CanvasNode": "Layer Forge (Editor, outpaintintg, Canvas Node)" + "LayerForgeNode": "Layer Forge (Editor, outpaintintg, Canvas Node)" } WEB_DIRECTORY = "./js" diff --git a/canvas_node.py b/canvas_node.py index cf15ffc..7377d65 100644 --- a/canvas_node.py +++ b/canvas_node.py @@ -90,7 +90,7 @@ class BiRefNet(torch.nn.Module): return [output] -class CanvasNode: +class LayerForgeNode: _canvas_data_storage = {} _storage_lock = threading.Lock() @@ -912,12 +912,3 @@ def convert_tensor_to_base64(tensor, alpha_mask=None, original_alpha=None): log_debug(f"Tensor shape: {tensor.shape}, dtype: {tensor.dtype}") raise -CanvasNode.setup_routes() - -NODE_CLASS_MAPPINGS = { - "CanvasNode": CanvasNode -} - -NODE_DISPLAY_NAME_MAPPINGS = { - "CanvasNode": "LayerForge" -} diff --git a/example_workflows/LayerForge_flux_fill_inpaint_example.json b/example_workflows/LayerForge_flux_fill_inpaint_example.json index 1207ba5..9324653 100644 --- a/example_workflows/LayerForge_flux_fill_inpaint_example.json +++ b/example_workflows/LayerForge_flux_fill_inpaint_example.json @@ -1,8 +1,8 @@ { "id": "d26732fd-91ea-4503-8d0d-383544823cec", "revision": 0, - "last_node_id": 49, - "last_link_id": 112, + "last_node_id": 52, + "last_link_id": 114, "nodes": [ { "id": 7, @@ -18,7 +18,7 @@ "flags": { "collapsed": true }, - "order": 6, + "order": 8, "mode": 0, "inputs": [ { @@ -62,7 +62,7 @@ 58 ], "flags": {}, - "order": 8, + "order": 10, "mode": 0, "inputs": [ { @@ -103,7 +103,7 @@ 26 ], "flags": {}, - "order": 7, + "order": 9, "mode": 0, "inputs": [ { @@ -260,7 +260,7 @@ 46 ], "flags": {}, - "order": 12, + "order": 14, "mode": 0, "inputs": [ { @@ -304,7 +304,7 @@ 58 ], "flags": {}, - "order": 10, + "order": 12, "mode": 0, "inputs": [ { @@ -344,7 +344,7 @@ 138 ], "flags": {}, - "order": 9, + "order": 11, "mode": 0, "inputs": [ { @@ -365,12 +365,12 @@ { "name": "pixels", "type": "IMAGE", - "link": 106 + "link": 113 }, { "name": "mask", "type": "MASK", - "link": 107 + "link": 114 } ], "outputs": [ @@ -421,7 +421,7 @@ 262 ], "flags": {}, - "order": 11, + "order": 13, "mode": 0, "inputs": [ { @@ -462,7 +462,7 @@ "widget_ue_connectable": {} }, "widgets_values": [ - 858769863184862, + 1006953529460557, "randomize", 20, 1, @@ -526,7 +526,7 @@ 893.8499755859375 ], "flags": {}, - "order": 13, + "order": 15, "mode": 0, "inputs": [ { @@ -550,15 +550,15 @@ "id": 23, "type": "CLIPTextEncode", "pos": [ - -835.4583129882812, - 878.8148193359375 + -905.195556640625, + 924.5140991210938 ], "size": [ 311.0955810546875, 108.43277740478516 ], "flags": {}, - "order": 5, + "order": 7, "mode": 0, "inputs": [ { @@ -591,48 +591,94 @@ "bgcolor": "#353" }, { - "id": 48, - "type": "CanvasNode", + "id": 51, + "type": "Note", "pos": [ - -514.2837524414062, - 543.1272583007812 + -916.8970947265625, + 476.72564697265625 ], "size": [ - 1862.893798828125, - 1237.79638671875 + 350.92510986328125, + 250.50831604003906 ], "flags": {}, "order": 4, "mode": 0, "inputs": [], + "outputs": [], + "properties": {}, + "widgets_values": [ + "How to Use Polygonal Selection\n- Start Drawing: Hold Shift + S and left-click to place the first point of your polygonal selection.\n\n- Add Points: Continue left-clicking to place additional points. Each click adds a new vertex to your polygon.\n\n- Close Selection: Click back on the first point (or close to it) to complete and close the polygonal selection.\n\n- Run Inpainting: Once your selection is complete, run your inpainting workflow as usual. The generated content will seamlessly integrate with the existing image." + ], + "color": "#432", + "bgcolor": "#653" + }, + { + "id": 52, + "type": "Note", + "pos": [ + -911.10205078125, + 769.1378173828125 + ], + "size": [ + 350.28143310546875, + 99.23915100097656 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [], + "outputs": [], + "properties": {}, + "widgets_values": [ + "Add a description at the bottom to tell the model what to generate." + ], + "color": "#432", + "bgcolor": "#653" + }, + { + "id": 50, + "type": "LayerForgeNode", + "pos": [ + -553.9073486328125, + 478.2644348144531 + ], + "size": [ + 1879.927490234375, + 1259.4072265625 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [], "outputs": [ { "name": "image", "type": "IMAGE", "links": [ - 106 + 113 ] }, { "name": "mask", "type": "MASK", "links": [ - 107 + 114 ] } ], "properties": { "cnr_id": "layerforge", - "ver": "22f5d028a2d4c3163014eba4896ef86810d81616", - "Node name for S&R": "CanvasNode", + "ver": "1bd261bee0c96c03cfac992ccabdea9544616a57", + "Node name for S&R": "LayerForgeNode", "widget_ue_connectable": {} }, "widgets_values": [ false, false, true, - 963, - "48", + 18, + "50", "" ] } @@ -734,22 +780,6 @@ 0, "IMAGE" ], - [ - 106, - 48, - 0, - 38, - 3, - "IMAGE" - ], - [ - 107, - 48, - 1, - 38, - 4, - "MASK" - ], [ 110, 38, @@ -773,6 +803,22 @@ 8, 0, "LATENT" + ], + [ + 113, + 50, + 0, + 38, + 3, + "IMAGE" + ], + [ + 114, + 50, + 1, + 38, + 4, + "MASK" ] ], "groups": [], @@ -781,8 +827,8 @@ "ds": { "scale": 0.6588450000000008, "offset": [ - 1318.77716124466, - -32.39290875553955 + 1117.7398801488407, + -110.40634975151642 ] }, "ue_links": [], diff --git a/example_workflows/LayerForge_flux_fill_inpaint_example.png b/example_workflows/LayerForge_flux_fill_inpaint_example.png index b9a4a05..73051d1 100644 Binary files a/example_workflows/LayerForge_flux_fill_inpaint_example.png and b/example_workflows/LayerForge_flux_fill_inpaint_example.png differ diff --git a/example_workflows/LayerForge_test_simple_workflow.jpg b/example_workflows/LayerForge_test_simple_workflow.jpg index fe0e4d0..f2ca786 100644 Binary files a/example_workflows/LayerForge_test_simple_workflow.jpg and b/example_workflows/LayerForge_test_simple_workflow.jpg differ diff --git a/example_workflows/LayerForge_test_simple_workflow.json b/example_workflows/LayerForge_test_simple_workflow.json index d108559..2910cf6 100644 --- a/example_workflows/LayerForge_test_simple_workflow.json +++ b/example_workflows/LayerForge_test_simple_workflow.json @@ -1,19 +1,137 @@ { "id": "c7ba7096-c52c-4978-8843-e87ce219b6a8", "revision": 0, - "last_node_id": 707, - "last_link_id": 1499, + "last_node_id": 710, + "last_link_id": 1505, "nodes": [ + { + "id": 708, + "type": "LayerForgeNode", + "pos": [ + -3077.55615234375, + -3358.0537109375 + ], + "size": [ + 1150, + 1000 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 1500 + ] + }, + { + "name": "mask", + "type": "MASK", + "links": [ + 1501 + ] + } + ], + "properties": { + "cnr_id": "layerforge", + "ver": "1bd261bee0c96c03cfac992ccabdea9544616a57", + "widget_ue_connectable": {}, + "Node name for S&R": "LayerForgeNode" + }, + "widgets_values": [ + false, + false, + false, + 11, + "708", + "" + ] + }, + { + "id": 709, + "type": "Reroute", + "pos": [ + -1920.4510498046875, + -3559.688232421875 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 1500 + } + ], + "outputs": [ + { + "name": "", + "type": "IMAGE", + "links": [ + 1502, + 1503 + ] + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 710, + "type": "Reroute", + "pos": [ + -1917.6273193359375, + -3524.312744140625 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 1501 + } + ], + "outputs": [ + { + "name": "", + "type": "MASK", + "links": [ + 1504, + 1505 + ] + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, { "id": 369, "type": "PreviewImage", "pos": [ - -1699.1021728515625, - -3355.60498046875 + -1914.3177490234375, + -2807.92724609375 ], "size": [ - 660.91162109375, - 400.2092590332031 + 710, + 450 ], "flags": {}, "order": 6, @@ -38,21 +156,21 @@ "id": 606, "type": "PreviewImage", "pos": [ - -1911.126708984375, - -2916.072998046875 + -1913.4202880859375, + -3428.773193359375 ], "size": [ - 551.7399291992188, - 546.8018798828125 + 700, + 510 ], "flags": {}, - "order": 1, + "order": 3, "mode": 0, "inputs": [ { "name": "images", "type": "IMAGE", - "link": 1495 + "link": 1503 } ], "outputs": [], @@ -64,92 +182,30 @@ }, "widgets_values": [] }, - { - "id": 603, - "type": "PreviewImage", - "pos": [ - -1344.1650390625, - -2915.117919921875 - ], - "size": [ - 601.4136962890625, - 527.1531372070312 - ], - "flags": {}, - "order": 4, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 1236 - } - ], - "outputs": [], - "properties": { - "cnr_id": "comfy-core", - "ver": "0.3.34", - "Node name for S&R": "PreviewImage", - "widget_ue_connectable": {} - }, - "widgets_values": [] - }, - { - "id": 680, - "type": "SaveImage", - "pos": [ - -1025.9984130859375, - -3357.975341796875 - ], - "size": [ - 278.8309020996094, - 395.84002685546875 - ], - "flags": {}, - "order": 5, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 1465 - } - ], - "outputs": [], - "properties": { - "cnr_id": "comfy-core", - "ver": "0.3.34", - "Node name for S&R": "SaveImage", - "widget_ue_connectable": {} - }, - "widgets_values": [ - "ComfyUI" - ] - }, { "id": 442, "type": "JoinImageWithAlpha", "pos": [ - -1902.5858154296875, - -3187.159423828125 + -1190.1787109375, + -3237.75732421875 ], "size": [ 176.86483764648438, 46 ], "flags": {}, - "order": 2, + "order": 5, "mode": 0, "inputs": [ { "name": "image", "type": "IMAGE", - "link": 1494 + "link": 1502 }, { "name": "alpha", "type": "MASK", - "link": 1497 + "link": 1505 } ], "outputs": [ @@ -170,25 +226,87 @@ }, "widgets_values": [] }, + { + "id": 603, + "type": "PreviewImage", + "pos": [ + -1188.5968017578125, + -3143.6875 + ], + "size": [ + 640, + 510 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 1236 + } + ], + "outputs": [], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.34", + "Node name for S&R": "PreviewImage", + "widget_ue_connectable": {} + }, + "widgets_values": [] + }, + { + "id": 680, + "type": "SaveImage", + "pos": [ + -536.2315673828125, + -3135.49755859375 + ], + "size": [ + 279.97137451171875, + 282 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 1465 + } + ], + "outputs": [], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.34", + "Node name for S&R": "SaveImage", + "widget_ue_connectable": {} + }, + "widgets_values": [ + "ComfyUI" + ] + }, { "id": 706, "type": "MaskToImage", "pos": [ - -1901.433349609375, - -3332.2021484375 + -1911.38525390625, + -2875.74658203125 ], "size": [ 184.62362670898438, 26 ], "flags": {}, - "order": 3, + "order": 4, "mode": 0, "inputs": [ { "name": "mask", "type": "MASK", - "link": 1498 + "link": 1504 } ], "outputs": [ @@ -203,57 +321,10 @@ "properties": { "cnr_id": "comfy-core", "ver": "0.3.44", - "widget_ue_connectable": {}, - "Node name for S&R": "MaskToImage" - } - }, - { - "id": 697, - "type": "CanvasNode", - "pos": [ - -2968.572998046875, - -3347.89306640625 - ], - "size": [ - 1044.9053955078125, - 980.680908203125 - ], - "flags": {}, - "order": 0, - "mode": 0, - "inputs": [], - "outputs": [ - { - "name": "image", - "type": "IMAGE", - "links": [ - 1494, - 1495 - ] - }, - { - "name": "mask", - "type": "MASK", - "links": [ - 1497, - 1498 - ] - } - ], - "properties": { - "cnr_id": "layerforge", - "ver": "22f5d028a2d4c3163014eba4896ef86810d81616", - "Node name for S&R": "CanvasNode", + "Node name for S&R": "MaskToImage", "widget_ue_connectable": {} }, - "widgets_values": [ - true, - false, - "697", - 15, - "697", - "" - ] + "widgets_values": [] } ], "links": [ @@ -273,38 +344,6 @@ 0, "IMAGE" ], - [ - 1494, - 697, - 0, - 442, - 0, - "IMAGE" - ], - [ - 1495, - 697, - 0, - 606, - 0, - "IMAGE" - ], - [ - 1497, - 697, - 1, - 442, - 1, - "MASK" - ], - [ - 1498, - 697, - 1, - 706, - 0, - "MASK" - ], [ 1499, 706, @@ -312,16 +351,64 @@ 369, 0, "IMAGE" + ], + [ + 1500, + 708, + 0, + 709, + 0, + "*" + ], + [ + 1501, + 708, + 1, + 710, + 0, + "*" + ], + [ + 1502, + 709, + 0, + 442, + 0, + "IMAGE" + ], + [ + 1503, + 709, + 0, + 606, + 0, + "IMAGE" + ], + [ + 1504, + 710, + 0, + 706, + 0, + "MASK" + ], + [ + 1505, + 710, + 0, + 442, + 1, + "MASK" ] ], "groups": [], "config": {}, "extra": { "ds": { - "scale": 0.9646149645000008, + "scale": 0.7972024500000005, "offset": [ - 3002.5649125522764, - 3543.443319064718 + 3208.3419155969927, + 3617.011371212156 ] }, "ue_links": [], diff --git a/example_workflows/LayerForge_test_simple_workflow.png b/example_workflows/LayerForge_test_simple_workflow.png index 75a133f..3d4c171 100644 Binary files a/example_workflows/LayerForge_test_simple_workflow.png and b/example_workflows/LayerForge_test_simple_workflow.png differ diff --git a/js/CanvasView.js b/js/CanvasView.js index 27a9a7a..4bd654e 100644 --- a/js/CanvasView.js +++ b/js/CanvasView.js @@ -1014,7 +1014,7 @@ async function createCanvasWidget(node, widget, app) { } const canvasNodeInstances = new Map(); app.registerExtension({ - name: "Comfy.CanvasNode", + name: "Comfy.LayerForgeNode", init() { addStylesheet(getUrl('./css/canvas_view.css')); const originalQueuePrompt = app.queuePrompt; @@ -1048,7 +1048,7 @@ app.registerExtension({ }; }, async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (nodeType.comfyClass === "CanvasNode") { + if (nodeType.comfyClass === "LayerForgeNode") { const onNodeCreated = nodeType.prototype.onNodeCreated; nodeType.prototype.onNodeCreated = function () { log.debug("CanvasNode onNodeCreated: Base widget setup."); diff --git a/src/CanvasView.ts b/src/CanvasView.ts index 51868e0..321faf8 100644 --- a/src/CanvasView.ts +++ b/src/CanvasView.ts @@ -1165,7 +1165,7 @@ $el("label.clipboard-switch.mask-switch", { const canvasNodeInstances = new Map(); app.registerExtension({ - name: "Comfy.CanvasNode", + name: "Comfy.LayerForgeNode", init() { addStylesheet(getUrl('./css/canvas_view.css')); @@ -1204,7 +1204,7 @@ app.registerExtension({ }, async beforeRegisterNodeDef(nodeType: any, nodeData: any, app: ComfyApp) { - if (nodeType.comfyClass === "CanvasNode") { + if (nodeType.comfyClass === "LayerForgeNode") { const onNodeCreated = nodeType.prototype.onNodeCreated; nodeType.prototype.onNodeCreated = function (this: ComfyNode) { log.debug("CanvasNode onNodeCreated: Base widget setup.");