Initial mostly working multiple splines for SplineEditor

Probably buggy and clumsy to use, but workable and way better than having to use multiple SplineEditors...
This commit is contained in:
kijai 2025-06-03 20:45:08 +03:00
parent 42c7641776
commit ea5482a6ee
2 changed files with 454 additions and 194 deletions

View File

@ -200,52 +200,77 @@ output types:
"""
def splinedata(self, mask_width, mask_height, coordinates, float_output_type, interpolation,
points_to_sample, sampling_method, points_store, tension, repeat_output,
min_value=0.0, max_value=1.0, bg_image=None):
points_to_sample, sampling_method, points_store, tension, repeat_output,
min_value=0.0, max_value=1.0, bg_image=None):
coordinates = json.loads(coordinates)
normalized = []
normalized_y_values = []
for coord in coordinates:
coord['x'] = int(round(coord['x']))
coord['y'] = int(round(coord['y']))
norm_x = (1.0 - (coord['x'] / mask_height) - 0.0) * (max_value - min_value) + min_value
norm_y = (1.0 - (coord['y'] / mask_height) - 0.0) * (max_value - min_value) + min_value
normalized_y_values.append(norm_y)
normalized.append({'x':norm_x, 'y':norm_y})
print("Coordinates: ", coordinates)
# Handle nested list structure if present
all_normalized = []
all_normalized_y_values = []
# Check if we have a nested list structure
if isinstance(coordinates, list) and len(coordinates) > 0 and isinstance(coordinates[0], list):
# Process each list of coordinates in the nested structure
coordinate_sets = coordinates
else:
# If not nested, treat as a single list of coordinates
coordinate_sets = [coordinates]
# Process each set of coordinates
for coord_set in coordinate_sets:
normalized = []
normalized_y_values = []
for coord in coord_set:
coord['x'] = int(round(coord['x']))
coord['y'] = int(round(coord['y']))
norm_x = (1.0 - (coord['x'] / mask_height) - 0.0) * (max_value - min_value) + min_value
norm_y = (1.0 - (coord['y'] / mask_height) - 0.0) * (max_value - min_value) + min_value
normalized_y_values.append(norm_y)
normalized.append({'x':norm_x, 'y':norm_y})
all_normalized.extend(normalized)
all_normalized_y_values.extend(normalized_y_values)
# Use the combined normalized values for output
if float_output_type == 'list':
out_floats = normalized_y_values * repeat_output
out_floats = all_normalized_y_values * repeat_output
elif float_output_type == 'pandas series':
try:
import pandas as pd
except:
raise Exception("MaskOrImageToWeight: pandas is not installed. Please install pandas to use this output_type")
out_floats = pd.Series(normalized_y_values * repeat_output),
out_floats = pd.Series(all_normalized_y_values * repeat_output),
elif float_output_type == 'tensor':
out_floats = torch.tensor(normalized_y_values * repeat_output, dtype=torch.float32)
out_floats = torch.tensor(all_normalized_y_values * repeat_output, dtype=torch.float32)
# Create a color map for grayscale intensities
color_map = lambda y: torch.full((mask_height, mask_width, 3), y, dtype=torch.float32)
# Create image tensors for each normalized y value
mask_tensors = [color_map(y) for y in normalized_y_values]
mask_tensors = [color_map(y) for y in all_normalized_y_values]
masks_out = torch.stack(mask_tensors)
masks_out = masks_out.repeat(repeat_output, 1, 1, 1)
masks_out = masks_out.mean(dim=-1)
if bg_image is None:
return (masks_out, json.dumps(coordinates), out_floats, len(out_floats) , json.dumps(normalized))
return (masks_out, json.dumps(coordinates), out_floats, len(out_floats), json.dumps(all_normalized))
else:
transform = transforms.ToPILImage()
image = transform(bg_image[0].permute(2, 0, 1))
buffered = io.BytesIO()
image.save(buffered, format="JPEG", quality=75)
# Step 3: Encode the image bytes to a Base64 string
# Encode the image bytes to a Base64 string
img_bytes = buffered.getvalue()
img_base64 = base64.b64encode(img_bytes).decode('utf-8')
return {
return {
"ui": {"bg_image": [img_base64]},
"result":(masks_out, json.dumps(coordinates), out_floats, len(out_floats) , json.dumps(normalized))
}
"result": (masks_out, json.dumps(coordinates), out_floats, len(out_floats), json.dumps(all_normalized))
}
class CreateShapeMaskOnPath:

View File

@ -155,6 +155,9 @@ app.registerExtension({
createMenuItem(3, "Background image"),
createMenuItem(4, "Invert point order"),
createMenuItem(5, "Clear Image"),
createMenuItem(6, "Add new spline"),
createMenuItem(7, "Delete current spline"),
createMenuItem(8, "Next spline"),
];
// Add mouseover and mouseout event listeners to each menu item for styling
@ -175,7 +178,7 @@ app.registerExtension({
document.body.appendChild(this.contextMenu);
this.addWidget("button", "New spline", null, () => {
this.addWidget("button", "New canvas", null, () => {
if (!this.properties || !("points" in this.properties)) {
this.editor = new SplineEditor(this);
this.addProperty("points", this.constructor.type, "string");
@ -341,29 +344,32 @@ this.heightWidget.callback = () => {
this.width = this.widthWidget.value;
this.height = this.heightWidget.value;
var i = 3;
this.points = [];
this.splines = [];
this.activeSplineIndex = 0; // Track which spline is being edited
if (!reset && this.pointsStoreWidget.value != "") {
this.points = JSON.parse(this.pointsStoreWidget.value);
} else {
this.points = pv.range(1, 4).map((i, index) => {
if (index === 0) {
// First point at the bottom-left corner
return { x: 0, y: this.height };
} else if (index === 2) {
// Last point at the top-right corner
return { x: this.width, y: 0 };
try {
const parsedData = JSON.parse(this.pointsStoreWidget.value);
// Check if it's already in the new format (array of splines)
if (Array.isArray(parsedData) && parsedData.length > 0 && parsedData[0].hasOwnProperty('points')) {
this.splines = parsedData;
} else {
// Other points remain as they were
return {
x: i * this.width / 5,
y: 50 + Math.random() * (this.height - 100)
};
// Convert old format (single array of points) to new format
this.splines = [{
points: parsedData,
color: "#1f77b4",
name: "Spline 1"
}];
}
});
this.pointsStoreWidget.value = JSON.stringify(this.points);
}
} catch (e) {
console.error("Error parsing spline data:", e);
this.initializeDefaultSplines();
}
} else {
this.initializeDefaultSplines();
this.pointsStoreWidget.value = JSON.stringify(this.splines);
}
this.vis = new pv.Panel()
.width(this.width)
.height(this.height)
@ -378,7 +384,7 @@ this.heightWidget.callback = () => {
x: this.mouse().x / app.canvas.ds.scale,
y: this.mouse().y / app.canvas.ds.scale
};
i = self.points.push(scaledMouse) - 1;
i = self.splines[self.activeSplineIndex].points.push(scaledMouse) - 1;
self.updatePath();
return this;
}
@ -390,16 +396,17 @@ this.heightWidget.callback = () => {
};
// Find the two closest points to the clicked location
let { point1Index, point2Index } = self.findClosestPoints(self.points, clickedPoint);
const activePoints = self.splines[self.activeSplineIndex].points;
let { point1Index, point2Index } = self.findClosestPoints(self.splines[self.activeSplineIndex].points, clickedPoint);
// Calculate the midpoint between the two closest points
let midpoint = {
x: (self.points[point1Index].x + self.points[point2Index].x) / 2,
y: (self.points[point1Index].y + self.points[point2Index].y) / 2
x: (activePoints[point1Index].x + activePoints[point2Index].x) / 2,
y: (activePoints[point1Index].y + activePoints[point2Index].y) / 2
};
// Insert the midpoint into the array
self.points.splice(point2Index, 0, midpoint);
activePoints.splice(point2Index, 0, midpoint);
i = point2Index;
self.updatePath();
}
@ -418,55 +425,73 @@ this.heightWidget.callback = () => {
.lineWidth(3)
.visible(() => self.drawRuler)
// vis.add(pv.Rule)
// .data(pv.range(0, points_to_sample, 1))
// .left(d => d * 512 / (points_to_sample - 1))
// .strokeStyle("gray")
// .lineWidth(2)
this.hoverSplineIndex = -1;
this.vis.add(pv.Line)
.data(() => this.points)
.left(d => d.x)
.top(d => d.y)
.interpolate(() => this.interpolation)
.tension(() => this.tension)
.segmented(() => false)
.strokeStyle(pv.Colors.category10().by(pv.index))
.lineWidth(3)
this.splines.forEach((spline, splineIndex) => {
this.vis.add(pv.Line)
.data(() => spline.points)
.left(d => d.x)
.top(d => d.y)
.interpolate(() => this.interpolation)
.tension(() => this.tension)
.segmented(() => false)
.strokeStyle(spline.color)
.lineWidth(() => {
// Change line width based on active or hover state
if (splineIndex === this.activeSplineIndex) return 3;
if (splineIndex === this.hoverSplineIndex) return 2;
return 1.5;
})
.event("mouseover", () => {
this.hoverSplineIndex = splineIndex;
this.vis.render();
})
.event("mouseout", () => {
this.hoverSplineIndex = -1;
this.vis.render();
})
.event("mousedown", () => {
if (this.activeSplineIndex !== splineIndex) {
this.activeSplineIndex = splineIndex;
this.refreshSplineElements();
}
});
});
this.vis.add(pv.Dot)
.data(() => this.points)
.left(d => d.x)
.top(d => d.y)
.radius(10)
.shape(function() {
return self.dotShape;
})
.angle(function() {
const index = this.index;
let angle = 0;
.data(() => this.splines[this.activeSplineIndex].points)
.left(d => d.x)
.top(d => d.y)
.radius(10)
.shape(function() {
return self.dotShape;
})
.angle(function() {
const index = this.index;
let angle = 0;
if (self.dotShape === "triangle") {
let dxNext = 0, dyNext = 0;
if (index < self.points.length - 1) {
dxNext = self.points[index + 1].x - self.points[index].x;
dyNext = self.points[index + 1].y - self.points[index].y;
if (self.dotShape === "triangle") {
const activePoints = self.splines[self.activeSplineIndex].points;
let dxNext = 0, dyNext = 0;
if (index < activePoints.length - 1) {
dxNext = activePoints[index + 1].x - activePoints[index].x;
dyNext = activePoints[index + 1].y - activePoints[index].y;
}
let dxPrev = 0, dyPrev = 0;
if (index > 0) {
dxPrev = activePoints[index].x - activePoints[index - 1].x;
dyPrev = activePoints[index].y - activePoints[index - 1].y;
}
const dx = (dxNext + dxPrev) / 2;
const dy = (dyNext + dyPrev) / 2;
angle = Math.atan2(dy, dx);
angle -= Math.PI / 2;
angle = (angle + 2 * Math.PI) % (2 * Math.PI);
}
let dxPrev = 0, dyPrev = 0;
if (index > 0) {
dxPrev = self.points[index].x - self.points[index - 1].x;
dyPrev = self.points[index].y - self.points[index - 1].y;
}
const dx = (dxNext + dxPrev) / 2;
const dy = (dyNext + dyPrev) / 2;
angle = Math.atan2(dy, dx);
angle -= Math.PI / 2;
angle = (angle + 2 * Math.PI) % (2 * Math.PI);
}
return angle;
})
.cursor("move")
@ -477,10 +502,11 @@ this.heightWidget.callback = () => {
i = this.index;
hoverIndex = this.index;
isDragging = true;
if (pv.event.button === 2 && i !== 0 && i !== self.points.length - 1) {
self.points.splice(i--, 1);
self.vis.render();
}
const activePoints = self.splines[self.activeSplineIndex].points;
if (pv.event.button === 2 && i !== 0 && i !== activePoints.length - 1) {
activePoints.splice(i--, 1);
self.vis.render();
}
return this;
})
.event("dragend", function() {
@ -499,7 +525,7 @@ this.heightWidget.callback = () => {
// Adjust the new position if it would place the dot outside the bounds of the vis.Panel
adjustedX = Math.max(0, Math.min(panelWidth, adjustedX));
adjustedY = Math.max(0, Math.min(panelHeight, adjustedY));
self.points[this.index] = { x: adjustedX, y: adjustedY }; // Update the point's position
self.splines[self.activeSplineIndex].points[this.index] = { x: adjustedX, y: adjustedY }; // Update the point's position
self.vis.render(); // Re-render the visualization to reflect the new position
})
.event("mouseover", function() {
@ -530,64 +556,92 @@ this.heightWidget.callback = () => {
})
.textStyle("orange")
if (this.points.length != 0) {
this.vis.render();
}
var svgElement = this.vis.canvas();
svgElement.style['zIndex'] = "2"
svgElement.style['position'] = "relative"
this.node.splineEditor.element.appendChild(svgElement);
this.pathElements = svgElement.getElementsByTagName('path'); // Get all path elements
if (this.width > 256) {
this.node.setSize([this.width + 45, this.node.size[1]]);
}
this.node.setSize([this.node.size[0], this.height + 430]);
this.updatePath();
this.refreshBackgroundImage();
}
updatePath = () => {
if (!this.points || this.points.length === 0) {
console.log("no points");
return;
}
if (this.samplingMethod != "controlpoints") {
var coords = this.samplePoints(this.pathElements[0], this.points_to_sample, this.samplingMethod, this.width);
}
else {
var coords = this.points
}
if (this.drawSamplePoints) {
if (this.pointsLayer) {
// Update the data of the existing points layer
this.pointsLayer.data(coords);
} else {
// Create the points layer if it doesn't exist
this.pointsLayer = this.vis.add(pv.Dot)
.data(coords)
.left(function(d) { return d.x; })
.top(function(d) { return d.y; })
.radius(5) // Adjust the radius as needed
.fillStyle("red") // Change the color as needed
.strokeStyle("black") // Change the stroke color as needed
.lineWidth(1); // Adjust the line width as needed
}
} else {
if (this.pointsLayer) {
// Remove the points layer
this.pointsLayer.data([]);
this.vis.render();
}
}
let coordsString = JSON.stringify(coords);
this.pointsStoreWidget.value = JSON.stringify(this.points);
if (this.coordWidget) {
this.coordWidget.value = coordsString;
if (this.splines.length != 0) {
this.vis.render();
}
this.vis.render();
};
var svgElement = this.vis.canvas();
svgElement.style['zIndex'] = "2"
svgElement.style['position'] = "relative"
this.node.splineEditor.element.appendChild(svgElement);
this.pathElements = svgElement.getElementsByTagName('path'); // Get all path elements
if (this.width > 256) {
this.node.setSize([this.width + 45, this.node.size[1]]);
}
this.node.setSize([this.node.size[0], this.height + 430]);
this.updatePath();
this.refreshBackgroundImage();
}
updatePath = () => {
if (!this.splines || this.splines.length === 0) {
console.log("no splines");
return;
}
// Get active spline points
console.log("this.activeSplineIndex", this.activeSplineIndex);
const activeSpline = this.splines[this.activeSplineIndex];
const activePoints = activeSpline.points;
if (!activePoints || activePoints.length === 0) {
console.log("no points in active spline");
return;
}
console.log("this.pathElements", this.pathElements);
let coords;
if (this.samplingMethod != "controlpoints") {
coords = this.samplePoints(this.pathElements[this.activeSplineIndex], this.points_to_sample, this.samplingMethod, this.width, this.activeSplineIndex);
} else {
coords = activePoints;
}
let allSplineCoords = [];
for (let i = 0; i < this.splines.length; i++) {
// Use the same sampling method for all splines
let splineCoords;
const pathElement = this.pathElements[i];
if (this.samplingMethod != "controlpoints" && pathElement) {
splineCoords = this.samplePoints(pathElement, this.points_to_sample, this.samplingMethod, this.width, i);
} else {
// Fall back to control points if no path element or sampling method is "controlpoints"
splineCoords = this.splines[i].points;
}
allSplineCoords.push(splineCoords);
}
if (this.drawSamplePoints) {
if (this.pointsLayer) {
// Update the data of the existing points layer
this.pointsLayer.data(coords);
} else {
// Create the points layer if it doesn't exist
this.pointsLayer = this.vis.add(pv.Dot)
.data(coords)
.left(function(d) { return d.x; })
.top(function(d) { return d.y; })
.radius(5) // Adjust the radius as needed
.fillStyle("red") // Change the color as needed
.strokeStyle("black") // Change the stroke color as needed
.lineWidth(1); // Adjust the line width as needed
}
} else {
if (this.pointsLayer) {
// Remove the points layer
this.pointsLayer.data([]);
this.vis.render();
}
}
this.pointsStoreWidget.value = JSON.stringify(this.splines);
if (this.coordWidget) {
this.coordWidget.value = JSON.stringify(allSplineCoords);
}
this.vis.render();
};
handleImageLoad = (img, file, base64String) => {
console.log(img.width, img.height); // Access width and height here
this.widthWidget.value = img.width;
@ -673,11 +727,143 @@ this.heightWidget.callback = () => {
}
};
refreshSplineElements = () => {
// Clear existing line elements and recreate them
const svgElement = this.vis.canvas();
// Remove all existing line elements
const oldLines = svgElement.querySelectorAll('path');
oldLines.forEach(line => line.remove());
this.pathElements = [];
this.lineObjects = [];
const originalChildren = [...this.vis.children];
// Find line objects to remove (those that represent splines)
const linesToRemove = originalChildren.filter(child =>
child instanceof pv.Line
);
linesToRemove.forEach(line => line.visible(false));
// Re-add all spline lines and store references to them
this.splines.forEach((spline, splineIndex) => {
const lineObj = this.vis.add(pv.Line)
.data(() => spline.points)
.left(d => d.x)
.top(d => d.y)
.interpolate(() => this.interpolation)
.tension(() => this.tension)
.segmented(() => false)
.strokeStyle(spline.color)
.lineWidth(() => {
// Change line width based on active or hover state
if (splineIndex === this.activeSplineIndex) return 3;
if (splineIndex === this.hoverSplineIndex) return 2;
return 1.5;
})
.event("mouseover", () => {
this.hoverSplineIndex = splineIndex;
this.vis.render();
})
.event("mouseout", () => {
this.hoverSplineIndex = -1;
this.vis.render();
})
.event("mousedown", () => {
if (this.activeSplineIndex !== splineIndex) {
this.activeSplineIndex = splineIndex;
this.refreshSplineElements();
}
});
this.lineObjects.push(lineObj);
});
this.vis.render();
requestAnimationFrame(() => {
const allPaths = Array.from(svgElement.querySelectorAll('path'));
this.pathElements = [];
// First try: look at paths with specific childIndex values
this.lineObjects.forEach((lineObj, i) => {
// Find paths that correspond to our line objects
const childIndex = lineObj.childIndex;
const matchingPath = allPaths.find(path =>
path.$scene && path.$scene.scenes &&
path.$scene.scenes.childIndex === childIndex
);
if (matchingPath) {
console.log("matchingPath:", matchingPath);
this.pathElements[i] = matchingPath;
}
});
// Check if we found all paths
if (this.pathElements.filter(p => p).length !== this.splines.length) {
// Fallback to color matching
this.pathElements = [];
for (let i = 0; i < this.splines.length; i++) {
const color = this.splines[i].color;
const matchingPath = allPaths.find(p =>
p.getAttribute('style')?.includes(color) &&
!this.pathElements.includes(p)
);
if (matchingPath) {
this.pathElements[i] = matchingPath;
}
}
}
// If we still don't have the right number of paths, use the first N paths
if (this.pathElements.filter(p => p).length !== this.splines.length) {
this.pathElements = allPaths.slice(0, this.splines.length);
}
this.updatePath();
});
};
initializeDefaultSplines() {
this.splines = [{
points: pv.range(1, 4).map((i, index) => {
if (index === 0) {
return { x: 0, y: this.height };
} else if (index === 2) {
return { x: this.width, y: 0 };
} else {
return {
x: i * this.width / 5,
y: 50 + Math.random() * (this.height - 100)
};
}
}),
color: this.getSplineColor(0),
name: "Spline 1"
}];
}
getSplineColor(index) {
const colors = [
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
"#bcbd22", "#17becf"
];
return colors[index % colors.length];
}
createContextMenu = () => {
self = this;
const self = this;
const oldMenu = this.node.contextMenu;
const newMenu = oldMenu.cloneNode(true);
oldMenu.parentNode.replaceChild(newMenu, oldMenu);
this.node.contextMenu = newMenu;
document.addEventListener('contextmenu', function (e) {
e.preventDefault();
});
document.addEventListener('click', function (e) {
@ -686,17 +872,18 @@ this.heightWidget.callback = () => {
});
});
this.node.menuItems.forEach((menuItem, index) => {
self = this;
menuItem.addEventListener('click', function (e) {
e.preventDefault();
switch (index) {
this.node.contextMenu.addEventListener('click', function(e) {
e.preventDefault();
if (e.target.tagName === 'A') {
const id = parseInt(e.target.id.split('-')[2]);
switch(id) {
case 0:
e.preventDefault();
if (!self.drawHandles) {
self.drawHandles = true
self.vis.add(pv.Line)
.data(() => self.points.map((point, index) => ({
.data(() => self.splines[self.activeSplineIndex].points.map((point, index) => ({
start: point,
end: [index]
})))
@ -715,12 +902,11 @@ this.heightWidget.callback = () => {
self.node.contextMenu.style.display = 'none';
break;
case 1:
e.preventDefault();
self.drawSamplePoints = !self.drawSamplePoints;
self.updatePath();
break;
case 2:
e.preventDefault();
if (self.dotShape == "circle"){
self.dotShape = "triangle"
}
@ -753,8 +939,7 @@ this.heightWidget.callback = () => {
self.node.contextMenu.style.display = 'none';
break;
case 4:
e.preventDefault();
self.points.reverse();
self.splines[self.activeSplineIndex].points.reverse();
self.updatePath();
break;
case 5:
@ -762,34 +947,93 @@ this.heightWidget.callback = () => {
self.node.properties.imgData = null;
self.node.contextMenu.style.display = 'none';
break;
case 6: // Add new spline
const newSplineIndex = self.splines.length;
self.splines.push({
points: [
// Create default points for the new spline
{ x: 0, y: self.height },
{ x: self.width/2, y: self.height/2 },
{ x: self.width, y: 0 }
],
color: self.getSplineColor(newSplineIndex),
name: `Spline ${newSplineIndex + 1}`
});
self.activeSplineIndex = newSplineIndex;
self.refreshSplineElements();
self.node.contextMenu.style.display = 'none';
break;
case 7: // Delete current spline
if (self.splines.length > 1) {
self.splines.splice(self.activeSplineIndex, 1);
self.activeSplineIndex = Math.min(self.activeSplineIndex, self.splines.length - 1);
self.refreshSplineElements();
}
self.node.contextMenu.style.display = 'none';
break;
case 8: // Next spline
self.activeSplineIndex = (self.activeSplineIndex + 1) % self.splines.length;
self.refreshSplineElements();
self.node.contextMenu.style.display = 'none';
break;
}
});
}
});
}
samplePoints(svgPathElement, numSamples, samplingMethod, width) {
var svgWidth = width; // Fixed width of the SVG element
var pathLength = svgPathElement.getTotalLength();
var points = [];
samplePoints(svgPathElement, numSamples, samplingMethod, width, splineIndex) {
if (!svgPathElement) {
console.warn(`Path element not found for spline index: ${splineIndex}. Available paths: ${this.pathElements.length}`);
for (var i = 0; i < numSamples; i++) {
if (samplingMethod === "time") {
// Calculate the x-coordinate for the current sample based on the SVG's width
var x = (svgWidth / (numSamples - 1)) * i;
// Find the point on the path that intersects the vertical line at the calculated x-coordinate
var point = this.findPointAtX(svgPathElement, x, pathLength);
}
else if (samplingMethod === "path") {
// 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);
const splinePoints = this.splines[splineIndex].points;
// If we have no points, return an empty array
if (!splinePoints || splinePoints.length === 0) {
return [];
}
// Create a simple interpolation between control points
const result = [];
for (let i = 0; i < numSamples; i++) {
const t = i / (numSamples - 1);
const idx = Math.min(
Math.floor(t * (splinePoints.length - 1)),
splinePoints.length - 2
);
const fraction = (t * (splinePoints.length - 1)) - idx;
const x = splinePoints[idx].x + fraction * (splinePoints[idx + 1].x - splinePoints[idx].x);
const y = splinePoints[idx].y + fraction * (splinePoints[idx + 1].y - splinePoints[idx].y);
result.push({ x, y });
}
return result;
}
var svgWidth = width; // Fixed width of the SVG element
var pathLength = svgPathElement.getTotalLength();
console.log(" pathLength:", pathLength);
var points = [];
// Add the point to the array of points
points.push({ x: point.x, y: point.y });
}
return points;
for (var i = 0; i < numSamples; i++) {
if (samplingMethod === "time") {
// Calculate the x-coordinate for the current sample based on the SVG's width
var x = (svgWidth / (numSamples - 1)) * i;
// Find the point on the path that intersects the vertical line at the calculated x-coordinate
var point = this.findPointAtX(svgPathElement, x, pathLength);
}
else if (samplingMethod === "path") {
// 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 });
}
return points;
}
findClosestPoints(points, clickedPoint) {
@ -846,16 +1090,7 @@ export function hideWidgetForGood(node, widget, suffix = '') {
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)