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