@litecanvas/utils 0.10.1 → 0.11.0

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,32 @@
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(width || engine.WIDTH - ox, height || engine.HEIGHT - oy);
146
151
  this.x = this.width / 2;
147
152
  this.y = this.height / 2;
148
153
  }
149
154
  resize(width, height) {
150
155
  this.width = width;
151
156
  this.height = height;
157
+ this._engine.emit("camera-resized", this);
152
158
  }
159
+ /**
160
+ * @param {boolean} [clip] default: `false`
161
+ */
153
162
  start(clip = false) {
154
- const centerX = this.width / 2, centerY = this.height / 2;
155
163
  this._engine.push();
164
+ if (clip) {
165
+ this._engine.cliprect(this.ox, this.oy, this.width, this.height);
166
+ }
167
+ const centerX = this.ox + this.width / 2, centerY = this.oy + this.height / 2;
156
168
  this._engine.translate(centerX, centerY);
157
169
  this._engine.scale(this.scale);
158
170
  this._engine.rotate(this.rotation);
159
171
  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
172
  }
164
173
  end() {
165
174
  this._engine.pop();
@@ -180,27 +189,95 @@
180
189
  this.x += dx;
181
190
  this.y += dy;
182
191
  }
192
+ /**
193
+ * @param {number} value
194
+ */
183
195
  zoom(value) {
184
196
  this.scale *= value;
185
197
  }
198
+ /**
199
+ * @param {number} value
200
+ */
186
201
  zoomTo(value) {
187
202
  this.scale = value;
188
203
  }
204
+ /**
205
+ * @param {number} radians
206
+ */
189
207
  rotate(radians) {
190
208
  this.rotation += radians;
191
209
  }
210
+ /**
211
+ * @param {number} radians
212
+ */
192
213
  rotateTo(radians) {
193
214
  this.rotation = radians;
194
215
  }
195
- getWorldPoint(x, y, output) {
216
+ /**
217
+ * @param {number} x
218
+ * @param {number} y
219
+ * @param {{x: number, y: number}} [output]
220
+ * @returns {{x: number, y: number}}
221
+ */
222
+ getWorldPoint(x, y, output = {}) {
196
223
  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 || {};
224
+ x = (x - this.width / 2 - this.ox) / this.scale;
225
+ y = (y - this.height / 2 - this.oy) / this.scale;
200
226
  output.x = c * x - s * y + this.x;
201
227
  output.y = s * x + c * y + this.y;
202
228
  return output;
203
229
  }
230
+ /**
231
+ * @param {number} x
232
+ * @param {number} y
233
+ * @param {{x: number, y: number}} [output]
234
+ * @returns {{x: number, y: number}}
235
+ */
236
+ getCameraPoint(x, y, output = {}) {
237
+ const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
238
+ x = x - this.x;
239
+ y = y - this.y;
240
+ x = c * x - s * y;
241
+ y = s * x + c * y;
242
+ output.x = x * this.scale + this.width / 2 + this.ox;
243
+ output.y = y * this.scale + this.height / 2 + this.oy;
244
+ return output;
245
+ }
246
+ /**
247
+ * @returns {number[]}
248
+ */
249
+ getBounds() {
250
+ return [this.ox, this.oy, this.width, this.height];
251
+ }
252
+ /**
253
+ * Check if a rect is inside of the camera.
254
+ *
255
+ * @param {number} x
256
+ * @param {number} y
257
+ * @param {number} width
258
+ * @param {number} height
259
+ * @returns {boolean}
260
+ */
261
+ viewing(x, y, width, height) {
262
+ const cameraX = this.width / 2 - this.x;
263
+ const cameraY = this.height / 2 - this.y;
264
+ const cameraWidth = this.width / this.scale;
265
+ const cameraHeight = this.height / this.scale;
266
+ return this._engine.colrect(
267
+ x,
268
+ y,
269
+ width,
270
+ height,
271
+ cameraX,
272
+ cameraY,
273
+ cameraWidth,
274
+ cameraHeight
275
+ );
276
+ }
277
+ /**
278
+ * @param {number} amplitude
279
+ * @param {number} duration in seconds
280
+ */
204
281
  shake(amplitude = 1, duration = 0.3) {
205
282
  if (this.shaking) return;
206
283
  this._shake.removeListener = this._engine.listen("update", (dt) => {
@@ -219,6 +296,9 @@
219
296
  this._shake.x = this._shake.y = 0;
220
297
  }
221
298
  }
299
+ /**
300
+ * @returns {boolean}
301
+ */
222
302
  get shaking() {
223
303
  return this._shake.removeListener !== null;
224
304
  }
@@ -630,7 +710,7 @@
630
710
  * @returns {number}
631
711
  */
632
712
  get width() {
633
- return this.sprite.width * this._s.y;
713
+ return this.sprite.width * this._s.x;
634
714
  }
635
715
  /**
636
716
  * @returns {number}
@@ -638,6 +718,16 @@
638
718
  get height() {
639
719
  return this.sprite.height * this._s.y;
640
720
  }
721
+ /**
722
+ * @returns {number[]}
723
+ */
724
+ getBounds(scaled = true) {
725
+ const w = this.sprite.width * (scaled ? this._s.x : 1);
726
+ const h = this.sprite.height * (scaled ? this._s.y : 1);
727
+ const x = this.pos.x - w * this.anchor.x;
728
+ const y = this.pos.y - h * this.anchor.y;
729
+ return [x, y, w, h];
730
+ }
641
731
  /**
642
732
  * @retuns {Vector}
643
733
  */
@@ -653,9 +743,7 @@
653
743
  if (this.hidden || this.opacity <= 0) return;
654
744
  litecanvas.push();
655
745
  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);
746
+ this.drawImage(litecanvas);
659
747
  litecanvas.pop();
660
748
  }
