Log in to post a comment.
const turbulence = .8; //min=0 max=1 step=.02 const lines = 250; //min=10 max=500 step=1 const contrast = .75; //min=0 max=1 step=.05 const radius = 60; //min=10 max=90 step=1 const colors = 20; // You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(1/colors); const directionZoom = .0001 + .0049 * turbulence; // Global code will be evaluated once. const bales = Bales(colors, true); const uniform = (pts) => { const candidates = Array.from({length: 10}).map(v => Math.random() * 1000); let winner = candidates[0]; if(pts.length > 0) { const ptsd = pts.map(v => v * 1000 - 1000).concat(pts.map(v => v*1000), pts.map(v => v * 1000 + 1000)); winner = candidates.map(c => [ c, Math.min(c - ptsd.filter(v => v <= c).sort((a,b) => a - b).pop(), ptsd.filter(v => v >= c).sort((a,b) => a - b).shift() - c) ]) .sort((a,b) => a[1] - b[1]).pop()[0]; } pts.push(winner / 1000); return pts[pts.length - 1]; } const pts = []; const otherColor = ((colors * contrast) | 0) - 1; // The walk function will be called until it returns false. function walk(i) { const noise = [ Math.random(), //offset new SimplexNoise((Math.random() * 100) | 0) //generator ]; const skew = [Math.random()*1000 - 500, Math.random()*1000 - 500] let pt = [uniform(pts) * 800].map(v => v < 200? [v - 100, -100]:v < 400? [100, v - 300]:v < 600? [v - 500, 100]:[-100, v - 700]).pop(); bales[0].jump(pt); bales[otherColor].jump(pt); while(-100 <= pt[0] && pt[0] <= 100 && -100 <= pt[1] && pt[1] <= 100) { const dir = radNoise(noise, add2(skew, pt)); pt = add2(pt, trans2(rot2(dir), [1,0])); if(lenSq2(pt) < radius**2) { bales[0].jump(pt); bales[otherColor].goto(pt); } else { bales[0].goto(pt); bales[otherColor].jump(pt); } } return i < lines; } function radNoise(n, pt) { return (n[1].noise2D(scale2(pt, directionZoom)) + n[0]) * Math.PI; } function add2(a, b) { return [a[0]+b[0], a[1]+b[1]]; } function scale2(a, s) { return [a[0]*s,a[1]*s]; } function lenSq2(a) { return a[0]**2+a[1]**2; } function rot2(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; } function trans2(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; } //Matrix(2x1) x Matrix(2x2) //////////////////////////////////////////////////////////////// // Simplex Noise utility code. Created by Reinder Nijhoff 2020 // https://turtletoy.net/turtle/6e4e06d42e // Based on: http://webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf //////////////////////////////////////////////////////////////// function SimplexNoise(seed = 1) {const grad = [ [1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0],[1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1],[0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1] ];const perm = new Uint8Array(512);const F2 = (Math.sqrt(3) - 1) / 2, F3 = 1/3;const G2 = (3 - Math.sqrt(3)) / 6, G3 = 1/6;const dot2 = (a, b) => a[0] * b[0] + a[1] * b[1];const sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]];const dot3 = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];const sub3 = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];class SimplexNoise {constructor(seed = 1) {for (let i = 0; i < 512; i++) {perm[i] = i & 255;}for (let i = 0; i < 255; i++) {const r = (seed = this.hash(i+seed)) % (256 - i) + i;const swp = perm[i];perm[i + 256] = perm[i] = perm[r];perm[r + 256] = perm[r] = swp;}}noise2D(p) {const s = dot2(p, [F2, F2]);const c = [Math.floor(p[0] + s), Math.floor(p[1] + s)];const i = c[0] & 255, j = c[1] & 255;const t = dot2(c, [G2, G2]);const p0 = sub2(p, sub2(c, [t, t]));const o = p0[0] > p0[1] ? [1, 0] : [0, 1];const p1 = sub2(sub2(p0, o), [-G2, -G2]);const p2 = sub2(p0, [1-2*G2, 1-2*G2]);let n = Math.max(0, 0.5-dot2(p0, p0))**4 * dot2(grad[perm[i+perm[j]] % 12], p0);n += Math.max(0, 0.5-dot2(p1, p1))**4 * dot2(grad[perm[i+o[0]+perm[j+o[1]]] % 12], p1);n += Math.max(0, 0.5-dot2(p2, p2))**4 * dot2(grad[perm[i+1+perm[j+1]] % 12], p2);return 70 * n;}hash(i) {i = 1103515245 * ((i >> 1) ^ i);const h32 = 1103515245 * (i ^ (i>>3));return h32 ^ (h32 >> 16);}}return new SimplexNoise(seed);} //////////////////////////////////////////////////////////////// // Bale utility code - Created by Jurgen Westerhof 2022 // https://turtletoy.net/turtle/7269af8a23 // Abusing the opacity, usage: // Canvas.setpenopacity(1/baleSize); // const bales = Array.apply(null,{length: baleSize}).map(b => new Bale(baleSize--); // Then use bales[x] wherever you would use a turtle object to 'draw' // in 'color' x (i.e Polygon hatching with a bale object and .15 interspacing) //////////////////////////////////////////////////////////////// function Bale(n) { class Bale { constructor(n) { this.turtles = Array.apply(null,{length: n}).map(i => new Turtle()); } back(e) { this.turtles.map(t => t.back(e)); return this; } backward(e) { this.turtles.map(t => t.backward(e)); return this; } bk(e) { this.turtles.map(t => t.bk(e)); return this; } fd(e) { this.turtles.map(t => t.fd(e)); return this; } forward(e) { this.turtles.map(t => t.forward(e)); return this; } left(e) { this.turtles.map(t => t.left(e)); return this; } lt(e) { this.turtles.map(t => t.lt(e)); return this; } right(e) { this.turtles.map(t => t.right(e)); return this; } rt(e) { this.turtles.map(t => t.rt(e)); return this; } seth(e) { this.turtles.map(t => t.seth(e)); return this; } setheading(e) { this.turtles.map(t => t.setheading(e)); return this; } setx(e) { this.turtles.map(t => t.setx(e)); return this; } sety(e) { this.turtles.map(t => t.sety(e)); return this; } setpos(x, y) { this.turtles.map(t => t.setpos(x, y)); return this; } setposition(x, y) { this.turtles.map(t => t.setposition(x, y)); return this; } toradians(e) { this.turtles.map(t => t.toradians(e)); return this; } degrees(e) { this.turtles.map(t => t.degrees(e)); return this; } goto(x, y) { this.turtles.map(t => t.goto(x, y)); return this; } jmp(x, y) { this.turtles.map(t => t.jmp(x, y)); return this; } jump(x, y) { this.turtles.map(t => t.jump(x, y)); return this; } circle(radius, extent, steps) { this.turtles.map(t => t.circle(radius, extent, steps)); return this; } clone() { let b = new Bale(this.turtle.length); this.turtles.map((t, k) => b.turtles[k] = t.clone()); return b; } h() { return this.turtles[0].h(); } heading() { return this.turtles[0].heading(); } home() { this.turtles.map(t => t.home()); return this; } isdown() { return this.turtles[0].isdown(); } pos() { return this.turtles[0].pos(); } position() { return this.turtles[0].position(); } pd() { this.turtles.map(t => t.pd()); return this; } pendown() { this.turtles.map(t => t.pendown()); return this; } penup() { this.turtles.map(t => t.penup()); return this; } pu() { this.turtles.map(t => t.pu()); return this; } down() { this.turtles.map(t => t.down()); return this; } up() { this.turtles.map(t => t.up()); return this; } radians() { this.turtles.map(t => t.radians()); return this; } x() { return this.turtles[0].x(); } xcor() { return this.turtles[0].xcor(); } y() { return this.turtles[0].y(); } ycor() { return this.turtles[0].ycor(); } } return new Bale(n); } function Bales(count, includeWhite = false) { if(count == 1) return [new Bale(1)]; const getExponent = (base, target) => Math.log(target) / Math.log(base); const baleSize = count - (includeWhite?1:0); const n = Array.apply(null,{length: baleSize}).map((v,k) => Math.round(getExponent(1 - 1/count, 1 - (count - k == count?.99:(baleSize - k)/baleSize)))); if(includeWhite) n.push(0); return n.map(i => new Bale(i)); }