Skip to content

Commit

Permalink
DuplicateCmd: Allow duplication of all children
Browse files Browse the repository at this point in the history
  • Loading branch information
tferr committed Jan 22, 2024
1 parent 4670b31 commit 3ee5bbf
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 51 deletions.
2 changes: 1 addition & 1 deletion src/main/java/sc/fiji/snt/PathManagerUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ private JMenuItem getDuplicateMenuItem(final SinglePathActionListener singlePath
SinglePathActionListener.DUPLICATE_CMD);
duplicateMitem.setIcon(IconFactory.getMenuIcon(IconFactory.GLYPH.CLONE));
duplicateMitem.addActionListener(singlePathListener);
duplicateMitem.setToolTipText("Duplicates a single path");
duplicateMitem.setToolTipText("Duplicates a full path and its children or a sub-section");
return duplicateMitem;
}

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/sc/fiji/snt/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -1640,8 +1640,10 @@ public Tree clone() {
if (path.getStartJoins() == null) continue;
final Path join = idToPathMap.get(path.getStartJoins().getID());
final PointInImage joinPoint = path.getStartJoinsPoint().clone();
path.unsetStartJoin();
path.setStartJoin(join, joinPoint);
if (join != null && joinPoint != null) {
path.unsetStartJoin();
path.setStartJoin(join, joinPoint);
}
}
return clone;
}
Expand Down
133 changes: 86 additions & 47 deletions src/main/java/sc/fiji/snt/gui/cmds/DuplicateCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@

package sc.fiji.snt.gui.cmds;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;

import org.scijava.ItemVisibility;
Expand All @@ -35,6 +37,7 @@
import net.imagej.ImageJ;
import sc.fiji.snt.Path;
import sc.fiji.snt.SNTService;
import sc.fiji.snt.Tree;

