Bug 12212 fixed: Export a polyline in 2D broke it into several segments
[scilab.git] / scilab / modules / scirenderer / src / org / scilab / forge / scirenderer / implementation / g2d / motor / AbstractDrawable3DObject.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  */
11
12 package org.scilab.forge.scirenderer.implementation.g2d.motor;
13
14 import java.awt.Color;
15 import java.awt.Graphics2D;
16 import java.awt.geom.Path2D;
17 import java.util.HashSet;
18 import java.util.Set;
19
20 import org.scilab.forge.scirenderer.tranformations.Vector3d;
21
22 /**
23  * @author Calixte DENIZET
24  */
25 public abstract class AbstractDrawable3DObject {
26
27     public static final double PRECISION = 1e-8;
28
29     private static int defaultPrecedence = 0;
30
31     protected final Vector3d[] vertices;
32
33     protected final Color[] colors;
34     protected int precedence;
35     protected Boolean is2d;
36     protected Double zindex;
37     protected Vector3d v0;
38     protected Vector3d v1;
39     protected Vector3d v0v1;
40     protected double nv0v1;
41     protected Vector3d normal;
42     protected BoundingBox bbox;
43     protected boolean marked;
44     protected boolean marked2;
45     protected Boolean degenerated;
46
47     /**
48      * Default constructor
49      * @param vertices the vertices
50      */
51     public AbstractDrawable3DObject(Vector3d[] vertices, Color[] colors) throws InvalidPolygonException {
52         if (vertices == null || vertices.length == 0) {
53             throw new InvalidPolygonException("Invalid 3D Object: no vertices was given");
54         }
55         this.vertices = vertices;
56         this.colors = colors;
57         if (isDegenerate()) {
58             throw new InvalidPolygonException("Invalid 3D Object: two vertices are the same");
59         }
60         if (isNanOrInf()) {
61             throw new InvalidPolygonException("Invalid 3D Object: contains NaN or Inf coordinates");
62         }
63         setPrecedence(defaultPrecedence++);
64         getNormal();
65     }
66
67     /**
68      * Get the bounding box
69      * @return the bounding box
70      */
71     final BoundingBox getBBox() {
72         if (bbox == null) {
73             bbox = BoundingBox.getBoundingBox(this);
74         }
75
76         return bbox;
77     }
78
79     /**
80      * Test if an array of colors contains only one color or not
81      * @param colors the colors array
82      * @return true if the array is monochromatic
83      */
84     public static boolean isMonochromatic(Color[] colors) {
85         if (colors != null && colors.length > 0) {
86             Color c = colors[0];
87             for (int i = 1; i < colors.length; i++) {
88                 if (!c.equals(colors[i])) {
89                     return false;
90                 }
91             }
92         }
93
94         return true;
95     }
96
97     /**
98      * Draw this object on a Graphics2D object
99      * @param g2d the Graphics2d object where to draw
100      */
101     public abstract void draw(Graphics2D g2d);
102
103     /**
104      * Reset the default precedence
105      */
106     public static void resetDefaultPrecedence() {
107         defaultPrecedence = 0;
108     }
109
110     /**
111      * Set the precedence of this object.
112      * @param precedence the precedence of this object
113      */
114     public void setPrecedence(int precedence) {
115         this.precedence = precedence;
116     }
117
118     /**
119      * Get the precedence of this object, i.e. its position in the list of the draw objects.
120      * The first object has a precedence of 0, the second has a precedence of 1, ...
121      * @param the precedence
122      */
123     public int getPrecedence() {
124         return precedence;
125     }
126
127     /**
128      * Determinates if this object is 2D in looking at the z coordinates
129      * (when all the drawn objects are 2D, we can avoid the projection)
130      */
131     public boolean is2D() {
132         if (is2d == null) {
133             for (Vector3d v : vertices) {
134                 if (v.getZ() != 0) {
135                     is2d = Boolean.FALSE;
136                     return is2d;
137                 }
138             }
139             is2d = Boolean.TRUE;
140         }
141
142         return is2d;
143     }
144
145     /**
146      * Get the normal vector.
147      * If no normal vector has been set then it is calculated in using the cross product of the first two vectors.
148      * @return the normal vector.
149      */
150     public Vector3d getProvidedNormal() {
151         return normal;
152     }
153
154     /**
155      * Get the normal vector.
156      * If no normal vector has been set then it is calculated in using the cross product of the first two vectors.
157      * @return the normal vector.
158      */
159     public Vector3d getNormal() {
160         if (v0v1 == null) {
161             setNormal();
162         }
163
164         return v0v1;
165     }
166
167     /**
168      * Set the normal vector
169      */
170     protected void setNormal() {
171         v0 = vertices[1].minus(vertices[0]);
172         if (vertices.length >= 3) {
173             v1 = vertices[2].minus(vertices[0]);
174             v0v1 = Vector3d.product(v0, v1);
175             nv0v1 = v0v1.getNorm();
176             v0v1 = v0v1.times(1 / nv0v1);;
177         } else {
178             v0v1 = new Vector3d(0, 0, 0);
179         }
180     }
181
182     /**
183      * Determinates if the object is contained into a plane
184      * @return true if the object is planar
185      */
186     protected boolean isPlanar() {
187         Vector3d n = getNormal();
188         if (n.isZero() || vertices.length == 3) {
189             return true;
190         }
191
192         for (int i = 1; i < vertices.length; i++) {
193             if (!isNull(n.scalar(vertices[0].minus(vertices[i])))) {
194                 return false;
195             }
196         }
197
198         return true;
199     }
200
201     public int isBehind(Vector3d v, double a) {
202         double[] mM = minmax3D(this, v);
203
204         if (isPositiveOrNull(mM[0] + a)) {
205             return 1;
206         }
207         if (mM[1] + a < 0) {
208             return -1;
209         }
210
211         return 0;
212     }
213
214     public static boolean isBehind(Vector3d M, Vector3d v, double a) {
215         return isPositiveOrNull(M.scalar(v) + a);
216     }
217
218     /**
219      * Get the projected polyline of this object
220      * @return a path 2D
221      */
222     protected Path2D getProjectedPolyLine() {
223         Path2D.Double path = new Path2D.Double();
224         path.moveTo(vertices[0].getX(), vertices[0].getY());
225         for (int i = 1; i < vertices.length; i++) {
226             path.lineTo(vertices[i].getX(), vertices[i].getY());
227         }
228
229         return path;
230     }
231
232     /**
233      * Get the projected contour (i.e. a closed polyline) of this object
234      * @return a path 2D
235      */
236     protected Path2D getProjectedContour() {
237         Path2D path = getProjectedPolyLine();
238         path.closePath();
239
240         return path;
241     }
242
243     /**
244      * @param d a number
245      * @return true if d is near zero
246      */
247     protected final static boolean isNull(final double d) {
248         return Math.abs(d) <= PRECISION;
249     }
250
251     /**
252      * @param d a number
253      * @return true if d is greater than zero
254      */
255     protected final static boolean isPositiveOrNull(final double d) {
256         return d >= 0 || isNull(d);
257     }
258
259     /**
260      * @param d a number
261      * @return true if d is greater than zero
262      */
263     protected final static boolean isNegativeOrNull(final double d) {
264         return d <= 0 || isNull(d);
265     }
266
267     /**
268      * @param d1 a number
269      * @param d2 a number
270      * @return true if d1 is greater than d2
271      */
272     protected final static boolean isGreaterOrEqual(final double d1, final double d2) {
273         return isPositiveOrNull(d1 - d2);
274     }
275
276     /**
277      * @param d1 a number
278      * @param d2 a number
279      * @return true if d1 is lower than d2
280      */
281     protected final static boolean isLowerOrEqual(final double d1, final double d2) {
282         return isPositiveOrNull(d2 - d1);
283     }
284
285     /**
286      * @param d1 a number
287      * @param d2 a number
288      * @return true if d1 is equal to d2
289      */
290     protected final static boolean isEqual(final double d1, final double d2) {
291         return isNull(d1 - d2);
292     }
293
294     /**
295      * Get min-max of the projections of the vertices of o on v
296      * @param o an object
297      * @param v a vector
298      * @return an array of size 2 containing min-max.
299      */
300     protected static final double[] minmax3D(final AbstractDrawable3DObject o, final Vector3d v) {
301         double min = v.scalar(o.vertices[0]);
302         double max = min;
303
304         for (int i = 1; i < o.vertices.length; i++) {
305             double s = v.scalar(o.vertices[i]);
306             if (s < min) {
307                 min = s;
308             } else if (s > max) {
309                 max = s;
310             }
311         }
312
313         return new double[] {min, max};
314     }
315
316     /**
317      * Get min-max of the projections of the vertices of o on v
318      * @param o an object
319      * @param v a vector
320      * @return an array of size 2 containing min-max.
321      */
322     protected static final double[] minmax2D(final AbstractDrawable3DObject o, final double x, final double y) {
323         double min = x * o.vertices[0].getX() + y * o.vertices[0].getY();
324         double max = min;
325
326         for (int i = 1; i < o.vertices.length; i++) {
327             double s = x * o.vertices[i].getX() + y * o.vertices[i].getY();
328             if (s < min) {
329                 min = s;
330             } else if (s > max) {
331                 max = s;
332             }
333         }
334
335         return new double[] {min, max};
336     }
337
338     protected static final Color getColorsBarycenter(final Color c1, final Color c2, final double w1, final double w2) {
339         if (c1 != null && c2 != null && !c1.equals(c2)) {
340             float[] comp1 = c1.getComponents(null);
341             float[] comp2 = c2.getComponents(null);
342
343             return new Color((float) (comp1[0] * w1 + comp2[0] * w2),
344                              (float) (comp1[1] * w1 + comp2[1] * w2),
345                              (float) (comp1[2] * w1 + comp2[2] * w2),
346                              (float) (comp1[3] * w1 + comp2[3] * w2));
347         }
348
349         return c1;
350     }
351
352     /**
353      * @return true if there are two vertices which are indentical
354      */
355     protected boolean isDegenerate() {
356         if (degenerated == null) {
357             Set<Vector3d> set = new HashSet<Vector3d>();
358             for (Vector3d v : vertices) {
359                 set.add(v);
360             }
361
362             degenerated = set.size() != vertices.length;
363         }
364
365         return degenerated;
366     }
367
368     protected boolean isNanOrInf() {
369         for (Vector3d v : vertices) {
370             if (isNanOrInf(v)) {
371                 return true;
372             }
373         }
374
375         return false;
376     }
377
378     public static final boolean isNanOrInf(final Vector3d v) {
379         final double x = v.getX();
380         if (Double.isNaN(x) || Double.isInfinite(x)) {
381             return true;
382         }
383         final double y = v.getY();
384         if (Double.isNaN(y) || Double.isInfinite(y)) {
385             return true;
386         }
387         final double z = v.getZ();
388         if (Double.isNaN(z) || Double.isInfinite(z)) {
389             return true;
390         }
391         return false;
392     }
393 }