| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- // -----------------------------------------------------------------------
- // <copyright file="VertexSorter.cs" company="">
- // Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
- // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
- // </copyright>
- // -----------------------------------------------------------------------
- namespace UnityEngine.U2D.Animation.TriangleNet
- .Tools
- {
- using System;
- using Animation.TriangleNet.Geometry;
- /// <summary>
- /// Sort an array of points using quicksort.
- /// </summary>
- internal class VertexSorter
- {
- private const int RANDOM_SEED = 57113;
- Random rand;
- Vertex[] points;
- VertexSorter(Vertex[] points, int seed)
- {
- this.points = points;
- this.rand = new Random(seed);
- }
- /// <summary>
- /// Sorts the given vertex array by x-coordinate.
- /// </summary>
- /// <param name="array">The vertex array.</param>
- /// <param name="seed">Random seed used for pivoting.</param>
- internal static void Sort(Vertex[] array, int seed = RANDOM_SEED)
- {
- var qs = new VertexSorter(array, seed);
- qs.QuickSort(0, array.Length - 1);
- }
- /// <summary>
- /// Impose alternating cuts on given vertex array.
- /// </summary>
- /// <param name="array">The vertex array.</param>
- /// <param name="length">The number of vertices to sort.</param>
- /// <param name="seed">Random seed used for pivoting.</param>
- internal static void Alternate(Vertex[] array, int length, int seed = RANDOM_SEED)
- {
- var qs = new VertexSorter(array, seed);
- int divider = length >> 1;
- // Re-sort the array of vertices to accommodate alternating cuts.
- if (length - divider >= 2)
- {
- if (divider >= 2)
- {
- qs.AlternateAxes(0, divider - 1, 1);
- }
- qs.AlternateAxes(divider, length - 1, 1);
- }
- }
- #region Quicksort
- /// <summary>
- /// Sort an array of vertices by x-coordinate, using the y-coordinate as a secondary key.
- /// </summary>
- /// <param name="left"></param>
- /// <param name="right"></param>
- /// <remarks>
- /// Uses quicksort. Randomized O(n log n) time. No, I did not make any of
- /// the usual quicksort mistakes.
- /// </remarks>
- private void QuickSort(int left, int right)
- {
- int oleft = left;
- int oright = right;
- int arraysize = right - left + 1;
- int pivot;
- double pivotx, pivoty;
- Vertex temp;
- var array = this.points;
- if (arraysize < 32)
- {
- // Insertion sort
- for (int i = left + 1; i <= right; i++)
- {
- var a = array[i];
- int j = i - 1;
- while (j >= left && (array[j].x > a.x || (array[j].x == a.x && array[j].y > a.y)))
- {
- array[j + 1] = array[j];
- j--;
- }
- array[j + 1] = a;
- }
- return;
- }
- // Choose a random pivot to split the array.
- pivot = rand.Next(left, right);
- pivotx = array[pivot].x;
- pivoty = array[pivot].y;
- // Split the array.
- left--;
- right++;
- while (left < right)
- {
- // Search for a vertex whose x-coordinate is too large for the left.
- do
- {
- left++;
- }
- while ((left <= right) && ((array[left].x < pivotx) ||
- ((array[left].x == pivotx) && (array[left].y < pivoty))));
- // Search for a vertex whose x-coordinate is too small for the right.
- do
- {
- right--;
- }
- while ((left <= right) && ((array[right].x > pivotx) ||
- ((array[right].x == pivotx) && (array[right].y > pivoty))));
- if (left < right)
- {
- // Swap the left and right vertices.
- temp = array[left];
- array[left] = array[right];
- array[right] = temp;
- }
- }
- if (left > oleft)
- {
- // Recursively sort the left subset.
- QuickSort(oleft, left);
- }
- if (oright > right + 1)
- {
- // Recursively sort the right subset.
- QuickSort(right + 1, oright);
- }
- }
- #endregion
- #region Alternate axes
- /// <summary>
- /// Sorts the vertices as appropriate for the divide-and-conquer algorithm with
- /// alternating cuts.
- /// </summary>
- /// <param name="left"></param>
- /// <param name="right"></param>
- /// <param name="axis"></param>
- /// <remarks>
- /// Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1.
- /// For the base case, subsets containing only two or three vertices are
- /// always sorted by x-coordinate.
- /// </remarks>
- private void AlternateAxes(int left, int right, int axis)
- {
- int size = right - left + 1;
- int divider = size >> 1;
- if (size <= 3)
- {
- // Recursive base case: subsets of two or three vertices will be
- // handled specially, and should always be sorted by x-coordinate.
- axis = 0;
- }
- // Partition with a horizontal or vertical cut.
- if (axis == 0)
- {
- VertexMedianX(left, right, left + divider);
- }
- else
- {
- VertexMedianY(left, right, left + divider);
- }
- // Recursively partition the subsets with a cross cut.
- if (size - divider >= 2)
- {
- if (divider >= 2)
- {
- AlternateAxes(left, left + divider - 1, 1 - axis);
- }
- AlternateAxes(left + divider, right, 1 - axis);
- }
- }
- /// <summary>
- /// An order statistic algorithm, almost. Shuffles an array of vertices so that the
- /// first 'median' vertices occur lexicographically before the remaining vertices.
- /// </summary>
- /// <param name="left"></param>
- /// <param name="right"></param>
- /// <param name="median"></param>
- /// <remarks>
- /// Uses the x-coordinate as the primary key. Very similar to the QuickSort()
- /// procedure, but runs in randomized linear time.
- /// </remarks>
- private void VertexMedianX(int left, int right, int median)
- {
- int arraysize = right - left + 1;
- int oleft = left, oright = right;
- int pivot;
- double pivot1, pivot2;
- Vertex temp;
- var array = this.points;
- if (arraysize == 2)
- {
- // Recursive base case.
- if ((array[left].x > array[right].x) ||
- ((array[left].x == array[right].x) &&
- (array[left].y > array[right].y)))
- {
- temp = array[right];
- array[right] = array[left];
- array[left] = temp;
- }
- return;
- }
- // Choose a random pivot to split the array.
- pivot = rand.Next(left, right);
- pivot1 = array[pivot].x;
- pivot2 = array[pivot].y;
- left--;
- right++;
- while (left < right)
- {
- // Search for a vertex whose x-coordinate is too large for the left.
- do
- {
- left++;
- }
- while ((left <= right) && ((array[left].x < pivot1) ||
- ((array[left].x == pivot1) && (array[left].y < pivot2))));
- // Search for a vertex whose x-coordinate is too small for the right.
- do
- {
- right--;
- }
- while ((left <= right) && ((array[right].x > pivot1) ||
- ((array[right].x == pivot1) && (array[right].y > pivot2))));
- if (left < right)
- {
- // Swap the left and right vertices.
- temp = array[left];
- array[left] = array[right];
- array[right] = temp;
- }
- }
- // Unlike in vertexsort(), at most one of the following conditionals is true.
- if (left > median)
- {
- // Recursively shuffle the left subset.
- VertexMedianX(oleft, left - 1, median);
- }
- if (right < median - 1)
- {
- // Recursively shuffle the right subset.
- VertexMedianX(right + 1, oright, median);
- }
- }
- /// <summary>
- /// An order statistic algorithm, almost. Shuffles an array of vertices so that
- /// the first 'median' vertices occur lexicographically before the remaining vertices.
- /// </summary>
- /// <param name="left"></param>
- /// <param name="right"></param>
- /// <param name="median"></param>
- /// <remarks>
- /// Uses the y-coordinate as the primary key. Very similar to the QuickSort()
- /// procedure, but runs in randomized linear time.
- /// </remarks>
- private void VertexMedianY(int left, int right, int median)
- {
- int arraysize = right - left + 1;
- int oleft = left, oright = right;
- int pivot;
- double pivot1, pivot2;
- Vertex temp;
- var array = this.points;
- if (arraysize == 2)
- {
- // Recursive base case.
- if ((array[left].y > array[right].y) ||
- ((array[left].y == array[right].y) &&
- (array[left].x > array[right].x)))
- {
- temp = array[right];
- array[right] = array[left];
- array[left] = temp;
- }
- return;
- }
- // Choose a random pivot to split the array.
- pivot = rand.Next(left, right);
- pivot1 = array[pivot].y;
- pivot2 = array[pivot].x;
- left--;
- right++;
- while (left < right)
- {
- // Search for a vertex whose x-coordinate is too large for the left.
- do
- {
- left++;
- }
- while ((left <= right) && ((array[left].y < pivot1) ||
- ((array[left].y == pivot1) && (array[left].x < pivot2))));
- // Search for a vertex whose x-coordinate is too small for the right.
- do
- {
- right--;
- }
- while ((left <= right) && ((array[right].y > pivot1) ||
- ((array[right].y == pivot1) && (array[right].x > pivot2))));
- if (left < right)
- {
- // Swap the left and right vertices.
- temp = array[left];
- array[left] = array[right];
- array[right] = temp;
- }
- }
- // Unlike in QuickSort(), at most one of the following conditionals is true.
- if (left > median)
- {
- // Recursively shuffle the left subset.
- VertexMedianY(oleft, left - 1, median);
- }
- if (right < median - 1)
- {
- // Recursively shuffle the right subset.
- VertexMedianY(right + 1, oright, median);
- }
- }
- #endregion
- }
- }
|