15using System.Collections.Generic;
17using Microsoft.Xna.Framework;
19#pragma warning disable CS1591
55 public Vertices(
int capacity) : base(capacity) { }
61 public List<Vertices>
Holes {
get;
set; }
70 for (
int i = 0; i < Count; i++)
76 for (
int i = 0; i <
Holes.Count; i++)
79 for (
int j = 0; j <
Holes[i].Count; j++)
81 temp[i] =
Holes[i][j].Transform(transform);
95 private static int[,]
_closePixels =
new[,] { { -1, -1 }, { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 } };
180 else if (value < 0.9f)
195 Initialize(
null,
null,
null,
null,
null,
null,
null,
null);
199 bool? holeDetection,
bool? multipartDetection,
bool? pixelOffsetOptimization,
Matrix? transform)
201 Initialize(
null,
null, alphaTolerance, hullTolerance, holeDetection,
202 multipartDetection, pixelOffsetOptimization, transform);
207 Initialize(data, width,
null,
null,
null,
null,
null,
null);
211 float? hullTolerance,
bool? holeDetection,
bool? multipartDetection,
212 bool? pixelOffsetOptimization,
Matrix? transform)
214 Initialize(data, width, alphaTolerance, hullTolerance, holeDetection,
215 multipartDetection, pixelOffsetOptimization, transform);
219 #region Initialization
220 private void Initialize(uint[] data,
int? width,
byte? alphaTolerance,
221 float? hullTolerance,
bool? holeDetection,
bool? multipartDetection,
222 bool? pixelOffsetOptimization,
Matrix? transform)
224 if (data !=
null && !width.HasValue)
225 throw new ArgumentNullException(
"width",
"'width' can't be null if 'data' is set.");
227 if (data ==
null && width.HasValue)
228 throw new ArgumentNullException(
"data",
"'data' can't be null if 'width' is set.");
230 if (data !=
null && width.HasValue)
233 if (alphaTolerance.HasValue)
238 if (hullTolerance.HasValue)
243 if (holeDetection.HasValue)
248 if (multipartDetection.HasValue)
253 if (pixelOffsetOptimization.HasValue)
258 if (transform.HasValue)
268 throw new ArgumentNullException(
"data",
"'data' can't be null.");
271 throw new ArgumentOutOfRangeException(
"data",
"'data' length can't be less then 4. Your texture must be at least 2 x 2 pixels in size.");
274 throw new ArgumentOutOfRangeException(
"width",
"'width' can't be less then 2. Your texture must be at least 2 x 2 pixels in size.");
276 if (data.Length % width != 0)
277 throw new ArgumentException(
"'width' has an invalid value.");
297 return detectedVerticesList[0];
307 public static List<Vector>
DetectVertices(uint[] data,
int width,
bool holeDetection)
317 return detectedVerticesList[0];
330 public static List<List<Vector>>
DetectVertices(uint[] data,
int width,
float hullTolerance,
byte alphaTolerance,
bool multiPartDetection,
bool holeDetection)
342 List<List<Vector>> result =
new List<List<Vector>>();
344 for (
int i = 0; i < detectedVerticesList.Count; i++)
346 result.Add(detectedVerticesList[i]);
354 #region Check TextureConverter setup.
357 throw new Exception(
"'_data' can't be null. You have to use SetTextureData(uint[] data, int width) before calling this method.");
359 if (
_data.Length < 4)
360 throw new Exception(
"'_data' length can't be less then 4. Your texture must be at least 2 x 2 pixels in size. " +
361 "You have to use SetTextureData(uint[] data, int width) before calling this method.");
364 throw new Exception(
"'_width' can't be less then 2. Your texture must be at least 2 x 2 pixels in size. " +
365 "You have to use SetTextureData(uint[] data, int width) before calling this method.");
368 throw new Exception(
"'_width' has an invalid value. You have to use SetTextureData(uint[] data, int width) before calling this method.");
372 List<Vertices> detectedPolygons =
new List<Vertices>();
374 Vector? holeEntrance =
null;
375 Vector? polygonEntrance =
null;
377 List<Vector> blackList =
new List<Vector>();
383 if (detectedPolygons.Count == 0)
388 if (polygon.Count > 2)
391 else if (polygonEntrance.HasValue)
402 if (polygon.Count > 2)
410 if (holeEntrance.HasValue)
412 if (!blackList.Contains(holeEntrance.Value))
414 blackList.Add(holeEntrance.Value);
417 if (holePolygon !=
null && holePolygon.Count > 2)
424 holePolygon.Add(holePolygon[0]);
426 int vertex1Index, vertex2Index;
427 if (
SplitPolygonEdge(polygon, holeEntrance.Value, out vertex1Index, out vertex2Index))
428 polygon.InsertRange(vertex2Index, holePolygon);
433 if (polygon.
Holes ==
null)
434 polygon.
Holes =
new List<Vertices>();
436 polygon.
Holes.Add(holePolygon);
450 detectedPolygons.Add(polygon);
461 if (detectedPolygons ==
null || (detectedPolygons !=
null && detectedPolygons.Count == 0))
462 throw new Exception(
"Couldn't detect any vertices.");
471 List<List<Vector>> temp =
new List<List<Vector>>();
472 for (
int i = 0; i < detectedPolygons.Count; i++)
474 temp.Add(
new List<Vector>());
475 for (
int j = 0; j < detectedPolygons[i].Count; j++)
477 temp[i].Add(
new Vector(detectedPolygons[i][j].
X, detectedPolygons[i][j].
Y));
486 for (
int i = 0; i < detectedPolygons.Count; i++)
488 detectedPolygons[i].Reverse();
490 if (detectedPolygons[i].Holes !=
null && detectedPolygons[i].Holes.Count > 0)
492 for (
int j = 0; j < detectedPolygons[i].Holes.Count; j++)
493 detectedPolygons[i].Holes[j].Reverse();
500 for (
int i = 0; i < detectedPolygons.Count; i++)
504 #region Data[] functions
521 if (x >= 0 && x < _width && y >= 0 && y <
_height)
539 return (coord.X >= 0f && coord.X <
_width && coord.Y >= 0f && coord.Y <
_height);
552 throw new ArgumentNullException(
"'polygon' can't be null.");
554 if (polygon.Count < 3)
555 throw new ArgumentException(
"'polygon.MainPolygon.Count' can't be less then 3.");
558 List<double> xCoords;
566 bool foundTransparent;
569 if (lastHoleEntrance.HasValue)
572 startY = (int)lastHoleEntrance.Value.
Y;
583 if (startY > 0 && startY < _height && endY > 0 && endY <
_height)
586 for (
int y = startY; y <= endY; y++)
595 if (xCoords.Count > 1 && xCoords.Count % 2 == 0)
601 for (
int i = 0; i < xCoords.Count; i += 2)
604 foundTransparent =
false;
607 for (
int x = (
int)xCoords[i]; x <= (int)xCoords[i + 1]; x++)
622 if (!foundTransparent)
628 if (foundSolid && foundTransparent)
630 entrance =
new Vector(lastSolid, y);
642 foundTransparent =
true;
649 if (xCoords.Count % 2 == 0)
650 Debug.WriteLine(
"SearchCrossingEdges() % 2 != 0");
661 throw new ArgumentNullException(
"polygon",
"'polygon' can't be null.");
663 if (polygon.Count < 3)
664 throw new ArgumentException(
"'polygon.MainPolygon.Count' can't be less then 3.");
669 if (polygon.
Holes !=
null)
671 for (
int i = 0; i < polygon.
Holes.Count; i++)
690 throw new ArgumentNullException(
"polygon",
"'polygon' can't be null.");
692 if (polygon.Count < 3)
693 throw new ArgumentException(
"'polygon.Count' can't be less then 3.");
696 Vector edgeVertex2 = polygon[polygon.Count - 1];
701 for (
int i = 0; i < polygon.Count; i++)
703 edgeVertex1 = polygon[i];
708 edgeVertex2 = polygon[i];
715 for (
int i = 0; i < polygon.Count; i++)
717 edgeVertex1 = polygon[i];
722 edgeVertex2 = polygon[i];
737 if (xCoords.Count > 0 && xCoords.Count % 2 == 0)
739 for (
int i = 0; i < xCoords.Count; i += 2)
741 if (xCoords[i] <= point.
X && xCoords[i + 1] >= point.
X)
754 double topMostValue =
double.MaxValue;
757 for (
int i = 0; i < vertices.Count; i++)
759 if (topMostValue > vertices[i].
Y)
761 topMostValue = vertices[i].Y;
762 topMost = vertices[i];
771 double returnValue =
double.MaxValue;
773 for (
int i = 0; i < vertices.Count; i++)
775 if (returnValue > vertices[i].
Y)
777 returnValue = vertices[i].Y;
786 double returnValue =
double.MinValue;
788 for (
int i = 0; i < vertices.Count; i++)
790 if (returnValue < vertices[i].
Y)
792 returnValue = vertices[i].Y;
802 throw new ArgumentNullException(
"polygon",
"'polygon' can't be null.");
804 if (polygon.Count < 3)
805 throw new ArgumentException(
"'polygon.MainPolygon.Count' can't be less then 3.");
809 if (polygon.
Holes !=
null)
811 for (
int i = 0; i < polygon.
Holes.Count; i++)
833 List<double> edges =
new List<double>();
846 if (polygon.Count > 2)
851 vertex2 = polygon[polygon.Count - 1];
854 for (
int i = 0; i < polygon.Count; i++)
856 vertex1 = polygon[i];
859 if ((vertex1.
Y >= y && vertex2.
Y <= y) ||
860 (vertex1.
Y <= y && vertex2.
Y >= y))
863 if (vertex1.
Y != vertex2.
Y)
866 slope = vertex2 - vertex1;
872 nextVertex = polygon[(i + 1) % polygon.Count];
873 nextSlope = vertex1 - nextVertex;
879 addFind = (nextSlope.
Y <= 0);
881 addFind = (nextSlope.
Y >= 0);
885 edges.Add((y - vertex1.Y) / slope.
Y * slope.
X + vertex1.X);
901 int nearestEdgeVertex1Index = 0;
902 int nearestEdgeVertex2Index = 0;
903 bool edgeFound =
false;
905 double shortestDistance =
float.MaxValue;
907 bool edgeCoordFound =
false;
915 foundEdgeCoord.
Y = coordInsideThePolygon.
Y;
917 if (xCoords !=
null && xCoords.Count > 1 && xCoords.Count % 2 == 0)
920 for (
int i = 0; i < xCoords.Count; i++)
922 if (xCoords[i] < coordInsideThePolygon.
X)
924 distance = coordInsideThePolygon.
X - xCoords[i];
926 if (distance < shortestDistance)
928 shortestDistance = distance;
929 foundEdgeCoord.
X = xCoords[i];
931 edgeCoordFound =
true;
938 shortestDistance =
float.MaxValue;
940 int edgeVertex2Index = polygon.Count - 1;
942 int edgeVertex1Index;
943 for (edgeVertex1Index = 0; edgeVertex1Index < polygon.Count; edgeVertex1Index++)
945 Vector tempVector1 = polygon[edgeVertex1Index];
946 Vector tempVector2 = polygon[edgeVertex2Index];
948 ref tempVector1, ref tempVector2);
949 if (distance < shortestDistance)
951 shortestDistance = distance;
953 nearestEdgeVertex1Index = edgeVertex1Index;
954 nearestEdgeVertex2Index = edgeVertex2Index;
959 edgeVertex2Index = edgeVertex1Index;
964 slope = polygon[nearestEdgeVertex2Index] - polygon[nearestEdgeVertex1Index];
967 Vector tempVector = polygon[nearestEdgeVertex1Index];
970 vertex1Index = nearestEdgeVertex1Index;
971 vertex2Index = nearestEdgeVertex1Index + 1;
973 polygon.Insert(nearestEdgeVertex1Index, distance * slope + polygon[vertex1Index]);
974 polygon.Insert(nearestEdgeVertex1Index, distance * slope + polygon[vertex2Index]);
992 bool entranceFound =
false;
993 bool endOfHull =
false;
1001 #region Entrance check
1010 current =
new Vector(entrance.X - 1f, entrance.Y);
1020 entranceFound =
true;
1028 entranceFound =
true;
1032 entranceFound =
false;
1042 polygon.Add(entrance);
1043 hullArea.Add(entrance);
1056 if (endOfHullArea.Contains(outstanding))
1059 polygon.Add(outstanding);
1068 polygon.Add(outstanding);
1069 hullArea.RemoveRange(0, hullArea.IndexOf(outstanding));
1088 if (next == entrance && !endOfHull)
1092 endOfHullArea.AddRange(hullArea);
1095 if (endOfHullArea.Contains(entrance))
1096 endOfHullArea.Remove(entrance);
1112 if (!searchingForSolidPixel ^
IsSolid(ref x, ref y))
1114 foundPixel =
new Vector(x, y);
1131 if (x >= 0 && x <= _width && y >= 0 && y <=
_height)
1133 if (x == (
int)near.X && y == (int)near.Y)
1146 for (
int y = 0; y <=
_height; y++)
1148 for (
int x = 0; x <=
_width; x++)
1152 entrance =
new Vector(x, y);
1174 bool foundTransparent =
false;
1175 bool inPolygon =
false;
1181 if (foundTransparent)
1187 for (
int polygonIdx = 0; polygonIdx < detectedPolygons.Count; polygonIdx++)
1189 if (
InPolygon(detectedPolygons[polygonIdx], entrance.Value))
1197 foundTransparent =
false;
1203 foundTransparent =
true;
1216 int indexOfPixelToCheck;
1222 x = (int)current.X +
_closePixels[indexOfPixelToCheck, 0];
1223 y = (
int)current.Y +
_closePixels[indexOfPixelToCheck, 1];
1225 if (x >= 0 && x < _width && y >= 0 && y <=
_height)
1244 if (hullArea.Count > 2)
1246 int hullAreaLastPoint = hullArea.Count - 1;
1249 Vector tempVector2 = hullArea[0];
1250 Vector tempVector3 = hullArea[hullAreaLastPoint];
1253 for (
int i = 1; i < hullAreaLastPoint; i++)
1255 tempVector1 = hullArea[i];
1260 outstandingResult = hullArea[i];
1267 outstanding = outstandingResult;
1283 switch ((
int)(current.X - last.X))
1286 switch ((
int)(current.Y - last.Y))
1300 switch ((
int)(current.Y - last.Y))
1311 switch ((
int)(current.Y - last.Y))
1334 Vector w = point - start;
1345 Vector pointOnLine = start + v * b;
void Transform(ref Matrix transform)
Transforms the polygon using the defined matrix.
Vertices(IEnumerable< Vector > vertices)
Muuttaa tekstuurin yhdeksi tai useammaksi listaksi verteksejä. Mahdollistaa myös reikien sisällyttämi...
bool _pixelOffsetOptimization
Matrix Transform
Can be used for scaling.
bool IsSolid(ref Vector v)
TextureToShapeConverter()
static double DistanceBetweenPointAndLineSegment(ref Vector point, ref Vector start, ref Vector end)
static List< Vector > DetectVertices(uint[] data, int width, bool holeDetection)
Detects the vertices of the supplied texture data.
float HullTolerance
Default is 1.5f.
bool DistanceToHullAcceptableHoles(Vertices polygon, Vector point, bool higherDetail)
VerticesDetectionType _polygonDetectionType
Vector? SearchHoleEntrance(Vertices polygon, Vector? lastHoleEntrance)
Function to search for an entrance point of a hole in a polygon. It searches the polygon from top to ...
static int[,] _closePixels
This array is ment to be readonly. It's not because it is accessed very frequently.
TextureToShapeConverter(uint[] data, int width, byte? alphaTolerance, float? hullTolerance, bool? holeDetection, bool? multipartDetection, bool? pixelOffsetOptimization, Matrix? transform)
bool SearchNearPixels(bool searchingForSolidPixel, ref Vector current, out Vector foundPixel)
void Initialize(uint[] data, int? width, byte? alphaTolerance, float? hullTolerance, bool? holeDetection, bool? multipartDetection, bool? pixelOffsetOptimization, Matrix? transform)
void ApplyTriangulationCompatibleWinding(ref List< Vertices > detectedPolygons)
double GetBottomMostCoord(Vertices vertices)
int GetIndexOfFirstPixelToCheck(ref Vector last, ref Vector current)
bool HoleDetection
Will detect texture 'holes' if set to true. Slows down the detection. Default is false.
bool SearchForOutstandingVertex(Vertices hullArea, out Vector outstanding)
List< List< Vector > > DetectVertices()
bool InPolygon(Vertices polygon, Vector point)
double GetTopMostCoord(Vertices vertices)
bool SearchHullEntrance(out Vector entrance)
Vertices CreateSimplePolygon(Vector entrance, Vector last)
byte AlphaTolerance
Alpha (coverage) tolerance. Default is 20: Every pixel with a coverage value equal or greater to 20 w...
Vector? GetTopMostVertex(Vertices vertices)
static List< Vector > DetectVertices(uint[] data, int width)
Detects the vertices of the supplied texture data. (PolygonDetectionType.Integrated)
void ApplyTransform(ref List< Vertices > detectedPolygons)
bool IsSolid(ref int x, ref int y)
void SetTextureData(uint[] data, int width)
TextureToShapeConverter(byte? alphaTolerance, float? hullTolerance, bool? holeDetection, bool? multipartDetection, bool? pixelOffsetOptimization, Matrix? transform)
bool MultipartDetection
Will detect texture multiple 'solid' isles if set to true. Slows down the detection....
List< double > SearchCrossingEdges(Vertices polygon, int y)
Searches the polygon for the x coordinates of the edges that cross the specified y coordinate.
bool SearchNextHullEntrance(List< Vertices > detectedPolygons, Vector start, out Vector? entrance)
Searches for the next shape.
VerticesDetectionType PolygonDetectionType
Get or set the polygon detection type.
bool DistanceToHullAcceptable(Vertices polygon, Vector point, bool higherDetail)
List< double > SearchCrossingEdgesHoles(Vertices polygon, int y)
const int ClosepixelsLength
bool InBounds(ref Vector coord)
bool IsSolid(ref int index)
bool GetNextHullPoint(ref Vector last, ref Vector current, out Vector next)
static List< List< Vector > > DetectVertices(uint[] data, int width, float hullTolerance, byte alphaTolerance, bool multiPartDetection, bool holeDetection)
Detects the vertices of the supplied texture data.
bool SplitPolygonEdge(Vertices polygon, Vector coordInsideThePolygon, out int vertex1Index, out int vertex2Index)
bool IsNearPixel(ref Vector current, ref Vector near)
TextureToShapeConverter(uint[] data, int width)
bool PixelOffsetOptimization
Will optimize the vertex positions along the interpolated normal between two edges about a half pixel...
VerticesDetectionType
The detection type affects the resulting polygon data.
@ Separated
The data of the main polygon and hole polygons is returned separately.
@ Integrated
Holes are integrated into the main polygon.
Microsoft.Xna.Framework.Matrix Matrix
double Y
Vektorin Y-komponentti
Vector Normalize()
Palauttaa uuden vektorin, jonka suunta pysyy samana, mutta pituudeksi tulee 1.0.
static double Distance(Vector p1, Vector p2)
Etäisyys kahden pisteen välillä.
static readonly Vector Zero
Nollavektori.
double X
Vektorin X-komponentti.
static double DotProduct(Vector left, Vector right)
Pistetulo.