1 module gl3n.plane;
2 
3 private {
4     import gl3n.linalg : Vector, dot, vec3;
5     import gl3n.math : almost_equal;
6 
7     import std.traits : isFloatingPoint;
8 }
9 
10 
11 /// Base template for all plane-types.
12 /// Params:
13 /// type = all values get stored as this type (must be floating point)
14 struct PlaneT(type = float) if(isFloatingPoint!type) {
15     alias type pt; /// Holds the internal type of the plane.
16     alias Vector!(pt, 3) vec3; /// Convenience alias to the corresponding vector type.
17 
18     union {
19         struct {
20             pt a; /// normal.x
21             pt b; /// normal.y
22             pt c; /// normal.z
23         }
24 
25         vec3 normal; /// Holds the planes normal.
26     }
27 
28     pt d; /// Holds the planes "constant" (HNF).
29 
30     @safe pure nothrow:
31 
32     /// Constructs the plane, from either four scalars of type $(I pt)
33     /// or from a 3-dimensional vector (= normal) and a scalar.
34     this(pt a, pt b, pt c, pt d) {
35         this.a = a;
36         this.b = b;
37         this.c = c;
38         this.d = d;
39     }
40 
41     /// ditto
42     this(vec3 normal, pt d) {
43         this.normal = normal;
44         this.d = d;
45     }
46 
47     unittest {
48         Plane p = Plane(0.0f, 1.0f, 2.0f, 3.0f);
49         assert(p.normal == vec3(0.0f, 1.0f, 2.0f));
50         assert(p.d == 3.0f);
51 
52         p.normal.x = 4.0f;
53         assert(p.normal == vec3(4.0f, 1.0f, 2.0f));
54         assert(p.a == 4.0f);
55         assert(p.b == 1.0f);
56         assert(p.c == 2.0f);
57         assert(p.d == 3.0f);
58     }
59 
60     /// Normalizes the plane inplace.
61     void normalize() {
62         pt det = 1.0 / normal.length;
63         normal *= det;
64         d *= det;
65     }
66 
67     /// Returns a normalized copy of the plane.
68     @property PlaneT normalized() const {
69         PlaneT ret = PlaneT(a, b, c, d);
70         ret.normalize();
71         return ret;
72     }
73 
74     unittest {
75         Plane p = Plane(0.0f, 1.0f, 2.0f, 3.0f);
76         Plane pn = p.normalized();
77         assert(pn.normal == vec3(0.0f, 1.0f, 2.0f).normalized);
78         assert(almost_equal(pn.d, 3.0f/vec3(0.0f, 1.0f, 2.0f).length));
79         p.normalize();
80         assert(p == pn);
81     }
82 
83     /// Returns the distance from a point to the plane.
84     /// Note: the plane $(RED must) be normalized, the result can be negative.
85     pt distance(vec3 point) const {
86         return dot(point, normal) + d;
87     }
88 
89     /// Returns the distance from a point to the plane.
90     /// Note: the plane does not have to be normalized, the result can be negative.
91     pt ndistance(vec3 point) const {
92         return (dot(point, normal) + d) / normal.length;
93     }
94 
95     unittest {
96         Plane p = Plane(-1.0f, 4.0f, 19.0f, -10.0f);
97         assert(almost_equal(p.ndistance(vec3(5.0f, -2.0f, 0.0f)), -1.182992));
98         assert(almost_equal(p.ndistance(vec3(5.0f, -2.0f, 0.0f)),
99                             p.normalized.distance(vec3(5.0f, -2.0f, 0.0f))));
100     }
101 
102     bool opEquals(PlaneT other) const {
103         return other.normal == normal && other.d == d;
104     }
105 
106 }
107 
108 alias PlaneT!(float) Plane;