// VisionMap builds a field of vision map for a viewer at src. It returns a
// cached slice of lighted nodes. Values can also be consulted individually
// with At.
//
// The algorithm works in a way that can remind of the Dijkstra algorithm, but
// within each cone between a diagonal and an orthogonal axis (an octant), only
// movements along those two directions are allowed. This allows the algorithm
// to be a simple pass on squares around the player, starting from radius 1
// until line of sight range.
//
// Going from a gruid.Point p to a gruid.Point q has a cost, which depends
// essentially on the type of terrain in p, and is determined by a Lighter.
//
// The obtained light rays are lines formed using at most two adjacent
// directions: a diagonal and an orthogonal one (for example north east and
// east).
VisionMap builds a field of vision map for a viewer at src. It returns a
cached slice of lighted nodes. Values can also be consulted individually
with At.

The algorithm works in a way that can remind of the Dijkstra algorithm, but
within each cone between a diagonal and an orthogonal axis (an octant), only
movements along those two directions are allowed. This allows the algorithm
to be a simple pass on squares around the player, starting from radius 1
until line of sight range.

Going from a gruid.Point p to a gruid.Point q has a cost, which depends
essentially on the type of terrain in p, and is determined by a Lighter.

The obtained light rays are lines formed using at most two adjacent
directions: a diagonal and an orthogonal one (for example north east and
east).
// VisionMap builds a field of vision map for a viewer at src . It returns a // cached slice of lighted nodes . Values can also be consulted individually // with At . // // The algorithm works in a way that can remind of the Dijkstra algorithm but // within each cone between a diagonal and an orthogonal axis ( an octant ) only // movements along those two directions are allowed . This allows the algorithm // to be a simple pass on squares around the player starting from radius 1 // until line of sight range . // // Going from a gruid . Point p to a gruid . Point q has a cost which depends // essentially on the type of terrain in p and is determined by a Lighter . // // The obtained light rays are lines formed using at most two adjacent // directions : a diagonal and an orthogonal one ( for example north east and // east ) .
func (fov *FOV) VisionMap(lt Lighter, src gruid.Point) []LightNode {
	fov.Lighted = fov.Lighted[:0]
	if !src.In(fov.Rg) {
		return fov.Lighted
	}
	if fov.Costs == nil {
		fov.Costs = make([]int, fov.Capacity)
	}
	for i := range fov.Costs {
		fov.Costs[i] = 0
	}
	fov.Src = src
	fov.Costs[fov.idx(src)] = 1
	fov.Lighted = append(fov.Lighted, LightNode{P: src, Cost: 0})
	for d := 1; d <= lt.MaxCost(src); d++ {
		rg := fov.Rg.Intersect(gruid.NewRange(src.X-d, src.Y-d+1, src.X+d+1, src.Y+d))
		if src.Y+d < fov.Rg.Max.Y {
			for x := rg.Min.X; x < rg.Max.X; x++ {
				fov.visionUpdate(lt, src, gruid.Point{x, src.Y + d})
			}
		}
		if src.Y-d >= fov.Rg.Min.Y {
			for x := rg.Min.X; x < rg.Max.X; x++ {
				fov.visionUpdate(lt, src, gruid.Point{x, src.Y - d})
			}
		}
		if src.X+d < fov.Rg.Max.X {
			for y := rg.Min.Y; y < rg.Max.Y; y++ {
				fov.visionUpdate(lt, src, gruid.Point{src.X + d, y})
			}
		}
		if src.X-d >= fov.Rg.Min.X {
			for y := rg.Min.Y; y < rg.Max.Y; y++ {
				fov.visionUpdate(lt, src, gruid.Point{src.X - d, y})
			}
		}
	}
	return fov.Lighted
}
func ( fov * FOV ) VisionMap ( lt Lighter , src gruid . Point ) [ ] LightNode { fov . Lighted = fov . Lighted [ : 0 ] if ! src . In ( fov . Rg ) { return fov . Lighted } if fov . Costs == nil { fov . Costs = make ( [ ] int , fov . Capacity ) } for i := range fov . Costs { fov . Costs [ i ] = 0 } fov . Src = src fov . Costs [ fov . idx ( src ) ] = 1 fov . Lighted = append ( fov . Lighted , LightNode { P : src , Cost : 0 } ) for d := 1 ; d <= lt . MaxCost ( src ) ; d ++ { rg := fov . Rg . Intersect ( gruid . NewRange ( src . X - d , src . Y - d + 1 , src . X + d + 1 , src . Y + d ) ) if src . Y + d < fov . Rg . Max . Y { for x := rg . Min . X ; x < rg . Max . X ; x ++ { fov . visionUpdate ( lt , src , gruid . Point { x , src . Y + d } ) } } if src . Y - d >= fov . Rg . Min . Y { for x := rg . Min . X ; x < rg . Max . X ; x ++ { fov . visionUpdate ( lt , src , gruid . Point { x , src . Y - d } ) } } if src . X + d < fov . Rg . Max . X { for y := rg . Min . Y ; y < rg . Max . Y ; y ++ { fov . visionUpdate ( lt , src , gruid . Point { src . X + d , y } ) } } if src . X - d >= fov . Rg . Min . X { for y := rg . Min . Y ; y < rg . Max . Y ; y ++ { fov . visionUpdate ( lt , src , gruid . Point { src . X - d , y } ) } } } return fov . Lighted }
VisionMap builds a field of vision map for a viewer at src.
VisionMap builds a field of vision map for a viewer at src .