smolskulls in turtle
smolskulls.xyz
Log in to post a comment.
/**
* SMOLSKULLs is a generative art NFT series minted on the Tezos blockchain
* and originated on fxhash.xyz by Mark Knol. <https://smolskulls.xyz>
*
* SMOLSKULLs are released under the NFT License 2.0.
* https://www.nftlicense.org/
* TLDR; You need to ask permission to reuse. Please DM @mknol on Twitter.
*/
const turtle = new Turtle();
//const turtle = new Slowpoke();
function getFaceProps() {
let faceProps;
while(!faceProps || (faceProps.eyeHeight == 3 && faceProps.mouthSize == 2)) {
faceProps = {
eyeHeight: pick([0, 1, 2, 3]),
mouthType: pick([0, 1, 2]),
mouthSize: pick([0, 1, 2, 3]),
eyeDirection: pick([0, 1, 2, 3]),
eyeDirectionOnly: fxrand() > 0.1,
};
}
return faceProps
}
let defaultFaceProps = getFaceProps()
// make similar
let makeSimilar = 0; // min=0, max=1, step=1 (false, true)
let grid = 5; // min=1, max=23, step=2
let skullScale = 0.99; // min=0.5, max=1.5, step=0.01
let doFillRect = 1; // min=0, max=1, step=1 (false, true)
const size = 200;
let scale = size / grid;
function fillRect(shape, intensity = 0.2) {
if (shape[1][0] - shape[0][0] > shape[2][1] - shape[0][1]) {
for(let x=shape[0][0];x<shape[1][0];x+=intensity) {
turtle.jump(x,shape[0][1]);
turtle.goto(x,shape[2][1]);
}
} else {
for(let y=shape[0][1];y<shape[2][1];y+=intensity) {
turtle.jump(shape[0][0],y);
turtle.goto(shape[1][0],y);
}
}
}
function walk(i) {
const x = (i % grid) - ((grid / 2) | 0);
const y = ((i / grid) | 0) - ((grid / 2) | 0);
let xr = (i % grid) / grid;
let yr = ((i / grid) | 0) / grid;
let shapes = createSkull(x, y, skullScale);
let shapeIdx = 0;
for (let shape of shapes) {
shape = shape.map(p1 => posToPx(scl2(p1, scale)));
shape.forEach((p1, idx, pts) => {
if (idx == 0) {
turtle.jump(p1[0], p1[1]);
} else {
turtle.goto(p1[0], p1[1]);
}
if (idx == pts.length - 1) {
let p2 = pts[(idx + 1) % pts.length];
turtle.goto(p2[0], p2[1]);
}
});
if (shapeIdx >= 4 && doFillRect) {
fillRect(shape)
}
shapeIdx++;
}
return i < (grid * grid) -1;
}
function posToPx(p, rotation = 0) {
p = rotate(p, rotation);
p = [toPx(p[0] *2), toPx(p[1] *2)];
return p;
}
function createSkull(x, y, scale = 1) {
let s = 11;
let px1 = 1 / s;
let px2 = 2 / s;
let px3 = 3 / s;
let px4 = 4 / s;
let chinX = pick([px2, px1]);
let chinY = pick([px1, px2, px3, px4]);
chinX = px2;
chinY = px3;
let faceProps = makeSimilar ? defaultFaceProps : getFaceProps();
function rect(x, y, w, h) {
[x, y, w, h] = [x * px1, y * px1, w * px1, h * px1];
return [
[x, y],
[x + w, y],
[x + w, y + h],
[x, y + h],
];
}
function arc(x, y, r, a1, a2, quality = 0.05) {
[x, y, r] = [x * px1, y * px1, r * px1];
a1 += Math.PI / 2;
a2 += Math.PI / 2;
let pts = [];
for (let a = a2; a >= a1; a -= quality) {
pts.push([x + Math.sin(a) * r, y + Math.cos(a) * r]);
}
return pts;
}
let eyeY = pick([1, 2, 3]);
if (faceProps.mouthSize == 2) eyeY = 1;
let mouthY = eyeY + 1 + faceProps.eyeHeight + pick([2, 3]);
let smolskull = [];
smolskull.push(rect(-3,-3, s+6,s+6))
smolskull.push([
// head
...arc(1, 1, 1, -Math.PI - Math.PI / 2, -Math.PI),
[px1, 0],
...arc(s - 1, 1, 1, 0, Math.PI / 2),
[1, 1 - chinY],
[1 - chinX, 1 - chinY],
[1 - chinX, 1],
[chinX, 1],
[chinX, 1 - chinY],
[0, 1 - chinY],
]);
// eyes
smolskull.push(rect(2, eyeY, 3, faceProps.eyeHeight + 2));
smolskull.push(rect(6, eyeY, 3, faceProps.eyeHeight + 2));
if (faceProps.eyeDirectionOnly) {
if (faceProps.eyeDirection == 0) {
smolskull.push(rect(2, eyeY, 1, faceProps.eyeHeight + 2));
smolskull.push(rect(6, eyeY, 1, faceProps.eyeHeight + 2));
} else if (faceProps.eyeDirection == 1) {
smolskull.push(rect(2 + 2, eyeY, 1, faceProps.eyeHeight + 2));
smolskull.push(rect(6 + 2, eyeY, 1, faceProps.eyeHeight + 2));
} else if (faceProps.eyeDirection == 2) {
smolskull.push(rect(2, faceProps.eyeHeight + eyeY + 1, 3, 1));
smolskull.push(rect(6, faceProps.eyeHeight + eyeY + 1, 3, 1));
} else if (faceProps.eyeDirection == 3) {
smolskull.push(rect(2, eyeY, 3, 1));
smolskull.push(rect(6, eyeY, 3, 1));
}
}
//mouth
let mouthHack = 0;//0.0375;
if (faceProps.mouthType == 0) {
// mouth []
smolskull.push(rect(3, mouthY, 5, Math.min(s - mouthY + mouthHack, (faceProps.mouthSize + 1 + mouthHack) * 2)));
} else if (faceProps.mouthType == 1) {
// mouth []
smolskull.push(rect(4, mouthY, 3, Math.min(s - mouthY + mouthHack, (faceProps.mouthSize + 1 + mouthHack) * 2)));
} else {
// mouth |||
smolskull.push(rect(3, mouthY, 1, Math.min(s - mouthY + mouthHack, (faceProps.mouthSize + 1 + mouthHack) * 2)));
smolskull.push(rect(5, mouthY, 1, Math.min(s - mouthY + mouthHack, (faceProps.mouthSize + 1 + mouthHack) * 2)));
smolskull.push(rect(7, mouthY, 1, Math.min(s - mouthY + mouthHack, (faceProps.mouthSize + 1 + mouthHack) * 2)));
}
// scale and position
let newSize = 17;
let newOffset = 3;
smolskull = smolskull.map((arr) => arr.map((p) => add2(scl2(add2([-0.5 + newOffset / newSize, -0.5 + newOffset / newSize], [(p[0] / newSize) * s, (p[1] / newSize) * s]), scale), [x, y])));
return smolskull;
}
function scl2(a, b) {
return [a[0] * b, a[1] * b];
}
function add2(a, b) {
return [a[0] + b[0], a[1] + b[1]];
}
function sub2(a, b) {
return [a[0] - b[0], a[1] - b[1]];
}
function dot2(a, b) {
return a[0] * b[0] + a[1] * b[1];
}
function len(a) {
return Math.sqrt(a[0] ** 2 + a[1] ** 2);
}
function dist2(a, b) {
return len(sub2(a, b));
}
function nrm2(a) {
return scl2(a, 1 / len(a));
}
function lrp2(a, b, f) {
return [lrp(a[0], b[0], f), lrp(a[1], b[1], f)];
}
function clamp01(v) {
return v < 0 ? 0 : v > 1 ? 1 : v;
}
function rotate(p, a) {
return [p[0] * Math.cos(a) + p[1] * Math.sin(a), p[1] * Math.cos(a) - p[0] * Math.sin(a)];
}
function toPx(unit) {
return (unit / 200) * 100;
}
function lrp(a, b, f) {
return a + (b - a) * f;
}
function range(a, b) {
return a + (b - a) * fxrand();
}
function pick(arr) {
return arr[(fxrand() * arr.length) | 0];
}
function fxrand() {
// fxrand is the random from fxhash, from which smolskull originates
// https://www.fxhash.xyz/generative/14
return Math.random()
}
////////////////////////////////////////////////////////////////
// Slowpoke utility code. Created by Reinder Nijhoff 2019
// https://turtletoy.net/turtle/cfe9091ad8
////////////////////////////////////////////////////////////////
function Slowpoke(x, y) {
const linesDrawn = {};
class Slowpoke extends Turtle {
goto(x, y) {
const p = Array.isArray(x) ? [...x] : [x, y];
if (this.isdown()) {
const o = [this.x(), this.y()];
const h1 = o[0].toFixed(2)+'_'+p[0].toFixed(2)+o[1].toFixed(2)+'_'+p[1].toFixed(2);
const h2 = p[0].toFixed(2)+'_'+o[0].toFixed(2)+p[1].toFixed(2)+'_'+o[1].toFixed(2);
if (linesDrawn[h1] || linesDrawn[h2]) {
super.up();
super.goto(p);
super.down();
return;
}
linesDrawn[h1] = linesDrawn[h2] = true;
}
super.goto(p);
}
}
return new Slowpoke(x,y);
}