You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

310 lines
8.0 KiB
JavaScript

class Cable {
start = "";
end = "";
strPath = "";
color = "";
strokeWidth = 10;
bufferSize = 20;
buffer = [];
path = null;
constructor(start) {
this.start = start;
this.randomColor();
}
randomColor() {
const hue = Math.floor(Math.random() * 360);
const saturation = Math.floor(50 + Math.random() * (50 + 1)) + "%";
const lightness = "75%";
this.color = "hsl(" + hue + ", " + saturation + ", " + lightness + ")";
}
createCable(position) {
this.path = document.createElementNS("http://www.w3.org/2000/svg", "path");
this.path.setAttribute("fill", "none");
this.path.setAttribute("stroke", this.color);
this.path.setAttribute("stroke-width", this.strokeWidth);
this.path.setAttribute("stroke-linecap", "round");
this.buffer = [];
this.appendToBuffer(position);
this.strPath = "M" + position.x + " " + position.y;
this.path.setAttribute("d", this.strPath);
}
appendToBuffer(position) {
this.buffer.push(position);
while (this.buffer.length > this.bufferSize) {
this.buffer.shift();
}
}
getAveragePoint(offset) {
// Calculate the average point, starting at offset in the buffer
let len = this.buffer.length;
if (len % 2 === 1 || len >= this.bufferSize) {
let totalX = 0;
let totalY = 0;
let position, i;
let count = 0;
for (i = offset; i < len; i++) {
count++;
position = this.buffer[i];
totalX += position.x;
totalY += position.y;
}
return {
x: totalX / count,
y: totalY / count,
};
}
return null;
}
updatePath() {
let position = this.getAveragePoint(0);
if (position) {
// Get the smoothed part of the path that will not change
this.strPath += " L" + position.x + " " + position.y;
// Get the last part of the path (close to the current mouse position)
// This part will change if the mouse moves again
var tmpPath = "";
for (var offset = 2; offset < this.buffer.length; offset += 2) {
position = this.getAveragePoint(offset);
tmpPath += " L" + position.x + " " + position.y;
}
this.path.setAttribute("d", this.strPath + tmpPath);
}
}
}
class Panel {
width = 0;
height = 0;
svg = null;
container = null;
containerBoundingClient = null;
model = {};
params = [];
sockets = [];
preset = {};
cable = null;
cables = null;
constructor(svg, container, preset = {}, debug = false) {
this.svg = this.htmlToElement(svg);
this.container = container;
this.container.style.position = "relative";
this.containerRect = container.getBoundingClientRect();
this.preset = { ...preset };
this.debug = debug;
while (container.firstChild) {
container.removeChild(container.lastChild);
}
container.appendChild(this.svg);
this.setSize();
this.createParams();
this.createSockets();
this.createCables();
this.svg.addEventListener("mousedown", (e) => this.startCable(e));
this.svg.addEventListener("mousemove", (e) => this.drawCable(e));
this.svg.addEventListener("mouseup", (e) => this.endCable(e));
// this.svg.addEventListener("mouseleave", (e) => this.endCable(e));
}
htmlToElement(string) {
var template = document.createElement("template");
string = string.trim(); // Never return a text node of whitespace as the result
template.innerHTML = string;
return template.content.firstChild;
}
setSize() {
this.width = this.svg.getAttribute("width");
this.height = this.svg.getAttribute("height");
this.container.style.width = this.width + "px";
this.container.style.height = this.height + "px";
}
createParams() {
// this.svg.querySelector("#params").style.visibility = "hidden";
this.params = this.svg.querySelectorAll("#params [fill='#FF0000']");
this.model.params = [];
for (const param of this.params) {
let rect = param.getBoundingClientRect();
let control = document.createElement("input");
control.setAttribute("type", "range");
control.setAttribute("data-width", rect.width);
control.setAttribute("data-height", rect.height);
control.setAttribute("data-fgcolor", "white");
control.classList.add("input-knob");
control.style.position = "absolute";
control.style.left = rect.left - this.containerRect.left + "px";
control.style.top = rect.top - this.containerRect.top + "px";
control.setAttribute("name", param.id);
if (this.preset.hasOwnProperty(param.id)) {
control.value = this.preset[param.id];
}
this.container.appendChild(control);
this.model.params.push(param.id);
if (this.debug) {
let label = document.createElement("label");
label.setAttribute("for", param.id);
label.innerHTML = param.id;
label.style.position = "absolute";
label.style.left = rect.left - this.containerRect.left + "px";
label.style.top = rect.top + rect.height + "px";
label.style.fontSize = "1rem";
label.style.backgroundColor = "red";
label.style.color = "white";
this.container.appendChild(label);
}
}
let group = this.svg.querySelector("#params");
if (group) group.style.display = "none";
}
createSockets() {
// this.svg.querySelector("#sockets").style.visibility = "hidden";
this.sockets = this.svg.querySelectorAll("#sockets [fill='#00FF00']");
this.model.sockets = [];
for (const socket of this.sockets) {
let rect = socket.getBoundingClientRect();
let input = document.createElement("input");
input.setAttribute("name", socket.id);
input.classList.add("socket");
input.style.position = "absolute";
input.style.left = rect.left - this.containerRect.left + "px";
input.style.top = rect.top - this.containerRect.top + "px";
input.style.width = rect.width + "px";
input.style.height = rect.height + "px";
input.style.border = "none";
input.style.background = "none";
input.style.opacity = 0;
if (this.debug) {
input.style.border = "1px solid #00FF00";
input.style.opacity = 1;
}
input.style.cursor = "alias";
input.addEventListener("mouseup", (e) => this.endCable(e));
input.addEventListener("mousedown", (e) => {
e.preventDefault();
this.startCable(e);
return false;
});
this.container.appendChild(input);
this.model.sockets.push(socket.id);
}
let group = this.svg.querySelector("#sockets");
if (group) group.style.display = "none";
}
createCables() {
let cables = document.createElementNS("http://www.w3.org/2000/svg", "svg");
cables.classList.add("cables");
cables.setAttribute("xmlns", "http://www.w3.org/2000/svg");
cables.setAttribute("width", this.width);
cables.setAttribute("height", this.height);
cables.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);
cables.setAttribute("fill", "none");
cables.style.pointerEvents = "none";
cables.style.position = "absolute";
cables.style.left = 0;
cables.style.top = 0;
cables.style.width = this.width;
cables.style.height = this.height;
this.cables = cables;
this.container.appendChild(this.cables);
}
startCable(event) {
let position = this.getMousePosition(event);
let socket = event.target;
if (socket && socket.classList.contains("socket")) {
this.cable = new Cable(socket.getAttribute("name"));
this.cable.createCable(position);
let cable = this.cable.path;
cable.style.pointerEvents = "stroke";
cable.addEventListener("click", (e) => {
console.log(e);
cable.remove();
});
this.cables.appendChild(this.cable.path);
for (const input of this.container.querySelectorAll(".input-knob")) {
input.style.pointerEvents = "none";
}
}
}
drawCable(event) {
if (this.cable) {
this.cable.appendToBuffer(this.getMousePosition(event));
this.cable.updatePath();
}
}
endCable(event) {
if (this.cable) {
let socket = event.target;
if (socket && socket.classList.contains("socket")) {
this.cable.end = socket.getAttribute("name");
// TODO: log cable
} else {
this.cable.path.remove();
}
for (const input of this.container.querySelectorAll(".input-knob")) {
input.style.pointerEvents = "all";
}
this.cable = null;
}
}
getMousePosition(e) {
return {
x: e.pageX - this.containerRect.left,
y: e.pageY - this.containerRect.top,
};
}
}