Bug 12289 fixed: sub_ticks property was ignored
[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             ));
91
92     /** Set of properties changed during a draw if auto-ticks is on for Y axis. */
93     private static final Set<Integer> Y_AXIS_TICKS_PROPERTIES = new HashSet<Integer>(Arrays.asList(
94                 GraphicObjectProperties.__GO_Y_AXIS_TICKS_LOCATIONS__,
95                 GraphicObjectProperties.__GO_Y_AXIS_TICKS_LABELS__
96             ));
97
98     /** Set of properties changed during a draw if auto-ticks is on for Z axis. */
99     private static final Set<Integer> Z_AXIS_TICKS_PROPERTIES = new HashSet<Integer>(Arrays.asList(
100                 GraphicObjectProperties.__GO_Z_AXIS_TICKS_LOCATIONS__,
101                 GraphicObjectProperties.__GO_Z_AXIS_TICKS_LABELS__
102             ));
103
104     /** Set of figure properties for witch a change doesn't lead to a redraw */
105     private static final Set<Integer> SILENT_FIGURE_PROPERTIES = new HashSet<Integer>(Arrays.asList(
106                 GraphicObjectProperties.__GO_ROTATION_TYPE__,
107                 GraphicObjectProperties.__GO_INFO_MESSAGE__,
108                 GraphicObjectProperties.__GO_FIGURE_NAME__,
109                 GraphicObjectProperties.__GO_AUTORESIZE__,
110                 GraphicObjectProperties.__GO_POSITION__,
111                 GraphicObjectProperties.__GO_SIZE__,
112                 GraphicObjectProperties.__GO_ID__
113             ));
114
115     private static final boolean DEBUG_MODE = false;
116
117     private final Component component;
118     private final Figure figure;
119     private final InteractionManager interactionManager;
120
121     private final ColorMapTextureDataProvider colorMapTextureDataProvider;
122
123     private final ScilabTextureManager textureManager;
124     private final MarkSpriteManager markManager;
125     private final LabelManager labelManager;
126     private final DataManager dataManager;
127     private final TextManager textManager;
128
129     private final ContouredObjectDrawer contouredObjectDrawer;
130     private final LegendDrawer legendDrawer;
131     private final AxesDrawer axesDrawer;
132     private final AxisDrawer axisDrawer;
133     private final ArrowDrawer arrowDrawer;
134     private final FecDrawer fecDrawer;
135
136     private DrawingTools drawingTools;
137     private Texture colorMapTexture;
138     private ColorMap colorMap;
139
140     private Axes currentAxes;
141     private Canvas canvas;
142
143     /**
144      * The map between the existing Figures' identifiers and their corresponding Visitor.
145      * Used to get access to the DrawerVisitor corresponding to a given Figure when the
146      * renderer module is accessed from another thread than the AWT's.
147      */
148     private final static Map<String, DrawerVisitor> visitorMap = new HashMap<String, DrawerVisitor>();
149     private final List<PostRendered> postRenderedList = new LinkedList<PostRendered>();
150
151     public DrawerVisitor(Component component, Canvas canvas, Figure figure) {
152         GraphicController.getController().register(this);
153
154         this.component = component;
155         this.canvas = canvas;
156         this.figure = figure;
157
158         this.interactionManager = new InteractionManager(this);
159         this.dataManager = new DataManager(canvas);
160         this.textureManager = new ScilabTextureManager(this);
161         this.markManager = new MarkSpriteManager(canvas.getTextureManager());
162         this.textManager = new TextManager(canvas.getTextureManager());
163         this.labelManager = new LabelManager(canvas.getTextureManager());
164         this.axesDrawer = new AxesDrawer(this);
165         this.axisDrawer = new AxisDrawer(this);
166         this.arrowDrawer = new ArrowDrawer(this);
167         this.contouredObjectDrawer = new ContouredObjectDrawer(this, this.dataManager, this.markManager);
168         this.legendDrawer = new LegendDrawer(this);
169         this.fecDrawer = new FecDrawer(this);
170         this.colorMapTextureDataProvider = new ColorMapTextureDataProvider();
171
172         /*
173          * Forces font loading from the main thread. This is done because
174          * if getSciFontManager (thus, font loading) is concurrently accessed from
175          * 2 different threads (the AWT's and the main one), freezing may occur.
176          */
177         FontManager.getSciFontManager();
178
179         visitorMap.put(figure.getIdentifier(), this);
180     }
181
182     public static void changeVisitor(Figure figure, DrawerVisitor visitor) {
183         if (visitor == null) {
184             visitorMap.remove(figure.getIdentifier());
185         } else {
186             visitorMap.put(figure.getIdentifier(), visitor);
187         }
188     }
189
190     public void setDrawingTools(DrawingTools drawingTools) {
191         this.drawingTools = drawingTools;
192     }
193
194     public DrawingTools getDrawingTools() {
195         return drawingTools;
196     }
197
198     public Canvas getCanvas() {
199         return canvas;
200     }
201
202     public void setCanvas(Canvas canvas) {
203         this.canvas = 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      * 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             } catch (Exception e) {
405                 System.err.println(e);
406                 e.printStackTrace();
407             }
408             axesDrawer.disableClipping(grayplot.getClipProperty());
409         }
410     }
411
412     @Override
413     public void visit(final Matplot matplot) {
414         if (matplot.isValid() && matplot.getVisible()) {
415             axesDrawer.enableClipping(currentAxes, matplot.getClipProperty());
416             try {
417                 if ((currentAxes != null) && (currentAxes.getXAxisLogFlag() || currentAxes.getYAxisLogFlag())) {
418                     DefaultGeometry geometry = new DefaultGeometry();
419                     geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
420                     geometry.setVertices(dataManager.getVertexBuffer(matplot.getIdentifier()));
421                     geometry.setColors(dataManager.getColorBuffer(matplot.getIdentifier()));
422                     geometry.setIndices(dataManager.getIndexBuffer(matplot.getIdentifier()));
423                     geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
424                     Appearance appearance = new Appearance();
425                     drawingTools.draw(geometry, appearance);
426                 } else {
427                     TransformationStack modelViewStack = drawingTools.getTransformationManager().getModelViewStack();
428                     Double[] scale = matplot.getScale();
429                     Double[] translate = matplot.getTranslate();
430                     Transformation t = TransformationFactory.getTranslateTransformation(translate[0], translate[1], 0);
431                     Transformation t2 = TransformationFactory.getScaleTransformation(scale[0], scale[1], 1);
432                     modelViewStack.pushRightMultiply(t);
433                     modelViewStack.pushRightMultiply(t2);
434                     drawingTools.draw(textureManager.getTexture(matplot.getIdentifier()));
435                     modelViewStack.pop();
436                     modelViewStack.pop();
437                 }
438             } catch (ObjectRemovedException e) {
439                 invalidate(matplot, e);
440             } catch (SciRendererException e) {
441                 invalidate(matplot, e);
442             } catch (OutOfMemoryException e) {
443                 invalidate(matplot, e);
444             }
445             axesDrawer.disableClipping(matplot.getClipProperty());
446         }
447     }
448
449     @Override
450     public void visit(Label label) {
451         if (label.isValid() && label.getVisible() && !label.isEmpty()) {
452             try {
453                 labelManager.draw(drawingTools, colorMap, label, axesDrawer);
454             } catch (SciRendererException e) {
455                 invalidate(label, e);
456             }
457         }
458     }
459
460     @Override
461     public void visit(Legend legend) {
462         if (legend.isValid() && legend.getVisible()) {
463             try {
464                 legendDrawer.draw(legend);
465             } catch (SciRendererException e) {
466                 invalidate(legend, e);
467             }
468         }
469     }
470
471     @Override
472     public void visit(final Polyline polyline) {
473         synchronized (polyline) {
474             if (polyline.isValid() && polyline.getVisible()) {
475                 axesDrawer.enableClipping(currentAxes, polyline.getClipProperty());
476                 try {
477                     DefaultGeometry geometry = new DefaultGeometry();
478
479                     geometry.setVertices(dataManager.getVertexBuffer(polyline.getIdentifier()));
480                     geometry.setIndices(dataManager.getIndexBuffer(polyline.getIdentifier()));
481                     geometry.setWireIndices(dataManager.getWireIndexBuffer(polyline.getIdentifier()));
482
483                     final int style = polyline.getPolylineStyle();
484                     if (style == 1 || style == 2 || style == 4 || style == 5) {
485                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS_STRIP);
486                     } else {
487                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
488                     }
489
490                     geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
491                     geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
492
493                     geometry.setPolygonOffsetMode(currentAxes.getCamera().getView() == ViewType.VIEW_3D);
494
495                     /* Interpolated color rendering is used only for basic polylines for now. */
496                     Appearance appearance = new Appearance();
497
498                     if (polyline.getInterpColorMode() && style == 1) {
499                         geometry.setTextureCoordinates(dataManager.getTextureCoordinatesBuffer(polyline.getIdentifier()));
500                         appearance.setTexture(getColorMapTexture());
501                     } else {
502                         geometry.setColors(null);
503                     }
504
505                     appearance.setLineColor(ColorFactory.createColor(colorMap, polyline.getLineColor()));
506                     appearance.setLineWidth(polyline.getLineThickness().floatValue());
507                     appearance.setLinePattern(polyline.getLineStyleAsEnum().asPattern());
508
509                     if (!polyline.getInterpColorMode() || style != 1) {
510                         int fillColor;
511
512                         /*
513                          * The line color is used as fill color for the filled patch polyline style
514                          * whereas the background color is used for all the other styles.
515                          */
516                         if (style == 5) {
517                             fillColor = polyline.getLineColor();
518                         } else {
519                             fillColor = polyline.getBackground();
520                         }
521
522                         appearance.setFillColor(ColorFactory.createColor(colorMap, fillColor));
523                     }
524
525                     drawingTools.draw(geometry, appearance);
526
527                     if (style == 4) {
528                         arrowDrawer.drawArrows(polyline.getParentAxes(), polyline.getIdentifier(), polyline.getArrowSizeFactor(),
529                                                polyline.getLineThickness(), false, false, polyline.getLineColor(), true);
530                     }
531
532                     if (polyline.getMarkMode()) {
533                         Texture sprite = markManager.getMarkSprite(polyline, colorMap);
534                         ElementsBuffer positions = dataManager.getVertexBuffer(polyline.getIdentifier());
535                         drawingTools.draw(sprite, AnchorPosition.CENTER, positions);
536                     }
537                 } catch (ObjectRemovedException e) {
538                     invalidate(polyline, e);
539                 } catch (OutOfMemoryException e) {
540                     invalidate(polyline, e);
541                 } catch (SciRendererException e) {
542                     invalidate(polyline, e);
543                 }
544                 axesDrawer.disableClipping(polyline.getClipProperty());
545             }
546         }
547     }
548
549     @Override
550     public void visit(Rectangle rectangle) {
551         if (rectangle.isValid() && rectangle.getVisible()) {
552             axesDrawer.enableClipping(currentAxes, rectangle.getClipProperty());
553             try {
554                 contouredObjectDrawer.draw(rectangle, currentAxes.getCamera().getView() == ViewType.VIEW_2D);
555             } catch (ObjectRemovedException e) {
556                 invalidate(rectangle, e);
557             } catch (OutOfMemoryException e) {
558                 invalidate(rectangle, e);
559             } catch (SciRendererException e) {
560                 invalidate(rectangle, e);
561             }
562             axesDrawer.disableClipping(rectangle.getClipProperty());
563         }
564     }
565
566     /*
567      * To do:
568      * -use common code for both the Fac3d and Plot3d visit methods
569      *  as they are mostly similar.
570      */
571     @Override
572     public void visit(final Fac3d fac3d) {
573         if (fac3d.isValid() && fac3d.getVisible()) {
574             axesDrawer.enableClipping(currentAxes, fac3d.getClipProperty());
575             try {
576                 if (fac3d.getSurfaceMode()) {
577                     DefaultGeometry geometry = new DefaultGeometry();
578                     geometry.setVertices(dataManager.getVertexBuffer(fac3d.getIdentifier()));
579                     geometry.setIndices(dataManager.getIndexBuffer(fac3d.getIdentifier()));
580
581                     geometry.setPolygonOffsetMode(true);
582
583                     /* Front-facing triangles */
584                     Appearance appearance = new Appearance();
585
586                     if (fac3d.getColorMode() != 0) {
587                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
588                         /* Back-facing triangles */
589                         if (fac3d.getHiddenColor() > 0) {
590                             geometry.setFaceCullingMode(axesDrawer.getBackFaceCullingMode());
591                             Appearance backTrianglesAppearance = new Appearance();
592                             backTrianglesAppearance.setFillColor(ColorFactory.createColor(colorMap, fac3d.getHiddenColor()));
593                             drawingTools.draw(geometry, backTrianglesAppearance);
594
595                             // Now we will draw front face.
596                             geometry.setFaceCullingMode(axesDrawer.getFrontFaceCullingMode());
597                         } else {
598                             geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
599                         }
600
601                         if (fac3d.getColorFlag() == 0) {
602                             appearance.setFillColor(ColorFactory.createColor(colorMap, Math.abs(fac3d.getColorMode())));
603                         } else if (fac3d.getColorFlag() > 0) {
604                             geometry.setTextureCoordinates(dataManager.getTextureCoordinatesBuffer(fac3d.getIdentifier()));
605                             appearance.setTexture(getColorMapTexture());
606                         } else {
607                             geometry.setColors(null);
608                         }
609                     } else {
610                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
611                     }
612
613                     if ((fac3d.getColorMode() >= 0) && (fac3d.getLineThickness() > 0.0)) {
614                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
615                         geometry.setWireIndices(dataManager.getWireIndexBuffer(fac3d.getIdentifier()));
616
617                         appearance.setLineColor(ColorFactory.createColor(colorMap, fac3d.getLineColor()));
618                         appearance.setLineWidth(fac3d.getLineThickness().floatValue());
619                     }
620
621                     drawingTools.draw(geometry, appearance);
622                 }
623
624                 if (fac3d.getMarkMode()) {
625                     Texture texture = markManager.getMarkSprite(fac3d, colorMap);
626                     ElementsBuffer positions = dataManager.getVertexBuffer(fac3d.getIdentifier());
627                     drawingTools.draw(texture, AnchorPosition.CENTER, positions);
628                 }
629             } catch (ObjectRemovedException e) {
630                 invalidate(fac3d, e);
631             } catch (OutOfMemoryException e) {
632                 invalidate(fac3d, e);
633             } catch (SciRendererException e) {
634                 invalidate(fac3d, e);
635             }
636             axesDrawer.disableClipping(fac3d.getClipProperty());
637         }
638
639     }
640
641     @Override
642     public void visit(final Plot3d plot3d) {
643         if (plot3d.isValid() && plot3d.getVisible()) {
644             axesDrawer.enableClipping(currentAxes, plot3d.getClipProperty());
645             try {
646                 if (plot3d.getSurfaceMode()) {
647                     DefaultGeometry geometry = new DefaultGeometry();
648                     if (plot3d.getColorMode() != 0) {
649                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
650                     } else {
651                         geometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
652                     }
653
654                     geometry.setPolygonOffsetMode(true);
655
656                     geometry.setVertices(dataManager.getVertexBuffer(plot3d.getIdentifier()));
657                     geometry.setIndices(dataManager.getIndexBuffer(plot3d.getIdentifier()));
658                     /* Back-facing triangles */
659                     if (plot3d.getHiddenColor() > 0) {
660                         geometry.setFaceCullingMode(axesDrawer.getBackFaceCullingMode());
661                         Appearance backTrianglesAppearance = new Appearance();
662                         backTrianglesAppearance.setFillColor(ColorFactory.createColor(colorMap, plot3d.getHiddenColor()));
663                         drawingTools.draw(geometry, backTrianglesAppearance);
664                     }
665
666                     /* Front-facing triangles */
667                     Appearance appearance = new Appearance();
668
669
670                     if (plot3d.getColorFlag() == 1) {
671                         geometry.setColors(dataManager.getColorBuffer(plot3d.getIdentifier()));
672                     } else {
673                         geometry.setColors(null);
674                     }
675
676                     if (plot3d.getHiddenColor() > 0) {
677                         geometry.setFaceCullingMode(axesDrawer.getFrontFaceCullingMode());
678                     } else {
679                         geometry.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
680                     }
681
682                     if (plot3d.getColorFlag() == 0) {
683                         appearance.setFillColor(ColorFactory.createColor(colorMap, Math.abs(plot3d.getColorMode())));
684                     }
685
686                     if ((plot3d.getColorMode() >= 0) && (plot3d.getLineThickness() > 0.0)) {
687                         geometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
688                         geometry.setWireIndices(dataManager.getWireIndexBuffer(plot3d.getIdentifier()));
689
690                         appearance.setLinePattern(plot3d.getLineStyleAsEnum().asPattern());
691                         appearance.setLineColor(ColorFactory.createColor(colorMap, plot3d.getLineColor()));
692                         appearance.setLineWidth(plot3d.getLineThickness().floatValue());
693                     }
694
695                     drawingTools.draw(geometry, appearance);
696                 }
697
698                 if (plot3d.getMarkMode()) {
699                     Texture texture = markManager.getMarkSprite(plot3d, colorMap);
700                     ElementsBuffer positions = dataManager.getVertexBuffer(plot3d.getIdentifier());
701                     drawingTools.draw(texture, AnchorPosition.CENTER, positions);
702                 }
703             } catch (ObjectRemovedException e) {
704                 invalidate(plot3d, e);
705             } catch (OutOfMemoryException e) {
706                 invalidate(plot3d, e);
707             } catch (SciRendererException e) {
708                 invalidate(plot3d, e);
709             }
710             axesDrawer.disableClipping(plot3d.getClipProperty());
711         }
712
713     }
714
715     @Override
716     public void visit(Text text) {
717         if (text.isValid() && text.getVisible()) {
718             axesDrawer.enableClipping(currentAxes, text.getClipProperty());
719             try {
720                 textManager.draw(drawingTools, colorMap, text);
721             } catch (SciRendererException e) {
722                 invalidate(text, e);
723             }
724             axesDrawer.disableClipping(text.getClipProperty());
725         }
726     }
727
728     @Override
729     public void visit(Arrow arrow) {
730         // TODO
731         System.out.println("How can I draw an arrow ?");
732     }
733
734     @Override
735     public void visit(final Champ champ) {
736         if (champ.isValid() && champ.getVisible()) {
737             axesDrawer.enableClipping(currentAxes, champ.getClipProperty());
738             try {
739                 DefaultGeometry segments = new DefaultGeometry();
740                 segments.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
741                 segments.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
742                 segments.setVertices(dataManager.getVertexBuffer(champ.getIdentifier()));
743                 segments.setWireIndices(dataManager.getWireIndexBuffer(champ.getIdentifier()));
744                 segments.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
745                 if (champ.getColored()) {
746                     segments.setColors(dataManager.getColorBuffer(champ.getIdentifier()));
747                 } else {
748                     segments.setColors(null);
749                 }
750
751                 if (champ.getLineMode()) {
752                     Appearance segmentAppearance = new Appearance();
753
754                     /* If not colored, all segments have the same color. */
755                     if (champ.getColored()) {
756                         segmentAppearance.setLineColor(null);
757                     } else {
758                         segmentAppearance.setLineColor(ColorFactory.createColor(colorMap, champ.getLineColor()));
759                     }
760
761                     segmentAppearance.setLineWidth(champ.getLineThickness().floatValue());
762                     segmentAppearance.setLinePattern(champ.getLineStyleAsEnum().asPattern());
763                     drawingTools.draw(segments, segmentAppearance);
764                 }
765
766                 /* Draw the arrows */
767                 if (champ.getArrowSize() != 0.0) {
768                     arrowDrawer.drawArrows(champ.getParentAxes(), champ.getIdentifier(), champ.getArrowSize(), champ.getLineThickness(), false,
769                                            champ.getColored(), champ.getLineColor(), false);
770                 }
771             } catch (OutOfMemoryException e) {
772                 invalidate(champ, e);
773             } catch (ObjectRemovedException e) {
774                 invalidate(champ, e);
775             } catch (SciRendererException e) {
776                 invalidate(champ, e);
777             }
778             axesDrawer.disableClipping(champ.getClipProperty());
779         }
780     }
781
782     @Override
783     public void visit(final Segs segs) {
784         if (segs.isValid() && segs.getVisible() && segs.getArrows().size() != 0) {
785             axesDrawer.enableClipping(currentAxes, segs.getClipProperty());
786             try {
787                 DefaultGeometry segments = new DefaultGeometry();
788                 segments.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
789                 segments.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
790                 segments.setVertices(dataManager.getVertexBuffer(segs.getIdentifier()));
791                 segments.setColors(dataManager.getColorBuffer(segs.getIdentifier()));
792                 segments.setWireIndices(dataManager.getWireIndexBuffer(segs.getIdentifier()));
793                 segments.setFaceCullingMode(Geometry.FaceCullingMode.BOTH);
794
795                 if (segs.getLineMode()) {
796                     Appearance segmentAppearance = new Appearance();
797                     segmentAppearance.setLineColor(null);
798                     segmentAppearance.setLineWidth(segs.getLineThickness().floatValue());
799                     segmentAppearance.setLinePattern(segs.getLineStyleAsEnum().asPattern());
800                     drawingTools.draw(segments, segmentAppearance);
801                 }
802
803                 /*
804                  * Segs does not derive from ContouredObject but Arrow does, hence we have to get the former's first Arrow
805                  * in order to obtain the latter's Mark (all arrows are supposed to have the same contour properties for now).
806                  */
807                 if (segs.getMarkMode()) {
808                     Texture texture = markManager.getMarkSprite(segs.getIdentifier(), segs.getArrows().get(0).getMark(), colorMap);
809                     ElementsBuffer positions = dataManager.getVertexBuffer(segs.getIdentifier());
810                     // Take only into account start-end of segs and not the arrow head.
811                     positions.getData().limit(segs.getNumberArrows() * 2 * 4);
812                     drawingTools.draw(texture, AnchorPosition.CENTER, positions);
813                     positions.getData().limit(positions.getData().capacity());
814                 }
815
816                 /* Draw the arrows */
817                 if (segs.getArrowSize() != 0.0) {
818                     arrowDrawer.drawArrows(segs.getParentAxes(), segs.getIdentifier(), segs.getArrowSize(), segs.getLineThickness(), true,
819                                            true, segs.getLineColor(), false);
820                 }
821             } catch (OutOfMemoryException e) {
822                 invalidate(segs, e);
823             } catch (ObjectRemovedException e) {
824                 invalidate(segs, e);
825             } catch (SciRendererException e) {
826                 invalidate(segs, e);
827             }
828             axesDrawer.disableClipping(segs.getClipProperty());
829         }
830     }
831
832     @Override
833     public void updateObject(String id, int property) {
834         try {
835             if (needUpdate(id, property)) {
836                 if (GraphicObjectProperties.__GO_COLORMAP__ == property && figure.getIdentifier().equals(id)) {
837                     labelManager.disposeAll();
838                     dataManager.disposeAllColorBuffers();
839                     dataManager.disposeAllTextureCoordinatesBuffers();
840                     markManager.disposeAll();
841                     textManager.disposeAll();
842                     axesDrawer.disposeAll();
843                     fecDrawer.updateAll();
844                     colorMapTextureDataProvider.update();
845                     textureManager.disposeAll();
846                 } else {
847                     labelManager.update(id, property);
848                     dataManager.update(id, property);
849                     markManager.update(id, property);
850                     textManager.update(id, property);
851                     axesDrawer.update(id, property);
852                     legendDrawer.update(id, property);
853                     fecDrawer.update(id, property);
854                 }
855
856                 if (GraphicObjectProperties.__GO_ANTIALIASING__ == property) {
857                     canvas.setAntiAliasingLevel(figure.getAntialiasing());
858                 }
859
860                 if (isImmediateDrawing(id)) {
861                     if (GraphicObjectProperties.__GO_IMMEDIATE_DRAWING__ == property) {
862                         canvas.redrawAndWait();
863                     } else {
864                         canvas.redraw();
865                     }
866                 }
867             }
868
869             if (GraphicObjectProperties.__GO_IMMEDIATE_DRAWING__ == property && !isImmediateDrawing(id)) {
870                 canvas.waitImage();
871             }
872
873         } catch (OutOfMemoryException e) {
874             invalidate(GraphicController.getController().getObjectFromId(id), e);
875         } catch (ObjectRemovedException e) {
876             // Object has been removed before draw : do nothing.
877         }
878     }
879
880     /**
881      * Check if the given changed property make the figure out of date.
882      * @param id the object updated
883      * @param property the changed property.
884      * @return true id the given changed property make the figure out of date.
885      */
886     protected boolean needUpdate(String id, int property) {
887         GraphicObject object = GraphicController.getController().getObjectFromId(id);
888         int objectType = (Integer) GraphicController.getController().getProperty(id, GraphicObjectProperties.__GO_TYPE__);
889         if ((object != null) && isFigureChild(id)
890                 && objectType != GraphicObjectProperties.__GO_UICONTROL__
891                 && objectType != GraphicObjectProperties.__GO_UIMENU__) {
892
893             if (GraphicObjectProperties.__GO_VALID__ == property) {
894                 return false;
895             }
896
897             if (object instanceof Axes) {
898                 Axes axes = (Axes) object;
899
900                 if (axes.getXAxisAutoTicks() && X_AXIS_TICKS_PROPERTIES.contains(property)) {
901                     return false;
902                 }
903
904                 if (axes.getYAxisAutoTicks() && Y_AXIS_TICKS_PROPERTIES.contains(property)) {
905                     return false;
906                 }
907
908                 if (axes.getZAxisAutoTicks() && Z_AXIS_TICKS_PROPERTIES.contains(property)) {
909                     return false;
910                 }
911
912                 if (property != GraphicObjectProperties.__GO_CHILDREN__) {
913                     axesDrawer.computeRulers(axes);
914                 }
915             }
916
917             if (object instanceof Figure) {
918                 if (property == GraphicObjectProperties.__GO_SIZE__ || property == GraphicObjectProperties.__GO_AXES_SIZE__ || property == GraphicObjectProperties.__GO_CHILDREN__) {
919                     Figure fig = (Figure) object;
920                     for (String gid : fig.getChildren()) {
921                         GraphicObject go = GraphicController.getController().getObjectFromId(gid);
922                         if (go instanceof Axes) {
923                             axesDrawer.computeRulers((Axes) go);
924                         }
925                     }
926                 }
927
928                 if (SILENT_FIGURE_PROPERTIES.contains(property)) {
929                     return false;
930                 }
931             }
932
933             if (!object.isValid()) {
934                 GraphicController.getController().setProperty(id, GraphicObjectProperties.__GO_VALID__, true);
935             }
936
937             return true;
938         } else {
939             return false;
940         }
941     }
942
943     private boolean isImmediateDrawing(String id) {
944         String parentId = (String) GraphicController.getController().getProperty(id, GraphicObjectProperties.__GO_PARENT_FIGURE__);
945         if (parentId == null || !parentId.equals(figure.getIdentifier())) {
946             return false;
947         } else {
948             Boolean b =  (Boolean) GraphicController.getController().getProperty(parentId, GraphicObjectProperties.__GO_IMMEDIATE_DRAWING__);
949             return (b == null) ? false : b;
950         }
951     }
952
953     @Override
954     public void createObject(String id) {
955     }
956
957     @Override
958     public void deleteObject(String id) {
959         if (isImmediateDrawing(id)) {
960             canvas.redraw();
961         }
962
963         dataManager.dispose(id);
964         markManager.dispose(id);
965         textManager.dispose(id);
966         labelManager.dispose(id);
967         axesDrawer.dispose(id);
968         legendDrawer.dispose(id);
969         fecDrawer.dispose(id);
970         textureManager.dispose(id);
971         /*
972          * Check we are deleting Figure managed by DrawerVisitor(this)
973          * Otherwise do nothing on deletion.
974          */
975         if (!figure.getIdentifier().equals(id)) {
976             return;
977         }
978
979         visitorMap.remove(id);
980         GraphicController.getController().unregister(this);
981         if (SwingUtilities.isEventDispatchThread()) {
982             canvas.destroy();
983         } else {
984             try {
985                 SwingUtilities.invokeAndWait(new Runnable() {
986                     public void run() {
987                         canvas.destroy();
988                     }
989                 });
990             } catch (Exception e) { }
991         }
992     }
993
994     /**
995      * Check if the given id correspond to a child of the current {@see Figure}.
996      * @param id the given id.
997      * @return true if the given id correspond to a child of the current {@see Figure}.
998      */
999     private boolean isFigureChild(String id) {
1000         String parentFigureID = (String) GraphicController.getController().getProperty(id, GraphicObjectProperties.__GO_PARENT_FIGURE__);
1001         return figure.getIdentifier().equals(parentFigureID);
1002     }
1003
1004     /**
1005      * Invalidate the given graphic object and inform the user.
1006      * @param graphicObject the graphic object to invalidate
1007      * @param exception the cause of invalidation.
1008      */
1009     public void invalidate(GraphicObject graphicObject, Exception exception) {
1010         if (DEBUG_MODE) {
1011             System.err.println("The " + graphicObject.getType() + " \"" + graphicObject.getIdentifier()
1012                                + "\" has been invalidated: " + exception.getMessage());
1013             exception.printStackTrace();
1014         }
1015         GraphicController.getController().setProperty(graphicObject.getIdentifier(), GraphicObjectProperties.__GO_VALID__, false);
1016     }
1017
1018     public LabelManager getLabelManager() {
1019         return labelManager;
1020     }
1021
1022     public Texture getColorMapTexture() {
1023         if (colorMapTexture == null) {
1024             colorMapTexture = canvas.getTextureManager().createTexture();
1025             colorMapTexture.setMagnificationFilter(Texture.Filter.NEAREST);
1026             colorMapTexture.setMinifyingFilter(Texture.Filter.NEAREST);
1027             colorMapTexture.setSWrappingMode(Texture.Wrap.CLAMP);
1028             colorMapTexture.setTWrappingMode(Texture.Wrap.CLAMP);
1029             colorMapTexture.setDataProvider(colorMapTextureDataProvider);
1030         }
1031         return colorMapTexture;
1032     }
1033
1034     /**
1035      * Figure getter.
1036      * @return the figure this visitor draw.
1037      */
1038     public Figure getFigure() {
1039         return figure;
1040     }
1041
1042     private Geometry cube;
1043     public Geometry getCube() {
1044         if (cube == null) {
1045             cube = CubeFactory.createCube(canvas);
1046         }
1047         return cube;
1048     }
1049
1050     /**
1051      * Component getter.
1052      * @return return the attached component.
1053      */
1054     public Component getComponent() {
1055         return component;
1056     }
1057
1058     /**
1059      * Interaction manager getter
1060      * @return the interaction manager.
1061      */
1062     public InteractionManager getInteractionManager() {
1063         return interactionManager;
1064     }
1065
1066     private class ColorMapTextureDataProvider extends AbstractTextureDataProvider {
1067         byte[] whiteColor = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
1068         byte[] blackColor = {0x00, 0x00, 0x00, (byte) 0xFF};
1069
1070         @Override
1071         public Dimension getTextureSize() {
1072             return new Dimension(colorMap.getSize() + 2, 1);
1073         }
1074
1075         @Override
1076         public ByteBuffer getData() {
1077             Double[] data = colorMap.getData();
1078             ByteBuffer buffer = ByteBuffer.allocate(4 * ((data.length / 3) + 2));
1079
1080             /* White and black are written in the first and second positions */
1081             buffer.put(whiteColor);
1082             buffer.put(blackColor);
1083
1084             for (int i = 0 ; i < data.length / 3 ; i++) {
1085                 buffer.put(toByte(data[i]));
1086                 buffer.put(toByte(data[i + colorMap.getSize()].floatValue()));
1087                 buffer.put(toByte(data[i + 2 * colorMap.getSize()].floatValue()));
1088                 buffer.put(toByte(1));
1089             }
1090             buffer.rewind();
1091             return buffer;
1092         }
1093
1094         @Override
1095         public ByteBuffer getSubData(int x, int y, int width, int height) {
1096             /*
1097              * For the moment, we presuppose that x and y are 0 and that
1098              * width is equal to the colormap's total size (with height == 1).
1099              * To be correctly implemented.
1100              */
1101             return getData();
1102         }
1103
1104         @Override
1105         public boolean isValid() {
1106             return true;
1107         }
1108
1109         public void update() {
1110             fireUpdate();
1111         }
1112     }
1113 }