Skip to content

Commit

Permalink
Implement SWC type tags from latest SWC specification ( SWC v1.0.0 )
Browse files Browse the repository at this point in the history
In addition:
- Fix non persistence of SWC-type tagging options
- Fix #203
 
The 'new' SWC specification is at
https://swc-specification.readthedocs.io/en/latest/swc.html
  • Loading branch information
tferr committed Nov 22, 2023
1 parent 82cbfd1 commit d5a7845
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 108 deletions.
78 changes: 43 additions & 35 deletions src/main/java/sc/fiji/snt/Path.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,32 +68,33 @@
public class Path implements Comparable<Path> {

// http://www.neuronland.org/NLMorphologyConverter/MorphologyFormats/SWC/Spec.html
/** Flag specifying the SWC type 'undefined'. @see Path#SWC_UNDEFINED_LABEL */

/** SWC type flag specifying {@value #SWC_UNDEFINED_LABEL} */
public static final int SWC_UNDEFINED = 0;
/** Flag specifying the SWC type 'soma'. @see Path#SWC_SOMA_LABEL */
/** SWC type flag specifying {@value #SWC_SOMA_LABEL} */
public static final int SWC_SOMA = 1;
/** Flag specifying the SWC type 'axon'. @see Path#SWC_AXON_LABEL */
/** SWC type flag specifying {@value #SWC_AXON_LABEL} */
public static final int SWC_AXON = 2;
/**
* Flag specifying the SWC type '(basal) dendrite'. @see
* Path#SWC_DENDRITE_LABEL
*/
/** SWC type flag specifying {@value #SWC_DENDRITE_LABEL} */
public static final int SWC_DENDRITE = 3;
/**
* Flag specifying the SWC type 'apical dendrite'. @see
* Path#SWC_APICAL_DENDRITE_LABEL
*/
/** SWC type flag specifying {@value #SWC_APICAL_DENDRITE_LABEL} */
public static final int SWC_APICAL_DENDRITE = 4;
/**
* Flag specifying the SWC type 'fork point' @see Path#SWC_FORK_POINT_LABEL
*/
/** SWC type flag specifying {@value #SWC_CUSTOM_LABEL} */
public static final int SWC_CUSTOM = 5;
/** SWC type flag specifying {@value #SWC_UNSPECIFIED_LABEL} */
public static final int SWC_UNSPECIFIED = 6;
/** SWC type flag specifying {@value #SWC_GLIA_LABEL} */
public static final int SWC_GLIA_PROCESS = 7;
/** SWC type flag specifying {@value #SWC_CUSTOM2_LABEL} */
public static final int SWC_CUSTOM2 = 8;

/** Deprecated. No longer part of the SWC specification */
@Deprecated
public static final int SWC_FORK_POINT = 5; // redundant
public static final int SWC_FORK_POINT = -5;
/** Deprecated. No longer part of the SWC specification */
@Deprecated
/** Flag specifying the SWC type 'end point'. @see Path#SWC_END_POINT_LABEL */
public static final int SWC_END_POINT = 6; // redundant
/** Flag specifying the SWC type 'custom'. @see Path#SWC_CUSTOM_LABEL */
public static final int SWC_CUSTOM = 7;
public static final int SWC_END_POINT = -6;

/** String representation of {@link Path#SWC_UNDEFINED} */
public static final String SWC_UNDEFINED_LABEL = "undefined";
/** String representation of {@link Path#SWC_SOMA} */
Expand All @@ -104,12 +105,14 @@ public class Path implements Comparable<Path> {
public static final String SWC_DENDRITE_LABEL = "(basal) dendrite";
/** String representation of {@link Path#SWC_APICAL_DENDRITE} */
public static final String SWC_APICAL_DENDRITE_LABEL = "apical dendrite";
/** String representation of {@link Path#SWC_FORK_POINT} */
public static final String SWC_FORK_POINT_LABEL = "fork point";
/** String representation of {@link Path#SWC_END_POINT} */
public static final String SWC_END_POINT_LABEL = "end point";
/** String representation of {@link Path#SWC_CUSTOM} */
public static final String SWC_CUSTOM_LABEL = "custom";
/** String representation of {@link Path#SWC_UNSPECIFIED} */
public static final String SWC_UNSPECIFIED_LABEL = "unspecified neurite";
/** String representation of {@link Path#SWC_GLIA_PROCESS} */
public static final String SWC_GLIA_PROCESS_LABEL = "glia process";
/** String representation of {@link Path#SWC_CUSTOM2} */
public static final String SWC_CUSTOM2_LABEL = "custom (2)";

// FIXME: this should be based on distance between points in the path, not a static number:
protected static final int noMoreThanOneEvery = 2;
Expand Down Expand Up @@ -1637,18 +1640,20 @@ public static Color getSWCcolor(final int swcType) {
switch (swcType) {
case Path.SWC_SOMA:
return Color.BLUE;
case Path.SWC_AXON:
return Color.RED;
case Path.SWC_DENDRITE:
return Color.GREEN;
case Path.SWC_APICAL_DENDRITE:
return Color.CYAN;
case Path.SWC_AXON:
return Color.RED;
case Path.SWC_FORK_POINT:
return Color.ORANGE;
case Path.SWC_END_POINT:
return Color.PINK;
case Path.SWC_CUSTOM:
return Color.YELLOW;
case Path.SWC_UNSPECIFIED:
return Color.ORANGE;
case Path.SWC_GLIA_PROCESS:
return Color.PINK;
case Path.SWC_CUSTOM2:
return Color.YELLOW.darker();
case Path.SWC_UNDEFINED:
default:
return SNT.DEFAULT_DESELECTED_COLOR;
Expand Down Expand Up @@ -1852,15 +1857,18 @@ public static String getSWCtypeName(final int type,
case SWC_APICAL_DENDRITE:
typeName = SWC_APICAL_DENDRITE_LABEL;
break;
case SWC_FORK_POINT:
typeName = SWC_FORK_POINT_LABEL;
break;
case SWC_END_POINT:
typeName = SWC_END_POINT_LABEL;
break;
case SWC_CUSTOM:
typeName = SWC_CUSTOM_LABEL;
break;
case SWC_UNSPECIFIED:
typeName = SWC_UNSPECIFIED_LABEL;
break;
case SWC_GLIA_PROCESS:
typeName = SWC_GLIA_PROCESS_LABEL;
break;
case SWC_CUSTOM2:
typeName = SWC_CUSTOM2_LABEL;
break;
case SWC_UNDEFINED:
default:
typeName = SWC_UNDEFINED_LABEL;
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/sc/fiji/snt/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,9 @@ public static Map<Integer, String> getSWCTypeMap() {
map.put(Path.SWC_DENDRITE, Path.SWC_DENDRITE_LABEL);
map.put(Path.SWC_APICAL_DENDRITE, Path.SWC_APICAL_DENDRITE_LABEL);
map.put(Path.SWC_CUSTOM, Path.SWC_CUSTOM_LABEL);
map.put(Path.SWC_UNSPECIFIED, Path.SWC_UNSPECIFIED_LABEL);
map.put(Path.SWC_GLIA_PROCESS, Path.SWC_GLIA_PROCESS_LABEL);
map.put(Path.SWC_CUSTOM2, Path.SWC_CUSTOM2_LABEL);
return map;
}

Expand Down
8 changes: 2 additions & 6 deletions src/main/java/sc/fiji/snt/analysis/RoiConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,9 @@ public List<PolygonRoi> getROIs(final Path path) {
* @see TreeAnalyzer#getTips()
* @param overlay the target overlay to hold converted point
*/
@SuppressWarnings("deprecation")
public void convertTips(Overlay overlay) {
if (overlay == null) overlay = new Overlay();
convertPoints(getTips(), overlay, Path.getSWCcolor(Path.SWC_END_POINT),
Path.SWC_END_POINT_LABEL);
convertPoints(getTips(), overlay, Color.PINK, "end point");
}

/**
Expand Down Expand Up @@ -279,11 +277,9 @@ private Overlay getImpOverlay() throws IllegalArgumentException {
* @see TreeAnalyzer#getBranchPoints()
* @param overlay the target overlay to hold converted point
*/
@SuppressWarnings("deprecation")
public void convertBranchPoints(Overlay overlay) {
if (overlay == null) overlay = new Overlay();
convertPoints(getBranchPoints(), overlay, Path.getSWCcolor(
Path.SWC_FORK_POINT), Path.SWC_FORK_POINT_LABEL);
convertPoints(getBranchPoints(), overlay, Color.ORANGE, "fork point");
}

/**
Expand Down
107 changes: 53 additions & 54 deletions src/main/java/sc/fiji/snt/gui/cmds/SWCTypeOptionsCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@
initializer = "init")
public class SWCTypeOptionsCmd extends ContextCommand {

private static final String HEADER = "<html><body><div style='width:500;'>";
private static final String MAP_KEY = "colors";
private static final String ASSIGN_KEY = "assign";
private static String HEADER = "<HTML><body><div style='width:"
+ GuiUtils.renderedWidth("When color pairing is enable, assigning a") + ";'>";

@Parameter
private PrefService prefService;
Expand All @@ -68,33 +69,40 @@ public class SWCTypeOptionsCmd extends ContextCommand {
@Parameter(required = false, label = " Enable color pairing")
private boolean enableColors;

@Parameter(required = true, persist = false, label = Path.SWC_DENDRITE_LABEL)
@Parameter(persist = false, label = Path.SWC_SOMA_LABEL)
private ColorRGB somaColor;

@Parameter(persist = false, label = Path.SWC_AXON_LABEL)
private ColorRGB axonColor;

@Parameter(persist = false, label = Path.SWC_DENDRITE_LABEL)
private ColorRGB basalDendriteColor;

@Parameter(required = true, persist = false,
label = Path.SWC_APICAL_DENDRITE_LABEL)
@Parameter(persist = false, label = Path.SWC_APICAL_DENDRITE_LABEL)
private ColorRGB apicalDendriteColor;

@Parameter(required = true, persist = false, label = Path.SWC_AXON_LABEL)
private ColorRGB axonColor;
@Parameter(persist = false, label = Path.SWC_UNDEFINED_LABEL)
private ColorRGB undefinedColor;

@Parameter(required = true, persist = false, label = Path.SWC_CUSTOM_LABEL)
private ColorRGB customColor;
@Parameter(persist = false, label = Path.SWC_UNSPECIFIED_LABEL)
private ColorRGB unspecifiedColor;

@Parameter(required = true, label = Path.SWC_SOMA_LABEL)
private ColorRGB somaColor;
@Parameter(persist = false, label = Path.SWC_GLIA_PROCESS_LABEL)
private ColorRGB gliaColor;

@Parameter(required = true, persist = false, label = Path.SWC_UNDEFINED_LABEL)
private ColorRGB undefinedColor;
@Parameter(persist = false, label = Path.SWC_CUSTOM_LABEL)
private ColorRGB customColor;

@Parameter(required = false, persist = false, label = "Reset Defaults",
callback = "reset")
@Parameter(persist = false, label = Path.SWC_CUSTOM2_LABEL)
private ColorRGB custom2Color;

@Parameter(required = false, persist = false, label = "Reset Defaults", callback = "reset")
private Button reset;

@Parameter(visibility = ItemVisibility.MESSAGE)
private final String msg = HEADER + "When <i>color pairing</i> is enabled, " +
"assigning a <i>SWC-type</i> tag automaticaly colors the path " +
"with the respective listed color. Note that it is also possible " +
"with its associated color. Note that it is also possible " +
"to assign ad-hoc colors using the <i>Tag>Color></i> menu.";

/*
Expand All @@ -104,54 +112,47 @@ public class SWCTypeOptionsCmd extends ContextCommand {
*/
@Override
public void run() {
final LinkedHashMap<String, String> map =
new LinkedHashMap<>();
map.put(String.valueOf(Path.SWC_DENDRITE), basalDendriteColor
.toHTMLColor());
map.put(String.valueOf(Path.SWC_APICAL_DENDRITE), apicalDendriteColor
.toHTMLColor());
map.put(String.valueOf(Path.SWC_AXON), axonColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_CUSTOM), customColor.toHTMLColor());
final LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put(String.valueOf(Path.SWC_SOMA), somaColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_AXON), axonColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_DENDRITE), basalDendriteColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_APICAL_DENDRITE), apicalDendriteColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_UNDEFINED), undefinedColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_UNSPECIFIED), unspecifiedColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_GLIA_PROCESS), gliaColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_CUSTOM), customColor.toHTMLColor());
map.put(String.valueOf(Path.SWC_CUSTOM2), custom2Color.toHTMLColor());
prefService.put(SWCTypeOptionsCmd.class, MAP_KEY, map);
prefService.put(SWCTypeOptionsCmd.class, ASSIGN_KEY, enableColors);
}

