From 1ae0c7de6352b39e2ecc43fa1fb0a22b2bb2f3fc Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Sun, 1 Jul 2007 19:27:40 +0000 Subject: [PATCH] Added an AsyncStarted event to the progression class, currently disabled for further consideration; set up the outline of a new spatial partitioning framework with an R*-Tree implementation; new AbortedException for indicating that a process was forcefully aborted git-svn-id: file:///srv/devel/repo-conversion/nusu@31 d2e56fa2-650e-0410-a79f-9358c0239efd --- Nuclex.Support (PC).csproj | 24 +++++ .../Collections/UnintrusivePriorityQueue.cs | 5 +- Source/Scheduling/AbortedException.cs | 56 +++++++++++ Source/Scheduling/IAbortable.cs | 13 ++- Source/Scheduling/Operation.cs | 17 +--- Source/Scheduling/ThreadedMethodOperation.cs | 5 +- .../SpatialPartitioning/BoundingRectangle.cs | 78 +++++++++++++++ Source/SpatialPartitioning/RTree2.cs | 95 ++++++++++++++++++ Source/SpatialPartitioning/RTreeLeaf2.cs | 35 +++++++ Source/SpatialPartitioning/RTreeNode2.cs | 65 ++++++++++++ Source/SpatialPartitioning/SpatialIndex2.cs | 30 ++++++ Source/Tracking/Progression.cs | 99 +++++++++++++++++-- Source/WeakReference.cs | 19 ++++ 13 files changed, 511 insertions(+), 30 deletions(-) create mode 100644 Source/Scheduling/AbortedException.cs create mode 100644 Source/SpatialPartitioning/BoundingRectangle.cs create mode 100644 Source/SpatialPartitioning/RTree2.cs create mode 100644 Source/SpatialPartitioning/RTreeLeaf2.cs create mode 100644 Source/SpatialPartitioning/RTreeNode2.cs create mode 100644 Source/SpatialPartitioning/SpatialIndex2.cs diff --git a/Nuclex.Support (PC).csproj b/Nuclex.Support (PC).csproj index 5ac35d5..bb3c9fa 100644 --- a/Nuclex.Support (PC).csproj +++ b/Nuclex.Support (PC).csproj @@ -162,6 +162,10 @@ SimpleRectanglePacker.Test SimpleRectanglePacker.cs + + false + AbortedException + false BinarySerializer.Test @@ -179,6 +183,26 @@ false IAbortable + + false + BoundingRectangle + + + false + RTree2 + + + false + RTreeLeaf2 + + + false + RTreeNode2 + + + false + SpatialIndex2 + false ObservedProgression diff --git a/Source/Collections/UnintrusivePriorityQueue.cs b/Source/Collections/UnintrusivePriorityQueue.cs index 253a5ab..8282bc8 100644 --- a/Source/Collections/UnintrusivePriorityQueue.cs +++ b/Source/Collections/UnintrusivePriorityQueue.cs @@ -25,7 +25,8 @@ namespace Nuclex.Support.Collections { /// Queue that dequeues items in order of their priority public class UnintrusivePriorityQueue - : ICollection, IEnumerable where PriorityType : IComparable { + : ICollection, IEnumerable + where PriorityType : IComparable { #region struct Entry @@ -39,7 +40,7 @@ namespace Nuclex.Support.Collections { this.Item = item; this.Priority = priority; } - + /// Item contained in this priority queue entry public ItemType Item; /// Priority assigned to this entry diff --git a/Source/Scheduling/AbortedException.cs b/Source/Scheduling/AbortedException.cs new file mode 100644 index 0000000..160c234 --- /dev/null +++ b/Source/Scheduling/AbortedException.cs @@ -0,0 +1,56 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion +using System; +using System.Collections.Generic; + +namespace Nuclex.Support.Scheduling { + + /// Indicates that an operation has been forcefully aborted + /// + /// This exception is the typical result of using AsyncAbort() on a running + /// background process. + /// + [Serializable] + public class AbortedException : ApplicationException { + + /// Initializes the exception + public AbortedException() { } + + /// Initializes the exception with an error message + /// Error message describing the cause of the exception + public AbortedException(string message) : base(message) { } + + /// Initializes the exception as a followup exception + /// Error message describing the cause of the exception + /// Preceding exception that has caused this exception + public AbortedException(string message, Exception inner) : base(message, inner) { } + + /// Initializes the exception from its serialized state + /// Contains the serialized fields of the exception + /// Additional environmental informations + protected AbortedException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context + ) + : base(info, context) { } + + } + +} // namespace Nuclex.Support.Scheduling diff --git a/Source/Scheduling/IAbortable.cs b/Source/Scheduling/IAbortable.cs index c26beb7..5771ce1 100644 --- a/Source/Scheduling/IAbortable.cs +++ b/Source/Scheduling/IAbortable.cs @@ -19,16 +19,23 @@ License along with this library #endregion using System; using System.Collections.Generic; -using System.Text; -namespace Nuclex.Support.Tracking { +namespace Nuclex.Support.Scheduling { /// Interface for abortable processes public interface IAbortable { /// Aborts the running process. Can be called from any thread. + /// + /// The receive should honor the abort request and stop whatever it is + /// doing as soon as possible. The method does not impose any requirement + /// on the timeliness of the reaction of the running process, but implementers + /// are advised to not ignore the abort request and try to design their code + /// in such a way that it can be stopped in a reasonable time + /// (eg. within 1 second of the abort request). + /// void AsyncAbort(); } -} // namespace Nuclex.Support.Tracking +} // namespace Nuclex.Support.Scheduling diff --git a/Source/Scheduling/Operation.cs b/Source/Scheduling/Operation.cs index a190771..112cfb7 100644 --- a/Source/Scheduling/Operation.cs +++ b/Source/Scheduling/Operation.cs @@ -19,24 +19,17 @@ License along with this library #endregion using System; using System.Collections.Generic; -using System.Text; -namespace Nuclex.Support.Tracking { +using Nuclex.Support.Tracking; + +namespace Nuclex.Support.Scheduling { /// Base class for observable operations running in the background public abstract class Operation : Progression { - /// Executes the operation + /// Launches the background operation public abstract void Start(); - /// - /// Executes the operation synchronously, blocking the calling thread - /// - public virtual void Execute() { - Start(); - WaitHandle.WaitOne(); - } - } -} // namespace Nuclex.Support.Tracking +} // namespace Nuclex.Support.Scheduling diff --git a/Source/Scheduling/ThreadedMethodOperation.cs b/Source/Scheduling/ThreadedMethodOperation.cs index 9958cb7..52b4f16 100644 --- a/Source/Scheduling/ThreadedMethodOperation.cs +++ b/Source/Scheduling/ThreadedMethodOperation.cs @@ -19,11 +19,10 @@ License along with this library #endregion using System; using System.Collections.Generic; -using System.Text; -namespace Nuclex.Support.Tracking { +namespace Nuclex.Support.Scheduling { /* public class ThreadedMethodOperation : Operation { } */ -} // namespace Nuclex.Support.Tracking +} // namespace Nuclex.Support.Scheduling diff --git a/Source/SpatialPartitioning/BoundingRectangle.cs b/Source/SpatialPartitioning/BoundingRectangle.cs new file mode 100644 index 0000000..7ecfdc6 --- /dev/null +++ b/Source/SpatialPartitioning/BoundingRectangle.cs @@ -0,0 +1,78 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion +using System; +using System.Collections.Generic; + +using Microsoft.Xna.Framework; + +namespace Nuclex.Support.SpatialPartitioning { + + /// Two-dimensional bounding rectangle + internal struct BoundingRectangle { + + /// Initializes a new two-dimensional bounding rectangle + /// Lesser coordinates of the bounding rectangle + /// Greater coordinates of the bounding rectangle + public BoundingRectangle(Vector2 min, Vector2 max) { + this.Min = min; + this.Max = max; + } + + /// + /// Builds the smallest bounding rectangle that contains the two + /// specified bounding rectangle. + /// + /// One of the bounding rectangles to contain + /// One of the bounding rectangles to contain + /// The resulting merged bounding rectangle + public static BoundingRectangle CreateMerged( + BoundingRectangle original, BoundingRectangle additional + ) { + BoundingRectangle result; + CreateMerged(ref original, ref additional, out result); + return result; + } + + /// + /// Builds the smallest bounding rectangle that contains the two + /// specified bounding rectangle. + /// + /// One of the bounding rectangles to contain + /// One of the bounding rectangles to contain + /// The resulting merged bounding rectangle + public static void CreateMerged( + ref BoundingRectangle original, ref BoundingRectangle additional, + out BoundingRectangle result + ) { + result = new BoundingRectangle(); + result.Min = Vector2.Min(original.Min, additional.Min); + result.Max = Vector2.Max(original.Max, additional.Max); + } + + + /// Coordinates of the lesser side of the bounding rectangle + public Vector2 Min; + + /// Coordinates of the greater side of the bounding rectangle + public Vector2 Max; + + } + +} // namespace Nuclex.Support.SpatialPartitioning diff --git a/Source/SpatialPartitioning/RTree2.cs b/Source/SpatialPartitioning/RTree2.cs new file mode 100644 index 0000000..56527fe --- /dev/null +++ b/Source/SpatialPartitioning/RTree2.cs @@ -0,0 +1,95 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion +using System; +using System.Collections.Generic; + +using Microsoft.Xna.Framework; + +namespace Nuclex.Support.SpatialPartitioning { + + /// R-Tree for two-dimensional data + /// + /// R-Trees essentially fullfill the same role that is traditionally + /// assigned to quadtrees in games. But unlike a quadtree, an R-Tree does not + /// require knowledge of the total area that is to be covered by the objects + /// that will populate the tree. + /// + public partial class RTree2 : SpatialIndex2 { + + /// Variants of R-Tree behaviors this implementation can assume + public enum Variants { + + /// + /// Insertions and deletions take linear time at the cost of degrading the + /// tree's overall performance. + /// + /// + /// Finds the two bounding boxes with the greatest normalized separation + /// along both axes, and split along this axis. The remaining bounding boxes + /// in the node are assigned to the nodes whose covering bounding box is + /// increased the least by the addition [Gutt84]. This method takes linear time. + /// + Linear, + + /// + /// Insertions and deletions take quadratic time while keeping the tree's + /// overall performance at a reasonable level. + /// + /// + /// Examines all the children of the overflowing node and find the pair of + /// bounding boxes that would waste the most area were they to be inserted + /// in the same node. This is determined by subtracting the sum of the areas + /// of the two bounding boxes from the area of the covering bounding box. + /// These two bounding boxes are placed in separate nodes, say j and k. + /// The set of remaining bounding boxes are examined and the bounding box i + /// whose addition maximizes the difference in coverage between the bounding + /// boxes associated with j and k is added to the node whose coverage + /// is minimized by the addition. This process is reapplied to the + /// remaining bounding boxes [Gutt84]. This method takes quadratic time. + /// + Quadratic, + + /// + /// Insertions and deletions vary in performance but the tree's overall + /// performance is kept high. + /// + /// + /// The R*-tree [Beck90c] is a name given to a variant of the R-tree which + /// makes use of the most complex of the node splitting algorithms. The + /// algorithm differs from the other algorithms as it attempts to reduce + /// both overlap and coverage. In particular, the primary focus is on + /// reducing overlap with ties broken by favoring the splits that reduce + /// the coverage by using the splits that minimize the perimeter of the + /// bounding boxes of the resulting nodes. In addition, when a node 'a' + /// overflows, instead of immediately splitting 'a', an attempt is made + /// first to see if some of the objects in 'a' could possibly be more suited + /// to being in another node. This is achieved by reinserting a fraction + /// (30% has been found to yield good performance [Beck90c]) of these + /// objects in the tree (termed 'forced reinsertion'). The node is only split + /// if it has been found to overflow after reinsertion has taken place. + /// This method is quite complex. + /// + RStar + + } + + } + +} // namespace Nuclex.Geometry.SpatialPartitioning diff --git a/Source/SpatialPartitioning/RTreeLeaf2.cs b/Source/SpatialPartitioning/RTreeLeaf2.cs new file mode 100644 index 0000000..f76129d --- /dev/null +++ b/Source/SpatialPartitioning/RTreeLeaf2.cs @@ -0,0 +1,35 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nuclex.Support.SpatialPartitioning { + + /// Leaf of an R-Tree + internal class RTreeLeaf2 { + + public BoundingRectangle BoundingRectangle; + public ItemType Item; + + + } + +} // namespace Nuclex.Support.SpatialPartitioning diff --git a/Source/SpatialPartitioning/RTreeNode2.cs b/Source/SpatialPartitioning/RTreeNode2.cs new file mode 100644 index 0000000..45cf63f --- /dev/null +++ b/Source/SpatialPartitioning/RTreeNode2.cs @@ -0,0 +1,65 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Nuclex.Support.SpatialPartitioning { + + /// Node in a two-dimensional R-Tree + /// Type of items that the R-Tree manages + internal class RTreeNode2 { + + /// Initializes a new R-Tree node + /// Number of items that can fit in the node + public RTreeNode2(int capacity) { + this.leafs = new RTreeLeaf2[capacity]; + } + + /// Inserts an item into this node + /// Item to be inserted + /// Bounding rectangle of the item + private void insertEntry(ItemType item, BoundingRectangle boundingRectangle) { + Debug.Assert(leafCount < this.leafs.Length); + + this.leafs[this.leafCount].Item = item; + this.leafs[this.leafCount].BoundingRectangle = boundingRectangle; + + BoundingRectangle.CreateMerged( + ref this.boundingRectangle, ref boundingRectangle, + out this.boundingRectangle + ); + } + + /// The node's minimum bounding rectangle + /// + /// This bounding rectangle is just large enough to contain all the items + /// belonging to this node and recursively all of its child nodes. + /// + private BoundingRectangle boundingRectangle; + + /// Leafs of this node + private RTreeLeaf2[] leafs; + /// Number of leafes in use + private int leafCount; + + } + +} // namespace Nuclex.Support.SpatialPartitioning diff --git a/Source/SpatialPartitioning/SpatialIndex2.cs b/Source/SpatialPartitioning/SpatialIndex2.cs new file mode 100644 index 0000000..7e3c5f6 --- /dev/null +++ b/Source/SpatialPartitioning/SpatialIndex2.cs @@ -0,0 +1,30 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion +using System; +using System.Collections.Generic; + +namespace Nuclex.Support.SpatialPartitioning { + + /// Interface for a 2D geometrical database + public abstract class SpatialIndex2 { + + } + +} // namespace Nuclex.Support.SpatialPartitioning diff --git a/Source/Tracking/Progression.cs b/Source/Tracking/Progression.cs index b6448ae..f3e5593 100644 --- a/Source/Tracking/Progression.cs +++ b/Source/Tracking/Progression.cs @@ -45,10 +45,13 @@ namespace Nuclex.Support.Tracking { #region class EndedDummyProgression /// Dummy progression which always is in the 'ended' state - internal class EndedDummyProgression : Progression { + private class EndedDummyProgression : Progression { /// Initializes a new ended dummy progression public EndedDummyProgression() { +#if PROGRESSION_STARTABLE + OnAsyncStarted(); +#endif OnAsyncEnded(); } @@ -67,14 +70,42 @@ namespace Nuclex.Support.Tracking { /// will be triggered to report when progress has been achieved public event EventHandler AsyncProgressUpdated; +#if PROGRESSION_STARTABLE + /// Will be triggered when the progression has ended + public event EventHandler AsyncStarted; +#endif + /// Will be triggered when the progression has ended public event EventHandler AsyncEnded; +#if PROGRESSION_STARTABLE + /// True when the progression has been started. + /// + /// This is also true if the progression has been started but is already finished. + /// To find out whether the progression is running just now, use the IsRunning + /// property instead. + /// + public bool Started { + get { return this.started; } + } +#endif + /// Whether the progression has ended already public bool Ended { get { return this.ended; } } +#if PROGRESSION_STARTABLE + /// Whether the progression is currently executing. + /// + /// This property is true when the progression is executing right at the time of + /// the call (eg. when it has been started and has not ended yet). + /// + public bool IsRunning { + get { return (this.started && !base.Ended); } + } +#endif + /// WaitHandle that can be used to wait for the progression to end public WaitHandle WaitHandle { get { @@ -88,7 +119,7 @@ namespace Nuclex.Support.Tracking { // the second one is assigned to this.doneEvent and thus gets set in the end. if(this.doneEvent == null) { - lock(this.syncRoot) { + lock(this) { if(this.doneEvent == null) this.doneEvent = new ManualResetEvent(this.ended); @@ -124,20 +155,66 @@ namespace Nuclex.Support.Tracking { copy(this, eventArguments); } +#if PROGRESSION_STARTABLE + /// Fires the AsyncStarted event + /// + /// + /// This event should be fired once when the implementing class begins its + /// work to indicate any observers that the process is now running. + /// + /// + /// Calling this method is mandatory. Care should be taken that it is called + /// exactly once and that it is called before OnAsyncEnded(). + /// + /// + protected virtual void OnAsyncStarted() { + + // Make sure that the progression is not started more than once. + lock(this) { + + // No double lock here as it would be an implementation fault if this exception + // should be triggered. There's no sense in sacrificing normal runtime speed + // for improved performance in a case that should never occur in the first place. + if(this.started) + throw new InvalidOperationException("The operation has already been started"); + + this.started = true; + + } + + // Fire the AsyncStarted event + EventHandler copy = AsyncStarted; + if(copy != null) + copy(this, EventArgs.Empty); + + } +#endif + /// Fires the AsyncEnded event /// - /// This event should be fired by the implementing class when its work is completed. - /// It's of no interest to this class whether the outcome of the process was successfull - /// or not, the outcome and results of the process taking place need to be communicated - /// seperately. + /// + /// This event should be fired by the implementing class when its work is completed. + /// It's of no interest to this class whether the outcome of the process was + /// successfull or not, the outcome and results of the process taking place both + /// need to be communicated seperately. + /// + /// + /// Calling this method is mandatory. Implementers need to take care that + /// the OnAsyncEnded() method is called on any instance of Progression that's + /// being created. This method also must not be called more than once. + /// /// protected virtual void OnAsyncEnded() { - + // Make sure the progression is not ended more than once. By guaranteeing that // a progression can only be ended once, we allow users of this class to // skip some safeguards against notifications arriving twice. - lock(this.syncRoot) { + lock(this) { + // No double lock here, this is an exception that indicates an implementation + // error that will not be triggered under normal circumstances. We don't want + // to waste any effort optimizing the speed at which an implementation fault + // will be noticed. if(this.ended) throw new InvalidOperationException("The progression has already been ended"); @@ -158,14 +235,16 @@ namespace Nuclex.Support.Tracking { } - /// Used to synchronize multithreaded accesses to this object - private object syncRoot = new object(); /// Event that will be set when the progression is completed /// /// This event is will only be created when it is specifically asked for using /// the WaitHandle property. /// private volatile ManualResetEvent doneEvent; +#if PROGRESSION_STARTABLE + /// Whether the operation has been started yet + private volatile bool started; +#endif /// Whether the operation has completed yet private volatile bool ended; diff --git a/Source/WeakReference.cs b/Source/WeakReference.cs index 7adbbc9..302a029 100644 --- a/Source/WeakReference.cs +++ b/Source/WeakReference.cs @@ -1,3 +1,22 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2007 Nuclex Development Labs + +This library is free software; you can redistribute it and/or +modify it under the terms of the IBM Common Public License as +published by the IBM Corporation; either version 1.0 of the +License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +IBM Common Public License for more details. + +You should have received a copy of the IBM Common Public +License along with this library +*/ +#endregion using System; using System.Collections.Generic; using System.Runtime.Serialization;