001package squidpony.squidmath; 002 003import java.io.Serializable; 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Iterator; 007import java.util.Map; 008 009/** 010 * A small extension of OrderedMap that specifically handles {@code short[]} regions as produced by {@link CoordPacker}. 011 * The methods {@link #allAt(int, int)}, {@link #containsRegion(short[])}, and {@link #regionsContaining(int, int)} are 012 * added here, and the minor extra work needed to handle array keys in OrderedMap is taken care of automatically. 013 * {@link #toString()} also produces nicer output by default for this usage, with the keys printed in a usable way. 014 * Created by Tommy Ettinger on 11/24/2016. 015 */ 016public class RegionMap<V> extends OrderedMap<short[], V> implements Serializable { 017 private static final long serialVersionUID = 2L; 018 019 public RegionMap(final int expected, final float f) { 020 super(expected, f, CrossHash.shortHasher); 021 CoordPacker.init(); 022 } 023 024 /** 025 * Creates a new RegionMap with 0.75f as load factor. 026 * 027 * @param expected the expected number of elements in the RegionMap. 028 */ 029 public RegionMap(final int expected) { 030 this(expected, DEFAULT_LOAD_FACTOR); 031 } 032 033 /** 034 * Creates a new RegionMap with initial expected 16 entries and 0.75f as load factor. 035 */ 036 public RegionMap() { 037 this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR); 038 } 039 040 /** 041 * Creates a new RegionMap copying a given one. 042 * 043 * @param m a {@link Map} to be copied into the new RegionMap. 044 * @param f the load factor. 045 */ 046 public RegionMap(final Map<short[], ? extends V> m, final float f) { 047 this(m.size(), f); 048 putAll(m); 049 } 050 051 /** 052 * Creates a new RegionMap with 0.75f as load factor copying a given one. 053 * 054 * @param m a {@link Map} to be copied into the new RegionMap. 055 */ 056 public RegionMap(final Map<short[], ? extends V> m) { 057 this(m, DEFAULT_LOAD_FACTOR); 058 } 059 060 /** 061 * Creates a new RegionMap using the elements of two parallel arrays. 062 * 063 * @param keyArray the array of keys of the new RegionMap. 064 * @param valueArray the array of corresponding values in the new RegionMap. 065 * @param f the load factor. 066 * @throws IllegalArgumentException if <code>k</code> and <code>v</code> have different lengths. 067 */ 068 public RegionMap(final short[][] keyArray, final V[] valueArray, final float f) { 069 this(keyArray.length, f); 070 if (keyArray.length != valueArray.length) 071 throw new IllegalArgumentException("The key array and the value array have different lengths (" + keyArray.length + " and " + valueArray.length + ")"); 072 for (int i = 0; i < keyArray.length; i++) 073 put(keyArray[i], valueArray[i]); 074 } 075 076 /** 077 * Creates a new RegionMap using the elements of two parallel arrays. 078 * 079 * @param keyColl the collection of keys of the new RegionMap. 080 * @param valueColl the collection of corresponding values in the new RegionMap. 081 * @param f the load factor. 082 * @throws IllegalArgumentException if <code>k</code> and <code>v</code> have different lengths. 083 */ 084 public RegionMap(final Collection<short[]> keyColl, final Collection<V> valueColl, final float f) { 085 this(keyColl.size(), f); 086 if (keyColl.size() != valueColl.size()) 087 throw new IllegalArgumentException("The key array and the value array have different lengths (" + keyColl.size() + " and " + valueColl.size() + ")"); 088 Iterator<short[]> ki = keyColl.iterator(); 089 Iterator<V> vi = valueColl.iterator(); 090 while (ki.hasNext() && vi.hasNext()) { 091 put(ki.next(), vi.next()); 092 } 093 } 094 095 /** 096 * Creates a new RegionMap with 0.75f as load factor using the elements of two parallel arrays. 097 * 098 * @param keyArray the array of keys of the new RegionMap. 099 * @param valueArray the array of corresponding values in the new RegionMap. 100 * @throws IllegalArgumentException if <code>k</code> and <code>v</code> have different lengths. 101 */ 102 public RegionMap(final short[][] keyArray, final V[] valueArray) { 103 this(keyArray, valueArray, DEFAULT_LOAD_FACTOR); 104 } 105 106 /** 107 * Gets a List of all values associated with regions containing a given x,y point. 108 * 109 * @param x the x coordinate of the point in question 110 * @param y the y coordinate of the point in question 111 * @return an ArrayList of all V values corresponding to regions containing the given x,y point. 112 */ 113 public ArrayList<V> allAt(int x, int y) { 114 ArrayList<V> found = new ArrayList<>(size); 115 OrderedSet<short[]> regions = CoordPacker.findManyPacked(x, y, keySet()); 116 int count = regions.size; 117 for (int i = 0; i < count; i++) { 118 found.add(get(regions.getAt(i))); 119 } 120 return found; 121 } 122 123 /** 124 * Checks if a region, stored as packed data (possibly from CoordPacker or this class) overlaps with regions stored 125 * in this object as keys. Returns true if there is any overlap, false otherwise 126 * 127 * @param region the packed region to check for overlap with regions this stores values for 128 * @return true if the region overlaps at all, false otherwise 129 */ 130 public boolean containsRegion(short[] region) { 131 return CoordPacker.regionsContain(region, keySet()); 132 } 133 134 /** 135 * Gets a List of all regions containing a given x,y point. 136 * 137 * @param x the x coordinate of the point in question 138 * @param y the y coordinate of the point in question 139 * @return an ArrayList of all regions in this data structure containing the given x,y point. 140 */ 141 public OrderedSet<short[]> regionsContaining(int x, int y) { 142 return CoordPacker.findManyPacked(x, y, keySet()); 143 } 144 145 public String toString(String separator) { 146 return toString(separator, false); 147 } 148 149 @Override 150 public String toString() { 151 return toString(", ", true); 152 } 153 154 private String toString(String separator, boolean braces) { 155 if (size == 0) return braces ? "{}" : ""; 156 StringBuilder buffer = new StringBuilder(32); 157 if (braces) buffer.append('{'); 158 short[][] keyTable = this.key; 159 V[] valueTable = this.value; 160 int i = keyTable.length; 161 while (i-- > 0) { 162 short[] key = keyTable[i]; 163 if (key == null) continue; 164 buffer.append("Packed Region:") 165 .append(CoordPacker.encodeASCII(key)) 166 .append('=') 167 .append(valueTable[i]); 168 break; 169 } 170 while (i-- > 0) { 171 short[] key = keyTable[i]; 172 if (key == null) continue; 173 buffer.append(separator) 174 .append("Packed Region:") 175 .append(CoordPacker.encodeASCII(key)) 176 .append('=') 177 .append(valueTable[i]); 178 } 179 if (braces) buffer.append('}'); 180 return buffer.toString(); 181 } 182}