/*- * #%L * Fiji distribution of ImageJ for the life sciences. * %% * Copyright (C) 2007 - 2017 Fiji developers. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package spim.fiji.spimdata.explorer.popup; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import bdv.AbstractSpimSource; import bdv.BigDataViewer; import bdv.tools.InitializeViewerState; import bdv.tools.transformation.TransformedSource; import bdv.viewer.Source; import bdv.viewer.ViewerOptions; import bdv.viewer.ViewerPanel; import bdv.viewer.state.SourceState; import bdv.viewer.state.ViewerState; import mpicbg.spim.data.generic.sequence.BasicImgLoader; import mpicbg.spim.data.registration.ViewRegistration; import mpicbg.spim.io.IOFunctions; import net.imglib2.Interval; import net.imglib2.realtransform.AffineTransform3D; import net.imglib2.util.LinAlgHelpers; import spim.fiji.plugin.apply.BigDataViewerTransformationWindow; import spim.fiji.spimdata.explorer.ViewSetupExplorerPanel; import spim.fiji.spimdata.imgloaders.AbstractImgLoader; import spim.fiji.spimdata.imgloaders.StackImgLoader; public class BDVPopup extends JMenuItem implements ViewExplorerSetable { private static final long serialVersionUID = 5234649267634013390L; public ViewSetupExplorerPanel< ?, ? > panel; public BigDataViewer bdv = null; public BDVPopup() { super( "Display in BigDataViewer (on/off)" ); this.addActionListener( new MyActionListener() ); } @Override public JMenuItem setViewExplorer( final ViewSetupExplorerPanel< ?, ? > panel ) { this.panel = panel; return this; } public class MyActionListener implements ActionListener { @Override public void actionPerformed( final ActionEvent e ) { if ( panel == null ) { IOFunctions.println( "Panel not set for " + this.getClass().getSimpleName() ); return; } new Thread( new Runnable() { @Override public void run() { // if BDV was closed by the user if ( bdv != null && !bdv.getViewerFrame().isVisible() ) bdv = null; if ( bdv == null ) { try { bdv = createBDV( panel ); } catch (Exception e) { IOFunctions.println( "Could not run BigDataViewer: " + e ); e.printStackTrace(); bdv = null; } } else { BigDataViewerTransformationWindow.disposeViewerWindow( bdv ); bdv = null; } } }).start(); } } public static boolean bdvRunning() { final BDVPopup p = ViewSetupExplorerPanel.bdvPopup(); return ( p != null && p.bdv != null && p.bdv.getViewerFrame().isVisible() ); } public static BigDataViewer createBDV( final ViewSetupExplorerPanel< ?, ? > panel ) { BasicImgLoader il = panel.getSpimData().getSequenceDescription().getImgLoader(); if ( AbstractImgLoader.class.isInstance( il ) || StackImgLoader.class.isInstance( il ) ) { if ( JOptionPane.showConfirmDialog( null, "Opening <SpimData> dataset that is not suited for interactive browsing.\n" + "Consider resaving as HDF5 for better performance.\n" + "Proceed anyways?", "Warning", JOptionPane.YES_NO_OPTION ) == JOptionPane.NO_OPTION ) return null; } BigDataViewer bdv = BigDataViewer.open( panel.getSpimData(), panel.xml(), IOFunctions.getProgressWriter(), ViewerOptions.options() ); // if ( !bdv.tryLoadSettings( panel.xml() ) ) TODO: this should work, but currently tryLoadSettings is protected. fix that. InitializeViewerState.initBrightness( 0.001, 0.999, bdv.getViewer(), bdv.getSetupAssignments() ); // do not rotate BDV view by default BDVPopup.initTransform( bdv.getViewer() ); ViewSetupExplorerPanel.updateBDV( bdv, panel.colorMode(), panel.getSpimData(), panel.firstSelectedVD(), panel.selectedRows() ); // final ArrayList< InterestPointSource > interestPointSources = new ArrayList< InterestPointSource >(); // interestPointSources.add( new InterestPointSource() // { // private final ArrayList< RealPoint > points; // { // points = new ArrayList< RealPoint >(); // final Random rand = new Random(); // for ( int i = 0; i < 1000; ++i ) // points.add( new RealPoint( rand.nextDouble() * 1400, rand.nextDouble() * 800, rand.nextDouble() * 300 ) ); // } // // @Override // public final Collection< ? extends RealLocalizable > getLocalCoordinates( final int timepointIndex ) // { // return points; // } // // @Override // public void getLocalToGlobalTransform( final int timepointIndex, final AffineTransform3D transform ) // { // transform.identity(); // } // } ); // final InterestPointOverlay interestPointOverlay = new InterestPointOverlay( bdv.getViewer(), interestPointSources ); // bdv.getViewer().addRenderTransformListener( interestPointOverlay ); // bdv.getViewer().getDisplay().addOverlayRenderer( interestPointOverlay ); // bdv.getViewer().removeTransformListener( interestPointOverlay ); // bdv.getViewer().getDisplay().removeOverlayRenderer( interestPointOverlay ); return bdv; } public void updateBDV() { if ( bdv == null ) return; for ( final ViewRegistration r : panel.getSpimData().getViewRegistrations().getViewRegistrationsOrdered() ) r.updateModel(); final ViewerPanel viewerPanel = bdv.getViewer(); final ViewerState viewerState = viewerPanel.getState(); final List< SourceState< ? > > sources = viewerState.getSources(); for ( final SourceState< ? > state : sources ) { Source< ? > source = state.getSpimSource(); while ( TransformedSource.class.isInstance( source ) ) { source = ( ( TransformedSource< ? > ) source ).getWrappedSource(); } if ( AbstractSpimSource.class.isInstance( source ) ) { final AbstractSpimSource< ? > s = ( AbstractSpimSource< ? > ) source; final int tpi = getCurrentTimePointIndex( s ); callLoadTimePoint( s, tpi ); } if ( state.asVolatile() != null ) { source = state.asVolatile().getSpimSource(); while ( TransformedSource.class.isInstance( source ) ) { source = ( ( TransformedSource< ? > ) source ).getWrappedSource(); } if ( AbstractSpimSource.class.isInstance( source ) ) { final AbstractSpimSource< ? > s = ( AbstractSpimSource< ? > ) source; final int tpi = getCurrentTimePointIndex( s ); callLoadTimePoint( s, tpi ); } } } bdv.getViewer().requestRepaint(); } public static void initTransform( final ViewerPanel viewer ) { final Dimension dim = viewer.getDisplay().getSize(); final ViewerState state = viewer.getState(); final AffineTransform3D viewerTransform = initTransform( dim.width, dim.height, false, state ); viewer.setCurrentViewerTransform( viewerTransform ); } public static AffineTransform3D initTransform( final int viewerWidth, final int viewerHeight, final boolean zoomedIn, final ViewerState state ) { final int cX = viewerWidth / 2; final int cY = viewerHeight / 2; final Source< ? > source = state.getSources().get( state.getCurrentSource() ).getSpimSource(); final int timepoint = state.getCurrentTimepoint(); if ( !source.isPresent( timepoint ) ) return new AffineTransform3D(); final AffineTransform3D sourceTransform = new AffineTransform3D(); source.getSourceTransform( timepoint, 0, sourceTransform ); final Interval sourceInterval = source.getSource( timepoint, 0 ); final double sX0 = sourceInterval.min( 0 ); final double sX1 = sourceInterval.max( 0 ); final double sY0 = sourceInterval.min( 1 ); final double sY1 = sourceInterval.max( 1 ); final double sZ0 = sourceInterval.min( 2 ); final double sZ1 = sourceInterval.max( 2 ); final double sX = ( sX0 + sX1 + 1 ) / 2; final double sY = ( sY0 + sY1 + 1 ) / 2; final double sZ = ( sZ0 + sZ1 + 1 ) / 2; final double[][] m = new double[ 3 ][ 4 ]; // NO rotation final double[] qViewer = new double[]{ 1, 0, 0, 0 }; LinAlgHelpers.quaternionToR( qViewer, m ); // translation final double[] centerSource = new double[] { sX, sY, sZ }; final double[] centerGlobal = new double[ 3 ]; final double[] translation = new double[ 3 ]; sourceTransform.apply( centerSource, centerGlobal ); LinAlgHelpers.quaternionApply( qViewer, centerGlobal, translation ); LinAlgHelpers.scale( translation, -1, translation ); LinAlgHelpers.setCol( 3, translation, m ); final AffineTransform3D viewerTransform = new AffineTransform3D(); viewerTransform.set( m ); // scale final double[] pSource = new double[] { sX1 + 0.5, sY1 + 0.5, sZ }; final double[] pGlobal = new double[ 3 ]; final double[] pScreen = new double[ 3 ]; sourceTransform.apply( pSource, pGlobal ); viewerTransform.apply( pGlobal, pScreen ); final double scaleX = cX / pScreen[ 0 ]; final double scaleY = cY / pScreen[ 1 ]; final double scale; if ( zoomedIn ) scale = Math.max( scaleX, scaleY ); else scale = Math.min( scaleX, scaleY ); viewerTransform.scale( scale ); // window center offset viewerTransform.set( viewerTransform.get( 0, 3 ) + cX, 0, 3 ); viewerTransform.set( viewerTransform.get( 1, 3 ) + cY, 1, 3 ); return viewerTransform; } private static final void callLoadTimePoint( final AbstractSpimSource< ? > s, final int timePointIndex ) { try { Class< ? > clazz = null; boolean found = false; do { if ( clazz == null ) clazz = s.getClass(); else clazz = clazz.getSuperclass(); if ( clazz != null ) for ( final Method method : clazz.getDeclaredMethods() ) if ( method.getName().equals( "loadTimepoint" ) ) found = true; } while ( !found && clazz != null ); if ( !found ) { System.out.println( "Failed to find SpimSource.loadTimepoint method. Quiting." ); return; } final Method loadTimepoint = clazz.getDeclaredMethod( "loadTimepoint", Integer.TYPE ); loadTimepoint.setAccessible( true ); loadTimepoint.invoke( s, timePointIndex ); } catch ( Exception e ) { e.printStackTrace(); } } private static final int getCurrentTimePointIndex( final AbstractSpimSource< ? > s ) { try { Class< ? > clazz = null; Field currentTimePointIndex = null; do { if ( clazz == null ) clazz = s.getClass(); else clazz = clazz.getSuperclass(); if ( clazz != null ) for ( final Field field : clazz.getDeclaredFields() ) if ( field.getName().equals( "currentTimePointIndex" ) ) currentTimePointIndex = field; } while ( currentTimePointIndex == null && clazz != null ); if ( currentTimePointIndex == null ) { System.out.println( "Failed to find AbstractSpimSource.currentTimePointIndex. Quiting." ); return -1; } currentTimePointIndex.setAccessible( true ); return currentTimePointIndex.getInt( s ); } catch ( Exception e ) { e.printStackTrace(); return -1; } } }