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}