Arrows pointing in all kinds of directions.
Log in to post a comment.
// Forked from "Double Circle Turnaround" by mcfly // https://turtletoy.net/turtle/49150890a6 // You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(1); // Global code will be evaluated once. const turtle = new Turtle(); // Adjustable variables. const size = 8.5; // min=1, max=10, step=.1 const grow = -.2; // min=-3, max=3, step=.1 const rows = 15; // min=1, max=50, step=1 const columns = 15; // min=1, max=50, step=1 const maxAngle = 90; // min=0, max=360, step=45 const turns = 1; // min=0, max=5, step=1 const border = 4; // min=1, max=20, step=1 // Adjust some parameters so they fit the screen. var len = size, space = 5, factor = 180 / (Math.max(columns,rows) * (len+space)); len *= factor; space *= factor; // Helper variables. const width = columns * (len+space), height = rows * (len+space); // The walk function will be called until it returns false. function walk(i) { if(!(function() { var row = Math.floor(i / columns); column = i % columns; if(row >= rows) return false; // Position. turtle.jump(-width/2 + (column+.5) * (len+space),-height/2 + (row+.5) * (len+space)); // Calculate rotation. let x = column - columns/2, y = row - rows/2, angle = Math.atan2(y,x) * 180 / Math.PI, distance = Math.sqrt(x*x + y*y) / (columns/2), rotation = Math.max(0,1 - distance) * maxAngle + turns*angle; // Rotate. turtle.setheading(rotation); // Drawn maple leaf. traceSvgPath("M17.5,10L10,17.5V14H3V6h7V2.5L17.5,10z",10,10,.1*len*(1+grow*Math.max(0,1-distance))); return true; })()) { finish(); return false; } return true; } // Evaluated once at the end. function finish() { // Cut the frame last. const frameWidth = width + 2 * border, frameHeight = height + 2 * border; turtle.jump(-frameWidth/2,-frameHeight/2); turtle.setheading(0); turtle.pendown(); turtle.forward(frameWidth); turtle.right(90); turtle.forward(frameHeight); turtle.right(90); turtle.forward(frameWidth); turtle.right(90); turtle.forward(frameHeight); } // Trace the provided "d" attribute of an SVG <path> element with the turtle, // starting at the current position (and heading). An offset and scaling factor // may be applied (offset first, scale second). Curves will become straight // lines. The position and heading of the turtle will be preserved, then pen // will be up. function traceSvgPath(path,offsetX = 0,offsetY = 0,scale = 1) { const pos = turtle.pos(), heading = turtle.heading(); angle = (dx,dy) => Math.atan2(dy,dx) * 180 / Math.PI, length = (dx,dy) => Math.sqrt(dx*dx + dy*dy); turtle.penup(); var x = 0, y = 0, a = 0, startX = 0, startY = 0, ta; parseSvgPath(path).forEach((cmd,i) => { // Transform commands into line commands. cmd = { H : ["L",cmd[1],y/(scale || 1) + offsetY], h : ["l",cmd[1],0], V : ["L",x/(scale || 1) + offsetX,cmd[1]], v : ["l",0,cmd[1]], Z : ["L",startX,startY], z : ["L",startX,startY], C : ["L",cmd[5],cmd[6]], c : ["l",cmd[1]+cmd[3]+cmd[5],cmd[2]+cmd[4]+cmd[6]], S : ["L",cmd[3],cmd[4]], s : ["l",cmd[1]+cmd[3],cmd[2]+cmd[4]], Q : ["L",cmd[3],cmd[4]], q : ["l",cmd[1]+cmd[3],cmd[2]+cmd[4]], T : ["L",cmd[1],cmd[2]], t : ["l",cmd[1],cmd[2]], A : ["L",cmd[6],cmd[7]], a : ["l",cmd[6],cmd[7]], }[cmd[0]] || cmd; switch(cmd[0]) { case "M": case "L": cmd[1] -= offsetX; cmd[2] -= offsetY; cmd[1] *= scale; cmd[2] *= scale; ta = angle(cmd[1]-x,cmd[2]-y); turtle.right(ta - a); if(cmd[0] === "L") turtle.pendown(); turtle.forward(length(cmd[1]-x,cmd[2]-y)); if(cmd[0] === "L") turtle.penup(); x = cmd[1]; y = cmd[2]; a = ta; break; case "m": case "l": cmd[1] *= scale; cmd[2] *= scale; ta = angle(cmd[1],cmd[2]); turtle.right(ta - a); if(cmd[0] === "l") turtle.pendown(); turtle.forward(length(cmd[1],cmd[2])); if(cmd[0] === "l") turtle.penup(); x += cmd[1]; y += cmd[2]; a = ta; break; } if(!i) { startX = x/(scale || 1) + offsetX; startY = y/(scale || 1) + offsetY; } }); turtle.jump(pos); turtle.setheading(heading); } // Taken from https://github.com/jkroso/parse-svg-path and adjusted somewhat. const cmdLength = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}; function parseSvgPath(path) { var data = []; path.replace(/([astvzqmhlc])([^astvzqmhlc]*)/ig, function(_, command, args){ var type = command.toLowerCase(); args = (args.match(/-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig) || []).map(Number); // overloaded moveTo if(type == 'm' && args.length > 2) { data.push([command].concat(args.splice(0, 2))); type = 'l'; command = command == 'm' ? 'l' : 'L'; } while(true) { if(args.length == cmdLength[type]) { args.unshift(command); return data.push(args); } if(args.length < cmdLength[type]) throw new Error('malformed path data') data.push([command].concat(args.splice(0, cmdLength[type]))); } }); return data; }