5af7b60ea38a8e72011e45e2609265e3178bf4f6
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / JoGLView / axes / AxesDrawer.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) 2013 - Scilab Enterprises - Calixte DENIZET
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11  */
12
13 package org.scilab.modules.renderer.JoGLView.axes;
14
15 import org.scilab.forge.scirenderer.DrawingTools;
16 import org.scilab.forge.scirenderer.SciRendererException;
17 import org.scilab.forge.scirenderer.clipping.ClippingPlane;
18 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
19 import org.scilab.forge.scirenderer.shapes.geometry.Geometry.FaceCullingMode;
20 import org.scilab.forge.scirenderer.tranformations.DegenerateMatrixException;
21 import org.scilab.forge.scirenderer.tranformations.Transformation;
22 import org.scilab.forge.scirenderer.tranformations.TransformationFactory;
23 import org.scilab.forge.scirenderer.tranformations.TransformationStack;
24 import org.scilab.forge.scirenderer.tranformations.Vector3d;
25 import org.scilab.forge.scirenderer.tranformations.Vector4d;
26 import org.scilab.modules.graphic_objects.axes.Axes;
27 import org.scilab.modules.graphic_objects.axes.Box;
28 import org.scilab.modules.graphic_objects.contouredObject.Line;
29 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
30 import org.scilab.modules.graphic_objects.graphicObject.GraphicObject;
31 import org.scilab.modules.graphic_objects.graphicObject.ClippableProperty;
32 import org.scilab.modules.graphic_objects.graphicObject.ClippableProperty.ClipStateType;
33 import org.scilab.modules.graphic_objects.figure.ColorMap;
34 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
35 import org.scilab.modules.renderer.JoGLView.axes.ruler.AxesRulerDrawer;
36 import org.scilab.modules.renderer.JoGLView.label.AxisLabelPositioner;
37 import org.scilab.modules.renderer.JoGLView.label.LabelManager;
38 import org.scilab.modules.renderer.JoGLView.label.LabelPositioner;
39 import org.scilab.modules.renderer.JoGLView.label.TitlePositioner;
40 import org.scilab.modules.renderer.JoGLView.label.YAxisLabelPositioner;
41 import org.scilab.modules.renderer.JoGLView.util.ColorFactory;
42 import org.scilab.modules.renderer.JoGLView.util.ScaleUtils;
43
44 import java.awt.Dimension;
45 import java.awt.geom.Rectangle2D;
46 import java.util.HashMap;
47 import java.util.Map;
48
49 /**
50  *
51  * AxesDrawer are used by {@see DrawerVisitor} to draw {@see Axes}.
52  *
53  * @author Pierre Lando
54  */
55 public class AxesDrawer {
56     private static final double DEFAULT_THETA = 270.0;
57     private static final Line.LineType HIDDEN_BORDER_PATTERN = Line.LineType.DASH;
58
59     /** An epsilon value used to move the clipping planes in order to prevent strict clipping. */
60     private static final double CLIPPING_EPSILON = 1e-5;
61
62     private final DrawerVisitor visitor;
63     private final Geometries geometries;
64
65     private final AxesRulerDrawer rulerDrawer;
66
67     /** The front face culling mode. */
68     private FaceCullingMode frontFaceCullingMode;
69
70     /** The back face culling mode. */
71     private FaceCullingMode backFaceCullingMode;
72
73     /** The label manager. */
74     private final LabelManager labelManager;
75
76     /** The x-axis label positioner. */
77     private final Map<String, AxisLabelPositioner> xAxisLabelPositioner = new HashMap<String, AxisLabelPositioner>();
78
79     /** The y-axis label positioner. */
80     private final Map<String, AxisLabelPositioner> yAxisLabelPositioner = new HashMap<String, AxisLabelPositioner>();
81
82     /** The z-axis label positioner. */
83     private final Map<String, AxisLabelPositioner> zAxisLabelPositioner = new HashMap<String, AxisLabelPositioner>();
84
85     /** The title positioner. */
86     private final Map<String, TitlePositioner> titlePositioner = new HashMap<String, TitlePositioner>();
87
88     /**
89      * The current reversed bounds. Used by the functions converting
90      * between object and box coordinates.
91      */
92     private double[] reversedBounds;
93
94     /** The current reversed bounds intervals. */
95     private double[] reversedBoundsIntervals;
96
97     /** The current projection (from object to window coordinates) used when drawing objects. */
98     private Transformation currentProjection;
99
100     /**  The current data scale and translate transformation. */
101     private Transformation currentDataTransformation;
102
103     /** The set of object to window coordinate projections associated to all the Axes drawn by this drawer. */
104     private final Map<String, Transformation> projectionMap = new HashMap<String, Transformation>();
105
106     /** The set of object (in 2d view mode) to window coordinate projections associated to all the Axes drawn by this drawer. */
107     private final Map<String, Transformation> projection2dViewMap = new HashMap<String, Transformation>();
108
109     /** This is a __MAP__ */
110     private final Map<String, Transformation> sceneProjectionMap = new HashMap<String, Transformation>();
111
112     /**
113      * Default constructor.
114      * @param visitor the parent {@see DrawerVisitor}.
115      */
116     public AxesDrawer(DrawerVisitor visitor) {
117         this.visitor = visitor;
118         this.labelManager = visitor.getLabelManager();
119         this.geometries = new Geometries(visitor.getCanvas());
120         this.rulerDrawer = new AxesRulerDrawer(visitor.getCanvas());
121
122         reversedBounds = new double[6];
123         reversedBoundsIntervals = new double[3];
124     }
125
126     public Transformation getCurrentProjection(Axes axes) throws DegenerateMatrixException {
127         DrawingTools drawingTools = visitor.getDrawingTools();
128         Integer[] size = visitor.getFigure().getAxesSize();
129         Transformation zoneProjection = computeZoneProjection(axes);
130         Transformation transformation = computeBoxTransformation(axes, new Dimension(size[0], size[1]), false);
131         Transformation dataTransformation = computeDataTransformation(axes);
132         Transformation windowTrans = drawingTools.getTransformationManager().getWindowTransformation().getInverseTransformation();
133         Transformation current = zoneProjection.rightTimes(transformation);
134         current = current.rightTimes(dataTransformation);
135
136         return windowTrans.rightTimes(current);
137     }
138
139     /**
140      * Compute the graduations on the axes
141      * @param axes the axes
142      */
143     public void computeRulers(Axes axes) {
144         DrawingTools drawingTools = visitor.getDrawingTools();
145         ColorMap colorMap = visitor.getColorMap();
146         try {
147             Integer[] size = visitor.getFigure().getAxesSize();
148             double w = ((double) (int) size[0]) / 2;
149             double h = ((double) (int) size[1]) / 2;
150             Transformation windowTrans = TransformationFactory.getAffineTransformation(new Vector3d(w, h, 1), new Vector3d(w, h, 0));
151             Transformation zoneProjection = computeZoneProjection(axes);
152             Transformation transformation = computeBoxTransformation(axes, new Dimension(size[0], size[1]), false);
153             Transformation canvasTrans = windowTrans.rightTimes(zoneProjection).rightTimes(transformation);
154
155             rulerDrawer.computeRulers(axes, this, colorMap, drawingTools, transformation, canvasTrans);
156         } catch (DegenerateMatrixException e) {
157
158         }
159     }
160
161     /**
162      * Draw the given {@see Axes}.
163      * @param axes {@see Axes} to draw.
164      * @throws org.scilab.forge.scirenderer.SciRendererException if the draw fail.
165      */
166     public void draw(Axes axes) throws SciRendererException {
167         DrawingTools drawingTools = visitor.getDrawingTools();
168         Integer[] size = visitor.getFigure().getAxesSize();
169         Dimension canvasDimension = new Dimension(size[0], size[1]);
170         ColorMap colorMap = visitor.getColorMap();
171         TransformationStack modelViewStack = drawingTools.getTransformationManager().getModelViewStack();
172         TransformationStack projectionStack = drawingTools.getTransformationManager().getProjectionStack();
173
174         // Set axes zone.
175         Transformation zoneProjection = computeZoneProjection(axes);
176         projectionStack.push(zoneProjection);
177
178         // Set box projection.
179         Transformation transformation = computeBoxTransformation(axes, canvasDimension, false);
180         modelViewStack.pushRightMultiply(transformation);
181
182         /* Compute the data scale and translate transformation. */
183         Transformation dataTransformation = computeDataTransformation(axes);
184
185         currentDataTransformation = dataTransformation;
186
187         /* Compute the object to window coordinates projection. */
188         currentProjection = zoneProjection.rightTimes(transformation);
189         currentProjection = currentProjection.rightTimes(dataTransformation);
190
191         sceneProjectionMap.put(axes.getIdentifier(), currentProjection);
192
193         Transformation windowTrans = drawingTools.getTransformationManager().getInverseWindowTransformation();
194         currentProjection = windowTrans.rightTimes(currentProjection);
195
196         /* Update the projection maps with the resulting projections. */
197         addProjection(axes.getIdentifier(), currentProjection);
198
199         /* 2d view projection, to do: optimize computation */
200         if (axes.getRotationAngles()[0] != 0 || axes.getRotationAngles()[1] != DEFAULT_THETA) {
201             Transformation transformation2dView = computeBoxTransformation(axes, canvasDimension, true);
202             currentProjection = zoneProjection.rightTimes(transformation2dView);
203             currentProjection = currentProjection.rightTimes(dataTransformation);
204             currentProjection = windowTrans.rightTimes(currentProjection);
205         }
206
207         addProjection2dView(axes.getIdentifier(), currentProjection);
208
209         /**
210          * Draw the axes background.
211          */
212         drawBackground(axes, drawingTools, colorMap);
213
214         /**
215          * Mirror the transformation such that the corner with the maximum Z value was (-1, -1, -1).
216          * And draw the box.
217          */
218         Transformation cubeOrientation = computeCubeMirroring(transformation);
219         modelViewStack.pushRightMultiply(cubeOrientation);
220         drawBox(axes, drawingTools, colorMap);
221         modelViewStack.pop();
222
223         // Ruler are drawn in box coordinate.
224         rulerDrawer.drawRuler(axes, this, colorMap, drawingTools);
225
226         /* Compute reversed bounds. */
227         computeReversedBounds(axes);
228
229         /**
230          * Scale and translate for data fitting.
231          * And draw data.
232          */
233         modelViewStack.pushRightMultiply(dataTransformation);
234
235         /* Compute the front and back culling modes */
236         computeFaceCullingModes(axes);
237
238         visitor.askAcceptVisitor(axes.getChildren());
239         modelViewStack.pop();
240
241         // Reset transformation stacks.
242         modelViewStack.pop();
243         projectionStack.pop();
244     }
245
246     /**
247      * Draw the axes background.
248      * @param axes the {@see Axes}
249      * @param drawingTools the {@see DrawingTools} to use.
250      * @param colorMap the current {@see ColorMap}
251      * @throws org.scilab.forge.scirenderer.SciRendererException if the draw fail.
252      */
253     private void drawBackground(Axes axes, DrawingTools drawingTools, ColorMap colorMap) throws SciRendererException {
254         if (axes.getFilled()) {
255             Appearance appearance = new Appearance();
256             appearance.setFillColor(ColorFactory.createColor(colorMap, axes.getBackground()));
257             drawingTools.draw(geometries.getCubeGeometry(), appearance);
258             drawingTools.clearDepthBuffer();
259         }
260     }
261
262     /**
263      * Draw the box of the given {@see Axes}
264      * @param axes the given {@see Axes}.
265      * @param drawingTools the {@see DrawingTools} to use.
266      * @param colorMap the current {@see ColorMap}.
267      * @throws org.scilab.forge.scirenderer.SciRendererException if the draw fail.
268      */
269     private void drawBox(Axes axes, DrawingTools drawingTools, ColorMap colorMap) throws SciRendererException {
270         Box.BoxType boxed = axes.getBox().getBox();
271         if (boxed != Box.BoxType.OFF) {
272             Appearance appearance = new Appearance();
273
274             /**
275              * Draw hidden part of box.
276              */
277             if (!visitor.is2DView()) {
278                 appearance.setLineColor(ColorFactory.createColor(colorMap, axes.getHiddenAxisColor()));
279                 appearance.setLineWidth(axes.getLineThickness().floatValue());
280                 appearance.setLinePattern(HIDDEN_BORDER_PATTERN.asPattern());
281                 drawingTools.draw(geometries.getHiddenBoxBorderGeometry(), appearance);
282             }
283
284             if (boxed != Box.BoxType.HIDDEN_AXES) {
285
286                 /**
287                  * Draw box border.
288                  */
289                 appearance.setLineColor(ColorFactory.createColor(colorMap, axes.getLineColor()));
290                 appearance.setLineWidth(axes.getLineThickness().floatValue());
291                 appearance.setLinePattern(axes.getLine().getLineStyle().asPattern());
292                 drawingTools.draw(geometries.getBoxBorderGeometry(), appearance);
293
294
295                 if (boxed != Box.BoxType.BACK_HALF) {
296                     /**
297                      * Draw front part of box.
298                      */
299                     drawingTools.draw(geometries.getFrontBoxBorderGeometry(), appearance);
300                 }
301             }
302         }
303     }
304
305     /**
306      * Compute the mirroring matrix needed to have the [-1; -1; -1] point projected with the maximal Z value.
307      * @param transformation the current transformation.
308      * @return the mirroring matrix needed to have the [-1; -1; -1] point projected with the maximal Z value.
309      */
310     private Transformation computeCubeMirroring(Transformation transformation) {
311         double[] matrix = transformation.getMatrix();
312         try {
313             return TransformationFactory.getScaleTransformation(
314                        matrix[2] < 0 ? 1 : -1,
315                        matrix[6] < 0 ? 1 : -1,
316                        matrix[10] < 0 ? 1 : -1
317                    );
318         } catch (DegenerateMatrixException e) {
319             // Should never happen.
320             return TransformationFactory.getIdentity();
321         }
322     }
323
324     /**
325      * Compute zone where the axes is draw. In normalized window coordinate.
326      * @param axes the given {@see axes}.
327      * @return the zone where the axes is draw.
328      */
329     private Rectangle2D computeZone(Axes axes) {
330         Double[] axesBounds = axes.getAxesBounds();
331         Double[] margins = axes.getMargins();
332
333         // TODO :  zoom box.
334         double x = (axesBounds[0] + axesBounds[2] * margins[0]) * 2 - 1;
335         double y = (1.0 - axesBounds[1] - axesBounds[3] * (1.0 - margins[3])) * 2 - 1;
336         double w = (1 - margins[0] - margins[1]) * axesBounds[2];
337         double h = (1 - margins[2] - margins[3]) * axesBounds[3];
338
339         if (axes.getIsoview()) {
340             double minSize = Math.min(w, h);
341             y += (h - minSize);
342             h = minSize;
343             x += (w - minSize);
344             w = minSize;
345         }
346
347         return new Rectangle2D.Double(x, y, w, h);
348     }
349
350     /**
351      * Compute the projection for the given axes.
352      * @param axes the given axes.
353      * @return the projection matrix.
354      * @throws DegenerateMatrixException if axes represent a nul area.
355      */
356     private Transformation computeZoneProjection(Axes axes) throws DegenerateMatrixException {
357         Rectangle2D zone = computeZone(axes);
358         Transformation zoneTranslation = TransformationFactory.getTranslateTransformation(zone.getMaxX(), zone.getMaxY(), 0);
359
360         // We scale by 0.5 in Z to allow ruler to be drawn.
361         Transformation zoneScale = TransformationFactory.getScaleTransformation(zone.getWidth(), zone.getHeight(), .5);
362
363         return zoneTranslation.rightTimes(zoneScale);
364     }
365
366     /**
367      * Compute data transformation for the given {@see Axes}.
368      *
369      * The data transformation is applied to data to fit in axes box.
370      *
371      * @param axes the given {@see Axes}
372      * @return data transformation.
373      * @throws DegenerateMatrixException if data bounds are not corrects.
374      */
375     private Transformation computeDataTransformation(Axes axes) throws DegenerateMatrixException {
376         Double[] bounds = axes.getDisplayedBounds();
377
378         // Reverse data if needed.
379         Transformation transformation = TransformationFactory.getScaleTransformation(
380                                             axes.getAxes()[0].getReverse() ? 1 : -1,
381                                             axes.getAxes()[1].getReverse() ? 1 : -1,
382                                             axes.getAxes()[2].getReverse() ? 1 : -1
383                                         );
384
385         // Scale data.
386         Transformation scaleTransformation = TransformationFactory.getScaleTransformation(
387                 2.0 / (bounds[1] - bounds[0]),
388                 2.0 / (bounds[3] - bounds[2]),
389                 2.0 / (bounds[5] - bounds[4])
390                                              );
391         transformation = transformation.rightTimes(scaleTransformation);
392
393
394         // Translate data.
395         Transformation translateTransformation = TransformationFactory.getTranslateTransformation(
396                     -(bounds[0] + bounds[1]) / 2.0,
397                     -(bounds[2] + bounds[3]) / 2.0,
398                     -(bounds[4] + bounds[5]) / 2.0
399                 );
400         transformation = transformation.rightTimes(translateTransformation);
401         return transformation;
402     }
403
404
405     /**
406      * Compute box transformation for the given axes.
407      *
408      * The box transformation is applied to the axes box to fit in the canvas.
409      *
410      * @param axes the given {@see Axes}.
411      * @param canvasDimension the current canvas {@see Canvas}.
412      * @param use2dView specifies whether the default 2d view rotation angles must be used (true) or the given Axes' ones (false).
413      * @return box transformation for the given axes.
414      * @throws DegenerateMatrixException if data bounds are incorrect or canvas with or length are zero.
415      */
416     private Transformation computeBoxTransformation(Axes axes, Dimension canvasDimension, boolean use2dView) throws DegenerateMatrixException {
417         Double[] bounds = axes.getDisplayedBounds();
418
419         double theta;
420
421         double tmpX;
422         double tmpY;
423         double tmpZ;
424
425         Transformation transformation;
426
427         // Set zone aspect ratio.
428         Rectangle2D zone = computeZone(axes);
429         double axesRatio = zone.getWidth() / zone.getHeight();
430         transformation = TransformationFactory.getPreferredAspectRatioTransformation(canvasDimension, axesRatio);
431
432         // Rotate.
433         if (use2dView) {
434             theta = 2 * DEFAULT_THETA;
435         } else {
436             double alpha = -axes.getRotationAngles()[0];
437             theta = DEFAULT_THETA + axes.getRotationAngles()[1];
438             if (alpha != 0) {
439                 Transformation alphaRotation = TransformationFactory.getRotationTransformation(alpha, 1.0, 0.0, 0.0);
440                 transformation = transformation.rightTimes(alphaRotation);
441             }
442         }
443
444         Transformation thetaRotation = TransformationFactory.getRotationTransformation(theta, 0.0, 0.0, 1.0);
445         transformation = transformation.rightTimes(thetaRotation);
446
447         // If there is no cube scaling, we must take into account the distribution of data.
448         if (!axes.getCubeScaling()) {
449             tmpX = (bounds[1] - bounds[0]);
450             tmpY = (bounds[3] - bounds[2]);
451             tmpZ = (bounds[5] - bounds[4]);
452
453             /**
454              * Here, we should divide the values by their maximum.
455              * But the next operation will automatically.
456              */
457             if (tmpX != 1 || tmpY != 1 || tmpZ != 1) {
458                 Transformation cubeScale = TransformationFactory.getScaleTransformation(tmpX, tmpY, tmpZ);
459                 transformation = transformation.rightTimes(cubeScale);
460             }
461         }
462
463         // Compute bounds of projected data.
464         double[] matrix = transformation.getMatrix();
465         tmpX = 1 / (Math.abs(matrix[0]) + Math.abs(matrix[4]) + Math.abs(matrix[8]));
466         tmpY = 1 / (Math.abs(matrix[1]) + Math.abs(matrix[5]) + Math.abs(matrix[9]));
467         tmpZ = 1 / (Math.abs(matrix[2]) + Math.abs(matrix[6]) + Math.abs(matrix[10]));
468
469         // Scale projected data to fit in the cube.
470         Transformation isoScale;
471         if (axes.getIsoview()) {
472             double w = zone.getWidth();
473             double h = zone.getHeight();
474             double minScale = Math.min(tmpX * w, tmpY * h);
475             isoScale = TransformationFactory.getScaleTransformation(minScale / w, minScale / h, tmpZ);
476         } else {
477             isoScale = TransformationFactory.getScaleTransformation(tmpX, tmpY, tmpZ);
478         }
479         transformation = transformation.leftTimes(isoScale);
480
481         return transformation;
482     }
483
484
485     /**
486      * Computes and sets the reversed bounds of the given Axes.
487      * @param axes the given {@see Axes}.
488      */
489     private void computeReversedBounds(Axes axes) {
490         Double[] currentBounds = axes.getDisplayedBounds();
491
492         /* Reverse */
493         if (axes.getAxes()[0].getReverse()) {
494             reversedBounds[0] = currentBounds[1];
495             reversedBounds[1] = currentBounds[0];
496         } else {
497             reversedBounds[0] = currentBounds[0];
498             reversedBounds[1] = currentBounds[1];
499         }
500
501         if (axes.getAxes()[1].getReverse()) {
502             reversedBounds[2] = currentBounds[3];
503             reversedBounds[3] = currentBounds[2];
504         } else {
505             reversedBounds[2] = currentBounds[2];
506             reversedBounds[3] = currentBounds[3];
507         }
508
509         if (axes.getAxes()[2].getReverse()) {
510             reversedBounds[4] = currentBounds[5];
511             reversedBounds[5] = currentBounds[4];
512         } else {
513             reversedBounds[4] = currentBounds[4];
514             reversedBounds[5] = currentBounds[5];
515         }
516
517         /*
518          * Interval values are set to 1 when bounds are equal to avoid divides by 0
519          * in the object to box coordinates conversion function.
520          */
521         if (reversedBounds[1] == reversedBounds[0]) {
522             reversedBoundsIntervals[0] = 1.0;
523         } else {
524             reversedBoundsIntervals[0] = reversedBounds[1] - reversedBounds[0];
525         }
526
527         if (reversedBounds[3] == reversedBounds[2]) {
528             reversedBoundsIntervals[1] = 1.0;
529         } else {
530             reversedBoundsIntervals[1] = reversedBounds[3] - reversedBounds[2];
531         }
532
533         if (reversedBounds[5] == reversedBounds[4]) {
534             reversedBoundsIntervals[2] = 1.0;
535         } else {
536             reversedBoundsIntervals[2] = reversedBounds[5] - reversedBounds[4];
537         }
538
539     }
540
541     /**
542      * Computes the culling modes respectively corresponding to front and back faces
543      * of the given Axes' child objects as a function of its {X,Y,Z} reverse properties.
544      * It must be called by draw prior to rendering any child object.
545      * @param axes the given {@see Axes}.
546      */
547     private void computeFaceCullingModes(Axes axes) {
548         if (axes.getAxes()[0].getReverse() ^ axes.getAxes()[1].getReverse() ^ axes.getAxes()[2].getReverse()) {
549             /* Front: CW */
550             this.frontFaceCullingMode = FaceCullingMode.CW;
551             this.backFaceCullingMode = FaceCullingMode.CCW;
552         } else {
553             /* Front: CCW */
554             this.frontFaceCullingMode = FaceCullingMode.CCW;
555             this.backFaceCullingMode = FaceCullingMode.CW;
556         }
557     }
558
559     /**
560      * Converts a point from object coordinates to box coordinates (used when drawing axis rulers).
561      * @param point the point in object coordinates.
562      * @return the point in box coordinates.
563      */
564     public Vector3d getBoxCoordinates(Vector3d point) {
565         double[] dataCoordinates = new double[3];
566
567         dataCoordinates[0] = 1 - 2.0 * (point.getX() - reversedBounds[0]) / reversedBoundsIntervals[0];
568         dataCoordinates[1] = 1 - 2.0 * (point.getY() - reversedBounds[2]) / reversedBoundsIntervals[1];
569         dataCoordinates[2] = 1 - 2.0 * (point.getZ() - reversedBounds[4]) / reversedBoundsIntervals[2];
570
571         return new Vector3d(dataCoordinates);
572     }
573
574     /**
575      * Converts a point from box coordinates (used when drawing axis rulers) to object coordinates.
576      * @param point the point in box coordinates.
577      * @return the point in object coordinates.
578      */
579     public Vector3d getObjectCoordinates(Vector3d point) {
580         double[] objectCoordinates = new double[3];
581
582         objectCoordinates[0] = 0.5 * (1.0 - point.getX()) * (reversedBounds[1] - reversedBounds[0]) + reversedBounds[0];
583         objectCoordinates[1] = 0.5 * (1.0 - point.getY()) * (reversedBounds[3] - reversedBounds[2]) + reversedBounds[2];
584         objectCoordinates[2] = 0.5 * (1.0 - point.getZ()) * (reversedBounds[5] - reversedBounds[4]) + reversedBounds[4];
585
586         return new Vector3d(objectCoordinates);
587     }
588
589     /**
590      * Computes and return the object to window coordinate projection corresponding to the given Axes object.
591      * @param axes the given Axes.
592      * @param drawingTools the drawing tools.
593      * @param canvasDimension the current canvas dimension.
594      * @param use2dView specifies whether the default 2d view rotation angles must be used (true) or the given Axes' ones (false).
595      * @return the projection
596      */
597     private Transformation computeProjection(Axes axes, DrawingTools drawingTools, Dimension canvasDimension, boolean use2dView) {
598         Transformation projection;
599
600         try {
601             /* Compute the zone projection. */
602             Transformation zoneProjection = computeZoneProjection(axes);
603
604             /* Compute the box transformation. */
605             Transformation transformation = computeBoxTransformation(axes, canvasDimension, use2dView);
606
607             /* Compute the data scale and translate transformation. */
608             Transformation dataTransformation = computeDataTransformation(axes);
609
610             /* Compute the object to window coordinates projection. */
611             projection = zoneProjection.rightTimes(transformation);
612             projection = projection.rightTimes(dataTransformation);
613
614             Transformation windowTrans = drawingTools.getTransformationManager().getWindowTransformation().getInverseTransformation();
615             projection = windowTrans.rightTimes(projection);
616         } catch (DegenerateMatrixException e) {
617             return TransformationFactory.getIdentity();
618         }
619
620         return projection;
621     }
622
623     /**
624      * Returns the current projection from object to window coordinates.
625      * @return the projection.
626      */
627     public Transformation getProjection() {
628         return currentProjection;
629     }
630
631     /**
632      * Returns the current data scale and translate transformation.
633      * @return the data transformation.
634      */
635     public Transformation getDataTransformation() {
636         return currentDataTransformation;
637     }
638
639     /**
640      * Adds the projection from object to window coordinates corresponding to a given Axes object
641      * to the projection map.
642      * @param axesId the identifier of the given Axes.
643      * @param projection the corresponding projection.
644      */
645     public synchronized void addProjection(String axesId, Transformation projection) {
646         projectionMap.put(axesId, projection);
647     }
648
649     /**
650      * Returns the projection from object to window coordinates corresponding
651      * to a given Axes object.
652      * @param id the identifier of the given Axes.
653      * @return the projection.
654      */
655     public Transformation getProjection(String id) {
656         return projectionMap.get(id);
657     }
658
659     /**
660      * Removes the object to window coordinate projection corresponding to a given Axes from
661      * the projection map.
662      * @param axesId the identifier of the given Axes.
663      */
664     public void removeProjection(String axesId) {
665         projectionMap.remove(axesId);
666     }
667
668     /**
669      * Adds the projection from object (in 2d view mode) to window coordinates corresponding to a given Axes object
670      * to the projection map.
671      * @param axesId the identifier of the given Axes.
672      * @param projection the corresponding projection.
673      */
674     public synchronized void addProjection2dView(String axesId, Transformation projection) {
675         projection2dViewMap.put(axesId, projection);
676     }
677
678     /**
679      * Returns the projection from object (in 2d view mode) to window coordinates corresponding
680      * to a given Axes object.
681      * @param id the identifier of the given Axes.
682      * @return the projection.
683      */
684     public Transformation getProjection2dView(String id) {
685         return projection2dViewMap.get(id);
686     }
687
688     public Transformation getSceneProjection(String id) {
689         return sceneProjectionMap.get(id);
690     }
691
692     /**
693      * Removes the object (in 2d view mode) to window coordinate projection corresponding to a given Axes from
694      * the projection map.
695      * @param axesId the identifier of the given Axes.
696      */
697     public void removeProjection2dView(String axesId) {
698         projection2dViewMap.remove(axesId);
699     }
700
701     /**
702      * Updates both the projection from object to window coordinates and the related
703      * object (in 2d view mode) to window coordinates projection for the given Axes object.
704      * @param axes the given Axes.
705      */
706     public static void updateAxesTransformation(Axes axes) {
707         DrawerVisitor currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
708         AxesDrawer axesDrawer = currentVisitor.getAxesDrawer();
709         Integer[] size = currentVisitor.getFigure().getAxesSize();
710         Dimension canvasDimension = new Dimension(size[0], size[1]);
711
712         Transformation transformation = axesDrawer.getProjection(axes.getIdentifier());
713
714         /* The projection must be updated */
715         if (transformation == null) {
716             Transformation projection = axesDrawer.computeProjection(axes, currentVisitor.getDrawingTools(), canvasDimension, false);
717
718             axesDrawer.addProjection(axes.getIdentifier(), projection);
719         }
720
721         Transformation transformation2dView = axesDrawer.getProjection2dView(axes.getIdentifier());
722
723         /* The projection must be updated */
724         if (transformation2dView == null) {
725             Transformation projection2dView = axesDrawer.computeProjection(axes, currentVisitor.getDrawingTools(), canvasDimension, true);
726
727             axesDrawer.addProjection2dView(axes.getIdentifier(), projection2dView);
728         }
729     }
730
731     /**
732      * Computes and returns the coordinates of a point projected onto the default 2d view plane.
733      * To compute them, the point is projected using the object to window coordinate projection, then
734      * unprojected using the object to window coordinate projection corresponding to the default 2d view
735      * (which uses the default camera rotation angles).
736      * To do: optimize by using the already computed 3d view projection.
737      * @param axes the given Axes.
738      * @param coordinates the object (x,y,z) coordinates to project onto the 2d view plane (3-element array).
739      * @returns the 2d view coordinates (3-element array).
740      */
741     public static double[] compute2dViewCoordinates(Axes axes, double[] coordinates) {
742         DrawerVisitor currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
743         AxesDrawer axesDrawer;
744         Transformation projection;
745         Transformation projection2d;
746
747         Vector3d point = new Vector3d(coordinates);
748
749         if (currentVisitor != null) {
750             Integer[] size = currentVisitor.getFigure().getAxesSize();
751             Dimension canvasDimension = new Dimension(size[0], size[1]);
752
753             axesDrawer = currentVisitor.getAxesDrawer();
754
755             projection = axesDrawer.computeProjection(axes, currentVisitor.getDrawingTools(), canvasDimension, false);
756             projection2d = axesDrawer.computeProjection(axes, currentVisitor.getDrawingTools(), canvasDimension, true);
757
758             point = projection.project(point);
759             point = projection2d.unproject(point);
760         }
761
762         return new double[] {point.getX(), point.getY(), point.getZ()};
763     }
764
765     /**
766      * Computes and returns the pixel coordinates from a point's coordinates expressed in the default
767      * 2d view coordinate frame, using the given Axes. The returned pixel coordinates are expressed
768      * in the AWT's 2d coordinate frame.
769      * @param axes the given Axes.
770      * @param coordinates the 2d view coordinates (3-element array: x, y, z).
771      * @returns the pixel coordinates (2-element array: x, y).
772      */
773     public static double[] computePixelFrom2dViewCoordinates(Axes axes, double[] coordinates) {
774         DrawerVisitor currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
775         AxesDrawer axesDrawer;
776         double[] coords2dView = new double[] {0.0, 0.0, 0.0};
777
778         if (currentVisitor != null) {
779             Integer[] size = currentVisitor.getFigure().getAxesSize();
780             double height = (double) size[1];
781
782             axesDrawer = currentVisitor.getAxesDrawer();
783
784             Transformation projection2d = axesDrawer.getProjection2dView(axes.getIdentifier());
785
786             if (projection2d == null) {
787                 updateAxesTransformation(axes);
788                 projection2d = axesDrawer.getProjection2dView(axes.getIdentifier());
789             }
790
791             Vector3d point = new Vector3d(coordinates);
792             point = projection2d.project(point);
793
794             /* Convert the window coordinates to pixel coordinates, only y changes due to the differing y-axis convention */
795             coords2dView[0] = point.getX();
796             coords2dView[1] = height - point.getY();
797         }
798
799         return coords2dView;
800     }
801
802     /**
803      * Computes and returns the pixel coordinates from a point's coordinates expressed in the current
804      * 3d view coordinate frame, using the given Axes. The returned pixel coordinates are expressed
805      * in the AWT's 2d coordinate frame.
806      * @param axes the given Axes.
807      * @param coordinates the 3d view coordinates (3-element array: x, y, z).
808      * @returns the pixel coordinates (2-element array: x, y).
809      */
810     public static double[] computePixelFrom3dCoordinates(Axes axes, double[] coordinates) {
811         DrawerVisitor currentVisitor;
812         AxesDrawer axesDrawer;
813         Transformation projection;
814         Transformation projection2d;
815         double height = 0.;
816
817         currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
818         boolean[] logFlags = { axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
819
820         Vector3d point = new Vector3d(coordinates);
821         point = ScaleUtils.applyLogScale(point, logFlags);
822
823         if (currentVisitor != null) {
824             axesDrawer = currentVisitor.getAxesDrawer();
825             Integer[] size = currentVisitor.getFigure().getAxesSize();
826             Dimension canvasDimension = new Dimension(size[0], size[1]);
827             height = (double) size[1];
828
829             projection = axesDrawer.computeProjection(axes, currentVisitor.getDrawingTools(), canvasDimension, false);
830
831             point = projection.project(point);
832         }
833
834         return new double[] {point.getX(), height - point.getY(), point.getZ()};
835     }
836
837     /**
838      * Computes and returns the coordinates of a point onto the 3d view plane.
839      * To compute them, the point is projected using the object to window coordinate projection, then
840      * unprojected using the object to window coordinate projection corresponding to the 3d view
841      * @param axes the given Axes.
842      * @param coordinates the object (x,y,z) coordinates to project onto the 2d view plane (3-element array).
843      * @returns the 3d view coordinates (3-element array).
844      */
845     public static double[] compute3dViewCoordinates(Axes axes, double[] coordinates) {
846         DrawerVisitor currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
847         AxesDrawer axesDrawer;
848         Transformation projection;
849         Transformation projection2d;
850
851         Vector3d point = new Vector3d(coordinates);
852
853         if (currentVisitor != null) {
854             Integer[] size = currentVisitor.getFigure().getAxesSize();
855             Dimension canvasDimension = new Dimension(size[0], size[1]);
856
857             axesDrawer = currentVisitor.getAxesDrawer();
858
859             projection = axesDrawer.computeProjection(axes, currentVisitor.getDrawingTools(), canvasDimension, false);
860             projection2d = axesDrawer.computeProjection(axes, currentVisitor.getDrawingTools(), canvasDimension, true);
861
862             point = projection2d.project(point);
863             point = projection.unproject(point);
864         }
865
866         return new double[] {point.getX(), point.getY(), point.getZ()};
867     }
868
869     /**
870      * Computes and returns the coordinates of a point projected onto the default 2d view plane
871      * from its pixel coordinates, using the given Axes. Pixel coordinates are expressed in
872      * the AWT's 2d coordinate frame.
873      * The returned point's z component is set to 0, as we only have x and y as an input.
874      * @param axes the given Axes.
875      * @param coordinates the pixel coordinates (2-element array: x, y).
876      * @return coordinates the 2d view coordinates (3-element array: x, y, z).
877      */
878     public static double[] compute2dViewFromPixelCoordinates(Axes axes, double[] coordinates) {
879         DrawerVisitor currentVisitor;
880         AxesDrawer axesDrawer;
881
882         double[] coords2dView = new double[] {0.0, 0.0, 0.0};
883
884         currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
885
886         if (currentVisitor != null) {
887             Integer[] size = currentVisitor.getFigure().getAxesSize();
888             double height = (double) size[1];
889
890             axesDrawer = currentVisitor.getAxesDrawer();
891
892             /* Convert the pixel coordinates to window coordinates, only y changes due to the differing y-axis convention */
893             Vector3d point = new Vector3d(coordinates[0], height - coordinates[1], 0.0);
894
895             Transformation projection2d = axesDrawer.getProjection2dView(axes.getIdentifier());
896             if (projection2d == null) {
897                 updateAxesTransformation(axes);
898                 projection2d = axesDrawer.getProjection2dView(axes.getIdentifier());
899             }
900
901             point = projection2d.unproject(point);
902             coords2dView = point.getData();
903         }
904
905         return coords2dView;
906     }
907
908     /**
909      * Un-project the given point from AWT coordinate to given axes coordinate.
910      * @param axes returned coordinate are relative to this axes.
911      * @param point un-projected point.
912      * @return The un-projected point.
913      */
914     public static Vector3d unProject(Axes axes, Vector3d point) {
915         DrawerVisitor currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
916
917         if (currentVisitor != null) {
918             AxesDrawer axesDrawer = currentVisitor.getAxesDrawer();
919             double height = currentVisitor.getCanvas().getHeight() - 1;
920
921             Transformation projection2d = axesDrawer.getProjection(axes.getIdentifier());
922             return projection2d.unproject(new Vector3d(point.getX(), height - point.getY(), point.getZ()));
923         } else {
924             return new Vector3d(0, 0, 0);
925         }
926     }
927
928     /**
929      * Computes and returns the viewing area corresponding to the given Axes object.
930      * The viewing area is described by the (x, y) coordinates of the Axes box's upper-left corner
931      * and the Axes box's dimensions (width and height), all values are in pixel.
932      * The 2d coordinate frame in which the area is expressed uses the AWT convention:
933      * upper-left window corner at (0, 0), y-axis pointing downwards).
934      * @param axes the given Axes.
935      * @return the Axes' viewing area (4-element array: x, y, width, height).
936      */
937     public static double[] getViewingArea(Axes axes) {
938         DrawerVisitor currentVisitor;
939
940         double[] viewingArea = new double[] {0.0, 0.0, 0.0, 0.0};
941
942         currentVisitor = DrawerVisitor.getVisitor(axes.getParentFigure());
943
944         if (currentVisitor != null) {
945             Integer[] size = currentVisitor.getFigure().getAxesSize();
946             double width = (double) size[0];
947             double height = (double) size[1];
948             double upperLeftY;
949             AxesDrawer axesDrawer = currentVisitor.getAxesDrawer();
950             Rectangle2D axesZone = axesDrawer.computeZone(axes);
951
952             /* Compute the upper-left point's y coordinate */
953             upperLeftY = axesZone.getY() + axesZone.getHeight() * 2.0;
954
955             /* Convert from normalized coordinates to 2D pixel coordinates */
956             viewingArea[0] = (axesZone.getX() + 1.0) * 0.5 * width;
957             viewingArea[1] = (1.0 - upperLeftY) * 0.5 * height;
958             viewingArea[2] = axesZone.getWidth() * width;
959             viewingArea[3] = axesZone.getHeight() * height;
960         }
961
962         return viewingArea;
963     }
964
965     /**
966      * Returns the culling mode corresponding to front faces.
967      * @return the front face culling mode.
968      */
969     public FaceCullingMode getFrontFaceCullingMode() {
970         return this.frontFaceCullingMode;
971     }
972
973     /**
974      * Returns the culling mode corresponding to back faces.
975      * @return the back face culling mode.
976      */
977     public FaceCullingMode getBackFaceCullingMode() {
978         return this.backFaceCullingMode;
979     }
980
981     /**
982      * Enables clipping for the given {@link ClippableProperty}, which describes
983      * the clipping state of a clippable graphic object.
984      * Depending on the object's clip state property, clipping can be either
985      * disabled (OFF), performed against the parent Axes' box planes (CLIPGRF),
986      * or performed against the planes defined by the object's clip box.
987      * To do: find a better way to compute the clipping planes' offsets as the current one
988      * may lead to problems when the interval between the min and max bounds is too small.
989      * @param parentAxes the clipped object's parent Axes.
990      * @param clipProperty the clipping property of a clippable object.
991      */
992     public void enableClipping(Axes parentAxes, ClippableProperty clipProperty) {
993         DrawingTools drawingTools = visitor.getDrawingTools();
994
995         if (clipProperty.getClipState() != ClipStateType.OFF) {
996             int numPlanes = 0;
997             Vector4d[] equations = null;
998
999             /* Stores the (xmin,xmax) ,(ymin,ymax) and (zmin,zmax) clipping bounds */
1000             double[] clipBounds = new double[6];
1001
1002             /* The offsets used for the x, y and z planes in order to avoid strict clipping */
1003             double[] offsets = new double[3];
1004
1005             if (clipProperty.getClipState() == ClipStateType.CLIPGRF) {
1006                 /*
1007                  * All the clipping planes are set as clipping is performed
1008                  * against the Axes box planes.
1009                  */
1010                 numPlanes = 6;
1011                 Double[] bounds = parentAxes.getDisplayedBounds();
1012
1013                 for (int i = 0; i < numPlanes; i++) {
1014                     clipBounds[i] = bounds[i];
1015                 }
1016
1017                 offsets[0] = CLIPPING_EPSILON * (bounds[1] - bounds[0]);
1018                 offsets[1] = CLIPPING_EPSILON * (bounds[3] - bounds[2]);
1019                 offsets[2] = CLIPPING_EPSILON * (bounds[5] - bounds[4]);
1020             } else if (clipProperty.getClipState() == ClipStateType.ON) {
1021                 /*
1022                  * The clip box property defines values only for the x and y axes,
1023                  * we therefore set only the x and y clipping planes.
1024                  */
1025                 numPlanes = 4;
1026                 Double[] clipBox = clipProperty.getClipBox();
1027
1028                 /* The clip box stores the upper-left point coordinates. */
1029                 clipBounds[0] = clipBox[0];
1030                 clipBounds[1] = clipBox[0] + clipBox[2];
1031                 clipBounds[2] = clipBox[1] - clipBox[3];
1032                 clipBounds[3] = clipBox[1];
1033
1034                 Double[] bounds = parentAxes.getMaximalDisplayedBounds();
1035
1036                 /*
1037                  * The logarithmic scale must be applied to clip bounds values.
1038                  * If any of them are invalid, we set them to the displayed
1039                  * left bounds (xmin or ymin).
1040                  */
1041                 if (parentAxes.getXAxisLogFlag()) {
1042                     if (clipBounds[0] <= 0.0) {
1043                         clipBounds[0] = bounds[0];
1044                     } else {
1045                         clipBounds[0] = Math.log10(clipBounds[0]);
1046                     }
1047
1048                     if (clipBounds[1] <= 0.0) {
1049                         clipBounds[1] = bounds[0];
1050                     } else {
1051                         clipBounds[1] = Math.log10(clipBounds[1]);
1052                     }
1053                 }
1054
1055                 if (parentAxes.getYAxisLogFlag()) {
1056                     if (clipBounds[2] <= 0.0) {
1057                         clipBounds[2] = bounds[2];
1058                     } else {
1059                         clipBounds[2] = Math.log10(clipBounds[2]);
1060                     }
1061
1062                     if (clipBounds[3] <= 0.0) {
1063                         clipBounds[3] = bounds[2];
1064                     } else {
1065                         clipBounds[3] = Math.log10(clipBounds[3]);
1066                     }
1067                 }
1068
1069                 offsets[0] = CLIPPING_EPSILON * (clipBounds[1] - clipBounds[0]);
1070                 offsets[1] = CLIPPING_EPSILON * (clipBounds[3] - clipBounds[2]);
1071             }
1072
1073             equations = new Vector4d[numPlanes];
1074
1075             equations[0] = new Vector4d(+1, 0, 0, -clipBounds[0] + offsets[0]);
1076             equations[1] = new Vector4d(-1, 0, 0, +clipBounds[1] + offsets[0]);
1077             equations[2] = new Vector4d(0, +1, 0, -clipBounds[2] + offsets[1]);
1078             equations[3] = new Vector4d(0, -1, 0, +clipBounds[3] + offsets[1]);
1079
1080             /* If clipping is performed against the Axes box, the z plane equations must be initialized. */
1081             if (numPlanes == 6) {
1082                 equations[4] = new Vector4d(0, 0, +1, -clipBounds[4] + offsets[2]);
1083                 equations[5] = new Vector4d(0, 0, -1, +clipBounds[5] + offsets[2]);
1084             }
1085
1086             Transformation currentTransformation = drawingTools.getTransformationManager().getTransformation();
1087             for (int i = 0 ; i < numPlanes; i++) {
1088                 ClippingPlane plane = drawingTools.getClippingManager().getClippingPlane(i);
1089                 plane.setTransformation(currentTransformation);
1090                 plane.setEquation(equations[i]);
1091                 plane.setEnable(true);
1092             }
1093         }
1094     }
1095
1096     /**
1097      * Disables clipping for the given {@link ClippableProperty}.
1098      * @param clipProperty the clip property for which clipping is disabled.
1099      */
1100     public void disableClipping(ClippableProperty clipProperty) {
1101         int numPlanes = 0;
1102
1103         if (clipProperty.getClipState() == ClipStateType.CLIPGRF) {
1104             numPlanes = 6;
1105         } else if (clipProperty.getClipState() == ClipStateType.ON) {
1106             numPlanes = 4;
1107         }
1108
1109         for (int i = 0 ; i < numPlanes; i++) {
1110             ClippingPlane plane = visitor.getDrawingTools().getClippingManager().getClippingPlane(i);
1111             plane.setEnable(false);
1112         }
1113
1114         visitor.getDrawingTools().getClippingManager().disableClipping();
1115     }
1116
1117     /**
1118      * Returns the x-axis label positioner.
1119      * @return the x-axis label positioner.
1120      */
1121     public AxisLabelPositioner getXAxisLabelPositioner(Axes axes) {
1122         AxisLabelPositioner positioner = this.xAxisLabelPositioner.get(axes.getIdentifier());
1123         if (positioner == null) {
1124             positioner = new AxisLabelPositioner();
1125             this.xAxisLabelPositioner.put(axes.getIdentifier(), positioner);
1126         }
1127
1128         return positioner;
1129     }
1130
1131     /**
1132      * Returns the y-axis label positioner.
1133      * @return the y-axis label positioner.
1134      */
1135     public AxisLabelPositioner getYAxisLabelPositioner(Axes axes) {
1136         AxisLabelPositioner positioner = this.yAxisLabelPositioner.get(axes.getIdentifier());
1137         if (positioner == null) {
1138             positioner = new YAxisLabelPositioner();
1139             this.yAxisLabelPositioner.put(axes.getIdentifier(), positioner);
1140         }
1141
1142         return positioner;
1143     }
1144
1145     /**
1146      * Returns the z-axis label positioner.
1147      * @return the z-axis label positioner.
1148      */
1149     public AxisLabelPositioner getZAxisLabelPositioner(Axes axes) {
1150         AxisLabelPositioner positioner = this.zAxisLabelPositioner.get(axes.getIdentifier());
1151         if (positioner == null) {
1152             positioner = new AxisLabelPositioner();
1153             this.zAxisLabelPositioner.put(axes.getIdentifier(), positioner);
1154         }
1155
1156         return positioner;
1157     }
1158
1159     /**
1160      * Returns the title positioner.
1161      * @return the title positioner.
1162      */
1163     public LabelPositioner getTitlePositioner(Axes axes) {
1164         TitlePositioner positioner = this.titlePositioner.get(axes.getIdentifier());
1165         if (positioner == null) {
1166             positioner = new TitlePositioner();
1167             this.titlePositioner.put(axes.getIdentifier(), positioner);
1168         }
1169
1170         return positioner;
1171     }
1172
1173     public void disposeAll() {
1174         this.rulerDrawer.disposeAll();
1175         this.projectionMap.clear();
1176         this.projection2dViewMap.clear();
1177         this.sceneProjectionMap.clear();
1178         this.xAxisLabelPositioner.clear();
1179         this.yAxisLabelPositioner.clear();
1180         this.zAxisLabelPositioner.clear();
1181         this.titlePositioner.clear();
1182     }
1183
1184     public void update(String id, int property) {
1185         if (this.rulerDrawer.update(id, property)) {
1186             GraphicObject object = GraphicController.getController().getObjectFromId(id);
1187             if (object instanceof Axes) {
1188                 computeRulers((Axes) object);
1189             }
1190         }
1191     }
1192
1193     public void dispose(String id) {
1194         this.rulerDrawer.dispose(id);
1195         projectionMap.remove(id);
1196         projection2dViewMap.remove(id);
1197         sceneProjectionMap.remove(id);
1198         this.xAxisLabelPositioner.remove(id);
1199         this.yAxisLabelPositioner.remove(id);
1200         this.zAxisLabelPositioner.remove(id);
1201         this.titlePositioner.remove(id);
1202     }
1203 }