diff --git a/.gitignore b/.gitignore
index b0a9905575783f18d92dd5f505a52d187802980d..57f16fb67c1b1589981416b323d7a9debc728665 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,41 @@
-.idea/
-.settings/
-build/
+/build*
+/workspace
+setting.xml
+release/
 target/
+!.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
-**/.DS_Store
\ 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 b5454c536209a709a3ea1bebe7dd073fa0cb9d6e..b3cfb94422f755cb869fe48c57a7dd7fbbeba45c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,13 +7,11 @@
     <parent>
         <groupId>org.bioimageanalysis.icy</groupId>
         <artifactId>pom-icy</artifactId>
-        <version>2.2.0</version>
+        <version>3.0.0-a.1</version>
     </parent>
 
     <artifactId>quickhull</artifactId>
-    <version>2.0.0</version>
-
-    <packaging>jar</packaging>
+    <version>2.0.0-a.1</version>
 
     <name>QuickHull</name>
     <description>
@@ -30,8 +28,7 @@
     <repositories>
         <repository>
             <id>icy</id>
-            <url>https://icy-nexus.pasteur.fr/repository/Icy/</url>
+            <url>https://nexus-icy.pasteur.cloud/repository/icy/</url>
         </repository>
     </repositories>
-
 </project>
\ No newline at end of file
diff --git a/src/main/java/plugins/adufour/quickhull/Face.java b/src/main/java/plugins/adufour/quickhull/Face.java
index 042eea7dd17d6ed255403726a30c6fbff8f6c7e2..2afe11a2eb3e0d862f81a5f8308aa640a39e9a39 100644
--- a/src/main/java/plugins/adufour/quickhull/Face.java
+++ b/src/main/java/plugins/adufour/quickhull/Face.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -18,6 +18,8 @@
 
 package plugins.adufour.quickhull;
 
+import org.jetbrains.annotations.NotNull;
+
 import javax.vecmath.Vector3d;
 
 /**
@@ -30,7 +32,7 @@ import javax.vecmath.Vector3d;
  *
  * @author John E. Lloyd, Fall 2004
  */
