-
Notifications
You must be signed in to change notification settings - Fork 31
/
collisionthrow.ts
73 lines (56 loc) · 2.58 KB
/
collisionthrow.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import { Vector3 } from "three";
import { Ball } from "../ball";
import { Collision } from "./collision";
import { I, m, R } from "./constants";
/**
* Based on
* https://billiards.colostate.edu/technical_proofs/new/TP_A-14.pdf
*
*/
export class CollisionThrow {
normalImpulse: number;
tangentialImpulse: number;
private dynamicFriction(vRel: number): number {
return 0.01 + 0.108 * Math.exp(-1.088 * vRel);
}
public updateVelocities(a: Ball, b: Ball) {
const contact = Collision.positionsAtContact(a, b);
a.ballmesh.trace.forceTrace(contact.a)
b.ballmesh.trace.forceTrace(contact.b)
const ab = contact.b.sub(contact.a).normalize();
const abTangent = new Vector3(-ab.y, ab.x, 0);
const e = 0.98
const vPoint = a.vel.clone().sub(b.vel).add(
ab.clone().multiplyScalar(-R).cross(a.rvel).sub(
ab.clone().multiplyScalar(R).cross(b.rvel)
)
);
const vRelNormalMag = ab.dot(vPoint);
const vRel = vPoint.addScaledVector(ab, -vRelNormalMag)
const vRelMag = vRel.length();
const vRelTangential = abTangent.dot(vRel); // slip velocity perpendicular to line of impact
const μ = this.dynamicFriction(vRelMag);
// let normalImpulse = vRelNormalMag;
// let tangentialImpulse = Math.min((μ * vRelNormalMag) / vRelMag, 1 / 7) * (-vRelTangential)
// matches paper when throwAngle = Math.atan2(tangentialImpulse, normalImpulse)
// Normal impulse (inelastic collision)
this.normalImpulse = -(1 + e) * vRelNormalMag / (2 / m);
// Tangential impulse (frictional constraint)
this.tangentialImpulse = Math.min(
(μ * Math.abs(this.normalImpulse)) / vRelMag,
1 / 7
) * -vRelTangential;
// Impulse vectors
const impulseNormal = ab.clone().multiplyScalar(this.normalImpulse);
const impulseTangential = abTangent.clone().multiplyScalar(this.tangentialImpulse);
// Apply impulses to linear velocities
a.vel.addScaledVector(impulseNormal, 1 / m).addScaledVector(impulseTangential, 1 / m);
b.vel.addScaledVector(impulseNormal, -1 / m).addScaledVector(impulseTangential, -1 / m);
// Angular velocity updates
const angularImpulseA = ab.clone().multiplyScalar(-R).cross(impulseTangential);
const angularImpulseB = ab.clone().multiplyScalar(R).cross(impulseTangential);
a.rvel.addScaledVector(angularImpulseA, 1 / I);
b.rvel.addScaledVector(angularImpulseB, 1 / I);
return vRelNormalMag;
}
}