/**
* GUI command for duplicating a Path.
Expand All @@ -44,42 +47,45 @@
@Plugin(type = Command.class, label = "Duplicate Path...", initializer = "init")
public class DuplicateCmd extends CommonDynamicCmd {

private final String CHOICE_FULL_LENGTH = "Full length";
private final String CHOICE_LENGTH = "Use % specified below";
private final String CHOICE_FIRST_BP = "Duplicate until first branch point";
private final String CHOICE_LAST_BP = "Duplicate until last branch point";
private final String CHOICE_NO_CHILDREN = "Do not duplicate child paths";
private final String CHOICE_IMMEDIATE_CHILDREN = "Duplicate immediate children only";
private final String CHOICE_ALL_CHILDREN = "Duplicate all children";

@Parameter(label = "Portion to be duplicated", callback = "portionChoiceChanged",
choices = {CHOICE_LENGTH, CHOICE_FIRST_BP, CHOICE_LAST_BP })
choices = {CHOICE_FULL_LENGTH, CHOICE_LENGTH, CHOICE_FIRST_BP, CHOICE_LAST_BP })
private String portionChoice;

@Parameter(label = "<HTML>&nbsp;", min = "0", max = "100",
style = NumberWidget.SCROLL_BAR_STYLE, callback = "percentageChanged")
private double percentage;

@Parameter(label = "Children", callback = "childrenChoiceChanged",
choices = { CHOICE_NO_CHILDREN, CHOICE_IMMEDIATE_CHILDREN, CHOICE_ALL_CHILDREN },
description = "NB: If choice includes children, The entire group will be duplicated at full length.")
private String childrenChoice;

@Parameter(persist = false, label = "Assign to channel", min="1")
private int channel;

@Parameter(persist = false, label = "Assign to frame", min="1")
private int frame;

@Parameter(label = "Duplicate immediate children", callback = "dupChildrenChanged",
description = "If selected, direct children will also be duplicated. The entire group will be duplicated at full length.")
private boolean dupChildren;

@Parameter(label = "Make primary (disconnect)",
description="If selected, duplicate path will become primary (root path)")
private boolean disconnect;

@Parameter(label = "<HTML>&nbsp", persist = false, required = false, visibility = ItemVisibility.MESSAGE)
private String SPACER;

@Parameter(required = false, persist = false, visibility = ItemVisibility.MESSAGE)
private String msg = "";

@Parameter(required = true, persist = false)
private Path path;

private double length;
private boolean dupChildren;
private TreeSet<Integer> junctionIndices;

@SuppressWarnings("unused")
Expand All @@ -94,8 +100,8 @@ private void init() {
getInfo().getMutableInput("disconnect", Boolean.class).setLabel("");
}
if (path.getChildren() == null || path.getChildren().isEmpty()) {
resolveInput("dupChildren");
getInfo().getMutableInput("dupChildren", Boolean.class).setLabel("");
resolveInput("childrenChoice");
getInfo().getMutableInput("childrenChoice", String.class).setLabel("");
}
if (path.size() == 1) {
percentage = 100;
Expand All @@ -112,57 +118,63 @@ private void init() {
}

private void portionChoiceChanged() {
percentage = (CHOICE_LENGTH.equals(portionChoice)) ? 100 : 0;
if (percentage != 100d) dupChildren = false;
updateMsg();
switch (portionChoice) {
case CHOICE_FULL_LENGTH:
percentage = 100d;
break;
case CHOICE_FIRST_BP:
case CHOICE_LAST_BP:
percentage = 0d;
break;
default:
percentage = 50d;
}
updatePrompt();
}

@SuppressWarnings("unused")
private void dupChildrenChanged() {
private void childrenChoiceChanged() {
dupChildren = !CHOICE_NO_CHILDREN.equals(childrenChoice);
if (dupChildren) {
portionChoice = CHOICE_LENGTH;
portionChoice = CHOICE_FULL_LENGTH;
percentage = 100;
portionChoiceChanged();
}
}

@SuppressWarnings("unused")
private void percentageChanged() {
portionChoice = (percentage==0d) ? CHOICE_FIRST_BP : CHOICE_LENGTH;
if (percentage != 100d) dupChildren = false;
updateMsg();
if (percentage == 0d)
portionChoice = CHOICE_FIRST_BP;
else if (percentage == 100d)
portionChoice = CHOICE_FULL_LENGTH;
else
portionChoice = CHOICE_LENGTH;
updatePrompt();
}

private void updateMsg() {
private void updatePrompt() {
switch((int)percentage) {
case 100:
msg = "Duplicating full length";
dupChildren = childrenChoice != null && !CHOICE_NO_CHILDREN.equals(childrenChoice);
break;
case 0:
if (junctionIndices == null || junctionIndices.isEmpty())
msg = "Invalid choice: Path has no branch points!";
else
msg = "...";
dupChildren = false;
break;
default:
msg = String.format("Aprox. length: %.2f", percentage / 100 * length);
dupChildren = false;
break;
}
if (!dupChildren)
childrenChoice = CHOICE_NO_CHILDREN;
}

private int getNthJunction(final int desiredIndex) {
final Iterator<Integer> itr = junctionIndices.iterator();
int currentIndex = 0;
int currentElement = 0;
while (itr.hasNext()) {
currentElement = itr.next();
if (currentIndex == desiredIndex) {
return currentElement;
}
currentIndex++;
}
return 0;
}

/*
* (non-Javadoc)
Expand All @@ -172,16 +184,15 @@ private int getNthJunction(final int desiredIndex) {
@Override
public void run() {

updatePrompt(); // ensure options are up-to-date
if (dupChildren && path.getChildren() != null && !path.getChildren().isEmpty()) {

final Path dup = path.clone(true);
connectToAncestorAsNeeded(dup);
setPropertiesAndAdd(dup, path);
int idx = 0;
for (final Path child : path.getChildren()) {
setPropertiesAndAdd(dup.getChildren().get(idx), child);
idx++;
}
final boolean recursive = CHOICE_ALL_CHILDREN.equals(childrenChoice);
final Tree dupTree = new Tree(getPathsToDuplicate(recursive)).clone();
dupTree.list().forEach(dupPath -> dupPath.setCTposition(Math.max(1, channel), Math.max(1, frame)));
final Path dupParentPath = dupTree.list().get(0);
connectToAncestorAsNeeded(dupParentPath);
snt.getPathAndFillManager().addTree(dupTree, "DUP");

} else {

Expand All @@ -201,6 +212,9 @@ public void run() {
dupIndex = junctionIndices.last();
}
break;
case CHOICE_FULL_LENGTH:
dupIndex = path.size() - 1;
break;
default:
dupIndex = (int) Math.round(percentage / 100 * path.size());
break;
Expand All @@ -217,33 +231,58 @@ public void run() {
// Now make the duplication and add to manager
final Path dup = path.getSection(0, dupIndex);
connectToAncestorAsNeeded(dup);
setPropertiesAndAdd(dup, path);
dup.setName("Dup " + dup.getName());
dup.setCTposition(Math.max(1, channel), Math.max(1, frame));
snt.getPathAndFillManager().addPath(dup, false, true);

}

resetUI();
}

private void connectToAncestorAsNeeded(final Path dup) {

// Disconnect duplicated path by default
if (dup.getStartJoins() != null) dup.unsetStartJoin();

// Now connect it to the parent of the original path if requested. Do nothing if original path has no parent
if (!path.isPrimary() && !disconnect) {
if (path.getStartJoins() != null) {
dup.setStartJoin(path.getStartJoins(), path.getStartJoinsPoint());
}
}
}

private int getNthJunction(final int desiredIndex) {
final Iterator<Integer> itr = junctionIndices.iterator();
int currentIndex = 0;
int currentElement = 0;
while (itr.hasNext()) {
currentElement = itr.next();
if (currentIndex == desiredIndex) {
return currentElement;
}
currentIndex++;
}
return 0;
}

private void setPropertiesAndAdd(final Path dup, final Path original) {
dup.setName("Dup " + original.getName());
dup.setCTposition(Math.max(1, channel), Math.max(1, frame));
snt.getPathAndFillManager().addPath(dup, false, true);
private List<Path> getPathsToDuplicate(final boolean allChildren) {
final List<Path> pathsToClone = new ArrayList<>();
pathsToClone.add(path);
appendChildren(pathsToClone, path, allChildren);
return pathsToClone;
}

private void appendChildren(final List<Path> set, final Path p, final boolean recursive) {
if (p.getChildren() != null && !p.getChildren().isEmpty()) {
p.getChildren().forEach(child -> {
set.add(child);
if (recursive)
appendChildren(set, child, recursive);
});
}
}


/* IDE debug method **/
public static void main(final String[] args) {
final ImageJ ij = new ImageJ();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/sc/fiji/snt/plugin/PathMatcherCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public class PathMatcherCmd extends CommonDynamicCmd {
persist = false, required = false, visibility = ItemVisibility.MESSAGE)
private String SPACER4;

@Parameter(label = "Assign unique colors to groups", description="<HTML><div WIDTH=500>"
@Parameter(label = "Assign contrast colors to groups", description="<HTML><div WIDTH=500>"
+ "Whether paths matched to the same neurite should be assigned a common color.")
private boolean assignUniqueColors;

Expand Down

0 comments on commit 3ee5bbf

Please sign in to comment.