Merge remote-tracking branch 'runelite/master' into 2009-merge
This commit is contained in:
@@ -5,6 +5,7 @@ description = 'Web API'
|
|||||||
dependencies {
|
dependencies {
|
||||||
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombok
|
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||||
|
|
||||||
|
compileOnly group: 'javax.inject', name: 'javax.inject', version: javaxInject
|
||||||
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombok
|
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||||
|
|
||||||
implementation group: 'com.google.code.gson', name: 'gson', version: gson
|
implementation group: 'com.google.code.gson', name: 'gson', version: gson
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ import com.google.gson.JsonParseException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import javax.inject.Inject;
|
||||||
import net.runelite.http.api.RuneLiteAPI;
|
import net.runelite.http.api.RuneLiteAPI;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -39,6 +41,14 @@ public class FeedClient
|
|||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(FeedClient.class);
|
private static final Logger logger = LoggerFactory.getLogger(FeedClient.class);
|
||||||
|
|
||||||
|
private final OkHttpClient client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FeedClient(OkHttpClient client)
|
||||||
|
{
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
public FeedResult lookupFeed() throws IOException
|
public FeedResult lookupFeed() throws IOException
|
||||||
{
|
{
|
||||||
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
||||||
@@ -51,7 +61,7 @@ public class FeedClient
|
|||||||
.url(url)
|
.url(url)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,8 +35,10 @@ import java.lang.reflect.Type;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.inject.Inject;
|
||||||
import net.runelite.http.api.RuneLiteAPI;
|
import net.runelite.http.api.RuneLiteAPI;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -46,6 +48,14 @@ public class ItemClient
|
|||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ItemClient.class);
|
private static final Logger logger = LoggerFactory.getLogger(ItemClient.class);
|
||||||
|
|
||||||
|
private final OkHttpClient client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ItemClient(OkHttpClient client)
|
||||||
|
{
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
public ItemPrice lookupItemPrice(int itemId) throws IOException
|
public ItemPrice lookupItemPrice(int itemId) throws IOException
|
||||||
{
|
{
|
||||||
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
||||||
@@ -60,7 +70,7 @@ public class ItemClient
|
|||||||
.url(url)
|
.url(url)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
@@ -96,7 +106,7 @@ public class ItemClient
|
|||||||
.url(url)
|
.url(url)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
@@ -129,7 +139,7 @@ public class ItemClient
|
|||||||
|
|
||||||
return Observable.defer(() ->
|
return Observable.defer(() ->
|
||||||
{
|
{
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
@@ -162,7 +172,7 @@ public class ItemClient
|
|||||||
.url(url)
|
.url(url)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
@@ -197,7 +207,7 @@ public class ItemClient
|
|||||||
.url(url)
|
.url(url)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
@@ -233,7 +243,7 @@ public class ItemClient
|
|||||||
.url(url)
|
.url(url)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import com.google.gson.JsonParseException;
|
|||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import javax.inject.Inject;
|
||||||
import net.runelite.http.api.RuneLiteAPI;
|
import net.runelite.http.api.RuneLiteAPI;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -40,6 +42,14 @@ public class WorldClient
|
|||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(WorldClient.class);
|
private static final Logger logger = LoggerFactory.getLogger(WorldClient.class);
|
||||||
|
|
||||||
|
private final OkHttpClient client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public WorldClient(OkHttpClient client)
|
||||||
|
{
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
public Observable<WorldResult> lookupWorlds()
|
public Observable<WorldResult> lookupWorlds()
|
||||||
{
|
{
|
||||||
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
||||||
@@ -54,7 +64,7 @@ public class WorldClient
|
|||||||
.url(url)
|
.url(url)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
try (Response response = client.newCall(request).execute())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.http.service.feed;
|
package net.runelite.http.service.feed;
|
||||||
|
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.Hasher;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -51,7 +54,26 @@ public class FeedController
|
|||||||
private final TwitterService twitterService;
|
private final TwitterService twitterService;
|
||||||
private final OSRSNewsService osrsNewsService;
|
private final OSRSNewsService osrsNewsService;
|
||||||
|
|
||||||
private FeedResult feedResult;
|
private static class MemoizedFeed
|
||||||
|
{
|
||||||
|
final FeedResult feedResult;
|
||||||
|
final String hash;
|
||||||
|
|
||||||
|
MemoizedFeed(FeedResult feedResult)
|
||||||
|
{
|
||||||
|
this.feedResult = feedResult;
|
||||||
|
|
||||||
|
Hasher hasher = Hashing.sha256().newHasher();
|
||||||
|
for (FeedItem itemPrice : feedResult.getItems())
|
||||||
|
{
|
||||||
|
hasher.putBytes(itemPrice.getTitle().getBytes()).putBytes(itemPrice.getContent().getBytes());
|
||||||
|
}
|
||||||
|
HashCode code = hasher.hash();
|
||||||
|
hash = code.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MemoizedFeed memoizedFeed;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public FeedController(BlogService blogService, TwitterService twitterService, OSRSNewsService osrsNewsService)
|
public FeedController(BlogService blogService, TwitterService twitterService, OSRSNewsService osrsNewsService)
|
||||||
@@ -93,20 +115,21 @@ public class FeedController
|
|||||||
log.warn(e.getMessage());
|
log.warn(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
feedResult = new FeedResult(items);
|
memoizedFeed = new MemoizedFeed(new FeedResult(items));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<FeedResult> getFeed()
|
public ResponseEntity<FeedResult> getFeed()
|
||||||
{
|
{
|
||||||
if (feedResult == null)
|
if (memoizedFeed == null)
|
||||||
{
|
{
|
||||||
return ResponseEntity.notFound()
|
return ResponseEntity.notFound()
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
|
.eTag(memoizedFeed.hash)
|
||||||
.cacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES).cachePublic())
|
.cacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES).cachePublic())
|
||||||
.body(feedResult);
|
.body(memoizedFeed.feedResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ import com.google.common.base.Supplier;
|
|||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.Hasher;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -53,20 +56,39 @@ public class ItemController
|
|||||||
private static final String RUNELITE_CACHE = "RuneLite-Cache";
|
private static final String RUNELITE_CACHE = "RuneLite-Cache";
|
||||||
private static final int MAX_BATCH_LOOKUP = 1024;
|
private static final int MAX_BATCH_LOOKUP = 1024;
|
||||||
|
|
||||||
|
private static class MemoizedPrices
|
||||||
|
{
|
||||||
|
final ItemPrice[] prices;
|
||||||
|
final String hash;
|
||||||
|
|
||||||
|
MemoizedPrices(ItemPrice[] prices)
|
||||||
|
{
|
||||||
|
this.prices = prices;
|
||||||
|
|
||||||
|
Hasher hasher = Hashing.sha256().newHasher();
|
||||||
|
for (ItemPrice itemPrice : prices)
|
||||||
|
{
|
||||||
|
hasher.putInt(itemPrice.getId()).putInt(itemPrice.getPrice());
|
||||||
|
}
|
||||||
|
HashCode code = hasher.hash();
|
||||||
|
hash = code.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Cache<Integer, Integer> cachedEmpty = CacheBuilder.newBuilder()
|
private final Cache<Integer, Integer> cachedEmpty = CacheBuilder.newBuilder()
|
||||||
.maximumSize(1024L)
|
.maximumSize(1024L)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private final ItemService itemService;
|
private final ItemService itemService;
|
||||||
|
|
||||||
private final Supplier<ItemPrice[]> memorizedPrices;
|
private final Supplier<MemoizedPrices> memoizedPrices;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ItemController(ItemService itemService)
|
public ItemController(ItemService itemService)
|
||||||
{
|
{
|
||||||
this.itemService = itemService;
|
this.itemService = itemService;
|
||||||
|
|
||||||
memorizedPrices = Suppliers.memoizeWithExpiration(() -> itemService.fetchPrices().stream()
|
memoizedPrices = Suppliers.memoizeWithExpiration(() -> new MemoizedPrices(itemService.fetchPrices().stream()
|
||||||
.map(priceEntry ->
|
.map(priceEntry ->
|
||||||
{
|
{
|
||||||
ItemPrice itemPrice = new ItemPrice();
|
ItemPrice itemPrice = new ItemPrice();
|
||||||
@@ -76,7 +98,7 @@ public class ItemController
|
|||||||
itemPrice.setTime(priceEntry.getTime());
|
itemPrice.setTime(priceEntry.getTime());
|
||||||
return itemPrice;
|
return itemPrice;
|
||||||
})
|
})
|
||||||
.toArray(ItemPrice[]::new), 30, TimeUnit.MINUTES);
|
.toArray(ItemPrice[]::new)), 30, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{itemId}")
|
@GetMapping("/{itemId}")
|
||||||
@@ -220,8 +242,10 @@ public class ItemController
|
|||||||
@GetMapping("/prices")
|
@GetMapping("/prices")
|
||||||
public ResponseEntity<ItemPrice[]> prices()
|
public ResponseEntity<ItemPrice[]> prices()
|
||||||
{
|
{
|
||||||
|
MemoizedPrices memorizedPrices = this.memoizedPrices.get();
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
|
.eTag(memorizedPrices.hash)
|
||||||
.cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES).cachePublic())
|
.cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES).cachePublic())
|
||||||
.body(memorizedPrices.get());
|
.body(memorizedPrices.prices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ package net.runelite.api;
|
|||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import net.runelite.api.coords.LocalPoint;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
@@ -213,7 +214,7 @@ public interface Actor extends Entity, Locatable
|
|||||||
* @return the convex hull
|
* @return the convex hull
|
||||||
* @see net.runelite.api.model.Jarvis
|
* @see net.runelite.api.model.Jarvis
|
||||||
*/
|
*/
|
||||||
Polygon getConvexHull();
|
Shape getConvexHull();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the world area that the actor occupies.
|
* Gets the world area that the actor occupies.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.api;
|
package net.runelite.api;
|
||||||
|
|
||||||
import java.awt.Polygon;
|
import java.awt.Shape;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a decorative object, such as an object on a wall.
|
* Represents a decorative object, such as an object on a wall.
|
||||||
@@ -37,8 +37,8 @@ public interface DecorativeObject extends TileObject
|
|||||||
* @return the convex hull
|
* @return the convex hull
|
||||||
* @see api.model.Jarvis
|
* @see api.model.Jarvis
|
||||||
*/
|
*/
|
||||||
Polygon getConvexHull();
|
Shape getConvexHull();
|
||||||
Polygon getConvexHull2();
|
Shape getConvexHull2();
|
||||||
|
|
||||||
Entity getEntity1();
|
Entity getEntity1();
|
||||||
Entity getEntity2();
|
Entity getEntity2();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.api;
|
package net.runelite.api;
|
||||||
|
|
||||||
import java.awt.Polygon;
|
import java.awt.Shape;
|
||||||
import net.runelite.api.coords.Angle;
|
import net.runelite.api.coords.Angle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,19 +54,12 @@ public interface GameObject extends TileObject
|
|||||||
Point getSceneMaxLocation();
|
Point getSceneMaxLocation();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the convex hull of the actors model.
|
* Gets the convex hull of the object's model.
|
||||||
*
|
*
|
||||||
* @return the convex hull
|
* @return the convex hull
|
||||||
* @see //net.runelite.api.model.Jarvis
|
* @see //net.runelite.api.model.Jarvis
|
||||||
*/
|
*/
|
||||||
Polygon getConvexHull();
|
Shape getConvexHull();
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the polygons that make up the game object model.
|
|
||||||
*
|
|
||||||
* @return the model polygons
|
|
||||||
*/
|
|
||||||
Polygon[] getPolygons();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the orientation of the object.
|
* Gets the orientation of the object.
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.api;
|
package net.runelite.api;
|
||||||
|
|
||||||
|
import java.awt.Shape;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an object on the ground of a tile.
|
* Represents an object on the ground of a tile.
|
||||||
*/
|
*/
|
||||||
@@ -32,4 +34,12 @@ public interface GroundObject extends TileObject
|
|||||||
Entity getEntity();
|
Entity getEntity();
|
||||||
|
|
||||||
Model getModel();
|
Model getModel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the convex hull of the objects model.
|
||||||
|
*
|
||||||
|
* @return the convex hull
|
||||||
|
* @see net.runelite.api.model.Jarvis
|
||||||
|
*/
|
||||||
|
Shape getConvexHull();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,24 +24,22 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.api;
|
package net.runelite.api;
|
||||||
|
|
||||||
import java.awt.geom.Path2D;
|
|
||||||
import static net.runelite.api.Constants.TILE_FLAG_BRIDGE;
|
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Shape;
|
||||||
import java.awt.geom.Area;
|
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import static net.runelite.api.Constants.TILE_FLAG_BRIDGE;
|
||||||
import net.runelite.api.coords.LocalPoint;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
|
import net.runelite.api.geometry.RectangleUnion;
|
||||||
|
import net.runelite.api.geometry.Shapes;
|
||||||
|
import net.runelite.api.geometry.SimplePolygon;
|
||||||
import net.runelite.api.model.Jarvis;
|
import net.runelite.api.model.Jarvis;
|
||||||
import net.runelite.api.model.Triangle;
|
|
||||||
import net.runelite.api.model.Vertex;
|
|
||||||
import net.runelite.api.widgets.Widget;
|
import net.runelite.api.widgets.Widget;
|
||||||
import net.runelite.api.widgets.WidgetInfo;
|
import net.runelite.api.widgets.WidgetInfo;
|
||||||
|
|
||||||
@@ -149,7 +147,74 @@ public class Perspective
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates a model's vertices into 2d space
|
||||||
|
*/
|
||||||
|
public static void modelToCanvas(Client client, int end, int x3dCenter, int y3dCenter, int z3dCenter, int rotate, int[] x3d, int[] y3d, int[] z3d, int[] x2d, int[] y2d)
|
||||||
|
{
|
||||||
|
final int
|
||||||
|
cameraPitch = client.getCameraPitch(),
|
||||||
|
cameraYaw = client.getCameraYaw(),
|
||||||
|
|
||||||
|
pitchSin = SINE[cameraPitch],
|
||||||
|
pitchCos = COSINE[cameraPitch],
|
||||||
|
yawSin = SINE[cameraYaw],
|
||||||
|
yawCos = COSINE[cameraYaw],
|
||||||
|
rotateSin = SINE[rotate],
|
||||||
|
rotateCos = COSINE[rotate],
|
||||||
|
|
||||||
|
cx = x3dCenter - client.getCameraX(),
|
||||||
|
cy = y3dCenter - client.getCameraY(),
|
||||||
|
cz = z3dCenter - client.getCameraZ(),
|
||||||
|
|
||||||
|
viewportXMiddle = client.getViewportWidth() / 2,
|
||||||
|
viewportYMiddle = client.getViewportHeight() / 2,
|
||||||
|
viewportXOffset = client.getViewportXOffset(),
|
||||||
|
viewportYOffset = client.getViewportYOffset(),
|
||||||
|
|
||||||
|
zoom3d = client.getScale();
|
||||||
|
|
||||||
|
for (int i = 0; i < end; i++)
|
||||||
|
{
|
||||||
|
int x = x3d[i];
|
||||||
|
int y = y3d[i];
|
||||||
|
int z = z3d[i];
|
||||||
|
|
||||||
|
if (rotate != 0)
|
||||||
|
{
|
||||||
|
int x0 = x;
|
||||||
|
x = x0 * rotateCos + y * rotateSin >> 16;
|
||||||
|
y = y * rotateCos - x0 * rotateSin >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
x += cx;
|
||||||
|
y += cy;
|
||||||
|
z += cz;
|
||||||
|
|
||||||
|
final int
|
||||||
|
x1 = x * yawCos + y * yawSin >> 16,
|
||||||
|
y1 = y * yawCos - x * yawSin >> 16,
|
||||||
|
y2 = z * pitchCos - y1 * pitchSin >> 16,
|
||||||
|
z1 = y1 * pitchCos + z * pitchSin >> 16;
|
||||||
|
|
||||||
|
int viewX, viewY;
|
||||||
|
|
||||||
|
if (z1 < 50)
|
||||||
|
{
|
||||||
|
viewX = Integer.MIN_VALUE;
|
||||||
|
viewY = Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
viewX = (viewportXMiddle + x1 * zoom3d / z1) + viewportXOffset;
|
||||||
|
viewY = (viewportYMiddle + y2 * zoom3d / z1) + viewportYOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
x2d[i] = viewX;
|
||||||
|
y2d[i] = viewY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -490,244 +555,178 @@ public class Perspective
|
|||||||
* Get the on-screen clickable area of {@code model} as though it's for the
|
* Get the on-screen clickable area of {@code model} as though it's for the
|
||||||
* object on the tile at ({@code localX}, {@code localY}) and rotated to
|
* object on the tile at ({@code localX}, {@code localY}) and rotated to
|
||||||
* angle {@code orientation}.
|
* angle {@code orientation}.
|
||||||
*
|
* @param client the game client
|
||||||
* @param client the game client
|
* @param model the model to calculate a clickbox for
|
||||||
* @param model the model to calculate a clickbox for
|
|
||||||
* @param orientation the orientation of the model (0-2048, where 0 is north)
|
* @param orientation the orientation of the model (0-2048, where 0 is north)
|
||||||
* @param point the coordinate of the tile
|
* @param point the coordinate of the tile
|
||||||
* @return the clickable area of the model
|
* @return the clickable area of the model
|
||||||
*/
|
*/
|
||||||
public static @Nullable Area getClickbox(@Nonnull Client client, Model model, int orientation, @Nonnull LocalPoint point)
|
@Nullable
|
||||||
|
public static Shape getClickbox(@Nonnull Client client, Model model, int orientation, LocalPoint point)
|
||||||
{
|
{
|
||||||
if (model == null)
|
if (model == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Triangle> triangles = model.getTriangles().stream()
|
int x = point.getX();
|
||||||
.map(triangle -> triangle.rotate(orientation))
|
int y = point.getY();
|
||||||
.collect(Collectors.toList());
|
int z = getTileHeight(client, point, client.getPlane());
|
||||||
|
|
||||||
List<Vertex> vertices = model.getVertices().stream()
|
SimplePolygon bounds = calculateAABB(client, model, orientation, x, y, z);
|
||||||
.map(v -> v.rotate(orientation))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
Area clickBox = get2DGeometry(client, triangles, point);
|
if (bounds == null)
|
||||||
Area visibleAABB = getAABB(client, vertices, point);
|
|
||||||
|
|
||||||
if (visibleAABB == null)
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
clickBox.intersect(visibleAABB);
|
if (model.isClickable())
|
||||||
return clickBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if a given point is off-screen.
|
|
||||||
*
|
|
||||||
* @param client
|
|
||||||
* @param point
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static boolean isOffscreen(@Nonnull Client client, @Nonnull Point point)
|
|
||||||
{
|
|
||||||
return (point.getX() < 0 || point.getX() >= client.getViewportWidth())
|
|
||||||
&& (point.getY() < 0 || point.getY() >= client.getViewportHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @Nonnull Area get2DGeometry(
|
|
||||||
@Nonnull Client client,
|
|
||||||
@Nonnull List<Triangle> triangles,
|
|
||||||
@Nonnull LocalPoint point
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int radius = 5;
|
|
||||||
Path2D.Double geometry = new Path2D.Double();
|
|
||||||
|
|
||||||
final int tileHeight = getTileHeight(client, point, client.getPlane());
|
|
||||||
|
|
||||||
for (Triangle triangle : triangles)
|
|
||||||
{
|
{
|
||||||
net.runelite.api.model.Vertex _a = triangle.getA();
|
return bounds;
|
||||||
Point a = localToCanvas(client,
|
|
||||||
point.getX() - _a.getX(),
|
|
||||||
point.getY() - _a.getZ(),
|
|
||||||
tileHeight + _a.getY());
|
|
||||||
if (a == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex _b = triangle.getB();
|
|
||||||
Point b = localToCanvas(client,
|
|
||||||
point.getX() - _b.getX(),
|
|
||||||
point.getY() - _b.getZ(),
|
|
||||||
tileHeight + _b.getY());
|
|
||||||
if (b == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vertex _c = triangle.getC();
|
|
||||||
Point c = localToCanvas(client,
|
|
||||||
point.getX() - _c.getX(),
|
|
||||||
point.getY() - _c.getZ(),
|
|
||||||
tileHeight + _c.getY());
|
|
||||||
if (c == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOffscreen(client, a) && isOffscreen(client, b) && isOffscreen(client, c))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int minX = Math.min(Math.min(a.getX(), b.getX()), c.getX());
|
|
||||||
int minY = Math.min(Math.min(a.getY(), b.getY()), c.getY());
|
|
||||||
|
|
||||||
// For some reason, this calculation is always 4 pixels short of the actual in-client one
|
|
||||||
int maxX = Math.max(Math.max(a.getX(), b.getX()), c.getX()) + 4;
|
|
||||||
int maxY = Math.max(Math.max(a.getY(), b.getY()), c.getY()) + 4;
|
|
||||||
|
|
||||||
Rectangle clickableRect = new Rectangle(
|
|
||||||
minX - radius, minY - radius,
|
|
||||||
maxX - minX + radius, maxY - minY + radius
|
|
||||||
);
|
|
||||||
|
|
||||||
if (geometry.contains(clickableRect))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry.append(clickableRect, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Area(geometry);
|
Shapes<SimplePolygon> bounds2d = calculate2DBounds(client, model, orientation, x, y, z);
|
||||||
}
|
if (bounds2d == null)
|
||||||
|
|
||||||
private static Area getAABB(
|
|
||||||
@Nonnull Client client,
|
|
||||||
@Nonnull List<Vertex> vertices,
|
|
||||||
@Nonnull LocalPoint point
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int maxX = 0;
|
|
||||||
int minX = 0;
|
|
||||||
int maxY = 0;
|
|
||||||
int minY = 0;
|
|
||||||
int maxZ = 0;
|
|
||||||
int minZ = 0;
|
|
||||||
|
|
||||||
for (Vertex vertex : vertices)
|
|
||||||
{
|
|
||||||
int x = vertex.getX();
|
|
||||||
int y = vertex.getY();
|
|
||||||
int z = vertex.getZ();
|
|
||||||
|
|
||||||
if (x > maxX)
|
|
||||||
{
|
|
||||||
maxX = x;
|
|
||||||
}
|
|
||||||
if (x < minX)
|
|
||||||
{
|
|
||||||
minX = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y > maxY)
|
|
||||||
{
|
|
||||||
maxY = y;
|
|
||||||
}
|
|
||||||
if (y < minY)
|
|
||||||
{
|
|
||||||
minY = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (z > maxZ)
|
|
||||||
{
|
|
||||||
maxZ = z;
|
|
||||||
}
|
|
||||||
if (z < minZ)
|
|
||||||
{
|
|
||||||
minZ = z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int centerX = (minX + maxX) / 2;
|
|
||||||
int centerY = (minY + maxY) / 2;
|
|
||||||
int centerZ = (minZ + maxZ) / 2;
|
|
||||||
|
|
||||||
int extremeX = (maxX - minX + 1) / 2;
|
|
||||||
int extremeY = (maxY - minY + 1) / 2;
|
|
||||||
int extremeZ = (maxZ - minZ + 1) / 2;
|
|
||||||
|
|
||||||
if (extremeX < 32)
|
|
||||||
{
|
|
||||||
extremeX = 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extremeZ < 32)
|
|
||||||
{
|
|
||||||
extremeZ = 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x1 = point.getX() - (centerX - extremeX);
|
|
||||||
int y1 = centerY - extremeY;
|
|
||||||
int z1 = point.getY() - (centerZ - extremeZ);
|
|
||||||
|
|
||||||
int x2 = point.getX() - (centerX + extremeX);
|
|
||||||
int y2 = centerY + extremeY;
|
|
||||||
int z2 = point.getY() - (centerZ + extremeZ);
|
|
||||||
|
|
||||||
final int tileHeight = getTileHeight(client, point, client.getPlane());
|
|
||||||
|
|
||||||
Point p1 = localToCanvas(client, x1, z1, tileHeight + y1);
|
|
||||||
Point p2 = localToCanvas(client, x1, z2, tileHeight + y1);
|
|
||||||
Point p3 = localToCanvas(client, x2, z2, tileHeight + y1);
|
|
||||||
|
|
||||||
Point p4 = localToCanvas(client, x2, z1, tileHeight + y1);
|
|
||||||
Point p5 = localToCanvas(client, x1, z1, tileHeight + y2);
|
|
||||||
Point p6 = localToCanvas(client, x1, z2, tileHeight + y2);
|
|
||||||
Point p7 = localToCanvas(client, x2, z2, tileHeight + y2);
|
|
||||||
Point p8 = localToCanvas(client, x2, z1, tileHeight + y2);
|
|
||||||
|
|
||||||
List<Point> points = new ArrayList<>(8);
|
|
||||||
points.add(p1);
|
|
||||||
points.add(p2);
|
|
||||||
points.add(p3);
|
|
||||||
points.add(p4);
|
|
||||||
points.add(p5);
|
|
||||||
points.add(p6);
|
|
||||||
points.add(p7);
|
|
||||||
points.add(p8);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
points = Jarvis.convexHull(points);
|
|
||||||
}
|
|
||||||
catch (NullPointerException e)
|
|
||||||
{
|
|
||||||
// No non-null screen points for this AABB e.g. for an way off-screen model
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (points == null)
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon hull = new Polygon();
|
for (SimplePolygon poly : bounds2d.getShapes())
|
||||||
for (Point p : points)
|
|
||||||
{
|
{
|
||||||
if (p != null)
|
poly.intersectWithConvex(bounds);
|
||||||
{
|
|
||||||
hull.addPoint(p.getX(), p.getY());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Area(hull);
|
return bounds2d;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SimplePolygon calculateAABB(Client client, Model m, int jauOrient, int x, int y, int z)
|
||||||
|
{
|
||||||
|
int ex = m.getExtremeX();
|
||||||
|
if (ex == -1)
|
||||||
|
{
|
||||||
|
// dynamic models don't get stored when they render where this normally happens
|
||||||
|
m.calculateBoundsCylinder();
|
||||||
|
m.calculateExtreme(0);
|
||||||
|
ex = m.getExtremeX();
|
||||||
|
}
|
||||||
|
|
||||||
|
int x1 = m.getCenterX();
|
||||||
|
int y1 = m.getCenterZ();
|
||||||
|
int z1 = m.getCenterY();
|
||||||
|
|
||||||
|
int ey = m.getExtremeZ();
|
||||||
|
int ez = m.getExtremeY();
|
||||||
|
|
||||||
|
int x2 = x1 + ex;
|
||||||
|
int y2 = y1 + ey;
|
||||||
|
int z2 = z1 + ez;
|
||||||
|
|
||||||
|
x1 -= ex;
|
||||||
|
y1 -= ey;
|
||||||
|
z1 -= ez;
|
||||||
|
|
||||||
|
int[] xa = new int[]{
|
||||||
|
x1, x2, x1, x2,
|
||||||
|
x1, x2, x1, x2
|
||||||
|
};
|
||||||
|
int[] ya = new int[]{
|
||||||
|
y1, y1, y2, y2,
|
||||||
|
y1, y1, y2, y2
|
||||||
|
};
|
||||||
|
int[] za = new int[]{
|
||||||
|
z1, z1, z1, z1,
|
||||||
|
z2, z2, z2, z2
|
||||||
|
};
|
||||||
|
|
||||||
|
int[] x2d = new int[8];
|
||||||
|
int[] y2d = new int[8];
|
||||||
|
|
||||||
|
modelToCanvas(client, 8, x, y, z, jauOrient, xa, ya, za, x2d, y2d);
|
||||||
|
|
||||||
|
return Jarvis.convexHull(x2d, y2d);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Shapes<SimplePolygon> calculate2DBounds(Client client, Model m, int jauOrient, int x, int y, int z)
|
||||||
|
{
|
||||||
|
int[] x2d = new int[m.getVerticesCount()];
|
||||||
|
int[] y2d = new int[m.getVerticesCount()];
|
||||||
|
|
||||||
|
Perspective.modelToCanvas(client,
|
||||||
|
m.getVerticesCount(),
|
||||||
|
x, y, z,
|
||||||
|
jauOrient,
|
||||||
|
m.getVerticesX(), m.getVerticesZ(), m.getVerticesY(),
|
||||||
|
x2d, y2d);
|
||||||
|
|
||||||
|
final int radius = 5;
|
||||||
|
|
||||||
|
int[][] tris = new int[][]{
|
||||||
|
m.getTrianglesX(),
|
||||||
|
m.getTrianglesY(),
|
||||||
|
m.getTrianglesZ()
|
||||||
|
};
|
||||||
|
|
||||||
|
int vpX1 = client.getViewportXOffset();
|
||||||
|
int vpY1 = client.getViewportXOffset();
|
||||||
|
int vpX2 = vpX1 + client.getViewportWidth();
|
||||||
|
int vpY2 = vpY1 + client.getViewportHeight();
|
||||||
|
|
||||||
|
List<RectangleUnion.Rectangle> rects = new ArrayList<>(m.getTrianglesCount());
|
||||||
|
|
||||||
|
nextTri:
|
||||||
|
for (int tri = 0; tri < m.getTrianglesCount(); tri++)
|
||||||
|
{
|
||||||
|
int
|
||||||
|
minX = Integer.MAX_VALUE,
|
||||||
|
minY = Integer.MAX_VALUE,
|
||||||
|
maxX = Integer.MIN_VALUE,
|
||||||
|
maxY = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (int[] vertex : tris)
|
||||||
|
{
|
||||||
|
final int idx = vertex[tri];
|
||||||
|
final int xs = x2d[idx];
|
||||||
|
final int ys = y2d[idx];
|
||||||
|
|
||||||
|
if (xs == Integer.MIN_VALUE || ys == Integer.MIN_VALUE)
|
||||||
|
{
|
||||||
|
continue nextTri;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xs < minX)
|
||||||
|
{
|
||||||
|
minX = xs;
|
||||||
|
}
|
||||||
|
if (xs > maxX)
|
||||||
|
{
|
||||||
|
maxX = xs;
|
||||||
|
}
|
||||||
|
if (ys < minY)
|
||||||
|
{
|
||||||
|
minY = ys;
|
||||||
|
}
|
||||||
|
if (ys > maxY)
|
||||||
|
{
|
||||||
|
maxY = ys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
minX -= radius;
|
||||||
|
minY -= radius;
|
||||||
|
maxX += radius;
|
||||||
|
maxY += radius;
|
||||||
|
|
||||||
|
if (vpX1 > maxX || vpX2 < minX || vpY1 > maxY || vpY2 < minY)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RectangleUnion.Rectangle r = new RectangleUnion.Rectangle(minX, minY, maxX, maxY);
|
||||||
|
|
||||||
|
rects.add(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RectangleUnion.union(rects);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import net.runelite.api.coords.LocalPoint;
|
|||||||
import net.runelite.api.coords.WorldPoint;
|
import net.runelite.api.coords.WorldPoint;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,5 +132,5 @@ public interface TileObject extends Locatable
|
|||||||
* @return the clickable area
|
* @return the clickable area
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Area getClickbox();
|
Shape getClickbox();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.api;
|
package net.runelite.api;
|
||||||
|
|
||||||
|
import java.awt.Shape;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the wall of a tile, which is an un-passable boundary.
|
* Represents the wall of a tile, which is an un-passable boundary.
|
||||||
*/
|
*/
|
||||||
@@ -55,4 +57,12 @@ public interface WallObject extends TileObject
|
|||||||
|
|
||||||
Model getModelA();
|
Model getModelA();
|
||||||
Model getModelB();
|
Model getModelB();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the convex hull of the objects model.
|
||||||
|
*
|
||||||
|
* @return the convex hull
|
||||||
|
* @see net.runelite.api.model.Jarvis
|
||||||
|
*/
|
||||||
|
Shape getConvexHull();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,415 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.geometry;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class RectangleUnion
|
||||||
|
{
|
||||||
|
private RectangleUnion()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
public static class Rectangle
|
||||||
|
{
|
||||||
|
private final int x1, y1, x2, y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a polygon representing the union of all of the passed rectangles.
|
||||||
|
* the passed List will be modified
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Shapes<SimplePolygon> union(List<Rectangle> lefts)
|
||||||
|
{
|
||||||
|
// https://stackoverflow.com/a/35362615/2977136
|
||||||
|
if (lefts.size() == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean trace = log.isTraceEnabled();
|
||||||
|
|
||||||
|
// Sort all of the rectangles so they are ordered by their left edge
|
||||||
|
lefts.sort(Comparator.comparing(Rectangle::getX1));
|
||||||
|
|
||||||
|
// Again, but for the right edge
|
||||||
|
// this should be relatively fast if the rectangles are similar sizes because timsort deals with partially
|
||||||
|
// presorted data well
|
||||||
|
List<Rectangle> rights = new ArrayList<>(lefts);
|
||||||
|
rights.sort(Comparator.comparing(Rectangle::getX2));
|
||||||
|
|
||||||
|
// ranges of our scan line with how many rectangles it is occluding
|
||||||
|
Segments segments = new Segments();
|
||||||
|
Shapes<SimplePolygon> out = new Shapes<>(new ArrayList<>());
|
||||||
|
ChangingState cs = new ChangingState(out);
|
||||||
|
|
||||||
|
// Walk a beam left to right, colliding with any vertical edges of rectangles
|
||||||
|
for (int l = 0, r = 0; ; )
|
||||||
|
{
|
||||||
|
Rectangle lr = null, rr = null;
|
||||||
|
if (l < lefts.size())
|
||||||
|
{
|
||||||
|
lr = lefts.get(l);
|
||||||
|
}
|
||||||
|
if (r < rights.size())
|
||||||
|
{
|
||||||
|
rr = rights.get(r);
|
||||||
|
}
|
||||||
|
if (lr == null && rr == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next edge, preferring + edges
|
||||||
|
Rectangle rect;
|
||||||
|
boolean remove = lr == null || (rr != null && rr.x2 < lr.x1);
|
||||||
|
if (remove)
|
||||||
|
{
|
||||||
|
cs.delta = -1;
|
||||||
|
cs.x = rr.x2;
|
||||||
|
r++;
|
||||||
|
rect = rr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cs.delta = 1;
|
||||||
|
cs.x = lr.x1;
|
||||||
|
l++;
|
||||||
|
rect = lr;
|
||||||
|
}
|
||||||
|
if (trace)
|
||||||
|
{
|
||||||
|
log.trace("{}{}", remove ? "-" : "+", rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
int y1 = rect.y1;
|
||||||
|
int y2 = rect.y2;
|
||||||
|
|
||||||
|
// Find or create the y1 edge
|
||||||
|
Segment n = segments.findLE(y1);
|
||||||
|
if (n == null)
|
||||||
|
{
|
||||||
|
n = segments.insertAfter(null, y1);
|
||||||
|
}
|
||||||
|
if (n.y != y1)
|
||||||
|
{
|
||||||
|
n = segments.insertAfter(n, y1);
|
||||||
|
n.value = n.previous.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; ; )
|
||||||
|
{
|
||||||
|
// create the y2 edge if the next edge is past
|
||||||
|
if (n.next == null || n.next.y > y2)
|
||||||
|
{
|
||||||
|
segments.insertAfter(n, y2);
|
||||||
|
}
|
||||||
|
cs.touch(n);
|
||||||
|
n = n.next;
|
||||||
|
if (n.y == y2)
|
||||||
|
{
|
||||||
|
cs.finish(n);
|
||||||
|
|
||||||
|
if (trace)
|
||||||
|
{
|
||||||
|
for (Segment s = segments.first; s != null; s = s.next)
|
||||||
|
{
|
||||||
|
String chunk = "";
|
||||||
|
if (s.chunk != null)
|
||||||
|
{
|
||||||
|
chunk = (s.left ? ">" : "[") + System.identityHashCode(s.chunk) + (s.left ? "]" : "<");
|
||||||
|
}
|
||||||
|
log.trace("{} = {} {}", s.y, s.value, chunk);
|
||||||
|
}
|
||||||
|
log.trace("");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert segments.allZero();
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class ChangingState
|
||||||
|
{
|
||||||
|
final Shapes<SimplePolygon> out;
|
||||||
|
|
||||||
|
int x;
|
||||||
|
int delta;
|
||||||
|
|
||||||
|
Segment first;
|
||||||
|
|
||||||
|
void touch(Segment s)
|
||||||
|
{
|
||||||
|
int oldValue = s.value;
|
||||||
|
s.value += delta;
|
||||||
|
if (oldValue <= 0 ^ s.value <= 0)
|
||||||
|
{
|
||||||
|
if (first == null)
|
||||||
|
{
|
||||||
|
first = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finish(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish(Segment s)
|
||||||
|
{
|
||||||
|
if (first == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first.chunk != null && s.chunk != null)
|
||||||
|
{
|
||||||
|
push(first);
|
||||||
|
push(s);
|
||||||
|
|
||||||
|
if (first.chunk == s.chunk)
|
||||||
|
{
|
||||||
|
Chunk c = first.chunk;
|
||||||
|
first.chunk = null;
|
||||||
|
s.chunk = null;
|
||||||
|
c.left = null;
|
||||||
|
c.right = null;
|
||||||
|
out.getShapes().add(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Chunk leftChunk, rightChunk;
|
||||||
|
if (!s.left)
|
||||||
|
{
|
||||||
|
leftChunk = s.chunk;
|
||||||
|
rightChunk = first.chunk;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
leftChunk = first.chunk;
|
||||||
|
rightChunk = s.chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.trace("Joining {} onto {}", System.identityHashCode(rightChunk), System.identityHashCode(leftChunk));
|
||||||
|
if (first.left == s.left)
|
||||||
|
{
|
||||||
|
log.trace("reverse");
|
||||||
|
if (first.left)
|
||||||
|
{
|
||||||
|
leftChunk.reverse();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rightChunk.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.trace("{} {}", first.y, s.y);
|
||||||
|
rightChunk.appendTo(leftChunk);
|
||||||
|
|
||||||
|
first.chunk = null;
|
||||||
|
s.chunk = null;
|
||||||
|
leftChunk.right.chunk = null;
|
||||||
|
rightChunk.left.chunk = null;
|
||||||
|
leftChunk.right = rightChunk.right;
|
||||||
|
leftChunk.left.chunk = leftChunk;
|
||||||
|
leftChunk.right.chunk = leftChunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (first.chunk == null && s.chunk == null)
|
||||||
|
{
|
||||||
|
first.chunk = new Chunk();
|
||||||
|
first.chunk.right = first;
|
||||||
|
first.left = false;
|
||||||
|
s.chunk = first.chunk;
|
||||||
|
first.chunk.left = s;
|
||||||
|
s.left = true;
|
||||||
|
|
||||||
|
push(first);
|
||||||
|
push(s);
|
||||||
|
}
|
||||||
|
else if (first.chunk == null)
|
||||||
|
{
|
||||||
|
push(s);
|
||||||
|
move(first, s);
|
||||||
|
push(first);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
push(first);
|
||||||
|
move(s, first);
|
||||||
|
push(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
first = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void move(Segment dst, Segment src)
|
||||||
|
{
|
||||||
|
dst.chunk = src.chunk;
|
||||||
|
dst.left = src.left;
|
||||||
|
src.chunk = null;
|
||||||
|
if (dst.left)
|
||||||
|
{
|
||||||
|
assert dst.chunk.left == src;
|
||||||
|
dst.chunk.left = dst;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert dst.chunk.right == src;
|
||||||
|
dst.chunk.right = dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void push(Segment s)
|
||||||
|
{
|
||||||
|
if (s.left)
|
||||||
|
{
|
||||||
|
s.chunk.pushLeft(x, s.y);
|
||||||
|
assert s.chunk.left == s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s.chunk.pushRight(x, s.y);
|
||||||
|
assert s.chunk.right == s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
private static class Segment
|
||||||
|
{
|
||||||
|
Segment next, previous;
|
||||||
|
|
||||||
|
Chunk chunk;
|
||||||
|
boolean left;
|
||||||
|
int y;
|
||||||
|
int value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
private static class Segments
|
||||||
|
{
|
||||||
|
Segment first;
|
||||||
|
|
||||||
|
Segment findLE(int y)
|
||||||
|
{
|
||||||
|
Segment s = first;
|
||||||
|
if (s == null || s.y > y)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (; ; )
|
||||||
|
{
|
||||||
|
if (s.y == y)
|
||||||
|
{
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment n = s.next;
|
||||||
|
if (n == null || n.y > y)
|
||||||
|
{
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Segment insertAfter(Segment before, int y)
|
||||||
|
{
|
||||||
|
Segment n = new Segment();
|
||||||
|
n.y = y;
|
||||||
|
if (before != null)
|
||||||
|
{
|
||||||
|
if (before.next != null)
|
||||||
|
{
|
||||||
|
n.next = before.next;
|
||||||
|
n.next.previous = n;
|
||||||
|
}
|
||||||
|
n.value = before.value;
|
||||||
|
before.next = n;
|
||||||
|
n.previous = before;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (first != null)
|
||||||
|
{
|
||||||
|
n.next = first;
|
||||||
|
first.previous = n;
|
||||||
|
}
|
||||||
|
first = n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allZero()
|
||||||
|
{
|
||||||
|
for (Segment s = first; s != null; s = s.next)
|
||||||
|
{
|
||||||
|
if (s.value != 0 || s.chunk != null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Chunk extends SimplePolygon
|
||||||
|
{
|
||||||
|
Segment left, right;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reverse()
|
||||||
|
{
|
||||||
|
super.reverse();
|
||||||
|
assert right.left == false;
|
||||||
|
assert left.left == true;
|
||||||
|
Segment tr = left;
|
||||||
|
left = right;
|
||||||
|
right = tr;
|
||||||
|
right.left = false;
|
||||||
|
left.left = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
212
runelite-api/src/main/java/net/runelite/api/geometry/Shapes.java
Normal file
212
runelite-api/src/main/java/net/runelite/api/geometry/Shapes.java
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.geometry;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.geom.PathIterator;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Shapes<T extends Shape> implements Shape
|
||||||
|
{
|
||||||
|
public Shapes(T ...shape)
|
||||||
|
{
|
||||||
|
this(Arrays.asList(shape));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final List<T> shapes;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle getBounds()
|
||||||
|
{
|
||||||
|
int
|
||||||
|
minX = Integer.MAX_VALUE,
|
||||||
|
minY = Integer.MAX_VALUE,
|
||||||
|
maxX = Integer.MIN_VALUE,
|
||||||
|
maxY = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (Shape shape : shapes)
|
||||||
|
{
|
||||||
|
Rectangle bounds = shape.getBounds();
|
||||||
|
minX = Math.min(bounds.x, minX);
|
||||||
|
minY = Math.min(bounds.y, minY);
|
||||||
|
maxX = Math.max(bounds.x + bounds.width, maxX);
|
||||||
|
maxY = Math.max(bounds.y + bounds.height, maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getBounds2D()
|
||||||
|
{
|
||||||
|
double
|
||||||
|
minX = Double.MAX_VALUE,
|
||||||
|
minY = Double.MAX_VALUE,
|
||||||
|
maxX = Double.MIN_VALUE,
|
||||||
|
maxY = Double.MIN_VALUE;
|
||||||
|
|
||||||
|
for (Shape shape : shapes)
|
||||||
|
{
|
||||||
|
Rectangle2D bounds = shape.getBounds2D();
|
||||||
|
minX = Math.min(bounds.getX(), minX);
|
||||||
|
minY = Math.min(bounds.getY(), minY);
|
||||||
|
maxX = Math.max(bounds.getMaxX(), maxX);
|
||||||
|
maxY = Math.max(bounds.getMaxY(), maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(double x, double y)
|
||||||
|
{
|
||||||
|
return shapes.stream().anyMatch(s -> s.contains(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Point2D p)
|
||||||
|
{
|
||||||
|
return shapes.stream().anyMatch(s -> s.contains(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(double x, double y, double w, double h)
|
||||||
|
{
|
||||||
|
return shapes.stream().anyMatch(s -> s.intersects(x, y, w, h));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(Rectangle2D r)
|
||||||
|
{
|
||||||
|
return shapes.stream().anyMatch(s -> s.intersects(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(double x, double y, double w, double h)
|
||||||
|
{
|
||||||
|
return shapes.stream().anyMatch(s -> s.contains(x, y, w, h));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Rectangle2D r)
|
||||||
|
{
|
||||||
|
return shapes.stream().anyMatch(s -> s.contains(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIterator getPathIterator(AffineTransform at)
|
||||||
|
{
|
||||||
|
return new ShapeIterator(shapes.stream()
|
||||||
|
.map(s -> s.getPathIterator(at))
|
||||||
|
.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIterator getPathIterator(AffineTransform at, double flatness)
|
||||||
|
{
|
||||||
|
return new ShapeIterator(shapes.stream()
|
||||||
|
.map(s -> s.getPathIterator(at, flatness))
|
||||||
|
.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ShapeIterator implements PathIterator
|
||||||
|
{
|
||||||
|
private final Iterator<PathIterator> iter;
|
||||||
|
private PathIterator current = null;
|
||||||
|
private final int windingRule;
|
||||||
|
|
||||||
|
ShapeIterator(Iterator<PathIterator> iter)
|
||||||
|
{
|
||||||
|
this.iter = iter;
|
||||||
|
if (iter.hasNext())
|
||||||
|
{
|
||||||
|
current = iter.next();
|
||||||
|
windingRule = current.getWindingRule();
|
||||||
|
checkDone();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
windingRule = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWindingRule()
|
||||||
|
{
|
||||||
|
return windingRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone()
|
||||||
|
{
|
||||||
|
return current == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void next()
|
||||||
|
{
|
||||||
|
current.next();
|
||||||
|
checkDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDone()
|
||||||
|
{
|
||||||
|
for (; current != null && current.isDone(); )
|
||||||
|
{
|
||||||
|
if (iter.hasNext())
|
||||||
|
{
|
||||||
|
current = iter.next();
|
||||||
|
assert windingRule == current.getWindingRule();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int currentSegment(float[] coords)
|
||||||
|
{
|
||||||
|
return current.currentSegment(coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int currentSegment(double[] coords)
|
||||||
|
{
|
||||||
|
return current.currentSegment(coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.geometry;
|
||||||
|
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.geom.PathIterator;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.runelite.api.Point;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple list of vertices that can be append or prepended to
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SimplePolygon implements Shape
|
||||||
|
{
|
||||||
|
private static final int GROW = 16;
|
||||||
|
|
||||||
|
protected int[] x, y;
|
||||||
|
protected int left, right;
|
||||||
|
|
||||||
|
public SimplePolygon()
|
||||||
|
{
|
||||||
|
this(new int[32], new int[32], 16, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimplePolygon(int[] x, int[] y, int length)
|
||||||
|
{
|
||||||
|
this(x, y, 0, length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushLeft(int xCoord, int yCoord)
|
||||||
|
{
|
||||||
|
left--;
|
||||||
|
if (left < 0)
|
||||||
|
{
|
||||||
|
expandLeft(GROW);
|
||||||
|
}
|
||||||
|
x[left] = xCoord;
|
||||||
|
y[left] = yCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void popLeft()
|
||||||
|
{
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void expandLeft(int grow)
|
||||||
|
{
|
||||||
|
int[] nx = new int[x.length + grow];
|
||||||
|
System.arraycopy(x, 0, nx, grow, x.length);
|
||||||
|
x = nx;
|
||||||
|
int[] ny = new int[nx.length];
|
||||||
|
System.arraycopy(y, 0, ny, grow, y.length);
|
||||||
|
y = ny;
|
||||||
|
left += grow;
|
||||||
|
right += grow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushRight(int xCoord, int yCoord)
|
||||||
|
{
|
||||||
|
right++;
|
||||||
|
if (right >= x.length)
|
||||||
|
{
|
||||||
|
expandRight(GROW);
|
||||||
|
}
|
||||||
|
x[right] = xCoord;
|
||||||
|
y[right] = yCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void popRight()
|
||||||
|
{
|
||||||
|
right--;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void expandRight(int grow)
|
||||||
|
{
|
||||||
|
int[] nx = new int[x.length + grow];
|
||||||
|
System.arraycopy(x, 0, nx, 0, x.length);
|
||||||
|
x = nx;
|
||||||
|
int[] ny = new int[nx.length];
|
||||||
|
System.arraycopy(y, 0, ny, 0, y.length);
|
||||||
|
y = ny;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX(int index)
|
||||||
|
{
|
||||||
|
return x[left + index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY(int index)
|
||||||
|
{
|
||||||
|
return y[left + index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return right - left + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Point> toRuneLitePointList()
|
||||||
|
{
|
||||||
|
List<Point> out = new ArrayList<>(size());
|
||||||
|
for (int i = left; i <= right; i++)
|
||||||
|
{
|
||||||
|
out.add(new Point(x[i], y[i]));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copyTo(int[] xDest, int[] yDest, int offset)
|
||||||
|
{
|
||||||
|
System.arraycopy(x, left, xDest, offset, size());
|
||||||
|
System.arraycopy(y, left, yDest, offset, size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendTo(SimplePolygon other)
|
||||||
|
{
|
||||||
|
int size = size();
|
||||||
|
if (size <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
other.expandRight(size);
|
||||||
|
copyTo(other.x, other.y, other.right + 1);
|
||||||
|
other.right += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reverse()
|
||||||
|
{
|
||||||
|
int half = size() / 2;
|
||||||
|
for (int i = 0; i < half; i++)
|
||||||
|
{
|
||||||
|
int li = left + i;
|
||||||
|
int ri = right - i;
|
||||||
|
int tx = x[li];
|
||||||
|
int ty = y[li];
|
||||||
|
x[li] = x[ri];
|
||||||
|
y[li] = y[ri];
|
||||||
|
x[ri] = tx;
|
||||||
|
y[ri] = ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clips the polygon with the passed convex polygon
|
||||||
|
*/
|
||||||
|
public void intersectWithConvex(SimplePolygon convex)
|
||||||
|
{
|
||||||
|
// Sutherland-Hodgman
|
||||||
|
int[] tx = new int[size()];
|
||||||
|
int[] ty = new int[tx.length];
|
||||||
|
|
||||||
|
int cx1 = convex.x[convex.right];
|
||||||
|
int cy1 = convex.y[convex.right];
|
||||||
|
for (int ci = convex.left; ci <= convex.right; ci++)
|
||||||
|
{
|
||||||
|
if (size() < 3)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tRight = this.right;
|
||||||
|
int tLeft = this.left;
|
||||||
|
|
||||||
|
int[] tmpX = x;
|
||||||
|
int[] tmpY = y;
|
||||||
|
|
||||||
|
this.x = tx;
|
||||||
|
this.y = ty;
|
||||||
|
this.left = 0;
|
||||||
|
this.right = -1;
|
||||||
|
tx = tmpX;
|
||||||
|
ty = tmpY;
|
||||||
|
|
||||||
|
int cx2 = convex.x[ci];
|
||||||
|
int cy2 = convex.y[ci];
|
||||||
|
|
||||||
|
int tx1 = tx[tRight];
|
||||||
|
int ty1 = ty[tRight];
|
||||||
|
|
||||||
|
for (int ti = tLeft; ti <= tRight; ti++)
|
||||||
|
{
|
||||||
|
int tx2 = tx[ti];
|
||||||
|
int ty2 = ty[ti];
|
||||||
|
|
||||||
|
int p1 = (cx2 - cx1) * (ty1 - cy1) - (cy2 - cy1) * (tx1 - cx1);
|
||||||
|
int p2 = (cx2 - cx1) * (ty2 - cy1) - (cy2 - cy1) * (tx2 - cx1);
|
||||||
|
|
||||||
|
if (p1 < 0 && p2 < 0)
|
||||||
|
{
|
||||||
|
pushRight(tx2, ty2);
|
||||||
|
}
|
||||||
|
else if (p1 >= 0 != p2 >= 0)
|
||||||
|
{
|
||||||
|
long nota = cx1 * cy2 - cy1 * cx2;
|
||||||
|
long clue = tx1 * ty2 - ty1 * tx2;
|
||||||
|
long div = ((cx1 - cx2) * (ty1 - ty2) - (cy1 - cy2) * (tx1 - tx2));
|
||||||
|
pushRight((int) ((nota * (tx1 - tx2) - (cx1 - cx2) * clue) / div),
|
||||||
|
(int) ((nota * (ty1 - ty2) - (cy1 - cy2) * clue) / div));
|
||||||
|
|
||||||
|
if (p1 >= 0)
|
||||||
|
{
|
||||||
|
pushRight(tx2, ty2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx1 = tx2;
|
||||||
|
ty1 = ty2;
|
||||||
|
}
|
||||||
|
|
||||||
|
cx1 = cx2;
|
||||||
|
cy1 = cy2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle getBounds()
|
||||||
|
{
|
||||||
|
int
|
||||||
|
minX = Integer.MAX_VALUE,
|
||||||
|
minY = Integer.MAX_VALUE,
|
||||||
|
maxX = Integer.MIN_VALUE,
|
||||||
|
maxY = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (int i = left; i <= right; i++)
|
||||||
|
{
|
||||||
|
final int xs = x[i];
|
||||||
|
final int ys = y[i];
|
||||||
|
|
||||||
|
if (xs < minX)
|
||||||
|
{
|
||||||
|
minX = xs;
|
||||||
|
}
|
||||||
|
if (xs > maxX)
|
||||||
|
{
|
||||||
|
maxX = xs;
|
||||||
|
}
|
||||||
|
if (ys < minY)
|
||||||
|
{
|
||||||
|
minY = ys;
|
||||||
|
}
|
||||||
|
if (ys > maxY)
|
||||||
|
{
|
||||||
|
maxY = ys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getBounds2D()
|
||||||
|
{
|
||||||
|
Rectangle b = getBounds();
|
||||||
|
return new Rectangle2D.Float(b.x, b.y, b.width, b.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(double cx, double cy)
|
||||||
|
{
|
||||||
|
if (size() < 3)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (crossings(cx, cy, false) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int crossings(double cx, double cy, boolean swap)
|
||||||
|
{
|
||||||
|
int collisions = 0;
|
||||||
|
|
||||||
|
int[] x = this.x;
|
||||||
|
int[] y = this.y;
|
||||||
|
if (swap)
|
||||||
|
{
|
||||||
|
y = this.x;
|
||||||
|
x = this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x0 = x[right], y0 = y[right], x1, y1, i = left; i <= right; i++, x0 = x1, y0 = y1)
|
||||||
|
{
|
||||||
|
x1 = x[i];
|
||||||
|
y1 = y[i];
|
||||||
|
|
||||||
|
if (y0 == y1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double dy0 = y0, dy1 = y1;
|
||||||
|
|
||||||
|
if (cy <= dy0 == cy <= dy1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double dx0 = x0, dx1 = x1;
|
||||||
|
|
||||||
|
boolean left = cx < dx0;
|
||||||
|
if (left == cx < dx1)
|
||||||
|
{
|
||||||
|
if (!left)
|
||||||
|
{
|
||||||
|
collisions++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dx1 - dx0) * (cy - dy0) - (cx - dx0) * (dy1 - dy0) > 0 == dy0 > dy1)
|
||||||
|
{
|
||||||
|
collisions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Point2D p)
|
||||||
|
{
|
||||||
|
return contains(p.getX(), p.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(double x0, double y0, double w, double h)
|
||||||
|
{
|
||||||
|
// this is horribly inefficient, but I don't think it will be called anywhere
|
||||||
|
|
||||||
|
double x1 = x0 + w;
|
||||||
|
double y1 = y0 + h;
|
||||||
|
|
||||||
|
return crossings(x0, y0, false) != crossings(x1, y0, false) // top
|
||||||
|
|| crossings(x0, y1, false) != crossings(x1, y1, false) // bottom
|
||||||
|
|| crossings(x0, y0, true) != crossings(x0, y1, true) // left
|
||||||
|
|| crossings(x1, y0, true) != crossings(x1, y1, true); // right
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(Rectangle2D r)
|
||||||
|
{
|
||||||
|
return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(double x, double y, double w, double h)
|
||||||
|
{
|
||||||
|
if (!getBounds().contains(x, y, w, h))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !intersects(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Rectangle2D r)
|
||||||
|
{
|
||||||
|
return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIterator getPathIterator(AffineTransform at)
|
||||||
|
{
|
||||||
|
if (at == null)
|
||||||
|
{
|
||||||
|
return new SimpleIterator();
|
||||||
|
}
|
||||||
|
return new TransformIterator(at);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIterator getPathIterator(AffineTransform at, double flatness)
|
||||||
|
{
|
||||||
|
return getPathIterator(at);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SimpleIterator implements PathIterator
|
||||||
|
{
|
||||||
|
private int i = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWindingRule()
|
||||||
|
{
|
||||||
|
return WIND_EVEN_ODD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone()
|
||||||
|
{
|
||||||
|
return size() == 0 || i > right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void next()
|
||||||
|
{
|
||||||
|
if (i == -1)
|
||||||
|
{
|
||||||
|
i = left;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int currentSegment(float[] coords)
|
||||||
|
{
|
||||||
|
if (i == -1)
|
||||||
|
{
|
||||||
|
coords[0] = x[right];
|
||||||
|
coords[1] = y[right];
|
||||||
|
return SEG_MOVETO;
|
||||||
|
}
|
||||||
|
|
||||||
|
coords[0] = x[i];
|
||||||
|
coords[1] = y[i];
|
||||||
|
return SEG_LINETO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int currentSegment(double[] coords)
|
||||||
|
{
|
||||||
|
if (i == -1)
|
||||||
|
{
|
||||||
|
coords[0] = x[right];
|
||||||
|
coords[1] = y[right];
|
||||||
|
return SEG_MOVETO;
|
||||||
|
}
|
||||||
|
|
||||||
|
coords[0] = x[i];
|
||||||
|
coords[1] = y[i];
|
||||||
|
return SEG_LINETO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransformIterator extends SimpleIterator
|
||||||
|
{
|
||||||
|
private final AffineTransform transform;
|
||||||
|
|
||||||
|
TransformIterator(AffineTransform transform)
|
||||||
|
{
|
||||||
|
this.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int currentSegment(float[] coords)
|
||||||
|
{
|
||||||
|
int v = super.currentSegment(coords);
|
||||||
|
transform.transform(coords, 0, coords, 0, 2);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int currentSegment(double[] coords)
|
||||||
|
{
|
||||||
|
int v = super.currentSegment(coords);
|
||||||
|
transform.transform(coords, 0, coords, 0, 2);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.api.model;
|
package net.runelite.api.model;
|
||||||
|
|
||||||
import net.runelite.api.Point;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
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,91 +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)
|
||||||
{
|
{
|
||||||
return (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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Abex
|
||||||
|
* 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.geometry;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class RectangleUnionTest
|
||||||
|
{
|
||||||
|
private static final int ITERATIONS = 100;
|
||||||
|
private static final int WIDTH = 1000;
|
||||||
|
private static final int MAX_RECTS = 50;
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
public void test() throws IOException
|
||||||
|
{
|
||||||
|
for (int count = 1; count < MAX_RECTS; count++)
|
||||||
|
{
|
||||||
|
for (int r = 0; r < ITERATIONS; r++)
|
||||||
|
{
|
||||||
|
Random rand = new Random(count << 16 | r);
|
||||||
|
String id = count + "rects_iteration" + r;
|
||||||
|
log.info(id);
|
||||||
|
BufferedImage wanted = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_BYTE_BINARY);
|
||||||
|
BufferedImage got = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_BYTE_BINARY);
|
||||||
|
|
||||||
|
Graphics2D wg = wanted.createGraphics();
|
||||||
|
wg.setColor(Color.WHITE);
|
||||||
|
Graphics2D gg = got.createGraphics();
|
||||||
|
gg.setColor(Color.WHITE);
|
||||||
|
|
||||||
|
List<RectangleUnion.Rectangle> rects = new ArrayList<>(count);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int x1, y1, x2, y2;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
x1 = rand.nextInt(WIDTH);
|
||||||
|
x2 = rand.nextInt(WIDTH);
|
||||||
|
}
|
||||||
|
while (x1 >= x2);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
y1 = rand.nextInt(WIDTH);
|
||||||
|
y2 = rand.nextInt(WIDTH);
|
||||||
|
}
|
||||||
|
while (y1 >= y2);
|
||||||
|
|
||||||
|
RectangleUnion.Rectangle rect = new RectangleUnion.Rectangle(x1, y1, x2, y2);
|
||||||
|
log.trace("{}", rect);
|
||||||
|
rects.add(rect);
|
||||||
|
|
||||||
|
wg.fillRect(x1, y1, x2 - x1, y2 - y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shape union = RectangleUnion.union(rects);
|
||||||
|
|
||||||
|
gg.fill(union);
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for (int x = 0; x < WIDTH; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < WIDTH; y++)
|
||||||
|
{
|
||||||
|
if (wanted.getRGB(x, y) != got.getRGB(x, y))
|
||||||
|
{
|
||||||
|
File tmp = new File(System.getProperty("java.io.tmpdir"));
|
||||||
|
ImageIO.write(wanted, "png", new File(tmp, id + "_wanted.png"));
|
||||||
|
ImageIO.write(got, "png", new File(tmp, id + "_got.png"));
|
||||||
|
|
||||||
|
Assert.fail(id);
|
||||||
|
break loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import com.google.inject.AbstractModule;
|
|||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Names;
|
||||||
import java.applet.Applet;
|
import java.applet.Applet;
|
||||||
|
import java.io.File;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -51,12 +52,15 @@ import net.runelite.client.task.Scheduler;
|
|||||||
import net.runelite.client.util.DeferredEventBus;
|
import net.runelite.client.util.DeferredEventBus;
|
||||||
import net.runelite.client.util.ExecutorServiceExceptionLogger;
|
import net.runelite.client.util.ExecutorServiceExceptionLogger;
|
||||||
import net.runelite.http.api.RuneLiteAPI;
|
import net.runelite.http.api.RuneLiteAPI;
|
||||||
|
import okhttp3.Cache;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class RuneLiteModule extends AbstractModule
|
public class RuneLiteModule extends AbstractModule
|
||||||
{
|
{
|
||||||
|
private static final int MAX_OKHTTP_CACHE_SIZE = 20 * 1024 * 1024; // 20mb
|
||||||
|
|
||||||
private final ClientUpdateCheckMode updateCheckMode;
|
private final ClientUpdateCheckMode updateCheckMode;
|
||||||
private final boolean developerMode;
|
private final boolean developerMode;
|
||||||
|
|
||||||
@@ -72,7 +76,9 @@ public class RuneLiteModule extends AbstractModule
|
|||||||
bindConstant().annotatedWith(Names.named("updateCheckMode")).to(updateCheckMode);
|
bindConstant().annotatedWith(Names.named("updateCheckMode")).to(updateCheckMode);
|
||||||
bindConstant().annotatedWith(Names.named("developerMode")).to(developerMode);
|
bindConstant().annotatedWith(Names.named("developerMode")).to(developerMode);
|
||||||
bind(ScheduledExecutorService.class).toInstance(new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor()));
|
bind(ScheduledExecutorService.class).toInstance(new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor()));
|
||||||
bind(OkHttpClient.class).toInstance(RuneLiteAPI.CLIENT);
|
bind(OkHttpClient.class).toInstance(RuneLiteAPI.CLIENT.newBuilder()
|
||||||
|
.cache(new Cache(new File(RuneLite.RUNELITE_DIR, "cache" + File.separator + "okhttp"), MAX_OKHTTP_CACHE_SIZE))
|
||||||
|
.build());
|
||||||
bind(MenuManager.class);
|
bind(MenuManager.class);
|
||||||
bind(ChatMessageManager.class);
|
bind(ChatMessageManager.class);
|
||||||
bind(ItemManager.class);
|
bind(ItemManager.class);
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public class ItemManager
|
|||||||
private final Client client;
|
private final Client client;
|
||||||
private final ScheduledExecutorService scheduledExecutorService;
|
private final ScheduledExecutorService scheduledExecutorService;
|
||||||
private final ClientThread clientThread;
|
private final ClientThread clientThread;
|
||||||
private final ItemClient itemClient = new ItemClient();
|
private final ItemClient itemClient;
|
||||||
private final ImmutableMap<Integer, ItemStats> itemStatMap;
|
private final ImmutableMap<Integer, ItemStats> itemStatMap;
|
||||||
private final LoadingCache<ImageKey, AsyncBufferedImage> itemImages;
|
private final LoadingCache<ImageKey, AsyncBufferedImage> itemImages;
|
||||||
private final LoadingCache<Integer, ItemDefinition> itemDefinitions;
|
private final LoadingCache<Integer, ItemDefinition> itemDefinitions;
|
||||||
@@ -149,12 +149,14 @@ public class ItemManager
|
|||||||
Client client,
|
Client client,
|
||||||
ScheduledExecutorService executor,
|
ScheduledExecutorService executor,
|
||||||
ClientThread clientThread,
|
ClientThread clientThread,
|
||||||
EventBus eventbus
|
EventBus eventbus,
|
||||||
|
ItemClient itemClient
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.scheduledExecutorService = executor;
|
this.scheduledExecutorService = executor;
|
||||||
this.clientThread = clientThread;
|
this.clientThread = clientThread;
|
||||||
|
this.itemClient = itemClient;
|
||||||
|
|
||||||
scheduledExecutorService.scheduleWithFixedDelay(this::loadPrices, 0, 30, TimeUnit.MINUTES);
|
scheduledExecutorService.scheduleWithFixedDelay(this::loadPrices, 0, 30, TimeUnit.MINUTES);
|
||||||
scheduledExecutorService.submit(this::loadStats);
|
scheduledExecutorService.submit(this::loadStats);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -90,7 +90,7 @@ class AgilityOverlay extends Overlay
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Area objectClickbox = object.getClickbox();
|
Shape objectClickbox = object.getClickbox();
|
||||||
if (objectClickbox != null)
|
if (objectClickbox != null)
|
||||||
{
|
{
|
||||||
AgilityShortcut agilityShortcut = obstacle.getShortcut();
|
AgilityShortcut agilityShortcut = obstacle.getShortcut();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ package net.runelite.client.plugins.blastfurnace;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
@@ -110,7 +110,7 @@ class BlastFurnaceClickBoxOverlay extends Overlay
|
|||||||
|
|
||||||
if (localLocation.distanceTo(location) <= MAX_DISTANCE)
|
if (localLocation.distanceTo(location) <= MAX_DISTANCE)
|
||||||
{
|
{
|
||||||
Area objectClickbox = object.getClickbox();
|
Shape objectClickbox = object.getClickbox();
|
||||||
if (objectClickbox != null)
|
if (objectClickbox != null)
|
||||||
{
|
{
|
||||||
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ public class DefaultWorldPlugin extends Plugin
|
|||||||
@Inject
|
@Inject
|
||||||
private ClientThread clientThread;
|
private ClientThread clientThread;
|
||||||
|
|
||||||
private final WorldClient worldClient = new WorldClient();
|
@Inject
|
||||||
|
private WorldClient worldClient;
|
||||||
|
|
||||||
private int worldCache;
|
private int worldCache;
|
||||||
private boolean worldChangeRequired;
|
private boolean worldChangeRequired;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import java.awt.FontMetrics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -326,10 +327,10 @@ class DevToolsOverlay extends Overlay
|
|||||||
|
|
||||||
// Draw a polygon around the convex hull
|
// Draw a polygon around the convex hull
|
||||||
// of the model vertices
|
// of the model vertices
|
||||||
Polygon p = gameObject.getConvexHull();
|
Shape p = gameObject.getConvexHull();
|
||||||
if (p != null)
|
if (p != null)
|
||||||
{
|
{
|
||||||
graphics.drawPolygon(p);
|
graphics.draw(p);
|
||||||
}
|
}
|
||||||
// This is incredibly taxing to run, only uncomment if you know what you're doing.
|
// This is incredibly taxing to run, only uncomment if you know what you're doing.
|
||||||
/*renderGameObjectWireframe(graphics, gameObject, Color.CYAN);*/
|
/*renderGameObjectWireframe(graphics, gameObject, Color.CYAN);*/
|
||||||
@@ -372,16 +373,16 @@ class DevToolsOverlay extends Overlay
|
|||||||
OverlayUtil.renderTileOverlay(graphics, decorObject, "ID: " + decorObject.getId(), DEEP_PURPLE);
|
OverlayUtil.renderTileOverlay(graphics, decorObject, "ID: " + decorObject.getId(), DEEP_PURPLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon p = decorObject.getConvexHull();
|
Shape p = decorObject.getConvexHull();
|
||||||
if (p != null)
|
if (p != null)
|
||||||
{
|
{
|
||||||
graphics.drawPolygon(p);
|
graphics.draw(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
p = decorObject.getConvexHull2();
|
p = decorObject.getConvexHull2();
|
||||||
if (p != null)
|
if (p != null)
|
||||||
{
|
{
|
||||||
graphics.drawPolygon(p);
|
graphics.draw(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -553,23 +554,6 @@ class DevToolsOverlay extends Overlay
|
|||||||
graphics.drawString(text, textX, textY);
|
graphics.drawString(text, textX, textY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderGameObjectWireframe(Graphics2D graphics, GameObject gameObject, Color color)
|
|
||||||
{
|
|
||||||
Polygon[] polys = gameObject.getPolygons();
|
|
||||||
|
|
||||||
if (polys == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
graphics.setColor(color);
|
|
||||||
|
|
||||||
for (Polygon p : polys)
|
|
||||||
{
|
|
||||||
graphics.drawPolygon(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderPlayerWireframe(Graphics2D graphics, Player player, Color color)
|
private void renderPlayerWireframe(Graphics2D graphics, Player player, Color color)
|
||||||
{
|
{
|
||||||
Polygon[] polys = player.getPolygons();
|
Polygon[] polys = player.getPolygons();
|
||||||
|
|||||||
@@ -66,13 +66,15 @@ public class FeedPlugin extends Plugin
|
|||||||
@Inject
|
@Inject
|
||||||
private ScheduledExecutorService executorService;
|
private ScheduledExecutorService executorService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private FeedClient feedClient;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private EventBus eventBus;
|
private EventBus eventBus;
|
||||||
|
|
||||||
private FeedPanel feedPanel;
|
private FeedPanel feedPanel;
|
||||||
private NavigationButton navButton;
|
private NavigationButton navButton;
|
||||||
|
|
||||||
private final FeedClient feedClient = new FeedClient();
|
|
||||||
private final Supplier<FeedResult> feedSupplier = Suppliers.memoizeWithExpiration(() ->
|
private final Supplier<FeedResult> feedSupplier = Suppliers.memoizeWithExpiration(() ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import java.awt.Font;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -201,7 +202,7 @@ public class GauntletOverlay extends Overlay
|
|||||||
|
|
||||||
if (plugin.isOverlayBoss())
|
if (plugin.isOverlayBoss())
|
||||||
{
|
{
|
||||||
Polygon polygon = boss.getConvexHull();
|
Shape polygon = boss.getConvexHull();
|
||||||
|
|
||||||
if (polygon == null)
|
if (polygon == null)
|
||||||
{
|
{
|
||||||
@@ -315,7 +316,7 @@ public class GauntletOverlay extends Overlay
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Don't use Convex Hull click box. As the room start to fill up, your FPS will dip.
|
// Don't use Convex Hull click box. As the room start to fill up, your FPS will dip.
|
||||||
Polygon polygon = object.getGameObject().getConvexHull();
|
Shape polygon = object.getGameObject().getConvexHull();
|
||||||
|
|
||||||
if (polygon == null)
|
if (polygon == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -46,12 +47,12 @@ import net.runelite.api.Point;
|
|||||||
import net.runelite.api.coords.LocalPoint;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import net.runelite.api.coords.WorldArea;
|
import net.runelite.api.coords.WorldArea;
|
||||||
import net.runelite.api.coords.WorldPoint;
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
import net.runelite.api.util.Text;
|
||||||
import net.runelite.client.graphics.ModelOutlineRenderer;
|
import net.runelite.client.graphics.ModelOutlineRenderer;
|
||||||
import net.runelite.client.ui.overlay.Overlay;
|
import net.runelite.client.ui.overlay.Overlay;
|
||||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||||
import net.runelite.api.util.Text;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class NpcSceneOverlay extends Overlay
|
public class NpcSceneOverlay extends Overlay
|
||||||
@@ -181,8 +182,9 @@ public class NpcSceneOverlay extends Overlay
|
|||||||
renderPoly(graphics, color, tilePoly);
|
renderPoly(graphics, color, tilePoly);
|
||||||
break;
|
break;
|
||||||
case HULL:
|
case HULL:
|
||||||
final Polygon objectClickbox = actor.getConvexHull();
|
final Shape objectClickbox = actor.getConvexHull();
|
||||||
renderPoly(graphics, color, objectClickbox);
|
graphics.setColor(color);
|
||||||
|
graphics.draw(objectClickbox);
|
||||||
break;
|
break;
|
||||||
case THIN_OUTLINE:
|
case THIN_OUTLINE:
|
||||||
modelOutliner.drawOutline(actor, 1, color);
|
modelOutliner.drawOutline(actor, 1, color);
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ package net.runelite.client.plugins.objectindicators;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Shape;
|
||||||
import java.awt.geom.Area;
|
|
||||||
import static java.lang.Math.floor;
|
import static java.lang.Math.floor;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -101,8 +100,8 @@ class ObjectIndicatorsOverlay extends Overlay
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HULL:
|
case HULL:
|
||||||
final Polygon polygon;
|
final Shape polygon;
|
||||||
Polygon polygon2 = null;
|
Shape polygon2 = null;
|
||||||
|
|
||||||
if (object instanceof GameObject)
|
if (object instanceof GameObject)
|
||||||
{
|
{
|
||||||
@@ -129,7 +128,7 @@ class ObjectIndicatorsOverlay extends Overlay
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CLICKBOX:
|
case CLICKBOX:
|
||||||
Area clickbox = object.getClickbox();
|
Shape clickbox = object.getClickbox();
|
||||||
if (clickbox != null)
|
if (clickbox != null)
|
||||||
{
|
{
|
||||||
OverlayUtil.renderHoverableArea(graphics, object.getClickbox(), client.getMouseCanvasPosition(), TRANSPARENT, objectColor, objectColor.darker());
|
OverlayUtil.renderHoverableArea(graphics, object.getClickbox(), client.getMouseCanvasPosition(), TRANSPARENT, objectColor, objectColor.darker());
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import java.awt.BasicStroke;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Shape;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
import net.runelite.api.Player;
|
import net.runelite.api.Player;
|
||||||
@@ -92,12 +92,12 @@ public class GangplankOverlay extends Overlay
|
|||||||
|
|
||||||
if (noviceGangplankTile != null)
|
if (noviceGangplankTile != null)
|
||||||
{
|
{
|
||||||
Polygon polygon = noviceGangplankTile.getGameObjects()[0].getConvexHull();
|
Shape polygon = noviceGangplankTile.getGameObjects()[0].getConvexHull();
|
||||||
if (polygon != null)
|
if (polygon != null)
|
||||||
{
|
{
|
||||||
graphics.setColor(noviceCbColor);
|
graphics.setColor(noviceCbColor);
|
||||||
graphics.setStroke(new BasicStroke(2));
|
graphics.setStroke(new BasicStroke(2));
|
||||||
graphics.drawPolygon(polygon);
|
graphics.draw(polygon);
|
||||||
graphics.setColor(setColorAlpha(noviceCbColor, 45));
|
graphics.setColor(setColorAlpha(noviceCbColor, 45));
|
||||||
graphics.fill(polygon);
|
graphics.fill(polygon);
|
||||||
|
|
||||||
@@ -112,12 +112,12 @@ public class GangplankOverlay extends Overlay
|
|||||||
|
|
||||||
if (intermediateGangplankTile != null)
|
if (intermediateGangplankTile != null)
|
||||||
{
|
{
|
||||||
Polygon polygon = intermediateGangplankTile.getGameObjects()[0].getConvexHull();
|
Shape polygon = intermediateGangplankTile.getGameObjects()[0].getConvexHull();
|
||||||
if (polygon != null)
|
if (polygon != null)
|
||||||
{
|
{
|
||||||
graphics.setColor(intermediateCbColor);
|
graphics.setColor(intermediateCbColor);
|
||||||
graphics.setStroke(new BasicStroke(2));
|
graphics.setStroke(new BasicStroke(2));
|
||||||
graphics.drawPolygon(polygon);
|
graphics.draw(polygon);
|
||||||
graphics.setColor(setColorAlpha(intermediateCbColor, 45));
|
graphics.setColor(setColorAlpha(intermediateCbColor, 45));
|
||||||
graphics.fill(polygon);
|
graphics.fill(polygon);
|
||||||
|
|
||||||
@@ -132,12 +132,12 @@ public class GangplankOverlay extends Overlay
|
|||||||
|
|
||||||
if (veteranGangplankTile != null)
|
if (veteranGangplankTile != null)
|
||||||
{
|
{
|
||||||
Polygon polygon = veteranGangplankTile.getGameObjects()[0].getConvexHull();
|
Shape polygon = veteranGangplankTile.getGameObjects()[0].getConvexHull();
|
||||||
if (polygon != null)
|
if (polygon != null)
|
||||||
{
|
{
|
||||||
graphics.setColor(veteranCbColor);
|
graphics.setColor(veteranCbColor);
|
||||||
graphics.setStroke(new BasicStroke(2));
|
graphics.setStroke(new BasicStroke(2));
|
||||||
graphics.drawPolygon(polygon);
|
graphics.draw(polygon);
|
||||||
graphics.setColor(setColorAlpha(veteranCbColor, 45));
|
graphics.setColor(setColorAlpha(veteranCbColor, 45));
|
||||||
graphics.fill(polygon);
|
graphics.fill(polygon);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
@@ -141,7 +142,7 @@ public class NpcHighlightOverlay extends Overlay
|
|||||||
|
|
||||||
private void renderHullOverlay(Graphics2D graphics, NPC npc, Color color)
|
private void renderHullOverlay(Graphics2D graphics, NPC npc, Color color)
|
||||||
{
|
{
|
||||||
Polygon objectClickbox = npc.getConvexHull();
|
Shape objectClickbox = npc.getConvexHull();
|
||||||
if (objectClickbox != null)
|
if (objectClickbox != null)
|
||||||
{
|
{
|
||||||
graphics.setColor(color);
|
graphics.setColor(color);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import java.awt.BasicStroke;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
import net.runelite.api.Constants;
|
import net.runelite.api.Constants;
|
||||||
import net.runelite.api.GameObject;
|
import net.runelite.api.GameObject;
|
||||||
@@ -185,7 +185,7 @@ public class RepairOverlay extends Overlay
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderObjectOverlay(Graphics2D graphics, Area area, Color color, Point mousePosition)
|
private void renderObjectOverlay(Graphics2D graphics, Shape area, Color color, Point mousePosition)
|
||||||
{
|
{
|
||||||
if (area == null)
|
if (area == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ package net.runelite.client.plugins.pyramidplunder;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -119,7 +119,7 @@ public class PyramidPlunderOverlay extends Overlay
|
|||||||
objectID = impostor.getId();
|
objectID = impostor.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
Area objectClickbox = object.getClickbox();
|
Shape objectClickbox = object.getClickbox();
|
||||||
if (objectClickbox != null)
|
if (objectClickbox != null)
|
||||||
{
|
{
|
||||||
Color configColor = Color.GREEN;
|
Color configColor = Color.GREEN;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package net.runelite.client.plugins.raids.shortcuts;
|
|||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Shape;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -48,7 +48,7 @@ public class ShortcutOverlay extends Overlay
|
|||||||
{
|
{
|
||||||
if (shortcut.getPlane() == client.getPlane())
|
if (shortcut.getPlane() == client.getPlane())
|
||||||
{
|
{
|
||||||
Polygon poly;
|
Shape poly;
|
||||||
if ((shortcut instanceof GameObject))
|
if ((shortcut instanceof GameObject))
|
||||||
{
|
{
|
||||||
poly = ((GameObject) shortcut).getConvexHull();
|
poly = ((GameObject) shortcut).getConvexHull();
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
@@ -74,7 +74,7 @@ public class RoguesDenOverlay extends Overlay
|
|||||||
{
|
{
|
||||||
if (tile.getPlane() == client.getPlane())
|
if (tile.getPlane() == client.getPlane())
|
||||||
{
|
{
|
||||||
final Area clickBox = obstacle.getClickbox();
|
final Shape clickBox = obstacle.getClickbox();
|
||||||
if (clickBox != null)
|
if (clickBox != null)
|
||||||
{
|
{
|
||||||
final Point mouse = client.getMouseCanvasPosition();
|
final Point mouse = client.getMouseCanvasPosition();
|
||||||
@@ -93,7 +93,7 @@ public class RoguesDenOverlay extends Overlay
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Polygon p;
|
Shape p;
|
||||||
if (obstacle instanceof GameObject)
|
if (obstacle instanceof GameObject)
|
||||||
{
|
{
|
||||||
p = ((GameObject) obstacle).getConvexHull();
|
p = ((GameObject) obstacle).getConvexHull();
|
||||||
@@ -106,7 +106,7 @@ public class RoguesDenOverlay extends Overlay
|
|||||||
if (p != null)
|
if (p != null)
|
||||||
{
|
{
|
||||||
graphics.setColor(OBJECT_COLOR);
|
graphics.setColor(OBJECT_COLOR);
|
||||||
graphics.drawPolygon(p);
|
graphics.draw(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
import net.runelite.api.DecorativeObject;
|
import net.runelite.api.DecorativeObject;
|
||||||
import net.runelite.api.NPC;
|
import net.runelite.api.NPC;
|
||||||
@@ -105,7 +105,7 @@ class AbyssOverlay extends Overlay
|
|||||||
}
|
}
|
||||||
|
|
||||||
Point mousePosition = client.getMouseCanvasPosition();
|
Point mousePosition = client.getMouseCanvasPosition();
|
||||||
Area objectClickbox = object.getClickbox();
|
Shape objectClickbox = object.getClickbox();
|
||||||
if (objectClickbox != null)
|
if (objectClickbox != null)
|
||||||
{
|
{
|
||||||
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -43,12 +44,12 @@ import net.runelite.api.Point;
|
|||||||
import net.runelite.api.coords.LocalPoint;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import net.runelite.api.coords.WorldArea;
|
import net.runelite.api.coords.WorldArea;
|
||||||
import net.runelite.api.coords.WorldPoint;
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
import net.runelite.api.util.Text;
|
||||||
import net.runelite.client.graphics.ModelOutlineRenderer;
|
import net.runelite.client.graphics.ModelOutlineRenderer;
|
||||||
import net.runelite.client.ui.overlay.Overlay;
|
import net.runelite.client.ui.overlay.Overlay;
|
||||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||||
import net.runelite.api.util.Text;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class TargetClickboxOverlay extends Overlay
|
public class TargetClickboxOverlay extends Overlay
|
||||||
@@ -129,7 +130,7 @@ public class TargetClickboxOverlay extends Overlay
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HULL:
|
case HULL:
|
||||||
Polygon objectClickbox = actor.getConvexHull();
|
Shape objectClickbox = actor.getConvexHull();
|
||||||
|
|
||||||
if (objectClickbox == null)
|
if (objectClickbox == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -69,11 +70,11 @@ public class TarnsLairOverlay extends Overlay
|
|||||||
{
|
{
|
||||||
if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
|
if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
|
||||||
{
|
{
|
||||||
Polygon p = tile.getGameObjects()[0].getConvexHull();
|
Shape p = tile.getGameObjects()[0].getConvexHull();
|
||||||
if (p != null)
|
if (p != null)
|
||||||
{
|
{
|
||||||
graphics.setColor(Color.GREEN);
|
graphics.setColor(Color.GREEN);
|
||||||
graphics.drawPolygon(p);
|
graphics.draw(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -82,11 +83,11 @@ public class TarnsLairOverlay extends Overlay
|
|||||||
{
|
{
|
||||||
if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
|
if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
|
||||||
{
|
{
|
||||||
Polygon p = tile.getGameObjects()[0].getConvexHull();
|
Shape p = tile.getGameObjects()[0].getConvexHull();
|
||||||
if (p != null)
|
if (p != null)
|
||||||
{
|
{
|
||||||
graphics.setColor(Color.CYAN);
|
graphics.setColor(Color.CYAN);
|
||||||
graphics.drawPolygon(p);
|
graphics.draw(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -248,7 +249,7 @@ public class NyloHandler extends RoomHandler
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Polygon objectClickbox = npc.getConvexHull();
|
Shape objectClickbox = npc.getConvexHull();
|
||||||
|
|
||||||
Color color;
|
Color color;
|
||||||
String name = npc.getName() != null ? npc.getName() : "";
|
String name = npc.getName() != null ? npc.getName() : "";
|
||||||
@@ -266,7 +267,8 @@ public class NyloHandler extends RoomHandler
|
|||||||
color = Color.LIGHT_GRAY;
|
color = Color.LIGHT_GRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPoly(graphics, color, objectClickbox);
|
graphics.setColor(color);
|
||||||
|
graphics.draw(objectClickbox);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -141,6 +141,9 @@ public class WorldHopperPlugin extends Plugin
|
|||||||
@Inject
|
@Inject
|
||||||
private WorldHopperPingOverlay worldHopperOverlay;
|
private WorldHopperPingOverlay worldHopperOverlay;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private WorldClient worldClient;
|
||||||
|
|
||||||
private ScheduledExecutorService hopperExecutorService;
|
private ScheduledExecutorService hopperExecutorService;
|
||||||
|
|
||||||
private NavigationButton navButton;
|
private NavigationButton navButton;
|
||||||
@@ -533,7 +536,7 @@ public class WorldHopperPlugin extends Plugin
|
|||||||
{
|
{
|
||||||
log.debug("Fetching worlds");
|
log.debug("Fetching worlds");
|
||||||
|
|
||||||
new WorldClient().lookupWorlds()
|
worldClient.lookupWorlds()
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.take(1)
|
.take(1)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.AnimationID;
|
import net.runelite.api.AnimationID;
|
||||||
@@ -174,7 +175,7 @@ public class ZalcanoOverlay extends Overlay
|
|||||||
{
|
{
|
||||||
if (plugin.getGolem() != null)
|
if (plugin.getGolem() != null)
|
||||||
{
|
{
|
||||||
Polygon hull = plugin.getGolem().getConvexHull();
|
Shape hull = plugin.getGolem().getConvexHull();
|
||||||
if (hull != null)
|
if (hull != null)
|
||||||
{
|
{
|
||||||
OverlayUtil.renderPolygon(graphics, hull, new Color(206, 41, 231));
|
OverlayUtil.renderPolygon(graphics, hull, new Color(206, 41, 231));
|
||||||
@@ -187,7 +188,7 @@ public class ZalcanoOverlay extends Overlay
|
|||||||
{
|
{
|
||||||
if (plugin.getZalcano() != null)
|
if (plugin.getZalcano() != null)
|
||||||
{
|
{
|
||||||
Polygon hull = plugin.getZalcano().getConvexHull();
|
Shape hull = plugin.getZalcano().getConvexHull();
|
||||||
if (hull != null)
|
if (hull != null)
|
||||||
{
|
{
|
||||||
OverlayUtil.renderPolygon(graphics, hull, config.zalcanoHullColor());
|
OverlayUtil.renderPolygon(graphics, hull, config.zalcanoHullColor());
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.Shape;
|
||||||
import java.awt.Stroke;
|
import java.awt.Stroke;
|
||||||
import java.awt.geom.Area;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import net.runelite.api.Actor;
|
import net.runelite.api.Actor;
|
||||||
@@ -61,14 +61,14 @@ public class OverlayUtil
|
|||||||
private static final int MINIMAP_DOT_RADIUS = 4;
|
private static final int MINIMAP_DOT_RADIUS = 4;
|
||||||
private static final double UNIT = Math.PI / 1024.0d;
|
private static final double UNIT = Math.PI / 1024.0d;
|
||||||
|
|
||||||
public static void renderPolygon(Graphics2D graphics, Polygon poly, Color color)
|
public static void renderPolygon(Graphics2D graphics, Shape poly, Color color)
|
||||||
{
|
{
|
||||||
graphics.setColor(color);
|
graphics.setColor(color);
|
||||||
final Stroke originalStroke = graphics.getStroke();
|
final Stroke originalStroke = graphics.getStroke();
|
||||||
graphics.setStroke(new BasicStroke(2));
|
graphics.setStroke(new BasicStroke(2));
|
||||||
graphics.drawPolygon(poly);
|
graphics.draw(poly);
|
||||||
graphics.setColor(new Color(0, 0, 0, 50));
|
graphics.setColor(new Color(0, 0, 0, 50));
|
||||||
graphics.fillPolygon(poly);
|
graphics.fill(poly);
|
||||||
graphics.setStroke(originalStroke);
|
graphics.setStroke(originalStroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ public class OverlayUtil
|
|||||||
renderImageLocation(client, graphics, localLocation, image, 0);
|
renderImageLocation(client, graphics, localLocation, image, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void renderHoverableArea(Graphics2D graphics, Area area, net.runelite.api.Point mousePosition, Color fillColor, Color borderColor, Color borderHoverColor)
|
public static void renderHoverableArea(Graphics2D graphics, Shape area, net.runelite.api.Point mousePosition, Color fillColor, Color borderColor, Color borderHoverColor)
|
||||||
{
|
{
|
||||||
if (area != null)
|
if (area != null)
|
||||||
{
|
{
|
||||||
@@ -326,7 +326,7 @@ public class OverlayUtil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void renderClickBox(Graphics2D graphics, Point mousePosition, Area objectClickbox, Color configColor)
|
public static void renderClickBox(Graphics2D graphics, Point mousePosition, Shape objectClickbox, Color configColor)
|
||||||
{
|
{
|
||||||
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package net.runelite.mixins;
|
package net.runelite.mixins;
|
||||||
|
|
||||||
import net.runelite.api.Entity;
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import net.runelite.api.Model;
|
import net.runelite.api.Model;
|
||||||
import net.runelite.api.Perspective;
|
import net.runelite.api.Perspective;
|
||||||
import java.awt.geom.Area;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import net.runelite.api.mixins.Inject;
|
import net.runelite.api.mixins.Inject;
|
||||||
import net.runelite.api.mixins.Mixin;
|
import net.runelite.api.mixins.Mixin;
|
||||||
import net.runelite.api.mixins.Shadow;
|
import net.runelite.api.mixins.Shadow;
|
||||||
import net.runelite.rs.api.RSBoundaryObject;
|
import net.runelite.rs.api.RSBoundaryObject;
|
||||||
import net.runelite.rs.api.RSClient;
|
import net.runelite.rs.api.RSClient;
|
||||||
|
import net.runelite.rs.api.RSEntity;
|
||||||
|
import net.runelite.rs.api.RSModel;
|
||||||
|
|
||||||
@Mixin(RSBoundaryObject.class)
|
@Mixin(RSBoundaryObject.class)
|
||||||
public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
||||||
@@ -34,9 +37,9 @@ public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Model getModelA()
|
public RSModel getModelA()
|
||||||
{
|
{
|
||||||
Entity entity = getEntity1();
|
RSEntity entity = getEntity1();
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -44,7 +47,7 @@ public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
|||||||
|
|
||||||
if (entity instanceof Model)
|
if (entity instanceof Model)
|
||||||
{
|
{
|
||||||
return (Model) entity;
|
return (RSModel) entity;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -53,9 +56,9 @@ public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Model getModelB()
|
public RSModel getModelB()
|
||||||
{
|
{
|
||||||
Entity entity = getEntity2();
|
RSEntity entity = getEntity2();
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -63,7 +66,7 @@ public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
|||||||
|
|
||||||
if (entity instanceof Model)
|
if (entity instanceof Model)
|
||||||
{
|
{
|
||||||
return (Model) entity;
|
return (RSModel) entity;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -73,12 +76,10 @@ public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Override
|
@Override
|
||||||
public Area getClickbox()
|
public Shape getClickbox()
|
||||||
{
|
{
|
||||||
Area clickbox = new Area();
|
Shape clickboxA = Perspective.getClickbox(client, getModelA(), 0, getLocalLocation());
|
||||||
|
Shape clickboxB = Perspective.getClickbox(client, getModelB(), 0, getLocalLocation());
|
||||||
Area clickboxA = Perspective.getClickbox(client, getModelA(), 0, getLocalLocation());
|
|
||||||
Area clickboxB = Perspective.getClickbox(client, getModelB(), 0, getLocalLocation());
|
|
||||||
|
|
||||||
if (clickboxA == null && clickboxB == null)
|
if (clickboxA == null && clickboxB == null)
|
||||||
{
|
{
|
||||||
@@ -87,14 +88,26 @@ public abstract class RSBoundaryObjectMixin implements RSBoundaryObject
|
|||||||
|
|
||||||
if (clickboxA != null)
|
if (clickboxA != null)
|
||||||
{
|
{
|
||||||
clickbox.add(clickboxA);
|
return clickboxA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clickboxB != null)
|
return clickboxB;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@Override
|
||||||
|
public Polygon getConvexHull()
|
||||||
|
{
|
||||||
|
RSModel model = getModelA();
|
||||||
|
|
||||||
|
if (model == null)
|
||||||
{
|
{
|
||||||
clickbox.add(clickboxB);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return clickbox;
|
int tileHeight = Perspective.getTileHeight(client, new LocalPoint(getX(), getY()), client.getPlane());
|
||||||
|
return model.getConvexHull(getX(), getY(), 0, tileHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package net.runelite.mixins;
|
package net.runelite.mixins;
|
||||||
|
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
import net.runelite.api.Model;
|
import net.runelite.api.Model;
|
||||||
import net.runelite.api.Perspective;
|
import net.runelite.api.Perspective;
|
||||||
import net.runelite.api.Entity;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import java.awt.geom.Area;
|
|
||||||
import net.runelite.api.mixins.Inject;
|
import net.runelite.api.mixins.Inject;
|
||||||
import net.runelite.api.mixins.Mixin;
|
import net.runelite.api.mixins.Mixin;
|
||||||
import net.runelite.api.mixins.Shadow;
|
import net.runelite.api.mixins.Shadow;
|
||||||
import net.runelite.rs.api.RSClient;
|
import net.runelite.rs.api.RSClient;
|
||||||
|
import net.runelite.rs.api.RSEntity;
|
||||||
import net.runelite.rs.api.RSFloorDecoration;
|
import net.runelite.rs.api.RSFloorDecoration;
|
||||||
|
import net.runelite.rs.api.RSModel;
|
||||||
|
|
||||||
@Mixin(RSFloorDecoration.class)
|
@Mixin(RSFloorDecoration.class)
|
||||||
public abstract class RSFloorDecorationMixin implements RSFloorDecoration
|
public abstract class RSFloorDecorationMixin implements RSFloorDecoration
|
||||||
@@ -35,9 +38,9 @@ public abstract class RSFloorDecorationMixin implements RSFloorDecoration
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Override
|
@Override
|
||||||
public Model getModel()
|
public RSModel getModel()
|
||||||
{
|
{
|
||||||
Entity entity = getEntity();
|
RSEntity entity = getEntity();
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -45,7 +48,7 @@ public abstract class RSFloorDecorationMixin implements RSFloorDecoration
|
|||||||
|
|
||||||
if (entity instanceof Model)
|
if (entity instanceof Model)
|
||||||
{
|
{
|
||||||
return (Model) entity;
|
return (RSModel) entity;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -53,9 +56,26 @@ public abstract class RSFloorDecorationMixin implements RSFloorDecoration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Override
|
@Override
|
||||||
public Area getClickbox()
|
public Polygon getConvexHull()
|
||||||
|
{
|
||||||
|
RSModel model = getModel();
|
||||||
|
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tileHeight = Perspective.getTileHeight(client, new LocalPoint(getX(), getY()), client.getPlane());
|
||||||
|
return model.getConvexHull(getX(), getY(), 0, tileHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@Override
|
||||||
|
public Shape getClickbox()
|
||||||
{
|
{
|
||||||
return Perspective.getClickbox(client, getModel(), 0, getLocalLocation());
|
return Perspective.getClickbox(client, getModel(), 0, getLocalLocation());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,7 @@
|
|||||||
package net.runelite.mixins;
|
package net.runelite.mixins;
|
||||||
|
|
||||||
import java.awt.Polygon;
|
import java.awt.Polygon;
|
||||||
import java.awt.geom.Area;
|
import java.awt.Shape;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import net.runelite.api.Model;
|
|
||||||
import net.runelite.api.Perspective;
|
import net.runelite.api.Perspective;
|
||||||
import net.runelite.api.Point;
|
import net.runelite.api.Point;
|
||||||
import net.runelite.api.coords.Angle;
|
import net.runelite.api.coords.Angle;
|
||||||
@@ -36,8 +33,6 @@ import net.runelite.api.coords.LocalPoint;
|
|||||||
import net.runelite.api.mixins.Inject;
|
import net.runelite.api.mixins.Inject;
|
||||||
import net.runelite.api.mixins.Mixin;
|
import net.runelite.api.mixins.Mixin;
|
||||||
import net.runelite.api.mixins.Shadow;
|
import net.runelite.api.mixins.Shadow;
|
||||||
import net.runelite.api.model.Triangle;
|
|
||||||
import net.runelite.api.model.Vertex;
|
|
||||||
import net.runelite.rs.api.RSClient;
|
import net.runelite.rs.api.RSClient;
|
||||||
import net.runelite.rs.api.RSEntity;
|
import net.runelite.rs.api.RSEntity;
|
||||||
import net.runelite.rs.api.RSGameObject;
|
import net.runelite.rs.api.RSGameObject;
|
||||||
@@ -85,94 +80,11 @@ public abstract class RSGameObjectMixin implements RSGameObject
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Override
|
@Override
|
||||||
public Area getClickbox()
|
public Shape getClickbox()
|
||||||
{
|
{
|
||||||
return Perspective.getClickbox(client, getModel(), getRsOrientation(), getLocalLocation());
|
return Perspective.getClickbox(client, getModel(), getRsOrientation(), getLocalLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
|
||||||
@Override
|
|
||||||
public Polygon[] getPolygons()
|
|
||||||
{
|
|
||||||
Model model = getModel();
|
|
||||||
|
|
||||||
if (model == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int localX = getX();
|
|
||||||
int localY = getY();
|
|
||||||
|
|
||||||
int orientation = getRsOrientation();
|
|
||||||
|
|
||||||
final int tileHeight = Perspective.getTileHeight(client, new LocalPoint(localX, localY), client.getPlane());
|
|
||||||
|
|
||||||
List<Triangle> triangles = model.getTriangles();
|
|
||||||
|
|
||||||
triangles = rotate(triangles, orientation);
|
|
||||||
|
|
||||||
List<Polygon> polys = new ArrayList<Polygon>();
|
|
||||||
for (Triangle triangle : triangles)
|
|
||||||
{
|
|
||||||
Vertex vx = triangle.getA();
|
|
||||||
Vertex vy = triangle.getB();
|
|
||||||
Vertex vz = triangle.getC();
|
|
||||||
|
|
||||||
Point x = Perspective.localToCanvas(client,
|
|
||||||
localX - vx.getX(),
|
|
||||||
localY - vx.getZ(),
|
|
||||||
tileHeight + vx.getY());
|
|
||||||
|
|
||||||
Point y = Perspective.localToCanvas(client,
|
|
||||||
localX - vy.getX(),
|
|
||||||
localY - vy.getZ(),
|
|
||||||
tileHeight + vy.getY());
|
|
||||||
|
|
||||||
Point z = Perspective.localToCanvas(client,
|
|
||||||
localX - vz.getX(),
|
|
||||||
localY - vz.getZ(),
|
|
||||||
tileHeight + vz.getY());
|
|
||||||
|
|
||||||
if (x == null || y == null || z == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] xx =
|
|
||||||
{
|
|
||||||
x.getX(), y.getX(), z.getX()
|
|
||||||
};
|
|
||||||
int[] yy =
|
|
||||||
{
|
|
||||||
x.getY(), y.getY(), z.getY()
|
|
||||||
};
|
|
||||||
polys.add(new Polygon(xx, yy, 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
return polys.toArray(new Polygon[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private List<Triangle> rotate(List<Triangle> triangles, int orientation)
|
|
||||||
{
|
|
||||||
List<Triangle> rotatedTriangles = new ArrayList<Triangle>();
|
|
||||||
for (Triangle triangle : triangles)
|
|
||||||
{
|
|
||||||
Vertex a = triangle.getA();
|
|
||||||
Vertex b = triangle.getB();
|
|
||||||
Vertex c = triangle.getC();
|
|
||||||
|
|
||||||
Triangle rotatedTriangle = new Triangle(
|
|
||||||
a.rotate(orientation),
|
|
||||||
b.rotate(orientation),
|
|
||||||
c.rotate(orientation)
|
|
||||||
);
|
|
||||||
rotatedTriangles.add(rotatedTriangle);
|
|
||||||
}
|
|
||||||
return rotatedTriangles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Override
|
@Override
|
||||||
public Polygon getConvexHull()
|
public Polygon getConvexHull()
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.mixins;
|
package net.runelite.mixins;
|
||||||
|
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import net.runelite.api.HeadIcon;
|
import net.runelite.api.HeadIcon;
|
||||||
import static net.runelite.api.HeadIcon.MAGIC;
|
import static net.runelite.api.HeadIcon.MAGIC;
|
||||||
import static net.runelite.api.HeadIcon.MELEE;
|
import static net.runelite.api.HeadIcon.MELEE;
|
||||||
@@ -43,17 +46,14 @@ import static net.runelite.api.SkullIcon.DEAD_MAN_TWO;
|
|||||||
import static net.runelite.api.SkullIcon.SKULL;
|
import static net.runelite.api.SkullIcon.SKULL;
|
||||||
import static net.runelite.api.SkullIcon.SKULL_FIGHT_PIT;
|
import static net.runelite.api.SkullIcon.SKULL_FIGHT_PIT;
|
||||||
import net.runelite.api.coords.LocalPoint;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import net.runelite.api.mixins.MethodHook;
|
|
||||||
import net.runelite.api.model.Triangle;
|
|
||||||
import net.runelite.api.model.Vertex;
|
|
||||||
import java.awt.Polygon;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import net.runelite.api.mixins.Copy;
|
import net.runelite.api.mixins.Copy;
|
||||||
import net.runelite.api.mixins.Inject;
|
import net.runelite.api.mixins.Inject;
|
||||||
|
import net.runelite.api.mixins.MethodHook;
|
||||||
import net.runelite.api.mixins.Mixin;
|
import net.runelite.api.mixins.Mixin;
|
||||||
import net.runelite.api.mixins.Replace;
|
import net.runelite.api.mixins.Replace;
|
||||||
import net.runelite.api.mixins.Shadow;
|
import net.runelite.api.mixins.Shadow;
|
||||||
|
import net.runelite.api.model.Triangle;
|
||||||
|
import net.runelite.api.model.Vertex;
|
||||||
import net.runelite.rs.api.RSClient;
|
import net.runelite.rs.api.RSClient;
|
||||||
import net.runelite.rs.api.RSModel;
|
import net.runelite.rs.api.RSModel;
|
||||||
import net.runelite.rs.api.RSPlayer;
|
import net.runelite.rs.api.RSPlayer;
|
||||||
@@ -169,28 +169,28 @@ public abstract class RSPlayerMixin implements RSPlayer
|
|||||||
System.err.println("vx: " + vx.getX() + " localX: " + localX);
|
System.err.println("vx: " + vx.getX() + " localX: " + localX);
|
||||||
|
|
||||||
Point x = Perspective.localToCanvas(client,
|
Point x = Perspective.localToCanvas(client,
|
||||||
localX - vx.getX(),
|
localX - vx.getX(),
|
||||||
localY - vx.getZ(),
|
localY - vx.getZ(),
|
||||||
tileHeight + vx.getY());
|
tileHeight + vx.getY());
|
||||||
|
|
||||||
Point y = Perspective.localToCanvas(client,
|
Point y = Perspective.localToCanvas(client,
|
||||||
localX - vy.getX(),
|
localX - vy.getX(),
|
||||||
localY - vy.getZ(),
|
localY - vy.getZ(),
|
||||||
tileHeight + vy.getY());
|
tileHeight + vy.getY());
|
||||||
|
|
||||||
Point z = Perspective.localToCanvas(client,
|
Point z = Perspective.localToCanvas(client,
|
||||||
localX - vz.getX(),
|
localX - vz.getX(),
|
||||||
localY - vz.getZ(),
|
localY - vz.getZ(),
|
||||||
tileHeight + vz.getY());
|
tileHeight + vz.getY());
|
||||||
|
|
||||||
int[] xx =
|
int[] xx =
|
||||||
{
|
{
|
||||||
x.getX(), y.getX(), z.getX()
|
x.getX(), y.getX(), z.getX()
|
||||||
};
|
};
|
||||||
int[] yy =
|
int[] yy =
|
||||||
{
|
{
|
||||||
x.getY(), y.getY(), z.getY()
|
x.getY(), y.getY(), z.getY()
|
||||||
};
|
};
|
||||||
polys.add(new Polygon(xx, yy, 3));
|
polys.add(new Polygon(xx, yy, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,9 +222,9 @@ public abstract class RSPlayerMixin implements RSPlayer
|
|||||||
Vertex c = triangle.getC();
|
Vertex c = triangle.getC();
|
||||||
|
|
||||||
Triangle rotatedTriangle = new Triangle(
|
Triangle rotatedTriangle = new Triangle(
|
||||||
a.rotate(orientation),
|
a.rotate(orientation),
|
||||||
b.rotate(orientation),
|
b.rotate(orientation),
|
||||||
c.rotate(orientation)
|
c.rotate(orientation)
|
||||||
);
|
);
|
||||||
rotatedTriangles.add(rotatedTriangle);
|
rotatedTriangles.add(rotatedTriangle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package net.runelite.mixins;
|
package net.runelite.mixins;
|
||||||
|
|
||||||
|
import java.awt.Polygon;
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.awt.geom.Area;
|
||||||
import net.runelite.api.Model;
|
import net.runelite.api.Model;
|
||||||
import net.runelite.api.Perspective;
|
import net.runelite.api.Perspective;
|
||||||
import net.runelite.api.coords.LocalPoint;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import java.awt.Polygon;
|
|
||||||
import java.awt.geom.Area;
|
|
||||||
import net.runelite.api.mixins.Inject;
|
import net.runelite.api.mixins.Inject;
|
||||||
import net.runelite.api.mixins.Mixin;
|
import net.runelite.api.mixins.Mixin;
|
||||||
import net.runelite.api.mixins.Shadow;
|
import net.runelite.api.mixins.Shadow;
|
||||||
@@ -86,14 +87,14 @@ public abstract class RSWallDecorationMixin implements RSWallDecoration
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Override
|
@Override
|
||||||
public Area getClickbox()
|
public Shape getClickbox()
|
||||||
{
|
{
|
||||||
Area clickbox = new Area();
|
Area clickbox = new Area();
|
||||||
|
|
||||||
LocalPoint lp = getLocalLocation();
|
LocalPoint lp = getLocalLocation();
|
||||||
Area clickboxA = Perspective.getClickbox(client, getModel1(), 0,
|
Shape clickboxA = Perspective.getClickbox(client, getModel1(), 0,
|
||||||
new LocalPoint(lp.getX() + getXOffset(), lp.getY() + getYOffset()));
|
new LocalPoint(lp.getX() + getXOffset(), lp.getY() + getYOffset()));
|
||||||
Area clickboxB = Perspective.getClickbox(client, getModel2(), 0, lp);
|
Shape clickboxB = Perspective.getClickbox(client, getModel2(), 0, lp);
|
||||||
|
|
||||||
if (clickboxA == null && clickboxB == null)
|
if (clickboxA == null && clickboxB == null)
|
||||||
{
|
{
|
||||||
@@ -102,15 +103,10 @@ public abstract class RSWallDecorationMixin implements RSWallDecoration
|
|||||||
|
|
||||||
if (clickboxA != null)
|
if (clickboxA != null)
|
||||||
{
|
{
|
||||||
clickbox.add(clickboxA);
|
return clickboxA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clickboxB != null)
|
return clickboxB;
|
||||||
{
|
|
||||||
clickbox.add(clickboxB);
|
|
||||||
}
|
|
||||||
|
|
||||||
return clickbox;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
|||||||
Reference in New Issue
Block a user