6bd10a4a065bb7fccb5da9a397fc03346fe144a8
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / JoGLView / datatip / DatatipTextDrawer.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Pedro Arthur dos S. Souza
4  * Copyright (C) 2012 - Caio Lucas dos S. Souza
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
14 package org.scilab.modules.renderer.JoGLView.datatip;
15
16 import org.scilab.forge.scirenderer.DrawingTools;
17 import org.scilab.forge.scirenderer.SciRendererException;
18 import org.scilab.forge.scirenderer.texture.AnchorPosition;
19 import org.scilab.forge.scirenderer.texture.Texture;
20 import org.scilab.forge.scirenderer.texture.TextureManager;
21 import org.scilab.forge.scirenderer.tranformations.DegenerateMatrixException;
22 import org.scilab.forge.scirenderer.tranformations.Transformation;
23 import org.scilab.forge.scirenderer.tranformations.TransformationFactory;
24 import org.scilab.forge.scirenderer.tranformations.Vector3d;
25 import org.scilab.modules.graphic_objects.PolylineData;
26 import org.scilab.modules.graphic_objects.axes.Axes;
27 import org.scilab.modules.graphic_objects.figure.ColorMap;
28 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
29 import org.scilab.modules.graphic_objects.datatip.Datatip;
30 import org.scilab.modules.renderer.CallRenderer;
31 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
32 import org.scilab.modules.renderer.JoGLView.util.ScaleUtils;
33 import java.awt.Dimension;
34 import org.scilab.modules.renderer.JoGLView.text.TextManager;
35
36 /**
37  * Datatip text drawer
38  *
39  * Draw the datatip text according to its orientation and mark properties
40  */
41
42 public class DatatipTextDrawer extends TextManager {
43
44     public DatatipTextDrawer(TextureManager textureManager) {
45         super(textureManager);
46     }
47
48     /**
49      * Draw the given Scilab {@see Datatip} with the given {@see DrawingTools}.
50      * @param drawingTools the given {@see DrawingTools}.
51      * @param colorMap the current {@see ColorMap}
52      * @param text the given Scilab {@see Datatip}
53      * @throws SciRendererException if the draw fails.
54      */
55     public final void draw(final DrawingTools drawingTools, final ColorMap colorMap, final Datatip datatip) throws SciRendererException {
56         Texture texture = getTexture(colorMap, datatip);
57
58         /* The unscaled texture's dimensions */
59         Dimension spriteDims = getSpriteDims(colorMap, datatip);
60
61         Transformation projection = drawingTools.getTransformationManager().getCanvasProjection();
62
63         Integer parentAxesId = datatip.getParentAxes();
64         Axes parentAxes = (Axes) GraphicController.getController().getObjectFromId(parentAxesId);
65
66         /* Compute the text box vectors and the text box to texture dimension ratios */
67         Vector3d[] textBoxVectors = computeTextBoxVectors(projection, datatip, texture.getDataProvider().getTextureSize(), parentAxes);
68
69         double[] ratios = computeRatios(projection, datatip, textBoxVectors, texture.getDataProvider().getTextureSize(), spriteDims);
70
71         /* If text box mode is equal to filled, the texture must be updated */
72         if (datatip.getTextBoxMode() == 2 && ratios[0] != 1.0) {
73             texture = updateSprite(colorMap, datatip, ratios[0], ratios[1]);
74         }
75
76         /* Compute the text texture's actual position, which depends on the object's text box mode property */
77         Vector3d[] cornerPositions = computeTextPosition(projection, datatip, textBoxVectors, texture.getDataProvider().getTextureSize());
78
79         /* Draw in window coordinates */
80         drawingTools.getTransformationManager().useWindowCoordinate();
81
82         Integer size = datatip.getMarkSize();
83         Integer unit = datatip.getMarkSizeUnit();
84
85         /* calculate the size of the mark to dont draw the text over the mark*/
86         double finalSize = (unit == 1) ? (8.0 + 2.0 * size) : size;
87         finalSize /= 2.0;
88         double r = datatip.getMarkStyle() == 11 ? 1.0 : 2.0;
89         finalSize -= (finalSize >= 2.0) ? r : 0.0;
90
91         Vector3d delta = new Vector3d(finalSize, finalSize, 0);
92         /* set up the text position according to the datatip orientation*/
93         if (datatip.isAutoOrientationEnabled()) {
94             int autopos = getAutoOrientation(datatip);
95             if (autopos != -1) {
96                 Vector3d cp = cornerPositions[0], d = delta, p;
97                 if (autopos == 2 || autopos == 3) {
98                     cp = cp.minus(textBoxVectors[1]);
99                     d = d.setY(-finalSize);
100                 }
101                 if (autopos == 0 || autopos == 2) {
102                     cp = cp.minus(textBoxVectors[0]);
103                     d = d.setX(-finalSize);
104                 }
105
106                 p = projection.unproject(cp.plus(textBoxVectors[0]).plus(textBoxVectors[1]));
107                 Vector3d ucp = projection.unproject(cp);
108                 if (p.getX() < -1 || p.getX() > 1 || p.getY() < -1 || p.getY() > 1 || ucp.getX() < -1 || ucp.getX() > 1 || ucp.getY() < -1 || ucp.getY() > 1) {
109                     autopos = -1;
110                 } else {
111                     cornerPositions[0] = cp;
112                     delta = d;
113                 }
114             }
115
116             if (autopos == -1) {
117                 Vector3d position = projection.unproject(cornerPositions[0].minus(textBoxVectors[0]).plus(textBoxVectors[1]));
118                 if (position.getX() >= -1 && position.getX() <= 1 && position.getY() >= -1 && position.getY() <= 1) {
119                     cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[0]);
120                     delta = delta.setX(-finalSize);
121                 } else {
122                     position = projection.unproject(cornerPositions[0].plus(textBoxVectors[0]).minus(textBoxVectors[1]));
123                     if (position.getX() >= -1 && position.getX() <= 1 && position.getY() >= -1 && position.getY() <= 1) {
124                         cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[1]);
125                         delta = delta.setY(-finalSize);
126                     } else {
127                         position = projection.unproject(cornerPositions[0].minus(textBoxVectors[0]).minus(textBoxVectors[1]));
128                         if (position.getX() >= -1 && position.getX() <= 1 && position.getY() >= -1 && position.getY() <= 1) {
129                             cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[1]);
130                             cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[0]);
131                             delta = delta.setX(-finalSize);
132                             delta = delta.setY(-finalSize);
133                         }
134                     }
135                 }
136             }
137         } else {
138             if (datatip.getOrientation() == 2 || datatip.getOrientation() == 3) {
139                 cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[1]);
140                 delta = delta.setY(-finalSize);
141             }
142             if (datatip.getOrientation() == 0 || datatip.getOrientation() == 2) {
143                 cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[0]);
144                 delta = delta.setX(-finalSize);
145             }
146         }
147
148         cornerPositions[0] = cornerPositions[0].plus(delta);
149         cornerPositions[1] = cornerPositions[1].plus(delta);
150
151         /* The Text object's rotation direction convention is opposite to the standard one, its angle is expressed in radians. */
152         drawingTools.draw(texture, AnchorPosition.LOWER_LEFT, cornerPositions[0], -180.0 * datatip.getFontAngle() / Math.PI);
153
154         drawingTools.getTransformationManager().useSceneCoordinate();
155
156         /* Compute the corners of the text's bounding box in window coordinates */
157         Vector3d[] projCorners;
158         if (datatip.getTextBoxMode() == 2) {
159             projCorners = computeProjTextBoxCorners(cornerPositions[1], datatip.getFontAngle(), textBoxVectors);
160         } else {
161             projCorners = computeProjCorners(cornerPositions[0], datatip.getFontAngle(), texture.getDataProvider().getTextureSize());
162         }
163
164         Vector3d[] corners = computeCorners(projection, projCorners, parentAxes);
165         Double[] coordinates = cornersToCoordinateArray(corners);
166
167         /* Set the computed coordinates */
168         datatip.setCorners(coordinates);
169     }
170
171     /**
172      * Update the given datatip text corners
173      * @param datatip the given datatip
174      */
175     public static void updateTextCorners(Datatip datatip) {
176         Vector3d[] projCorners = null;
177         DrawerVisitor currentVisitor = DrawerVisitor.getVisitor(datatip.getParentFrameOrFigure());
178         Transformation currentProj = currentVisitor.getAxesDrawer().getProjection(datatip.getParentAxes());
179         Axes parentAxes = (Axes) GraphicController.getController().getObjectFromId(datatip.getParentAxes());
180         Dimension spriteDim = currentVisitor.getDatatipTextDrawer().getSpriteDims(currentVisitor.getColorMap(), datatip);
181
182         /* Compute the corners */
183         try {
184             Vector3d[] textBoxVectors = currentVisitor.getDatatipTextDrawer().computeTextBoxVectors(currentProj, datatip, spriteDim, parentAxes);
185             Vector3d[] cornerPositions = currentVisitor.getDatatipTextDrawer().computeTextPosition(currentProj, datatip, textBoxVectors, spriteDim);
186
187             Integer size = datatip.getMarkSize();
188             Integer unit = datatip.getMarkSizeUnit();
189
190             /* calculate the size of the mark to dont position the text over the mark*/
191             double finalSize = (unit == 1) ? (8.0 + 2.0 * size) : size;
192             finalSize /= 2.0;
193             double r = datatip.getMarkStyle() == 11 ? 1.0 : 2.0;
194             finalSize -= (finalSize >= 2.0) ? r : 0.0;
195
196             Vector3d delta = new Vector3d(finalSize, finalSize, 0);
197             /* set up the text position according to the datatip orientation*/
198             if (datatip.getOrientation() == 2 || datatip.getOrientation() == 3) {
199                 cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[1]);
200                 delta = delta.setY(-finalSize);
201             }
202             if (datatip.getOrientation() == 0 || datatip.getOrientation() == 2) {
203                 cornerPositions[0] = cornerPositions[0].minus(textBoxVectors[0]);
204                 delta = delta.setX(-finalSize);
205             }
206
207             cornerPositions[0] = cornerPositions[0].plus(delta);
208             cornerPositions[1] = cornerPositions[1].plus(delta);
209
210             if (datatip.getTextBoxMode() == 2) {
211                 projCorners = currentVisitor.getDatatipTextDrawer().computeProjTextBoxCorners(cornerPositions[1], datatip.getFontAngle(), textBoxVectors);
212             } else {
213                 projCorners = currentVisitor.getDatatipTextDrawer().computeProjCorners(cornerPositions[0], datatip.getFontAngle(), spriteDim);
214             }
215         } catch (DegenerateMatrixException e) {
216             // TODO Auto-generated catch block
217             e.printStackTrace();
218         }
219
220         Vector3d[] corners = currentVisitor.getDatatipTextDrawer().computeCorners(currentProj, projCorners, parentAxes);
221         Double[] coordinates = currentVisitor.getDatatipTextDrawer().cornersToCoordinateArray(corners);
222
223         /* Set the computed coordinates */
224         datatip.setCorners(coordinates);
225     }
226
227     /**
228      * Calculates the anchor point from datatip (Used to draw the datatip mark)
229      * @param datatip the given datatip
230      * @return Vector3d the anchor point position
231      */
232     public static Vector3d calculateAnchorPoint(Datatip datatip) {
233         Axes axes = (Axes) GraphicController.getController().getObjectFromId(datatip.getParentAxes());
234         double[][] factors = axes.getScaleTranslateFactors();
235         boolean[] logFlags = new boolean[] {axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
236         Vector3d v = ScaleUtils.applyLogScale(new Vector3d(datatip.getTipData()), logFlags);
237
238         return new Vector3d(v.getX() * factors[0][0] + factors[1][0], v.getY() * factors[0][1] + factors[1][1], v.getZ() * factors[0][2] + factors[1][2]);
239     }
240
241     private static int getAutoOrientation(Datatip datatip) {
242         final double[] dataX = (double[]) PolylineData.getDataX(datatip.getParent());
243         int index = datatip.getIndexes();
244         if (index == 0 || index >= dataX.length - 1) {
245             return -1;
246         }
247
248         final double[] dataY = (double[]) PolylineData.getDataY(datatip.getParent());
249         final double[] first, second, third;
250         Integer axes = datatip.getParentAxes();
251
252         if (datatip.isUsing3Component()) {
253             final double[] dataZ = (double[]) PolylineData.getDataZ(datatip.getParent());
254             first = CallRenderer.getPixelFrom3dCoordinates(axes, new double[] {dataX[index - 1], dataY[index - 1], dataZ[index - 1]});
255             second = CallRenderer.getPixelFrom3dCoordinates(axes, new double[] {dataX[index], dataY[index], dataZ[index]});
256             third = CallRenderer.getPixelFrom3dCoordinates(axes, new double[] {dataX[index + 1], dataY[index + 1], dataZ[index + 1]});
257         } else {
258             first = CallRenderer.getPixelFrom3dCoordinates(axes, new double[] {dataX[index - 1], dataY[index - 1], 0});
259             second = CallRenderer.getPixelFrom3dCoordinates(axes, new double[] {dataX[index], dataY[index], 0});
260             third = CallRenderer.getPixelFrom3dCoordinates(axes, new double[] {dataX[index + 1], dataY[index + 1], 0});
261         }
262
263         final double a = Math.atan2(second[1] - first[1], first[0] - second[0]);
264         final double b = Math.atan2(second[1] - third[1], third[0] - second[0]);
265
266         final int quadA = getQuad(a);
267         final int quadB = getQuad(b);
268         final int quadDatatip;
269
270         // UL quadrant is 1, UR quadrant is 0
271         // LL quadrant is 2, LR quadrant is 3
272
273         if (quadA == quadB) {
274             quadDatatip = (quadA + 2) % 4;
275         } else {
276             final int sum = quadA + quadB;
277             if (sum == 3) {
278                 quadDatatip = (quadA * quadB + 1) % 3;
279             } else if (sum == 1 || sum == 5) {
280                 quadDatatip = (sum + 3) % 8;
281             } else {
282                 final double M = Math.max(a, b);
283                 final double m = Math.min(a, b);
284                 if (sum == 4) {
285                     if (M - m <= Math.PI) {
286                         quadDatatip = 2;
287                     } else {
288                         quadDatatip = 0;
289                     }
290                 } else {
291                     if (M - m <= Math.PI) {
292                         quadDatatip = 1;
293                     } else {
294                         quadDatatip = 3;
295                     }
296                 }
297             }
298         }
299
300         if (quadDatatip <= 1) {
301             return 1 - quadDatatip;
302         }
303
304         return quadDatatip;
305     }
306
307     private static int getQuad(final double a) {
308         if (a >= 0) {
309             if (a <= Math.PI / 2) {
310                 return 0;
311             } else {
312                 return 1;
313             }
314         } else {
315             if (a <= -Math.PI / 2) {
316                 return 2;
317             } else {
318                 return 3;
319             }
320         }
321     }
322 }