1 ///
2 module isodi.camera;
3 
4 import std.math;
5 import std.typecons;
6 
7 import isodi.object3d;
8 
9 private immutable rad = PI / 180;
10 
11 
12 @safe:
13 
14 
15 /// Represents a camera, giving a view into the Isodi world.
16 struct Camera {
17 
18     alias Offset = Tuple!(
19         float, "x",
20         float, "y",
21         float, "height",
22     );
23 
24     /// Represents the angle the camera is looking at.
25     struct Angle {
26 
27         private float _x = 45;
28         invariant(_x >= 0, "Somehow camera angle X is negative, this shouldn't be possible.");
29         invariant(_x < 360, "Somehow camera angle X is >= 360, this shouldn't be possible.");
30         invariant(!_x.isNaN, "Camera angle X is NaN, did you forget to initialize a float?");
31 
32         /// Y, vertical angle of the camera.
33         ///
34         /// Must be between 0° (side view) and 90° (top-down), defaults to 45°
35         float y = 45;
36         invariant(y >= 0, "Camera angle Y must be at least 0°.");
37         invariant(y <= 90, "Camera angle Y must be at most 90°.");
38         invariant(!y.isNaN, "Camera angle Y is NaN, did you forget to initialize a float?");
39 
40         /// X, horizontal angle of the camera.
41         ///
42         /// Automatically modulated by 360°.
43         @property
44         float x() const { return _x; }
45 
46         /// Ditto
47         @property
48         float x(float value) {
49 
50             // Modulate
51             value %= 360;
52 
53             // Prevent being negative
54             if (value < 0) value += 360;
55 
56             return _x = value;
57 
58         }
59 
60     }
61 
62     /// Object this camera follows.
63     ///
64     /// This can be null. If so, follows position (0, 0, 0).
65     Object3D follow;
66 
67     /// Angle the camera is looking from.
68     Angle angle;
69 
70     /// Distance between the camera and the followed object.
71     ///
72     /// Uses cells as the unit. `display.cellSize * distance` will be the cell distance in the renderer.
73     float distance = 15;
74 
75     /// Offset between camera and the followed object.
76     auto offset = Offset(0, 0, 1);
77 
78     /// Change the offset relative to the screen
79     void offsetScreenX(float value) {
80 
81         const alpha = (angle.x+90) * rad;
82         offset.x += value * alpha.sin;
83         offset.y += value * alpha.cos;
84 
85     }
86 
87     /// Change the offset relative to the screen
88     void offsetScreenY(float value) {
89 
90         const alpha = angle.x * rad;
91         offset.x += value * alpha.sin;
92         offset.y += value * alpha.cos;
93 
94     }
95 
96 }