-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #68 from bigdataviewer/bdvsynchronizer
adds bdv windows synchronization actions + command + bdvhandle widget…
- Loading branch information
Showing
11 changed files
with
574 additions
and
12 deletions.
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
src/main/java/sc/fiji/bdvpg/bdv/navigate/ViewerTransformSyncStarter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package sc.fiji.bdvpg.bdv.navigate; | ||
|
||
import bdv.util.BdvHandle; | ||
import net.imglib2.realtransform.AffineTransform3D; | ||
import net.imglib2.ui.TransformListener; | ||
|
||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* Action which synchronizes the display location of n BdvHandle | ||
* | ||
* Works in combination with the action ViewerTransformSyncStopper | ||
* | ||
* See also ViewTransformSynchronizationDemo | ||
* | ||
* Principle : for every changed view transform of a specific BdvHandle, | ||
* the view transform change is triggered to the following BdvHandle in a closed loop manner | ||
* | ||
* To avoid inifinite loop, the stop condition is : if the view transform is unnecessary (between | ||
* the view target is equal to the source), then there's no need to trigger a view transform change | ||
* to the next BdvHandle | ||
* | ||
* author Nicolas Chiaruttini, BIOP EPFL, [email protected] | ||
*/ | ||
|
||
public class ViewerTransformSyncStarter implements Runnable { | ||
|
||
/** | ||
* Array of BdvHandles to synchronize | ||
*/ | ||
BdvHandle[] bdvHandles; | ||
|
||
/** | ||
* Reference to the BdvHandle which will serve as a reference for the | ||
* first synchronization. Most of the time this has to be the BdvHandle | ||
* currently used by the user. If not set, the first synchronization | ||
* will look like it's a random BdvHandle which is used (one not in focus) | ||
*/ | ||
BdvHandle bdvHandleInitialReference = null; | ||
|
||
/** | ||
* Map which links each BdvHandle to the TransformListener which has been added | ||
* for synchronization purpose. This object contains all what's neede to stop | ||
* the synchronization | ||
*/ | ||
Map<BdvHandle, TransformListener<AffineTransform3D>> bdvHandleToTransformListener = new HashMap<>(); | ||
|
||
public ViewerTransformSyncStarter(BdvHandle[] bdvHandles) { | ||
this.bdvHandles = bdvHandles; | ||
} | ||
|
||
public void setBdvHandleInitialReference(BdvHandle bdvHandle) { | ||
bdvHandleInitialReference = bdvHandle; | ||
} | ||
|
||
@Override | ||
public void run() { | ||
|
||
// Getting transform for initial sync | ||
AffineTransform3D at3Dorigin = getViewTransformForInitialSynchronization(); | ||
|
||
// Building circularly linked listeners with stop condition when all transforms are equal, | ||
// cf documentation | ||
|
||
for (int i = 0; i< bdvHandles.length; i++) { | ||
|
||
// The idea is that bdvHandles[i], when it has a view transform, | ||
// triggers an identical ViewTransform to the next bdvHandle in the array | ||
// (called nextBdvHandle). nextBdvHandle is bdvHandles[i+1] in most cases, | ||
// unless it's the end of the array, | ||
// where in this case nextBdvHandle is bdvHandles[0] | ||
BdvHandle currentBdvHandle = bdvHandles[i]; | ||
BdvHandle nextBdvHandle; | ||
|
||
// Identifying nextBdvHandle | ||
if (i == bdvHandles.length-1) { | ||
nextBdvHandle = bdvHandles[0]; | ||
} else { | ||
nextBdvHandle = bdvHandles[i+1]; | ||
} | ||
|
||
// Building the TransformListener of currentBdvHandle | ||
TransformListener<AffineTransform3D> listener = | ||
(at3D) -> { | ||
// Is the transform necessary ? That's the stop condition | ||
AffineTransform3D ati = new AffineTransform3D(); | ||
nextBdvHandle.getViewerPanel().getState().getViewerTransform(ati); | ||
if (!Arrays.equals(at3D.getRowPackedCopy(), ati.getRowPackedCopy())) { | ||
// Yes -> triggers a transform change to the nextBdvHandle | ||
nextBdvHandle.getViewerPanel().setCurrentViewerTransform(at3D.copy()); | ||
nextBdvHandle.getViewerPanel().requestRepaint(); | ||
} | ||
}; | ||
|
||
// Adding this transform listener to the currenBdvHandle | ||
currentBdvHandle.getViewerPanel().addTransformListener(listener); | ||
|
||
// Storing the transform listener -> needed to remove them in order to stop synchronization when needed | ||
bdvHandleToTransformListener.put(bdvHandles[i], listener); | ||
} | ||
|
||
// Setting first transform for initial synchronization, | ||
// but only if the two necessary objects are present (the origin BdvHandle and the transform | ||
if ((bdvHandleInitialReference !=null)&&(at3Dorigin!=null)) { | ||
for (BdvHandle bdvh: bdvHandles) { | ||
bdvh.getViewerPanel().setCurrentViewerTransform(at3Dorigin.copy()); | ||
bdvh.getViewerPanel().requestRepaint(); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* A simple search to identify the view transform of the BdvHandle that will be used | ||
* for the initial synchronization (first reference) | ||
* @return | ||
*/ | ||
private AffineTransform3D getViewTransformForInitialSynchronization() { | ||
AffineTransform3D at3Dorigin = null; | ||
for (int i = 0; i< bdvHandles.length; i++) { | ||
BdvHandle bdvHandle = bdvHandles[i]; | ||
// if the BdvHandle is the one that should be used for initial synchronization | ||
if (bdvHandle.equals(bdvHandleInitialReference)) { | ||
// Storing the transform that will be used for first synchronization | ||
at3Dorigin = new AffineTransform3D(); | ||
bdvHandle.getViewerPanel().getState().getViewerTransform(at3Dorigin); | ||
} | ||
} | ||
return at3Dorigin; | ||
} | ||
|
||
/** | ||
* output of this action : this map can be used to stop the synchronization | ||
* see ViewerTransformSyncStopper | ||
* @return | ||
*/ | ||
public Map<BdvHandle, TransformListener<AffineTransform3D>> getSynchronizers() { | ||
return bdvHandleToTransformListener; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/sc/fiji/bdvpg/bdv/navigate/ViewerTransformSyncStopper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package sc.fiji.bdvpg.bdv.navigate; | ||
|
||
import bdv.util.BdvHandle; | ||
import net.imglib2.realtransform.AffineTransform3D; | ||
import net.imglib2.ui.TransformListener; | ||
import java.util.Map; | ||
|
||
/** | ||
* Action which stops the synchronization of the display location of n BdvHandle | ||
* Works in combination with the action ViewerTransformSyncStarter | ||
* | ||
* See also ViewTransformSynchronizationDemo | ||
* | ||
* author Nicolas Chiaruttini, BIOP EPFL, [email protected] | ||
**/ | ||
|
||
public class ViewerTransformSyncStopper implements Runnable { | ||
|
||
Map<BdvHandle, TransformListener<AffineTransform3D>> bdvHandleToTransformListener; | ||
|
||
public ViewerTransformSyncStopper(Map<BdvHandle, TransformListener<AffineTransform3D>> bdvHandleToTransformListener) { | ||
this.bdvHandleToTransformListener = bdvHandleToTransformListener; | ||
} | ||
|
||
@Override | ||
public void run() { | ||
bdvHandleToTransformListener.forEach((bdvHandle, listener) -> { | ||
bdvHandle.getViewerPanel().removeTransformListener(listener); | ||
}); | ||
} | ||
|
||
|
||
} |
71 changes: 71 additions & 0 deletions
71
src/main/java/sc/fiji/bdvpg/scijava/command/bdv/ViewSynchronizerCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package sc.fiji.bdvpg.scijava.command.bdv; | ||
|
||
import bdv.util.BdvHandle; | ||
import org.scijava.command.Command; | ||
import org.scijava.plugin.Parameter; | ||
import org.scijava.plugin.Plugin; | ||
import sc.fiji.bdvpg.bdv.navigate.ViewerTransformSyncStarter; | ||
import sc.fiji.bdvpg.bdv.navigate.ViewerTransformSyncStopper; | ||
import sc.fiji.bdvpg.scijava.BdvHandleHelper; | ||
import sc.fiji.bdvpg.scijava.ScijavaBdvDefaults; | ||
import sc.fiji.bdvpg.services.BdvService; | ||
|
||
import javax.swing.*; | ||
import java.awt.*; | ||
import java.awt.event.WindowAdapter; | ||
import java.awt.event.WindowEvent; | ||
|
||
/** | ||
* I wanted to do this as an Interactive Command but there's no callback | ||
* when an interactive command is closed (bug https://github.com/scijava/scijava-common/issues/379) | ||
* -> we cannot stop the synchronization appropriately. | ||
* | ||
* Hence the dirty JFrame the user has to close to stop synchronization ... | ||
* | ||
* author Nicolas Chiaruttini | ||
*/ | ||
|
||
@Plugin(type = Command.class, menuPath = ScijavaBdvDefaults.RootMenu+"Bdv>Synchronize Views") | ||
public class ViewSynchronizerCommand implements Command { | ||
|
||
@Parameter(label = "Select Windows to synchronize") | ||
BdvHandle[] bdvhs; | ||
|
||
ViewerTransformSyncStarter sync; | ||
|
||
public void run() { | ||
// Starting synchronnization of selected bdvhandles | ||
sync = new ViewerTransformSyncStarter(bdvhs); | ||
sync.setBdvHandleInitialReference(BdvService.getSourceAndConverterDisplayService().getActiveBdv()); | ||
sync.run(); | ||
|
||
// JFrame serving the purpose of stopping synchronization when it is being closed | ||
JFrame frameStopSync = new JFrame(); | ||
frameStopSync.addWindowListener(new WindowAdapter() { | ||
@Override | ||
public void windowClosing(WindowEvent e) { | ||
super.windowClosing(e); | ||
new ViewerTransformSyncStopper(sync.getSynchronizers()).run(); | ||
e.getWindow().dispose(); | ||
} | ||
}); | ||
frameStopSync.setTitle("Close window to stop synchronization"); | ||
|
||
// Building JFrame with a simple panel and textarea | ||
String text = ""; | ||
for (BdvHandle bdvh:bdvhs) { | ||
text+= BdvHandleHelper.getWindowTitle(bdvh)+"\n"; | ||
} | ||
|
||
JPanel pane = new JPanel(); | ||
JTextArea textArea = new JTextArea(text); | ||
textArea.setEditable(false); | ||
pane.add(textArea); | ||
frameStopSync.add(pane); | ||
frameStopSync.setPreferredSize(new Dimension(600,100)); | ||
|
||
frameStopSync.pack(); | ||
frameStopSync.setVisible(true); | ||
} | ||
|
||
} |
38 changes: 38 additions & 0 deletions
38
src/main/java/sc/fiji/bdvpg/scijava/converters/StringToBdvHandle.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package sc.fiji.bdvpg.scijava.converters; | ||
|
||
import bdv.util.BdvHandle; | ||
import org.scijava.convert.AbstractConverter; | ||
import org.scijava.object.ObjectService; | ||
import org.scijava.plugin.Parameter; | ||
import org.scijava.plugin.Plugin; | ||
import sc.fiji.bdvpg.scijava.BdvHandleHelper; | ||
|
||
import java.util.Optional; | ||
|
||
@Plugin(type = org.scijava.convert.Converter.class) | ||
public class StringToBdvHandle<I extends String, O extends BdvHandle> extends AbstractConverter<I, O> { | ||
@Parameter | ||
ObjectService os; | ||
|
||
@Override | ||
public <T> T convert(Object src, Class<T> dest) { | ||
Optional<BdvHandle> ans = os.getObjects(BdvHandle.class).stream().filter(bdvh -> | ||
(bdvh.toString().equals(src))||(BdvHandleHelper.getWindowTitle(bdvh).equals(src)) | ||
).findFirst(); | ||
if (ans.isPresent()) { | ||
return (T) ans.get(); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
public Class<O> getOutputType() { | ||
return (Class<O>) BdvHandle.class; | ||
} | ||
|
||
@Override | ||
public Class<I> getInputType() { | ||
return (Class<I>) String.class; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
src/main/java/sc/fiji/bdvpg/scijava/widget/BdvHandleListWidget.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package sc.fiji.bdvpg.scijava.widget; | ||
|
||
import bdv.util.BdvHandle; | ||
import org.scijava.widget.InputWidget; | ||
|
||
public interface BdvHandleListWidget<U> extends InputWidget<BdvHandle[], U> { | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/sc/fiji/bdvpg/scijava/widget/BdvHandleWidget.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package sc.fiji.bdvpg.scijava.widget; | ||
|
||
import bdv.util.BdvHandle; | ||
import bdv.viewer.SourceAndConverter; | ||
import org.scijava.widget.InputWidget; | ||
|
||
public interface BdvHandleWidget<U> extends InputWidget<BdvHandle, U> { | ||
} |
Oops, something went wrong.