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