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
This commit is contained in:
Markus Ewald 2007-07-01 19:27:40 +00:00
parent 6d79fe3ebc
commit 1ae0c7de63
13 changed files with 511 additions and 30 deletions

View File

@ -162,6 +162,10 @@
<Name>SimpleRectanglePacker.Test</Name>
<DependentUpon>SimpleRectanglePacker.cs</DependentUpon>
</Compile>
<Compile Include="Source\Scheduling\AbortedException.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>AbortedException</Name>
</Compile>
<Compile Include="Source\Serialization\BinarySerializer.Test.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>BinarySerializer.Test</Name>
@ -179,6 +183,26 @@
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>IAbortable</Name>
</Compile>
<Compile Include="Source\SpatialPartitioning\BoundingRectangle.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>BoundingRectangle</Name>
</Compile>
<Compile Include="Source\SpatialPartitioning\RTree2.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>RTree2</Name>
</Compile>
<Compile Include="Source\SpatialPartitioning\RTreeLeaf2.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>RTreeLeaf2</Name>
</Compile>
<Compile Include="Source\SpatialPartitioning\RTreeNode2.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>RTreeNode2</Name>
</Compile>
<Compile Include="Source\SpatialPartitioning\SpatialIndex2.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>SpatialIndex2</Name>
</Compile>
<Compile Include="Source\Tracking\Internal\ObservedProgression.cs">
<XNAUseContentPipeline>false</XNAUseContentPipeline>
<Name>ObservedProgression</Name>

View File

@ -25,7 +25,8 @@ namespace Nuclex.Support.Collections {
/// <summary>Queue that dequeues items in order of their priority</summary>
public class UnintrusivePriorityQueue<ItemType, PriorityType>
: ICollection, IEnumerable<ItemType> where PriorityType : IComparable<PriorityType> {
: ICollection, IEnumerable<ItemType>
where PriorityType : IComparable<PriorityType> {
#region struct Entry
@ -39,7 +40,7 @@ namespace Nuclex.Support.Collections {
this.Item = item;
this.Priority = priority;
}
/// <summary>Item contained in this priority queue entry</summary>
public ItemType Item;
/// <summary>Priority assigned to this entry</summary>

View File

@ -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 {
/// <summary>Indicates that an operation has been forcefully aborted</summary>
/// <remarks>
/// This exception is the typical result of using AsyncAbort() on a running
/// background process.
/// </remarks>
[Serializable]
public class AbortedException : ApplicationException {
/// <summary>Initializes the exception</summary>
public AbortedException() { }
/// <summary>Initializes the exception with an error message</summary>
/// <param name="message">Error message describing the cause of the exception</param>
public AbortedException(string message) : base(message) { }
/// <summary>Initializes the exception as a followup exception</summary>
/// <param name="message">Error message describing the cause of the exception</param>
/// <param name="inner">Preceding exception that has caused this exception</param>
public AbortedException(string message, Exception inner) : base(message, inner) { }
/// <summary>Initializes the exception from its serialized state</summary>
/// <param name="info">Contains the serialized fields of the exception</param>
/// <param name="context">Additional environmental informations</param>
protected AbortedException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context
)
: base(info, context) { }
}
} // namespace Nuclex.Support.Scheduling

View File

@ -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 {
/// <summary>Interface for abortable processes</summary>
public interface IAbortable {
/// <summary>Aborts the running process. Can be called from any thread.</summary>
/// <remarks>
/// 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).
/// </remarks>
void AsyncAbort();
}
} // namespace Nuclex.Support.Tracking
} // namespace Nuclex.Support.Scheduling

View File

@ -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 {
/// <summary>Base class for observable operations running in the background</summary>
public abstract class Operation : Progression {
/// <summary>Executes the operation</summary>
/// <summary>Launches the background operation</summary>
public abstract void Start();
/// <summary>
/// Executes the operation synchronously, blocking the calling thread
/// </summary>
public virtual void Execute() {
Start();
WaitHandle.WaitOne();
}
}
} // namespace Nuclex.Support.Tracking
} // namespace Nuclex.Support.Scheduling

View File

@ -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

View File

@ -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 {
/// <summary>Two-dimensional bounding rectangle</summary>
internal struct BoundingRectangle {
/// <summary>Initializes a new two-dimensional bounding rectangle</summary>
/// <param name="min">Lesser coordinates of the bounding rectangle</param>
/// <param name="max">Greater coordinates of the bounding rectangle</param>
public BoundingRectangle(Vector2 min, Vector2 max) {
this.Min = min;
this.Max = max;
}
/// <summary>
/// Builds the smallest bounding rectangle that contains the two
/// specified bounding rectangle.
/// </summary>
/// <param name="original">One of the bounding rectangles to contain</param>
/// <param name="additional">One of the bounding rectangles to contain</param>
/// <returns>The resulting merged bounding rectangle</returns>
public static BoundingRectangle CreateMerged(
BoundingRectangle original, BoundingRectangle additional
) {
BoundingRectangle result;
CreateMerged(ref original, ref additional, out result);
return result;
}
/// <summary>
/// Builds the smallest bounding rectangle that contains the two
/// specified bounding rectangle.
/// </summary>
/// <param name="original">One of the bounding rectangles to contain</param>
/// <param name="additional">One of the bounding rectangles to contain</param>
/// <param name="result">The resulting merged bounding rectangle</param>
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);
}
/// <summary>Coordinates of the lesser side of the bounding rectangle</summary>
public Vector2 Min;
/// <summary>Coordinates of the greater side of the bounding rectangle</summary>
public Vector2 Max;
}
} // namespace Nuclex.Support.SpatialPartitioning