661
749
  /**
@@ -665,7 +753,15 @@
665
753
  litecanvas.translate(this.pos.x, this.pos.y);
666
754
  litecanvas.rotate(this.angle);
667
755
  litecanvas.scale(this._s.x, this._s.y);
756
+ }
757
+ /**
758
+ * @param {LitecanvasInstance} litecanvas
759
+ */
760
+ drawImage(litecanvas) {
761
+ const anchorX = this.sprite.width * this.anchor.x;
762
+ const anchorY = this.sprite.height * this.anchor.y;
668
763
  litecanvas.alpha(this.opacity);
764
+ litecanvas.image(-anchorX, -anchorY, this.sprite);
669
765
  }
670
766
  };
671
767
 
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||t.WIDTH-s,o||t.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,32 @@
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(width || engine.WIDTH - ox, height || engine.HEIGHT - oy);
39
44
  this.x = this.width / 2;
40
45
  this.y = this.height / 2;
41
46
  }
42
47
  resize(width, height) {
43
48
  this.width = width;
44
49
  this.height = height;
50
+ this._engine.emit("camera-resized", this);
45
51
  }
52
+ /**
53
+ * @param {boolean} [clip] default: `false`
54
+ */
46
55
  start(clip = false) {
47
- const centerX = this.width / 2, centerY = this.height / 2;
48
56
  this._engine.push();
57
+ if (clip) {
58
+ this._engine.cliprect(this.ox, this.oy, this.width, this.height);
59
+ }
60
+ const centerX = this.ox + this.width / 2, centerY = this.oy + this.height / 2;
49
61
  this._engine.translate(centerX, centerY);
50
62
  this._engine.scale(this.scale);
51
63
  this._engine.rotate(this.rotation);
52
64
  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
65
  }
57
66
  end() {
58
67
  this._engine.pop();
@@ -73,27 +82,95 @@
73
82
  this.x += dx;
74
83
  this.y += dy;
75
84
  }
85
+ /**
86
+ * @param {number} value
87
+ */
76
88
  zoom(value) {
77
89
  this.scale *= value;
78
90
  }
91
+ /**
92
+ * @param {number} value
93
+ */
79
94
  zoomTo(value) {
80
95
  this.scale = value;
81
96
  }
97
+ /**
98
+ * @param {number} radians
99
+ */
82
100
  rotate(radians) {
83
101
  this.rotation += radians;
84
102
  }
103
+ /**
104
+ * @param {number} radians
105
+ */
85
106
  rotateTo(radians) {
86
107
  this.rotation = radians;
87
108
  }
