/// <summary>Computes the cells the specified rectangle falls into.</summary>
        /// <param name="rectangle">The rectangle.</param>
        /// <returns>The cells the rectangle intersects with.</returns>
        private static IEnumerable <Tuple <ulong, TPoint> > ComputeCells(TRectangle rectangle)
        {
#if FALSE && FARMATH // Only works when automatically normalizing.
            var left   = rectangle.X.Segment;
            var right  = rectangle.Right.Segment;
            var top    = rectangle.Y.Segment;
            var bottom = rectangle.Bottom.Segment;
#elif FARMATH // TODO make sure this works
            var left   = rectangle.X.Segment + (int)(rectangle.X.Offset / FarValue.SegmentSizeHalf);
            var right  = rectangle.Right.Segment + (int)(rectangle.Right.Offset / FarValue.SegmentSizeHalf);
            var top    = rectangle.Y.Segment + (int)(rectangle.Y.Offset / FarValue.SegmentSizeHalf);
            var bottom = rectangle.Bottom.Segment + (int)(rectangle.Bottom.Offset / FarValue.SegmentSizeHalf);
#else
            var left   = (int)(rectangle.X / CellSize);
            var right  = (int)(rectangle.Right / CellSize);
            var top    = (int)(rectangle.Y / CellSize);
            var bottom = (int)(rectangle.Bottom / CellSize);
#endif

            TPoint center;
            for (var x = left; x <= right; x++)
            {
                center.X = -x * CellSize;
                for (var y = top; y <= bottom; y++)
                {
                    center.Y = -y * CellSize;
                    yield return(Tuple.Create(BitwiseMagic.Pack(x, y), center));
                }
            }
        }
Exemple #2
0
        /// <summary>
        ///     Creates a <see cref="FarRectangle"/> defining the area where one rectangle overlaps with another rectangle.
        /// </summary>
        /// <param name="value1">
        ///     The first <see cref="FarRectangle"/> to compare.
        /// </param>
        /// <param name="value2">
        ///     The second <see cref="FarRectangle"/> to compare.
        /// </param>
        /// <param name="result">The area where the two first parameters overlap.</param>
        public static void Intersect(ref FarRectangle value1, ref FarRectangle value2, out FarRectangle result)
        {
            var right1  = value1.X + value1.Width;
            var right2  = value2.X + value2.Width;
            var bottom1 = value1.Y + value1.Height;
            var bottom2 = value2.Y + value2.Height;
            var left    = (value1.X > value2.X) ? value1.X : value2.X;
            var top     = (value1.Y > value2.Y) ? value1.Y : value2.Y;
            var right   = (right1 < right2) ? right1 : right2;
            var bottom  = (bottom1 < bottom2) ? bottom1 : bottom2;

            if ((right > left) && (bottom > top))
            {
                result.X      = left;
                result.Y      = top;
                result.Width  = (float)(right - left);
                result.Height = (float)(bottom - top);
            }
            else
            {
                result.X      = 0;
                result.Y      = 0;
                result.Width  = 0;
                result.Height = 0;
            }
        }
Exemple #3
0
 /// <summary>
 ///     Determines whether a specified <see cref="FarRectangle"/> intersects with this <see cref="FarRectangle"/>.
 /// </summary>
 /// <param name="other">
 ///     The <see cref="FarRectangle"/> to evaluate.
 /// </param>
 /// <param name="result">
 ///     true if the specified <see cref="FarRectangle"/> intersects with this one; false otherwise.
 /// </param>
 public void Intersects(FarRectangle other, out bool result)
 {
     result = X < (other.X + other.Width) &&
              other.X < (X + Width) &&
              Y < (other.Y + other.Height) &&
              other.Y < (Y + Height);
 }
Exemple #4
0
 /// <summary>
 ///     Determines whether a specified <see cref="FarRectangle"/> intersects with this <see cref="FarRectangle"/>.
 /// </summary>
 /// <param name="other">
 ///     The <see cref="FarRectangle"/> to evaluate.
 /// </param>
 /// <returns>
 ///     <c>true</c> if the rectangles intersect; otherwise, <c>false</c>.
 /// </returns>
 public bool Intersects(FarRectangle other)
 {
     return(X < (other.X + other.Width) &&
            other.X < (X + Width) &&
            Y < (other.Y + other.Height) &&
            other.Y < (Y + Height));
 }