private Map<Integer, ColorRGB> getDefaultMap() {
final LinkedHashMap<Integer, ColorRGB> map =
new LinkedHashMap<>();
map.put(Path.SWC_DENDRITE, getDefaultSWCColorRGB(Path.SWC_DENDRITE));
map.put(Path.SWC_APICAL_DENDRITE, getDefaultSWCColorRGB(
Path.SWC_APICAL_DENDRITE));
map.put(Path.SWC_AXON, getDefaultSWCColorRGB(Path.SWC_AXON));
map.put(Path.SWC_CUSTOM, getDefaultSWCColorRGB(Path.SWC_CUSTOM));
final LinkedHashMap<Integer, ColorRGB> map = new LinkedHashMap<>();
map.put(Path.SWC_SOMA, getDefaultSWCColorRGB(Path.SWC_SOMA));
map.put(Path.SWC_AXON, getDefaultSWCColorRGB(Path.SWC_AXON));
map.put(Path.SWC_DENDRITE, getDefaultSWCColorRGB(Path.SWC_DENDRITE));
map.put(Path.SWC_APICAL_DENDRITE, getDefaultSWCColorRGB(Path.SWC_APICAL_DENDRITE));
map.put(Path.SWC_UNDEFINED, getDefaultSWCColorRGB(Path.SWC_UNDEFINED));
map.put(Path.SWC_UNSPECIFIED, getDefaultSWCColorRGB(Path.SWC_UNSPECIFIED));
map.put(Path.SWC_GLIA_PROCESS, getDefaultSWCColorRGB(Path.SWC_GLIA_PROCESS));
map.put(Path.SWC_CUSTOM, getDefaultSWCColorRGB(Path.SWC_CUSTOM));
map.put(Path.SWC_CUSTOM2, getDefaultSWCColorRGB(Path.SWC_CUSTOM2));
return map;
}

