Merge remote-tracking branch 'github/jarvis'
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,13 @@ import java.util.List;
|
||||
import net.runelite.api.model.Triangle;
|
||||
import net.runelite.api.model.Vertex;
|
||||
|
||||
public class Model
|
||||
public class Model extends Renderable
|
||||
{
|
||||
private final net.runelite.rs.api.Model model;
|
||||
|
||||
public Model(net.runelite.rs.api.Model model)
|
||||
{
|
||||
super(model);
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public class Node
|
||||
|
||||
if (node instanceof net.runelite.rs.api.Renderable)
|
||||
{
|
||||
return new Renderable((net.runelite.rs.api.Renderable) node);
|
||||
return Renderable.of((net.runelite.rs.api.Renderable) node);
|
||||
}
|
||||
|
||||
if (node instanceof net.runelite.rs.api.WidgetNode)
|
||||
|
||||
@@ -128,24 +128,12 @@ public class Player extends Actor
|
||||
Vertex c = triangle.getC();
|
||||
|
||||
Triangle rotatedTriangle = new Triangle(
|
||||
rotate(a, orientation),
|
||||
rotate(b, orientation),
|
||||
rotate(c, orientation)
|
||||
a.rotate(orientation),
|
||||
b.rotate(orientation),
|
||||
c.rotate(orientation)
|
||||
);
|
||||
rotatedTriangles.add(rotatedTriangle);
|
||||
}
|
||||
return rotatedTriangles;
|
||||
}
|
||||
|
||||
private Vertex rotate(Vertex vertex, int orientation)
|
||||
{
|
||||
int sin = Perspective.SINE[orientation];
|
||||
int cos = Perspective.COSINE[orientation];
|
||||
|
||||
return new Vertex(
|
||||
vertex.getX() * cos + vertex.getZ() * sin >> 16,
|
||||
vertex.getY(),
|
||||
vertex.getZ() * cos - vertex.getX() * sin >> 16
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,22 @@ public class Renderable extends Node
|
||||
|
||||
public Model getModel()
|
||||
{
|
||||
return new Model(renderable.getModel());
|
||||
net.runelite.rs.api.Model model = renderable.getModel();
|
||||
return model != null ? new Model(model) : null;
|
||||
}
|
||||
|
||||
public static Renderable of(net.runelite.rs.api.Renderable renderable)
|
||||
{
|
||||
return (Renderable) Node.of(renderable);
|
||||
if (renderable == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (renderable instanceof net.runelite.rs.api.Model)
|
||||
{
|
||||
return new Model((net.runelite.rs.api.Model) renderable);
|
||||
}
|
||||
|
||||
return new Renderable(renderable);
|
||||
}
|
||||
}
|
||||
|
||||
117
runelite-api/src/main/java/net/runelite/api/model/Jarvis.java
Normal file
117
runelite-api/src/main/java/net/runelite/api/model/Jarvis.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@
|
||||
*/
|
||||
package net.runelite.api.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Triangle
|
||||
{
|
||||
private final Vertex a;
|
||||
@@ -43,6 +45,43 @@ public class Triangle
|
||||
return "Triangle{" + "a=" + a + ", b=" + b + ", c=" + c + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 7;
|
||||
hash = 13 * hash + Objects.hashCode(this.a);
|
||||
hash = 13 * hash + Objects.hashCode(this.b);
|
||||
hash = 13 * hash + Objects.hashCode(this.c);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final Triangle other = (Triangle) obj;
|
||||
if (!Objects.equals(this.a, other.a))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.b, other.b))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.c, other.c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Vertex getA()
|
||||
{
|
||||
return a;
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
*/
|
||||
package net.runelite.api.model;
|
||||
|
||||
import net.runelite.api.Perspective;
|
||||
|
||||
public class Vertex
|
||||
{
|
||||
private final int x;
|
||||
@@ -43,6 +45,43 @@ public class Vertex
|
||||
return "Vertex{" + "x=" + x + ", y=" + y + ", z=" + z + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 7;
|
||||
hash = 67 * hash + this.x;
|
||||
hash = 67 * hash + this.y;
|
||||
hash = 67 * hash + this.z;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final Vertex other = (Vertex) obj;
|
||||
if (this.x != other.x)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this.y != other.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this.z != other.z)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getX()
|
||||
{
|
||||
return x;
|
||||
@@ -57,4 +96,21 @@ public class Vertex
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the vertex by the given orientation
|
||||
* @param orientation
|
||||
* @return the newly rotated vertex
|
||||
*/
|
||||
public Vertex rotate(int orientation)
|
||||
{
|
||||
int sin = Perspective.SINE[orientation];
|
||||
int cos = Perspective.COSINE[orientation];
|
||||
|
||||
return new Vertex(
|
||||
x * cos + z * sin >> 16,
|
||||
y,
|
||||
z * cos - x * sin >> 16
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -252,6 +252,14 @@ public class DevToolsOverlay extends Overlay
|
||||
{
|
||||
OverlayUtil.renderTileOverlay(graphics, gameObject, "ID: " + gameObject.getId(), GREEN);
|
||||
}
|
||||
|
||||
// Draw a polygon around the convex hull
|
||||
// of the model vertices
|
||||
Polygon p = gameObject.getConvexHull();
|
||||
if (p != null)
|
||||
{
|
||||
graphics.drawPolygon(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ package net.runelite.rs.api;
|
||||
|
||||
import net.runelite.mapping.Import;
|
||||
|
||||
public interface Model
|
||||
public interface Model extends Renderable
|
||||
{
|
||||
@Import("verticesX")
|
||||
int[] getVerticesX();
|
||||
|
||||
Reference in New Issue
Block a user