ComfyUI-KJNodes/web/js/spline_editor.js
2024-04-17 19:43:49 +03:00

539 lines
19 KiB
JavaScript

import { app } from '../../../scripts/app.js'
export const loadScript = (
FILE_URL,
async = true,
type = 'text/javascript',
) => {
return new Promise((resolve, reject) => {
try {
// Check if the script already exists
const existingScript = document.querySelector(`script[src="${FILE_URL}"]`)
if (existingScript) {
resolve({ status: true, message: 'Script already loaded' })
return
}
const scriptEle = document.createElement('script')
scriptEle.type = type
scriptEle.async = async
scriptEle.src = FILE_URL
scriptEle.addEventListener('load', (ev) => {
resolve({ status: true })
})
scriptEle.addEventListener('error', (ev) => {
reject({
status: false,
message: `Failed to load the script ${FILE_URL}`,
})
})
document.body.appendChild(scriptEle)
} catch (error) {
reject(error)
}
})
}
const create_documentation_stylesheet = () => {
const tag = 'kj-splineditor-stylesheet'
let styleTag = document.head.querySelector(tag)
if (!styleTag) {
styleTag = document.createElement('style')
styleTag.type = 'text/css'
styleTag.id = tag
styleTag.innerHTML = `
.spline-editor {
background: var(--comfy-menu-bg);
position: absolute;
color: var(--fg-color);
font: 12px monospace;
line-height: 1.5em;
padding: 10px;
z-index: 3;
overflow: hidden;
border-radius: 10px;
border-style: solid;
border-width: medium;
border-color: var(--border-color);
}
`
document.head.appendChild(styleTag)
}
}
loadScript('/kjweb_async/svg-path-properties.min.js').catch((e) => {
console.log(e)
})
loadScript('/kjweb_async/protovis.min.js').catch((e) => {
console.log(e)
})
create_documentation_stylesheet()
function chainCallback(object, property, callback) {
if (object == undefined) {
//This should not happen.
console.error("Tried to add callback to non-existant object")
return;
}
if (property in object) {
const callback_orig = object[property]
object[property] = function () {
const r = callback_orig.apply(this, arguments);
callback.apply(this, arguments);
return r
};
} else {
object[property] = callback;
}
}
app.registerExtension({
name: 'KJNodes.curves',
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData?.name == 'SplineEditor') {
chainCallback(nodeType.prototype, "onNodeCreated", function () {
var element = document.createElement("div");
console.log(this)
var splineEditor = this.addDOMWidget(nodeData.name, "SplineEditorWidget", element, {
serialize: false,
hideOnZoom: false,
});
splineEditor.parentEl = document.createElement("div");
splineEditor.parentEl.className = "spline-editor";
splineEditor.parentEl.style['width'] = "100%"
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");
//addElement(nodeData, nodeType, this)
var w = 512
var h = 512
var i = 3
var segmented = false
if (points == null) {
var points = pv.range(1, 5).map(i => ({
x: i * w / 5,
y: 50 + Math.random() * (h - 100)
}));
}
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")
vis.render();
var svgElement = vis.canvas();
svgElement.style['zIndex'] = "7"
svgElement.style['position'] = "fixed"
console.log("Spline editor rendered", svgElement)
splineEditor.element.appendChild(svgElement);
});
}
}
})
// 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();
var points = [];
for (var i = 0; i < numSamples; i++) {
// Calculate the distance along the path for the current sample
var distance = (pathLength / (numSamples - 1)) * i;
// Get the point at the current distance
var point = svgPathElement.getPointAtLength(distance);
// Add the point to the array of points
points.push({ x: point.x, y: point.y });
}
//console.log(points);
return points;
}