|
|
|
@ -61,7 +61,10 @@ class KnotSimulation {
|
|
|
|
|
this.ghostPath = []
|
|
|
|
|
}
|
|
|
|
|
if (buttons === 2) {
|
|
|
|
|
const [i] = Vec3.findNearest3d(this.particleSimulation.positions, [...this.mousePosition, 0])
|
|
|
|
|
const [i] = Vec2.findNearest(
|
|
|
|
|
this.particleSimulation.positions.map(([x, y]) => [x, y]),
|
|
|
|
|
this.mousePosition
|
|
|
|
|
)
|
|
|
|
|
this.draggingIndex = i
|
|
|
|
|
}
|
|
|
|
|
if (buttons === 4) {
|
|
|
|
@ -147,35 +150,62 @@ class KnotSimulation {
|
|
|
|
|
const next = positions.at((i + 1) % N)
|
|
|
|
|
|
|
|
|
|
const baseForce = Vec3.scale(Vec3.add(Vec3.sub(prev, curr), Vec3.sub(next, curr)), 0.5)
|
|
|
|
|
const jointFactor = 0.5
|
|
|
|
|
const jointFactor = 0.1
|
|
|
|
|
|
|
|
|
|
Vec3.Mutate.add(positions.at((i - 1) % N), Vec3.scale(baseForce, -jointFactor / 2))
|
|
|
|
|
Vec3.Mutate.add(positions[i], Vec3.scale(baseForce, jointFactor))
|
|
|
|
|
Vec3.Mutate.add(positions.at((i + 1) % N), Vec3.scale(baseForce, -jointFactor / 2))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// repulsione per ogni coppia
|
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
|
|
|
for (let j = 0; j < N; j++) {
|
|
|
|
|
if (
|
|
|
|
|
i < j &&
|
|
|
|
|
Math.abs(i - j) > PASSTHROUGH_MIN_INDICES &&
|
|
|
|
|
Math.abs(i - j + N) > PASSTHROUGH_MIN_INDICES &&
|
|
|
|
|
Math.abs(i - j - N) > PASSTHROUGH_MIN_INDICES
|
|
|
|
|
) {
|
|
|
|
|
const p1 = positions[i]
|
|
|
|
|
const p2 = positions[j]
|
|
|
|
|
|
|
|
|
|
const dist = Vec3.distance2(p1, p2)
|
|
|
|
|
if (dist <= NODE_BALL_RADIUS) {
|
|
|
|
|
const [newPt1, newPt2] = enforceDistance(p1, p2, NODE_BALL_RADIUS)
|
|
|
|
|
positions[i] = newPt1
|
|
|
|
|
positions[j] = newPt2
|
|
|
|
|
// the error of the next constraint will be at most of 90%
|
|
|
|
|
let globalNodeMinDistance = 0
|
|
|
|
|
let iter = 0
|
|
|
|
|
while (globalNodeMinDistance < NODE_BALL_RADIUS * 0.99 && iter++ < 100) {
|
|
|
|
|
// repulsione per ogni coppia
|
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
|
|
|
for (let j = i + 1; j < N; j++) {
|
|
|
|
|
if (
|
|
|
|
|
Math.abs(i - j) > PASSTHROUGH_MIN_INDICES &&
|
|
|
|
|
Math.abs(i - j + N) > PASSTHROUGH_MIN_INDICES &&
|
|
|
|
|
Math.abs(i - j - N) > PASSTHROUGH_MIN_INDICES
|
|
|
|
|
) {
|
|
|
|
|
const p1 = positions[i]
|
|
|
|
|
const p2 = positions[j]
|
|
|
|
|
|
|
|
|
|
const dist = Vec3.distance2(p1, p2)
|
|
|
|
|
if (dist <= NODE_BALL_RADIUS) {
|
|
|
|
|
const [newPt1, newPt2] = enforceDistance(p1, p2, NODE_BALL_RADIUS)
|
|
|
|
|
positions[i] = newPt1
|
|
|
|
|
positions[j] = newPt2
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// estimating global previous constraint error
|
|
|
|
|
globalNodeMinDistance = +Infinity
|
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
|
|
|
for (let j = i + 1; j < N; j++) {
|
|
|
|
|
if (
|
|
|
|
|
Math.abs(i - j) > PASSTHROUGH_MIN_INDICES &&
|
|
|
|
|
Math.abs(i - j + N) > PASSTHROUGH_MIN_INDICES &&
|
|
|
|
|
Math.abs(i - j - N) > PASSTHROUGH_MIN_INDICES
|
|
|
|
|
) {
|
|
|
|
|
const p1 = positions[i]
|
|
|
|
|
const p2 = positions[j]
|
|
|
|
|
|
|
|
|
|
const dist = Vec3.distance2(p1, p2)
|
|
|
|
|
|
|
|
|
|
if (dist < globalNodeMinDistance) {
|
|
|
|
|
globalNodeMinDistance = dist
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(`Reached solution in ${iter} iterations with global min distance ${globalNodeMinDistance}`)
|
|
|
|
|
|
|
|
|
|
this.particleSimulation.update(newAccelerations)
|
|
|
|
|
|
|
|
|
|
// attrito viscoso
|
|
|
|
|