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