001package fr.aumgn.dac2.shape; 002 003import java.util.Arrays; 004import java.util.Iterator; 005import java.util.List; 006 007import fr.aumgn.bukkitutils.geom.Vector; 008import fr.aumgn.bukkitutils.geom.Vector2D; 009import fr.aumgn.dac2.shape.column.Column; 010import fr.aumgn.dac2.shape.iterator.ColumnsIterator; 011 012@ShapeName("polygonal") 013public class PolygonalShape implements FlatShape { 014 015 private final int minY; 016 private final int maxY; 017 private final Vector2D[] points; 018 019 private transient Vector2D min2D = null; 020 private transient Vector2D max2D = null; 021 022 public PolygonalShape(int minY, int maxY, Vector2D[] points) { 023 this.minY = minY; 024 this.maxY = maxY; 025 this.points = points; 026 } 027 028 @Override 029 public boolean contains(Vector pt) { 030 int targetY = pt.getBlockY(); 031 return targetY >= minY && targetY <= maxY && contains2D(pt.to2D()); 032 } 033 034 @Override 035 public double getMinY() { 036 return minY; 037 } 038 039 @Override 040 public double getMaxY() { 041 return maxY; 042 } 043 044 @Override 045 public Vector2D getMin2D() { 046 if (min2D == null) { 047 calculateMinMax2D(); 048 } 049 050 return min2D; 051 } 052 053 @Override 054 public Vector2D getMax2D() { 055 if (max2D == null) { 056 calculateMinMax2D(); 057 } 058 059 return max2D; 060 } 061 062 private void calculateMinMax2D() { 063 double minX = Double.MAX_VALUE; 064 double minZ = Double.MAX_VALUE; 065 double maxX = -Double.MAX_VALUE; 066 double maxZ = -Double.MAX_VALUE; 067 068 for (Vector2D pt : points) { 069 if (minX > pt.getX()) { 070 minX = pt.getX(); 071 } 072 if (minZ > pt.getZ()) { 073 minZ = pt.getZ(); 074 } 075 if (maxX < pt.getX()) { 076 maxX = pt.getX(); 077 } 078 if (maxZ < pt.getZ()) { 079 maxZ = pt.getZ(); 080 } 081 } 082 083 min2D = new Vector2D(minX, minZ); 084 max2D = new Vector2D(maxX, maxZ); 085 } 086 087 /* 088 * Extracted (and slightly adapted) from WorldEdit. 089 * Credits goes to sk89q. 090 */ 091 @Override 092 public boolean contains2D(Vector2D pt) { 093 if (points.length < 3) { 094 return false; 095 } 096 097 if (min2D == null) { 098 calculateMinMax2D(); 099 } 100 if (!pt.isInside(min2D, max2D)) { 101 return false; 102 } 103 104 int targetX = pt.getBlockX(); //wide 105 int targetZ = pt.getBlockZ(); //depth 106 107 boolean inside = false; 108 int npoints = points.length; 109 int xNew, zNew; 110 int xOld, zOld; 111 int x1, z1; 112 int x2, z2; 113 long crossproduct; 114 int i; 115 116 xOld = points[npoints - 1].getBlockX(); 117 zOld = points[npoints - 1].getBlockZ(); 118 119 for (i = 0; i < npoints; ++i) { 120 xNew = points[i].getBlockX(); 121 zNew = points[i].getBlockZ(); 122 //Check for corner 123 if (xNew == targetX && zNew == targetZ) { 124 return true; 125 } 126 if (xNew > xOld) { 127 x1 = xOld; 128 x2 = xNew; 129 z1 = zOld; 130 z2 = zNew; 131 } else { 132 x1 = xNew; 133 x2 = xOld; 134 z1 = zNew; 135 z2 = zOld; 136 } 137 if (x1 <= targetX && targetX <= x2) { 138 crossproduct = ((long) targetZ - (long) z1) * (long) (x2 - x1) 139 - ((long) z2 - (long) z1) * (long) (targetX - x1); 140 if (crossproduct == 0) { 141 if ((z1 <= targetZ) == (targetZ <= z2)) return true; //on edge 142 } else if (crossproduct < 0 && (x1 != targetX)) { 143 inside = !inside; 144 } 145 } 146 xOld = xNew; 147 zOld = zNew; 148 } 149 150 return inside; 151 } 152 153 @Override 154 public Column getColumn(Vector2D pt) { 155 return new Column(this, pt); 156 } 157 158 @Override 159 public Iterator<Column> iterator() { 160 return new ColumnsIterator(this); 161 } 162 163 public List<Vector2D> getPoints() { 164 return Arrays.<Vector2D>asList(points); 165 } 166}