-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
Below is a TypeScript function that models the collision between two spinning billiard balls using the equations from the paper by Hyeong-Chan Kim. This function uses the `vector3` library and assumes that each `Ball` object has properties `vel` (velocity) and `wvel` (angular velocity). | ||
|
||
Please note that some variables like angles (`theta0`, `phi`) and physical constants (`mu`, `mu_s`, `e_star`, `nu`, `R`) need to be provided, and their values depend on the specific scenario of the collision. | ||
|
||
```typescript | ||
import { Vector3 } from 'vector3'; | ||
|
||
interface Ball { | ||
pos: Vector3; | ||
vel: Vector3; | ||
wvel: Vector3; | ||
} | ||
|
||
interface CollisionParams { | ||
e_star: number; // Effective coefficient of restitution | ||
mu: number; // Friction coefficient between balls | ||
mu_s: number; // Friction coefficient between ball and table | ||
nu: number; // Moment of inertia coefficient (for solid sphere, nu = 2/5) | ||
R: number; // Radius of the ball | ||
} | ||
|
||
function collide(a: Ball, b: Ball, params: CollisionParams): void { | ||
// Extract parameters | ||
const e_star = params.e_star; // Effective coefficient of restitution | ||
const mu = params.mu; // Friction coefficient between balls | ||
const mu_s = params.mu_s; // Friction coefficient between ball and table | ||
const nu = params.nu; // Moment of inertia coefficient (for solid sphere, nu = 2/5) | ||
const R = params.R; // Radius of the ball | ||
|
||
// Positions and velocities | ||
const U_i = a.vel.clone(); // Initial velocity of ball a (cue ball) | ||
const V_i = b.vel.clone(); // Initial velocity of ball b (object ball) | ||
|
||
// Collision normal vector from a to b | ||
const n = b.pos.clone().subtract(a.pos).normalize(); // x̂ | ||
|
||
// Define ẑ as the vertical axis | ||
const z_hat = new Vector3(0, 0, 1); | ||
|
||
// Compute ŷ as perpendicular to x̂ and ẑ | ||
const y_hat = z_hat.clone().cross(n).normalize(); | ||
|
||
// Recompute ẑ to ensure orthogonality | ||
const z_hat_corrected = n.clone().cross(y_hat).normalize(); | ||
|
||
// Decompose U_i into components along x̂, ŷ, ẑ | ||
const U_i_x = U_i.dot(n); // Component along x̂ | ||
const U_i_mag = U_i.length(); | ||
|
||
// Compute cosϕ and sinϕ | ||
const cos_phi = U_i_x / U_i_mag; | ||
const sin_phi = Math.sqrt(1 - cos_phi * cos_phi); | ||
const tan_phi = sin_phi / cos_phi; | ||
|
||
// Assume θ₀ is the angle between the angular velocity ω_i and the collision plane | ||
const omega_i = a.wvel.clone(); | ||
const omega_i_mag = omega_i.length(); | ||
|
||
let cos_theta0 = 0; | ||
let sin_theta0 = 0; | ||
|
||
if (omega_i_mag !== 0) { | ||
cos_theta0 = omega_i.dot(z_hat_corrected) / omega_i_mag; | ||
sin_theta0 = Math.sqrt(1 - cos_theta0 * cos_theta0); | ||
} | ||
|
||
// Heaviside step functions | ||
const theta0 = Math.acos(cos_theta0); // θ₀ in radians | ||
const heaviside_neg_theta0 = theta0 < 0 ? 1 : 0; // Θ(-θ₀) | ||
const heaviside_theta0 = theta0 >= 0 ? 1 : 0; // Θ(θ₀) | ||
|
||
// Now compute U_f and V_f according to equations (52) and (53) | ||
|
||
// Equation (52) for U_f (velocity of cue ball after collision) | ||
const term1_Uf = n.clone().multiplyScalar(((1 - e_star) / 2) * U_i_mag * cos_phi); | ||
const term2_Uf = n.clone().multiplyScalar(mu * mu_s * (1 + e_star) * sin_theta0 / 4); | ||
const term3_Uf = y_hat.clone().multiplyScalar(mu * (1 + e_star) * (cos_theta0 / 2 + tan_phi / 2)); | ||
const term4_Uf = z_hat_corrected.clone().multiplyScalar(mu * (1 + e_star) * sin_theta0 * heaviside_neg_theta0 / 2); | ||
|
||
const U_f = term1_Uf.add(term2_Uf).add(term3_Uf).subtract(term4_Uf); | ||
|
||
// Equation (53) for V_f (velocity of object ball after collision) | ||
const term1_Vf = n.clone().multiplyScalar(((1 + e_star) / 2) * U_i_mag * cos_phi); | ||
const term2_Vf = n.clone().multiplyScalar(mu * mu_s * sin_theta0 / 2); | ||
const term3_Vf = y_hat.clone().multiplyScalar(mu * cos_theta0); | ||
const term4_Vf = z_hat_corrected.clone().multiplyScalar(mu * sin_theta0 * heaviside_theta0); | ||
|
||
const V_f = term1_Vf.add(term2_Vf).add(term3_Vf).add(term4_Vf); | ||
|
||
// Update velocities | ||
a.vel = U_f; | ||
b.vel = V_f; | ||
|
||
// Now compute changes in angular velocities according to equation (54) | ||
|
||
// For the cue ball | ||
const delta_omega = y_hat.clone().multiplyScalar(mu * (1 + e_star) * U_i_mag * cos_phi * sin_theta0 / (2 * nu * R)) | ||
.subtract(z_hat_corrected.clone().multiplyScalar(mu * (1 + e_star) * U_i_mag * cos_phi * cos_theta0 / (2 * nu * R))); | ||
|
||
// Update angular velocity of cue ball | ||
a.wvel = a.wvel.add(delta_omega); | ||
|
||
// For the object ball | ||
const omega_f_b = y_hat.clone().multiplyScalar(mu * (1 + e_star) * U_i_mag * cos_phi * (1 + mu_s) * sin_theta0 / (2 * nu * R)) | ||
.subtract(z_hat_corrected.clone().multiplyScalar(mu * (1 + e_star) * U_i_mag * cos_phi * cos_theta0 / (2 * nu * R))); | ||
|
||
// Update angular velocity of object ball | ||
b.wvel = omega_f_b; | ||
} | ||
``` | ||
|
||
**Assumptions and Notes:** | ||
|
||
1. **Coordinate System:** | ||
- `x̂` (`n`): Unit vector from the center of ball `a` to ball `b` at the point of collision. | ||
- `ŷ`: Perpendicular to both `x̂` and `ẑ`, computed using the cross product. | ||
- `ẑ`: The vertical axis, assumed to be `[0, 0, 1]`. | ||
|
||
2. **Angles:** | ||
- `ϕ` (`phi`): The angle between the initial velocity of ball `a` and the collision normal `x̂`. | ||
- `θ₀` (`theta0`): The angle between the angular velocity vector of ball `a` and the collision plane. | ||
|
||
3. **Heaviside Step Function:** | ||
- Implemented using simple conditional checks for `Θ(θ₀)` and `Θ(-θ₀)`. | ||
|
||
4. **Physical Constants:** | ||
- `e_star`: Effective coefficient of restitution between the balls. | ||
- `μ` (`mu`): Friction coefficient between the balls. | ||
- `μ_s` (`mu_s`): Friction coefficient between the balls and the table. | ||
- `ν` (`nu`): Moment of inertia coefficient (for a solid sphere, `ν = 2/5`). | ||
- `R`: Radius of the balls. | ||
|
||
5. **Angular Velocities:** | ||
- The change in angular velocity for the cue ball (`a`) and the new angular velocity for the object ball (`b`) are computed using equation (54). | ||
|
||
6. **Vector Operations:** | ||
- Ensure that all vector operations (addition, subtraction, cross product, dot product, scaling) are correctly implemented as per the `vector3` library's methods. | ||
|
||
**Usage Example:** | ||
|
||
```typescript | ||
// Define the balls | ||
const ballA: Ball = { | ||
pos: new Vector3(0, 0, 0), | ||
vel: new Vector3(1, 0, 0), | ||
wvel: new Vector3(0, 0, 0), | ||
}; | ||
|
||
const ballB: Ball = { | ||
pos: new Vector3(1, 0, 0), | ||
vel: new Vector3(0, 0, 0), | ||
wvel: new Vector3(0, 0, 0), | ||
}; | ||
|
||
// Define collision parameters | ||
const collisionParams: CollisionParams = { | ||
e_star: 0.9, | ||
mu: 0.05, | ||
mu_s: 0.2, | ||
nu: 2 / 5, | ||
R: 0.05715, // Standard billiard ball radius in meters | ||
}; | ||
|
||
// Perform collision | ||
collide(ballA, ballB, collisionParams); | ||
|
||
// After collision, ballA.vel and ballB.vel will be updated | ||
console.log('Ball A velocity:', ballA.vel); | ||
console.log('Ball B velocity:', ballB.vel); | ||
console.log('Ball A angular velocity:', ballA.wvel); | ||
console.log('Ball B angular velocity:', ballB.wvel); | ||
``` | ||
|
||
**Important Considerations:** | ||
|
||
- **Validation:** Ensure that the inputs are valid, and handle edge cases (e.g., zero velocities or angular velocities) to prevent division by zero or undefined behavior. | ||
- **Units:** Consistency in units is crucial. Make sure all measurements are in SI units (meters, seconds, kilograms). | ||
- **Further Refinement:** The physical model assumes ideal conditions. In practice, additional factors like ball deformations, spin decay, and air resistance might need to be considered. | ||
- **Testing:** Verify the function with known scenarios to ensure accuracy. | ||
|
||
**References:** | ||
|
||
- Hyeong-Chan Kim, "Collision of two spinning billiard balls and the role of table," [Link to the paper if available]. | ||
|
||
By following the equations and carefully translating them into code, this function should model the collision between two spinning billiard balls as described in the provided paper. |