// Copyright (c) 2013-2014 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE using System; using System.Collections.Generic; using System.Collections.ObjectModel; namespace SharpNav { /// /// A cell is a column of voxels represented in s. /// public class Cell { private List spans; private int height; /// /// Initializes a new instance of the class. /// /// The number of voxels in the column. public Cell(int height) { this.height = height; spans = new List(); } /// /// Gets the height of the cell in number of voxels. /// public int Height { get { return height; } } /// /// Gets the number of spans in the cell. /// public int SpanCount { get { return spans.Count; } } /// /// Gets the number of spans that are in walkable s. /// public int WalkableSpanCount { get { int count = 0; for (int i = 0; i < spans.Count; i++) if (spans[i].Area.IsWalkable) count++; return count; } } /// /// Gets a readonly list of all the s contained in the cell. /// /// A readonly list of spans. public ReadOnlyCollection Spans { get { return spans.AsReadOnly(); } } /// /// Gets a modifiable list of all the s contained in the cell. /// Should only be used for filtering in . /// /// A list of spans for modification. internal List MutableSpans { get { return spans; } } /// /// Gets the that contains the specified voxel. /// /// The voxel to search for. /// The span containing the voxel. Null if the voxel is empty. public Span? this[int location] { get { if (location < 0 || location >= height) throw new ArgumentOutOfRangeException("Location must be a value between 0 and " + height + "."); //iterate the list of spans foreach (Span s in spans) { if (s.Minimum > location) break; else if (s.Maximum >= location) return s; } return null; } } /// /// Adds a to the cell. /// /// A span. /// Thrown if an invalid span is provided. public void AddSpan(Span span) { if (span.Minimum > span.Maximum) { int tmp = span.Minimum; span.Minimum = span.Maximum; span.Maximum = tmp; } //clamp the span to the cell's range of [0, maxHeight] MathHelper.Clamp(ref span.Minimum, 0, height); MathHelper.Clamp(ref span.Maximum, 0, height); lock (spans) { for (int i = 0; i < spans.Count; i++) { //Check whether the current span is below, or overlapping existing spans. //If the span is completely above the current span the loop will continue. Span cur = spans[i]; if (cur.Minimum > span.Maximum) { //The new span is below the current one and is not intersecting. spans.Insert(i, span); return; } else if (cur.Maximum >= span.Minimum) { //The new span is colliding with the current one, merge them together. if (cur.Minimum < span.Minimum) span.Minimum = cur.Minimum; if (cur.Maximum == span.Maximum) { //In the case that both spans end at the same voxel, the area gets merged. The new span's area //has priority if both spans are walkable, so the only case where the area gets set is when //the new area isn't walkable and the old one is. if (!span.Area.IsWalkable && cur.Area.IsWalkable) span.Area = cur.Area; } else if (cur.Maximum > span.Maximum) { span.Maximum = cur.Maximum; span.Area = cur.Area; } //Remove the current span and adjust i. //We do this to avoid duplicating the current span. spans.RemoveAt(i); i--; } } //If the span is not inserted, it is the highest span and will be added to the end. spans.Add(span); } } } }