/// <summary> /// Determines the location of the specified <see cref="PointI"/> coordinates relative to /// the <see cref="LineI"/>, assuming they are collinear.</summary> /// <param name="q"> /// The <see cref="PointI"/> coordinates to examine.</param> /// <returns> /// A <see cref="LineLocation"/> value indicating the location of <paramref name="q"/> /// relative to the <see cref="LineI"/>.</returns> /// <remarks> /// <b>LocateCollinear</b> is identical with <see cref="Locate"/> but assumes that <paramref /// name="q"/> is collinear with the <see cref="LineI"/>, and therefore never returns the /// values <see cref="LineLocation.Left"/> or <see cref="LineLocation.Right"/>.</remarks> public LineLocation LocateCollinear(PointI q) { int qx0 = q.X - Start.X, qy0 = q.Y - Start.Y; if (qx0 == 0 && qy0 == 0) { return(LineLocation.Start); } int qx1 = q.X - End.X, qy1 = q.Y - End.Y; if (qx1 == 0 && qy1 == 0) { return(LineLocation.End); } if (qx0 * qx1 <= 0 && qy0 * qy1 <= 0) { return(LineLocation.Between); } if ((End.X - Start.X) * qx0 < 0 || (End.Y - Start.Y) * qy0 < 0) { return(LineLocation.Before); } else { return(LineLocation.After); } }
/// <summary> /// Determines the squared distance between the <see cref="LineI"/> and the specified <see /// cref="PointI"/> coordinates.</summary> /// <param name="q"> /// The <see cref="PointI"/> coordinates to examine.</param> /// <returns> /// The squared distance between <paramref name="q"/> and the <see cref="LineI"/>.</returns> /// <remarks> /// <b>DistanceSquared</b> returns either the squared length of the perpendicular dropped /// from <paramref name="q"/> on the <see cref="LineI"/>, or the squared distance between /// <paramref name="q"/> and <see cref="Start"/> or <see cref="End"/> if the perpendicular /// does not intersect the <see cref="LineI"/>.</remarks> public double DistanceSquared(PointI q) { if (q == Start || q == End) { return(0); } double x = Start.X, y = Start.Y; int ax = End.X - Start.X, ay = End.Y - Start.Y; // set (x,y) to nearest LineI point from q if (ax != 0 || ay != 0) { double u = ((q.X - x) * ax + (q.Y - y) * ay) / (double)(ax * ax + ay * ay); if (u > 1) { x = End.X; y = End.Y; } else if (u > 0) { x += u * ax; y += u * ay; } } x = q.X - x; y = q.Y - y; return(x * x + y * y); }
/// <summary> /// Initializes a new instance of the <see cref="RectI"/> structure with the specified /// <see cref="PointI"/> coordinates and <see cref="SizeI"/> dimensions.</summary> /// <param name="location"> /// The <see cref="Location"/> of the <see cref="RectI"/>.</param> /// <param name="size"> /// The <see cref="Size"/> of the <see cref="RectI"/>.</param> public RectI(PointI location, SizeI size) { X = location.X; Y = location.Y; Width = size.Width; Height = size.Height; }
/// <summary> /// Finds the distance vector from the specified <see cref="PointI"/> coordinates to the /// nearest edges of the <see cref="RectI"/>.</summary> /// <param name="q"> /// The <see cref="PointI"/> coordinates to examine.</param> /// <returns> /// A <see cref="PointI"/> vector indicating the distance of each <paramref name="q"/> /// coordinate from the nearest corresponding edge of the <see cref="RectI"/>.</returns> /// <remarks><para> /// <b>GetDistanceVector</b> defines the components of the returned <see cref="PointI"/> /// vector as follows, assuming that <em>qx</em> and <em>qy</em> are the coordinates of /// <paramref name="q"/>: /// </para><list type="table"><listheader> /// <term><b>X</b></term><term><b>Y</b></term><description>Condition</description> /// </listheader><item> /// <term>0</term><term></term><description> /// <see cref="Left"/> <= <em>qx</em> <= <see cref="Right"/></description> /// </item><item> /// <term><em>qx</em> – <see cref="Left"/></term><term></term> /// <description><em>qx</em> < <see cref="Left"/></description> /// </item><item> /// <term><em>qx</em> – <see cref="Right"/></term><term></term> /// <description><em>qx</em> > <see cref="Right"/></description> /// </item><item> /// <term/><term>0</term><description> /// <see cref="Top"/> <= <em>qy</em> <= <see cref="Bottom"/></description> /// </item><item> /// <term/><term><em>qy</em> – <see cref="Top"/></term> /// <description><em>qy</em> < <see cref="Top"/></description> /// </item><item> /// <term/><term><em>qy</em> – <see cref="Bottom"/></term> /// <description><em>qy</em> > <see cref="Bottom"/></description> /// </item></list><para> /// Each vector component is zero exactly if the corresponding <paramref name="q"/> /// coordinate lies within the corresponding <see cref="RectI"/> extension. Otherwise, the /// component’s absolute value indicates the coordinate’s distance from the nearest <see /// cref="RectI"/> edge, and its sign indicates that edge itself.</para></remarks> public PointI GetDistanceVector(PointI q) { int qx = q.X - X, qy = q.Y - Y; int x = (qx <0 ? qx : qx> Width ? qx - Width : 0); int y = (qy <0 ? qy : qy> Height ? qy - Height : 0); return(new PointI(x, y)); }
/// <summary> /// Determines the location of the specified <see cref="PointI"/> coordinates relative to /// the <see cref="RectI"/>, including <see cref="Right"/> and <see cref="Bottom"/>. /// </summary> /// <param name="q"> /// The <see cref="PointI"/> coordinates to examine.</param> /// <returns> /// A <see cref="RectLocation"/> value indicating the location of <paramref name="q"/> /// relative to the <see cref="RectI"/>.</returns> /// <remarks> /// <b>LocateClosed</b> is identical with <see cref="Locate"/> but assumes that the <see /// cref="RectI"/> contains the <see cref="Right"/> and <see cref="Bottom"/> coordinates, /// emulating <see cref="RectD"/> and <see cref="RectF"/> behavior.</remarks> public RectLocation LocateClosed(PointI q) { int qx = q.X - X, qy = q.Y - Y; RectLocation x = (qx < 0 ? RectLocation.BeforeX : (qx == 0 ? RectLocation.StartX : (qx < Width ? RectLocation.InsideX : (qx == Width ? RectLocation.EndX : RectLocation.AfterX)))); RectLocation y = (qy < 0 ? RectLocation.BeforeY : (qy == 0 ? RectLocation.StartY : (qy < Height ? RectLocation.InsideY : (qy == Height ? RectLocation.EndY : RectLocation.AfterY)))); return(x | y); }
/// <summary> /// Determines the location of the specified <see cref="PointI"/> coordinates relative to /// the <see cref="LineI"/>.</summary> /// <param name="q"> /// The <see cref="PointI"/> coordinates to examine.</param> /// <returns> /// A <see cref="LineLocation"/> value indicating the location of <paramref name="q"/> /// relative to the <see cref="LineI"/>.</returns> /// <remarks><para> /// <b>Locate</b> never returns <see cref="LineLocation.None"/>. The values <see /// cref="LineLocation.Left"/> and <see cref="LineLocation.Right"/> assume that /// y-coordinates increase upward. /// </para><para> /// <b>Locate</b> is based on the <c>classify</c> algorithm by Michael J. Laszlo, /// <em>Computational Geometry and Computer Graphics in C++</em>, Prentice Hall 1996, p.76. /// </para></remarks> public LineLocation Locate(PointI q) { int qx0 = q.X - Start.X, qy0 = q.Y - Start.Y; if (qx0 == 0 && qy0 == 0) { return(LineLocation.Start); } int qx1 = q.X - End.X, qy1 = q.Y - End.Y; if (qx1 == 0 && qy1 == 0) { return(LineLocation.End); } int ax = End.X - Start.X, ay = End.Y - Start.Y; int area = ax * qy0 - qx0 * ay; if (area > 0) { return(LineLocation.Left); } if (area < 0) { return(LineLocation.Right); } if (qx0 * qx1 <= 0 && qy0 * qy1 <= 0) { return(LineLocation.Between); } if (ax * qx0 < 0 || ay * qy0 < 0) { return(LineLocation.Before); } else { return(LineLocation.After); } }
/// <summary> /// Finds the intersection between the <see cref="LineI"/> and the perpendicular dropped /// from the specified <see cref="PointI"/> coordinates.</summary> /// <param name="q"> /// The <see cref="PointI"/> coordinates to examine.</param> /// <returns> /// The <see cref="PointD"/> coordinates where the perpendicular dropped from <paramref /// name="q"/> intersects the <see cref="LineI"/> or its infinite extension.</returns> /// <remarks> /// <b>Intersect</b> returns <see cref="Start"/> if <see cref="Length"/> equals zero. /// </remarks> public PointD Intersect(PointI q) { if (q == Start) { return(Start); } if (q == End) { return(End); } int x = Start.X, y = Start.Y; int ax = End.X - x, ay = End.Y - y; if (ax == 0 && ay == 0) { return(Start); } double u = ((q.X - x) * ax + (q.Y - y) * ay) / (double)(ax * ax + ay * ay); return(new PointD(x + u * ax, y + u * ay)); }
/// <summary> /// Converts the specified <see cref="PointI"/> to a GDI <see cref="Point"/>.</summary> /// <param name="point"> /// The <see cref="PointI"/> instance to convert.</param> /// <returns> /// A new GDI <see cref="Point"/> instance whose coordinates equal those of the specified /// <paramref name="point"/>.</returns> public static Point ToGdiPoint(this PointI point) { return(new Point(point.X, point.Y)); }
/// <summary> /// Initializes a new instance of the <see cref="LineI"/> structure with the specified start /// and end coordinates.</summary> /// <param name="start"> /// The <see cref="Start"/> point of the <see cref="LineI"/>.</param> /// <param name="end"> /// The <see cref="End"/> point of the <see cref="LineI"/>.</param> public LineI(PointI start, PointI end) { Start = start; End = end; }
/// <overloads> /// Initializes a new instance of the <see cref="LineI"/> structure.</overloads> /// <summary> /// Initializes a new instance of the <see cref="LineI"/> structure with the specified start /// and end coordinates.</summary> /// <param name="startX"> /// The <see cref="PointI.X"/> coordinate of the <see cref="Start"/> point.</param> /// <param name="startY"> /// The <see cref="PointI.Y"/> coordinate of the <see cref="Start"/> point.</param> /// <param name="endX"> /// The <see cref="PointI.X"/> coordinate of the <see cref="End"/> point.</param> /// <param name="endY"> /// The the <see cref="PointI.Y"/> coordinate of the <see cref="End"/> point.</param> public LineI(int startX, int startY, int endX, int endY) { Start = new PointI(startX, startY); End = new PointI(endX, endY); }
/// <summary> /// Moves the <see cref="RectI"/> by the specified <see cref="PointI"/> vector.</summary> /// <param name="vector"> /// A <see cref="PointI"/> value whose components define the horizontal and vertical offset /// applied to the <see cref="RectI"/>.</param> /// <returns> /// A new <see cref="RectI"/> with the same <see cref="Size"/> as this instance, and whose /// <see cref="Location"/> is offset by the specified <paramref name="vector"/>.</returns> public RectI Offset(PointI vector) { return(new RectI(Location + vector, Size)); }
/// <summary> /// Indicates whether the <see cref="RectI"/> contains the specified <see cref="PointI"/> /// coordinates, including <see cref="Right"/> and <see cref="Bottom"/>.</summary> /// <param name="point"> /// The <see cref="PointI"/> to examine.</param> /// <returns> /// <c>true</c> if the <see cref="RectI"/> contains the specified <paramref name="point"/>, /// including <see cref="Right"/> and <see cref="Bottom"/>; otherwise, <c>false</c>. /// </returns> /// <remarks> /// <b>ContainsClosed</b> assumes that the <see cref="RectI"/> contains the <see /// cref="Right"/> and <see cref="Bottom"/> coordinates, emulating <see cref="RectD"/> and /// <see cref="RectF"/> behavior.</remarks> public bool ContainsClosed(PointI point) { return(ContainsClosed(point.X, point.Y)); }
/// <summary> /// Indicates whether the <see cref="RectI"/> contains the specified <see cref="PointI"/> /// coordinates.</summary> /// <param name="point"> /// The <see cref="PointI"/> to examine.</param> /// <returns> /// <c>true</c> if the <see cref="RectI"/> contains the specified <paramref name="point"/>; /// otherwise, <c>false</c>.</returns> /// <remarks> /// <b>Contains</b> assumes that the <see cref="RectI"/> does not contain the <see /// cref="Right"/> and <see cref="Bottom"/> coordinates.</remarks> public bool Contains(PointI point) { return(Contains(point.X, point.Y)); }
/// <summary> /// Creates the regions of the Voronoi diagram.</summary> /// <remarks> /// <b>CreateRegions</b> stores its output in the <see cref="VoronoiRegions"/> property. /// Please see there for details.</remarks> private void CreateRegions() { /* * 1. Create list of unsorted edges for each region * ================================================ * First we accumulate the raw material for each Voronoi region. * All edges are stored as indices into VoronoiVertices here. */ var listRegions = new LinkedList <PointI> [GeneratorSites.Length]; for (int i = 0; i < listRegions.Length; i++) { listRegions[i] = new LinkedList <PointI>(); } foreach (VoronoiEdge edge in VoronoiEdges) { PointI vertex = new PointI(edge.Vertex1, edge.Vertex2); listRegions[edge.Site1].AddLast(vertex); listRegions[edge.Site2].AddLast(vertex); } /* * 2. Sort and complete list of edges for each region * ================================================== * Sort each edge list so that an edge’s second vertex index equals * the first vertex index of the subsequent edge in the region list. * We may need to swap an edge’s vertex indices to allow this connection. * * Because all Voronoi edges are terminated with a pseudo-vertex where they * intersect the clipping rectangle, a Voronoi region may appear as several * internally connected, but mutually disconnected series of edges. To get * a single connected list, we must then insert pseudo-edges that span either * a border or even a corner of the clipping rectangle. */ for (int i = 0; i < listRegions.Length; i++) { var candidates = listRegions[i]; var listRegion = new LinkedList <PointI>(); // start with first unsorted edge listRegion.AddFirst(candidates.First.Value); candidates.RemoveFirst(); var candidate = candidates.First; bool wasEdgeAdded = false; while (candidate != null) { // save next candidate before removing current one var nextCandidate = candidate.Next; PointI vertex = candidate.Value; for (var node = listRegion.First; node != null; node = node.Next) { Debug.Assert(vertex != node.Value); // all vertices are distinct // invert edges with out-of-order indices if (vertex.X == node.Value.X || vertex.Y == node.Value.Y) { vertex = new PointI(vertex.Y, vertex.X); } // move preceding edge to sorted list if (vertex.Y == node.Value.X) { candidates.Remove(candidate); listRegion.AddBefore(node, vertex); wasEdgeAdded = true; break; } // move succeeding edge to sorted list if (node.Value.Y == vertex.X) { candidates.Remove(candidate); listRegion.AddAfter(node, vertex); wasEdgeAdded = true; break; } } // try next unsorted edge candidate = nextCandidate; if (candidate == null && candidates.Count > 0) { // connection across border or corner required if (!wasEdgeAdded) { ConnectCandidates(candidates, listRegion); } // start over with first candidate node candidate = candidates.First; wasEdgeAdded = false; } } // replace unsorted with sorted list listRegions[i] = listRegion; } /* * 3. Transform index list into polygon for each region * ==================================================== * We now have sorted lists containing all edges of each Voronoi region. * For closed (interior) regions, the last edge connects to the first, * and we store exactly one vertex per edge (we always choose the first). * * For open (exterior) regions, the first edge begins and the last edge ends * with two different pseudo-vertices. We must now close them in the same way * in which we connected separate sublists in step 2. * * That is, we add one or more pseudo-edges that connect the outer vertices * of the list across a border or corner of the clipping rectangle. Unlike * step 2, we may need to extend the connection across two corners. */ _voronoiRegions = new PointD[GeneratorSites.Length][]; for (int i = 0; i < listRegions.Length; i++) { LinkedList <PointI> listRegion = listRegions[i]; int firstIndex = listRegion.First.Value.X; int lastIndex = listRegion.Last.Value.Y; if (firstIndex != lastIndex) { // extend region to last pseudo-vertex listRegion.AddLast(new PointI(lastIndex, Int32.MinValue)); PointD firstVertex = GetVertex(firstIndex); PointD lastVertex = GetVertex(lastIndex); // check if pseudo-vertices span one or two corners of clipping region if (firstVertex.X != lastVertex.X && firstVertex.Y != lastVertex.Y) { CloseCornerRegion(listRegion, firstVertex, lastVertex); } } PointD[] region = new PointD[listRegion.Count]; _voronoiRegions[i] = region; // store coordinates for first vertex of each edge int j = 0; for (var edge = listRegion.First; edge != null; edge = edge.Next, j++) { region[j] = GetVertex(edge.Value.X); } } }