diff --git a/package.json b/package.json index f5456e5..acfabd9 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "husky": "^7.0.1", "size-limit": "^5.0.1", "tsdx": "^0.14.1", - "tslib": "^2.3.0", - "typescript": "^4.3.5" + "tslib": "^2.3.1", + "typescript": "^4.6.3" } } diff --git a/src/grid.ts b/src/grid.ts index 04f33ef..5415154 100644 --- a/src/grid.ts +++ b/src/grid.ts @@ -25,7 +25,7 @@ export function grid_traverse_initStep( t1: number, t2: number ): [number, number, number] { - let v: number = t2 - t1; + const v: number = t2 - t1; if (v > 0) return [1, cellSize / v, ((ct + v) * cellSize - t1) / v]; else if (v < 0) @@ -39,37 +39,37 @@ export function grid_traverse( y1: number, x2: number, y2: number, - f: any + traverseFunction: (cx: number, cy: number) => void ): void { - let [cx1, cy1] = grid_toCell(cellSize, x1, y1); - let [cx2, cy2] = grid_toCell(cellSize, x2, y2); + const [cx1, cy1] = grid_toCell(cellSize, x1, y1); + const [cx2, cy2] = grid_toCell(cellSize, x2, y2); let [stepX, dx, tx] = grid_traverse_initStep(cellSize, cx1, x1, x2); let [stepY, dy, ty] = grid_traverse_initStep(cellSize, cy1, y1, y2); let [cx, cy] = [cx1, cy1]; - f(cx, cy); + traverseFunction(cx, cy); - //The default implementation had an infinite loop problem when - //approaching the last cell in some occassions.We finish iterating - //when we are * next * to the last cell + // The default implementation had an infinite loop problem when + // approaching the last cell in some occassions. We finish iterating + // when we are *next* to the last cell. do { if (tx < ty) { [tx, cx] = [tx + dx, cx + stepX]; - f(cx, cy); + traverseFunction(cx, cy); } else { // Addition: include both cells when going through corners - if (tx == ty) f(cx + stepX, cy); + if (tx == ty) traverseFunction(cx + stepX, cy); ty = ty + dy; cy = cy + stepY; - f(cx, cy); + traverseFunction(cx, cy); } } while (Math.abs(cx - cx2) + Math.abs(cy - cy2) > 1); //If we have not arrived to the last cell, use it - if (cx != cx2 || cy != cy2) f(cx2, cy2); + if (cx != cx2 || cy != cy2) traverseFunction(cx2, cy2); } export function grid_toCellRect( @@ -81,8 +81,8 @@ export function grid_toCellRect( ): [number, number, number, number] { let [cx, cy] = grid_toCell(cellSize, x, y); - const cr = Math.ceil((x + w) / cellSize); - const cb = Math.ceil((y + h) / cellSize); + const cr: number = Math.ceil((x + w) / cellSize); + const cb: number = Math.ceil((y + h) / cellSize); return [cx, cy, cr - cx + 1, cb - cy + 1]; } diff --git a/src/helpers/generic/assertIsPositiveNumber.ts b/src/helpers/generic/assertIsPositiveNumber.ts index b0dcd1f..f15809d 100644 --- a/src/helpers/generic/assertIsPositiveNumber.ts +++ b/src/helpers/generic/assertIsPositiveNumber.ts @@ -1,11 +1,9 @@ -export default function assertIsPositiveNumber(value: any, name: string): void { +export default function assertIsPositiveNumber( + value: number, + name: string +): void { if (isNaN(value) || value <= 0) throw new Error( - name + - ' must be a positive integer, but was ' + - value + - ' (' + - typeof value + - ')' + `"${name}" must be a positive integer, but was ${value} (${typeof value})` ); } diff --git a/src/helpers/generic/assertType.ts b/src/helpers/generic/assertType.ts index 9624862..99865b7 100644 --- a/src/helpers/generic/assertType.ts +++ b/src/helpers/generic/assertType.ts @@ -1,6 +1,6 @@ export default function assertType(desiredType: string, value: any, name: string): void { if (typeof value !== desiredType) throw new Error( - `${name} must be a ${desiredType}, but was a ${value} (${typeof value})` + `"${name}" must be a ${desiredType}, but was a ${value} (${typeof value})` ); } diff --git a/src/helpers/world/responses.ts b/src/helpers/world/responses.ts index b2d0aae..4db4e2a 100644 --- a/src/helpers/world/responses.ts +++ b/src/helpers/world/responses.ts @@ -1,6 +1,6 @@ -import { Coords, World } from '../../index'; +import { Collision, ICoords, World } from '../../index'; -type ResponseType = ( +export type Response = ( world: World, col: any, x: number, @@ -10,11 +10,17 @@ type ResponseType = ( goalX: number, goalY: number, filter: any -) => [number, number, any, number]; +) => { x: number; y: number; collisions: Collision[] }; export function touch( _world: World, - col: any, + column: { + touch: ICoords; + move: ICoords; + normal: ICoords; + slide: ICoords; + item: any; + }, _x: number, _y: number, _w: number, @@ -22,13 +28,19 @@ export function touch( _goalX: number, _goalY: number, _filter: any -): { x: number; y: number; collisions: any[] } { - return { x: col.touch.x, y: col.touch.y, collisions: [] }; +): { x: number; y: number; collisions: Collision[] } { + return { x: column.touch.x, y: column.touch.y, collisions: [] }; } export function cross( world: any, - col: any, + column: { + touch: ICoords; + move: ICoords; + normal: ICoords; + slide: ICoords; + item: any; + }, x: number, y: number, w: number, @@ -36,15 +48,30 @@ export function cross( goalX: number, goalY: number, filter: any -): { x: number; y: number; collisions: any[] } { - const collisions = world.project(col.item, x, y, w, h, goalX, goalY, filter); +): { x: number; y: number; collisions: Collision[] } { + const collisions = world.project( + column.item, + x, + y, + w, + h, + goalX, + goalY, + filter + ); return { x: goalX, y: goalY, collisions }; } export function slide( world: World, - col: any, + column: { + touch: ICoords; + move: ICoords; + normal: ICoords; + slide: ICoords; + item: any; + }, x: number, y: number, w: number, @@ -52,24 +79,24 @@ export function slide( goalX: number, goalY: number, filter?: any -): { x: number; y: number; collisions: any[] } { +): { x: number; y: number; collisions: Collision[] } { let _goalX: number = isNaN(goalX) ? x : goalX; let _goalY: number = isNaN(goalY) ? y : goalY; - const tch: Coords = col.touch; - const move: Coords = col.move; + const tch: ICoords = column.touch; + const move: ICoords = column.move; if (move.x !== 0 || move.y !== 0) - if (col.normal.x !== 0) _goalX = tch.x; + if (column.normal.x !== 0) _goalX = tch.x; else _goalY = tch.y; - col.slide = { x: _goalX, y: _goalY }; + column.slide = { x: _goalX, y: _goalY }; const _x: number = tch.x; const _y: number = tch.y; const collisions = world.project( - col.item, + column.item, _x, _y, w, @@ -92,7 +119,7 @@ export function bounce( goalX?: number, goalY?: number, filter?: any -): { x: number; y: number; collisions: any[] } { +): { x: number; y: number; collisions: Collision[] } { const _goalX: number = isNaN(goalX as number) ? x : goalX!; const _goalY: number = isNaN(goalY as number) ? y : goalY!; diff --git a/src/helpers/world/sortByTiAndDistance.ts b/src/helpers/world/sortByTiAndDistance.ts index 000592d..77cd9e5 100644 --- a/src/helpers/world/sortByTiAndDistance.ts +++ b/src/helpers/world/sortByTiAndDistance.ts @@ -1,4 +1,4 @@ -import { Rect } from '../..'; +import { IRect } from '../..'; import { rect_getSquareDistance } from '../../rect'; interface Item { @@ -7,15 +7,15 @@ interface Item { // w: number; // h: number; ti: number; - itemRect: Rect; - otherRect: Rect; + itemRect: IRect; + otherRect: IRect; } export default function sortByTiAndDistance(a: Item, b: Item): number { if (a.ti === b.ti) { - const ir: Rect = a.itemRect; - const ar: Rect = a.otherRect; - const br: Rect = b.otherRect; + const ir: IRect = a.itemRect; + const ar: IRect = a.otherRect; + const br: IRect = b.otherRect; const ad = rect_getSquareDistance( ir.x, diff --git a/src/index.ts b/src/index.ts index 4b1a79d..57c2ccc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,21 +15,34 @@ import { grid_traverse, } from './grid'; import assertIsRect from './helpers/generic/assertIsRect'; -import { bounce, cross, slide, touch } from './helpers/world/responses'; +import { + bounce, + cross, + Response, + slide, + touch, +} from './helpers/world/responses'; import sortByTiAndDistance from './helpers/world/sortByTiAndDistance'; function defaultFilter(): string { return 'slide'; } -export interface Rect { +interface IItemInfo { + item: string; + ti1: number; + ti2: number; + weight: number; +} + +export interface IRect { x: number; y: number; w: number; h: number; } -export interface Coords { +export interface ICoords { x: number; y: number; } @@ -40,18 +53,23 @@ export interface Collision { type?: 'touch' | 'cross' | 'slide' | 'bounce' | string; overlaps: boolean; ti: number; - move: Coords; - normal: Coords; - touch: Coords; - itemRect: Rect; - otherRect: Rect; - slide?: Coords; - bounce?: Coords; + move: ICoords; + normal: ICoords; + touch: ICoords; + itemRect: IRect; + otherRect: IRect; + slide?: ICoords; + bounce?: ICoords; } -export type Cell = { ID: string; x: number; y: number; items: any[] }; +export type Cell = { + ID: string; + x: number; + y: number; + items: { [itemID: string]: boolean }; +}; -function sortByWeight(a: any, b: any): number { +function sortByWeight(a: IItemInfo, b: IItemInfo): number { return a.weight - b.weight; } @@ -62,14 +80,14 @@ function getCellsTouchedBySegment( x2: number, y2: number ): Cell[] { - let cells: Cell[] = []; - let visited: { [itemID: string]: boolean } = {}; + const cells: Cell[] = []; + const visited: { [itemID: string]: boolean } = {}; grid_traverse(self.cellSize, x1, y1, x2, y2, function( cx: number, cy: number ) { - let row: any[] = self.rows[cy]; + let row: Cell[] = self.rows[cy]; if (!row) return; @@ -97,7 +115,7 @@ function getInfoAboutItemsTouchedBySegment( let rect, l, t, w, h, ti1, ti2; let visited: { [itemID: string]: boolean } = {}; - let itemInfo: any[] = []; + let itemInfo: IItemInfo[] = []; for (const cell of cells) { if (cell?.items) @@ -148,8 +166,8 @@ function getInfoAboutItemsTouchedBySegment( itemInfo.push({ item: itemID, - ti1: ti1, - ti2: ti2, + ti1: ti1!, + ti2: ti2!, weight: Math.min(tii0 || 0, tii1 || 0), }); } @@ -162,10 +180,10 @@ function getInfoAboutItemsTouchedBySegment( } export class World { - responses: { [responseID: string]: any } = {}; + responses: { [responseID: string]: Response } = {}; cellSize: number = 0; - rows: any[][]; - rects: { [itemID: string]: Rect }; + rows: Cell[][]; + rects: { [itemID: string]: IRect }; nonEmptyCells: { [cellID: string]: boolean }; constructor(input: { @@ -182,7 +200,10 @@ export class World { this.responses = input.responses; } - addResponse(name: string, response: any): void { + addResponse( + name: 'bounce' | 'slide' | 'cross' | 'touch', + response: Response + ): void { this.responses[name] = response; } @@ -203,7 +224,7 @@ export class World { h: number, goalX?: number, goalY?: number, - filter?: (...args: any[]) => any + filter?: (...args: any[]) => string ): Collision[] { assertIsRect(x, y, w, h); @@ -277,7 +298,7 @@ export class World { countCells(): number { let count = 0; - for (const row of this.rows.filter((row) => !!row)) + for (const row of this.rows.filter(row => !!row)) for (const _col of row) if (!!_col) count++; return count; @@ -287,8 +308,8 @@ export class World { return !!this.rects[item]; } - getItems(): Rect[] { - return Object.keys(this.rects).map((rectID) => this.rects[rectID]); + getItems(): IRect[] { + return Object.keys(this.rects).map(rectID => this.rects[rectID]); } countItems(): number { @@ -316,7 +337,7 @@ export class World { if (!cell.items[itemID]) cell.items[itemID] = true; } - getRect(itemID: string): Rect { + getRect(itemID: string): IRect { let rect = this.rects[itemID]; if (!rect) @@ -513,7 +534,7 @@ export class World { } add(itemID: string, x: number, y: number, w: number, h: number): string { - let rect: Rect = this.rects[itemID]; + const rect: IRect = this.rects[itemID]; if (rect) throw new Error(`Item "${itemID}" added to the world twice.`); @@ -521,7 +542,7 @@ export class World { this.rects[itemID] = { x, y, w, h }; - let [cl, ct, cw, ch] = grid_toCellRect(this.cellSize, x, y, w, h); + const [cl, ct, cw, ch] = grid_toCellRect(this.cellSize, x, y, w, h); for (let cy = ct; cy < ct + ch; cy++) for (let cx = cl; cx < cl + cw; cx++) this.addItemToCell(itemID, cx, cy); @@ -530,7 +551,7 @@ export class World { } remove(itemID: string): void { - const itemRect: Rect = JSON.parse(JSON.stringify(this.getRect(itemID))); + const itemRect: IRect = JSON.parse(JSON.stringify(this.getRect(itemID))); delete this.rects[itemID]; @@ -547,13 +568,19 @@ export class World { this.removeItemFromCell(itemID, cx, cy); } - update(itemID: string, x2: number, y2: number, w2?: any, h2?: number): void { + update( + itemID: string, + x2: number, + y2: number, + w2?: number, + h2?: number + ): void { let itemRect = this.getRect(itemID); w2 = isNaN(w2 as number) ? itemRect.w : w2; h2 = isNaN(h2 as number) ? itemRect.h : h2; - assertIsRect(x2, y2, w2, h2!); + assertIsRect(x2, y2, w2!, h2!); if ( itemRect.x != x2 || @@ -561,7 +588,7 @@ export class World { itemRect.w != w2 || itemRect.h != h2 ) { - let [cl1, ct1, cw1, ch1] = grid_toCellRect( + const [cl1, ct1, cw1, ch1] = grid_toCellRect( this.cellSize, itemRect.x, itemRect.y, @@ -569,20 +596,20 @@ export class World { itemRect.h ); - let [cl2, ct2, cw2, ch2] = grid_toCellRect( + const [cl2, ct2, cw2, ch2] = grid_toCellRect( this.cellSize, x2, y2, - w2, + w2!, h2! ); if (cl1 != cl2 || ct1 != ct2 || cw1 != cw2 || ch1 != ch2) { - let cr1: number = cl1 + cw1 - 1; - let cb1: number = ct1 + ch1 - 1; + const cr1: number = cl1 + cw1 - 1; + const cb1: number = ct1 + ch1 - 1; - let cr2: number = cl2 + cw2 - 1; - let cb2: number = ct2 + ch2 - 1; + const cr2: number = cl2 + cw2 - 1; + const cb2: number = ct2 + ch2 - 1; let cyOut: boolean; @@ -603,7 +630,7 @@ export class World { } } - const rect: Rect = this.rects[itemID]; + const rect: IRect = this.rects[itemID]; rect.x = x2; rect.y = y2; @@ -618,7 +645,7 @@ export class World { goalY: number, filter?: (...args: any[]) => any ): { x: number; y: number; collisions: Collision[] } { - let { x, y, collisions } = this.check(itemID, goalX, goalY, filter); + const { x, y, collisions } = this.check(itemID, goalX, goalY, filter); this.update(itemID, x, y); @@ -644,7 +671,7 @@ export class World { let detectedCollisions: Collision[] = []; - let itemRect: Rect = this.getRect(itemID); + const itemRect: IRect = this.getRect(itemID); // this is returning an empty array. WHY? let projectedCollisions: Collision[] = this.project( @@ -661,13 +688,13 @@ export class World { let collisionsCounter: number = projectedCollisions?.length || 0; while (collisionsCounter > 0) { - let collision: any = projectedCollisions[0]; + const collision: Collision = projectedCollisions[0]; detectedCollisions.push(collision); visited[collision.other] = true; - let response = this.getResponseByName(collision.type); + let response = this.getResponseByName(collision.type!); const { x, y, collisions } = response( this, @@ -700,7 +727,7 @@ const bump = { assertIsPositiveNumber(cellSize, 'cellSize'); - let world = new World({ + const world: World = new World({ cellSize: cellSize, rects: {}, rows: [], @@ -732,4 +759,4 @@ const bump = { }, }; -export default Object.seal(bump); +export default Object.freeze(bump); diff --git a/src/rect.ts b/src/rect.ts index 7c1985f..8200dc7 100644 --- a/src/rect.ts +++ b/src/rect.ts @@ -1,4 +1,4 @@ -import { Collision, Coords, Rect } from '.'; +import { Collision, ICoords, IRect } from '.'; import { DELTA } from './constants'; import nearest from './helpers/generic/nearest'; @@ -9,7 +9,7 @@ export function rect_getNearestCorner( h: number, px: number, py: number -): Coords { +): ICoords { return { x: nearest(px, x, x + w), y: nearest(py, y, y + h) }; } @@ -133,7 +133,7 @@ export function rect_getDiff( y2: number, w2: number, h2: number -): Rect { +): IRect { return { x: x2 - x1 - w1, y: y2 - y1 - h1, diff --git a/yarn.lock b/yarn.lock index 8ccb07d..034dba9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7891,11 +7891,16 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3, tslib@^2.3.0: +tslib@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tslib@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.17.1: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -7964,10 +7969,10 @@ typescript@^3.7.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== -typescript@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typescript@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" + integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== unbox-primitive@^1.0.1: version "1.0.1"