001package squidpony.squidgrid.mapping;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Comparator;
006import java.util.Iterator;
007import java.util.List;
008
009import squidpony.squidgrid.Direction;
010import squidpony.squidgrid.iterator.SquidIterators;
011import squidpony.squidmath.Coord;
012
013/**
014 * Rectangles in 2D grids. Checkout {@link Utils} for utility methods.
015 * 
016 * @author smelC
017 * 
018 * @see RectangleRoomFinder How to find rectangles in a dungeon
019 */
020public interface Rectangle {
021
022        /**
023         * @return The bottom left coordinate of the room.
024         */
025        Coord getBottomLeft();
026
027        /**
028         * @return The room's width (from {@link #getBottomLeft()). It is greater or
029         *         equal than 0.
030         */
031        int getWidth();
032
033        /**
034         * @return The room's height (from {@link #getBottomLeft()). It is greater
035         *         or equal than 0.
036         */
037        int getHeight();
038
039        /**
040         * Utilities pertaining to {@link Room}
041         * 
042         * @author smelC
043         */
044        class Utils {
045
046                /** A comparator that uses {@link #size(Rectangle)} as the measure. */
047                public static final Comparator<Rectangle> SIZE_COMPARATOR = new Comparator<Rectangle>() {
048                        @Override
049                        public int compare(Rectangle o1, Rectangle o2) {
050                                return Integer.compare(size(o1), size(o2));
051                        }
052                };
053
054                /**
055                 * @param r
056                 * @param c
057                 * @return Whether {@code r} contains {@code c}.
058                 */
059                public static boolean contains(Rectangle r, Coord c) {
060                        final Coord bottomLeft = r.getBottomLeft();
061                        final int width = r.getWidth();
062                        final int height = r.getHeight();
063                        if (c.x < bottomLeft.x)
064                                /* Too much to the left */
065                                return false;
066                        if (bottomLeft.x + width < c.x)
067                                /* Too much to the right */
068                                return false;
069                        if (bottomLeft.y < c.y)
070                                /* Too low */
071                                return false;
072                        if (c.y < bottomLeft.y - height)
073                                /* Too high */
074                                return false;
075                        return true;
076                }
077
078                /**
079                 * @param r
080                 * @param c
081                 * @return {@code true} if {@code r} contains a member of {@code cs}.
082                 */
083                public static boolean containsAny(Rectangle r, Collection<Coord> cs) {
084                        for (Coord c : cs) {
085                                if (contains(r, c))
086                                        return true;
087                        }
088                        return false;
089                }
090
091                /**
092                 * @param rs
093                 * @param c
094                 * @return {@code true} if a member of {@code rs}
095                 *         {@link #contains(Room, Coord) contains} {@code c}.
096                 */
097                public static boolean contains(Iterable<? extends Rectangle> rs, Coord c) {
098                        for (Rectangle r : rs) {
099                                if (contains(r, c))
100                                        return true;
101                        }
102                        return false;
103                }
104
105                /**
106                 * @param r
107                 * @return The number of cells that {@code r} covers.
108                 */
109                public static int size(Rectangle r) {
110                        return r.getWidth() * r.getHeight();
111                }
112
113                /**
114                 * @param r
115                 * @return The center of {@code r}.
116                 */
117                public static Coord center(Rectangle r) {
118                        final Coord bl = r.getBottomLeft();
119                        /*
120                         * bl.y - ... : because we're in SquidLib coordinates (0,0) is top
121                         * left
122                         */
123                        return Coord.get(bl.x + Math.round(r.getWidth() / 2), bl.y - Math.round(r.getHeight() / 2));
124                }
125
126                /**
127                 * Use {@link #cellsList(Rectangle)} if you want them all.
128                 * 
129                 * @param r
130                 * @return The cells that {@code r} contains, from bottom left to top
131                 *         right; lazily computed.
132                 */
133                public static Iterator<Coord> cells(Rectangle r) {
134                        return new SquidIterators.RectangleFromBottomLeftToTopRight(r.getBottomLeft(), r.getWidth(),
135                                        r.getHeight());
136                }
137
138                /**
139                 * Use {@link #cellsList(Rectangle)} if you may stop before the end of
140                 * the list, you'll save some memory.
141                 * 
142                 * @param r
143                 * @return The cells that {@code r} contains, from bottom left to top
144                 *         right.
145                 */
146                public static List<Coord> cellsList(Rectangle r) {
147                        /* Allocate it with the right size, to avoid internal resizings */
148                        final List<Coord> result = new ArrayList<Coord>(size(r));
149                        final Iterator<Coord> it = cells(r);
150                        while (it.hasNext())
151                                result.add(it.next());
152                        return result;
153                }
154
155                /**
156                 * @param d
157                 *            A direction.
158                 * @return {@code r} extended to {@code d} by one row and/or column.
159                 */
160                public static Rectangle extend(Rectangle r, Direction d) {
161                        final Coord bl = r.getBottomLeft();
162                        final int width = r.getWidth();
163                        final int height = r.getHeight();
164
165                        switch (d) {
166                        case DOWN_LEFT:
167                                return new Rectangle.Impl(bl.translate(Direction.DOWN_LEFT), width + 1, height + 1);
168                        case DOWN_RIGHT:
169                                return new Rectangle.Impl(bl.translate(Direction.DOWN), width + 1, height + 1);
170                        case NONE:
171                                return r;
172                        case UP_LEFT:
173                                return new Rectangle.Impl(bl.translate(Direction.LEFT), width + 1, height + 1);
174                        case UP_RIGHT:
175                                return new Rectangle.Impl(bl, width + 1, height + 1);
176                        case DOWN:
177                                return new Rectangle.Impl(bl.translate(Direction.DOWN), width, height + 1);
178                        case LEFT:
179                                return new Rectangle.Impl(bl.translate(Direction.LEFT), width + 1, height);
180                        case RIGHT:
181                                return new Rectangle.Impl(bl, width + 1, height);
182                        case UP:
183                                return new Rectangle.Impl(bl, width, height + 1);
184                        }
185                        throw new IllegalStateException("Unmatched direction in Rectangle.Utils::extend: " + d);
186                }
187
188        }
189
190        /**
191         * @author smelC
192         */
193        class Impl implements Rectangle {
194
195                protected final Coord bottomLeft;
196                protected final int width;
197                protected final int height;
198
199                public Impl(Coord bottomLeft, int width, int height) {
200                        this.bottomLeft = bottomLeft;
201                        this.width = width;
202                        this.height = height;
203                }
204
205                @Override
206                public Coord getBottomLeft() {
207                        return bottomLeft;
208                }
209
210                @Override
211                public int getWidth() {
212                        return width;
213                }
214
215                @Override
216                public int getHeight() {
217                        return height;
218                }
219
220                @Override
221                public int hashCode() {
222                        final int prime = 31;
223                        int result = 1;
224                        result = prime * result + ((bottomLeft == null) ? 0 : bottomLeft.hashCode());
225                        result = prime * result + height;
226                        result = prime * result + width;
227                        return result;
228                }
229
230                @Override
231                public boolean equals(Object obj) {
232                        if (this == obj)
233                                return true;
234                        if (obj == null)
235                                return false;
236                        if (getClass() != obj.getClass())
237                                return false;
238                        Impl other = (Impl) obj;
239                        if (bottomLeft == null) {
240                                if (other.bottomLeft != null)
241                                        return false;
242                        } else if (!bottomLeft.equals(other.bottomLeft))
243                                return false;
244                        if (height != other.height)
245                                return false;
246                        if (width != other.width)
247                                return false;
248                        return true;
249                }
250
251                @Override
252                public String toString() {
253                        return "Room at " + bottomLeft + ", width:" + width + ", height:" + height;
254                }
255        }
256
257}