/*
* ******************************************************************************
* * Copyright 2015 See AUTHORS file.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *****************************************************************************
*/
package com.uwsoft.editor.utils.poly.tracer;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
/**
* http://astroboid.x50.cc/blog/2010/06/mapping-box2d-shapes-from-textures/ A
* mostly "blind" C# conversion of the TextureConverter code from Farseer, I
* even left the original german comments in place. Seems to work.
*/
public class TextureConverter {
// User contribution from Sickbattery
// / <summary>
// / TODO:
// / 1.) Das Array welches ich bekomme am besten in einen boolean array
// verwandeln. Wurde die Geschwindigkeit verbessern
// / </summary>
private static int[][] ClosePixels = new int[][] { { -1, -1 }, { 0, -1 },
{ 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 } };
public static Array<Vector2> createPolygon(int[] data, int width, int height)
throws Exception {
PolygonCreationAssistance pca = new PolygonCreationAssistance(data,
width, height);
Array<Array<Vector2>> verts = createPolygon(pca);
return verts.get(0);
}
public static Array<Vector2> createPolygon(int[] data, int width,
int height, boolean holeDetection) throws Exception {
PolygonCreationAssistance pca = new PolygonCreationAssistance(data,
width, height);
pca.HoleDetection = holeDetection;
Array<Array<Vector2>> verts = createPolygon(pca);
return verts.get(0);
}
public static Array<Array<Vector2>> createPolygon(int[] data, int width,
int height, float hullTolerance, int alphaTolerance,
boolean multiPartDetection, boolean holeDetection) throws Exception {
PolygonCreationAssistance pca = new PolygonCreationAssistance(data,
width, height);
pca.setHullTolerance(hullTolerance);
pca.setAlphaTolerance(alphaTolerance);
pca.MultipartDetection = multiPartDetection;
pca.HoleDetection = holeDetection;
return createPolygon(pca);
}
private static Array<Array<Vector2>> createPolygon(
PolygonCreationAssistance pca) throws Exception {
Array<Array<Vector2>> polygons = new Array<Array<Vector2>>();
Array<Vector2> polygon;
Array<Vector2> holePolygon;
Vector2 holeEntrance = null;
Vector2 polygonEntrance = null;
Array<Vector2> blackList = new Array<Vector2>();
// First of all: Check the array you just got.
if (pca.IsValid()) {
boolean searchOn;
do {
if (polygons.size == 0) {
polygon = CreateSimplePolygon(pca, new Vector2(),
new Vector2());
if (polygon != null && polygon.size > 2) {
polygonEntrance = GetTopMostVertex(polygon);
}
} else if (polygonEntrance != null) {
polygon = CreateSimplePolygon(pca, polygonEntrance,
new Vector2(polygonEntrance.x - 1f,
polygonEntrance.y));
} else {
break;
}
searchOn = false;
if (polygon != null && polygon.size > 2) {
if (pca.HoleDetection) {
do {
holeEntrance = GetHoleHullEntrance(pca, polygon,
holeEntrance);
if (holeEntrance != null) {
if (!vectorListContains(blackList, holeEntrance)) {
blackList.add(holeEntrance);
holePolygon = CreateSimplePolygon(pca,
holeEntrance, new Vector2(
holeEntrance.x + 1,
holeEntrance.y));
if (holePolygon != null
&& holePolygon.size > 2) {
holePolygon.add(holePolygon.get(0));
Reference<Integer> vertex2IndexRef = new Reference<Integer>(
0);
Reference<Integer> vertex1IndexRef = new Reference<Integer>(
0);
if (SplitPolygonEdge(polygon,
EdgeAlignment.Vertical,
holeEntrance, vertex1IndexRef,
vertex2IndexRef)) {
polygon.ensureCapacity(holePolygon.size);
for (int i = holePolygon.size - 1; i <= 0; i--) {
polygon.insert(
vertex2IndexRef.v,
holePolygon.get(i));
}
}
}
} else {
break;
}
} else {
break;
}
} while (true);
}
polygons.add(polygon);
if (pca.MultipartDetection) {
// 1: 95 / 151
// 2: 232 / 252
//
polygonEntrance = new Vector2();
while (GetNextHullEntrance(pca, polygonEntrance,
polygonEntrance)) {
boolean inPolygon = false;
for (int i = 0; i < polygons.size; i++) {
polygon = polygons.get(i);
if (InPolygon(pca, polygon, polygonEntrance)) {
inPolygon = true;
break;
}
}
if (!inPolygon) {
searchOn = true;
break;
}
}
}
}
} while (searchOn);
} else {
throw new Exception(
"Sizes don't match: Color array must contain texture width * texture height elements.");
}
return polygons;
}
private static Vector2 GetHoleHullEntrance(PolygonCreationAssistance pca,
Array<Vector2> polygon, Vector2 startVertex) throws Exception {
Array<CrossingEdgeInfo> edges = new Array<CrossingEdgeInfo>();
Vector2 entrance;
int startLine;
int endLine;
int lastSolid = 0;
boolean foundSolid;
boolean foundTransparent;
if (polygon != null && polygon.size > 0) {
if (startVertex != null) {
startLine = (int) startVertex.y;
} else {
startLine = (int) GetTopMostCoord(polygon);
}
endLine = (int) GetBottomMostCoord(polygon);
if (startLine > 0 && startLine < pca.Height && endLine > 0
&& endLine < pca.Height) {
// go from top to bottom of the polygon
for (int y = startLine; y <= endLine; y += pca
.getHoleDetectionLineStepSize()) {
// get x-coord of every polygon edge which crosses y
edges = GetCrossingEdges(polygon, EdgeAlignment.Vertical, y);
// we need an even number of crossing edges
if (edges.size > 1 && edges.size % 2 == 0) {
for (int i = 0; i < edges.size; i += 2) {
foundSolid = false;
foundTransparent = false;
for (int x = (int) edges.get(i).CrossingPoint.x; x <= (int) edges
.get(i + 1).CrossingPoint.x; x++) {
if (pca.IsSolid(x, y)) {
if (!foundTransparent) {
foundSolid = true;
lastSolid = x;
}
if (foundSolid && foundTransparent) {
entrance = new Vector2(lastSolid, y);
if (DistanceToHullAcceptable(pca,
polygon, entrance, true)) {
return entrance;
}
entrance = null;
break;
}
} else {
if (foundSolid) {
foundTransparent = true;
}
}
}
}
}
}
}
}
return null;
}
private static boolean DistanceToHullAcceptable(
PolygonCreationAssistance pca, Array<Vector2> polygon,
Vector2 point, boolean higherDetail) {
if (polygon != null && polygon.size > 2) {
Vector2 edgeVertex2 = polygon.get(polygon.size - 1).cpy();
Vector2 edgeVertex1 = new Vector2();
if (higherDetail) {
for (int i = 0; i < polygon.size; i++) {
edgeVertex1.set(polygon.get(i));
if (LineTools.DistanceBetweenPointAndLineSegment(point,
edgeVertex1, edgeVertex2) <= pca.getHullTolerance()
|| LineTools.DistanceBetweenPointAndPoint(point,
edgeVertex1) <= pca.getHullTolerance()) {
return false;
}
edgeVertex2.set(polygon.get(i));
}
return true;
} else {
for (int i = 0; i < polygon.size; i++) {
edgeVertex1.set(polygon.get(i));
if (LineTools.DistanceBetweenPointAndLineSegment(point,
edgeVertex1, edgeVertex2) <= pca.getHullTolerance()) {
return false;
}
edgeVertex2.set(polygon.get(i));
}
return true;
}
}
return false;
}
private static boolean InPolygon(PolygonCreationAssistance pca,
Array<Vector2> polygon, Vector2 point) throws Exception {
boolean inPolygon = !DistanceToHullAcceptable(pca, polygon, point, true);
if (!inPolygon) {
Array<CrossingEdgeInfo> edges = GetCrossingEdges(polygon,
EdgeAlignment.Vertical, (int) point.y);
if (edges.size > 0 && edges.size % 2 == 0) {
for (int i = 0; i < edges.size; i += 2) {
if (edges.get(i).CrossingPoint.x <= point.x
&& edges.get(i + 1).CrossingPoint.x >= point.x) {
return true;
}
}
return false;
}
return false;
}
return true;
}
private static Vector2 GetTopMostVertex(Array<Vector2> vertices) {
float topMostValue = Float.MAX_VALUE;
Vector2 topMost = null;
for (int i = 0; i < vertices.size; i++) {
if (topMostValue > vertices.get(i).y) {
topMostValue = vertices.get(i).y;
topMost = vertices.get(i);
}
}
return topMost.cpy();
}
private static float GetTopMostCoord(Array<Vector2> vertices) {
float returnValue = Float.MAX_VALUE;
for (int i = 0; i < vertices.size; i++) {
if (returnValue > vertices.get(i).y) {
returnValue = vertices.get(i).y;
}
}
return returnValue;
}
private static float GetBottomMostCoord(Array<Vector2> vertices) {
float returnValue = Float.MIN_VALUE;
for (int i = 0; i < vertices.size; i++) {
if (returnValue < vertices.get(i).y) {
returnValue = vertices.get(i).y;
}
}
return returnValue;
}
public static Boolean vectorEquals(Vector2 v1, Vector2 v2) {
return v1.x == v2.x && v1.y == v2.y;
}
public static int vectorListIndexOf(Array list, Vector2 v) {
for (int i = 0; i < list.size; i++) {
Object obj = list.get(i);
if (obj == v)
return i;
if (obj instanceof Vector2) {
Vector2 vect = (Vector2) obj;
if (vectorEquals(v, vect))
return i;
}
}
return -1;
}
public static Boolean vectorListContains(Array list, Vector2 v) {
int index = vectorListIndexOf(list, v);
return index != -1;
}
public static Vector2 vectorSub(Vector2 v1, Vector2 v2) {
return new Vector2(v1.x - v2.x, v1.y - v2.y);
}
public static Vector2 vectorAdd(Vector2 v1, Vector2 v2) {
return new Vector2(v1.x + v2.x, v1.y + v2.y);
}
public static float vectorCross(Vector2 v1, Vector2 v2) {
return v1.x * v2.y - v1.y * v2.x;
}
public static Vector2 vectorCross(Vector2 v1, float scalar) {
return new Vector2(scalar * v1.y, -scalar * v1.x);
}
public static float vectorDot(Vector2 v1, Vector2 v2) {
return v1.x * v2.x + v1.y * v2.y;
}
public static Vector2 vectorMul(Vector2 v1, float scalar) {
return new Vector2(v1.x * scalar, v1.y * scalar);
}
private static Array<CrossingEdgeInfo> GetCrossingEdges(
Array<Vector2> polygon, EdgeAlignment edgeAlign, int checkLine)
throws Exception {
Array<CrossingEdgeInfo> edges = new Array<CrossingEdgeInfo>();
Vector2 slope = new Vector2();
Vector2 edgeVertex1 = new Vector2();
Vector2 edgeVertex2 = new Vector2();
Vector2 slopePreview = new Vector2();
Vector2 edgeVertexPreview = new Vector2();
Vector2 crossingPoint = new Vector2();
boolean addCrossingPoint;
if (polygon.size > 1) {
edgeVertex2.set(polygon.get(polygon.size - 1));
switch (edgeAlign) {
case Vertical:
for (int i = 0; i < polygon.size; i++) {
edgeVertex1.set(polygon.get(i));
if ((edgeVertex1.y >= checkLine && edgeVertex2.y <= checkLine)
|| (edgeVertex1.y <= checkLine && edgeVertex2.y >= checkLine)) {
if (edgeVertex1.y != edgeVertex2.y) {
addCrossingPoint = true;
slope.set(vectorSub(edgeVertex2, edgeVertex1));
if (edgeVertex1.y == checkLine) {
edgeVertexPreview.set(polygon.get((i + 1)
% polygon.size));
slopePreview.set(vectorSub(edgeVertex1,
edgeVertexPreview));
if (slope.y > 0) {
addCrossingPoint = (slopePreview.y <= 0);
} else {
addCrossingPoint = (slopePreview.y >= 0);
}
}
if (addCrossingPoint) {
crossingPoint = new Vector2(
(checkLine - edgeVertex1.y) / slope.y
* slope.x + edgeVertex1.x,
checkLine);
edges.add(new CrossingEdgeInfo(edgeVertex1,
edgeVertex2, crossingPoint, edgeAlign));
}
}
}
edgeVertex2.set(edgeVertex1);
}
break;
case Horizontal:
throw new Exception(
"EdgeAlignment.Horizontal isn't implemented yet. Sorry.");
}
}
edges.sort();
// Collections.sort(edges);
return edges;
}
private static boolean SplitPolygonEdge(Array<Vector2> polygon,
EdgeAlignment edgeAlign, Vector2 coordInsideThePolygon,
Reference<Integer> vertex1IndexRef,
Reference<Integer> vertex2IndexRef) throws Exception {
Array<CrossingEdgeInfo> edges;
Vector2 slope = new Vector2();
int nearestEdgeVertex1Index = 0;
int nearestEdgeVertex2Index = 0;
boolean edgeFound = false;
float shortestDistance = Float.MAX_VALUE;
boolean edgeCoordFound = false;
Vector2 foundEdgeCoord = new Vector2();
vertex1IndexRef.v = 0;
vertex2IndexRef.v = 0;
switch (edgeAlign) {
case Vertical:
edges = GetCrossingEdges(polygon, EdgeAlignment.Vertical,
(int) coordInsideThePolygon.y);
foundEdgeCoord.y = coordInsideThePolygon.y;
if (edges != null && edges.size > 1 && edges.size % 2 == 0) {
float distance;
for (int i = 0; i < edges.size; i++) {
if (edges.get(i).CrossingPoint.x < coordInsideThePolygon.x) {
distance = coordInsideThePolygon.x
- edges.get(i).CrossingPoint.x;
if (distance < shortestDistance) {
shortestDistance = distance;
foundEdgeCoord.x = edges.get(i).CrossingPoint.x;
edgeCoordFound = true;
}
}
}
if (edgeCoordFound) {
shortestDistance = Float.MAX_VALUE;
int edgeVertex2Index = polygon.size - 1;
int edgeVertex1Index;
for (edgeVertex1Index = 0; edgeVertex1Index < polygon.size; edgeVertex1Index++) {
Vector2 tempVector1 = polygon.get(edgeVertex1Index)
.cpy();
Vector2 tempVector2 = polygon.get(edgeVertex2Index)
.cpy();
distance = LineTools
.DistanceBetweenPointAndLineSegment(
foundEdgeCoord, tempVector1,
tempVector2);
if (distance < shortestDistance) {
shortestDistance = distance;
nearestEdgeVertex1Index = edgeVertex1Index;
nearestEdgeVertex2Index = edgeVertex2Index;
edgeFound = true;
}
edgeVertex2Index = edgeVertex1Index;
}
if (edgeFound) {
slope.set(vectorSub(
polygon.get(nearestEdgeVertex2Index),
polygon.get(nearestEdgeVertex1Index)));
slope.nor();
Vector2 tempVector = polygon.get(
nearestEdgeVertex1Index).cpy();
distance = LineTools.DistanceBetweenPointAndPoint(
tempVector, foundEdgeCoord);
vertex1IndexRef.v = nearestEdgeVertex1Index;
vertex2IndexRef.v = nearestEdgeVertex1Index + 1;
// distance * slope + polygon[vertex1Index]
polygon.insert(
nearestEdgeVertex1Index,
vectorAdd(vectorMul(slope, distance),
polygon.get(vertex1IndexRef.v)));
polygon.insert(
nearestEdgeVertex1Index,
vectorAdd(vectorMul(slope, distance),
polygon.get(vertex2IndexRef.v)));
return true;
}
}
}
break;
case Horizontal:
throw new Exception(
"EdgeAlignment.Horizontal isn't implemented yet. Sorry.");
}
return false;
}
private static Array<Vector2> CreateSimplePolygon(
PolygonCreationAssistance pca, Vector2 entrance, Vector2 last) {
boolean entranceFound = false;
boolean endOfHull = false;
Array<Vector2> polygon = new Array<Vector2>();
Array<Vector2> hullArea = new Array<Vector2>();
Array<Vector2> endOfHullArea = new Array<Vector2>();
Vector2 current = new Vector2();
Vector2 zeroVec = new Vector2();
// Get the entrance point. //todo: alle moglichkeiten testen
if (vectorEquals(entrance, zeroVec) || !pca.InBounds(entrance)) {
entranceFound = GetHullEntrance(pca, entrance);
if (entranceFound) {
current.set(entrance.x - 1f, entrance.y);
}
} else {
if (pca.IsSolid(entrance)) {
if (IsNearPixel(pca, entrance, last)) {
current.set(last);
entranceFound = true;
} else {
Vector2 temp = new Vector2();
if (SearchNearPixels(pca, false, entrance, temp)) {
current.set(temp);
entranceFound = true;
} else {
entranceFound = false;
}
}
}
}
if (entranceFound) {
polygon.add(entrance);
hullArea.add(entrance);
Vector2 next = entrance.cpy();
do {
// Search in the pre vision list for an outstanding point.
Vector2 outstanding = new Vector2();
if (SearchForOutstandingVertex(hullArea,
pca.getHullTolerance(), outstanding)) {
if (endOfHull) {
// We have found the next pixel, but is it on the last
// bit of the
// hull?
if (vectorListContains(endOfHullArea, outstanding)
&& !vectorListContains(polygon, outstanding)) {
// Indeed.
polygon.add(outstanding);
}
// That's enough, quit.
break;
}
// Add it and remove all vertices that don't matter anymore
// (all the vertices before the outstanding).
polygon.add(outstanding);
int index = vectorListIndexOf(hullArea, outstanding);
if (index == -1) {
int debug = 1;
}
if (index >= 0) {
// hullArea = hullArea.subList(index + 1,
// hullArea.size);
// Array<Vector2> newArray = new Array<Vector2>
// (hullArea.size - (index + 1));
int counter = 0;
for (int i = index + 1; i < hullArea.size; i++) {
Vector2 v = hullArea.get(index);
// newArray.add(v);
hullArea.set(counter, v);
counter++;
}
// hullArea.clear();
// hullArea = newArray;
for (int i = 0; i < index + 1; i++) {
hullArea.pop();
}
}
}
// Last point gets current and current gets next. Our little
// spider is
// moving forward on the hull ;).
last.set(current);
current.set(next);
// Get the next point on hull.
next = new Vector2();
if (GetNextHullPoint(pca, last, current, next)) {
// Add the vertex to a hull pre vision list.
hullArea.add(next);
} else {
// Quit
break;
}
if (vectorEquals(next, entrance) && !endOfHull) {
// It's the last bit of the hull, search on and exit at next
// found
// vertex.
endOfHull = true;
endOfHullArea.addAll(hullArea);
}
} while (true);
}
return polygon;
}
private static boolean SearchNearPixels(PolygonCreationAssistance pca,
boolean searchingForSolidPixel, Vector2 current, Vector2 foundPixel) {
int x;
int y;
for (int i = 0; i < 8; i++) {
x = (int) current.x + ClosePixels[i][0];
y = (int) current.y + ClosePixels[i][1];
if (!searchingForSolidPixel ^ pca.IsSolid(x, y)) {
foundPixel.set(x, y);
return true;
}
}
// Nothing found.
foundPixel.set(0, 0);
return false;
}
private static boolean IsNearPixel(PolygonCreationAssistance pca,
Vector2 current, Vector2 near) {
for (int i = 0; i < 8; i++) {
int x = (int) current.x + ClosePixels[i][0];
int y = (int) current.y + ClosePixels[i][1];
if (x >= 0 && x <= pca.Width && y >= 0 && y <= pca.Height) {
if (x == (int) near.x && y == (int) near.y) {
return true;
}
}
}
return false;
}
private static boolean GetHullEntrance(PolygonCreationAssistance pca,
Vector2 entrance) {
// Search for first solid pixel.
for (int y = 0; y < pca.Height; y++) {
for (int x = 0; x < pca.Width; x++) {
if (pca.IsSolid(x, y)) {
entrance.set(x, y);
return true;
}
}
}
// If there are no solid pixels.
entrance.set(0, 0);
return false;
}
private static boolean GetNextHullEntrance(PolygonCreationAssistance pca,
Vector2 start, Vector2 entrance) {
// Search for first solid pixel.
int size = pca.Height * pca.Width;
int x;
boolean foundTransparent = false;
for (int i = (int) start.x + (int) start.y * pca.Width; i < size; i++) {
if (pca.IsSolid(i)) {
if (foundTransparent) {
x = i % pca.Width;
entrance.set(x, (i - x) / pca.Width);
return true;
}
} else {
foundTransparent = true;
}
}
// If there are no solid pixels.
entrance.set(0, 0);
return false;
}
private static boolean GetNextHullPoint(PolygonCreationAssistance pca,
Vector2 last, Vector2 current, Vector2 next) {
int x;
int y;
int indexOfFirstPixelToCheck = GetIndexOfFirstPixelToCheck(last,
current);
int indexOfPixelToCheck;
int pixelsToCheck = 8; // _closePixels.Length;
for (int i = 0; i < pixelsToCheck; i++) {
indexOfPixelToCheck = (indexOfFirstPixelToCheck + i)
% pixelsToCheck;
x = (int) current.x + ClosePixels[indexOfPixelToCheck][0];
y = (int) current.y + ClosePixels[indexOfPixelToCheck][1];
if (x >= 0 && x < pca.Width && y >= 0 && y <= pca.Height) {
if (pca.IsSolid(x, y)) // todo
{
next.set(x, y);
return true;
}
}
}
next.set(0, 0);
return false;
}
private static boolean SearchForOutstandingVertex(Array<Vector2> hullArea,
float hullTolerance, Vector2 outstanding) {
Vector2 outstandingResult = new Vector2();
boolean found = false;
if (hullArea.size > 2) {
int hullAreaLastPoint = hullArea.size - 1;
Vector2 tempVector1;
Vector2 tempVector2 = hullArea.get(0);
Vector2 tempVector3 = hullArea.get(hullAreaLastPoint);
// Search between the first and last hull point.
for (int i = 1; i < hullAreaLastPoint; i++) {
tempVector1 = hullArea.get(i);
// Check if the distance is over the one that's tolerable.
if (LineTools.DistanceBetweenPointAndLineSegment(tempVector1,
tempVector2, tempVector3) >= hullTolerance) {
outstandingResult.set(hullArea.get(i));
found = true;
break;
}
}
}
outstanding.set(outstandingResult);
return found;
}
private static int GetIndexOfFirstPixelToCheck(Vector2 last, Vector2 current) {
// .: pixel
// l: last position
// c: current position
// f: first pixel for next search
// f . .
// l c .
// . . .
// Calculate in which direction the last move went and decide over the
// next
// first pixel.
switch ((int) (current.x - last.x)) {
case 1:
switch ((int) (current.y - last.y)) {
case 1:
return 1;
case 0:
return 0;
case -1:
return 7;
}
break;
case 0:
switch ((int) (current.y - last.y)) {
case 1:
return 2;
case -1:
return 6;
}
break;
case -1:
switch ((int) (current.y - last.y)) {
case 1:
return 3;
case 0:
return 4;
case -1:
return 5;
}
break;
}
return 0;
}
}
enum EdgeAlignment {
Vertical, Horizontal
}
class CrossingEdgeInfo implements Comparable<CrossingEdgeInfo> {
private EdgeAlignment _alignment;
private Vector2 _crossingPoint;
@SuppressWarnings("unused")
private Vector2 _edgeVertex2;
@SuppressWarnings("unused")
private Vector2 _egdeVertex1;
public Vector2 EdgeVertex1;
public Vector2 EdgeVertex2;
public EdgeAlignment CheckLineAlignment;
public Vector2 CrossingPoint;
public CrossingEdgeInfo(Vector2 edgeVertex1, Vector2 edgeVertex2,
Vector2 crossingPoint, EdgeAlignment checkLineAlignment) {
_egdeVertex1 = edgeVertex1.cpy();
_edgeVertex2 = edgeVertex2.cpy();
_alignment = checkLineAlignment;
_crossingPoint = crossingPoint;
}
public int compareTo(CrossingEdgeInfo obj) {
CrossingEdgeInfo cei = obj;
int result = 0;
switch (_alignment) {
case Vertical:
if (_crossingPoint.x < cei.CrossingPoint.y) {
result = -1;
} else if (_crossingPoint.x > cei.CrossingPoint.y) {
result = 1;
}
break;
case Horizontal:
if (_crossingPoint.y < cei.CrossingPoint.y) {
result = -1;
} else if (_crossingPoint.y > cei.CrossingPoint.y) {
result = 1;
}
break;
}
return result;
}
}
// / <summary>
// / Class used as a tools container and helper for the texture-to-vertices code.
// / </summary>
class PolygonCreationAssistance {
private int _alphaTolerance;
private int _holeDetectionLineStepSize;
private float _hullTolerance;
public PolygonCreationAssistance(int[] data, int width, int height) {
Data = data;
Width = width;
Height = height;
setAlphaTolerance((byte) 20);
setHullTolerance(1.5f);
setHoleDetectionLineStepSize(1);
HoleDetection = false;
MultipartDetection = false;
}
private int[] Data;
public int Width;
public int Height;
public int getAlphaTolerance() {
return _alphaTolerance;
}
public void setAlphaTolerance(int value) {
_alphaTolerance = value & 0xFF;
}
public float getHullTolerance() {
return _hullTolerance;
}
public void setHullTolerance(float value) {
float hullTolerance = value;
if (hullTolerance > 4f)
hullTolerance = 4f;
if (hullTolerance < 0.9f)
hullTolerance = 0.9f;
_hullTolerance = hullTolerance;
}
public int getHoleDetectionLineStepSize() {
return _holeDetectionLineStepSize;
}
private void setHoleDetectionLineStepSize(int value) {
if (value < 1) {
_holeDetectionLineStepSize = 1;
} else {
if (value > 10) {
_holeDetectionLineStepSize = 10;
} else {
_holeDetectionLineStepSize = value;
}
}
}
public boolean HoleDetection;
public boolean MultipartDetection;
public boolean IsSolid(Vector2 pixel) {
return IsSolid((int) pixel.x, (int) pixel.y);
}
public boolean IsSolid(int x, int y) {
if (x >= 0 && x < Width && y >= 0 && y < Height) {
int data = Data[x + y * Width];
long mask1 = (long) data & 0xFFFFFFFFL;
long mask2 = mask1 & 0x000000FF;
Boolean opaque = mask2 >= _alphaTolerance;
if (opaque || mask2 != 0) {
int debug = 1;
}
return opaque;
}
return false;
}
public boolean IsSolid(int index) {
if (index >= 0 && index < Width * Height) {
int data = Data[index];
long mask1 = (long) data & 0xFFFFFFFFL;
long mask2 = mask1 & 0x000000FF;
Boolean opaque = mask2 >= _alphaTolerance;
if (opaque || mask2 != 0) {
int debug = 1;
}
return opaque;
// return (mask1 >= _alphaToleranceRealValue);
}
return false;
}
public boolean InBounds(Vector2 coord) {
return (coord.x >= 0f && coord.x < Width && coord.y >= 0f && coord.y < Height);
}
public boolean IsValid() {
if (Data != null && Data.length > 0)
return Data.length == Width * Height;
return false;
}
}
class Reference<K> {
public K v;
public Reference(K v) {
this.v = v;
}
@Override
public String toString() {
return v.toString();
}
@Override
public boolean equals(Object obj) {
return v.equals(obj);
}
@Override
public int hashCode() {
return v.hashCode();
}
}
class LineTools {
public static float DistanceBetweenPointAndPoint(Vector2 point1,
Vector2 point2) {
Vector2 v = TextureConverter.vectorSub(point1, point2);
return v.len();
}
public static float DistanceBetweenPointAndLineSegment(Vector2 point,
Vector2 lineEndPoint1, Vector2 lineEndPoint2) {
Vector2 v = TextureConverter.vectorSub(lineEndPoint2, lineEndPoint1);
Vector2 w = TextureConverter.vectorSub(point, lineEndPoint1);
float c1 = TextureConverter.vectorDot(w, v);
if (c1 <= 0)
return DistanceBetweenPointAndPoint(point, lineEndPoint1);
float c2 = TextureConverter.vectorDot(v, v);
if (c2 <= c1)
return DistanceBetweenPointAndPoint(point, lineEndPoint2);
float b = c1 / c2;
Vector2 pointOnLine = TextureConverter.vectorAdd(lineEndPoint1,
TextureConverter.vectorMul(v, b));
return DistanceBetweenPointAndPoint(point, pointOnLine);
}
}