Bug 11982 fixed: ticks computation were made when drawing
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / JoGLView / DrawerVisitor.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - DIGITEO - Pierre Lando
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-en.txt
10  */
11
12 package org.scilab.modules.renderer.JoGLView;
13
14 import org.scilab.forge.scirenderer.Canvas;
15 import org.scilab.forge.scirenderer.Drawer;
16 import org.scilab.forge.scirenderer.DrawingTools;
17 import org.scilab.forge.scirenderer.SciRendererException;
18 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
19 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
20 import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry;
21 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
22 import org.scilab.forge.scirenderer.texture.AnchorPosition;
23 import org.scilab.forge.scirenderer.texture.AbstractTextureDataProvider;
24 import org.scilab.forge.scirenderer.texture.Texture;
25 import org.scilab.forge.scirenderer.tranformations.Transformation;
26 import org.scilab.forge.scirenderer.tranformations.TransformationFactory;
27 import org.scilab.forge.scirenderer.tranformations.TransformationStack;
28 import org.scilab.forge.scirenderer.utils.shapes.geometry.CubeFactory;
29 import org.scilab.modules.graphic_objects.ObjectRemovedException;
30 import org.scilab.modules.graphic_objects.arc.Arc;
31 import org.scilab.modules.graphic_objects.axes.Axes;
32 import org.scilab.modules.graphic_objects.axes.Camera.ViewType;
33 import org.scilab.modules.graphic_objects.axis.Axis;
34 import org.scilab.modules.graphic_objects.compound.Compound;
35 import org.scilab.modules.graphic_objects.fec.Fec;
36 import org.scilab.modules.graphic_objects.figure.ColorMap;
37 import org.scilab.modules.graphic_objects.figure.Figure;
38 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
39 import org.scilab.modules.graphic_objects.graphicObject.GraphicObject;
40 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
41 import org.scilab.modules.graphic_objects.graphicObject.Visitor;
42 import org.scilab.modules.graphic_objects.graphicView.GraphicView;
43 import org.scilab.modules.graphic_objects.imageplot.Grayplot;
44 import org.scilab.modules.graphic_objects.imageplot.Matplot;
45 import org.scilab.modules.graphic_objects.label.Label;
46 import org.scilab.modules.graphic_objects.legend.Legend;
47 import org.scilab.modules.graphic_objects.polyline.Polyline;
48 import org.scilab.modules.graphic_objects.rectangle.Rectangle;
49 import org.scilab.modules.graphic_objects.surface.Fac3d;
50 import org.scilab.modules.graphic_objects.surface.Plot3d;
51 import org.scilab.modules.graphic_objects.textObject.Text;
52 import org.scilab.modules.graphic_objects.vectfield.Arrow;
53 import org.scilab.modules.graphic_objects.vectfield.Champ;
54 import org.scilab.modules.graphic_objects.vectfield.Segs;
55 import org.scilab.modules.renderer.JoGLView.arrowDrawing.ArrowDrawer;
56 import org.scilab.modules.renderer.JoGLView.axes.AxesDrawer;
57 import org.scilab.modules.renderer.JoGLView.contouredObject.ContouredObjectDrawer;
58 import org.scilab.modules.renderer.JoGLView.interaction.InteractionManager;
59 import org.scilab.modules.renderer.JoGLView.label.LabelManager;
60 import org.scilab.modules.renderer.JoGLView.legend.LegendDrawer;
61 import org.scilab.modules.renderer.JoGLView.mark.MarkSpriteManager;
62 import org.scilab.modules.renderer.JoGLView.postRendering.PostRendered;
63 import org.scilab.modules.renderer.JoGLView.text.TextManager;
64 import org.scilab.modules.renderer.JoGLView.util.ColorFactory;
65 import org.scilab.modules.renderer.JoGLView.util.OutOfMemoryException;
66 import org.scilab.modules.renderer.utils.textRendering.FontManager;
67
68 import java.awt.Component;
69 import java.awt.Dimension;
70 import java.nio.ByteBuffer;
71 import java.util.Arrays;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.LinkedList;
75 import java.util.List;
76 import java.util.Map;
77 import java.util.Set;
78
79 import javax.swing.SwingUtilities;
80
81 /**
82  * @author Pierre Lando
83  */
84 public class DrawerVisitor implements Visitor, Drawer, GraphicView {
85
86     /** Set of properties changed during a draw if auto-ticks is on for X axis. */
87     private static final Set<Integer> X_AXIS_TICKS_PROPERTIES = new HashSet<Integer>(Arrays.asList(
88                 GraphicObjectProperties.__GO_X_AXIS_TICKS_LOCATIONS__,
89                 GraphicObjectProperties.__GO_X_AXIS_TICKS_LABELS__,
90                 GraphicObjectProperties.__GO_X_AXIS_SUBTICKS__
91             ));
92
93     /** Set of properties changed during a draw if auto-ticks is on for Y axis. */
94     private static final Set<Integer> Y_AXIS_TICKS_PROPERTIES = new HashSet<Integer>(Arrays.asList(
95                 GraphicObjectProperties.__GO_Y_AXIS_TICKS_LOCATIONS__,
96                 GraphicObjectProperties.__GO_Y_AXIS_TICKS_LABELS__,
97                 GraphicObjectProperties.__GO_Y_AXIS_SUBTICKS__
98             ));
99
100     /** Set of properties changed during a draw if auto-ticks is on for Z axis. */
101     private static final Set<Integer> Z_AXIS_TICKS_PROPERTIES = new HashSet<Integer>(Arrays.asList(
102                 GraphicObjectProperties.__GO_Z_AXIS_TICKS_LOCATIONS__,
103                 GraphicObjectProperties.__GO_Z_AXIS_TICKS_LABELS__,
104                 GraphicObjectProperties.__GO_Z_AXIS_SUBTICKS__
105             ));
106
107     /** Set of figure properties for witch a change doesn't lead to a redraw */
108     private static final Set<Integer> SILENT_FIGURE_PROPERTIES = new HashSet<Integer>(Arrays.asList(
109                 GraphicObjectProperties.__GO_ROTATION_TYPE__,
110                 GraphicObjectProperties.__GO_INFO_MESSAGE__,
111                 GraphicObjectProperties.__GO_FIGURE_NAME__,
112                 GraphicObjectProperties.__GO_AUTORESIZE__,
113                 GraphicObjectProperties.__GO_POSITION__,
114                 GraphicObjectProperties.__GO_SIZE__,
115                 GraphicObjectProperties.__GO_ID__
116             ));
117
118     private static final boolean DEBUG_MODE = false;
119
120     private final Component component;
121     private final Figure figure;
122     private final InteractionManager interactionManager;
123
124     private final ColorMapTextureDataProvider colorMapTextureDataProvider;
125
126     private final ScilabTextureManager textureManager;
127     private final MarkSpriteManager markManager;
128     private final LabelManager labelManager;
129     private final DataManager dataManager;
130     private final TextManager textManager;
131
132     private final ContouredObjectDrawer contouredObjectDrawer;
133     private final LegendDrawer legendDrawer;
134     private final AxesDrawer axesDrawer;
135     private final AxisDrawer axisDrawer;
136     private final ArrowDrawer arrowDrawer;
137     private final FecDrawer fecDrawer;
138
139     private DrawingTools drawingTools;
140     private Texture colorMapTexture;
141     private ColorMap colorMap;
142
143     private Axes currentAxes;
144     private Canvas canvas;
145
146     /**
147      * The map between the existing Figures' identifiers and their corresponding Visitor.
148      * Used to get access to the DrawerVisitor corresponding to a given Figure when the
149      * renderer module is accessed from another thread than the AWT's.
150      */
151     private final static Map<String, DrawerVisitor> visitorMap = new HashMap<String, DrawerVisitor>();
152     private final List<PostRendered> postRenderedList = new LinkedList<PostRendered>();
153
154     public DrawerVisitor(Component component, Canvas canvas, Figure figure) {
155         GraphicController.getController().register(this);
156
157         this.component = component;
158         this.canvas = canvas;
159         this.figure = figure;
160
161         this.interactionManager = new InteractionManager(this);
162         this.dataManager = new DataManager(canvas);
163         this.textureManager = new ScilabTextureManager(this);
164         this.markManager = new MarkSpriteManager(canvas.getTextureManager());
165         this.textManager = new TextManager(canvas.getTextureManager());
166         this.labelManager = new LabelManager(canvas.getTextureManager());
167         this.axesDrawer = new AxesDrawer(this);
168         this.axisDrawer = new AxisDrawer(this);
169         this.arrowDrawer = new ArrowDrawer(this);
170         this.contouredObjectDrawer = new ContouredObjectDrawer(this, this.dataManager, this.markManager);
171         this.legendDrawer = new LegendDrawer(this);
172         this.fecDrawer = new FecDrawer(this);
173         this.colorMapTextureDataProvider = new ColorMapTextureDataProvider();
174
175         /*
176          * Forces font loading from the main thread. This is done because
177          * if getSciFontManager (thus, font loading) is concurrently accessed from
178          * 2 different threads (the AWT's and the main one), freezing may occur.
179          */
180         FontManager.getSciFontManager();
181
182         visitorMap.put(figure.getIdentifier(), this);
183     }
184
185     public static void changeVisitor(Figure figure, DrawerVisitor visitor) {
186         if (visitor == null) {
187             visitorMap.remove(figure.getIdentifier());
188         } else {
189             visitorMap.put(figure.getIdentifier(), visitor);
190         }
191     }
192
193     public void setDrawingTools(DrawingTools drawingTools) {
194         this.drawingTools = drawingTools;
195     }
196
197     public DrawingTools getDrawingTools() {
198         return drawingTools;
199     }
200
201     public Canvas getCanvas() {
202         return canvas;
203     }
204
205     public void setCanvas(Canvas canvas) {
206         this.canvas = canvas;
207     }
208
209     /**
210      * @return the DataManager
211      */
212     public DataManager getDataManager() {
213         return dataManager;
214     }
215
216     /**
217      * @return the TextManager
218      */
219     public TextManager getTextManager() {
220         return textManager;
221     }
222
223     /**
224      * Mark manager getter.
225      * @return the mark manager.
226      */
227     public MarkSpriteManager getMarkManager() {
228         return markManager;
229     }
230
231     /**
232      * @return the AxesDrawer
233      */
234     public AxesDrawer getAxesDrawer() {
235         return axesDrawer;
236     }
237
238     /**
239      * @return the ArrowDrawer
240      */
241     public ArrowDrawer getArrowDrawer() {
242         return arrowDrawer;
243     }
244
245     public ColorMap getColorMap() {
246         return colorMap;
247     }
248
249     /**
250      * Returns the visitor corresponding to the Figure identifier.
251      * @param figureId the figure identifier.
252      * @return the visitor.
253      */
254     public static DrawerVisitor getVisitor(String figureId) {
255         return visitorMap.get(figureId);
256     }
257
258     public void addPostRendering(PostRendered postRendered) {
259         if (postRendered != null) {
260             postRenderedList.add(postRendered);
261         }
262     }
263
264     public void removePostRendering(PostRendered postRendered) {
265         postRenderedList.remove(postRendered);
266     }
267
268     @Override
269     public void draw(DrawingTools drawingTools) {
270         this.drawingTools = drawingTools;
271         figure.accept(this);
272
273         for (PostRendered postRendered : postRenderedList) {
274             try {
275                 postRendered.draw(drawingTools);
276             } catch (SciRendererException e) {
277                 System.err.println("A 'PostRendered' is not drawable because: '" + e.getMessage() + "'");
278             }
279         }
280         drawingTools.getTransformationManager().useSceneCoordinate();
281     }
282
283     /**
284      * Ask the given object to accept visitor.
285      * @param childrenId array of object identifier.
286      */
287     public void askAcceptVisitor(String[] childrenId) {
288         if (childrenId != null) {
289
290             for (int i = childrenId.length - 1; i >= 0; --i) {
291                 GraphicObject child = GraphicController.getController().getObjectFromId(childrenId[i]);
292                 if (child != null) {
293                     try {
294                         child.accept(this);
295                     } catch (ObjectRemovedException e) {
296                         if (DEBUG_MODE) {
297                             System.err.println("[DEBUG] Try to draw an already removed object");
298                             System.err.println("[DEBUG] " + e);
299                             System.err.println("[DEBUG] Skipped...");
300                         }
301                     }
302                 }
303             }
304         }
305     }
306
307     /**
308      * @return true if it is a 2D view
309      */
310     public boolean is2DView() {
311         return currentAxes.getViewAsEnum() == ViewType.VIEW_2D;
312     }
313
314     @Override
315     public void visit(Axes axes) {
316         synchronized (axes) {
317             if (axes.isValid() && axes.getVisible()) {
318                 try {
319                     currentAxes = axes;
320                     axesDrawer.draw(axes);
321                 } catch (SciRendererException e) {
322                     invalidate(axes, e);
323                 }
324             }
325         }
326     }
327
328     @Override
329     public void visit(Arc arc) {
330         if (arc.isValid() && arc.getVisible()) {
331             axesDrawer.enableClipping(currentAxes, arc.getClipProperty());
332             try {
333                 contouredObjectDrawer.draw(arc, currentAxes.getViewAsEnum() == ViewType.VIEW_2D);
334             } catch (OutOfMemoryException e) {
335                 invalidate(arc, e);
336             } catch (SciRendererException e) {
337                 invalidate(arc, e);
338             } catch (ObjectRemovedException e) {
339                 invalidate(arc, e);
340             }
341             axesDrawer.disableClipping(arc.getClipProperty());
342         }
343     }
344
345     @Override
346     public void visit(Axis axis) {
347         if (axis.getVisible()) {
348             axesDrawer.enableClipping(currentAxes, axis.getClipProperty());
349             axisDrawer.draw(axis);
350             axesDrawer.disableClipping(axis.getClipProperty());
351         }
352     }
353
354     @Override
355     public void visit(Compound compound) {
356         if (compound.getVisible()) {
357             askAcceptVisitor(compound.getChildren());
358         }
359     }
360
361     @Override
362     public void visit(Fec fec) throws ObjectRemovedException {
363         if (fec.isValid() && fec.getVisible()) {
364             axesDrawer.enableClipping(currentAxes, fec.getClipProperty());
365             try {
366                 fecDrawer.draw(fec);
367             } catch (OutOfMemoryException e) {
368                 invalidate(fec, e);
369             }
370             axesDrawer.disableClipping(fec.getClipProperty());
371         }
372     }
373
374     @Override
375     public void visit(Figure figure) {
376         synchronized (figure) {
377             /** Set the current {@see ColorMap}. */
378             colorMap = figure.getColorMap();
379             drawingTools.clear(ColorFactory.createColor(colorMap, figure.getBackground()));
380             drawingTools.clearDepthBuffer();
381             if (figure.isValid() && figure.getVisible() && figure.getImmediateDrawing()) {
382                 askAcceptVisitor(figure.getChildren());
383             }
384         }
385     }
386
387     @Override
388     public void visit(final Grayplot grayplot) {
389         if (grayplot.isValid() && grayplot.getVisible()) {
390             axesDrawer.enableClipping(currentAxes, grayplot.getClipProperty());
391             try {
392                 DefaultGeometry triangles = new DefaultGeometry();
393                 triangles.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
394                 triangles.setVertices(dataManager.getVertexBuffer(grayplot.getIdentifier()));
395                 triangles.setColors(dataManager.getColorBuffer(grayplot.getIdentifier()));
396                 triangles.setIndices(dataManager.getIndexBuffer(grayplot.getIdentifier()));
397                 triangles.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
398                 Appearance trianglesAppearance = new Appearance();
399                 drawingTools.draw(triangles, trianglesAppearance);
400             } catch (ObjectRemovedException e) {
401                 invalidate(grayplot, e);
402             } catch (SciRendererException e) {
403                 invalidate(grayplot, e);
404             } catch (OutOfMemoryException e) {
405                 invalidate(grayplot, e);
406             }
407             axesDrawer.disableClipping(grayplot.getClipProperty());
408         }
409     }
410
411     @Override
412     public void visit(final Matplot matplot) {
413         if (matplot.isValid() && matplot.getVisible()) {
414             axesDrawer.enableClipping(currentAxes, matplot.getClipProperty());
415             try {
416                 if ((currentAxes != null) && (currentAxes.getXAxisLogFlag() || currentAxes.getYAxisLogFlag())) {
417                     DefaultGeometry geometry = new DefaultGeometry();
418                     geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
419                     geometry.setVertices(dataManager.getVertexBuffer(matplot.getIdentifier()));
420                     geometry.setColors(dataManager.getColorBuffer(matplot.getIdentifier()));
421                     geometry.setIndices(dataManager.getIndexBuffer(matplot.getIdentifier()));
422                     geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
423                     Appearance appearance = new Appearance();
424                     drawingTools.draw(geometry, appearance);
425                 } else {
426                     TransformationStack modelViewStack = drawingTools.getTransformationManager().getModelViewStack();
427                     Double[] scale = matplot.getScale();
428                     Double[] translate = matplot.getTranslate();
429                     Transformation t = TransformationFactory.getTranslateTransformation(translate[0], translate[1], 0);
430                     Transformation t2 = TransformationFactory.getScaleTransformation(scale[0], scale[1], 1);
431                     modelViewStack.pushRightMultiply(t);
432                     modelViewStack.pushRightMultiply(t2);
433                     drawingTools.draw(textureManager.getTexture(matplot.getIdentifier()));
434                     modelViewStack.pop();
435                     modelViewStack.pop();
436                 }
437             } catch (ObjectRemovedException e) {
438                 invalidate(matplot, e);
439             } catch (SciRendererException e) {
440                 invalidate(matplot, e);
441             } catch (OutOfMemoryException e) {
442                 invalidate(matplot, e);
443             }
444             axesDrawer.disableClipping(matplot.getClipProperty());
445         }
446     }
447
448     @Override
449     public void visit(Label label) {
450         if (label.isValid() && label.getVisible() && !label.isEmpty()) {
451             try {
452                 labelManager.draw(drawingTools, colorMap, label, axesDrawer);
453             } catch (SciRendererException e) {
454                 invalidate(label, e);
455             }
456         }
457     }
458
459     @Override
460     public void visit(Legend legend) {
461         if (legend.isValid() && legend.getVisible()) {
462             try {
463                 legendDrawer.draw(legend);
464             } catch (SciRendererException e) {
465                 invalidate(legend, e);
466             }
467         }
468     }
469
470     @Override
471     public void visit(final Polyline polyline) {
472         synchronized (polyline) {
473             if (polyline.isValid() && polyline.getVisible()) {
474                 axesDrawer.enableClipping(currentAxes, polyline.getClipProperty());
475                 try {
476                     DefaultGeometry geometry = new DefaultGeometry();
477
478                     geometry.setVertices(dataManager.getVertexBuffer(polyline.getIdentifier()));
479                     geometry.setIndices(dataManager.getIndexBuffer(polyline.getIdentifier()));
480                     geometry.setWireIndices(dataManager.getWireIndexBuffer(polyline.getIdentifier()));
481
482                     final int style = polyline.getPolylineStyle();
483                     if (style == 1 || style == 2 || style == 4 || style == 5) {
484                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS_STRIP);
485                     } else {
486                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
487                     }
488
489                     geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
490                     geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
491
492                     geometry.setPolygonOffsetMode(currentAxes.getCamera().getView() == ViewType.VIEW_3D);
493
494                     /* Interpolated color rendering is used only for basic polylines for now. */
495                     Appearance appearance = new Appearance();
496
497                     if (polyline.getInterpColorMode() && style == 1) {
498                         geometry.setTextureCoordinates(dataManager.getTextureCoordinatesBuffer(polyline.getIdentifier()));
499                         appearance.setTexture(getColorMapTexture());
500                     } else {
501                         geometry.setColors(null);
502                     }
503
504                     appearance.setLineColor(ColorFactory.createColor(colorMap, polyline.getLineColor()));
505                     appearance.setLineWidth(polyline.getLineThickness().floatValue());
506                     appearance.setLinePattern(polyline.getLineStyleAsEnum().asPattern());
507
508                     if (!polyline.getInterpColorMode() || style != 1) {
509                         int fillColor;
510
511                         /*
512                          * The line color is used as fill color for the filled patch polyline style
513                          * whereas the background color is used for all the other styles.
514                          */
515                         if (style == 5) {
516                             fillColor = polyline.getLineColor();
517                         } else {
518                             fillColor = polyline.getBackground();
519                         }
520
521                         appearance.setFillColor(ColorFactory.createColor(colorMap, fillColor));
522                     }
523
524                     drawingTools.draw(geometry, appearance);
525
526                     if (style == 4) {
527                         arrowDrawer.drawArrows(polyline.getParentAxes(), polyline.getIdentifier(), polyline.getArrowSizeFactor(),
528                                                polyline.getLineThickness(), false, false, polyline.getLineColor(), true);
529                     }
530
531                     if (polyline.getMarkMode()) {
532                         Texture sprite = markManager.getMarkSprite(polyline, colorMap);
533                         ElementsBuffer positions = dataManager.getVertexBuffer(polyline.getIdentifier());
534                         drawingTools.draw(sprite, AnchorPosition.CENTER, positions);
535                     }
536                 } catch (ObjectRemovedException e) {
537                     invalidate(polyline, e);
538                 } catch (OutOfMemoryException e) {
539                     invalidate(polyline, e);
540                 } catch (SciRendererException e) {
541                     invalidate(polyline, e);
542                 }
543                 axesDrawer.disableClipping(polyline.getClipProperty());
544             }
545         }
546     }
547
548     @Override
549     public void visit(Rectangle rectangle) {
550         if (rectangle.isValid() && rectangle.getVisible()) {
551             axesDrawer.enableClipping(currentAxes, rectangle.getClipProperty());
552             try {
553                 contouredObjectDrawer.draw(rectangle, currentAxes.getCamera().getView() == ViewType.VIEW_2D);
554             } catch (ObjectRemovedException e) {
555                 invalidate(rectangle, e);
556             } catch (OutOfMemoryException e) {
557                 invalidate(rectangle, e);
558             } catch (SciRendererException e) {
559                 invalidate(rectangle, e);
560             }
561             axesDrawer.disableClipping(rectangle.getClipProperty());
562         }
563     }
564
565     /*
566      * To do:
567      * -use common code for both the Fac3d and Plot3d visit methods
568      *  as they are mostly similar.
569      */
570     @Override
571     public void visit(final Fac3d fac3d) {
572         if (fac3d.isValid() && fac3d.getVisible()) {
573             axesDrawer.enableClipping(currentAxes, fac3d.getClipProperty());
574             try {
575                 if (fac3d.getSurfaceMode()) {
576                     DefaultGeometry geometry = new DefaultGeometry();
577                     geometry.setVertices(dataManager.getVertexBuffer(fac3d.getIdentifier()));
578                     geometry.setIndices(dataManager.getIndexBuffer(fac3d.getIdentifier()));
579
580                     geometry.setPolygonOffsetMode(true);
581
582                     /* Front-facing triangles */
583                     Appearance appearance = new Appearance();
584
585                     if (fac3d.getColorMode() != 0) {
586                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
587                         /* Back-facing triangles */
588                         if (fac3d.getHiddenColor() > 0) {
589                             geometry.setFaceCullingMode(axesDrawer.getBackFaceCullingMode());
590                             Appearance backTrianglesAppearance = new Appearance();
591                             backTrianglesAppearance.setFillColor(ColorFactory.createColor(colorMap, fac3d.getHiddenColor()));
592                             drawingTools.draw(geometry, backTrianglesAppearance);
593
594                             // Now we will draw front face.
595                             geometry.setFaceCullingMode(axesDrawer.getFrontFaceCullingMode());
596                         } else {
597                             geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
598                         }
599
600                         if (fac3d.getColorFlag() == 0) {
601                             appearance.setFillColor(ColorFactory.createColor(colorMap, Math.abs(fac3d.getColorMode())));
602                         } else if (fac3d.getColorFlag() > 0) {
603                             geometry.setTextureCoordinates(dataManager.getTextureCoordinatesBuffer(fac3d.getIdentifier()));
604                             appearance.setTexture(getColorMapTexture());
605                         } else {
606                             geometry.setColors(null);
607                         }
608                     } else {
609                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
610                     }
611
612                     if ((fac3d.getColorMode() >= 0) && (fac3d.getLineThickness() > 0.0)) {
613                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
614                         geometry.setWireIndices(dataManager.getWireIndexBuffer(fac3d.getIdentifier()));
615
616                         appearance.setLineColor(ColorFactory.createColor(colorMap, fac3d.getLineColor()));
617                         appearance.setLineWidth(fac3d.getLineThickness().floatValue());
618                     }
619
620                     drawingTools.draw(geometry, appearance);
621                 }
622
623                 if (fac3d.getMarkMode()) {
624                     Texture texture = markManager.getMarkSprite(fac3d, colorMap);
625                     ElementsBuffer positions = dataManager.getVertexBuffer(fac3d.getIdentifier());
626                     drawingTools.draw(texture, AnchorPosition.CENTER, positions);
627                 }
628             } catch (ObjectRemovedException e) {
629                 invalidate(fac3d, e);
630             } catch (OutOfMemoryException e) {
631                 invalidate(fac3d, e);
632             } catch (SciRendererException e) {
633                 invalidate(fac3d, e);
634             }
635             axesDrawer.disableClipping(fac3d.getClipProperty());
636         }
637
638     }
639
640     @Override
641     public void visit(final Plot3d plot3d) {
642         if (plot3d.isValid() && plot3d.getVisible()) {
643             axesDrawer.enableClipping(currentAxes, plot3d.getClipProperty());
644             try {
645                 if (plot3d.getSurfaceMode()) {
646                     DefaultGeometry geometry = new DefaultGeometry();
647                     if (plot3d.getColorMode() != 0) {
648                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
649                     } else {
650                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
651                     }
652
653                     geometry.setPolygonOffsetMode(true);
654
655                     geometry.setVertices(dataManager.getVertexBuffer(plot3d.getIdentifier()));
656                     geometry.setIndices(dataManager.getIndexBuffer(plot3d.getIdentifier()));
657                     /* Back-facing triangles */
658                     if (plot3d.getHiddenColor() > 0) {
659                         geometry.setFaceCullingMode(axesDrawer.getBackFaceCullingMode());
660                         Appearance backTrianglesAppearance = new Appearance();
661                         backTrianglesAppearance.setFillColor(ColorFactory.createColor(colorMap, plot3d.getHiddenColor()));
662                         drawingTools.draw(geometry, backTrianglesAppearance);
663                     }
664
665                     /* Front-facing triangles */
666                     Appearance appearance = new Appearance();
667
668
669                     if (plot3d.getColorFlag() == 1) {
670                         geometry.setColors(dataManager.getColorBuffer(plot3d.getIdentifier()));
671                     } else {
672                         geometry.setColors(null);
673                     }
674
675                     if (plot3d.getHiddenColor() > 0) {
676                         geometry.setFaceCullingMode(axesDrawer.getFrontFaceCullingMode());
677                     } else {
678                         geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
679                     }
680
681                     if (plot3d.getColorFlag() == 0) {
682                         appearance.setFillColor(ColorFactory.createColor(colorMap, Math.abs(plot3d.getColorMode())));
683                     }
684
685                     if ((plot3d.getColorMode() >= 0) && (plot3d.getLineThickness() > 0.0)) {
686                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
687                         geometry.setWireIndices(dataManager.getWireIndexBuffer(plot3d.getIdentifier()));
688
689                         appearance.setLinePattern(plot3d.getLineStyleAsEnum().asPattern());
690                         appearance.setLineColor(ColorFactory.createColor(colorMap, plot3d.getLineColor()));
691                         appearance.setLineWidth(plot3d.getLineThickness().floatValue());
692                     }
693
694                     drawingTools.draw(geometry, appearance);
695                 }
696
697                 if (plot3d.getMarkMode()) {
698                     Texture texture = markManager.getMarkSprite(plot3d, colorMap);
699                     ElementsBuffer positions = dataManager.getVertexBuffer(plot3d.getIdentifier());
700                     drawingTools.draw(texture, AnchorPosition.CENTER, positions);
701                 }
702             } catch (ObjectRemovedException e) {
703                 invalidate(plot3d, e);
704             } catch (OutOfMemoryException e) {
705                 invalidate(plot3d, e);
706             } catch (SciRendererException e) {
707                 invalidate(plot3d, e);
708             }
709             axesDrawer.disableClipping(plot3d.getClipProperty());
710         }
711
712     }
713
714     @Override
715     public void visit(Text text) {
716         if (text.isValid() && text.getVisible()) {
717             axesDrawer.enableClipping(currentAxes, text.getClipProperty());
718             try {
719                 textManager.draw(drawingTools, colorMap, text);
720             } catch (SciRendererException e) {
721                 invalidate(text, e);
722             }
723             axesDrawer.disableClipping(text.getClipProperty());
724         }
725     }
726
727     @Override
728     public void visit(Arrow arrow) {
729         // TODO
730         System.out.println("How can I draw an arrow ?");
731     }
732
733     @Override
734     public void visit(final Champ champ) {
735         if (champ.isValid() && champ.getVisible()) {
736             axesDrawer.enableClipping(currentAxes, champ.getClipProperty());
737             try {
738                 DefaultGeometry segments = new DefaultGeometry();
739                 segments.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
740                 segments.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
741                 segments.setVertices(dataManager.getVertexBuffer(champ.getIdentifier()));
742                 segments.setWireIndices(dataManager.getWireIndexBuffer(champ.getIdentifier()));
743                 segments.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
744                 if (champ.getColored()) {
745                     segments.setColors(dataManager.getColorBuffer(champ.getIdentifier()));
746                 } else {
747                     segments.setColors(null);
748                 }
749
750                 if (champ.getLineMode()) {
751                     Appearance segmentAppearance = new Appearance();
752
753                     /* If not colored, all segments have the same color. */
754                     if (champ.getColored()) {
755                         segmentAppearance.setLineColor(null);
756                     } else {
757                         segmentAppearance.setLineColor(ColorFactory.createColor(colorMap, champ.getLineColor()));
758                     }
759
760                     segmentAppearance.setLineWidth(champ.getLineThickness().floatValue());
761                     segmentAppearance.setLinePattern(champ.getLineStyleAsEnum().asPattern());
762                     drawingTools.draw(segments, segmentAppearance);
763                 }
764
765                 /* Draw the arrows */
766                 if (champ.getArrowSize() != 0.0) {
767                     arrowDrawer.drawArrows(champ.getParentAxes(), champ.getIdentifier(), champ.getArrowSize(), champ.getLineThickness(), false,
768                                            champ.getColored(), champ.getLineColor(), false);
769                 }
770             } catch (OutOfMemoryException e) {
771                 invalidate(champ, e);
772             } catch (ObjectRemovedException e) {
773                 invalidate(champ, e);
774             } catch (SciRendererException e) {
775                 invalidate(champ, e);
776             }
777             axesDrawer.disableClipping(champ.getClipProperty());
778         }
779     }
780
781     @Override
782     public void visit(final Segs segs) {
783         if (segs.isValid() && segs.getVisible() && segs.getArrows().size() != 0) {
784             axesDrawer.enableClipping(currentAxes, segs.getClipProperty());
785             try {
786                 DefaultGeometry segments = new DefaultGeometry();
787                 segments.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
788                 segments.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
789                 segments.setVertices(dataManager.getVertexBuffer(segs.getIdentifier()));
790                 segments.setColors(dataManager.getColorBuffer(segs.getIdentifier()));
791                 segments.setWireIndices(dataManager.getWireIndexBuffer(segs.getIdentifier()));
792                 segments.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
793
794                 if (segs.getLineMode()) {
795                     Appearance segmentAppearance = new Appearance();
796                     segmentAppearance.setLineColor(null);
797                     segmentAppearance.setLineWidth(segs.getLineThickness().floatValue());
798                     segmentAppearance.setLinePattern(segs.getLineStyleAsEnum().asPattern());
799                     drawingTools.draw(segments, segmentAppearance);
800                 }
801
802                 /*
803                  * Segs does not derive from ContouredObject but Arrow does, hence we have to get the former's first Arrow
804                  * in order to obtain the latter's Mark (all arrows are supposed to have the same contour properties for now).
805                  */
806                 if (segs.getMarkMode()) {
807                     Texture texture = markManager.getMarkSprite(segs.getIdentifier(), segs.getArrows().get(0).getMark(), colorMap);
808                     ElementsBuffer positions = dataManager.getVertexBuffer(segs.getIdentifier());
809                     // Take only into account start-end of segs and not the arrow head.
810                     positions.getData().limit(segs.getNumberArrows() * 2 * 4);
811                     drawingTools.draw(texture, AnchorPosition.CENTER, positions);
812                     positions.getData().limit(positions.getData().capacity());
813                 }
814
815                 /* Draw the arrows */
816                 if (segs.getArrowSize() != 0.0) {
817                     arrowDrawer.drawArrows(segs.getParentAxes(), segs.getIdentifier(), segs.getArrowSize(), segs.getLineThickness(), true,
818                                            true, segs.getLineColor(), false);
819                 }
820             } catch (OutOfMemoryException e) {
821                 invalidate(segs, e);
822             } catch (ObjectRemovedException e) {
823                 invalidate(segs, e);
824             } catch (SciRendererException e) {
825                 invalidate(segs, e);
826             }
827             axesDrawer.disableClipping(segs.getClipProperty());
828         }
829     }
830
831     @Override
832     public void updateObject(String id, int property) {
833         try {
834             if (needUpdate(id, property)) {
835                 if (GraphicObjectProperties.__GO_COLORMAP__ == property && figure.getIdentifier().equals(id)) {
836                     labelManager.disposeAll();
837                     dataManager.disposeAllColorBuffers();
838                     dataManager.disposeAllTextureCoordinatesBuffers();
839                     markManager.disposeAll();
840                     textManager.disposeAll();
841                     axesDrawer.disposeAll();
842                     fecDrawer.updateAll();
843                     colorMapTextureDataProvider.update();
844                     textureManager.disposeAll();
845                 } else {
846                     labelManager.update(id, property);
847                     dataManager.update(id, property);
848                     markManager.update(id, property);
849                     textManager.update(id, property);
850                     axesDrawer.update(id, property);
851                     legendDrawer.update(id, property);
852                     fecDrawer.update(id, property);
853                 }
854
855                 if (GraphicObjectProperties.__GO_ANTIALIASING__ == property) {
856                     canvas.setAntiAliasingLevel(figure.getAntialiasing());
857                 }
858
859                 if (isImmediateDrawing(id)) {
860                     if (GraphicObjectProperties.__GO_IMMEDIATE_DRAWING__ == property) {
861                         canvas.redrawAndWait();
862                     } else {
863                         canvas.redraw();
864                     }
865                 }
866             }
867
868             if (GraphicObjectProperties.__GO_IMMEDIATE_DRAWING__ == property && !isImmediateDrawing(id)) {
869                 canvas.waitImage();
870             }
871
872         } catch (OutOfMemoryException e) {
873             invalidate(GraphicController.getController().getObjectFromId(id), e);
874         } catch (ObjectRemovedException e) {
875             // Object has been removed before draw : do nothing.
876         }
877     }
878
879     /**
880      * Check if the given changed property make the figure out of date.
881      * @param id the object updated
882      * @param property the changed property.
883      * @return true id the given changed property make the figure out of date.
884      */
885     private boolean needUpdate(String id, int property) {
886         GraphicObject object = GraphicController.getController().getObjectFromId(id);
887         int objectType = (Integer) GraphicController.getController().getProperty(id, GraphicObjectProperties.__GO_TYPE__);
888         if ((object != null) && isFigureChild(id)
889                 && objectType != GraphicObjectProperties.__GO_UICONTROL__
890                 && objectType != GraphicObjectProperties.__GO_UIMENU__) {
891
892             if (GraphicObjectProperties.__GO_VALID__ == property) {
893                 return false;
894             }
895
896             if (object instanceof Axes) {
897                 Axes axes = (Axes) object;
898
899                 if (axes.getXAxisAutoTicks() && X_AXIS_TICKS_PROPERTIES.contains(property)) {
900                     return false;
901                 }
902
903                 if (axes.getYAxisAutoTicks() && Y_AXIS_TICKS_PROPERTIES.contains(property)) {
904                     return false;
905                 }
906
907                 if (axes.getZAxisAutoTicks() && Z_AXIS_TICKS_PROPERTIES.contains(property)) {
908                     return false;
909                 }
910
911                 if (property != GraphicObjectProperties.__GO_CHILDREN__) {
912                     axesDrawer.computeRulers(axes);
913                 }
914             }
915
916             if (object instanceof Figure) {
917                 if (property == GraphicObjectProperties.__GO_SIZE__ || property == GraphicObjectProperties.__GO_AXES_SIZE__ || property == GraphicObjectProperties.__GO_CHILDREN__) {
918                     Figure fig = (Figure) object;
919                     for (String gid : fig.getChildren()) {
920                         GraphicObject go = GraphicController.getController().getObjectFromId(gid);
921                         if (go instanceof Axes) {
922                             axesDrawer.computeRulers((Axes) go);
923                         }
924                     }
925                 }
926
927                 if (SILENT_FIGURE_PROPERTIES.contains(property)) {
928                     return false;
929                 }
930             }
931
932             if (!object.isValid()) {
933                 GraphicController.getController().setProperty(id, GraphicObjectProperties.__GO_VALID__, true);
934             }
935
936             return true;
937         } else {
938             return false;
939         }
940     }
941
942     private boolean isImmediateDrawing(String id) {
943         String parentId = (String) GraphicController.getController().getProperty(id, GraphicObjectProperties.__GO_PARENT_FIGURE__);
944         if (parentId == null || !parentId.equals(figure.getIdentifier())) {
945             return false;
946         } else {
947             Boolean b =  (Boolean) GraphicController.getController().getProperty(parentId, GraphicObjectProperties.__GO_IMMEDIATE_DRAWING__);
948             return (b == null) ? false : b;
949         }
950     }
951
952     @Override
953     public void createObject(String id) {
954     }
955
956     @Override
957     public void deleteObject(String id) {
958         if (isImmediateDrawing(id)) {
959             canvas.redraw();
960         }
961
962         dataManager.dispose(id);
963         markManager.dispose(id);
964         textManager.dispose(id);
965         labelManager.dispose(id);
966         axesDrawer.dispose(id);
967         legendDrawer.dispose(id);
968         fecDrawer.dispose(id);
969         textureManager.dispose(id);
970         /*
971          * Check we are deleting Figure managed by DrawerVisitor(this)
972          * Otherwise do nothing on deletion.
973          */
974         if (!figure.getIdentifier().equals(id)) {
975             return;
976         }
977
978         visitorMap.remove(id);
979         GraphicController.getController().unregister(this);
980         if (SwingUtilities.isEventDispatchThread()) {
981             canvas.destroy();
982         } else {
983             try {
984                 SwingUtilities.invokeAndWait(new Runnable() {
985                     public void run() {
986                         canvas.destroy();
987                     }
988                 });
989             } catch (Exception e) { }
990         }
991     }
992
993     /**
994      * Check if the given id correspond to a child of the current {@see Figure}.
995      * @param id the given id.
996      * @return true if the given id correspond to a child of the current {@see Figure}.
997      */
998     private boolean isFigureChild(String id) {
999         String parentFigureID = (String) GraphicController.getController().getProperty(id, GraphicObjectProperties.__GO_PARENT_FIGURE__);
1000         return figure.getIdentifier().equals(parentFigureID);
1001     }
1002
1003     /**
1004      * Invalidate the given graphic object and inform the user.
1005      * @param graphicObject the graphic object to invalidate
1006      * @param exception the cause of invalidation.
1007      */
1008     public void invalidate(GraphicObject graphicObject, Exception exception) {
1009         if (DEBUG_MODE) {
1010             System.err.println("The " + graphicObject.getType() + " \"" + graphicObject.getIdentifier()
1011                                + "\" has been invalidated: " + exception.getMessage());
1012             exception.printStackTrace();
1013         }
1014         GraphicController.getController().setProperty(graphicObject.getIdentifier(), GraphicObjectProperties.__GO_VALID__, false);
1015     }
1016
1017     public LabelManager getLabelManager() {
1018         return labelManager;
1019     }
1020
1021     public Texture getColorMapTexture() {
1022         if (colorMapTexture == null) {
1023             colorMapTexture = canvas.getTextureManager().createTexture();
1024             colorMapTexture.setMagnificationFilter(Texture.Filter.NEAREST);
1025             colorMapTexture.setMinifyingFilter(Texture.Filter.NEAREST);
1026             colorMapTexture.setSWrappingMode(Texture.Wrap.CLAMP);
1027             colorMapTexture.setTWrappingMode(Texture.Wrap.CLAMP);
1028             colorMapTexture.setDataProvider(colorMapTextureDataProvider);
1029         }
1030         return colorMapTexture;
1031     }
1032
1033     /**
1034      * Figure getter.
1035      * @return the figure this visitor draw.
1036      */
1037     public Figure getFigure() {
1038         return figure;
1039     }
1040
1041     private Geometry cube;
1042     public Geometry getCube() {
1043         if (cube == null) {
1044             cube = CubeFactory.createCube(canvas);
1045         }
1046         return cube;
1047     }
1048
1049     /**
1050      * Component getter.
1051      * @return return the attached component.
1052      */
1053     public Component getComponent() {
1054         return component;
1055     }
1056
1057     /**
1058      * Interaction manager getter
1059      * @return the interaction manager.
1060      */
1061     public InteractionManager getInteractionManager() {
1062         return interactionManager;
1063     }
1064
1065     private class ColorMapTextureDataProvider extends AbstractTextureDataProvider {
1066         byte[] whiteColor = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
1067         byte[] blackColor = {0x00, 0x00, 0x00, (byte) 0xFF};
1068
1069         @Override
1070         public Dimension getTextureSize() {
1071             return new Dimension(colorMap.getSize() + 2, 1);
1072         }
1073
1074         @Override
1075         public ByteBuffer getData() {
1076             Double[] data = colorMap.getData();
1077             ByteBuffer buffer = ByteBuffer.allocate(4 * ((data.length / 3) + 2));
1078
1079             /* White and black are written in the first and second positions */
1080             buffer.put(whiteColor);
1081             buffer.put(blackColor);
1082
1083             for (int i = 0 ; i < data.length / 3 ; i++) {
1084                 buffer.put(toByte(data[i]));
1085                 buffer.put(toByte(data[i + colorMap.getSize()].floatValue()));
1086                 buffer.put(toByte(data[i + 2 * colorMap.getSize()].floatValue()));
1087                 buffer.put(toByte(1));
1088             }
1089             buffer.rewind();
1090             return buffer;
1091         }
1092
1093         @Override
1094         public ByteBuffer getSubData(int x, int y, int width, int height) {
1095             /*
1096              * For the moment, we presuppose that x and y are 0 and that
1097              * width is equal to the colormap's total size (with height == 1).
1098              * To be correctly implemented.
1099              */
1100             return getData();
1101         }
1102
1103         @Override
1104         public boolean isValid() {
1105             return true;
1106         }
1107
1108         public void update() {
1109             fireUpdate();
1110         }
1111     }
1112 }