Skip to content

Commit

Permalink
feat: ✨ add a color type and widget
Browse files Browse the repository at this point in the history
The widget part is very much WIP... an input takes precedence over the
widget. i.e the widget is only used if there is no input.
  • Loading branch information
melMass committed Jun 23, 2023
1 parent 7f3070d commit 9a2e986
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 11 deletions.
21 changes: 10 additions & 11 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import sys
from .utils import here

# Add extern folder to path
extern = (here / "extern", here / "extern" / "SadTalker")

# append all extern folders to path
sys.path.extend([ x.as_posix() for x in extern])
from .utils import log

NODE_CLASS_MAPPINGS = {}
try:
from .nodes.deep_bump import DeepBump
NODE_CLASS_MAPPINGS["Deep Bump (mtb)"] = DeepBump
except Exception:
print("DeepBump nodes failed to load.")
log.error("DeepBump nodes failed to load.")
from .nodes.latent_processing import LatentLerp
from .nodes.roop import Roop
# from .nodes.geometries import LoadGeometry, GeometryInfo
try:
from .nodes.fun import QRNode
NODE_CLASS_MAPPINGS["QR Code (mtb)"] = QRNode
except Exception:
print("QRNode failed to load.")
log.error("QRNode failed to load.")

from .nodes.image_processing import (
ImageCompare,
Expand All @@ -27,11 +22,13 @@
HSVtoRGB,
RGBtoHSV,
ColorCorrect,
MaskToImage,
ColoredImage,
)
try:
from .nodes.image_processing import DeglazeImage
except Exception:
print("DeglazeImage failed to load. This is probably an opencv mismatch. This node requires opencv-python-contrib.")
log.error("DeglazeImage failed to load. This is probably an opencv mismatch. This node requires opencv-python-contrib.")

from .nodes.crop import Crop, Uncrop, BoundingBox
from .nodes.graph_utils import IntToNumber, Modulo
Expand All @@ -54,6 +51,8 @@
"Modulo (mtb)": Modulo,
"Deglaze Image (mtb)": DeglazeImage,
"Smart Step (mtb)": SmartStep,
"Mask to Image (mtb)": MaskToImage,
"Colored Image (mtb)": ColoredImage,
# "Load Geometry (mtb)": LoadGeometry,
# "Geometry Info (mtb)": GeometryInfo,
}
5 changes: 5 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
import torch
from pathlib import Path
import sys
from logging import getLogger
import logging

log = getLogger(__package__)
log.setLevel(logging.DEBUG)

# Get the absolute path of the parent directory of the current script
here = Path(__file__).parent.resolve()
Expand Down
173 changes: 173 additions & 0 deletions web/colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Define the Color Picker widget class
import parseCss from '/extensions/mtb/extern/parse-css.js'
import { app } from "/scripts/app.js";
import { ComfyWidgets } from "/scripts/widgets.js";

export function CUSTOM_INT(node, inputName, val, func, config = {}) {
return {
widget: node.addWidget(
"number",
inputName,
val,
func,
Object.assign({}, { min: 0, max: 4096, step: 640, precision: 0 }, config)
),
};
}
const dumb_call = (v,d,node) => {
console.log("dumb_call", {v,d,node});
}
function isColorBright (rgb, threshold=240) {
const brightess = getBrightness(rgb)

return brightess > threshold
}

function getBrightness (rgbObj) {
return Math.round(((parseInt(rgbObj[0]) * 299) + (parseInt(rgbObj[1]) * 587) + (parseInt(rgbObj[2]) * 114)) /1000)
}


/**
* @returns {import("/types/litegraph").IWidget} widget
*/
const custom = (key,val) => {
/** @type {import("/types/litegraph").IWidget} */
const widget = {}
// widget.y = 0;
widget.name = key;
widget.type = "COLOR";
widget.options = { default: "#ff0000" };
widget.value = val || "#ff0000";
widget.draw = function (ctx,
node,
widgetWidth,
widgetY,
height) {
const border = 3;

// draw a rect with a border and a fill color
ctx.fillStyle = "#000";
ctx.fillRect(0, widgetY, widgetWidth, height);
ctx.fillStyle = this.value;
ctx.fillRect(border, widgetY + border, widgetWidth - border * 2, height - border * 2);
// write the input name
// choose the fill based on the luminoisty of this.value color
const color = parseCss(this.value.default || this.value)
if (!color) {
return
}
ctx.fillStyle = isColorBright(color.values, 125) ? "#000" : "#fff";


ctx.font = "14px Arial";
ctx.textAlign = "center";
ctx.fillText(this.name, widgetWidth * 0.5, widgetY + 14);


widget.mouse = function (e, pos, node) {
if (e.type === "pointerdown") {
console.log({e,pos,node})
// get widgets of type type : "COLOR"
const widgets = node.widgets.filter(w => w.type === "COLOR");

for (const w of widgets) {
// color picker
const rect = [w.last_y, w.last_y + 32];
console.log({rect,pos})
if (pos[1] > rect[0] && pos[1] < rect[1]) {
console.log("color picker", node)
const picker = document.createElement("input");
picker.type = "color";
picker.value = this.value;


document.body.appendChild(picker);

picker.addEventListener("change", () => {
this.value = picker.value;
node.graph._version++;
node.setDirtyCanvas(true, true);
document.body.removeChild(picker);
});

// simulate click with screen center
const pointer_event = new MouseEvent('click', {
bubbles: false,
// cancelable: true,
pointerType: "mouse",
clientX: window.innerWidth / 2,
clientY: window.innerHeight / 2,
x: window.innerWidth / 2,
y: window.innerHeight / 2,
offsetX: window.innerWidth / 2,
offsetY: window.innerHeight / 2,
screenX: window.innerWidth / 2,
screenY: window.innerHeight / 2,


});
console.log(e)
picker.dispatchEvent(pointer_event);

}}}}
widget.computeSize = function (width) {
return [width, 32];
}
return widget;
}

app.registerExtension({
name: "mtb.ColorPicker",
init: () => {
ComfyWidgets.COLOR = function () {
return {
widget:custom("color", "#ff0000")
};
};
},
async beforeRegisterNodeDef(nodeType, nodeData, app) {

//console.log("mtb.ColorPicker", { nodeType, nodeData, app });
const rinputs = nodeData.input?.required; // object with key/value pairs, "0" is the type
// console.log(nodeData.name, { nodeType, nodeData, app });

if (!rinputs) return;


let has_color = false;
for (const [key, input] of Object.entries(rinputs)) {
if (input[0] === "COLOR") {
has_color = true;
// input[1] = { default: "#ff0000" };

}}

if (!has_color) return;

const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
this.serialize_widgets = true;
// if (rinputs[0] === "COLOR") {
// console.log(nodeData.name, { nodeType, nodeData, app });

// loop through the inputs to find the color inputs
for (const [key, input] of Object.entries(rinputs)) {
if (input[0] === "COLOR") {
this.addCustomWidget(custom(key,input[1]))
}
// }
}

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();
}
}
};
}
}
});

0 comments on commit 9a2e986

Please sign in to comment.