private Map<Integer, ColorRGB> getSavedMap() {
final Map<String, String> smap = prefService.getMap(SWCTypeOptionsCmd.class,
MAP_KEY);
if (smap == null || smap.isEmpty()) {
final Map<String, String> smap = prefService.getMap(SWCTypeOptionsCmd.class, MAP_KEY);
if (smap == null || smap.isEmpty() || smap.size() < 9) { // only 6 colors before v4.3
return getDefaultMap();
}
final LinkedHashMap<Integer, ColorRGB> map =
new LinkedHashMap<>();
for (final Map.Entry<String, String> entry : smap.entrySet()) {
final String key = entry.getKey();
final String value = entry.getValue();
map.put(Integer.valueOf(key), ColorRGB.fromHTMLColor(value));
}
// while at it, read other preferences
enableColors = prefService.getBoolean(SWCTypeOptionsCmd.class, ASSIGN_KEY,
true);
final LinkedHashMap<Integer, ColorRGB> map = new LinkedHashMap<>();
smap.forEach((k, v) -> map.put(Integer.valueOf(k), ColorRGB.fromHTMLColor(v)));
return map;
}

@SuppressWarnings("unused")
private void init() {
enableColors = isColorPairingEnabled();
assignColors(getSavedMap());
}

Expand All @@ -163,12 +164,15 @@ private void reset() {
}

private void assignColors(final Map<Integer, ColorRGB> map) {
apicalDendriteColor = map.get(Path.SWC_APICAL_DENDRITE);
somaColor = map.get(Path.SWC_SOMA);
axonColor = map.get(Path.SWC_AXON);
basalDendriteColor = map.get(Path.SWC_DENDRITE);
customColor = map.get(Path.SWC_CUSTOM);
somaColor = map.get(Path.SWC_SOMA);
apicalDendriteColor = map.get(Path.SWC_APICAL_DENDRITE);
undefinedColor = map.get(Path.SWC_UNDEFINED);
unspecifiedColor = map.get(Path.SWC_UNSPECIFIED);
gliaColor = map.get(Path.SWC_GLIA_PROCESS);
customColor = map.get(Path.SWC_CUSTOM);
custom2Color = map.get(Path.SWC_CUSTOM2);
}

private static class SWCTypeComparator implements Comparator<Integer> {
Expand All @@ -183,18 +187,13 @@ public int compare(final Integer i1, final Integer i2) {

public TreeMap<Integer, Color> getColorMap() {
final Map<Integer, ColorRGB> maprgb = getSavedMap();
final TreeMap<Integer, Color> map = new TreeMap<>(
new SWCTypeComparator()); // new SWCTypeComparator());
for (final Map.Entry<Integer, ColorRGB> entry : maprgb.entrySet()) {
final int key = entry.getKey();
final ColorRGB color = entry.getValue();
map.put(key, getColorFromColorRGB(color));
}
final TreeMap<Integer, Color> map = new TreeMap<>(new SWCTypeComparator());
maprgb.forEach((k,v) -> map.put(k, getColorFromColorRGB(v)));
return map;
}

public boolean isColorPairingEnabled() {
return enableColors;
return prefService.getBoolean(SWCTypeOptionsCmd.class, ASSIGN_KEY, true);
}

private Color getColorFromColorRGB(final ColorRGB c) {
Expand Down
25 changes: 12 additions & 13 deletions src/main/java/sc/fiji/snt/util/SWCPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,22 @@ public int hashCode() {
/**
* Converts a collection of SWC points into a Reader.
*
* @param points the collection of SWC points to be converted into a space/
* tab separated String. Points should be sorted by sample number to
* ensure valid connectivity.
* @param points the collection of SWC points to be converted into a space
* separated String. Points should be sorted by sample number to
* ensure valid connectivity.
* @return the Reader
*/
public static StringReader collectionAsReader(
final Collection<SWCPoint> points)
{
public static StringReader collectionAsReader(final Collection<SWCPoint> points) {
final String SEP = " ";
final StringBuilder sb = new StringBuilder();
for (final SWCPoint p : points) {
sb.append(p.id).append("\t") //
.append(p.type).append("\t") // see https://github.com/morphonets/SNT/issues/147
.append(String.format(Locale.US, "%.6f", p.x)).append(" ") //
.append(String.format(Locale.US, "%.6f", p.y)).append(" ") //
.append(String.format(Locale.US, "%.6f", p.z)).append(" ") //
.append(String.format(Locale.US, "%.6f", p.radius)).append("\t") //
.append(p.parent).append(System.lineSeparator());
sb.append(p.id).append(SEP) //
.append(p.type).append(SEP) // see https://github.com/morphonets/SNT/issues/147
.append(String.format(Locale.US, "%.6f", p.x)).append(SEP) //
.append(String.format(Locale.US, "%.6f", p.y)).append(SEP) //
.append(String.format(Locale.US, "%.6f", p.z)).append(SEP) //
.append(String.format(Locale.US, "%.6f", p.radius)).append(SEP) //
.append(p.parent).append(System.lineSeparator());
}
return new StringReader(sb.toString());
}
Expand Down

0 comments on commit d5a7845

Please sign in to comment.