@litecanvas/utils 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,8 @@ Small collection of tools for developing games with [Litecanvas](https://github.
6
6
 
7
7
  - **Camera**: Move-, zoom- and rotatable camera with shake. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/camera)
8
8
  - **Vector**: Modular 2D vector. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/vector)
9
- - **Grid** class. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/grid)
9
+ - **Actor**: class to represent game entities. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/actor)
10
+ - **Grid**: class to handle retangular grid areas. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/grid)
10
11
  - **Collision** utilities. [Usage & Docs](https://github.com/litecanvas/utils/tree/main/src/collision)
11
12
  - And [some math utilities](https://github.com/litecanvas/utils/tree/main/src/math)
12
13
 
package/dist/actor.js ADDED
@@ -0,0 +1,156 @@
1
+ (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ // src/actor/index.js
9
+ var actor_exports = {};
10
+ __export(actor_exports, {
11
+ ANCHOR_BOT_LEFT: () => ANCHOR_BOT_LEFT,
12
+ ANCHOR_BOT_RIGHT: () => ANCHOR_BOT_RIGHT,
13
+ ANCHOR_CENTER: () => ANCHOR_CENTER,
14
+ ANCHOR_TOP_LEFT: () => ANCHOR_TOP_LEFT,
15
+ ANCHOR_TOP_RIGHT: () => ANCHOR_TOP_RIGHT,
16
+ Actor: () => Actor
17
+ });
18
+
19
+ // src/vector/index.js
20
+ var Vector = class {
21
+ /** @type {number} */
22
+ x;
23
+ /** @type {number} */
24
+ y;
25
+ /**
26
+ * @param {number} x
27
+ * @param {number} y
28
+ */
29
+ constructor(x = 0, y = x) {
30
+ this.x = x;
31
+ this.y = y;
32
+ }
33
+ /**
34
+ * @returns {string}
35
+ */
36
+ toString() {
37
+ return `Vector (${this.x}, ${this.y})`;
38
+ }
39
+ };
40
+ var vec = (x = 0, y = x) => new Vector(x, y);
41
+ var veccopy = (v) => vec(v.x, v.y);
42
+
43
+ // src/actor/index.js
44
+ var ANCHOR_CENTER = vec(0.5, 0.5);
45
+ var ANCHOR_TOP_LEFT = vec(0, 0);
46
+ var ANCHOR_TOP_RIGHT = vec(1, 0);
47
+ var ANCHOR_BOT_LEFT = vec(0, 1);
48
+ var ANCHOR_BOT_RIGHT = vec(1, 1);
49
+ var Actor = class {
50
+ /** @type {Image|HTMLCanvasElement|OffscreenCanvas} */
51
+ sprite;
52
+ /** @type {Vector} The actor position */
53
+ _p;
54
+ /** @type {Vector} The actor anchor (origin) */
55
+ _o;
56
+ /** @type {Vector} The actor scale */
57
+ _s;
58
+ /** @type {number} The actor angle (in radians) */
59
+ angle = 0;
60
+ /** @type {number} The actor opacity */
61
+ opacity = 1;
62
+ /** @type {boolean} If `true` the actor will not be drawn. */
63
+ hidden = false;
64
+ /**
65
+ * @param {Image|HTMLCanvasElement|OffscreenCanvas} sprite
66
+ * @param {Vector} position
67
+ */
68
+ constructor(sprite, position) {
69
+ this.sprite = sprite;
70
+ this._p = position || vec(0);
71
+ this._o = veccopy(ANCHOR_TOP_LEFT);
72
+ this._s = vec(1, 1);
73
+ }
74
+ /**
75
+ * @param {number}
76
+ */
77
+ set x(value) {
78
+ this._p.x = value;
79
+ }
80
+ /**
81
+ * @returns {number}
82
+ */
83
+ get x() {
84
+ return this._p.x;
85
+ }
86
+ /**
87
+ * @param {number}
88
+ */
89
+ set y(value) {
90
+ this._p.y = value;
91
+ }
92
+ /**
93
+ * @returns {number}
94
+ */
95
+ get y() {
96
+ return this._p.y;
97
+ }
98
+ /**
99
+ * @param {Vector}
100
+ */
101
+ set anchor(vec2) {
102
+ this._o.x = vec2.x;
103
+ this._o.y = vec2.y;
104
+ }
105
+ /**
106
+ * @returns {Vector}
107
+ */
108
+ get anchor() {
109
+ return this._o;
110
+ }
111
+ /**
112
+ * @returns {number}
113
+ */
114
+ get width() {
115
+ return this.sprite.width * this._s.y;
116
+ }
117
+ /**
118
+ * @returns {number}
119
+ */
120
+ get height() {
121
+ return this.sprite.height * this._s.y;
122
+ }
123
+ /**
124
+ * @retuns {Vector}
125
+ */
126
+ get scale() {
127
+ return this._s;
128
+ }
129
+ /**
130
+ * Update the transformation matrix, sets the opacity and draw the actor sprite image.
131
+ *
132
+ * @param {LitecanvasInstance} [litecanvas]
133
+ */
134
+ draw(litecanvas = globalThis) {
135
+ if (this.hidden || this.opacity <= 0) return;
136
+ litecanvas.push();
137
+ this.transform(litecanvas);
138
+ const anchorX = this.sprite.width * this.anchor.x;
139
+ const anchorY = this.sprite.height * this.anchor.y;
140
+ litecanvas.image(-anchorX, -anchorY, this.sprite);
141
+ litecanvas.pop();
142
+ }
143
+ /**
144
+ * @param {LitecanvasInstance} litecanvas
145
+ */
146
+ transform(litecanvas) {
147
+ litecanvas.translate(this._p.x, this._p.y);
148
+ litecanvas.rotate(this.angle);
149
+ litecanvas.scale(this._s.x, this._s.y);
150
+ litecanvas.alpha(this.opacity);
151
+ }
152
+ };
153
+
154
+ // src/actor/_web.js
155
+ globalThis.utils = Object.assign(globalThis.utils || {}, actor_exports);
156
+ })();
@@ -0,0 +1 @@
1
+ (()=>{var f=Object.defineProperty;var p=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})};var a={};p(a,{ANCHOR_BOT_LEFT:()=>d,ANCHOR_BOT_RIGHT:()=>x,ANCHOR_CENTER:()=>u,ANCHOR_TOP_LEFT:()=>c,ANCHOR_TOP_RIGHT:()=>h,Actor:()=>n});var s=class{x;y;constructor(t=0,r=t){this.x=t,this.y=r}toString(){return`Vector (${this.x}, ${this.y})`}},o=(e=0,t=e)=>new s(e,t);var i=e=>o(e.x,e.y);var u=o(.5,.5),c=o(0,0),h=o(1,0),d=o(0,1),x=o(1,1),n=class{sprite;_p;_o;_s;angle=0;opacity=1;hidden=!1;constructor(t,r){this.sprite=t,this._p=r||o(0),this._o=i(c),this._s=o(1,1)}set x(t){this._p.x=t}get x(){return this._p.x}set y(t){this._p.y=t}get y(){return this._p.y}set anchor(t){this._o.x=t.x,this._o.y=t.y}get anchor(){return this._o}get width(){return this.sprite.width*this._s.y}get height(){return this.sprite.height*this._s.y}get scale(){return this._s}draw(t=globalThis){if(this.hidden||this.opacity<=0)return;t.push(),this.transform(t);let r=this.sprite.width*this.anchor.x,l=this.sprite.height*this.anchor.y;t.image(-r,-l,this.sprite),t.pop()}transform(t){t.translate(this._p.x,this._p.y),t.rotate(this.angle),t.scale(this._s.x,this._s.y),t.alpha(this.opacity)}};globalThis.utils=Object.assign(globalThis.utils||{},a);})();
package/dist/all.js CHANGED
@@ -8,6 +8,12 @@
8
8
  // src/index.js
