As seen here: paulbourke.net/geometry/supershape
Now with polygons.
Log in to post a comment.
// Forked from "Supershapes / Superformula" by llemarie // https://turtletoy.net/turtle/6976a7ffc8 // LL 2021 // Supershapes / Superformula: http://paulbourke.net/geometry/supershape/ // Variations: // https://turtletoy.net/turtle/df87f9e91d#p_m=4,p_n1=100,p_n2=100,p_n3=100,p_a=1,p_b=1,t_m=4,t_n1=100,t_n2=100,t_n3=100,t_a=1,t_b=1,density=200,scale=40,loops=2.3,edges=6,opacity=0.5,mapping=0,noisy=1,checkerboard=0 // https://turtletoy.net/turtle/df87f9e91d#p_m=10,p_n1=63.9,p_n2=63.41,p_n3=63.41,p_a=1,p_b=1,t_m=10,t_n1=19.99,t_n2=19.86,t_n3=19.86,t_a=1.91,t_b=0.55,density=200,scale=29.24,loops=1,opacity=0.5,mapping=1,noisy=0 // https://turtletoy.net/turtle/df87f9e91d#p_m=6,p_n1=-0.68,p_n2=22.77,p_n3=0.75,p_a=2.99,p_b=0.64,t_m=3,t_n1=65.9,t_n2=0.35,t_n3=49.59,t_a=1.05,t_b=0.28,density=200,scale=40,loops=1,edges=15,opacity=0.5,mapping=1,noisy=0,checkerboard=0 // https://turtletoy.net/turtle/df87f9e91d#p_m=3,p_n1=99.24,p_n2=99.24,p_n3=98.99,p_a=1,p_b=1,t_m=3,t_n1=98.74,t_n2=98.86,t_n3=98.86,t_a=1.91,t_b=0.55,density=322,scale=40,loops=2,edges=15,opacity=0.5,mapping=0,noisy=1,checkerboard=1 // https://turtletoy.net/turtle/df87f9e91d#p_m=8.8,p_n1=21.85,p_n2=39.64,p_n3=3.94,p_a=0.47,p_b=1.8,t_m=10.7,t_n1=17.37,t_n2=4.06,t_n3=4.06,t_a=1.83,t_b=1.3,density=196,scale=40.57,loops=1,edges=3,opacity=0.5,mapping=1,noisy=0,checkerboard=0 // https://turtletoy.net/turtle/df87f9e91d#p_m=17.6,p_n1=0.58,p_n2=8.29,p_n3=1,p_a=1,p_b=1,t_m=0,t_n1=1,t_n2=1,t_n3=1,t_a=1,t_b=1,density=200,scale=37.12,loops=1,edges=5,opacity=0.54,mapping=1,noisy=0,checkerboard=0 const turtle = new Turtle(); const p_m = 0; // min=0 max=100 step=0.1 const p_n1 = 1; // min=-1 max=100 step=0.01 const p_n2 = 1; // min=-1 max=100 step=0.01 const p_n3 = 1; // min=-1 max=100 step=0.01 const p_a = 1; // min=0.01 max=10 step=0.01 const p_b = 1; // min=0.01 max=10 step=0.01 const t_m = 0; // min=0 max=100 step=0.1 const t_n1 = 1; // min=-1 max=100 step=0.01 const t_n2 = 1; // min=-1 max=100 step=0.01 const t_n3 = 1; // min=-1 max=100 step=0.01 const t_a = 1; // min=0.01 max=10 step=0.01 const t_b = 1; // min=0.01 max=10 step=0.01 const density = 100; // min=1 max=200 step=1 const scale = 40; // min=0 max=200 step=0.01 const loops = 1; // min=1 max=20 step=0.1 const edges = 6; // min=0 max=15 step=1 const opacity = 0.5; // min=-1 max=1 step=0.01 const mapping = 1; // min=0 max=1 step=1 (Spherical,Toroidal) const noisy = 0; // min=0 max=1 step=1 (No,Yes) const checkerboard = 0; // min=0 max=1 step=1 (No,Yes) const camera_angle = 0.14; /// min=0 max=1 step=0.01 const camera_height = 150; /// min=0 max=500 step=1 const camera_distance = 400; const exaggeration = 1; /// min=0 max=2 step=0.01 Canvas.setpenopacity(opacity); let quads; const style = 1; /// min=0 max=1 step=1 (Lines (fast),Polygons (slow)) let polygons; function walk(i, t) { if (i==0) { reset_random(); polygons = new Polygons(); cameraPos = [camera_distance * Math.cos(Math.PI * 2 * (camera_angle + t)), camera_height, camera_distance * Math.sin(Math.PI * 2 * (camera_angle + t))]; viewProjectionMatrix = setupCamera(cameraPos, cameraLookAt); quads = []; for (var j=0; j<density*density; j++) { if (checkerboard) if ((j&1)^(((j/density)|0)&1)) continue; const points = []; for (var iy=0; iy<2; iy++) { for (var ix=0; ix<2; ix++) { const ip = ((j+ix) % density) + (noisy ? random() : 0)*0.1; const it = Math.floor(j / density + iy) + (noisy ? random() : 0)*0.1; const phi = -Math.PI/2 + Math.PI * ip / density * loops * (mapping + 1); const theta = -Math.PI + Math.PI * 2 * it / density * loops; points.push(getPoint(phi, theta)); } } quads.push(points); } sortQuads(); } const quad = quads.shift(); drawQuad(quad); if (quads.length == 0) { drawPoints([[-110, -110], [110, -110], [110, 110], [-110, 110]], 0.25) } return quads.length > 0; } function getPoint(phi, theta) { const p_r = getR(p_m, p_n1, p_n2, p_n3, p_a, p_b, phi); const t_r = getR(t_m, t_n1, t_n2, t_n3, t_a, t_b, theta); var x, y, z; if (mapping == 0) { // Spherical x = p_r * Math.cos(phi) * t_r * Math.cos(theta); y = p_r * Math.sin(phi) * t_r * Math.cos(theta); z = t_r * Math.sin(theta); } else { // Toroidal x = Math.cos(phi) * (p_r + t_r * Math.cos(theta)); y = Math.sin(phi) * (p_r + t_r * Math.cos(theta)); z = t_r * Math.sin(theta); } return [x, y, z]; } function dot(x, y, z) { const max_r = 0.5, min_r = 0.1, step = 0.1; [x, y] = project(x * scale, y * scale, z * scale); if (Math.abs(x-max_r) <= 100 && Math.abs(y-max_r) <= 100) { for (var r = max_r; r >= min_r; r -= step) { turtle.jump(x, y-r); turtle.circle(r); } } } function sortQuads() { for (var i1=0; i1<quads.length; i1++) { var min_i = i1; var min_dist = getDistance(quads[i1]); for (var i2=i1+1; i2<quads.length; i2++) { const dist = getDistance(quads[i2]); if (dist < min_dist) { min_i = i2; min_dist = dist; } } if (i1 != min_i) { [quads[i1], quads[min_i]] = [quads[min_i], quads[i1]] } } } function getDistance(quad) { return Math.min(quad[0][2], Math.min(quad[1][2], Math.min(quad[2][2], quad[3][2]))); // TODO: that's not even close to correct. } function drawQuad(quad) { var x, y; const points = []; [0, 1, 3, 2, 0].forEach(j => { [x, y] = project(quad[j][0] * scale, quad[j][1] * scale, quad[j][2] * scale); points.push([x, y]); }); drawPoints(points); } function drawPoints(dpoints, hatching=0) { if (style == 0) { turtle.jump(dpoints[dpoints.length-1]); dpoints.forEach(p=>turtle.goto(p)); } else { const p1 = polygons.create(); p1.addPoints(...dpoints); //p1.addOutline(); if (edges&1) p1.addSegments(p1.cp[0], p1.cp[1]); if (edges&2) p1.addSegments(p1.cp[1], p1.cp[2]); if (edges&4) p1.addSegments(p1.cp[2], p1.cp[3]); if (edges&8) p1.addSegments(p1.cp[3], p1.cp[0]); if (hatching) p1.addHatching(-Math.PI / 4, hatching); polygons.draw(turtle, p1, true); } } function getR(m, n1, n2, n3, a, b, angle) { const t1 = Math.pow(Math.abs(Math.cos(m * angle / 4) / a), n2); const t2 = Math.pow(Math.abs(Math.sin(m * angle / 4) / b), n3); var r = Math.pow(t1 + t2, 1/n1); if (Math.abs(r) != 0) { r = 1 / r; } return r; } function project(x, y, z) { const p = transform4([x, z * -exaggeration, y, 1], viewProjectionMatrix); const s = 50; return [ p[0]/p[3]*s, -p[1]/p[3]*s ]; } //////////////////////////////////////////////////////////////// // Projection from reinder's https://turtletoy.net/turtle/b3acf08303 let cameraPos, viewProjectionMatrix; const cameraLookAt = [0,0,0]; function setupCamera(t,e){const m=lookAt4m(t,e,[0,1,0]),n=perspective4m(.25,1);return multiply4m(n,m)} function lookAt4m(o,n,r){const s=new Float32Array(16);n=normalize3(sub3(o,n)),r=normalize3(cross3(r,n));const t=normalize3(cross3(n,r));return s[0]=r[0],s[1]=t[0],s[2]=n[0],s[3]=0,s[4]=r[1],s[5]=t[1],s[6]=n[1],s[7]=0,s[8]=r[2],s[9]=t[2],s[10]=n[2],s[11]=0,s[12]=-(r[0]*o[0]+r[1]*o[1]+r[2]*o[2]),s[13]=-(t[0]*o[0]+t[1]*o[1]+t[2]*o[2]),s[14]=-(n[0]*o[0]+n[1]*o[1]+n[2]*o[2]),s[15]=1,s} function perspective4m(t,n){const e=new Float32Array(16).fill(0,0);return e[5]=1/Math.tan(t/2),e[0]=e[5]/n,e[10]=e[11]=-1,e} function multiply4m(t,r){const l=new Float32Array(16);for(let n=0;16>n;n+=4)for(let o=0;4>o;o++)l[n+o]=r[n+0]*t[0+o]+r[n+1]*t[4+o]+r[n+2]*t[8+o]+r[n+3]*t[12+o];return l} function transform4(r,n){const t=new Float32Array(4);for(let o=0;4>o;o++)t[o]=n[o]*r[0]+n[o+4]*r[1]+n[o+8]*r[2]+n[o+12];return t} function normalize3(a) { return scale3(a,1/len3(a)); } function scale3(a,b) { return [a[0]*b,a[1]*b,a[2]*b]; } function len3(a) { return Math.sqrt(dot3(a,a)); } function sub3(a,b) { return [a[0]-b[0],a[1]-b[1],a[2]-b[2]]; } function dot3(a,b) { return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; } function cross3(a,b) { return [a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]; } // Cached random for animations function mrandom() { return Math.random(); } function random() { while (rand_index >= rand_cache.length) rand_cache.push(mrandom()); return rand_cache[rand_index++]; } function reset_random() { rand_index = 0; } const rand_cache = []; var rand_index = 0; //////////////////////////////////////////////////////////////// // Polygon Clipping utility code - Created by Reinder Nijhoff 2019 // https://turtletoy.net/turtle/a5befa1f8d //////////////////////////////////////////////////////////////// function Polygons(){let t=[];const s=class{constructor(){this.cp=[],this.dp=[],this.aabb=[]}addPoints(...t){let s=1e5,e=-1e5,h=1e5,i=-1e5;(this.cp=[...this.cp,...t]).forEach(t=>{s=Math.min(s,t[0]),e=Math.max(e,t[0]),h=Math.min(h,t[1]),i=Math.max(i,t[1])}),this.aabb=[(s+e)/2,(h+i)/2,(e-s)/2,(i-h)/2]}addSegments(...t){t.forEach(t=>this.dp.push(t))}addOutline(){for(let t=0,s=this.cp.length;t<s;t++)this.dp.push(this.cp[t],this.cp[(t+1)%s])}draw(t){for(let s=0,e=this.dp.length;s<e;s+=2)t.jump(this.dp[s]),t.goto(this.dp[s+1])}addHatching(t,e){const h=new s;h.cp.push([-1e5,-1e5],[1e5,-1e5],[1e5,1e5],[-1e5,1e5]);const i=Math.sin(t)*e,n=Math.cos(t)*e,a=200*Math.sin(t),p=200*Math.cos(t);for(let t=.5;t<150/e;t++)h.dp.push([i*t+p,n*t-a],[i*t-p,n*t+a]),h.dp.push([-i*t+p,-n*t-a],[-i*t-p,-n*t+a]);h.boolean(this,!1),this.dp=[...this.dp,...h.dp]}inside(t){let s=0;for(let e=0,h=this.cp.length;e<h;e++)this.segment_intersect(t,[.13,-1e3],this.cp[e],this.cp[(e+1)%h])&&s++;return 1&s}boolean(t,s=!0){if(s&&Math.abs(this.aabb[0]-t.aabb[0])-(t.aabb[2]+this.aabb[2])>=0&&Math.abs(this.aabb[1]-t.aabb[1])-(t.aabb[3]+this.aabb[3])>=0)return this.dp.length>0;const e=[];for(let h=0,i=this.dp.length;h<i;h+=2){const i=this.dp[h],n=this.dp[h+1],a=[];for(let s=0,e=t.cp.length;s<e;s++){const h=this.segment_intersect(i,n,t.cp[s],t.cp[(s+1)%e]);!1!==h&&a.push(h)}if(0===a.length)s===!t.inside(i)&&e.push(i,n);else{a.push(i,n);const h=n[0]-i[0],p=n[1]-i[1];a.sort((t,s)=>(t[0]-i[0])*h+(t[1]-i[1])*p-(s[0]-i[0])*h-(s[1]-i[1])*p);for(let h=0;h<a.length-1;h++)(a[h][0]-a[h+1][0])**2+(a[h][1]-a[h+1][1])**2>=.001&&s===!t.inside([(a[h][0]+a[h+1][0])/2,(a[h][1]+a[h+1][1])/2])&&e.push(a[h],a[h+1])}}return(this.dp=e).length>0}segment_intersect(t,s,e,h){const i=(h[1]-e[1])*(s[0]-t[0])-(h[0]-e[0])*(s[1]-t[1]);if(0===i)return!1;const n=((h[0]-e[0])*(t[1]-e[1])-(h[1]-e[1])*(t[0]-e[0]))/i,a=((s[0]-t[0])*(t[1]-e[1])-(s[1]-t[1])*(t[0]-e[0]))/i;return n>=0&&n<=1&&a>=0&&a<=1&&[t[0]+n*(s[0]-t[0]),t[1]+n*(s[1]-t[1])]}};return{list:()=>t,create:()=>new s,draw:(s,e,h=!0)=>{for(let s=0;s<t.length&&e.boolean(t[s]);s++);e.draw(s),h&&t.push(e)}}}