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