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;
034import org.openimaj.image.DisplayUtilities;
035import org.openimaj.image.Image;
036import org.openimaj.image.MBFImage;
037import org.openimaj.image.colour.RGBColour;
038import org.openimaj.math.geometry.point.Point2d;
039import org.openimaj.math.geometry.point.Point2dImpl;
040import org.openimaj.math.geometry.transforms.TransformUtilities;
041import org.openimaj.vis.DataUnitsTransformer;
042
043import Jama.Matrix;
044
045/**
046 *      TODO: javadoc
047 *
048 *      @author David Dupplaw (dpd@ecs.soton.ac.uk)
049 *      @param <Q> The pixel type of the image you'll be drawing the axes to
050 *      @param <I> The type of image being drawn to
051 *  @created 3 Jun 2013
052 */
053public class AxesRenderer2D<Q,I extends Image<Q,I>>
054        implements DataUnitsTransformer<Q,double[],int[]>
055{
056        /** The axis renderer we'll use to render the x axis */
057        private final AxisRenderer2D<Q> xAxisRenderer = new AxisRenderer2D<Q>();
058
059        /** The axis renderer we'll use to render the y axis */
060        private final AxisRenderer2D<Q> yAxisRenderer = new AxisRenderer2D<Q>();
061
062        /** The configuration for the xAxis */
063        private AxisConfig<Q> xAxisConfig = this.xAxisRenderer.getConfig();
064
065        /** The configuration for the yAxis */
066        private AxisConfig<Q> yAxisConfig = this.yAxisRenderer.getConfig();
067
068        /** How far from the left of the image will the x axis start */
069        private int axisPaddingLeft = 20;
070
071        /** How far from the right of the image will the x axis stop */
072        private int axisPaddingRight = 20;
073
074        /** How far from the top of the image with the y axis start */
075        private int axisPaddingTop = 20;
076
077        /** How far from the bottom of the image will the y axis stop */
078        private int axisPaddingBottom = 20;
079
080        /** Whether to work out the ideal position for the axes */
081        private boolean autoScaleAxes = false;
082
083        private Matrix dataTransformMatrix;
084
085        /**
086         *      Default constructor
087         */
088        public AxesRenderer2D()
089        {
090                this.xAxisConfig.setOrientation( new double[] { 0 } );
091                this.yAxisConfig.setOrientation( new double[] {
092                                this.xAxisConfig.getOrientation()[0]-Math.PI/2d } );
093        }
094
095        /**
096         *      Set the orientation of the x axis
097         *      @param rads angle in radians
098         */
099        public void setOrientation( final double rads )
100        {
101                this.xAxisConfig.setOrientation( new double[] { rads } );
102                this.yAxisConfig.setOrientation( new double[] {
103                                this.xAxisConfig.getOrientation()[0]-Math.PI/2d } );
104
105        }
106
107        /**
108         *      Render the axis to the given image
109         *      @param image The image to draw the axes to
110         */
111        public void renderAxis( final I image )
112        {
113                // Set the image to draw upon
114                this.setImage( image );
115
116                this.xAxisRenderer.renderAxis();
117                this.yAxisRenderer.renderAxis();
118
119//
120//              // Create the renderer to draw to the image
121//              final ImageRenderer<Q, ? extends Image<Q, I>> ir = image.createRenderer( RenderHints.ANTI_ALIASED );
122//
123//              // Get the dimensions of the image to draw to
124//              final int w = image.getWidth();
125//              final int h = image.getHeight();
126//
127//              // Find the pixel spacing to use
128//              final double xRange = this.maxXValue - this.minXValue;
129//              this.xUnitSizePx = (w-this.axisPaddingLeft-this.axisPaddingRight) / xRange;
130//              final double yRange = this.maxYValue - this.minYValue;
131//              this.yUnitSizePx = (h-this.axisPaddingBottom-this.axisPaddingTop) / yRange;
132//
133//              // Pixel position of where the axes crossing happens
134//              this.yAxisConfig.setLocation( new double[] {this.axisPaddingLeft - this.minXValue*this.xUnitSizePx,0} );
135//              this.xAxisPosition = h - this.axisPaddingBottom + this.minYValue*this.yUnitSizePx;
136//
137//              // Draw the x-axis minor ticks
138//              if( this.drawXAxis && this.drawXTicks )
139//              {
140//                      for( double v = this.minXValue; v <= this.maxXValue; v += this.xMinorTickSpacing )
141//                      {
142//                              if( this.drawMinorTickGrid )
143//                                      ir.drawLine(
144//                                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
145//                                                      0,
146//                                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
147//                                                      image.getHeight(),
148//                                                      this.minorGridThickness,
149//                                                      this.minorGridColour );
150//
151//                              ir.drawLine(
152//                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
153//                                      (int)(this.xAxisPosition-this.minorTickLength),
154//                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
155//                                      (int)(this.xAxisPosition+this.minorTickLength),
156//                                      this.minorTickThickness,
157//                                      this.minorTickColour );
158//                      }
159//              }
160//
161//              // Draw the x-axis major ticks
162//              if( this.drawXAxis && this.drawXTicks )
163//              {
164//                      for( double v = this.minXValue; v <= this.maxXValue; v += this.xMajorTickSpacing )
165//                      {
166//                              if( this.drawMajorTickGrid )
167//                                      ir.drawLine(
168//                                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
169//                                                      0,
170//                                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
171//                                                      image.getHeight(),
172//                                                      this.majorGridThickness,
173//                                                      this.majorGridColour );
174//
175//                              ir.drawLine(
176//                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
177//                                      (int)(this.xAxisPosition-this.majorTickLength),
178//                                      (int)(this.yAxisConfig.getLocation()[0] + v * this.xUnitSizePx),
179//                                      (int)(this.xAxisPosition+this.majorTickLength),
180//                                      this.majorTickThickness,
181//                                      this.majorTickColour );
182//                      }
183//              }
184//
185//              // Draw the x tick labels
186//              double maxXLabelPosition = 0;
187//              if( this.drawXAxis && this.drawXTickLabels )
188//              {
189//                      final int yPos = (int)(this.xAxisPosition + this.xTickLabelSize + this.majorTickLength
190//                                      + this.xTickLabelSize/2 );
191//
192//                      @SuppressWarnings( "rawtypes" )
193//                      final FontStyle s = this.xTickLabelFont.createStyle( ir );
194//                      s.setFontSize( this.xTickLabelSize );
195//
196//                      @SuppressWarnings( "rawtypes" )
197//                      final FontRenderer r = this.xTickLabelFont.getRenderer( ir );
198//
199//                      for( double v = this.minXValue; v <= this.maxXValue; v += this.xLabelSpacing )
200//                      {
201//                              String text = ""+v;
202//                              if( this.xAxisLabelTransformer != null )
203//                                      text = this.xAxisLabelTransformer.transform( v );
204//
205//                              final float fw = r.getBounds( text, s ).width;
206//                              final int xPos = (int)(this.yAxisConfig.getLocation()[0] + v*this.xUnitSizePx - fw/2);
207//                              ir.drawText( text, xPos, yPos, this.xTickLabelFont,
208//                                              this.xTickLabelSize, this.xTickLabelColour );
209//                      }
210//                      maxXLabelPosition = yPos;
211//              }
212//
213//              // Draw the y-axis ticks
214//              if( this.drawYAxis && this.drawYTicks )
215//              {
216//                      for( double v = this.minYValue; v <= this.maxYValue; v += this.yMinorTickSpacing )
217//                      {
218//                              if( this.drawMinorTickGrid )
219//                                      ir.drawLine(
220//                                                      0,
221//                                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
222//                                                      image.getWidth(),
223//                                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
224//                                                      this.minorGridThickness,
225//                                                      this.minorGridColour );
226//
227//                              ir.drawLine(
228//                                      (int)(this.yAxisConfig.getLocation()[0]-this.minorTickLength),
229//                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
230//                                      (int)(this.yAxisConfig.getLocation()[0]+this.minorTickLength),
231//                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
232//                                      this.minorTickThickness,
233//                                      this.minorTickColour );
234//                      }
235//              }
236//
237//              // Draw the y-axis ticks
238//              if( this.drawYAxis && this.drawYTicks )
239//              {
240//                      for( double v = this.minYValue; v <= this.maxYValue; v += this.yMajorTickSpacing )
241//                      {
242//                              if( this.drawMajorTickGrid )
243//                                      ir.drawLine(
244//                                                      0,
245//                                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
246//                                                      image.getWidth(),
247//                                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
248//                                                      this.majorGridThickness,
249//                                                      this.majorGridColour );
250//
251//                              ir.drawLine(
252//                                      (int)(this.yAxisPosition-this.majorTickLength),
253//                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
254//                                      (int)(this.yAxisPosition+this.majorTickLength),
255//                                      (int)(this.xAxisPosition - v * this.yUnitSizePx),
256//                                      this.majorTickThickness,
257//                                      this.majorTickColour );
258//                      }
259//              }
260//
261//              // Draw the x tick labels
262//              double minYLabelPosition = this.yAxisPosition;
263//              if( this.drawYAxis && this.drawYTickLabels )
264//              {
265//                      @SuppressWarnings( "rawtypes" )
266//                      final FontStyle s = this.yTickLabelFont.createStyle( ir );
267//                      s.setFontSize( this.yTickLabelSize );
268//
269//                      @SuppressWarnings( "rawtypes" )
270//                      final FontRenderer r = this.yTickLabelFont.getRenderer( ir );
271//
272//                      for( double v = this.minYValue; v <= this.maxYValue; v += this.yLabelSpacing )
273//                      {
274//                              String text = ""+v;
275//                              if( this.yAxisLabelTransformer != null )
276//                                      text = this.yAxisLabelTransformer.transform( v );
277//
278//                              final float fw = r.getBounds( text, s ).width;
279//                              final int xPos = (int)(this.yAxisPosition - fw - this.majorTickLength
280//                                              - this.yTickLabelSize/2 );      // Last part is just a bit of padding
281//                              final int yPos = (int)(this.xAxisPosition - v*this.yUnitSizePx + this.yTickLabelSize/2 );
282//                              ir.drawText( text, xPos, yPos, this.yTickLabelFont,
283//                                              this.yTickLabelSize, this.yTickLabelColour );
284//                              minYLabelPosition = Math.min( xPos, minYLabelPosition );
285//                      }
286//              }
287//
288//              // Draw the X-axis
289//              if( this.drawXAxis )
290//                      ir.drawLine( this.axisPaddingLeft, (int)this.xAxisPosition, w-this.axisPaddingRight,
291//                              (int)this.xAxisPosition, this.xAxisThickness, this.xAxisColour );
292//
293//              // Draw the Y-axis
294//              if( this.drawYAxis )
295//                      ir.drawLine( (int)this.yAxisPosition, this.axisPaddingTop, (int)this.yAxisPosition,
296//                              h-this.axisPaddingBottom, this.yAxisThickness, this.yAxisColour );
297//
298//              // Draw the X-axis label
299//              if( this.drawXAxis && this.drawXAxisName )
300//                      ir.drawText( this.xAxisName, this.axisPaddingLeft,
301//                                      (int)(maxXLabelPosition + this.xAxisNameSize),
302//                                      this.xAxisNameFont,
303//                                      this.xAxisNameSize, this.xAxisNameColour );
304//
305//              // Draw the Y-axis label
306//              if( this.drawYAxis && this.drawYAxisName )
307//              {
308//                      @SuppressWarnings( "rawtypes" )
309//                      final FontStyle s = this.yAxisNameFont.createStyle( ir );
310//                      s.setFontSize( this.yAxisNameSize );
311//
312//                      final float fw = this.yAxisNameFont.getRenderer( ir ).getBounds(
313//                                      this.yAxisName, s ).width;
314//
315//                      ir.drawText( this.yAxisName, (int)(minYLabelPosition - fw),
316//                                      this.yAxisNameSize + this.axisPaddingTop, this.yAxisNameFont,
317//                                      this.yAxisNameSize, this.yAxisNameColour );
318//              }
319        }
320
321        /**
322         *      For a given coordinate in the units of the data, will calculate
323         *      the pixel position.
324         *
325         *      @param image The image in which the axes were drawn
326         *      @param x The x position
327         *      @param y The y position
328         *      @return The pixel position
329         */
330//      @Override
331//      public Point2d calculatePosition(
332//                      final I image, final double x, final double y )
333//      {
334//              return new Point2dImpl( (float)(this.yAxisPosition + x*this.xUnitSizePx),
335//                              (float)(this.xAxisPosition - y*this.yUnitSizePx) );
336//      }
337
338        /**
339         *      @param drawXAxis the drawXAxis to set
340         */
341        public void setDrawXAxis( final boolean drawXAxis )
342        {
343                this.xAxisConfig.getRenderingConfig().setRenderAxis( drawXAxis );
344        }
345
346        /**
347         *      @param drawYAxis the drawYAxis to set
348         */
349        public void setDrawYAxis( final boolean drawYAxis )
350        {
351                this.yAxisConfig.getRenderingConfig().setRenderAxis( drawYAxis );
352        }
353
354        /**
355         *      @return the axisPaddingLeft
356         */
357        public int getAxisPaddingLeft()
358        {
359                return this.axisPaddingLeft;
360        }
361
362        /**
363         *      @param axisPaddingLeft the axisPaddingLeft to set
364         */
365        public void setAxisPaddingLeft( final int axisPaddingLeft )
366        {
367                this.axisPaddingLeft = axisPaddingLeft;
368        }
369
370        /**
371         *      @return the axisPaddingRight
372         */
373        public int getAxisPaddingRight()
374        {
375                return this.axisPaddingRight;
376        }
377
378        /**
379         *      @param axisPaddingRight the axisPaddingRight to set
380         */
381        public void setAxisPaddingRight( final int axisPaddingRight )
382        {
383                this.axisPaddingRight = axisPaddingRight;
384        }
385
386        /**
387         *      @return the axisPaddingTop
388         */
389        public int getAxisPaddingTop()
390        {
391                return this.axisPaddingTop;
392        }
393
394        /**
395         *      @param axisPaddingTop the axisPaddingTop to set
396         */
397        public void setAxisPaddingTop( final int axisPaddingTop )
398        {
399                this.axisPaddingTop = axisPaddingTop;
400        }
401
402        /**
403         *      @return the axisPaddingBottom
404         */
405        public int getAxisPaddingBottom()
406        {
407                return this.axisPaddingBottom;
408        }
409
410        /**
411         *      @param axisPaddingBottom the axisPaddingBottom to set
412         */
413        public void setAxisPaddingBottom( final int axisPaddingBottom )
414        {
415                this.axisPaddingBottom = axisPaddingBottom;
416        }
417
418        /**
419         *      @param xAxisPosition the xAxisPosition to set
420         */
421        public void setxAxisPosition( final double xAxisPosition )
422        {
423                this.xAxisConfig.setLocation( new double[] { this.axisPaddingLeft, xAxisPosition } );
424        }
425
426        /**
427         *      The y position of the x axis.
428         *      @return the y position.
429         */
430        public double getxAxisPosition()
431        {
432                return this.xAxisConfig.getLocation()[1];
433        }
434
435        /**
436         *      @return the autoScaleAxes
437         */
438        public boolean isAutoScaleAxes()
439        {
440                return this.autoScaleAxes;
441        }
442
443        /**
444         *      @param autoScaleAxes the autoScaleAxes to set
445         */
446        public void setAutoScaleAxes( final boolean autoScaleAxes )
447        {
448                this.autoScaleAxes = autoScaleAxes;
449        }
450
451        /**
452         *      @param xAxisThickness the xAxisThickness to set
453         */
454        public void setxAxisThickness( final int xAxisThickness )
455        {
456                this.xAxisConfig.getRenderingConfig().setThickness( xAxisThickness );
457        }
458
459        /**
460         *      @param xAxisColour the xAxisColour to set
461         */
462        public void setxAxisColour( final Q xAxisColour )
463        {
464                this.xAxisConfig.getRenderingConfig().setColour( xAxisColour );
465        }
466
467        /**
468         *      @param yAxisThickness the yAxisThickness to set
469         */
470        public void setyAxisThickness( final int yAxisThickness )
471        {
472                this.yAxisConfig.getRenderingConfig().setThickness( yAxisThickness );
473        }
474
475        /**
476         *      @param yAxisColour the yAxisColour to set
477         */
478        public void setyAxisColour( final Q yAxisColour )
479        {
480                this.yAxisConfig.getRenderingConfig().setColour( yAxisColour );
481        }
482
483        /**
484         *      @param maxXValue the maxXValue to set
485         */
486        public void setMaxXValue( final double maxXValue )
487        {
488                this.xAxisConfig.setMaxValue( this.xAxisRenderer.nearestHigherMajorTick( maxXValue ) );
489        }
490
491        /**
492         *      @param minXValue the minXValue to set
493         */
494        public void setMinXValue( final double minXValue )
495        {
496                this.xAxisConfig.setMinValue( this.xAxisRenderer.nearestLowerMajorTick( minXValue ) );
497        }
498
499        /**
500         *      @param maxYValue the maxYValue to set
501         */
502        public void setMaxYValue( final double maxYValue )
503        {
504                this.yAxisConfig.setMaxValue( this.yAxisRenderer.nearestHigherMajorTick( maxYValue ) );
505        }
506
507        /**
508         *      Returns the maximum value of the y axis
509         *      @return The y axis maximum value
510         */
511        public double getMaxYValue()
512        {
513                return this.yAxisConfig.getMaxValue();
514        }
515
516        /**
517         *      @param minYValue the minYValue to set
518         */
519        public void setMinYValue( final double minYValue )
520        {
521                this.yAxisConfig.setMinValue( this.yAxisRenderer.nearestLowerMajorTick( minYValue ) );
522        }
523
524        /**
525         *      @param xMinorTickSpacing the xMinorTickSpacing to set
526         */
527        public void setxMinorTickSpacing( final double xMinorTickSpacing )
528        {
529                this.xAxisConfig.getRenderingConfig().setMinorTickSpacing( xMinorTickSpacing );
530        }
531
532        /**
533         *      @param yMinorTickSpacing the yMinorTickSpacing to set
534         */
535        public void setyMinorTickSpacing( final double yMinorTickSpacing )
536        {
537                this.yAxisConfig.getRenderingConfig().setMinorTickSpacing( yMinorTickSpacing );
538        }
539
540        /**
541         *      @param xMajorTickSpacing the xMajorTickSpacing to set
542         */
543        public void setxMajorTickSpacing( final double xMajorTickSpacing )
544        {
545                this.xAxisConfig.getRenderingConfig().setMajorTickSpacing( xMajorTickSpacing );
546        }
547
548        /**
549         *      @param yMajorTickSpacing the yMajorTickSpacing to set
550         */
551        public void setyMajorTickSpacing( final double yMajorTickSpacing )
552        {
553                this.yAxisConfig.getRenderingConfig().setMajorTickSpacing( yMajorTickSpacing );
554        }
555
556        /**
557         *      @param minorTickLength the minorTickLength to set
558         */
559        public void setMinorTickLength( final int minorTickLength )
560        {
561                this.yAxisConfig.getRenderingConfig().setMinorTickLength( minorTickLength );
562                this.xAxisConfig.getRenderingConfig().setMinorTickLength( minorTickLength );
563        }
564
565        /**
566         *      @param majorTickLength the majorTickLength to set
567         */
568        public void setMajorTickLength( final int majorTickLength )
569        {
570                this.yAxisConfig.getRenderingConfig().setMajorTickLength( majorTickLength );
571                this.xAxisConfig.getRenderingConfig().setMajorTickLength( majorTickLength );
572        }
573
574        /**
575         *      @param minorTickColour the minorTickColour to set
576         */
577        public void setMinorTickColour( final Q minorTickColour )
578        {
579                this.yAxisConfig.getRenderingConfig().setMinorTickColour( minorTickColour );
580                this.xAxisConfig.getRenderingConfig().setMinorTickColour( minorTickColour );
581        }
582
583        /**
584         *      @param majorTickColour the majorTickColour to set
585         */
586        public void setMajorTickColour( final Q majorTickColour )
587        {
588                this.yAxisConfig.getRenderingConfig().setMajorTickColour( majorTickColour );
589                this.xAxisConfig.getRenderingConfig().setMajorTickColour( majorTickColour );
590        }
591
592        /**
593         *      @param majorTickThickness the majorTickThickness to set
594         */
595        public void setMajorTickThickness( final int majorTickThickness )
596        {
597                this.yAxisConfig.getRenderingConfig().setMajorTickThickness( majorTickThickness );
598                this.xAxisConfig.getRenderingConfig().setMajorTickThickness( majorTickThickness );
599        }
600
601        /**
602         *      @param minorTickThickness the minorTickThickenss to set
603         */
604        public void setMinorTickThickenss( final int minorTickThickness )
605        {
606                this.yAxisConfig.getRenderingConfig().setMinorTickThickness( minorTickThickness );
607                this.xAxisConfig.getRenderingConfig().setMinorTickThickness( minorTickThickness );
608        }
609
610        /**
611         *      @param drawXTickLabels the drawXTickLabels to set
612         */
613        public void setDrawXTickLabels( final boolean drawXTickLabels )
614        {
615                this.xAxisConfig.getRenderingConfig().setDrawMajorTickLabels( drawXTickLabels );
616                this.xAxisConfig.getRenderingConfig().setDrawMinorTickLabels( drawXTickLabels );
617        }
618
619        /**
620         *      @param drawYTickLabels the drawYTickLabels to set
621         */
622        public void setDrawYTickLabels( final boolean drawYTickLabels )
623        {
624                this.yAxisConfig.getRenderingConfig().setDrawMajorTickLabels( drawYTickLabels );
625                this.yAxisConfig.getRenderingConfig().setDrawMinorTickLabels( drawYTickLabels );
626        }
627
628        /**
629         *      @param xTickLabelSize the xTickLabelSize to set
630         */
631        public void setxTickLabelSize( final int xTickLabelSize )
632        {
633//              this.xTickLabelSize = xTickLabelSize;
634                // TODO:
635        }
636
637        /**
638         *      @param xTickLabelColour the xTickLabelColour to set
639         */
640        public void setxTickLabelColour( final Q xTickLabelColour )
641        {
642//              this.xTickLabelColour = xTickLabelColour;
643                // TODO:
644        }
645
646        /**
647         *      @param yTickLabelSize the yTickLabelSize to set
648         */
649        public void setyTickLabelSize( final int yTickLabelSize )
650        {
651//              yAxisConfig.getRenderingConfig().setMajorTickLabelFont( majorTickLabelFont );
652                // TODO:
653        }
654
655        /**
656         *      @param yTickLabelColour the yTickLabelColour to set
657         */
658        public void setyTickLabelColour( final Q yTickLabelColour )
659        {
660//              this.yTickLabelColour = yTickLabelColour;
661//              yAxisConfig.getRenderingConfig().getMajor
662                // TODO:
663        }
664
665        /**
666         *      @param autoSpaceLabels the autoSpaceLabels to set
667         */
668        public void setAutoSpaceLabels( final boolean autoSpaceLabels )
669        {
670//              this.autoSpaceLabels = autoSpaceLabels;
671                // TODO:
672        }
673
674        /**
675         *      @param autoSpaceTicks the autoSpaceTicks to set
676         */
677        public void setAutoSpaceTicks( final boolean autoSpaceTicks )
678        {
679//              this.autoSpaceTicks = autoSpaceTicks;
680                // TODO:
681        }
682
683        /**
684         *      @param minTickSpacing the minTickSpacing to set
685         */
686        public void setMinTickSpacing( final int minTickSpacing )
687        {
688                // TODO:
689        }
690
691        /**
692         *      @param xLabelSpacing the xLabelSpacing to set
693         */
694        public void setxLabelSpacing( final double xLabelSpacing )
695        {
696//              xAxisConfig.getRenderingConfig().setMajor
697                // TODO:
698        }
699
700        /**
701         *      @param yLabelSpacing the yLabelSpacing to set
702         */
703        public void setyLabelSpacing( final double yLabelSpacing )
704        {
705//              this.yLabelSpacing = yLabelSpacing;
706                // TODO:
707        }
708
709        /**
710         *      @param xAxisName the xAxisName to set
711         */
712        public void setxAxisName( final String xAxisName )
713        {
714                this.xAxisConfig.setName( xAxisName );
715        }
716
717        /**
718         *      @param yAxisName the yAxisName to set
719         */
720        public void setyAxisName( final String yAxisName )
721        {
722                this.yAxisConfig.setName( yAxisName );
723        }
724
725        /**
726         *      @param drawYAxisName the drawYAxisName to set
727         */
728        public void setDrawYAxisName( final boolean drawYAxisName )
729        {
730//              this.drawYAxisName = drawYAxisName;
731                // TODO:
732        }
733
734        /**
735         *      @param drawXAxisName the drawXAxisName to set
736         */
737        public void setDrawXAxisName( final boolean drawXAxisName )
738        {
739                // xAxisConfig.getRenderingConfig()
740                // TODO:
741        }
742
743        /**
744         *      @param xAxisNameSize the xAxisNameSize to set
745         */
746        public void setxAxisNameSize( final int xAxisNameSize )
747        {
748                this.xAxisConfig.getRenderingConfig().setNameSize( xAxisNameSize );
749        }
750
751        /**
752         *      @param xAxisNameColour the xAxisNameColour to set
753         */
754        public void setxAxisNameColour( final Q xAxisNameColour )
755        {
756                this.xAxisConfig.getRenderingConfig().setNameColour( xAxisNameColour );
757        }
758
759        /**
760         *      @param yAxisNameSize the yAxisNameSize to set
761         */
762        public void setyAxisNameSize( final int yAxisNameSize )
763        {
764                this.yAxisConfig.getRenderingConfig().setNameSize( yAxisNameSize );
765        }
766
767        /**
768         *      @param yAxisNameColour the yAxisNameColour to set
769         */
770        public void setyAxisNameColour( final Q yAxisNameColour )
771        {
772                this.yAxisConfig.getRenderingConfig().setNameColour( yAxisNameColour );
773        }
774
775        /**
776         *      @param drawXTicks the drawXTicks to set
777         */
778        public void setDrawXTicks( final boolean drawXTicks )
779        {
780                this.xAxisConfig.getRenderingConfig().setDrawMajorTicks( drawXTicks );
781                this.xAxisConfig.getRenderingConfig().setDrawMinorTicks( drawXTicks );
782        }
783
784        /**
785         *      @param drawYTicks the drawYTicks to set
786         */
787        public void setDrawYTicks( final boolean drawYTicks )
788        {
789                this.yAxisConfig.getRenderingConfig().setDrawMajorTicks( drawYTicks );
790                this.yAxisConfig.getRenderingConfig().setDrawMinorTicks( drawYTicks );
791        }
792
793        /**
794         *      @param xAxisLabelTransformer the xAxisLabelTransformer to set
795         */
796        public void setxAxisLabelTransformer( final LabelTransformer xAxisLabelTransformer )
797        {
798                this.xAxisConfig.getRenderingConfig().setLabelTransformer( xAxisLabelTransformer );
799        }
800
801        /**
802         *      @param yAxisLabelTransformer the yAxisLabelTransformer to set
803         */
804        public void setyAxisLabelTransformer( final LabelTransformer yAxisLabelTransformer )
805        {
806                this.yAxisConfig.getRenderingConfig().setLabelTransformer( yAxisLabelTransformer );
807        }
808
809        /**
810         *      @param drawMinorTickGrid the drawMinorTickGrid to set
811         */
812        public void setDrawMinorTickGrid( final boolean drawMinorTickGrid )
813        {
814                this.xAxisConfig.getRenderingConfig().setDrawMinorGrid( drawMinorTickGrid );
815                this.yAxisConfig.getRenderingConfig().setDrawMinorGrid( drawMinorTickGrid );
816        }
817
818        /**
819         *      @param drawMajorTickGrid the drawMajorTickGrid to set
820         */
821        public void setDrawMajorTickGrid( final boolean drawMajorTickGrid )
822        {
823                this.xAxisConfig.getRenderingConfig().setDrawMajorGrid( drawMajorTickGrid );
824                this.yAxisConfig.getRenderingConfig().setDrawMajorGrid( drawMajorTickGrid );
825        }
826
827        /**
828         *      @param minorGridColour the minorGridColour to set
829         */
830        public void setMinorGridColour( final Q minorGridColour )
831        {
832                this.xAxisConfig.getRenderingConfig().setMinorGridColour( minorGridColour );
833                this.yAxisConfig.getRenderingConfig().setMinorGridColour( minorGridColour );
834        }
835
836        /**
837         *      @param majorGridColour the majorGridColour to set
838         */
839        public void setMajorGridColour( final Q majorGridColour )
840        {
841                this.xAxisConfig.getRenderingConfig().setMajorGridColour( majorGridColour );
842                this.yAxisConfig.getRenderingConfig().setMajorGridColour( majorGridColour );
843        }
844
845        /**
846         *      @param majorGridThickness the majorGridThickness to set
847         */
848        public void setMajorGridThickness( final int majorGridThickness )
849        {
850                this.xAxisConfig.getRenderingConfig().setMajorGridThickness( majorGridThickness );
851                this.yAxisConfig.getRenderingConfig().setMajorGridThickness( majorGridThickness );
852        }
853
854        /**
855         *      @param minorGridThickness the minorGridThickness to set
856         */
857        public void setMinorGridThickness( final int minorGridThickness )
858        {
859                this.xAxisConfig.getRenderingConfig().setMinorGridThickness( minorGridThickness );
860                this.yAxisConfig.getRenderingConfig().setMinorGridThickness( minorGridThickness );
861        }
862
863        /**
864         *      Returns a data pixel transformer that is based on this axes renderer but has its values
865         *      offset by the given number of pixels.
866         *
867         *      // TODO: This method doesn't work at the moment
868         *
869         *      @param xOffset The x location
870         *      @param yOffset The y location
871         *      @return A new data pixel transformer
872         */
873        public DataUnitsTransformer<Q,double[],int[]> getRelativePixelTransformer(
874                        final int xOffset, final int yOffset )
875        {
876                // TODO: This needs completing
877                return new DataUnitsTransformer<Q,double[],int[]>()
878                {
879                        @Override
880                        public void precalc()
881                        {
882                                // TODO Auto-generated method stub
883                        }
884
885                        @Override
886                        public int[] calculatePosition( final double[] units )
887                        {
888                                // TODO Auto-generated method stub
889                                return null;
890                        }
891
892                        @Override
893                        public double[] calculateUnitsAt( final int[] position )
894                        {
895                                // TODO Auto-generated method stub
896                                return null;
897                        }
898
899                        @Override
900                        public int[] scaleDimension( final double[] dimension )
901                        {
902                                // TODO Auto-generated method stub
903                                return null;
904                        }
905
906//                      @Override
907//                      public void precalc( final double[] dim )
908//                      {
909//                              AxesRenderer2D.this.precalc( dim );
910//                      }
911//
912//                      @Override
913//                      public Point2d calculatePosition( final double[] dim, final double x, final double y )
914//                      {
915//                              // Calculate the position of the data
916//                              final Point2d p = AxesRenderer2D.this.calculatePosition( dim, x, y );
917//
918//                              // Then translate it back by the offset
919//                              p.translate( -xOffset, -yOffset );
920//                              return p;
921//                      }
922//
923//                      @Override
924//                      public double[] calculateUnitsAt( final double[] dim, int x, int y )
925//                      {
926//                              // Translate the pixel to the original coordinates
927//                              x += xOffset;
928//                              y += yOffset;
929//                              // Now calculate the value
930//                              return AxesRenderer2D.this.calculateUnitsAt( dim, x, y );
931//                      }
932                };
933        }
934
935        /**
936         *      @return the xAxisConfig
937         */
938        public AxisConfig<Q> getxAxisConfig()
939        {
940                return this.xAxisConfig;
941        }
942
943        /**
944         *      @param xAxisConfig the xAxisConfig to set
945         */
946        public void setxAxisConfig( final AxisConfig<Q> xAxisConfig )
947        {
948                this.xAxisConfig = xAxisConfig;
949        }
950
951        /**
952         *      @return the yAxisConfig
953         */
954        public AxisConfig<Q> getyAxisConfig()
955        {
956                return this.yAxisConfig;
957        }
958
959        /**
960         *      @param yAxisConfig the yAxisConfig to set
961         */
962        public void setyAxisConfig( final AxisConfig<Q> yAxisConfig )
963        {
964                this.yAxisConfig = yAxisConfig;
965        }
966
967        /**
968         *      {@inheritDoc}
969         *      @see org.openimaj.vis.DataUnitsTransformer#calculatePosition(java.lang.Object)
970         */
971        @Override
972        public int[] calculatePosition( final double[] units )
973        {
974                Point2dImpl p = new Point2dImpl( (float)units[0], (float)units[1] );
975                p = p.transform( this.dataTransformMatrix );
976                return new int[] { Math.round(p.getX()), Math.round(p.getY()) };
977
978//              final double[] dx = this.xAxisRenderer.calculatePosition( units[0] );
979//              final double[] dy = this.yAxisRenderer.calculatePosition( units[1] );
980//
981//              System.out.println( "units[0] = "+units[0]+" --> "+Arrays.toString( dx ) );
982//              System.out.println( "units[1] = "+units[1]+" --> "+Arrays.toString( dy ) );
983//
984//              return new int[] { (int) dx[0], (int)dy[1] };
985//                              //(int)(dx[0]+dy[0]), (int)(dx[1]+dy[1]) };
986        }
987
988        /**
989         *      {@inheritDoc}
990         *      @see org.openimaj.vis.DataUnitsTransformer#calculateUnitsAt(java.lang.Object)
991         */
992        @Override
993        public double[] calculateUnitsAt( final int[] position )
994        {
995                return new double[] {
996                                this.xAxisRenderer.calculateUnitsAt(
997                                                new double[] {position[0],position[1]} ),
998                                this.yAxisRenderer.calculateUnitsAt(
999                                                new double[] {position[0],position[1]} )
1000                };
1001        }
1002
1003        /**
1004         *      Given two dimensions, returns the dimensions scaled to the appropriate sizes.
1005         *      @param xs The x dimension
1006         *      @param ys The y dimension
1007         *      @return The scaled dimensions
1008         */
1009        public double[] scaleDimensions( final double xs, final double ys )
1010        {
1011                return new double[]{
1012                        xs * this.xAxisRenderer.getCurrentScale(),
1013                        ys * this.yAxisRenderer.getCurrentScale()
1014                };
1015        }
1016
1017        /**
1018         *      {@inheritDoc}
1019         *      @see org.openimaj.vis.DataUnitsTransformer#scaleDimension(java.lang.Object)
1020         */
1021        @Override
1022        public int[] scaleDimension( final double[] dimension )
1023        {
1024                return new int[]
1025                {
1026                        (int)(dimension[0] * this.xAxisRenderer.getCurrentScale()),
1027                        (int)(dimension[1] * this.yAxisRenderer.getCurrentScale())
1028                };
1029        }
1030
1031        /**
1032         *      Helper function to calulate the render position of a data unit
1033         *      @param x The data unit x
1034         *      @param y The data unit y
1035         *      @return The render position
1036         */
1037        public Point2d calculatePosition( final double x, final double y )
1038        {
1039                final int[] p = this.calculatePosition( new double[] {x, y} );
1040                return new Point2dImpl( p[0], p[1] );
1041        }
1042
1043        /**
1044         *      Config may be null as it is not used.
1045         *
1046         *      {@inheritDoc}
1047         *      @see org.openimaj.vis.DataUnitsTransformer#precalc()
1048         */
1049        @Override
1050        synchronized public void precalc()
1051        {
1052                // Set the position of the x axis
1053                final double[] xLoc = this.xAxisConfig.getLocation();
1054                xLoc[0] = this.axisPaddingLeft;
1055                xLoc[1] = this.getxAxisPosition();
1056                this.xAxisRenderer.precalc();
1057
1058                // Calculate a data transform matrix for working out the position of data
1059                this.dataTransformMatrix = Matrix.identity( 3, 3 );
1060                this.dataTransformMatrix = TransformUtilities.translateMatrix(
1061                                -this.xAxisConfig.getMinValue(), 0 ).times( this.dataTransformMatrix );
1062                this.dataTransformMatrix = TransformUtilities.scaleMatrix(
1063                                this.xAxisRenderer.getCurrentScale(), -this.yAxisRenderer.getCurrentScale() )
1064                                .times( this.dataTransformMatrix );
1065                this.dataTransformMatrix = TransformUtilities.rotationMatrix(
1066                                this.xAxisConfig.getOrientation()[0] ).times( this.dataTransformMatrix );
1067                this.dataTransformMatrix = TransformUtilities.translateMatrix( xLoc[0], xLoc[1] )
1068                                .times( this.dataTransformMatrix );
1069
1070                // Re-seat the y axis
1071                final double[] yLoc = this.yAxisConfig.getLocation();
1072                final Point2d dd = this.calculatePosition( 0, this.yAxisConfig.getMinValue() );
1073                yLoc[0] = dd.getX();
1074                yLoc[1] = dd.getY();
1075                this.yAxisRenderer.precalc();
1076        }
1077
1078        /**
1079         *      @param image the image to set
1080         */
1081        public void setImage( final I image )
1082        {
1083                synchronized( this )
1084                {
1085                        this.xAxisRenderer.setImage( image );
1086                        this.yAxisRenderer.setImage( image );
1087
1088                        this.xAxisRenderer.setAxisLength(
1089                                image.getWidth() - this.axisPaddingLeft - this.axisPaddingRight );
1090                        this.yAxisRenderer.setAxisLength(
1091                                image.getHeight() - this.axisPaddingBottom - this.axisPaddingTop );
1092                }
1093        }
1094
1095        /**
1096         *      @return the xAxisRenderer
1097         */
1098        public AxisRenderer2D<Q> getxAxisRenderer()
1099        {
1100                return this.xAxisRenderer;
1101        }
1102
1103        /**
1104         *      @return the yAxisRenderer
1105         */
1106        public AxisRenderer2D<Q> getyAxisRenderer()
1107        {
1108                return this.yAxisRenderer;
1109        }
1110
1111        /**
1112         *      @param args
1113         */
1114        public static void main( final String[] args )
1115        {
1116                final MBFImage visImage = new MBFImage( 1000, 600, 3 );
1117                final AxesRenderer2D<Float[],MBFImage> ar = new AxesRenderer2D<Float[],MBFImage>();
1118
1119                ar.setxAxisColour( RGBColour.WHITE );
1120                ar.setyAxisColour( RGBColour.WHITE );
1121                ar.setMajorTickColour( RGBColour.WHITE );
1122                ar.setMinorTickColour( RGBColour.GRAY );
1123                ar.setxTickLabelColour( RGBColour.GRAY );
1124                ar.setyTickLabelColour( RGBColour.GRAY );
1125                ar.setxAxisNameColour( RGBColour.WHITE );
1126                ar.setyAxisNameColour( RGBColour.WHITE );
1127
1128                ar.setxAxisPosition( 100 );
1129                ar.setMinXValue( -1 );
1130                ar.setMaxXValue( 1 );
1131                ar.setMinYValue( -5 );
1132                ar.setMaxYValue( 1 );
1133                ar.setDrawYAxis( true );
1134                ar.setDrawXAxis( true );
1135                ar.setDrawXAxisName( true );
1136                ar.setDrawYAxisName( true );
1137                ar.setDrawXTickLabels( true );
1138                ar.setMinorTickLength( 5 );
1139                ar.setMajorTickLength( 7 );
1140                ar.setMajorTickThickness( 3 );
1141                ar.setMinorTickThickenss( 1 );
1142                ar.setxMinorTickSpacing( 0.2 );
1143                ar.setxMajorTickSpacing( 1 );
1144                ar.setxLabelSpacing( 0.25 );
1145//              ar.setxTickLabelFont( new GeneralFont( "Arial", java.awt.Font.PLAIN ) );
1146                ar.setxTickLabelSize( 14 );
1147                ar.setyMinorTickSpacing( 0.1 );
1148                ar.setyMajorTickSpacing( 1 );
1149                ar.setyLabelSpacing( 0.5 );
1150//              ar.setyTickLabelFont = new GeneralFont( "Arial", java.awt.Font.PLAIN );
1151                ar.setyTickLabelSize( 14 );
1152                ar.setxAxisName( "Stuff" );
1153//              ar.setxAxisNameFont = new GeneralFont( "Times New Roman", java.awt.Font.PLAIN );
1154                ar.setxAxisNameSize( 25 );
1155                ar.setyAxisName( "Things" );
1156//              ar.setyAxisNameFont = new GeneralFont( "Times New Roman", java.awt.Font.PLAIN );
1157                ar.setyAxisNameSize( 25 );
1158
1159                ar.precalc();
1160                ar.renderAxis( visImage );
1161                DisplayUtilities.display( visImage );
1162        }
1163}