Exemple #5
0
 /// <summary>
 ///     Determines whether this <see cref="FarRectangle"/> entirely contains a specified <see cref="FarRectangle"/>.
 /// </summary>
 /// <param name="value">
 ///     The <see cref="FarRectangle"/> to evaluate.
 /// </param>
 /// <param name="result">
 ///     On exit, is true if this <see cref="FarRectangle"/> entirely contains the specified
 ///     <see cref="FarRectangle"/>, or false if not.
 /// </param>
 public void Contains(ref FarRectangle value, out bool result)
 {
     result = X <= value.X &&
              (value.X + value.Width) <= (X + Width) &&
              Y <= value.Y &&
              (value.Y + value.Height) <= (Y + Height);
 }
Exemple #6
0
 /// <summary>
 ///     Determines whether this <see cref="FarRectangle"/> entirely contains a specified <see cref="FarRectangle"/>.
 /// </summary>
 /// <param name="value">
 ///     The <see cref="FarRectangle"/> to evaluate.
 /// </param>
 /// <returns>
 ///     <c>true</c> if the rectangle contains the specified value; otherwise, <c>false</c>.
 /// </returns>
 public bool Contains(FarRectangle value)
 {
     return(X <= value.X &&
            (value.X + value.Width) <= (X + Width) &&
            Y <= value.Y &&
            (value.Y + value.Height) <= (Y + Height));
 }
Exemple #7
0
        /// <summary>
        ///     Creates a new <see cref="FarRectangle"/> that exactly contains two other rectangles.
        /// </summary>
        /// <param name="value1">
        ///     The first <see cref="FarRectangle"/> to contain.
        /// </param>
        /// <param name="value2">
        ///     The second <see cref="FarRectangle"/> to contain.
        /// </param>
        /// <param name="result">
        ///     The <see cref="FarRectangle"/> that must be the union of the first two rectangles.
        /// </param>
        public static void Union(ref FarRectangle value1, ref FarRectangle value2, out FarRectangle result)
        {
            var right1  = value1.X + value1.Width;
            var right2  = value2.X + value2.Width;
            var bottom1 = value1.Y + value1.Height;
            var bottom2 = value2.Y + value2.Height;
            var left    = (value1.X < value2.X) ? value1.X : value2.X;
            var top     = (value1.Y < value2.Y) ? value1.Y : value2.Y;
            var right   = (right1 > right2) ? right1 : right2;
            var bottom  = (bottom1 > bottom2) ? bottom1 : bottom2;

            result.X      = left;
            result.Y      = top;
            result.Width  = (float)(right - left);
            result.Height = (float)(bottom - top);
        }
        /// <summary>
        ///     Perform an area query on this index. This will return all entries in the tree that are contained in or
        ///     intersecting with the specified query rectangle.
        /// </summary>
        /// <param name="rectangle">The query rectangle.</param>
        /// <param name="callback">The method to call for each found hit.</param>
        /// <returns></returns>
        public bool Find(TRectangle rectangle, SimpleQueryCallback <T> callback)
        {
            // Getting the full list and then iterating it seems to actually be faster
            // than injecting a delegate...

            /*
             *
             * // HashSet we might use for filtering duplicate results. We initialize it lazily.
             * HashSet<T> filter = null;
             *
             * foreach (var cell in ComputeCells(rectangle))
             * {
             *  if (_entries.ContainsKey(cell.Item1))
             *  {
             *      // Convert the query bounds to the tree's local coordinate space.
             *      var relativeFarBounds = rectangle;
             *      relativeFarBounds.Offset(cell.Item2);
             *
             *      // And do the query.
             *      var relativeBounds = (Math.RectangleF)relativeFarBounds;
             *      if (!_entries[cell.Item1].Find(relativeBounds,
             *          value => !Filter(value, ref filter) || callback(value)))
             *      {
             *          return false;
             *      }
             *  }
             * }
             *
             * /*/

            ISet <T> results = new HashSet <T>();

            Find(rectangle, results);
            foreach (var result in results)
            {
                if (!callback(result))
                {
                    return(false);
                }
            }

            //*/

            return(true);
        }
        /// <summary>
        ///     Perform an area query on this index. This will return all entries in the tree that are contained in or
        ///     intersecting with the specified query rectangle.
        /// </summary>
        /// <param name="rectangle">The query rectangle.</param>
        /// <param name="results">The results.</param>
        public void Find(TRectangle rectangle, ISet <T> results)
        {
            foreach (var cell in ComputeCells(rectangle))
            {
                if (_cells.ContainsKey(cell.Item1))
                {
                    // Convert the query to the tree's local coordinate space.
                    var relativeFarBounds = rectangle;
                    relativeFarBounds.Offset(cell.Item2);

                    // And do the query.
// ReSharper disable RedundantCast Necessary for FarCollections.
                    var relativeBounds = (Math.RectangleF)relativeFarBounds;
// ReSharper restore RedundantCast
                    _cells[cell.Item1].Find(relativeBounds, results);
                }
            }
        }
        /// <summary>Add a new item to the index, with the specified bounds.</summary>
        /// <param name="bounds">The bounds of the item.</param>
        /// <param name="item">The item.</param>
        /// <exception cref="T:System.ArgumentException">The item is already stored in the index.</exception>
        public void Add(TRectangle bounds, T item)
        {
            if (Contains(item))
            {
                throw new ArgumentException("Entry is already in the index.", "item");
            }

            // Extend bounds.
            bounds.Inflate(_boundExtension, _boundExtension);

            // Add to each cell the element's bounds intersect with.
            foreach (var cell in ComputeCells(bounds))
            {
                // Create the quad tree for that cell if it doesn't yet exist.
                if (!_cells.ContainsKey(cell.Item1))
                {
                    // No need to extend again, we already did.
                    _cells.Add(
                        cell.Item1,
                        new Collections.DynamicQuadTree <T>(
                            _maxEntriesPerNode,
                            _minNodeBounds,
                            0f,
                            0f,
                            _packetizer,
                            _depacketizer));
                }

                // Convert the item bounds to the tree's local coordinate space.
                var relativeBounds = bounds;
                relativeBounds.Offset(cell.Item2);

                // And add the item to the tree.
// ReSharper disable RedundantCast Necessary for FarCollections.
                _cells[cell.Item1].Add((Math.RectangleF)relativeBounds, item);
// ReSharper restore RedundantCast
            }

            // Store element itself for future retrieval (removals, item lookup).
            _entryBounds.Add(item, bounds);
        }
