diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3d47f986c41db29ec6dc0d5036bf760b3a1cf366 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/ +target/ +.settings/ +*.iml +.project +.classpath \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..e61cd1639e0e43c1147a81f2a40564739ff73cb3 --- /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.3</version> + </parent> + + <!-- Project Information --> + <artifactId>scale-bar</artifactId> + <version>3.1.6</version> + + <packaging>jar</packaging> + + <name>Scale Bar</name> + <description> + Displays a scale bar overlay on the sequence. + Warning: this plugin needs correct sequence metadata to be effective. Otherwise it will display wrong values. + </description> + <url>http://icy.bioimageanalysis.org/plugin/scale-bar/</url> + <inceptionYear>2020</inceptionYear> + + <organization> + <name>Institut Pasteur</name> + <url>https://pasteur.fr</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>sdallongeville</id> + <name>Stéphane Dallongeville</name> + <url>https://research.pasteur.fr/fr/member/stephane-dallongeville/</url> + <roles> + <role>founder</role> + <role>lead</role> + <role>architect</role> + <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> + </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> \ No newline at end of file diff --git a/src/main/java/plugins/tprovoost/scale/Scale.java b/src/main/java/plugins/tprovoost/scale/Scale.java new file mode 100644 index 0000000000000000000000000000000000000000..af5f315e38bb02b0c8311a2d50de3a06eea6d881 --- /dev/null +++ b/src/main/java/plugins/tprovoost/scale/Scale.java @@ -0,0 +1,56 @@ +package plugins.tprovoost.scale; + +import icy.gui.main.GlobalSequenceListener; +import icy.main.Icy; +import icy.plugin.abstract_.Plugin; +import icy.plugin.interface_.PluginDaemon; +import icy.sequence.Sequence; + +public class Scale extends Plugin implements PluginDaemon +{ + ScalePainter sp; + GlobalSequenceListener globalSequenceListener; + + @Override + public void run() + { + // nothing to do here + } + + @Override + public void stop() + { + for (Sequence sequence : Icy.getMainInterface().getSequences()) + sequence.removeOverlay(sp); + + Icy.getMainInterface().removeGlobalSequenceListener(globalSequenceListener); + } + + @Override + public void init() + { + // create painter + sp = new ScalePainter(); + globalSequenceListener = new GlobalSequenceListener() + { + @Override + public void sequenceOpened(Sequence sequence) + { + sequence.addOverlay(sp); + } + + @Override + public void sequenceClosed(Sequence sequence) + { + sequence.removeOverlay(sp); + } + }; + + // add the Scale Bar overlay on opened sequences + for (Sequence sequence : Icy.getMainInterface().getSequences()) + sequence.addOverlay(sp); + + // listen new opened sequence + Icy.getMainInterface().addGlobalSequenceListener(globalSequenceListener); + } +} diff --git a/src/main/java/plugins/tprovoost/scale/ScalePainter.java b/src/main/java/plugins/tprovoost/scale/ScalePainter.java new file mode 100644 index 0000000000000000000000000000000000000000..af4f9ce1b0e19fd317131b0a09148e3a9d765b2e --- /dev/null +++ b/src/main/java/plugins/tprovoost/scale/ScalePainter.java @@ -0,0 +1,159 @@ +package plugins.tprovoost.scale; + +import icy.canvas.IcyCanvas; +import icy.canvas.IcyCanvas2D; +import icy.math.MathUtil; +import icy.math.UnitUtil; +import icy.math.UnitUtil.UnitPrefix; +import icy.painter.Overlay; +import icy.sequence.Sequence; +import icy.util.GraphicsUtil; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; + +public class ScalePainter extends Overlay +{ + /** + * Possible rounded scale factors : 1 --> 900 + */ + final static double[] scaleRoundedFactors = new double[] {1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d, 10d, 20d, 30d, 40d, + 50d, 60d, 70d, 80d, 90d, 100d, 200d, 300d, 400d, 500d, 600d, 700d, 800d, 900d}; + + public ScalePainter() + { + super("Scale Bar"); + } + + @Override + public void paint(Graphics2D g2, Sequence sequence, IcyCanvas canvas) + { + if (g2 == null) + return; + if (!(canvas instanceof IcyCanvas2D)) + return; + + final IcyCanvas2D c2d = (IcyCanvas2D) canvas; + final Graphics2D g = (Graphics2D) g2.create(); + + double scaleFactor = c2d.getScaleX(); + g.transform(c2d.getInverseTransform()); + + BasicStroke[] stroke = new BasicStroke[4]; + stroke[0] = new BasicStroke(2f); + stroke[1] = new BasicStroke(3f); + stroke[2] = new BasicStroke(4f); + stroke[3] = new BasicStroke(5f); + int w = canvas.getCanvasSizeX(); + int h = canvas.getCanvasSizeY(); + double pxSize = sequence.getPixelSizeX(); + int space = 20; // spacing from borders + + int sizeW = w / 8; + // get the value with the best display possible + double valueReal = sizeW * pxSize / scaleFactor / 10; + UnitPrefix bestUnit = UnitUtil.getBestUnit(valueReal, UnitPrefix.MICRO, 1); + double valueRealBestUnit = UnitUtil.getValueInUnit(valueReal * 10, UnitPrefix.MICRO, bestUnit); + + double closestScale = MathUtil.closest(valueRealBestUnit, scaleRoundedFactors); + + // closest scale in px + int closestScPx = (int) (UnitUtil.getValueInUnit(closestScale, bestUnit, UnitPrefix.MICRO) / pxSize * scaleFactor); + // int closestScPx = closestScale; + + g.setColor(Color.BLACK); + g.setStroke(stroke[3]); + // g.drawLine(w - space - closestScPx, h - space, w - space, h - space); + g.drawLine(space, h - space, space + closestScPx, h - space); + + g.setColor(Color.WHITE); + g.setStroke(stroke[2]); + g.drawLine(space, h - space, space + closestScPx, h - space); + + int fontSize = 14; + Font font = new Font("Arial", Font.PLAIN, fontSize); + g.setFont(font); + String pixelString = "" + closestScale + " " + bestUnit + "m"; + int txW = g.getFontMetrics().charsWidth(pixelString.toCharArray(), 0, pixelString.length()); + int txH = g.getFontMetrics().getHeight(); + + g.setColor(Color.black); + // GraphicsUtil.drawString(g, pixelString, w - space - closestScPx / 2 - + // txW / 2, h - space - txH, true); + GraphicsUtil.drawString(g, pixelString, space + closestScPx / 2 - txW / 2, h - space - txH, true); + + g.dispose(); + } + + double convertScale(IcyCanvas canvas, double value) + { + return canvas.canvasToImageLogDeltaX((int) value); + } + + public class CompositeStroke implements Stroke + { + private Stroke stroke1, stroke2; + + public CompositeStroke(Stroke stroke1, Stroke stroke2) + { + this.stroke1 = stroke1; + this.stroke2 = stroke2; + } + + @Override + public Shape createStrokedShape(Shape shape) + { + return stroke2.createStrokedShape(stroke1.createStrokedShape(shape)); + } + } + +// private int findBestScale(IcyCanvas2D canvas, Sequence s) +// { +// int w = s.getWidth(); +// int minSize = w / 8; +// int maxSize = w / 4; +// double pxSize = s.getPixelSizeX(); +// double tab[] = new double[maxSize - minSize]; +// double scaleFactor = canvas.getScaleX(); +// +// // generate all values +// for (int i = minSize; i < maxSize; ++i) +// { +// +// // get the value with the best display possible +// double valueReal = i * pxSize / scaleFactor; +// UnitPrefix bestUnit = UnitUtil.getBestUnit(valueReal, UnitPrefix.MICRO, 1); +// double bestValue = UnitUtil.getValueInUnit(valueReal, UnitPrefix.MICRO, bestUnit); +// bestValue = ((int) (bestValue * 100)) / 100d; +// +// tab[i - minSize] = bestValue; +// } +// +// // return the first value with a round value +// for (int i = 0; i < maxSize - minSize; ++i) +// { +// double bestValue = tab[i]; +// if (bestValue == (int) bestValue) +// { +// return i + minSize; +// } +// } +// +// // otherwise, return first value with only one decimal +// for (int i = 0; i < maxSize - minSize; ++i) +// { +// double bestValue = tab[i]; +// if (bestValue == ((int) (bestValue * 10)) / 10d) +// { +// return i + minSize; +// } +// } +// +// // otherwise, return w / 4 +// return w / 4; +// } +} diff --git a/src/main/java/plugins/tprovoost/scale/ScalePainterOld.java b/src/main/java/plugins/tprovoost/scale/ScalePainterOld.java new file mode 100644 index 0000000000000000000000000000000000000000..caf0c37945f25f8037de04c34b7d146ec29fdd2a --- /dev/null +++ b/src/main/java/plugins/tprovoost/scale/ScalePainterOld.java @@ -0,0 +1,105 @@ +package plugins.tprovoost.scale; + +import icy.canvas.IcyCanvas; +import icy.canvas.IcyCanvas2D; +import icy.math.UnitUtil; +import icy.math.UnitUtil.UnitPrefix; +import icy.painter.Overlay; +import icy.roi.ROI2D; +import icy.sequence.Sequence; +import icy.util.GraphicsUtil; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; + +public class ScalePainterOld extends Overlay +{ + public ScalePainterOld() + { + super("Scale Bar"); + } + + @Override + public void paint(Graphics2D g2, Sequence sequence, IcyCanvas canvas) + { + if (g2 == null) + return; + Graphics2D g = (Graphics2D) g2.create(); + // g.transform(((IcyCanvas2D) canvas).getInverseTransform()); + int w = sequence.getWidth(); + int h = sequence.getHeight(); + int sizeW = w / 10; + double pxSize = sequence.getPixelSizeX(); + int spaceFBords = 20; // spacing from borders + + BasicStroke[] stroke = new BasicStroke[4]; + stroke[0] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(2)); + stroke[1] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(3)); + stroke[2] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(4)); + stroke[3] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(5)); + + // get the value with the best display possible + double valueReal = sizeW * pxSize / 10; + UnitPrefix bestUnit = UnitUtil.getBestUnit(valueReal, UnitPrefix.MICRO, 1); + double bestValue = UnitUtil.getValueInUnit(valueReal * 10, UnitPrefix.MICRO, bestUnit); + + // get the best scale possible + int closestScale = 0; + if (bestValue < 10) + { + closestScale = (int) bestValue; + } else + closestScale = (int) (bestValue / 10) * 10; + + // closest scale in px + int closestScPx = (int) (UnitUtil.getValueInUnit(closestScale, bestUnit, UnitPrefix.MICRO) / pxSize); + + g.setColor(Color.BLACK); + g.setStroke(stroke[3]); + g.drawLine(w - spaceFBords - closestScPx, h - spaceFBords, w - spaceFBords, h - spaceFBords); + + g.setColor(Color.WHITE); + g.setStroke(stroke[2]); + g.drawLine(w - spaceFBords - closestScPx, h - spaceFBords, w - spaceFBords, h - spaceFBords); + + int fontSize = (int) canvas.canvasToImageLogDeltaX(14); + Font font = new Font("Arial", Font.PLAIN, fontSize); + g.setFont(font); + String pixelString = "" + closestScale + " " + bestUnit + "m"; + int txW = g.getFontMetrics().charsWidth(pixelString.toCharArray(), 0, pixelString.length()); + int txH = g.getFontMetrics().getHeight(); + + // g.fillRect(w - spaceFBords - closestScPx / 2 - txW / 2, h - + // spaceFBords - txH, txW, txH); + + g.setColor(Color.black); + GraphicsUtil.drawString(g, pixelString, w - spaceFBords - closestScPx / 2 - txW / 2, h - spaceFBords - txH, true); + + g.dispose(); + } + + double convertScale(IcyCanvas canvas, double value) + { + return canvas.canvasToImageLogDeltaX((int) value); + } + + public class CompositeStroke implements Stroke + { + private Stroke stroke1, stroke2; + + public CompositeStroke(Stroke stroke1, Stroke stroke2) + { + this.stroke1 = stroke1; + this.stroke2 = stroke2; + } + + public Shape createStrokedShape(Shape shape) + { + return stroke2.createStrokedShape(stroke1.createStrokedShape(shape)); + } + } +} diff --git a/src/main/resources/Scale.png b/src/main/resources/Scale.png new file mode 100644 index 0000000000000000000000000000000000000000..2227c16fad9f21113d628a11d5b912a4f42f1431 Binary files /dev/null and b/src/main/resources/Scale.png differ diff --git a/src/main/resources/Scale.xml b/src/main/resources/Scale.xml new file mode 100644 index 0000000000000000000000000000000000000000..9a16e0be213bfede02c8022c11ac819abd1d518d --- /dev/null +++ b/src/main/resources/Scale.xml @@ -0,0 +1,78 @@ +<?xml version='1.0' encoding='UTF-8' standalone='no'?> +<root> +<url><![CDATA[http://icy.bioimageanalysis.org/repository/getXMLPluginFile.php?pluginId=179&beta=0]]></url><name>Scale Bar</name><version>2.0.0.0</version><required_kernel_version>1.3.6.0</required_kernel_version><kernel_ver>1.3.6.0</kernel_ver><jar_url><![CDATA[http://icy.bioimageanalysis.org/repository/getJarFile.php?pluginId=179&beta=0]]></jar_url><icon_url><![CDATA[http://bioimageanalysis.org/icy/image.php?idAttach=1229]]></icon_url><image_url><![CDATA[http://bioimageanalysis.org/icy/image.php?idAttach=2020]]></image_url><description><![CDATA[Displays a scale bar overlay on the sequence. Be advised, this plugin needs correct Metadata on the sequence to be effective or it will display wrong values.]]></description><classname><![CDATA[plugins.tprovoost.scale.Scale]]></classname><author><![CDATA[tprovoost - Thomas Provoost]]></author><changelog><![CDATA[- +Version 2.0.0.0 +Date 2013-07-03 12:15:38 + +* Bottom left now. This will make it avoid being invisible because of the zoom factor is displayed at the same time +* the scale bar now fully works + +- +Version 1.6.0.0 +Date 2013-07-03 12:02:48 + +* Uses orders of magnitude instead of precise values. +* size of scale bar never changes now: will always be : size of the canvas / 8. + +- +Version 1.5.0.0 +Date 2013-06-26 16:52:26 + +* Now only updates text while zooming, and change the size and value after the zoom + +- +Version 1.4.0.0 +Date 2013-06-26 11:20:13 + +* fixed bar location issue + +- +Version 1.3.1.0 +Date 2013-06-17 15:18:23 + +* removed debug information + +- +Version 1.3.0.0 +Date 2013-06-17 15:17:46 + +* now scales with images zoom + +- +Version 1.2.0.0 +Date 2013-06-17 14:15:50 + +* removed white rectangle +* now shadow on text + +- +Version 1.1.0.0 +Date 2013-01-11 16:30:30 + +Updated to 1.3.0.0 + +- +Version 1.0.0.2 +Date 2012-10-22 17:10:43 + +* corrected 3D issue. + +- +Version 1.0.0.1 +Date 2012-07-03 09:23:11 + +Corrected issue with compilation. + +- +Version 1.0.0.0 +Date 2012-07-02 21:17:10 + +Updated to Icy last version. + +- +Version 0.0.1.0 +Date 2012-06-20 14:33:49 + + + +]]></changelog><web><![CDATA[http://icy.bioimageanalysis.org/plugin/Scale_Bar]]></web><dependencies></dependencies></root> \ No newline at end of file diff --git a/src/main/resources/Scale_icon.png b/src/main/resources/Scale_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..46567ed34a2c5de77c00a72963814c1866260f07 Binary files /dev/null and b/src/main/resources/Scale_icon.png differ