com.tussle.main.Utility.java Source code

Java tutorial

Introduction

Here is the source code for com.tussle.main.Utility.java

Source

/*
 * Copyright (c) 2017 eaglgenes101
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.tussle.main;

import com.badlogic.ashley.core.Entity;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.utils.JsonValue;
import com.tussle.collision.CollisionShape;
import com.tussle.collision.CollisionStadium;
import com.tussle.collision.ProjectionVector;
import com.tussle.collision.StageElement;
import org.apache.commons.math3.util.FastMath;

import java.io.IOException;
import java.io.Reader;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;

public strictfp class Utility {
    public static final long MEMOIZE_CAPACITY = 1 << 20;

    public static double[] getXYfromDM(double direction, double magnitude) {
        double[] returnVect = new double[2];
        returnVect[0] = magnitude * FastMath.cos(FastMath.toRadians(direction));
        returnVect[1] = magnitude * FastMath.sin(FastMath.toDegrees(direction));
        return returnVect;
    }

    public static double addTowards(double value, double addend, double base) {
        if (addend == 0)
            return value;
        if (addend * (base - value - addend) > 0)
            return value + addend;
        else
            return addend > 0 ? FastMath.max(base, value) : FastMath.min(base, value);
    }

    public static double addAway(double value, double addend, double base) {
        if (addend * (base - value) <= 0)
            return value + addend;
        else
            return value;
    }

    public static double addFrom(double value, double amount, double base) {
        if (amount < 0 && FastMath.min(value - base, base - value) > amount)
            return base;
        else
            return amount * FastMath.copySign(1, value - base) + value;
    }

    public static double[] projection(double x1, double y1, double x2, double y2) {
        double[] returnVec = new double[2];
        double scalarProj = (x1 * x2 + y1 * y2) / (x2 * x2 + y2 * y2);
        returnVec[0] = x2 * scalarProj;
        returnVec[1] = y2 * scalarProj;
        return returnVec;
    }

    public static double[] rejection(double x1, double y1, double x2, double y2) {
        double[] returnVec = new double[2];
        double scalarProj = (x1 * x2 + y1 * y2) / (x2 * x2 + y2 * y2);
        returnVec[0] = x1 - x2 * scalarProj;
        returnVec[1] = y1 - y2 * scalarProj;
        return returnVec;
    }

    public static double angle(double x, double y) {
        return FastMath.toDegrees(FastMath.atan2(y, x));
    }

    public static double[][] outsidePoints(double[] xpoints, double[] ypoints) {
        ArrayList<Integer> vertexIndices = new ArrayList<>();
        int leftmostIndex = 0;
        double leftmostCoord = xpoints[0];
        for (int i = 0; i < xpoints.length; i++) {
            if (xpoints[i] < leftmostCoord) {
                leftmostIndex = i;
                leftmostCoord = xpoints[i];
            }
        }
        double xvec = 0;
        double yvec = -1;
        int currentIndex = leftmostIndex;
        do {
            vertexIndices.add(currentIndex);
            int endpointIndex = 0;
            for (int i = 0; i < xpoints.length; i++) {
                if (endpointIndex == currentIndex) {
                    endpointIndex = i;
                    continue;
                }
                double dx = xpoints[endpointIndex] - xpoints[currentIndex];
                double dy = ypoints[endpointIndex] - ypoints[currentIndex];
                double crossProduct = dx * yvec - dy * xvec;
                if (crossProduct > 0)
                    endpointIndex = i;
            }
            xvec = xpoints[endpointIndex] - xpoints[currentIndex];
            yvec = ypoints[endpointIndex] - ypoints[currentIndex];
            currentIndex = endpointIndex;
        } while (currentIndex != leftmostIndex);
        double[][] returnArray = new double[2][vertexIndices.size()];
        for (int i = 0; i < vertexIndices.size(); i++) {
            returnArray[0][i] = xpoints[vertexIndices.get(i)];
            returnArray[1][i] = ypoints[vertexIndices.get(i)];
        }
        return returnArray;
    }

    public static boolean isPruned(Collection<ProjectionVector> vectors) {
        if (vectors.size() <= 2)
            return true;
        Iterator<ProjectionVector> i = vectors.iterator();
        ProjectionVector p0 = i.next();
        ProjectionVector p1 = i.next();
        double cos0 = p0.xNorm();
        double sin0 = p0.yNorm();
        double cos1 = p1.xNorm();
        double sin1 = p1.yNorm();
        //zeroth on the right, first on the left
        if (cos0 * sin1 < cos1 * sin0) {
            double tmpcos = cos1;
            double tmpsin = sin1;
            cos1 = cos0;
            sin1 = sin0;
            cos0 = tmpcos;
            sin0 = tmpsin;
        }
        while (i.hasNext()) {
            ProjectionVector next = i.next();
            double nextcos = next.xNorm();
            double nextsin = next.yNorm();
            if (nextcos * sin0 >= cos0 * nextsin && cos1 * nextsin >= nextcos * sin1) {
                //Case 0: Within cross product bounds
            } else if (nextcos * sin0 >= cos0 * nextsin) {
                //Case 1: Over the left, extend those bounds
                cos1 = nextcos;
                sin1 = nextsin;
            } else if (cos1 * nextsin >= nextcos * sin1) {
                //Case 2: Over the right, extend those bounds
                cos0 = nextcos;
                sin0 = nextsin;
            } else {
                //Case 3: Opposite side, immediately return false
                return false;
            }
        }
        return true;
    }

    public static Collection<ProjectionVector> prunedProjections(Collection<ProjectionVector> vectors) {
        SortedSet<ProjectionVector> sortedVectors = new ConcurrentSkipListSet<>(
                Comparator.comparingDouble((ProjectionVector p) -> -p.magnitude()));
        sortedVectors.addAll(vectors);
        if (isPruned(sortedVectors))
            return sortedVectors;
        double reduceMagnitude = 0;
        Iterator<ProjectionVector> i = sortedVectors.iterator();
        ProjectionVector p0 = i.next();
        ProjectionVector p1 = i.next();
        double cos0 = p0.xNorm();
        double sin0 = p0.yNorm();
        double cos1 = p1.xNorm();
        double sin1 = p1.yNorm();
        //zeroth on the right, first on the left
        if (cos0 * sin1 < cos1 * sin0) {
            double tmpcos = cos1;
            double tmpsin = sin1;
            cos1 = cos0;
            sin1 = sin0;
            cos0 = tmpcos;
            sin0 = tmpsin;
        }
        while (i.hasNext()) {
            ProjectionVector next = i.next();
            double nextcos = next.xNorm();
            double nextsin = next.yNorm();
            if (nextcos * sin0 >= cos0 * nextsin && cos1 * nextsin >= nextcos * sin1) {
                //Case 0: Within cross product bounds
            } else if (nextcos * sin0 >= cos0 * nextsin) {
                //Case 1: Over the left, extend those bounds
                cos1 = nextcos;
                sin1 = nextsin;
            } else if (cos1 * nextsin >= nextcos * sin1) {
                //Case 2: Over the right, extend those bounds
                cos0 = nextcos;
                sin0 = nextsin;
            } else {
                //Case 3: Opposite side, immediately return false
                reduceMagnitude = next.magnitude();
                break;
            }
        }
        //Now given reduceMagnitude, remove elements with lesser magnitude and
        //reduce the magnitude of remaining elements
        if (Double.isFinite(reduceMagnitude)) {
            for (Iterator<ProjectionVector> j = sortedVectors.iterator(); j.hasNext();) {
                ProjectionVector vec = j.next();
                if (vec.magnitude() <= reduceMagnitude)
                    j.remove();
                else
                    vec = new ProjectionVector(vec.xNorm(), vec.yNorm(), vec.magnitude() - reduceMagnitude);
            }
        }
        return sortedVectors;
    }

    public static ProjectionVector combineProjections(Collection<ProjectionVector> vectors) {
        Iterator<ProjectionVector> i = vectors.iterator();
        if (vectors.size() == 0)
            return null;
        if (vectors.size() == 1)
            return i.next();
        ProjectionVector p0 = i.next();
        ProjectionVector p1 = i.next();
        //Get bordering unit vectors
        double cos0 = p0.xNorm();
        double sin0 = p0.yNorm();
        double cos1 = p1.xNorm();
        double sin1 = p1.yNorm();
        //zeroth on the right, first on the left
        if (cos0 * sin1 < cos1 * sin0) {
            double tmpcos = cos1;
            double tmpsin = sin1;
            cos1 = cos0;
            sin1 = sin0;
            cos0 = tmpcos;
            sin0 = tmpsin;
        }
        while (i.hasNext()) {
            ProjectionVector next = i.next();
            double nextcos = next.xNorm();
            double nextsin = next.yNorm();
            if (nextcos * sin0 >= cos0 * nextsin && cos1 * nextsin >= nextcos * sin1) {
                //Case 0: Within cross product bounds
            } else if (nextcos * sin0 >= cos0 * nextsin) {
                //Case 1: Over the left, extend those bounds
                cos1 = nextcos;
                sin1 = nextsin;
            } else if (cos1 * nextsin >= nextcos * sin1) {
                //Case 2: Over the right, extend those bounds
                cos0 = nextcos;
                sin0 = nextsin;
            } else {
                //Case 3: something went horribly wrong
                return null;
            }
        }
        //Now... project all vectors onto the sum of the borders.
        double sumcos = cos0 + cos1;
        double sumsin = sin0 + sin1;
        double len = FastMath.hypot(sumcos, sumsin);
        if (len == 0)
            return null;
        sumcos /= len;
        sumsin /= len;
        double maxlen = Double.NEGATIVE_INFINITY;
        for (ProjectionVector v : vectors) {
            double scalarProj = (v.xComp() * sumcos + v.yComp() * sumsin) / (sumcos * sumcos + sumsin * sumsin);
            if (scalarProj > maxlen)
                maxlen = scalarProj;
        }
        return new ProjectionVector(sumcos, sumsin, maxlen);
    }

    //Find the point that 1-2 and 3-4 both point towards
    public static double[] segmentsIntersectionPoint(double x1, double y1, double x2, double y2, double x3,
            double y3, double x4, double y4) {
        // This problem can be reduced to the following linear system:
        // (y2-aftY)x + (aftX-x2)y = aftX*y2 - x2*aftY
        // (y4-y3)x + (x3-x4)y = x3*y4 - x4*y3

        //Yay for Julia's sympy package for doing work for me
        double numX = x1 * x3 * y2 - x1 * x3 * y4 - x1 * x4 * y2 + x1 * x4 * y3 - x2 * x3 * y1 + x2 * x3 * y4
                + x2 * x4 * y1 - x2 * x4 * y3;
        double numY = x1 * y2 * y3 - x1 * y2 * y4 - x2 * y1 * y3 + x2 * y1 * y4 - x3 * y1 * y4 + x3 * y2 * y4
                + x4 * y1 * y3 - x4 * y2 * y3;
        double den = x1 * y3 - x1 * y4 - x2 * y3 + x2 * y4 - x3 * y1 + x3 * y2 + x4 * y1 - x4 * y2;
        return new double[] { numX / den, numY / den };
    }

    public static double pointSegmentPosition(double sx, double sy, double ex, double ey, double x, double y) {
        double dx = x - sx;
        double dy = y - sy;
        double lx = ex - sx;
        double ly = ey - sy;
        return (dx * lx + dy * ly) / (lx * lx + ly * ly);
    }

    public static double partSegmentsIntersecting(double x1, double y1, double x2, double y2, double x3, double y3,
            double x4, double y4) {
        double den = x1 * y3 - x1 * y4 - x2 * y3 + x2 * y4 - x3 * y1 + x3 * y2 + x4 * y1 - x4 * y2;
        double x = (x1 * x3 * y2 - x1 * x3 * y4 - x1 * x4 * y2 + x1 * x4 * y3 - x2 * x3 * y1 + x2 * x3 * y4
                + x2 * x4 * y1 - x2 * x4 * y3) / den;
        double y = (x1 * y2 * y3 - x1 * y2 * y4 - x2 * y1 * y3 + x2 * y1 * y4 - x3 * y1 * y4 + x3 * y2 * y4
                + x4 * y1 * y3 - x4 * y2 * y3) / den;
        double dx = x - x1;
        double dy = y - y1;
        double lx = x2 - x1;
        double ly = y2 - y1;
        return (dx * lx + dy * ly) / (lx * lx + ly * ly);
    }

    public static double displacementDots(CollisionShape startShape, CollisionShape endShape,
            CollisionStadium startStad, CollisionStadium endStad) {
        double[] startPos = startShape.nearestPoint(startStad);
        double[] endPos = endShape.nearestPoint(endStad);
        double startEcbPortion = startShape.stadiumPortion(startStad);
        double endEcbPortion = endShape.stadiumPortion(endStad);
        double stadStartDX = (1 - startEcbPortion) * startStad.getStartx() + startEcbPortion * startStad.getEndx()
                - startPos[0];
        double stadStartDY = (1 - startEcbPortion) * startStad.getStarty() + startEcbPortion * startStad.getEndy()
                - startPos[1];
        double stadEndDX = (1 - endEcbPortion) * endStad.getStartx() + endEcbPortion * endStad.getEndx()
                - endPos[0];
        double stadEndDY = (1 - endEcbPortion) * endStad.getStarty() + endEcbPortion * endStad.getEndy()
                - endPos[1];
        if ((stadStartDX == 0 && stadStartDY == 0) || (stadEndDX == 0 && stadEndDY == 0)) {
            return Double.NaN; //Sorry, can't help you here
        } else {
            return (stadStartDX * stadEndDX + stadStartDY * stadEndDY) / FastMath.hypot(stadStartDX, stadStartDY)
                    / FastMath.hypot(stadEndDX, stadEndDY);
        }
    }

    public static double[] displacementDiff(CollisionShape startShape, CollisionShape endShape,
            CollisionStadium startStad, CollisionStadium endStad) {
        double[] startPos = startShape.nearestPoint(startStad);
        double[] endPos = endShape.nearestPoint(endStad);
        double startEcbPortion = startShape.stadiumPortion(startStad);
        double endEcbPortion = endShape.stadiumPortion(endStad);
        double stadStartDX = (1 - startEcbPortion) * startStad.getStartx() + startEcbPortion * startStad.getEndx()
                - startPos[0];
        double stadStartDY = (1 - startEcbPortion) * startStad.getStarty() + startEcbPortion * startStad.getEndy()
                - startPos[1];
        double stadEndDX = (1 - endEcbPortion) * endStad.getStartx() + endEcbPortion * endStad.getEndx()
                - endPos[0];
        double stadEndDY = (1 - endEcbPortion) * endStad.getStarty() + endEcbPortion * endStad.getEndy()
                - endPos[1];
        return new double[] { stadEndDX - stadStartDX, stadEndDY - stadStartDY };
    }

    public static CollisionStadium middleStad(CollisionStadium s1, CollisionStadium s2) {
        return new CollisionStadium((s1.getStartx() + s2.getStartx()) / 2, (s1.getStarty() + s2.getStarty()) / 2,
                (s1.getEndx() + s2.getEndx()) / 2, (s1.getEndy() + s2.getEndy()) / 2,
                (s1.getRadius() + s2.getRadius()) / 2);
    }

    public static JsonValue exceptionToJson(Throwable ex) {
        JsonValue topValue = new JsonValue(JsonValue.ValueType.object);
        JsonValue exceptionClass = new JsonValue(ex.getClass().toString());
        topValue.addChild("Exception Class", exceptionClass);
        if (ex.getLocalizedMessage() != null) {
            JsonValue exceptionMessage = new JsonValue(ex.getLocalizedMessage());
            topValue.addChild("Exception Message", exceptionMessage);
        }
        if (ex.getCause() != null) {
            JsonValue exceptionCause = new JsonValue(ex.getCause().toString());
            topValue.addChild("Exception Cause", exceptionCause);
        }
        JsonValue stackTrace = new JsonValue(JsonValue.ValueType.array);
        for (StackTraceElement element : ex.getStackTrace())
            stackTrace.addChild(new JsonValue(element.toString()));
        topValue.addChild("Stack Trace", stackTrace);
        return topValue;
    }

    public static String readAll(Reader reader) throws IOException {
        StringBuilder toReturn = new StringBuilder();
        while (reader.ready()) {
            char[] holder = new char[1024];
            int charsRead = reader.read(holder);
            toReturn.append(holder, 0, charsRead);
        }
        return toReturn.toString();
    }

    public static Preferences getPreferencesFor(String name) {
        Preferences top = Gdx.app.getPreferences("Tussle");
        return Gdx.app.getPreferences(top.getString(name));
    }

    public static <T extends CollisionShape> StageElement<T> stageElementFor(T shape, Entity e) {
        return new StageElement(shape, Components.positionMapper.get(e), null);
    }
}