Log in to post a comment.

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(1);

function getRandomInt(max) {
  return Math.floor(Math.random() * Math.floor(max + 1));
}
function rndm(min, max) {
    return min + getRandomInt(max - min);
}

// Global code will be evaluated once.
const turtle = new Turtle();
var lines = [
    new Ray2(new Vec2(rndm(-95, -85), -110), new Vec2(rndm(-15, 25), 220))
    ,new Ray2(new Vec2(rndm(-55, -45), -110), new Vec2(rndm(-15, 25), 220))
    ,new Ray2(new Vec2(rndm(-15, -5), -110), new Vec2(rndm(-15, 25), 220))
    ,new Ray2(new Vec2(rndm(25, 35), -110), new Vec2(rndm(-15, 25), 220))
    ,new Ray2(new Vec2(rndm(65, 75), -110), new Vec2(rndm(-15, 25), 220))
]
var circles = [
    new Circle(new Vec2(50, -50), rndm(20, 40))
    ,new Circle(new Vec2(0, 10), rndm(30, 60))
    ,new Circle(new Vec2(30, 70), rndm(30, 60))
   ,new Circle(new Vec2(-30, -50), rndm(20, 40))
   , new Circle(new Vec2(-80, 40), rndm(20, 30))
]
var penState = 0;

function togglePen() {
    //console.log(penState);
    if(penState == 0) {
        turtle.penup();
        penState = 1;
        return;
    }
    turtle.pendown();
    penState = 0;
}

// The walk function will be called until it returns false.
function walk(i) {
    var r = new Ray2(new Vec2(-100, (i / 8) - 100), new Vec2(200, 0));
    intersections = [r.o];

    for(var j = 0; j < lines.length; j++) {
        var ints = r.getIntersectionRay2(lines[j]);
        if(ints) {
            intersections.push(ints.point);
        }
    }
    for(var j = 0; j < circles.length; j++) {
        var ints = circles[j].getIntersectionsRay2(r);
        if(ints && ints.length > 1) {
            intersections.push(ints[0].point);
            intersections.push(ints[1].point);
        }
    }
    
    intersections.push(r.o.clone().add(r.d));
    
    intersections.sort(function(a,b) { return a.x - b.x;})
    
    penState = (r.o.y + 110) % 60 > 30? 1: 0;
    turtle.penup();
    for(var j = 0; j < intersections.length; j++) {
        turtle.goto(intersections[j].simple());
        togglePen();
    }
    return i < 1600;
}


/* All methods by Jurgen, some are commodity, some from Reinder's vec2 functions, some methods implemented to comply with the Vector2 class of the Unity framework */
function Vec2(x, y) {if(typeof y == 'undefined') {this.x = x[0]; this.y = x[1];} else {this.x = x; this.y = y;}}
/* returns scalar: length of vector */
Vec2.prototype.length = function() {return Math.sqrt(this.dot(this));}
/* returns this after making this' length 1 */
Vec2.prototype.normalize = function() {var l = this.length(); this.x /= l; this.y /= l; return this;}
/* returns this after rotating this in radians in clockwise direction for positive rads */
Vec2.prototype.rotate = function(rads) {var x = (Math.cos(rads) * this.x) - (Math.sin(rads) * this.y); var y = (Math.sin(rads) * this.x) + (Math.cos(rads) * this.y); this.x = x; this.y = y; return this;}
/* returns Vec2: a copy of this */
Vec2.prototype.clone = function() {return new Vec2(this.x, this.y);}
/* returns this after adding parameter Vec2 */
Vec2.prototype.add = function(v) {this.x += v.x; this.y += v.y; return this;}
/* returns this after subtracting parameter Vec2 */
Vec2.prototype.subtract = function(v) {this.x -= v.x; this.y -= v.y; return this;}
/* returns scalar: dot product of this and parameter Vec2 */
Vec2.prototype.dot = function(v) {return (this.x * v.x) + (this.y * v.y);}
/* returns boolean: true if distance(squared) between this and parameter Vec2 v is smaller than optional parameter scalar margin */
Vec2.prototype.equals = function(v, margin = .001) {return this.distanceSquared(v) < margin;}
/* returns boolean: true if this and parameter Vec2 are on lines that are parallel to each other within scalar parameter margin */
Vec2.prototype.isParallel = function(v, margin = .001) { var thi = this.clone().normalize().scale(100); var tha = v.clone().normalize().scale(100); return thi.equals(tha, margin) || thi.equals(tha.scale(-1), margin);}
/* returns scalar: the squared distance between this an parameter Vec2 */
Vec2.prototype.distanceSquared = function(v) {return Math.pow(this.x - v.x, 2) + Math.pow(this.y - v.y, 2);}
/* returns scalar: the distance between this an parameter Vec2 */
Vec2.prototype.distance = function(v) {return Math.sqrt(this.distanceSquared(v));}
/* returns scalar: the angle in radians between this and parameter Vec2 */
Vec2.prototype.angle = function(v) {return Math.acos( this.dot(v) / (this.length() * v.length()) );}
/* returns Vec2: interpolated linearly between parameter t = 0 (this) and t = 1 (paramter Vec2) (e.g. t = .5 gives a Vec2 with minimum and equal distance to this and parameter Vec2 */
Vec2.prototype.lerp = function(v, t) {if(t < 0 || 1 < t) { throw new Error('Parameter t out of bounds for Vec2.lerp(v, t)'); } var dx = v.x - this.x; var dy = v.y - this.y; this.x += t * dx; this.y += t * dy; return this;}
/* returns Vec2: alias of lerp() */
Vec2.prototype.interpolateLinearly = function(v, t) {return this.lerp(v, t);}
/* returns this after multiplying itself with scalar parameter */
Vec2.prototype.scale = function(s) {this.x *= s; this.y *= s; return this;}
/* returns array[2]: an array representation of this as [x, y] */
Vec2.prototype.simple = function() {return [this.x, this.y];}
/* returns this after making this the length of parameter length */
Vec2.prototype.scaleAbsolute = function(length) {return this.scale(length / this.length());}
/* returns this: alias of scale() */
Vec2.prototype.scaleRelative = function(s) {return this.scale(s);}
/* returns Vec2: a vector of same magnitude as this, perpendicular to this. The result is always rotated 90-degrees in a counter-clockwise direction for a 2D coordinate system where the positive Y axis goes up. */
Vec2.prototype.getPerpendicular = function() {return new Vec2(-this.y, this.x);}
/* returns Vec2: a vector reflected by a surface perpendicular to parameter norm as if the surface is a perfect mirror */
Vec2.prototype.reflect = function(norm) {var n = norm.clone().normalize(); return this.clone().subtract( n.scale(2 * this.dot(n)) ).scale(-1);}
/* return scalar: the Z-component of the resulting 3D Vector would this and parameter Vec2 be in the 3D plane x = 0, y = 0 */
Vec2.prototype.cross = function(v) {return (this.x * v.y) - (this.y * v.x);}

