From 884a5b700c6c924a0d85e656e83994069ee3f716 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 4 Nov 2018 20:05:12 +0100 Subject: Add simple math library for line segments --- src/math/line.ts | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/math/line.ts diff --git a/src/math/line.ts b/src/math/line.ts new file mode 100644 index 0000000..98e126b --- /dev/null +++ b/src/math/line.ts @@ -0,0 +1,98 @@ +import { mat2, vec2 } from 'gl-matrix'; + +const rot90 = mat2.fromRotation(mat2.create(), Math.PI / 2); + +export function normal(out: vec2, a: vec2): vec2 { + return vec2.transformMat2(out, a, rot90); +} + +export function crossz(a: vec2, b: vec2): number { + return a[0] * b[1] - a[1] * b[0]; +} + +export class Line { + constructor( + public readonly p: vec2, + public readonly v: vec2, + ) {} + + public projectPointDistance(p2: vec2): number { + const v2 = vec2.sub(vec2.create(), p2, this.p); + return vec2.dot(this.v, v2); + } + + public projectPoint(out: vec2, p2: vec2): vec2 { + const d = this.projectPointDistance(p2); + return vec2.scaleAndAdd(out, this.p, this.v, d); + } + + public distancePoint(p2: vec2): number { + const v2 = vec2.sub(vec2.create(), p2, this.p); + return crossz(this.v, v2); + } + + public intersectLine(out: vec2, l2: Line): vec2 { + const vp = vec2.sub(vec2.create(), l2.p, this.p); + const d = crossz(vp, this.v); + const d2 = d / crossz(this.v, l2.v); + return vec2.scaleAndAdd(out, l2.p, l2.v, d2); + } + + public equals(l2: Line): boolean { + return vec2.equals(this.p, l2.p) && vec2.equals(this.v, l2.v); + } +} + +export class LineSegment extends Line { + public static fromPoints(p1: vec2, p2: vec2): LineSegment { + const d = vec2.dist(p1, p2); + const v = vec2.sub(vec2.create(), p2, p1); + vec2.scale(v, v, 1 / d); + + return new LineSegment(p1, v, d); + } + + constructor( + p: vec2, + v: vec2, + public readonly l: number, + ) { + super(p, v); + } + + public getP2(out: vec2): vec2 { + return vec2.scaleAndAdd(out, this.p, this.v, this.l); + } + + public collidesPoint(p2: vec2): boolean { + const d = this.projectPointDistance(p2); + return (d >= 0 && d <= this.l); + } + + public collidesLine(l2: Line): boolean { + const x = this.intersectLine(vec2.create(), l2); + return this.collidesPoint(x); + } + + public collidesLineSegment(l2: LineSegment): boolean { + const x = this.intersectLine(vec2.create(), l2); + if (!this.collidesPoint(x)) + return false; + if (!l2.collidesPoint(x)) + return false; + + return true; + } + + public collidesMove(move: LineSegment): boolean { + if (!this.collidesLineSegment(move)) + return false; + + const p2 = move.getP2(vec2.create()); + return this.distancePoint(p2) < 0; + } + + public equals(l2: LineSegment): boolean { + return super.equals(l2) && this.l === l2.l; + } +} -- cgit v1.2.3