diff --git a/.gitignore b/.gitignore
index 3d47f986c41db29ec6dc0d5036bf760b3a1cf366..57f16fb67c1b1589981416b323d7a9debc728665 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,41 @@
-.idea/
+/build*
+/workspace
+setting.xml
+release/
 target/
-.settings/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+icy.log
+
+### IntelliJ IDEA ###
+.idea/
+*.iws
 *.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
 .project
-.classpath
\ No newline at end of file
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+**/.DS_Store
+Icon?
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b2ac9bb9ab412940e18222b5c0b6d2155c1eef41..68dd4b727d41c21cb3019e6c8a508ebaba688b96 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,23 +6,21 @@
 
     <!-- Inherited Icy Parent POM -->
     <parent>
-		<artifactId>pom-icy</artifactId>
+        <artifactId>pom-icy</artifactId>
         <groupId>org.bioimageanalysis.icy</groupId>
-        <version>2.0.0</version>
-	</parent>
+        <version>3.0.0-a.1</version>
+    </parent>
 
     <!-- Project Information -->
     <artifactId>track-painter</artifactId>
-    <version>2.2.1</version>
-
-    <packaging>jar</packaging>
+    <version>3.0.0-a.1</version>
 
     <name>Track Painter</name>
     <description>
         Track Manager plugin that displays available tracks on the selected sequence, in both 2D and 3D viewers, with various visualisation
         options. Tracks never looked so good!
     </description>
-    <url>http://icy.bioimageanalysis.org/plugin/track-painter/</url>
+    <url>https://icy.bioimageanalysis.org/plugin/track-painter/</url>
     <inceptionYear>2020</inceptionYear>
 
     <organization>
@@ -56,39 +54,23 @@
         </developer>
     </developers>
 
-    <!-- Project properties -->
-    <properties>
-
-    </properties>
-
-    <profiles>
-        <profile>
-            <id>icy-plugin</id>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-    </profiles>
-
-    <!-- List of project's dependencies -->
     <dependencies>
-        <!-- The core of Icy -->
         <dependency>
             <groupId>org.bioimageanalysis.icy</groupId>
-            <artifactId>icy-kernel</artifactId>
-			<version>${icy-kernel.version}</version>
+            <artifactId>ezplug</artifactId>
         </dependency>
-		
-		<dependency>
+
+        <dependency>
             <groupId>org.bioimageanalysis.icy</groupId>
-            <artifactId>icy-vtk</artifactId>
-			<version>${icy-vtk.version}</version>
+            <artifactId>spot-detection-utilities</artifactId>
         </dependency>
-
         <dependency>
             <groupId>org.bioimageanalysis.icy</groupId>
             <artifactId>track-manager</artifactId>
