Definitly fix bug 11067: add ticks_format and ticks_st properties to improve ticks...
[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                 }
173             }
174
175             distanceRatio = rulerDrawingResult.getMaxDistToTicksDirNorm();
176
177             xAxisLabelPositioner.setTicksDirection(xTicksDirection);
178             xAxisLabelPositioner.setDistanceRatio(distanceRatio);
179             xAxisLabelPositioner.setProjectedTicksDirection(rulerDrawingResult.getNormalizedTicksDirection().setZ(0));
180         } else {
181             xAxisLabelPositioner.setTicksDirection(xTicksDirection);
182             Vector3d projTicksDir = canvasProjection.projectDirection(xTicksDirection);
183             xAxisLabelPositioner.setDistanceRatio((double) TICKS_LENGTH / projTicksDir.getNorm());
184             xAxisLabelPositioner.setProjectedTicksDirection(projTicksDir.getNormalized().setZ(0));
185         }
186
187         TitlePositioner titlePositioner = axesDrawer.getTitlePositioner(axes);
188         Dimension xdim = axesDrawer.getLabelManager().getXLabelSize(colorMap, axes, axesDrawer);
189         titlePositioner.setXLabelHeight(xdim.height);
190         titlePositioner.setDistanceRatio(xAxisLabelPositioner.getDistanceRatio());
191
192         // Draw Y ruler
193         rulerModel = getDefaultRulerModel(axes, colorMap);
194         rulerModel.setTicksDirection(yTicksDirection);
195         rulerModel.setFirstPoint(yAxisPosition.setY(-1));
196         rulerModel.setSecondPoint(yAxisPosition.setY(1));
197         if (!axes.getAutoSubticks()) {
198             rulerModel.setSubticksNumber(axes.getYAxisSubticks());
199         }
200
201         setRulerBounds(axes.getYAxis(), rulerModel, bounds[2], bounds[3]);
202         rulerModel.setFormat(axes.getYAxisFormat());
203         rulerModel.setSTFactors(axes.getYAxisSTFactors());
204         rulerModel.setLogarithmic(axes.getYAxis().getLogFlag());
205         rulerModel.setMinimalSubTicksDistance(axes.getYAxis().getLogFlag() ? LOG_MINIMAL_SUB_TICKS_DISTANCE : LINEAR_MINIMAL_SUB_TICKS_DISTANCE);
206
207         if (!axes.getYAxis().getAutoTicks()) {
208             rulerModel.setUserGraduation(new UserDefineGraduation(axes.getYAxis(), bounds[2], bounds[3]));
209             if (axes.getAutoSubticks()) {
210                 rulerModel.setSubticksNumber(axes.getYAxisSubticks());
211             }
212             rulerModel.setAutoTicks(false);
213         } else {
214             rulerModel.setAutoTicks(true);
215         }
216
217         AxisLabelPositioner yAxisLabelPositioner = axesDrawer.getYAxisLabelPositioner(axes);
218         yAxisLabelPositioner.setLabelPosition(yAxisPosition);
219
220         if (axes.getYAxisVisible()) {
221             rulerDrawingResult = rulerDrawers[1].computeRuler(rulerModel, canvasProjection);
222             values = rulerDrawingResult.getTicksValues();
223             if (axes.getYAxisAutoTicks()) {
224                 Arrays.sort(values);
225                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Y_AXIS_TICKS_LOCATIONS__, toDoubleArray(values));
226                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Y_AXIS_TICKS_LABELS__, toStringArray(values, rulerDrawingResult.getFormat()));
227                 if (axes.getAutoSubticks()) {
228                     GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Y_AXIS_SUBTICKS__, rulerDrawingResult.getSubTicksDensity());
229                 }
230             }
231
232             distanceRatio = rulerDrawingResult.getMaxDistToTicksDirNorm();
233
234             yAxisLabelPositioner.setTicksDirection(yTicksDirection);
235             yAxisLabelPositioner.setDistanceRatio(distanceRatio);
236             yAxisLabelPositioner.setProjectedTicksDirection(rulerDrawingResult.getNormalizedTicksDirection().setZ(0));
237         } else {
238             /* y-axis not visible: compute the projected ticks direction and distance ratio (see the x-axis case). */
239             yAxisLabelPositioner.setTicksDirection(yTicksDirection);
240             Vector3d projTicksDir = canvasProjection.projectDirection(yTicksDirection);
241             yAxisLabelPositioner.setDistanceRatio((double) TICKS_LENGTH / projTicksDir.getNorm());
242             yAxisLabelPositioner.setProjectedTicksDirection(projTicksDir.getNormalized().setZ(0));
243         }
244
245         // Draw Z ruler
246         if (axes.getViewAsEnum() == Camera.ViewType.VIEW_3D) {
247             double txs, tys, xs, ys;
248             if (Math.abs(matrix[2]) < Math.abs(matrix[6])) {
249                 xs = matrix[2] > 0 ? 1 : -1;
250                 ys = matrix[6] > 0 ? -1 : 1;
251                 txs = xs;
252                 tys = 0;
253             } else {
254                 xs = matrix[2] > 0 ? -1 : 1;
255                 ys = matrix[6] > 0 ? 1 : -1;
256                 txs = 0;
257                 tys = ys;
258             }
259
260             rulerModel = getDefaultRulerModel(axes, colorMap);
261             rulerModel.setFirstPoint(new Vector3d(xs, ys, -1));
262             rulerModel.setSecondPoint(new Vector3d(xs, ys, 1));
263             rulerModel.setTicksDirection(new Vector3d(txs, tys, 0));
264             if (!axes.getAutoSubticks()) {
265                 rulerModel.setSubticksNumber(axes.getZAxisSubticks());
266             }
267
268             setRulerBounds(axes.getZAxis(), rulerModel, bounds[4], bounds[5]);
269             rulerModel.setFormat(axes.getZAxisFormat());
270             rulerModel.setSTFactors(axes.getZAxisSTFactors());
271             rulerModel.setLogarithmic(axes.getZAxis().getLogFlag());
272             rulerModel.setMinimalSubTicksDistance(axes.getZAxis().getLogFlag() ? LOG_MINIMAL_SUB_TICKS_DISTANCE : LINEAR_MINIMAL_SUB_TICKS_DISTANCE);
273
274             if (!axes.getZAxis().getAutoTicks()) {
275                 rulerModel.setUserGraduation(new UserDefineGraduation(axes.getZAxis(), bounds[4], bounds[5]));
276                 if (axes.getAutoSubticks()) {
277                     rulerModel.setSubticksNumber(axes.getZAxisSubticks());
278                 }
279                 rulerModel.setAutoTicks(false);
280             } else {
281                 rulerModel.setAutoTicks(true);
282             }
283
284             AxisLabelPositioner zAxisLabelPositioner = axesDrawer.getZAxisLabelPositioner(axes);
285             zAxisLabelPositioner.setLabelPosition(new Vector3d(xs, ys, 0));
286
287             if (axes.getZAxisVisible()) {
288                 rulerDrawingResult = rulerDrawers[2].computeRuler(rulerModel, canvasProjection);
289                 values = rulerDrawingResult.getTicksValues();
290                 if (axes.getZAxisAutoTicks()) {
291                     Arrays.sort(values);
292                     GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Z_AXIS_TICKS_LOCATIONS__, toDoubleArray(values));
293                     GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Z_AXIS_TICKS_LABELS__, toStringArray(values, rulerDrawingResult.getFormat()));
294                     if (axes.getAutoSubticks()) {
295                         GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_Z_AXIS_SUBTICKS__, rulerDrawingResult.getSubTicksDensity());
296                     }
297                 }
298
299                 distanceRatio = rulerDrawingResult.getMaxDistToTicksDirNorm();
300
301                 zAxisLabelPositioner.setTicksDirection(new Vector3d(txs, tys, 0.0));
302                 zAxisLabelPositioner.setDistanceRatio(distanceRatio);
303                 zAxisLabelPositioner.setProjectedTicksDirection(rulerDrawingResult.getNormalizedTicksDirection().setZ(0).setY(1e-7));
304             } else {
305                 /* z-axis not visible: compute the projected ticks direction and distance ratio (see the x-axis case). */
306                 Vector3d zTicksDirection = new Vector3d(txs, tys, 0);
307
308                 zAxisLabelPositioner.setTicksDirection(zTicksDirection);
309                 Vector3d projTicksDir = canvasProjection.projectDirection(zTicksDirection);
310                 zAxisLabelPositioner.setDistanceRatio((double) TICKS_LENGTH / projTicksDir.getNorm());
311                 zAxisLabelPositioner.setProjectedTicksDirection(projTicksDir.getNormalized().setZ(0));
312             }
313         }
314     }
315
316     /**
317      * Draw the ruler.
318      * @param axes the current {@see Axes}
319      * @param axesDrawer the drawer used to draw the current {@see Axes}
320      * @param colorMap current {@see ColorMap}
321      * @param drawingTools the used {@see DrawingTools}
322      * @throws org.scilab.forge.scirenderer.SciRendererException if draw fail.
323      */
324     public void drawRuler(Axes axes, AxesDrawer axesDrawer, ColorMap colorMap, DrawingTools drawingTools) throws SciRendererException {
325         Appearance gridAppearance = new Appearance();
326
327         Double[] bounds = axes.getDisplayedBounds();
328         double[] matrix = drawingTools.getTransformationManager().getModelViewStack().peek().getMatrix();
329
330         RulerDrawer[] rulerDrawers = rulerDrawerManager.get(axes);
331         ElementsBuffer vertexBuffer = drawingTools.getCanvas().getBuffersManager().createElementsBuffer();
332         final boolean is3D = axes.getViewAsEnum() == Camera.ViewType.VIEW_3D;// && axes.getRotationAngles()[1] != 90.0;
333
334         if (rulerDrawers[0].getModel() == null || rulerDrawers[1].getModel() == null || (is3D && rulerDrawers[2].getModel() == null)) {
335             computeRulers(axes, axesDrawer, colorMap, drawingTools.getTransformationManager().getModelViewStack().peek(), drawingTools.getTransformationManager().getCanvasProjection());
336         }
337
338         int gridPosition;
339         if (axes.getGridPositionAsEnum().equals(Axes.GridPosition.FOREGROUND)) {
340             gridPosition = 1;
341         } else {
342             gridPosition = -1;
343         }
344
345         // Draw X ruler
346         if (axes.getXAxisVisible()) {
347             rulerDrawers[0].draw(drawingTools);
348
349             if (axes.getXAxisGridColor() != -1) {
350                 FloatBuffer vertexData;
351                 if (axes.getXAxisLogFlag()) {
352                     List<Double> values = rulerDrawers[0].getSubTicksValue();
353                     if (values == null || values.isEmpty()) {
354                         vertexData = getXGridData(rulerDrawers[0].getTicksValue(), rulerDrawers[0].getModel());
355                     } else {
356                         vertexData = getXGridData(values, rulerDrawers[0].getModel());
357                     }
358                 } else {
359                     vertexData = getXGridData(rulerDrawers[0].getTicksValue(), rulerDrawers[0].getModel());
360                 }
361                 vertexBuffer.setData(vertexData, 4);
362
363                 Transformation mirror;
364                 try {
365                     mirror = TransformationFactory.getScaleTransformation(
366                                  1,
367                                  matrix[6] < 0 ? gridPosition : -gridPosition,
368                                  matrix[10] < 0 ? gridPosition : -gridPosition
369                              );
370                 } catch (DegenerateMatrixException ignored) {
371                     // Should never happens as long as gridPosition the value 1 or -1
372                     mirror = TransformationFactory.getIdentity();
373                 }
374
375                 gridAppearance.setLineColor(ColorFactory.createColor(colorMap, axes.getXAxisGridColor()));
376                 gridAppearance.setLineWidth(axes.getXAxisGridThickness().floatValue());
377                 gridAppearance.setLinePattern(Line.LineType.fromScilabIndex(axes.getXAxisGridStyle()).asPattern());
378                 drawingTools.getTransformationManager().getModelViewStack().pushRightMultiply(mirror);
379                 DefaultGeometry gridGeometry = new DefaultGeometry();
380                 gridGeometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
381                 gridGeometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
382                 gridGeometry.setVertices(vertexBuffer);
383                 drawingTools.draw(gridGeometry, gridAppearance);
384                 drawingTools.getTransformationManager().getModelViewStack().pop();
385             }
386         }
387
388         // Draw Y ruler
389         if (axes.getYAxisVisible()) {
390             rulerDrawers[1].draw(drawingTools);
391
392             if (axes.getYAxisGridColor() != -1) {
393                 FloatBuffer vertexData;
394                 if (axes.getYAxisLogFlag()) {
395                     List<Double> values = rulerDrawers[1].getSubTicksValue();
396                     if (values == null || values.isEmpty()) {
397                         vertexData = getYGridData(rulerDrawers[1].getTicksValue(), rulerDrawers[1].getModel());
398                     } else {
399                         vertexData = getYGridData(values, rulerDrawers[1].getModel());
400                     }
401                 } else {
402                     vertexData = getYGridData(rulerDrawers[1].getTicksValue(), rulerDrawers[1].getModel());
403                 }
404                 vertexBuffer.setData(vertexData, 4);
405
406                 Transformation mirror;
407                 try {
408                     mirror = TransformationFactory.getScaleTransformation(
409                                  matrix[2] < 0 ? gridPosition : -gridPosition,
410                                  1,
411                                  matrix[10] < 0 ? gridPosition : -gridPosition
412                              );
413                 } catch (DegenerateMatrixException ignored) {
414                     // Should never happens as long as gridPosition the value 1 or -1
415                     mirror = TransformationFactory.getIdentity();
416                 }
417
418                 gridAppearance.setLineColor(ColorFactory.createColor(colorMap, axes.getYAxisGridColor()));
419                 gridAppearance.setLineWidth(axes.getYAxisGridThickness().floatValue());
420                 gridAppearance.setLinePattern(Line.LineType.fromScilabIndex(axes.getYAxisGridStyle()).asPattern());
421                 drawingTools.getTransformationManager().getModelViewStack().pushRightMultiply(mirror);
422                 DefaultGeometry gridGeometry = new DefaultGeometry();
423                 gridGeometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
424                 gridGeometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
425                 gridGeometry.setVertices(vertexBuffer);
426                 drawingTools.draw(gridGeometry, gridAppearance);
427                 drawingTools.getTransformationManager().getModelViewStack().pop();
428             }
429         }
430
431         // Draw Z ruler
432         if (is3D) {
433             if (axes.getZAxisVisible()) {
434                 rulerDrawers[2].draw(drawingTools);
435
436                 if (axes.getZAxisGridColor() != -1) {
437                     FloatBuffer vertexData;
438                     if (axes.getZAxisLogFlag()) {
439                         List<Double> values = rulerDrawers[2].getSubTicksValue();
440                         if (values == null || values.isEmpty()) {
441                             vertexData = getZGridData(rulerDrawers[2].getTicksValue(), rulerDrawers[2].getModel());
442                         } else {
443                             vertexData = getZGridData(values, rulerDrawers[2].getModel());
444                         }
445                     } else {
446                         vertexData = getZGridData(rulerDrawers[2].getTicksValue(), rulerDrawers[2].getModel());
447                     }
448                     vertexBuffer.setData(vertexData, 4);
449
450                     Transformation mirror;
451                     try {
452                         mirror = TransformationFactory.getScaleTransformation(
453                                      matrix[2] < 0 ? gridPosition : -gridPosition,
454                                      matrix[6] < 0 ? gridPosition : -gridPosition,
455                                      1
456                                  );
457                     } catch (DegenerateMatrixException ignored) {
458                         // Should never happens as long as gridPosition the value 1 or -1
459                         mirror = TransformationFactory.getIdentity();
460                     }
461
462                     gridAppearance.setLineColor(ColorFactory.createColor(colorMap, axes.getZAxisGridColor()));
463                     gridAppearance.setLineWidth(axes.getZAxisGridThickness().floatValue());
464                     gridAppearance.setLinePattern(Line.LineType.fromScilabIndex(axes.getZAxisGridStyle()).asPattern());
465                     drawingTools.getTransformationManager().getModelViewStack().pushRightMultiply(mirror);
466                     DefaultGeometry gridGeometry = new DefaultGeometry();
467                     gridGeometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
468                     gridGeometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
469                     gridGeometry.setVertices(vertexBuffer);
470                     drawingTools.draw(gridGeometry, gridAppearance);
471                     drawingTools.getTransformationManager().getModelViewStack().pop();
472                 }
473             }
474         }
475
476         drawingTools.getCanvas().getBuffersManager().dispose(vertexBuffer);
477     }
478
479     private static final double getNonZeroSignum(double value) {
480         if (value < 0) {
481             return -1;
482         } else {
483             return 1;
484         }
485     }
486
487     private void setRulerBounds(AxisProperty axis, DefaultRulerModel rulerModel, double axisMin, double axisMax) {
488         double min, max;
489         if (axis.getReverse()) {
490             min = axisMin;
491             max = axisMax;
492         } else {
493             min = axisMax;
494             max = axisMin;
495         }
496
497         if (axis.getLogFlag()) {
498             min = Math.pow(10, min);
499             max = Math.pow(10, max);
500         }
501         rulerModel.setValues(min, max);
502     }
503
504     private Vector3d computeXAxisPosition(double[] projectionMatrix, Double[] bounds, AxisProperty.AxisLocation axisLocation, boolean reverse) {
505         double y, z;
506         switch (axisLocation) {
507             default:
508             case BOTTOM:
509                 z = -Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was minimal.
510                 y = -Math.signum(projectionMatrix[6]) * z * Math.signum(projectionMatrix[10]);
511                 if (y == 0) {
512                     y = +1;
513                 }
514                 break;
515             case MIDDLE:
516                 z = Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was maximal.
517                 y = 0;
518                 break;
519             case TOP:
520                 z = Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was maximal.
521                 y = -Math.signum(projectionMatrix[6]) * z * Math.signum(projectionMatrix[10]);
522                 if (y == 0) {
523                     y = -1;
524                 }
525                 break;
526             case ORIGIN:
527                 z = Math.signum(projectionMatrix[9]);  // First : switch Z such that Y was maximal.
528                 y = (reverse ? -1 : 1) * (bounds[3] + bounds[2]) / (bounds[3] - bounds[2]);
529                 if (Math.abs(y) > 1) {
530                     y = Math.signum(y);
531                 }
532                 break;
533         }
534         return new Vector3d(0, y, z);
535     }
536
537     private Vector3d computeYAxisPosition(double[] matrix, Double[] bounds, AxisProperty.AxisLocation axisLocation, boolean reverse) {
538         double x, z;
539         switch (axisLocation) {
540             default:
541             case LEFT:
542                 z = -Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
543                 x = -Math.signum(matrix[2]) * z * Math.signum(matrix[10]);
544                 if (x == 0) {
545                     x = +1;
546                 }
547                 break;
548             case MIDDLE:
549                 z = Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
550                 x = 0;
551                 break;
552             case RIGHT:
553                 z = Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
554                 x = -Math.signum(matrix[2]) * z * Math.signum(matrix[10]); // Then switch X such that Z max but not in the middle.
555                 if (x == 0) {
556                     x = -1;
557                 }
558                 break;
559             case ORIGIN:
560                 z = Math.signum(matrix[9]);  // First : switch Z such that Y was minimal.
561                 x = (reverse ? -1 : 1) * (bounds[1] + bounds[0]) / (bounds[1] - bounds[0]);
562                 if (Math.abs(x) > 1) {
563                     x = Math.signum(x);
564                 }
565                 break;
566         }
567         return new Vector3d(x, 0, z);
568     }
569
570     private String[] toStringArray(double[] values, DecimalFormat format) {
571         AxesRulerSpriteFactory.setScilabStyle(format);
572         String[] r = new String[values.length];
573         for (int i = 0; i < values.length; i++) {
574             r[i] = format.format(values[i]);
575         }
576         return r;
577     }
578
579     private Double[] toDoubleArray(double[] values) {
580         Double[] r = new Double[values.length];
581         for (int i = 0; i < values.length; i++) {
582             r[i] = values[i];
583         }
584         return r;
585     }
586
587     /**
588      * Build X grid data.
589      * @param values X values where grid is drawn.
590      * @param rulerModel used rulerModel to compute grid world position.
591      * @return X grid data.
592      */
593     private FloatBuffer getXGridData(List<Double> values, RulerModel rulerModel) {
594         FloatBuffer vertexData = FloatBuffer.allocate(values.size() * 16);
595         for (double value : values) {
596             float p = (float) rulerModel.getPosition(value).getX();
597             vertexData.put(p);
598             vertexData.put(+1);
599             vertexData.put(+1);
600             vertexData.put(1);
601             vertexData.put(p);
602             vertexData.put(-1);
603             vertexData.put(+1);
604             vertexData.put(1);
605             vertexData.put(p);
606             vertexData.put(+1);
607             vertexData.put(+1);
608             vertexData.put(1);
609             vertexData.put(p);
610             vertexData.put(+1);
611             vertexData.put(-1);
612             vertexData.put(1);
613         }
614         vertexData.rewind();
615         return vertexData;
616     }
617
618     /**
619      * Build Y grid data.
620      * @param values Y values where grid is drawn.
621      * @param rulerModel used rulerModel to compute grid world position.
622      * @return Y grid data.
623      */
624     private FloatBuffer getYGridData(List<Double> values, RulerModel rulerModel) {
625         FloatBuffer vertexData = FloatBuffer.allocate(values.size() * 16);
626         for (double value : values) {
627             float p = (float) rulerModel.getPosition(value).getY();
628             vertexData.put(+1);
629             vertexData.put(p);
630             vertexData.put(+1);
631             vertexData.put(1);
632             vertexData.put(-1);
633             vertexData.put(p);
634             vertexData.put(+1);
635             vertexData.put(1);
636             vertexData.put(+1);
637             vertexData.put(p);
638             vertexData.put(+1);
639             vertexData.put(1);
640             vertexData.put(+1);
641             vertexData.put(p);
642             vertexData.put(-1);
643             vertexData.put(1);
644         }
645         vertexData.rewind();
646         return vertexData;
647     }
648
649     /**
650      * Build Z grid data.
651      * @param values Z values where grid is drawn.
652      * @param rulerModel used rulerModel to compute grid world position.
653      * @return Z grid data.
654      */
655     private FloatBuffer getZGridData(List<Double> values, RulerModel rulerModel) {
656         FloatBuffer vertexData = FloatBuffer.allocate(values.size() * 16);
657         int limit = 0;
658         for (double value : values) {
659             float p = (float) rulerModel.getPosition(value).getZ();
660             vertexData.put(+1);
661             vertexData.put(+1);
662             vertexData.put(p);
663             vertexData.put(1);
664             vertexData.put(-1);
665             vertexData.put(+1);
666             vertexData.put(p);
667             vertexData.put(1);
668             vertexData.put(+1);
669             vertexData.put(+1);
670             vertexData.put(p);
671             vertexData.put(1);
672             vertexData.put(+1);
673             vertexData.put(-1);
674             vertexData.put(p);
675             vertexData.put(1);
676             limit += 16;
677         }
678         vertexData.limit(limit);
679         return vertexData;
680     }
681
682     public void disposeAll() {
683         this.rulerDrawerManager.disposeAll();
684     }
685
686     public boolean update(Integer id, int property) {
687         return this.rulerDrawerManager.update(id, property);
688     }
689
690     public void dispose(Integer id) {
691         this.rulerDrawerManager.dispose(id);
692     }
693 }