-class Face {
+public class Face {
     HalfEdge he0;
     private final Vector3d normal;
     double area;
@@ -49,7 +51,7 @@ class Face {
 
     Vertex outside;
 
-    public void computeCentroid(final Vector3d centroid) {
+    public void computeCentroid(final @NotNull Vector3d centroid) {
         centroid.set(0, 0, 0);
         HalfEdge he = he0;
         do {
@@ -95,7 +97,7 @@ class Face {
         }
     }
 
-    public void computeNormal(final Vector3d normal) {
+    public void computeNormal(final @NotNull Vector3d normal) {
         HalfEdge he1 = he0.next;
         HalfEdge he2 = he1.next;
 
@@ -153,7 +155,7 @@ class Face {
         planeOffset = normal.dot(centroid);
     }
 
-    public static Face createTriangle(final Vertex v0, final Vertex v1, final Vertex v2) {
+    public static @NotNull Face createTriangle(final Vertex v0, final Vertex v1, final Vertex v2) {
         return createTriangle(v0, v1, v2, 0);
     }
 
@@ -164,7 +166,7 @@ class Face {
      * @param v1 second vertex
      * @param v2 third vertex
      */
-    public static Face createTriangle(final Vertex v0, final Vertex v1, final Vertex v2, final double minArea) {
+    public static @NotNull Face createTriangle(final Vertex v0, final Vertex v1, final Vertex v2, final double minArea) {
         final Face face = new Face();
         final HalfEdge he0 = new HalfEdge(v0, face);
         final HalfEdge he1 = new HalfEdge(v1, face);
@@ -184,7 +186,7 @@ class Face {
         return face;
     }
 
-    public static Face create(final Vertex[] vtxArray, final int[] indices) {
+    public static @NotNull Face create(final Vertex[] vtxArray, final int @NotNull [] indices) {
         final Face face = new Face();
         HalfEdge hePrev = null;
         for (final int j : indices) {
@@ -261,7 +263,7 @@ class Face {
      * @param p the point
      * @return distance from the point to the plane
      */
-    public double distanceToPlane(final Vector3d p) {
+    public double distanceToPlane(final @NotNull Vector3d p) {
         return normal.x * p.x + normal.y * p.y + normal.z * p.z - planeOffset;
     }
 
@@ -297,7 +299,7 @@ class Face {
         return s.toString();
     }
 
-    public void getVertexIndices(final int[] idxs) {
+    public void getVertexIndices(final int @NotNull [] idxs) {
         HalfEdge he = he0;
         int i = 0;
         do {
@@ -306,7 +308,7 @@ class Face {
         } while (he != he0);
     }
 
-    private Face connectHalfEdges(final HalfEdge hedgePrev, final HalfEdge hedge) {
+    private Face connectHalfEdges(final @NotNull HalfEdge hedgePrev, final @NotNull HalfEdge hedge) {
         Face discardedFace = null;
 
         if (hedgePrev.oppositeFace() == hedge.oppositeFace()) { // then there is a redundant edge that we can get rid off
@@ -389,7 +391,7 @@ class Face {
 
     }
 
-    public int mergeAdjacentFace(final HalfEdge hedgeAdj, final Face[] discarded) {
+    public int mergeAdjacentFace(final @NotNull HalfEdge hedgeAdj, final Face @NotNull [] discarded) {
         final Face oppFace = hedgeAdj.oppositeFace();
         int numDiscarded = 0;
 
@@ -481,6 +483,5 @@ class Face {
         for (Face face = face0; face != null; face = face.next) {
             face.checkConsistency();
         }
-
     }
 }
diff --git a/src/main/java/plugins/adufour/quickhull/FaceList.java b/src/main/java/plugins/adufour/quickhull/FaceList.java
index 1713a1210f3e7f2a95204e28ae8d411044df3dac..be6d7b2bd91077ffbd2675d079c2af26a152256b 100644
--- a/src/main/java/plugins/adufour/quickhull/FaceList.java
+++ b/src/main/java/plugins/adufour/quickhull/FaceList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -21,7 +21,7 @@ package plugins.adufour.quickhull;
 /**
  * Maintains a single-linked list of faces for use by QuickHull3D
  */
-class FaceList {
+public class FaceList {
     private Face head;
     private Face tail;
 
diff --git a/src/main/java/plugins/adufour/quickhull/HalfEdge.java b/src/main/java/plugins/adufour/quickhull/HalfEdge.java
index 1290f57b48b741c00bddc7dfb2856f9357f303a8..e31d21e232f13a407a5bb9027cccf15c320cf6e9 100644
--- a/src/main/java/plugins/adufour/quickhull/HalfEdge.java
+++ b/src/main/java/plugins/adufour/quickhull/HalfEdge.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -18,6 +18,9 @@
 
 package plugins.adufour.quickhull;
 
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
 import javax.vecmath.Vector3d;
 
 /**
@@ -25,7 +28,7 @@ import javax.vecmath.Vector3d;
  *
  * @author John E. Lloyd, Fall 2004
  */
-class HalfEdge {
+public class HalfEdge {
     /**
      * The vertex associated with the head of this half-edge.
      */
@@ -58,11 +61,13 @@ class HalfEdge {
      * @param v head vertex
      * @param f left-hand triangular face
      */
+    @Contract(pure = true)
     public HalfEdge(final Vertex v, final Face f) {
         vertex = v;
         face = f;
     }
 
+    @Contract(pure = true)
     public HalfEdge() {
     }
 
@@ -125,7 +130,7 @@ class HalfEdge {
      *
      * @param edge opposite half-edge
      */
-    public void setOpposite(final HalfEdge edge) {
+    public void setOpposite(final @NotNull HalfEdge edge) {
         opposite = edge;
         edge.opposite = this;
     }
diff --git a/src/main/java/plugins/adufour/quickhull/QuickHull.java b/src/main/java/plugins/adufour/quickhull/QuickHull.java
index 1be8ced11f2604d7018236dd257eade51d8375f9..5277b47d54fe474a364af75cb1d45163c1c0ec1f 100644
--- a/src/main/java/plugins/adufour/quickhull/QuickHull.java
+++ b/src/main/java/plugins/adufour/quickhull/QuickHull.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -18,8 +18,10 @@
 
 package plugins.adufour.quickhull;
 
-import icy.plugin.abstract_.Plugin;
-import icy.plugin.interface_.PluginLibrary;
+import org.bioimageanalysis.icy.extension.plugin.abstract_.Plugin;
+import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginIcon;
+import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginName;
+import org.bioimageanalysis.icy.extension.plugin.interface_.PluginLibrary;
 
 /**
  * Main class of the QuickHull library for Icy
@@ -29,6 +31,8 @@ import icy.plugin.interface_.PluginLibrary;
  *
  * @author Alexandre Dufour
  */
+@IcyPluginName("QuickHull")
+@IcyPluginIcon(path = "/quickhull.png")
 public class QuickHull extends Plugin implements PluginLibrary {
 
 }
diff --git a/src/main/java/plugins/adufour/quickhull/QuickHull2D.java b/src/main/java/plugins/adufour/quickhull/QuickHull2D.java
index 2f2542b8f2f76c693bed3e76cf24335e41fe4866..df97cf9c6e539cf00dfbe39ac59f780eb4ef22b4 100644
--- a/src/main/java/plugins/adufour/quickhull/QuickHull2D.java
+++ b/src/main/java/plugins/adufour/quickhull/QuickHull2D.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -18,21 +18,24 @@
 
 package plugins.adufour.quickhull;
 
-import icy.plugin.abstract_.Plugin;
+import org.bioimageanalysis.icy.extension.plugin.abstract_.Plugin;
+import org.bioimageanalysis.icy.extension.plugin.annotation_.IcyPluginName;
+import org.jetbrains.annotations.NotNull;
 
 import java.awt.geom.Line2D;
 import java.awt.geom.Point2D;
 import java.util.ArrayList;
 import java.util.List;
 
+@IcyPluginName("QuickHull 2D")
 public class QuickHull2D extends Plugin {
-    public static List<Point2D> computeConvexEnvelope(final List<Point2D> points) {
+    public static @NotNull List<Point2D> computeConvexEnvelope(final @NotNull List<Point2D> points) {
         final ArrayList<Point2D> envelope = new ArrayList<>();
 
         // find two points: right (bottom) and left (top)
 
-        Point2D l = points.get(0);
-        Point2D r = points.get(0);
+        Point2D l = points.getFirst();
+        Point2D r = points.getFirst();
 
         for (int i = 1; i < points.size(); i++) {
             final Point2D p = points.get(i);
@@ -69,8 +72,8 @@ public class QuickHull2D extends Plugin {
         return envelope;
     }
 
-    private static void quickhull(final ArrayList<Point2D> envelope, final Point2D a, final Point2D b, final ArrayList<Point2D> neighbors) {
-        if (neighbors.size() == 0) return;
+    private static void quickhull(final ArrayList<Point2D> envelope, final Point2D a, final Point2D b, final @NotNull ArrayList<Point2D> neighbors) {
+        if (neighbors.isEmpty()) return;
 
         final Point2D c = farthestpoint(a, b, neighbors);
 
@@ -95,7 +98,7 @@ public class QuickHull2D extends Plugin {
         quickhull(envelope, c, b, al2);
     }
 
-    private static Point2D farthestpoint(final Point2D a, final Point2D b, final ArrayList<Point2D> points) {
+    private static Point2D farthestpoint(final Point2D a, final Point2D b, final @NotNull ArrayList<Point2D> points) {
         double maxD = -1;
         Point2D maxP = null;
 
diff --git a/src/main/java/plugins/adufour/quickhull/QuickHull3D.java b/src/main/java/plugins/adufour/quickhull/QuickHull3D.java
index dac0a2f64c73c3a27a01d3b946d9216b37939689..d370f7991c9cbf7d0e0fdb9a732d8dbdb80b6bcd 100644
--- a/src/main/java/plugins/adufour/quickhull/QuickHull3D.java
+++ b/src/main/java/plugins/adufour/quickhull/QuickHull3D.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -17,6 +17,10 @@
  */
 package plugins.adufour.quickhull;
 
+import org.bioimageanalysis.icy.system.logging.IcyLogger;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
 import javax.vecmath.Point3d;
 import javax.vecmath.Vector3d;
 import java.io.*;
@@ -224,7 +228,7 @@ public class QuickHull3D {
         return explicitTolerance;
     }
 
-    private void addPointToFace(final Vertex vtx, final Face face) {
+    private void addPointToFace(final @NotNull Vertex vtx, final @NotNull Face face) {
         vtx.face = face;
 
         if (face.outside == null) {
@@ -236,7 +240,7 @@ public class QuickHull3D {
         face.outside = vtx;
     }
 
-    private void removePointFromFace(final Vertex vtx, final Face face) {
+    private void removePointFromFace(final Vertex vtx, final @NotNull Face face) {
         if (vtx == face.outside) {
             if (vtx.next != null && vtx.next.face == face) {
                 face.outside = vtx.next;
@@ -248,7 +252,7 @@ public class QuickHull3D {
         claimed.delete(vtx);
     }
 
-    private Vertex removeAllPointsFromFace(final Face face) {
+    private @Nullable Vertex removeAllPointsFromFace(final @NotNull Face face) {
         if (face.outside != null) {
             Vertex end = face.outside;
             while (end.next != null && end.next.face == face) {
@@ -293,7 +297,7 @@ public class QuickHull3D {
         build(points, points.length);
     }
 
-    private HalfEdge findHalfEdge(final Vertex tail, final Vertex head) {
+    private @Nullable HalfEdge findHalfEdge(final Vertex tail, final Vertex head) {
         // brute force ... OK, since setHull is not used much
         for (final Face face : faces) {
             final HalfEdge he = face.findEdge(tail, head);
@@ -322,7 +326,7 @@ public class QuickHull3D {
         }
     }
 
-    private void printQhullErrors(final Process proc) throws IOException {
+    private void printQhullErrors(final @NotNull Process proc) throws IOException {
         boolean wrote = false;
         final InputStream es = proc.getErrorStream();
         while (es.available() > 0) {
@@ -340,7 +344,7 @@ public class QuickHull3D {
             commandStr += " -Qt";
         }
         try {
-            final Process proc = Runtime.getRuntime().exec(commandStr);
+            final Process proc = Runtime.getRuntime().exec(commandStr); // FIXME change command from String to String[]
             final PrintStream ps = new PrintStream(proc.getOutputStream());
             final StreamTokenizer stok = new StreamTokenizer(new InputStreamReader(proc.getInputStream()));
 
@@ -374,7 +378,7 @@ public class QuickHull3D {
                         System.out.println("Expecting face index");
                         System.exit(1);
                     }
-                    indexList.add(0, (int) stok.nval);
+                    indexList.addFirst((int) stok.nval);
                 }
                 faceIndices[i] = new int[indexList.size()];
                 int k = 0;
@@ -385,8 +389,9 @@ public class QuickHull3D {
             setHull(coords, nump, faceIndices, numf);
         }
         catch (final Exception e) {
-            e.printStackTrace();
-            System.exit(1);
+            IcyLogger.fatal(this.getClass(), e, e.getLocalizedMessage());
+            //System.exit(1);
+            throw new RuntimeException(e);
         }
     }
 
@@ -878,7 +883,7 @@ public class QuickHull3D {
         }
     }
 
-    private void getFaceIndices(final int[] indices, final Face face, final int flags) {
+    private void getFaceIndices(final int[] indices, final @NotNull Face face, final int flags) {
         final boolean ccw = ((flags & CLOCKWISE) == 0);
         final boolean indexedFromOne = ((flags & INDEXED_FROM_ONE) != 0);
         final boolean pointRelative = ((flags & POINT_RELATIVE) != 0);
@@ -956,11 +961,11 @@ public class QuickHull3D {
     private static final int NONCONVEX_WRT_LARGER_FACE = 1;
     private static final int NONCONVEX = 2;
 
-    protected double oppFaceDistance(final HalfEdge he) {
+    protected double oppFaceDistance(final @NotNull HalfEdge he) {
         return he.face.distanceToPlane(he.opposite.face.getCentroid());
     }
 
-    private boolean doAdjacentMerge(final Face face, final int mergeType) {
+    private boolean doAdjacentMerge(final @NotNull Face face, final int mergeType) {
         HalfEdge hedge = face.he0;
 
         boolean convex = true;
@@ -1050,14 +1055,14 @@ public class QuickHull3D {
         } while (edge != edge0);
     }
 
-    private HalfEdge addAdjoiningFace(final Vertex eyeVtx, final HalfEdge he) {
+    private HalfEdge addAdjoiningFace(final Vertex eyeVtx, final @NotNull HalfEdge he) {
         final Face face = Face.createTriangle(eyeVtx, he.tail(), he.head());
         faces.add(face);
         face.getEdge(-1).setOpposite(he.getOpposite());
         return face.getEdge(0);
     }
 
-    protected void addNewFaces(final FaceList newFaces, final Vertex eyeVtx, final Vector<HalfEdge> horizon) {
+    protected void addNewFaces(final @NotNull FaceList newFaces, final Vertex eyeVtx, final @NotNull Vector<HalfEdge> horizon) {
         newFaces.clear();
 
         HalfEdge hedgeSidePrev = null;
@@ -1153,7 +1158,7 @@ public class QuickHull3D {
         }
     }
 
-    private void markFaceVertices(final Face face, final int mark) {
+    private void markFaceVertices(final @NotNull Face face, final int mark) {
         final HalfEdge he0 = face.getFirstEdge();
         HalfEdge he = he0;
         do {
@@ -1189,7 +1194,7 @@ public class QuickHull3D {
         }
     }
 
-    protected boolean checkFaceConvexity(final Face face, final double tol, final PrintStream ps) {
+    protected boolean checkFaceConvexity(final @NotNull Face face, final double tol, final PrintStream ps) {
         double dist;
         HalfEdge he = face.he0;
         do {
diff --git a/src/main/java/plugins/adufour/quickhull/Vertex.java b/src/main/java/plugins/adufour/quickhull/Vertex.java
index b652810c483efa49a5b10f856204856ba50a1697..cdbbfc37cc0db3dfc798171e122f98726bf6c73d 100644
--- a/src/main/java/plugins/adufour/quickhull/Vertex.java
+++ b/src/main/java/plugins/adufour/quickhull/Vertex.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -18,6 +18,8 @@
 
 package plugins.adufour.quickhull;
 
+import org.jetbrains.annotations.Contract;
+
 import javax.vecmath.Vector3d;
 
 /**
@@ -26,7 +28,7 @@ import javax.vecmath.Vector3d;
  *
  * @author John E. Lloyd, Fall 2004
  */
-class Vertex {
+public class Vertex {
     /**
      * Spatial point associated with this vertex.
      */
@@ -55,6 +57,7 @@ class Vertex {
     /**
      * Constructs a vertex and sets its coordinates to 0.
      */
+    @Contract(pure = true)
     public Vertex() {
         pnt = new Vector3d();
     }
@@ -63,6 +66,7 @@ class Vertex {
      * Constructs a vertex with the specified coordinates
      * and index.
      */
+    @Contract(pure = true)
     public Vertex(final double x, final double y, final double z, final int idx) {
         pnt = new Vector3d(x, y, z);
         index = idx;
diff --git a/src/main/java/plugins/adufour/quickhull/VertexList.java b/src/main/java/plugins/adufour/quickhull/VertexList.java
index f6d6247b1b9a65228fc201e3635a1461b67ce9e9..2b6816f191a0f48ec14b3dad4bbaf261066dad27 100644
--- a/src/main/java/plugins/adufour/quickhull/VertexList.java
+++ b/src/main/java/plugins/adufour/quickhull/VertexList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2023. Institut Pasteur.
+ * Copyright (c) 2010-2024. Institut Pasteur.
  *
  * This file is part of Icy.
  * Icy is free software: you can redistribute it and/or modify
@@ -18,10 +18,12 @@
 
 package plugins.adufour.quickhull;
 
+import org.jetbrains.annotations.NotNull;
+
 /**
  * Maintains a double-linked list of vertices for use by QuickHull3D
  */
-class VertexList {
+public class VertexList {
     private Vertex head;
     private Vertex tail;
 
@@ -67,7 +69,7 @@ class VertexList {
     /**
      * Deletes a vertex from this list.
      */
-    public void delete(final Vertex vtx) {
+    public void delete(final @NotNull Vertex vtx) {
         if (vtx.prev == null) {
             head = vtx.next;
         }
@@ -85,7 +87,7 @@ class VertexList {
     /**
      * Deletes a chain of vertices from this list.
      */
-    public void delete(final Vertex vtx1, final Vertex vtx2) {
+    public void delete(final @NotNull Vertex vtx1, final Vertex vtx2) {
         if (vtx1.prev == null) {
             head = vtx2.next;
         }
@@ -103,7 +105,7 @@ class VertexList {
     /**
      * Inserts a vertex into this list before another specificed vertex.
      */
-    public void insertBefore(final Vertex vtx, final Vertex next) {
+    public void insertBefore(final @NotNull Vertex vtx, final @NotNull Vertex next) {
         vtx.prev = next.prev;
         if (next.prev == null) {
             head = vtx;
diff --git a/src/main/resources/quickhull.png b/src/main/resources/quickhull.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c8c783a833888f37d0123b8de62ecbe8cdd7ea6
Binary files /dev/null and b/src/main/resources/quickhull.png differ