From 3c51a1994f41b625823c4f15e92396b5498ce23c Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 24 Dec 2019 13:53:16 +0100 Subject: Move renderer into "runtime" subdirectory --- src/renderer/runtime/math/line.ts | 141 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/renderer/runtime/math/line.ts (limited to 'src/renderer/runtime/math/line.ts') diff --git a/src/renderer/runtime/math/line.ts b/src/renderer/runtime/math/line.ts new file mode 100644 index 0000000..db99035 --- /dev/null +++ b/src/renderer/runtime/math/line.ts @@ -0,0 +1,141 @@ +import { mat2, vec2 } from 'gl-matrix'; +import { Collidable } from './collision'; + +const rot90 = mat2.fromValues( + 0, 1, + -1, 0, +); + +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 getNormal(out: vec2): vec2 { + return normal(out, this.v); + } + + 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); + } +} + +export class Movement { + public readonly v: vec2; + + constructor( + public readonly src: vec2, + public readonly dest: vec2, + ) { + this.v = vec2.sub(vec2.create(), dest, src); + } + + public intersectLine(out: vec2, l: Line): vec2 { + const vp = vec2.sub(vec2.create(), l.p, this.src); + const d = crossz(vp, this.v); + const d2 = d / crossz(this.v, l.v); + return vec2.scaleAndAdd(out, l.p, l.v, d2); + } + + public passes(p: vec2): boolean { + const vp = vec2.sub(vec2.create(), p, this.src); + const d = vec2.dot(this.v, vp); + return d >= 0 && d <= vec2.sqrLen(this.v); + } + + public toLineSegment(): LineSegment { + return LineSegment.fromPoints(this.src, this.dest); + } + + public translate(t: vec2): Movement { + const src = vec2.add(vec2.create(), this.src, t); + const dest = vec2.add(vec2.create(), this.dest, t); + return new Movement(src, dest); + } +} + +export class LineSegment extends Line implements Collidable { + 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 containsPoint(p2: vec2): boolean { + const d = this.projectPointDistance(p2); + return (d >= 0 && d <= this.l); + } + + public collide(out: vec2, move: Movement, r: number): boolean { + if (this.distancePoint(move.src) < 0) + return false; + + if (crossz(move.v, this.v) < 0) + return false; + + const t = this.getNormal(vec2.create()); + vec2.scale(t, t, -r); + + const refMove = move.translate(t); + + if (!this.collideRef(out, refMove)) + return false; + + vec2.sub(out, out, t); + return true; + } + + private collideRef(out: vec2, move: Movement): boolean { + if (this.distancePoint(move.dest) >= 0) + return false; + + const x = move.intersectLine(vec2.create(), this); + if (!this.containsPoint(x)) + return false; + + this.projectPoint(out, move.dest); + + return true; + } +} -- cgit v1.2.3