/// <summary> /// Generate a field of view within this cell map /// </summary> /// <param name="fromPoint"></param> /// <param name="maxRange"></param> /// <param name="transparencyCondition"></param> /// <param name="outGrid"></param> /// <param name="visibleValue">The value to set in the output grid to indicate visibility</param> public static void FieldOfView <E, T>(this ICellMap <T> grid, Vector fromPoint, double maxRange, Func <T, bool> transparencyCondition, ICellMap <E> outGrid, E visibleValue) { double tolerance = 0.00001; double maxRangeSqd = maxRange * maxRange; AngleIntervals shadows = new AngleIntervals(); IList <int> cIs = grid.CellsOrderedByDistance(fromPoint, Math.Ceiling(maxRange)); int current = grid.IndexAt(fromPoint); foreach (int index in cIs) { Vector centroid = grid.CellPosition(index); double distSqd = fromPoint.DistanceToSquared(centroid); if (distSqd > maxRangeSqd) { return; //Shortcut, reached end of range (?) } T item = grid[index]; bool opaque = !transparencyCondition.Invoke(item); if ((opaque || !shadows.IsEmpty) && index != current) { double angle = fromPoint.AngleTo(centroid).NormalizeTo2PI(); if (!shadows.isInsideRegion(angle, tolerance)) { //Is visible: if (!opaque) { outGrid[index] = visibleValue; } } if (opaque) { //Add to shadows: Angle mindA = 0; Angle maxdA = 0; for (int i = 0; i < grid.VertexCount(index); i++) { Vector vertex = grid.CellVertex(index, i); Angle dA = (fromPoint.AngleTo(vertex) - angle).Normalize(); if (dA < mindA) { mindA = dA; } if (dA > maxdA) { maxdA = dA; } } mindA *= 0.9; maxdA *= 0.9; Angle start = (angle + mindA).NormalizeTo2PI(); Angle end = (angle + maxdA).NormalizeTo2PI(); shadows.addRegion(start, end); //Shortcut: end if full: if (shadows.IsFull) { return; } } } else { outGrid[index] = visibleValue; } } }