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 }