View File

@ -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 {
/// <summary>R-Tree for two-dimensional data</summary>
/// <remarks>
/// 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.
/// </remarks>
public partial class RTree2<ItemType> : SpatialIndex2 {
/// <summary>Variants of R-Tree behaviors this implementation can assume</summary>
public enum Variants {
/// <summary>
/// Insertions and deletions take linear time at the cost of degrading the
/// tree's overall performance.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
Linear,
/// <summary>
/// Insertions and deletions take quadratic time while keeping the tree's
/// overall performance at a reasonable level.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
Quadratic,
/// <summary>
/// Insertions and deletions vary in performance but the tree's overall
/// performance is kept high.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
RStar
}
}
} // namespace Nuclex.Geometry.SpatialPartitioning

View File

@ -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 {
/// <summary>Leaf of an R-Tree</summary>
internal class RTreeLeaf2<ItemType> {
public BoundingRectangle BoundingRectangle;
public ItemType Item;
}
} // namespace Nuclex.Support.SpatialPartitioning

View File

@ -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 {
/// <summary>Node in a two-dimensional R-Tree</summary>
/// <typeparam name="ItemType">Type of items that the R-Tree manages</typeparam>
internal class RTreeNode2<ItemType> {
/// <summary>Initializes a new R-Tree node</summary>
/// <param name="capacity">Number of items that can fit in the node</param>
public RTreeNode2(int capacity) {
this.leafs = new RTreeLeaf2<ItemType>[capacity];
}
/// <summary>Inserts an item into this node</summary>
/// <param name="item">Item to be inserted</param>
/// <param name="boundingRectangle">Bounding rectangle of the item</param>
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
);
}
/// <summary>The node's minimum bounding rectangle</summary>
/// <remarks>
/// This bounding rectangle is just large enough to contain all the items
/// belonging to this node and recursively all of its child nodes.
/// </remarks>
private BoundingRectangle boundingRectangle;
/// <summary>Leafs of this node</summary>
private RTreeLeaf2<ItemType>[] leafs;
/// <summary>Number of leafes in use</summary>
private int leafCount;
}
} // namespace Nuclex.Support.SpatialPartitioning

View File

@ -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 {
/// <summary>Interface for a 2D geometrical database</summary>
public abstract class SpatialIndex2 {
}
} // namespace Nuclex.Support.SpatialPartitioning

View File

@ -45,10 +45,13 @@ namespace Nuclex.Support.Tracking {
#region class EndedDummyProgression
/// <summary>Dummy progression which always is in the 'ended' state</summary>
internal class EndedDummyProgression : Progression {
private class EndedDummyProgression : Progression {
/// <summary>Initializes a new ended dummy progression</summary>
public EndedDummyProgression() {
#if PROGRESSION_STARTABLE
OnAsyncStarted();
#endif
OnAsyncEnded();
}
@ -67,14 +70,42 @@ namespace Nuclex.Support.Tracking {
/// <summary>will be triggered to report when progress has been achieved</summary>
public event EventHandler<ProgressUpdateEventArgs> AsyncProgressUpdated;
#if PROGRESSION_STARTABLE
/// <summary>Will be triggered when the progression has ended</summary>
public event EventHandler AsyncStarted;
#endif
/// <summary>Will be triggered when the progression has ended</summary>
public event EventHandler AsyncEnded;
#if PROGRESSION_STARTABLE
/// <summary>True when the progression has been started.</summary>
/// <remarks>
/// 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.
/// </remarks>
public bool Started {
get { return this.started; }
}
#endif
/// <summary>Whether the progression has ended already</summary>
public bool Ended {
get { return this.ended; }
}
#if PROGRESSION_STARTABLE
/// <summary>Whether the progression is currently executing.</summary>
/// <remarks>
/// 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).
/// </remarks>
public bool IsRunning {
get { return (this.started && !base.Ended); }
}
#endif
/// <summary>WaitHandle that can be used to wait for the progression to end</summary>
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
/// <summary>Fires the AsyncStarted event</summary>
/// <remarks>
/// <para>
/// This event should be fired once when the implementing class begins its
/// work to indicate any observers that the process is now running.
/// </para>
/// <para>
/// Calling this method is mandatory. Care should be taken that it is called
/// exactly once and that it is called before OnAsyncEnded().
/// </para>
/// </remarks>
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
/// <summary>Fires the AsyncEnded event</summary>
/// <remarks>
/// 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.
/// <para>
/// 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.
/// </para>
/// <para>
/// 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.
/// </para>
/// </remarks>
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 {
}
/// <summary>Used to synchronize multithreaded accesses to this object</summary>
private object syncRoot = new object();
/// <summary>Event that will be set when the progression is completed</summary>
/// <remarks>
/// This event is will only be created when it is specifically asked for using
/// the WaitHandle property.
/// </remarks>
private volatile ManualResetEvent doneEvent;
#if PROGRESSION_STARTABLE
/// <summary>Whether the operation has been started yet</summary>
private volatile bool started;
#endif
/// <summary>Whether the operation has completed yet</summary>
private volatile bool ended;

View File

@ -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;