@litecanvas/utils 0.10.1 → 0.11.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/dist/actor.js CHANGED
@@ -122,7 +122,7 @@
122
122
  * @returns {number}
123
123
  */
124
124
  get width() {
125
- return this.sprite.width * this._s.y;
125
+ return this.sprite.width * this._s.x;
126
126
  }
127
127
  /**
128
128
  * @returns {number}
@@ -130,6 +130,16 @@
130
130
  get height() {
131
131
  return this.sprite.height * this._s.y;
132
132
  }
133
+ /**
134
+ * @returns {number[]}
135
+ */
136
+ getBounds(scaled = true) {
137
+ const w = this.sprite.width * (scaled ? this._s.x : 1);
138
+ const h = this.sprite.height * (scaled ? this._s.y : 1);
139
+ const x = this.pos.x - w * this.anchor.x;
140
+ const y = this.pos.y - h * this.anchor.y;
141
+ return [x, y, w, h];
142
+ }
133
143
  /**
134
144
  * @retuns {Vector}
135
145
  */
@@ -145,9 +155,7 @@
145
155
  if (this.hidden || this.opacity <= 0) return;
146
156
  litecanvas.push();
147
157
  this.transform(litecanvas);
148
- const anchorX = this.sprite.width * this.anchor.x;
149
- const anchorY = this.sprite.height * this.anchor.y;
150
- litecanvas.image(-anchorX, -anchorY, this.sprite);
158
+ this.drawImage(litecanvas);
151
159
  litecanvas.pop();
152
160
  }
153
161
  /**
@@ -157,7 +165,15 @@
157
165
  litecanvas.translate(this.pos.x, this.pos.y);
158
166
  litecanvas.rotate(this.angle);
159
167
  litecanvas.scale(this._s.x, this._s.y);
168
+ }
169
+ /**
170
+ * @param {LitecanvasInstance} litecanvas
171
+ */
172
+ drawImage(litecanvas) {
173
+ const anchorX = this.sprite.width * this.anchor.x;
174
+ const anchorY = this.sprite.height * this.anchor.y;
160
175
  litecanvas.alpha(this.opacity);
176
+ litecanvas.image(-anchorX, -anchorY, this.sprite);
161
177
  }
162
178
  };
163
179
 
