pocket-physics
Version:
Verlet physics extracted from pocket-ces demos
154 lines (134 loc) • 3.55 kB
text/typescript
export type Vector2<V extends number = number> = { x: V; y: V };
export function v2<V extends number>(x?: V, y?: V): Vector2<V> {
return { x: x || (0 as V), y: y || (0 as V) };
}
export const copy = <V extends number>(out: Vector2<V>, a: Vector2<V>) => {
out.x = a.x;
out.y = a.y;
return out;
};
export const set = <V extends number>(out: Vector2<V>, x: V, y: V) => {
out.x = x;
out.y = y;
return out;
};
export const add = <V extends number>(
out: Vector2<V>,
a: Vector2<V>,
b: Vector2<V>
) => {
out.x = (a.x + b.x) as V;
out.y = (a.y + b.y) as V;
return out;
};
export const sub = <V extends number>(
out: Vector2<V>,
a: Vector2<V>,
b: Vector2<V>
) => {
out.x = (a.x - b.x) as V;
out.y = (a.y - b.y) as V;
return out;
};
export const dot = <V extends number>(a: Vector2<V>, b: Vector2<V>) =>
a.x * b.x + a.y * b.y;
export const scale = <V extends number>(
out: Vector2<V>,
a: Vector2<V>,
factor: number
) => {
out.x = (a.x * factor) as V;
out.y = (a.y * factor) as V;
return out;
};
export const distance = <V extends number>(v1: Vector2<V>, v2: Vector2<V>) => {
const x = v1.x - v2.x;
const y = v1.y - v2.y;
return Math.sqrt(x * x + y * y);
};
export const distance2 = <V extends number>(v1: Vector2<V>, v2: Vector2<V>) => {
const x = v1.x - v2.x;
const y = v1.y - v2.y;
return x * x + y * y;
};
export const magnitude = <V extends number>(v1: Vector2<V>) => {
const x = v1.x;
const y = v1.y;
return Math.sqrt(x * x + y * y);
};
export const normalize = <V extends number>(out: Vector2<V>, a: Vector2<V>) => {
const x = a.x;
const y = a.y;
let len = x * x + y * y;
if (len > 0) {
len = 1 / Math.sqrt(len);
out.x = (a.x * len) as V;
out.y = (a.y * len) as V;
}
return out;
};
/**
* Compute the normal pointing away perpendicular from two vectors.
* Given v1(0,0) -> v2(10, 0), the normal will be (0, 1)
* */
export const normal = <V extends number>(
out: Vector2<V>,
v1: Vector2<V>,
v2: Vector2<V>
) => {
out.y = (v2.x - v1.x) as V;
out.x = (v1.y - v2.y) as V;
return normalize(out, out);
};
// the perpendicular dot product, also known as "cross" elsewhere
// http://stackoverflow.com/a/243977/169491
export const perpDot = <V extends number>(v1: Vector2<V>, v2: Vector2<V>) => {
return v1.x * v2.y - v1.y * v2.x;
};
/**
* This is mostly useful for moving a verlet-style [current, previous]
* by the same amount, translating them while preserving velocity.
* @param by the vector to add to each subsequent vector
* @param vN any number of vectors to translate
*/
export const translate = <V extends number>(
by: Vector2<V>,
...vN: Vector2<V>[]
) => {
for (let i = 0; i < vN.length; i++) {
const v = vN[i];
add(v, v, by);
}
};
/**
*
* @param v Print this vector for nice logs
*/
export function vd(v: Vector2) {
return `(${v.x}, ${v.y})`;
}
/**
* Rotate a vector around another point. Taken nearly verbatim from gl-matrix
*/
export const rotate2d = <V extends Vector2>(
out: V,
target: V,
origin: V,
rad: number
) => {
//Translate point to the origin
const p0 = target.x - origin.x;
const p1 = target.y - origin.y;
const sinC = Math.sin(rad);
const cosC = Math.cos(rad);
//perform rotation and translate to correct position
out.x = p0 * cosC - p1 * sinC + origin.x;
out.y = p0 * sinC + p1 * cosC + origin.y;
return out;
};
/**
* Compute the Theta angle between a vector and the origin.
*/
export function angleOf(v: Vector2) {
return Math.atan2(v.y, v.x);
}