-			<version>${track-manager.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bioimageanalysis.icy</groupId>
+            <artifactId>vecmath</artifactId>
         </dependency>
     </dependencies>
 
@@ -96,8 +78,7 @@
     <repositories>
         <repository>
             <id>icy</id>
-            <name>Icy's Nexus</name>
-            <url>https://icy-nexus.pasteur.fr/repository/Icy/</url>
+            <url>https://nexus-icy.pasteur.cloud/repository/icy/</url>
         </repository>
     </repositories>
 </project>
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$1.class b/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$1.class
deleted file mode 100644
index eb6281a50c89ae7fad5d7bd157e72eaca215bf47..0000000000000000000000000000000000000000
Binary files a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$1.class and /dev/null differ
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$2.class b/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$2.class
deleted file mode 100644
index 12df67be3bdcbd8aea76960581666b999dd539e7..0000000000000000000000000000000000000000
Binary files a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$2.class and /dev/null differ
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$3.class b/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$3.class
deleted file mode 100644
index 64de8e96805093b8f6d8f660db86941cb3ef52e8..0000000000000000000000000000000000000000
Binary files a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$3.class and /dev/null differ
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$4.class b/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$4.class
deleted file mode 100644
index 7b1497261f426535cfe5942d43016f37fc73a535..0000000000000000000000000000000000000000
Binary files a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$4.class and /dev/null differ
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$5.class b/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$5.class
deleted file mode 100644
index 8e4a2a969e26241a7c08063ed838303688d3c275..0000000000000000000000000000000000000000
Binary files a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay$5.class and /dev/null differ
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay.class b/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay.class
deleted file mode 100644
index 7fbca38c9758049c2a618310ecf5ef7642844dde..0000000000000000000000000000000000000000
Binary files a/src/main/java/plugins/adufour/trackprocessors/TrackPainter$TrackOverlay.class and /dev/null differ
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter.class b/src/main/java/plugins/adufour/trackprocessors/TrackPainter.class
deleted file mode 100644
index bdd3ca266783068259e3fa5acca8cb930244d83f..0000000000000000000000000000000000000000
Binary files a/src/main/java/plugins/adufour/trackprocessors/TrackPainter.class and /dev/null differ
diff --git a/src/main/java/plugins/adufour/trackprocessors/TrackPainter.java b/src/main/java/plugins/adufour/trackprocessors/TrackPainter.java
index 1d432ee11f158157e6c0f271e492c2924b0babea..8fd7a828a78f9c778a00655bbce876c9fbd04f2e 100644
--- a/src/main/java/plugins/adufour/trackprocessors/TrackPainter.java
+++ b/src/main/java/plugins/adufour/trackprocessors/TrackPainter.java
@@ -1,34 +1,34 @@
-package plugins.adufour.trackprocessors;
-
-import java.awt.BasicStroke;
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseWheelEvent;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.Line2D;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.TreeMap;
+/*
+ * Copyright (c) 2010-2024. Institut Pasteur.
+ *
+ * This file is part of Icy.
+ * Icy is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Icy is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Icy. If not, see <https://www.gnu.org/licenses/>.
+ */
 
-import javax.swing.BoxLayout;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.border.TitledBorder;
-import javax.vecmath.Point2d;
-import javax.vecmath.Point3d;
+package plugins.adufour.trackprocessors;
 
-import icy.canvas.IcyCanvas;
-import icy.canvas.IcyCanvas2D;
-import icy.painter.Overlay;
-import icy.painter.OverlayWrapper;
-import icy.painter.VtkPainter;
-import icy.sequence.Sequence;
-import icy.type.point.Point5D;
+import org.bioimageanalysis.icy.common.geom.point.Point5D;
+import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginIcon;
+import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginName;
+import org.bioimageanalysis.icy.gui.canvas.IcyCanvas;
+import org.bioimageanalysis.icy.gui.canvas.IcyCanvas2D;
+import org.bioimageanalysis.icy.gui.canvas.VtkCanvas;
+import org.bioimageanalysis.icy.model.overlay.Overlay;
+import org.bioimageanalysis.icy.model.overlay.VtkPainter;
+import org.bioimageanalysis.icy.model.sequence.Sequence;
+import org.bioimageanalysis.icy.system.logging.IcyLogger;
+import org.jetbrains.annotations.NotNull;
 import plugins.adufour.vars.gui.swing.SwingVarEditor;
 import plugins.adufour.vars.lang.Var;
 import plugins.adufour.vars.lang.VarBoolean;
@@ -36,50 +36,47 @@ import plugins.fab.trackmanager.PluginTrackManagerProcessor;
 import plugins.fab.trackmanager.TrackManagerPainter;
 import plugins.fab.trackmanager.TrackPool;
 import plugins.fab.trackmanager.TrackSegment;
-import plugins.kernel.canvas.VtkCanvas;
 import plugins.nchenouard.spot.Detection;
-import vtk.vtkActor;
-import vtk.vtkAppendPolyData;
-import vtk.vtkGlyph3D;
-import vtk.vtkLODActor;
-import vtk.vtkParametricFunctionSource;
-import vtk.vtkParametricSpline;
-import vtk.vtkPoints;
-import vtk.vtkPolyData;
-import vtk.vtkPolyDataMapper;
-import vtk.vtkProp;
-import vtk.vtkRenderer;
-import vtk.vtkSphereSource;
-import vtk.vtkTubeFilter;
-import vtk.vtkUnsignedCharArray;
-
-public class TrackPainter extends PluginTrackManagerProcessor
-{
+import vtk.*;
+
+import javax.swing.*;
+import javax.swing.border.TitledBorder;
+import javax.vecmath.Point2d;
+import javax.vecmath.Point3d;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+@IcyPluginName("Track Painter")
+@IcyPluginIcon(path = "/track-painter.png")
+public class TrackPainter extends PluginTrackManagerProcessor {
     private static final String filterName = "Display tracks on sequence";
 
     private TrackOverlay overlay;
-    private boolean keepLegacyOverlay = true;
+    private final boolean keepLegacyOverlay = true;
     private Overlay legacyOverlay;
 
     private List<TrackSegment> tracks;
 
-    public TrackPainter()
-    {
+    public TrackPainter() {
         super();
 
         setName(filterName);
-        tracks = new ArrayList<TrackSegment>();
+        tracks = new ArrayList<>();
     }
 
     @Override
-    public void Close()
-    {
+    public void Close() {
         if (overlay != null)
             overlay.remove();
 
         // put the legacy overlay back in place
-        if (keepLegacyOverlay && legacyOverlay != null)
-        {
+        if (keepLegacyOverlay && legacyOverlay != null) {
             if (trackPool.getDisplaySequence() != null)
                 trackPool.getDisplaySequence().addOverlay(legacyOverlay);
 
@@ -88,11 +85,9 @@ public class TrackPainter extends PluginTrackManagerProcessor
     }
 
     @Override
-    public void displaySequenceChanged()
-    {
-        System.out.println("display sequence changed");
-        if (overlay != null)
-        {
+    public void displaySequenceChanged() {
+        IcyLogger.info(this.getClass(), "Display sequence changed.");
+        if (overlay != null) {
             overlay.remove();
             overlay = null;
         }
@@ -100,22 +95,21 @@ public class TrackPainter extends PluginTrackManagerProcessor
     }
 
     @Override
-    public void Compute()
-    {
-        if (overlay == null)
-        {
-            Sequence s = trackPool.getDisplaySequence();
-            if (s != null)
-            {
+    public void Compute() {
+        if (overlay == null) {
+            final Sequence s = trackPool.getDisplaySequence();
+            if (s != null) {
                 // remove the current legacy painter and store it here
-                for (Overlay layer : s.getOverlays(OverlayWrapper.class))
-                {
-                    OverlayWrapper wrapper = (OverlayWrapper) layer;
-                    if (wrapper.getPainter() instanceof TrackManagerPainter)
-                    {
+                /*for (final Overlay layer : s.getOverlays(OverlayWrapper.class)) {
+                    final OverlayWrapper wrapper = (OverlayWrapper) layer;
+                    if (wrapper.getPainter() instanceof TrackManagerPainter) {
                         legacyOverlay = layer;
                         s.removeOverlay(legacyOverlay);
                     }
+                }*/
+
+                for (final Overlay layer : s.getOverlays(TrackManagerPainter.class)) {
+                    s.removeOverlay(layer);
                 }
 
                 s.addOverlay(overlay = new TrackOverlay(trackPool));
@@ -126,26 +120,22 @@ public class TrackPainter extends PluginTrackManagerProcessor
                 panel.invalidate();
             }
         }
-        else
-        {
-            // overlay.upToDate = false;
-        }
+        //else {
+        // overlay.upToDate = false;
+        //}
 
-        // store current state of tracks (so we display it correctly depending the order of the processor)
-        final List<TrackSegment> newTracks = new ArrayList<TrackSegment>();
-        for (TrackSegment track : trackPool.getTrackSegmentList())
-        {
-            final ArrayList<Detection> detections = new ArrayList<Detection>();
+        // store current state of tracks (so we display it correctly depending on the order of the processor)
+        final List<TrackSegment> newTracks = new ArrayList<>();
+        for (final TrackSegment track : trackPool.getTrackSegmentList()) {
+            final ArrayList<Detection> detections = new ArrayList<>();
 
-            try
-            {
-                for (Detection detection : track.getDetectionList())
+            try {
+                for (final Detection detection : track.getDetectionList())
                     detections.add((Detection) detection.clone());
             }
-            catch (CloneNotSupportedException e)
-            {
+            catch (final CloneNotSupportedException e) {
                 // should never happen
-                e.printStackTrace();
+                IcyLogger.error(this.getClass(), e, "Clone not supported.");
             }
 
             newTracks.add(new TrackSegment(detections));
@@ -158,8 +148,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
             overlay.upToDate = false;
     }
 
-    public class TrackOverlay extends Overlay implements VtkPainter
-    {
+    public class TrackOverlay extends Overlay implements VtkPainter {
         private final TrackPool trackPool;
 
         private double xScale, yScale, zScale;
@@ -175,7 +164,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
         private final vtkPolyDataMapper lineMapper = new vtkPolyDataMapper();
         private final vtkActor lineActor = new vtkActor();
 
-        private final List<vtkActor> visibleActors = new ArrayList<vtkActor>(2);
+        private final List<vtkActor> visibleActors = new ArrayList<>(2);
 
         private IcyCanvas canvas;
         private vtkRenderer renderer = null;
@@ -183,29 +172,26 @@ public class TrackPainter extends PluginTrackManagerProcessor
         private boolean upToDate = false;
 
         // user settings (detections)
-        private final VarBoolean detectionDisplay = new VarBoolean("Show detections", true)
-        {
-            protected void fireVariableChanged(Boolean oldValue, Boolean newValue)
-            {
+        private final VarBoolean detectionDisplay = new VarBoolean("Show detections", true) {
+            @Override
+            protected void fireVariableChanged(final Boolean oldValue, final Boolean newValue) {
                 setDetectionDisplay(newValue);
                 super.fireVariableChanged(oldValue, newValue);
             }
         };
 
         // user settings (tracks)
-        private final VarBoolean trackDisplay = new VarBoolean("Show tracks", true)
-        {
-            protected void fireVariableChanged(Boolean oldValue, Boolean newValue)
-            {
+        private final VarBoolean trackDisplay = new VarBoolean("Show tracks", true) {
+            @Override
+            protected void fireVariableChanged(final Boolean oldValue, final Boolean newValue) {
                 setTrackDisplay(newValue);
                 super.fireVariableChanged(oldValue, newValue);
             }
         };
 
-        private final VarBoolean displaySelectionOnly = new VarBoolean("Show selected tracks", false)
-        {
-            protected void fireVariableChanged(Boolean oldValue, Boolean newValue)
-            {
+        private final VarBoolean displaySelectionOnly = new VarBoolean("Show selected tracks", false) {
+            @Override
+            protected void fireVariableChanged(final Boolean oldValue, final Boolean newValue) {
                 super.fireVariableChanged(oldValue, newValue);
                 upToDate = false;
                 painterChanged();
@@ -214,12 +200,10 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
         // 3D settings
 
-        private final VarBoolean autoScale3D = new VarBoolean("Auto-scale with zoom", true)
-        {
-            protected void fireVariableChanged(Boolean oldValue, Boolean newValue)
-            {
-                if (newValue)
-                {
+        private final VarBoolean autoScale3D = new VarBoolean("Auto-scale with zoom", true) {
+            @Override
+            protected void fireVariableChanged(final Boolean oldValue, final @NotNull Boolean newValue) {
+                if (newValue) {
                     upToDate = false;
                     painterChanged();
                 }
@@ -227,31 +211,27 @@ public class TrackPainter extends PluginTrackManagerProcessor
             }
         };
 
-        private final VarBoolean trackTubes = new VarBoolean("Show tracks as tubes", true)
-        {
-            protected void fireVariableChanged(Boolean oldValue, Boolean newValue)
-            {
+        private final VarBoolean trackTubes = new VarBoolean("Show tracks as tubes", true) {
+            @Override
+            protected void fireVariableChanged(final Boolean oldValue, final Boolean newValue) {
                 setTrackDisplayAsTubes(newValue);
                 super.fireVariableChanged(oldValue, newValue);
             }
         };
 
-        public TrackOverlay(TrackPool trackPool)
-        {
+        public TrackOverlay(final @NotNull TrackPool trackPool) {
             super(filterName);
 
             this.trackPool = trackPool;
 
             final Sequence seq = trackPool.getDisplaySequence();
 
-            if (seq == null)
-            {
+            if (seq == null) {
                 xScale = 1d;
                 yScale = 1d;
                 zScale = 1d;
             }
-            else
-            {
+            else {
                 xScale = seq.getPixelSizeX();
                 yScale = seq.getPixelSizeY();
                 zScale = seq.getPixelSizeZ();
@@ -265,21 +245,20 @@ public class TrackPainter extends PluginTrackManagerProcessor
             // createGUI(getOptionsPanel());
         }
 
-        public JPanel getOptionsPanel()
-        {
-            JPanel container = new JPanel();
+        @Override
+        public JPanel getOptionsPanel() {
+            final JPanel container = new JPanel();
             container.setBorder(null);
             container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
 
             // Create a panel for the detections
 
-            JPanel detectionPanel = new JPanel(new GridBagLayout());
+            final JPanel detectionPanel = new JPanel(new GridBagLayout());
             detectionPanel.setBorder(new TitledBorder("Common settings"));
             container.add(detectionPanel);
 
-            for (Var<?> var : new Var<?>[] {detectionDisplay, trackDisplay, displaySelectionOnly})
-            {
-                GridBagConstraints gbc = new GridBagConstraints();
+            for (final Var<?> var : new Var<?>[]{detectionDisplay, trackDisplay, displaySelectionOnly}) {
+                final GridBagConstraints gbc = new GridBagConstraints();
 
                 gbc.insets = new Insets(2, 10, 2, 5);
                 gbc.fill = GridBagConstraints.HORIZONTAL;
@@ -287,7 +266,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
                 gbc.weightx = 1;
                 gbc.gridwidth = GridBagConstraints.REMAINDER;
-                SwingVarEditor<?> editor = (SwingVarEditor<?>) var.createVarEditor();
+                final SwingVarEditor<?> editor = (SwingVarEditor<?>) var.createVarEditor();
                 editor.setEnabled(true);
                 editor.getEditorComponent().setFocusable(false);
                 detectionPanel.add(editor.getEditorComponent(), gbc);
@@ -295,13 +274,12 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
             // Create a panel for the tracks
 
-            JPanel trackPanel = new JPanel(new GridBagLayout());
+            final JPanel trackPanel = new JPanel(new GridBagLayout());
             trackPanel.setBorder(new TitledBorder("3D settings"));
             container.add(trackPanel);
 
-            for (Var<?> var : new Var<?>[] {autoScale3D, trackTubes})
-            {
-                GridBagConstraints gbc = new GridBagConstraints();
+            for (final Var<?> var : new Var<?>[]{autoScale3D, trackTubes}) {
+                final GridBagConstraints gbc = new GridBagConstraints();
 
                 gbc.insets = new Insets(2, 10, 2, 5);
                 gbc.fill = GridBagConstraints.HORIZONTAL;
@@ -309,7 +287,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
                 gbc.weightx = 1;
                 gbc.gridwidth = GridBagConstraints.REMAINDER;
-                SwingVarEditor<?> editor = (SwingVarEditor<?>) var.createVarEditor();
+                final SwingVarEditor<?> editor = (SwingVarEditor<?>) var.createVarEditor();
                 editor.setEnabled(true);
                 editor.getEditorComponent().setFocusable(false);
                 trackPanel.add(editor.getEditorComponent(), gbc);
@@ -320,11 +298,10 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
         /**
          * Initializes (immutable) rendering components and default user settings
-         * 
+         *
          * @param vtkCanvas
          */
-        private void initRenderer(VtkCanvas vtkCanvas)
-        {
+        private void initRenderer(final @NotNull VtkCanvas vtkCanvas) {
             // 1) initialize immutable items
 
             renderer = vtkCanvas.getRenderer();
@@ -357,14 +334,12 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
         /**
          * Enables/Disables detection display. See below for further display options
-         * 
-         * @param display
-         *        <code>true</code> if detections should be displayed, <code>false</code>
-         *        otherwise
-         * see #setDetectionRadius(double)
+         *
+         * @param display <code>true</code> if detections should be displayed, <code>false</code>
+         *                otherwise
+         *                see #setDetectionRadius(double)
          */
-        public void setDetectionDisplay(boolean display)
-        {
+        public void setDetectionDisplay(final boolean display) {
             this.detectionDisplay.setValue(display);
 
             // VTK uses integers...
@@ -378,14 +353,12 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
         /**
          * Enables/Disables track display. See below for further display options
-         * 
-         * @param display
-         *        <code>true</code> if tracks should be displayed, <code>false</code> otherwise
+         *
+         * @param display <code>true</code> if tracks should be displayed, <code>false</code> otherwise
          * @see #setTrackDisplayAsTubes(boolean)
          * see #setTrackTubeRadius(double)
          */
-        public void setTrackDisplay(boolean display)
-        {
+        public void setTrackDisplay(final boolean display) {
             this.trackDisplay.setValue(display);
 
             // VTK uses integers...
@@ -398,14 +371,12 @@ public class TrackPainter extends PluginTrackManagerProcessor
         }
 
         /**
-         * @param displayAsTubes
-         *        <code>true</code> if the tracks should be displayed as 3D tubes, or
-         *        <code>false</code> to display tracks as lines (no thickness). Note that
-         *        displaying tracks as 3D tubes can be considerably slower on some systems
-         * see #setTrackTubeRadius(double)
+         * @param displayAsTubes <code>true</code> if the tracks should be displayed as 3D tubes, or
+         *                       <code>false</code> to display tracks as lines (no thickness). Note that
+         *                       displaying tracks as 3D tubes can be considerably slower on some systems
+         *                       see #setTrackTubeRadius(double)
          */
-        public void setTrackDisplayAsTubes(boolean displayAsTubes)
-        {
+        public void setTrackDisplayAsTubes(final boolean displayAsTubes) {
             this.trackTubes.setValue(displayAsTubes);
 
             upToDate = false;
@@ -413,29 +384,22 @@ public class TrackPainter extends PluginTrackManagerProcessor
         }
 
         @Override
-        public void paint(Graphics2D g, Sequence sequence, IcyCanvas theCanvas)
-        {
-            if (theCanvas instanceof VtkCanvas)
-            {
-                VtkCanvas vtk = (VtkCanvas) theCanvas;
-
-                if (renderer != vtk.getRenderer())
-                {
+        public void paint(final Graphics2D g, final Sequence sequence, final IcyCanvas theCanvas) {
+            if (theCanvas instanceof final VtkCanvas vtk) {
+                if (renderer != vtk.getRenderer()) {
                     initRenderer(vtk);
                     upToDate = false;
                 }
 
                 if ((sequence.getPixelSizeX() != xScale) || (sequence.getPixelSizeY() != yScale)
-                        || (sequence.getPixelSizeZ() != zScale))
-                {
+                        || (sequence.getPixelSizeZ() != zScale)) {
                     xScale = sequence.getPixelSizeX();
                     yScale = sequence.getPixelSizeY();
                     zScale = sequence.getPixelSizeZ();
                     upToDate = false;
                 }
 
-                if (theCanvas.getPositionT() != positionT)
-                {
+                if (theCanvas.getPositionT() != positionT) {
                     positionT = theCanvas.getPositionT();
                     // need to check that because of the screenshot rendering which doesn't send position change event
                     if (trackPool.getTrackManager().getCurrentPositionT() != positionT)
@@ -454,61 +418,56 @@ public class TrackPainter extends PluginTrackManagerProcessor
 
                 upToDate = true;
             }
-            else if (theCanvas instanceof IcyCanvas2D)
-            {
+            else if (theCanvas instanceof IcyCanvas2D) {
                 drawTracks2D((Graphics2D) g.create(), (IcyCanvas2D) theCanvas);
             }
         }
 
-        private void drawTracks2D(Graphics2D g, IcyCanvas2D theCanvas)
-        {
+        private void drawTracks2D(final Graphics2D g, final @NotNull IcyCanvas2D theCanvas) {
             // need to check that because of the screenshot rendering which doesn't send position change event
             if (trackPool.getTrackManager().getCurrentPositionT() != theCanvas.getPositionT())
                 trackPool.getTrackManager().setCurrentPositionT(theCanvas.getPositionT());
 
-            Graphics2D g2 = (Graphics2D) g.create();
-            double zoom = theCanvas.getScaleX();
+            final Graphics2D g2 = (Graphics2D) g.create();
+            final double zoom = theCanvas.getScaleX();
 
-            double lodThreshold = 2.0 / zoom;
+            final double lodThreshold = 2.0 / zoom;
 
-            float strokeWidth = (float) theCanvas.canvasToImageLogDeltaX(2);
+            final float strokeWidth = (float) theCanvas.canvasToImageLogDeltaX(2);
 
             g.setStroke(new BasicStroke(strokeWidth));
 
-            Point2d p1 = new Point2d(), p2 = new Point2d();
-            Line2D.Double l = new Line2D.Double();
+            final Point2d p1 = new Point2d();
+            final Point2d p2 = new Point2d();
+            final Line2D.Double l = new Line2D.Double();
 
-            double shapeFactor = 5.0;
-            Ellipse2D.Double detectionShape = new Ellipse2D.Double(0, 0, strokeWidth * shapeFactor,
+            final double shapeFactor = 5.0;
+            final Ellipse2D.Double detectionShape = new Ellipse2D.Double(0, 0, strokeWidth * shapeFactor,
                     strokeWidth * shapeFactor);
-            double shapeCenter = strokeWidth * shapeFactor / 2;
+            final double shapeCenter = strokeWidth * shapeFactor / 2;
 
             g.translate(0.5, 0.5);
 
-            for (TrackSegment ts : tracks)
-            {
-                ArrayList<Detection> detections = ts.getDetectionList();
+            for (final TrackSegment ts : tracks) {
+                final ArrayList<Detection> detections = ts.getDetectionList();
 
-                int nDet = detections.size();
+                final int nDet = detections.size();
 
-                for (int t = 0; t < nDet; t++)
-                {
-                    Detection d1 = detections.get(t);
+                for (int t = 0; t < nDet; t++) {
+                    final Detection d1 = detections.get(t);
                     if (d1 == null || !d1.isEnabled())
                         continue;
                     p1.set(d1.getX(), d1.getY());
                     g.setColor(d1.getColor());
 
-                    if (displaySelectionOnly.getValue())
-                    {
+                    if (displaySelectionOnly.getValue()) {
                         if (!d1.isSelected())
                             continue;
                     }
                     else if (d1.isSelected())
                         g.setColor(Color.white);
 
-                    if (d1.getT() == theCanvas.getPositionT() && d1 != null && detectionDisplay.getValue())
-                    {
+                    if (d1.getT() == theCanvas.getPositionT() && d1 != null && detectionDisplay.getValue()) {
                         // draw current detection
                         // if selected, switch color to white
                         detectionShape.x = p1.x - shapeCenter;
@@ -518,21 +477,17 @@ public class TrackPainter extends PluginTrackManagerProcessor
                     }
 
                     // LOD find the shortest segment above the drawing threshold
-                    for (; t < nDet - 1; t++)
-                    {
-                        Detection d2 = detections.get(t + 1);
+                    for (; t < nDet - 1; t++) {
+                        final Detection d2 = detections.get(t + 1);
                         if (d2 == null || !d2.isEnabled())
                             break;
 
-                        if (trackDisplay.getValue())
-                        {
+                        if (trackDisplay.getValue()) {
                             p2.set(d2.getX(), d2.getY());
 
-                            if (p1.distanceSquared(p2) < lodThreshold)
-                            {
+                            if (p1.distanceSquared(p2) < lodThreshold) {
                                 // draw the detection in case it was skipped due to the LOD
-                                if (d2.getT() == theCanvas.getPositionT() && d2 != null && detectionDisplay.getValue())
-                                {
+                                if (d2.getT() == theCanvas.getPositionT() && d2 != null && detectionDisplay.getValue()) {
                                     // draw current detection
                                     // if selected, switch color to white
                                     detectionShape.x = p2.x - shapeCenter;
@@ -556,40 +511,37 @@ public class TrackPainter extends PluginTrackManagerProcessor
             }
         }
 
-        private void drawTracks3D()
-        {
-            double viewScale = Math.sqrt(((VtkCanvas) canvas).getCamera().GetDistance()) * (xScale + yScale + zScale)
+        private void drawTracks3D() {
+            final double viewScale = Math.sqrt(((VtkCanvas) canvas).getCamera().GetDistance()) * (xScale + yScale + zScale)
                     / 3;
-            double tubeRadius = viewScale / 15.0;
+            final double tubeRadius = viewScale / 15.0;
 
-            int tubeSection = (int) Math.min(8, 100.0 / viewScale);
-            int tubeSmoothness = Math.max(1, tubeSection - 1);
+            final int tubeSection = (int) Math.min(8, 100.0 / viewScale);
+            final int tubeSmoothness = Math.max(1, tubeSection - 1);
 
             lines.RemoveAllInputs();
 
-            for (TrackSegment ts : tracks)
-            {
-                List<Detection> detections = ts.getDetectionList();
+            for (final TrackSegment ts : tracks) {
+                final List<Detection> detections = ts.getDetectionList();
 
-                vtkPoints points = new vtkPoints();
-                vtkUnsignedCharArray colors = new vtkUnsignedCharArray();
+                final vtkPoints points = new vtkPoints();
+                final vtkUnsignedCharArray colors = new vtkUnsignedCharArray();
                 colors.SetName("colors");
                 colors.SetNumberOfComponents(3);
 
-                for (int t = 0; t < detections.size(); t++)
-                {
-                    Detection detection = detections.get(t);
-
+                for (final Detection detection : detections) {
                     if (detection == null)
                         continue;
                     if (displaySelectionOnly.getValue() && !detection.isSelected())
                         continue;
 
-                    if (detection.isEnabled())
-                    {
-                        Point3d position = new Point3d(detection.getX() * xScale, detection.getY() * yScale,
-                                detection.getZ() * zScale);
-                        Color color = (displaySelectionOnly.getValue() == detection.isSelected()) ? detection.getColor()
+                    if (detection.isEnabled()) {
+                        final Point3d position = new Point3d(
+                                detection.getX() * xScale,
+                                detection.getY() * yScale,
+                                detection.getZ() * zScale
+                        );
+                        final Color color = (displaySelectionOnly.getValue() == detection.isSelected()) ? detection.getColor()
                                 : Color.white;
 
                         points.InsertNextPoint(position.x, position.y, position.z);
@@ -599,20 +551,19 @@ public class TrackPainter extends PluginTrackManagerProcessor
                     }
                 }
 
-                vtkParametricSpline spline = new vtkParametricSpline();
+                final vtkParametricSpline spline = new vtkParametricSpline();
                 spline.SetPoints(points);
 
-                vtkParametricFunctionSource function = new vtkParametricFunctionSource();
+                final vtkParametricFunctionSource function = new vtkParametricFunctionSource();
                 function.SetParametricFunction(spline);
-                function.SetUResolution(points.GetNumberOfPoints() * tubeSmoothness);
+                function.SetUResolution((int) (points.GetNumberOfPoints() * tubeSmoothness));
                 function.Update();
-                vtkPolyData lineData = function.GetOutput();
+                final vtkPolyData lineData = function.GetOutput();
                 lineData.GetPointData().SetScalars(colors);
 
-                if (trackTubes.getValue())
-                {
+                if (trackTubes.getValue()) {
                     // draw the splines as thick 3D tubes
-                    vtkTubeFilter tuber = new vtkTubeFilter();
+                    final vtkTubeFilter tuber = new vtkTubeFilter();
                     tuber.SetInputData(lineData);
                     tuber.SetRadius(tubeRadius);
                     tuber.CappingOn();
@@ -627,8 +578,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
                     // cleanup
                     tuber.Delete();
                 }
-                else
-                {
+                else {
                     // just draw the spline itself (no thickness)
                     lines.AddInputData(lineData);
                 }
@@ -645,19 +595,17 @@ public class TrackPainter extends PluginTrackManagerProcessor
             }
         }
 
-        private void drawDetections3D(int positionT)
-        {
-            double viewScale = (xScale + yScale + zScale) / 3;
-            double sphereRadius = Math.sqrt(((VtkCanvas) canvas).getCamera().GetDistance()) * viewScale / 5.0;
+        private void drawDetections3D(final int positionT) {
+            final double viewScale = (xScale + yScale + zScale) / 3;
+            final double sphereRadius = Math.sqrt(((VtkCanvas) canvas).getCamera().GetDistance()) * viewScale / 5.0;
             sphere.SetRadius(sphereRadius);
 
-            vtkPoints spherePositions = new vtkPoints();
-            vtkUnsignedCharArray sphereColors = new vtkUnsignedCharArray();
+            final vtkPoints spherePositions = new vtkPoints();
+            final vtkUnsignedCharArray sphereColors = new vtkUnsignedCharArray();
             sphereColors.SetName("colors");
             sphereColors.SetNumberOfComponents(3);
 
-            for (Detection detection : trackPool.getAllDetection())
-            {
+            for (final Detection detection : trackPool.getAllDetection()) {
                 if (detection == null || !detection.isEnabled())
                     continue;
 
@@ -667,9 +615,9 @@ public class TrackPainter extends PluginTrackManagerProcessor
                 if (displaySelectionOnly.getValue() && !detection.isSelected())
                     continue;
 
-                Point3d position = new Point3d(detection.getX() * xScale, detection.getY() * yScale,
+                final Point3d position = new Point3d(detection.getX() * xScale, detection.getY() * yScale,
                         detection.getZ() * zScale);
-                Color color = (displaySelectionOnly.getValue() == detection.isSelected()) ? detection.getColor()
+                final Color color = (displaySelectionOnly.getValue() == detection.isSelected()) ? detection.getColor()
                         : Color.white;
 
                 spherePositions.InsertNextPoint(position.x, position.y, position.z);
@@ -686,8 +634,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
         }
 
         @Override
-        public void remove()
-        {
+        public void remove() {
             if (renderer != null)
                 renderer = null;
 
@@ -695,8 +642,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
         }
 
         @Override
-        public void mouseClick(MouseEvent e, Point5D.Double imagePoint, IcyCanvas theCanvas)
-        {
+        public void mouseClick(final MouseEvent e, final Point5D.Double imagePoint, final IcyCanvas theCanvas) {
             super.mouseClick(e, imagePoint, theCanvas);
 
             if (!(theCanvas instanceof IcyCanvas2D))
@@ -705,33 +651,37 @@ public class TrackPainter extends PluginTrackManagerProcessor
             imagePoint.x -= 0.5;
             imagePoint.y -= 0.5;
 
-            double selectThreshold = Math.min(10.0, Math.max(0.25, 10.0 / theCanvas.getScaleX()));
+            final double selectThreshold = Math.min(10.0, Math.max(0.25, 10.0 / theCanvas.getScaleX()));
 
             // List all candidate tracks close to the click
             // => store this in a map of [distance <=> track]
-            TreeMap<Double, TrackSegment> candidates = new TreeMap<Double, TrackSegment>();
+            final TreeMap<Double, TrackSegment> candidates = new TreeMap<>();
 
-            for (TrackSegment ts : trackPool.getTrackSegmentList())
-            {
-                ArrayList<Detection> detections = ts.getDetectionList();
+            for (final TrackSegment ts : trackPool.getTrackSegmentList()) {
+                final ArrayList<Detection> detections = ts.getDetectionList();
 
-                int nDet = detections.size() - 1;
+                final int nDet = detections.size() - 1;
 
-                detectionLoop: for (int t = 0; t < nDet; t++)
-                {
+                detectionLoop:
+                for (int t = 0; t < nDet; t++) {
                     // Retrieve the "d1-d2" segment
-                    Detection d1 = detections.get(t);
-                    Detection d2 = detections.get(t + 1);
+                    final Detection d1 = detections.get(t);
+                    final Detection d2 = detections.get(t + 1);
 
                     if (d1 == null || d2 == null || !d1.isEnabled() || !d2.isEnabled())
                         continue;
 
                     // Check if the click is close to the segment
-                    double distSq = Line2D.ptSegDistSq(d1.getX(), d1.getY(), d2.getX(), d2.getY(), imagePoint.x,
-                            imagePoint.y);
-
-                    if (distSq < selectThreshold)
-                    {
+                    final double distSq = Line2D.ptSegDistSq(
+                            d1.getX(),
+                            d1.getY(),
+                            d2.getX(),
+                            d2.getY(),
+                            imagePoint.x,
+                            imagePoint.y
+                    );
+
+                    if (distSq < selectThreshold) {
                         candidates.put(distSq, ts);
                         // move to the next track segment
                         break detectionLoop;
@@ -739,10 +689,9 @@ public class TrackPainter extends PluginTrackManagerProcessor
                 }
             }
 
-            if (candidates.size() > 0)
-            {
+            if (!candidates.isEmpty()) {
                 // highlight the closest candidate
-                for (Detection d : candidates.firstEntry().getValue().getDetectionList())
+                for (final Detection d : candidates.firstEntry().getValue().getDetectionList())
                     d.setSelected(!d.isSelected());
 
                 trackPool.fireTrackEditorProcessorChange();
@@ -750,8 +699,7 @@ public class TrackPainter extends PluginTrackManagerProcessor
         }
 
         @Override
-        public void mouseWheelMoved(MouseWheelEvent e, icy.type.point.Point5D.Double imagePoint, IcyCanvas theCanvas)
-        {
+        public void mouseWheelMoved(final MouseWheelEvent e, final Point5D.Double imagePoint, final IcyCanvas theCanvas) {
             if (autoScale3D.getValue())
                 upToDate = false;
 
@@ -759,9 +707,8 @@ public class TrackPainter extends PluginTrackManagerProcessor
         }
 
         @Override
-        public vtkProp[] getProps()
-        {
-            return new vtkProp[] {lineActor, sphereActor};
+        public vtkProp[] getProps() {
+            return new vtkProp[]{lineActor, sphereActor};
         }
     }
 }
diff --git a/src/main/resources/track-painter.png b/src/main/resources/track-painter.png
new file mode 100644
index 0000000000000000000000000000000000000000..33283de571a12bdc0a1a87c395ede199ff040035
Binary files /dev/null and b/src/main/resources/track-painter.png differ