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.DisplayUtilities;
036import org.openimaj.image.Image;
037import org.openimaj.image.MBFImage;
038import org.openimaj.image.colour.RGBColour;
039import org.openimaj.math.geometry.line.Line2d;
040import org.openimaj.math.geometry.point.Point2d;
041import org.openimaj.math.geometry.point.Point2dImpl;
042import org.openimaj.math.geometry.transforms.TransformUtilities;
043import org.openimaj.vis.DataUnitsTransformer;
044
045import Jama.Matrix;
046
047
048/**
049 *
050 *
051 *      @author David Dupplaw (dpd@ecs.soton.ac.uk)
052 *      @param <Q>
053 *  @created 9 Jul 2013
054 */
055public class AxisRenderer2D<Q> extends AxisRenderer<Q>
056        implements DataUnitsTransformer<Q,Double,double[]>
057{
058        /** The image we're drawing to */
059        private Image<Q,?> image;
060
061        /** Desired size of the axis in pixels */
062        protected double axisLength = 100;
063
064        /** The data point transform we've calculated */
065        private Matrix transform;
066
067        /** The axis line we've calculated */
068        private Line2d axisLine;
069
070        /** Units to pixels scaling */
071        private double currentScale = 1;
072
073        /**
074         *      Default constructor
075         */
076        public AxisRenderer2D()
077        {
078        }
079
080        /**
081         *      Constructor to set the config
082         *      @param conf The config
083         */
084        public AxisRenderer2D( final AxisConfig<Q> conf )
085        {
086                this.config = conf;
087        }
088
089        @Override
090        public double[] scaleDimension( final Double dimension )
091        {
092                return new double[] { dimension * this.getCurrentScale() };
093        }
094
095        @Override
096        public void precalc()
097        {
098                // Create an axis line between the min and max value
099                this.axisLine = new Line2d( (float)this.config.getMinValue(), 0,
100                                (float)this.config.getMaxValue(), 0 );
101
102                // Transform to (0,0) in data units
103                this.transform = TransformUtilities.translateMatrix( -this.config.getMinValue(), 0 );
104
105                // Scale to pixels
106                this.currentScale = this.axisLength / (this.config.getMaxValue() - this.config.getMinValue());
107                this.transform = TransformUtilities.scaleMatrix( (float)this.currentScale, 1 )
108                                .times(this.transform);
109
110                // Rotate to the preferred orientation
111                this.transform = TransformUtilities.rotationMatrix( this.config.getOrientation()[0] )
112                                .times(this.transform);
113
114                // Translate to the preferred position
115                this.transform = TransformUtilities.translateMatrix(
116                                this.config.getLocation()[0], this.config.getLocation()[1] )
117                                .times( this.transform );
118
119                // Transform the axis
120                this.axisLine = this.axisLine.transform( this.transform );
121        }
122
123        /**
124         *      {@inheritDoc}
125         *      @see org.openimaj.vis.general.AxisRenderer#drawAxis(org.openimaj.vis.general.AxisConfig)
126         */
127        @Override
128        public void drawAxis( final AxisConfig<Q> config )
129        {
130                if( this.axisLine == null ) this.precalc( );
131
132                this.image.drawLine( this.axisLine,
133                                (int)config.getRenderingConfig().getThickness(),
134                                config.getRenderingConfig().getColour() );
135        }
136
137        /**
138         *      {@inheritDoc}
139         *      @see org.openimaj.vis.general.AxisRenderer#drawAxisLabel(org.openimaj.vis.general.AxisConfig)
140         */
141        @Override
142        public void drawAxisLabel( final AxisConfig<Q> config )
143        {
144        }
145
146        /**
147         *      {@inheritDoc}
148         *      @see org.openimaj.vis.general.AxisRenderer#drawMajorTick(double, org.openimaj.vis.general.AxisConfig)
149         */
150        @Override
151        public void drawMajorTick( final double location, final AxisConfig<Q> config )
152        {
153                if( this.transform == null ) this.precalc( );
154
155                final float x = (float)location;
156                final float y = (float)config.getRenderingConfig().getMajorTickLength();
157                Line2d l = new Line2d( x, -y, x, y );
158                l = l.transform( this.transform );
159                this.image.drawLine( l, (int)config.getRenderingConfig().getMajorTickThickness(),
160                                config.getRenderingConfig().getMajorTickColour() );
161        }
162
163        /**
164         *      {@inheritDoc}
165         *      @see org.openimaj.vis.general.AxisRenderer#drawMajorTickGridline(double, org.openimaj.vis.general.AxisConfig)
166         */
167        @Override
168        public void drawMajorTickGridline( final double location, final AxisConfig<Q> config )
169        {
170                if( this.transform == null ) this.precalc( );
171
172                final float x = (float)location;
173                final float y = this.image.getHeight()*2;
174                Line2d l = new Line2d( x, -y, x, y );
175                l = l.transform( this.transform );
176                this.image.drawLine( l, (int)config.getRenderingConfig().getMajorGridThickness(),
177                                config.getRenderingConfig().getMajorGridColour() );
178        }
179
180        /**
181         *      {@inheritDoc}
182         *      @see org.openimaj.vis.general.AxisRenderer#drawMinorTick(double, org.openimaj.vis.general.AxisConfig)
183         */
184        @Override
185        public void drawMinorTick( final double location, final AxisConfig<Q> config )
186        {
187                if( this.transform == null ) this.precalc( );
188
189                final float x = (float)location;
190                final float y = (float)config.getRenderingConfig().getMinorTickLength();
191                Line2d l = new Line2d( x, -y, x, y );
192                l = l.transform( this.transform );
193                this.image.drawLine( l, (int)config.getRenderingConfig().getMinorTickThickness(),
194                                config.getRenderingConfig().getMinorTickColour() );
195        }
196
197        /**
198         *      {@inheritDoc}
199         *      @see org.openimaj.vis.general.AxisRenderer#drawMinorTickGridline(double, org.openimaj.vis.general.AxisConfig)
200         */
201        @Override
202        public void drawMinorTickGridline( final double location, final AxisConfig<Q> config )
203        {
204                if( this.transform == null ) this.precalc( );
205
206                final float x = (float)location;
207                final float y = this.image.getHeight()*2;
208                Line2d l = new Line2d( x, -y, x, y );
209                l = l.transform( this.transform );
210                this.image.drawLine( l, (int)config.getRenderingConfig().getMinorGridThickness(),
211                                config.getRenderingConfig().getMinorGridColour() );
212        }
213
214        /**
215         *      @return the axisLength
216         */
217        public double getAxisLength()
218        {
219                return this.axisLength;
220        }
221
222        /**
223         *      @param axisLength the axisLength to set
224         */
225        public void setAxisLength( final double axisLength )
226        {
227                this.axisLength = axisLength;
228                this.precalc();
229        }
230
231        /**
232         *      {@inheritDoc}
233         *      @see org.openimaj.vis.DataUnitsTransformer#calculatePosition(java.lang.Object)
234         */
235        @Override
236        public double[] calculatePosition( final Double units )
237        {
238                if( this.transform == null ) this.precalc();
239
240                final Point2d p = new Point2dImpl( units.floatValue(), 0f );
241                final Point2d p2 = p.transform( this.transform );
242                return new double[] {p2.getX(), p2.getY()};
243        }
244
245        /**
246         *      {@inheritDoc}
247         *      @see org.openimaj.vis.DataUnitsTransformer#calculateUnitsAt(java.lang.Object)
248         */
249        @Override
250        public Double calculateUnitsAt( final double[] position )
251        {
252                if( this.transform == null ) this.precalc( );
253
254                final Point2d p = new Point2dImpl( (float)position[0], (float)position[1] );
255                final Point2d p2 = p.transform( this.transform.inverse() );
256                return new Double(p2.getX());
257        }
258
259        /**
260         *      @return the image
261         */
262        public Image<Q, ?> getImage()
263        {
264                return this.image;
265        }
266
267        /**
268         *      @param image the image to set
269         */
270        public void setImage( final Image<Q, ?> image )
271        {
272                this.image = image;
273        }
274
275        /**
276         *      @return the currentScale
277         */
278        public double getCurrentScale()
279        {
280                return this.currentScale;
281        }
282
283        /**
284         *      @return the config
285         */
286        @Override
287        public AxisConfig<Q> getConfig()
288        {
289                return this.config;
290        }
291
292        /**
293         *      @param config the config to set
294         */
295        @Override
296        public void setConfig( final AxisConfig<Q> config )
297        {
298                this.config = config;
299        }
300
301        /**
302         *      Simple test
303         *      @param args command-line args (not used)
304         */
305        public static void main( final String[] args )
306        {
307                // Create the image to draw into
308                final MBFImage img = new MBFImage( 400, 400, 3 );
309
310                // Create the configuration for our axis
311                final AxisConfig<Float[]> conf = new AxisConfig<Float[]>();
312                conf.setLocation( new double[]{ 20, 200 } );
313                conf.setOrientation( new double[] {0/(360/(2*Math.PI))} );
314                conf.setMaxValue( 10 );
315                conf.setMinValue( 5 );
316                conf.getRenderingConfig().setMajorTickSpacing( 1 );
317                conf.getRenderingConfig().setMinorTickSpacing( 0.5d );
318                conf.getRenderingConfig().setColour( RGBColour.WHITE );
319                conf.getRenderingConfig().setMajorTickColour( RGBColour.WHITE );
320                conf.getRenderingConfig().setMinorTickColour( RGBColour.WHITE );
321                conf.getRenderingConfig().setMinorGridColour( RGBColour.GRAY  );
322                conf.getRenderingConfig().setMajorGridColour( RGBColour.GRAY );
323                conf.getRenderingConfig().setDrawMajorGrid( true );
324                conf.getRenderingConfig().setDrawMinorGrid( true );
325
326                // Create the axis renderer for the image
327                final AxisRenderer2D<Float[]> r = new AxisRenderer2D<Float[]>( conf );
328                r.setAxisLength( 360 );
329                r.setImage( img );
330
331                r.renderAxis();
332
333                DisplayUtilities.display( img );
334        }
335}