runelite-api: add Jarvis march algorithm and and getConvexHull to GameObject

This commit is contained in:
Adam
2017-08-12 22:28:34 -04:00
parent 409aa9eb1f
commit 1a95cdf5a6
3 changed files with 311 additions and 0 deletions

View File

@@ -24,6 +24,12 @@
*/
package net.runelite.api;
import java.awt.Polygon;
import java.util.ArrayList;
import java.util.List;
import net.runelite.api.model.Jarvis;
import net.runelite.api.model.Vertex;
/**
*
* @author Adam
@@ -55,4 +61,82 @@ public class GameObject extends TileObject
{
return gameObject.getY();
}
public Renderable getRenderable()
{
return Renderable.of(gameObject.getRenderable());
}
public Polygon getConvexHull()
{
Renderable renderable = getRenderable();
if (renderable == null)
{
return null;
}
Model model;
if (renderable instanceof Model)
{
model = (Model) renderable;
}
else
{
model = renderable.getModel();
}
if (model == null)
{
return null;
}
int localX = gameObject.getX();
int localY = gameObject.getY();
// models are orientated north (1024) and there are 2048 angles total
int orientation = (gameObject.getOrientation() + 1024) % 2048;
List<Vertex> verticies = model.getVertices();
if (orientation != 0)
{
// rotate verticies
for (int i = 0; i < verticies.size(); ++i)
{
Vertex v = verticies.get(i);
verticies.set(i, v.rotate(orientation));
}
}
List<Point> points = new ArrayList<>();
for (Vertex v : verticies)
{
// Compute canvas location of vertex
Point p = Perspective.worldToCanvas(client,
localX - v.getX(),
localY - v.getZ(),
-v.getY());
if (p != null)
{
points.add(p);
}
}
// Run Jarvis march algorithm
points = Jarvis.convexHull(points);
if (points == null)
{
return null;
}
// Convert to a polygon
Polygon p = new Polygon();
for (Point point : points)
{
p.addPoint(point.getX(), point.getY());
}
return p;
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.api.model;
import java.util.ArrayList;
import java.util.List;
import net.runelite.api.Point;
/**
* Implementation of the Jarvis march algorithm
* https://en.wikipedia.org/wiki/Gift_wrapping_algorithm
* @author adam
*/
public class Jarvis
{
/**
* compute the convex hull of a given set of points
*
* @param points
* @return
*/
public static List<Point> convexHull(List<Point> points)
{
if (points.size() < 3)
{
return null;
}
List<Point> ch = new ArrayList<>();
// find the left most point
Point left = findLeftMost(points);
// current point we are on
Point current = left;
do
{
ch.add(current);
// the next point - all points are to the right of the
// line between current and next
Point next = null;
for (Point p : points)
{
if (next == null)
{
next = p;
continue;
}
int cp = crossProduct(current, p, next);
if (cp > 0 || (cp == 0 && current.distanceTo(p) > current.distanceTo(next)))
{
next = p;
}
}
assert next != null;
assert ch.size() <= points.size() : "hull has more points than graph";
current = next;
}
while (current != left);
return ch;
}
private static Point findLeftMost(List<Point> points)
{
Point left = null;
for (Point p : points)
{
if (left == null || p.getX() < left.getX())
{
left = p;
}
else if (p.getX() == left.getX() && p.getY() < left.getY())
{
left = p;
}
}
return left;
}
private static int crossProduct(Point p, Point q, Point r)
{
int val = (q.getY() - p.getY()) * (r.getX() - q.getX())
- (q.getX() - p.getX()) * (r.getY() - q.getY());
return val;
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.api.model;
import java.util.Arrays;
import java.util.List;
import net.runelite.api.Point;
import org.junit.Assert;
import org.junit.Test;
public class JarvisTest
{
@Test
public void test()
{
Point[] points =
{
new Point(0, 3),
new Point(1, 1),
new Point(2, 2),
new Point(4, 4),
new Point(0, 0),
new Point(1, 2),
new Point(3, 1),
new Point(3, 3)
};
List<Point> result = Jarvis.convexHull(Arrays.asList(points));
Assert.assertEquals(4, result.size());
Assert.assertEquals(new Point(0, 0), result.get(0));
Assert.assertEquals(new Point(0, 3), result.get(1));
Assert.assertEquals(new Point(4, 4), result.get(2));
Assert.assertEquals(new Point(3, 1), result.get(3));
}
@Test
public void test2()
{
Point[] points =
{
new Point(0, 3),
new Point(4, 2),
new Point(3, 5),
new Point(5, 3),
new Point(3, 0),
new Point(1, 1),
new Point(1, 2),
new Point(2, 2)
};
List<Point> result = Jarvis.convexHull(Arrays.asList(points));
Assert.assertEquals(5, result.size());
Assert.assertEquals(new Point(0, 3), result.get(0));
Assert.assertEquals(new Point(3, 5), result.get(1));
Assert.assertEquals(new Point(5, 3), result.get(2));
Assert.assertEquals(new Point(3, 0), result.get(3));
Assert.assertEquals(new Point(1, 1), result.get(4));
}
@Test
public void testCollinear()
{
Point[] points = new Point[]
{
new Point(604, 76),
new Point(609, 81),
new Point(606, 78),
new Point(602, 74),
new Point(610, 74),
new Point(609, 77),
new Point(602, 72),
new Point(606, 77),
new Point(611, 77),
new Point(607, 72),
new Point(616, 74),
new Point(611, 70),
new Point(611, 71),
new Point(610, 73),
new Point(614, 82),
new Point(615, 79),
new Point(615, 77),
new Point(615, 76),
new Point(634, 71),
new Point(604, 76)
};
Jarvis.convexHull(Arrays.asList(points));
}
}