All files / owid-grapher/clientUtils PointVector.ts

84.29% Statements 59/70
100% Branches 9/9
63.64% Functions 7/11
84.29% Lines 59/70

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 981x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 1x 1x 106x 106x 1x 1x 432x 432x 1x 1x 459x 459x 1x 1x 564x 564x 1x 1x 563x 563x 563x 14x 14x 14x 1x 1x           1x 1x     1x 1x 1x 1x 1x     1x 1x     1x 1x 52x 52x 52x 52x                                                        
/*
 * Vector utility class
 * Partly based on the Unity vector: https://docs.unity3d.com/ScriptReference/Vector2.html
 * Wraps the Victor library, mainly so we can do type hinting
 *
 * @project Our World In Data
 * @author  Jaiden Mispy
 * @created 2017-03-15
 */
 
export class PointVector {
    x: number
    y: number
 
    constructor(x: number, y: number) {
        this.x = x
        this.y = y
    }
 
    subtract(v: PointVector): PointVector {
        return new PointVector(this.x - v.x, this.y - v.y)
    }
 
    add(v: PointVector): PointVector {
        return new PointVector(this.x + v.x, this.y + v.y)
    }
 
    times(n: number): PointVector {
        return new PointVector(this.x * n, this.y * n)
    }
 
    get magnitude(): number {
        return Math.sqrt(this.x ** 2 + this.y ** 2)
    }
 
    normalize(): PointVector {
        const magnitude = this.magnitude
        if (magnitude > 1e-5)
            return new PointVector(this.x / magnitude, this.y / magnitude)
 
        return new PointVector(0, 0)
    }
 
    normals(): [PointVector, PointVector] {
        return [
            new PointVector(-this.y, this.x),
            new PointVector(this.y, -this.x),
        ]
    }
 
    toString(): string {
        return `PointVector<${this.x}, ${this.y}>`
    }
 
    static up = new PointVector(0, -1)
    static zero = new PointVector(0, 0)
 
    static distanceSq(a: PointVector, b: PointVector): number {
        return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
    }
 
    static distance(a: PointVector, b: PointVector): number {
        return Math.sqrt(PointVector.distanceSq(a, b))
    }
 
    static angle(a: PointVector, b: PointVector): number {
        return (
            Math.acos(
                Math.max(
                    Math.min(PointVector.dot(a.normalize(), b.normalize()), 1),
                    -1
                )
            ) * 57.29578
        )
    }
 
    private static dot(lhs: PointVector, rhs: PointVector): number {
        return lhs.x * rhs.x + lhs.y * rhs.y
    }
 
    // From: http://stackoverflow.com/a/1501725/1983739
    static distanceFromPointToLineSq(
        p: PointVector,
        v: PointVector,
        w: PointVector
    ): number {
        const l2 = PointVector.distanceSq(v, w)
        if (l2 === 0) return PointVector.distanceSq(p, v)
 
        let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2
        t = Math.max(0, Math.min(1, t))
        return PointVector.distanceSq(
            p,
            new PointVector(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y))
        )
    }
}