From d9712b0e04c70c6299fa61ce5c128ba56228ea11 Mon Sep 17 00:00:00 2001 From: kijai <40791699+kijai@users.noreply.github.com> Date: Thu, 18 Apr 2024 01:45:32 +0300 Subject: [PATCH] spline editor work --- nodes.py | 2 +- web/js/jsnodes.js | 3 - web/js/spline_editor.js | 425 ++++++++-------------------------------- 3 files changed, 84 insertions(+), 346 deletions(-) diff --git a/nodes.py b/nodes.py index e3d6c9a..aca7c44 100644 --- a/nodes.py +++ b/nodes.py @@ -4584,7 +4584,7 @@ class SplineEditor: CATEGORY = "KJNodes/experimental" def splinedata(self, mask_width, mask_height, coordinates, interpolation, points_to_sample): - + print(coordinates) coordinates = json.loads(coordinates) print(coordinates) diff --git a/web/js/jsnodes.js b/web/js/jsnodes.js index 2349636..3ab7d4c 100644 --- a/web/js/jsnodes.js +++ b/web/js/jsnodes.js @@ -5,9 +5,6 @@ app.registerExtension({ async beforeRegisterNodeDef(nodeType, nodeData, app) { switch (nodeData.name) { case "ConditioningMultiCombine": - nodeType.prototype.onNodeMoved = function () { - console.log(this.pos[0]) - } nodeType.prototype.onNodeCreated = function () { //this.inputs_offset = nodeData.name.includes("selective")?1:0 this.cond_type = "CONDITIONING" diff --git a/web/js/spline_editor.js b/web/js/spline_editor.js index 96334e4..94e1d81 100644 --- a/web/js/spline_editor.js +++ b/web/js/spline_editor.js @@ -1,5 +1,15 @@ import { app } from '../../../scripts/app.js' +//from melmass +export function makeUUID() { + let dt = new Date().getTime() + const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = ((dt + Math.random() * 16) % 16) | 0 + dt = Math.floor(dt / 16) + return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16) + }) + return uuid +} export const loadScript = ( FILE_URL, @@ -97,27 +107,34 @@ app.registerExtension({ async beforeRegisterNodeDef(nodeType, nodeData) { if (nodeData?.name == 'SplineEditor') { chainCallback(nodeType.prototype, "onNodeCreated", function () { + hideWidgetForGood(this, this.widgets.find(w => w.name === "coordinates")) var element = document.createElement("div"); - - console.log(this) + this.uuid = makeUUID() + element.id = `spline-editor-${this.uuid}` + var splineEditor = this.addDOMWidget(nodeData.name, "SplineEditorWidget", element, { serialize: false, hideOnZoom: false, - }); + getValue() { + return element.value; + }, + setValue(v) { + element.value = v; + }, + }); + this.setSize([620, 760]) splineEditor.parentEl = document.createElement("div"); splineEditor.parentEl.className = "spline-editor"; - splineEditor.parentEl.style['width'] = "100%" + splineEditor.parentEl.id = `spline-editor-${this.uuid}` + splineEditor.parentEl.style['width'] = "90%" splineEditor.parentEl.style['height'] = "544px" element.appendChild(splineEditor.parentEl); - splineEditor.canvasEl = document.createElement("div"); - splineEditor.canvasEl.style['width'] = "512px" - splineEditor.canvasEl.style['height'] = "512px" - splineEditor.canvasEl.style['zIndex'] = "0" - var coordWidget = this.widgets.find(w => w.name === "coordinates"); - var interpolationWidget = this.widgets.find(w => w.name === "interpolation"); - var pointsWidget = this.widgets.find(w => w.name === "points_to_sample"); + + this.coordWidget = this.widgets.find(w => w.name === "coordinates"); + this.interpolationWidget = this.widgets.find(w => w.name === "interpolation"); + this.pointsWidget = this.widgets.find(w => w.name === "points_to_sample"); //addElement(nodeData, nodeType, this) var w = 512 var h = 512 @@ -142,8 +159,20 @@ app.registerExtension({ i = points.push(this.mouse()) - 1; return this; } - }); - vis.add(pv.Rule) + }) + .event("mouseup", function() { + if (this.pathElements !== null) { + console.log(this.pathElements) + let coords = samplePoints(this.pathElements[0], this.pointsWidget.value); + let coordsString = JSON.stringify(coords); + if (this.coordWidget) { + this.coordWidget.value = coordsString; + console.log(this.coordWidget.value) + } + } + }); + + vis.add(pv.Rule) .data(pv.range(0, 8, .5)) .bottom(d => d * 70 + 9.5) .strokeStyle("gray") @@ -153,7 +182,7 @@ app.registerExtension({ .data(() => points) .left(d => d.x) .top(d => d.y) - .interpolate(() => interpolationWidget.value) + .interpolate(() => this.interpolationWidget.value) .segmented(() => segmented) .strokeStyle(pv.Colors.category10().by(pv.index)) .tension(0.5) @@ -170,6 +199,10 @@ app.registerExtension({ .event("mousedown", pv.Behavior.drag()) .event("dragstart", function() { i = this.index; + if (pv.event.button === 2) { + points.splice(i--, 1); + vis.render(); + } return this; }) .event("drag", vis) @@ -186,339 +219,23 @@ app.registerExtension({ var svgElement = vis.canvas(); svgElement.style['zIndex'] = "7" svgElement.style['position'] = "fixed" - console.log("Spline editor rendered", svgElement) + vis.id = `spline-editor-vis-${this.uuid}` splineEditor.element.appendChild(svgElement); + svgElement.id = `spline-editor-svg-${this.uuid}` + + this.pathElements = svgElement.getElementsByTagName('path'); // Get all path elements }); + } + //disable context menu on right click + document.addEventListener('contextmenu', function(e) { + if (e.button === 2) { // Right mouse button + e.preventDefault(); + e.stopPropagation(); + } + }) } }) -// const drawFg = nodeType.prototype.onDrawForeground -// nodeType.prototype.onDrawForeground = function (ctx) { -// const r = drawFg ? drawFg.apply(this, arguments) : undefined -// if (this.flags.collapsed) return r - -// var w = 512 -// var h = 512 -// var i = 3 - -// if (points == null) { -// var points = pv.range(1, 5).map(i => ({ -// x: i * w / 5, -// y: 50 + Math.random() * (h - 100) -// })); -// } - -// var segmented = false -// let vis = new pv.Panel() -// .width(w) -// .height(h) -// .fillStyle("var(--comfy-menu-bg)") -// .strokeStyle("orange") -// .lineWidth(0) -// .antialias(false) -// .margin(10) -// .event("mousedown", function() { -// if (pv.event.shiftKey) { // Use pv.event to access the event object -// i = points.push(this.mouse()) - 1; -// return this; -// } -// }); -// vis.add(pv.Rule) -// .data(pv.range(0, 8, .5)) -// .bottom(d => d * 70 + 9.5) -// .strokeStyle("gray") -// .lineWidth(1) - -// vis.add(pv.Line) -// .data(() => points) -// .left(d => d.x) -// .top(d => d.y) -// .interpolate(() => interpolationWidget.value) -// .segmented(() => segmented) -// .strokeStyle(pv.Colors.category10().by(pv.index)) -// .tension(0.5) -// .lineWidth(3) - -// vis.add(pv.Dot) -// .data(() => points) -// .left(d => d.x) -// .top(d => d.y) -// .radius(7) -// .cursor("move") -// .strokeStyle(function() { return i == this.index ? "#ff7f0e" : "#1f77b4"; }) -// .fillStyle(function() { return "rgba(100, 100, 100, 0.2)"; }) -// .event("mousedown", pv.Behavior.drag()) -// .event("dragstart", function() { -// i = this.index; -// return this; -// }) -// .event("drag", vis) -// .anchor("top").add(pv.Label) -// .font(d => Math.sqrt(d[2]) * 32 + "px sans-serif") -// //.text(d => `(${Math.round(d.x)}, ${Math.round(d.y)})`) -// .text(d => { -// // Normalize y to range 0.0 to 1.0, considering the inverted y-axis -// var normalizedY = 1.0 - (d.y / h); -// return `${normalizedY.toFixed(2)}`; -// }) -// .textStyle("orange") - -// //disable context menu on right click -// document.addEventListener('contextmenu', function(e) { -// if (e.button === 2) { // Right mouse button -// e.preventDefault(); -// e.stopPropagation(); -// } -// }) -// //right click remove dot -// pv.listen(window, "mousedown", () => { -// window.focus(); -// if (pv.event.button === 2) { -// points.splice(i--, 1); -// vis.render(); -// } -// }); -// //send coordinates to node on mouseup -// pv.listen(window, "mouseup", () => { -// if (pathElements !== null) { -// let coords = samplePoints(pathElements[0], pointsWidget.value); -// let coordsString = JSON.stringify(coords); -// if (coordWidget) { -// coordWidget.value = coordsString; -// } -// } -// }); - -// vis.render(); -// var svgElement = vis.canvas(); -// console.log("Spline editor rendered", svgElement) -// splineEditor.element.appendChild(svgElement); -// } -// }, -// }) - -// export const addElement = function(nodeData, nodeType, context) { -// console.log("Creating spline editor for node", nodeData.name); -// console.log(context); -// const iconSize = 24 -// const iconMargin = 4 - - -// var splineEditor = context.widgets.find(w => w.name === "SplineEditor"); - -// console.log(splineEditor); -// let vis = null -// var show_doc = true -// //close button -// const closeButton = document.createElement('div'); -// closeButton.textContent = '❌'; -// closeButton.style.position = 'absolute'; -// closeButton.style.top = '0'; -// closeButton.style.right = '0'; -// closeButton.style.cursor = 'pointer'; -// closeButton.style.padding = '5px'; -// closeButton.style.color = 'red'; -// closeButton.style.fontSize = '12px'; -// closeButton.style.zIndex = '7'; -// closeButton.addEventListener('mousedown', (e) => { -// e.stopPropagation(); -// show_doc = !show_doc -// splineEditor.element.parentNode.removeChild(splineEditor.element) -// splineEditor.element = null -// }); -// splineEditor.element.appendChild(closeButton) - - - -// nodeType.prototype.onNodeCreated = function () { -// console.log("Node created") -// var coordWidget = context.widgets.find(w => w.name === "coordinates"); -// var interpolationWidget = context.widgets.find(w => w.name === "interpolation"); -// var pointsWidget = context.widgets.find(w => w.name === "points_to_sample"); -// } -// nodeType.prototype.onRemoved = function () { -// console.log("Node removed") -// if (splineEditor !== null) { -// splineEditor.element.parentNode.removeChild(splineEditor.element) -// splineEditor.element = null -// } -// } -// const drawFg = nodeType.prototype.onDrawForeground -// nodeType.prototype.onDrawForeground = function (ctx) { -// console.log("Drawing foreground") -// const r = drawFg ? drawFg.apply(this, arguments) : undefined -// if (context.flags.collapsed) return r - -// const x = context.size[0] - iconSize - iconMargin - -// //if (show_doc && splineEditor === null) { -// console.log("Drawing spline editor") - -// var w = 512 -// var h = 512 -// var i = 3 - -// if (points == null) { -// var points = pv.range(1, 5).map(i => ({ -// x: i * w / 5, -// y: 50 + Math.random() * (h - 100) -// })); -// } - -// var segmented = false -// vis = new pv.Panel() -// .width(w) -// .height(h) -// .fillStyle("var(--comfy-menu-bg)") -// .strokeStyle("orange") -// .lineWidth(0) -// .antialias(false) -// .margin(10) -// .event("mousedown", function() { -// if (pv.event.shiftKey) { // Use pv.event to access the event object -// i = points.push(this.mouse()) - 1; -// return this; -// } -// }); -// vis.add(pv.Rule) -// .data(pv.range(0, 8, .5)) -// .bottom(d => d * 70 + 9.5) -// .strokeStyle("gray") -// .lineWidth(1) - -// vis.add(pv.Line) -// .data(() => points) -// .left(d => d.x) -// .top(d => d.y) -// .interpolate(() => interpolationWidget.value) -// .segmented(() => segmented) -// .strokeStyle(pv.Colors.category10().by(pv.index)) -// .tension(0.5) -// .lineWidth(3) - -// vis.add(pv.Dot) -// .data(() => points) -// .left(d => d.x) -// .top(d => d.y) -// .radius(7) -// .cursor("move") -// .strokeStyle(function() { return i == this.index ? "#ff7f0e" : "#1f77b4"; }) -// .fillStyle(function() { return "rgba(100, 100, 100, 0.2)"; }) -// .event("mousedown", pv.Behavior.drag()) -// .event("dragstart", function() { -// i = this.index; -// return this; -// }) -// .event("drag", vis) -// .anchor("top").add(pv.Label) -// .font(d => Math.sqrt(d[2]) * 32 + "px sans-serif") -// //.text(d => `(${Math.round(d.x)}, ${Math.round(d.y)})`) -// .text(d => { -// // Normalize y to range 0.0 to 1.0, considering the inverted y-axis -// var normalizedY = 1.0 - (d.y / h); -// return `${normalizedY.toFixed(2)}`; -// }) -// .textStyle("orange") - -// //disable context menu on right click -// document.addEventListener('contextmenu', function(e) { -// if (e.button === 2) { // Right mouse button -// e.preventDefault(); -// e.stopPropagation(); -// } -// }) -// //right click remove dot -// pv.listen(window, "mousedown", () => { -// window.focus(); -// if (pv.event.button === 2) { -// points.splice(i--, 1); -// vis.render(); -// } -// }); -// //send coordinates to node on mouseup -// pv.listen(window, "mouseup", () => { -// if (pathElements !== null) { -// let coords = samplePoints(pathElements[0], pointsWidget.value); -// let coordsString = JSON.stringify(coords); -// if (coordWidget) { -// coordWidget.value = coordsString; -// } -// } -// }); - -// vis.render(); -// var svgElement = vis.canvas(); -// console.log("Spline editor rendered", svgElement) -// splineEditor.element.appendChild(svgElement); - -// //document.body.appendChild(splineEditor) -// var pathElements = svgElement.getElementsByTagName('path'); // Get all path elements - //} - // close the popup - // else if (!show_doc && splineEditor !== null) { - // splineEditor.element.parentNode.removeChild(splineEditor.element) - // splineEditor.element = null - // } - - // if (show_doc && splineEditor.element !== null && vis !== null) { - // const rect = ctx.canvas.getBoundingClientRect() - // const scaleX = rect.width / ctx.canvas.width - // const scaleY = rect.height / ctx.canvas.height - - // const transform = new DOMMatrix() - // .scaleSelf(scaleX, scaleY) - // .translateSelf(this.size[0] * scaleX, 0) - // .multiplySelf(ctx.getTransform()) - // .translateSelf(100, -32) - - // const scale = new DOMMatrix() - // .scaleSelf(transform.a, transform.d); - - // const styleObject = { - // transformOrigin: '0 0', - // transform: scale, - // left: `${transform.a + transform.e}px`, - // top: `${transform.d + transform.f}px`, - // }; - // Object.assign(splineEditor.element.style, styleObject); - // } - // ctx.save() - // ctx.translate(x - 2, iconSize - 45) - // ctx.scale(iconSize / 32, iconSize / 32) - // ctx.strokeStyle = 'rgba(255,255,255,0.3)' - // ctx.lineCap = 'round' - // ctx.lineJoin = 'round' - // ctx.lineWidth = 2.4 - // ctx.font = 'bold 36px monospace' - // ctx.fillStyle = 'orange'; - // ctx.fillText('📈', 0, 24) - // ctx.restore() - // return r - - // // handle clicking of the icon - // const mouseDown = nodeType.prototype.onMouseDown - // nodeType.prototype.onMouseDown = function (e, localPos, canvas) { - // const r = mouseDown ? mouseDown.apply(this, arguments) : undefined - // const iconX = this.size[0] - iconSize - iconMargin - // const iconY = iconSize - 45 - // if ( - // localPos[0] > iconX && - // localPos[0] < iconX + iconSize && - // localPos[1] > iconY && - // localPos[1] < iconY + iconSize - // ) { - // if (this.show_doc === undefined) { - // this.show_doc = true - // } else { - // this.show_doc = !this.show_doc - // } - // return true; - // } - // return r; - // } - - - function samplePoints(svgPathElement, numSamples) { var pathLength = svgPathElement.getTotalLength(); @@ -536,4 +253,28 @@ function samplePoints(svgPathElement, numSamples) { } //console.log(points); return points; +} + +//from melmass +export function hideWidgetForGood(node, widget, suffix = '') { + widget.origType = widget.type + widget.origComputeSize = widget.computeSize + widget.origSerializeValue = widget.serializeValue + widget.computeSize = () => [0, -4] // -4 is due to the gap litegraph adds between widgets automatically + widget.type = "converted-widget" + suffix + // widget.serializeValue = () => { + // // Prevent serializing the widget if we have no input linked + // const w = node.inputs?.find((i) => i.widget?.name === widget.name); + // if (w?.link == null) { + // return undefined; + // } + // return widget.origSerializeValue ? widget.origSerializeValue() : widget.value; + // }; + + // Hide any linked widgets, e.g. seed+seedControl + if (widget.linkedWidgets) { + for (const w of widget.linkedWidgets) { + hideWidgetForGood(node, w, ':' + widget.name) + } + } } \ No newline at end of file