Delete openGL component in Frame if removed.
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / JoGLView / axes / ruler / AxesRulerDrawer.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2010 - DIGITEO - Pierre Lando
4  * Copyright (C) 2011 - DIGITEO - Manuel Juliachs
5  * Copyright (C) 2013-2014 - Scilab Enterprises - Calixte DENIZET
6  *
7  * This file must be used under the terms of the CeCILL.
8  * This source file is licensed as described in the file COPYING, which
9  * you should have received as part of this distribution.  The terms
10  * are also available at
11  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
12  */
13
14 package org.scilab.modules.renderer.JoGLView.axes.ruler;
15
16 import org.scilab.forge.scirenderer.Canvas;
17 import org.scilab.forge.scirenderer.DrawingTools;
18 import org.scilab.forge.scirenderer.SciRendererException;
19 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
20 import org.scilab.forge.scirenderer.ruler.DefaultRulerModel;
21 import org.scilab.forge.scirenderer.ruler.RulerDrawer;
22 import org.scilab.forge.scirenderer.ruler.RulerDrawingResult;
23 import org.scilab.forge.scirenderer.ruler.RulerModel;
24 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
25 import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry;
26 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
27 import org.scilab.forge.scirenderer.tranformations.DegenerateMatrixException;
28 import org.scilab.forge.scirenderer.tranformations.Transformation;
29 import org.scilab.forge.scirenderer.tranformations.TransformationFactory;
30 import org.scilab.forge.scirenderer.tranformations.Vector3d;
31 import org.scilab.modules.graphic_objects.axes.Axes;
32 import org.scilab.modules.graphic_objects.axes.AxisProperty;
33 import org.scilab.modules.graphic_objects.axes.Camera;
34 import org.scilab.modules.graphic_objects.contouredObject.Line;
35 import org.scilab.modules.graphic_objects.figure.ColorMap;
36 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
37 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
38 import org.scilab.modules.renderer.JoGLView.axes.AxesDrawer;
39 import org.scilab.modules.renderer.JoGLView.label.AxisLabelPositioner;
40 import org.scilab.modules.renderer.JoGLView.label.TitlePositioner;
41 import org.scilab.modules.renderer.JoGLView.util.ColorFactory;
42
43 import java.awt.Dimension;
44 import java.nio.FloatBuffer;
45 import java.text.DecimalFormat;
46 import java.util.Arrays;
47 import java.util.List;
48
49 /**
50  * @author Pierre Lando
51  */
52 public class AxesRulerDrawer {
53
54     private static final double LINEAR_MINIMAL_SUB_TICKS_DISTANCE = 8;
55     private static final double LOG_MINIMAL_SUB_TICKS_DISTANCE = 2;
56
57     /** Ticks length in pixels. */
58     public static final int TICKS_LENGTH = 6;
59
60     /** Sub-ticks length in pixels. */
61     private static final int SUB_TICKS_LENGTH = 3;
62
63     /**Ticks sprites distance in pixels. */
64     private static final int SPRITE_DISTANCE = 12;
65
66
67     private final RulerDrawerManager rulerDrawerManager;
68
69     public AxesRulerDrawer(Canvas canvas) {
70         this.rulerDrawerManager = new RulerDrawerManager(canvas.getTextureManager());
71     }
72
73     public RulerDrawer getRulerDrawer(Axes axes, int axis) {
74         return rulerDrawerManager.get(axes)[axis];
75     }
76
77     /**
78      * Get default ruler model
79      * @param axes the axes
80      * @param colorMap the colorMap
81      * @return a DefaultRulerModel
82      */
83     private final DefaultRulerModel getDefaultRulerModel(Axes axes, ColorMap colorMap) {
84         DefaultRulerModel rulerModel = new DefaultRulerModel();
85         rulerModel.setTicksLength(TICKS_LENGTH);
86         rulerModel.setSubTicksLength(SUB_TICKS_LENGTH);
87         rulerModel.setLineWidth(axes.getLineThickness());
88         rulerModel.setSpriteDistance(SPRITE_DISTANCE);
89         rulerModel.setColor(ColorFactory.createColor(colorMap, axes.getLineColor()));
90
91         return rulerModel;
92     }
93
94     /**
95      * Compute ticks and subticks on the rulers
96      * @param axes the current {@see Axes}
97      * @param axesDrawer the drawer used to draw the current {@see Axes}
98      * @param colorMap current {@see ColorMap}
99      * @param transformation the current modelView projection
100      * @param canvasProjection the canvas projection
101      * @throws org.scilab.forge.scirenderer.SciRendererException if draw fail.
102      */
103     public void computeRulers(Axes axes, AxesDrawer axesDrawer, ColorMap colorMap, Transformation transformation, Transformation canvasProjection) {
104         Double[] bounds = axes.getDisplayedBounds();
105         double[] matrix = transformation.getMatrix();
106
107         RulerDrawingResult rulerDrawingResult;
108         double[] values;
109
110         RulerDrawer[] rulerDrawers = rulerDrawerManager.get(axes);
111         DefaultRulerModel rulerModel = getDefaultRulerModel(axes, colorMap);
112
113         Vector3d xAxisPosition = computeXAxisPosition(matrix, bounds, axes.getXAxis().getAxisLocation(), axes.getYAxis().getReverse());
114         Vector3d yAxisPosition = computeYAxisPosition(matrix, bounds, axes.getYAxis().getAxisLocation(), axes.getXAxis().getReverse());
115
116         Vector3d px = canvasProjection.projectDirection(new Vector3d(1, 0, 0)).setZ(0);
117         Vector3d py = canvasProjection.projectDirection(new Vector3d(0, 1, 0)).setZ(0);
118         Vector3d pz = canvasProjection.projectDirection(new Vector3d(0, 0, 1)).setZ(0);
119
120         Vector3d xTicksDirection, yTicksDirection;
121         if (py.getNorm2() > pz.getNorm2()) {
122             xTicksDirection = new Vector3d(0, getNonZeroSignum(xAxisPosition.getY()), 0);
123         } else {
124             xTicksDirection = new Vector3d(0, 0, getNonZeroSignum(xAxisPosition.getZ()));
125         }
126
127         if (px.getNorm2() > pz.getNorm2()) {
128             yTicksDirection = new Vector3d(getNonZeroSignum(yAxisPosition.getX()), 0, 0);
129         } else {
130             yTicksDirection = new Vector3d(0, 0, getNonZeroSignum(yAxisPosition.getZ()));
131         }
132
133         // Draw X ruler
134         rulerModel.setTicksDirection(xTicksDirection);
135         rulerModel.setFirstPoint(xAxisPosition.setX(-1));
136         rulerModel.setSecondPoint(xAxisPosition.setX(1));
137         if (!axes.getAutoSubticks()) {
138             rulerModel.setSubticksNumber(axes.getXAxisSubticks());
139         }
140
141         setRulerBounds(axes.getXAxis(), rulerModel, bounds[0], bounds[1]);
142
143         rulerModel.setFormat(axes.getXAxisFormat());
144         rulerModel.setSTFactors(axes.getXAxisSTFactors());
145         rulerModel.setLogarithmic(axes.getXAxis().getLogFlag());
146         rulerModel.setMinimalSubTicksDistance(axes.getXAxis().getLogFlag() ? LOG_MINIMAL_SUB_TICKS_DISTANCE : LINEAR_MINIMAL_SUB_TICKS_DISTANCE);
147
148         if (!axes.getXAxis().getAutoTicks()) {
149             rulerModel.setUserGraduation(new UserDefineGraduation(axes.getXAxis(), bounds[0], bounds[1]));
150             if (axes.getAutoSubticks()) {
151                 rulerModel.setSubticksNumber(axes.getXAxisSubticks());
152             }
153             rulerModel.setAutoTicks(false);
154         } else {
155             rulerModel.setAutoTicks(true);
156         }
157
158         double distanceRatio;
159         AxisLabelPositioner xAxisLabelPositioner = axesDrawer.getXAxisLabelPositioner(axes);
160         xAxisLabelPositioner.setLabelPosition(xAxisPosition);
161
162         if (axes.getXAxisVisible()) {
163             rulerDrawingResult = rulerDrawers[0].computeRuler(rulerModel, canvasProjection);
164             values = rulerDrawingResult.getTicksValues();
165
166             if (axes.getXAxisAutoTicks()) {
167                 Arrays.sort(values);
168                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_X_AXIS_TICKS_LOCATIONS__, toDoubleArray(values));
169                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_X_AXIS_TICKS_LABELS__, toStringArray(values, rulerDrawingResult.getFormat()));
170                 if (axes.getAutoSubticks()) {
171                     //GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_X_AXIS_SUBTICKS__, rulerDrawingResult.getSubTicksDensity());
172                     axes.setXAxisSubticks(rulerDrawingResult.getSubTicksDensity());
173                 }
174             }
175
176             distanceRatio = rulerDrawingResult.getMaxDistToTicksDirNorm();
177
178             xAxisLabelPositioner.setTicksDirection(xTicksDirection);
179             xAxisLabelPositioner.setDistanceRatio(distanceRatio);
180             xAxisLabelPositioner.setProjectedTicksDirection(rulerDrawingResult.getNormalizedTicksDirection().setZ(0));
181         } else {
182             xAxisLabelPositioner.setTicksDirection(xTicksDirection);
183             Vector3d projTicksDir = canvasProjection.projectDirection(xTicksDirection);
184             xAxisLabelPositioner.setDistanceRatio((double) TICKS_LENGTH / projTicksDir.getNorm());
185             xAxisLabelPositioner.setProjectedTicksDirection(projTicksDir.getNormalized().setZ(0));
186         }
187
188         TitlePositioner titlePositioner = axesDrawer.getTitlePositioner(axes);
189         Dimension xdim = axesDrawer.getLabelManager().getXLabelSize(colorMap, axes, axesDrawer);
190         titlePositioner.setXLabelHeight(xdim.height);
191         titlePositioner.setDistanceRatio(xAxisLabelPositioner.getDistanceRatio());
192
193         // Draw Y ruler
194         rulerModel = getDefaultRulerModel(axes, colorMap);
195         rulerModel.setTicksDirection(yTicksDirection);
196         rulerModel.setFirstPoint(yAxisPosition.setY(-1));
197         rulerModel.setSecondPoint(yAxisPosition.setY(1));
198         if (!axes.getAutoSubticks()) {
199             rulerModel.setSubticksNumber(axes.getYAxisSubticks());
200         }
201
202         setRulerBounds(axes.getYAxis(), rulerModel, bounds[2], bounds[3]);
203         rulerModel.setFormat(axes.getYAxisFormat());
204         rulerModel.setSTFactors(axes.getYAxisSTFactors());
205         rulerModel.setLogarithmic(axes.getYAxis().getLogFlag());
206         rulerModel.setMinimalSubTicksDistance(axes.getYAxis().getLogFlag() ? LOG_MINIMAL_SUB_TICKS_DISTANCE : LINEAR_MINIMAL_SUB_TICKS_DISTANCE);
207
208         if (!axes.getYAxis().getAutoTicks()) {
209             rulerModel.setUserGraduation(new UserDefineGraduation(axes.getYAxis(), bounds[2], bounds[3]));
210             if (axes.getAutoSubticks()) {
211                 rulerModel.setSubticksNumber(axes.getYAxisSubticks());
212             }
213             rulerModel.setAutoTicks(false);
214         } else {
215             rulerModel.setAutoTicks(true);
216         }
217
218         AxisLabelPositioner yAxisLabelPositioner = axesDrawer.getYAxisLabelPositioner(axes);
219         yAxisLabelPositioner.setLabelPosition(yAxisPosition);
220
221         if (axes.getYAxisVisible()) {
222             rulerDrawingResult = rulerDrawers[1].computeRuler(rulerModel, canvasProjection);
223             values = rulerDrawingResult.getTicksValues();
224             if (axes.getYAxisAutoTicks()) {
225                 Arrays.sort(values);
226                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Y_AXIS_TICKS_LOCATIONS__, toDoubleArray(values));
227                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Y_AXIS_TICKS_LABELS__, toStringArray(values, rulerDrawingResult.getFormat()));
228                 if (axes.getAutoSubticks()) {
229                     //GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Y_AXIS_SUBTICKS__, rulerDrawingResult.getSubTicksDensity());
230                     axes.setYAxisSubticks(rulerDrawingResult.getSubTicksDensity());
231                 }
232             }
233
234             distanceRatio = rulerDrawingResult.getMaxDistToTicksDirNorm();
235
236             yAxisLabelPositioner.setTicksDirection(yTicksDirection);
237             yAxisLabelPositioner.setDistanceRatio(distanceRatio);
238             yAxisLabelPositioner.setProjectedTicksDirection(rulerDrawingResult.getNormalizedTicksDirection().setZ(0));
239         } else {
240             /* y-axis not visible: compute the projected ticks direction and distance ratio (see the x-axis case). */
241             yAxisLabelPositioner.setTicksDirection(yTicksDirection);
242             Vector3d projTicksDir = canvasProjection.projectDirection(yTicksDirection);
243             yAxisLabelPositioner.setDistanceRatio((double) TICKS_LENGTH / projTicksDir.getNorm());
244             yAxisLabelPositioner.setProjectedTicksDirection(projTicksDir.getNormalized().setZ(0));
245         }
246
247         // Draw Z ruler
248         if (axes.getViewAsEnum() == Camera.ViewType.VIEW_3D) {
249             double txs, tys, xs, ys;
250             if (Math.abs(matrix[2]) < Math.abs(matrix[6])) {
251                 xs = matrix[2] > 0 ? 1 : -1;
252                 ys = matrix[6] > 0 ? -1 : 1;
253                 txs = xs;
254                 tys = 0;
255             } else {
256                 xs = matrix[2] > 0 ? -1 : 1;
257                 ys = matrix[6] > 0 ? 1 : -1;
258                 txs = 0;
259                 tys = ys;
260             }
261
262             rulerModel = getDefaultRulerModel(axes, colorMap);
263             rulerModel.setFirstPoint(new Vector3d(xs, ys, -1));
264             rulerModel.setSecondPoint(new Vector3d(xs, ys, 1));
265             rulerModel.setTicksDirection(new Vector3d(txs, tys, 0));
266             if (!axes.getAutoSubticks()) {
267                 rulerModel.setSubticksNumber(axes.getZAxisSubticks());
268             }
269
270             setRulerBounds(axes.getZAxis(), rulerModel, bounds[4], bounds[5]);
271             rulerModel.setFormat(axes.getZAxisFormat());
272             rulerModel.setSTFactors(axes.getZAxisSTFactors());
273             rulerModel.setLogarithmic(axes.getZAxis().getLogFlag());
274             rulerModel.setMinimalSubTicksDistance(axes.getZAxis().getLogFlag() ? LOG_MINIMAL_SUB_TICKS_DISTANCE : LINEAR_MINIMAL_SUB_TICKS_DISTANCE);
275
276             if (!axes.getZAxis().getAutoTicks()) {
277                 rulerModel.setUserGraduation(new UserDefineGraduation(axes.getZAxis(), bounds[4], bounds[5]));
278                 if (axes.getAutoSubticks()) {
279                     rulerModel.setSubticksNumber(axes.getZAxisSubticks());
280                 }
281                 rulerModel.setAutoTicks(false);
282             } else {
283                 rulerModel.setAutoTicks(true);
284             }
285
286             AxisLabelPositioner zAxisLabelPositioner = axesDrawer.getZAxisLabelPositioner(axes);
287             zAxisLabelPositioner.setLabelPosition(new Vector3d(xs, ys, 0));
288
289             if (axes.getZAxisVisible()) {
290                 rulerDrawingResult = rulerDrawers[2].computeRuler(rulerModel, canvasProjection);
291                 values = rulerDrawingResult.getTicksValues();
292                 if (axes.getZAxisAutoTicks()) {
293                     Arrays.sort(values);
294                     GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Z_AXIS_TICKS_LOCATIONS__, toDoubleArray(values));
295                     GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Z_AXIS_TICKS_LABELS__, toStringArray(values, rulerDrawingResult.getFormat()));
296                     if (axes.getAutoSubticks()) {
297                         //GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Z_AXIS_SUBTICKS__, rulerDrawingResult.getSubTicksDensity());
298                         axes.setZAxisSubticks(rulerDrawingResult.getSubTicksDensity());
299                     }
300                 }
301
302                 distanceRatio = rulerDrawingResult.getMaxDistToTicksDirNorm();
303
304                 zAxisLabelPositioner.setTicksDirection(new Vector3d(txs, tys, 0.0));
305                 zAxisLabelPositioner.setDistanceRatio(distanceRatio);
306                 zAxisLabelPositioner.setProjectedTicksDirection(rulerDrawingResult.getNormalizedTicksDirection().setZ(0).setY(1e-7));
307             } else {
308                 /* z-axis not visible: compute the projected ticks direction and distance ratio (see the x-axis case). */
309                 Vector3d zTicksDirection = new Vector3d(txs, tys, 0);
310
311                 zAxisLabelPositioner.setTicksDirection(zTicksDirection);
312                 Vector3d projTicksDir = canvasProjection.projectDirection(zTicksDirection);
313                 zAxisLabelPositioner.setDistanceRatio((double) TICKS_LENGTH / projTicksDir.getNorm());
314                 zAxisLabelPositioner.setProjectedTicksDirection(projTicksDir.getNormalized().setZ(0));
315             }
316         }
317     }
318
319     /**
320      * Draw the ruler.
321      * @param axes the current {@see Axes}
322      * @param axesDrawer the drawer used to draw the current {@see Axes}
323      * @param colorMap current {@see ColorMap}
324      * @param drawingTools the used {@see DrawingTools}
325      * @throws org.scilab.forge.scirenderer.SciRendererException if draw fail.
326      */
327     public void drawRuler(Axes axes, AxesDrawer axesDrawer, ColorMap colorMap, DrawingTools drawingTools) throws SciRendererException {
328         Appearance gridAppearance = new Appearance();
329
330         Double[] bounds = axes.getDisplayedBounds();
331         double[] matrix = drawingTools.getTransformationManager().getModelViewStack().peek().getMatrix();
332
333         RulerDrawer[] rulerDrawers = rulerDrawerManager.get(axes);
334         ElementsBuffer vertexBuffer = drawingTools.getCanvas().getBuffersManager().createElementsBuffer();
335         final boolean is3D = axes.getViewAsEnum() == Camera.ViewType.VIEW_3D;// && axes.getRotationAngles()[1] != 90.0;
336
337         if (rulerDrawers[0].getModel() == null || rulerDrawers[1].getModel() == null || (is3D && rulerDrawers[2].getModel() == null)) {
338             computeRulers(axes, axesDrawer, colorMap, drawingTools.getTransformationManager().getModelViewStack().peek(), drawingTools.getTransformationManager().getCanvasProjection());
339         }
340
341         int gridPosition;
342         if (axes.getGridPositionAsEnum().equals(Axes.GridPosition.FOREGROUND)) {
343             gridPosition = 1;
344         } else {
345             gridPosition = -1;
346         }
347
348         // Draw X ruler
349         if (axes.getXAxisVisible()) {
350             rulerDrawers[0].draw(drawingTools);
351
352             if (axes.getXAxisGridColor() != -1) {
353                 FloatBuffer vertexData;
354                 if (axes.getXAxisLogFlag()) {
355                     List<Double> values = rulerDrawers[0].getSubTicksValue();
356                     if (values == null || values.isEmpty()) {
357                         vertexData = getXGridData(rulerDrawers[0].getTicksValue(), rulerDrawers[0].getModel());
358                     } else {
359                         vertexData = getXGridData(values, rulerDrawers[0].getModel());
360                     }
361                 } else {
362                     vertexData = getXGridData(rulerDrawers[0].getTicksValue(), rulerDrawers[0].getModel());
363                 }
364                 vertexBuffer.setData(vertexData, 4);
365
366                 Transformation mirror;
367                 try {
368                     mirror = TransformationFactory.getScaleTransformation(
369                                  1,
370                                  matrix[6] < 0 ? gridPosition : -gridPosition,
371                                  matrix[10] < 0 ? gridPosition : -gridPosition
372                              );
373                 } catch (DegenerateMatrixException ignored) {
374                     // Should never happens as long as gridPosition the value 1 or -1
375                     mirror = TransformationFactory.getIdentity();
376                 }
377
378                 gridAppearance.setLineColor(ColorFactory.createColor(colorMap, axes.getXAxisGridColor()));
379                 gridAppearance.setLineWidth(axes.getXAxisGridThickness().floatValue());
380                 gridAppearance.setLinePattern(Line.LineType.fromScilabIndex(axes.getXAxisGridStyle()).asPattern());
381                 drawingTools.getTransformationManager().getModelViewStack().pushRightMultiply(mirror);
382                 DefaultGeometry gridGeometry = new DefaultGeometry();
383                 gridGeometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
384                 gridGeometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
385                 gridGeometry.setVertices(vertexBuffer);
386                 drawingTools.draw(gridGeometry, gridAppearance);
387                 drawingTools.getTransformationManager().getModelViewStack().pop();
388             }
389         }
390
391         // Draw Y ruler
392         if (axes.getYAxisVisible()) {
393             rulerDrawers[1].draw(drawingTools);
394
395             if (axes.getYAxisGridColor() != -1) {
396                 FloatBuffer vertexData;
397                 if (axes.getYAxisLogFlag()) {
398                     List<Double> values = rulerDrawers[1].getSubTicksValue();
399                     if (values == null || values.isEmpty()) {
400                         vertexData = getYGridData(rulerDrawers[1].getTicksValue(), rulerDrawers[1].getModel());
401                     } else {
402                         vertexData = getYGridData(values, rulerDrawers[1].getModel());
403                     }
404                 } else {
405                     vertexData = getYGridData(rulerDrawers[1].getTicksValue(), rulerDrawers[1].getModel());
406                 }
407                 vertexBuffer.setData(vertexData, 4);
408
409                 Transformation mirror;
410                 try {
411                     mirror = TransformationFactory.getScaleTransformation(
412                                  matrix[2] < 0 ? gridPosition : -gridPosition,
413                                  1,
414                                  matrix[10] < 0 ? gridPosition : -gridPosition
415                              );
416                 } catch (DegenerateMatrixException ignored) {
417                     // Should never happens as long as gridPosition the value 1 or -1
418                     mirror = TransformationFactory.getIdentity();
419                 }
420
421                 gridAppearance.setLineColor(ColorFactory.createColor(colorMap, axes.getYAxisGridColor()));
422                 gridAppearance.setLineWidth(axes.getYAxisGridThickness().floatValue());
423                 gridAppearance.setLinePattern(Line.LineType.fromScilabIndex(axes.getYAxisGridStyle()).asPattern());
424                 drawingTools.getTransformationManager().getModelViewStack().pushRightMultiply(mirror);
425                 DefaultGeometry gridGeometry = new DefaultGeometry();
426                 gridGeometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
427                 gridGeometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
428                 gridGeometry.setVertices(vertexBuffer);
429                 drawingTools.draw(gridGeometry, gridAppearance);
430                 drawingTools.getTransformationManager().getModelViewStack().pop();
431             }
432         }
433
434         // Draw Z ruler
435         if (is3D) {
436             if (axes.getZAxisVisible()) {
437                 rulerDrawers[2].draw(drawingTools);
438
439                 if (axes.getZAxisGridColor() != -1) {
440                     FloatBuffer vertexData;
441                     if (axes.getZAxisLogFlag()) {
442                         List<Double> values = rulerDrawers[2].getSubTicksValue();
443                         if (values == null || values.isEmpty()) {
444                             vertexData = getZGridData(rulerDrawers[2].getTicksValue(), rulerDrawers[2].getModel());
445                         } else {
446                             vertexData = getZGridData(values, rulerDrawers[2].getModel());
447                         }
448                     } else {
449                         vertexData = getZGridData(rulerDrawers[2].getTicksValue(), rulerDrawers[2].getModel());
450                     }
451                     vertexBuffer.setData(vertexData, 4);
452
453                     Transformation mirror;
454                     try {
455                         mirror = TransformationFactory.getScaleTransformation(
456                                      matrix[2] < 0 ? gridPosition : -gridPosition,
457                                      matrix[6] < 0 ? gridPosition : -gridPosition,
458                                      1
459                                  );
460                     } catch (DegenerateMatrixException ignored) {
461                         // Should never happens as long as gridPosition the value 1 or -1
462                         mirror = TransformationFactory.getIdentity();
463                     }
464
465                     gridAppearance.setLineColor(ColorFactory.createColor(colorMap, axes.getZAxisGridColor()));
466                     gridAppearance.setLineWidth(axes.getZAxisGridThickness().floatValue());
467                     gridAppearance.setLinePattern(Line.LineType.fromScilabIndex(axes.getZAxisGridStyle()).asPattern());
468                     drawingTools.getTransformationManager().getModelViewStack().pushRightMultiply(mirror);
469                     DefaultGeometry gridGeometry = new DefaultGeometry();
470                     gridGeometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
471                     gridGeometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
472                     gridGeometry.setVertices(vertexBuffer);
473                     drawingTools.draw(gridGeometry, gridAppearance);
474                     drawingTools.getTransformationManager().getModelViewStack().pop();
475                 }
476             }
477         }
478
479         drawingTools.getCanvas().getBuffersManager().dispose(vertexBuffer);
480     }
481
482     private static final double getNonZeroSignum(double value) {
483         if (value < 0) {
484             return -1;
485         } else {
486             return 1;
487         }
488     }
489
490     private void setRulerBounds(AxisProperty axis, DefaultRulerModel rulerModel, double axisMin, double axisMax) {
491         double min, max;
492         if (axis.getReverse()) {
493             min = axisMin;
494             max = axisMax;
495         } else {
496             min = axisMax;
497             max = axisMin;
498         }
499
500         if (axis.getLogFlag()) {
501             min = Math.pow(10, min);
502             max = Math.pow(10, max);
503         }
504         rulerModel.setValues(min, max);
505     }
506
507     private Vector3d computeXAxisPosition(double[] projectionMatrix, Double[] bounds, AxisProperty.AxisLocation axisLocation, boolean reverse) {
508         double y, z;
509         switch (axisLocation) {
510             default:
511             case BOTTOM:
512                 z = -Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was minimal.
513                 y = -Math.signum(projectionMatrix[6]) * z * Math.signum(projectionMatrix[10]);
514                 if (y == 0) {
515                     y = +1;
516                 }
517                 break;
518             case MIDDLE:
519                 z = Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was maximal.
520                 y = 0;
521                 break;
522             case TOP:
523                 z = Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was maximal.
524                 y = -Math.signum(projectionMatrix[6]) * z * Math.signum(projectionMatrix[10]);
525                 if (y == 0) {
526                     y = -1;
527                 }
528                 break;
529             case ORIGIN:
530                 z = Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was maximal.
531                 y = (reverse ? -1 : 1) * (bounds[3] + bounds[2]) / (bounds[3] - bounds[2]);
532                 if (Math.abs(y) > 1) {
533                     y = Math.signum(y);
534                 }
535                 break;
536         }
537         return new Vector3d(0, y, z);
538     }
539
540     private Vector3d computeYAxisPosition(double[] matrix, Double[] bounds, AxisProperty.AxisLocation axisLocation, boolean reverse) {
541         double x, z;
542         switch (axisLocation) {
543             default:
544             case LEFT:
545                 z = -Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
546                 x = -Math.signum(matrix[2]) * z * Math.signum(matrix[10]);
547                 if (x == 0) {
548                     x = +1;
549                 }
550                 break;
551             case MIDDLE:
552                 z = Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
553                 x = 0;
554                 break;
555             case RIGHT:
556                 z = Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
557                 x = -Math.signum(matrix[2]) * z * Math.signum(matrix[10]); // Then switch X such that Z max but not in the middle.
558                 if (x == 0) {
559                     x = -1;
560                 }
561                 break;
562             case ORIGIN:
563                 z = Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
564                 x = (reverse ? -1 : 1) * (bounds[1] + bounds[0]) / (bounds[1] - bounds[0]);
565                 if (Math.abs(x) > 1) {
566                     x = Math.signum(x);
567                 }
568                 break;
569         }
570         return new Vector3d(x, 0, z);
571     }
572
573     private String[] toStringArray(double[] values, DecimalFormat format) {
574         AxesRulerSpriteFactory.setScilabStyle(format);
575         String[] r = new String[values.length];
576         for (int i = 0; i < values.length; i++) {
577             r[i] = format.format(values[i]);
578         }
579         return r;
580     }
581
582     private Double[] toDoubleArray(double[] values) {
583         Double[] r = new Double[values.length];
584         for (int i = 0; i < values.length; i++) {
585             r[i] = values[i];
586         }
587         return r;
588     }
589
590     /**
591      * Build X grid data.
592      * @param values X values where grid is drawn.
593      * @param rulerModel used rulerModel to compute grid world position.
594      * @return X grid data.
595      */
596     private FloatBuffer getXGridData(List<Double> values, RulerModel rulerModel) {
597         FloatBuffer vertexData = FloatBuffer.allocate(values.size() * 16);
598         for (double value : values) {
599             float p = (float) rulerModel.getPosition(value).getX();
600             vertexData.put(p);
601             vertexData.put(+1);
602             vertexData.put(+1);
603             vertexData.put(1);
604             vertexData.put(p);
605             vertexData.put(-1);
606             vertexData.put(+1);
607             vertexData.put(1);
608             vertexData.put(p);
609             vertexData.put(+1);
610             vertexData.put(+1);
611             vertexData.put(1);
612             vertexData.put(p);
613             vertexData.put(+1);
614             vertexData.put(-1);
615             vertexData.put(1);
616         }
617         vertexData.rewind();
618         return vertexData;
619     }
620
621     /**
622      * Build Y grid data.
623      * @param values Y values where grid is drawn.
624      * @param rulerModel used rulerModel to compute grid world position.
625      * @return Y grid data.
626      */
627     private FloatBuffer getYGridData(List<Double> values, RulerModel rulerModel) {
628         FloatBuffer vertexData = FloatBuffer.allocate(values.size() * 16);
629         for (double value : values) {
630             float p = (float) rulerModel.getPosition(value).getY();
631             vertexData.put(+1);
632             vertexData.put(p);
633             vertexData.put(+1);
634             vertexData.put(1);
635             vertexData.put(-1);
636             vertexData.put(p);
637             vertexData.put(+1);
638             vertexData.put(1);
639             vertexData.put(+1);
640             vertexData.put(p);
641             vertexData.put(+1);
642             vertexData.put(1);
643             vertexData.put(+1);
644             vertexData.put(p);
645             vertexData.put(-1);
646             vertexData.put(1);
647         }
648         vertexData.rewind();
649         return vertexData;
650     }
651
652     /**
653      * Build Z grid data.
654      * @param values Z values where grid is drawn.
655      * @param rulerModel used rulerModel to compute grid world position.
656      * @return Z grid data.
657      */
658     private FloatBuffer getZGridData(List<Double> values, RulerModel rulerModel) {
659         FloatBuffer vertexData = FloatBuffer.allocate(values.size() * 16);
660         int limit = 0;
661         for (double value : values) {
662             float p = (float) rulerModel.getPosition(value).getZ();
663             vertexData.put(+1);
664             vertexData.put(+1);
665             vertexData.put(p);
666             vertexData.put(1);
667             vertexData.put(-1);
668             vertexData.put(+1);
669             vertexData.put(p);
670             vertexData.put(1);
671             vertexData.put(+1);
672             vertexData.put(+1);
673             vertexData.put(p);
674             vertexData.put(1);
675             vertexData.put(+1);
676             vertexData.put(-1);
677             vertexData.put(p);
678             vertexData.put(1);
679             limit += 16;
680         }
681         vertexData.limit(limit);
682         return vertexData;
683     }
684
685     public void disposeAll() {
686         this.rulerDrawerManager.disposeAll();
687     }
688
689     public boolean update(Integer id, int property) {
690         return this.rulerDrawerManager.update(id, property);
691     }
692
693     public void dispose(Integer id) {
694         this.rulerDrawerManager.dispose(id);
695     }
696 }