diff --git a/src/index.html b/src/index.html
index 9fad841..2546a6c 100644
--- a/src/index.html
+++ b/src/index.html
@@ -7,14 +7,16 @@
-
+
+
-
+
+
diff --git a/src/main.js b/src/main.js
index 3b9c66a..b253951 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,4 +1,4 @@
-window.addEventListener('DOMContentLoaded', () => {
+window.addEventListener('DOMContentLoaded', () => document.fonts.ready.then(() => {
const floorCanvas = document.getElementById('canvas-floor');
const gridCanvas = document.getElementById('canvas-grid');
const agentCanvas = document.getElementById('canvas-agent');
@@ -8,4 +8,4 @@ window.addEventListener('DOMContentLoaded', () => {
const grid = new Grid(gridCanvas);
const agent = new Agent(agentCanvas);
const products = new Products(productsCanvas);
-});
\ No newline at end of file
+}));
\ No newline at end of file
diff --git a/src/modules/agent.js b/src/modules/agent.js
index e2b5be2..3cb730e 100644
--- a/src/modules/agent.js
+++ b/src/modules/agent.js
@@ -2,12 +2,47 @@ class Agent extends AgentController {
constructor(canvas) {
super(canvas);
- let { x, y } = Grid.instance.randomPlace();
- this.moveTo(x, y);
+ const cycle = async () => {
- this.onDestinationReached(() => {
- let { x, y } = Grid.instance.randomPlace();
- this.moveTo(x, y);
- })
+ await this.takeFromStore(Product.RANDOM_FROM_REGISTRY(), Math.floor(Math.random() * 40) + 10);
+ const randomPlace = Grid.instance.randomPlace();
+ await this.deliver(randomPlace.x, randomPlace.y);
+
+ await waitFor(2000);
+ cycle();
+ };
+
+ cycle();
+
+ }
+
+ /**
+ * Nakaż agentowi przejść na wybraną pozycję
+ * @param {number} x Współrzędna x
+ * @param {number} y Współrzędna y
+ */
+ moveTo(x, y) {
+ return super.moveTo(x, y);
+ }
+
+ /**
+ * Nakaż agentowi dostarczyć produkt, który aktualnie niesie na wybraną pozycję
+ * @param {number} x Współrzędna x
+ * @param {number} y Współrzędna y
+ */
+ async deliver(x, y) {
+ await this.moveTo(x, y);
+ this.ownedProductAmount = 0;
+ this.ownedProduct = Product.REGISTRY.empty;
+ }
+
+ /**
+ *
+ * @param {*} product
+ */
+ async takeFromStore(product, amount) {
+ await this.moveTo(6, 8);
+ this.ownedProductAmount = amount;
+ this.ownedProduct = product;
}
}
\ No newline at end of file
diff --git a/src/modules/agentController.js b/src/modules/agentController.js
index a6a0501..8502906 100644
--- a/src/modules/agentController.js
+++ b/src/modules/agentController.js
@@ -1,14 +1,25 @@
class AgentController {
constructor(canvas) {
+ // Inicjalizacja
this.ctx = canvas.getContext('2d');
- this.events = {
- 'ON_DESTINATION_REACHED': [],
- };
-
this.setCanvasSize(canvas);
- this.position = { x: 6, y: 0 };
+ this.ownedProduct = Product.REGISTRY.empty;
+ this.ownedProductAmount = 0;
+ // Akcje
+ this.actions = {
+ 'STATIONARY': 0,
+ 'IN_MOVE': 1,
+ 'IN_ROTATION': 2,
+ }
+ this.currentAction = this.actions.STATIONARY;
+
+ // Pozycja i poruszanie się
+ this.position = { x: 6, y: 0 };
+ this.rotation = { z: 0 };
+ this.speed = 30;
+ this.rotationSpeed = 2;
this.update();
}
@@ -21,7 +32,6 @@ class AgentController {
this.clearCanvas();
if (this.currentPath) {
this.drawPath(this.currentPath);
- this.updateAgentPosition();
}
this.drawAgent();
@@ -29,35 +39,141 @@ class AgentController {
}
updateAgentPosition() {
- const speed = 30;
- if (!this.currentPathIndex) {
- this.currentPathIndex = 0;
- }
-
- let nearestPoint = this.currentPath[this.currentPathIndex];
-
- if (nearestPoint) {
- this.position = {
- x: this.position.x + (Math.sign(nearestPoint.x - this.position.x)/speed),
- y: this.position.y + (Math.sign(nearestPoint.y - this.position.y)/speed)
+ return new Promise((resolve, reject) => {
+ const cycle = async () => {
+ if (!this.currentPathIndex) {
+ this.currentPathIndex = 0;
+ }
+
+ let nearestPoint = this.currentPath[this.currentPathIndex];
+
+ if (nearestPoint) {
+ const requiredRotation = this.findRequiredRotation(nearestPoint);
+ await this.rotate({ z: requiredRotation });
+ if (!nearestPoint.options || !nearestPoint.options.isShelf) {
+ await this.moveInLine(nearestPoint);
+ }
+
+ nearestPoint = this.currentPath[++this.currentPathIndex];
+ if (!nearestPoint) {
+ this.currentPath = [];
+ this.currentPathIndex = 0;
+ this.currentAction = this.actions.STATIONARY;
+ resolve();
+ } else {
+ cycle();
+ }
+ }
}
- if (Math.abs(nearestPoint.x - this.position.x) < 0.001 && Math.abs(nearestPoint.y - this.position.y) < 0.001) {
- nearestPoint = this.currentPath[++this.currentPathIndex];
- if (!nearestPoint) {
+
+ cycle();
+ })
+ }
+
+ /**
+ *
+ * @param {{ x: number, y: number }} position
+ */
+ moveInLine(position) {
+ return new Promise((resolve, reject) => {
+ const cycle = () => {
+ const { speed } = this;
+ this.currentAction = this.actions.IN_MOVE;
+ this.position = {
+ x: this.position.x + (Math.sign(position.x - this.position.x)/speed),
+ y: this.position.y + (Math.sign(position.y - this.position.y)/speed)
+ }
+
+ if (Math.abs(position.x - this.position.x) < 0.001 && Math.abs(position.y - this.position.y) < 0.001) {
this.position = {
x: Math.round(this.position.x),
y: Math.round(this.position.y)
}
- this.currentPath = [];
- this.currentPathIndex = 0;
- this.events.ON_DESTINATION_REACHED.forEach(func => func());
+ resolve();
+ } else {
+ requestAnimationFrame(() => cycle());
}
}
+
+ cycle();
+ });
+ }
+
+ /**
+ *
+ * @param {{ x: number, y: number }} position
+ */
+ findRequiredRotation(position) {
+ if (position.x == this.position.x && position.y == this.position.y) {
+ return this.rotation.z;
+ }
+
+ const compare = (a, b) => {
+ return Math.abs(a - b) < 1;
+ }
+
+ if (compare(this.position.x, position.x)) {
+ if (this.position.y < position.y) {
+ return 180;
+ } else {
+ return 0;
+ }
+ } else {
+ if (this.position.x < position.x) {
+ return 90;
+ } else {
+ return 270;
+ }
}
}
+ /**
+ *
+ * @param {{ z: number}} rotation
+ * @returns
+ */
+ rotate(rotation) {
+ return new Promise((resolve, reject) => {
+ // console.log(this.rotation); // 270
+ // console.log(rotation); // 0
+
+ if (this.rotation.z == rotation.z) {
+ resolve();
+ }
+
+ const sign = (() => {
+ if (((this.rotation.z > rotation.z) || (this.rotation.z == 0 && rotation.z == 270)) && !(this.rotation.z == 270 && rotation.z == 0)) {
+ return -1;
+ }
+ return 1;
+ })();
+
+ const cycle = () => {
+ this.currentAction = this.actions.IN_ROTATION;
+
+ const oldZRotation = this.rotation.z;
+ const newZRotation = rotation.z;
+
+ const clampRotation = (rot) => (rot + 360) % 360;
+
+ this.rotation = { z: clampRotation(oldZRotation + (this.rotationSpeed * sign)) };
+
+ if (Math.abs(oldZRotation - newZRotation) < 1) {
+ resolve();
+ this.rotation = { z: clampRotation(newZRotation) }
+ } else {
+ requestAnimationFrame(() => cycle());
+ }
+ }
+
+ cycle();
+ });
+ }
+
moveTo(x, y) {
this.currentPath = this.findPathTo(x, y, this.position.x, this.position.y);
+ console.log(this.currentPath);
+ return this.updateAgentPosition();
}
drawPath(path) {
@@ -74,8 +190,43 @@ class AgentController {
drawAgent() {
const {x, y} = this.position;
const offset = 5;
- this.ctx.clearRect((x * 100) + (offset), (y * 100) + (offset), 100 - (offset * 2), 100 - (offset * 2));
- this.roundRect((x * 100) + (offset), (y * 100) + (offset), 100 - (offset * 2), 100 - (offset * 2));
+ const rotation = this.rotation.z;
+
+ const newX = (x * 100) + (offset);
+ const newY = (y * 100) + (offset);
+ const w = 100 - (offset * 2);
+ const h = 100 - (offset * 2);
+
+ this.ctx.translate(newX + (w / 2), newY + (h / 2));
+ this.ctx.rotate(rotation * Math.PI / 180);
+
+ // Base shape
+ this.ctx.clearRect(-(w / 2), -(h / 2), w, h);
+ this.roundRect(-(w / 2), -(h / 2), w, h);
+
+ // Eyes
+ // this.roundRect(10 - (w / 2), 10 - (h / 2), w / 4, h / 4);
+ // this.roundRect(80 - (3 * w / 4), 10 - (h / 2), w / 4, h / 4);
+ this.ctx.fillStyle = 'red';
+ this.ctx.fillRect(-(w/2) + 20, (-h/2) - 10, w - 40, 10);
+
+ this.ctx.rotate(-rotation * Math.PI / 180);
+
+ // Product
+ this.ctx.font = '900 40px "Font Awesome 5 Free"';
+ this.ctx.textAlign = 'center';
+ this.ctx.fillStyle = 'white';
+ this.ctx.fillText(this.ownedProduct.icon, 0, 15);
+
+ // Amount
+ if (this.ownedProductAmount) {
+ this.ctx.font = '20px Montserrat';
+ this.ctx.textAlign = 'left';
+ this.ctx.fillStyle = 'white';
+ this.ctx.fillText(this.ownedProductAmount, 10, 35);
+ }
+
+ this.ctx.translate(-(newX + (w / 2)), -(newY + (h / 2)));
}
findPathTo(dx, dy, sx, sy) {
@@ -119,10 +270,6 @@ class AgentController {
}
}
- onDestinationReached(func) {
- this.events.ON_DESTINATION_REACHED.push(func);
- }
-
clearCanvas() {
this.ctx.clearRect(0, 0, 2000, 900);
}
diff --git a/src/modules/grid.js b/src/modules/grid.js
index f15ea2a..5b4f86d 100644
--- a/src/modules/grid.js
+++ b/src/modules/grid.js
@@ -59,7 +59,7 @@ class Grid {
const x = Math.floor(Math.random() * this.grid.length);
const y = Math.floor(Math.random() * this.grid[0].length);
const rand = this.grid[x][y];
- if (rand === GRID_FIELD_TYPE.SHELF || rand === GRID_FIELD_TYPE.PATH) {
+ if (rand === GRID_FIELD_TYPE.SHELF) {
return { x, y };
}
return this.randomPlace();
diff --git a/src/modules/product.js b/src/modules/product.js
new file mode 100644
index 0000000..0fec2f4
--- /dev/null
+++ b/src/modules/product.js
@@ -0,0 +1,29 @@
+class Product {
+ /**
+ * Stwórz produkt o określonej nazwie i ikonie
+ * @param {string} name
+ * @param {string} icon
+ * @example const product = new Product('apple', 'fa-apple')
+ */
+ constructor(name, icon) {
+ this.name = name;
+ this.icon = icon;
+ }
+
+ static get REGISTRY() {
+ return {
+ empty: new Product('', ''),
+ apple: new Product('apple', '\uf5d1'),
+ bacon: new Product('bacon', '\uf7e5'),
+ bone: new Product('bone', '\uf5d7'),
+ bread: new Product('bread', '\uf7ec'),
+ candyCane: new Product('candy cane', '\uf786'),
+ carrot: new Product('carrot', '\uf787'),
+ cheese: new Product('cheese', '\uf7ef')
+ };
+ }
+
+ static RANDOM_FROM_REGISTRY() {
+ return Product.REGISTRY[Object.keys(Product.REGISTRY).filter(p => p !== 'empty')[Math.floor(Math.random() * (Object.keys(Product.REGISTRY).length - 1))]];
+ }
+}
\ No newline at end of file
diff --git a/src/modules/products.js b/src/modules/products.js
index c5d8939..d7c632a 100644
--- a/src/modules/products.js
+++ b/src/modules/products.js
@@ -2,27 +2,27 @@ class Products {
constructor(canvas) {
this.gridProducts = [
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i'],
- ['a','', 'c', 'd', '', 'f', 'g', '', 'i']
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ ['','', '', '', '', '', '', '', ''],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ ['','', '', '', '', '', '', '', ''],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()],
+ [Product.RANDOM_FROM_REGISTRY(),'', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY(), Product.RANDOM_FROM_REGISTRY(), '', Product.RANDOM_FROM_REGISTRY()]
];
this.gridProductsAmount = []
@@ -76,7 +76,7 @@ class Products {
product(x, y, v, productsKey, productsValue) {
let fontSize = 40;
- this.ctx.font = `${fontSize}px Montserrat`;
+ this.ctx.font = `900 ${fontSize}px "Font Awesome 5 Free"`;
this.ctx.textAlign = "center";
if (v <= 10){
this.ctx.fillStyle = '#ff0000';
@@ -89,7 +89,7 @@ class Products {
}
let productsColumn = this.gridProducts[productsKey];
let prdct = productsColumn[productsValue];
- this.ctx.fillText(prdct, x+50, y+60);
+ this.ctx.fillText(prdct.icon, x+50, y+60);
}
clearCanvas() {
@@ -102,4 +102,8 @@ class Products {
count: this.gridProductsAmount[x][y]
}
}
+
+ setProductAt(product, count, x, y) {
+ // TODO: Implement this function
+ }
}
\ No newline at end of file
diff --git a/src/modules/utilities.js b/src/modules/utilities.js
index d3fa400..142e48d 100644
--- a/src/modules/utilities.js
+++ b/src/modules/utilities.js
@@ -21,3 +21,14 @@ function fromTable(header, ...rows) {
function clamp(v, lo, hi) {
return v <= lo ? lo : v >= hi ? hi : v
}
+
+/**
+ * Async timeout function
+ * @param {number} miliseconds Number of miliseconds to wait
+ * @returns
+ */
+function waitFor(miliseconds) {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => resolve(), miliseconds);
+ })
+}