1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2011-2012 - DIGITEO - Manuel JULIACHS
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  */
14 
15 package org.scilab.modules.renderer.JoGLView.legend;
16 
17 import org.scilab.forge.scirenderer.Canvas;
18 import org.scilab.forge.scirenderer.DrawingTools;
19 import org.scilab.forge.scirenderer.SciRendererException;
20 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
21 import org.scilab.forge.scirenderer.buffers.IndicesBuffer;
22 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
23 import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry;
24 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
25 import org.scilab.forge.scirenderer.texture.AnchorPosition;
26 import org.scilab.forge.scirenderer.texture.Texture;
27 import org.scilab.forge.scirenderer.texture.TextureManager;
28 import org.scilab.forge.scirenderer.tranformations.Transformation;
29 import org.scilab.forge.scirenderer.tranformations.TransformationFactory;
30 import org.scilab.forge.scirenderer.tranformations.TransformationStack;
31 import org.scilab.forge.scirenderer.tranformations.Vector3d;
32 import org.scilab.modules.graphic_objects.axes.Axes;
33 import org.scilab.modules.graphic_objects.figure.ColorMap;
34 import org.scilab.modules.graphic_objects.figure.Figure;
35 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
36 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
37 import org.scilab.modules.graphic_objects.legend.Legend;
38 import org.scilab.modules.graphic_objects.legend.Legend.LegendLocation;
39 import org.scilab.modules.graphic_objects.polyline.Polyline;
40 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
41 import org.scilab.modules.renderer.JoGLView.mark.MarkSpriteManager;
42 import org.scilab.modules.renderer.JoGLView.util.ColorFactory;
43 
44 import java.awt.Dimension;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.HashSet;
48 import java.util.Map;
49 import java.util.Set;
50 import java.util.concurrent.ConcurrentHashMap;
51 
52 
53 /**
54  * LegendDrawer class.
55  * Utility class used by DrawerVisitor to draw the Legend object.
56  *
57  * To do:
58  * - clean up the code
59  * - modify bounds depending on the location (for OUT_* values)
60  * - take into account the actual tick/label size (instead of an arbitrary value)
61  * - implement box clipping mode
62  * - use the GraphicController to update links / move the update code somewhere else
63  *   in order to perform it when objects are deleted, add the Links property to the set of
64  *   properties affecting the sprite's update.
65  *
66  * @author Manuel JULIACHS
67  */
68 public class LegendDrawer {
69 
70     /**
71      * Set of properties that affect the legend sprite
72      */
73     private static final Set<Integer> SPRITE_PROPERTIES = new HashSet<Integer>(Arrays.asList(
74                 GraphicObjectProperties.__GO_FONT_SIZE__,
75                 GraphicObjectProperties.__GO_FONT_COLOR__,
76                 GraphicObjectProperties.__GO_FONT_STYLE__,
77                 GraphicObjectProperties.__GO_FONT_FRACTIONAL__,
78                 GraphicObjectProperties.__GO_TEXT_ARRAY_DIMENSIONS__,
79                 GraphicObjectProperties.__GO_TEXT_STRINGS__
80             ));
81 
82     /** The height of a bar relative to the height difference between two lines */
83     private static final float BAR_HEIGHT = 0.5f;
84 
85     /** The DrawerVisitor used */
86     private final DrawerVisitor visitor;
87 
88     /** The SpriteManager used */
89     private final TextureManager textureManager;
90 
91     /** The MarkSpriteManager used */
92     private final MarkSpriteManager markManager;
93 
94     /** The Legend sprite drawer */
95     private LegendSpriteDrawer legendSpriteDrawer;
96 
97     /** The relative line width */
98     public static final double LINE_WIDTH = 0.1;
99 
100     /** The relative y-offset */
101     public static final double Y_OFFSET = 0.01;
102 
103     /** The relative tick and label size (arbitrarily chosen) */
104     private static final double TICK_LABEL_SIZE = 0.055;
105 
106     /** The z-value corresponding to the frontmost position */
107     private static final float Z_FRONT = 0.99f;
108 
109     /** The legend background's vertices */
110     private ElementsBuffer rectangleVertices;
111 
112     /** The vertices used to draw lines */
113     private ElementsBuffer lineVertices;
114 
115     /** The vertices used to draw bars */
116     private ElementsBuffer barVertices;
117 
118     /** The indices used to draw lines */
119     private IndicesBuffer lineIndices;
120 
121     /** The indices used to draw a rectangle */
122     private IndicesBuffer rectangleIndices;
123 
124     /** The indices used to draw the outline of a rectangle */
125     private IndicesBuffer rectangleOutlineIndices;
126 
127     /** The map storing legend text sprites */
128     private Map<Integer, Texture> textureMap;
129 
130     /**
131      * Constructor.
132      * @param visitor the DrawerVisitor {@see DrawerVisitor}.
133      */
LegendDrawer(DrawerVisitor visitor)134     public LegendDrawer(DrawerVisitor visitor) {
135         this.visitor = visitor;
136         this.textureManager = visitor.getCanvas().getTextureManager();
137         this.markManager = visitor.getMarkManager();
138 
139         rectangleVertices = visitor.getCanvas().getBuffersManager().createElementsBuffer();
140         lineVertices = visitor.getCanvas().getBuffersManager().createElementsBuffer();
141         barVertices = visitor.getCanvas().getBuffersManager().createElementsBuffer();
142         lineIndices = visitor.getCanvas().getBuffersManager().createIndicesBuffer();
143         rectangleIndices = visitor.getCanvas().getBuffersManager().createIndicesBuffer();
144         rectangleOutlineIndices = visitor.getCanvas().getBuffersManager().createIndicesBuffer();
145 
146         textureMap = new ConcurrentHashMap<Integer, Texture>();
147 
148         int[] lineIndexData = new int[] {0, 1, 1, 2};
149         lineIndices.setData(lineIndexData);
150     }
151 
computeDimensions(Axes parentAxes, Legend legend)152     public Dimension computeDimensions(Axes parentAxes, Legend legend) {
153         LegendLocation loc = legend.getLegendLocationAsEnum();
154         if (loc != LegendLocation.IN_UPPER_RIGHT && loc != LegendLocation.IN_UPPER_LEFT &&
155                 loc != LegendLocation.IN_LOWER_RIGHT && loc != LegendLocation.IN_LOWER_LEFT) {
156             Figure figure = (Figure) GraphicController.getController().getObjectFromId(parentAxes.getParentFigure());
157             final ColorMap colorMap = figure.getColorMap();
158             final Integer[] links = legend.getLinks();
159             final int nbValidLinks = getNumberValidLinks(legend);
160             if (nbValidLinks < links.length) {
161                 dispose(legend.getIdentifier());
162                 updateLinks(legend, nbValidLinks);
163             }
164 
165             if (nbValidLinks > 0) {
166                 Texture legendSprite = getTexture(colorMap, legend);
167                 return legendSprite.getDataProvider().getTextureSize();
168             }
169         }
170 
171         return null;
172     }
173 
174     /**
175      * Draws the given Legend.
176      * @param legend the Legend to draw.
177      * @throws SciRendererException if the draw fail.
178      */
draw(Legend legend)179     public void draw(Legend legend) throws SciRendererException {
180         /* The coordinates of the legend box's lower-left corner */
181         double[] legendCorner = new double[] {0.25, 0.75, Z_FRONT};
182 
183         DrawingTools drawingTools = visitor.getDrawingTools();
184         ColorMap colorMap = visitor.getColorMap();
185         Canvas canvas = visitor.getCanvas();
186 
187         Integer[] links = legend.getLinks();
188 
189         /*
190          * Determine whether any links have become invalid,
191          * force the sprite's update and update the Legend's
192          * links property as needed.
193          */
194         int nbValidLinks = getNumberValidLinks(legend);
195 
196         if (nbValidLinks < links.length) {
197             dispose(legend.getIdentifier());
198             updateLinks(legend, nbValidLinks);
199         }
200 
201         links = legend.getLinks();
202 
203         /*
204          * Set the projection and modelview transformations so that coordinates
205          * are specified in the {0,+1,0,+1,0,+1} space.
206          */
207         TransformationStack modelViewStack = drawingTools.getTransformationManager().getModelViewStack();
208         TransformationStack projectionStack = drawingTools.getTransformationManager().getProjectionStack();
209 
210         Transformation identity = TransformationFactory.getIdentity();
211         modelViewStack.push(identity);
212 
213         Transformation orthoProj = TransformationFactory.getOrthographic(0.0, 1.0, 0.0, 1.0, -1.0, 0.0);
214         projectionStack.push(orthoProj);
215 
216 
217         /* First, compute the legend box's position and dimensions from the Axes' parameters and the text sprite's dimensions */
218 
219         Integer parentAxesID = legend.getParentAxes();
220         Axes parentAxes = (Axes) GraphicController.getController().getObjectFromId(parentAxesID);
221 
222         Double[] axesBounds = parentAxes.getAxesBounds();
223         Double[] margins = parentAxes.getMargins();
224 
225         int xAxisLocation = parentAxes.getXAxisLocation();
226         int yAxisLocation = parentAxes.getYAxisLocation();
227 
228 
229         int canvasWidth = canvas.getWidth();
230         int canvasHeight = canvas.getHeight();
231 
232         if (canvasWidth == 0) {
233             canvasWidth = 1;
234         }
235 
236         if (canvasHeight == 0) {
237             canvasHeight = 1;
238         }
239 
240         Texture legendSprite = null;
241         double normSpriteWidth = 0;
242         double normSpriteHeight = 0;
243 
244         if (nbValidLinks > 0) {
245             legendSprite = getTexture(colorMap, legend);
246             Dimension textureSize = legendSprite.getDataProvider().getTextureSize();
247             normSpriteWidth = textureSize.getWidth() / (double) canvasWidth;
248             normSpriteHeight = textureSize.getHeight() / (double) canvasHeight;
249         }
250 
251         double lineWidth;
252 
253         /* The legend box's width and height */
254         double[] legendDims = new double[2];
255 
256         double[] axesPos = new double[2];
257         double[] axesDims = new double[2];
258 
259         lineWidth = legend.getLineWidth() * (axesBounds[2]) * (1.0 - margins[0] - margins[1]);
260 
261         double xOffset = LINE_WIDTH * (axesBounds[2]) * (1.0 - margins[0] - margins[1]) / 8.0;
262         double yOffset = (Y_OFFSET * (axesBounds[3]) * (1.0 - margins[2] - margins[3]));
263 
264         /*
265          * If one of these relations is modified, then AxesDrawer::computeMargins should be modified too
266          */
267         legendDims[0] = normSpriteWidth + lineWidth + 3.0 * xOffset;
268         legendDims[1] = normSpriteHeight + 2.0 * yOffset;
269 
270         /* Save the legend box size */
271         Double[] DimsToSet = { legendDims[0], legendDims[1]};
272         legend.setSize(DimsToSet);
273 
274         axesPos[0] = axesBounds[0];
275         axesPos[1] = 1.0 - (axesBounds[1] + axesBounds[3]);
276 
277         axesDims[0] = axesBounds[2];
278         axesDims[1] = axesBounds[3];
279 
280         /* The {x, y} coordinates of the axes box's lower-left and upper-right corners (as defined by bounds and margins) */
281         double[] llBoxCorner = new double[2];
282         double[] urBoxCorner = new double[2];
283 
284         LegendLocation legendLocation = legend.getLegendLocationAsEnum();
285 
286         llBoxCorner[0] = axesPos[0] + margins[0] * axesDims[0];
287         llBoxCorner[1] = axesPos[1] + margins[3] * axesDims[1];
288 
289         urBoxCorner[0] = axesPos[0] + (1.0 - margins[1]) * axesDims[0];
290         urBoxCorner[1] = axesPos[1] + (1.0 - margins[2]) * axesDims[1];
291 
292         switch (legendLocation) {
293             case IN_UPPER_RIGHT:
294                 legendCorner[0] = (float) (urBoxCorner[0] - xOffset - legendDims[0]);
295                 legendCorner[1] = (float) (urBoxCorner[1] - yOffset - legendDims[1]);
296                 break;
297             case IN_UPPER_LEFT:
298                 legendCorner[0] = (float) (llBoxCorner[0] + xOffset);
299                 legendCorner[1] = (float) (urBoxCorner[1] - yOffset - legendDims[1]);
300                 break;
301             case IN_LOWER_RIGHT:
302                 legendCorner[0] = (float) (urBoxCorner[0] - xOffset - legendDims[0]);
303                 legendCorner[1] = (float) (llBoxCorner[1] + yOffset);
304                 break;
305             case IN_LOWER_LEFT:
306                 legendCorner[0] = (float) (llBoxCorner[0] + xOffset);
307                 legendCorner[1] = (float) (llBoxCorner[1] + yOffset);
308                 break;
309             case OUT_UPPER_RIGHT:
310                 legendCorner[0] = (float) (urBoxCorner[0] + xOffset);
311                 legendCorner[1] = (float) (urBoxCorner[1] - legendDims[1]);
312                 break;
313             case OUT_UPPER_LEFT:
314                 legendCorner[0] = (float) (llBoxCorner[0] - xOffset - legendDims[0]);
315                 legendCorner[1] = (float) (urBoxCorner[1] - legendDims[1]);
316                 break;
317             case OUT_LOWER_RIGHT:
318                 legendCorner[0] = (float) (urBoxCorner[0] + xOffset);
319                 legendCorner[1] = (float) (llBoxCorner[1]);
320                 break;
321             case OUT_LOWER_LEFT:
322                 legendCorner[0] = (float) (llBoxCorner[0] - xOffset - legendDims[0]);
323                 legendCorner[1] = (float) (llBoxCorner[1]);
324                 break;
325             case UPPER_CAPTION:
326                 legendCorner[0] = (float) (llBoxCorner[0]);
327                 legendCorner[1] = (float) (urBoxCorner[1] + yOffset);
328 
329                 /* x-axis at the top */
330                 if (xAxisLocation == 1) {
331                     /* To do: use the actual label+tick bounding box height */
332                     legendCorner[1] += TICK_LABEL_SIZE;
333                 }
334                 break;
335             case LOWER_CAPTION:
336                 legendCorner[0] = (float) (llBoxCorner[0]);
337                 legendCorner[1] = (float) (llBoxCorner[1] - yOffset - legendDims[1]);
338 
339                 /* x-axis at the bottom */
340                 if (xAxisLocation == 0) {
341                     /* To do: use the actual label+tick bounding box height */
342                     legendCorner[1] -= TICK_LABEL_SIZE;
343                 }
344                 break;
345             case BY_COORDINATES:
346                 Double[] legPos = legend.getPosition();
347 
348                 legendCorner[0] = (float) (axesPos[0] + legPos[0] * axesBounds[2]);
349                 legendCorner[1] = (float) (axesPos[1] + (1.0 - legPos[1]) * axesBounds[3] - legendDims[1]);
350                 break;
351         }
352 
353         /* y-axis positioned to the left */
354         if ((legendLocation == LegendLocation.OUT_UPPER_LEFT || legendLocation == LegendLocation.OUT_LOWER_LEFT) && yAxisLocation == 4) {
355             /* To do: use the actual label+tick bounding box width */
356             legendCorner[0] -= TICK_LABEL_SIZE;
357             /* y-axis positioned to the right */
358         } else if ((legendLocation == LegendLocation.OUT_UPPER_RIGHT || legendLocation == LegendLocation.OUT_LOWER_RIGHT) && yAxisLocation == 5) {
359             /* To do: use the actual label+tick bounding box width */
360             legendCorner[0] += TICK_LABEL_SIZE;
361         }
362 
363 
364         /* Afterwards, draw the elements making up the Legend using the previously computed values */
365 
366         /* Legend background vertex data: lower-left, lower-right, upper-left and upper-right corners */
367         float[] rectangleVertexData = new float[] {
368             (float)legendCorner[0], (float)legendCorner[1], Z_FRONT, 1.0f,
369             (float)(legendCorner[0] + legendDims[0]), (float)legendCorner[1], Z_FRONT, 1.0f,
370             (float)legendCorner[0], (float)(legendCorner[1] + legendDims[1]), Z_FRONT, 1.0f,
371             (float)(legendCorner[0] + legendDims[0]), (float)(legendCorner[1] + legendDims[1]), Z_FRONT, 1.0f
372         };
373 
374         /* The indices of a rectangle's triangles and a rectangle outline's segment loop */
375         int[] rectangleIndexData = new int[] {0, 1, 3, 0, 3, 2};
376         int[] rectangleOutlineIndexData = new int[] {0, 1, 1, 3, 3, 2, 2, 0};
377 
378         rectangleIndices.setData(rectangleIndexData);
379         rectangleOutlineIndices.setData(rectangleOutlineIndexData);
380 
381         rectangleVertices.setData(rectangleVertexData, 4);
382 
383         /* Legend rectangle background and outline */
384         DefaultGeometry legendRectangle = new DefaultGeometry();
385         legendRectangle.setVertices(rectangleVertices);
386         legendRectangle.setIndices(rectangleIndices);
387 
388         Appearance appearance = new Appearance();
389 
390         if (legend.getFillMode()) {
391             legendRectangle.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
392             appearance.setFillColor(ColorFactory.createColor(colorMap, legend.getBackground()));
393         } else {
394             legendRectangle.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
395         }
396 
397         /* Legend outline */
398         if (legend.getLineMode()) {
399             legendRectangle.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
400             legendRectangle.setWireIndices(rectangleOutlineIndices);
401 
402             appearance.setLineColor(ColorFactory.createColor(colorMap, legend.getLineColor()));
403             appearance.setLineWidth(legend.getLineThickness().floatValue());
404             appearance.setLinePattern(legend.getLineStyleAsEnum().asPattern());
405         } else {
406             legendRectangle.setLineDrawingMode(Geometry.LineDrawingMode.NONE);
407         }
408 
409         drawingTools.draw(legendRectangle, appearance);
410 
411         /* Lines: 3 vertices each, left, middle, and right */
412         float[] lineVertexData = new float[] {0.25f, 0.75f, Z_FRONT, 1.0f,
413                                               0.5f, 0.75f, Z_FRONT, 1.0f,
414                                               0.75f, 0.75f, Z_FRONT, 1.0f
415                                              };
416 
417         double normSpriteMargin = 0.0;
418 
419         if (nbValidLinks > 0) {
420             normSpriteMargin = (double) legendSpriteDrawer.getVMargin() / (double) canvasHeight;
421         }
422 
423         lineVertexData[0] = (float) (legendCorner[0] + xOffset);
424         lineVertexData[1] = (float) (legendCorner[1] + normSpriteMargin + yOffset);
425 
426         lineVertexData[4] = lineVertexData[0] + 0.5f * (float) lineWidth;
427         lineVertexData[5] = lineVertexData[1];
428 
429         lineVertexData[8] = lineVertexData[0] + (float) lineWidth;
430         lineVertexData[9] = lineVertexData[1];
431 
432         float deltaHeight = 0.0f;
433 
434         if (links.length > 0) {
435             deltaHeight = (float) (normSpriteHeight - 2.0 * normSpriteMargin) / ((float)(links.length));
436         }
437 
438         lineVertexData[1] = lineVertexData[1] + 0.5f * deltaHeight;
439         lineVertexData[5] = lineVertexData[5] + 0.5f * deltaHeight;
440         lineVertexData[9] = lineVertexData[9] + 0.5f * deltaHeight;
441 
442         /* Bar vertex data: lower-left, lower-right, upper-left and upper-right corners */
443         float[] barVertexData = new float[] {0.25f, 0.75f, Z_FRONT, 1.0f,
444                                              0.75f, 0.75f, Z_FRONT, 1.0f,
445                                              0.25f, 1.00f, Z_FRONT, 1.0f,
446                                              0.75f, 1.00f, Z_FRONT, 1.0f
447                                             };
448 
449         float barHeight = BAR_HEIGHT * deltaHeight;
450 
451         barVertexData[0] = (float) (legendCorner[0] + xOffset);
452         barVertexData[1] = (float) (legendCorner[1] + normSpriteMargin + yOffset) + 0.5f * (deltaHeight - barHeight);
453 
454         barVertexData[4] = barVertexData[0] + (float) lineWidth;
455         barVertexData[5] = barVertexData[1];
456 
457         barVertexData[8] = barVertexData[0];
458         barVertexData[9] = barVertexData[1] + barHeight;
459 
460         barVertexData[12] = barVertexData[4];
461         barVertexData[13] = barVertexData[9];
462 
463         for (Integer link : links) {
464             Polyline currentLine = (Polyline) GraphicController.getController().getObjectFromId(link);
465 
466             drawLegendItem(legend, drawingTools, colorMap, currentLine, barVertexData, lineVertexData);
467 
468             /* Update the vertex data's vertical position */
469             lineVertexData[1] += deltaHeight;
470             lineVertexData[5] += deltaHeight;
471             lineVertexData[9] += deltaHeight;
472 
473             barVertexData[1] += deltaHeight;
474             barVertexData[5] += deltaHeight;
475             barVertexData[9] += deltaHeight;
476             barVertexData[13] += deltaHeight;
477         }
478 
479         /* Legend text */
480         float[] spritePosition = new float[] {lineVertexData[8] + (float) xOffset, (float) (legendCorner[1] + yOffset), Z_FRONT};
481 
482         /* Draw the sprite only if there are valid links */
483         if (nbValidLinks > 0) {
484             drawingTools.draw(legendSprite, AnchorPosition.LOWER_LEFT, new Vector3d(spritePosition));
485         }
486 
487         /* Restore the transformation stacks */
488         modelViewStack.pop();
489         projectionStack.pop();
490 
491         /* Output the position if required */
492         Double[] legendPosition = new Double[2];
493 
494         if (axesDims[0] == 0.0) {
495             axesDims[0] = 1.0;
496         }
497 
498         if (axesDims[1] == 0.0) {
499             axesDims[1] = 1.0;
500         }
501 
502         legendPosition[0] = (legendCorner[0] - axesPos[0]) / axesDims[0];
503         legendPosition[1] = 1.0 - (legendCorner[1] + legendDims[1] - axesPos[1]) / axesDims[1];
504 
505         if (legendLocation != LegendLocation.BY_COORDINATES) {
506             legend.setPosition(legendPosition);
507         }
508     }
509 
510     /**
511      * Draw the legend item corresponding to the given polyline.
512      * It draws either a horizontal line or bar depending on the polyline's properties (style, fill and line modes).
513      * @param legend the Legend.
514      * @param drawingTools the DrawingTools {@see DrawingTools} used to draw the Legend.
515      * @param colorMap the colorMap used.
516      * @param polyline the given polyline.
517      * @param barVertexData a bar's vertex data (4 consecutive (x,y,z,w) quadruplets: lower-left, lower-right, upper-left and upper-right corners.
518      * @param lineVertexData a line's vertex data (3 consecutive (x,y,z,w) quadruplets: left, middle and right vertices).
519      * @throws org.scilab.forge.scirenderer.SciRendererException if the draw fail.
520      */
drawLegendItem(Legend legend, DrawingTools drawingTools, ColorMap colorMap, Polyline polyline, float[] barVertexData, float[] lineVertexData)521     private void drawLegendItem(Legend legend, DrawingTools drawingTools, ColorMap colorMap, Polyline polyline, float[] barVertexData, float[] lineVertexData) throws SciRendererException {
522         int polylineStyle = polyline.getPolylineStyle();
523 
524         int lineColor = polyline.getLineColor();
525         double lineThickness = polyline.getLineThickness();
526         short linePattern = polyline.getLineStyleAsEnum().asPattern();
527 
528         boolean isBar = (polylineStyle == 6) || (polylineStyle == 7);
529         boolean barDrawn = isBar || polyline.getFillMode();
530 
531         /* Draw a bar if the curve is a bar or if it is filled */
532         if (barDrawn) {
533             barVertices.setData(barVertexData, 4);
534 
535             DefaultGeometry bar = new DefaultGeometry();
536             bar.setFillDrawingMode(Geometry.FillDrawingMode.TRIANGLES);
537 
538             Appearance barAppearance = new Appearance();
539             barAppearance.setFillColor(ColorFactory.createColor(colorMap, polyline.getBackground()));
540             bar.setVertices(barVertices);
541             bar.setIndices(rectangleIndices);
542 
543             /* Bar outline */
544             if (isBar || polyline.getLineMode()) {
545                 bar.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
546                 bar.setWireIndices(rectangleOutlineIndices);
547 
548                 barAppearance.setLineColor(ColorFactory.createColor(colorMap, polyline.getLineColor()));
549                 barAppearance.setLineWidth((float) lineThickness);
550                 barAppearance.setLinePattern(linePattern);
551             } else {
552                 bar.setLineDrawingMode(Geometry.LineDrawingMode.NONE);
553             }
554 
555             drawingTools.draw(bar, barAppearance);
556         }
557 
558         /* Draw a line otherwise */
559         if (!barDrawn) {
560             lineVertices.setData(lineVertexData, 4);
561 
562             /* A line must also be drawn for the vertical polyline style (3), whatever line mode's value */
563             if (polyline.getLineMode() || polylineStyle == 3) {
564                 DefaultGeometry line = new DefaultGeometry();
565                 line.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
566                 line.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS_STRIP);
567                 line.setVertices(lineVertices);
568                 Appearance lineAppearance = new Appearance();
569                 lineAppearance.setLineColor(ColorFactory.createColor(colorMap, lineColor));
570                 lineAppearance.setLineWidth((float) lineThickness);
571                 lineAppearance.setLinePattern(linePattern);
572 
573                 drawingTools.draw(line, lineAppearance);
574             }
575         }
576 
577         /* Draw arrows */
578         if (polylineStyle == 4) {
579             if (barDrawn) {
580                 /*
581                  * Overlap can occur between the arrow heads of the bar's two smallest segments and the adjacent items.
582                  * To do: adjust arrow size to correct this.
583                  */
584                 visitor.getArrowDrawer().drawArrows(polyline.getParentAxes(), barVertices, rectangleOutlineIndices,
585                                                     polyline.getArrowSizeFactor(), lineThickness, lineColor, false);
586             } else {
587                 visitor.getArrowDrawer().drawArrows(polyline.getParentAxes(), lineVertices, lineIndices,
588                                                     polyline.getArrowSizeFactor(), lineThickness, lineColor, false);
589             }
590         }
591 
592         if (polyline.getMarkMode()) {
593             Appearance lineAppearance = new Appearance();
594             lineAppearance.setLineWidth((float) lineThickness);
595             Texture markTexture = markManager.getMarkSprite(polyline, colorMap, lineAppearance);
596             if (barDrawn) {
597                 drawingTools.draw(markTexture, AnchorPosition.CENTER, barVertices);
598             } else {
599                 int marksCount = legend.getMarksCount();
600                 float[] data;
601                 if (marksCount != 0) {
602                     switch (marksCount) {
603                         case 1:
604                             data = new float[] {lineVertexData[4], lineVertexData[5], lineVertexData[6], lineVertexData[7]};
605                             break;
606                         case 2:
607                             data = new float[] {lineVertexData[0], lineVertexData[1], lineVertexData[2], lineVertexData[3],
608                                                 lineVertexData[8], lineVertexData[9], lineVertexData[10], lineVertexData[11]
609                                            };
610                             break;
611                         default:
612                             data = new float[] {lineVertexData[0], lineVertexData[1], lineVertexData[2], lineVertexData[3],
613                                                 lineVertexData[4], lineVertexData[5], lineVertexData[6], lineVertexData[7],
614                                                 lineVertexData[8], lineVertexData[9], lineVertexData[10], lineVertexData[11]
615                                            };
616                             break;
617                     }
618                     lineVertices.setData(data, 4);
619                     drawingTools.draw(markTexture, AnchorPosition.CENTER, lineVertices);
620                 }
621             }
622         }
623     }
624 
625     /**
626      * Updates the legend by disposing its sprite.
627      * @param id the legend id.
628      * @param property the property to update.
629      */
update(Integer id, int property)630     public void update(Integer id, int property) {
631         if (textureMap.containsKey(id)) {
632             if (SPRITE_PROPERTIES.contains(property)) {
633                 dispose(id);
634             }
635         }
636     }
637 
638     /**
639      * Disposes the Legend sprite corresponding to the given id.
640      * @param id the legend id.
641      */
dispose(Integer id)642     public void dispose(Integer id) {
643         Texture texture = textureMap.get(id);
644         if (texture != null) {
645             textureManager.dispose(texture);
646             textureMap.remove(id);
647         }
648     }
649 
650     /**
651      * Disposes all the Legend resources.
652      */
disposeAll()653     public void disposeAll() {
654         visitor.getCanvas().getBuffersManager().dispose(rectangleVertices);
655         visitor.getCanvas().getBuffersManager().dispose(lineVertices);
656         visitor.getCanvas().getBuffersManager().dispose(barVertices);
657         visitor.getCanvas().getBuffersManager().dispose(lineIndices);
658         visitor.getCanvas().getBuffersManager().dispose(rectangleIndices);
659         visitor.getCanvas().getBuffersManager().dispose(rectangleOutlineIndices);
660 
661         textureManager.dispose(textureMap.values());
662         textureMap.clear();
663     }
664 
665     /**
666      * Returns the legend text texture.
667      * @param colorMap the color map.
668      * @param legend the Legend.
669      * @return the text sprite.
670      */
getTexture(ColorMap colorMap, Legend legend)671     private Texture getTexture(ColorMap colorMap, Legend legend) {
672         Texture texture = textureMap.get(legend.getIdentifier());
673         if (texture == null) {
674             this.legendSpriteDrawer = new LegendSpriteDrawer(colorMap, legend);
675             texture = textureManager.createTexture();
676             texture.setDrawer(legendSpriteDrawer);
677             textureMap.put(legend.getIdentifier(), texture);
678         }
679         return texture;
680     }
681 
682     /**
683      * Determines and returns the number of valid links for the given Legend object.
684      * @param legend the given Legend.
685      * @return the number of valid links.
686      */
getNumberValidLinks(Legend legend)687     private int getNumberValidLinks(Legend legend) {
688         int nbValidLinks = 0;
689         Integer[] links = legend.getLinks();
690 
691         for (Integer link : links) {
692             Polyline currentLine = (Polyline) GraphicController.getController().getObjectFromId(link);
693 
694             if (currentLine != null) {
695                 nbValidLinks++;
696             }
697         }
698 
699         return nbValidLinks;
700     }
701 
702     /**
703      * Updates the links and text properties of the Legend depending
704      * on the number of valid links provided (the number of links
705      * to non-null objects).
706      * To do: use the graphic controller to perform the update;
707      * move the link update from LegendDrawer to somewhere more appropriate.
708      * @param legend the Legend to update.
709      * @param nbValidLinks the number of valid links.
710      */
updateLinks(Legend legend, int nbValidLinks)711     private void updateLinks(Legend legend, int nbValidLinks) {
712         int i1 = 0;
713         ArrayList <Integer> newLinks = new ArrayList<Integer>(legend.getLinks().length);
714         String[] newStrings;
715         Integer[] newDims = new Integer[2];
716 
717         /*
718          * In case there are no valid links, we create a single empty String
719          * in order to retain the Legend's font properties.
720          */
721         if (nbValidLinks == 0) {
722             newDims[0] = 1;
723         } else {
724             newDims[0] = nbValidLinks;
725         }
726 
727         newDims[1] = 1;
728 
729         newStrings = new String[newDims[0]];
730 
731         if (nbValidLinks == 0) {
732             newStrings[0] = new String("");
733         }
734 
735         Integer[] links = legend.getLinks();
736         String[] strings = legend.getTextStrings();
737 
738         for (int i = 0; i < links.length; i++) {
739             Polyline currentLine = (Polyline) GraphicController.getController().getObjectFromId(links[i]);
740 
741             /* Text strings are stored in reverse order relative to links. */
742             if (currentLine != null) {
743                 newLinks.add(links[i]);
744 
745                 newStrings[nbValidLinks - i1 - 1] = new String(strings[strings.length - i - 1]);
746                 i1++;
747             }
748         }
749 
750         /* Update the legend's links and text */
751         legend.setLinks(newLinks);
752 
753         legend.setTextArrayDimensions(newDims);
754         legend.setTextStrings(newStrings);
755     }
756 }
757