let c = [];
let centre;
let points = [];

let player;

let sceneX = 400;
let sceneW = 400;
let sceneH = 400;

let drawingRectangle = false;

function setup() {
    sketchesCreateCanvas(800, 400);

    c = [
        new Circle(createVector(100, 150), 20),
        new Circle(createVector(300, 125), 30),
        new Rectangle(createVector(200, 50), 50, 40),
        new Rectangle(createVector(20, 200), 20, 100),

        new LineSegment(createVector(0, 0), createVector(0, 400)),
        new LineSegment(createVector(0, 400), createVector(400, 400)),
        new LineSegment(createVector(400, 400), createVector(400, 0)),
        new LineSegment(createVector(400, 0), createVector(0, 0)),
    ];
    player = new Player(sceneW, radians(60));


}

function draw() {
    background(25);

    for (let ci of c)
        ci.show();

    player.update(c);
    player.draw();

    drawScene();

    if (drawingRectangle) {
        var rect = c[c.length - 1];
        var newSize = createVector(Math.abs(rect.pos.x - mouseX), Math.abs(rect.pos.y - mouseY));
        console.log(newSize);
        rect.size = newSize;
    }
}

function drawScene() {
    push();
    noStroke();
    fill(0);
    rect(sceneX, 0, width, height);
    rectMode(CENTER);
    const increment = sceneW / player.sight.length;
    let o = 0;
    for (let d of player.sight) {
        fill(map(d, 0, 400, 255, 0));
        let h = map(400 / d, 1, 10, 0, sceneH)
        rect(sceneX + o, sceneH / 2, increment, h);
        o += increment;
    }
    pop();
}

function mousePressed() {
    c.push(new Rectangle(createVector(mouseX, mouseY), 0, 0))
    drawingRectangle = true;
}

function mouseReleased() {
    drawingRectangle = false;
}
    
    class CastObject {
    constructor() {
        this.pos = createVector();
    }
    distance(p) { return null }
}

class Circle extends CastObject {
    constructor(pos, r) {
        super();
        this.radius = r;
        this.pos = pos;
    }
    distance(p) {
        return p.dist(this.pos) - this.radius;
    }
    show() {
        push();
        noStroke();
        fill(0);
        ellipse(this.pos.x, this.pos.y, this.radius * 2);
        pop();
    }
}

class Rectangle extends CastObject {
    constructor(pos, sizeX, sizeY) {
        super();
        this.pos = pos.copy();
        this.size = createVector(sizeX, sizeY);
    }
    distance(p) {
        let offset = vAbs(p5.Vector.sub(p, this.pos)).sub(this.size);
        return vMax(offset, 0).mag(); // Change to include negative
    }
    show() {
        push();
        rectMode(RADIUS);
        noStroke();
        fill(0);
        rect(this.pos.x, this.pos.y, this.size.x, this.size.y);
        pop();
    }
}

class LineSegment extends CastObject{
  constructor(a, b){
    super();
    this.a = a;
    this.b = b;
  }
  distance(p) {
      let l2 = p5.Vector.sub(this.a,this.b).magSq();
      if (l2 == 0) return dist2(p, v);
      var t = ((p.x - this.a.x) * (this.b.x - this.a.x) + (p.y - this.a.y) * (this.b.y - this.a.y)) / l2;
      t = Math.max(0, Math.min(1, t));
      return createVector(this.a.x + t * (this.b.x - this.a.x), this.a.y + t * (this.b.y - this.a.y)).sub(p).mag();
  }
  
  show(){
    push();
    strokeWeight(2);
    stroke(0);
    line(this.a.x, this.a.y, this.b.x, this.b.y)
    pop();
  }
}


function vAbs(v) {
    return createVector(abs(v.x), abs(v.y));
}

function vMax(v1, s) {
    return createVector(Math.max(v1.x, s), Math.max(v1.y, s));
}
    
    const min = 0.1;
const max = 500;

function castRay(pos, direction, objects, out = null) {
    let p = pos.copy(),
        r;
    if (out) { out.begin(pos, direction); }

    do {
        r = Infinity;
        for (let object of objects) {
            let d = object.distance(p);
            if (d < r) r = d;
        }
        if (r > max) break;
        if (out) { out.addStep(p, r); }
        p.add(p5.Vector.mult(direction, r));
    } while (r > min);
    if (out) { out.finish(p); }
    return p.sub(pos).mag();
}

class Ray {
    constructor() {
        this.start = createVector();

        this.steps = [];
        this.end = createVector();
        this.vector = createVector();
        this.length = 0;
    }

    begin(pos) {
        this.start = pos.copy();
    }

    addStep(pos, r) {
        this.steps.push([pos.copy(), r]);
    }

    finish(endPoint) {
        this.end = endPoint.copy();
        this.vector = p5.Vector.sub(endPoint, this.pos)
        this.length = this.vector.mag();
    }

    draw(drawSteps = true) {
        push();

        stroke(255, 255, 255, 100);
        strokeWeight(2);
        line(this.start.x, this.start.y, this.end.x, this.end.y);

        if (drawSteps) {
            strokeWeight(1);
            fill(255, 255, 255, 50);
            for (let step of this.steps) {
                ellipse(step[0].x, step[0].y, step[1] * 2);
            }
        }

        stroke(200);
        strokeWeight(8);
        point(this.start);

        pop();
    }

}
    
    class Raycaster {
    constructor(pos, definition, fov) {
        this.pos = pos.copy();
        this.def = definition;

        this.rotation = 0;
        this.fov = fov;
    }

    cast(c) {
        let m = []
        for (let a = this.rotation - this.fov / 2; a < this.rotation + this.fov / 2; a += this.def) {
            //let ray = new Ray();
            let dir = p5.Vector.fromAngle(a);

            m.push(castRay(this.pos, dir, c));
            //ray.draw(true);
        }
        return m;
    }
}
    
    class Player extends Raycaster {
    constructor(width, fov) {
        super(createVector(200, 200), fov / width, fov);
        this.speed = 200;
        this.rotSpeed = 0.05;

        this.sight = [];

    }

    move(dist) {
        this.pos.add(p5.Vector.fromAngle(this.rotation).mult(dist));
    }

    update(c) {
        this.sight = player.cast(c);

        if (keyIsPressed) {
            if (keyIsDown(UP_ARROW) || keyIsDown(87)) {
                player.move(this.frameDistance());
            } else if (keyIsDown(DOWN_ARROW) || keyIsDown(83)) {
                player.move(-this.frameDistance());
            }
            if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) {
                player.rotation -= this.rotSpeed;
            }
            if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) {
                player.rotation += this.rotSpeed;
            }
        }
    }

    frameDistance() {
        return this.speed * deltaTime / 1000;
    }

    draw() {
        push();
        strokeWeight(5);
        stroke(1);
        translate(this.pos);
        rotate(this.rotation - PI / 2);
        fill(255);
        stroke(255);
        triangle(0, 0, 5, 5, -5, 5);
        point(0, 0);
        pop();
    }
}
    
    
Return to Sketch