Inverse Circles ☀️

Inverse of turtletoy.net/turtle/ab7954417c

Log in to post a comment.

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

const grid = 5;
const radius = 15;

// Global code will be evaluated once.
const turtle = new Turtle();

var size = 200 / grid;

// The walk function will be called until it returns false.
function walk(i) {
    var l1 = getRandomPoint(1);
    var l2 = getRandomPoint(-1);
    
    var intersections = [];
    //var O = [0, 0];
    
    for(var j = 0; j < grid * grid; j++) {
        var x = j % grid;
        var y = j / grid | 0;

        var M = [((x + .5) * size) - 100, ((y + .5) * size) - 100];
        
        var D = [l2[0] - l1[0], l2[1] - l1[1]];
        var O = l1;
    
// If you are in 2D vector form the equations above can be represented as vectors by using
// origin O and direction of a ray D, where |D| must be strongly positive for the ray to
// intersect the circle. Let's say that the circle center is at position vector M and its
// radius is R. First, you need to define the vector from the center of the circle being M
// to the ray origin O:
// 
// OM = O - M (O and M are position vectors)
        var OM = [O[0] - M[0], O[1] - M[1]];
// In vector form you can define the quadratic equation coefficients like follows ( ^ means
// to the power of, dot means dot product between two 2D vectors, |VECTOR| means length of
// vector ):
//
// A = |D|^2
        var A = vecLength(D) * vecLength(D);
// B = 2 * D dot OM
        var B = 2 * dot2d(D, OM);
// C = |OM|^2 - R^2
        var C = (vecLength(OM) * vecLength(OM)) - (radius * radius)
// Now we can calculate the discriminant: Q = B^2 - 4*A*C
        var Q = (B*B) - (4*A*C);
    
        if(Q > 0) {
// If the given value is negative, there is no intersection if the value is zero, you have
// one root and one intersection as two roots for two intersection points. Store the value 
// of G = 1/(2*A) in a variable it will become in handy later. Now calculate the determinant
// Q = G*sqrt(Q) and update the value of B = (-B * G)
            var G = 1 / (2*A);
            Q = G*Math.sqrt(Q);
            B = (-1 * B * G);
// Now we are ready to calculate the intersection points. Let's call them P1 and P2 where we
// will have an equation as follows where B and Q are number scalars:
    
// P1 = D * (B + Q) + O
            //var p1 = [(D[0] * (B + Q)) + O[0], (D[1] * (B + Q)) + O[1]];
            //var p1 = addVec(multiplyVec(D, B+Q), O);
            intersections.push(B+Q);
// P2 = D * (B - Q) + O
            //var p2 = [(D[0] * (B - Q)) + O[0], (D[1] * (B - Q)) + O[1]];
            //var p2 = addVec(multiplyVec(D, B-Q), O);
            intersections.push(B-Q);
// The closest intersection point to the ray origin is P2 and the distant one is P1
            //line(l1, p2);
            //line(p1, l2);
        } else {
            //line(l1, l2);
        }
    }
    
    var points = [l1];
    
    intersections.sort(function(a,b) { return a - b;});
    for(var j = 0; j < intersections.length; j++) {
        points.push(addVec(multiplyVec(D, intersections[j]), O));
    }
    points.push(l2);
    
    for(var j = 0; j < points.length; j = j + 2) {
        line(points[j], points[j + 1]);
    }
    
    return i < 200;
}

function getRandomPoint(side) {
    //var pt = 400 * Math.sin(Math.random() * Math.PI * 2);
    var pt = 400 * Math.sin(Math.random() * Math.PI);
    if(pt < 200) {
        return side < 0? [pt - 100, -100]: [-100, pt - 100]; //top: left
    }
    return side < 0? [100, pt - 300]: [pt - 300, 100]; //right: bottom
}

const line = (p1, p2) => {
    turtle.jump(p1[0], p1[1]);
    turtle.goto(p2[0], p2[1]);
}

function dot2d(v1, v2) {
    return (v1[0] * v2[0]) + (v1[1] * v2[1]);
    
}

function vecLength(v) {
    return Math.sqrt((v[0] * v[0]) + (v[1] * v[1]));
}

function multiplyVec(vec, multiplier) {
    return [vec[0] * multiplier, vec[1] * multiplier];
}

function addVec(vec1, vec2) {
    return [vec1[0] + vec2[0], vec1[1] + vec2[1]];
}