Skip to content

Commit

Permalink
Merge pull request #228 from saalfeldlab/interpolation-painting
Browse files Browse the repository at this point in the history
Interpolation painting
  • Loading branch information
hanslovsky authored Jun 20, 2019
2 parents 1741b57 + 135ecd4 commit 1a7e66c
Show file tree
Hide file tree
Showing 31 changed files with 2,467 additions and 859 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ Usage: Paintera [-h] [--default-to-temp-directory] [--print-error-codes]
| `F` + left click | 2D Flood-fill in current viewer plane with id that was last toggled active (if any) |
| `Shift` + `F` + left click | Flood-fill with id that was last toggled active (if any) |
| `N` | Select new, previously unused id |
| `S` | Enter shape interpolation mode |
| `1` / `2` | Edit first/second section when previewing interpolated shape |
| `Enter` | Commit interpolated shape into canvas |
| `Esc` | Abort shape interpolation mode |
| `Ctrl` + `C` | Show dialog to commit canvas and/or assignments |
| `C` | Increment ARGB stream seed by one |
| `Shift` + `C` | Decrement ARGB stream seed by one |
Expand All @@ -174,6 +178,14 @@ Usage: Paintera [-h] [--default-to-temp-directory] [--print-error-codes]
| `Ctrl` + `Shift` + `N` | Create new label dataset |
| `Ctrl` + `T` | Threshold raw source (only available if current source is raw source) |
### Shape interpolation mode
The mode is activated by pressing the `S` key when the current source is a label source. Then, you can select the objects in the sections by left/right clicking (scrolling automatically fixes the selection in the current section).
When you're done with selecting the objects in the second section and initiate scrolling, the preview of the interpolated shape will be displayed. If something is not right, you can edit the selection in the first or second section by pressing `1` or `2`, which will update the preview. When the desired result is reached, hit `Enter` to commit the results into the canvas and return back to normal mode.
While in the shape interpolation mode, at any point in time you can hit `Esc` to discard the current state and exit the mode.
## Data
In [#61](https://github.com/saalfeldlab/paintera/issues/61) we introduced a specification for the data format that Paintera can load through the opener dialog (`Ctrl O`).
Expand Down
38 changes: 20 additions & 18 deletions src/main/java/bdv/fx/viewer/ViewerPanelFX.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,43 +36,26 @@
import bdv.viewer.Source;
import bdv.viewer.SourceAndConverter;
import bdv.viewer.ViewerOptions;
import gnu.trove.list.array.TIntArrayList;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.StackPane;
import net.imglib2.Interval;
import net.imglib2.Point;
import net.imglib2.Positionable;
import net.imglib2.RealInterval;
import net.imglib2.RealLocalizable;
import net.imglib2.RealPoint;
import net.imglib2.RealPositionable;
import net.imglib2.algorithm.fill.FloodFill;
import net.imglib2.algorithm.neighborhood.DiamondShape;
import net.imglib2.img.array.ArrayImg;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.img.basictypeaccess.array.IntArray;
import net.imglib2.img.cell.CellGrid;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.type.numeric.integer.IntType;
import net.imglib2.ui.TransformListener;
import net.imglib2.util.Intervals;
import net.imglib2.view.Views;

import org.janelia.saalfeldlab.paintera.data.axisorder.AxisOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
Expand Down Expand Up @@ -287,6 +270,25 @@ public void displayToGlobalCoordinates(final double x, final double y, final Rea
viewerTransform.applyInverse(gPos, lPos);
}

/**
* Set {@code pos} to the display coordinates (x,y,0)<sup>T</sup> transformed into the source coordinate system.
*
* @param pos
* is set to the source coordinates at display (x,y,0)<sup>T</sup>.
*/
public <P extends RealLocalizable & RealPositionable> void displayToSourceCoordinates(
final double x,
final double y,
final AffineTransform3D sourceTransform,
final P pos)
{
pos.setPosition(x, 0);
pos.setPosition(y, 1);
pos.setPosition(0, 2);
displayToGlobalCoordinates(pos);
sourceTransform.applyInverse(pos, pos);
}

/**
* Set {@code gPos} to the current mouse coordinates transformed into the global coordinate system.
*
Expand Down Expand Up @@ -499,7 +501,7 @@ public ReadOnlyDoubleProperty mouseYProperty()
* 1. {@code 0 < sceenScales[i] <= 1} for all {@code i}
* 2. {@code screenScales[i] < screenScales[i - 1]} for all {@code i > 0}
*/
public void setScreenScales(double[] screenScales)
public void setScreenScales(final double[] screenScales)
{
LOG.debug("Setting screen scales to {}", screenScales);
this.renderUnit.setScreenScales(screenScales.clone());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
import java.util.function.Consumer;
import java.util.function.Function;

import org.janelia.saalfeldlab.paintera.control.navigation.AffineTransformWithListeners;
import org.janelia.saalfeldlab.paintera.control.navigation.TransformConcatenator;
import org.janelia.saalfeldlab.paintera.data.axisorder.AxisOrder;
import org.janelia.saalfeldlab.paintera.state.GlobalTransformManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import bdv.cache.CacheControl;
import bdv.fx.viewer.ViewerPanelFX;
import bdv.util.volatiles.SharedQueue;
import bdv.viewer.Interpolation;
import bdv.viewer.Source;
import bdv.viewer.SourceAndConverter;
Expand All @@ -16,14 +22,8 @@
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.scene.layout.GridPane;
import net.imglib2.realtransform.AffineTransform3D;
import org.janelia.saalfeldlab.paintera.control.navigation.AffineTransformWithListeners;
import org.janelia.saalfeldlab.paintera.control.navigation.TransformConcatenator;
import org.janelia.saalfeldlab.paintera.data.axisorder.AxisOrder;
import org.janelia.saalfeldlab.paintera.state.GlobalTransformManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Wrap a {@link ResizableGridPane2x2} with {@link ViewerPanelFX viewer panels} at top left, top right, and bottom left. Bottom right
Expand Down Expand Up @@ -157,7 +157,7 @@ public ResizableGridPane2x2<ViewerPanelFX, ViewerPanelFX, ViewerPanelFX, BR> gri
*
* @return {@link ResizableGridPane2x2#pane()} for the underlying {@link ResizableGridPane2x2}
*/
public Pane pane()
public GridPane pane()
{
return grid().pane();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;

/**
* A wrapper around {@link GridPane} that holds for children organized in a 2x2 grid. The underlying
Expand Down Expand Up @@ -62,7 +61,7 @@ public ResizableGridPane2x2(
*
* @return underlying {@link GridPane}
*/
public Pane pane()
public GridPane pane()
{
return this.grid;
}
Expand Down Expand Up @@ -149,11 +148,18 @@ public void manage(final GridConstraintsManager manager)
manager.manageGrid(this.grid);
}

public Node getNodeAt(final int col, final int row)
{
for (final Node child : grid.getChildren())
if (GridPane.getColumnIndex(child) == col && GridPane.getRowIndex(child) == row)
return child;
return null;
}

private static void replace(final GridPane grid, final Node oldValue, final Node newValue, final int col, final int
row)
{
grid.getChildren().remove(oldValue);
grid.add(newValue, col, row);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,14 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.*;
import java.util.function.BiConsumer;
import java.util.function.LongSupplier;
import java.util.function.LongUnaryOperator;
import java.util.function.Supplier;

import bdv.fx.viewer.ViewerPanelFX;
import bdv.viewer.Source;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ObservableObjectValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.control.*;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.util.Duration;
import net.imglib2.RealPoint;
import org.janelia.saalfeldlab.fx.TitledPanes;
import org.janelia.saalfeldlab.fx.ortho.OrthogonalViews;
import org.janelia.saalfeldlab.fx.ortho.OrthogonalViews.ViewerAndTransforms;
Expand All @@ -57,6 +37,40 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import bdv.fx.viewer.ViewerPanelFX;
import bdv.viewer.Source;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableObjectValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.control.TitledPane;
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.util.Duration;
import net.imglib2.RealPoint;

public class BorderPaneWithStatusBars
{

Expand Down Expand Up @@ -160,16 +174,29 @@ public BorderPaneWithStatusBars(
center.orthogonalViews(),
center.viewer3D().meshesGroup(),
center.sourceInfo()
);

center.sourceInfo().currentNameProperty().addListener((obs, oldv, newv) -> {
currentSourceStatus.textProperty().unbind();
Optional.ofNullable(newv).ifPresent(currentSourceStatus.textProperty()::bind);
});
);

final SingleChildStackPane sourceDisplayStatus = new SingleChildStackPane();
center.sourceInfo().currentState().addListener((obs, oldv, newv) -> sourceDisplayStatus.setChild(newv.getDisplayStatus()));

// show source name by default, or override it with source status text if any
center.sourceInfo().currentState().addListener((obs, oldv, newv) -> {
sourceDisplayStatus.setChild(newv.getDisplayStatus());
currentSourceStatus.textProperty().unbind();
currentSourceStatus.textProperty().bind(Bindings.createStringBinding(
() -> {
if (newv.statusTextProperty() != null && newv.statusTextProperty().get() != null)
return newv.statusTextProperty().get();
else if (newv.nameProperty().get() != null)
return newv.nameProperty().get();
else
return null;
},
newv.nameProperty(),
newv.statusTextProperty()
));
});

// for positioning the 'show status bar' checkbox on the right
final Region valueStatusSpacing = new Region();
HBox.setHgrow(valueStatusSpacing, Priority.ALWAYS);
Expand Down Expand Up @@ -272,21 +299,25 @@ public BorderPaneWithStatusBars(
this.sideBar.setHbarPolicy(ScrollBarPolicy.NEVER);
this.sideBar.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
this.sideBar.setVisible(true);
this.sideBar.prefWidthProperty().set(250);
this.sideBar.prefWidthProperty().set(280);
sourceTabs.widthProperty().bind(sideBar.prefWidthProperty());
settingsContents.prefWidthProperty().bind(sideBar.prefWidthProperty());

resizeSideBar = new ResizeOnLeftSide(sideBar, sideBar.prefWidthProperty(), dist -> Math.abs(dist) < 5);
}

public ScrollPane getSideBar()
{
return sideBar;
}

public void toggleSideBar()
{
if (pane.getRight() == null)
{
pane.setRight(sideBar);
resizeSideBar.install();
}

else
{
resizeSideBar.remove();
Expand Down
Loading

0 comments on commit 1a7e66c

Please sign in to comment.