-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Still needs some work, especially in widget drawing, but already useful as is, so it will do for now
- Loading branch information
Showing
2 changed files
with
241 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from ..utils import tensor2pil | ||
from ..log import log | ||
import io, base64 | ||
import torch | ||
|
||
|
||
class Debug: | ||
@classmethod | ||
def INPUT_TYPES(cls): | ||
return { | ||
"required": {"anything_1": ("*")}, | ||
} | ||
|
||
RETURN_TYPES = ("STRING",) | ||
FUNCTION = "do_debug" | ||
CATEGORY = "mtb/debug" | ||
OUTPUT_NODE = True | ||
|
||
def do_debug(self, **kwargs): | ||
output = { | ||
"ui": {"b64_images": [], "text": []}, | ||
"result": ("A"), | ||
} | ||
for k, v in kwargs.items(): | ||
anything = v | ||
text = "" | ||
if isinstance(anything, torch.Tensor): | ||
log.debug(f"Tensor: {anything.shape}") | ||
|
||
# write the images to temp | ||
|
||
image = tensor2pil(anything) | ||
b64_imgs = [] | ||
for im in image: | ||
buffered = io.BytesIO() | ||
im.save(buffered, format="JPEG") | ||
b64_imgs.append( | ||
base64.b64encode(buffered.getvalue()).decode("utf-8") | ||
) | ||
|
||
output["ui"]["b64_images"] += b64_imgs | ||
log.debug(f"Input {k} contains {len(b64_imgs)} images") | ||
elif isinstance(anything, bool): | ||
log.debug(f"Input {k} contains boolean: {anything}") | ||
output["ui"]["text"] += ["True" if anything else "False"] | ||
else: | ||
text = str(anything) | ||
log.debug(f"Input {k} contains text: {text}") | ||
output["ui"]["text"] += [text] | ||
|
||
return output | ||
|
||
|
||
__nodes__ = [Debug] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
import { app } from "/scripts/app.js"; | ||
import * as shared from '/extensions/mtb/comfy_shared.js' | ||
import { log } from '/extensions/mtb/comfy_shared.js' | ||
|
||
// TODO: respect inputs order... | ||
|
||
|
||
const DEBUG_WIDGETS = { | ||
"image": (val, index) => { | ||
const w = { | ||
name: `anything_${index}`, | ||
type: "image", | ||
value: val, | ||
draw: function (ctx, | ||
node, | ||
widgetWidth, | ||
widgetY, | ||
height) { | ||
const [cw, ch] = this.computeSize(widgetWidth) | ||
shared.offsetDOMWidget(this, ctx, node, widgetWidth, widgetY, ch) | ||
}, | ||
computeSize: function (width) { | ||
const ratio = this.inputRatio || 1; | ||
if (width) { | ||
return [width, width / ratio + 4] | ||
} | ||
return [128, 128] | ||
}, | ||
onRemove: function () { | ||
if (this.inputEl) { | ||
this.inputEl.remove(); | ||
} | ||
} | ||
} | ||
|
||
w.inputEl = document.createElement("img"); | ||
w.inputEl.src = "data:image/jpeg;base64," + w.value; | ||
w.inputEl.onload = function () { | ||
w.inputRatio = w.inputEl.naturalWidth / w.inputEl.naturalHeight; | ||
} | ||
document.body.appendChild(w.inputEl); | ||
return w | ||
}, | ||
"text": (val, index) => { | ||
const w = { | ||
name: `anything_${index}`, | ||
type: "debug_text", | ||
val: val, | ||
draw: function (ctx, | ||
node, | ||
widgetWidth, | ||
widgetY, | ||
height) { | ||
// const [cw, ch] = this.computeSize(widgetWidth) | ||
shared.offsetDOMWidget(this, ctx, node, widgetWidth, widgetY, height) | ||
}, | ||
computeSize: function (width) { | ||
const value = this.inputEl.innerHTML | ||
if (!value) { | ||
return [32, 32] | ||
} | ||
if (!width) { | ||
log(`No width ${this.parent.size}`) | ||
} | ||
|
||
const fontSize = 25; // Assuming 1rem = 16px | ||
|
||
const oldFont = app.ctx.font | ||
app.ctx.font = `${fontSize}px Arial`; | ||
|
||
const words = value.split(" "); | ||
const lines = []; | ||
let currentLine = ""; | ||
for (const word of words) { | ||
const testLine = currentLine.length === 0 ? word : `${currentLine} ${word}`; | ||
|
||
const testWidth = app.ctx.measureText(testLine).width; | ||
|
||
// log(`Testing line ${testLine}, width: ${testWidth}, width: ${width}, ratio: ${testWidth / width}`) | ||
if (testWidth > width) { | ||
lines.push(currentLine); | ||
currentLine = word; | ||
} else { | ||
currentLine = testLine; | ||
} | ||
} | ||
app.ctx.font = oldFont; | ||
lines.push(currentLine); | ||
|
||
// Step 3: Calculate the widget width and height | ||
const textHeight = lines.length * (fontSize + 2); // You can adjust the line height (2 in this case) | ||
const maxLineWidth = lines.reduce((maxWidth, line) => Math.max(maxWidth, app.ctx.measureText(line).width), 0); | ||
const widgetWidth = Math.max(width || this.width || 32, maxLineWidth); | ||
const widgetHeight = textHeight + 10; // Additional padding for spacing | ||
return [widgetWidth, widgetHeight + 4] | ||
|
||
}, | ||
onRemove: function () { | ||
if (this.inputEl) { | ||
this.inputEl.remove(); | ||
} | ||
|
||
} | ||
} | ||
w.inputEl = document.createElement("p"); | ||
w.inputEl.style.textAlign = "center"; | ||
w.inputEl.style.fontSize = "1.5em"; | ||
w.inputEl.style.color = "var(--input-text)"; | ||
w.inputEl.style.fontFamily = "monospace"; | ||
w.inputEl.innerHTML = val | ||
document.body.appendChild(w.inputEl); | ||
|
||
return w | ||
} | ||
} | ||
|
||
app.registerExtension({ | ||
name: "mtb.Debug", | ||
async beforeRegisterNodeDef(nodeType, nodeData, app) { | ||
if (nodeData.name === "Debug (mtb)") { | ||
const onConnectionsChange = nodeType.prototype.onConnectionsChange; | ||
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) { | ||
const r = onConnectionsChange ? onConnectionsChange.apply(this, arguments) : undefined; | ||
// TODO: remove all widgets on disconnect once computed | ||
shared.dynamic_connection(this, index, connected, "anything_", "*") | ||
|
||
//- infer type | ||
if (link_info) { | ||
const fromNode = this.graph._nodes.find((otherNode) => otherNode.id == link_info.origin_id); | ||
const type = fromNode.outputs[link_info.origin_slot].type; | ||
this.inputs[index].type = type; | ||
// this.inputs[index].label = type.toLowerCase() | ||
} | ||
//- restore dynamic input | ||
if (!connected) { | ||
this.inputs[index].type = "*"; | ||
this.inputs[index].label = `anything_${index + 1}` | ||
} | ||
} | ||
|
||
const onExecuted = nodeType.prototype.onExecuted; | ||
nodeType.prototype.onExecuted = function (message) { | ||
log(message) | ||
onExecuted?.apply(this, arguments); | ||
log(message) | ||
if (this.widgets) { | ||
// const pos = this.widgets.findIndex((w) => w.name === "anything_1"); | ||
// if (pos !== -1) { | ||
for (let i = 0; i < this.widgets.length; i++) { | ||
this.widgets[i].onRemove?.(); | ||
} | ||
this.widgets.length = 0; | ||
|
||
} | ||
let widgetI = 1 | ||
if (message.text) { | ||
for (const txt of message.text) { | ||
const w = this.addCustomWidget(DEBUG_WIDGETS["text"](txt, widgetI)) | ||
w.parent = this; | ||
widgetI++; | ||
} | ||
} | ||
if (message.b64_images) { | ||
for (const img of message.b64_images) { | ||
const w = this.addCustomWidget(DEBUG_WIDGETS["image"](img, widgetI)) | ||
w.parent = this; | ||
widgetI++; | ||
} | ||
// this.onResize?.(this.size); | ||
// this.resize?.(this.size) | ||
this.setSize(this.computeSize()) | ||
}; | ||
|
||
this.onRemoved = function () { | ||
// When removing this node we need to remove the input from the DOM | ||
for (let y in this.widgets) { | ||
if (this.widgets[y].canvas) { | ||
this.widgets[y].canvas.remove(); | ||
} | ||
this.widgets[y].onRemove?.(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
); |