9
9
  var src_exports = {};
10
10
  __export(src_exports, {
11
+ ANCHOR_BOT_LEFT: () => ANCHOR_BOT_LEFT,
12
+ ANCHOR_BOT_RIGHT: () => ANCHOR_BOT_RIGHT,
13
+ ANCHOR_CENTER: () => ANCHOR_CENTER,
14
+ ANCHOR_TOP_LEFT: () => ANCHOR_TOP_LEFT,
15
+ ANCHOR_TOP_RIGHT: () => ANCHOR_TOP_RIGHT,
16
+ Actor: () => Actor,
11
17
  Camera: () => Camera,
12
18
  DOWN: () => DOWN,
13
19
  Grid: () => Grid,
@@ -521,6 +527,117 @@
521
527
  var DOWN = /* @__PURE__ */ vec(0, 1);
522
528
  var LEFT = /* @__PURE__ */ vec(-1, 0);
523
529
 
530
+ // src/actor/index.js
531
+ var ANCHOR_CENTER = vec(0.5, 0.5);
532
+ var ANCHOR_TOP_LEFT = vec(0, 0);
533
+ var ANCHOR_TOP_RIGHT = vec(1, 0);
534
+ var ANCHOR_BOT_LEFT = vec(0, 1);
535
+ var ANCHOR_BOT_RIGHT = vec(1, 1);
536
+ var Actor = class {
537
+ /** @type {Image|HTMLCanvasElement|OffscreenCanvas} */
538
+ sprite;
539
+ /** @type {Vector} The actor position */
540
+ _p;
541
+ /** @type {Vector} The actor anchor (origin) */
542
+ _o;
543
+ /** @type {Vector} The actor scale */
544
+ _s;
545
+ /** @type {number} The actor angle (in radians) */
546
+ angle = 0;
547
+ /** @type {number} The actor opacity */
548
+ opacity = 1;
549
+ /** @type {boolean} If `true` the actor will not be drawn. */
550
+ hidden = false;
551
+ /**
552
+ * @param {Image|HTMLCanvasElement|OffscreenCanvas} sprite
553
+ * @param {Vector} position
554
+ */
555
+ constructor(sprite, position) {
556
+ this.sprite = sprite;
557
+ this._p = position || vec(0);
558
+ this._o = veccopy(ANCHOR_TOP_LEFT);
559
+ this._s = vec(1, 1);
560
+ }
561
+ /**
562
+ * @param {number}
563
+ */
564
+ set x(value) {
565
+ this._p.x = value;
566
+ }
567
+ /**
568
+ * @returns {number}
569
+ */
570
+ get x() {
571
+ return this._p.x;
572
+ }
573
+ /**
574
+ * @param {number}
575
+ */
576
+ set y(value) {
577
+ this._p.y = value;
578
+ }
579
+ /**
580
+ * @returns {number}
581
+ */
582
+ get y() {
583
+ return this._p.y;
584
+ }
585
+ /**
586
+ * @param {Vector}
587
+ */
588
+ set anchor(vec2) {
589
+ this._o.x = vec2.x;
590
+ this._o.y = vec2.y;
591
+ }
592
+ /**
593
+ * @returns {Vector}
594
+ */
595
+ get anchor() {
596
+ return this._o;
597
+ }
598
+ /**
599
+ * @returns {number}
600
+ */
601
+ get width() {
602
+ return this.sprite.width * this._s.y;
603
+ }
604
+ /**
605
+ * @returns {number}
606
+ */
607
+ get height() {
608
+ return this.sprite.height * this._s.y;
609
+ }
610
+ /**
611
+ * @retuns {Vector}
612
+ */
613
+ get scale() {
614
+ return this._s;
615
+ }
616
+ /**
617
+ * Update the transformation matrix, sets the opacity and draw the actor sprite image.
618
+ *
619
+ * @param {LitecanvasInstance} [litecanvas]
620
+ */
621
+ draw(litecanvas = globalThis) {
622
+ if (this.hidden || this.opacity <= 0) return;
623
+ litecanvas.push();
624
+ this.transform(litecanvas);
625
+ const anchorX = this.sprite.width * this.anchor.x;
626
+ const anchorY = this.sprite.height * this.anchor.y;
627
+ litecanvas.image(-anchorX, -anchorY, this.sprite);
628
+ litecanvas.pop();
629
+ }
630
+ /**
631
+ * @param {LitecanvasInstance} litecanvas
632
+ */
633
+ transform(litecanvas) {
634
+ litecanvas.translate(this._p.x, this._p.y);
635
+ litecanvas.rotate(this.angle);
636
+ litecanvas.scale(this._s.x, this._s.y);
637
+ litecanvas.alpha(this.opacity);
638
+ }
639
+ };
640
+
524
641
  // src/_web.js
525
642
  globalThis.utils = src_exports;
526
643
  })();
