unified widget_vector

This commit is contained in:
Alexander G. Morano
2025-04-30 23:19:44 -04:00
parent 68eb3e7ea8
commit 583ecbba3e
5 changed files with 88 additions and 180 deletions
+46 -1
View File
@@ -2,7 +2,6 @@
import { api } from "../../../scripts/api.js";
import { app } from "../../../scripts/app.js";
import { domShowModal } from '../util/util.js'
import { apiJovimetrix } from '../util/util_jov.js'
import { bubbles } from '../util/util_fun.js'
@@ -10,6 +9,52 @@ const _id = "DELAY (JOV) ✋🏽"
const EVENT_JOVI_DELAY = "jovi-delay-user";
const EVENT_JOVI_UPDATE = "jovi-delay-update";
function domShowModal(innerHTML, eventCallback, timeout=null) {
return new Promise((resolve, reject) => {
const modal = document.createElement("div");
modal.className = "modal";
modal.innerHTML = innerHTML;
document.body.appendChild(modal);
// center
const modalContent = modal.querySelector(".jov-modal-content");
modalContent.style.position = "absolute";
modalContent.style.left = "50%";
modalContent.style.top = "50%";
modalContent.style.transform = "translate(-50%, -50%)";
let timeoutId;
const handleEvent = (event) => {
const targetId = event.target.id;
const result = eventCallback(targetId);
if (result != null) {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
modal.remove();
resolve(result);
}
};
modalContent.addEventListener("click", handleEvent);
modalContent.addEventListener("dblclick", handleEvent);
if (timeout) {
timeout *= 1000;
timeoutId = setTimeout(() => {
modal.remove();
reject(new Error("TIMEOUT"));
}, timeout);
}
//setTimeout(() => {
// modal.dispatchEvent(new Event('tick'));
//}, 1000);
});
}
app.registerExtension({
name: 'jovimetrix.node.' + _id,
async beforeRegisterNodeDef(nodeType, nodeData) {
-157
View File
@@ -1,157 +0,0 @@
/**/
import { app } from "../../../scripts/app.js"
function arrayToObject(values, length, parseFn) {
const result = {};
for (let i = 0; i < length; i++) {
result[i] = parseFn(values[i]);
}
return result;
}
export function domRenderTemplate(template, data) {
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g')
template = template.replace(regex, data[key])
}
}
return template
}
export function domFoldableToggle(elementId, symbolId) {
const content = document.getElementById(elementId)
const symbol = document.getElementById(symbolId)
if (content.style.display == 'none' || content.style.display == '') {
content.style.display = 'flex'
symbol.innerHTML = '&#9661' // Down arrow
} else {
content.style.display = 'none'
symbol.innerHTML = '&#9655' // Right arrow
}
}
export function domInnerValueChange(node, pos, widget, value, event=undefined) {
const type = widget.type.includes("INT") ? Number : parseFloat
widget.value = arrayToObject(value, Object.keys(value).length, type);
if (
widget.options &&
widget.options.property &&
node.properties[widget.options.property] !== undefined
) {
node.setProperty(widget.options.property, widget.value)
}
if (widget.callback) {
widget.callback(widget.value, app.canvas, node, pos, event)
}
}
export function domWidgetOffset(
widget,
ctx,
node,
widgetWidth,
widgetY,
height
) {
const margin = 10
const elRect = ctx.canvas.getBoundingClientRect()
const transform = new DOMMatrix()
.scaleSelf(
elRect.width / ctx.canvas.width,
elRect.height / ctx.canvas.height
)
.multiplySelf(ctx.getTransform())
.translateSelf(0, widgetY + margin)
const scale = new DOMMatrix().scaleSelf(transform.a, transform.d)
Object.assign(widget.inputEl.style, {
transformOrigin: '0 0',
transform: scale,
left: `${transform.e}px`,
top: `${transform.d + transform.f}px`,
width: `${widgetWidth}px`,
height: `${(height || widget.parent?.inputHeight || 32) - margin}px`,
position: 'absolute',
background: !node.color ? '' : node.color,
color: !node.color ? '' : 'white',
zIndex: 5, //app.graph._nodes.indexOf(node),
})
}
export function domEscapeHtml(unsafe) {
return unsafe
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
}
export function domShowModal(innerHTML, eventCallback, timeout=null) {
return new Promise((resolve, reject) => {
const modal = document.createElement("div");
modal.className = "modal";
modal.innerHTML = innerHTML;
document.body.appendChild(modal);
// center
const modalContent = modal.querySelector(".jov-modal-content");
modalContent.style.position = "absolute";
modalContent.style.left = "50%";
modalContent.style.top = "50%";
modalContent.style.transform = "translate(-50%, -50%)";
let timeoutId;
const handleEvent = (event) => {
const targetId = event.target.id;
const result = eventCallback(targetId);
if (result != null) {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
modal.remove();
resolve(result);
}
};
modalContent.addEventListener("click", handleEvent);
modalContent.addEventListener("dblclick", handleEvent);
if (timeout) {
timeout *= 1000;
timeoutId = setTimeout(() => {
modal.remove();
reject(new Error("TIMEOUT"));
}, timeout);
}
//setTimeout(() => {
// modal.dispatchEvent(new Event('tick'));
//}, 1000);
});
}
/*
* Parse a string "255,255,255,255" or a List[255,255,255,255] into hex
*/
export function colorLerpHex(colorStart, colorEnd, lerp) {
// Parse color strings into RGB arrays
const startRGB = colorHex2RGB(colorStart);
const endRGB = colorHex2RGB(colorEnd);
// Linearly interpolate each RGB component
const lerpedRGB = startRGB.map((component, index) => {
return Math.round(component + (endRGB[index] - component) * lerp);
});
// Convert the interpolated RGB values back to a hex color string
return colorRGB2Hex(lerpedRGB);
}
+8 -4
View File
@@ -125,15 +125,19 @@ export function widgetHookControl(node, control_key, target, matchFloatSize=fals
combo.callback = () => {
const me = oldCallback?.apply(this, arguments);
widgetHide(node, target, "-jov");
if (["VEC2", "VEC2INT", "COORD2D", "VEC3", "VEC3INT", "VEC4", "VEC4INT", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) {
//if (["VEC2", "VEC2INT", "COORD2D", "VEC3", "VEC3INT", "VEC4", "VEC4INT", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) {
if (["VEC2", "VEC3", "VEC4", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) {
let type = combo.value;
if (matchFloatSize) {
type = "FLOAT";
if (["VEC2", "VEC2INT", "COORD2D"].includes(combo.value)) {
// if (["VEC2", "VEC2INT", "COORD2D"].includes(combo.value)) {
if (["VEC2"].includes(combo.value)) {
type = "VEC2";
} else if (["VEC3", "VEC3INT"].includes(combo.value)) {
//} else if (["VEC3", "VEC3INT"].includes(combo.value)) {
} else if (["VEC3"].includes(combo.value)) {
type = "VEC3";
} else if (["VEC4", "VEC4INT"].includes(combo.value)) {
//} else if (["VEC4", "VEC4INT"].includes(combo.value)) {
} else if (["VEC4"].includes(combo.value)) {
type = "VEC4";
}
}
+4 -4
View File
@@ -11,12 +11,12 @@ const _MAP = {
INT: "🔟",
FLOAT: "🛟",
VEC2: "🇽🇾",
COORD2D: "🇽🇾",
VEC2INT: "🇽🇾",
//COORD2D: "🇽🇾",
//VEC2INT: "🇽🇾",
VEC3: "🇽🇾\u200c🇿",
VEC3INT: "🇽🇾\u200c🇿",
//VEC3INT: "🇽🇾\u200c🇿",
VEC4: "🇽🇾\u200c🇿\u200c🇼",
VEC4INT: "🇽🇾\u200c🇿\u200c🇼",
//VEC4INT: "🇽🇾\u200c🇿\u200c🇼",
LIST: "🧾",
DICT: "📖",
IMAGE: "🖼️",
@@ -1,10 +1,32 @@
/**/
import { app } from "../../../scripts/app.js"
import { $el } from "../../../scripts/ui.js"
import { domInnerValueChange } from '../util/util.js'
import { app } from "../../scripts/app.js"
import { $el } from "../../scripts/ui.js"
/** @import { IWidget, LGraphCanvas } from '../../types/litegraph/litegraph.d.ts' */
function arrayToObject(values, length, parseFn) {
const result = {};
for (let i = 0; i < length; i++) {
result[i] = parseFn(values[i]);
}
return result;
}
function domInnerValueChange(node, pos, widget, value, event=undefined) {
//const numtype = widget.type.includes("INT") ? Number : parseFloat
widget.value = arrayToObject(value, Object.keys(value).length, widget.convert);
if (
widget.options &&
widget.options.property &&
node.properties[widget.options.property] !== undefined
) {
node.setProperty(widget.options.property, widget.value)
}
if (widget.callback) {
widget.callback(widget.value, app.canvas, node, pos, event)
}
}
function colorHex2RGB(hex) {
hex = hex.replace(/^#/, '');
const bigint = parseInt(hex, 16);
@@ -32,13 +54,14 @@ const VectorWidget = (app, inputName, options, initial) => {
/** @type {IWidget} */
const widget = {
name: inputName,
type2: options[0],
//type2: options[0],
type: options[0],
y: 0,
value: values,
options: options[1]
}
widget.convert = parseFloat;
widget.options.precision = 3;
widget.options.step = 0.01;
widget.options.round = 1 / 10 ** widget.options.step;
@@ -47,6 +70,7 @@ const VectorWidget = (app, inputName, options, initial) => {
widget.options.step = 1;
widget.options.round = 1;
widget.options.precision = 0;
widget.convert = Number;
}
if (widget.options?.rgb || false) {
@@ -65,7 +89,8 @@ const VectorWidget = (app, inputName, options, initial) => {
let picker;
widget.draw = function(ctx, node, width, Y, height) {
if ((app.canvas.ds.scale < 0.50) || (!this.type2.startsWith("VEC") && this.type2 != "COORD2D")) return;
// if ((app.canvas.ds.scale < 0.50) || (!this.type2.startsWith("VEC") && this.type2 != "COORD2D")) return;
if ((app.canvas.ds.scale < 0.50) || (!this.type.startsWith("VEC"))) return;
ctx.save()
ctx.beginPath()
ctx.lineWidth = 1
@@ -269,15 +294,6 @@ app.registerExtension({
}),
VEC4: (node, inputName, inputData, app) => ({
widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0, 0, 0])),
}),
VEC2INT: (node, inputName, inputData, app) => ({
widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0])),
}),
VEC3INT: (node, inputName, inputData, app) => ({
widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0, 0])),
}),
VEC4INT: (node, inputName, inputData, app) => ({
widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0, 0, 0])),
})
}
}