sync multiple splines in speed mode

This commit is contained in:
kijai 2025-06-09 18:31:38 +03:00
parent ed8294d7fb
commit aeab1a7de5

View File

@ -1222,8 +1222,8 @@ this.lastMousePosition = { x: this.width/2, y: this.height/2 };
// Sort positions along path // Sort positions along path
pathPositions.sort((a, b) => a - b); pathPositions.sort((a, b) => a - b);
// Create a smooth speed mapping function using cubic spline interpolation // Create a smooth speed mapping function with synchronization
const createSmoothMapping = () => { const createSynchronizedMapping = () => {
// Calculate segment lengths and densities // Calculate segment lengths and densities
const segments = []; const segments = [];
let totalLength = pathPositions[pathPositions.length - 1] - pathPositions[0]; let totalLength = pathPositions[pathPositions.length - 1] - pathPositions[0];
@ -1239,72 +1239,63 @@ this.lastMousePosition = { x: this.width/2, y: this.height/2 };
}); });
} }
// Create cubic spline interpolation of densities // Create mapping function with forced synchronization at endpoints
const positions = segments.map(seg => seg.position / totalLength); return t => {
const densities = segments.map(seg => seg.density); // Force synchronization at t=0 and t=1
if (t === 0) return 0;
if (t === 1) return pathLength;
// Add control points at beginning and end with gradual transition // For intermediate points, use the speed control
positions.unshift(0); // Scale t to fit between first and last control points
positions.push(1); const firstPos = pathPositions[0];
densities.unshift(densities[0]); const lastPos = pathPositions[pathPositions.length - 1];
densities.push(densities[densities.length - 1]);
// Smooth the density curve by applying a moving average // Create a density-weighted position mapping
const smoothedDensities = []; let totalWeight = 0;
const windowSize = 3; let weights = [];
for (let i = 0; i < densities.length; i++) { for (let i = 0; i < segments.length; i++) {
let sum = 0; totalWeight += segments[i].density;
let count = 0; weights.push(segments[i].density);
for (let j = Math.max(0, i - windowSize); j <= Math.min(densities.length - 1, i + windowSize); j++) {
// Apply a triangular window weight based on distance
const weight = 1 - Math.abs(i - j) / (windowSize + 1);
sum += densities[j] * weight;
count += weight;
} }
smoothedDensities.push(sum / count); // Normalize weights
} const normalizedWeights = weights.map(w => w / totalWeight);
// Calculate cumulative density function // Calculate cumulative weights
let totalDensity = 0; let cumulativeWeight = 0;
const cumulativeDensities = smoothedDensities.map(density => { const cumulativeWeights = normalizedWeights.map(w => {
totalDensity += density; cumulativeWeight += w;
return totalDensity; return cumulativeWeight;
}); });
// Normalize to [0,1] range // Find the segment for this t value
const normalizedCumulative = cumulativeDensities.map(cd => cd / totalDensity); let segmentIndex = 0;
for (let i = 0; i < cumulativeWeights.length; i++) {
// Create mapping function if (t <= cumulativeWeights[i]) {
return t => { segmentIndex = i;
// Find the segment containing t using binary search break;
let low = 0;
let high = normalizedCumulative.length - 1;
while (low < high) {
const mid = Math.floor((low + high) / 2);
if (normalizedCumulative[mid] < t) {
low = mid + 1;
} else {
high = mid;
} }
} }
// Interpolate within the segment // Calculate position within segment
const i = Math.max(0, low - 1); const segmentStart = segmentIndex > 0 ? cumulativeWeights[segmentIndex - 1] : 0;
const segT = (t - (i > 0 ? normalizedCumulative[i] : 0)) / const segmentEnd = cumulativeWeights[segmentIndex];
(normalizedCumulative[i+1] - (i > 0 ? normalizedCumulative[i] : 0)); const segmentT = (t - segmentStart) / (segmentEnd - segmentStart);
const pos = positions[i] + segT * (positions[i+1] - positions[i]); // Map to path position
return pos * totalLength + pathPositions[0]; const pathStart = pathPositions[segmentIndex];
const pathEnd = pathPositions[segmentIndex + 1];
const pos = pathStart + segmentT * (pathEnd - pathStart);
// Scale to fill entire path
return pos;
}; };
}; };
const mapToPath = createSmoothMapping(); const mapToPath = createSynchronizedMapping();
// Sample using the smooth mapping function // Sample using the synchronized mapping function
for (let i = 0; i < numSamples; i++) { for (let i = 0; i < numSamples; i++) {
const t = i / (numSamples - 1); const t = i / (numSamples - 1);
const pathPos = mapToPath(t); const pathPos = mapToPath(t);
@ -1313,6 +1304,7 @@ this.lastMousePosition = { x: this.width/2, y: this.height/2 };
} }
return points; return points;
} }
else{ else{
for (var i = 0; i < numSamples; i++) { for (var i = 0; i < numSamples; i++) {