    001    //$HeadURL: svn+ssh://mschneider@svn.wald.intevation.org/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005       Department of Geography, University of Bonn
    006     and
    007       lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035    ----------------------------------------------------------------------------*/
    036
    037    package org.deegree.tools.app3d;
    038
    039    import java.awt.BorderLayout;
    040    import java.awt.Color;
    041    import java.awt.Dimension;
    042    import java.awt.GraphicsConfigTemplate;
    043    import java.awt.GridBagConstraints;
    044    import java.awt.GridBagLayout;
    045    import java.awt.Insets;
    046    import java.awt.event.ActionEvent;
    047    import java.awt.event.ActionListener;
    048    import java.awt.event.KeyEvent;
    049    import java.awt.event.KeyListener;
    050    import java.awt.event.WindowAdapter;
    051    import java.awt.event.WindowEvent;
    052    import java.io.File;
    053    import java.io.FileWriter;
    054    import java.io.IOException;
    055    import java.security.InvalidParameterException;
    056    import java.util.ArrayList;
    057    import java.util.Enumeration;
    058    import java.util.List;
    059    import java.util.prefs.Preferences;
    060
    061    import javax.media.j3d.Appearance;
    062    import javax.media.j3d.Background;
    063    import javax.media.j3d.BoundingBox;
    064    import javax.media.j3d.BoundingSphere;
    065    import javax.media.j3d.Bounds;
    066    import javax.media.j3d.BranchGroup;
    067    import javax.media.j3d.Canvas3D;
    068    import javax.media.j3d.ColoringAttributes;
    069    import javax.media.j3d.DirectionalLight;
    070    import javax.media.j3d.GraphicsConfigTemplate3D;
    071    import javax.media.j3d.Group;
    072    import javax.media.j3d.Light;
    073    import javax.media.j3d.Material;
    074    import javax.media.j3d.RenderingAttributes;
    075    import javax.media.j3d.Texture;
    076    import javax.media.j3d.Transform3D;
    077    import javax.media.j3d.TransformGroup;
    078    import javax.media.j3d.View;
    079    import javax.swing.BorderFactory;
    080    import javax.swing.JButton;
    081    import javax.swing.JDialog;
    082    import javax.swing.JFileChooser;
    083    import javax.swing.JFrame;
    084    import javax.swing.JLabel;
    085    import javax.swing.JOptionPane;
    086    import javax.swing.JPanel;
    087    import javax.swing.JProgressBar;
    088    import javax.swing.JRadioButton;
    089    import javax.swing.SwingConstants;
    090    import javax.swing.WindowConstants;
    091    import javax.swing.border.BevelBorder;
    092    import javax.swing.border.Border;
    093    import javax.swing.filechooser.FileFilter;
    094    import javax.vecmath.Color3f;
    095    import javax.vecmath.Point3d;
    096    import javax.vecmath.Vector3d;
    097    import javax.vecmath.Vector3f;
    098
    099    import org.deegree.framework.log.ILogger;
    100    import org.deegree.framework.log.LoggerFactory;
    101    import org.j3d.renderer.java3d.geom.Sphere;
    102    import org.w3c.dom.Node;
    103
    104    import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
    105    import com.sun.j3d.utils.geometry.ColorCube;
    106    import com.sun.j3d.utils.universe.SimpleUniverse;
    107
    108    /**
    109     * The <code>View3DFile</code> class can display shape and gml/citygml file in 3D.
    110     *
    111     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
    112     *
    113     * @author last edited by: $Author:$
    114     *
    115     * @version $Revision:$, $Date:$
    116     *
    117     */
    118
    119    public class View3DFile extends JFrame implements ActionListener, KeyListener {
    120        /**
    121         *
    122         */
    123        private static final long serialVersionUID = 7698388852544865855L;
    124
    125        private static ILogger LOG = LoggerFactory.getLogger( View3DFile.class );
    126
    127        private SimpleUniverse simpleUniverse;
    128
    129        private Canvas3D canvas;
    130
    131        private MouseRotate trackBall;
    132
    133        private BranchGroup scene;
    134
    135        private TransformGroup rotationGroup;
    136
    137        private Light firstLight, secondLight, thirdLight;
    138
    139        private Point3d centroid;
    140
    141        private Preferences prefs;
    142
    143        private final static String OPEN_KEY = "lastOpenLocation";
    144
    145        private final static String LAST_EXTENSION = "lastFileExtension";
    146
    147        private final static String WIN_TITLE = "Deegree 3D Object viewer: ";
    148
    149        private Background backGround;
    150
    151        private double zoomX = 1;
    152
    153        private double zoomY = 1;
    154
    155        private double zoomZ = 1;
    156
    157        private final Vector3d upVector = new Vector3d( 0, 1, 0 );
    158
    159        private List<CustomFileFilter> supportedOpenFilter = new ArrayList<CustomFileFilter>();
    160
    161        /**
    162         * A panel showing some key stroke helps
    163         */
    164        JPanel helpLister;
    165
    166        /**
    167         * Creates a frame with the menus and the canvas3d set and tries to load the file from given location.
    168         *
    169         * @param fileName
    170         *            to be loaded.
    171         */
    172        public View3DFile( String fileName ) {
    173            this( false );
    174            readFile( fileName );
    175        }
    176
    177        /**
    178         * Creates a new frame with the menus and the canvas3d set.
    179         *
    180         * @param testSphere
    181         *            true if a sphere should be displayed.
    182         */
    183        public View3DFile( boolean testSphere ) {
    184            super( WIN_TITLE );
    185            prefs = Preferences.userNodeForPackage( View3DFile.class );
    186            setupGUI();
    187
    188            // openFileChooser();
    189
    190            setupJava3D( testSphere );
    191            ArrayList<String> extensions = new ArrayList<String>();
    192
    193            extensions.add( "gml" );
    194            extensions.add( "xml" );
    195            supportedOpenFilter.add( new CustomFileFilter( extensions, "(*.gml, *.xml) GML or CityGML-Files" ) );
    196
    197            extensions.clear();
    198            extensions.add( "shp" );
    199            supportedOpenFilter.add( new CustomFileFilter( extensions, "(*.shp) Esri ShapeFiles" ) );
    200
    201            extensions.clear();
    202            extensions.add( "vrml" );
    203            extensions.add( "wrl" );
    204            supportedOpenFilter.add( new CustomFileFilter( extensions,
    205                                                           "(*.vrml, *.wrl) VRML97 - Virtual Reality Modelling Language" ) );
    206
    207            pack();
    208        }
    209
    210        /**
    211         * GUI stuff
    212         */
    213        private void setupGUI() {
    214            // add listener for closing the frame/application
    215            addWindowListener( new WindowAdapter() {
    216                @Override
    217                public void windowClosing( WindowEvent evt ) {
    218                    System.exit( 0 );
    219                }
    220            } );
    221            setLayout( new BorderLayout() );
    222            setMinimumSize( new Dimension( 600, 600 ) );
    223            setPreferredSize( new Dimension( 600, 600 ) );
    224            setVisible( true );
    225            // Adding the button panel
    226            JPanel totalPanel = new JPanel( new BorderLayout() );
    227            // JPanel buttonPanel = new JPanel( new FlowLayout() );
    228            // createButtons( buttonPanel );
    229            totalPanel.add( createButtons(), BorderLayout.NORTH );
    230            addKeyListener( this );
    231            helpLister = new JPanel( new GridBagLayout() );
    232            Border border = BorderFactory.createTitledBorder( BorderFactory.createBevelBorder( BevelBorder.LOWERED ),
    233                                                              "Instant help" );
    234            helpLister.setBorder( border );
    235            GridBagConstraints gb = new GridBagConstraints();
    236            gb.ipadx = 10;
    237            gb.gridx = 0;
    238            gb.gridy = 0;
    239            JLabel tmp = new JLabel( "x: move postive X-axis" );
    240            helpLister.add( tmp, gb );
    241
    242            gb.gridx++;
    243            tmp = new JLabel( "X: move negative X-axis" );
    244            helpLister.add( tmp, gb );
    245
    246            gb.gridx = 0;
    247            gb.gridy++;
    248            tmp = new JLabel( "y: move positve Y-axis" );
    249            helpLister.add( tmp, gb );
    250
    251            gb.gridx++;
    252            tmp = new JLabel( "Y: move negative Y-axis" );
    253            helpLister.add( tmp, gb );
    254
    255            gb.gridy++;
    256            gb.gridx = 0;
    257            tmp = new JLabel( "z: move positve Z-axis" );
    258            helpLister.add( tmp, gb );
    259            gb.gridx++;
    260
    261            tmp = new JLabel( "Z: move negative Z-axis" );
    262            helpLister.add( tmp, gb );
    263            helpLister.setVisible( false );
    264
    265            totalPanel.add( helpLister, BorderLayout.SOUTH );
    266            getContentPane().add( totalPanel, BorderLayout.SOUTH );
    267
    268        }
    269
    270        private JFileChooser createFileChooser( List<CustomFileFilter> fileFilter ) {
    271            // Setting up the fileChooser.
    272
    273            String lastLoc = prefs.get( OPEN_KEY, System.getProperty( "user.home" ) );
    274
    275            File lastFile = new File( lastLoc );
    276            if ( !lastFile.exists() ) {
    277                lastFile = new File( System.getProperty( "user.home" ) );
    278            }
    279            JFileChooser fileChooser = new JFileChooser( lastFile );
    280            fileChooser.setMultiSelectionEnabled( false );
    281            if ( fileFilter != null && fileFilter.size() > 0 ) {
    282                // the *.* file fileter is off
    283                fileChooser.setAcceptAllFileFilterUsed( false );
    284                String lastExtension = prefs.get( LAST_EXTENSION, "*" );
    285                FileFilter selected = fileFilter.get( 0 );
    286                for ( CustomFileFilter filter : fileFilter ) {
    287                    fileChooser.setFileFilter( filter );
    288                    if ( filter.accepts( lastExtension ) ) {
    289                        selected = filter;
    290                    }
    291                }
    292
    293                fileChooser.setFileFilter( selected );
    294            }
    295
    296            return fileChooser;
    297        }
    298
    299        private void setupJava3D( boolean testSphere ) {
    300            // setting up Java3D
    301            GraphicsConfigTemplate3D configTemplate = new GraphicsConfigTemplate3D();
    302            configTemplate.setSceneAntialiasing( GraphicsConfigTemplate.PREFERRED );
    303            configTemplate.setDoubleBuffer( GraphicsConfigTemplate.REQUIRED );
    304            canvas = new Canvas3D( SimpleUniverse.getPreferredConfiguration() );
    305            canvas.setDoubleBufferEnable( true );
    306            simpleUniverse = new SimpleUniverse( canvas );
    307            if ( canvas != null ) {
    308                getContentPane().add( canvas, BorderLayout.CENTER );
    309            }
    310
    311            View view = simpleUniverse.getViewer().getView();
    312
    313            // view parameters
    314            view.setSceneAntialiasingEnable( true );
    315            view.setUserHeadToVworldEnable( true );
    316
    317            centroid = new Point3d( 0.3, 0.3, 0.3 );
    318            scene = new BranchGroup();
    319            firstLight = createDirectionalLight( new Vector3f( 0, 0, 1 ) );
    320            secondLight = createDirectionalLight( new Vector3f( 0, -1, -1 ) );
    321            thirdLight = createDirectionalLight( new Vector3f( -1, 0, 0 ) );
    322
    323            scene.addChild( firstLight );
    324            scene.addChild( secondLight );
    325            scene.addChild( thirdLight );
    326
    327            // is handled by the mouse rotater, all objects will be added to it.
    328            rotationGroup = new TransformGroup();
    329            rotationGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
    330            rotationGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
    331            rotationGroup.setCapability( BranchGroup.ALLOW_DETACH );
    332            rotationGroup.setCapability( Group.ALLOW_CHILDREN_EXTEND );
    333            rotationGroup.setCapability( Group.ALLOW_CHILDREN_READ );
    334            rotationGroup.setCapability( Group.ALLOW_CHILDREN_WRITE );
    335            scene.addChild( rotationGroup );
    336
    337            backGround = new Background( new Color3f( Color.LIGHT_GRAY ) );
    338            backGround.setCapability( javax.media.j3d.Node.ALLOW_BOUNDS_WRITE );
    339            backGround.setCapability( javax.media.j3d.Node.ALLOW_BOUNDS_READ );
    340            backGround.setCapability( Background.ALLOW_APPLICATION_BOUNDS_READ );
    341            backGround.setCapability( Background.ALLOW_APPLICATION_BOUNDS_WRITE );
    342
    343            scene.addChild( backGround );
    344
    345            trackBall = new MouseRotate( rotationGroup );
    346            scene.addChild( trackBall );
    347
    348            simpleUniverse.addBranchGraph( scene );
    349
    350            // adding the key listeners
    351            canvas.addKeyListener( this );
    352
    353            if ( testSphere ) {
    354                BranchGroup sphere = createStartScene( null );
    355                addBranchGroupToScene( sphere );
    356            }
    357        }
    358
    359        /**
    360         * Create a directional light, with color.WHITE.
    361         */
    362        private DirectionalLight createDirectionalLight( Vector3f lightDir ) {
    363            // create the color for the light
    364            Color3f color = new Color3f( Color.WHITE );
    365            // create the directional light with the color and direction
    366            DirectionalLight light = new DirectionalLight( color, lightDir );
    367            light.setCapability( Light.ALLOW_INFLUENCING_BOUNDS_READ );
    368            light.setCapability( Light.ALLOW_INFLUENCING_BOUNDS_WRITE );
    369            return light;
    370        }
    371
    372        /**
    373         * Add the given branch group to the scene and set the appropriate distance etc. After adding the branch group to
    374         * the rotation group which is controlled by the mouse rotator.
    375         *
    376         * @param b
    377         */
    378        private void addBranchGroupToScene( BranchGroup b ) {
    379            rotationGroup.removeAllChildren();
    380            // translationGroup.removeAllChildren();
    381            // System.out.println( b.getBounds() );
    382            Bounds bounds = b.getBounds();
    383            BranchGroup wrapper = new BranchGroup();
    384            wrapper.setCapability( BranchGroup.ALLOW_DETACH );
    385            wrapper.setCapability( Group.ALLOW_CHILDREN_READ );
    386            wrapper.setCapability( Texture.ALLOW_ENABLE_READ );
    387            wrapper.setCapability( Texture.ALLOW_IMAGE_READ );
    388            if ( bounds != null ) {
    389                LOG.logDebug( "Old centroid: " + centroid );
    390                BoundingSphere bs = new BoundingSphere( bounds );
    391                bs.getCenter( centroid );
    392                LOG.logDebug( "New centroid: " + centroid );
    393
    394                TransformGroup viewToWorld = simpleUniverse.getViewingPlatform().getViewPlatformTransform();
    395                Transform3D trans = new Transform3D();
    396
    397                BoundingBox bbox = new BoundingBox( bounds );
    398                Point3d lower = new Point3d();
    399                bbox.getLower( lower );
    400                Point3d upper = new Point3d();
    401                bbox.getUpper( upper );
    402                double lengthX = Math.abs( upper.x - lower.x );
    403                double lengthY = Math.abs( upper.y - lower.y );
    404                double lengthZ = Math.abs( upper.z - lower.z );
    405
    406                zoomX = lengthX * 0.1;
    407                zoomY = lengthY * 0.1;
    408                zoomZ = lengthZ * 0.1;
    409
    410                double radius = ( lengthX > lengthY ) ? ( ( lengthX > lengthZ ) ? lengthX
    411                                                                               : ( ( lengthY > lengthZ ) ? lengthY
    412                                                                                                        : lengthZ ) )
    413                                                     : ( ( lengthY > lengthZ ) ? lengthY : lengthZ );
    414                Point3d eye = new Point3d();
    415                eye.z += ( ( lower.z - upper.z < 0 ) ? -1 : 1 ) * ( radius * 1.5 );
    416                if ( Math.abs( eye.z ) < 0.00001 && Math.abs( centroid.z ) < 0.00001 ) {
    417                    eye.z = 1;
    418                }
    419                trans.lookAt( eye, centroid, upVector );
    420
    421                LOG.logDebug( "Trans Matrix after lookat:\n" + trans );
    422                LOG.logDebug( "BBox lower: " + lower );
    423                LOG.logDebug( "BBox upper: " + upper );
    424                LOG.logDebug( "Center: " + centroid );
    425                LOG.logDebug( "eye: " + eye );
    426                LOG.logDebug( "up: " + upVector );
    427                LOG.logDebug( "radius: " + bs.getRadius() );
    428                LOG.logDebug( "used radius (of largest axis): " + radius );
    429                LOG.logDebug( "zoomX: " + zoomX );
    430                LOG.logDebug( "zoomY: " + zoomY );
    431                LOG.logDebug( "zoomZ: " + zoomZ );
    432                viewToWorld.setTransform( trans );
    433                View view = simpleUniverse.getViewer().getView();
    434
    435                // view parameters
    436                view.setBackClipDistance( radius * 100 );
    437                // the near clippingplane is one hundereth of the far.
    438                view.setFrontClipDistance( radius * 0.001 );
    439
    440                BoundingSphere influence = new BoundingSphere( eye, radius * 4 );
    441
    442                firstLight.setInfluencingBounds( influence );
    443                secondLight.setInfluencingBounds( influence );
    444                thirdLight.setInfluencingBounds( influence );
    445                trackBall.setSchedulingBounds( influence );
    446                backGround.setApplicationBounds( influence );
    447
    448                // add the negativ translation for the mouse tracker to work.
    449                TransformGroup resultTrans = new TransformGroup();
    450                Transform3D negativTranslation = new Transform3D();
    451                negativTranslation.setTranslation( new Vector3d( -centroid.x, -centroid.y, -centroid.z ) );
    452                resultTrans.setTransform( negativTranslation );
    453                resultTrans.addChild( b );
    454                wrapper.addChild( resultTrans );
    455            } else {
    456                wrapper.addChild( b );
    457            }
    458            rotationGroup.addChild( wrapper );
    459        }
    460
    461        /**
    462         * @return a shere and a cube
    463         */
    464        private BranchGroup createStartScene( Point3d translation ) {
    465            Appearance app = new Appearance();
    466            RenderingAttributes ra = new RenderingAttributes();
    467            ra.setDepthBufferEnable( true );
    468            ra.setDepthBufferWriteEnable( true );
    469            app.setRenderingAttributes( ra );
    470
    471            ColoringAttributes ca = new ColoringAttributes();
    472            ca.setShadeModel( ColoringAttributes.SHADE_GOURAUD );
    473            ca.setCapability( ColoringAttributes.NICEST );
    474            app.setColoringAttributes( ca );
    475
    476            Material material = new Material();
    477            material.setAmbientColor( new Color3f( Color.WHITE ) );
    478            material.setDiffuseColor( new Color3f( Color.RED ) );
    479            material.setSpecularColor( new Color3f( Color.ORANGE ) );
    480            app.setMaterial( material );
    481            TransformGroup tg = new TransformGroup();
    482            Transform3D trans = new Transform3D();
    483            if ( translation != null ) {
    484                trans.setTranslation( new Vector3d( translation ) );
    485                tg.setTransform( trans );
    486            }
    487            tg.setCapability( BranchGroup.ALLOW_DETACH );
    488            tg.setCapability( Group.ALLOW_CHILDREN_READ );
    489            tg.addChild( new ColorCube( 0.2f ) );
    490            BranchGroup b = new BranchGroup();
    491            b.setCapability( BranchGroup.ALLOW_DETACH );
    492            b.addChild( tg );
    493
    494            tg.addChild( new Sphere( 0.3f, app ) );
    495
    496            return b;
    497        }
    498
    499        private JPanel createButtons() {
    500            JPanel buttonPanel = new JPanel( new GridBagLayout() );
    501            GridBagConstraints gb = new GridBagConstraints();
    502            gb.gridx = 0;
    503            gb.gridy = 0;
    504
    505            JRadioButton help = new JRadioButton( "Activate help" );
    506            help.addActionListener( new ActionListener() {
    507                public void actionPerformed( ActionEvent e ) {
    508                    if ( helpLister.isVisible() ) {
    509                        helpLister.setVisible( false );
    510                        ( (JRadioButton) e.getSource() ).setText( "Activate help" );
    511                    } else {
    512                        helpLister.setVisible( true );
    513                        ( (JRadioButton) e.getSource() ).setText( "De-Activate help" );
    514
    515                    }
    516                }
    517            } );
    518            buttonPanel.add( help, gb );
    519
    520            gb.insets = new Insets( 10, 10, 10, 10 );
    521            gb.gridx++;
    522            JButton button = new JButton( "Open File" );
    523            button.setMnemonic( KeyEvent.VK_O );
    524            button.addActionListener( this );
    525            buttonPanel.add( button, gb );
    526
    527            gb.gridx++;
    528            button = new JButton( "Export File" );
    529            button.setMnemonic( KeyEvent.VK_O );
    530            button.addActionListener( this );
    531            buttonPanel.add( button, gb );
    532
    533            return buttonPanel;
    534        }
    535
    536        private void readFile( String fileName ) {
    537
    538            if ( fileName == null || "".equals( fileName.trim() ) ) {
    539                throw new InvalidParameterException( "the file name may not be null or empty" );
    540            }
    541            fileName = fileName.trim();
    542
    543            final Open3DFile openFile = new Open3DFile( fileName, this );
    544
    545            final JDialog dialog = new JDialog( this, "Loading", true );
    546
    547            dialog.getContentPane().setLayout( new BorderLayout() );
    548            dialog.getContentPane().add(
    549                                         new JLabel( "<HTML>Loading file:<br>" + fileName + "<br>Please wait!</HTML>",
    550                                                     SwingConstants.CENTER ), BorderLayout.NORTH );
    551            final JProgressBar progressBar = new JProgressBar();
    552            progressBar.setStringPainted( true );
    553            progressBar.setIndeterminate( false );
    554            dialog.getContentPane().add( progressBar, BorderLayout.CENTER );
    555
    556            dialog.pack();
    557            dialog.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE );
    558            dialog.setResizable( false );
    559            dialog.setLocationRelativeTo( this );
    560
    561            final Thread openThread = new Thread() {
    562                /**
    563                 * Opens the file in a separate thread.
    564                 */
    565                @Override
    566                public void run() {
    567                    openFile.openFile( progressBar );
    568                    if ( dialog.isDisplayable() ) {
    569                        dialog.setVisible( false );
    570                        dialog.dispose();
    571                    }
    572                }
    573            };
    574            openThread.start();
    575
    576            dialog.setVisible( true );
    577            BranchGroup result = openFile.getOpenedFile();
    578            //
    579            if ( result != null ) {
    580                addBranchGroupToScene( result );
    581                File f = new File( fileName );
    582                setTitle( WIN_TITLE + f.getName() );
    583            } else {
    584                showExceptionDialog( "The file: " + fileName
    585                                     + " could not be read,\nSee error log for detailed information." );
    586            }
    587
    588        }
    589
    590        /**
    591         * Shows an export dialog to the user.
    592         */
    593        @SuppressWarnings("unchecked")
    594        private void doExport() {
    595
    596            Export3DFile exportEvaluater = new Export3DFile( this );
    597            // find the scene graph to export
    598            Enumeration<Node> en = rotationGroup.getAllChildren();
    599            BranchGroup toExport = null;
    600            if ( en.hasMoreElements() ) {
    601                toExport = (BranchGroup) en.nextElement();
    602            }
    603            if ( toExport == null ) {
    604                showExceptionDialog( "Could not get the scene to export." );
    605                return;
    606            }
    607            StringBuilder sb = exportEvaluater.exportBranchgroup( toExport );
    608            if ( sb.length() == 0 ) {
    609                showExceptionDialog( "Exporting failed, see error log for details." );
    610                return;
    611            }
    612            addBranchGroupToScene( toExport );
    613            JFileChooser chooser = createFileChooser( null );
    614            int result = chooser.showSaveDialog( this );
    615            if ( JFileChooser.APPROVE_OPTION == result ) {
    616                File f = chooser.getSelectedFile();
    617                FileFilter ff = chooser.getFileFilter();
    618                if ( ff instanceof CustomFileFilter ) {
    619                    prefs.put( LAST_EXTENSION, ( (CustomFileFilter) ff ).getExtension( f ) );
    620                    prefs.put( OPEN_KEY, f.getParent() );
    621                }
    622                try {
    623                    FileWriter output = new FileWriter( f );
    624                    output.write( sb.toString() );
    625                    output.flush();
    626                    output.close();
    627                } catch ( IOException e ) {
    628                    LOG.logError( e.getMessage(), e );
    629                    showExceptionDialog( "Exporting failed, see error log for details." );
    630                }
    631
    632            }
    633        }
    634
    635        /**
    636         * @param errorMessage
    637         *            to display
    638         */
    639        public void showExceptionDialog( String errorMessage ) {
    640            JOptionPane.showMessageDialog( this, errorMessage );
    641        }
    642
    643        /*
    644         * (non-Javadoc)
    645         *
    646         * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
    647         */
    648        public void actionPerformed( ActionEvent e ) {
    649            Object source = e.getSource();
    650            if ( source instanceof JButton ) {
    651                JButton clicked = (JButton) source;
    652                if ( clicked.getText().startsWith( "Export" ) ) {
    653                    doExport();
    654                } else {
    655                    JFileChooser fileChooser = createFileChooser( supportedOpenFilter );
    656                    int result = fileChooser.showOpenDialog( this );
    657                    if ( JFileChooser.APPROVE_OPTION == result ) {
    658                        File f = fileChooser.getSelectedFile();
    659                        if ( f != null ) {
    660                            String path = f.getAbsolutePath();
    661                            prefs.put( LAST_EXTENSION, ( (CustomFileFilter) fileChooser.getFileFilter() ).getExtension( f ) );
    662                            prefs.put( OPEN_KEY, f.getParent() );
    663                            readFile( path );
    664                        }
    665
    666                    }
    667                }
    668            }
    669        }
    670
    671        /*
    672         * (non-Javadoc)
    673         *
    674         * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
    675         */
    676        public void keyPressed( KeyEvent arg0 ) {
    677            // nottin
    678        }
    679
    680        /*
    681         * (non-Javadoc)
    682         *
    683         * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
    684         */
    685        public void keyReleased( KeyEvent arg0 ) {
    686            // nottin
    687        }
    688
    689        /*
    690         * (non-Javadoc)
    691         *
    692         * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
    693         */
    694        public void keyTyped( KeyEvent e ) {
    695            double x = 0;
    696            double y = 0;
    697            double z = 0;
    698            if ( e.getKeyChar() == 'q' ) {
    699                System.exit( 0 );
    700            } else if ( e.getKeyChar() == 'x' ) {
    701                x = zoomX;
    702            } else if ( e.getKeyChar() == 'X' ) {
    703                x = -zoomX;
    704            } else if ( e.getKeyChar() == 'y' ) {
    705                y = zoomY;
    706            } else if ( e.getKeyChar() == 'Y' ) {
    707                y = -zoomY;
    708            } else if ( e.getKeyChar() == 'z' ) {
    709                z = zoomZ;
    710            } else if ( e.getKeyChar() == 'Z' ) {
    711                z = -zoomZ;
    712            }
    713
    714            TransformGroup viewToWorld = simpleUniverse.getViewingPlatform().getViewPlatformTransform();
    715            Transform3D trans = new Transform3D();
    716            viewToWorld.getTransform( trans );
    717            trans.invert();
    718            Vector3d translation = new Vector3d();
    719            trans.get( translation );
    720
    721            x += translation.x;
    722            y += translation.y;
    723            z += translation.z;
    724
    725            Point3d eye = new Point3d( x, y, z );
    726            trans.lookAt( eye, centroid, upVector );
    727            LOG.logDebug( "Trans after:\n" + trans + "\ncentroid: " + centroid );
    728            LOG.logDebug( "Center: " + centroid );
    729            LOG.logDebug( "eye: " + eye );
    730
    731            Vector3d dist = new Vector3d( centroid );
    732            dist.sub( eye );
    733            // trackBall.setSchedulingBounds( new BoundingSphere( centroid, dist.length() ) );
    734            viewToWorld.setTransform( trans );
    735
    736        }
    737
    738        /**
    739         * @param args
    740         */
    741        public static void main( String[] args ) {
    742            View3DFile viewer = new View3DFile( true );
    743            viewer.toFront();
    744
    745        }
    746
    747        /**
    748         *
    749         * The <code>CustomFileFilter</code> class adds functionality to the filefilter mechanism of the JFileChooser.
    750         *
    751         * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
    752         *
    753         * @author last edited by: $Author:$
    754         *
    755         * @version $Revision:$, $Date:$
    756         *
    757         */
    758        private class CustomFileFilter extends FileFilter {
    759
    760            private List<String> acceptedExtensions;
    761
    762            private String desc;
    763
    764            /**
    765             * @param acceptedExtensions
    766             *            list of extensions this filter accepts.
    767             * @param description
    768             *            to show
    769             */
    770            CustomFileFilter( List<String> acceptedExtensions, String description ) {
    771                this.acceptedExtensions = new ArrayList<String>( acceptedExtensions.size() );
    772                StringBuilder sb = new StringBuilder();
    773                if ( acceptedExtensions.size() > 0 ) {
    774
    775                    sb.append( "(" );
    776                    int i = 0;
    777                    for ( String ext : acceptedExtensions ) {
    778                        if ( ext.startsWith( "." ) ) {
    779                            ext = ext.substring( 1 );
    780                        } else if ( ext.startsWith( "*." ) ) {
    781                            ext = ext.substring( 2 );
    782                        } else if ( ext.startsWith( "*" ) ) {
    783                            ext = ext.substring( 1 );
    784                        }
    785
    786                        this.acceptedExtensions.add( ext.trim().toUpperCase() );
    787                        sb.append( "*." );
    788                        sb.append( ext );
    789                        if ( ++i < acceptedExtensions.size() ) {
    790                            sb.append( ", " );
    791                        }
    792                    }
    793                    sb.append( ")" );
    794                }
    795                sb.append( description );
    796                desc = sb.toString();
    797            }
    798
    799            /**
    800             * @param extension
    801             * @return true if the extension is accepted
    802             */
    803            public boolean accepts( String extension ) {
    804                return extension != null && acceptedExtensions.contains( extension.toUpperCase() );
    805            }
    806
    807            @Override
    808            public boolean accept( File pathname ) {
    809                if ( pathname.isDirectory() ) {
    810                    return true;
    811                }
    812
    813                String extension = getExtension( pathname );
    814                if ( extension != null ) {
    815                    if ( acceptedExtensions.contains( extension.trim().toUpperCase() ) ) {
    816                        return true;
    817                    }
    818                }
    819                return false;
    820            }
    821
    822            /**
    823             * @param f
    824             * @return the file extension (e.g. gml/shp/xml etc.)
    825             */
    826            String getExtension( File f ) {
    827                String ext = null;
    828                String s = f.getName();
    829                int i = s.lastIndexOf( '.' );
    830
    831                if ( i > 0 && i < s.length() - 1 ) {
    832                    ext = s.substring( i + 1 ).toLowerCase();
    833                }
    834                return ext;
    835            }
    836
    837            @Override
    838            public String getDescription() {
    839                return desc;
    840            }
    841        }
    842
    843    }