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 java.nio.DoubleBuffer;
036import java.nio.IntBuffer;
037
038import javax.media.opengl.GL;
039import javax.media.opengl.GL2;
040import javax.media.opengl.GLAutoDrawable;
041import javax.media.opengl.fixedfunc.GLMatrixFunc;
042
043import org.openimaj.image.colour.ColourMap;
044import org.openimaj.image.colour.RGBColour;
045import org.openimaj.util.array.ArrayUtils;
046import org.openimaj.vis.Visualisation3D;
047
048/**
049 * Plots oneOverDataLength bars in oneOverDataLength 3-dimensional space, which means there are 2 dimensions for
050 * representing the coordinate of oneOverDataLength bar.
051 *
052 * @author David Dupplaw (dpd@ecs.soton.ac.uk)
053 * @created 4 Jul 2013
054 * @version $Author$, $Revision$, $Date$
055 */
056public class BarVisualisation3D extends Visualisation3D<double[][]>
057{
058        private AxesRenderer3D axesRenderer;
059
060        /** The colour map for the bars */
061        private ColourMap colourMap = ColourMap.Autumn;
062
063        /** The name of the x axis */
064        private String xAxisName = "X-Axis";
065
066        /** The name of the y axis */
067        private String yAxisName = "Y-Axis";
068
069        /** The name of the z axis */
070        private String zAxisName = "Z-Axis";
071
072        /** The colour of the x axis */
073        private Float[] xAxisColour = RGBColour.WHITE;
074
075        /** The colour of the y axis */
076        private Float[] yAxisColour = RGBColour.GREEN;
077
078        /** The colour of the z axis */
079        private Float[] zAxisColour = RGBColour.BLUE;
080
081        /** The maximum value of the data (for auto scaling) */
082        private double max = 1;
083
084        /** Whether to automatically calculate the maximum value and scale */
085        private boolean autoScale = true;
086
087        /** Precalculated 1/data.length */
088        private double oneOverDataLength;
089
090        /** Precalculated 1/max */
091        private double oneOverMax;
092
093        /**
094         *      @param width
095         *      @param height
096         */
097        public BarVisualisation3D( final int width, final int height )
098        {
099                super( width, height );
100        }
101
102        /**
103         * {@inheritDoc}
104         *
105         * @see org.openimaj.vis.VisualisationImageProvider#updateVis()
106         */
107        @Override
108        public void updateVis()
109        {
110        }
111
112        /**
113         * Renders the visualisation
114         */
115        @Override
116        protected void renderVis( final GLAutoDrawable drawable )
117        {
118                if( drawable == null || this.axesRenderer == null ) return;
119
120                final GL2 gl = drawable.getGL().getGL2();
121                gl.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
122
123                this.axesRenderer.renderAxis( drawable );
124
125                // Create the boxes
126                if( this.data != null )
127                {
128//                              synchronized( this.data )
129                        {
130                                for( int z = 0; z < this.data.length; z++ )
131                                {
132                                        final double b = 1d / this.data[z].length;
133                                        for( int x = 0; x < this.data[z].length; x++ )
134                                        {
135                                                final double v = this.oneOverMax * this.data[z][x];
136                                                gl.glPushMatrix();
137                                                {
138                                                        final float[] colour = new float[3];
139                                                        this.colourMap.apply( (float) (this.data[z][x] / this.max), colour );
140                                                        gl.glColor3f( colour[0], colour[1], colour[2] );
141                                                        gl.glTranslatef(
142                                                                        (float) (b * x + b / 2d),
143                                                                        (float) (v / 2d),
144                                                                        (float)(this.oneOverDataLength * z + this.oneOverDataLength / 2d)-1f );
145                                                        gl.glScalef( (float) b, (float)Math.abs(v), (float) this.oneOverDataLength );
146                                                        gl.glEnable( GL.GL_POLYGON_OFFSET_FILL );
147                                                        gl.glPolygonOffset( 1, 1 );
148                                                        this.glut.glutSolidCube( 1f );
149                                                        gl.glDisable( GL.GL_POLYGON_OFFSET_FILL );
150                                                        gl.glColor3f( 0, 0, 0 );
151                                                        this.glut.glutWireCube( 1f );
152                                                }
153                                                gl.glPopMatrix();
154                                        }
155                                }
156                        }
157                }
158        }
159
160        protected DoubleBuffer get2dPoint( final GL2 gl, final double x, final double y, final double z )
161        {
162                final DoubleBuffer model = DoubleBuffer.allocate(16);
163                gl.glGetDoublev( GLMatrixFunc.GL_MODELVIEW_MATRIX, model );
164
165                final DoubleBuffer proj = DoubleBuffer.allocate(16);
166                gl.glGetDoublev( GLMatrixFunc.GL_PROJECTION_MATRIX, proj );
167
168                final IntBuffer view = IntBuffer.allocate(4);
169                gl.glGetIntegerv( GL.GL_VIEWPORT, view );
170
171                final DoubleBuffer winPos = DoubleBuffer.allocate(3);
172                final boolean b = this.glu.gluProject( x, y, z, model, proj, view, winPos );
173
174                if( !b ) System.out.println( "FAIL ");
175                return winPos;
176        }
177
178        /**
179         * {@inheritDoc}
180         *
181         * @see javax.media.opengl.GLEventListener#init(javax.media.opengl.GLAutoDrawable)
182         */
183        @Override
184        public void init( final GLAutoDrawable drawable )
185        {
186                super.init( drawable );
187
188                this.axesRenderer = new AxesRenderer3D();
189
190                // Set the initial look at
191                final float eyeX = 0.5f, eyeY = 1f, eyeZ = 2f;
192                final float lookAtX = 0.5f, lookAtY = 0, lookAtZ = -1f;
193                final float upX = 0, upY = 1, upZ = 0;
194                this.glu.gluLookAt( eyeX, eyeY, eyeZ, lookAtX, lookAtY, lookAtZ, upX, upY, upZ );
195
196                // Instantiate the camera mover
197                this.cameraPosition = new RotatingCameraProvider(
198                                eyeX, eyeY, eyeZ,
199                                lookAtX, lookAtY, lookAtZ,
200                                0.0004f, 0.0004f, 0f, 0.75f );
201        }
202
203        /**
204         * {@inheritDoc}
205         *
206         * @see org.openimaj.vis.Visualisation#setData(java.lang.Object)
207         */
208        @Override
209        public void setData( final double[][] data )
210        {
211
212                super.setData( data );
213
214                if( this.autoScale )
215                {
216                        this.max = 0;
217                        for( final double[] d : this.data )
218                                this.max = Math.max( this.max,
219                                                Math.max( Math.abs( ArrayUtils.maxValue( d ) ),
220                                                                Math.abs( ArrayUtils.minValue(d) ) ) );
221                }
222
223                this.oneOverDataLength = 1d / this.data.length;
224                this.oneOverMax = 1d / this.max;
225        }
226
227        /**
228         *      Set the maximum data value
229         *      @param max The maximum
230         */
231        public void setMaximum( final double max )
232        {
233                this.max = max;
234                this.oneOverMax = 1d / this.max;
235        }
236
237        /**
238         *      @return the colourMap
239         */
240        public ColourMap getColourMap()
241        {
242                return this.colourMap;
243        }
244
245        /**
246         *      @param colourMap the colourMap to set
247         */
248        public void setColourMap( final ColourMap colourMap )
249        {
250                this.colourMap = colourMap;
251        }
252
253        /**
254         *      @return the xAxisName
255         */
256        public String getxAxisName()
257        {
258                return this.xAxisName;
259        }
260
261        /**
262         *      @param xAxisName the xAxisName to set
263         */
264        public void setxAxisName( final String xAxisName )
265        {
266                this.xAxisName = xAxisName;
267        }
268
269        /**
270         *      @return the yAxisName
271         */
272        public String getyAxisName()
273        {
274                return this.yAxisName;
275        }
276
277        /**
278         *      @param yAxisName the yAxisName to set
279         */
280        public void setyAxisName( final String yAxisName )
281        {
282                this.yAxisName = yAxisName;
283        }
284
285        /**
286         *      @return the zAxisName
287         */
288        public String getzAxisName()
289        {
290                return this.zAxisName;
291        }
292
293        /**
294         *      @param zAxisName the zAxisName to set
295         */
296        public void setzAxisName( final String zAxisName )
297        {
298                this.zAxisName = zAxisName;
299        }
300
301        /**
302         *      @return the xAxisColour
303         */
304        public Float[] getxAxisColour()
305        {
306                return this.xAxisColour;
307        }
308
309        /**
310         *      @param xAxisColour the xAxisColour to set
311         */
312        public void setxAxisColour( final Float[] xAxisColour )
313        {
314                this.xAxisColour = xAxisColour;
315        }
316
317        /**
318         *      @return the yAxisColour
319         */
320        public Float[] getyAxisColour()
321        {
322                return this.yAxisColour;
323        }
324
325        /**
326         *      @param yAxisColour the yAxisColour to set
327         */
328        public void setyAxisColour( final Float[] yAxisColour )
329        {
330                this.yAxisColour = yAxisColour;
331        }
332
333        /**
334         *      @return the zAxisColour
335         */
336        public Float[] getzAxisColour()
337        {
338                return this.zAxisColour;
339        }
340
341        /**
342         *      @param zAxisColour the zAxisColour to set
343         */
344        public void setzAxisColour( final Float[] zAxisColour )
345        {
346                this.zAxisColour = zAxisColour;
347        }
348
349        /**
350         *      @return the cameraPosition
351         */
352        public CameraPositionProvider getCameraPosition()
353        {
354                return this.cameraPosition;
355        }
356
357        /**
358         *      @param cameraPosition the cameraPosition to set
359         */
360        public void setCameraPosition( final CameraPositionProvider cameraPosition )
361        {
362                this.cameraPosition = cameraPosition;
363        }
364
365        /**
366         *      @return the autoScale
367         */
368        public boolean isAutoScale()
369        {
370                return this.autoScale;
371        }
372
373        /**
374         *      @param autoScale the autoScale to set
375         */
376        public void setAutoScale( final boolean autoScale )
377        {
378                this.autoScale = autoScale;
379        }
380
381        /**
382         * @param args
383         */
384        public static void main( final String[] args )
385        {
386                final BarVisualisation3D bv = new BarVisualisation3D( 1000, 1000 );
387                bv.setData( new double[][]
388                {
389                        { 6, 7, 8, 9, 10 },
390                        { 5, 6, 7, 8, 9 },
391                        { 4, 5, 6, 7, 8 },
392                        { 3, 4, 5, 6, 7 },
393                        { 2, 3, 4, 5, 6 },
394                        { 1, 2, 3, 4, 5 },
395                        { 0, 1, 2, 3, 4 },
396                        {-1, 0, 1, 2, 3 },
397                        {-2, -1, 0, 1, 2},
398                        {-3, -2, -1, 0, 1},
399                        {-4, -3, -2, -1, 0},
400                        {-5, -4, -3, -2, -1}
401                });
402        }
403}