package/dist/actor.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var f=Object.defineProperty;var p=(e,t)=>{for(var s in t)f(e,s,{get:t[s],enumerable:!0})};globalThis.utils=globalThis.utils||{};globalThis.utils.global=()=>{for(let e in globalThis.utils)e!=="global"&&(globalThis[e]=globalThis.utils[e])};var a={};p(a,{ANCHOR_BOT_LEFT:()=>d,ANCHOR_BOT_RIGHT:()=>x,ANCHOR_CENTER:()=>u,ANCHOR_TOP_LEFT:()=>l,ANCHOR_TOP_RIGHT:()=>h,Actor:()=>i});var n=class{x;y;constructor(t=0,s=t){this.x=t,this.y=s}toString(){return`Vector (${this.x}, ${this.y})`}},o=(e=0,t=e)=>new n(e,t);var c=e=>o(e.x,e.y);var u=o(.5,.5),l=o(0,0),h=o(1,0),d=o(0,1),x=o(1,1),i=class{sprite;pos;_o;_s;angle=0;opacity=1;hidden=!1;constructor(t,s,r=l){this.sprite=t,this.pos=s||o(0),this._o=c(r),this._s=o(1,1)}set x(t){this.pos.x=t}get x(){return this.pos.x}set y(t){this.pos.y=t}get y(){return this.pos.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,r=this.sprite.height*this.anchor.y;t.image(-s,-r,this.sprite),t.pop()}transform(t){t.translate(this.pos.x,this.pos.y),t.rotate(this.angle),t.scale(this._s.x,this._s.y),t.alpha(this.opacity)}};globalThis.utils=Object.assign(globalThis.utils||{},a);})();
1
+ (()=>{var h=Object.defineProperty;var u=(e,t)=>{for(var o in t)h(e,o,{get:t[o],enumerable:!0})};globalThis.utils=globalThis.utils||{};globalThis.utils.global=()=>{for(let e in globalThis.utils)e!=="global"&&(globalThis[e]=globalThis.utils[e])};var a={};u(a,{ANCHOR_BOT_LEFT:()=>g,ANCHOR_BOT_RIGHT:()=>y,ANCHOR_CENTER:()=>d,ANCHOR_TOP_LEFT:()=>l,ANCHOR_TOP_RIGHT:()=>x,Actor:()=>i});var n=class{x;y;constructor(t=0,o=t){this.x=t,this.y=o}toString(){return`Vector (${this.x}, ${this.y})`}},s=(e=0,t=e)=>new n(e,t);var c=e=>s(e.x,e.y);var d=s(.5,.5),l=s(0,0),x=s(1,0),g=s(0,1),y=s(1,1),i=class{sprite;pos;_o;_s;angle=0;opacity=1;hidden=!1;constructor(t,o,r=l){this.sprite=t,this.pos=o||s(0),this._o=c(r),this._s=s(1,1)}set x(t){this.pos.x=t}get x(){return this.pos.x}set y(t){this.pos.y=t}get y(){return this.pos.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.x}get height(){return this.sprite.height*this._s.y}getBounds(t=!0){let o=this.sprite.width*(t?this._s.x:1),r=this.sprite.height*(t?this._s.y:1),f=this.pos.x-o*this.anchor.x,p=this.pos.y-r*this.anchor.y;return[f,p,o,r]}get scale(){return this._s}draw(t=globalThis){this.hidden||this.opacity<=0||(t.push(),this.transform(t),this.drawImage(t),t.pop())}transform(t){t.translate(this.pos.x,this.pos.y),t.rotate(this.angle),t.scale(this._s.x,this._s.y)}drawImage(t){let o=this.sprite.width*this.anchor.x,r=this.sprite.height*this.anchor.y;t.alpha(this.opacity),t.image(-o,-r,this.sprite)}};globalThis.utils=Object.assign(globalThis.utils||{},a);})();
package/dist/all.js CHANGED
@@ -119,10 +119,14 @@
119
119
  var Camera = class {
120
120
  /** @type {LitecanvasInstance} */
121
121
  _engine = null;
122
- /** @type {number} */
122
+ /** @type {number} camera center X */
123
123
  x = 0;
124
- /** @type {number} */
124
+ /** @type {number} camera center Y */
125
125
  y = 0;
126
+ /** @type {number} offset X */
127
+ ox = 0;
128
+ /** @type {number} offset Y*/
129
+ oy = 0;
126
130
  /** @type {number} */
127
131
  width = 0;
128
132
  /** @type {number} */
@@ -139,27 +143,35 @@
139
143
  /**
140
144
  * @param {LitecanvasInstance} engine
141
145
  */
142
- constructor(engine = null) {
143
- engine = engine || globalThis;
144
- this._engine = engine;
145
- this.resize(engine.WIDTH || 0, engine.HEIGHT || 0);
146
+ constructor(engine = null, ox = 0, oy = 0, width = null, height = null) {
147
+ this._engine = engine || globalThis;
148
+ this.ox = ox;
149
+ this.oy = oy;
150
+ this.resize(
151
+ width || this._engine.WIDTH - ox,
152
+ height || this._engine.HEIGHT - oy
153
+ );
146
154
  this.x = this.width / 2;
147
155
  this.y = this.height / 2;
148
156
  }
149
157
  resize(width, height) {
150
158
  this.width = width;
151
159
  this.height = height;
160
+ this._engine.emit("camera-resized", this);
152
161
  }
162
+ /**
163
+ * @param {boolean} [clip] default: `false`
164
+ */
153
165
  start(clip = false) {
154
- const centerX = this.width / 2, centerY = this.height / 2;
155
166
  this._engine.push();
167
+ if (clip) {
168
+ this._engine.cliprect(this.ox, this.oy, this.width, this.height);
169
+ }
170
+ const centerX = this.ox + this.width / 2, centerY = this.oy + this.height / 2;
156
171
  this._engine.translate(centerX, centerY);
157
172
  this._engine.scale(this.scale);
158
173
  this._engine.rotate(this.rotation);
159
174
  this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y);
160
- if (clip) {
161
- this._engine.cliprect(this.x, this.y, this.width, this.height);
162
- }
163
175
  }
164
176
  end() {
165
177
  this._engine.pop();
@@ -180,27 +192,95 @@
180
192
  this.x += dx;
181
193
  this.y += dy;
182
194
  }
195
+ /**
196
+ * @param {number} value
197
+ */
183
198
  zoom(value) {
184
199
  this.scale *= value;
185
200
  }
201
+ /**
202
+ * @param {number} value
203
+ */
186
204
  zoomTo(value) {
187
205
  this.scale = value;
188
206
  }
207
+ /**
208
+ * @param {number} radians
209
+ */
189
210
  rotate(radians) {
190
211
  this.rotation += radians;
191
212
  }
213
+ /**
214
+ * @param {number} radians
215
+ */
192
216
  rotateTo(radians) {
193
217
  this.rotation = radians;
194
218
  }
195
- getWorldPoint(x, y, output) {
219
+ /**
220
+ * @param {number} x
221
+ * @param {number} y
222
+ * @param {{x: number, y: number}} [output]
223
+ * @returns {{x: number, y: number}}
224
+ */
225
+ getWorldPoint(x, y, output = {}) {
196
226
  const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
197
- x = (x - this.width / 2) / this.scale;
198
- y = (y - this.height / 2) / this.scale;
199
- output = output || {};
227
+ x = (x - this.width / 2 - this.ox) / this.scale;
228
+ y = (y - this.height / 2 - this.oy) / this.scale;
200
229
  output.x = c * x - s * y + this.x;
201
230
  output.y = s * x + c * y + this.y;
202
231
  return output;
203
232
  }
233
+ /**
234
+ * @param {number} x
235
+ * @param {number} y
236
+ * @param {{x: number, y: number}} [output]
237
+ * @returns {{x: number, y: number}}
238
+ */
239
+ getCameraPoint(x, y, output = {}) {
240
+ const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
241
+ x = x - this.x;
242
+ y = y - this.y;
243
+ x = c * x - s * y;
244
+ y = s * x + c * y;
245
+ output.x = x * this.scale + this.width / 2 + this.ox;
246
+ output.y = y * this.scale + this.height / 2 + this.oy;
247
+ return output;
248
+ }
249
+ /**
250
+ * @returns {number[]}
251
+ */
252
+ getBounds() {
253
+ return [this.ox, this.oy, this.width, this.height];
254
+ }
255
+ /**
256
+ * Check if a rect is inside of the camera.
257
+ *
258
+ * @param {number} x
259
+ * @param {number} y
260
+ * @param {number} width
261
+ * @param {number} height
262
+ * @returns {boolean}
263
+ */
264
+ viewing(x, y, width, height) {
265
+ const cameraX = this.width / 2 - this.x;
266
+ const cameraY = this.height / 2 - this.y;
267
+ const cameraWidth = this.width / this.scale;
268
+ const cameraHeight = this.height / this.scale;
269
+ return this._engine.colrect(
270
+ x,
271
+ y,
272
+ width,
273
+ height,
274
+ cameraX,
275
+ cameraY,
276
+ cameraWidth,
277
+ cameraHeight
278
+ );
279
+ }
280
+ /**
281
+ * @param {number} amplitude
282
+ * @param {number} duration in seconds
283
+ */
204
284
  shake(amplitude = 1, duration = 0.3) {
205
285
  if (this.shaking) return;
206
286
  this._shake.removeListener = this._engine.listen("update", (dt) => {
@@ -219,6 +299,9 @@
219
299
  this._shake.x = this._shake.y = 0;
220
300
  }
221
301
  }
302
+ /**
303
+ * @returns {boolean}
304
+ */
222
305
  get shaking() {
223
306
  return this._shake.removeListener !== null;
224
307
  }
@@ -630,7 +713,7 @@
630
713
  * @returns {number}
631
714
  */
632
715
  get width() {
633
- return this.sprite.width * this._s.y;
716
+ return this.sprite.width * this._s.x;
634
717
  }
635
718
  /**
636
719
  * @returns {number}
@@ -638,6 +721,16 @@
638
721
  get height() {
639
722
  return this.sprite.height * this._s.y;
640
723
  }
724
+ /**
725
+ * @returns {number[]}
726
+ */
727
+ getBounds(scaled = true) {
728
+ const w = this.sprite.width * (scaled ? this._s.x : 1);
729
+ const h = this.sprite.height * (scaled ? this._s.y : 1);
730
+ const x = this.pos.x - w * this.anchor.x;
731
+ const y = this.pos.y - h * this.anchor.y;
732
+ return [x, y, w, h];
733
+ }
641
734
  /**
642
735
  * @retuns {Vector}
643
736
  */
@@ -653,9 +746,7 @@
653
746
  if (this.hidden || this.opacity <= 0) return;
654
747
  litecanvas.push();
655
748
  this.transform(litecanvas);
656
- const anchorX = this.sprite.width * this.anchor.x;
657
- const anchorY = this.sprite.height * this.anchor.y;
658
- litecanvas.image(-anchorX, -anchorY, this.sprite);
749
+ this.drawImage(litecanvas);
659
750
  litecanvas.pop();
660
751
  }
661
752
  /**
@@ -665,7 +756,15 @@
665
756
  litecanvas.translate(this.pos.x, this.pos.y);
666
757
  litecanvas.rotate(this.angle);
667
758
  litecanvas.scale(this._s.x, this._s.y);
759
+ }
760
+ /**
761
+ * @param {LitecanvasInstance} litecanvas
762
+ */
763
+ drawImage(litecanvas) {
764
+ const anchorX = this.sprite.width * this.anchor.x;
765
+ const anchorY = this.sprite.height * this.anchor.y;
668
766
  litecanvas.alpha(this.opacity);
767
+ litecanvas.image(-anchorX, -anchorY, this.sprite);
669
768
  }
670
769
  };
671
770
 
package/dist/all.min.js CHANGED
@@ -1,2 +1,2 @@
1
- (()=>{var v=Object.defineProperty;var F=(e,t)=>{for(var s in t)v(e,s,{get:t[s],enumerable:!0})};globalThis.utils=globalThis.utils||{};globalThis.utils.global=()=>{for(let e in globalThis.utils)e!=="global"&&(globalThis[e]=globalThis.utils[e])};var M={};F(M,{ANCHOR_BOT_LEFT:()=>nt,ANCHOR_BOT_RIGHT:()=>at,ANCHOR_CENTER:()=>it,ANCHOR_TOP_LEFT:()=>L,ANCHOR_TOP_RIGHT:()=>rt,Actor:()=>I,Camera:()=>p,DOWN:()=>st,Grid:()=>g,LEFT:()=>ot,ONE:()=>Q,RIGHT:()=>et,TypedGrid:()=>w,UP:()=>tt,Vector:()=>u,ZERO:()=>K,diff:()=>P,fract:()=>D,intersection:()=>x,isvector:()=>c,resolve:()=>C,vec:()=>i,vecadd:()=>z,vecconfig:()=>H,veccopy:()=>E,veccross:()=>U,vecdir:()=>N,vecdist:()=>$,vecdist2:()=>q,vecdiv:()=>_,vecdot:()=>V,veceq:()=>S,veclerp:()=>Z,veclimit:()=>j,vecmag:()=>Y,vecmag2:()=>W,vecmult:()=>b,vecnorm:()=>B,vecrand:()=>J,vecrot:()=>G,vecset:()=>X,vecsub:()=>R,wave:()=>O});var P=(e,t)=>Math.abs(t-e)||0;var D=e=>e%1||0;var O=(e,t,s,o=Math.sin)=>e+(o(s)+1)/2*(t-e);var x=(e,t,s,o,n,r,a,h)=>{let l=Math.max(e,n),A=Math.min(e+s,n+a)-l,d=Math.max(t,r),m=Math.min(t+o,r+h)-d;return[l,d,A,m]};var C=(e,t,s,o,n,r,a,h)=>{let[l,A,d,m]=x(e,t,s,o,n,r,a,h),f="",y=e,T=t;return d<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.resize(t.WIDTH||0,t.HEIGHT||0),this.x=this.width/2,this.y=this.height/2}resize(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}getWorldPoint(t,s,o){let n=Math.cos(-this.rotation),r=Math.sin(-this.rotation);return t=(t-this.width/2)/this.scale,s=(s-this.height/2)/this.scale,o=o||{},o.x=n*t-r*s+this.x,o.y=r*t+n*s+this.y,o}shake(t=1,s=.3){this.shaking||(this._shake.removeListener=this._engine.listen("update",o=>{this._shake.x=this._engine.randi(-t,t),this._shake.y=this._engine.randi(-t,t),s-=o,s<=0&&this.unshake()}))}unshake(){this.shaking&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}get shaking(){return this._shake.removeListener!==null}};var g=class e{_w;_h;_c;constructor(t,s,o=[]){this._w=Math.max(1,~~t),this._h=Math.max(1,~~s),this._c=o}clear(){this.forEach((t,s)=>this.set(t,s,void 0))}get width(){return this._w}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 k(t,0,this._w-1)}clampY(t){return k(t,0,this._h-1)}toArray(){return this._c.slice()}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
- `)}},w=class e extends g{constructor(t,s,o=Uint8Array){super(t,s,null),this._c=new o(this._w*this._h)}has(t,s){return this.get(t,s)!==0}clone(){let t=new e(this._w,this._h,this._c.constructor);return this.forEach((s,o,n)=>{t.set(s,o,n)}),t}};function k(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})`}},i=(e=0,t=e)=>new u(e,t),S=(e,t,s=t)=>c(t)?S(e,t.x,t.y):e.x===t&&e.y===s,E=e=>i(e.x,e.y),X=(e,t,s=t)=>{c(t)?X(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)},R=(e,t,s=t)=>{c(t)?R(e,t.x,t.y):(e.x-=t,e.y-=s)},b=(e,t,s=t)=>{c(t)?b(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},Y=e=>Math.sqrt(e.x*e.x+e.y*e.y),W=e=>e.x*e.x+e.y*e.y,B=e=>{let t=Y(e);t>0&&_(e,t)},j=(e,t)=>{let s=W(e);s>t*t&&(_(e,Math.sqrt(s)),b(e,t))},$=(e,t)=>{let s=e.x-t.x,o=e.y-t.y;return Math.sqrt(s*s+o*o)},q=(e,t)=>{let s=e.x-t.x,o=e.y-t.y;return s*s+o*o},N=e=>Math.atan2(e.y,e.x),V=(e,t)=>e.x*t.x+e.y*t.y,U=(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},J=(e=1,t=e)=>{let s=H.random()*2*Math.PI,o=H.random()*(t-e)+e;return i(Math.cos(s)*o,Math.sin(s)*o)},c=e=>e instanceof u,H={random:()=>globalThis.rand?rand():Math.random()},K=i(0,0),Q=i(1,1),tt=i(0,-1),et=i(1,0),st=i(0,1),ot=i(-1,0);var it=i(.5,.5),L=i(0,0),rt=i(1,0),nt=i(0,1),at=i(1,1),I=class{sprite;pos;_o;_s;angle=0;opacity=1;hidden=!1;constructor(t,s,o=L){this.sprite=t,this.pos=s||i(0),this._o=E(o),this._s=i(1,1)}set x(t){this.pos.x=t}get x(){return this.pos.x}set y(t){this.pos.y=t}get y(){return this.pos.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.pos.x,this.pos.y),t.rotate(this.angle),t.scale(this._s.x,this._s.y),t.alpha(this.opacity)}};globalThis.utils=Object.assign(globalThis.utils||{},M);})();
1
+ (()=>{var v=Object.defineProperty;var B=(e,t)=>{for(var s in t)v(e,s,{get:t[s],enumerable:!0})};globalThis.utils=globalThis.utils||{};globalThis.utils.global=()=>{for(let e in globalThis.utils)e!=="global"&&(globalThis[e]=globalThis.utils[e])};var M={};B(M,{ANCHOR_BOT_LEFT:()=>nt,ANCHOR_BOT_RIGHT:()=>at,ANCHOR_CENTER:()=>ot,ANCHOR_TOP_LEFT:()=>L,ANCHOR_TOP_RIGHT:()=>rt,Actor:()=>b,Camera:()=>p,DOWN:()=>st,Grid:()=>x,LEFT:()=>it,ONE:()=>Q,RIGHT:()=>et,TypedGrid:()=>w,UP:()=>tt,Vector:()=>u,ZERO:()=>K,diff:()=>A,fract:()=>D,intersection:()=>g,isvector:()=>l,resolve:()=>C,vec:()=>n,vecadd:()=>S,vecconfig:()=>H,veccopy:()=>E,veccross:()=>U,vecdir:()=>N,vecdist:()=>$,vecdist2:()=>q,vecdiv:()=>_,vecdot:()=>V,veceq:()=>X,veclerp:()=>Z,veclimit:()=>j,vecmag:()=>R,vecmag2:()=>W,vecmult:()=>I,vecnorm:()=>G,vecrand:()=>J,vecrot:()=>F,vecset:()=>z,vecsub:()=>Y,wave:()=>O});var A=(e,t)=>Math.abs(t-e)||0;var D=e=>e%1||0;var O=(e,t,s,i=Math.sin)=>e+(i(s)+1)/2*(t-e);var g=(e,t,s,i,r,o,a,h)=>{let c=Math.max(e,r),P=Math.min(e+s,r+a)-c,d=Math.max(t,o),m=Math.min(t+i,o+h)-d;return[c,d,P,m]};var C=(e,t,s,i,r,o,a,h)=>{let[c,P,d,m]=g(e,t,s,i,r,o,a,h),f="",y=e,T=t;return d<m?e<r?(f="right",y=r-s):(f="left",y=r+a):t<o?(f="bottom",T=o-i):(f="top",T=o+h),{direction:f,x:y,y:T}};var p=class{_engine=null;x=0;y=0;ox=0;oy=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(t=null,s=0,i=0,r=null,o=null){this._engine=t||globalThis,this.ox=s,this.oy=i,this.resize(r||this._engine.WIDTH-s,o||this._engine.HEIGHT-i),this.x=this.width/2,this.y=this.height/2}resize(t,s){this.width=t,this.height=s,this._engine.emit("camera-resized",this)}start(t=!1){this._engine.push(),t&&this._engine.cliprect(this.ox,this.oy,this.width,this.height);let s=this.ox+this.width/2,i=this.oy+this.height/2;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)}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}getWorldPoint(t,s,i={}){let r=Math.cos(-this.rotation),o=Math.sin(-this.rotation);return t=(t-this.width/2-this.ox)/this.scale,s=(s-this.height/2-this.oy)/this.scale,i.x=r*t-o*s+this.x,i.y=o*t+r*s+this.y,i}getCameraPoint(t,s,i={}){let r=Math.cos(-this.rotation),o=Math.sin(-this.rotation);return t=t-this.x,s=s-this.y,t=r*t-o*s,s=o*t+r*s,i.x=t*this.scale+this.width/2+this.ox,i.y=s*this.scale+this.height/2+this.oy,i}getBounds(){return[this.ox,this.oy,this.width,this.height]}viewing(t,s,i,r){let o=this.width/2-this.x,a=this.height/2-this.y,h=this.width/this.scale,c=this.height/this.scale;return this._engine.colrect(t,s,i,r,o,a,h,c)}shake(t=1,s=.3){this.shaking||(this._shake.removeListener=this._engine.listen("update",i=>{this._shake.x=this._engine.randi(-t,t),this._shake.y=this._engine.randi(-t,t),s-=i,s<=0&&this.unshake()}))}unshake(){this.shaking&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}get shaking(){return this._shake.removeListener!==null}};var x=class e{_w;_h;_c;constructor(t,s,i=[]){this._w=Math.max(1,~~t),this._h=Math.max(1,~~s),this._c=i}clear(){this.forEach((t,s)=>this.set(t,s,void 0))}get width(){return this._w}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 a=this.indexToPointX(i),h=this.indexToPointY(i),c=this._c[i];if(t(a,h,c,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 k(t,0,this._w-1)}clampY(t){return k(t,0,this._h-1)}toArray(){return this._c.slice()}toString(t=" ",s=!0){if(!s)return this._c.join(t);let i=[];return this.forEach((r,o,a)=>{i[o]=i[o]||"",i[o]+=a+t}),i.join(`
2
+ `)}},w=class e extends x{constructor(t,s,i=Uint8Array){super(t,s,null),this._c=new i(this._w*this._h)}has(t,s){return this.get(t,s)!==0}clone(){let t=new e(this._w,this._h,this._c.constructor);return this.forEach((s,i,r)=>{t.set(s,i,r)}),t}};function k(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})`}},n=(e=0,t=e)=>new u(e,t),X=(e,t,s=t)=>l(t)?X(e,t.x,t.y):e.x===t&&e.y===s,E=e=>n(e.x,e.y),z=(e,t,s=t)=>{l(t)?z(e,t.x,t.y):(e.x=t,e.y=s)},S=(e,t,s=t)=>{l(t)?S(e,t.x,t.y):(e.x+=t,e.y+=s)},Y=(e,t,s=t)=>{l(t)?Y(e,t.x,t.y):(e.x-=t,e.y-=s)},I=(e,t,s=t)=>{l(t)?I(e,t.x,t.y):(e.x*=t,e.y*=s)},_=(e,t,s=t)=>{l(t)?_(e,t.x,t.y):(e.x/=t,e.y/=s)},F=(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},R=e=>Math.sqrt(e.x*e.x+e.y*e.y),W=e=>e.x*e.x+e.y*e.y,G=e=>{let t=R(e);t>0&&_(e,t)},j=(e,t)=>{let s=W(e);s>t*t&&(_(e,Math.sqrt(s)),I(e,t))},$=(e,t)=>{let s=e.x-t.x,i=e.y-t.y;return Math.sqrt(s*s+i*i)},q=(e,t)=>{let s=e.x-t.x,i=e.y-t.y;return s*s+i*i},N=e=>Math.atan2(e.y,e.x),V=(e,t)=>e.x*t.x+e.y*t.y,U=(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},J=(e=1,t=e)=>{let s=H.random()*2*Math.PI,i=H.random()*(t-e)+e;return n(Math.cos(s)*i,Math.sin(s)*i)},l=e=>e instanceof u,H={random:()=>globalThis.rand?rand():Math.random()},K=n(0,0),Q=n(1,1),tt=n(0,-1),et=n(1,0),st=n(0,1),it=n(-1,0);var ot=n(.5,.5),L=n(0,0),rt=n(1,0),nt=n(0,1),at=n(1,1),b=class{sprite;pos;_o;_s;angle=0;opacity=1;hidden=!1;constructor(t,s,i=L){this.sprite=t,this.pos=s||n(0),this._o=E(i),this._s=n(1,1)}set x(t){this.pos.x=t}get x(){return this.pos.x}set y(t){this.pos.y=t}get y(){return this.pos.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.x}get height(){return this.sprite.height*this._s.y}getBounds(t=!0){let s=this.sprite.width*(t?this._s.x:1),i=this.sprite.height*(t?this._s.y:1),r=this.pos.x-s*this.anchor.x,o=this.pos.y-i*this.anchor.y;return[r,o,s,i]}get scale(){return this._s}draw(t=globalThis){this.hidden||this.opacity<=0||(t.push(),this.transform(t),this.drawImage(t),t.pop())}transform(t){t.translate(this.pos.x,this.pos.y),t.rotate(this.angle),t.scale(this._s.x,this._s.y)}drawImage(t){let s=this.sprite.width*this.anchor.x,i=this.sprite.height*this.anchor.y;t.alpha(this.opacity),t.image(-s,-i,this.sprite)}};globalThis.utils=Object.assign(globalThis.utils||{},M);})();
package/dist/camera.js CHANGED
@@ -12,10 +12,14 @@
12
12
  var Camera = class {
13
13
  /** @type {LitecanvasInstance} */
14
14
  _engine = null;
15
- /** @type {number} */
15
+ /** @type {number} camera center X */
16
16
  x = 0;
17
- /** @type {number} */
17
+ /** @type {number} camera center Y */
18
18
  y = 0;
19
+ /** @type {number} offset X */
20
+ ox = 0;
21
+ /** @type {number} offset Y*/
22
+ oy = 0;
19
23
  /** @type {number} */
20
24
  width = 0;
21
25
  /** @type {number} */
@@ -32,27 +36,35 @@
32
36
  /**
33
37
  * @param {LitecanvasInstance} engine
34
38
  */
35
- constructor(engine = null) {
36
- engine = engine || globalThis;
37
- this._engine = engine;
38
- this.resize(engine.WIDTH || 0, engine.HEIGHT || 0);
39
+ constructor(engine = null, ox = 0, oy = 0, width = null, height = null) {
40
+ this._engine = engine || globalThis;
41
+ this.ox = ox;
42
+ this.oy = oy;
43
+ this.resize(
44
+ width || this._engine.WIDTH - ox,
45
+ height || this._engine.HEIGHT - oy
46
+ );
39
47
  this.x = this.width / 2;
40
48
  this.y = this.height / 2;
41
49
  }
42
50
  resize(width, height) {
43
51
  this.width = width;
44
52
  this.height = height;
53
+ this._engine.emit("camera-resized", this);
45
54
  }
55
+ /**
56
+ * @param {boolean} [clip] default: `false`
57
+ */
46
58
  start(clip = false) {
47
- const centerX = this.width / 2, centerY = this.height / 2;
48
59
  this._engine.push();
60
+ if (clip) {
61
+ this._engine.cliprect(this.ox, this.oy, this.width, this.height);
62
+ }
63
+ const centerX = this.ox + this.width / 2, centerY = this.oy + this.height / 2;
49
64
  this._engine.translate(centerX, centerY);
50
65
  this._engine.scale(this.scale);
51
66
  this._engine.rotate(this.rotation);
52
67
  this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y);
53
- if (clip) {
54
- this._engine.cliprect(this.x, this.y, this.width, this.height);
55
- }
56
68
  }
57
69
  end() {
58
70
  this._engine.pop();
@@ -73,27 +85,95 @@
73
85
  this.x += dx;
74
86
  this.y += dy;
75
87
  }
88
+ /**
89
+ * @param {number} value
90
+ */
76
91
  zoom(value) {
77
92
  this.scale *= value;
78
93
  }
94
+ /**
95
+ * @param {number} value
96
+ */
79
97
  zoomTo(value) {
80
98
  this.scale = value;
81
99
  }
100
+ /**
101
+ * @param {number} radians
102
+ */
82
103
  rotate(radians) {
83
104
  this.rotation += radians;
84
105
  }
106
+ /**
107
+ * @param {number} radians
108
+ */
85
109
  rotateTo(radians) {
86
110
  this.rotation = radians;
87
111
  }
88
- getWorldPoint(x, y, output) {
112
+ /**
113
+ * @param {number} x
114
+ * @param {number} y
115
+ * @param {{x: number, y: number}} [output]
116
+ * @returns {{x: number, y: number}}
117
+ */
118
+ getWorldPoint(x, y, output = {}) {
89
119
  const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
90
- x = (x - this.width / 2) / this.scale;
91
- y = (y - this.height / 2) / this.scale;
92
- output = output || {};
120
+ x = (x - this.width / 2 - this.ox) / this.scale;
121
+ y = (y - this.height / 2 - this.oy) / this.scale;
93
122
  output.x = c * x - s * y + this.x;
94
123
  output.y = s * x + c * y + this.y;
95
124
  return output;
96
125
  }
126
+ /**
127
+ * @param {number} x
128
+ * @param {number} y
129
+ * @param {{x: number, y: number}} [output]
130
+ * @returns {{x: number, y: number}}
131
+ */
132
+ getCameraPoint(x, y, output = {}) {
133
+ const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
134
+ x = x - this.x;
135
+ y = y - this.y;
136
+ x = c * x - s * y;
137
+ y = s * x + c * y;
138
+ output.x = x * this.scale + this.width / 2 + this.ox;
139
+ output.y = y * this.scale + this.height / 2 + this.oy;
140
+ return output;
141
+ }
142
+ /**
143
+ * @returns {number[]}
144
+ */
145
+ getBounds() {
146
+ return [this.ox, this.oy, this.width, this.height];
147
+ }
148
+ /**
149
+ * Check if a rect is inside of the camera.
150
+ *
151
+ * @param {number} x
152
+ * @param {number} y
153
+ * @param {number} width
154
+ * @param {number} height
155
+ * @returns {boolean}
156
+ */
157
+ viewing(x, y, width, height) {
158
+ const cameraX = this.width / 2 - this.x;
159
+ const cameraY = this.height / 2 - this.y;
160
+ const cameraWidth = this.width / this.scale;
161
+ const cameraHeight = this.height / this.scale;
162
+ return this._engine.colrect(
163
+ x,
164
+ y,
165
+ width,
166
+ height,
167
+ cameraX,
168
+ cameraY,
169
+ cameraWidth,
170
+ cameraHeight
171
+ );
172
+ }
173
+ /**
174
+ * @param {number} amplitude
175
+ * @param {number} duration in seconds
176
+ */
97
177
  shake(amplitude = 1, duration = 0.3) {
98
178
  if (this.shaking) return;
99
179
  this._shake.removeListener = this._engine.listen("update", (dt) => {
@@ -112,6 +192,9 @@
112
192
  this._shake.x = this._shake.y = 0;
113
193
  }
114
194
  }
195
+ /**
196
+ * @returns {boolean}
197
+ */
115
198
  get shaking() {
116
199
  return this._shake.removeListener !== null;
117
200
  }
@@ -1 +1 @@
1
- (()=>{globalThis.utils=globalThis.utils||{};globalThis.utils.global=()=>{for(let e in globalThis.utils)e!=="global"&&(globalThis[e]=globalThis.utils[e])};var h=class{_engine=null;x=0;y=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(i=null){i=i||globalThis,this._engine=i,this.resize(i.WIDTH||0,i.HEIGHT||0),this.x=this.width/2,this.y=this.height/2}resize(i,s){this.width=i,this.height=s}start(i=!1){let s=this.width/2,t=this.height/2;this._engine.push(),this._engine.translate(s,t),this._engine.scale(this.scale),this._engine.rotate(this.rotation),this._engine.translate(-this.x+this._shake.x,-this.y+this._shake.y),i&&this._engine.cliprect(this.x,this.y,this.width,this.height)}end(){this._engine.pop()}lookAt(i,s){this.x=i,this.y=s}move(i,s){this.x+=i,this.y+=s}zoom(i){this.scale*=i}zoomTo(i){this.scale=i}rotate(i){this.rotation+=i}rotateTo(i){this.rotation=i}getWorldPoint(i,s,t){let n=Math.cos(-this.rotation),a=Math.sin(-this.rotation);return i=(i-this.width/2)/this.scale,s=(s-this.height/2)/this.scale,t=t||{},t.x=n*i-a*s+this.x,t.y=a*i+n*s+this.y,t}shake(i=1,s=.3){this.shaking||(this._shake.removeListener=this._engine.listen("update",t=>{this._shake.x=this._engine.randi(-i,i),this._shake.y=this._engine.randi(-i,i),s-=t,s<=0&&this.unshake()}))}unshake(){this.shaking&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}get shaking(){return this._shake.removeListener!==null}};globalThis.utils=Object.assign(globalThis.utils||{},{Camera:h});})();
1
+ (()=>{globalThis.utils=globalThis.utils||{};globalThis.utils.global=()=>{for(let o in globalThis.utils)o!=="global"&&(globalThis[o]=globalThis.utils[o])};var n=class{_engine=null;x=0;y=0;ox=0;oy=0;width=0;height=0;rotation=0;scale=1;_shake={x:0,y:0,removeListener:null};constructor(i=null,t=0,s=0,h=null,e=null){this._engine=i||globalThis,this.ox=t,this.oy=s,this.resize(h||this._engine.WIDTH-t,e||this._engine.HEIGHT-s),this.x=this.width/2,this.y=this.height/2}resize(i,t){this.width=i,this.height=t,this._engine.emit("camera-resized",this)}start(i=!1){this._engine.push(),i&&this._engine.cliprect(this.ox,this.oy,this.width,this.height);let t=this.ox+this.width/2,s=this.oy+this.height/2;this._engine.translate(t,s),this._engine.scale(this.scale),this._engine.rotate(this.rotation),this._engine.translate(-this.x+this._shake.x,-this.y+this._shake.y)}end(){this._engine.pop()}lookAt(i,t){this.x=i,this.y=t}move(i,t){this.x+=i,this.y+=t}zoom(i){this.scale*=i}zoomTo(i){this.scale=i}rotate(i){this.rotation+=i}rotateTo(i){this.rotation=i}getWorldPoint(i,t,s={}){let h=Math.cos(-this.rotation),e=Math.sin(-this.rotation);return i=(i-this.width/2-this.ox)/this.scale,t=(t-this.height/2-this.oy)/this.scale,s.x=h*i-e*t+this.x,s.y=e*i+h*t+this.y,s}getCameraPoint(i,t,s={}){let h=Math.cos(-this.rotation),e=Math.sin(-this.rotation);return i=i-this.x,t=t-this.y,i=h*i-e*t,t=e*i+h*t,s.x=i*this.scale+this.width/2+this.ox,s.y=t*this.scale+this.height/2+this.oy,s}getBounds(){return[this.ox,this.oy,this.width,this.height]}viewing(i,t,s,h){let e=this.width/2-this.x,a=this.height/2-this.y,l=this.width/this.scale,r=this.height/this.scale;return this._engine.colrect(i,t,s,h,e,a,l,r)}shake(i=1,t=.3){this.shaking||(this._shake.removeListener=this._engine.listen("update",s=>{this._shake.x=this._engine.randi(-i,i),this._shake.y=this._engine.randi(-i,i),t-=s,t<=0&&this.unshake()}))}unshake(){this.shaking&&(this._shake.removeListener(),this._shake.removeListener=null,this._shake.x=this._shake.y=0)}get shaking(){return this._shake.removeListener!==null}};globalThis.utils=Object.assign(globalThis.utils||{},{Camera:n});})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litecanvas/utils",
3
- "version": "0.10.1",
3
+ "version": "0.11.1",
4
4
  "description": "Utilities to help build litecanvas games",
5
5
  "author": "Luiz Bills <luizbills@pm.me>",
6
6
  "license": "MIT",
@@ -88,7 +88,7 @@ export class Actor {
88
88
  * @returns {number}
89
89
  */
90
90
  get width() {
91
- return this.sprite.width * this._s.y
91
+ return this.sprite.width * this._s.x
92
92
  }
93
93
 
94
94
  /**
@@ -98,6 +98,17 @@ export class Actor {
98
98
  return this.sprite.height * this._s.y
99
99
  }
100
100
 
101
+ /**
102
+ * @returns {number[]}
103
+ */
104
+ getBounds(scaled = true) {
105
+ const w = this.sprite.width * (scaled ? this._s.x : 1)
106
+ const h = this.sprite.height * (scaled ? this._s.y : 1)
107
+ const x = this.pos.x - w * this.anchor.x
108
+ const y = this.pos.y - h * this.anchor.y
109
+ return [x, y, w, h]
110
+ }
111
+
101
112
  /**
102
113
  * @retuns {Vector}
103
114
  */
@@ -116,10 +127,7 @@ export class Actor {
116
127
  litecanvas.push()
117
128
 
118
129
  this.transform(litecanvas)
119
-
120
- const anchorX = this.sprite.width * this.anchor.x
121
- const anchorY = this.sprite.height * this.anchor.y
122
- litecanvas.image(-anchorX, -anchorY, this.sprite)
130
+ this.drawImage(litecanvas)
123
131
 
124
132
  litecanvas.pop()
125
133
  }
@@ -131,6 +139,15 @@ export class Actor {
131
139
  litecanvas.translate(this.pos.x, this.pos.y)
132
140
  litecanvas.rotate(this.angle)
133
141
  litecanvas.scale(this._s.x, this._s.y)
142
+ }
143
+
144
+ /**
145
+ * @param {LitecanvasInstance} litecanvas
146
+ */
147
+ drawImage(litecanvas) {
148
+ const anchorX = this.sprite.width * this.anchor.x
149
+ const anchorY = this.sprite.height * this.anchor.y
134
150
  litecanvas.alpha(this.opacity)
151
+ litecanvas.image(-anchorX, -anchorY, this.sprite)
135
152
  }
136
153
  }
@@ -27,8 +27,10 @@ function draw() {
27
27
 
28
28
  ## Properties
29
29
 
30
- - `x` the camera X-position
31
- - `y` the camera Y-position
30
+ - `x` the camera position-X
31
+ - `y` the camera position-Y
32
+ - `ox` the camera offset-X
33
+ - `oy` the camera offset-Y
32
34
  - `shaking` it's `true` when the camera is shaking
33
35
  - `scale` the camera zoom
34
36
  - `rotation` the camera rotation
@@ -37,6 +39,14 @@ function draw() {
37
39
 
38
40
  ## Methods
39
41
 
42
+ ### start(clip: boolean = true)
43
+
44
+ Apply the camera transformations (move, zoom and rotate). You must call this method before draw anything inside of the camera.
45
+
46
+ ### stop()
47
+
48
+ Stop looking through the camera. You must call this method after draw anything inside of the camera.
49
+
40
50
  ### lookAt(x: number, y: number)
41
51
 
42
52
  Sets the camera X and Y
@@ -96,7 +106,7 @@ function update(dt) {
96
106
  }
97
107
  ```
