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