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 / Motor3D.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012-2013 - 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.Dimension;
16 import java.awt.Graphics2D;
17 import java.awt.RenderingHints;
18 import java.awt.Stroke;
19 import java.awt.image.BufferedImage;
20 import java.nio.FloatBuffer;
21 import java.nio.IntBuffer;
22 import java.util.Arrays;
23 import java.util.List;
24
25 import org.scilab.forge.scirenderer.DrawingTools;
26 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
27 import org.scilab.forge.scirenderer.clipping.ClippingPlane;
28 import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvas;
29 import org.scilab.forge.scirenderer.implementation.g2d.buffers.G2DElementsBuffer;
30 import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureDrawingTools;
31 import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureManager;
32 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
33 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
34 import org.scilab.forge.scirenderer.shapes.geometry.Geometry.FaceCullingMode;
35 import org.scilab.forge.scirenderer.texture.AnchorPosition;
36 import org.scilab.forge.scirenderer.texture.Texture;
37 import org.scilab.forge.scirenderer.tranformations.Transformation;
38 import org.scilab.forge.scirenderer.tranformations.Vector3d;
39
40 /**
41  * @author Calixte DENIZET
42  */
43 public class Motor3D {
44
45     private Transformation transf;
46     private Transformation singleTransf;
47     private FaceCullingMode mode = FaceCullingMode.BOTH;
48     private Graphics2D g2d;
49     private Dimension dim;
50     private G2DTextureDrawingTools textureDrawingTools;
51     private G2DCanvas canvas;
52
53     /**
54      * Default constructor
55      * @param g2d a Graphics2D object where to draw
56      * @param dim the graphic dimensions
57      */
58     public Motor3D(G2DCanvas canvas, Graphics2D g2d, Dimension dim) {
59         this.canvas = canvas;
60         this.g2d = g2d;
61         this.dim = dim;
62         this.textureDrawingTools = new G2DTextureDrawingTools(g2d);
63         AbstractDrawable3DObject.resetDefaultPrecedence();
64     }
65
66     public void setGraphics(Graphics2D g2d) {
67         this.g2d = g2d;
68         this.textureDrawingTools.setGraphics(g2d);
69     }
70
71     public void setAntialiased(boolean aa) {
72         if (aa) {
73             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
74         } else {
75             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
76         }
77     }
78
79     public boolean is2DView() {
80         return canvas.getMainDrawer().is2DView();
81     }
82
83     public void setClippingPlanes(List<ClippingPlane> clippingPlanes) {
84         Scene.setClippingPlanes(clippingPlanes);
85     }
86
87     /**
88      * Set the face culling mode
89      * @param mode the mode to set
90      */
91     public void setFaceCullingMode(FaceCullingMode mode) {
92         this.mode = mode;
93     }
94
95     /**
96      * Set the current transformation
97      * @param transf the transformation to set
98      */
99     public void setTransformation(Transformation transf, Transformation single) {
100         this.transf = transf;
101         this.singleTransf = single;
102     }
103
104     public Transformation getCurrentTransformation() {
105         return transf;
106     }
107
108     public Transformation getCurrentSingleTransformation() {
109         return singleTransf;
110     }
111
112     /**
113      * Reset this motor
114      * @param color the filling color
115      */
116     public void reset(Color color) {
117         transf = null;
118         mode = FaceCullingMode.BOTH;
119         g2d.setColor(color);
120         g2d.fillRect(0, 0, (int) dim.getWidth(), (int) dim.getHeight());
121         Scene.clear();
122     }
123
124     /**
125      * Clear the depth buffer
126      */
127     public void clearDepth() {
128         Scene.clearDepth();
129     }
130
131     /**
132      * Draw the scene in the Graphics2D
133      */
134     public void draw() {
135         Scene.drawRoot(g2d);
136         Scene.clearAll();
137         G2DTextureManager.clear();
138     }
139
140     public void drawTexture(DrawingTools drawingTools, BufferedImage image, Texture texture) {
141         try {
142             SpritedRectangle o = new SpritedRectangle(new Vector3d(0, 0, 0), transf, image, texture.getMagnificationFilter());
143             add(o);
144         } catch (InvalidPolygonException e) { }
145     }
146
147     /**
148      * Add the geometry to the scene
149      * @param drawingTools the DrawingTools
150      * @param geometry the geometry to draw
151      * @param appearance the appearance to use
152      */
153     public void draw(DrawingTools drawingTools, Geometry geometry, Appearance appearance) {
154         setFaceCullingMode(geometry.getFaceCullingMode());
155         FloatBuffer vertexBuffer = geometry.getVertices().getData();
156
157         IntBuffer indicesBuffer = null;;
158         if (geometry.getIndices() != null) {
159             indicesBuffer = geometry.getIndices().getData();
160         }
161
162         IntBuffer wireIndicesBuffer = null;
163         if (geometry.getWireIndices() != null) {
164             wireIndicesBuffer = geometry.getWireIndices().getData();
165         }
166
167         FloatBuffer colorBuffer = null;
168         if (geometry.getColors() != null) {
169             colorBuffer = geometry.getColors().getData();
170         }
171
172         FloatBuffer normalBuffer = null;
173         if (geometry.getNormals() != null) {
174             normalBuffer = geometry.getNormals().getData();
175         }
176
177         Texture texture = appearance.getTexture();
178         FloatBuffer textureCoordinatesBuffer = null;
179         BufferedImage image = null;
180         if (texture != null && geometry.getTextureCoordinates() != null) {
181             textureCoordinatesBuffer = geometry.getTextureCoordinates().getData();
182             image = ((G2DTextureManager.G2DTexture) texture).getImage();
183         }
184
185         if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) {
186             addTriangles(vertexBuffer, normalBuffer, colorBuffer, appearance.getFillColor(), indicesBuffer, textureCoordinatesBuffer, image, texture, geometry.getFillDrawingMode());
187         }
188
189         if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) {
190             if (appearance.getLineColor() == null) {
191                 addSegments(vertexBuffer, colorBuffer, null, wireIndicesBuffer, geometry.getLineDrawingMode(), appearance);
192             } else {
193                 addSegments(vertexBuffer, null, appearance.getLineColor(), wireIndicesBuffer, geometry.getLineDrawingMode(), appearance);
194             }
195         }
196     }
197
198     public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, ElementsBuffer positions, double rotationAngle) {
199         FloatBuffer positionsBuffer = positions.getData();
200         float[] buffer;
201
202         positionsBuffer.rewind();
203         if (positionsBuffer.hasArray()) {
204             buffer = positionsBuffer.array();
205         } else {
206             buffer = new float[positionsBuffer.limit()];
207             positionsBuffer.get(buffer);
208         }
209         Vector3d[] verticesArray = getMultiVectors(buffer, transf, false);
210
211         for (Vector3d v : verticesArray) {
212             try {
213                 SpritedRectangle o = new SpritedRectangle(v, texture, anchor, textureDrawingTools, rotationAngle);
214                 add(o);
215             } catch (InvalidPolygonException e) { }
216         }
217     }
218
219     public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) {
220         try {
221             add(new SpritedRectangle(transf.project(position), texture, anchor, textureDrawingTools, rotationAngle));
222         } catch (InvalidPolygonException e) { }
223     }
224
225     /**
226      * Add a Triangle to the scene
227      * @param tri the triangle to add
228      */
229     private void add(Triangle tri) {
230         Vector3d normal = tri.getNormal();
231         if (normal != null) {
232             //normal = transf.projectDirection(normal);
233             if ((mode == FaceCullingMode.CW && normal.getZ() > 0) || (mode == FaceCullingMode.CCW && normal.getZ() < 0) || mode == FaceCullingMode.BOTH) {
234                 Scene.addToRoot(is2DView(), tri);
235             }
236         } else {
237             Scene.addToRoot(is2DView(), tri);
238         }
239     }
240
241     /**
242      * Add a segment to the scene
243      * @param s the segment to add
244      */
245     private void add(Segment s) {
246         Scene.addToRoot(is2DView(), s);
247     }
248
249     private void add(SpritedRectangle sprite) {
250         Scene.addToRoot(is2DView(), sprite);
251     }
252
253     private void add(PolyLine p) {
254         Scene.addToRoot(is2DView(), p);
255     }
256
257     /**
258      * Get arrays from Buffer
259      * @param vertices a buffer containing vertices
260      * @param colors a buffer containing the colors
261      * @param defaultColor the color to use when colors is null
262      * @param indices a buffer containg the index of the vertices to retrieve
263      * @return an array of length 2 containing the vertices array and the colors array
264      */
265     private Object[] getArrays(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, FloatBuffer textureCoords, IntBuffer indices) {
266         float[] buffer;
267         Vector3d[] verticesArray;
268         Vector3d[] textureCoordsArray = null;
269         Color[] colorsArray;
270
271         vertices.rewind();
272         if (vertices.hasArray()) {
273             buffer = vertices.array();
274         } else {
275             buffer = new float[vertices.limit()];
276             vertices.get(buffer);
277         }
278         verticesArray = getMultiVectors(buffer, transf, false);
279
280         if (colors != null) {
281             colors.rewind();
282             if (colors.hasArray()) {
283                 buffer = colors.array();
284             } else {
285                 buffer = new float[colors.limit()];
286                 colors.get(buffer);
287             }
288             colorsArray = getMultiColors(buffer);
289         } else {
290             colorsArray = new Color[vertices.limit() / G2DElementsBuffer.ELEMENT_SIZE];
291             Arrays.fill(colorsArray, defaultColor);
292         }
293
294         if (textureCoords != null) {
295             textureCoords.rewind();
296             if (textureCoords.hasArray()) {
297                 buffer = textureCoords.array();
298             } else {
299                 buffer = new float[textureCoords.limit()];
300                 textureCoords.get(buffer);
301             }
302             textureCoordsArray = getMultiVectors(buffer);
303         }
304
305         if (indices != null) {
306             indices.rewind();
307             int[] ind;
308             if (indices.hasArray()) {
309                 ind = indices.array();
310             } else {
311                 ind = new int[indices.limit()];
312                 indices.get(ind);
313             }
314             Vector3d[] va = new Vector3d[ind.length];
315             Color[] ca = new Color[ind.length];
316             Vector3d[] ta = null;
317             if (textureCoords != null) {
318                 ta = new Vector3d[ind.length];
319             }
320             for (int i = 0; i < ind.length; i++) {
321                 va[i] = verticesArray[ind[i]];
322                 ca[i] = colorsArray[ind[i]];
323                 if (ta != null) {
324                     ta[i] = textureCoordsArray[ind[i]];
325                 }
326             }
327             verticesArray = va;
328             colorsArray = ca;
329             textureCoordsArray = ta;
330         }
331
332         return new Object[] {verticesArray, colorsArray, textureCoordsArray};
333     }
334
335     /**
336      * Convert the buffer vertices into vertices array and put the segments in the scene
337      * @param vertices a buffer containing vertices
338      * @param colors a buffer containing the colors
339      * @param defaultColor the color to use when colors is null
340      * @param indices a buffer containg the index of the vertices to retrieve
341      * @param drawingMode the drawing mode
342      * @param stroke the Stroke to use to draw a segment
343      */
344     private void addSegments(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, IntBuffer indices, Geometry.LineDrawingMode drawingMode, Appearance appearance) {
345         Object[] arrays = getArrays(vertices, colors, defaultColor, null, indices);
346         Vector3d[] verticesArray = (Vector3d[]) arrays[0];
347         Color[] colorsArray = (Color[]) arrays[1];
348         Vector3d[] v;
349         Color[] c;
350         double cumLength = 0;
351
352         if (verticesArray.length <= 1) {
353             return;
354         }
355
356         switch (drawingMode) {
357             case SEGMENTS_STRIP :
358                 if (is2DView()) {
359                     List<PolyLine> list = PolyLine.getPolyLines(verticesArray, colorsArray, G2DStroke.getStroke(appearance, 0), false);
360                     for (PolyLine p : list) {
361                         add(p);
362                     }
363                 } else {
364                     for (int i = 0; i < verticesArray.length - 1; i++) {
365                         v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
366                         c = new Color[] {colorsArray[i], colorsArray[i + 1]};
367                         try {
368                             add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength)));
369                             cumLength += Segment.getLength(v);
370                         } catch (InvalidPolygonException e) {
371                             cumLength = 0;
372                         }
373                     }
374                 }
375                 break;
376             case SEGMENTS_LOOP :
377                 if (is2DView()) {
378                     List<PolyLine> list = PolyLine.getPolyLines(verticesArray, colorsArray, G2DStroke.getStroke(appearance, 0), true);
379                     for (PolyLine p : list) {
380                         add(p);
381                     }
382                 } else {
383                     for (int i = 0; i < verticesArray.length - 1; i++) {
384                         v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
385                         c = new Color[] {colorsArray[i], colorsArray[i + 1]};
386                         try {
387                             add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength)));
388                             cumLength += Segment.getLength(v);
389                         } catch (InvalidPolygonException e) {
390                             cumLength = 0;
391                         }
392                     }
393                     int n = verticesArray.length - 1;
394                     v = new Vector3d[] {verticesArray[n], verticesArray[0]};
395                     c = new Color[] {colorsArray[n], colorsArray[0]};
396                     try {
397                         add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength)));
398                     } catch (InvalidPolygonException e) { }
399                 }
400                 break;
401             case SEGMENTS :
402             default :
403                 for (int i = 0; i < verticesArray.length - 1; i += 2) {
404                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
405                     c = new Color[] {colorsArray[i], colorsArray[i + 1]};
406                     try {
407                         add(new Segment(v, c, G2DStroke.getStroke(appearance, 0)));
408                     } catch (InvalidPolygonException e) { }
409                 }
410                 break;
411         }
412     }
413
414     /**
415      * Convert the buffer vertices into vertices array and put the triangles in the scene
416      * @param vertices a buffer containing vertices
417      * @param normals a buffer containing the normals (not used)
418      * @param colors a buffer containing the colors
419      * @param defaultColor the color to use when colors is null
420      * @param indices a buffer containg the index of the vertices to retrieve
421      * @param drawingMode the drawing mode
422      */
423     private void addTriangles(FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors, Color defaultColor, IntBuffer indices, FloatBuffer textureCoords, final BufferedImage image, Texture texture, Geometry.FillDrawingMode drawingMode) {
424         Object[] arrays = getArrays(vertices, colors, defaultColor, textureCoords, indices);
425         Vector3d[] verticesArray = (Vector3d[]) arrays[0];
426         Color[] colorsArray = (Color[]) arrays[1];
427         Vector3d[] textureCoordsArray = (Vector3d[]) arrays[2];
428         Vector3d[] v;
429         Color[] c;
430         Texture.Filter filter = Texture.Filter.NEAREST;
431
432         if (texture != null) {
433             filter = texture.getMagnificationFilter();
434         }
435
436         switch (drawingMode) {
437             case TRIANGLE_FAN :
438                 for (int i = 1; i < verticesArray.length - 1; i++) {
439                     v = new Vector3d[] {verticesArray[0], verticesArray[i], verticesArray[i + 1]};
440                     try {
441                         if (image == null) {
442                             c = new Color[] {colorsArray[0], colorsArray[i], colorsArray[i + 1]};
443                             add(new Triangle(v, c, null));
444                         } else {
445                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[i], textureCoordsArray[i + 1]}, image, filter));
446                         }
447                     } catch (InvalidPolygonException e) { }
448                 }
449                 int n = verticesArray.length - 1;
450                 v = new Vector3d[] {verticesArray[0], verticesArray[n], verticesArray[1]};
451                 try {
452                     if (image == null) {
453                         c = new Color[] {colorsArray[0], colorsArray[n], colorsArray[1]};
454                         add(new Triangle(v, c, null));
455                     } else {
456                         add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[n], textureCoordsArray[1]}, image, filter));
457                     }
458                 } catch (InvalidPolygonException e) { }
459                 break;
460             case TRIANGLE_STRIP :
461                 for (int i = 0; i < verticesArray.length - 2; i++) {
462                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]};
463                     try {
464                         if (image == null) {
465                             c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]};
466                             add(new Triangle(v, c, null));
467                         } else {
468                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter));
469                         }
470                     } catch (InvalidPolygonException e) { }
471                 }
472                 break;
473             case TRIANGLES :
474             default :
475                 for (int i = 0; i < verticesArray.length - 2; i += 3) {
476                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]};
477                     try {
478                         if (image == null) {
479                             c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]};
480                             add(new Triangle(v, c, null));
481                         } else {
482                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter));
483                         }
484                     } catch (InvalidPolygonException e) { }
485                 }
486                 break;
487         }
488     }
489
490     /**
491      * Convert an array of float into an array of Vector3d objects
492      * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates
493      * @param t the transformation to use for the projection
494      * @param dir if true t.projectDirection() is used rather than t.project()
495      * @return an array of Vector3d containg the vertices
496      */
497     private static final Vector3d[] getMultiVectors(final float[] vertices, final Transformation t, final boolean dir) {
498         Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE];
499         if (dir) {
500             int j = 0;
501             for (int i = 0; i < v.length; i++) {
502                 v[i] = t.projectDirection(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]));
503                 j += G2DElementsBuffer.ELEMENT_SIZE;
504             }
505         } else {
506             int j = 0;
507             for (int i = 0; i < v.length; i++) {
508                 v[i] = t.project(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]));
509                 j += G2DElementsBuffer.ELEMENT_SIZE;
510             }
511         }
512
513         return v;
514     }
515
516     /**
517      * Convert an array of float into an array of Vector3d objects
518      * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates
519      * @return an array of Vector3d containg the vertices
520      */
521     private static final Vector3d[] getMultiVectors(final float[] vertices) {
522         Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE];
523         int j = 0;
524         for (int i = 0; i < v.length; i++) {
525             v[i] = new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]);
526             j += G2DElementsBuffer.ELEMENT_SIZE;
527         }
528
529         return v;
530     }
531
532     /**
533      * Convert a float array into a Color array
534      * @param colors a float array
535      * @return an array of Color
536      */
537     private static final Color[] getMultiColors(final float[] colors) {
538         Color[] c = new Color[colors.length / G2DElementsBuffer.ELEMENT_SIZE];
539         int j = 0;
540         Color prev = Color.BLACK;
541         for (int i = 0; i < c.length; i++) {
542             c[i] = new Color(colors[j], colors[j + 1], colors[j + 2], colors[j + 3]);
543             if (prev.equals(c[i])) {
544                 c[i] = prev;
545             }
546             prev = c[i];
547             j += G2DElementsBuffer.ELEMENT_SIZE;
548         }
549
550         return c;
551     }
552 }