My take on taking a stroll
Wandering πΆπ½ββοΈ (variation)
Wandering πΆπ½ββοΈ (variation)
Wandering πΆπ½ββοΈ (variation)
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(1); const growFrom = 2; //min=0 max=3 step=1 (Center, Edges, Both, Random) const seeds = 4; //min=1 max=10 step=1 const seedRotationDeg = 0; //min=0 max=359 step=1 const progressionArcDeg = 1.3; //min=0 max=5 step=.1 const progressionDirection = 0; //min=0 max=2 step=1 (Left, Right, Both) const spawnChance = .5; //min=0 max=1 step=.05 const spawnEveryXSteps = 2; //min=0 max=100 step=1 const spawnAnglePartOfPI = 3; //min=2 max=40 step=1 const spawnDirection = 0; //min=0 max=2 step=1 (Left, Right, Both) const extraSpawnsExp = 0; //min=0 max=5 step=1 const extraSpawns = extraSpawnsExp == 0? 0: 10**extraSpawnsExp; const dir = (setting) => setting == 2? (Math.random() < .5? -1: 1): setting == 1? -1: 1; const seedRotation = Math.PI/180*seedRotationDeg; // Global code will be evaluated once. const segmentTracker = new SegmentTracker(); //let turtles = [new Wanderer([0,0],[1,0]), new Wanderer([0,0],[-.5,3**.5/-2]), new Wanderer([0,0],[-.5,3**.5/2])]; let wanderers = []; if(growFrom == 0 || growFrom == 2) { for(let i = 0; i < seeds; i++) { const p = i / seeds * 2 * Math.PI; wanderers.push( new Wanderer(segmentTracker, [0,0], [Math.cos(p+seedRotation),Math.sin(p+seedRotation)], dir(progressionDirection), progressionArcDeg) ); } } if(growFrom == 1 || growFrom == 2) { for(let i = 0; i < seeds; i++) { const p = i / seeds * 2 * Math.PI; wanderers.push( new Wanderer(segmentTracker, [Math.cos(p+Math.PI*3/4+seedRotation)*175,Math.sin(p+Math.PI*3/4+seedRotation)*175], [Math.cos(p+seedRotation),Math.sin(p+seedRotation)], dir(progressionDirection), progressionArcDeg) ); } } if(growFrom == 3) { for(let i = 0; i < seeds; i++) { wanderers.push( new Wanderer(segmentTracker, [Math.random() * 200 - 100, Math.random() * 200 - 100], randomNormalVector(), dir(progressionDirection), progressionArcDeg) ); } } function randomNormalVector() { const a = Math.random() * 2 * Math.PI; return [Math.cos(a), Math.sin(a)]; } let tries = 0; // The walk function will be called until it returns false. function walk(i) { wanderers.forEach(i => { i.step(); if(i.active) { const w = i.spawn(spawnChance, spawnAnglePartOfPI, dir(spawnDirection), spawnEveryXSteps); if(w != null) { wanderers.push(new Wanderer(segmentTracker, ...w, dir(progressionDirection), progressionArcDeg)); } } }); wanderers = wanderers.filter(i => i.active); if(wanderers.length == 0 && tries < extraSpawns) { let randomSegment = segmentTracker.getRandomSegment(); //console.log(randomSegment); wanderers.push( // new Wanderer(segmentTracker, [Math.random() * 200 - 100, Math.random() * 200 - 100], randomNormalVector()) new Wanderer(segmentTracker, [randomSegment[0][0] + randomSegment[1][0] * .5, randomSegment[0][1] + randomSegment[1][1] * .5], randomNormalVector(), dir(progressionDirection), progressionArcDeg) ); tries++; } if(i == -1){//200) { console.group('in walk'); console.log(segmentTracker); console.log(segmentTracker.segments.map((v,k) => k).filter(i => i !== undefined)); console.log(Object.keys(segmentTracker.segments)); console.groupEnd(); } return wanderers.length > 0; } const maxRender = 200; function SegmentTracker() { class SegmentTracker { constructor() { this.segments = []; } intersectionInfo(origin, direction) { const cx = Math.min(maxRender-1, Math.max(1-maxRender, origin[0] | 0)); const cy = Math.min(maxRender-1, Math.max(1-maxRender, origin[1] | 0)); let intersections = []; for(let x = cx-1; x <= cx+1; x++) { if(this.segments[x] == undefined) continue; for(let y = cy-1; y <= cy+1; y++) { if(this.segments[x][y] == undefined) continue; for(let i = 0; i < this.segments[x][y].length; i++) { let inter = this.intersection([origin, direction], this.segments[x][y][i]); if(inter != null) { if(0.001 < inter[0] && inter[0] <= 1) { intersections.push(inter[0]); } } } } } if(intersections.length < 1) return null; intersections.sort(); return intersections.pop(); } intersection(ray1, ray2) { let dx = ray2[0][0] - ray1[0][0]; let dy = ray2[0][1] - ray1[0][1]; let det = (ray2[1][0] * ray1[1][1]) - (ray2[1][1] * ray1[1][0]); if(det == 0) return null; let u = ((dy * ray2[1][0]) - (dx * ray2[1][1])) / det; let v = ((dy * ray1[1][0]) - (dx * ray1[1][1])) / det; return [u, v]; } register(ray) { if(this.segments[ray[0][0]|0] === undefined) this.segments[ray[0][0]|0] = []; if(this.segments[ray[0][0]|0][ray[0][1]|0] === undefined) this.segments[ray[0][0]|0][ray[0][1]|0] = []; this.segments[ray[0][0]|0][ray[0][1]|0].push(ray); } getRandomSegment() { const xKeys = Object.keys(this.segments); const x = xKeys[(Math.random() * xKeys.length) | 0]; const yKeys = Object.keys(this.segments[x]); const y = yKeys[(Math.random() * yKeys.length) | 0]; return this.segments[x][y][(Math.random() * this.segments[x][y].length) | 0]; } } return new SegmentTracker(); } function Wanderer(st, p, h, progressionDirection, progressionArcDeg) { const add2 = (a,b) => [a[0]+b[0], a[1]+b[1]]; const rot2 = (a) => [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; const trans2 = (m, a) => [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; const scale2 = (a,b) => [a[0]*b,a[1]*b]; class Wanderer { constructor(st, position, heading, progressionDirection, progressionArcDeg) { this.active = true; this.segmentTracker = st; this.turtle = new Turtle(position); this.heading = heading; this.direction = progressionDirection; this.progressionArcDeg = progressionArcDeg; this.stepCounter = 0; } step() { this.stepCounter++; let direction = trans2(rot2(Math.PI*(this.stepCounter/180)*this.direction*this.progressionArcDeg), this.heading); let source = this.turtle.pos(); let inter = this.segmentTracker.intersectionInfo(source, direction); if(inter != null) { direction = scale2(direction, inter); this.active = false; } let target = add2(source, direction); this.segmentTracker.register([source, direction]); this.turtle.goto(target); if(target[0] <= -maxRender || maxRender <= target[0] || target[1] <= -maxRender || maxRender <= target[1]) { this.active = false; return false; } } spawn(chance = .5, anglePartOfPI = 8, spawnDirection = 1, chanceEveryXSteps = 1) { if(!this.active || this.stepCounter == 0 || this.stepCounter % chanceEveryXSteps != 0) return null; //const spawnDirection = Math.random() < .5?1:-1; const angleOfSpawn = Math.PI/anglePartOfPI * spawnDirection; if(this.active && Math.random() < chance) { return [this.turtle.pos(), trans2(rot2(Math.PI*(this.stepCounter/180) *this.direction*this.progressionArcDeg + angleOfSpawn), this.heading)]; } } } return new Wanderer(st, p, h, progressionDirection, progressionArcDeg); }