geometry: implement `Point::dist` for 3D points
[nit.git] / lib / geometry / points_and_lines.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Interfaces and classes to represent basic geometry needs.
18 module points_and_lines is serialize
19
20 import serialization
21
22 # Abstract 2d point, strongly linked to its implementation `Point`
23 interface IPoint[N: Numeric]
24
25 # Horizontal coordinate
26 fun x: N is abstract
27
28 # Vertical coordinate
29 fun y: N is abstract
30
31 redef fun to_s do return "({x}, {y})"
32
33 # Distance with `other`
34 #
35 # ~~~
36 # var p0 = new Point[Float](0.0, 0.0)
37 # var p1 = new Point[Float](2.0, 3.0)
38 # assert p0.dist(p1).is_approx(3.6, 0.01)
39 # ~~~
40 #
41 # If `self` or `other` are in 3D, the distance takes into account the 3 axes.
42 # For a 2D point, the Z coordinate is considered to be 0.
43 #
44 # ~~~
45 # var p2 = new Point3d[Float](0.0, 0.0, 0.0)
46 # var p3 = new Point3d[Float](2.0, 3.0, 4.0)
47 # var p4 = new Point[Float](2.0, 3.0)
48 # assert p2.dist(p3).is_approx(5.385, 0.01)
49 # assert p2.dist(p4).is_approx(3.606, 0.01)
50 # ~~~
51 fun dist(other: Point[Numeric]): N
52 do
53 return x.value_of(dist2(other).to_f.sqrt)
54 end
55
56 # Square of the distance with `other`
57 #
58 # May be used as an approximation to compare distance between two points.
59 #
60 # ~~~
61 # var p0 = new Point[Float](0.0, 0.0)
62 # var p1 = new Point[Float](2.0, 3.0)
63 # assert p0.dist2(p1) == 13.0
64 # ~~~
65 #
66 # If `self` or `other` are in 3D, the distance takes into account the 3 axes.
67 # For a 2D point, the Z coordinate is considered to be 0.
68 #
69 # ~~~
70 # var p2 = new Point3d[Float](0.0, 0.0, 0.0)
71 # var p3 = new Point3d[Float](2.0, 3.0, 4.0)
72 # var p4 = new Point[Float](2.0, 3.0)
73 # assert p2.dist2(p3).is_approx(29.0, 0.01)
74 # assert p2.dist2(p4).is_approx(13.0, 0.01)
75 # assert p4.dist2(p2).is_approx(13.0, 0.01)
76 # ~~~
77 fun dist2(other: Point[Numeric]): N
78 do return x.value_of(other.dist2_with_2d(self))
79
80 private fun dist2_with_2d(other: IPoint[Numeric]): Numeric
81 do return dist2_xy(other)
82
83 private fun dist2_with_3d(other: IPoint3d[Numeric]): Numeric
84 do return dist2_xy(other).add(other.z.mul(other.z))
85
86 # Square of the distance with `other` on the X and Y axes
87 private fun dist2_xy(other: IPoint[N]): N
88 do
89 var dx = other.x.sub(x)
90 var dy = other.y.sub(y)
91 var s = (dx.mul(dx)).add(dy.mul(dy))
92 return x.value_of(s)
93 end
94
95 # Linear interpolation between `self` and `other` at `p` out of `1.0`
96 #
97 # ~~~
98 # var p0 = new Point[Float](0.0, 0.0)
99 # var p1 = new Point[Float](2.0, 3.0)
100 # assert p0.lerp(p1, 0.0) == p0
101 # assert p0.lerp(p1, 1.0) == p1
102 # assert p0.lerp(p1, 0.5) == new Point[Float](1.0, 1.5)
103 # ~~~
104 #
105 # TODO 3D implementation.
106 fun lerp(other: Point[Numeric], p: Float): Point[N]
107 do
108 var xx = x.to_f + (other.x.to_f - x.to_f).to_f * p
109 var yy = y.to_f + (other.y.to_f - y.to_f).to_f * p
110 return new Point[N](x.value_of(xx), y.value_of(yy))
111 end
112
113 redef fun ==(o) do return o isa IPoint[Numeric] and o.x == x and o.y == y
114 end
115
116 # 2D point with `x` and `z`
117 class Point[N: Numeric]
118 super IPoint[N]
119
120 redef var x: N = 0.0 is writable, optional
121 redef var y: N = 0.0 is writable, optional
122 end
123
124 # Abstract 3d point, strongly linked to its implementation `Point3d`
125 interface IPoint3d[N: Numeric]
126 super IPoint[N]
127
128 # Depth coordinate
129 fun z: N is abstract
130
131 redef fun to_s do return "({x}, {y}, {z})"
132
133 redef fun dist2(other)
134 do return x.value_of(other.dist2_with_3d(self))
135
136 redef fun dist2_with_2d(other)
137 do return dist2_xy(other).add(z.mul(z))
138
139 redef fun dist2_with_3d(other)
140 do
141 var dz = other.z.sub(z)
142 var s = dist2_xy(other).add(dz.mul(dz))
143 return x.value_of(s)
144 end
145 end
146
147 # 3D point with `x`, `y` and `z`
148 class Point3d[N: Numeric]
149 super IPoint3d[N]
150 super Point[N]
151
152 redef var z: N = 0.0 is writable, optional
153 end
154
155 # Abstract 2D line segment between two ordered points
156 interface ILine[N: Numeric]
157 # The type of points that ends the segment
158 type P: IPoint[N]
159
160 # Point at the left-end of the segment
161 fun point_left: P is abstract
162
163 # Point at the right-end of the segment
164 fun point_right: P is abstract
165
166 redef fun to_s do return "{point_left}--{point_right}"
167 end
168
169 # 2D line segment between two ordered points
170 class Line[N: Numeric]
171 super ILine[N]
172
173 redef var point_left
174 redef var point_right
175
176 init
177 do
178 var a = point_left
179 var b = point_right
180 if a.x > b.x then
181 point_left = b
182 point_right = a
183 end
184 end
185 end
186
187 # Abstract 3D line segment between two ordered points
188 interface ILine3d[N: Numeric]
189 super ILine[N]
190
191 redef type P: IPoint3d[N]
192 end
193
194 # 3D line segment between two ordered points
195 class Line3d[N: Numeric]
196 super Line[N]
197 super ILine3d[N]
198 end