package/dist/all.min.js CHANGED
@@ -1,2 +1,2 @@
1
- (()=>{var j=Object.defineProperty;var D=(e,t)=>{for(var s in t)j(e,s,{get:t[s],enumerable:!0})};var T={};D(T,{Camera:()=>f,DOWN:()=>Q,Grid:()=>p,LEFT:()=>tt,ONE:()=>C,RIGHT:()=>K,UP:()=>J,Vector:()=>u,ZERO:()=>B,diff:()=>I,fract:()=>L,intersection:()=>_,isvector:()=>c,resolve:()=>X,vec:()=>h,vecadd:()=>z,vecconfig:()=>k,veccopy:()=>G,veccross:()=>V,vecdir:()=>F,vecdist:()=>W,vecdist2:()=>$,vecdiv:()=>y,vecdot:()=>U,veceq:()=>q,veclerp:()=>Z,veclimit:()=>S,vecmag:()=>O,vecmag2:()=>b,vecmult:()=>M,vecnorm:()=>R,vecrand:()=>v,vecrot:()=>N,vecset:()=>A,vecsub:()=>H,wave:()=>P});var I=(e,t)=>Math.abs(t-e)||0;var L=e=>e%1||0;var P=(e,t,s,i=Math.sin)=>e+(i(s)+1)/2*(t-e);var _=(e,t,s,i,r,o,n,a)=>{let l=Math.max(e,r),E=Math.min(e+s,r+n)-l,d=Math.max(t,o),g=Math.min(t+i,o+a)-d;return[l,d,E,g]};var X=(e,t,s,i,r,o,n,a)=>{let[l,E,d,g]=_(e,t,s,i,r,o,n,a),x="",m=e,w=t;return d<g?e<r?(x="right",m=r-s):(x="left",m=r+n):t<o?(x="bottom",w=o-i):(x="top",w=o+a),{direction:x,x:m,y:w}};var f=class{_engine=null;x=0;y=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(t=null){t=t||globalThis,this._engine=t,this.size(t.WIDTH||0,t.HEIGHT||0),this.x=this.width/2,this.y=this.height/2}size(t,s){this.width=t,this.height=s}start(t=!1){let s=this.width/2,i=this.height/2;this._engine.push(),this._engine.translate(s,i),this._engine.scale(this.scale),this._engine.rotate(this.rotation),this._engine.translate(-this.x+this._shake.x,-this.y+this._shake.y),t&&this._engine.cliprect(this.x,this.y,this.width,this.height)}end(){this._engine.pop()}lookAt(t,s){this.x=t,this.y=s}move(t,s){this.x+=t,this.y+=s}zoom(t){this.scale*=t}zoomTo(t){this.scale=t}rotate(t){this.rotation+=t}rotateTo(t){this.rotation=t}shake(t=.3,s=1){this.shaking()||(this._shake.removeListener=this._engine.listen("update",i=>{this._shake.x=this._engine.randi(-s,s),this._shake.y=this._engine.randi(-s,s),t-=i,t<=0&&this.unshake()}))}unshake(){this.shaking()&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}shaking(){return this._shake.removeListener!==null}};var p=class e{_w;_h;_c;static fromArray(t,s,i){let r=new e(t,s);for(let o=0;o<i.length;o++)r._c[o]=i[o];return r}constructor(t,s){this.width=t,this.height=s,this.clear()}clear(){this._c=Array(this._w*this._h)}set width(t){this._w=Math.max(1,~~t)}get width(){return this._w}set height(t){this._h=Math.max(1,~~t)}get height(){return this._h}set(t,s,i){this._c[this.pointToIndex(t,s)]=i}get(t,s){return this._c[this.pointToIndex(t,s)]}has(t,s){return this.get(t,s)!=null}get length(){return this._w*this._h}pointToIndex(t,s){return this.clampX(~~t)+this.clampY(~~s)*this._w}indexToPointX(t){return t%this._w}indexToPointY(t){return Math.floor(t/this._w)}forEach(t,s=!1){let i=s?this.length-1:0,r=s?-1:this.length,o=s?-1:1;for(;i!==r;){let n=this.indexToPointX(i),a=this.indexToPointY(i),l=this._c[i];if(t(n,a,l,this)===!1)break;i+=o}}fill(t){this.forEach((s,i)=>{this.set(s,i,t)})}clone(){return e.fromArray(this._w,this._h,this._c)}clampX(t){return Y(t,0,this._w-1)}clampY(t){return Y(t,0,this._h-1)}toArray(){return this._c.slice(0)}toString(t=" ",s=!0){if(!s)return this._c.join(t);let i=[];return this.forEach((r,o,n)=>{i[o]=i[o]||"",i[o]+=n+t}),i.join(`
2
- `)}};function Y(e,t,s){return e<t?t:e>s?s:e}var u=class{x;y;constructor(t=0,s=t){this.x=t,this.y=s}toString(){return`Vector (${this.x}, ${this.y})`}},h=(e=0,t=e)=>new u(e,t),q=(e,t,s=t)=>c(t)?q(e,t.x,t.y):e.x===t&&e.y===s,G=e=>h(e.x,e.y),A=(e,t,s=t)=>{c(t)?A(e,t.x,t.y):(e.x=t,e.y=s)},z=(e,t,s=t)=>{c(t)?z(e,t.x,t.y):(e.x+=t,e.y+=s)},H=(e,t,s=t)=>{c(t)?H(e,t.x,t.y):(e.x-=t,e.y-=s)},M=(e,t,s=t)=>{c(t)?M(e,t.x,t.y):(e.x*=t,e.y*=s)},y=(e,t,s=t)=>{c(t)?y(e,t.x,t.y):(e.x/=t,e.y/=s)},N=(e,t)=>{let s=Math.cos(t),i=Math.sin(t);e.x=s*e.x-i*e.y,e.y=i*e.x+s*e.y},O=e=>Math.sqrt(e.x*e.x+e.y*e.y),b=e=>e.x*e.x+e.y*e.y,R=e=>{let t=O(e);t>0&&y(e,t)},S=(e,t)=>{let s=b(e);s>t*t&&(y(e,Math.sqrt(s)),M(e,t))},W=(e,t)=>{let s=e.x-t.x,i=e.y-t.y;return Math.sqrt(s*s+i*i)},$=(e,t)=>{let s=e.x-t.x,i=e.y-t.y;return s*s+i*i},F=e=>Math.atan2(e.y,e.x),U=(e,t)=>e.x*t.x+e.y*t.y,V=(e,t)=>e.x*t.y-e.y*t.x,Z=(e,t,s)=>{e.x+=(t.x-e.x)*s||0,e.y+=(t.y-e.y)*s||0},v=(e=1,t=e)=>{let s=k.random()*2*Math.PI,i=k.random()*(t-e)+e;return h(Math.cos(s)*i,Math.sin(s)*i)},c=e=>e instanceof u,k={random:()=>globalThis.rand?rand():Math.random()},B=h(0,0),C=h(1,1),J=h(0,-1),K=h(1,0),Q=h(0,1),tt=h(-1,0);globalThis.utils=T;})();
1
+ (()=>{var L=Object.defineProperty;var v=(e,t)=>{for(var s in t)L(e,s,{get:t[s],enumerable:!0})};var A={};v(A,{ANCHOR_BOT_LEFT:()=>it,ANCHOR_BOT_RIGHT:()=>nt,ANCHOR_CENTER:()=>ot,ANCHOR_TOP_LEFT:()=>W,ANCHOR_TOP_RIGHT:()=>rt,Actor:()=>I,Camera:()=>p,DOWN:()=>et,Grid:()=>u,LEFT:()=>st,ONE:()=>K,RIGHT:()=>tt,UP:()=>Q,Vector:()=>d,ZERO:()=>J,diff:()=>M,fract:()=>P,intersection:()=>g,isvector:()=>c,resolve:()=>O,vec:()=>i,vecadd:()=>S,vecconfig:()=>w,veccopy:()=>H,veccross:()=>V,vecdir:()=>$,vecdist:()=>B,vecdist2:()=>j,vecdiv:()=>_,vecdot:()=>q,veceq:()=>k,veclerp:()=>U,veclimit:()=>F,vecmag:()=>R,vecmag2:()=>Y,vecmult:()=>E,vecnorm:()=>N,vecrand:()=>Z,vecrot:()=>G,vecset:()=>z,vecsub:()=>X,wave:()=>b});var M=(e,t)=>Math.abs(t-e)||0;var P=e=>e%1||0;var b=(e,t,s,o=Math.sin)=>e+(o(s)+1)/2*(t-e);var g=(e,t,s,o,n,r,a,h)=>{let l=Math.max(e,n),D=Math.min(e+s,n+a)-l,x=Math.max(t,r),m=Math.min(t+o,r+h)-x;return[l,x,D,m]};var O=(e,t,s,o,n,r,a,h)=>{let[l,D,x,m]=g(e,t,s,o,n,r,a,h),f="",y=e,T=t;return x<m?e<n?(f="right",y=n-s):(f="left",y=n+a):t<r?(f="bottom",T=r-o):(f="top",T=r+h),{direction:f,x:y,y:T}};var p=class{_engine=null;x=0;y=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(t=null){t=t||globalThis,this._engine=t,this.size(t.WIDTH||0,t.HEIGHT||0),this.x=this.width/2,this.y=this.height/2}size(t,s){this.width=t,this.height=s}start(t=!1){let s=this.width/2,o=this.height/2;this._engine.push(),this._engine.translate(s,o),this._engine.scale(this.scale),this._engine.rotate(this.rotation),this._engine.translate(-this.x+this._shake.x,-this.y+this._shake.y),t&&this._engine.cliprect(this.x,this.y,this.width,this.height)}end(){this._engine.pop()}lookAt(t,s){this.x=t,this.y=s}move(t,s){this.x+=t,this.y+=s}zoom(t){this.scale*=t}zoomTo(t){this.scale=t}rotate(t){this.rotation+=t}rotateTo(t){this.rotation=t}shake(t=.3,s=1){this.shaking()||(this._shake.removeListener=this._engine.listen("update",o=>{this._shake.x=this._engine.randi(-s,s),this._shake.y=this._engine.randi(-s,s),t-=o,t<=0&&this.unshake()}))}unshake(){this.shaking()&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}shaking(){return this._shake.removeListener!==null}};var u=class e{_w;_h;_c;static fromArray(t,s,o){let n=new e(t,s);for(let r=0;r<o.length;r++)n._c[r]=o[r];return n}constructor(t,s){this.width=t,this.height=s,this.clear()}clear(){this._c=Array(this._w*this._h)}set width(t){this._w=Math.max(1,~~t)}get width(){return this._w}set height(t){this._h=Math.max(1,~~t)}get height(){return this._h}set(t,s,o){this._c[this.pointToIndex(t,s)]=o}get(t,s){return this._c[this.pointToIndex(t,s)]}has(t,s){return this.get(t,s)!=null}get length(){return this._w*this._h}pointToIndex(t,s){return this.clampX(~~t)+this.clampY(~~s)*this._w}indexToPointX(t){return t%this._w}indexToPointY(t){return Math.floor(t/this._w)}forEach(t,s=!1){let o=s?this.length-1:0,n=s?-1:this.length,r=s?-1:1;for(;o!==n;){let a=this.indexToPointX(o),h=this.indexToPointY(o),l=this._c[o];if(t(a,h,l,this)===!1)break;o+=r}}fill(t){this.forEach((s,o)=>{this.set(s,o,t)})}clone(){return e.fromArray(this._w,this._h,this._c)}clampX(t){return C(t,0,this._w-1)}clampY(t){return C(t,0,this._h-1)}toArray(){return this._c.slice(0)}toString(t=" ",s=!0){if(!s)return this._c.join(t);let o=[];return this.forEach((n,r,a)=>{o[r]=o[r]||"",o[r]+=a+t}),o.join(`
2
+ `)}};function C(e,t,s){return e<t?t:e>s?s:e}var d=class{x;y;constructor(t=0,s=t){this.x=t,this.y=s}toString(){return`Vector (${this.x}, ${this.y})`}},i=(e=0,t=e)=>new d(e,t),k=(e,t,s=t)=>c(t)?k(e,t.x,t.y):e.x===t&&e.y===s,H=e=>i(e.x,e.y),z=(e,t,s=t)=>{c(t)?z(e,t.x,t.y):(e.x=t,e.y=s)},S=(e,t,s=t)=>{c(t)?S(e,t.x,t.y):(e.x+=t,e.y+=s)},X=(e,t,s=t)=>{c(t)?X(e,t.x,t.y):(e.x-=t,e.y-=s)},E=(e,t,s=t)=>{c(t)?E(e,t.x,t.y):(e.x*=t,e.y*=s)},_=(e,t,s=t)=>{c(t)?_(e,t.x,t.y):(e.x/=t,e.y/=s)},G=(e,t)=>{let s=Math.cos(t),o=Math.sin(t);e.x=s*e.x-o*e.y,e.y=o*e.x+s*e.y},R=e=>Math.sqrt(e.x*e.x+e.y*e.y),Y=e=>e.x*e.x+e.y*e.y,N=e=>{let t=R(e);t>0&&_(e,t)},F=(e,t)=>{let s=Y(e);s>t*t&&(_(e,Math.sqrt(s)),E(e,t))},B=(e,t)=>{let s=e.x-t.x,o=e.y-t.y;return Math.sqrt(s*s+o*o)},j=(e,t)=>{let s=e.x-t.x,o=e.y-t.y;return s*s+o*o},$=e=>Math.atan2(e.y,e.x),q=(e,t)=>e.x*t.x+e.y*t.y,V=(e,t)=>e.x*t.y-e.y*t.x,U=(e,t,s)=>{e.x+=(t.x-e.x)*s||0,e.y+=(t.y-e.y)*s||0},Z=(e=1,t=e)=>{let s=w.random()*2*Math.PI,o=w.random()*(t-e)+e;return i(Math.cos(s)*o,Math.sin(s)*o)},c=e=>e instanceof d,w={random:()=>globalThis.rand?rand():Math.random()},J=i(0,0),K=i(1,1),Q=i(0,-1),tt=i(1,0),et=i(0,1),st=i(-1,0);var ot=i(.5,.5),W=i(0,0),rt=i(1,0),it=i(0,1),nt=i(1,1),I=class{sprite;_p;_o;_s;angle=0;opacity=1;hidden=!1;constructor(t,s){this.sprite=t,this._p=s||i(0),this._o=H(W),this._s=i(1,1)}set x(t){this._p.x=t}get x(){return this._p.x}set y(t){this._p.y=t}get y(){return this._p.y}set anchor(t){this._o.x=t.x,this._o.y=t.y}get anchor(){return this._o}get width(){return this.sprite.width*this._s.y}get height(){return this.sprite.height*this._s.y}get scale(){return this._s}draw(t=globalThis){if(this.hidden||this.opacity<=0)return;t.push(),this.transform(t);let s=this.sprite.width*this.anchor.x,o=this.sprite.height*this.anchor.y;t.image(-s,-o,this.sprite),t.pop()}transform(t){t.translate(this._p.x,this._p.y),t.rotate(this.angle),t.scale(this._s.x,this._s.y),t.alpha(this.opacity)}};globalThis.utils=A;})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litecanvas/utils",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Utilities to help build litecanvas games",
5
5
  "author": "Luiz Bills <luizbills@pm.me>",
