Merge branch 'master' of https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet into new_init_config_disabled
This commit is contained in:
@@ -72,7 +72,7 @@ let PreviewImageSize = PreviewImageSizeLS
|
||||
const SUPPORTS_FORMAT = {
|
||||
image: ["jpg", "jpeg", "bmp", "png", "gif", "tiff", "avif"],
|
||||
video: ["mp4", "webm"],
|
||||
audio: ["ogg", "wav", "mp3"],
|
||||
audio: ["ogg", "wav", "mp3", "webm"],
|
||||
};
|
||||
|
||||
const loadingContent = (src) => {
|
||||
@@ -86,15 +86,21 @@ const loadingContent = (src) => {
|
||||
img.onload = (e) => res({ raw: img, type: "image", src });
|
||||
img.onerror = (err) => rej(err);
|
||||
img.src = src;
|
||||
} else if (SUPPORTS_FORMAT.video.includes(ext)) {
|
||||
}
|
||||
|
||||
if (SUPPORTS_FORMAT.video.includes(ext)) {
|
||||
const video = document.querySelector(".preview_vid");
|
||||
video.onerror = (err) => rej(err);
|
||||
|
||||
video.addEventListener("canplay", (e) => {
|
||||
res({ raw: video, type: "video", src });
|
||||
if (video.videoWidth !== 0) {
|
||||
res({ raw: video, type: "video", src });
|
||||
}
|
||||
});
|
||||
video.src = src;
|
||||
} else if (SUPPORTS_FORMAT.audio.includes(ext)) {
|
||||
}
|
||||
|
||||
if (SUPPORTS_FORMAT.audio.includes(ext)) {
|
||||
const audio = document.querySelector(".preview_audio");
|
||||
audio.onerror = (err) => rej(err);
|
||||
|
||||
@@ -139,9 +145,9 @@ app.registerExtension({
|
||||
style: {
|
||||
position: "absolute",
|
||||
maxWidth: "160px",
|
||||
right: "calc(-1% - 160px)",
|
||||
zIndex: 9999999,
|
||||
},
|
||||
parent: this.root,
|
||||
parent: document.body,
|
||||
},
|
||||
[
|
||||
$el("canvas.preview_img", {
|
||||
@@ -168,6 +174,16 @@ app.registerExtension({
|
||||
this.root.preview_content_combo = preview_content_combo;
|
||||
}
|
||||
|
||||
const closeItem = LiteGraph.ContextMenu.prototype.close;
|
||||
LiteGraph.ContextMenu.prototype.close = function () {
|
||||
closeItem?.apply(this, arguments);
|
||||
|
||||
if (this.root?.preview_content_combo) {
|
||||
this.root.preview_content_combo.remove();
|
||||
this.root.preview_content_combo = null;
|
||||
}
|
||||
};
|
||||
|
||||
LiteGraph.pointerListenerAdd(element, "enter", (e) => {
|
||||
if (element?.dataset?.value) {
|
||||
const body_rect = document.body.getBoundingClientRect();
|
||||
@@ -208,12 +224,12 @@ app.registerExtension({
|
||||
root_rect.right + previewWidth > body_rect.width
|
||||
) {
|
||||
this.root.preview_content_combo.style.left = `${
|
||||
-previewWidth - 10
|
||||
root_rect.left - previewWidth - 10
|
||||
}px`;
|
||||
this.root.preview_content_combo.style.right = "auto";
|
||||
} else {
|
||||
this.root.preview_content_combo.style.left = "auto";
|
||||
this.root.preview_content_combo.style.right = `calc(-1% - ${this.root.preview_content_combo.style.maxWidth})`;
|
||||
this.root.preview_content_combo.style.left = `${
|
||||
root_rect.left + root_rect.width + 10
|
||||
}px`;
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
@@ -268,6 +284,16 @@ app.registerExtension({
|
||||
});
|
||||
};
|
||||
|
||||
const originalCloseAllContextMenus = LiteGraph.closeAllContextMenus;
|
||||
LiteGraph.closeAllContextMenus = function () {
|
||||
originalCloseAllContextMenus?.apply(this, arguments);
|
||||
|
||||
const previewCombo = document.querySelector(".preview_content_combo");
|
||||
if (previewCombo) {
|
||||
previewCombo.remove();
|
||||
}
|
||||
};
|
||||
|
||||
// PreviewImage settings ui
|
||||
app.ui.settings.addSetting({
|
||||
id: `${idExt}.PreviewImage`,
|
||||
|
||||
+15
-5
@@ -118,6 +118,13 @@ function animateTransitionProps(
|
||||
preStyles = { display: "block" }
|
||||
) {
|
||||
Object.assign(el.style, preStyles);
|
||||
|
||||
el.style.transition =
|
||||
!el.style.transition ||
|
||||
!window.getComputedStyle(el).getPropertyValue("transition")
|
||||
? "all .8s"
|
||||
: el.style.transition;
|
||||
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
Object.assign(el.style, props);
|
||||
@@ -145,9 +152,12 @@ function animateClick(target, params = {}) {
|
||||
const hide = +target.style.opacity === 0;
|
||||
return animateTransitionProps(target, {
|
||||
opacity: hide ? opacityVal : 0,
|
||||
})
|
||||
.then(() => showHide({ elements: [target], hide: !hide }))
|
||||
.then(() => callback());
|
||||
}).then((el) => {
|
||||
const isHide = hide || el.style.display === "none";
|
||||
showHide({ elements: [target], hide: !hide });
|
||||
callback();
|
||||
return isHide;
|
||||
});
|
||||
}
|
||||
|
||||
function showHide({ elements = [], hide = null, displayProp = "block" } = {}) {
|
||||
@@ -442,8 +452,8 @@ function createWindowModal({
|
||||
|
||||
switch (typeof text) {
|
||||
case "string":
|
||||
if (/^\<*.\/?\>$/.test(text)) {
|
||||
parent.innerHTML(text);
|
||||
if (/^\<.*\/?\>$/.test(text)) {
|
||||
parent.innerHTML = text;
|
||||
} else {
|
||||
parent.textContent = text;
|
||||
}
|
||||
|
||||
@@ -113,3 +113,24 @@ npm run brushes
|
||||
- Update image (not update the images of the node with piping in image (**Note:** you must enable this option so that the mask does not change))
|
||||
|
||||

|
||||
|
||||
# User custom fonts
|
||||
|
||||
> To add custom fonts, they must be written in the css file: `PainterNode\css\painter_node_user_fonts.css`
|
||||
|
||||
### Two variants:
|
||||
|
||||
**Variant 1**: Path to fonts, fonts puts in the `PainterNode\fonts`, adn add to the code in the css.
|
||||
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "Pricedown";
|
||||
src: url("./../../fonts/painternode/pricedown.otf") format("opentype");
|
||||
}
|
||||
```
|
||||
|
||||
**Variant 2**: Add url to font in the `painter_node_user_fonts.css`, example used link [google fonts.](https://fonts.google.com/)
|
||||
|
||||
```css
|
||||
@import url("https://fonts.googleapis.com/css2?family=Jacquard+12&display=swap");
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/* Default styles */
|
||||
@import url("https://fonts.googleapis.com/css2?family=Jacquard+12&display=swap");
|
||||
|
||||
/* User fonts */
|
||||
@import url("./painter_node_user_fonts.css");
|
||||
@@ -0,0 +1,27 @@
|
||||
/* Example add font file for different formats, and supports old browsers: */
|
||||
/*
|
||||
@font-face {
|
||||
font-family: 'MyNameFont';
|
||||
src: url('webfont.eot');
|
||||
src: url('webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('webfont.woff') format('woff'),
|
||||
url('webfont.ttf') format('truetype'),
|
||||
url("webfont.otf") format("opentype");
|
||||
url('webfont.svg#svgFontName') format('svg');
|
||||
}
|
||||
*/
|
||||
|
||||
/* User fonts files */
|
||||
|
||||
/* Pricedown font */
|
||||
@font-face {
|
||||
font-family: "Pricedown";
|
||||
src: url("./../../fonts/painternode/pricedown.otf") format("opentype");
|
||||
}
|
||||
|
||||
/* Harry_font__YOURSSINCERELYLARRY_ font */
|
||||
@font-face {
|
||||
font-family: "Harry_font__YOURSSINCERELYLARRY_";
|
||||
src: url("./../../fonts/painternode/HarryFontYourssincerelylarry.ttf")
|
||||
format("truetype");
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
+129
-41
@@ -22,6 +22,7 @@ import {
|
||||
isEmptyObject,
|
||||
THEMES_MODAL_WINDOW,
|
||||
} from "./utils.js";
|
||||
import "./lib/painternode/fontfaceobserver.js";
|
||||
import { MyPaintManager } from "./lib/painternode/manager_mypaint.js";
|
||||
|
||||
// ================= FUNCTIONS ================
|
||||
@@ -80,6 +81,30 @@ function resizeCanvas(node, sizes) {
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const FONTS = {};
|
||||
const STATES = {
|
||||
fontsLoaded: false,
|
||||
};
|
||||
|
||||
async function getLoadedFonts() {
|
||||
const getListFonts = async () => {
|
||||
const fontFaces = await document.fonts.ready;
|
||||
const loadedFonts = [];
|
||||
|
||||
document.fonts.forEach((font) => {
|
||||
loadedFonts.push(font.family);
|
||||
});
|
||||
|
||||
return [...new Set(loadedFonts)];
|
||||
};
|
||||
|
||||
await getListFonts().then((fonts) => {
|
||||
fonts.forEach((font) => {
|
||||
FONTS[font] = { type: "custom" };
|
||||
});
|
||||
});
|
||||
}
|
||||
// ================= END FUNCTIONS ================
|
||||
|
||||
// ================= CLASS PAINTER ================
|
||||
@@ -110,13 +135,14 @@ class Painter {
|
||||
// this.redo_history = this.node.LS_Cls.LS_Painters.redo_history || [];
|
||||
|
||||
this.fonts = {
|
||||
Arial: "arial",
|
||||
"Times New Roman": "Times New Roman",
|
||||
Verdana: "verdana",
|
||||
Georgia: "georgia",
|
||||
Courier: "courier",
|
||||
"Comic Sans MS": "comic sans ms",
|
||||
Impact: "impact",
|
||||
Arial: { type: "default" },
|
||||
"Times New Roman": { type: "default" },
|
||||
Verdana: { type: "default" },
|
||||
Georgia: { type: "default" },
|
||||
Courier: { type: "default" },
|
||||
"Comic Sans MS": { type: "default" },
|
||||
Impact: { type: "default" },
|
||||
...FONTS,
|
||||
};
|
||||
|
||||
this.bringFrontSelected = true;
|
||||
@@ -372,6 +398,7 @@ class Painter {
|
||||
});
|
||||
|
||||
this.painter_bg_setting.appendChild(this.bgImageFile);
|
||||
|
||||
this.changePropertyBrush();
|
||||
this.createBrushesToolbar();
|
||||
this.bindEvents();
|
||||
@@ -980,29 +1007,71 @@ class Painter {
|
||||
});
|
||||
const separator = makeElement("div", { class: ["separator"] });
|
||||
const selectFontFamily = makeElement("select", {
|
||||
dataset: { prop: "prop_fontFamily" },
|
||||
class: ["font_family_select"],
|
||||
});
|
||||
|
||||
for (let f in this.fonts) {
|
||||
for (let font in this.fonts) {
|
||||
const option = makeElement("option");
|
||||
if (f === "Arial") option.setAttribute("selected", true);
|
||||
option.value = this.fonts[f];
|
||||
option.textContent = f;
|
||||
if (font === "Arial") option.setAttribute("selected", true);
|
||||
option.value = font;
|
||||
option.textContent = font;
|
||||
selectFontFamily.appendChild(option);
|
||||
}
|
||||
|
||||
// Select front event
|
||||
selectFontFamily.onchange = (e) => {
|
||||
if (this.getActiveStyle("fontFamily") != selectFontFamily.value)
|
||||
this.setActiveStyle("fontFamily", selectFontFamily.value);
|
||||
if (this.getActiveStyle("fontFamily") != selectFontFamily.value) {
|
||||
if (this.fonts[selectFontFamily.value].type == "default") {
|
||||
this.setActiveStyle("fontFamily", selectFontFamily.value);
|
||||
return;
|
||||
}
|
||||
|
||||
const font = new FontFaceObserver(selectFontFamily.value);
|
||||
font.load().then(
|
||||
() => {
|
||||
// console.log("Font is available");
|
||||
this.setActiveStyle("fontFamily", selectFontFamily.value);
|
||||
},
|
||||
() => {
|
||||
// console.log("Font not is available");
|
||||
}
|
||||
);
|
||||
|
||||
this.uploadPaintFile(this.node.name);
|
||||
}
|
||||
};
|
||||
|
||||
const infoFontsButton = makeElement("button", {
|
||||
style: {},
|
||||
textContent: "?",
|
||||
title: "Info for fonts",
|
||||
onclick: (e) =>
|
||||
createWindowModal({
|
||||
textTitle: "NOTE",
|
||||
textBody:
|
||||
"<b>If fonts not loaded in the canvas, refresh page browser!😅</b>",
|
||||
...THEMES_MODAL_WINDOW.warning,
|
||||
options: {
|
||||
auto: { autohide: true, autoshow: true, autoremove: true },
|
||||
parent: this.canvas.wrapperEl,
|
||||
overlay: {
|
||||
overlay_enabled: true,
|
||||
overlayStyles: {
|
||||
position: "absolute",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
property_textbox.append(
|
||||
buttonItalic,
|
||||
buttonBold,
|
||||
buttonUnderline,
|
||||
separator,
|
||||
selectFontFamily
|
||||
selectFontFamily,
|
||||
infoFontsButton
|
||||
);
|
||||
this.painter_drawning_box_property.append(property_textbox);
|
||||
}
|
||||
@@ -1577,12 +1646,20 @@ class Painter {
|
||||
if (!targets || targets.length == 0) return;
|
||||
|
||||
// Selected tools
|
||||
const setProps = (style, check) => {
|
||||
const setProps = (style, value) => {
|
||||
const propEl = this.painter_drawning_box_property.querySelector(
|
||||
`#prop_${style}`
|
||||
`[data-prop=prop_${style}]`
|
||||
);
|
||||
|
||||
if (propEl) propEl.classList[check ? "remove" : "add"]("active");
|
||||
if (propEl) {
|
||||
switch (propEl.dataset.prop) {
|
||||
case "prop_fontFamily": {
|
||||
propEl.value = value;
|
||||
}
|
||||
default:
|
||||
propEl.classList[value ? "remove" : "add"]("active");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
targets.forEach((target) => {
|
||||
@@ -1602,6 +1679,8 @@ class Painter {
|
||||
"underline",
|
||||
Boolean(this.getActiveStyle("underline", target)) == false
|
||||
);
|
||||
|
||||
setProps("fontFamily", this.getActiveStyle("fontFamily", target));
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -1991,6 +2070,29 @@ class Painter {
|
||||
);
|
||||
}
|
||||
|
||||
getImageByName(image_name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = (e) => resolve(new Error("Image not load!"));
|
||||
|
||||
let name = image_name;
|
||||
let folder_separator = name.lastIndexOf("/");
|
||||
let subfolder = "";
|
||||
|
||||
if (folder_separator > -1) {
|
||||
subfolder = name.substring(0, folder_separator);
|
||||
name = name.substring(folder_separator + 1);
|
||||
}
|
||||
|
||||
img.src = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
name
|
||||
)}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async addImageToCanvas(image, options = {}) {
|
||||
async function uploadFile(file) {
|
||||
try {
|
||||
@@ -2007,28 +2109,7 @@ class Painter {
|
||||
let path = data.name;
|
||||
if (data.subfolder) path = data.subfolder + "/" + path;
|
||||
|
||||
const img = await new Promise((resolve, reject) => {
|
||||
let img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = (e) => resolve(new Error("Image not load!"));
|
||||
|
||||
let name = path;
|
||||
let folder_separator = name.lastIndexOf("/");
|
||||
let subfolder = "";
|
||||
|
||||
if (folder_separator > -1) {
|
||||
subfolder = name.substring(0, folder_separator);
|
||||
name = name.substring(folder_separator + 1);
|
||||
}
|
||||
|
||||
img.src = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
name
|
||||
)}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||
);
|
||||
});
|
||||
|
||||
return img;
|
||||
return await getImageByName(path);
|
||||
} else {
|
||||
console.log(`${resp.status} - ${resp.statusText}`);
|
||||
return resp.statusText;
|
||||
@@ -2106,7 +2187,7 @@ class Painter {
|
||||
await new Promise((res) => {
|
||||
const uploadFile = async (blobFile) => {
|
||||
try {
|
||||
const resp = await fetch("/upload/image", {
|
||||
const resp = await api.fetchApi("/upload/image", {
|
||||
method: "POST",
|
||||
body: blobFile,
|
||||
});
|
||||
@@ -2528,6 +2609,7 @@ app.registerExtension({
|
||||
async init(app) {
|
||||
// Add styles
|
||||
addStylesheet("css/painternode/painter_node_styles.css", import.meta.url);
|
||||
addStylesheet("css/painternode/painter_node_fonts.css", import.meta.url);
|
||||
|
||||
// Add settings params painter node
|
||||
app.ui.settings.addSetting({
|
||||
@@ -2616,6 +2698,13 @@ app.registerExtension({
|
||||
},
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === "PainterNode") {
|
||||
if (!STATES.fontsLoaded) {
|
||||
await getLoadedFonts().then(() => {
|
||||
STATES.fontsLoaded = true;
|
||||
console.log("PainterNode: Loading fonts completed");
|
||||
});
|
||||
}
|
||||
|
||||
// Create node
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
@@ -2689,7 +2778,6 @@ app.registerExtension({
|
||||
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
||||
nodeType.prototype.getExtraMenuOptions = async function (_, options) {
|
||||
getExtraMenuOptions?.apply(this, arguments);
|
||||
await this.getTitle();
|
||||
|
||||
const past_index = options.findIndex(
|
||||
(m) => m?.content === "Paste (Clipspace)"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/* Font Face Observer v2.3.0 - © Bram Stein. License: BSD-3-Clause */(function(){function p(a,c){document.addEventListener?a.addEventListener("scroll",c,!1):a.attachEvent("scroll",c)}function u(a){document.body?a():document.addEventListener?document.addEventListener("DOMContentLoaded",function b(){document.removeEventListener("DOMContentLoaded",b);a()}):document.attachEvent("onreadystatechange",function g(){if("interactive"==document.readyState||"complete"==document.readyState)document.detachEvent("onreadystatechange",g),a()})};function w(a){this.g=document.createElement("div");this.g.setAttribute("aria-hidden","true");this.g.appendChild(document.createTextNode(a));this.h=document.createElement("span");this.i=document.createElement("span");this.m=document.createElement("span");this.j=document.createElement("span");this.l=-1;this.h.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.i.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";
|
||||
this.j.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.m.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;max-width:none;";this.h.appendChild(this.m);this.i.appendChild(this.j);this.g.appendChild(this.h);this.g.appendChild(this.i)}
|
||||
function x(a,c){a.g.style.cssText="max-width:none;min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;white-space:nowrap;font-synthesis:none;font:"+c+";"}function B(a){var c=a.g.offsetWidth,b=c+100;a.j.style.width=b+"px";a.i.scrollLeft=b;a.h.scrollLeft=a.h.scrollWidth+100;return a.l!==c?(a.l=c,!0):!1}function C(a,c){function b(){var e=g;B(e)&&null!==e.g.parentNode&&c(e.l)}var g=a;p(a.h,b);p(a.i,b);B(a)};function D(a,c,b){c=c||{};b=b||window;this.family=a;this.style=c.style||"normal";this.weight=c.weight||"normal";this.stretch=c.stretch||"normal";this.context=b}var E=null,F=null,G=null,H=null;function I(a){null===F&&(M(a)&&/Apple/.test(window.navigator.vendor)?(a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))(?:\.([0-9]+))/.exec(window.navigator.userAgent),F=!!a&&603>parseInt(a[1],10)):F=!1);return F}function M(a){null===H&&(H=!!a.document.fonts);return H}
|
||||
function N(a,c){var b=a.style,g=a.weight;if(null===G){var e=document.createElement("div");try{e.style.font="condensed 100px sans-serif"}catch(q){}G=""!==e.style.font}return[b,g,G?a.stretch:"","100px",c].join(" ")}
|
||||
D.prototype.load=function(a,c){var b=this,g=a||"BESbswy",e=0,q=c||3E3,J=(new Date).getTime();return new Promise(function(K,L){if(M(b.context)&&!I(b.context)){var O=new Promise(function(r,t){function h(){(new Date).getTime()-J>=q?t(Error(""+q+"ms timeout exceeded")):b.context.document.fonts.load(N(b,'"'+b.family+'"'),g).then(function(n){1<=n.length?r():setTimeout(h,25)},t)}h()}),P=new Promise(function(r,t){e=setTimeout(function(){t(Error(""+q+"ms timeout exceeded"))},q)});Promise.race([P,O]).then(function(){clearTimeout(e);
|
||||
K(b)},L)}else u(function(){function r(){var d;if(d=-1!=k&&-1!=l||-1!=k&&-1!=m||-1!=l&&-1!=m)(d=k!=l&&k!=m&&l!=m)||(null===E&&(d=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent),E=!!d&&(536>parseInt(d[1],10)||536===parseInt(d[1],10)&&11>=parseInt(d[2],10))),d=E&&(k==y&&l==y&&m==y||k==z&&l==z&&m==z||k==A&&l==A&&m==A)),d=!d;d&&(null!==f.parentNode&&f.parentNode.removeChild(f),clearTimeout(e),K(b))}function t(){if((new Date).getTime()-J>=q)null!==f.parentNode&&f.parentNode.removeChild(f),
|
||||
L(Error(""+q+"ms timeout exceeded"));else{var d=b.context.document.hidden;if(!0===d||void 0===d)k=h.g.offsetWidth,l=n.g.offsetWidth,m=v.g.offsetWidth,r();e=setTimeout(t,50)}}var h=new w(g),n=new w(g),v=new w(g),k=-1,l=-1,m=-1,y=-1,z=-1,A=-1,f=document.createElement("div");f.dir="ltr";x(h,N(b,"sans-serif"));x(n,N(b,"serif"));x(v,N(b,"monospace"));f.appendChild(h.g);f.appendChild(n.g);f.appendChild(v.g);b.context.document.body.appendChild(f);y=h.g.offsetWidth;z=n.g.offsetWidth;A=v.g.offsetWidth;t();
|
||||
C(h,function(d){k=d;r()});x(h,N(b,'"'+b.family+'",sans-serif'));C(n,function(d){l=d;r()});x(n,N(b,'"'+b.family+'",serif'));C(v,function(d){m=d;r()});x(v,N(b,'"'+b.family+'",monospace'))})})};"object"===typeof module?module.exports=D:(window.FontFaceObserver=D,window.FontFaceObserver.prototype.load=D.prototype.load);}());
|
||||
@@ -1,3 +1,11 @@
|
||||
.canvas_container_openpose {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.canvas_container_openpose canvas {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.pose_panelButtons {
|
||||
position: absolute;
|
||||
padding: 4px;
|
||||
@@ -6,6 +14,9 @@
|
||||
flex-direction: column;
|
||||
width: fit-content;
|
||||
}
|
||||
.pose_panelButtons button {
|
||||
width: 32px;
|
||||
}
|
||||
.pose_panelButtons .clear_history {
|
||||
border-color: var(--error-text);
|
||||
color: var(--error-text) !important;
|
||||
|
||||
+255
-285
@@ -2,10 +2,10 @@
|
||||
* Title: Set Poses in ComflyUI from ControlNet
|
||||
* Author: AlekPet
|
||||
* Description: I rewrote the main.js file as a class, from fkunn1326's openpose-editor (https://github.com/fkunn1326/openpose-editor/blob/master/javascript/main.js)
|
||||
* Version: 2024.10.13
|
||||
* Github: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet
|
||||
*/
|
||||
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { $el } from "../../scripts/ui.js";
|
||||
import { addStylesheet } from "../../scripts/utils.js";
|
||||
@@ -78,19 +78,16 @@ const default_keypoints = [
|
||||
[260, 72],
|
||||
];
|
||||
|
||||
window.LS_Poses = {};
|
||||
|
||||
function LS_Save() {
|
||||
localStorage.setItem("ComfyUI_Poses", JSON.stringify(LS_Poses));
|
||||
}
|
||||
|
||||
function resizeCanvas(node, sizes) {
|
||||
const { width, height } = sizes ?? node.openPose.currentCanvasSize;
|
||||
const { width, height } = sizes ?? node.openPose.settings.currentCanvasSize;
|
||||
|
||||
node.openPose.canvas.setDimensions({
|
||||
width: width,
|
||||
height: height,
|
||||
});
|
||||
node.openPose.canvas.setDimensions(
|
||||
{
|
||||
width: width,
|
||||
height: height,
|
||||
},
|
||||
{ cssOnly: false, backstoreOnly: true }
|
||||
);
|
||||
|
||||
node.openPose.canvas.getElement().width = width;
|
||||
node.openPose.canvas.getElement().height = height;
|
||||
@@ -101,18 +98,26 @@ function resizeCanvas(node, sizes) {
|
||||
|
||||
class OpenPose {
|
||||
constructor(node, canvasElement) {
|
||||
this.lockMode = false;
|
||||
this.visibleEyes = true;
|
||||
this.flipped = false;
|
||||
this.node = node;
|
||||
this.undo_history = LS_Poses[node.name].undo_history || [];
|
||||
this.redo_history = LS_Poses[node.name].redo_history || [];
|
||||
this.history_change = false;
|
||||
this.currentCanvasSize = { width: 512, height: 512 };
|
||||
|
||||
this.maxSizeNodeW = 1024;
|
||||
this.maxSizehNodeH = 200;
|
||||
|
||||
this.settings = {
|
||||
undo_history: [],
|
||||
redo_history: [],
|
||||
currentCanvasSize: { width: 512, height: 512 },
|
||||
background: null,
|
||||
};
|
||||
|
||||
this.canvas = this.initCanvas(canvasElement);
|
||||
this.image = node.widgets.find((w) => w.name === "image");
|
||||
}
|
||||
getSettings() {
|
||||
return JSON.parse(JSON.stringify(this.settings));
|
||||
}
|
||||
|
||||
setCanvasSize(new_width, new_height, resetPose = false) {
|
||||
resizeCanvas(this.node, {
|
||||
@@ -120,20 +125,13 @@ class OpenPose {
|
||||
height: new_height,
|
||||
});
|
||||
|
||||
this.currentCanvasSize = { width: new_width, height: new_height };
|
||||
|
||||
// Check isset local storage key currentCanvasSize
|
||||
if (!LS_Poses[this.node.name].hasOwnProperty("currentCanvasSize"))
|
||||
LS_Poses[this.node.name]["currentCanvasSize"] = {};
|
||||
|
||||
LS_Poses[this.node.name]["currentCanvasSize"] = this.currentCanvasSize;
|
||||
this.settings.currentCanvasSize = { width: new_width, height: new_height };
|
||||
|
||||
this.node.title = `${this.node.type} - ${new_width}x${new_height}`;
|
||||
resetPose && this.resetCanvas();
|
||||
this.canvas.renderAll();
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
this.node.onResize();
|
||||
LS_Save();
|
||||
}
|
||||
|
||||
setPose(keypoints) {
|
||||
@@ -248,6 +246,9 @@ class OpenPose {
|
||||
this.canvas = new fabric.Canvas(this.canvas, {
|
||||
backgroundColor: "#000",
|
||||
preserveObjectStacking: true,
|
||||
containerClass: "canvas_container_openpose",
|
||||
width: this.settings.currentCanvasSize.width,
|
||||
height: this.settings.currentCanvasSize.height,
|
||||
});
|
||||
|
||||
const updateLines = (target) => {
|
||||
@@ -354,68 +355,68 @@ class OpenPose {
|
||||
});
|
||||
|
||||
this.canvas.on("object:modified", (e) => {
|
||||
if (
|
||||
this.lockMode ||
|
||||
this.canvas.getActiveObject().type == "activeSelection"
|
||||
) {
|
||||
if (this.canvas.getActiveObject().type == "activeSelection") {
|
||||
this.uploadPoseFile(this.node.name);
|
||||
return;
|
||||
}
|
||||
|
||||
this.undo_history.push(this.getJSON());
|
||||
this.redo_history.length = 0;
|
||||
this.history_change = true;
|
||||
this.settings.undo_history.push(this.getJSON());
|
||||
this.settings.redo_history.length = 0;
|
||||
this.uploadPoseFile(this.node.name);
|
||||
});
|
||||
|
||||
if (!LS_Poses[this.node.name].undo_history.length) {
|
||||
if (!this.settings.undo_history.length) {
|
||||
this.setPose(default_keypoints);
|
||||
this.undo_history.push(this.getJSON());
|
||||
this.settings.undo_history.push(this.getJSON());
|
||||
}
|
||||
return this.canvas;
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.undo_history.length > 0) {
|
||||
this.lockMode = true;
|
||||
if (this.undo_history.length > 1)
|
||||
this.redo_history.push(this.undo_history.pop());
|
||||
if (this.settings.undo_history.length > 0) {
|
||||
if (this.settings.undo_history.length > 1)
|
||||
this.settings.redo_history.push(this.settings.undo_history.pop());
|
||||
|
||||
const content = this.undo_history[this.undo_history.length - 1];
|
||||
const content =
|
||||
this.settings.undo_history[this.settings.undo_history.length - 1];
|
||||
this.loadPreset(content);
|
||||
this.canvas.renderAll();
|
||||
this.lockMode = false;
|
||||
this.history_change = true;
|
||||
|
||||
this.uploadPoseFile(this.node.name);
|
||||
}
|
||||
}
|
||||
|
||||
redo() {
|
||||
if (this.redo_history.length > 0) {
|
||||
this.lockMode = true;
|
||||
const content = this.redo_history.pop();
|
||||
this.undo_history.push(content);
|
||||
if (this.settings.redo_history.length > 0) {
|
||||
const content = this.settings.redo_history.pop();
|
||||
this.settings.undo_history.push(content);
|
||||
this.loadPreset(content);
|
||||
|
||||
this.canvas.renderAll();
|
||||
this.lockMode = false;
|
||||
this.history_change = true;
|
||||
|
||||
this.uploadPoseFile(this.node.name);
|
||||
}
|
||||
}
|
||||
|
||||
resetCanvas(reset_size = false) {
|
||||
if (reset_size && confirm("Reset canvas size?")) {
|
||||
this.setCanvasSize(512, 512);
|
||||
if (reset_size) {
|
||||
if (confirm("Clear undo/redo?")) {
|
||||
this.settings.undo_history = [];
|
||||
this.settings.redo_history = [];
|
||||
}
|
||||
|
||||
if (confirm("Reset canvas size?")) {
|
||||
this.setCanvasSize(512, 512);
|
||||
|
||||
const backgroundImg = this.canvas.backgroundImage;
|
||||
this.canvas.clear();
|
||||
this.canvas.backgroundColor = "#000";
|
||||
this.addPose();
|
||||
|
||||
this.setBackground(backgroundImg);
|
||||
this.uploadPoseFile(this.node.name);
|
||||
}
|
||||
}
|
||||
|
||||
const backgroundImg = this.canvas.backgroundImage;
|
||||
this.canvas.clear();
|
||||
this.canvas.backgroundColor = "#000";
|
||||
this.addPose();
|
||||
|
||||
this.setBackground(backgroundImg);
|
||||
this.uploadPoseFile(this.node.name);
|
||||
}
|
||||
|
||||
setBackground(backgroundImg) {
|
||||
@@ -431,15 +432,6 @@ class OpenPose {
|
||||
}
|
||||
}
|
||||
|
||||
updateHistoryData() {
|
||||
if (this.history_change) {
|
||||
LS_Poses[this.node.name].undo_history = this.undo_history;
|
||||
LS_Poses[this.node.name].redo_history = this.redo_history;
|
||||
LS_Save();
|
||||
this.history_change = false;
|
||||
}
|
||||
}
|
||||
|
||||
uploadPoseFile(fileName) {
|
||||
// Upload pose to temp folder ComfyUI
|
||||
const hideShowControls = (show) => {
|
||||
@@ -456,44 +448,33 @@ class OpenPose {
|
||||
|
||||
hideShowControls(false);
|
||||
|
||||
const uploadFile = async (blobFile) => {
|
||||
try {
|
||||
const resp = await fetch("/upload/image", {
|
||||
method: "POST",
|
||||
body: blobFile,
|
||||
});
|
||||
|
||||
if (resp.status === 200) {
|
||||
const data = await resp.json();
|
||||
|
||||
if (!this.image.options.values.includes(data.name)) {
|
||||
this.image.options.values.push(data.name);
|
||||
}
|
||||
|
||||
this.image.value = data.name;
|
||||
this.updateHistoryData();
|
||||
|
||||
hideShowControls(true);
|
||||
} else {
|
||||
alert(resp.status + " - " + resp.statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
hideShowControls(true);
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
//Temporarily hide background image
|
||||
let tmp_BackgroundImg = this.canvas.backgroundImage;
|
||||
this.canvas.backgroundImage = null;
|
||||
this.canvas.renderAll();
|
||||
|
||||
this.canvas.lowerCanvasEl.toBlob(function (blob) {
|
||||
let formData = new FormData();
|
||||
this.canvas.lowerCanvasEl.toBlob((blob) => {
|
||||
const formData = new FormData();
|
||||
formData.append("image", blob, fileName);
|
||||
formData.append("overwrite", "true");
|
||||
formData.append("type", "input");
|
||||
uploadFile(formData);
|
||||
|
||||
this.uploadFileToServer(formData)
|
||||
.then((jsonData) => {
|
||||
if (!jsonData.success) {
|
||||
throw new Error(jsonData.error);
|
||||
}
|
||||
|
||||
const { name } = jsonData.data;
|
||||
|
||||
if (!this.image.options.values.includes(name)) {
|
||||
this.image.options.values.push(name);
|
||||
}
|
||||
|
||||
this.image.value = name;
|
||||
hideShowControls(true);
|
||||
})
|
||||
.catch(() => hideShowControls(true));
|
||||
}, "image/png");
|
||||
// - end
|
||||
|
||||
@@ -510,26 +491,80 @@ class OpenPose {
|
||||
};
|
||||
}
|
||||
|
||||
async uploadFileToServer(formData) {
|
||||
try {
|
||||
const resp = await api.fetchApi("/upload/image", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (resp.status !== 200) {
|
||||
return { success: false, error: `${resp.status} - ${resp.statusText}` };
|
||||
}
|
||||
|
||||
return { success: true, data: await resp.json() };
|
||||
} catch (error) {
|
||||
return { success: false, error };
|
||||
}
|
||||
}
|
||||
|
||||
getImageUrlByName(jsonData) {
|
||||
return {
|
||||
url: api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(
|
||||
jsonData.name
|
||||
)}&type=input&subfolder=${
|
||||
jsonData.subfolder
|
||||
}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||
),
|
||||
name: jsonData.name,
|
||||
subfolder: jsonData.subfolder,
|
||||
};
|
||||
}
|
||||
|
||||
onLoadBackground(e) {
|
||||
try {
|
||||
const file = this.backgroundInput.files[0];
|
||||
const url = URL.createObjectURL(file);
|
||||
fabric.Image.fromURL(url, (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,
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("image", file);
|
||||
formData.append("overwrite", "true");
|
||||
formData.append("type", "input");
|
||||
|
||||
this.uploadFileToServer(formData)
|
||||
.then((jsonData) => {
|
||||
if (!jsonData.success) {
|
||||
throw new Error(jsonData.error);
|
||||
}
|
||||
);
|
||||
});
|
||||
} catch (err) {}
|
||||
return this.getImageUrlByName(jsonData.data);
|
||||
})
|
||||
.then((image_data) => {
|
||||
if (!image_data) {
|
||||
console.log("Invalid data image!");
|
||||
return;
|
||||
}
|
||||
this.settings.background = image_data.url;
|
||||
|
||||
fabric.Image.fromURL(image_data.url, (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,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
getJSON() {
|
||||
@@ -563,84 +598,20 @@ class OpenPose {
|
||||
// Create OpenPose widget
|
||||
function createOpenPose(node, inputName, inputData, app) {
|
||||
node.name = inputName;
|
||||
const widget = {
|
||||
type: "openpose",
|
||||
name: `w${inputName}`,
|
||||
|
||||
draw: function (ctx, _, widgetWidth, y, widgetHeight) {
|
||||
const margin = 10,
|
||||
visible = app.canvas.ds.scale > 0.5 && this.type === "openpose",
|
||||
clientRectBound = ctx.canvas.getBoundingClientRect(),
|
||||
transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
clientRectBound.width / ctx.canvas.width,
|
||||
clientRectBound.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(margin, margin + y),
|
||||
w = (widgetWidth - margin * 2 - 3) * transform.a;
|
||||
|
||||
let aspect_ratio = 1;
|
||||
if (node.openPose.canvas.width !== node.openPose.canvas.height) {
|
||||
aspect_ratio = node.openPose.canvas.width / node.openPose.canvas.height;
|
||||
}
|
||||
|
||||
Object.assign(this.openpose.style, {
|
||||
left: `${transform.a * margin + transform.e + clientRectBound.left}px`,
|
||||
top: `${transform.d + transform.f + clientRectBound.top}px`,
|
||||
width: w + "px",
|
||||
height: w / aspect_ratio + "px",
|
||||
position: "absolute",
|
||||
zIndex: app.graph._nodes.indexOf(node),
|
||||
});
|
||||
|
||||
Object.assign(this.openpose.children[0].style, {
|
||||
width: w + "px",
|
||||
height: w / aspect_ratio + "px",
|
||||
});
|
||||
|
||||
Object.assign(this.openpose.children[1].style, {
|
||||
width: w + "px",
|
||||
height: w / aspect_ratio + "px",
|
||||
});
|
||||
|
||||
Array.from(this.openpose.children[2].children).forEach((element) => {
|
||||
Object.assign(element.style, {
|
||||
width: `${28.0 * transform.a}px`,
|
||||
height: `${22.0 * transform.d}px`,
|
||||
fontSize: `${transform.d * 10.0}px`,
|
||||
});
|
||||
|
||||
if (element.classList.contains("posenode_cnavas_size")) {
|
||||
const svg = element.children[0];
|
||||
|
||||
Object.assign(element.style, {
|
||||
height: `${28.0 * transform.d}px`,
|
||||
});
|
||||
|
||||
Object.assign(svg.style, {
|
||||
width: `${20.0 * transform.a}px`,
|
||||
height: `${20.0 * transform.d}px`,
|
||||
});
|
||||
}
|
||||
|
||||
element.hidden = !visible;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Fabric canvas
|
||||
let canvasOpenPose = document.createElement("canvas");
|
||||
node.openPose = new OpenPose(node, canvasOpenPose);
|
||||
const openPoseWrapper = $el("div.openPoseWrapper");
|
||||
const canvasOpenPose = $el("canvas");
|
||||
node.openPoseWrapper = openPoseWrapper;
|
||||
|
||||
node.openPose.canvas.setWidth(512);
|
||||
node.openPose.canvas.setHeight(512);
|
||||
// Add buttons add, reset, undo, redo poses
|
||||
node.addWidget("button", "Add pose", "add_pose", () => {
|
||||
node.openPose.addPose();
|
||||
});
|
||||
|
||||
let widgetCombo = node.widgets.filter((w) => w.type === "combo");
|
||||
widgetCombo[0].value = node.name;
|
||||
|
||||
widget.openpose = node.openPose.canvas.wrapperEl;
|
||||
widget.parent = node;
|
||||
node.addWidget("button", "Reset pose", "reset_pose", () => {
|
||||
node.openPose.resetCanvas(true);
|
||||
});
|
||||
|
||||
// Create elements undo, redo, clear history
|
||||
let panelButtons = $el("div.pose_panelButtons.comfy-menu-btns", [
|
||||
@@ -710,8 +681,8 @@ function createOpenPose(node, inputName, inputData, app) {
|
||||
},
|
||||
}),
|
||||
$el("button.posenode_cnavas_size", {
|
||||
innerHTML: `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<path d="m15.8,18.93q0,0.22 -0.17,0.4l-5.73,5.73l2.48,2.48q0.33,0.33 0.33,0.78t-0.33,0.78q-0.33,0.33 -0.78,0.33l-7.73,0q-0.45,0 -0.78,-0.33t-0.33,-0.78l0,-7.73q0,-0.45 0.33,-0.78t0.78,-0.33q0.45,0 0.78,0.33l2.48,2.48l5.73,-5.73q0.17,-0.17 0.4,-0.17t0.4,0.17l1.97,1.97q0.17,0.17 0.17,0.4zm13.47,-14.91l0,7.73q0,0.45 -0.33,0.78t-0.78,0.33q-0.45,0 -0.78,-0.33l-2.48,-2.48l-5.73,5.73q-0.17,0.17 -0.4,0.17t-0.4,-0.17l-1.97,-1.97q-0.17,-0.17 -0.17,-0.4t0.17,-0.4l5.73,-5.73l-2.48,-2.48q-0.33,-0.33 -0.33,-0.78t0.33,-0.78q0.33,-0.33 0.78,-0.33l7.73,0q0.45,0 0.78,0.33t0.33,0.78z" fill="currentColor" id="posenode_svg_canvas_size"/>
|
||||
innerHTML: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 32 32">
|
||||
<path d="m15.8,18.93q0,0.22 -0.17,0.4l-5.73,5.73l2.48,2.48q0.33,0.33 0.33,0.78t-0.33,0.78q-0.33,0.33 -0.78,0.33l-7.73,0q-0.45,0 -0.78,-0.33t-0.33,-0.78l0,-7.73q0,-0.45 0.33,-0.78t0.78,-0.33q0.45,0 0.78,0.33l2.48,2.48l5.73,-5.73q0.17,-0.17 0.4,-0.17t0.4,0.17l1.97,1.97q0.17,0.17 0.17,0.4zm13.47,-14.91l0,7.73q0,0.45 -0.33,0.78t-0.78,0.33q-0.45,0 -0.78,-0.33l-2.48,-2.48l-5.73,5.73q-0.17,0.17 -0.4,0.17t-0.4,-0.17l-1.97,-1.97q-0.17,-0.17 -0.17,-0.4t0.17,-0.4l5.73,-5.73l-2.48,-2.48q-0.33,-0.33 -0.33,-0.78t0.33,-0.78q0.33,-0.33 0.78,-0.33l7.73,0q0.45,0 0.78,0.33t0.33,0.78z" fill="currentColor" id="posenode_svg_canvas_size"/>
|
||||
</svg>`,
|
||||
style: { margin: 0, padding: 0 },
|
||||
title: "Change canvas size",
|
||||
@@ -741,6 +712,24 @@ function createOpenPose(node, inputName, inputData, app) {
|
||||
}),
|
||||
]);
|
||||
|
||||
node.openPose = new OpenPose(node, canvasOpenPose);
|
||||
const widget = node.addDOMWidget(
|
||||
"widget_openpose",
|
||||
"openpose",
|
||||
openPoseWrapper,
|
||||
{
|
||||
getValue() {
|
||||
return node.openPose.getSettings();
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
let widgetCombo = node.widgets.filter((w) => w.type === "combo");
|
||||
widgetCombo[0].value = node.name;
|
||||
|
||||
widget.openpose = node.openPose.canvas.wrapperEl;
|
||||
widget.parent = node;
|
||||
|
||||
// Background image
|
||||
node.openPose.backgroundInput = $el("input", {
|
||||
type: "file",
|
||||
@@ -754,53 +743,27 @@ function createOpenPose(node, inputName, inputData, app) {
|
||||
node.openPose.backgroundInput
|
||||
);
|
||||
|
||||
document.body.appendChild(widget.openpose);
|
||||
openPoseWrapper.appendChild(widget.openpose);
|
||||
|
||||
// Add buttons add, reset, undo, redo poses
|
||||
node.addWidget("button", "Add pose", "add_pose", () => {
|
||||
node.openPose.addPose();
|
||||
});
|
||||
node.onResize = function () {
|
||||
let [w, h] = this.size;
|
||||
|
||||
node.addWidget("button", "Reset pose", "reset_pose", () => {
|
||||
node.openPose.resetCanvas(true);
|
||||
});
|
||||
let aspect_ratio = 1;
|
||||
|
||||
// Add customWidget to node
|
||||
node.addCustomWidget(widget);
|
||||
aspect_ratio = node.openPose.canvas.height / node.openPose.canvas.width;
|
||||
|
||||
node.onRemoved = () => {
|
||||
if (Object.hasOwn(LS_Poses, node.name)) {
|
||||
delete LS_Poses[node.name];
|
||||
LS_Save();
|
||||
}
|
||||
const buffer = 140;
|
||||
|
||||
// When removing this node we need to remove the input from the DOM
|
||||
for (let y in node.widgets) {
|
||||
if (node.widgets[y].openpose) {
|
||||
node.widgets[y].openpose.remove();
|
||||
}
|
||||
}
|
||||
if (w > 1024) w = w - (w - 1024);
|
||||
if (w < 200) w = 200;
|
||||
|
||||
h = w * aspect_ratio + buffer;
|
||||
|
||||
if (h < 200) h = 200 + h / 2;
|
||||
|
||||
this.size = [w, h];
|
||||
};
|
||||
|
||||
widget.onRemove = () => {
|
||||
widget.openpose?.remove();
|
||||
};
|
||||
|
||||
app.canvas.onDrawBackground = function () {
|
||||
// Draw node isnt fired once the node is off the screen
|
||||
// if it goes off screen quickly, the input may not be removed
|
||||
// this shifts it off screen so it can be moved back if the node is visible.
|
||||
for (let n in app.graph._nodes) {
|
||||
const currNode = app.graph._nodes[n];
|
||||
for (let w in currNode.widgets) {
|
||||
let wid = currNode.widgets[w];
|
||||
if (Object.hasOwn(wid, "openpose")) {
|
||||
wid.openpose.style.left = -8000 + "px";
|
||||
wid.openpose.style.position = "absolute";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return { widget: widget };
|
||||
}
|
||||
|
||||
@@ -810,80 +773,87 @@ app.registerExtension({
|
||||
async init(app) {
|
||||
addStylesheet("css/posenode/pose_node_styles.css", import.meta.url);
|
||||
},
|
||||
async setup(app) {
|
||||
let openPoseNode = app.graph._nodes.filter((wi) => wi.type == "PoseNode");
|
||||
|
||||
if (openPoseNode.length) {
|
||||
openPoseNode.map((n) => {
|
||||
console.log(`Setup PoseNode: ${n.name}`);
|
||||
let widgetImage = n.widgets.find((w) => w.name == "image");
|
||||
if (widgetImage && Object.hasOwn(LS_Poses, n.name)) {
|
||||
let pose_ls = LS_Poses[n.name].undo_history;
|
||||
n.openPose.loadPreset(
|
||||
pose_ls.length > 0
|
||||
? pose_ls[pose_ls.length - 1]
|
||||
: { keypoints: default_keypoints }
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
async setup(app) {},
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === "PoseNode") {
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
const r = onNodeCreated
|
||||
? onNodeCreated.apply(this, arguments)
|
||||
: undefined;
|
||||
|
||||
let openPoseNode = app.graph._nodes.filter(
|
||||
(wi) => wi.type == "PoseNode"
|
||||
),
|
||||
nodeName = `Pose_${openPoseNode.length}`,
|
||||
nodeNamePNG = `${nodeName}.png`;
|
||||
const node_title = await this.getTitle();
|
||||
const node_id = this.id;
|
||||
|
||||
const nodeName = `Pose_${node_id}`;
|
||||
const nodeNamePNG = `${nodeName}.png`;
|
||||
|
||||
console.log(`Create PoseNode: ${nodeNamePNG}`);
|
||||
|
||||
LS_Poses =
|
||||
localStorage.getItem("ComfyUI_Poses") &&
|
||||
JSON.parse(localStorage.getItem("ComfyUI_Poses"));
|
||||
if (!LS_Poses) {
|
||||
localStorage.setItem("ComfyUI_Poses", JSON.stringify({}));
|
||||
LS_Poses = JSON.parse(localStorage.getItem("ComfyUI_Poses"));
|
||||
}
|
||||
|
||||
if (!Object.hasOwn(LS_Poses, nodeNamePNG)) {
|
||||
LS_Poses[nodeNamePNG] = {
|
||||
undo_history: [],
|
||||
redo_history: [],
|
||||
currentCanvasSize: { width: 512, height: 512 },
|
||||
};
|
||||
LS_Save();
|
||||
}
|
||||
|
||||
createOpenPose.apply(this, [this, nodeNamePNG, {}, app]);
|
||||
|
||||
setTimeout(() => {
|
||||
if (LS_Poses[nodeNamePNG]?.currentCanvasSize) {
|
||||
this.openPose.currentCanvasSize =
|
||||
LS_Poses[nodeNamePNG]?.currentCanvasSize;
|
||||
|
||||
this.openPose.setCanvasSize(
|
||||
this.openPose.currentCanvasSize.width,
|
||||
this.openPose.currentCanvasSize.height
|
||||
);
|
||||
}
|
||||
|
||||
this.openPose.uploadPoseFile(nodeNamePNG);
|
||||
// Resize window
|
||||
window.addEventListener("resize", (e) => resizeCanvas(this), false);
|
||||
}, 1);
|
||||
|
||||
this.setSize([530, 620]);
|
||||
|
||||
this.openPose.uploadPoseFile(nodeNamePNG);
|
||||
|
||||
// Resize window
|
||||
window.addEventListener("resize", (e) => resizeCanvas(this), false);
|
||||
return r;
|
||||
};
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = async function (widget) {
|
||||
onConfigure?.apply(this, arguments);
|
||||
|
||||
setTimeout(() => {
|
||||
const data = widget.widgets_values[3];
|
||||
|
||||
if (data) {
|
||||
const {
|
||||
currentCanvasSize,
|
||||
background,
|
||||
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 (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.settings = data;
|
||||
this.openPose.value = data;
|
||||
|
||||
this.openPose.canvas.renderAll();
|
||||
this.openPose.uploadPoseFile(this.name);
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
+1
-1
@@ -285,7 +285,7 @@ def install_node(nodeElement):
|
||||
# -- Copy tree folders
|
||||
web_extensions_dir = os.path.join(extension_folder, extension_dirs[0])
|
||||
|
||||
extensions_dirs_copy = ["js", "css", "assets", "lib"]
|
||||
extensions_dirs_copy = ["js", "css", "assets", "lib", "fonts"]
|
||||
for dir_name in extensions_dirs_copy:
|
||||
folder_curr = os.path.join(extension_folder, nodeElement, dir_name)
|
||||
if os.path.exists(folder_curr):
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui_custom_nodes_alekpet"
|
||||
description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, ChatGLM4TranslateCLIPTextEncodeNode, ChatGLM4TranslateTextNode, ChatGLM4InstructNode, ChatGLM4InstructMediaNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode."
|
||||
version = "1.0.37"
|
||||
version = "1.0.45"
|
||||
license = { file = "LICENSE" }
|
||||
|
||||
[project.urls]
|
||||
|
||||
Reference in New Issue
Block a user