88
- getWorldPoint(x, y, output) {
109
+ /**
110
+ * @param {number} x
111
+ * @param {number} y
112
+ * @param {{x: number, y: number}} [output]
113
+ * @returns {{x: number, y: number}}
114
+ */
115
+ getWorldPoint(x, y, output = {}) {
89
116
  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 || {};
117
+ x = (x - this.width / 2 - this.ox) / this.scale;
118
+ y = (y - this.height / 2 - this.oy) / this.scale;
93
119
  output.x = c * x - s * y + this.x;
94
120
  output.y = s * x + c * y + this.y;
95
121
  return output;
96
122
  }
123
+ /**
124
+ * @param {number} x
125
+ * @param {number} y
126
+ * @param {{x: number, y: number}} [output]
127
+ * @returns {{x: number, y: number}}
128
+ */
129
+ getCameraPoint(x, y, output = {}) {
130
+ const c = Math.cos(-this.rotation), s = Math.sin(-this.rotation);
131
+ x = x - this.x;
132
+ y = y - this.y;
133
+ x = c * x - s * y;
134
+ y = s * x + c * y;
135
+ output.x = x * this.scale + this.width / 2 + this.ox;
136
+ output.y = y * this.scale + this.height / 2 + this.oy;
137
+ return output;
138
+ }
139
+ /**
140
+ * @returns {number[]}
141
+ */
142
+ getBounds() {
143
+ return [this.ox, this.oy, this.width, this.height];
144
+ }
145
+ /**
146
+ * Check if a rect is inside of the camera.
147
+ *
148
+ * @param {number} x
149
+ * @param {number} y
150
+ * @param {number} width
151
+ * @param {number} height
152
+ * @returns {boolean}
153
+ */
154
+ viewing(x, y, width, height) {
155
+ const cameraX = this.width / 2 - this.x;
156
+ const cameraY = this.height / 2 - this.y;
157
+ const cameraWidth = this.width / this.scale;
158
+ const cameraHeight = this.height / this.scale;
159
+ return this._engine.colrect(
160
+ x,
161
+ y,
162
+ width,
163
+ height,
164
+ cameraX,
165
+ cameraY,
166
+ cameraWidth,
167
+ cameraHeight
168
+ );
169
+ }
170
+ /**
171
+ * @param {number} amplitude
172
+ * @param {number} duration in seconds
173
+ */
97
174
  shake(amplitude = 1, duration = 0.3) {
98
175
  if (this.shaking) return;
99
176
  this._shake.removeListener = this._engine.listen("update", (dt) => {
@@ -112,6 +189,9 @@
112
189
  this._shake.x = this._shake.y = 0;
113
190
  }
114
191
  }
192
+ /**
193
+ * @returns {boolean}
194
+ */
115
195
  get shaking() {
116
196
  return this._shake.removeListener !== null;
117
197
  }
@@ -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(t=null,i=0,s=0,h=null,e=null){this._engine=t||globalThis,this.ox=i,this.oy=s,this.resize(h||t.WIDTH-i,e||t.HEIGHT-s),this.x=this.width/2,this.y=this.height/2}resize(t,i){this.width=t,this.height=i,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 i=this.ox+this.width/2,s=this.oy+this.height/2;this._engine.translate(i,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(t,i){this.x=t,this.y=i}move(t,i){this.x+=t,this.y+=i}zoom(t){this.scale*=t}zoomTo(t){this.scale=t}rotate(t){this.rotation+=t}rotateTo(t){this.rotation=t}getWorldPoint(t,i,s={}){let h=Math.cos(-this.rotation),e=Math.sin(-this.rotation);return t=(t-this.width/2-this.ox)/this.scale,i=(i-this.height/2-this.oy)/this.scale,s.x=h*t-e*i+this.x,s.y=e*t+h*i+this.y,s}getCameraPoint(t,i,s={}){let h=Math.cos(-this.rotation),e=Math.sin(-this.rotation);return t=t-this.x,i=i-this.y,t=h*t-e*i,i=e*t+h*i,s.x=t*this.scale+this.width/2+this.ox,s.y=i*this.scale+this.height/2+this.oy,s}getBounds(){return[this.ox,this.oy,this.width,this.height]}viewing(t,i,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(t,i,s,h,e,a,l,r)}shake(t=1,i=.3){this.shaking||(this._shake.removeListener=this._engine.listen("update",s=>{this._shake.x=this._engine.randi(-t,t),this._shake.y=this._engine.randi(-t,t),i-=s,i<=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.0",
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,14 @@ 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(width || engine.WIDTH - ox, height || engine.HEIGHT - oy)
42
+
32
43
  this.x = this.width / 2
33
44
  this.y = this.height / 2
34
45
  }
@@ -36,21 +47,26 @@ export default class Camera {
36
47
  resize(width, height) {
37
48
  this.width = width
38
49
  this.height = height
50
+ this._engine.emit("camera-resized", this)
39
51
  }
40
52
 
53
+ /**
54
+ * @param {boolean} [clip] default: `false`
55
+ */
41
56
  start(clip = false) {
42
- const centerX = this.width / 2,
43
- centerY = this.height / 2
44
-
45
57
  this._engine.push()
58
+
59
+ if (clip) {
60
+ this._engine.cliprect(this.ox, this.oy, this.width, this.height)
61
+ }
62
+
63
+ const centerX = this.ox + this.width / 2,
64
+ centerY = this.oy + this.height / 2
65
+
46
66
  this._engine.translate(centerX, centerY)
47
67
  this._engine.scale(this.scale)
48
68
  this._engine.rotate(this.rotation)
49
69
  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
70
  }
55
71
 
56
72
  end() {
@@ -75,36 +91,112 @@ export default class Camera {
75
91
  this.y += dy
76
92
  }
77
93
 
94
+ /**
95
+ * @param {number} value
96
+ */
78
97
  zoom(value) {
79
98
  this.scale *= value
80
99
  }
81
100
 
101
+ /**
102
+ * @param {number} value
103
+ */
82
104
  zoomTo(value) {
83
105
  this.scale = value
84
106
  }
85
107
 
108
+ /**
109
+ * @param {number} radians
110
+ */
86
111
  rotate(radians) {
87
112
  this.rotation += radians
88
113
  }
89
114
 
115
+ /**
116
+ * @param {number} radians
117
+ */
90
118
  rotateTo(radians) {
91
119
  this.rotation = radians
92
120
  }
93
121
 
94
- getWorldPoint(x, y, output) {
122
+ /**
123
+ * @param {number} x
124
+ * @param {number} y
125
+ * @param {{x: number, y: number}} [output]
126
+ * @returns {{x: number, y: number}}
127
+ */
128
+ getWorldPoint(x, y, output = {}) {
95
129
  const c = Math.cos(-this.rotation),
96
130
  s = Math.sin(-this.rotation)
97
131
 
98
- x = (x - this.width / 2) / this.scale
99
- y = (y - this.height / 2) / this.scale
132
+ x = (x - this.width / 2 - this.ox) / this.scale
133
+ y = (y - this.height / 2 - this.oy) / this.scale
100
134
 
101
- output = output || {}
102
135
  output.x = c * x - s * y + this.x
103
136
  output.y = s * x + c * y + this.y
104
137
 
105
138
  return output
106
139
  }
107
140
 
141
+ /**
142
+ * @param {number} x
143
+ * @param {number} y
144
+ * @param {{x: number, y: number}} [output]
145
+ * @returns {{x: number, y: number}}
146
+ */
147
+ getCameraPoint(x, y, output = {}) {
148
+ const c = Math.cos(-this.rotation),
149
+ s = Math.sin(-this.rotation)
150
+
151
+ x = x - this.x
152
+ y = y - this.y
153
+ x = c * x - s * y
154
+ y = s * x + c * y
155
+
156
+ output.x = x * this.scale + this.width / 2 + this.ox
157
+ output.y = y * this.scale + this.height / 2 + this.oy
158
+
159
+ return output
160
+ }
161
+
162
+ /**
163
+ * @returns {number[]}
164
+ */
165
+ getBounds() {
166
+ return [this.ox, this.oy, this.width, this.height]
167
+ }
168
+
169
+ /**
170
+ * Check if a rect is inside of the camera.
171
+ *
172
+ * @param {number} x
173
+ * @param {number} y
174
+ * @param {number} width
175
+ * @param {number} height
176
+ * @returns {boolean}
177
+ */
178
+ viewing(x, y, width, height) {
179
+ const cameraX = this.width / 2 - this.x
180
+ const cameraY = this.height / 2 - this.y
181
+ const cameraWidth = this.width / this.scale
182
+ const cameraHeight = this.height / this.scale
183
+
184
+ return this._engine.colrect(
185
+ x,
186
+ y,
187
+ width,
188
+ height,
189
+ cameraX,
190
+ cameraY,
191
+ cameraWidth,
192
+ cameraHeight
193
+ )
194
+ }
195
+
196
+ /**
197
+ * @param {number} amplitude
198
+ * @param {number} duration in seconds
199
+ */
108
200
  shake(amplitude = 1, duration = 0.3) {
109
201
  if (this.shaking) return
110
202
 
@@ -126,6 +218,9 @@ export default class Camera {
126
218
  }
127
219
  }
128
220
 
221
+ /**
222
+ * @returns {boolean}
223
+ */
129
224
  get shaking() {
130
225
  return this._shake.removeListener !== null
131
226
  }