Jarvis: use primitive arrays

This creates much less garbage and allows use with modelToCanvas
This commit is contained in:
Max Weber
2019-09-09 10:38:27 -06:00
parent b77461a3e0
commit 80709f1bfa

View File

@@ -24,9 +24,9 @@
*/ */
package net.runelite.api.model; package net.runelite.api.model;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.runelite.api.Point; import net.runelite.api.Point;
import net.runelite.api.geometry.SimplePolygon;
/** /**
* Provides utility methods for computing the convex hull of a list of * Provides utility methods for computing the convex hull of a list of
@@ -41,92 +41,147 @@ public class Jarvis
/** /**
* Computes and returns the convex hull of the passed points. * Computes and returns the convex hull of the passed points.
* <p> * <p>
* The size of the list must be at least 4, otherwise this method will * The size of the list must be at least 3, otherwise this method will
* return null. * return null.
* *
* @param points list of points * @param points list of points
* @return list containing the points part of the convex hull * @return list containing the points part of the convex hull
*/ */
@Deprecated
public static List<Point> convexHull(List<Point> points) public static List<Point> convexHull(List<Point> points)
{ {
if (points.size() < 3) int[] xs = new int[points.size()];
int[] ys = new int[xs.length];
for (int i = 0; i < xs.length; i++)
{
Point p = points.get(i);
xs[i] = p.getX();
ys[i] = p.getY();
}
SimplePolygon poly = convexHull(xs, ys);
if (poly == null)
{ {
return null; return null;
} }
List<Point> ch = new ArrayList<>(); return poly.toRuneLitePointList();
}
/**
* Computes and returns the convex hull of the passed points.
* <p>
* The size of the list must be at least 3, otherwise this method will
* return null.
*
* @return a shape the points part of the convex hull
*/
public static SimplePolygon convexHull(int[] xs, int[] ys)
{
int length = xs.length;
// remove any invalid entries
{
int i = 0, offset = 0;
for (; i < length; i++)
{
if (xs[i] == Integer.MIN_VALUE)
{
offset++;
i++;
break;
}
}
for (; i < length; i++)
{
if (xs[i] == Integer.MIN_VALUE)
{
offset++;
continue;
}
xs[i - offset] = xs[i];
ys[i - offset] = ys[i];
}
length -= offset;
}
if (length < 3)
{
return null;
}
// find the left most point // find the left most point
Point left = findLeftMost(points); int left = findLeftMost(xs, ys, length);
// current point we are on // current point we are on
Point current = left; int current = left;
SimplePolygon out = new SimplePolygon(new int[16], new int[16], 0);
do do
{ {
ch.add(current); int cx = xs[current];
assert ch.size() <= points.size() : "hull has more points than graph"; int cy = ys[current];
if (ch.size() > points.size()) out.pushRight(cx, cy);
if (out.size() > length)
{ {
// Just to make sure we never somehow get stuck in this loop
return null; return null;
} }
// the next point - all points are to the right of the // the next point - all points are to the right of the
// line between current and next // line between current and next
Point next = null; int next = 0;
int nx = xs[next];
int ny = ys[next];
for (Point p : points) for (int i = 1; i < length; i++)
{ {
if (next == null) long cp = crossProduct(cx, cy, xs[i], ys[i], nx, ny);
if (cp > 0 || (cp == 0 && square(cx - xs[i]) + square(cy - ys[i]) > square(cx - nx) + square(cy - ny)))
{ {
next = p; next = i;
continue; nx = xs[next];
ny = ys[next];
} }
long cp = crossProduct(current, p, next);
if (cp > 0 || (cp == 0 && current.distanceTo(p) > current.distanceTo(next)))
{
next = p;
}
}
// Points can be null if they are behind or very close to the camera.
if (next == null)
{
return null;
} }
current = next; current = next;
} }
while (current != left); while (current != left);
return ch; return out;
} }
private static Point findLeftMost(List<Point> points) private static int square(int x)
{ {
Point left = null; return x * x;
}
for (Point p : points) private static int findLeftMost(int[] xs, int[] ys, int length)
{
int idx = 0;
int x = xs[idx];
int y = ys[idx];
for (int i = 1; i < length; i++)
{ {
if (left == null || p.getX() < left.getX()) int ix = xs[i];
if (ix < x || ix == x && ys[i] < y)
{ {
left = p; idx = i;
} x = xs[idx];
else if (p.getX() == left.getX() && p.getY() < left.getY()) y = ys[idx];
{
left = p;
} }
} }
return left; return idx;
} }
private static long crossProduct(Point p, Point q, Point r) private static long crossProduct(int px, int py, int qx, int qy, int rx, int ry)
{ {
long val = (long)(q.getY() - p.getY()) * (r.getX() - q.getX()) long val = (long) (qy - py) * (rx - qx)
- (long)(q.getX() - p.getX()) * (r.getY() - q.getY()); - (long) (qx - px) * (ry - qy);
return val; return val;
} }
} }