Skip to content
Snippets Groups Projects
Select Git revision
  • 7c7c2350701c50e84e002df36e1c98e2be3735cf
  • master default
2 results

release-rules.js

Blame
  • CubicBezierCurve2d.ts 4.51 KiB
    import { Vector2 } from "three";
    
    function lerp(a: Vector2, b: Vector2, t: number): Vector2 {
        return new Vector2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
    }
    
    export class CubicBezierCurve2d {
        private _startPoint: Vector2;
        private _endPoint: Vector2;
        private _startControlPoint: Vector2;
        private _endControlPoint: Vector2;
    
        private _dirty: boolean = true;
        private _resolution: number = 5;
        private _points: Vector2[] = [];
        private _coefficients: number[][] = [];
    
        constructor(
            startPoint: Vector2,
            endPoint: Vector2,
            startControlPoint: Vector2,
            endControlPoint: Vector2
        ) {
            this._startPoint = startPoint;
            this._endPoint = endPoint;
            this._startControlPoint = startControlPoint;
            this._endControlPoint = endControlPoint;
        }
    
        get startPoint(): Vector2 {
            return this._startPoint;
        }
    
        get endPoint(): Vector2 {
            return this._endPoint;
        }
    
        get startControlPoint(): Vector2 {
            return this._startControlPoint;
        }
    
        get endControlPoint(): Vector2 {
            return this._endControlPoint;
        }
    
        get points(): Vector2[] {
            return this._points;
        }
    
        get coefficients(): number[][] {
            return this._coefficients;
        }
    
        set startPoint(value: Vector2) {
            this._startPoint = value;
            this._dirty = true;
            this.generatePoints(this._resolution);
        }
    
        set endPoint(value: Vector2) {
            this._endPoint = value;
            this._dirty = true;
            this.generatePoints(this._resolution);
        }
    
        set startControlPoint(value: Vector2) {
            this._startControlPoint = value;
            this._dirty = true;
            this.generatePoints(this._resolution);
        }
    
        set endControlPoint(value: Vector2) {
            this._endControlPoint = value;
            this._dirty = true;
            this.generatePoints(this._resolution);
        }
    
        bernstain(s: number): [Vector2, number[]] {
    
            // p(t) = (1-t)³ * p0 + 3 * (1-t)² * t * p1 + 
            //        3 * (1-t) * t² * p2 + t³ * p3
            // => subtitute: 1 - t = s
            // p(t) = k³ * p0 + 3 * k² * t * p1 + 
            //        3 * k * t² * p2 + t³ * p3
    
            const p0 = this.startPoint;
            const p1 = this.startControlPoint;
            const p2 = this.endControlPoint;
            const p3 = this.endPoint;
    
            // set k = 1 - s => substitution
            const t = s;
            const k = 1 - s;
    
            // calculate the coefficients
            const u0 = 1 * Math.pow(k, 3) * Math.pow(t, 0);
            const u1 = 3 * Math.pow(k, 2) * Math.pow(t, 1);
            const u2 = 3 * Math.pow(k, 1) * Math.pow(t, 2);
            const u3 = 1 * Math.pow(k, 0) * Math.pow(t, 3);
    
            // calculate the point
            const p = new Vector2(
                u0 * p0.x + u1 * p1.x + u2 * p2.x + u3 * p3.x,
                u0 * p0.y + u1 * p1.y + u2 * p2.y + u3 * p3.y
            );
    
            return [p, [u0, u1, u2, u3]];
        }
    
        deCasteljau(t: number): Vector2[][] {
    
            const p0 = this.startPoint;
            const p1 = this.startControlPoint;
            const p2 = this.endControlPoint;
            const p3 = this.endPoint;
    
            // iterative implementation of deCasteljau
            let points: Vector2[] = [p0, p1, p2, p3];
            let temporary: Vector2[] = [];
    
            const results: Vector2[][] = [];
            results.push(points);
            while (points.length > 1) {
                temporary = [];
                for (let i = 0; i < points.length - 1; i++)
                    temporary.push(lerp(points[i], points[i + 1], t));
                points = temporary;
                results.push(points); // save every iteration
            }
    
            return results;
        }
    
        generatePoints(resolution: number): Vector2[] {
    
            if (!this._dirty && this._points.length === resolution)
                return this._points;
            
            this._coefficients = [];
            this._points = [];        
    
            this._resolution = resolution;
            const sampleSize = 1 / resolution;
    
            for (let t = 0; t <= 1; t += sampleSize) {
                t = Math.round(t * 10000) / 10000;
                const [point, coefficients] = this.bernstain(t);
                this._points.push(point);
                this._coefficients.push(coefficients);
            }
    
            this._dirty = false;
    
            return this._points;
        }
    
        generatePointAt(t: number): [Vector2[][], number[], Vector2] {
            const points = this.deCasteljau(t);
            const coefficients = this._coefficients[Math.floor(t * this._resolution)];
            const point = points[points.length - 1];
            return [points, coefficients, point[0]];
        }
    }