/* All methods by Jurgen, some are commodity, some methods implemented to comply with the Ray2D class of the Unity framework */
function Ray2(originVec2, directionVec2) { this.o = originVec2; this.d = directionVec2; }
/* returns Ray2: a copy of this */
Ray2.prototype.clone = function() { return new Ray2(this.o.clone(), this.d.clone()); }
/* returns this: draws this ray (from t = 0 to t = 1) on parameter turtle t */
Ray2.prototype.draw = function(t) { t.jump(this.o.x, this.o.y); t.goto(this.o.x + this.d.x, this.o.y + this.d.y); return this; }
/* returns this: draws this ray (from t = 0 to t = 1) on parameter turtle t with an arrow head of size parameter ratio */
Ray2.prototype.drawArrow = function(t, arrawHeadRatio=.3) { this.draw(t); var newO = this.o.clone().add(this.d); var c1 = this.d.clone().scale(-1); var c2 = c1.clone().rotate(Math.PI / 4).scale(arrawHeadRatio); c1.rotate(Math.PI / -4).scale(arrawHeadRatio); new Ray2(newO, c1).draw(t); new Ray2(newO, c2).draw(t); return this; }
/* returns Vec2: alias for getPointAbsolute */
Ray2.prototype.getPoint = function(distance) { return this.getPointAbsolute(distance); }
/* returns Vec2: A point on this ray when the direction is resized to length parameter distance */
Ray2.prototype.getPointAbsolute = function(distance) { return this.o.clone().add(this.d.clone().scaleAbsolute( distance )); }
/* returns Vec2: A point on this ray when the direction is multiplied by parameter t */
Ray2.prototype.getPointRelative = function(t) { return this.o.clone().add(this.d.clone().scale(t)); }
/* returns intersectioninfo or false: object with a point (Vec2) of intersection and a t1 and t2 representing the the scalar to apply to resp this' direction or parameter r's direction to get to that point */
Ray2.prototype.getIntersectionRay2 = function (r) { var deltaO = this.o.clone().subtract(r.o); var perp = this.d.getPerpendicular(); var det = r.d.dot(perp); if(det == 0) { return false; } var t1 = r.d.cross(deltaO) / det; var t2 = deltaO.dot(perp) / det; return { point: this.getPointRelative(t1), t1: t1, t2: t2 } }

/* All methods by Jurgen */
function Circle(centerVector, radius) { this.o = centerVector; this.r = radius; }
/* returns this: draws this circle on parameter turtle t */
Circle.prototype.draw = function(t) { var h = t.heading(); t.setheading(0); t.jump(this.o.x, this.o.y - this.r); t.circle(this.r); t.setheading(h); return this; }
/* returns intersectioninfo or false: array of objects where each item has a point (Vec2) of intersection and a t representing the the scalar to apply parameter r's direction to get to that point */
Circle.prototype.getIntersectionsRay2 = function(r) { var intersections = []; var OM = r.o.clone().subtract(this.o); var A = Math.pow( r.d.length(), 2 ); var B = 2 * r.d.dot( OM ); var C = Math.pow(OM.length(), 2) - Math.pow(this.r, 2); var Q = (B*B) - (4*A*C); var G = 1 / (2*A); var QQ = G*Math.sqrt(Q); var BB = (-1 * B * G); if(Q > 0) { intersections.push(BB + QQ); intersections.push(BB - QQ); } else if (Q == 0){ intersections.push(BB - QQ); } else { return false; } intersections.sort(function(a,b) { return a - b;}); for(var i = 0; i < intersections.length; i++) { intersections[i] = { point: r.getPointRelative(intersections[i]), t: intersections[i]}; } return intersections; }