diff --git a/src/main/java/org/mastodon/mamut/tomancak/TomancakPlugins.java b/src/main/java/org/mastodon/mamut/tomancak/TomancakPlugins.java index 03eb1903..4f7dd54d 100644 --- a/src/main/java/org/mastodon/mamut/tomancak/TomancakPlugins.java +++ b/src/main/java/org/mastodon/mamut/tomancak/TomancakPlugins.java @@ -248,8 +248,8 @@ public List< ViewMenuBuilder.MenuItem > getMenuItems() { return Arrays.asList( menu( "Plugins", - item( COPY_TAG ), - item( MIRROR_SPOTS ), + menu( "Tags", + item( COPY_TAG ) ), menu( "Auxiliary Displays", item( COMPACT_LINEAGE_VIEW ) ), menu( "Trees Management", @@ -266,10 +266,11 @@ public List< ViewMenuBuilder.MenuItem > getMenuItems() menu( "Exports", item( EXPORTS_LINEAGE_LENGTHS ), item( EXPORTS_SPOTS_COUNTS ), - item( EXPORT_PHYLOXML ) ) ), + item( EXPORT_PHYLOXML ) ), + item( MIRROR_SPOTS ) ), menu( "File", item( TWEAK_DATASET_PATH ), - item( MERGE_PROJECTS )) ); + item( MERGE_PROJECTS ) ) ); } @Override diff --git a/src/main/java/org/mastodon/mamut/tomancak/divisiontagset/CellDivisionsTagSetCommand.java b/src/main/java/org/mastodon/mamut/tomancak/divisiontagset/CellDivisionsTagSetCommand.java new file mode 100644 index 00000000..924b5666 --- /dev/null +++ b/src/main/java/org/mastodon/mamut/tomancak/divisiontagset/CellDivisionsTagSetCommand.java @@ -0,0 +1,170 @@ +/*- + * #%L + * mastodon-tomancak + * %% + * Copyright (C) 2018 - 2024 Tobias Pietzsch + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package org.mastodon.mamut.tomancak.divisiontagset; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.apache.commons.lang3.tuple.Pair; +import org.mastodon.mamut.ProjectModel; +import org.mastodon.mamut.model.Model; +import org.mastodon.mamut.model.Spot; +import org.mastodon.mamut.model.branch.BranchSpot; +import org.mastodon.mamut.model.branch.ModelBranchGraph; +import org.mastodon.model.tag.TagSetStructure; +import org.mastodon.util.ColorUtils; +import org.mastodon.util.TagSetUtils; +import org.scijava.Cancelable; +import org.scijava.command.Command; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; +import org.scijava.util.ColorRGB; + +/** + * {@link Command A SciJava command} that is called by the {@link CellDivisionsTagSetPlugin}. + *

+ * The command creates a tag set that highlights cell divisions. The user interface allows to + * specify the number of spots to highlight before and after a division, and the highlight and + * background colors. + */ +@Plugin( type = Command.class, label = "Add a Tag Set to Highlight Cell Divisions", visible = false ) +public class CellDivisionsTagSetCommand implements Command, Cancelable +{ + @Parameter + private ProjectModel projectModel; + + @Parameter( label = "Tag set name" ) + private String tagSetName = "cell divisions"; + + @Parameter( label = "Default color" ) + private ColorRGB defaultColor = new ColorRGB( "dark gray" ); + + @Parameter( label = "Highlight color" ) + private ColorRGB highlightColor = new ColorRGB( "pink" ); + + @Parameter( label = "Timepoints to highlight before and after cell division" ) + private int n = 3; + + private List< TagSetStructure.Tag > tagsBefore; + + private List< TagSetStructure.Tag > tagsAfter; + + private TagSetStructure.Tag backgroundTag; + + private Model model; + + @Override + public void run() + { + projectModel.getBranchGraphSync().sync(); + model = projectModel.getModel(); + createTagSet(); + applyTags(); + } + + private void createTagSet() + { + int defaultColor = this.defaultColor.getARGB(); + int highlightColor = this.highlightColor.getARGB(); + List< Pair< String, Integer > > tagColors = new ArrayList<>( Collections.nCopies( 2 * n + 1, null ) ); + tagColors.set( 0, Pair.of( "default", defaultColor ) ); + for ( int i = 0; i < n; i++ ) + { + int color = ColorUtils.mixColors( highlightColor, defaultColor, ( float ) i / n ); + String label = "T-" + ( i + 1 ); + tagColors.set( n - i, Pair.of( label, color ) ); + } + for ( int i = 0; i < n; i++ ) + { + int color = ColorUtils.mixColors( highlightColor, defaultColor, ( float ) i / n ); + String label = "T" + ( i + 1 ); + tagColors.set( n + i + 1, Pair.of( label, color ) ); + } + TagSetStructure.TagSet tagset = TagSetUtils.addNewTagSetToModel( model, tagSetName, tagColors ); + List< TagSetStructure.Tag > allTags = tagset.getTags(); + backgroundTag = allTags.get( 0 ); + tagsBefore = IntStream.range( 0, n ).mapToObj( i -> allTags.get( n - i ) ).collect( Collectors.toList() ); + tagsAfter = IntStream.range( 0, n ).mapToObj( i -> allTags.get( n + i + 1 ) ).collect( Collectors.toList() ); + } + + private void applyTags() + { + ModelBranchGraph branchGraph = model.getBranchGraph(); + for ( BranchSpot branch : branchGraph.vertices() ) + { + int start = branch.getFirstTimePoint(); + int end = branch.getTimepoint(); + boolean dividesBefore = !branch.incomingEdges().isEmpty(); + boolean dividesAfter = !branch.outgoingEdges().isEmpty(); + Iterator< Spot > iterator = branchGraph.vertexBranchIterator( branch ); + while ( iterator.hasNext() ) + { + Spot spot = iterator.next(); + int time = spot.getTimepoint(); + int startOffset = time - start; + int endOffset = end - time; + TagSetStructure.Tag tag = getTag( dividesBefore, dividesAfter, startOffset, endOffset ); + model.getTagSetModel().getVertexTags().set( spot, tag ); + } + branchGraph.releaseIterator( iterator ); + } + } + + private TagSetStructure.Tag getTag( boolean dividesBefore, + boolean dividesAfter, int startOffset, int endOffset ) + { + if ( dividesAfter && ( endOffset < tagsBefore.size() ) ) + return tagsBefore.get( endOffset ); + if ( dividesBefore && ( startOffset < tagsAfter.size() ) ) + return tagsAfter.get( startOffset ); + return backgroundTag; + } + + @Override + public boolean isCanceled() + { + return false; + } + + @Override + public void cancel( String reason ) + { + + } + + @Override + public String getCancelReason() + { + return null; + } +} diff --git a/src/main/java/org/mastodon/mamut/tomancak/divisiontagset/CellDivisionsTagSetPlugin.java b/src/main/java/org/mastodon/mamut/tomancak/divisiontagset/CellDivisionsTagSetPlugin.java new file mode 100644 index 00000000..08bd6643 --- /dev/null +++ b/src/main/java/org/mastodon/mamut/tomancak/divisiontagset/CellDivisionsTagSetPlugin.java @@ -0,0 +1,132 @@ +/*- + * #%L + * mastodon-tomancak + * %% + * Copyright (C) 2018 - 2024 Tobias Pietzsch + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package org.mastodon.mamut.tomancak.divisiontagset; + +import static org.mastodon.app.ui.ViewMenuBuilder.item; +import static org.mastodon.app.ui.ViewMenuBuilder.menu; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.mastodon.app.ui.ViewMenuBuilder; +import org.mastodon.mamut.KeyConfigScopes; +import org.mastodon.mamut.ProjectModel; +import org.mastodon.mamut.plugin.MamutPlugin; +import org.mastodon.ui.keymap.KeyConfigContexts; +import org.scijava.command.CommandService; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; +import org.scijava.ui.behaviour.io.gui.CommandDescriptionProvider; +import org.scijava.ui.behaviour.io.gui.CommandDescriptions; +import org.scijava.ui.behaviour.util.AbstractNamedAction; +import org.scijava.ui.behaviour.util.Actions; +import org.scijava.ui.behaviour.util.RunnableAction; + +/** + * A Mastodon plugin that adds a tag set for highlighting cell divisions. + *

+ * This class executes the {@link CellDivisionsTagSetCommand} when the + * menu item is clicked. + */ +@Plugin( type = MamutPlugin.class ) +public class CellDivisionsTagSetPlugin implements MamutPlugin +{ + @Parameter + private CommandService commandService; + + private static final String ID = "[tomancak] create cell divisions tag set"; + + private static final String[] KEYS = { "not mapped" }; + + private static final Map< String, String > menuTexts = Collections.singletonMap( ID, "Add Tag Set to Highlight Cell Divisions ..." ); + + private ProjectModel projectModel = null; + + @Plugin( type = CommandDescriptionProvider.class ) + public static class Descriptions extends CommandDescriptionProvider + { + public Descriptions() + { + super( KeyConfigScopes.MAMUT, KeyConfigContexts.TRACKSCHEME, KeyConfigContexts.BIGDATAVIEWER ); + } + + @Override + public void getCommandDescriptions( CommandDescriptions descriptions ) + { + descriptions.add( ID, KEYS, "Adds a tag set to highlight cell divisions." ); + } + } + + private final AbstractNamedAction action; + + public CellDivisionsTagSetPlugin() + { + action = new RunnableAction( ID, () -> { + if ( projectModel != null ) + run(); + } ); + updateEnabledActions(); + } + + @Override + public void setAppPluginModel( ProjectModel projectModel ) + { + this.projectModel = projectModel; + updateEnabledActions(); + } + + private void updateEnabledActions() + { + action.setEnabled( projectModel != null ); + } + + @Override + public List< ViewMenuBuilder.MenuItem > getMenuItems() + { + return Collections.singletonList( menu( "Plugins", menu( "Tags", item( ID ) ) ) ); + } + + @Override + public Map< String, String > getMenuTexts() + { + return menuTexts; + } + + @Override + public void installGlobalActions( Actions actions ) + { + actions.namedAction( action, KEYS ); + } + + private void run() + { + commandService.run( CellDivisionsTagSetCommand.class, true, "projectModel", projectModel ); + } +}