001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.collection; 020 021import com.plotsquared.core.util.RegionUtil; 022import com.sk89q.worldedit.regions.CuboidRegion; 023 024import java.util.HashSet; 025import java.util.Set; 026 027public class QuadMap<T> { 028 029 public final int size; 030 public final int x; 031 public final int z; 032 private final int newsize; 033 private final int min; 034 public HashSet<T> objects; 035 public QuadMap<T> one; 036 public QuadMap<T> two; 037 public QuadMap<T> three; 038 public QuadMap<T> four; 039 public QuadMap<T> skip; 040 041 public QuadMap(int size, int x, int z) { 042 this.size = size; 043 this.x = x; 044 this.z = z; 045 this.newsize = size >> 1; 046 this.min = 512; 047 } 048 049 public QuadMap(int size, int x, int z, int min) { 050 this.size = size; 051 this.x = x; 052 this.z = z; 053 this.newsize = size >> 1; 054 this.min = min; 055 } 056 057 public int count() { 058 int size = countBelow(); 059 if (this.objects != null) { 060 size += this.objects.size(); 061 } 062 return size; 063 } 064 065 public Set<T> getAll() { 066 HashSet<T> all = new HashSet<>(); 067 if (this.objects != null) { 068 all.addAll(this.objects); 069 } 070 if (this.skip != null) { 071 all.addAll(this.skip.getAll()); 072 return all; 073 } 074 if (this.one != null) { 075 all.addAll(this.one.getAll()); 076 } 077 if (this.two != null) { 078 all.addAll(this.two.getAll()); 079 } 080 if (this.three != null) { 081 all.addAll(this.three.getAll()); 082 } 083 if (this.four != null) { 084 all.addAll(this.four.getAll()); 085 } 086 return all; 087 } 088 089 public int countCurrent() { 090 return this.objects == null ? 0 : this.objects.size(); 091 } 092 093 public int countBelow() { 094 int size = 0; 095 if (this.one != null) { 096 size += this.one.count(); 097 } 098 if (this.two != null) { 099 size += this.two.count(); 100 } 101 if (this.three != null) { 102 size += this.three.count(); 103 } 104 if (this.four != null) { 105 size += this.four.count(); 106 } 107 return size; 108 } 109 110 public void add(T area) { 111 if (this.size <= this.min) { 112 if (this.objects == null) { 113 this.objects = new HashSet<>(); 114 } 115 this.objects.add(area); 116 return; 117 } 118 CuboidRegion region = getRegion(area); 119 if (region.getMinimumPoint().getX() >= this.x) { 120 if (region.getMinimumPoint().getZ() >= this.z) { 121 if (this.one == null) { 122 this.one = 123 newInstance(this.newsize, this.x + this.newsize, this.z + this.newsize, 124 this.min 125 ); 126 } 127 this.one.add(area); 128 recalculateSkip(); 129 return; 130 } else if (region.getMaximumPoint().getZ() < this.z) { 131 if (this.two == null) { 132 this.two = 133 newInstance(this.newsize, this.x + this.newsize, this.z - this.newsize, 134 this.min 135 ); 136 } 137 this.two.add(area); 138 recalculateSkip(); 139 return; 140 } 141 } else if (region.getMaximumPoint().getX() < this.x) { 142 if (region.getMinimumPoint().getZ() >= this.z) { 143 if (this.four == null) { 144 this.four = 145 newInstance(this.newsize, this.x - this.newsize, this.z + this.newsize, 146 this.min 147 ); 148 } 149 this.four.add(area); 150 recalculateSkip(); 151 return; 152 } else if (region.getMaximumPoint().getZ() < this.z) { 153 if (this.three == null) { 154 this.three = 155 newInstance(this.newsize, this.x - this.newsize, this.z - this.newsize, 156 this.min 157 ); 158 } 159 this.three.add(area); 160 recalculateSkip(); 161 return; 162 } 163 } 164 if (this.objects == null) { 165 this.objects = new HashSet<>(); 166 } 167 this.objects.add(area); 168 } 169 170 public CuboidRegion getRegion(T value) { 171 return null; 172 } 173 174 public QuadMap<T> newInstance(int newsize, int x, int z, int min) { 175 try { 176 return new QuadMap<T>(newsize, x, z, min) { 177 @Override 178 public CuboidRegion getRegion(T value) { 179 return QuadMap.this.getRegion(value); 180 } 181 }; 182 } catch (Throwable e) { 183 e.printStackTrace(); 184 return null; 185 } 186 } 187 188 public boolean remove(T area) { 189 if (this.objects != null) { 190 if (this.objects.remove(area)) { 191 return this.objects.isEmpty(); 192 } 193 } 194 if (this.skip != null) { 195 if (this.skip.remove(area)) { 196 this.skip = null; 197 } 198 } else { 199 CuboidRegion region = getRegion(area); 200 if (region.getMinimumPoint().getX() >= this.x) { 201 if (region.getMinimumPoint().getZ() >= this.z) { 202 if (this.one != null) { 203 if (this.one.remove(area)) { 204 this.one = null; 205 } 206 return countCurrent() == 0; 207 } 208 } else { 209 if (this.two != null) { 210 if (this.two.remove(area)) { 211 this.two = null; 212 } 213 return countCurrent() == 0; 214 } 215 } 216 } else { 217 if (region.getMinimumPoint().getZ() >= this.z) { 218 if (this.four != null) { 219 if (this.four.remove(area)) { 220 this.four = null; 221 } 222 return countCurrent() == 0; 223 } 224 } else { 225 if (this.three != null) { 226 if (this.three.remove(area)) { 227 this.three = null; 228 } 229 return countCurrent() == 0; 230 } 231 } 232 } 233 } 234 return false; 235 } 236 237 @SuppressWarnings("unchecked") 238 public void recalculateSkip() { 239 QuadMap<T> map = null; 240 for (QuadMap<T> current : new QuadMap[]{this.one, this.two, this.three, this.four}) { 241 if (current != null) { 242 if (map != null) { 243 this.skip = null; 244 return; 245 } 246 map = current; 247 } 248 } 249 this.skip = map.skip == null ? map : map.skip; 250 } 251 252 public Set<T> get(CuboidRegion region) { 253 HashSet<T> set = new HashSet<>(); 254 if (this.objects != null) { 255 for (T obj : this.objects) { 256 if (RegionUtil.intersects(getRegion(obj), region)) { 257 set.add(obj); 258 } 259 } 260 } 261 if (this.skip != null) { 262 if (this.skip.intersects(region)) { 263 set.addAll(this.skip.get(region)); 264 } 265 } else { 266 if (this.one != null && this.one.intersects(region)) { 267 set.addAll(this.one.get(region)); 268 } 269 if (this.two != null && this.two.intersects(region)) { 270 set.addAll(this.two.get(region)); 271 } 272 if (this.three != null && this.three.intersects(region)) { 273 set.addAll(this.three.get(region)); 274 } 275 if (this.four != null && this.four.intersects(region)) { 276 set.addAll(this.four.get(region)); 277 } 278 } 279 return set; 280 } 281 282 public boolean intersects(CuboidRegion other) { 283 return (other.getMinimumPoint().getX() <= this.x + this.size) && ( 284 other.getMaximumPoint().getX() >= this.x - this.size) && (other.getMinimumPoint().getZ() 285 <= this.z + this.size) && (other.getMaximumPoint().getZ() >= this.z - this.size); 286 } 287 288 public T get(int x, int z) { 289 if (this.objects != null) { 290 for (T obj : this.objects) { 291 if (RegionUtil.contains(getRegion(obj), x, z)) { 292 return obj; 293 } 294 } 295 } 296 if (this.skip != null) { 297 return this.skip.get(x, z); 298 } else { 299 if (x >= this.x) { 300 if (z >= this.z) { 301 if (this.one != null) { 302 return this.one.get(x, z); 303 } 304 } else { 305 if (this.two != null) { 306 return this.two.get(x, z); 307 } 308 } 309 } else { 310 if (z >= this.z) { 311 if (this.four != null) { 312 return this.four.get(x, z); 313 } 314 } else { 315 if (this.three != null) { 316 return this.three.get(x, z); 317 } 318 } 319 } 320 } 321 return null; 322 } 323 324}