From 583eb5989fdc685b97f5578b1aa5ad8d575da617 Mon Sep 17 00:00:00 2001 From: Albert Cardona Date: Mon, 13 Apr 2009 19:28:18 +0200 Subject: [PATCH] Moved lots of math and geometry functions to class M --- ini/trakem2/display/AreaList.java | 12 +-- ini/trakem2/display/Display.java | 2 +- ini/trakem2/display/Dissector.java | 11 +-- ini/trakem2/display/Patch.java | 7 +- ini/trakem2/display/Pipe.java | 3 +- ini/trakem2/display/Profile.java | 5 +- ini/trakem2/display/Selection.java | 3 +- ini/trakem2/utils/M.java | 174 +++++++++++++++++++++++++++++++++++++ ini/trakem2/utils/Utils.java | 162 ---------------------------------- 9 files changed, 198 insertions(+), 181 deletions(-) diff --git a/ini/trakem2/display/AreaList.java b/ini/trakem2/display/AreaList.java index 7cc4f456..ac87df89 100644 --- a/ini/trakem2/display/AreaList.java +++ b/ini/trakem2/display/AreaList.java @@ -1381,7 +1381,7 @@ public class AreaList extends ZDisplayable { public void add(final long layer_id, final ShapeRoi roi) throws NoninvertibleTransformException{ if (null == roi) return; Area a = getArea(layer_id); - Area asr = Utils.getArea(roi).createTransformedArea(this.at.createInverse()); + Area asr = M.getArea(roi).createTransformedArea(this.at.createInverse()); if (null == a) { ht_areas.put(layer_id, asr); } else { @@ -1395,7 +1395,7 @@ public class AreaList extends ZDisplayable { if (null == roi) return; Area a = getArea(layer_id); if (null == a) return; - a.subtract(Utils.getArea(roi).createTransformedArea(this.at.createInverse())); + a.subtract(M.getArea(roi).createTransformedArea(this.at.createInverse())); calculateBoundingBox(); updateInDatabase("points=" + layer_id); } @@ -1403,14 +1403,14 @@ public class AreaList extends ZDisplayable { /** Subtracts the given ROI, and then creates a new AreaList with identical properties and the content of the subtracted part. Returns null if there is no intersection between sroi and the Area for layer_id. */ public AreaList part(final long layer_id, final ShapeRoi sroi) throws NoninvertibleTransformException { // The Area to subtract, in world coordinates: - Area sub = Utils.getArea(sroi); + Area sub = M.getArea(sroi); // The area to subtract from: Area a = getArea(layer_id); - if (null == a || Utils.isEmpty(a)) return null; + if (null == a || M.isEmpty(a)) return null; // The intersection: Area inter = a.createTransformedArea(this.at); inter.intersect(sub); - if (Utils.isEmpty(inter)) return null; + if (M.isEmpty(inter)) return null; // Subtract from this: this.subtract(layer_id, sroi); @@ -1470,7 +1470,7 @@ public class AreaList extends ZDisplayable { Roi roi = dc.getFakeImagePlus().getRoi(); if (null == roi) return; // Check ROI - if (!Utils.isAreaROI(roi)) { + if (!M.isAreaROI(roi)) { Utils.log("AreaList only accepts region ROIs, not lines."); return; } diff --git a/ini/trakem2/display/Display.java b/ini/trakem2/display/Display.java index 6fa9818f..4d38fb07 100644 --- a/ini/trakem2/display/Display.java +++ b/ini/trakem2/display/Display.java @@ -2358,7 +2358,7 @@ public final class Display extends DBObject implements ActionListener, ImageList public void actionPerformed(final ActionEvent ae) { final String command = ae.getActionCommand(); - final java.awt.geom.Area aroi = Utils.getArea(d.canvas.getFakeImagePlus().getRoi()); + final java.awt.geom.Area aroi = M.getArea(d.canvas.getFakeImagePlus().getRoi()); d.dispatcher.exec(new Runnable() { public void run() { diff --git a/ini/trakem2/display/Dissector.java b/ini/trakem2/display/Dissector.java index 66e5fe15..ede69eb7 100644 --- a/ini/trakem2/display/Dissector.java +++ b/ini/trakem2/display/Dissector.java @@ -29,6 +29,7 @@ import ij.measure.ResultsTable; import ini.trakem2.Project; import ini.trakem2.utils.IJError; import ini.trakem2.utils.ProjectToolbar; +import ini.trakem2.utils.M; import ini.trakem2.utils.Utils; import ini.trakem2.utils.Search; import ini.trakem2.persistence.DBObject; @@ -240,8 +241,8 @@ public class Dissector extends ZDisplayable { // Convert point from local to world coordinates //Point2D.Double po = transformPoint(p[0][i], p[1][i]); // Convert point from world to screen coordinates - //po = Utils.transform(gt, po.x, po.y); - final Point2D.Double po = Utils.transform(aff, p[0][i], p[1][i]); + //po = M.transform(gt, po.x, po.y); + final Point2D.Double po = M.transform(aff, p[0][i], p[1][i]); final int px = (int)po.x; final int py = (int)po.y; g.drawOval(px - M_radius, py - M_radius, M_radius+M_radius, M_radius+M_radius); @@ -253,8 +254,8 @@ public class Dissector extends ZDisplayable { // Convert point to world coordinates //Point2D.Double po = transformPoint(p[0][paint_i], p[1][paint_i]); // Convert point to screen coordinates - //po = Utils.transform(gt, po.x, po.y); - final Point2D.Double po = Utils.transform(aff, p[0][paint_i], p[1][paint_i]); + //po = M.transform(gt, po.x, po.y); + final Point2D.Double po = M.transform(aff, p[0][paint_i], p[1][paint_i]); final int px = (int)po.x; final int py = (int)po.y; g.drawRect(px - M_radius, py - M_radius, M_radius+M_radius, M_radius+M_radius); @@ -269,7 +270,7 @@ public class Dissector extends ZDisplayable { Utils.log("Dissector.addResults: could not find layer with id " + p_layer[i]); continue; } - final Point2D.Double po = Utils.transform(Dissector.this.at, p[0][i], p[1][i]); + final Point2D.Double po = M.transform(Dissector.this.at, p[0][i], p[1][i]); rt.incrementCounter(); rt.addLabel("units", cal.getUnit()); rt.addValue(0, Dissector.this.id); diff --git a/ini/trakem2/display/Patch.java b/ini/trakem2/display/Patch.java index f14a2bf8..cde85428 100644 --- a/ini/trakem2/display/Patch.java +++ b/ini/trakem2/display/Patch.java @@ -32,6 +32,7 @@ import ij.process.ByteProcessor; import ij.process.ImageProcessor; import ini.trakem2.Project; import ini.trakem2.imaging.PatchStack; +import ini.trakem2.utils.M; import ini.trakem2.utils.Utils; import ini.trakem2.utils.IJError; import ini.trakem2.utils.Search; @@ -1250,7 +1251,7 @@ public final class Patch extends Displayable { case KeyEvent.VK_F: // fill mask with current ROI using Utils.log2("VK_F: roi is " + roi); - if (null != roi && Utils.isAreaROI(roi)) { + if (null != roi && M.isAreaROI(roi)) { Bureaucrat.createAndStart(new Worker("Filling image mask") { public void run() { try { startedWorking(); ByteProcessor mask = project.getLoader().fetchImageMask(Patch.this); @@ -1264,9 +1265,9 @@ public final class Patch extends Displayable { try { // a roi local to the image bounding box final Area a = new Area(new Rectangle(0, 0, (int)width, (int)height)); - a.intersect(Utils.getArea(roi).createTransformedArea(Patch.this.at.createInverse())); + a.intersect(M.getArea(roi).createTransformedArea(Patch.this.at.createInverse())); - if (Utils.isEmpty(a)) { + if (M.isEmpty(a)) { Utils.log("ROI does not intersect the active image!"); return; } diff --git a/ini/trakem2/display/Pipe.java b/ini/trakem2/display/Pipe.java index 2be3e5da..2da57cb2 100644 --- a/ini/trakem2/display/Pipe.java +++ b/ini/trakem2/display/Pipe.java @@ -29,6 +29,7 @@ import ini.trakem2.Project; import ini.trakem2.utils.IJError; import ini.trakem2.utils.ProjectToolbar; import ini.trakem2.utils.Utils; +import ini.trakem2.utils.M; import ini.trakem2.utils.Search; import ini.trakem2.utils.Vector3; import ini.trakem2.persistence.DBObject; @@ -2015,7 +2016,7 @@ public class Pipe extends ZDisplayable implements Line3D { public boolean intersects(final Layer layer, final Area area) { final Polygon[] pol = getSubPerimeters(layer); // transformed if (null == pol) return false; - for (Polygon p : pol) if (Utils.intersects(new Area(p), area)) return true; + for (Polygon p : pol) if (M.intersects(new Area(p), area)) return true; return false; } diff --git a/ini/trakem2/display/Profile.java b/ini/trakem2/display/Profile.java index 30da2daf..e11fe65b 100644 --- a/ini/trakem2/display/Profile.java +++ b/ini/trakem2/display/Profile.java @@ -31,6 +31,7 @@ import ini.trakem2.persistence.DBObject; import ini.trakem2.persistence.Loader; import ini.trakem2.utils.ProjectToolbar; import ini.trakem2.utils.Utils; +import ini.trakem2.utils.M; import ini.trakem2.utils.IJError; import ini.trakem2.render3d.Perimeter2D; import ini.trakem2.display.Display3D; @@ -1608,7 +1609,7 @@ public class Profile extends Displayable { if (!closed) return 0; if (0 == p_i[0].length) generateInterpolatedPoints(0.05); Calibration cal = getLayerSet().getCalibration(); - return Utils.measureArea(new Area(getPerimeter()), getProject().getLoader()) * cal.pixelWidth * cal.pixelHeight; + return M.measureArea(new Area(getPerimeter()), getProject().getLoader()) * cal.pixelWidth * cal.pixelHeight; } /** Measures the calibrated length, the lateral surface as the length times the layer thickness, and the volume (if closed) as the area times the layer thickness. */ @@ -1652,7 +1653,7 @@ public class Profile extends Displayable { // Surface: calibrated sum of the area of all triangles in the mesh. double surface = 0; for (int i=2; i 2048 || bounds.height > 2048) { // TODO value 2048 should be reconsidered as a project property + scale = 2048.0 / bounds.width; + } + if (0 == scale) { + Utils.log("Can't measure: area too large, out of scale range for approximation."); + return sum; + } + AffineTransform at = new AffineTransform(); + at.translate(-bounds.x, -bounds.y); + at.scale(scale, scale); + area = area.createTransformedArea(at); + bounds = area.getBounds(); + if (0 == bounds.width || 0 == bounds.height) { + Utils.log("Can't measure: area too large, approximates zero."); + return sum; + } + if (null != loader) loader.releaseToFit(bounds.width * bounds.height * 3); + BufferedImage bi = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_BYTE_INDEXED); + Graphics2D g = bi.createGraphics(); + g.setColor(Color.white); + g.fill(area); + final byte[] pixels = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); // buffer.getData(); + for (int i=pixels.length-1; i>-1; i--) { + //if (255 == (pixels[i]&0xff)) sum++; + if (0 != pixels[i]) sum++; + } + bi.flush(); + g.dispose(); + if (1 != scale) sum = sum / (scale * scale); + } catch (Throwable e) { + IJError.print(e); + } + return sum; + } + + /** Compute the area of the triangle defined by 3 points in 3D space, returning half of the length of the vector resulting from the cross product of vectors p1p2 and p1p3. */ + static public final double measureArea(final Point3f p1, final Point3f p2, final Point3f p3) { + final Vector3f v = new Vector3f(); + v.cross(new Vector3f(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z), + new Vector3f(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z)); + return 0.5 * Math.abs(v.x * v.x + v.y * v.y + v.z * v.z); + } + + /** Returns true if the roi is of closed shape type like an OvalRoi, ShapeRoi, a Roi rectangle, etc. */ + static public final boolean isAreaROI(final Roi roi) { + switch (roi.getType()) { + case Roi.POLYLINE: + case Roi.FREELINE: + case Roi.LINE: + case Roi.POINT: + return false; + } + return true; + } + + static public final Collection getPolygons(Area area) { + final ArrayList pols = new ArrayList(); + Polygon pol = new Polygon(); + + final float[] coords = new float[6]; + for (PathIterator pit = area.getPathIterator(null); !pit.isDone(); ) { + int seg_type = pit.currentSegment(coords); + switch (seg_type) { + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + pol.addPoint((int)coords[0], (int)coords[1]); + break; + case PathIterator.SEG_CLOSE: + pols.add(pol); + pol = new Polygon(); + break; + default: + Utils.log2("WARNING: unhandled seg type."); + break; + } + pit.next(); + if (pit.isDone()) { + break; + } + } + return pols; + } + + static private Field shape_field = null; + + static public final Shape getShape(final ShapeRoi roi) { + try { + if (null == shape_field) { + shape_field = ShapeRoi.class.getDeclaredField("shape"); + shape_field.setAccessible(true); + } + return (Shape)shape_field.get(roi); + } catch (Exception e) { + IJError.print(e); + } + return null; + } + + } diff --git a/ini/trakem2/utils/Utils.java b/ini/trakem2/utils/Utils.java index 869ccd9f..12826438 100644 --- a/ini/trakem2/utils/Utils.java +++ b/ini/trakem2/utils/Utils.java @@ -38,8 +38,6 @@ import ij.Menus; import ij.WindowManager; import ij.gui.GenericDialog; import ij.gui.YesNoCancelDialog; -import ij.gui.Roi; -import ij.gui.ShapeRoi; import ij.gui.OvalRoi; import ij.text.TextWindow; import ij.measure.ResultsTable; @@ -55,17 +53,6 @@ import java.awt.Component; import java.awt.MenuBar; import java.awt.Menu; import java.awt.MenuItem; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.NoninvertibleTransformException; -import java.awt.geom.Area; -import java.awt.Rectangle; -import java.awt.Polygon; -import java.awt.geom.PathIterator; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferByte; import java.io.*; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; @@ -87,9 +74,6 @@ import java.util.Map; import java.util.regex.Pattern; import java.util.regex.Matcher; -import javax.vecmath.Point3f; -import javax.vecmath.Vector3f; - /** Utils class: stores generic widely used methods. In particular, those for logging text messages (for debugging) and also some math and memory utilities. * * @@ -1058,25 +1042,6 @@ public class Utils implements ij.plugin.PlugIn { }); } - static public final Point2D.Double transform(final AffineTransform affine, final double x, final double y) { - final Point2D.Double pSrc = new Point2D.Double(x, y); - if (affine.isIdentity()) return pSrc; - final Point2D.Double pDst = new Point2D.Double(); - affine.transform(pSrc, pDst); - return pDst; - } - static public final Point2D.Double inverseTransform(final AffineTransform affine, final double x, final double y) { - final Point2D.Double pSrc = new Point2D.Double(x, y); - if (affine.isIdentity()) return pSrc; - final Point2D.Double pDst = new Point2D.Double(); - try { - affine.createInverse().transform(pSrc, pDst); - } catch (NoninvertibleTransformException nite) { - IJError.print(nite); - } - return pDst; - } - /** Returns the time as HH:MM:SS */ static public final String now() { /* Java time management is retarded. */ @@ -1124,14 +1089,6 @@ public class Utils implements ij.plugin.PlugIn { return Color.getHSBColor(a[0], a[1], a[2]); } - /** Test whether the areas intersect each other. */ - static public final boolean intersects(final Area a1, final Area a2) { - final Area b = new Area(a1); - b.intersect(a2); - final java.awt.Rectangle r = b.getBounds(); - return 0 != r.width && 0 != r.height; - } - /** 1 A, 2 B, 3 C --- 26 - z, 27 AA, 28 AB, 29 AC --- 26*27 AAA */ static public final String getCharacter(int i) { i--; @@ -1141,21 +1098,6 @@ public class Utils implements ij.plugin.PlugIn { return new StringBuffer().append(getCharacter(k)).append(c).toString(); } - static private Field shape_field = null; - - static public final Shape getShape(final ShapeRoi roi) { - try { - if (null == shape_field) { - shape_field = ShapeRoi.class.getDeclaredField("shape"); - shape_field.setAccessible(true); - } - return (Shape)shape_field.get(roi); - } catch (Exception e) { - IJError.print(e); - } - return null; - } - /** Get by reflection a private or protected field in the given object. */ static public final Object getField(final Object ob, final String field_name) { if (null == ob || null == field_name) return null; @@ -1169,110 +1111,6 @@ public class Utils implements ij.plugin.PlugIn { return null; } - static public final Area getArea(final Roi roi) { - if (null == roi) return null; - ShapeRoi sroi = new ShapeRoi(roi); - AffineTransform at = new AffineTransform(); - Rectangle bounds = sroi.getBounds(); - at.translate(bounds.x, bounds.y); - Area area = new Area(getShape(sroi)); - return area.createTransformedArea(at); - } - - static public final boolean isEmpty(final Area area) { - final Rectangle b = area.getBounds(); - return 0 == b.width || 0 == b.height; - } - - /** Returns the approximated area of the given Area object. */ - static public final double measureArea(Area area, final Loader loader) { - double sum = 0; - try { - Rectangle bounds = area.getBounds(); - double scale = 1; - if (bounds.width > 2048 || bounds.height > 2048) { - scale = 2048.0 / bounds.width; - } - if (0 == scale) { - Utils.log("Can't measure: area too large, out of scale range for approximation."); - return sum; - } - AffineTransform at = new AffineTransform(); - at.translate(-bounds.x, -bounds.y); - at.scale(scale, scale); - area = area.createTransformedArea(at); - bounds = area.getBounds(); - if (0 == bounds.width || 0 == bounds.height) { - Utils.log("Can't measure: area too large, approximates zero."); - return sum; - } - if (null != loader) loader.releaseToFit(bounds.width * bounds.height * 3); - BufferedImage bi = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_BYTE_INDEXED); - Graphics2D g = bi.createGraphics(); - g.setColor(Color.white); - g.fill(area); - final byte[] pixels = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData(); // buffer.getData(); - for (int i=pixels.length-1; i>-1; i--) { - //if (255 == (pixels[i]&0xff)) sum++; - if (0 != pixels[i]) sum++; - } - bi.flush(); - g.dispose(); - if (1 != scale) sum = sum / (scale * scale); - } catch (Throwable e) { - IJError.print(e); - } - return sum; - } - - /** Compute the area of the triangle defined by 3 points in 3D space, returning half of the length of the vector resulting from the cross product of vectors p1p2 and p1p3. */ - static public final double measureArea(final Point3f p1, final Point3f p2, final Point3f p3) { - final Vector3f v = new Vector3f(); - v.cross(new Vector3f(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z), - new Vector3f(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z)); - return 0.5 * Math.abs(v.x * v.x + v.y * v.y + v.z * v.z); - } - - /** Returns true if the roi is of closed shape type like an OvalRoi, ShapeRoi, a Roi rectangle, etc. */ - static public final boolean isAreaROI(final Roi roi) { - switch (roi.getType()) { - case Roi.POLYLINE: - case Roi.FREELINE: - case Roi.LINE: - case Roi.POINT: - return false; - } - return true; - } - - static public final Collection getPolygons(Area area) { - final ArrayList pols = new ArrayList(); - Polygon pol = new Polygon(); - - final float[] coords = new float[6]; - for (PathIterator pit = area.getPathIterator(null); !pit.isDone(); ) { - int seg_type = pit.currentSegment(coords); - switch (seg_type) { - case PathIterator.SEG_MOVETO: - case PathIterator.SEG_LINETO: - pol.addPoint((int)coords[0], (int)coords[1]); - break; - case PathIterator.SEG_CLOSE: - pols.add(pol); - pol = new Polygon(); - break; - default: - Utils.log2("WARNING: unhandled seg type."); - break; - } - pit.next(); - if (pit.isDone()) { - break; - } - } - return pols; - } - /** A method that circumvents the findMinAndMax when creating a float processor from an existing processor. Ignores color calibrations and does no scaling at all. */ static public final FloatProcessor fastConvertToFloat(final ByteProcessor ip) { final byte[] pix = (byte[])ip.getPixels(); -- 2.11.4.GIT