1 /*
2 * Copyright (C) 2017 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.geometry;
17
18 import com.irurueta.algebra.AlgebraException;
19
20 import java.io.Serializable;
21 import java.util.Arrays;
22
23 /**
24 * This class defines an ellipsoid.
25 */
26 public class Ellipsoid implements Serializable {
27
28 /**
29 * Number of dimensions.
30 */
31 public static final int DIMENSIONS = 3;
32
33 /**
34 * Constant to compute approximate surface area.
35 */
36 private static final double P = 1.6075;
37
38 /**
39 * Center of ellipse.
40 */
41 private Point3D center;
42
43
44 /**
45 * Lengths of all three semi-axes.
46 */
47 private double[] semiAxesLengths;
48
49 /**
50 * Rotation.
51 */
52 private Rotation3D rotation;
53
54 /**
55 * Empty constructor.
56 * Creates an ellipsoid equal to a sphere located at space origin (0,0,0)
57 * with radius 1.0.
58 */
59 public Ellipsoid() {
60 center = Point3D.create();
61 semiAxesLengths = new double[DIMENSIONS];
62 Arrays.fill(semiAxesLengths, 1.0);
63 rotation = Rotation3D.create();
64 }
65
66 /**
67 * Sets ellipsoid parameters.
68 *
69 * @param center center of ellipsoid.
70 * @param semiAxesLengths lengths of all three semi-axes.
71 * @param rotation rotation.
72 * @throws IllegalArgumentException if length of provided array is not
73 * three.
74 */
75 public Ellipsoid(final Point3D center, final double[] semiAxesLengths, final Rotation3D rotation) {
76 setCenterAxesAndRotation(center, semiAxesLengths, rotation);
77 }
78
79 /**
80 * Returns center of ellipsoid.
81 *
82 * @return center of ellipsoid.
83 */
84 public Point3D getCenter() {
85 return center;
86 }
87
88 /**
89 * Sets center of ellipsoid.
90 *
91 * @param center center of ellipsoid.
92 * @throws NullPointerException raised if provided center is null.
93 */
94 public void setCenter(final Point3D center) {
95 if (center == null) {
96 throw new NullPointerException();
97 }
98 this.center = center;
99 }
100
101 /**
102 * Gets lengths of all three semi-axes.
103 *
104 * @return lengths of all three semi-axes.
105 */
106 public double[] getSemiAxesLengths() {
107 return semiAxesLengths;
108 }
109
110 /**
111 * Sets lengths of all three semi-axes.
112 *
113 * @param semiAxesLengths lengths of all three semi-axes.
114 * @throws IllegalArgumentException if length of provided array is not
115 * three.
116 */
117 public void setSemiAxesLengths(final double[] semiAxesLengths) {
118 if (semiAxesLengths.length != DIMENSIONS) {
119 throw new IllegalArgumentException();
120 }
121 this.semiAxesLengths = semiAxesLengths;
122 }
123
124 /**
125 * Gets rotation.
126 *
127 * @return rotation.
128 */
129 public Rotation3D getRotation() {
130 return rotation;
131 }
132
133 /**
134 * Sets rotation.
135 *
136 * @param rotation rotation.
137 */
138 public void setRotation(final Rotation3D rotation) {
139 this.rotation = rotation;
140 }
141
142 /**
143 * Sets ellipsoid parameters.
144 *
145 * @param center center of ellipsoid.
146 * @param semiAxesLengths lengths of all three semi-axes.
147 * @param rotation rotation.
148 * @throws IllegalArgumentException if length of provided array is not
149 * three.
150 */
151 public final void setCenterAxesAndRotation(
152 final Point3D center, final double[] semiAxesLengths, final Rotation3D rotation) {
153 if (semiAxesLengths.length != DIMENSIONS) {
154 throw new IllegalArgumentException();
155 }
156 this.center = center;
157 this.semiAxesLengths = semiAxesLengths;
158 this.rotation = rotation;
159 }
160
161 /**
162 * Returns volume of this ellipsoid.
163 *
164 * @return volume of this ellipsoid.
165 */
166 public double getVolume() {
167 final var a = semiAxesLengths[0];
168 final var b = semiAxesLengths[1];
169 final var c = semiAxesLengths[2];
170 return 4.0 / 3.0 * Math.PI * a * b * c;
171 }
172
173 /**
174 * Returns surface of this ellipsoid.
175 *
176 * @return surface of this ellipsoid.
177 */
178 public double getSurface() {
179 final var a = semiAxesLengths[0];
180 final var b = semiAxesLengths[1];
181 final var c = semiAxesLengths[2];
182
183 return 4.0 * Math.PI * Math.pow((Math.pow(a * b, P) + Math.pow(a * c, P) + Math.pow(b * c, P)) / 3.0, 1.0 / P);
184 }
185
186 /**
187 * Converts this ellipsoid into a quadric.
188 * Quadrics a re a more general representation of ellipsoids.
189 *
190 * @return a quadric representing this ellipsoid.
191 * @throws GeometryException if quadric cannot be determined due to
192 * numerical instabilities.
193 */
194 public Quadric toQuadric() throws GeometryException {
195 // A quadric has the following matrix form:
196 // Q = [A D F G]
197 // [D B E H]
198 // [F E C I]
199 // [G H I J]
200 // [x y z w][A D F G][x] = [x y z w][A*x + D*y + F*z + G*w] =
201 // [D B E H][y] [D*x + B*y + E*z + H*w]
202 // [F E C I][z] [F*x + E*y + C*z + I*w]
203 // [G H I J][w] [G*x + H*y + I*z + J*w]
204 // = A*x^2 + D*x*y + F*x*z + G*x*w + D*x*y + B*y^2 + E*y*z + H*y*w +
205 // F*x*z + E*y*z + C*z^2 + I*z*w + G*x*w + H*y*w + I*z*w + J*w^2 =
206 // = A*x^2 + B*y^2 + C*z^2 + 2*D*x*y + 2*E*y*z + 2*F*x*z + 2*G*x*w + 2*H*y*w + 2*I*z*w + J*w^2
207 // which follows expression:
208 // A*x^2 + B*y^2 + C*z^2 + 2*D*x*y + 2*E*y*z + 2*F*x*z + 2*G*x*w + 2*H*y*w + 2*I*z*w + J*w^2 = 0
209
210 // An Ellipsoid is a particular form of a quadric following expression:
211 // x^2 / a^2 + y^2 / b^2 + z^2 / c^2 = 1
212 // when centered at origin and having no rotation
213
214 // if we take an arbitrary center position:
215 final var a = 1.0 / Math.pow(semiAxesLengths[0], 2.0);
216 final var b = 1.0 / Math.pow(semiAxesLengths[1], 2.0);
217 final var c = 1.0 / Math.pow(semiAxesLengths[2], 2.0);
218 final var d = 0.0;
219 final var e = 0.0;
220 final var f = 0.0;
221 final var g = 0.0;
222 final var h = 0.0;
223 final var i = 0.0;
224 final var j = -1;
225
226 final var q = new Quadric(a, b, c, d, e, f, g, h, i, j);
227 final var t = new EuclideanTransformation3D(rotation, new double[]{
228 center.getInhomX(), center.getInhomY(), center.getInhomZ()});
229
230 try {
231 t.transform(q);
232 } catch (final NonSymmetricMatrixException | AlgebraException ex) {
233 throw new GeometryException(ex);
234 }
235
236 return q;
237 }
238
239 /**
240 * Sets parameters of this ellipsoid from a sphere.
241 *
242 * @param sphere a sphere to set parameters from.
243 */
244 public final void setFromSphere(final Sphere sphere) {
245 center = sphere.getCenter();
246 Arrays.fill(semiAxesLengths, sphere.getRadius());
247 rotation = Rotation3D.create();
248 }
249 }