diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..025e963ead2aa54de670d6e933032e42ea68e61d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/target
+.project
+.classpath
+*.prefs
+*.jardesc
+*.jar
+.settings/
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9595cda03eef3585d8cfcc24e8b9cebabf60e260
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!-- Inherited Icy Parent POM -->
+    <parent>
+        <groupId>org.bioimageanalysis.icy</groupId>
+        <artifactId>parent-pom-plugin</artifactId>
+        <version>1.0.1</version>
+    </parent>
+
+    <!-- Project Information -->
+    <artifactId>intensity-projection</artifactId>
+    <version>1.6.2</version>
+    
+    <packaging>jar</packaging>
+
+    <name>Intensity Projection plugin</name>
+    <description>Intensity projection along depth or time with multiple algorithms: mean, max, median, variance, standard deviation, saturated sum. Projection can be restricted to ROI.</description>
+    <url>http://icy.bioimageanalysis.org/plugin/intensity-projection</url>
+    <inceptionYear>2020</inceptionYear>
+
+    <organization>
+        <name>Institut Pasteur - BIA</name>
+        <url>http://icy.bioimageanalysis.org</url>
+    </organization>
+
+    <licenses>
+        <license>
+            <name>GNU GPLv3</name>
+            <url>https://www.gnu.org/licenses/gpl-3.0.en.html</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <developers>
+        <developer>
+            <id>adufour</id>
+            <name>Alexandre Dufour</name>
+            <roles>
+                <role>developer</role>
+                <role>debugger</role>
+                <role>tester</role>
+                <role>maintainer</role>
+                <role>support</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <!-- Project properties -->
+    <properties>
+
+    </properties>
+
+    <!-- Project build configuration -->
+    <build>
+
+    </build>
+
+    <!-- List of project's dependencies -->
+    <dependencies>
+        <!-- The core of Icy -->
+        <dependency>
+            <groupId>org.bioimageanalysis.icy</groupId>
+            <artifactId>icy-kernel</artifactId>
+        </dependency>
+
+        <!-- The EzPlug library, simplifies writing UI for Icy plugins. -->
+        <dependency>
+            <groupId>org.bioimageanalysis.icy</groupId>
+            <artifactId>ezplug</artifactId>
+        </dependency>
+
+        <!-- The EzPlug library, simplifies writing UI for Icy plugins. -->
+        <dependency>
+            <groupId>org.bioimageanalysis.icy</groupId>
+            <artifactId>protocols</artifactId>
+            <version>${protocols.version}</version>
+        </dependency>
+    </dependencies>
+
+    <!-- Icy Maven repository (to find parent POM) -->
+    <repositories>
+        <repository>
+            <id>icy</id>
+            <name>Icy's Nexus</name>
+            <url>https://icy-nexus.pasteur.fr/repository/Icy/</url>
+        </repository>
+    </repositories>
+</project>
diff --git a/src/main/java/plugins/adufour/projection/Projection.java b/src/main/java/plugins/adufour/projection/Projection.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e85f6af86e56e5c12124dc545874309a42f6030
--- /dev/null
+++ b/src/main/java/plugins/adufour/projection/Projection.java
@@ -0,0 +1,688 @@
+package plugins.adufour.projection;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import icy.image.IcyBufferedImage;
+import icy.main.Icy;
+import icy.math.ArrayMath;
+import icy.plugin.PluginLauncher;
+import icy.plugin.PluginLoader;
+import icy.roi.ROI;
+import icy.sequence.Sequence;
+import icy.sequence.SequenceUtil;
+import icy.system.SystemUtil;
+import icy.type.DataType;
+import icy.type.collection.array.Array1DUtil;
+import plugins.adufour.blocks.lang.Block;
+import plugins.adufour.blocks.util.VarList;
+import plugins.adufour.ezplug.EzPlug;
+import plugins.adufour.ezplug.EzStoppable;
+import plugins.adufour.ezplug.EzVarBoolean;
+import plugins.adufour.ezplug.EzVarEnum;
+import plugins.adufour.ezplug.EzVarSequence;
+import plugins.adufour.vars.lang.VarSequence;
+
+public class Projection extends EzPlug implements Block, EzStoppable
+{
+    public enum ProjectionDirection
+    {
+        Z, T
+    }
+
+    public enum ProjectionType
+    {
+        MAX("Maximum"), MEAN("Average"), MED("Median"), MIN("Minimum"), STD("Standard Deviation"),
+        SATSUM("Saturated Sum");
+
+        private final String description;
+
+        ProjectionType(String description)
+        {
+            this.description = description;
+        }
+
+        @Override
+        public String toString()
+        {
+            return description;
+        }
+    }
+
+    private final EzVarSequence input = new EzVarSequence("Input");
+
+    private final EzVarEnum<ProjectionDirection> projectionDir = new EzVarEnum<Projection.ProjectionDirection>(
+            "Project along", ProjectionDirection.values(), ProjectionDirection.Z);
+
+    private final EzVarEnum<ProjectionType> projectionType = new EzVarEnum<Projection.ProjectionType>("Projection type",
+            ProjectionType.values(), ProjectionType.MAX);
+
+    private final EzVarBoolean restrictToROI = new EzVarBoolean("Restrict to ROI", false);
+
+    private final VarSequence output = new VarSequence("projected sequence", null);
+
+    @Override
+    protected void initialize()
+    {
+        addEzComponent(input);
+        addEzComponent(projectionDir);
+        addEzComponent(projectionType);
+
+        restrictToROI.setToolTipText(
+                "Check this option to project only the intensity data contained within the sequence ROI");
+        addEzComponent(restrictToROI);
+
+        setTimeDisplay(true);
+    }
+
+    @Override
+    protected void execute()
+    {
+        switch (projectionDir.getValue())
+        {
+            case T:
+                output.setValue(
+                        tProjection(input.getValue(true), projectionType.getValue(), true, restrictToROI.getValue()));
+                break;
+            case Z:
+                output.setValue(
+                        zProjection(input.getValue(true), projectionType.getValue(), true, restrictToROI.getValue()));
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                        "Projection along " + projectionDir.getValue() + " not supported");
+        }
+
+        if (getUI() != null)
+            addSequence(output.getValue());
+    }
+
+    @Override
+    public void clean()
+    {
+
+    }
+
+    /**
+     * Performs a Z projection of the input sequence using the specified algorithm. If the sequence
+     * is already 2D, then a copy of the sequence is returned
+     * 
+     * @param in
+     *        the sequence to project
+     * @param projection
+     *        the type of projection to perform (see {@link ProjectionType} enumeration)
+     * @param multiThread
+     *        true if the process should be multi-threaded
+     * @return the projected sequence
+     */
+    public static Sequence zProjection(final Sequence in, final ProjectionType projection, boolean multiThread)
+    {
+        return zProjection(in, projection, multiThread, false);
+    }
+
+    /**
+     * Performs a Z projection of the input sequence using the specified algorithm. If the sequence
+     * is already 2D, then a copy of the sequence is returned
+     * 
+     * @param in
+     *        the sequence to project
+     * @param projection
+     *        the type of projection to perform (see {@link ProjectionType} enumeration)
+     * @param multiThread
+     *        true if the process should be multi-threaded
+     * @param restrictToROI
+     *        <code>true</code> projects only data located within the sequence ROI,
+     *        <code>false</code> projects the entire data set
+     * @return the projected sequence
+     */
+    public static Sequence zProjection(final Sequence in, final ProjectionType projection, boolean multiThread,
+            boolean restrictToROI)
+    {
+        final int depth = in.getSizeZ();
+        if (depth == 1 && !restrictToROI)
+            return SequenceUtil.getCopy(in);
+
+        final Sequence out = new Sequence(projection.name() + " projection of " + in.getName());
+        out.copyMetaDataFrom(in, false);
+
+        final int width = in.getSizeX();
+        final int height = in.getSizeY();
+        final int frames = in.getSizeT();
+        final int channels = in.getSizeC();
+        final DataType dataType = in.getDataType_();
+
+        final Collection<ROI> rois = in.getROISet();
+        final boolean processROI = restrictToROI && (rois.size() > 0);
+        final int cpus = SystemUtil.getNumberOfCPUs();
+
+        ExecutorService service = multiThread ? Executors.newFixedThreadPool(cpus)
+                : Executors.newSingleThreadExecutor();
+        ArrayList<Future<?>> futures = new ArrayList<Future<?>>();
+
+        for (int frame = 0; frame < frames; frame++)
+        {
+            if (Thread.currentThread().isInterrupted())
+            {
+                // stop all task now
+                service.shutdownNow();
+                break;
+            }
+
+            final int t = frame;
+
+            // set new image in result sequence
+            out.setImage(t, 0, new IcyBufferedImage(width, height, channels, dataType));
+
+            for (int channel = 0; channel < channels; channel++)
+            {
+                final int c = channel;
+                // to optimize image access in main loop !
+                final IcyBufferedImage[] imagesZ = in.getImages(t).toArray(new IcyBufferedImage[0]);
+
+                futures.add(service.submit(new Runnable()
+                {
+                    @Override
+                    public void run()
+                    {
+                        final IcyBufferedImage resultImg = out.getImage(t, 0);
+                        final Object resultData = resultImg.getDataXY(c);
+                        int offset = 0;
+
+                        for (int y = 0; y < height; y++)
+                        {
+                            for (int x = 0; x < width; x++, offset++)
+                            {
+                                double[] stackPixels = new double[depth];
+                                int nbPixel = 0;
+
+                                for (int z = 0; z < depth; z++)
+                                {
+                                    boolean processPixel;
+
+                                    if (processROI)
+                                    {
+                                        processPixel = false;
+
+                                        for (ROI roi : rois)
+                                        {
+                                            if (roi.contains(x, y, z, t, c))
+                                            {
+                                                processPixel = true;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                    else
+                                        processPixel = true;
+
+                                    if (processPixel)
+                                        stackPixels[nbPixel++] = imagesZ[z].getData(x, y, c);
+                                }
+
+                                if (nbPixel == 0)
+                                    continue;
+
+                                // adjust pixel array size if needed
+                                stackPixels = Arrays.copyOf(stackPixels, nbPixel);
+
+                                double result = 0d;
+
+                                switch (projection)
+                                {
+                                    case MAX:
+                                        result = ArrayMath.max(stackPixels);
+                                        break;
+                                    case MEAN:
+                                        result = ArrayMath.mean(stackPixels);
+                                        break;
+                                    case MED:
+                                        result = ArrayMath.median(stackPixels, false);
+                                        break;
+                                    case MIN:
+                                        result = ArrayMath.min(stackPixels);
+                                        break;
+                                    case STD:
+                                        result = ArrayMath.std(stackPixels, true);
+                                        break;
+                                    case SATSUM:
+                                        result = ArrayMath.sum(stackPixels);
+                                        break;
+                                    default:
+                                        throw new UnsupportedOperationException(
+                                                projection + " intensity projection not implemented");
+                                }
+
+                                // set result in data array
+                                Array1DUtil.setValue(resultData, offset, dataType, result);
+                            }
+
+                            // task interrupted ?
+                            if (Thread.currentThread().isInterrupted())
+                            {
+                                // propagate partial changes and stop here
+                                resultImg.setDataXY(c, resultData);
+                                return;
+                            }
+                        }
+
+                        // data changed and cache update
+                        resultImg.setDataXY(c, resultData);
+                    }
+                }));
+            }
+        }
+
+        try
+        {
+            for (Future<?> future : futures)
+                future.get();
+        }
+        catch (InterruptedException e)
+        {
+            // ignore
+            service.shutdownNow();
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+
+        service.shutdown();
+
+        // Copy color map information
+        for (int c = 0; c < in.getSizeC(); c++)
+            out.getColorModel().setColorMap(c, in.getColorMap(c), true);
+
+        return out;
+    }
+
+    /**
+     * Performs a Z projection of the input sequence using the specified algorithm. If the sequence
+     * is already 2D, then a copy of the sequence is returned
+     * 
+     * @param in
+     *        the sequence to project
+     * @param projection
+     *        the type of projection to perform (see {@link ProjectionType} enumeration)
+     * @param multiThread
+     *        true if the process should be multi-threaded
+     * @param restrictToROI
+     *        <code>true</code> projects only data located within the sequence ROI,
+     *        <code>false</code> projects the entire data set
+     * @return the projected sequence
+     */
+    public static Sequence zProjection_(final Sequence in, final ProjectionType projection, boolean multiThread,
+            boolean restrictToROI)
+    {
+        final int depth = in.getSizeZ();
+        if (depth == 1 && !restrictToROI)
+            return SequenceUtil.getCopy(in);
+
+        final Sequence out = new Sequence(projection.name() + " projection of " + in.getName());
+        out.copyMetaDataFrom(in, false);
+
+        final int width = in.getSizeX();
+        final int height = in.getSizeY();
+        final int frames = in.getSizeT();
+        final int channels = in.getSizeC();
+        final DataType dataType = in.getDataType_();
+
+        final Collection<ROI> rois = in.getROISet();
+        final boolean processROI = restrictToROI && rois.size() > 0;
+
+        int cpus = SystemUtil.getNumberOfCPUs();
+        int chunkSize = width * height / cpus;
+        final int[] minOffsets = new int[cpus];
+        final int[] maxOffsets = new int[cpus];
+        for (int cpu = 0; cpu < cpus; cpu++)
+        {
+            minOffsets[cpu] = chunkSize * cpu;
+            maxOffsets[cpu] = chunkSize * (cpu + 1);
+        }
+        // NB: the last chunk must include the remaining pixels (in case rounding off occurs)
+        maxOffsets[cpus - 1] = width * height;
+
+        ExecutorService service = multiThread ? Executors.newFixedThreadPool(cpus)
+                : Executors.newSingleThreadExecutor();
+        ArrayList<Future<?>> futures = new ArrayList<Future<?>>(channels * frames * cpus);
+
+        for (int frame = 0; frame < frames; frame++)
+        {
+            final int t = frame;
+
+            if (Thread.currentThread().isInterrupted())
+                break;
+
+            out.setImage(t, 0, new IcyBufferedImage(width, height, channels, dataType));
+
+            for (int channel = 0; channel < channels; channel++)
+            {
+                final int c = channel;
+                final Object[] in_Z_XY = (Object[]) in.getDataXYZ(t, c);
+                final Object out_Z_XY = out.getDataXY(t, 0, c);
+
+                for (int cpu = 0; cpu < cpus; cpu++)
+                {
+                    final int minOffset = minOffsets[cpu];
+                    final int maxOffset = maxOffsets[cpu];
+
+                    futures.add(service.submit(new Runnable()
+                    {
+                        @Override
+                        public void run()
+                        {
+                            double[] buffer = new double[depth];
+                            double[] dataToProject = null;
+
+                            for (int offset = minOffset; offset < maxOffset; offset++)
+                            {
+                                if (processROI)
+                                {
+                                    int x = offset % width;
+                                    int y = offset / width;
+
+                                    int nbValues = 0;
+
+                                    for (int z = 0; z < depth; z++)
+                                        for (ROI roi : rois)
+                                            if (roi.contains(x, y, z, t, c))
+                                            {
+                                                buffer[nbValues++] = Array1DUtil.getValue(in_Z_XY[z], offset, dataType);
+                                                break;
+                                            }
+
+                                    if (nbValues == 0)
+                                        continue;
+
+                                    dataToProject = (nbValues == buffer.length) ? buffer
+                                            : Arrays.copyOf(buffer, nbValues);
+                                }
+                                else
+                                {
+                                    for (int z = 0; z < depth; z++)
+                                        buffer[z] = Array1DUtil.getValue(in_Z_XY[z], offset, dataType);
+                                    dataToProject = buffer;
+                                }
+
+                                switch (projection)
+                                {
+                                    case MAX:
+                                        Array1DUtil.setValue(out_Z_XY, offset, dataType, ArrayMath.max(dataToProject));
+                                        break;
+                                    case MEAN:
+                                        Array1DUtil.setValue(out_Z_XY, offset, dataType, ArrayMath.mean(dataToProject));
+                                        break;
+                                    case MED:
+                                        Array1DUtil.setValue(out_Z_XY, offset, dataType,
+                                                ArrayMath.median(dataToProject, false));
+                                        break;
+                                    case MIN:
+                                        Array1DUtil.setValue(out_Z_XY, offset, dataType, ArrayMath.min(dataToProject));
+                                        break;
+                                    case STD:
+                                        Array1DUtil.setValue(out_Z_XY, offset, dataType,
+                                                ArrayMath.std(dataToProject, true));
+                                        break;
+                                    case SATSUM:
+                                        Array1DUtil.setValue(out_Z_XY, offset, dataType,
+                                                Math.min(ArrayMath.sum(dataToProject), dataType.getMaxValue()));
+                                        break;
+                                    default:
+                                        throw new UnsupportedOperationException(
+                                                projection + " intensity projection not implemented");
+                                }
+                            } // offset
+                        }
+                    }));
+                }
+            }
+        }
+
+        try
+        {
+            for (Future<?> future : futures)
+                future.get();
+        }
+        catch (InterruptedException iE)
+        {
+            Thread.currentThread().interrupt();
+        }
+        catch (ExecutionException eE)
+        {
+            throw new RuntimeException(eE);
+        }
+
+        service.shutdown();
+
+        // Copy color map information
+        for (int c = 0; c < in.getSizeC(); c++)
+            out.getColorModel().setColorMap(c, in.getColorMap(c), true);
+
+        out.dataChanged();
+
+        return out;
+    }
+
+    /**
+     * Performs a T projection of the input sequence using the specified algorithm. If the sequence
+     * has only one time point, then a copy of the sequence is returned
+     * 
+     * @param in
+     *        the sequence to project
+     * @param projection
+     *        the type of projection to perform (see {@link ProjectionType} enumeration)
+     * @param multiThread
+     *        true if the process should be multi-threaded
+     * @return the projected sequence
+     */
+    public static Sequence tProjection(final Sequence in, final ProjectionType projection, boolean multiThread)
+    {
+        return tProjection(in, projection, multiThread, false);
+    }
+
+    /**
+     * Performs a T projection of the input sequence using the specified algorithm. If the sequence
+     * has only one time point, then a copy of the sequence is returned
+     * 
+     * @param in
+     *        the sequence to project
+     * @param projection
+     *        the type of projection to perform (see {@link ProjectionType} enumeration)
+     * @param multiThread
+     *        true if the process should be multi-threaded
+     * @param restrictToROI
+     *        <code>true</code> projects only data located within the sequence ROI,
+     *        <code>false</code> projects the entire data set
+     * @return the projected sequence
+     */
+    public static Sequence tProjection(final Sequence in, final ProjectionType projection, boolean multiThread,
+            boolean restrictToROI)
+    {
+        final int frames = in.getSizeT();
+        if (frames == 1 && !restrictToROI)
+            return SequenceUtil.getCopy(in);
+
+        final Sequence out = new Sequence(projection.name() + " projection of " + in.getName());
+        out.copyMetaDataFrom(in, false);
+
+        final int width = in.getSizeX();
+        final int height = in.getSizeY();
+        final int depth = in.getSizeZ();
+        final int channels = in.getSizeC();
+        final DataType dataType = in.getDataType_();
+
+        final Collection<ROI> rois = in.getROISet();
+        final boolean processROI = restrictToROI && (rois.size() > 0);
+        final int cpus = SystemUtil.getNumberOfCPUs();
+
+        ExecutorService service = multiThread ? Executors.newFixedThreadPool(cpus)
+                : Executors.newSingleThreadExecutor();
+        ArrayList<Future<?>> futures = new ArrayList<Future<?>>();
+
+        for (int slice = 0; slice < depth; slice++)
+        {
+            if (Thread.currentThread().isInterrupted())
+            {
+                // stop all task now
+                service.shutdownNow();
+                break;
+            }
+
+            final int z = slice;
+
+            // set new image in result sequence
+            out.setImage(0, z, new IcyBufferedImage(width, height, channels, dataType));
+
+            for (int channel = 0; channel < channels; channel++)
+            {
+                final int c = channel;
+
+                futures.add(service.submit(new Runnable()
+                {
+                    @Override
+                    public void run()
+                    {
+                        final IcyBufferedImage resultImg = out.getImage(0, z);
+                        final Object resultData = resultImg.getDataXY(c);
+                        int offset = 0;
+
+                        for (int y = 0; y < height; y++)
+                        {
+                            for (int x = 0; x < width; x++, offset++)
+                            {
+                                double[] framePixels = new double[frames];
+                                int nbPixel = 0;
+
+                                for (int t = 0; t < frames; t++)
+                                {
+                                    boolean processPixel;
+
+                                    if (processROI)
+                                    {
+                                        processPixel = false;
+
+                                        for (ROI roi : rois)
+                                        {
+                                            if (roi.contains(x, y, z, t, c))
+                                            {
+                                                processPixel = true;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                    else
+                                        processPixel = true;
+
+                                    if (processPixel)
+                                        framePixels[nbPixel++] = in.getData(t, z, c, y, x);
+                                }
+
+                                if (nbPixel == 0)
+                                    continue;
+
+                                // adjust pixel array size if needed
+                                framePixels = Arrays.copyOf(framePixels, nbPixel);
+
+                                double result = 0d;
+
+                                switch (projection)
+                                {
+                                    case MAX:
+                                        result = ArrayMath.max(framePixels);
+                                        break;
+                                    case MEAN:
+                                        result = ArrayMath.mean(framePixels);
+                                        break;
+                                    case MED:
+                                        result = ArrayMath.median(framePixels, false);
+                                        break;
+                                    case MIN:
+                                        result = ArrayMath.min(framePixels);
+                                        break;
+                                    case STD:
+                                        result = ArrayMath.std(framePixels, true);
+                                        break;
+                                    case SATSUM:
+                                        result = ArrayMath.sum(framePixels);
+                                        break;
+                                    default:
+                                        throw new UnsupportedOperationException(
+                                                projection + " intensity projection not implemented");
+                                }
+
+                                // set result in data array
+                                Array1DUtil.setValue(resultData, offset, dataType, result);
+                            }
+
+                            // task interrupted ?
+                            if (Thread.currentThread().isInterrupted())
+                            {
+                                // propagate partial changes and stop here
+                                resultImg.setDataXY(c, resultData);
+                                return;
+                            }
+                        }
+
+                        // data changed and cache update
+                        resultImg.setDataXY(c, resultData);
+                    }
+                }));
+            }
+        }
+
+        try
+        {
+            for (Future<?> future : futures)
+                future.get();
+        }
+        catch (InterruptedException e)
+        {
+            service.shutdownNow();
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+
+        service.shutdown();
+
+        // Copy color map information
+        for (int c = 0; c < in.getSizeC(); c++)
+            out.getColorModel().setColorMap(c, in.getColorMap(c), true);
+
+        return out;
+    }
+
+    @Override
+    public void declareInput(VarList inputMap)
+    {
+        inputMap.add("input", input.getVariable());
+        inputMap.add("projection direction", projectionDir.getVariable());
+        inputMap.add("projection type", projectionType.getVariable());
+        inputMap.add("restrict to ROI", restrictToROI.getVariable());
+    }
+
+    @Override
+    public void declareOutput(VarList outputMap)
+    {
+        outputMap.add("projection output", output);
+    }
+
+    /**
+     * @param args
+     *        input args
+     */
+    public static void main(String[] args)
+    {
+        // start icy
+        Icy.main(args);
+
+        // then start plugin
+        PluginLauncher.start(PluginLoader.getPlugin(Projection.class.getName()));
+    }
+}