// Transforms. Created by Reinder Nijhoff 2019
// @reindernijhoff
//
// Another small utility class. The Tortoise class extends the Turtle class by adding support
// for transforms. A transform is a function that maps an input [x,y] coordinate to a
// transformed [x', y'] output coordinate.
//
// https://turtletoy.net/turtle/102cbd7c4d
//

const wave_x = 1; // min=0, max=50, step=0.1
const wave_y = 1; // min=0, max=10, step=0.1


Canvas.setpenopacity(0.8);

function Translate(x,y) { return p => [p[0]+x, p[1]+y]; }
function Wave(f, a) { return p => [p[0], p[1]+Math.sin(p[0]*f)*a]; }
function Rotate(a) { return p => [p[0]*Math.cos(a)+p[1]*Math.sin(a), p[1]*Math.cos(a)-p[0]*Math.sin(a)]; }
function Scale(s) { return p => [p[0]*s, p[1]*s]; }
function Barrel(b) { return p => { let s = (1+(p[0]**2 + p[1]**2)*b); return [p[0]*s, p[1]*s]; } }


//drawWave(new Tortoise())
drawWave(new Tortoise().addTransform(Wave(wave_x,wave_y)));


function drawWave(t){
    const points = [];
    const velocities = [];
    const acceleration = [];
    
    for (let i = 0; i < 30; i++) {
        points.push(i * (200 / 30) - 100, 0.25 * 100 * (Math.random() - Math.random()));
        velocities.push(0, 0);
        acceleration.push(0,0)
    }
    for(let temp=0;temp<100;temp++){
      for (let y = 0; y < 60; y++) {
            
            time = Math.random() - Math.random()
            
            acceleration[y] = 0.05*time//(2*Math.random()-1)
            velocities[y] += 0.04 * time +(1/2)*acceleration[y]*(time**2);
            points[y] += velocities[y];
    	}
    	
    	
    	if(Math.random()<0.75){
    	    if(temp<10){
    	        t.drawSpline(points.slice(30));
    	    }else{
    	        t.drawSpline(points);
    	    }
    	}
    	
    }
}


////////////////////////////////////////////////////////////////
// Tortoise utility code (Minimal Turtle and Transforms)
// https://turtletoy.net/turtle/102cbd7c4d
////////////////////////////////////////////////////////////////

function Tortoise(x, y) {
    class Tortoise extends Turtle {
        constructor(x, y) {
            super(x, y);
            this.ps = Array.isArray(x) ? [...x] : [x || 0, y || 0];
            this.transforms = [];
        }
        addTransform(t) {
            this.transforms.push(t);
            this.jump(this.ps);
            return this;
        }
        applyTransforms(p) {
            if (!this.transforms) return p;
            let pt = [...p];
            this.transforms.map(t => { pt = t(pt); });
            return pt;
        }
        goto(x, y) {
            const p = Array.isArray(x) ? [...x] : [x, y];
            const pt = this.applyTransforms(p);
            if (this.isdown() && (this.pt[0]-pt[0])**2 + (this.pt[1]-pt[1])**2 > 4) {
               this.goto((this.ps[0]+p[0])/2, (this.ps[1]+p[1])/2);
               this.goto(p);
            } else {
                super.goto(pt);
                this.ps = p;
                this.pt = pt;
            }
        }
        position() { return this.ps; }
        drawSpline(pts, tension = 0.5, isClosed = false, numOfSegments = 16){
            const res = []; // clone array
            const _pts = pts.slice(0);
            // The algorithm require a previous and next point to the actual point array.
            // Check if we will draw closed or open curve.
            // If closed, copy end points to beginning and first points to end
            // If open, duplicate first points to befinning, end points to end
            if (isClosed) {
                _pts.unshift(pts[pts.length - 1]);
                _pts.unshift(pts[pts.length - 2]);
                _pts.unshift(pts[pts.length - 1]);
                _pts.unshift(pts[pts.length - 2]);
                _pts.push(pts[0]);
                _pts.push(pts[1]);
            }
            else {
                _pts.unshift(pts[1]);   //copy 1. point and insert at beginning
                _pts.unshift(pts[0]);
                _pts.push(pts[pts.length - 2]); //copy last point and append
                _pts.push(pts[pts.length - 1]);
            }
            // ok, lets start..
            // 1. loop goes through point array
            // 2. loop goes through each segment between the 2 pts + 1e point before and after
            for (let i = 2; i < (_pts.length - 4); i += 2) {
                for (let t = 0; t <= numOfSegments; t++) {
                    // calc tension vectors
                    const t1x = (_pts[i+2] - _pts[i-2]) * tension;
                    const t2x = (_pts[i+4] - _pts[i]) * tension;
                    const t1y = (_pts[i+3] - _pts[i-1]) * tension;
                    const t2y = (_pts[i+5] - _pts[i+1]) * tension;
                    // calc step
                    const st = t / numOfSegments;
                    // calc cardinals
                    const c1 =   2 * Math.pow(st, 3)  - 3 * Math.pow(st, 2) + 1; 
                    const c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2); 
                    const c3 =       Math.pow(st, 3)  - 2 * Math.pow(st, 2) + st; 
                    const c4 =       Math.pow(st, 3)  -     Math.pow(st, 2);
                    // calc x and y cords with common control vectors
                    const x = c1 * _pts[i]    + c2 * _pts[i+2] + c3 * t1x + c4 * t2x;
                    const y = c1 * _pts[i+1]  + c2 * _pts[i+3] + c3 * t1y + c4 * t2y;
                    //store points in array
                    res.push(x);
                    res.push(y);
                }
            }
            // draw
            this.goto(res[0], res[1]);
            this.down();
            for(let i = 2; i < res.length - 1; i += 2) this.goto(res[i], res[i+ 1]);
            this.up();
            return res;
        }
    }
    return new Tortoise(x,y);
}