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;
18 
19 private
20 {
21 	import std.meta;
22 	import std.traits;
23 	import structuresd.utils;
24 }
25 
26 public final struct Point(uint DIMS, TYPE = double)
27 {
28 	private static pure
29 	{
30 		enum bool goodSingualType(T) = __traits(compiles, {T a; TYPE b = a;});
31 		bool suiableTypes(T...)()
32 		{
33 			bool result = true;
34 			static foreach(I; T)
35 			{
36 				static if(!goodSingualType!I && !(__traits(isSame, TemplateOf!I, Point) && goodSingualType!(TemplateArgsOf!I[1])))
37 				{
38 					result = false;
39 				}
40 			}
41 			return result;
42 		}
43 		ulong countDims(T...)()
44 		{
45 			ulong result = 0;
46 			static foreach(I; T)
47 			{
48 				static if(goodSingualType!I)
49 				{
50 					result++;
51 				}
52 				else static if(__traits(isSame, TemplateOf!I, Point) && goodSingualType!(TemplateArgsOf!I[1]))
53 				{
54 					result += TemplateArgsOf!I[0];
55 				}
56 				else
57 				{
58 					static assert(false);
59 				}
60 			}
61 			return result;
62 		}
63 	}
64 
65 	public TYPE[DIMS] dims;
66 
67 	public this(T...)(T data) if(suiableTypes!T && countDims!T == DIMS)
68 	{
69 		ulong position = 0;
70 		static foreach(i; data)
71 		{
72 			static if(goodSingualType!(typeof(i)))
73 			{
74 				this.dims[position] = i;
75 				position++;
76 			}
77 			else static if(__traits(isSame, TemplateOf!(typeof(i)), Point) && goodSingualType!(TemplateArgsOf!(typeof(i))[1]))
78 			{
79 				static foreach(j; i.dims)
80 				{
81 					this.dims[position] = j;
82 					position++;
83 				}
84 			}
85 			else
86 			{
87 				static assert(false);
88 			}
89 		}
90 	}
91 	public this(TYPE[DIMS] dims)
92 	{
93 		this.dims = dims;
94 	}
95 
96 	public Point!(DIMS, TYPE) opBinary(string OP)(TYPE scalar) if(OP == "*" || OP == "/")
97 	{
98 		Point!(DIMS, TYPE) result;
99 		static foreach(uint i; 0..DIMS)
100 		{
101 			mixin("result.dims[i] = this.dims[i] " ~ OP ~ " scalar;");
102 		}
103 		return result;
104 	}
105 
106 	public Point!(DIMS, TYPE) opBinary(string OP)(Point p) if(OP == "+" || OP == "-")
107 	{
108 		Point!(DIMS, TYPE) result;
109 		static foreach(uint i; 0..DIMS)
110 		{
111 			mixin("result.dims[i] = this.dims[i] " ~ OP ~ " p.dims[i];");
112 		}
113 		return result;
114 	}
115 
116 	public ref Point!(DIMS, TYPE) opOpAssign(string OP)(Point!(DIMS, TYPE) p) if(OP == "+" || OP == "-")
117 	{
118 		static foreach(uint i; 0..DIMS)
119 		{
120 			mixin("this.dims[i] " ~ OP ~ "= p.dims[i];");
121 		}
122 		return this;
123 	}
124 }
125 
126 private struct _GeometryFuns(T1, T2)
127 {
128 	public nothrow pure T1 maxGeometry(T1) { T1 res; return res; }
129 	public nothrow pure T2 volume() { T2 res; return res; };
130 	public nothrow pure bool contains(T1) { return false; };
131 }
132 
133 public final struct Sphere(uint DIMS, TYPE = double)
134 {
135 	static assert(DIMS > 0);
136 
137 	public Point!(DIMS, TYPE) center;
138 	public TYPE radius;
139 }
140 
141 public final struct Cuboid(uint DIMS, TYPE = double)
142 {
143 	public alias BASE_TYPE = TYPE;
144 
145 	public Point!(DIMS, TYPE) a;
146 	public Point!(DIMS, TYPE) b;
147 
148 	public this(Point!(DIMS, TYPE) a, Point!(DIMS, TYPE) b)
149 	{
150 		static foreach(ulong i; 0..DIMS)
151 		{
152 			this.a.dims[i] = min(a.dims[i], b.dims[i]);
153 			this.b.dims[i] = max(a.dims[i], b.dims[i]);
154 		}
155 	}
156 
157 	public nothrow pure Cuboid maxGeometry(Cuboid c)
158 	{
159 		Cuboid res;
160 		static foreach(uint i; 0..DIMS)
161 		{
162 			res.a.dims[i] = min(this.a.dims[i], this.b.dims[i], c.a.dims[i], c.b.dims[i]);
163 			res.b.dims[i] = max(this.a.dims[i], this.b.dims[i], c.a.dims[i], c.b.dims[i]);
164 		}
165 		return res;
166 	}
167 	public nothrow pure TYPE volume()
168 	{
169 		TYPE res;
170 		for(uint i = 0; i < DIMS; i++)
171 		{
172 			res *= this.b.dims[i] - this.b.dims[i];
173 		}
174 		return res < 0 ? -res : res;
175 	}
176 	public nothrow pure bool containsPoint(Point!(DIMS, TYPE) p)
177 	{
178 		static foreach(ulong i; 0..DIMS)
179 		{
180 			if((min(this.a.dims[i], this.b.dims[i]) > p.dims[i]) | (p.dims[i] > max(this.a.dims[i], this.b.dims[i])))
181 			{
182 				return false;
183 			}
184 		}
185 		return true;
186 	}
187 	public nothrow pure bool contains(Cuboid c)
188 	{
189 		return this.allDotsCheck(c) && c.allDotsCheck(this);
190 	}
191 
192 	private pure nothrow bool allDotsCheck(Cuboid c)
193 	{
194 		foreach(ulong i; 0..(1 << DIMS))
195 		{
196 			TYPE[DIMS] tmp;
197 			static foreach(ulong j; 0..DIMS)
198 			{
199 				tmp[j] = ((1 << j) & i) == 0 ? this.a.dims[j] : this.b.dims[j];
200 			}
201 			if(this.containsPoint(Point!(DIMS, TYPE)(tmp)))
202 			{
203 				return true;
204 			}
205 		}
206 		return false;
207 	}
208 }
209 
210 public enum bool isGeometry(T) = __traits(hasMember, T, "BASE_TYPE") &&
211                                  isSameContainerFunction!(_GeometryFuns!(T, T.BASE_TYPE), T, "maxGeometry") &&
212                                  isSameContainerFunction!(_GeometryFuns!(T, T.BASE_TYPE), T, "volume") &&
213                                  isSameContainerFunction!(_GeometryFuns!(T, T.BASE_TYPE), T, "contains");
214 
215 
216 public TYPE.BASE_TYPE getInseredVolume(TYPE)(TYPE old, TYPE insert) if(isGeometry!(TYPE))
217 {
218 	return old.maxGeometry(insert).volume() - old.volume();
219 }
220 
221 private unittest
222 {
223 	static assert(isSameContainerFunction!(_GeometryFuns!(Cuboid!2, Cuboid!(2).BASE_TYPE), Cuboid!2, "maxGeometry"));
224 	static assert(isSameContainerFunction!(_GeometryFuns!(Cuboid!2, Cuboid!(2).BASE_TYPE), Cuboid!2, "volume"));
225 	static assert(isSameContainerFunction!(_GeometryFuns!(Cuboid!2, Cuboid!(2).BASE_TYPE), Cuboid!2, "contains"));
226 	static assert(isGeometry!(Cuboid!2));
227 }