| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- // -----------------------------------------------------------------------
- // <copyright file="Statistic.cs">
- // 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.Topology;
- using Animation.TriangleNet.Geometry;
- /// <summary>
- /// Gather mesh statistics.
- /// </summary>
- internal class Statistic
- {
- #region Static members
- /// <summary>
- /// Number of incircle tests performed.
- /// </summary>
- internal static long InCircleCount = 0;
- internal static long InCircleAdaptCount = 0;
- /// <summary>
- /// Number of counterclockwise tests performed.
- /// </summary>
- internal static long CounterClockwiseCount = 0;
- internal static long CounterClockwiseAdaptCount = 0;
- /// <summary>
- /// Number of 3D orientation tests performed.
- /// </summary>
- internal static long Orient3dCount = 0;
- /// <summary>
- /// Number of right-of-hyperbola tests performed.
- /// </summary>
- internal static long HyperbolaCount = 0;
- /// <summary>
- /// // Number of circumcenter calculations performed.
- /// </summary>
- internal static long CircumcenterCount = 0;
- /// <summary>
- /// Number of circle top calculations performed.
- /// </summary>
- internal static long CircleTopCount = 0;
- /// <summary>
- /// Number of vertex relocations.
- /// </summary>
- internal static long RelocationCount = 0;
- #endregion
- #region Properties
- double minEdge = 0;
- /// <summary>
- /// Gets the shortest edge.
- /// </summary>
- public double ShortestEdge { get { return minEdge; } }
- double maxEdge = 0;
- /// <summary>
- /// Gets the longest edge.
- /// </summary>
- public double LongestEdge { get { return maxEdge; } }
- //
- double minAspect = 0;
- /// <summary>
- /// Gets the shortest altitude.
- /// </summary>
- public double ShortestAltitude { get { return minAspect; } }
- double maxAspect = 0;
- /// <summary>
- /// Gets the largest aspect ratio.
- /// </summary>
- public double LargestAspectRatio { get { return maxAspect; } }
- double minArea = 0;
- /// <summary>
- /// Gets the smallest area.
- /// </summary>
- public double SmallestArea { get { return minArea; } }
- double maxArea = 0;
- /// <summary>
- /// Gets the largest area.
- /// </summary>
- public double LargestArea { get { return maxArea; } }
- double minAngle = 0;
- /// <summary>
- /// Gets the smallest angle.
- /// </summary>
- public double SmallestAngle { get { return minAngle; } }
- double maxAngle = 0;
- /// <summary>
- /// Gets the largest angle.
- /// </summary>
- public double LargestAngle { get { return maxAngle; } }
- int[] angleTable;
- /// <summary>
- /// Gets the angle histogram.
- /// </summary>
- public int[] AngleHistogram { get { return angleTable; } }
- int[] minAngles;
- /// <summary>
- /// Gets the min angles histogram.
- /// </summary>
- public int[] MinAngleHistogram { get { return minAngles; } }
- int[] maxAngles;
- /// <summary>
- /// Gets the max angles histogram.
- /// </summary>
- public int[] MaxAngleHistogram { get { return maxAngles; } }
- double meshArea = 0;
- /// <summary>
- /// Gets the total mesh area.
- /// </summary>
- public double MeshArea { get { return meshArea; } }
- #endregion
- #region Private methods
- private void GetAspectHistogram(Mesh mesh)
- {
- int[] aspecttable;
- double[] ratiotable;
- aspecttable = new int[16];
- ratiotable = new double[]
- {
- 1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 10.0, 15.0, 25.0, 50.0,
- 100.0, 300.0, 1000.0, 10000.0, 100000.0, 0.0
- };
- Otri tri = default(Otri);
- Vertex[] p = new Vertex[3];
- double[] dx = new double[3], dy = new double[3];
- double[] edgelength = new double[3];
- double triarea;
- double trilongest2;
- double triminaltitude2;
- double triaspect2;
- int aspectindex;
- int i, j, k;
- tri.orient = 0;
- foreach (var t in mesh.triangles)
- {
- tri.tri = t;
- p[0] = tri.Org();
- p[1] = tri.Dest();
- p[2] = tri.Apex();
- trilongest2 = 0.0;
- for (i = 0; i < 3; i++)
- {
- j = plus1Mod3[i];
- k = minus1Mod3[i];
- dx[i] = p[j].x - p[k].x;
- dy[i] = p[j].y - p[k].y;
- edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
- if (edgelength[i] > trilongest2)
- {
- trilongest2 = edgelength[i];
- }
- }
- //triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
- triarea = Math.Abs((p[2].x - p[0].x) * (p[1].y - p[0].y) -
- (p[1].x - p[0].x) * (p[2].y - p[0].y)) / 2.0;
- triminaltitude2 = triarea * triarea / trilongest2;
- triaspect2 = trilongest2 / triminaltitude2;
- aspectindex = 0;
- while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15))
- {
- aspectindex++;
- }
- aspecttable[aspectindex]++;
- }
- }
- #endregion
- static readonly int[] plus1Mod3 = { 1, 2, 0 };
- static readonly int[] minus1Mod3 = { 2, 0, 1 };
- /// <summary>
- /// Update statistics about the quality of the mesh.
- /// </summary>
- /// <param name="mesh"></param>
- public void Update(Mesh mesh, int sampleDegrees)
- {
- Point[] p = new Point[3];
- int k1, k2;
- int degreeStep;
- //sampleDegrees = 36; // sample every 5 degrees
- //sampleDegrees = 45; // sample every 4 degrees
- sampleDegrees = 60; // sample every 3 degrees
- double[] cosSquareTable = new double[sampleDegrees / 2 - 1];
- double[] dx = new double[3];
- double[] dy = new double[3];
- double[] edgeLength = new double[3];
- double dotProduct;
- double cosSquare;
- double triArea;
- double triLongest2;
- double triMinAltitude2;
- double triAspect2;
- double radconst = Math.PI / sampleDegrees;
- double degconst = 180.0 / Math.PI;
- // New angle table
- angleTable = new int[sampleDegrees];
- minAngles = new int[sampleDegrees];
- maxAngles = new int[sampleDegrees];
- for (int i = 0; i < sampleDegrees / 2 - 1; i++)
- {
- cosSquareTable[i] = Math.Cos(radconst * (i + 1));
- cosSquareTable[i] = cosSquareTable[i] * cosSquareTable[i];
- }
- for (int i = 0; i < sampleDegrees; i++)
- {
- angleTable[i] = 0;
- }
- minAspect = mesh.bounds.Width + mesh.bounds.Height;
- minAspect = minAspect * minAspect;
- maxAspect = 0.0;
- minEdge = minAspect;
- maxEdge = 0.0;
- minArea = minAspect;
- maxArea = 0.0;
- minAngle = 0.0;
- maxAngle = 2.0;
- meshArea = 0.0;
- bool acuteBiggest = true;
- bool acuteBiggestTri = true;
- double triMinAngle, triMaxAngle = 1;
- foreach (var tri in mesh.triangles)
- {
- triMinAngle = 0; // Min angle: 0 < a < 60 degress
- triMaxAngle = 1; // Max angle: 60 < a < 180 degress
- p[0] = tri.vertices[0];
- p[1] = tri.vertices[1];
- p[2] = tri.vertices[2];
- triLongest2 = 0.0;
- for (int i = 0; i < 3; i++)
- {
- k1 = plus1Mod3[i];
- k2 = minus1Mod3[i];
- dx[i] = p[k1].x - p[k2].x;
- dy[i] = p[k1].y - p[k2].y;
- edgeLength[i] = dx[i] * dx[i] + dy[i] * dy[i];
- if (edgeLength[i] > triLongest2)
- {
- triLongest2 = edgeLength[i];
- }
- if (edgeLength[i] > maxEdge)
- {
- maxEdge = edgeLength[i];
- }
- if (edgeLength[i] < minEdge)
- {
- minEdge = edgeLength[i];
- }
- }
- //triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
- triArea = Math.Abs((p[2].x - p[0].x) * (p[1].y - p[0].y) -
- (p[1].x - p[0].x) * (p[2].y - p[0].y));
- meshArea += triArea;
- if (triArea < minArea)
- {
- minArea = triArea;
- }
- if (triArea > maxArea)
- {
- maxArea = triArea;
- }
- triMinAltitude2 = triArea * triArea / triLongest2;
- if (triMinAltitude2 < minAspect)
- {
- minAspect = triMinAltitude2;
- }
- triAspect2 = triLongest2 / triMinAltitude2;
- if (triAspect2 > maxAspect)
- {
- maxAspect = triAspect2;
- }
- for (int i = 0; i < 3; i++)
- {
- k1 = plus1Mod3[i];
- k2 = minus1Mod3[i];
- dotProduct = dx[k1] * dx[k2] + dy[k1] * dy[k2];
- cosSquare = dotProduct * dotProduct / (edgeLength[k1] * edgeLength[k2]);
- degreeStep = sampleDegrees / 2 - 1;
- for (int j = degreeStep - 1; j >= 0; j--)
- {
- if (cosSquare > cosSquareTable[j])
- {
- degreeStep = j;
- }
- }
- if (dotProduct <= 0.0)
- {
- angleTable[degreeStep]++;
- if (cosSquare > minAngle)
- {
- minAngle = cosSquare;
- }
- if (acuteBiggest && (cosSquare < maxAngle))
- {
- maxAngle = cosSquare;
- }
- // Update min/max angle per triangle
- if (cosSquare > triMinAngle)
- {
- triMinAngle = cosSquare;
- }
- if (acuteBiggestTri && (cosSquare < triMaxAngle))
- {
- triMaxAngle = cosSquare;
- }
- }
- else
- {
- angleTable[sampleDegrees - degreeStep - 1]++;
- if (acuteBiggest || (cosSquare > maxAngle))
- {
- maxAngle = cosSquare;
- acuteBiggest = false;
- }
- // Update max angle for (possibly non-acute) triangle
- if (acuteBiggestTri || (cosSquare > triMaxAngle))
- {
- triMaxAngle = cosSquare;
- acuteBiggestTri = false;
- }
- }
- }
- // Update min angle histogram
- degreeStep = sampleDegrees / 2 - 1;
- for (int j = degreeStep - 1; j >= 0; j--)
- {
- if (triMinAngle > cosSquareTable[j])
- {
- degreeStep = j;
- }
- }
- minAngles[degreeStep]++;
- // Update max angle histogram
- degreeStep = sampleDegrees / 2 - 1;
- for (int j = degreeStep - 1; j >= 0; j--)
- {
- if (triMaxAngle > cosSquareTable[j])
- {
- degreeStep = j;
- }
- }
- if (acuteBiggestTri)
- {
- maxAngles[degreeStep]++;
- }
- else
- {
- maxAngles[sampleDegrees - degreeStep - 1]++;
- }
- acuteBiggestTri = true;
- }
- minEdge = Math.Sqrt(minEdge);
- maxEdge = Math.Sqrt(maxEdge);
- minAspect = Math.Sqrt(minAspect);
- maxAspect = Math.Sqrt(maxAspect);
- minArea *= 0.5;
- maxArea *= 0.5;
- if (minAngle >= 1.0)
- {
- minAngle = 0.0;
- }
- else
- {
- minAngle = degconst * Math.Acos(Math.Sqrt(minAngle));
- }
- if (maxAngle >= 1.0)
- {
- maxAngle = 180.0;
- }
- else
- {
- if (acuteBiggest)
- {
- maxAngle = degconst * Math.Acos(Math.Sqrt(maxAngle));
- }
- else
- {
- maxAngle = 180.0 - degconst * Math.Acos(Math.Sqrt(maxAngle));
- }
- }
- }
- /// <summary>
- /// Compute angle information for given triangle.
- /// </summary>
- /// <param name="triangle">The triangle to check.</param>
- /// <param name="data">Array of doubles (length 6).</param>
- /// <remarks>
- /// On return, the squared cosines of the minimum and maximum angle will
- /// be stored at position data[0] and data[1] respectively.
- /// If the triangle was obtuse, data[2] will be set to -1 and maximum angle
- /// is computed as (pi - acos(sqrt(data[1]))).
- /// </remarks>
- internal static void ComputeAngles(ITriangle triangle, double[] data)
- {
- double min = 0.0;
- double max = 1.0;
- var va = triangle.GetVertex(0);
- var vb = triangle.GetVertex(1);
- var vc = triangle.GetVertex(2);
- double dxa = vb.x - vc.x;
- double dya = vb.y - vc.y;
- double lena = dxa * dxa + dya * dya;
- double dxb = vc.x - va.x;
- double dyb = vc.y - va.y;
- double lenb = dxb * dxb + dyb * dyb;
- double dxc = va.x - vb.x;
- double dyc = va.y - vb.y;
- double lenc = dxc * dxc + dyc * dyc;
- // Dot products.
- double dota = data[0] = dxb * dxc + dyb * dyc;
- double dotb = data[1] = dxc * dxa + dyc * dya;
- double dotc = data[2] = dxa * dxb + dya * dyb;
- // Squared cosines.
- data[3] = (dota * dota) / (lenb * lenc);
- data[4] = (dotb * dotb) / (lenc * lena);
- data[5] = (dotc * dotc) / (lena * lenb);
- // The sign of the dot product will tell us, if the angle is
- // acute (value < 0) or obtuse (value > 0).
- bool acute = true;
- double cos, dot;
- for (int i = 0; i < 3; i++)
- {
- dot = data[i];
- cos = data[3 + i];
- if (dot <= 0.0)
- {
- if (cos > min)
- {
- min = cos;
- }
- if (acute && (cos < max))
- {
- max = cos;
- }
- }
- else
- {
- // Update max angle for (possibly non-acute) triangle
- if (acute || (cos > max))
- {
- max = cos;
- acute = false;
- }
- }
- }
- data[0] = min;
- data[1] = max;
- data[2] = acute ? 1.0 : -1.0;
- }
- }
- }
|