1 /**
2 Plane in hessian form.
3 
4 Copyright:
5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur.
6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md)
7 Copyright (c) 2017 Godot-D contributors
8 Copyright (c) 2022 Godot-DLang contributors
9 
10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License)
11 
12 
13 */
14 module godot.plane;
15 
16 import godot.api.types;
17 import godot.vector3;
18 
19 import std.math;
20 
21 public import godot.globalenums : ClockDirection;
22 
23 /**
24 Plane represents a normalized plane equation. Basically, “normal” is the normal of the plane (a,b,c normalized), and “d” is the distance from the origin to the plane (in the direction of “normal”). “Over” or “Above” the plane is considered the side of the plane towards where the normal is pointing.
25 */
26 struct Plane {
27 @nogc nothrow:
28 
29     Vector3 normal = Vector3(0, 0, 0);
30     real_t d = 0;
31 
32     Vector3 center() const {
33         return normal * d;
34     }
35 
36     Plane opUnary(string op : "-")() const {
37         return Plane(-normal, -d);
38     }
39 
40     Vector3 project(in Vector3 p_point) const {
41         return p_point - normal * distanceTo(p_point);
42     }
43 
44     void normalize() {
45         real_t l = normal.length();
46         if (l == 0) {
47             this = Plane(Vector3(0, 0, 0), 0);
48             return;
49         }
50         normal /= l;
51         d /= l;
52     }
53 
54     Plane normalized() const {
55         Plane p = this;
56         p.normalize();
57         return p;
58     }
59 
60     Vector3 getAnyPoint() const {
61         return normal * d;
62     }
63 
64     Vector3 getAnyPerpendicularNormal() const {
65         enum Vector3 p1 = Vector3(1, 0, 0);
66         enum Vector3 p2 = Vector3(0, 1, 0);
67         Vector3 p;
68 
69         if (fabs(normal.dot(p1)) > 0.99) // if too similar to p1
70             p = p2; // use p2
71         else
72             p = p1; // use p1
73 
74         p -= normal * normal.dot(p);
75         p.normalize();
76 
77         return p;
78     }
79 
80     /* intersections */
81 
82     bool intersect3(in Plane p_plane1, in Plane p_plane2, Vector3* r_result = null) const {
83         const Plane p_plane0 = this;
84         Vector3 normal0 = p_plane0.normal;
85         Vector3 normal1 = p_plane1.normal;
86         Vector3 normal2 = p_plane2.normal;
87 
88         real_t denom = normal0.cross(normal1).dot(normal2);
89 
90         if (fabs(denom) <= CMP_EPSILON)
91             return false;
92 
93         if (r_result) {
94             *r_result = ((normal1.cross(normal2) * p_plane0.d) +
95                     (
96                         normal2.cross(normal0) * p_plane1.d) +
97                     (normal0.cross(normal1) * p_plane2.d)) / denom;
98         }
99         return true;
100     }
101 
102     bool intersectsRay(in Vector3 p_from, in Vector3 p_dir, Vector3* p_intersection = null) const {
103         Vector3 segment = p_dir;
104         real_t den = normal.dot(segment);
105 
106         //printf("den is %i\n",den);
107         if (fabs(den) <= CMP_EPSILON) {
108             return false;
109         }
110 
111         real_t dist = (normal.dot(p_from) - d) / den;
112         //printf("dist is %i\n",dist);
113 
114         if (dist > CMP_EPSILON) { //this is a ray, before the emiting pos (p_from) doesnt exist
115             return false;
116         }
117 
118         dist = -dist;
119         if (p_intersection)
120             *p_intersection = p_from + segment * dist;
121 
122         return true;
123     }
124 
125     bool intersectsSegment(in Vector3 p_begin, in Vector3 p_end, Vector3* p_intersection) const {
126         Vector3 segment = p_begin - p_end;
127         real_t den = normal.dot(segment);
128 
129         //printf("den is %i\n",den);
130         if (fabs(den) <= CMP_EPSILON)
131             return false;
132 
133         real_t dist = (normal.dot(p_begin) - d) / den;
134         //printf("dist is %i\n",dist);
135 
136         if (dist < -CMP_EPSILON || dist > (1.0 + CMP_EPSILON))
137             return false;
138 
139         dist = -dist;
140         if (p_intersection)
141             *p_intersection = p_begin + segment * dist;
142 
143         return true;
144     }
145 
146     /* misc */
147 
148     bool isAlmostLike(in Plane p_plane) const {
149         return (normal.dot(p_plane.normal) > _PLANE_EQ_DOT_EPSILON && fabs(d - p_plane.d) < _PLANE_EQ_D_EPSILON);
150     }
151 
152     /+String opCast(T : String)() const
153 	{
154 		// return normal.operator String() + ", " + rtos(d);
155 		return String(); // @Todo
156 	}+/
157 
158     bool isPointOver(in Vector3 p_point) const {
159         return (normal.dot(p_point) > d);
160     }
161 
162     real_t distanceTo(in Vector3 p_point) const {
163         return (normal.dot(p_point) - d);
164     }
165 
166     bool hasPoint(in Vector3 p_point, real_t _epsilon = CMP_EPSILON) const {
167         real_t dist = normal.dot(p_point) - d;
168         dist = fabs(dist);
169         return (dist <= _epsilon);
170 
171     }
172 
173     this(in Vector3 p_normal, real_t p_d) {
174         normal = p_normal;
175         d = p_d;
176     }
177 
178     this(real_t p_a, real_t p_b, real_t p_c, real_t p_d) {
179         normal = Vector3(p_a, p_b, p_c);
180         d = p_d;
181     }
182 
183     this(in Vector3 p_point, in Vector3 p_normal) {
184         normal = p_normal;
185         d = p_normal.dot(p_point);
186     }
187 
188     this(in Vector3 p_point1, in Vector3 p_point2, in Vector3 p_point3, ClockDirection p_dir = ClockDirection
189             .counterclockwise) {
190         if (p_dir == ClockDirection.clockwise)
191             normal = (p_point1 - p_point3).cross(p_point1 - p_point2);
192         else
193             normal = (p_point1 - p_point2).cross(p_point1 - p_point3);
194         normal.normalize();
195         d = normal.dot(p_point1);
196     }
197 }