diff --git a/README.md b/README.md index d9a7808..e6b0131 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,6 @@ const weight = pathfinder.getWeight( ## Example ```ts -// index.ts - const pathfinding = new Pathfinding({ loopRate: 500, }); diff --git a/dist/index.js b/dist/index.js index 5c760cd..6a99603 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1 +1 @@ -(()=>{var n={595:n=>{n.exports='/******/ (() => { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 731:\n/***/ ((module, __unused_webpack_exports, __webpack_require__) => {\n\nmodule.exports = __webpack_require__(297);\n\n\n/***/ }),\n\n/***/ 297:\n/***/ (function(module, exports) {\n\nvar __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Generated by CoffeeScript 1.8.0\n(function() {\n var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;\n\n floor = Math.floor, min = Math.min;\n\n\n /*\n Default comparison function to be used\n */\n\n defaultCmp = function(x, y) {\n if (x < y) {\n return -1;\n }\n if (x > y) {\n return 1;\n }\n return 0;\n };\n\n\n /*\n Insert item x in list a, and keep it sorted assuming a is sorted.\n \n If x is already in a, insert it to the right of the rightmost x.\n \n Optional args lo (default 0) and hi (default a.length) bound the slice\n of a to be searched.\n */\n\n insort = function(a, x, lo, hi, cmp) {\n var mid;\n if (lo == null) {\n lo = 0;\n }\n if (cmp == null) {\n cmp = defaultCmp;\n }\n if (lo < 0) {\n throw new Error(\'lo must be non-negative\');\n }\n if (hi == null) {\n hi = a.length;\n }\n while (lo < hi) {\n mid = floor((lo + hi) / 2);\n if (cmp(x, a[mid]) < 0) {\n hi = mid;\n } else {\n lo = mid + 1;\n }\n }\n return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);\n };\n\n\n /*\n Push item onto heap, maintaining the heap invariant.\n */\n\n heappush = function(array, item, cmp) {\n if (cmp == null) {\n cmp = defaultCmp;\n }\n array.push(item);\n return _siftdown(array, 0, array.length - 1, cmp);\n };\n\n\n /*\n Pop the smallest item off the heap, maintaining the heap invariant.\n */\n\n heappop = function(array, cmp) {\n var lastelt, returnitem;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n lastelt = array.pop();\n if (array.length) {\n returnitem = array[0];\n array[0] = lastelt;\n _siftup(array, 0, cmp);\n } else {\n returnitem = lastelt;\n }\n return returnitem;\n };\n\n\n /*\n Pop and return the current smallest value, and add the new item.\n \n This is more efficient than heappop() followed by heappush(), and can be\n more appropriate when using a fixed size heap. Note that the value\n returned may be larger than item! That constrains reasonable use of\n this routine unless written as part of a conditional replacement:\n if item > array[0]\n item = heapreplace(array, item)\n */\n\n heapreplace = function(array, item, cmp) {\n var returnitem;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n returnitem = array[0];\n array[0] = item;\n _siftup(array, 0, cmp);\n return returnitem;\n };\n\n\n /*\n Fast version of a heappush followed by a heappop.\n */\n\n heappushpop = function(array, item, cmp) {\n var _ref;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n if (array.length && cmp(array[0], item) < 0) {\n _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];\n _siftup(array, 0, cmp);\n }\n return item;\n };\n\n\n /*\n Transform list into a heap, in-place, in O(array.length) time.\n */\n\n heapify = function(array, cmp) {\n var i, _i, _j, _len, _ref, _ref1, _results, _results1;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n _ref1 = (function() {\n _results1 = [];\n for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }\n return _results1;\n }).apply(this).reverse();\n _results = [];\n for (_i = 0, _len = _ref1.length; _i < _len; _i++) {\n i = _ref1[_i];\n _results.push(_siftup(array, i, cmp));\n }\n return _results;\n };\n\n\n /*\n Update the position of the given item in the heap.\n This function should be called every time the item is being modified.\n */\n\n updateItem = function(array, item, cmp) {\n var pos;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n pos = array.indexOf(item);\n if (pos === -1) {\n return;\n }\n _siftdown(array, 0, pos, cmp);\n return _siftup(array, pos, cmp);\n };\n\n\n /*\n Find the n largest elements in a dataset.\n */\n\n nlargest = function(array, n, cmp) {\n var elem, result, _i, _len, _ref;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n result = array.slice(0, n);\n if (!result.length) {\n return result;\n }\n heapify(result, cmp);\n _ref = array.slice(n);\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n elem = _ref[_i];\n heappushpop(result, elem, cmp);\n }\n return result.sort(cmp).reverse();\n };\n\n\n /*\n Find the n smallest elements in a dataset.\n */\n\n nsmallest = function(array, n, cmp) {\n var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n if (n * 10 <= array.length) {\n result = array.slice(0, n).sort(cmp);\n if (!result.length) {\n return result;\n }\n los = result[result.length - 1];\n _ref = array.slice(n);\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n elem = _ref[_i];\n if (cmp(elem, los) < 0) {\n insort(result, elem, 0, null, cmp);\n result.pop();\n los = result[result.length - 1];\n }\n }\n return result;\n }\n heapify(array, cmp);\n _results = [];\n for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {\n _results.push(heappop(array, cmp));\n }\n return _results;\n };\n\n _siftdown = function(array, startpos, pos, cmp) {\n var newitem, parent, parentpos;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n newitem = array[pos];\n while (pos > startpos) {\n parentpos = (pos - 1) >> 1;\n parent = array[parentpos];\n if (cmp(newitem, parent) < 0) {\n array[pos] = parent;\n pos = parentpos;\n continue;\n }\n break;\n }\n return array[pos] = newitem;\n };\n\n _siftup = function(array, pos, cmp) {\n var childpos, endpos, newitem, rightpos, startpos;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n endpos = array.length;\n startpos = pos;\n newitem = array[pos];\n childpos = 2 * pos + 1;\n while (childpos < endpos) {\n rightpos = childpos + 1;\n if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {\n childpos = rightpos;\n }\n array[pos] = array[childpos];\n pos = childpos;\n childpos = 2 * pos + 1;\n }\n array[pos] = newitem;\n return _siftdown(array, startpos, pos, cmp);\n };\n\n Heap = (function() {\n Heap.push = heappush;\n\n Heap.pop = heappop;\n\n Heap.replace = heapreplace;\n\n Heap.pushpop = heappushpop;\n\n Heap.heapify = heapify;\n\n Heap.updateItem = updateItem;\n\n Heap.nlargest = nlargest;\n\n Heap.nsmallest = nsmallest;\n\n function Heap(cmp) {\n this.cmp = cmp != null ? cmp : defaultCmp;\n this.nodes = [];\n }\n\n Heap.prototype.push = function(x) {\n return heappush(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.pop = function() {\n return heappop(this.nodes, this.cmp);\n };\n\n Heap.prototype.peek = function() {\n return this.nodes[0];\n };\n\n Heap.prototype.contains = function(x) {\n return this.nodes.indexOf(x) !== -1;\n };\n\n Heap.prototype.replace = function(x) {\n return heapreplace(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.pushpop = function(x) {\n return heappushpop(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.heapify = function() {\n return heapify(this.nodes, this.cmp);\n };\n\n Heap.prototype.updateItem = function(x) {\n return updateItem(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.clear = function() {\n return this.nodes = [];\n };\n\n Heap.prototype.empty = function() {\n return this.nodes.length === 0;\n };\n\n Heap.prototype.size = function() {\n return this.nodes.length;\n };\n\n Heap.prototype.clone = function() {\n var heap;\n heap = new Heap();\n heap.nodes = this.nodes.slice(0);\n return heap;\n };\n\n Heap.prototype.toArray = function() {\n return this.nodes.slice(0);\n };\n\n Heap.prototype.insert = Heap.prototype.push;\n\n Heap.prototype.top = Heap.prototype.peek;\n\n Heap.prototype.front = Heap.prototype.peek;\n\n Heap.prototype.has = Heap.prototype.contains;\n\n Heap.prototype.copy = Heap.prototype.clone;\n\n return Heap;\n\n })();\n\n (function(root, factory) {\n if (true) {\n return !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),\n\t\t__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === \'function\' ?\n\t\t(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),\n\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n } else {}\n })(this, function() {\n return Heap;\n });\n\n}).call(this);\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tvar cachedModule = __webpack_module_cache__[moduleId];\n/******/ \t\tif (cachedModule !== undefined) {\n/******/ \t\t\treturn cachedModule.exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t(() => {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = (module) => {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\t() => (module[\'default\']) :\n/******/ \t\t\t\t() => (module);\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t(() => {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = (exports, definition) => {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t(() => {\n/******/ \t\t__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))\n/******/ \t})();\n/******/ \t\n/************************************************************************/\nvar __webpack_exports__ = {};\n// This entry need to be wrapped in an IIFE because it need to be in strict mode.\n(() => {\n"use strict";\n\n;// CONCATENATED MODULE: external "worker_threads"\nconst external_worker_threads_namespaceObject = require("worker_threads");\n;// CONCATENATED MODULE: ./src/events/index.ts\nclass PathfindingEvents {\n listeners = new Map();\n parent;\n constructor(parent) {\n this.parent = parent;\n this.parent.on(\'message\', (data) => {\n this.listeners.get(data.event)?.(data.payload);\n });\n }\n on(event, callback) {\n this.listeners.set(event, callback);\n }\n send(event, payload) {\n this.parent.postMessage({ event, payload });\n }\n}\n\n;// CONCATENATED MODULE: ./src/events/types.ts\nvar PathfindingEvent;\n(function (PathfindingEvent) {\n PathfindingEvent["CreateTask"] = "CreateTask";\n PathfindingEvent["CompleteTask"] = "CompleteTask";\n PathfindingEvent["CancelTask"] = "CancelTask";\n PathfindingEvent["AddLayer"] = "AddLayer";\n PathfindingEvent["RemoveLayer"] = "RemoveLayer";\n PathfindingEvent["SetWalkable"] = "SetWalkable";\n PathfindingEvent["SetWeight"] = "SetWeight";\n})(PathfindingEvent || (PathfindingEvent = {}));\n\n;// CONCATENATED MODULE: ./src/process/const.ts\nconst PATHFINDING_PROCESS_LOOP_RATE = 200;\nconst PATHFINDING_PROCESS_NEXT_DIRECTIINS_STRAIGHT = {\n R: { x: 1, y: 0 }, // →\n L: { x: -1, y: 0 }, // ←\n D: { x: 0, y: 1 }, // ↓\n U: { x: 0, y: -1 }, // ↑\n};\nconst PATHFINDING_PROCESS_NEXT_DIRECTIINS_DIAGONAL = {\n RD: { x: 1, y: 1 }, // ↘\n RU: { x: 1, y: -1 }, // ↗\n LU: { x: -1, y: -1 }, // ↖\n LD: { x: -1, y: 1 }, // ↙\n};\n\n;// CONCATENATED MODULE: ./src/process/index.ts\n\nclass PathfindingProcess {\n layers = {};\n weights = [];\n taskQueue = [];\n timer;\n constructor(loopRate = PATHFINDING_PROCESS_LOOP_RATE) {\n this.timer = setInterval(() => {\n try {\n this.next();\n }\n catch (error) {\n console.error(\'Pathfinding process error:\', error);\n }\n }, loopRate);\n }\n destroy() {\n clearTimeout(this.timer);\n }\n createTask(task) {\n this.taskQueue.push(task);\n }\n cancelTask(idTask) {\n const taskIndex = this.taskQueue.findIndex((task) => task.id === idTask);\n if (taskIndex !== -1) {\n this.taskQueue.splice(taskIndex, 1);\n }\n }\n addLayer(layer, grid) {\n this.layers[layer] = grid;\n }\n removeLayer(layer) {\n if (this.layers[layer]) {\n delete this.layers[layer];\n }\n }\n setWeight(position, weight) {\n if (!this.weights[position.y]) {\n this.weights[position.y] = [];\n }\n this.weights[position.y][position.x] = weight;\n }\n resetWeight(position) {\n if (this.weights[position.y]) {\n delete this.weights[position.y][position.x];\n }\n }\n setWalkable(layer, position, state) {\n const grid = this.layers[layer];\n if (!grid) {\n return;\n }\n grid[position.y][position.x] = state;\n }\n next() {\n const task = this.taskQueue[0];\n if (!task) {\n return;\n }\n const currentNode = task.takeLastNode();\n if (currentNode) {\n if (currentNode.position.x === task.to.x &&\n currentNode.position.y === task.to.y) {\n this.taskQueue.shift();\n task.complete(currentNode.compute());\n }\n else {\n this.getNextDirections(task, currentNode).forEach((offset) => {\n const position = {\n x: currentNode.position.x + offset.x,\n y: currentNode.position.y + offset.y,\n };\n const nextWeight = task.getNextWeight(currentNode, offset, this.weights);\n const nextNode = task.pickNode(position);\n if (nextNode) {\n if (nextWeight < nextNode.getWeight()) {\n task.useNode(currentNode, nextNode, nextWeight);\n }\n }\n else {\n task.addNode(currentNode, position, nextWeight);\n }\n });\n }\n }\n else {\n this.taskQueue.shift();\n task.complete({\n path: null,\n weight: Infinity,\n });\n }\n this.next();\n }\n getNextDirections(task, node) {\n const straightClear = {};\n const allowedDirs = [];\n Object.entries(PATHFINDING_PROCESS_NEXT_DIRECTIINS_STRAIGHT).forEach(([key, direction]) => {\n if (this.isWalkable(node, task.layer, direction)) {\n straightClear[key] = true;\n allowedDirs.push(direction);\n }\n });\n Object.entries(PATHFINDING_PROCESS_NEXT_DIRECTIINS_DIAGONAL).forEach(([key, direction]) => {\n const clear = straightClear[key[0]] && straightClear[key[1]];\n if (clear && this.isWalkable(node, task.layer, direction)) {\n allowedDirs.push(direction);\n }\n });\n return allowedDirs;\n }\n isWalkable(node, layer, direction) {\n const position = {\n x: node.position.x + direction.x,\n y: node.position.y + direction.y,\n };\n return Boolean(this.layers[layer]?.[position.y]?.[position.x]);\n }\n}\n\n// EXTERNAL MODULE: ./node_modules/heap/index.js\nvar heap = __webpack_require__(731);\nvar heap_default = /*#__PURE__*/__webpack_require__.n(heap);\n;// CONCATENATED MODULE: ./src/node/index.ts\nclass PathfindingNode {\n position;\n distance;\n parent = null;\n weight;\n constructor({ position, weight = 1.0, distance, }) {\n this.position = { ...position };\n this.distance = distance;\n this.weight = weight;\n }\n getBetterGuessDistance() {\n return this.weight + this.distance;\n }\n getWeight() {\n return this.weight;\n }\n setWeight(weight) {\n this.weight = weight;\n }\n getParent() {\n return this.parent;\n }\n setParent(parent) {\n this.parent = parent;\n }\n compute() {\n const path = [{ ...this.position }];\n const weight = this.parent ? this.parent.getWeight() : 0;\n let parent = this.getParent();\n while (parent) {\n path.push({ ...parent.position });\n parent = parent.getParent();\n }\n path.reverse();\n return { path, weight };\n }\n getNextWeight(shift, weights) {\n const nextPosition = {\n x: this.position.x + shift.x,\n y: this.position.y + shift.y,\n };\n const weight = weights[nextPosition.y]?.[nextPosition.x] ?? 1.0;\n if (Math.abs(shift.x) + Math.abs(shift.y) !== 1) {\n return (weight * Math.SQRT2 +\n (weights[this.position.y]?.[nextPosition.x] ?? 0.0) +\n (weights[nextPosition.y]?.[this.position.x] ?? 0.0));\n }\n return weight;\n }\n}\n\n;// CONCATENATED MODULE: ./src/task/index.ts\n\n\nclass PathfindingTask {\n from;\n to;\n id;\n layer;\n tree = [];\n nodes;\n complete;\n constructor({ idTask, from, to, layer }, onComplete) {\n this.id = idTask;\n this.from = { ...from };\n this.to = { ...to };\n this.layer = layer;\n this.complete = onComplete;\n this.nodes = new (heap_default())((nodeA, nodeB) => nodeA.getBetterGuessDistance() - nodeB.getBetterGuessDistance());\n const node = new PathfindingNode({\n position: this.from,\n distance: this.getDistanceFrom(this.from),\n });\n this.pushNode(node);\n }\n getDistanceFrom(position) {\n return Math.sqrt((position.x - this.to.x) ** 2 + (position.y - this.to.y) ** 2);\n }\n addNode(parent, position, weight) {\n const node = new PathfindingNode({\n position,\n distance: this.getDistanceFrom(position),\n weight,\n });\n node.setParent(parent);\n this.pushNode(node);\n }\n pushNode(node) {\n this.nodes.push(node);\n if (!this.tree[node.position.y]) {\n this.tree[node.position.y] = [];\n }\n this.tree[node.position.y][node.position.x] = node;\n }\n pickNode(position) {\n return this.tree[position.y]?.[position.x];\n }\n takeLastNode() {\n return this.nodes.pop() ?? null;\n }\n useNode(current, next, weight) {\n next.setWeight(weight);\n next.setParent(current);\n this.nodes.updateItem(next);\n }\n getNextWeight(currentNode, shift, weights) {\n return currentNode.getWeight() + currentNode.getNextWeight(shift, weights);\n }\n}\n\n;// CONCATENATED MODULE: ./src/worker.ts\n\n\n\n\n\nif (!external_worker_threads_namespaceObject.parentPort) {\n throw Error(\'Undefined parent port of pathfinding worker\');\n}\nconst events = new PathfindingEvents(external_worker_threads_namespaceObject.parentPort);\nconst process = new PathfindingProcess(external_worker_threads_namespaceObject.workerData.loopRate);\nevents.on(PathfindingEvent.CreateTask, (payload) => {\n const task = new PathfindingTask(payload, (result) => {\n events.send(PathfindingEvent.CompleteTask, {\n idTask: payload.idTask,\n result,\n });\n });\n process.createTask(task);\n});\nevents.on(PathfindingEvent.CancelTask, (payload) => {\n process.cancelTask(payload.idTask);\n});\nevents.on(PathfindingEvent.SetWeight, (payload) => {\n if (payload.value === null) {\n process.resetWeight(payload.position);\n }\n else {\n process.setWeight(payload.position, payload.value);\n }\n});\nevents.on(PathfindingEvent.SetWalkable, (payload) => {\n process.setWalkable(payload.layer, payload.position, payload.state);\n});\nevents.on(PathfindingEvent.AddLayer, (payload) => {\n process.addLayer(payload.layer, payload.grid);\n});\nevents.on(PathfindingEvent.RemoveLayer, (payload) => {\n process.removeLayer(payload.layer);\n});\n\n})();\n\nmodule.exports = __webpack_exports__;\n/******/ })()\n;'}},e={};function t(r){var s=e[r];if(void 0!==s)return s.exports;var a=e[r]={exports:{}};return n[r](a,a.exports,t),a.exports}t.n=n=>{var e=n&&n.__esModule?()=>n.default:()=>n;return t.d(e,{a:e}),e},t.d=(n,e)=>{for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.o=(n,e)=>Object.prototype.hasOwnProperty.call(n,e),t.r=n=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})};var r={};(()=>{"use strict";t.r(r),t.d(r,{Pathfinding:()=>u});const n=require("worker_threads");class e{listeners=new Map;parent;constructor(n){this.parent=n,this.parent.on("message",(n=>{this.listeners.get(n.event)?.(n.payload)}))}on(n,e){this.listeners.set(n,e)}send(n,e){this.parent.postMessage({event:n,payload:e})}}var s;!function(n){n.CreateTask="CreateTask",n.CompleteTask="CompleteTask",n.CancelTask="CancelTask",n.AddLayer="AddLayer",n.RemoveLayer="RemoveLayer",n.SetWalkable="SetWalkable",n.SetWeight="SetWeight"}(s||(s={}));const a=require("fs");var i=t.n(a);const o=require("path");var p=t.n(o),l=t(595),h=t.n(l);class d{workerPath;constructor(n){this.workerPath=p().resolve(__dirname,n)}workerExists(){return i().existsSync(this.workerPath)}createWorker(){return i().writeFileSync(this.workerPath,h())}}class u{worker;weights=[];layers={};lastTaskId=0;resultCallbacks=new Map;events;constructor({workerPath:t="./pathfinding.worker.js",loopRate:r}={}){const a=new d(t);a.workerExists()||a.createWorker(),this.worker=new n.Worker(a.workerPath,{workerData:{loopRate:r}}),this.events=new e(this.worker),this.events.on(s.CompleteTask,(({idTask:n,result:e})=>{const t=this.resultCallbacks.get(n);t&&(t(e),this.resultCallbacks.delete(n))}))}destroy(){return this.worker.terminate()}addLayer(n,e){this.layers[n]=e,this.events.send(s.AddLayer,{layer:n,grid:e})}getLayer(n){return this.layers[n]??null}removeLayer(n){if(!this.layers[n])throw Error(`Layer of pathfinding grid '${n}' is not found`);delete this.layers[n],this.events.send(s.RemoveLayer,{layer:n})}setWalkable(n,e,t){if(!this.layers[n])throw Error(`Layer of pathfinding grid '${n}' is not found`);this.isWalkable(n,e)!==t&&(this.layers[n][e.y]||(this.layers[n][e.y]=[]),this.layers[n][e.y][e.x]=t,this.events.send(s.SetWalkable,{position:e,layer:n,state:t}))}isWalkable(n,e){if(!this.layers[n])throw Error(`Layer of pathfinding grid '${n}' is not found`);return Boolean(this.layers[n][e.y]?.[e.x])}setWeight(n,e){this.getWeight(n)!==e&&(this.weights[n.y]||(this.weights[n.y]=[]),this.weights[n.y][n.x]=e,this.events.send(s.SetWeight,{position:n,value:e}))}resetWeight(n){void 0!==this.weights[n.y]?.[n.x]&&(delete this.weights[n.y][n.x],this.events.send(s.SetWeight,{position:n,value:null}))}getWeight(n){return this.weights[n.y]?.[n.x]??1}createTask(n,e){if(!this.layers[n.layer])throw Error(`Layer of pathfinding grid '${n.layer}' is not found`);const t=++this.lastTaskId;return this.events.send(s.CreateTask,{...n,idTask:t}),this.resultCallbacks.set(t,e),t}cancelTask(n){if(!this.resultCallbacks.has(n))throw Error(`Pathfinding task with id '${n}' is not found`);this.events.send(s.CancelTask,{idTask:n}),this.resultCallbacks.delete(n)}}})(),module.exports=r})(); \ No newline at end of file +(()=>{var n={595:n=>{n.exports='/******/ (() => { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 731:\n/***/ ((module, __unused_webpack_exports, __webpack_require__) => {\n\nmodule.exports = __webpack_require__(297);\n\n\n/***/ }),\n\n/***/ 297:\n/***/ (function(module, exports) {\n\nvar __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Generated by CoffeeScript 1.8.0\n(function() {\n var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;\n\n floor = Math.floor, min = Math.min;\n\n\n /*\n Default comparison function to be used\n */\n\n defaultCmp = function(x, y) {\n if (x < y) {\n return -1;\n }\n if (x > y) {\n return 1;\n }\n return 0;\n };\n\n\n /*\n Insert item x in list a, and keep it sorted assuming a is sorted.\n \n If x is already in a, insert it to the right of the rightmost x.\n \n Optional args lo (default 0) and hi (default a.length) bound the slice\n of a to be searched.\n */\n\n insort = function(a, x, lo, hi, cmp) {\n var mid;\n if (lo == null) {\n lo = 0;\n }\n if (cmp == null) {\n cmp = defaultCmp;\n }\n if (lo < 0) {\n throw new Error(\'lo must be non-negative\');\n }\n if (hi == null) {\n hi = a.length;\n }\n while (lo < hi) {\n mid = floor((lo + hi) / 2);\n if (cmp(x, a[mid]) < 0) {\n hi = mid;\n } else {\n lo = mid + 1;\n }\n }\n return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);\n };\n\n\n /*\n Push item onto heap, maintaining the heap invariant.\n */\n\n heappush = function(array, item, cmp) {\n if (cmp == null) {\n cmp = defaultCmp;\n }\n array.push(item);\n return _siftdown(array, 0, array.length - 1, cmp);\n };\n\n\n /*\n Pop the smallest item off the heap, maintaining the heap invariant.\n */\n\n heappop = function(array, cmp) {\n var lastelt, returnitem;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n lastelt = array.pop();\n if (array.length) {\n returnitem = array[0];\n array[0] = lastelt;\n _siftup(array, 0, cmp);\n } else {\n returnitem = lastelt;\n }\n return returnitem;\n };\n\n\n /*\n Pop and return the current smallest value, and add the new item.\n \n This is more efficient than heappop() followed by heappush(), and can be\n more appropriate when using a fixed size heap. Note that the value\n returned may be larger than item! That constrains reasonable use of\n this routine unless written as part of a conditional replacement:\n if item > array[0]\n item = heapreplace(array, item)\n */\n\n heapreplace = function(array, item, cmp) {\n var returnitem;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n returnitem = array[0];\n array[0] = item;\n _siftup(array, 0, cmp);\n return returnitem;\n };\n\n\n /*\n Fast version of a heappush followed by a heappop.\n */\n\n heappushpop = function(array, item, cmp) {\n var _ref;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n if (array.length && cmp(array[0], item) < 0) {\n _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];\n _siftup(array, 0, cmp);\n }\n return item;\n };\n\n\n /*\n Transform list into a heap, in-place, in O(array.length) time.\n */\n\n heapify = function(array, cmp) {\n var i, _i, _j, _len, _ref, _ref1, _results, _results1;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n _ref1 = (function() {\n _results1 = [];\n for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }\n return _results1;\n }).apply(this).reverse();\n _results = [];\n for (_i = 0, _len = _ref1.length; _i < _len; _i++) {\n i = _ref1[_i];\n _results.push(_siftup(array, i, cmp));\n }\n return _results;\n };\n\n\n /*\n Update the position of the given item in the heap.\n This function should be called every time the item is being modified.\n */\n\n updateItem = function(array, item, cmp) {\n var pos;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n pos = array.indexOf(item);\n if (pos === -1) {\n return;\n }\n _siftdown(array, 0, pos, cmp);\n return _siftup(array, pos, cmp);\n };\n\n\n /*\n Find the n largest elements in a dataset.\n */\n\n nlargest = function(array, n, cmp) {\n var elem, result, _i, _len, _ref;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n result = array.slice(0, n);\n if (!result.length) {\n return result;\n }\n heapify(result, cmp);\n _ref = array.slice(n);\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n elem = _ref[_i];\n heappushpop(result, elem, cmp);\n }\n return result.sort(cmp).reverse();\n };\n\n\n /*\n Find the n smallest elements in a dataset.\n */\n\n nsmallest = function(array, n, cmp) {\n var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n if (n * 10 <= array.length) {\n result = array.slice(0, n).sort(cmp);\n if (!result.length) {\n return result;\n }\n los = result[result.length - 1];\n _ref = array.slice(n);\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n elem = _ref[_i];\n if (cmp(elem, los) < 0) {\n insort(result, elem, 0, null, cmp);\n result.pop();\n los = result[result.length - 1];\n }\n }\n return result;\n }\n heapify(array, cmp);\n _results = [];\n for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {\n _results.push(heappop(array, cmp));\n }\n return _results;\n };\n\n _siftdown = function(array, startpos, pos, cmp) {\n var newitem, parent, parentpos;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n newitem = array[pos];\n while (pos > startpos) {\n parentpos = (pos - 1) >> 1;\n parent = array[parentpos];\n if (cmp(newitem, parent) < 0) {\n array[pos] = parent;\n pos = parentpos;\n continue;\n }\n break;\n }\n return array[pos] = newitem;\n };\n\n _siftup = function(array, pos, cmp) {\n var childpos, endpos, newitem, rightpos, startpos;\n if (cmp == null) {\n cmp = defaultCmp;\n }\n endpos = array.length;\n startpos = pos;\n newitem = array[pos];\n childpos = 2 * pos + 1;\n while (childpos < endpos) {\n rightpos = childpos + 1;\n if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {\n childpos = rightpos;\n }\n array[pos] = array[childpos];\n pos = childpos;\n childpos = 2 * pos + 1;\n }\n array[pos] = newitem;\n return _siftdown(array, startpos, pos, cmp);\n };\n\n Heap = (function() {\n Heap.push = heappush;\n\n Heap.pop = heappop;\n\n Heap.replace = heapreplace;\n\n Heap.pushpop = heappushpop;\n\n Heap.heapify = heapify;\n\n Heap.updateItem = updateItem;\n\n Heap.nlargest = nlargest;\n\n Heap.nsmallest = nsmallest;\n\n function Heap(cmp) {\n this.cmp = cmp != null ? cmp : defaultCmp;\n this.nodes = [];\n }\n\n Heap.prototype.push = function(x) {\n return heappush(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.pop = function() {\n return heappop(this.nodes, this.cmp);\n };\n\n Heap.prototype.peek = function() {\n return this.nodes[0];\n };\n\n Heap.prototype.contains = function(x) {\n return this.nodes.indexOf(x) !== -1;\n };\n\n Heap.prototype.replace = function(x) {\n return heapreplace(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.pushpop = function(x) {\n return heappushpop(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.heapify = function() {\n return heapify(this.nodes, this.cmp);\n };\n\n Heap.prototype.updateItem = function(x) {\n return updateItem(this.nodes, x, this.cmp);\n };\n\n Heap.prototype.clear = function() {\n return this.nodes = [];\n };\n\n Heap.prototype.empty = function() {\n return this.nodes.length === 0;\n };\n\n Heap.prototype.size = function() {\n return this.nodes.length;\n };\n\n Heap.prototype.clone = function() {\n var heap;\n heap = new Heap();\n heap.nodes = this.nodes.slice(0);\n return heap;\n };\n\n Heap.prototype.toArray = function() {\n return this.nodes.slice(0);\n };\n\n Heap.prototype.insert = Heap.prototype.push;\n\n Heap.prototype.top = Heap.prototype.peek;\n\n Heap.prototype.front = Heap.prototype.peek;\n\n Heap.prototype.has = Heap.prototype.contains;\n\n Heap.prototype.copy = Heap.prototype.clone;\n\n return Heap;\n\n })();\n\n (function(root, factory) {\n if (true) {\n return !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),\n\t\t__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === \'function\' ?\n\t\t(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),\n\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n } else {}\n })(this, function() {\n return Heap;\n });\n\n}).call(this);\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tvar cachedModule = __webpack_module_cache__[moduleId];\n/******/ \t\tif (cachedModule !== undefined) {\n/******/ \t\t\treturn cachedModule.exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t(() => {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = (module) => {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\t() => (module[\'default\']) :\n/******/ \t\t\t\t() => (module);\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t(() => {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = (exports, definition) => {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t})();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t(() => {\n/******/ \t\t__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))\n/******/ \t})();\n/******/ \t\n/************************************************************************/\nvar __webpack_exports__ = {};\n// This entry need to be wrapped in an IIFE because it need to be in strict mode.\n(() => {\n"use strict";\n\n;// CONCATENATED MODULE: external "worker_threads"\nconst external_worker_threads_namespaceObject = require("worker_threads");\n;// CONCATENATED MODULE: ./src/events/index.ts\nclass PathfindingEvents {\n listeners = new Map();\n parent;\n constructor(parent) {\n this.parent = parent;\n this.parent.on(\'message\', (data) => {\n this.listeners.get(data.event)?.(data.payload);\n });\n }\n on(event, callback) {\n this.listeners.set(event, callback);\n }\n send(event, payload) {\n this.parent.postMessage({ event, payload });\n }\n}\n\n;// CONCATENATED MODULE: ./src/events/types.ts\nvar PathfindingEvent;\n(function (PathfindingEvent) {\n PathfindingEvent["CreateTask"] = "CreateTask";\n PathfindingEvent["CompleteTask"] = "CompleteTask";\n PathfindingEvent["CancelTask"] = "CancelTask";\n PathfindingEvent["AddLayer"] = "AddLayer";\n PathfindingEvent["RemoveLayer"] = "RemoveLayer";\n PathfindingEvent["SetWalkable"] = "SetWalkable";\n PathfindingEvent["SetWeight"] = "SetWeight";\n})(PathfindingEvent || (PathfindingEvent = {}));\n\n;// CONCATENATED MODULE: ./src/process/const.ts\nconst PATHFINDING_PROCESS_LOOP_RATE = 200;\nconst PATHFINDING_PROCESS_NEXT_DIRECTIINS_STRAIGHT = {\n R: { x: 1, y: 0 }, // →\n L: { x: -1, y: 0 }, // ←\n D: { x: 0, y: 1 }, // ↓\n U: { x: 0, y: -1 }, // ↑\n};\nconst PATHFINDING_PROCESS_NEXT_DIRECTIINS_DIAGONAL = {\n RD: { x: 1, y: 1 }, // ↘\n RU: { x: 1, y: -1 }, // ↗\n LU: { x: -1, y: -1 }, // ↖\n LD: { x: -1, y: 1 }, // ↙\n};\n\n;// CONCATENATED MODULE: ./src/process/index.ts\n\nclass PathfindingProcess {\n layers = {};\n weights = [];\n taskQueue = [];\n timer;\n constructor(loopRate = PATHFINDING_PROCESS_LOOP_RATE) {\n this.timer = setInterval(() => {\n try {\n this.next();\n }\n catch (error) {\n console.error(\'Pathfinding process error:\', error);\n }\n }, loopRate);\n }\n destroy() {\n clearTimeout(this.timer);\n }\n createTask(task) {\n this.taskQueue.push(task);\n }\n cancelTask(idTask) {\n const taskIndex = this.taskQueue.findIndex((task) => task.id === idTask);\n if (taskIndex !== -1) {\n this.taskQueue.splice(taskIndex, 1);\n }\n }\n addLayer(layer, grid) {\n this.layers[layer] = grid;\n }\n removeLayer(layer) {\n if (this.layers[layer]) {\n delete this.layers[layer];\n }\n }\n setWeight(position, weight) {\n if (!this.weights[position.y]) {\n this.weights[position.y] = [];\n }\n this.weights[position.y][position.x] = weight;\n }\n resetWeight(position) {\n if (this.weights[position.y]) {\n delete this.weights[position.y][position.x];\n }\n }\n setWalkable(layer, position, state) {\n const grid = this.layers[layer];\n if (!grid) {\n return;\n }\n grid[position.y][position.x] = state;\n }\n next() {\n const task = this.taskQueue[0];\n if (!task) {\n return;\n }\n const currentNode = task.takeLastNode();\n if (currentNode) {\n if (currentNode.position.x === task.to.x &&\n currentNode.position.y === task.to.y) {\n this.taskQueue.shift();\n task.complete(currentNode.compute());\n }\n else {\n this.getNextDirections(task, currentNode).forEach((offset) => {\n const position = {\n x: currentNode.position.x + offset.x,\n y: currentNode.position.y + offset.y,\n };\n const nextWeight = task.getNextWeight(currentNode, offset, this.weights);\n const nextNode = task.pickNode(position);\n if (nextNode) {\n if (nextWeight < nextNode.getWeight()) {\n task.useNode(currentNode, nextNode, nextWeight);\n }\n }\n else {\n task.addNode(currentNode, position, nextWeight);\n }\n });\n }\n }\n else {\n this.taskQueue.shift();\n task.complete({\n path: null,\n weight: Infinity,\n });\n }\n this.next();\n }\n getNextDirections(task, node) {\n const straightClear = {};\n const allowedDirs = [];\n Object.entries(PATHFINDING_PROCESS_NEXT_DIRECTIINS_STRAIGHT).forEach(([key, direction]) => {\n if (this.isWalkable(node, task.layer, direction)) {\n straightClear[key] = true;\n allowedDirs.push(direction);\n }\n });\n Object.entries(PATHFINDING_PROCESS_NEXT_DIRECTIINS_DIAGONAL).forEach(([key, direction]) => {\n const clear = straightClear[key[0]] && straightClear[key[1]];\n if (clear && this.isWalkable(node, task.layer, direction)) {\n allowedDirs.push(direction);\n }\n });\n return allowedDirs;\n }\n isWalkable(node, layer, direction) {\n const position = {\n x: node.position.x + direction.x,\n y: node.position.y + direction.y,\n };\n return Boolean(this.layers[layer]?.[position.y]?.[position.x]);\n }\n}\n\n// EXTERNAL MODULE: ./node_modules/heap/index.js\nvar heap = __webpack_require__(731);\nvar heap_default = /*#__PURE__*/__webpack_require__.n(heap);\n;// CONCATENATED MODULE: ./src/node/index.ts\nclass PathfindingNode {\n position;\n distance;\n parent = null;\n weight;\n constructor({ position, weight = 1.0, distance, }) {\n this.position = { ...position };\n this.distance = distance;\n this.weight = weight;\n }\n getBetterGuessDistance() {\n return this.weight + this.distance;\n }\n getWeight() {\n return this.weight;\n }\n setWeight(weight) {\n this.weight = weight;\n }\n getParent() {\n return this.parent;\n }\n setParent(parent) {\n this.parent = parent;\n }\n compute() {\n const path = [{ ...this.position }];\n const weight = this.parent ? this.parent.getWeight() : 0;\n let parent = this.getParent();\n while (parent) {\n path.push({ ...parent.position });\n parent = parent.getParent();\n }\n path.reverse();\n return { path, weight };\n }\n getNextWeight(shift, weights) {\n const nextPosition = {\n x: this.position.x + shift.x,\n y: this.position.y + shift.y,\n };\n const weight = weights[nextPosition.y]?.[nextPosition.x] ?? 1.0;\n if (Math.abs(shift.x) + Math.abs(shift.y) !== 1) {\n return (weight * Math.SQRT2 +\n (weights[this.position.y]?.[nextPosition.x] ?? 0.0) +\n (weights[nextPosition.y]?.[this.position.x] ?? 0.0));\n }\n return weight;\n }\n}\n\n;// CONCATENATED MODULE: ./src/task/index.ts\n\n\nclass PathfindingTask {\n from;\n to;\n id;\n layer;\n tree = [];\n nodes;\n complete;\n constructor({ idTask, from, to, layer }, onComplete) {\n this.id = idTask;\n this.from = { ...from };\n this.to = { ...to };\n this.layer = layer;\n this.complete = onComplete;\n this.nodes = new (heap_default())((nodeA, nodeB) => nodeA.getBetterGuessDistance() - nodeB.getBetterGuessDistance());\n const node = new PathfindingNode({\n position: this.from,\n distance: this.getDistanceFrom(this.from),\n });\n this.pushNode(node);\n }\n getDistanceFrom(position) {\n return Math.sqrt((position.x - this.to.x) ** 2 + (position.y - this.to.y) ** 2);\n }\n addNode(parent, position, weight) {\n const node = new PathfindingNode({\n position,\n distance: this.getDistanceFrom(position),\n weight,\n });\n node.setParent(parent);\n this.pushNode(node);\n }\n pushNode(node) {\n this.nodes.push(node);\n if (!this.tree[node.position.y]) {\n this.tree[node.position.y] = [];\n }\n this.tree[node.position.y][node.position.x] = node;\n }\n pickNode(position) {\n return this.tree[position.y]?.[position.x];\n }\n takeLastNode() {\n return this.nodes.pop() ?? null;\n }\n useNode(current, next, weight) {\n next.setWeight(weight);\n next.setParent(current);\n this.nodes.updateItem(next);\n }\n getNextWeight(currentNode, shift, weights) {\n return currentNode.getWeight() + currentNode.getNextWeight(shift, weights);\n }\n}\n\n;// CONCATENATED MODULE: ./src/worker.ts\n\n\n\n\n\nif (!external_worker_threads_namespaceObject.parentPort) {\n throw Error(\'Undefined parent port of pathfinding worker\');\n}\nconst events = new PathfindingEvents(external_worker_threads_namespaceObject.parentPort);\nconst process = new PathfindingProcess(external_worker_threads_namespaceObject.workerData.loopRate);\nevents.on(PathfindingEvent.CreateTask, (payload) => {\n const task = new PathfindingTask(payload, (result) => {\n events.send(PathfindingEvent.CompleteTask, {\n idTask: payload.idTask,\n result,\n });\n });\n process.createTask(task);\n});\nevents.on(PathfindingEvent.CancelTask, (payload) => {\n process.cancelTask(payload.idTask);\n});\nevents.on(PathfindingEvent.SetWeight, (payload) => {\n if (payload.value === null) {\n process.resetWeight(payload.position);\n }\n else {\n process.setWeight(payload.position, payload.value);\n }\n});\nevents.on(PathfindingEvent.SetWalkable, (payload) => {\n process.setWalkable(payload.layer, payload.position, payload.state);\n});\nevents.on(PathfindingEvent.AddLayer, (payload) => {\n process.addLayer(payload.layer, payload.grid);\n});\nevents.on(PathfindingEvent.RemoveLayer, (payload) => {\n process.removeLayer(payload.layer);\n});\n\n})();\n\nmodule.exports = __webpack_exports__;\n/******/ })()\n;'}},e={};function t(r){var s=e[r];if(void 0!==s)return s.exports;var a=e[r]={exports:{}};return n[r](a,a.exports,t),a.exports}t.n=n=>{var e=n&&n.__esModule?()=>n.default:()=>n;return t.d(e,{a:e}),e},t.d=(n,e)=>{for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},t.o=(n,e)=>Object.prototype.hasOwnProperty.call(n,e),t.r=n=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})};var r={};(()=>{"use strict";t.r(r),t.d(r,{Pathfinding:()=>u});const n=require("worker_threads");class e{listeners=new Map;parent;constructor(n){this.parent=n,this.parent.on("message",(n=>{this.listeners.get(n.event)?.(n.payload)}))}on(n,e){this.listeners.set(n,e)}send(n,e){this.parent.postMessage({event:n,payload:e})}}var s;!function(n){n.CreateTask="CreateTask",n.CompleteTask="CompleteTask",n.CancelTask="CancelTask",n.AddLayer="AddLayer",n.RemoveLayer="RemoveLayer",n.SetWalkable="SetWalkable",n.SetWeight="SetWeight"}(s||(s={}));const a=require("fs");var i=t.n(a);const o=require("path");var p=t.n(o),l=t(595),h=t.n(l);class d{workerPath;constructor(n){this.workerPath=p().resolve(__dirname,n)}workerExists(){return i().existsSync(this.workerPath)}createWorker(){i().writeFileSync(this.workerPath,h())}}class u{worker;weights=[];layers={};lastTaskId=0;resultCallbacks=new Map;events;constructor({workerPath:t="./pathfinding.worker.js",loopRate:r}={}){const a=new d(t);a.workerExists()||a.createWorker(),this.worker=new n.Worker(a.workerPath,{workerData:{loopRate:r}}),this.events=new e(this.worker),this.events.on(s.CompleteTask,(({idTask:n,result:e})=>{const t=this.resultCallbacks.get(n);t&&(t(e),this.resultCallbacks.delete(n))}))}destroy(){return this.worker.terminate()}addLayer(n,e){this.layers[n]=e,this.events.send(s.AddLayer,{layer:n,grid:e})}getLayer(n){return this.layers[n]??null}removeLayer(n){if(!this.layers[n])throw Error(`Layer of pathfinding grid '${n}' is not found`);delete this.layers[n],this.events.send(s.RemoveLayer,{layer:n})}setWalkable(n,e,t){if(!this.layers[n])throw Error(`Layer of pathfinding grid '${n}' is not found`);this.isWalkable(n,e)!==t&&(this.layers[n][e.y]||(this.layers[n][e.y]=[]),this.layers[n][e.y][e.x]=t,this.events.send(s.SetWalkable,{position:e,layer:n,state:t}))}isWalkable(n,e){if(!this.layers[n])throw Error(`Layer of pathfinding grid '${n}' is not found`);return Boolean(this.layers[n][e.y]?.[e.x])}setWeight(n,e){this.getWeight(n)!==e&&(this.weights[n.y]||(this.weights[n.y]=[]),this.weights[n.y][n.x]=e,this.events.send(s.SetWeight,{position:n,value:e}))}resetWeight(n){void 0!==this.weights[n.y]?.[n.x]&&(delete this.weights[n.y][n.x],this.events.send(s.SetWeight,{position:n,value:null}))}getWeight(n){return this.weights[n.y]?.[n.x]??1}createTask(n,e){if(!this.layers[n.layer])throw Error(`Layer of pathfinding grid '${n.layer}' is not found`);const t=++this.lastTaskId;return this.events.send(s.CreateTask,{...n,idTask:t}),this.resultCallbacks.set(t,e),t}cancelTask(n){if(!this.resultCallbacks.has(n))throw Error(`Pathfinding task with id '${n}' is not found`);this.events.send(s.CancelTask,{idTask:n}),this.resultCallbacks.delete(n)}}})(),module.exports=r})(); \ No newline at end of file diff --git a/package.json b/package.json index c99daef..9cea016 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pathfinding-worker", "description": "Fast node.js pathfinding on workers for grid-based games", - "version": "1.0.0", + "version": "1.0.1", "keywords": [ "astar", "dijkstra", @@ -22,9 +22,10 @@ }, "main": "./dist/index.js", "scripts": { - "build": "yarn build:worker && yarn build:entrypoint", + "build": "yarn build:worker && yarn build:entrypoint && yarn clear", "build:worker": "webpack --config ./webpack/worker.config.js --mode production", "build:entrypoint": "webpack --config ./webpack/entrypoint.config.js --mode production", + "clear": "rm -R ./.tmp", "test": "jest", "lint": "eslint \"./src/**/*.ts\" --fix" }, diff --git a/src/runtime/index.ts b/src/runtime/index.ts index a1541f4..e6602db 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -2,7 +2,7 @@ import fs from 'fs'; import path from 'path'; // Import pre-builded inline worker as string -import INLINE_WORKER from '../../.temp/worker.inline.js'; +import INLINE_WORKER from '../../.tmp/worker.inline.js'; export class PathfindingRuntime { public readonly workerPath: string; diff --git a/webpack/const.js b/webpack/const.js new file mode 100644 index 0000000..a21caeb --- /dev/null +++ b/webpack/const.js @@ -0,0 +1,3 @@ +module.exports = { + WEBPACK_INLINE_WORKER_TEMP_FILE_NAME: 'worker.inline.js', +}; diff --git a/webpack/worker.config.js b/webpack/worker.config.js index ff75369..fddee60 100644 --- a/webpack/worker.config.js +++ b/webpack/worker.config.js @@ -1,5 +1,6 @@ const path = require('path'); +const { WEBPACK_INLINE_WORKER_TEMP_FILE_NAME } = require('./const'); const InjectWorkerPlugin = require('./worker.plugin'); const root = path.resolve(__dirname, '..'); @@ -11,8 +12,8 @@ module.exports = { }, entry: path.resolve(root, 'src/worker.ts'), output: { - path: path.resolve(root, '.temp'), - filename: 'worker.inline.js', + path: path.resolve(root, '.tmp'), + filename: WEBPACK_INLINE_WORKER_TEMP_FILE_NAME, libraryTarget: 'commonjs2', clean: true, }, diff --git a/webpack/worker.plugin.js b/webpack/worker.plugin.js index 7cab943..2cf4021 100644 --- a/webpack/worker.plugin.js +++ b/webpack/worker.plugin.js @@ -1,32 +1,24 @@ /* eslint-disable no-undef */ /* eslint-disable @typescript-eslint/no-var-requires */ -const ModuleFilenameHelpers = require('webpack/lib/ModuleFilenameHelpers'); +const { WEBPACK_INLINE_WORKER_TEMP_FILE_NAME } = require('./const'); class InjectWorkerPlugin { apply(compiler) { const ConcatSource = compiler.webpack.sources.ConcatSource; - const tester = { test: this.test }; - compiler.hooks.compilation.tap('InjectWorkerPlugin', (compilation) => { - compilation.hooks.afterOptimizeChunkAssets.tap('InjectWorkerPlugin', (chunks) => { - for (const chunk of chunks) { - for (const fileName of chunk.files) { - if (ModuleFilenameHelpers.matchObject(tester, fileName)) { - wrapFile(compilation, fileName); - } - } - } - }); - }); - - function wrapFile(compilation, fileName) { - compilation.assets[fileName] = new ConcatSource( - 'module.exports = `', - compilation.assets[fileName], - '`;', - ); - } + compiler.hooks.compilation.tap( + 'InjectWorkerPlugin', + ({ hooks, assets }) => { + hooks.afterOptimizeChunkAssets.tap('InjectWorkerPlugin', () => { + assets[WEBPACK_INLINE_WORKER_TEMP_FILE_NAME] = new ConcatSource( + 'module.exports = `', + assets[WEBPACK_INLINE_WORKER_TEMP_FILE_NAME], + '`;', + ); + }); + }, + ); } }