001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030/** 031 * 032 */ 033package org.openimaj.vis.general; 034 035import org.openimaj.image.MBFImage; 036import org.openimaj.image.colour.ColourMap; 037import org.openimaj.image.colour.RGBColour; 038import org.openimaj.image.typography.hershey.HersheyFont; 039import org.openimaj.image.typography.hershey.HersheyFontStyle; 040import org.openimaj.math.geometry.point.Point2d; 041import org.openimaj.math.geometry.point.Point2dImpl; 042import org.openimaj.math.geometry.shape.Rectangle; 043import org.openimaj.util.array.ArrayUtils; 044import org.openimaj.vis.general.BarVisualisation.Bar; 045 046/** 047 * The {@link BarVisualisation} can be used to draw to an image a bar graph of 048 * any data set to an RGBA MBFImage. 049 * 050 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 051 * @created 26 Jul 2012 052 * @version $Author$, $Revision$, $Date$ 053 */ 054public class BarVisualisation extends XYPlotVisualisation<Bar> 055 implements ItemPlotter<Bar, Float[], MBFImage> 056{ 057 /** 058 * Represents a single bar to draw. 059 * 060 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 061 * @created 6 Aug 2013 062 * @version $Author$, $Revision$, $Date$ 063 */ 064 public static class Bar 065 { 066 /** The value (height) of the bar */ 067 public double value; 068 069 /** Data units where the bar is drawn */ 070 public double startX; 071 072 /** Data units where the bar drawing ends */ 073 public double endX; 074 075 /** The colour of the bar */ 076 public Float[] colour; 077 078 /** The stroke colour */ 079 public Float[] strokeColour = RGBColour.BLACK; 080 081 /** 082 * Default constructor 083 * 084 * @param value 085 * Height of the bar 086 * @param startX 087 * units of the start of the bar 088 * @param endX 089 * units at the end of the bar 090 * @param colour 091 * The colour of the bar 092 */ 093 public Bar(final double value, final double startX, final double endX, final Float[] colour) 094 { 095 this.value = value; 096 this.startX = startX; 097 this.endX = endX; 098 this.colour = colour; 099 } 100 101 @Override 102 public String toString() 103 { 104 return "Bar[" + this.startX + " to " + this.endX + " = " + this.value + "]"; 105 } 106 } 107 108 /** */ 109 private static final long serialVersionUID = 1L; 110 111 /** The colour of the background */ 112 private Float[] backgroundColour = new Float[] 113 { 0f, 0f, 0f, 1f }; 114 115 /** The colour of the bar */ 116 private Float[] barColour = new Float[] 117 { 1f, 0f, 0f, 1f }; 118 119 /** The colour to stroke the bar */ 120 private Float[] strokeColour = new Float[] 121 { 0f, 0f, 0f, 1f }; 122 123 /** The colour of the text to draw */ 124 private Float[] textColour = new Float[] 125 { 1f, 1f, 1f, 1f }; 126 127 /** The colour to stroke any text */ 128 private Float[] textStrokeColour = new Float[] 129 { 0f, 0f, 0f, 1f }; 130 131 /** Colour of the axis */ 132 private Float[] axisColour = new Float[] 133 { 1f, 1f, 1f, 1f }; 134 135 /** Width of the axis line */ 136 private int axisWidth = 1; 137 138 /** Number of pixels to pad the base of the text */ 139 private int textBasePad = 4; 140 141 /** Whether to auto scale the vertical axis */ 142 private boolean autoScale = true; 143 144 /** The maximum value of the scale (if autoScale is false) */ 145 // private double maxValue = 1d; 146 147 /** The minimum value of the scale (if autoScale if false) */ 148 // private double minValue = 0d; 149 150 /** Whether to draw the value of the bar in each bar */ 151 private boolean drawValue = false; 152 153 /** Whether to use individual colours for each bar */ 154 private final boolean useIndividualBarColours = false; 155 156 /** Whether or not to fix the axis */ 157 private boolean fixAxis = false; 158 159 /** The location of the fixed axis, if it is to be fixed */ 160 private double axisLocation = 100; 161 162 /** 163 * If the minimum value > 0 (or the max < 0), then whether the make the axis 164 * visible 165 */ 166 private boolean axisAlwaysVisible = true; 167 168 /** Whether to outline the text used to draw the values */ 169 private boolean outlineText = false; 170 171 /** The size of the text to draw */ 172 private int textSize = 12; 173 174 /** Whether to use a colour map or not */ 175 private boolean useColourMap = true; 176 177 /** The colour map to use if useColourMap == true */ 178 private ColourMap colourMap = ColourMap.Autumn; 179 180 /** The scalar being used to plot the data */ 181 private final double yscale = 0; 182 183 /** The range of the data being viewed */ 184 // private final double axisRangeY = 0; 185 186 /** 187 * Whether to use a fixed bar width. If so barWidth gives the size in data 188 * units 189 */ 190 private boolean useFixedBarWidth = true; 191 192 /** The width of each bar that's drawn */ 193 private double barWidth = 1; 194 195 /** 196 * Whether to centre the bars on the values (rather than between the values) 197 */ 198 private boolean centreBarsOnValues = false; 199 200 /** The label transformer used for the data point values */ 201 private LabelTransformer transformer = null; 202 203 private StrokeColourProvider<Float[]> strokeColourProvider = new StrokeColourProvider<Float[]>() { 204 205 @Override 206 public Float[] getStrokeColour(final int row) { 207 return BarVisualisation.this.strokeColour; 208 } 209 }; 210 211 /** 212 * Create a bar visualisation of the given size 213 * 214 * @param width 215 * The width of the image 216 * @param height 217 * The height of the image 218 */ 219 public BarVisualisation(final int width, final int height) 220 { 221 super(width, height); 222 super.setItemPlotter(this); 223 super.setRenderAxesLast(false); 224 } 225 226 /** 227 * Creates the given visualisation with the given data 228 * 229 * @param width 230 * The width of the image 231 * @param height 232 * The height of the image 233 * @param data 234 * The data to visualise 235 */ 236 public BarVisualisation(final int width, final int height, final double[] data) 237 { 238 this(width, height); 239 this.setData(data); 240 } 241 242 /** 243 * {@inheritDoc} 244 * 245 * @see org.openimaj.vis.general.ItemPlotter#renderRestarting() 246 */ 247 @Override 248 public void renderRestarting() 249 { 250 } 251 252 /** 253 * Plots a single bar into the visualisation. 254 * 255 * {@inheritDoc} 256 * 257 * @see org.openimaj.vis.general.ItemPlotter#plotObject(org.openimaj.image.Image, 258 * org.openimaj.vis.general.XYPlotVisualisation.LocatedObject, 259 * org.openimaj.vis.general.AxesRenderer2D) 260 */ 261 @Override 262 public void plotObject(final MBFImage visImage, final LocatedObject<Bar> object, 263 final AxesRenderer2D<Float[], MBFImage> renderer) 264 { 265 // Position on the x-axis 266 final int[] p = this.axesRenderer2D.calculatePosition(new double[] { object.x, object.y }); 267 268 // The position of the x axis and the start of the bar 269 final int[] z = this.axesRenderer2D.calculatePosition(new double[] { object.object.startX, 0 }); 270 271 // The position of the end of the bar (y is ignored) 272 final int[] p2 = this.axesRenderer2D.calculatePosition(new double[] { object.object.endX, 0 }); 273 274 // The width of the bar in pixels 275 final int barWidth = p2[0] - z[0]; 276 277 // The height of the bar 278 int barHeight = z[1] - p[1]; 279 int y = p[1]; 280 281 if (barHeight < 0) { 282 barHeight = -barHeight; 283 y = z[1]; 284 } 285 286 // The rectangle delimiting the bar 287 final Rectangle rect = new Rectangle(p[0], y, barWidth, barHeight); 288 289 // Work out what colour it should be 290 Float[] c = this.getBarColour(); 291 if (this.useColourMap) 292 c = this.colourMap.apply((float) (Math.abs(object.y) / this.axesRenderer2D.getMaxYValue())); 293 if (this.useIndividualBarColours) 294 c = object.object.colour; 295 296 // Draw the bar 297 visImage.drawShapeFilled(rect, c); 298 299 // Stroke the bar 300 if (object.object.strokeColour != null) 301 visImage.drawShape(rect, object.object.strokeColour); 302 303 // If we're to draw the value, do that here. 304 if (this.drawValue) 305 { 306 // We'll draw the bar's value 307 String text; 308 if (this.transformer != null) 309 text = this.transformer.transform(object.y); 310 else 311 text = "" + object.y; 312 313 // Find the width and height of the text to draw 314 final HersheyFont f = HersheyFont.TIMES_BOLD; 315 final HersheyFontStyle<Float[]> style = f.createStyle(this.visImage.createRenderer()); 316 style.setFontSize(this.textSize); 317 final Rectangle r = f.createStyle(this.visImage.createRenderer()) 318 .getRenderer(this.visImage.createRenderer()) 319 .getSize(text, style); 320 321 // Work out where to put the text 322 // tx is the centre of the bar minus half the text bounds 323 int tx = (int) (z[0] + barWidth / 2 - r.width / 2); 324 325 // ty is the top of the bar minus a small padding 326 final int ty = (int) ((object.y >= 0 ? rect.y : rect.y + rect.height + r.height) - 327 (object.y >= 0 ? this.textBasePad : -this.textBasePad)); 328 329 // Make sure the text will be drawn within the bounds of the image. 330 if (tx < 0) 331 tx = 0; 332 if (tx + r.width > this.getWidth()) 333 tx = this.getWidth() - (int) r.width; 334 335 // Stroke the text, if necessary 336 if (this.isOutlineText()) 337 { 338 this.visImage.drawText(text, tx - 1, ty - 1, f, this.textSize, this.getTextStrokeColour()); 339 this.visImage.drawText(text, tx + 1, ty - 1, f, this.textSize, this.getTextStrokeColour()); 340 this.visImage.drawText(text, tx - 1, ty + 1, f, this.textSize, this.getTextStrokeColour()); 341 this.visImage.drawText(text, tx + 1, ty + 1, f, this.textSize, this.getTextStrokeColour()); 342 } 343 344 // Fill the text 345 this.visImage.drawText(text, tx, ty, f, this.textSize, this.getTextColour()); 346 } 347 } 348 349 /** 350 * Sets whether values are drawn to the image. 351 * 352 * @param tf 353 * TRUE to draw values 354 */ 355 public void setDrawValues(final boolean tf) 356 { 357 this.drawValue = tf; 358 } 359 360 /** 361 * Set the data from a float array. 362 * 363 * @param data 364 * The data to set 365 */ 366 public void setData(final float[] data) 367 { 368 this.setData(ArrayUtils.convertToDouble(data)); 369 } 370 371 /** 372 * Set the data from a long array. 373 * 374 * @param data 375 * The data to set 376 */ 377 public void setData(final long[] data) 378 { 379 this.setData(ArrayUtils.convertToDouble(data)); 380 } 381 382 /** 383 * Set the data to a double array 384 * 385 * @param data 386 * The data 387 */ 388 public void setData(final double[] data) 389 { 390 super.data.clear(); 391 392 if (this.useFixedBarWidth) 393 for (int i = 0; i < data.length; i++) 394 super.data.add(new LocatedObject<Bar>(i, data[i], 395 new Bar(data[i], i, i + this.barWidth, RGBColour.RED))); 396 else 397 for (int i = 0; i < data.length; i++) 398 super.data.add(new LocatedObject<Bar>(i, data[i], 399 new Bar(data[i], i, i + this.barWidth, RGBColour.RED))); 400 401 super.validateData(); 402 this.axesRenderer2D.setMaxXValue(data.length); 403 404 // Force the axis to be zero if the axis always visible flag is set 405 if (axisAlwaysVisible) 406 if (getMinValue() > 0) 407 setMinValue(0); 408 else if (getMaxValue() < 0) 409 setMaxValue(0); 410 411 super.updateVis(); 412 } 413 414 /** 415 * Fix the x-axis to the given position in pixels. Note that the position is 416 * given from the bottom of the visualisation window. 417 * 418 * @param position 419 * The position in pixels 420 */ 421 public void fixAxis(final int position) 422 { 423 this.axisLocation = -position; 424 this.fixAxis = true; 425 } 426 427 /** 428 * Allow the x-axis to move as best to fit the data 429 */ 430 public void floatAxis() 431 { 432 this.fixAxis = false; 433 } 434 435 /** 436 * @return the outlineText 437 */ 438 public boolean isOutlineText() 439 { 440 return this.outlineText; 441 } 442 443 /** 444 * @param outlineText 445 * the outlineText to set 446 */ 447 public void setOutlineText(final boolean outlineText) 448 { 449 this.outlineText = outlineText; 450 } 451 452 /** 453 * @return the textSize 454 */ 455 public int getTextSize() 456 { 457 return this.textSize; 458 } 459 460 /** 461 * @param textSize 462 * the textSize to set 463 */ 464 public void setTextSize(final int textSize) 465 { 466 this.textSize = textSize; 467 } 468 469 /** 470 * Whether to use a colour map and which one. 471 * 472 * @param cp 473 * The colour map to use 474 */ 475 public void useColourMap(final ColourMap cp) 476 { 477 this.colourMap = cp; 478 this.useColourMap = true; 479 } 480 481 /** 482 * Revert back to using a static colour rather than a colour map; 483 */ 484 public void useStaticColour() 485 { 486 this.useColourMap = false; 487 } 488 489 /** 490 * @return the barColour 491 */ 492 public Float[] getBarColour() 493 { 494 return this.barColour; 495 } 496 497 /** 498 * @param row 499 * the row 500 * @return the strokeColour 501 */ 502 public Float[] getStrokeColour(final int row) 503 { 504 return this.strokeColourProvider.getStrokeColour(row); 505 } 506 507 /** 508 * @param prov 509 */ 510 public void setStrokeProvider(final StrokeColourProvider<Float[]> prov) { 511 this.strokeColourProvider = prov; 512 } 513 514 /** 515 * @return the textColour 516 */ 517 public Float[] getTextColour() 518 { 519 return this.textColour; 520 } 521 522 /** 523 * @return the textStrokeColour 524 */ 525 public Float[] getTextStrokeColour() 526 { 527 return this.textStrokeColour; 528 } 529 530 /** 531 * @return the backgroundColour 532 */ 533 public Float[] getBackgroundColour() 534 { 535 return this.backgroundColour; 536 } 537 538 /** 539 * @param backgroundColour 540 * the backgroundColour to set 541 */ 542 public void setBackgroundColour(final Float[] backgroundColour) 543 { 544 this.backgroundColour = backgroundColour; 545 } 546 547 /** 548 * @param barColour 549 * the barColour to set 550 */ 551 public void setBarColour(final Float[] barColour) 552 { 553 this.barColour = barColour; 554 this.useColourMap = false; 555 } 556 557 /** 558 * @param strokeColour 559 * the strokeColour to set 560 */ 561 public void setStrokeColour(final Float[] strokeColour) 562 { 563 this.strokeColour = strokeColour; 564 } 565 566 /** 567 * @param textColour 568 * the textColour to set 569 */ 570 public void setTextColour(final Float[] textColour) 571 { 572 this.textColour = textColour; 573 } 574 575 /** 576 * @param textStrokeColour 577 * the textStrokeColour to set 578 */ 579 public void setTextStrokeColour(final Float[] textStrokeColour) 580 { 581 this.textStrokeColour = textStrokeColour; 582 } 583 584 /** 585 * @return the axisColour 586 */ 587 public Float[] getAxisColour() 588 { 589 return this.axisColour; 590 } 591 592 /** 593 * @param axisColour 594 * the axisColour to set 595 */ 596 public void setAxisColour(final Float[] axisColour) 597 { 598 this.axisColour = axisColour; 599 } 600 601 /** 602 * Get the width of the axis being drawn 603 * 604 * @return The axis width 605 */ 606 public int getAxisWidth() 607 { 608 return this.axisWidth; 609 } 610 611 /** 612 * Set the axis width 613 * 614 * @param axisWidth 615 * The new axis width 616 */ 617 public void setAxisWidth(final int axisWidth) 618 { 619 this.axisWidth = axisWidth; 620 } 621 622 /** 623 * Returns whether the bars are auto scaling 624 * 625 * @return TRUE if auto scaling 626 */ 627 public boolean isAutoScale() 628 { 629 return this.autoScale; 630 } 631 632 /** 633 * Set whether the bars should auto scale to fit all values within the vis. 634 * 635 * @param autoScale 636 * TRUE to auto scale the values 637 */ 638 public void setAutoScale(final boolean autoScale) 639 { 640 this.autoScale = autoScale; 641 } 642 643 /** 644 * Get the maximum value for the scaling 645 * 646 * @return The maximum value 647 */ 648 public double getMaxValue() 649 { 650 return axesRenderer2D.getyAxisConfig().getMaxValue(); 651 } 652 653 /** 654 * Set the maximum value (in units) for the bars. Automatically sets the 655 * autoScaling to FALSE. 656 * 657 * @param maxValue 658 * Set the maximum value to use 659 */ 660 public void setMaxValue(final double maxValue) 661 { 662 axesRenderer2D.getyAxisConfig().setMaxValue(maxValue); 663 this.autoScale = false; 664 } 665 666 /** 667 * Get the minimum value in use. 668 * 669 * @return The minimum value 670 */ 671 public double getMinValue() 672 { 673 return axesRenderer2D.getyAxisConfig().getMinValue(); 674 } 675 676 /** 677 * Set the minimum value (in units) to use to plot the bars. Automatically 678 * sets the auto scaling to FALSE. 679 * 680 * @param minValue 681 * the minimum value 682 */ 683 public void setMinValue(final double minValue) 684 { 685 axesRenderer2D.getyAxisConfig().setMinValue(minValue); 686 this.autoScale = false; 687 } 688 689 /** 690 * Whether the axis is always visible 691 * 692 * @return TRUE if the axis is always visible 693 */ 694 public boolean isAxisAlwaysVisible() 695 { 696 return this.axisAlwaysVisible; 697 } 698 699 /** 700 * Set whether the axis should always be visible. If the minimum value is > 701 * 0 or maximum value < 0, then the axis will be made visible (either at the 702 * bottom or the top of the viewport respectively) if this is TRUE. This has 703 * no effect if the axis is fixed and set to a point outside the viewport. 704 * 705 * @param axisAlwaysVisible 706 * TRUE to make the axis always visible 707 */ 708 public void setAxisAlwaysVisible(final boolean axisAlwaysVisible) 709 { 710 this.axisAlwaysVisible = axisAlwaysVisible; 711 } 712 713 /** 714 * Returns the last calculated axis location 715 * 716 * @return the axisLocation The axis location 717 */ 718 public double getAxisLocation() 719 { 720 return axesRenderer2D.getyAxisRenderer().calculatePosition(0d)[1]; 721 } 722 723 /** 724 * Set the axis location. Automatically fixes the axis location 725 * 726 * @param axisLocation 727 * the axisLocation to set 728 */ 729 public void setAxisLocation(final double axisLocation) 730 { 731 this.axisLocation = axisLocation; 732 this.fixAxis = true; 733 } 734 735 /** 736 * Returns whether the axis is fixed or not. 737 * 738 * @return the fixAxis TRUE if the axis is fixed; FALSE otherwise 739 */ 740 public boolean isFixAxis() 741 { 742 return this.fixAxis; 743 } 744 745 /** 746 * Set whether the axis should be fixed. 747 * 748 * @param fixAxis 749 * TRUE to fix the axis; FALSE to allow it to float 750 */ 751 public void setFixAxis(final boolean fixAxis) 752 { 753 this.fixAxis = fixAxis; 754 } 755 756 /** 757 * The y-scale being used to plot the data. 758 * 759 * @return the yscale The y-scale 760 */ 761 public double getYscale() 762 { 763 return axesRenderer2D.getyAxisRenderer().scaleDimension(1d)[0]; 764 } 765 766 /** 767 * The data range being displayed. 768 * 769 * @return the axisRangeY 770 */ 771 public double getAxisRangeY() 772 { 773 return axesRenderer2D.getyAxisConfig().getMaxValue() - 774 axesRenderer2D.getyAxisConfig().getMinValue(); 775 } 776 777 /** 778 * The data range being displayed. 779 * 780 * @return the axisRangeX 781 */ 782 public double getAxisRangeX() 783 { 784 return axesRenderer2D.getxAxisConfig().getMaxValue() - 785 axesRenderer2D.getxAxisConfig().getMinValue(); 786 } 787 788 /** 789 * Returns the units value at the given pixel coordinate. 790 * 791 * @param x 792 * The x pixel coordinate 793 * @param y 794 * The y pixel coordinate 795 * @return The cartesian unit coordinate 796 */ 797 public Point2d getValueAt(final int x, final int y) 798 { 799 return new Point2dImpl(x * this.data.size() / this.getWidth(), 800 (float) ((this.axisLocation - y) / this.yscale)); 801 } 802 803 /** 804 * @return the barWidth 805 */ 806 public double getBarWidth() 807 { 808 return this.barWidth; 809 } 810 811 /** 812 * @param barWidth 813 * the barWidth to set 814 */ 815 public void setBarWidth(final double barWidth) 816 { 817 this.barWidth = barWidth; 818 } 819 820 /** 821 * @return the useFixedBarWidth 822 */ 823 public boolean isUseFixedBarWidth() 824 { 825 return this.useFixedBarWidth; 826 } 827 828 /** 829 * @param useFixedBarWidth 830 * the useFixedBarWidth to set 831 */ 832 public void setUseFixedBarWidth(final boolean useFixedBarWidth) 833 { 834 this.useFixedBarWidth = useFixedBarWidth; 835 } 836 837 /** 838 * @return the centreBarsOnValues 839 */ 840 public boolean isCentreBarsOnValues() 841 { 842 return this.centreBarsOnValues; 843 } 844 845 /** 846 * @param centreBarsOnValues 847 * the centreBarsOnValues to set 848 */ 849 public void setCentreBarsOnValues(final boolean centreBarsOnValues) 850 { 851 this.centreBarsOnValues = centreBarsOnValues; 852 } 853 854 /** 855 * @return the textBasePad 856 */ 857 public int getTextBasePad() 858 { 859 return this.textBasePad; 860 } 861 862 /** 863 * @param textBasePad 864 * the textBasePad to set 865 */ 866 public void setTextBasePad(final int textBasePad) 867 { 868 this.textBasePad = textBasePad; 869 } 870 871 /** 872 * @return the transformer 873 */ 874 public LabelTransformer getTransformer() 875 { 876 return this.transformer; 877 } 878 879 /** 880 * @param transformer 881 * the transformer to set 882 */ 883 public void setTransformer(final LabelTransformer transformer) 884 { 885 this.transformer = transformer; 886 } 887 888 /** 889 * Shows a basic bar visualisation. 890 * 891 * @param args 892 * The bar visualisation. 893 */ 894 public static void main(final String[] args) 895 { 896 final int nPoints = 10; 897 898 final double[] data = new double[nPoints]; 899 for (int i = 0; i < nPoints; i++) 900 data[i] = nPoints * (Math.random() * 2 - 1); 901 902 final BarVisualisation bv = new BarVisualisation(1000, 600); 903 bv.setDrawValues(true); 904 bv.setData(data); 905 bv.setTransformer(new LabelTransformer() 906 { 907 @Override 908 public String transform(final double value) 909 { 910 return String.format("%2.2f", value); 911 } 912 }); 913 bv.showWindow("Bar Visualisation Demo"); 914 } 915}