UNPKG

@guinetik/penrose-js

Version:

A JavaScript library for generating beautiful Penrose tilings

1 lines 6.11 kB
!function(t,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports):"function"==typeof define&&define.amd?define(["exports"],o):o((t="undefined"!=typeof globalThis?globalThis:t||self).penrosejs={})}(this,(function(t){"use strict";class o{constructor(t,o=0){this.real=t,this.imag=o}static fromPolar(t,r){return new o(t*Math.cos(r),t*Math.sin(r))}add(t){return new o(this.real+t.real,this.imag+t.imag)}subtract(t){return new o(this.real-t.real,this.imag-t.imag)}multiply(t){return new o(this.real*t.real-this.imag*t.imag,this.real*t.imag+this.imag*t.real)}divide(t){return new o(this.real/t,this.imag/t)}scale(t){return new o(this.real*t,this.imag*t)}abs(){return Math.sqrt(this.real*this.real+this.imag*this.imag)}}const r=(Math.sqrt(5)+1)/2;function e(t){const o=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return o?[parseInt(o[1],16)/255,parseInt(o[2],16)/255,parseInt(o[3],16)/255,1]:[0,0,0,1]}function n(t){let e=[];for(let t=0;t<10;t++){const r=o.fromPolar(1,(2*t-1)*Math.PI/10),n=o.fromPolar(1,(2*t+1)*Math.PI/10);t%2==0?e.push(["thin",new o(0),n,r]):e.push(["thin",new o(0),r,n])}for(let o=0;o<t;o++){const t=[];for(const[o,n,a,i]of e)if("thin"===o){const o=n.add(a.subtract(n).scale(1/r));t.push(["thin",i,o,a]),t.push(["thicc",o,i,n])}else{const o=a.add(n.subtract(a).scale(1/r)),e=a.add(i.subtract(a).scale(1/r));t.push(["thicc",e,i,n]),t.push(["thicc",o,e,a]),t.push(["thin",e,o,n])}e=t}return e}const a={divisions:5,zoomType:"in",width:800,height:800,color1:[1,0,0,1],color2:[0,0,1,1],color3:[0,0,0,1],backgroundColor:[1,1,1,1]};class i{constructor(t={}){this.options={...a,...t}}generatePenroseTiling(t){throw new Error("Not implemented - use a concrete subclass")}_processOptions(t){const o={...this.options,...t};return"string"==typeof o.color1&&(o.color1=e(o.color1)),"string"==typeof o.color2&&(o.color2=e(o.color2)),"string"==typeof o.color3&&(o.color3=e(o.color3)),"string"==typeof o.backgroundColor&&(o.backgroundColor=e(o.backgroundColor)),o}}t.Complex=o,t.DEFAULT_OPTIONS=a,t.PHI=r,t.Penrose=i,t.PenroseBitmap=class extends i{generatePenroseTiling(t){const o=this._processOptions(t),{divisions:r,zoomType:e,width:a,height:i,color1:l,color2:s,color3:h,backgroundColor:c}=o,f=new Uint8ClampedArray(a*i*4);for(let t=0;t<f.length;t+=4)f[t]=255*c[0],f[t+1]=255*c[1],f[t+2]=255*c[2],f[t+3]=255*(c[3]||1);const u="in"===e?1:2,d=Math.max(a,i),g=d/u,m=d/u,M=.5*u,y=.5*u,p=n(r);function x(t){return{x:Math.floor((t.real*g+M*g)*a/d),y:Math.floor((t.imag*m+y*m)*i/d)}}for(const[t,o,r,e]of p){const n=x(o),h=x(r),c=x(e),u="thin"===t?l:s;this._fillTriangle(f,n,h,c,u,a,i)}if(h&&h[3]>0)for(const[t,o,r,e]of p){const t=x(o),n=x(r),l=x(e);this._drawLine(f,t,n,h,a,i),this._drawLine(f,n,l,h,a,i),this._drawLine(f,l,t,h,a,i)}return f}_fillTriangle(t,o,r,e,n,a,i){o.y>r.y&&([o,r]=[r,o]),o.y>e.y&&([o,e]=[e,o]),r.y>e.y&&([r,e]=[e,r]);const l=Math.round(255*n[0]),s=Math.round(255*n[1]),h=Math.round(255*n[2]),c=Math.round(255*(n[3]||1));if(r.y===e.y)this._fillFlatBottomTriangle(t,o,r,e,l,s,h,c,a,i);else if(o.y===r.y)this._fillFlatTopTriangle(t,o,r,e,l,s,h,c,a,i);else{const n={x:Math.floor(o.x+(r.y-o.y)/(e.y-o.y)*(e.x-o.x)),y:r.y};this._fillFlatBottomTriangle(t,o,r,n,l,s,h,c,a,i),this._fillFlatTopTriangle(t,r,n,e,l,s,h,c,a,i)}}_fillFlatBottomTriangle(t,o,r,e,n,a,i,l,s,h){const c=(r.x-o.x)/(r.y-o.y||1),f=(e.x-o.x)/(e.y-o.y||1);let u=o.x,d=o.x;for(let e=o.y;e<=r.y;e++){if(e>=0&&e<h){const o=Math.max(0,Math.min(Math.floor(u),s-1)),r=Math.max(0,Math.min(Math.floor(d),s-1));for(let h=Math.min(o,r);h<=Math.max(o,r);h++){const o=4*(e*s+h);o>=0&&o<t.length-3&&(t[o]=n,t[o+1]=a,t[o+2]=i,t[o+3]=l)}}u+=c,d+=f}}_fillFlatTopTriangle(t,o,r,e,n,a,i,l,s,h){const c=(e.x-o.x)/(e.y-o.y||1),f=(e.x-r.x)/(e.y-r.y||1);let u=e.x,d=e.x;for(let r=e.y;r>o.y;r--)if(r>=0&&r<h){u-=c,d-=f;const o=Math.max(0,Math.min(Math.floor(u),s-1)),e=Math.max(0,Math.min(Math.floor(d),s-1));for(let h=Math.min(o,e);h<=Math.max(o,e);h++){const o=4*(r*s+h);o>=0&&o<t.length-3&&(t[o]=n,t[o+1]=a,t[o+2]=i,t[o+3]=l)}}}_drawLine(t,o,r,e,n,a){const i=Math.round(255*e[0]),l=Math.round(255*e[1]),s=Math.round(255*e[2]),h=Math.round(255*(e[3]||1));let c=o.x,f=o.y,u=r.x,d=r.y;const g=Math.abs(u-c),m=Math.abs(d-f),M=c<u?1:-1,y=f<d?1:-1;let p=g-m;for(;;){if(c>=0&&c<n&&f>=0&&f<a){const o=4*(f*n+c);if(o>=0&&o<t.length-3){const r=h/255;t[o]=Math.round(t[o]*(1-r)+i*r),t[o+1]=Math.round(t[o+1]*(1-r)+l*r),t[o+2]=Math.round(t[o+2]*(1-r)+s*r),t[o+3]=255}}if(c===u&&f===d)break;const o=2*p;o>-m&&(p-=m,c+=M),o<g&&(p+=g,f+=y)}}},t.PenroseCanvas=class extends i{generatePenroseTiling(t){if(!t.ctx)throw new Error("Canvas context (ctx) is required for PenroseCanvas");const o=this._processOptions(t),{ctx:r,divisions:e,zoomType:a,width:i,height:l,color1:s,color2:h,color3:c,backgroundColor:f}=o;r.setTransform(1,0,0,1,0,0),r.clearRect(0,0,i,l),f&&(r.fillStyle=`rgba(${Math.round(255*f[0])}, ${Math.round(255*f[1])}, ${Math.round(255*f[2])}, ${f[3]||1})`,r.fillRect(0,0,i,l));const u="in"===a?1:2,d=Math.max(i,l);r.scale(d/u,d/u),r.translate(.5*u,.5*u);const g=n(e);r.beginPath();for(const[t,o,e,n]of g)"thin"===t&&(r.moveTo(o.real,o.imag),r.lineTo(e.real,e.imag),r.lineTo(n.real,n.imag),r.closePath());r.fillStyle=`rgba(${Math.round(255*s[0])}, ${Math.round(255*s[1])}, ${Math.round(255*s[2])}, ${s[3]||1})`,r.fill(),r.beginPath();for(const[t,o,e,n]of g)"thicc"===t&&(r.moveTo(o.real,o.imag),r.lineTo(e.real,e.imag),r.lineTo(n.real,n.imag),r.closePath());r.fillStyle=`rgba(${Math.round(255*h[0])}, ${Math.round(255*h[1])}, ${Math.round(255*h[2])}, ${h[3]||1})`,r.fill(),r.beginPath();for(const[t,o,e,n]of g)r.moveTo(e.real,e.imag),r.lineTo(o.real,o.imag),r.lineTo(n.real,n.imag);r.strokeStyle=`rgba(${Math.round(255*c[0])}, ${Math.round(255*c[1])}, ${Math.round(255*c[2])}, ${c[3]||1})`;const m=function(t){return t>3?Math.pow(t,-3):Math.pow(t,-5)}(e);return r.lineWidth=m,r.lineJoin="round",r.stroke(),r.setTransform(1,0,0,1,0,0),r}},t.getRandomColor=function(){let t="#";for(let o=0;o<6;o++)t+="0123456789ABCDEF"[Math.floor(16*Math.random())];return t},t.hexToRgb=e,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));