/// <summary> /// Determines whether an unobstructed path exists between two points. /// </summary> /// <param name="from">The from point.</param> /// <param name="to">The to point.</param> /// <param name="unitProps">The unit properties to test against (Walkability).</param> /// <param name="matrix">The matrix where both <paramref name="from"/> and <paramref name="to"/> must be part of.</param> /// <param name="costStrategy">The cost strategy.</param> /// <returns><c>true</c> if an unobstructed path exists between the two points; otherwise <c>false</c>.</returns> public static bool CanReducePath(IPositioned from, IPositioned to, IUnitProperties unitProps, CellMatrix matrix, ICellCostStrategy costStrategy) { //The reference cell is always the from cell. The fact the points are swapped to simplify tangents does not affect this. var refCell = matrix.GetCell(from.position, false); Vector3 p1; Vector3 p2; //Assign the points so we start with the point with the lowest x-value to simplify things if (from.position.x > to.position.x) { p1 = to.position; p2 = from.position; } else { p1 = from.position; p2 = to.position; } var unitRadius = unitProps.radius; var tan = Tangents.Create(p1, p2, unitRadius, matrix); var slopeDir = tan.slopeDir; var cellSize = matrix.cellSize; var halfCell = cellSize * 0.5f; //Get the start and end cells, get the cost of the actual start and end, and then reassign the start and end to accommodate for unit size. var startCell = matrix.GetCell(p1, false); var startCost = costStrategy.GetCellCost(startCell, unitProps); startCell = matrix.GetCell(new Vector3(p1.x - unitRadius, p1.y, p1.z), true); var endCell = matrix.GetCell(p2, false); var endCost = costStrategy.GetCellCost(endCell, unitProps); endCell = matrix.GetCell(new Vector3(p2.x + unitRadius, p2.y, p2.z), true); //The movement across the x-axis float minXCoord = (startCell.position.x - matrix.start.x) - halfCell; float maxXCoord = (startCell.position.x - matrix.start.x) + halfCell; int minZ; int maxZ; if (slopeDir < 0) { var distLowerCellBoundary = halfCell - (endCell.position.z - p2.z); var minOverlap = Mathf.CeilToInt((unitRadius - distLowerCellBoundary) / cellSize); minZ = Math.Max(0, endCell.matrixPosZ - Math.Max(0, minOverlap)); var distUpperCellBoundary = halfCell - (p1.z - startCell.position.z); var maxOverlap = Mathf.CeilToInt((unitRadius - distUpperCellBoundary) / cellSize); maxZ = Math.Min(matrix.rows - 1, startCell.matrixPosZ + Math.Max(0, maxOverlap)); } else { var distLowerCellBoundary = halfCell - (startCell.position.z - p1.z); var minOverlap = Mathf.CeilToInt((unitRadius - distLowerCellBoundary) / cellSize); minZ = Math.Max(0, startCell.matrixPosZ - Math.Max(0, minOverlap)); var distUpperCellBoundary = halfCell - (p2.z - endCell.position.z); var maxOverlap = Mathf.CeilToInt((unitRadius - distUpperCellBoundary) / cellSize); maxZ = Math.Min(matrix.rows - 1, endCell.matrixPosZ + Math.Max(0, maxOverlap)); } int startX = startCell.matrixPosX; int endX = endCell.matrixPosX; bool isVertical = tan.isVertical; var cellMatrix = matrix.rawMatrix; for (int x = startX; x <= endX; x++) { int startZ; int endZ; if (isVertical) { startZ = Math.Min(startCell.matrixPosZ, endCell.matrixPosZ); endZ = Math.Max(startCell.matrixPosZ, endCell.matrixPosZ); } else { if (slopeDir < 0) { startZ = Math.Max((int)(tan.LowTangent(maxXCoord) / cellSize), minZ); endZ = Math.Min((int)(tan.HighTangent(minXCoord) / cellSize), maxZ); } else { startZ = Math.Max((int)(tan.LowTangent(minXCoord) / cellSize), minZ); endZ = Math.Min((int)(tan.HighTangent(maxXCoord) / cellSize), maxZ); } } for (int z = startZ; z <= endZ; z++) { var intermediary = cellMatrix[x, z]; if (!isVertical && tan.IsOutsideSecants(intermediary.position)) { continue; } var intermediaryCost = costStrategy.GetCellCost(intermediary, unitProps); if (!intermediary.IsWalkableFrom(refCell, unitProps) || (startCost < intermediaryCost || endCost < intermediaryCost)) { return(false); } } minXCoord = maxXCoord; maxXCoord = maxXCoord + cellSize; } return(true); }
public static bool CanReducePath(IPositioned point1, IPositioned point3, IUnitProperties unitProps, CellMatrix matrix, ICellCostStrategy costStrategy) { Vector3 p1; Vector3 p3; //Assign the points so we start with the point with the lowest x-value to simplify things if (point1.position.x > point3.position.x) { p1 = point3.position; p3 = point1.position; } else { p1 = point1.position; p3 = point3.position; } var requesterRadius = unitProps.radius; var tan = Tangents.Create(p1, p3, requesterRadius); var incZ = tan.slopeDir; var cellSize = matrix.cellSize; var halfCell = cellSize / 2.0f; //Adjust the start and end cells to possibly include their immediate neighbour if the unit's radius crossed said boundary. var radiusAdjust = new Vector3(requesterRadius, 0.0f, requesterRadius * incZ); //Get the start and end cells, get the cost of the actual start and end, and then reassign the start and end with the above adjustment. var startCell = matrix.GetCell(p1, true); var startCost = costStrategy.GetCellCost(startCell, unitProps); startCell = matrix.GetCell(p1 - radiusAdjust, true); var endCell = matrix.GetCell(p3, true); var endCost = costStrategy.GetCellCost(endCell, unitProps); endCell = matrix.GetCell(p3 + radiusAdjust, true); //We want x to end up on cell boundaries, the first of which is this far from the first points position var xAdj = p1.x + (startCell.position.x - p1.x) + halfCell; //We want to adjust z so that we correctly count impacted cells, this adjusts z so it starts at the bottom boundary of the first cell (for purposes of calculation) var zAdj = p1.z - (halfCell + ((p1.z - startCell.position.z) * incZ)); //The movement across the x-axis float deltaX = 0.0f; var cellMatrix = matrix.rawMatrix; int indexX = 0; for (int x = startCell.matrixPosX; x <= endCell.matrixPosX; x++) { //So instead of just checking all cells in the bounding rect defined by the two cells p1 and p3, //we limit it to the cells immediately surrounding the proposed line (tangents), including enough cells that we ensure the unit will be able to pass through, //at the extreme routes between the two cells (i.e top corner to top corner and bottom corner to bottom corner int startZ; int endZ; //If the tangents are horizontal or vertical z range is obvious if (tan.isAxisAligned) { startZ = startCell.matrixPosZ; endZ = endCell.matrixPosZ + incZ; } else { if (indexX == 0) { startZ = startCell.matrixPosZ; } else { var startCellsPassed = Mathf.FloorToInt((tan.LowTangent(deltaX) - zAdj) / cellSize) * incZ; startZ = LimitStart( startCell.matrixPosZ + startCellsPassed, startCell.matrixPosZ, incZ); } //The movement this step will perform across the x-axis deltaX = xAdj + (indexX * cellSize); var endCellsIntercepted = Mathf.FloorToInt((tan.HighTangent(deltaX) - zAdj) / cellSize) * incZ; endZ = LimitEnd( startCell.matrixPosZ + endCellsIntercepted, endCell.matrixPosZ, incZ) + incZ; } indexX++; for (int z = startZ; z != endZ; z += incZ) { var intermediary = cellMatrix[x, z]; var intermediaryCost = costStrategy.GetCellCost(intermediary, unitProps); if (!intermediary.isWalkableFrom(startCell, unitProps) || (startCost < intermediaryCost && endCost < intermediaryCost)) { return false; } } } return true; }
private static bool CanReducePath(IPositioned point1, IPositioned point3, IUnitProperties unitProps, CellMatrix matrix, ICellCostStrategy costStrategy) { Vector3 p1; Vector3 p3; //Assign the points so we start with the point with the lowest x-value to simplify things if (point1.position.x > point3.position.x) { p1 = point3.position; p3 = point1.position; } else { p1 = point1.position; p3 = point3.position; } var requesterRadius = unitProps.radius; var tan = Tangents.Create(p1, p3, requesterRadius); var incZ = tan.slopeDir; var cellSize = matrix.cellSize; var halfCell = cellSize / 2.0f; //Adjust the start and end cells to possibly include their immediate neighbour if the unit's radius crossed said boundary. var radiusAdjust = new Vector3(requesterRadius, 0.0f, requesterRadius * incZ); //Get the start and end cells, get the cost of the actual start and end, and then reassign the start and end with the above adjustment. var startCell = matrix.GetCell(p1, true); var startCost = costStrategy.GetCellCost(startCell, unitProps); startCell = matrix.GetCell(p1 - radiusAdjust, true); var endCell = matrix.GetCell(p3, true); var endCost = costStrategy.GetCellCost(endCell, unitProps); endCell = matrix.GetCell(p3 + radiusAdjust, true); //We want x to end up on cell boundaries, the first of which is this far from the first points position var xAdj = p1.x + (startCell.position.x - p1.x) + halfCell; //We want to adjust z so that we correctly count impacted cells, this adjusts z so it starts at the bottom boundary of the first cell (for purposes of calculation) var zAdj = p1.z - (halfCell + ((p1.z - startCell.position.z) * incZ)); //The movement across the x-axis float deltaX = 0.0f; var cellMatrix = matrix.rawMatrix; int indexX = 0; for (int x = startCell.matrixPosX; x <= endCell.matrixPosX; x++) { //So instead of just checking all cells in the bounding rect defined by the two cells p1 and p3, //we limit it to the cells immediately surrounding the proposed line (tangents), including enough cells that we ensure the unit will be able to pass through, //at the extreme routes between the two cells (i.e top corner to top corner and bottom corner to bottom corner int startZ; int endZ; //If the tangents are horizontal or vertical z range is obvious if (tan.isAxisAligned) { startZ = startCell.matrixPosZ; endZ = endCell.matrixPosZ + incZ; } else { if (indexX == 0) { startZ = startCell.matrixPosZ; } else { var startCellsPassed = Mathf.FloorToInt((tan.LowTangent(deltaX) - zAdj) / cellSize) * incZ; startZ = LimitStart( startCell.matrixPosZ + startCellsPassed, startCell.matrixPosZ, incZ); } //The movement this step will perform across the x-axis deltaX = xAdj + (indexX * cellSize); var endCellsIntercepted = Mathf.FloorToInt((tan.HighTangent(deltaX) - zAdj) / cellSize) * incZ; endZ = LimitEnd( startCell.matrixPosZ + endCellsIntercepted, endCell.matrixPosZ, incZ) + incZ; } indexX++; for (int z = startZ; z != endZ; z += incZ) { var intermediary = cellMatrix[x, z]; var intermediaryCost = costStrategy.GetCellCost(intermediary, unitProps); if (!intermediary.isWalkableFrom(startCell, unitProps) || (startCost < intermediaryCost && endCost < intermediaryCost)) { return(false); } } } return(true); }