98
108
 
99
- ### shake(amplitude:number = 1, duration: number = 0.3)
109
+ ### shake(duration: number = 0.3, amplitude: number = 1)
100
110
 
101
111
  Shakes the camera view
102
112
 
@@ -113,10 +123,20 @@ Makes the camera stop shaking immediately
113
123
 
114
124
  ### getWorldPoint(x: number, y: number, output?: {x: number, y: number}): {x: number, y: number}
115
125
 
116
- This method is used to convert a point (X, Y) if the camera is moved, rotated or zoomed.
126
+ This method is used to convert a camera point (X, Y) to world point.
117
127
 
118
128
  ```
129
+ // convert a mouse/touch to world position
119
130
  function tapped(x, y) {
120
131
  const fixedTap = camera.getWorldPoint(x, y)
121
132
  }
122
133
  ```
134
+
135
+ ### getCameraPoint(x: number, y: number, output?: {x: number, y: number}): {x: number, y: number}
136
+
137
+ This method is used to convert a world point (X, Y) to camera point.
138
+
139
+ ### getBounds(): number[]
140
+
141
+ Returns an array containing the position-X, position-Y, the width, and the height of the camera.
142
+ Useful to see if an object is visible on the screen.
@@ -2,10 +2,17 @@ export default class Camera {
2
2
  /** @type {LitecanvasInstance} */
3
3
  _engine = null
4
4
 
5
- /** @type {number} */
5
+ /** @type {number} camera center X */
6
6
  x = 0
7
- /** @type {number} */
7
+ /** @type {number} camera center Y */
8
8
  y = 0
9
+
10
+ /** @type {number} offset X */
11
+ ox = 0
12
+
13
+ /** @type {number} offset Y*/
14
+ oy = 0
15
+
9
16
  /** @type {number} */
10
17
  width = 0
11
18
  /** @type {number} */
@@ -25,10 +32,17 @@ export default class Camera {
25
32
  /**
26
33
  * @param {LitecanvasInstance} engine
27
34
  */
28
- constructor(engine = null) {
29
- engine = engine || globalThis
30
- this._engine = engine
31
- this.resize(engine.WIDTH || 0, engine.HEIGHT || 0)
35
+ constructor(engine = null, ox = 0, oy = 0, width = null, height = null) {
36
+ this._engine = engine || globalThis
37
+
38
+ this.ox = ox
39
+ this.oy = oy
40
+
41
+ this.resize(
42
+ width || this._engine.WIDTH - ox,
43
+ height || this._engine.HEIGHT - oy
44
+ )
45
+
32
46
  this.x = this.width / 2
33
47
  this.y = this.height / 2
34
48
  }
@@ -36,21 +50,26 @@ export default class Camera {
36
50
  resize(width, height) {
37
51
  this.width = width
38
52
  this.height = height
53
+ this._engine.emit("camera-resized", this)
39
54
  }
40
55
 
56
+ /**
57
+ * @param {boolean} [clip] default: `false`
58
+ */
41
59
  start(clip = false) {
42
- const centerX = this.width / 2,
43
- centerY = this.height / 2
44
-
45
60
  this._engine.push()
61
+
62
+ if (clip) {
63
+ this._engine.cliprect(this.ox, this.oy, this.width, this.height)
64
+ }
65
+
66
+ const centerX = this.ox + this.width / 2,
67
+ centerY = this.oy + this.height / 2
68
+
46
69
  this._engine.translate(centerX, centerY)
47
70
  this._engine.scale(this.scale)
48
71
  this._engine.rotate(this.rotation)
49
72
  this._engine.translate(-this.x + this._shake.x, -this.y + this._shake.y)
50
-
51
- if (clip) {
52
- this._engine.cliprect(this.x, this.y, this.width, this.height)
53
- }
54
73
  }
55
74
 
56
75
  end() {
@@ -75,36 +94,112 @@ export default class Camera {
75
94
  this.y += dy
76
95
  }
77
96
 
97
+ /**
98
+ * @param {number} value
99
+ */
78
100
  zoom(value) {
79
101
  this.scale *= value
80
102
  }
81
103
 
104
+ /**
105
+ * @param {number} value
106
+ */
82
107
  zoomTo(value) {
83
108
  this.scale = value
84
109
  }
85
110
 
111
+ /**
112
+ * @param {number} radians
113
+ */
86
114
  rotate(radians) {
87
115
  this.rotation += radians
88
116
  }
89
117
 
118
+ /**
119
+ * @param {number} radians
120
+ */
90
121
  rotateTo(radians) {
91
122
  this.rotation = radians
92
123
  }
93
124
 
94
- getWorldPoint(x, y, output) {
125
+ /**
126
+ * @param {number} x
127
+ * @param {number} y
128
+ * @param {{x: number, y: number}} [output]
129
+ * @returns {{x: number, y: number}}
130
+ */
131
+ getWorldPoint(x, y, output = {}) {
95
132
  const c = Math.cos(-this.rotation),
96
133
  s = Math.sin(-this.rotation)
97
134
 
98
- x = (x - this.width / 2) / this.scale
99
- y = (y - this.height / 2) / this.scale
135
+ x = (x - this.width / 2 - this.ox) / this.scale
136
+ y = (y - this.height / 2 - this.oy) / this.scale
100
137
 
101
- output = output || {}
102
138
  output.x = c * x - s * y + this.x
103
139
  output.y = s * x + c * y + this.y
104
140
 
105
141
  return output
106
142
  }
107
143
 
144
+ /**
145
+ * @param {number} x
146
+ * @param {number} y
147
+ * @param {{x: number, y: number}} [output]
148
+ * @returns {{x: number, y: number}}
149
+ */
150
+ getCameraPoint(x, y, output = {}) {
151
+ const c = Math.cos(-this.rotation),
152
+ s = Math.sin(-this.rotation)
153
+
154
+ x = x - this.x
155
+ y = y - this.y
156
+ x = c * x - s * y
157
+ y = s * x + c * y
158
+
159
+ output.x = x * this.scale + this.width / 2 + this.ox
160
+ output.y = y * this.scale + this.height / 2 + this.oy
161
+
162
+ return output
163
+ }
164
+
165
+ /**
166
+ * @returns {number[]}
167
+ */
168
+ getBounds() {
169
+ return [this.ox, this.oy, this.width, this.height]
170
+ }
171
+
172
+ /**
173
+ * Check if a rect is inside of the camera.
174
+ *
175
+ * @param {number} x
176
+ * @param {number} y
177
+ * @param {number} width
178
+ * @param {number} height
179
+ * @returns {boolean}
180
+ */
181
+ viewing(x, y, width, height) {
182
+ const cameraX = this.width / 2 - this.x
183
+ const cameraY = this.height / 2 - this.y
184
+ const cameraWidth = this.width / this.scale
185
+ const cameraHeight = this.height / this.scale
186
+
187
+ return this._engine.colrect(
188
+ x,
189
+ y,
190
+ width,
191
+ height,
192
+ cameraX,
193
+ cameraY,
194
+ cameraWidth,
195
+ cameraHeight
196
+ )
197
+ }
198
+
199
+ /**
200
+ * @param {number} amplitude
201
+ * @param {number} duration in seconds
202
+ */
108
203
  shake(amplitude = 1, duration = 0.3) {
109
204
  if (this.shaking) return
110
205
 
@@ -126,6 +221,9 @@ export default class Camera {
126
221
  }
127
222
  }
128
223
 
224
+ /**
225
+ * @returns {boolean}
226
+ */
129
227
  get shaking() {
130
228
  return this._shake.removeListener !== null
131
229
  }