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);
}