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}