1 module grape.geometry;
2 
3 import grape.math;
4 import std.stdio;
5 import std.algorithm;
6 import std.array;
7 
8 struct CoordinateSystem {
9   public:
10     CoordinateSystem opBinary(string op)(Vec3 vec3) if (op == "+") {
11       CoordinateSystem result;
12       result.set_position( _list[0].vec3 + vec3,
13                            _list[1].vec3 + vec3,
14                            _list[2].vec3 + vec3 );
15       return result;
16     }
17 
18     CoordinateSystem opBinaryRight(string op)(Vec3 vec3) if (op == "+") {
19       return opBinary!op(vec3);
20     }
21 
22     ref CoordinateSystem opOpAssign(string op)(Vec3 vec3) if (op == "+") {
23       _list[0].set(_list[0].vec3 + vec3);
24       _list[1].set(_list[1].vec3 + vec3);
25       _list[2].set(_list[2].vec3 + vec3);
26       return this;
27     }
28 
29     void set_position(Vec3 x, Vec3 y, Vec3 z) {
30       _list = [ Quat(x), Quat(y), Quat(z) ];
31     }
32 
33     void set_position(Quat x, Quat y, Quat z) {
34       _list = [ x, y, z ];
35     }
36 
37     void rotate(Quat rotQuat) {
38       _list = map!(pos => rotQuat.conjugate * pos * rotQuat)(_list).array;
39     }
40 
41     @property {
42       Vec3 x() {
43         return _list[0].vec3;
44       }
45 
46       Vec3 y() {
47         return _list[1].vec3;
48       }
49 
50       Vec3 z() {
51         return _list[2].vec3;
52       }
53     }
54 
55   private:
56     Quat[] _list = [ Quat(Vec3(1, 0, 0)),
57                      Quat(Vec3(0, 1, 0)),
58                      Quat(Vec3(0, 0, 1)) ];
59 }
60 
61 // TODO atomic
62 class Geometry {
63   public:
64     void set_position(Vec3 vec3) {
65       auto distance = vec3 - _origin.vec3;
66 
67       _origin.set(_origin.vec3 + distance);
68       _vertices = map!(x => x + distance)(_vertices).array; 
69     }
70 
71     void forward(in float distance) {
72       auto distanceVec3 = _localCS.x * distance;
73       move_impl(distanceVec3);
74     }
75 
76     void back(in float distance) {
77       auto distanceVec3 = _localCS.x * (-distance);
78       move_impl(distanceVec3);
79     }
80 
81     void up(in float distance) {
82       auto distanceVec3 = _localCS.y * distance;
83       move_impl(distanceVec3);
84     }
85 
86     void down(in float distance) {
87       auto distanceVec3 = _localCS.y * (-distance);
88       move_impl(distanceVec3);
89     }
90 
91     void right(in float distance) {
92       auto distanceVec3 = _localCS.z * distance;
93       move_impl(distanceVec3);
94     }
95 
96     void left(in float distance) {
97       auto distanceVec3 = _localCS.z * (-distance);
98       move_impl(distanceVec3);
99     }
100 
101     void pitch(in float rad) {
102       rotate_impl(_localCS.x, rad, _origin.vec3);
103     }
104 
105     void yaw(in float rad) {
106       rotate_impl(_localCS.y, rad, _origin.vec3);
107     }
108 
109     void roll(in float rad) {
110       rotate_impl(_localCS.z, rad, _origin.vec3);
111     }
112 
113     // 任意の直線の方向に移動
114     void translate(Vec3 axis, in float distance) {
115       auto distanceVec3 = axis * distance;
116       move_impl(distanceVec3);
117     }
118 
119     // 任意の点(pos)を通る直線(axis)を中心軸に回転
120     void rotate(in Vec3 axis, in float rad, in Vec3 pos=Vec3(0, 0, 0)) {
121       rotate_impl(axis, rad, pos);
122     }
123 
124     void scale(in float ratio) {
125       foreach (ref vertex; _vertices) {
126         vertex = vertex * ratio; 
127       } 
128     }
129 
130     @property {
131       Quat origin() {
132         return _origin;
133       }
134 
135       Vec3[] vertices() {
136         return _vertices;
137       }
138 
139       int[] indices() {
140         return _indices;
141       }
142 
143       Vec3[] normals() {
144         return _normals;
145       }
146     }
147 
148   protected:
149     void move_impl(in Vec3 distanceVec3) {
150       foreach (ref vertex; _vertices) {
151         vertex += distanceVec3;
152       }
153       _origin.set(_origin.vec3 + distanceVec3);
154     }
155 
156     void rotate_impl(in Vec3 axis, in float rad, in Vec3 pos) {
157       void delegate() impl = {
158         auto rotQuat = Quat(axis, rad);
159 
160         // _originの回転
161         if (pos != _origin.vec3) _origin = rotQuat.conjugate * _origin * rotQuat;
162 
163         // _localCSの回転 
164         _localCS.rotate(rotQuat);
165 
166         // _verticesの回転
167         auto tmp = map!(vec3 => Quat(vec3))(_vertices);
168         _vertices = map!(pos => (rotQuat.conjugate * pos * rotQuat).vec3)(tmp).array;
169 
170         // _normalsの回転
171         auto tmp2 = map!(vec3 => Quat(vec3))(_normals);
172         _normals = map!(pos => (rotQuat.conjugate * pos * rotQuat).vec3)(tmp2).array;
173       };
174 
175       _vertices = map!(x => x - pos)(_vertices).array;
176       impl();
177       _vertices = map!(x => x + pos)(_vertices).array;
178     }
179 
180     CoordinateSystem _localCS;
181     Quat _origin = Quat(Vec3(0, 0, 0));
182     Vec3[] _vertices;
183     int[] _indices;
184     Vec3[] _normals;
185 }
186 
187 class BoxGeometry : Geometry {
188   public:
189     this(in float width, in float height, in float depth) {
190       auto x = width / 2;
191       auto y = height / 2;
192       auto z = depth / 2;
193 
194       _vertices = [ Vec3(x, -y, -z),
195                     Vec3(x, -y, z),
196                     Vec3(-x, -y, z),
197                     Vec3(-x, -y, -z),
198                     Vec3(x, y, -z),
199                     Vec3(x, y, z),
200                     Vec3(-x, y, z),
201                     Vec3(-x, y, -z) ];
202       _indices = [ 0, 1, 2, 0, 2, 3,
203                    0, 1, 4, 1, 4, 5,
204                    1, 2, 5, 2, 5, 6,
205                    0, 3, 4, 3, 4, 7,
206                    4, 5, 6, 4, 6, 7,
207                    2, 3, 7, 2, 7, 6 ];
208       _normals = [ Vec3(1.0, -1.0, -1.0),
209                    Vec3(1.0, -1.0, 1.0),
210                    Vec3(-1.0, -1.0, 1.0),
211                    Vec3(-1.0, -1.0, -1.0),
212                    Vec3(1.0, 1.0, -1.0),
213                    Vec3(1.0, 1.0, 1.0),
214                    Vec3(-1.0, 1.0, 1.0),
215                    Vec3(-1.0, 1.0, -1.0) ];
216     }
217 
218     this(in float width, in float height, in float depth, in int widthSegments=1, in int heightSegments=1, in int depthSegments=1) {
219       this(width, height, depth);
220     }
221 }
222 
223 class PlaneGeometry : Geometry {
224   public:
225     this(in float width, in float height, in int widthSegments=1, in int heightSegments=1) {
226       auto x = width / 2;
227       auto y = height / 2;
228 
229       _vertices = [ Vec3(-x, y, 0),
230                     Vec3(x, y, 0),
231                     Vec3(x, -y, 0),
232                     Vec3(-x, -y, 0) ];
233       _indices = [ 0, 1, 2, 0, 2, 3 ];
234     }
235 }
236 
237 class CustomGeometry : Geometry {
238   public:
239     this(Vec3[] vertices=[], int[] indices=[], Vec3[] normals=[]) {
240       _vertices = vertices;
241       _indices = indices;
242       _normals = normals;
243     }
244 }
245 
246 unittest {
247   import std.range : zip;
248 
249   bool nearly_equal(Vec3 a, Vec3 b) {
250     foreach (v; zip(a.coord, b.coord))
251       if (v[0] - v[1] > 0.001) return false;
252     return true;
253   }
254 
255   auto geometry = new BoxGeometry(1, 1, 1);
256   assert(geometry.origin.vec3 == Vec3(0, 0, 0));
257   assert(geometry.vertices == [ Vec3([0.5, -0.5, -0.5]), Vec3([0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, -0.5]), Vec3([0.5, 0.5, -0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, -0.5])]);
258 
259   geometry.set_position(Vec3(1, 0, 0));
260   assert(geometry.origin.vec3 == Vec3(1, 0, 0));
261   assert(geometry.vertices == [ Vec3([1.5, -0.5, -0.5]), Vec3([1.5, -0.5, 0.5]), Vec3([0.5, -0.5, 0.5]), Vec3([0.5, -0.5, -0.5]), Vec3([1.5, 0.5, -0.5]), Vec3([1.5, 0.5, 0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([0.5, 0.5, -0.5])]);
262 
263   geometry.set_position(Vec3(0, 0, 0));
264   geometry.forward(1.0);
265   assert(geometry.origin.vec3 == Vec3(1, 0, 0));
266   assert(geometry.vertices == [ Vec3([1.5, -0.5, -0.5]), Vec3([1.5, -0.5, 0.5]), Vec3([0.5, -0.5, 0.5]), Vec3([0.5, -0.5, -0.5]), Vec3([1.5, 0.5, -0.5]), Vec3([1.5, 0.5, 0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([0.5, 0.5, -0.5])]);
267   geometry.back(1.0);
268   assert(geometry.origin.vec3 == Vec3(0, 0, 0));
269   assert(geometry.vertices == [ Vec3([0.5, -0.5, -0.5]), Vec3([0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, -0.5]), Vec3([0.5, 0.5, -0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, -0.5])]);
270   geometry.right(1.0);
271   assert(geometry.origin.vec3 == Vec3(0, 0, 1));
272   assert(geometry.vertices == [ Vec3([0.5, -0.5, 0.5]), Vec3([0.5, -0.5, 1.5]), Vec3([-0.5, -0.5, 1.5]), Vec3([-0.5, -0.5, 0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([0.5, 0.5, 1.5]), Vec3([-0.5, 0.5, 1.5]), Vec3([-0.5, 0.5, 0.5])]);
273   geometry.left(1.0);
274   assert(geometry.origin.vec3 == Vec3(0, 0, 0));
275   assert(geometry.vertices == [ Vec3([0.5, -0.5, -0.5]), Vec3([0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, -0.5]), Vec3([0.5, 0.5, -0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, -0.5])]);
276   geometry.up(1.0);
277   assert(geometry.origin.vec3 == Vec3(0, 1, 0));
278   assert(geometry.vertices == [ Vec3([0.5, 0.5, -0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, -0.5]), Vec3([0.5, 1.5, -0.5]), Vec3([0.5, 1.5, 0.5]), Vec3([-0.5, 1.5, 0.5]), Vec3([-0.5, 1.5, -0.5])]);
279   geometry.down(1.0);
280   assert(geometry.origin.vec3 == Vec3(0, 0, 0));
281   assert(geometry.vertices == [ Vec3([0.5, -0.5, -0.5]), Vec3([0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, 0.5]), Vec3([-0.5, -0.5, -0.5]), Vec3([0.5, 0.5, -0.5]), Vec3([0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, 0.5]), Vec3([-0.5, 0.5, -0.5])]);
282 
283 
284   /*
285   geometry.rotate(Vec3(0, 1, 0), PI);
286   assert(nearly_equal(geometry.origin.vec3, Vec3(-1, 0, 0)));
287 
288   geometry.pitch(PI_2);
289   geometry.yaw(PI_2);
290   geometry.roll(PI_2);
291   geometry.rotate(Vec3(0, 1, 0), PI, Vec3(1, 1, 1));
292   */
293 }
294