2023-03-03 16:23:46 +01:00
|
|
|
import { injectable } from "tsyringe";
|
|
|
|
|
|
|
|
export class FindSlotResult
|
|
|
|
{
|
|
|
|
success: boolean;
|
|
|
|
x: any;
|
|
|
|
y: any;
|
|
|
|
rotation: boolean;
|
|
|
|
constructor(success = false, x = null, y = null, rotation = false)
|
|
|
|
{
|
|
|
|
this.success = success;
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.rotation = rotation;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@injectable()
|
|
|
|
export class ContainerHelper
|
|
|
|
{
|
2023-07-24 16:52:55 +02:00
|
|
|
/**
|
|
|
|
* Finds a slot for an item in a given 2D container map
|
|
|
|
* @param container2D Array of container with slots filled/free
|
|
|
|
* @param itemWidth Width of item
|
|
|
|
* @param itemHeight Height of item
|
|
|
|
* @returns Location to place item in container
|
2023-03-03 16:23:46 +01:00
|
|
|
*/
|
|
|
|
public findSlotForItem(container2D: number[][], itemWidth: number, itemHeight: number): FindSlotResult
|
|
|
|
{
|
|
|
|
let rotation = false;
|
|
|
|
const minVolume = (itemWidth < itemHeight ? itemWidth : itemHeight) - 1;
|
|
|
|
const containerY = container2D.length;
|
|
|
|
const containerX = container2D[0].length;
|
|
|
|
const limitY = containerY - minVolume;
|
|
|
|
const limitX = containerX - minVolume;
|
|
|
|
|
|
|
|
for (let y = 0; y < limitY; y++)
|
|
|
|
{
|
|
|
|
for (let x = 0; x < limitX; x++)
|
|
|
|
{
|
|
|
|
let foundSlot = this.locateSlot(container2D, containerX, containerY, x, y, itemWidth, itemHeight);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Try to rotate if there is enough room for the item
|
|
|
|
* Only occupies one grid of items, no rotation required
|
2023-11-16 02:35:05 +01:00
|
|
|
*/
|
2023-03-03 16:23:46 +01:00
|
|
|
if (!foundSlot && itemWidth * itemHeight > 1)
|
|
|
|
{
|
|
|
|
foundSlot = this.locateSlot(container2D, containerX, containerY, x, y, itemHeight, itemWidth);
|
|
|
|
|
|
|
|
if (foundSlot)
|
|
|
|
{
|
|
|
|
rotation = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!foundSlot)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new FindSlotResult(true, x, y, rotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new FindSlotResult();
|
|
|
|
}
|
|
|
|
|
2023-07-24 16:52:55 +02:00
|
|
|
/**
|
|
|
|
* Find a slot inside a container an item can be placed in
|
|
|
|
* @param container2D Container to find space in
|
|
|
|
* @param containerX Container x size
|
|
|
|
* @param containerY Container y size
|
|
|
|
* @param x ???
|
|
|
|
* @param y ???
|
|
|
|
* @param itemW Items width
|
|
|
|
* @param itemH Items height
|
|
|
|
* @returns True - slot found
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
protected locateSlot(
|
|
|
|
container2D: number[][],
|
|
|
|
containerX: number,
|
|
|
|
containerY: number,
|
|
|
|
x: number,
|
|
|
|
y: number,
|
|
|
|
itemW: number,
|
|
|
|
itemH: number,
|
|
|
|
): boolean
|
2023-07-24 16:52:55 +02:00
|
|
|
{
|
|
|
|
let foundSlot = true;
|
|
|
|
|
|
|
|
for (let itemY = 0; itemY < itemH; itemY++)
|
|
|
|
{
|
|
|
|
if (foundSlot && y + itemH - 1 > containerY - 1)
|
|
|
|
{
|
|
|
|
foundSlot = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Does item fit x-ways
|
|
|
|
for (let itemX = 0; itemX < itemW; itemX++)
|
|
|
|
{
|
|
|
|
if (foundSlot && x + itemW - 1 > containerX - 1)
|
|
|
|
{
|
|
|
|
foundSlot = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (container2D[y + itemY][x + itemX] !== 0)
|
|
|
|
{
|
|
|
|
foundSlot = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!foundSlot)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return foundSlot;
|
|
|
|
}
|
|
|
|
|
2023-07-23 13:29:00 +02:00
|
|
|
/**
|
|
|
|
* Find a free slot for an item to be placed at
|
|
|
|
* @param container2D Container to palce item in
|
|
|
|
* @param x Container x size
|
|
|
|
* @param y Container y size
|
|
|
|
* @param itemW Items width
|
|
|
|
* @param itemH Items height
|
|
|
|
* @param rotate is item rotated
|
|
|
|
* @returns Location to place item
|
|
|
|
*/
|
2023-11-16 02:35:05 +01:00
|
|
|
public fillContainerMapWithItem(
|
|
|
|
container2D: number[][],
|
|
|
|
x: number,
|
|
|
|
y: number,
|
|
|
|
itemW: number,
|
|
|
|
itemH: number,
|
|
|
|
rotate: boolean,
|
|
|
|
): number[][]
|
2023-03-03 16:23:46 +01:00
|
|
|
{
|
|
|
|
const itemWidth = rotate ? itemH : itemW;
|
|
|
|
const itemHeight = rotate ? itemW : itemH;
|
|
|
|
|
|
|
|
for (let tmpY = y; tmpY < y + itemHeight; tmpY++)
|
|
|
|
{
|
|
|
|
for (let tmpX = x; tmpX < x + itemWidth; tmpX++)
|
|
|
|
{
|
|
|
|
if (container2D[tmpY][tmpX] === 0)
|
|
|
|
{
|
|
|
|
container2D[tmpY][tmpX] = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-10-10 13:03:20 +02:00
|
|
|
throw new Error(`Slot at (${x}, ${y}) is already filled`);
|
2023-03-03 16:23:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return container2D;
|
|
|
|
}
|
2023-11-16 02:35:05 +01:00
|
|
|
}
|