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