using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Chernobyl.DesignPatterns.Extension;
using Chernobyl.Mathematics.Movement;
using Chernobyl.Utility;
namespace Chernobyl.Mathematics.Geometry
{
///
/// The default implementation of . This type is a
/// representation of a part of a curve that is bounded by two distinct
/// end points and contains every point on the curve between its end points.
/// The holds several instances
/// that represent points along the arc. The first
/// is the first end point and the last is the last
/// end point. instances are ordered in this
/// so that drawing an arc through these
/// points in the order specified by this
/// represents the actual arc. If an is stored at
/// the beginning of the and at the end of it
/// then the arc is a loop. If this has only
/// two points then it is a straight line segment.
///
public class Arc : IArc
{
///
/// Initializes a new instance of the class.
///
/// The points that make up this arc. The first
/// is the first end point and the last
/// is the last end point.
/// instances are ordered in this
/// so that drawing an arc through these
/// points in the order specified by this
/// represents the actual arc. If an is stored at
/// the beginning of the and at the end of it
/// then the arc is a loop. If this has only
/// two points then it is a straight line segment.
/// Thrown if the passed
/// in contains
/// less than two points.
public Arc(IEnumerable points) : this()
{
const int minimumPoints = 2;
if (points.Take(minimumPoints).Count() < minimumPoints)
throw new ArgumentOutOfRangeException("points", points,
"An arc can only be composed of at least 2 points.");
_points = points;
}
///
/// Initializes a new instance of the class.
///
/// The lines.
/// Thrown if the passed
/// in is empty.
public Arc(IEnumerable lines) : this()
{
if (lines.Any() == false)
throw new ArgumentOutOfRangeException("lines", lines,
"An arc can only be " +
"composed of at least 2 " +
"points.");
LinkedList lineList = new LinkedList(lines);
int pointCount = lineList.Count * 2; // each line has 2 points.
List points = new List(pointCount);
// We start with the first line segment.
LinkedListNode currentNode = lineList.First;
ITransform currentNodePoint = currentNode.Value.Point2;
ITransform siblingNodePoint = currentNode.Value.Point1;
points.Add(siblingNodePoint);
lineList.Remove(currentNode);
bool finished = false;
do
{
// Find the next line segment in the list.
LinkedListNode nextNode = null;
using (IEnumerator> nodeEnum = lineList.GetNodes().GetEnumerator())
{
while (nodeEnum.MoveNext() == true && nextNode == null)
{
// Now check this segment for any points that may equal
// the point we are currently on.
LinkedListNode potentialNext = nodeEnum.Current;
if (currentNodePoint.Position == potentialNext.Value.Point1.Position)
{
// Point1 connects to the previous line segment so
// this is the next line segment in line. We will
// now move to Point2 of this segment to find the
// next matching line segment.
nextNode = potentialNext;
currentNodePoint = potentialNext.Value.Point2;
siblingNodePoint = potentialNext.Value.Point1;
}
else if (currentNodePoint.Position == potentialNext.Value.Point2.Position)
{
// Point2 connects to the previous line segment so
// this is the next line segment in line. We will
// now move to Point1 of this segment to find the
// next matching line segment.
// Point1 of this segment is the next point in the
// arc.
nextNode = potentialNext;
currentNodePoint = potentialNext.Value.Point1;
siblingNodePoint = potentialNext.Value.Point2;
}
}
}
if (nextNode != null)
{
// The next line segment was found so the second point can
// be included in the point list.
points.Add(siblingNodePoint);
currentNode = nextNode;
nextNode = null;
// We are finished with this node and don't want it included
// in future searches.
lineList.Remove(currentNode);
}
else if (lineList.First != null)
{
// The next segment in the line could not be found but the
// list of nodes is not empty. That means this is not a
// continguous line.
throw new ArgumentException("An arc must contain continguous " +
"points. The set of line segments " +
"passed in is not contiguous.",
"lines");
}
// If there are no more nodes then we are at the end of the
// line. Add the 2nd point and we are done.
if (lineList.First == null)
{
points.Add(currentNodePoint);
finished = true;
}
} while (finished == false);
_points = points;
}
///
/// Default constructor that performs common initialition.
///
Arc()
{
Extensions = new List();
}
///
/// Returns an enumerator that iterates through the collection.
///
/// A that can be used to iterate
/// through the collection.
///
public IEnumerator GetEnumerator()
{
return _points.GetEnumerator();
}
///
/// Returns an enumerator that iterates through a collection.
///
/// An object that can be used to
/// iterate through the collection.
///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// The list of or
/// instances that have
/// attached themselves to this instance.
///
public IList Extensions { get; private set; }
///
/// The points that make up this arc. The first
/// is the first end point and the last is the
/// last end point. instances are ordered in
/// this so that drawing an arc through
/// these points in the order specified by this
/// represents the actual arc. If an is stored at
/// the beginning of the and at the end of it
/// then the arc is a loop. If this has only
/// two points then it is a straight line segment.
///
IEnumerable _points;
}
}