UNPKG

permissive-fov

Version:

An implementation of Permissive Field Of View algorithm in JavaScript.

136 lines (135 loc) 6.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var View_1 = require("./View"); var Line_1 = require("./Line"); var Point_1 = require("./Point"); var ViewBump_1 = require("./ViewBump"); var PermissiveFov = (function () { function PermissiveFov(mapWidth, mapHeight, isTransparent) { this.mapWidth = mapWidth; this.mapHeight = mapHeight; this.isTransparent = isTransparent; } PermissiveFov.prototype.compute = function (startX, startY, radius, setVisible) { var data = { setVisible: setVisible, isTransparent: this.isTransparent, startX: startX, startY: startY, radius: radius, visited: [] }; data.setVisible(startX, startY); data.visited[this.mapWidth * startY + startX] = true; var minExtentX = (startX < radius ? startX : radius); var maxExtentX = (this.mapWidth - startX <= radius ? this.mapWidth - startX - 1 : radius); var minExtentY = (startY < radius ? startY : radius); var maxExtentY = (this.mapHeight - startY <= radius ? this.mapHeight - startY - 1 : radius); this.computeQuadrant(data, 1, 1, maxExtentX, maxExtentY); this.computeQuadrant(data, 1, -1, maxExtentX, minExtentY); this.computeQuadrant(data, -1, -1, minExtentX, minExtentY); this.computeQuadrant(data, -1, 1, minExtentX, maxExtentY); }; ; PermissiveFov.prototype.setMapDimensions = function (mapWidth, mapHeight) { this.mapWidth = mapWidth; this.mapHeight = mapHeight; }; ; PermissiveFov.prototype.setIsTransparent = function (isTransparent) { this.isTransparent = isTransparent; }; ; PermissiveFov.prototype.computeQuadrant = function (data, deltaX, deltaY, maxX, maxY) { var activeViews = []; var startJ; var maxJ; var maxI = maxX + maxY; var shallowLine = new Line_1.Line(0, 1, maxX, 0); var steepLine = new Line_1.Line(1, 0, 0, maxY); var viewIndex = 0; activeViews.push(new View_1.View(shallowLine, steepLine)); for (var i = 1; i <= maxI && activeViews.length; ++i) { startJ = (0 > i - maxX ? 0 : i - maxX); maxJ = (i < maxY ? i : maxY); for (var j = startJ; j <= maxJ && viewIndex < activeViews.length; ++j) { this.visitPoint(data, i - j, j, deltaX, deltaY, viewIndex, activeViews); } } }; PermissiveFov.prototype.visitPoint = function (data, x, y, deltaX, deltaY, viewIndex, activeViews) { var topLeft = new Point_1.Point(x, y + 1); var bottomRight = new Point_1.Point(x + 1, y); var realX = x * deltaX; var realY = y * deltaY; for (; viewIndex < activeViews.length && activeViews[viewIndex].steepLine.isBelowOrCollinear(bottomRight.x, bottomRight.y); ++viewIndex) { } if (viewIndex === activeViews.length || activeViews[viewIndex].shallowLine.isAboveOrCollinear(topLeft.x, topLeft.y)) { return; } var visitedPoint = new Point_1.Point(data.startX + realX, data.startY + realY); var visitedPointIndex = this.mapWidth * visitedPoint.y + visitedPoint.x; if (!data.visited[visitedPointIndex]) { data.visited[visitedPointIndex] = true; data.setVisible(visitedPoint.x, visitedPoint.y); } if (data.isTransparent(visitedPoint.x, visitedPoint.y)) { return; } var shallowLineIsAboveBottomRight = activeViews[viewIndex].shallowLine.isAbove(bottomRight.x, bottomRight.y); var steepLineIsBelowTopLeft = activeViews[viewIndex].steepLine.isBelow(topLeft.x, topLeft.y); if (shallowLineIsAboveBottomRight && steepLineIsBelowTopLeft) { activeViews.splice(viewIndex, 1); } else if (shallowLineIsAboveBottomRight) { this.addShallowBump(topLeft.x, topLeft.y, activeViews, viewIndex); this.checkView(activeViews, viewIndex); } else if (steepLineIsBelowTopLeft) { this.addSteepBump(bottomRight.x, bottomRight.y, activeViews, viewIndex); this.checkView(activeViews, viewIndex); } else { var shallowViewIndex = viewIndex; var steepViewIndex = ++viewIndex; activeViews.splice(shallowViewIndex, 0, activeViews[shallowViewIndex].clone()); this.addSteepBump(bottomRight.x, bottomRight.y, activeViews, shallowViewIndex); if (!this.checkView(activeViews, shallowViewIndex)) { --steepViewIndex; } this.addShallowBump(topLeft.x, topLeft.y, activeViews, steepViewIndex); this.checkView(activeViews, steepViewIndex); } }; PermissiveFov.prototype.addShallowBump = function (x, y, activeViews, viewIndex) { activeViews[viewIndex].shallowLine.setFarPoint(x, y); activeViews[viewIndex].shallowBump = new ViewBump_1.ViewBump(x, y, activeViews[viewIndex].shallowBump); for (var curBump = activeViews[viewIndex].steepBump; curBump; curBump = curBump.parent) { if (activeViews[viewIndex].shallowLine.isAbove(curBump.x, curBump.y)) { activeViews[viewIndex].shallowLine.setNearPoint(curBump.x, curBump.y); } } }; PermissiveFov.prototype.addSteepBump = function (x, y, activeViews, viewIndex) { activeViews[viewIndex].steepLine.setFarPoint(x, y); activeViews[viewIndex].steepBump = new ViewBump_1.ViewBump(x, y, activeViews[viewIndex].steepBump); for (var curBump = activeViews[viewIndex].shallowBump; curBump; curBump = curBump.parent) { if (activeViews[viewIndex].steepLine.isBelow(curBump.x, curBump.y)) { activeViews[viewIndex].steepLine.setNearPoint(curBump.x, curBump.y); } } }; PermissiveFov.prototype.checkView = function (activeViews, viewIndex) { if (activeViews[viewIndex].shallowLine.isLineCollinear(activeViews[viewIndex].steepLine) && (activeViews[viewIndex].shallowLine.isCollinear(0, 1) || activeViews[viewIndex].shallowLine.isCollinear(1, 0))) { activeViews.splice(viewIndex, 1); return false; } return true; }; return PermissiveFov; }()); exports.PermissiveFov = PermissiveFov;