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}