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