An imperfect grid of imperfect circles.
Set sliders to zero for randomness.
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(1);
// UI settings
let padding = 0; // min=0, max=100, step=5
let grid = 0 // min=0, max=6, step=1
// setup
var width = 200
var height = 200
var start = {x:-100, y:-100}
const random = mulberry32(Date.now());
const big_blobs = 3
const small_blobs = 2
const square_iterations = 4
const iterations = Math.max(big_blobs, small_blobs,square_iterations)
const blob_step = 0.1
// Global code will be evaluated once.
const turtle = new Turtle();
turtle.jump(start.x,start.y);
turtle.pendown();
if (grid == 0) grid = Math.floor(randomIn(1.1,3.1)) * 2
if (padding == 0) padding = Math.floor(randomIn(20,40))
// The walk function will be called until it returns false.
function walk(i) {
if (i>=iterations) return false
buildGrid(i)
return true
}
function buildGrid(iter) {
let w = (width - padding) / (grid+1);
let h = (height - padding) / (grid+1);
let gridSpan = (width - grid * w) / (grid + 1);
let x = 0
let y = gridSpan
for (let i = 0; i < grid; i++) {
x = gridSpan + randomIn(0, 3);
for (let j = 0; j < grid; j++) {
drawRect(x, y, w, h)
if (iter < big_blobs) drawBlob(x, y, w)
if (iter < small_blobs) drawBlob(x + w / 4, y + w / 4, w / 2)
x += w + gridSpan;
}
y += h + gridSpan
}
}
function drawRect(x, y, w, h) {
x = x + start.x
y = y + start.y
turtle.jump( x+posNoise(), y+posNoise())
turtle.goto( x+w+posNoise(), y+posNoise())
turtle.jump(x + w + posNoise(), y + posNoise())
turtle.goto(x + w + posNoise(), y + h + posNoise())
turtle.jump(x + posNoise(), y + h + posNoise())
turtle.goto(x + w + posNoise(), y + h + posNoise())
turtle.jump(x + posNoise(), y + h + posNoise())
turtle.goto(x + posNoise(), y + posNoise());
}
function drawBlob(cx, cy, diameter) {
let radius = diameter / 2
let delta = random(diameter / 20, diameter/10)
cx = cx + start.x + radius
cy = cy + start.y + radius
radius = radius - random(delta)
let x = 0
let y = 0
let cxoff = cx
let cyoff = cy
let close = {x:0, y:0}
for (var a = 0; a < 2*Math.PI; a += blob_step) {
let offset = map(noise(cxoff, cyoff), 0, 1, -delta, delta);
let r = radius + offset;
x = cx+r * Math.cos(a);
y = cy+r * Math.sin(a);
if (a==0) {
close = { x: x, y:y}
turtle.jump(x,y)
}
else turtle.goto(x, y);
cxoff += 0.1
cyoff += 0.1
}
turtle.goto(close.x, close.y)
}
// utilities
class Noise {
// http://mrl.nyu.edu/~perlin/noise/
constructor(octaves = 1) {
this.p = new Uint8Array(512);
this.octaves = octaves;
for (let i = 0; i < 512; ++i) {
this.p[i] = Math.random() * 256*100;
}
}
lerp(t, a, b) {
return a + t * (b - a);
}
grad2d(i, x, y) {
const v = (i & 1) === 0 ? x : y;
return (i & 2) === 0 ? -v : v;
}
noise2d(x2d, y2d) {
const X = Math.floor(x2d) & 255;
const Y = Math.floor(y2d) & 255;
const x = x2d - Math.floor(x2d);
const y = y2d - Math.floor(y2d);
const fx = (3 - 2 * x) * x * x;
const fy = (3 - 2 * y) * y * y;
const p0 = this.p[X] + Y;
const p1 = this.p[X + 1] + Y;
return this.lerp(
fy,
this.lerp(
fx,
this.grad2d(this.p[p0], x, y),
this.grad2d(this.p[p1], x - 1, y)
),
this.lerp(
fx,
this.grad2d(this.p[p0 + 1], x, y - 1),
this.grad2d(this.p[p1 + 1], x - 1, y - 1)
)
);
}
noise(x, y, scale=0.5) {
let e = 1,
k = 1,
s = 0;
for (let i = 0; i < this.octaves; ++i) {
e *= scale; // This constant factor will adjust where the lines are drawn
s += e * (1 + this.noise2d(k * x, k * y)) / 2;
k *= 2;
}
return s;
}
}
const perlin = new Noise(3);
function noise(x,y) {
return perlin.noise2d(x,y)
}
function posNoise(type) {
return (randomIn(-3,3))
}
function randomIn(min, max) {
return random() * (max - min) + min;
}
function map(number, inMin, inMax, outMin, outMax) {
return (number - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
function mulberry32(a) {
return function() {
var t = a += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}