@raducal/isomer
Version:
A simple isometric graphics library for HTML5 canvas
2 lines (1 loc) • 9.99 kB
JavaScript
"use strict";var z=Object.defineProperty;var P=(x,t,s)=>t in x?z(x,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):x[t]=s;var r=(x,t,s)=>(P(x,typeof t!="symbol"?t+"":t,s),s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class m{constructor(t,s){r(this,"elem");r(this,"ctx");r(this,"width");r(this,"height");r(this,"draw",(t,s,e)=>{if(e){const a=this.calculateBoundingBox(t);this.ctx.lineWidth=Math.min(a.width,a.height)*.2,this.ctx.drawImage(e,a.x,a.y,a.width,a.height)}else this.ctx.fillStyle=s.toHex(),this.ctx.fill();this.ctx.stroke(),this.ctx.restore()});this.elem=t;const e=this.elem.getContext("2d",{willReadFrequently:(s==null?void 0:s.willReadFrequently)??!1,alpha:(s==null?void 0:s.alpha)??!0});if(!e)throw new Error("Context is null");this.ctx=e;const a=(s==null?void 0:s.dpr)||1;this.width=this.elem.width*a,this.height=this.elem.height*a}clear(){this.ctx.clearRect(0,0,this.width,this.height)}path(t,s,e){this.ctx.lineWidth=1,this.ctx.beginPath(),this.ctx.moveTo(t[0].x,t[0].y);for(let a=1;a<t.length;a++)this.ctx.lineTo(t[a].x,t[a].y);this.ctx.closePath(),this.ctx.save(),this.ctx.globalAlpha=s.a,this.ctx.strokeStyle=s.toHex(),this.ctx.clip(),this.draw(t,s,e)}calculateBoundingBox(t){const s=t.map(o=>o.x),e=t.map(o=>o.y),a=Math.min(...s),h=Math.min(...e),n=Math.max(...s),c=Math.max(...e);return{x:a,y:h,width:n-a,height:c-h}}}class y{constructor(t,s,e,a=255){r(this,"r");r(this,"g");r(this,"b");r(this,"a");r(this,"h");r(this,"s");r(this,"l");this.init(t,s,e,a)}update(t,s,e,a=255){this.init(t,s,e,a)}init(t,s,e,a=255){this.r=Math.round(t),this.g=Math.round(s),this.b=Math.round(e),this.a=Math.round(a)}duplicate(){return new y(this.r,this.g,this.b,this.a)}toHex(){let t=(Math.floor(this.r)*256*256+Math.floor(this.g)*256+Math.floor(this.b)).toString(16);return t.length<6&&(t=new Array(6-t.length+1).join("0")+t),"#"+t}lighten(t,s=new y(255,255,255)){this.r=s.r/255*this.r,this.g=s.g/255*this.g,this.b=s.b/255*this.b,this.a=s.a/255*this.a;const e=this.calculateHsl();this.h=e.h,this.s=e.s,this.l=e.l,this.l=Math.min(this.l+t,1);const a=this.calculateRgb();return this.r=a.r,this.g=a.g,this.b=a.b,this}calculateHsl(){const t=this.r/255,s=this.g/255,e=this.b/255,a=Math.max(t,s,e),h=Math.min(t,s,e);let n=0,c,o=(a+h)/2;if(a===h)n=c=0;else{const p=a-h;switch(c=o>.5?p/(2-a-h):p/(a+h),a){case t:n=(s-e)/p+(s<e?6:0);break;case s:n=(e-t)/p+2;break;case e:n=(t-s)/p+4;break}n/=6}return{h:n,s:c,l:o}}calculateRgb(){let t,s,e;const a=this.h,h=this.s,n=this.l;if(h===0)t=s=e=n;else{const c=n<.5?n*(1+h):n+h-n*h,o=2*n-c;t=this.hue2rgb(o,c,a+1/3),s=this.hue2rgb(o,c,a),e=this.hue2rgb(o,c,a-1/3)}return t*=255,s*=255,e*=255,{r:t,g:s,b:e}}hue2rgb(t,s,e){return e<0&&(e+=1),e>1&&(e-=1),e<1/6?t+(s-t)*6*e:e<1/2?s:e<2/3?t+(s-t)*(2/3-e)*6:t}}const f=class f{constructor(t=0,s=0,e=0){r(this,"x");r(this,"y");r(this,"z");this.init(t,s,e)}static FromPoint(t){return new f(t.x,t.y,t.z)}init(t=0,s=0,e=0){this.x=t,this.y=s,this.z=e}update(t=0,s=0,e=0){this.init(t,s,e)}translate(t=0,s=0,e=0){return new f(this.x+t,this.y+s,this.z+e)}scale(t,s,e,a){let h=this.translate(-t.x,-t.y,-t.z);return e=typeof e=="number"?e:s,a=typeof a=="number"?a:1,h.x*=s,h.y*=e,h.z*=a,h.translate(t.x,t.y,t.z)}rotateX(t,s){let e=this.translate(-t.x,-t.y,-t.z),a=e.z*Math.cos(s)-e.y*Math.sin(s),h=e.z*Math.sin(s)+e.y*Math.cos(s);return e.z=a,e.y=h,e.translate(t.x,t.y,t.z)}rotateY(t,s){let e=this.translate(-t.x,-t.y,-t.z),a=e.x*Math.cos(s)-e.z*Math.sin(s),h=e.x*Math.sin(s)+e.z*Math.cos(s);return e.x=a,e.z=h,e.translate(t.x,t.y,t.z)}rotateZ(t,s){let e=this.translate(-t.x,-t.y,-t.z),a=e.x*Math.cos(s)-e.y*Math.sin(s),h=e.x*Math.sin(s)+e.y*Math.cos(s);return e.x=a,e.y=h,e.translate(t.x,t.y,t.z)}depth(){return this.x+this.y-2*this.z}duplicate(){return f.FromPoint(this)}static distance(t,s){let e=s.x-t.x,a=s.y-t.y,h=s.z-t.z;return Math.sqrt(e*e+a*a+h*h)}};r(f,"ORIGIN",new f(0,0,0));let i=f;class l{constructor(t){r(this,"points");this.points=t.map(s=>s.duplicate())}getPoints(){return this.points}duplicate(){return new l(this.points)}push(t){this.points.push(t)}reverse(){const t=this.points.slice().reverse();return new l(t)}translate(t=0,s=0,e=0){const a=this.points.map(h=>h.translate(t,s,e));return new l(a)}rotateX(t,s){const e=this.points.map(a=>a.rotateX(t,s));return new l(e)}rotateY(t,s){const e=this.points.map(a=>a.rotateY(t,s));return new l(e)}rotateZ(t,s){const e=this.points.map(a=>a.rotateZ(t,s));return new l(e)}scale(t,s,e,a){const h=this.points.map(n=>n.scale(t,s,e,a));return new l(h)}depth(){return this.points.reduce((t,s)=>t+s.depth(),0)/(this.points.length||1)}static Rectangle(t,s=1,e=1){return new l([t,new i(t.x+s,t.y,t.z),new i(t.x+s,t.y+e,t.z),new i(t.x,t.y+e,t.z)])}static Circle(t,s,e=20){const a=[];for(let h=0;h<e;h++)a.push(new i(t.x+s*Math.cos(h*2*Math.PI/e),t.y+s*Math.sin(h*2*Math.PI/e),t.z));return new l(a)}static Star(t,s,e,a){const h=[];for(let n=0;n<a*2;n++){const c=n%2===0?s:e;h.push(new i(t.x+c*Math.cos(n*Math.PI/a),t.y+c*Math.sin(n*Math.PI/a),t.z))}return new l(h)}}class u{constructor(t=[]){r(this,"paths");this.paths=t.map(s=>s.duplicate())}push(t){this.paths.push(t)}getPaths(){return this.paths}translate(t=0,s=0,e=0){return new u(this.paths.map(a=>a.translate(t,s,e)))}rotateX(t,s){return new u(this.paths.map(e=>e.rotateX(t,s)))}rotateY(t,s){return new u(this.paths.map(e=>e.rotateY(t,s)))}rotateZ(t,s){return new u(this.paths.map(e=>e.rotateZ(t,s)))}scale(t,s,e,a){return new u(this.paths.map(h=>h.scale(t,s,e,a)))}orderedPaths(){return this.paths.slice().sort((t,s)=>s.depth()-t.depth())}static extrude(t,s=1){const e=t.translate(0,0,s),a=new u,h=e.getPoints(),n=t.getPoints();a.push(t.reverse()),a.push(e);for(let c=0;c<n.length;c++)a.push(new l([h[c],n[c],n[(c+1)%n.length],h[(c+1)%h.length]]));return a}static Prism(t,s=1,e=1,a=1){const h=new u,n=new l([t,new i(t.x+s,t.y,t.z),new i(t.x+s,t.y,t.z+a),new i(t.x,t.y,t.z+a)]);h.push(n),h.push(n.reverse().translate(0,e,0));const c=new l([t,new i(t.x,t.y,t.z+a),new i(t.x,t.y+e,t.z+a),new i(t.x,t.y+e,t.z)]);h.push(c),h.push(c.reverse().translate(s,0,0));const o=new l([t,new i(t.x+s,t.y,t.z),new i(t.x+s,t.y+e,t.z),new i(t.x,t.y+e,t.z)]);return h.push(o.reverse()),h.push(o.translate(0,0,a)),h}static Pyramid(t,s=1,e=1,a=1){const h=new u,n=new l([t,new i(t.x+s,t.y,t.z),new i(t.x+s/2,t.y+e/2,t.z+a)]);h.push(n),h.push(n.rotateZ(t.translate(s/2,e/2),Math.PI));const c=new l([t,new i(t.x+s/2,t.y+e/2,t.z+a),new i(t.x,t.y+e,t.z)]);return h.push(c),h.push(c.rotateZ(t.translate(s/2,e/2),Math.PI)),h}static Cylinder(t,s=1,e,a){const h=l.Circle(t,s,e);return u.extrude(h,a)}}class w{constructor(t=0,s=0,e=0){r(this,"i");r(this,"j");r(this,"k");this.i=t,this.j=s,this.k=e}static fromTwoPoints(t,s){return new w(s.x-t.x,s.y-t.y,s.z-t.z)}static crossProduct(t,s){const e=t.j*s.k-s.j*t.k,a=-1*(t.i*s.k-s.i*t.k),h=t.i*s.j-s.i*t.j;return new w(e,a,h)}static dotProduct(t,s){return t.i*s.i+t.j*s.j+t.k*s.k}magnitude(){return Math.sqrt(this.i*this.i+this.j*this.j+this.k*this.k)}normalize(){const t=this.magnitude();return t===0?new w(0,0,0):new w(this.i/t,this.j/t,this.k/t)}}class M{constructor(t,s){r(this,"drawables");r(this,"paths");r(this,"scope");this.scope=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope?"worker":"main",this.drawables=[],this.paths=[],t&&this.updatePaths(t),s&&this.updateSources(s)}updatePaths(t){this.paths=t instanceof u?t.orderedPaths():t}updateSources(t){this.drawables=[],this.init(t)}init(t){const s=Array.isArray(t)?t:[t];for(const e of s){if(this.scope==="worker"){if(![ImageBitmap,OffscreenCanvas].some(o=>e instanceof o))continue;this.drawables.push(e);continue}[ImageBitmap,HTMLImageElement,HTMLCanvasElement].some(n=>e instanceof n)&&this.drawables.push(e)}if(this.drawables.length===0)throw new Error("No sources could be loaded. Please ensure the correct source types are being passed.")}getPaths(){return this.paths}getDrawables(){return this.drawables}}const k=new y(120,120,120);class d{constructor(t,s={}){r(this,"canvas");r(this,"angle");r(this,"scale");r(this,"originX");r(this,"originY");r(this,"lightPosition");r(this,"lightAngle");r(this,"colorDifference");r(this,"lightColor");r(this,"transformation");this.canvas=new m(t,s==null?void 0:s.canvas),this.init(s)}init(t){this.angle=Math.PI/6,this.scale=t.scale||70,this.transformation=[[this.scale*Math.cos(this.angle),this.scale*Math.sin(this.angle)],[this.scale*Math.cos(Math.PI-this.angle),this.scale*Math.sin(Math.PI-this.angle)]],this.originX=t.originX||this.canvas.width/2,this.originY=t.originY||this.canvas.height*.9,this.lightPosition=t.lightPosition||new w(2,-1,3),this.lightAngle=this.lightPosition.normalize(),this.colorDifference=.2,this.lightColor=t.lightColor||new y(255,255,255)}setLightPosition(t,s,e){this.lightPosition=new w(t,s,e),this.lightAngle=this.lightPosition.normalize()}translatePoint(t){const s=new i(t.x*this.transformation[0][0],t.x*this.transformation[0][1]),e=new i(t.y*this.transformation[1][0],t.y*this.transformation[1][1]),a=this.originX+s.x+e.x,h=this.originY-s.y-e.y-t.z*this.scale;return new i(a,h)}add(t,s){switch(!0){case Array.isArray(t):{t.forEach(e=>this.add(e,s));break}case t instanceof l:{this.addPath(t,s);break}case t instanceof u:{t.orderedPaths().forEach(a=>this.addPath(a,s));break}case t instanceof M:{const e=t.getPaths(),a=t.getDrawables();if(a.length>1&&a.length!==e.length)throw new Error("You must pass in only one image of define an image for each path");e.forEach((h,n)=>{let c=a[0];a[n]&&(c=a[n]),this.addPath(h,s,c)});break}default:throw new Error(".add could not be executed, please ensure passed parameters are correct")}}addPath(t,s=k,e){const a=t.getPoints(),h=w.fromTwoPoints(a[1],a[0]),n=w.fromTwoPoints(a[2],a[1]),c=w.crossProduct(h,n).normalize(),o=w.dotProduct(c,this.lightAngle),p=s.lighten(o*this.colorDifference,this.lightColor);this.canvas.path(a.map(b=>this.translatePoint(b)),p,e)}}r(d,"Canvas",m),r(d,"Textured",M),r(d,"Color",y),r(d,"Path",l),r(d,"Point",i),r(d,"Shape",u),r(d,"Vector",w);exports.Isomer=d;