Exemple #11
0
 /// <summary>Reads a far rectangle value.</summary>
 /// <param name="packet">The packet to read from.</param>
 /// <param name="data">The read value.</param>
 /// <returns>This packet, for call chaining.</returns>
 /// <exception cref="PacketException">The packet has not enough available data for the read operation.</exception>
 public static IReadablePacket Read(this IReadablePacket packet, out FarRectangle data)
 {
     data = packet.ReadFarRectangle();
     return(packet);
 }
Exemple #12
0
 /// <summary>Writes the specified rectangle value.</summary>
 /// <param name="packet">The packet to write to.</param>
 /// <param name="data">The value to write.</param>
 /// <returns>This packet, for call chaining.</returns>
 public static IWritablePacket Write(this IWritablePacket packet, FarRectangle data)
 {
     return(packet.Write(data.X).Write(data.Y).Write(data.Width).Write(data.Height));
 }
        /// <summary>
        ///     Update an entry by changing its bounds. If the item is not stored in the index, this will return <code>false</code>
        ///     .
        /// </summary>
        /// <param name="newBounds">The new bounds of the item.</param>
        /// <param name="delta">The amount by which the object moved.</param>
        /// <param name="item">The item for which to update the bounds.</param>
        /// <returns>
        ///     <c>true</c> if the update was successful; <c>false</c> otherwise.
        /// </returns>
        public bool Update(TRectangle newBounds, Vector2 delta, T item)
        {
            // Check if we have that entry, if not add it.
            if (!Contains(item))
            {
                return(false);
            }

            // Get the old bounds.
            var oldBounds = _entryBounds[item];

            // Nothing to do if our approximation in the tree still contains the item.
            if (oldBounds.Contains(newBounds))
            {
                return(false);
            }

            // Estimate movement by bounds delta to predict position and
            // extend the bounds accordingly, to avoid tree updates.
            delta.X *= _movingBoundMultiplier;
            delta.Y *= _movingBoundMultiplier;
            var absDeltaX = delta.X < 0 ? -delta.X : delta.X;
            var absDeltaY = delta.Y < 0 ? -delta.Y : delta.Y;

            newBounds.Width += (int)absDeltaX;
            if (delta.X < 0)
            {
                newBounds.X += (int)delta.X;
            }
            newBounds.Height += (int)absDeltaY;
            if (delta.Y < 0)
            {
                newBounds.Y += (int)delta.Y;
            }

            // Extend bounds.
            newBounds.Inflate(_boundExtension, _boundExtension);

            // Figure out what changed (the delta in cells).

            // Because we already did the bound extensions the update method in the
            // related quad trees would just do superfluous work, so instead we can
            // just remove and re-insert the entries where necessary. This also makes
            // this function a lot simpler.

            /*
             *
             * var oldCells = new HashSet<Tuple<ulong, TPoint>>(ComputeCells(oldBounds));
             * var newCells = new HashSet<Tuple<ulong, TPoint>>(ComputeCells(newBounds));
             *
             * // Get all cells that the entry no longer is in.
             * var removedCells = new HashSet<Tuple<ulong, TPoint>>(oldCells);
             * removedCells.ExceptWith(newCells);
             * foreach (var cell in removedCells)
             * {
             *  // Remove from the tree.
             *  _entries[cell.Item1].Remove(item);
             *
             *  // Clean up: remove the tree if it's empty.
             *  if (_entries[cell.Item1].Count == 0)
             *  {
             *      _entries.Remove(cell.Item1);
             *  }
             * }
             *
             * // Get all the cells the entry now is in.
             * var addedCells = new HashSet<Tuple<ulong, TPoint>>(newCells);
             * addedCells.ExceptWith(oldCells);
             * foreach (var cell in addedCells)
             * {
             *  // Create the quad tree for that cell if it doesn't yet exist.
             *  if (!_entries.ContainsKey(cell.Item1))
             *  {
             *      // No need to extend again, we already did.
             *      _entries.Add(cell.Item1, new Collections.QuadTree<T>(_maxEntriesPerNode, _minNodeBounds, 0f, 0f));
             *  }
             *
             *  // Convert the item bounds to the tree's local coordinate space.
             *  var relativeBounds = newBounds;
             *  relativeBounds.Offset(cell.Item2);
             *
             *  // And add the item to the tree.
             *  _entries[cell.Item1].Add((Math.RectangleF)relativeBounds, item);
             * }
             *
             * // Get all cells the entry still is in.
             * oldCells.ExceptWith(addedCells);
             * oldCells.ExceptWith(removedCells);
             * foreach (var cell in oldCells)
             * {
             *  // Convert the item bounds to the tree's local coordinate space.
             *  var relativeBounds = newBounds;
             *  relativeBounds.Offset(cell.Item2);
             *
             *  // And update the item to the tree.
             *  _entries[cell.Item1].Update((Math.RectangleF)relativeBounds, Vector2.Zero, item);
             * }
             *
             * /*/

            // Remove from old cells.
            foreach (var cell in ComputeCells(oldBounds))
            {
                Collections.DynamicQuadTree <T> tree;
                _cells.TryGetValue(cell.Item1, out tree);
                if (tree != null)
                {
                    tree.Remove(item);
                }
            }

            // Add to new cells.
            foreach (var cell in ComputeCells(newBounds))
            {
                // Create the quad tree for that cell if it doesn't yet exist.
                if (!_cells.ContainsKey(cell.Item1))
                {
                    // No need to extend again, we already did.
                    _cells.Add(
                        cell.Item1,
                        new Collections.DynamicQuadTree <T>(
                            _maxEntriesPerNode,
                            _minNodeBounds,
                            0f,
                            0f,
                            _packetizer,
                            _depacketizer));
                }

                // Convert the item bounds to the tree's local coordinate space.
                var relativeBounds = newBounds;
                relativeBounds.Offset(cell.Item2);

                // And add the item to the tree.
// ReSharper disable RedundantCast Necessary for FarCollections.
                _cells[cell.Item1].Add((Math.RectangleF)relativeBounds, item);
// ReSharper restore RedundantCast
            }

            //*/

            // Store the new item bounds.
            _entryBounds[item] = newBounds;

            return(true);
        }
Exemple #14
0
        /// <summary>
        ///     Compares the two specified <see cref="FarRectangle"/>s for equality.
        /// </summary>
        /// <param name="other">
        ///     The other <see cref="FarRectangle"/>.
        /// </param>
        /// <returns>
        ///     <c>true</c> if the <see cref="FarRectangle"/>s are equal; otherwise, <c>false</c>.
        /// </returns>
        public bool Equals(FarRectangle other)
        {
// ReSharper disable CompareOfFloatsByEqualityOperator Intentional.
            return((X == other.X) && (Y == other.Y) && (Width == other.Width) && (Height == other.Height));
// ReSharper restore CompareOfFloatsByEqualityOperator
        }