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