com.kotcrab.vis.editor.module.physicseditor.util.trace.TextureConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.kotcrab.vis.editor.module.physicseditor.util.trace.TextureConverter.java

Source

/*
 * Copyright 2014-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.kotcrab.vis.editor.module.physicseditor.util.trace;

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.
 */
//TODO clean this up
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 data 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);
    }
}