// 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]]; }