1 /** 2 Copyright: Copyright (c) 2015-2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.storage.volume; 7 8 import std.experimental.logger; 9 import std.math : floor; 10 import std.range : chain, only; 11 import dlib.math.vector; 12 13 import voxelman.storage.coordinates; 14 15 Volume calcVolume(ChunkWorldPos cwp, int viewRadius) 16 { 17 int size = viewRadius*2 + 1; 18 return Volume(cast(ivec3)(cwp.vector - viewRadius), 19 ivec3(size, size, size)); 20 } 21 22 // 3d grid volume 23 struct Volume 24 { 25 ivec3 position; 26 ivec3 size; 27 28 int volume() 29 { 30 return size.x * size.y * size.z; 31 } 32 33 bool empty() @property 34 { 35 return size.x == 0 && size.y == 0 && size.z == 0; 36 } 37 38 bool contains(ivec3 otherPosition) 39 { 40 if (otherPosition.x < position.x || otherPosition.x >= position.x + size.x) return false; 41 if (otherPosition.y < position.y || otherPosition.y >= position.y + size.y) return false; 42 if (otherPosition.z < position.z || otherPosition.z >= position.z + size.z) return false; 43 return true; 44 } 45 46 bool opEquals()(auto ref const Volume other) const 47 { 48 return position == other.position && size == other.size; 49 } 50 51 import std.algorithm : cartesianProduct, map, joiner, equal, canFind; 52 import std.range : iota, walkLength; 53 import std.array : array; 54 55 // generates all positions within volume. 56 auto positions() @property 57 { 58 return cartesianProduct( 59 iota(position.x, position.x + size.x), 60 iota(position.y, position.y + size.y), 61 iota(position.z, position.z + size.z)) 62 .map!((a)=>ivec3(a[0], a[1], a[2])); 63 } 64 65 unittest 66 { 67 assert(Volume(ivec3(0,0,0), ivec3(3,3,3)).positions.walkLength == 27); 68 } 69 } 70 71 struct TrisectResult 72 { 73 Volume[] aVolumes; 74 Volume intersection; 75 Volume[] bVolumes; 76 import std.algorithm : map, joiner; 77 auto aPositions() @property 78 { 79 return aVolumes.map!(a => a.positions).joiner; 80 } 81 auto bPositions() @property 82 { 83 return bVolumes.map!(a => a.positions).joiner; 84 } 85 } 86 87 // Finds intersection between two boxes 88 // [0] a - b == a if no intersection 89 // [1] a intersects b 90 // [2] b - a == b if no intersection 91 TrisectResult trisect(Volume a, Volume b) 92 { 93 // no intersection 94 if (rangeIntersection(a, b).empty) 95 { 96 return TrisectResult([a], Volume(), [b]); 97 } 98 99 auto xTrisect = trisectAxis(a.position.x, a.position.x + a.size.x, b.position.x, b.position.x + b.size.x); 100 auto yTrisect = trisectAxis(a.position.y, a.position.y + a.size.y, b.position.y, b.position.y + b.size.y); 101 auto zTrisect = trisectAxis(a.position.z, a.position.z + a.size.z, b.position.z, b.position.z + b.size.z); 102 103 TrisectResult result; 104 105 foreach(xa; xTrisect.aranges[0..xTrisect.numRangesA].chain(only(xTrisect.irange))) 106 foreach(ya; yTrisect.aranges[0..yTrisect.numRangesA].chain(only(yTrisect.irange))) 107 foreach(za; zTrisect.aranges[0..zTrisect.numRangesA].chain(only(zTrisect.irange))) 108 { 109 if (!(xa.isIntersection && ya.isIntersection && za.isIntersection)) 110 result.aVolumes ~= Volume(ivec3(xa.start, ya.start, za.start), 111 ivec3(xa.length, ya.length, za.length)); 112 } 113 114 foreach(xb; xTrisect.branges[0..xTrisect.numRangesB].chain(only(xTrisect.irange))) 115 foreach(yb; yTrisect.branges[0..yTrisect.numRangesB].chain(only(yTrisect.irange))) 116 foreach(zb; zTrisect.branges[0..zTrisect.numRangesB].chain(only(zTrisect.irange))) 117 { 118 if (!(xb.isIntersection && yb.isIntersection && zb.isIntersection)) 119 result.bVolumes ~= Volume(ivec3(xb.start, yb.start, zb.start), 120 ivec3(xb.length, yb.length, zb.length)); 121 } 122 123 result.intersection = Volume( 124 ivec3(xTrisect.irange.start, yTrisect.irange.start, zTrisect.irange.start), 125 ivec3(xTrisect.irange.length, yTrisect.irange.length, zTrisect.irange.length)); 126 127 return result; 128 } 129 130 struct AxisRange 131 { 132 int start; 133 int end; 134 bool isIntersection; 135 int length() @property 136 out (result) 137 { 138 assert(result >= 0); 139 } 140 body 141 { 142 return end - start; 143 } 144 } 145 146 struct TrisectAxisResult 147 { 148 AxisRange[2] aranges; 149 ubyte numRangesA; 150 AxisRange irange; 151 AxisRange[2] branges; 152 ubyte numRangesB; 153 } 154 155 // a aStart *----* aEnd 156 // b bStart *----* bEnd 157 // does not handle situation when there is no intersection 158 TrisectAxisResult trisectAxis(int aStart, int aEnd, int bStart, int bEnd) 159 { 160 TrisectAxisResult res; 161 162 if (aStart < bStart) 163 { 164 res.aranges[res.numRangesA++] = AxisRange(aStart, bStart); 165 // bOnlyStart1 = 0 166 // bOnlyEnd1 = 0 167 res.irange.start = bStart; 168 } 169 else if (aStart > bStart) 170 { 171 // aOnlyStart1 = 0 172 // aOnlyEnd1 = 0 173 res.branges[res.numRangesB++] = AxisRange(bStart, aStart); 174 res.irange.start = aStart; 175 } 176 else 177 { 178 // aOnlyStart1 = 0 179 // aOnlyEnd1 = 0 180 // bOnlyStart1 = 0 181 // bOnlyEnd1 = 0 182 res.irange.start = aStart; 183 } 184 185 if (aEnd < bEnd) 186 { 187 res.irange.end = aEnd; 188 // aOnlyStart2 = 0 189 // aOnlyEnd2 = 0 190 res.branges[res.numRangesB++] = AxisRange(aEnd, bEnd); 191 } 192 else if (aEnd > bEnd) 193 { 194 res.irange.end = bEnd; 195 res.aranges[res.numRangesA++] = AxisRange(bEnd, aEnd); 196 // bOnlyStart2 = 0 197 // bOnlyEnd2 = 0 198 } 199 else 200 { 201 res.irange.end = bEnd; 202 // aOnlyStart2 = 0 203 // aOnlyEnd2 = 0 204 // bOnlyStart2 = 0 205 // bOnlyEnd2 = 0 206 } 207 208 res.irange.isIntersection = true; 209 210 return res; 211 } 212 213 Volume rangeIntersection(Volume a, Volume b) 214 { 215 Volume result; 216 if (a.position.x < b.position.x) 217 { 218 if (a.position.x + a.size.x < b.position.x) return Volume(); 219 result.position.x = b.position.x; 220 result.size.x = a.size.x - (b.position.x - a.position.x); 221 } 222 else 223 { 224 if (b.position.x + b.size.x < a.position.x) return Volume(); 225 result.position.x = a.position.x; 226 result.size.x = b.size.x - (a.position.x - b.position.x); 227 } 228 229 if (a.position.y < b.position.y) 230 { 231 if (a.position.y + a.size.y < b.position.y) return Volume(); 232 result.position.y = b.position.y; 233 result.size.y = a.size.y - (b.position.y - a.position.y); 234 } 235 else 236 { 237 if (b.position.y + b.size.y < a.position.y) return Volume(); 238 result.position.y = a.position.y; 239 result.size.y = b.size.y - (a.position.y - b.position.y); 240 } 241 242 if (a.position.z < b.position.z) 243 { 244 if (a.position.z + a.size.z < b.position.z) return Volume(); 245 result.position.z = b.position.z; 246 result.size.z = a.size.z - (b.position.z - a.position.z); 247 } 248 else 249 { 250 if (b.position.z + b.size.z < a.position.z) return Volume(); 251 result.position.z = a.position.z; 252 result.size.z = b.size.z - (a.position.z - b.position.z); 253 } 254 255 result.size.x = result.size.x > 0 ? result.size.x : -result.size.x; 256 result.size.y = result.size.y > 0 ? result.size.y : -result.size.y; 257 result.size.z = result.size.z > 0 ? result.size.z : -result.size.z; 258 259 return result; 260 } 261 262 unittest 263 { 264 assert(rangeIntersection( 265 Volume(ivec3(0,0,0), ivec3(2,2,2)), 266 Volume(ivec3(1,1,1), ivec3(2,2,2))) == 267 Volume(ivec3(1,1,1), ivec3(1,1,1))); 268 assert(rangeIntersection( 269 Volume(ivec3(0,0,0), ivec3(2,2,2)), 270 Volume(ivec3(3,3,3), ivec3(4,4,4))) == 271 Volume(ivec3())); 272 assert(rangeIntersection( 273 Volume(ivec3(1,1,1), ivec3(2,2,2)), 274 Volume(ivec3(0,0,0), ivec3(2,2,2))) == 275 Volume(ivec3(1,1,1), ivec3(1,1,1))); 276 assert(rangeIntersection( 277 Volume(ivec3(1,1,1), ivec3(1,1,1)), 278 Volume(ivec3(1,1,1), ivec3(1,1,1))) == 279 Volume(ivec3(1,1,1), ivec3(1,1,1))); 280 assert(rangeIntersection( 281 Volume(ivec3(0,0,0), ivec3(2,2,2)), 282 Volume(ivec3(0,0,-1), ivec3(2,2,2))) == 283 Volume(ivec3(0,0,0), ivec3(2,2,1))); 284 }