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