1 /* Supporting different types and containers. 2 * Copyright (C) 2017 Marko Semet 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 module structuresd.dimension.rtree; 18 19 private 20 { 21 import core.exception; 22 import std.algorithm.sorting; 23 import structuresd; 24 import structuresd.dimension; 25 import structuresd.utils; 26 } 27 28 /** 29 * Do split of a two big container. Innto two lower. 30 * @param data Source data to split 31 * @param toCompare function to get geometry to compare 32 * @return two list for one of the old container and one for the new one 33 */ 34 private pure nothrow T1[][2] _split(T1, size_t MIN, size_t MAX, T2 = T1)(T1[] data, T2 function(T1) nothrow pure toCompare) 35 in { 36 static assert(MIN >= 1); 37 assert(2 * MIN <= data.length); 38 assert(2 * MAX >= data.length); 39 } 40 out(outData) { 41 assert(outData[0].length >= MIN); 42 assert(outData[0].length <= MAX); 43 assert(outData[1].length >= MIN); 44 assert(outData[1].length <= MAX); 45 } 46 body { 47 // Most away 48 T1 a = data[0]; 49 T1 b = data[1]; 50 while(true) 51 { 52 T1 tmp = b; 53 T2.BASE_TYPE tmp2 = toCompare(b).maxGeometry(toCompare(a)).volume(); 54 foreach(ref T1 t; data) 55 { 56 if((t != a) && (t != b)) 57 { 58 if(tmp2 < toCompare(a).maxGeometry(toCompare(t)).volume()) 59 { 60 tmp = t; 61 tmp2 = toCompare(a).maxGeometry(toCompare(t)).volume(); 62 } 63 } 64 } 65 66 if(tmp == b) 67 { 68 break; 69 } 70 else 71 { 72 b = a; 73 a = tmp; 74 } 75 } 76 77 // Do sorting 78 { 79 struct SortElement 80 { 81 T1 t1; 82 T2.BASE_TYPE t2; 83 84 this(T1 t) 85 { 86 t1 = t; 87 t2 = min(getInseredVolume(toCompare(a), toCompare(t)), getInseredVolume(toCompare(b), toCompare(t))); 88 } 89 90 int opCmp(const SortElement b) 91 { 92 return this.t2 < b.t2 ? -1 : this.t2 > b.t2 ? 1 : 0; 93 } 94 bool opEquals(const SortElement b) 95 { 96 return this.t2 == b.t2; 97 } 98 } 99 100 SortElement[] sorted = new SortElement[data.length]; 101 for(size_t i = 0; i < data.length; i++) 102 { 103 sorted[i] = SortElement(data[i]); 104 } 105 size_t tmp = 0; 106 foreach(SortElement t; sorted.sort) 107 { 108 data[tmp] = t.t1; 109 tmp++; 110 } 111 } 112 113 // Do split 114 T1[] resA = new T1[MAX]; 115 T1[] resB = new T1[MAX]; 116 size_t sizeA = 1; 117 size_t sizeB = 1; 118 resA[0] = a; 119 resB[0] = b; 120 size_t missing = data.length - 2; 121 bool addedA = false; 122 bool addedB = false; 123 foreach(ref T1 t; data) 124 { 125 if((a == t) && (!addedA)) 126 { 127 addedA = true; 128 continue; 129 } 130 if((b == t) && (!addedB)) 131 { 132 addedB = true; 133 continue; 134 } 135 136 if(sizeA + missing == MIN) 137 { 138 resA[sizeA] = t; 139 sizeA++; 140 } 141 else if(sizeB + missing == MIN) 142 { 143 resB[sizeB] = t; 144 sizeB++; 145 } 146 else 147 { 148 if(getInseredVolume(toCompare(a), toCompare(t)) < getInseredVolume(toCompare(b), toCompare(t))) 149 { 150 resA[sizeA] = t; 151 sizeA++; 152 } 153 else 154 { 155 resB[sizeB] = t; 156 sizeB++; 157 } 158 } 159 missing--; 160 } 161 return [resA[0..sizeA], resB[0..sizeB]]; 162 } 163 164 /** 165 * A RTree implementation. 166 * @tparam DATA_T The data type that will be insert. It have to be castable to TYPE. 167 * @tparam TYPE Is the geometry structure that will be used. It have to match the isGeometry in parent package. 168 * @tparam MAX Maximal node size before spilt. MAX have to be at least 3 and odd. 169 * @tparam MIN Minimal node isze before merge. MIN have to be greater then 0 and lower or equal to 2 * MAX + 1. 170 */ 171 public final class RTree(DATA_T, TYPE, size_t MAX, size_t MIN, FEATURES features = FEATURES.NON) 172 { 173 static assert(isGeometry!TYPE); 174 static assert(MAX % 2 == 1); 175 static assert(MAX >= 3); 176 static assert(MIN * 2 <= MAX + 1); 177 static assert(MIN > 0); 178 179 private struct _Node 180 { 181 public size_t elements = 0; 182 public bool isLeave = false; 183 public _Node* parent = null; 184 public TYPE ownSize; 185 union 186 { 187 public DATA_T[MAX] leaves; 188 public _Node*[MAX] nodes; 189 } 190 191 @nogc 192 private nothrow pure _Node* getRoot() 193 { 194 _Node* current = &this; 195 while(current.parent !is null) 196 { 197 current = current.parent; 198 } 199 return current; 200 } 201 202 public pure nothrow _Node* insert(ref DATA_T toInsert) 203 { 204 if(this.isLeave) 205 { 206 if(this.elements < MAX) 207 { 208 // No split 209 this.leaves[this.elements] = toInsert; 210 this.elements++; 211 this.updateSize!true(); 212 return this.getRoot(); 213 } 214 else 215 { 216 // Prepare split 217 DATA_T[][2] tmp = _split!(DATA_T, MIN, MAX, TYPE)(this.leaves ~ [toInsert], function TYPE(DATA_T t) => (cast(TYPE) t)); 218 this.leaves[0..tmp[0].length] = tmp[0]; 219 this.elements = tmp[0].length; 220 this.updateSize!false(); 221 222 _Node* newNode = new _Node; 223 newNode.isLeave = true; 224 newNode.leaves[0..tmp[1].length] = tmp[1]; 225 newNode.elements = tmp[1].length; 226 newNode.updateSize!false(); 227 228 // Do split 229 if(this.parent is null) 230 { 231 // New root 232 _Node* root = new _Node; 233 root.isLeave = false; 234 root.elements = 2; 235 root.nodes[0..2] = [&this, newNode]; 236 root.updateSize!false(); 237 238 this.parent = root; 239 newNode.parent = root; 240 } 241 else 242 { 243 // Use root 244 this.parent.insert(newNode); 245 } 246 return this.getRoot(); 247 } 248 } 249 else 250 { 251 // Select min node 252 _Node* best = this.nodes[0]; 253 TYPE.BASE_TYPE bestInsert = getInseredVolume(this.nodes[0].ownSize, cast(TYPE) toInsert); 254 foreach(_Node* node; this.nodes) 255 { 256 TYPE.BASE_TYPE tmp = getInseredVolume(this.nodes[0].ownSize, cast(TYPE) toInsert); 257 if(bestInsert > tmp) 258 { 259 bestInsert = tmp; 260 best = node; 261 } 262 } 263 264 // insert 265 return best.insert(toInsert); 266 } 267 } 268 private pure nothrow void insert(_Node* node) 269 { 270 if(this.elements < MAX) 271 { 272 // Normal insert 273 this.nodes[this.elements] = node; 274 this.elements++; 275 node.parent = &this; 276 this.updateSize!true(); 277 } 278 else 279 { 280 // Prepare split 281 node.parent = &this; 282 _Node*[][2] tmp = _split!(_Node*, MIN, MAX, TYPE)(this.nodes ~ [node], function TYPE(_Node* t) => t.ownSize); 283 284 this.elements = tmp[0].length; 285 this.nodes[0..tmp[0].length] = tmp[0]; 286 this.updateSize!false(); 287 288 _Node* newNode = new _Node; 289 newNode.isLeave = false; 290 newNode.elements = tmp[1].length; 291 newNode.nodes[0..tmp[1].length] = tmp[1]; 292 newNode.updateSize!false(); 293 foreach(_Node* i; tmp[1]) 294 { 295 i.parent = newNode; 296 } 297 298 // Split 299 if(this.parent == null) 300 { 301 // New root 302 _Node* root = new _Node; 303 root.elements = 2; 304 root.nodes[0..2] = [&this, newNode]; 305 root.isLeave = false; 306 root.updateSize!false(); 307 308 this.parent = root; 309 newNode.parent = root; 310 } 311 else 312 { 313 // Call parent 314 this.parent.insert(newNode); 315 } 316 } 317 } 318 @nogc 319 private pure nothrow void updateSize(bool RECUSIVE)() 320 { 321 _Node* tmp = &this; 322 while(tmp !is null) 323 { 324 TYPE res = tmp.isLeave ? cast(TYPE)(tmp.leaves[0]) : tmp.nodes[0].ownSize; 325 for(size_t i = 1; i < tmp.elements; i++) 326 { 327 res = res.maxGeometry(tmp.isLeave ? cast(TYPE)(tmp.leaves[i]) : tmp.nodes[i].ownSize); 328 } 329 tmp.ownSize = res; 330 331 static if(RECUSIVE) 332 { 333 tmp = tmp.parent; 334 } 335 else 336 { 337 return; 338 } 339 } 340 } 341 } 342 343 /** 344 * Query iterator to parse queries. 345 */ 346 public final class QueryIterator 347 { 348 private _Node* current; 349 private size_t index; 350 private TYPE range; 351 private bool useable; 352 353 pragma(inline, true) 354 package pure nothrow this(_Node* root, TYPE range) 355 { 356 this.current = root; 357 this.index = 0; 358 this.range = range; 359 this.useable = false; 360 } 361 362 private void toNext() 363 { 364 while(this.current !is null) 365 { 366 if(this.index < this.current.elements) 367 { 368 // Scan if useable 369 if(this.current.isLeave) 370 { 371 if((cast(TYPE) this.current.leaves[this.index]).contains(this.range)) 372 { 373 this.useable = true; 374 break; 375 } 376 else 377 { 378 this.index++; 379 } 380 } 381 else 382 { 383 if(this.current.nodes[this.index].ownSize.contains(this.range)) 384 { 385 this.current = this.current.nodes[this.index]; 386 this.index = 0; 387 } 388 else 389 { 390 this.index++; 391 } 392 } 393 } 394 else 395 { 396 // Move up one position 397 _Node* old = this.current; 398 this.current = old.parent; 399 this.index = 0; 400 if(this.current is null) 401 { 402 break; 403 } 404 while(true) 405 { 406 if(this.current.nodes[this.index] is old) 407 { 408 this.index++; 409 break; 410 } 411 else 412 { 413 this.index++; 414 if(this.index >= this.current.elements) 415 { 416 throw new RangeError("Data structure currupted."); 417 } 418 } 419 } 420 } 421 } 422 } 423 424 public void popFront() 425 { 426 if(!this.useable) 427 this.toNext(); 428 this.useable = false; 429 this.index++; 430 } 431 @property 432 pure nothrow bool empty() 433 { 434 if(!useable) 435 { 436 this.toNext(); 437 return !this.useable; 438 } 439 return false; 440 } 441 DATA_T front() 442 { 443 if(!useable) 444 { 445 this.toNext(); 446 } 447 if(useable) 448 { 449 return this.current.leaves[this.index]; 450 } 451 else 452 { 453 throw new RangeError("Out of range"); 454 } 455 } 456 457 int opApply(scope int delegate(DATA_T) func) 458 { 459 int counter = 0; 460 while(!this.empty()) 461 { 462 func(front()); 463 this.popFront(); 464 counter++; 465 } 466 return counter; 467 } 468 } 469 470 private _Node* _root; 471 472 /** 473 * Generates an RTree structure. 474 */ 475 public pure nothrow this() 476 { 477 this._root = new _Node; 478 this._root.isLeave = true; 479 this._root.elements = 0; 480 this._root.parent = null; 481 } 482 483 /** 484 * 485 */ 486 public nothrow pure QueryIterator query(TYPE range) 487 { 488 return new QueryIterator(this._root, range); 489 } 490 491 /** 492 * Insert data to the tree. 493 * @param data Source data to insert. 494 * @return was insert successfully. 495 */ 496 public pure nothrow bool insert(DATA_T data) 497 { 498 this._root = this._root.insert(data); 499 return true; 500 } 501 } 502 503 private unittest 504 { 505 RTree!(Cuboid!2, Cuboid!2, 11, 5) rtree; 506 }