6
6
  "license": "MIT",
@@ -0,0 +1,140 @@
1
+ # Actor
2
+
3
+ **CDN**: https://unpkg.com/@litecanvas/utils/dist/actor.js
4
+
5
+ ## Usage
6
+
7
+ ```js
8
+ import litecanvas from "@litecanvas"
9
+ import { Actor } from "@litecanvas/utils"
10
+
11
+ let player, mySprite
12
+
13
+ litecanvas({
14
+ loop: { init, tapped, draw },
15
+ })
16
+
17
+ // create a sprite image
18
+ mySprite = paint(3, 3, ["303", "030", "303"], { scale: 10 })
19
+
20
+ function init() {
21
+ // create a actor
22
+ player = new Actor(mySprite)
23
+ }
24
+
25
+ function tapped(tapx, tapy) {
26
+ // update the actor position
27
+ player.x = tapx
28
+ player.y = tapy
29
+ }
30
+
31
+ function draw() {
32
+ cls(0)
33
+
34
+ // draw the actor sprite
35
+ player.draw()
36
+ }
37
+ ```
38
+
39
+ ## Actor#x / Actor#y
40
+
41
+ Set or get the actor position
42
+
43
+ ```js
44
+ player.x = 100
45
+ player.y = 200
46
+ ```
47
+
48
+ ## Actor#scale
49
+
50
+ Set or get the actor scale/size
51
+
52
+ ```js
53
+ // twice bigger
54
+ player.scale.x = 2
55
+ player.scale.y = 2
56
+ ```
57
+
58
+ ## Actor#anchor
59
+
60
+ Set or get the actor anchor (origin). By default, the anchor is a vector `(0, 0)` (meaning anchor Top Left).
61
+
62
+ ```js
63
+ // position the sprite based on their center
64
+ player.anchor.x = 0.5
65
+ player.anchor.y = 0.5
66
+ ```
67
+
68
+ ```js
69
+ // alternatively
70
+ import { ANCHOR_CENTER } from "@litecanvas/utils"
71
+
72
+ player.anchor = ANCHOR_CENTER
73
+ ```
74
+
75
+ > Note:
76
+ > You can import and use the following constants to help you choose an actor anchor: , `ANCHOR_TOP_LEFT` (default), `ANCHOR_TOP_RIGHT`, `ANCHOR_BOT_LEFT`, `ANCHOR_BOT_RIGHT` or `ANCHOR_CENTER`.
77
+
78
+ ## Actor#angle
79
+
80
+ Set or get the actor angle (in radians).
81
+
82
+ ```js
83
+ player.angle = HALF_PI / 2 // 45 degrees
84
+
85
+ // or use the litecanvas' `deg2rad()` helper
86
+ player.angle = deg2rad(45)
87
+ ```
88
+
89
+ ## Actor#opacity
90
+
91
+ Set or get the actor opacity (alpha).
92
+
93
+ ```js
94
+ player.opacity = 0.5 // 50% transparent
95
+ ```
96
+
97
+ ## Actor#hidden
98
+
99
+ Set or get the actor hidden (`boolean`) state.
100
+
101
+ ```js
102
+ player.hidden = true // hides the actor image
103
+ player.hidden = false // display the actor image
104
+ ```
105
+
106
+ ## Actor#width
107
+
108
+ Get (not set) the actor current width.
109
+
110
+ ```js
111
+ console.log(player.width) // => 30
112
+
113
+ player.scale.x = 1.5
114
+
115
+ console.log(player.width) // => 45
116
+ ```
117
+
118
+ ## Actor#height
119
+
120
+ Get (not set) the actor current height.
121
+
122
+ ```js
123
+ console.log(player.height) // => 30
124
+
125
+ player.scale.x = 2
126
+
127
+ console.log(player.height) // => 60
128
+ ```
129
+
130
+ ## Actor#sprite
131
+
132
+ Set or get the actor sprite image. Useful to make animations.
133
+
134
+ ```js
135
+ player.sprite = anotherSprite
136
+ ```
137
+
138
+ > The actor sprite must be a `Image`, `HTMLCanvas` or `OffscreenCanvas`.
139
+ >
140
+ > Remember, you can create a image using the litecanvas' `paint()` built-in helper or [load a image](https://github.com/litecanvas/plugin-asset-loader?tab=readme-ov-file#loading-images) with plugins.
@@ -0,0 +1,3 @@
1
+ import * as actorUtils from "./index.js"
2
+
3
+ globalThis.utils = Object.assign(globalThis.utils || {}, actorUtils)
@@ -0,0 +1,135 @@
1
+ import { Vector, vec, veccopy } from "../vector/index.js"
2
+ import "litecanvas"
3
+
4
+ export const ANCHOR_CENTER = vec(0.5, 0.5)
5
+ export const ANCHOR_TOP_LEFT = vec(0, 0)
6
+ export const ANCHOR_TOP_RIGHT = vec(1, 0)
7
+ export const ANCHOR_BOT_LEFT = vec(0, 1)
8
+ export const ANCHOR_BOT_RIGHT = vec(1, 1)
9
+
10
+ export class Actor {
11
+ /** @type {Image|HTMLCanvasElement|OffscreenCanvas} */
12
+ sprite
13
+
14
+ /** @type {Vector} The actor position */
15
+ _p
16
+
17
+ /** @type {Vector} The actor anchor (origin) */
18
+ _o
19
+
20
+ /** @type {Vector} The actor scale */
21
+ _s
22
+
23
+ /** @type {number} The actor angle (in radians) */
24
+ angle = 0
25
+
26
+ /** @type {number} The actor opacity */
27
+ opacity = 1
28
+
29
+ /** @type {boolean} If `true` the actor will not be drawn. */
30
+ hidden = false
31
+
32
+ /**
33
+ * @param {Image|HTMLCanvasElement|OffscreenCanvas} sprite
34
+ * @param {Vector} position
35
+ */
36
+ constructor(sprite, position) {
37
+ this.sprite = sprite
38
+ this._p = position || vec(0)
39
+ this._o = veccopy(ANCHOR_TOP_LEFT)
40
+ this._s = vec(1, 1)
41
+ }
42
+
43
+ /**
44
+ * @param {number}
45
+ */
46
+ set x(value) {
47
+ this._p.x = value
48
+ }
49
+
50
+ /**
51
+ * @returns {number}
52
+ */
53
+ get x() {
54
+ return this._p.x
55
+ }
56
+
57
+ /**
58
+ * @param {number}
59
+ */
60
+ set y(value) {
61
+ this._p.y = value
62
+ }
63
+
64
+ /**
65
+ * @returns {number}
66
+ */
67
+ get y() {
68
+ return this._p.y
69
+ }
70
+
71
+ /**
72
+ * @param {Vector}
73
+ */
74
+ set anchor(vec) {
75
+ this._o.x = vec.x
76
+ this._o.y = vec.y
77
+ }
78
+
79
+ /**
80
+ * @returns {Vector}
81
+ */
82
+ get anchor() {
83
+ return this._o
84
+ }
85
+
86
+ /**
87
+ * @returns {number}
88
+ */
89
+ get width() {
90
+ return this.sprite.width * this._s.y
91
+ }
92
+
93
+ /**
94
+ * @returns {number}
95
+ */
96
+ get height() {
97
+ return this.sprite.height * this._s.y
98
+ }
99
+
100
+ /**
101
+ * @retuns {Vector}
102
+ */
103
+ get scale() {
104
+ return this._s
105
+ }
106
+
107
+ /**
108
+ * Update the transformation matrix, sets the opacity and draw the actor sprite image.
109
+ *
110
+ * @param {LitecanvasInstance} [litecanvas]
111
+ */
112
+ draw(litecanvas = globalThis) {
113
+ if (this.hidden || this.opacity <= 0) return
114
+
115
+ litecanvas.push()
116
+
117
+ this.transform(litecanvas)
118
+
119
+ const anchorX = this.sprite.width * this.anchor.x
120
+ const anchorY = this.sprite.height * this.anchor.y
121
+ litecanvas.image(-anchorX, -anchorY, this.sprite)
122
+
123
+ litecanvas.pop()
124
+ }
125
+
126
+ /**
127
+ * @param {LitecanvasInstance} litecanvas
128
+ */
129
+ transform(litecanvas) {
130
+ litecanvas.translate(this._p.x, this._p.y)
131
+ litecanvas.rotate(this.angle)
132
+ litecanvas.scale(this._s.x, this._s.y)
133
+ litecanvas.alpha(this.opacity)
134
+ }
135
+ }
@@ -7,8 +7,10 @@
7
7
  Lets build an arena with [ASCII graphics](https://en.wikipedia.org/wiki/ASCII_art) like in classic roguelikes.
8
8
 
9
9
  ```js
10
+ import { Grid } from "@litecanvas/utils"
11
+
10
12
  // make a grid 13x13
11
- let grid = new utils.Grid(13, 13)
13
+ let grid = new Grid(13, 13)
12
14
 
13
15
  // fill the entire grid with "."
14
16
  grid.fill(".")
package/src/index.js CHANGED
@@ -6,3 +6,4 @@ export { default as intersection } from "./collision/intersection.js"
6
6
  export { default as Camera } from "./camera/index.js"
7
7
  export { default as Grid } from "./grid/index.js"
8
8
  export * from "./vector/index.js"
9
+ export * from "./actor/index.js"