diff --git a/runelite-api/src/main/java/net/runelite/api/GameObject.java b/runelite-api/src/main/java/net/runelite/api/GameObject.java index 12c9fef56b..2c83eb9b4c 100644 --- a/runelite-api/src/main/java/net/runelite/api/GameObject.java +++ b/runelite-api/src/main/java/net/runelite/api/GameObject.java @@ -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 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 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; + } } diff --git a/runelite-api/src/main/java/net/runelite/api/model/Jarvis.java b/runelite-api/src/main/java/net/runelite/api/model/Jarvis.java new file mode 100644 index 0000000000..04ac76e20f --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/model/Jarvis.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2017, Adam + * 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 convexHull(List points) + { + if (points.size() < 3) + { + return null; + } + + List 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 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; + } +} diff --git a/runelite-api/src/test/java/net/runelite/api/model/JarvisTest.java b/runelite-api/src/test/java/net/runelite/api/model/JarvisTest.java new file mode 100644 index 0000000000..1a6f3f1aaa --- /dev/null +++ b/runelite-api/src/test/java/net/runelite/api/model/JarvisTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2017, Adam + * 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 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 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)); + } +}