dynmap/DynmapCore/src/main/java/org/dynmap/markers/impl/AreaMarkerImpl.java

642 lines
20 KiB
Java

package org.dynmap.markers.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapWorld;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.hdmap.HDPerspective;
import org.dynmap.markers.AreaMarker;
import org.dynmap.markers.EnterExitMarker;
import org.dynmap.markers.MarkerSet;
import org.dynmap.markers.impl.MarkerAPIImpl.MarkerUpdate;
import org.dynmap.utils.Vector3D;
class AreaMarkerImpl implements AreaMarker, EnterExitMarker {
private String markerid;
private String label;
private boolean markup;
private String desc;
private MarkerSetImpl markerset;
private String world;
private String normalized_world;
private boolean ispersistent;
private ArrayList<Coord> corners;
private int lineweight = 3;
private double lineopacity = 0.8;
private int linecolor = 0xFF0000;
private double fillopacity = 0.35;
private int fillcolor = 0xFF0000;
private double ytop = 64.0;
private double ybottom = 64.0;
private boolean boostflag = false;
private int minzoom;
private int maxzoom;
private EnterExitText greeting;
private EnterExitText farewell;
private static class Coord {
double x, z;
Coord(double x, double z) {
this.x = x; this.z = z;
}
}
private static class BoundingBox {
double xmin, xmax;
double ymin, ymax;
double xp[];
double yp[];
}
private Map<String, BoundingBox> bb_cache = null;
/**
* Create area marker
* @param id - marker ID
* @param lbl - label
* @param markup - if true, label is HTML markup
* @param world - world id
* @param x - x coord list
* @param z - z coord list
* @param persistent - true if persistent
* @param set - marker set
*/
AreaMarkerImpl(String id, String lbl, boolean markup, String world, double x[], double z[], boolean persistent, MarkerSetImpl set) {
markerid = id;
if(lbl != null)
label = lbl;
else
label = id;
this.markup = markup;
this.corners = new ArrayList<Coord>();
for(int i = 0; i < x.length; i++) {
this.corners.add(new Coord(x[i], z[i]));
}
this.world = world;
this.normalized_world = DynmapWorld.normalizeWorldName(world);
this.desc = null;
ispersistent = persistent;
markerset = set;
if(MapManager.mapman != null) {
DynmapWorld w = MapManager.mapman.getWorld(world);
if(w != null) {
ytop = ybottom = w.sealevel+1; /* Default to world sealevel */
}
}
this.minzoom = -1;
this.maxzoom = -1;
this.greeting = null;
this.farewell = null;
}
/**
* Make bare area marker - used for persistence load
* @param id - marker ID
* @param set - marker set
*/
AreaMarkerImpl(String id, MarkerSetImpl set) {
markerid = id;
markerset = set;
label = id;
markup = false;
desc = null;
corners = new ArrayList<Coord>();
world = normalized_world = "world";
this.minzoom = -1;
this.maxzoom = -1;
this.greeting = null;
this.farewell = null;
}
/**
* Load marker from configuration node
* @param node - configuration node
*/
boolean loadPersistentData(ConfigurationNode node) {
label = node.getString("label", markerid);
markup = node.getBoolean("markup", false);
ytop = node.getDouble("ytop", 64.0);
ybottom = node.getDouble("ybottom", 64.0);
List<Double> xx = node.getList("x");
List<Double> zz = node.getList("z");
corners.clear();
if((xx != null) && (zz != null)) {
for(int i = 0; (i < xx.size()) && (i < zz.size()); i++)
corners.add(new Coord(xx.get(i), zz.get(i)));
}
world = node.getString("world", "world");
normalized_world = DynmapWorld.normalizeWorldName(world);
desc = node.getString("desc", null);
lineweight = node.getInteger("strokeWeight", -1);
if(lineweight == -1) { /* Handle typo-saved value */
lineweight = node.getInteger("stokeWeight", 3);
}
lineopacity = node.getDouble("strokeOpacity", 0.8);
linecolor = node.getInteger("strokeColor", 0xFF0000);
fillopacity = node.getDouble("fillOpacity", 0.35);
fillcolor = node.getInteger("fillColor", 0xFF0000);
boostflag = node.getBoolean("boostFlag", false);
minzoom = node.getInteger("minzoom", -1);
maxzoom = node.getInteger("maxzoom", -1);
String gt = node.getString("greeting", null);
String gst = node.getString("greetingsub", null);
if ((gt != null) || (gst != null)) {
greeting = new EnterExitText();
greeting.title = gt;
greeting.subtitle = gst;
}
String ft = node.getString("farewell", null);
String fst = node.getString("farewellsub", null);
if ((ft != null) || (fst != null)) {
farewell = new EnterExitText();
farewell.title = ft;
farewell.subtitle = fst;
}
ispersistent = true; /* Loaded from config, so must be */
return true;
}
void cleanup() {
corners.clear();
markerset = null;
bb_cache = null;
}
@Override
public String getMarkerID() {
return markerid;
}
@Override
public MarkerSet getMarkerSet() {
return markerset;
}
@Override
public void deleteMarker() {
if(markerset == null) return;
markerset.removeAreaMarker(this); /* Remove from our marker set (notified by set) */
cleanup();
}
@Override
public boolean isPersistentMarker() {
return ispersistent;
}
@Override
public String getLabel() {
return label;
}
@Override
public void setLabel(String lbl) {
setLabel(lbl, false);
}
@Override
public void setLabel(String lbl, boolean markup) {
if(markerset == null) return;
label = lbl;
this.markup = markup;
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
/**
* Get configuration node to be saved
* @return node
*/
Map<String, Object> getPersistentData() {
if(!ispersistent) /* Nothing if not persistent */
return null;
HashMap<String, Object> node = new HashMap<String, Object>();
node.put("label", label);
node.put("markup", markup);
List<Double> xx = new ArrayList<Double>();
List<Double> zz = new ArrayList<Double>();
for(int i = 0; i < corners.size(); i++) {
xx.add(corners.get(i).x);
zz.add(corners.get(i).z);
}
node.put("x", xx);
node.put("ytop", Double.valueOf(ytop));
node.put("ybottom", Double.valueOf(ybottom));
node.put("z", zz);
node.put("world", world);
if(desc != null)
node.put("desc", desc);
node.put("strokeWeight", lineweight);
node.put("strokeOpacity", lineopacity);
node.put("strokeColor", linecolor);
node.put("fillOpacity", fillopacity);
node.put("fillColor", fillcolor);
if (boostflag) {
node.put("boostFlag", true);
}
if (minzoom >= 0) {
node.put("minzoom", minzoom);
}
if (maxzoom >= 0) {
node.put("maxzoom", maxzoom);
}
if (greeting != null) {
if (greeting.title != null) {
node.put("greeting", greeting.title);
}
if (greeting.subtitle != null) {
node.put("greetingsub", greeting.subtitle);
}
}
if (farewell != null) {
if (farewell.title != null) {
node.put("farewell", farewell.title);
}
if (farewell.subtitle != null) {
node.put("farewellsub", farewell.subtitle);
}
}
return node;
}
@Override
public String getWorld() {
return world;
}
@Override
public String getNormalizedWorld() {
return normalized_world;
}
@Override
public boolean isLabelMarkup() {
return markup;
}
@Override
public void setDescription(String desc) {
if(markerset == null) return;
if((this.desc == null) || (this.desc.equals(desc) == false)) {
this.desc = desc;
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
}
/**
* Get marker description
* @return descrption
*/
public String getDescription() {
return this.desc;
}
@Override
public double getTopY() {
return ytop;
}
@Override
public double getBottomY() {
return ybottom;
}
@Override
public void setRangeY(double ytop, double ybottom) {
if(markerset == null) return;
if((this.ytop != ytop) || (this.ybottom != ybottom)) {
this.ytop = ytop;
this.ybottom = ybottom;
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
bb_cache = null;
}
}
@Override
public int getCornerCount() {
return corners.size();
}
@Override
public double getCornerX(int n) {
Coord c = corners.get(n);
if(c != null)
return c.x;
return 0;
}
@Override
public double getCornerZ(int n) {
Coord c = corners.get(n);
if(c != null)
return c.z;
return 0;
}
@Override
public void setCornerLocation(int n, double x, double z) {
if(markerset == null) return;
Coord c;
if(n >= corners.size()) {
corners.add(new Coord(x, z));
}
else {
c = corners.get(n);
if((c.x == x) && (c.z == z))
return;
c.x = x;
c.z = z;
}
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
bb_cache = null;
}
@Override
public void deleteCorner(int n) {
if(markerset == null) return;
if(n < corners.size()) {
corners.remove(n);
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
bb_cache = null;
}
}
@Override
public void setCornerLocations(double[] x, double[] z) {
if(markerset == null) return;
/* Check if equals */
if(x.length == corners.size()) {
boolean match = true;
for(int i = 0; i < x.length; i++) {
Coord c = corners.get(i);
if((c.x != x[i]) || (c.z != z[i])) {
match = false;
break;
}
}
if(match)
return;
}
corners.clear();
for(int i = 0; (i < x.length) && (i < z.length); i++) {
corners.add(new Coord(x[i], z[i]));
}
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
bb_cache = null;
}
@Override
public void setLineStyle(int weight, double opacity, int color) {
if(markerset == null) return;
if((weight != lineweight) || (opacity != lineopacity) || (color != linecolor)) {
lineweight = weight;
lineopacity = opacity;
linecolor = color;
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
}
@Override
public int getLineWeight() {
return lineweight;
}
@Override
public double getLineOpacity() {
return lineopacity;
}
@Override
public int getLineColor() {
return linecolor;
}
@Override
public void setFillStyle(double opacity, int color) {
if(markerset == null) return;
if((opacity != fillopacity) || (color != fillcolor)) {
fillopacity = opacity;
fillcolor = color;
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
}
@Override
public double getFillOpacity() {
return fillopacity;
}
@Override
public int getFillColor() {
return fillcolor;
}
@Override
public void setMarkerSet(MarkerSet newset) {
if(markerset != null) {
markerset.removeAreaMarker(this); /* Remove from our marker set (notified by set) */
}
markerset = (MarkerSetImpl)newset;
markerset.insertAreaMarker(this);
}
@Override
public void setBoostFlag(boolean bflag) {
if (this.boostflag != bflag) {
this.boostflag = bflag;
if (markerset != null) {
setMarkerSet(markerset);
}
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
}
@Override
public boolean getBoostFlag() {
return boostflag;
}
final boolean testTileForBoostMarkers(DynmapWorld w, HDPerspective perspective, final double tile_x, final double tile_y, final double tile_dim) {
Map<String, BoundingBox> bbc = bb_cache;
if(bbc == null) {
bbc = new ConcurrentHashMap<String, BoundingBox>();
}
BoundingBox bb = bbc.get(perspective.getName());
if (bb == null) { // No cached bounding box, so generate it
bb = new BoundingBox();
Vector3D v = new Vector3D();
Vector3D v2 = new Vector3D();
bb.xmin = Double.MAX_VALUE;
bb.xmax = -Double.MAX_VALUE;
bb.ymin = Double.MAX_VALUE;
bb.ymax = -Double.MAX_VALUE;
if (corners != null) {
ArrayList<Coord> crn = corners;
int cnt = crn.size();
if (cnt == 2) { // Special case
cnt = 4;
crn = new ArrayList<Coord>();
Coord c0 = corners.get(0);
Coord c1 = corners.get(1);
crn.add(c0);
crn.add(new Coord(c0.x, c1.z));
crn.add(c1);
crn.add(new Coord(c1.x, c0.z));
}
double ymid = (this.ytop + this.ybottom) / 2.0;
bb.xp = new double[cnt];
bb.yp = new double[cnt];
for (int i = 0; i < cnt; i++) {
Coord c = crn.get(i);
v.x = c.x; v.y = ymid; v.z = c.z; // get coords of point, in world coord
perspective.transformWorldToMapCoord(v, v2); // Transform to map coord
if (v2.x < bb.xmin) bb.xmin = v2.x;
if (v2.y < bb.ymin) bb.ymin = v2.y;
if (v2.x > bb.xmax) bb.xmax = v2.x;
if (v2.y > bb.ymax) bb.ymax = v2.y;
bb.xp[i] = v2.x;
bb.yp[i] = v2.y;
}
}
//System.out.println("x=" + bb.xmin + " - " + bb.xmax + ", y=" + bb.ymin + " - " + bb.ymax);
bbc.put(perspective.getName(), bb);
bb_cache = bbc;
}
final double tile_x2 = tile_x + tile_dim;
final double tile_y2 = tile_y + tile_dim;
if ((bb.xmin > tile_x2) || (bb.xmax < tile_x) || (bb.ymin > tile_y2) || (bb.ymax < tile_y)) {
//System.out.println("tile: " + tile_x + " / " + tile_y + " - miss");
return false;
}
final int cnt = bb.xp.length;
final double[] px = bb.xp;
final double[] py = bb.yp;
/* Now see if tile square intersects polygon - start with seeing if any point inside */
if(MarkerImpl.testPointInPolygon(tile_x, tile_y, px, py)) {
return true; // If tile corner inside, we intersect
}
if(MarkerImpl.testPointInPolygon(tile_x2, tile_y, px, py)) {
return true; // If tile corner inside, we intersect
}
if(MarkerImpl.testPointInPolygon(tile_x, tile_y2, px, py)) {
return true; // If tile corner inside, we intersect
}
if(MarkerImpl.testPointInPolygon(tile_x2, tile_y2, px, py)) {
return true; // If tile corner inside, we intersect
}
/* Test if any polygon corners are inside square */
for(int i = 0; i < cnt; i++) {
if((px[i] >= tile_x) && (px[i] <= tile_x2) && (py[i] >= tile_y) && (py[i] <= tile_y2)) {
return true; // If poly corner inside tile, we intersect
}
}
// Otherwise, only intersects if at least one edge crosses
//for (int i = 0, j = cnt-1; i < cnt; j = i++) {
// // Test for X=tile_x side
// if ((px[i] < tile_x) && (px[j] >= tile_x) && ()
// }
//System.out.println("tile: " + tile_x + " / " + tile_y + " - hit");
return false;
}
@Override
public int getMinZoom() {
return minzoom;
}
@Override
public void setMinZoom(int zoom) {
if (zoom < 0) zoom = -1;
if (this.minzoom == zoom) return;
this.minzoom = zoom;
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
@Override
public int getMaxZoom() {
return maxzoom;
}
@Override
public void setMaxZoom(int zoom) {
if (zoom < 0) zoom = -1;
if (this.maxzoom == zoom) return;
this.maxzoom = zoom;
MarkerAPIImpl.areaMarkerUpdated(this, MarkerUpdate.UPDATED);
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
@Override
public EnterExitText getGreetingText() {
return greeting;
}
@Override
public EnterExitText getFarewellText() {
return farewell;
}
@Override
public void setGreetingText(String title, String subtitle) {
if ((title != null) || (subtitle != null)) {
greeting = new EnterExitText();
greeting.title = title;
greeting.subtitle = subtitle;
}
else {
greeting = null;
}
if (markerset != null) {
setMarkerSet(markerset);
}
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
@Override
public void setFarewellText(String title, String subtitle) {
if ((title != null) || (subtitle != null)) {
farewell = new EnterExitText();
farewell.title = title;
farewell.subtitle = subtitle;
}
else {
farewell = null;
}
if (markerset != null) {
setMarkerSet(markerset);
}
if(ispersistent)
MarkerAPIImpl.saveMarkers();
}
@Override
public boolean testIfPointWithinMarker(String worldid, double x, double y, double z) {
// Wrong world
if (!worldid.equals(this.world)) {
return false;
}
// If Y is in range (if there is a range)
if ((ytop != ybottom) && ((y < ybottom) || (y > ytop))) {
return false;
}
// Test if inside polygon
int nvert = corners.size();
int i, j;
Coord v0, v1;
boolean c = false;
if (nvert == 2) { // Diagonal corners (simple rectangle
v0 = corners.get(0);
v1 = corners.get(1);
if (((v0.x > x) != (v1.x > x)) &&
((v0.z > z) != (v1.z > z))) {
c = true;
}
}
else {
for (i = 0, j = nvert-1; i < nvert; j = i++) {
v0 = corners.get(i);
v1 = corners.get(j);
if (((v0.z > z) != (v1.z > z)) &&
(((x - v0.x) * (v1.z - v0.z)) <
((v1.x - v0.x) * (z - v0.z)))) {
c = !c;
}
}
}
return c;
}
}