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;
// Add control points at beginning and end with gradual transition if (t === 1) return pathLength;
positions.unshift(0);
positions.push(1);
densities.unshift(densities[0]);
densities.push(densities[densities.length - 1]);
// Smooth the density curve by applying a moving average
const smoothedDensities = [];
const windowSize = 3;
for (let i = 0; i < densities.length; i++) {
let sum = 0;
let count = 0;
for (let j = Math.max(0, i - windowSize); j <= Math.min(densities.length - 1, i + windowSize); j++) { // For intermediate points, use the speed control
// Apply a triangular window weight based on distance // Scale t to fit between first and last control points
const weight = 1 - Math.abs(i - j) / (windowSize + 1); const firstPos = pathPositions[0];
sum += densities[j] * weight; const lastPos = pathPositions[pathPositions.length - 1];
count += weight;
// Create a density-weighted position mapping
let totalWeight = 0;
let weights = [];
for (let i = 0; i < segments.length; i++) {
totalWeight += segments[i].density;
weights.push(segments[i].density);
} }
smoothedDensities.push(sum / count); // Normalize weights
} const normalizedWeights = weights.map(w => w / totalWeight);
// Calculate cumulative density function
let totalDensity = 0;
const cumulativeDensities = smoothedDensities.map(density => {
totalDensity += density;
return totalDensity;
});
// Normalize to [0,1] range
const normalizedCumulative = cumulativeDensities.map(cd => cd / totalDensity);
// Create mapping function
return t => {
// Find the segment containing t using binary search
let low = 0;
let high = normalizedCumulative.length - 1;
while (low < high) { // Calculate cumulative weights
const mid = Math.floor((low + high) / 2); let cumulativeWeight = 0;
if (normalizedCumulative[mid] < t) { const cumulativeWeights = normalizedWeights.map(w => {
low = mid + 1; cumulativeWeight += w;
} else { return cumulativeWeight;
high = mid; });
// Find the segment for this t value
let segmentIndex = 0;
for (let i = 0; i < cumulativeWeights.length; i++) {
if (t <= cumulativeWeights[i]) {
segmentIndex = i;
break;
} }
} }
// 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++) {