Java tutorial
/////////////////////////////////////////////////////////////////////////////// // AUTHOR: Henry Pinkard, henry.pinkard@gmail.com // // COPYRIGHT: University of California, San Francisco, 2015 // // LICENSE: This file is distributed under the BSD license. // License text is included with the source distribution. // // This file 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. // // IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES. // package org.micromanager.plugins.magellan.surfacesandregions; import org.micromanager.plugins.magellan.acq.FixedAreaAcquisitionSettings; import org.micromanager.plugins.magellan.bidc.JavaLayerImageConstructor; import org.micromanager.plugins.magellan.coordinates.AffineUtils; import org.micromanager.plugins.magellan.coordinates.XYStagePosition; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.TreeSet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import org.micromanager.plugins.magellan.main.Magellan; import org.micromanager.plugins.magellan.misc.Log; import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D; import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet; import org.apache.commons.math3.geometry.euclidean.twod.Vector2D; import org.apache.commons.math3.geometry.euclidean.twod.hull.ConvexHull2D; import org.apache.commons.math3.geometry.euclidean.twod.hull.MonotoneChain; import org.apache.commons.math3.geometry.partitioning.Region; import org.apache.commons.math3.geometry.partitioning.RegionFactory; import org.micromanager.plugins.magellan.propsandcovariants.CovariantPairingsManager; import org.micromanager.plugins.magellan.propsandcovariants.SurfaceData; /** * * @author Henry */ public abstract class SurfaceInterpolator implements XYFootprint { public static final int MIN_PIXELS_PER_INTERP_POINT = 2; public static final int NUM_XY_TEST_POINTS = 8; private static final int ABOVE_SURFACE = 0; private static final int BELOW_SURFACE = 1; private static final int ON_SURFACE = 1; private String name_; //surface coordinates are neccessarily associated with the coordinate space of particular xy and z devices private final String xyDeviceName_, zDeviceName_; private final boolean towardsSampleIsPositive_; protected volatile TreeSet<Point3d> points_; private MonotoneChain mChain_; private final RegionFactory<Euclidean2D> regionFacotry_ = new RegionFactory<Euclidean2D>(); protected volatile Vector2D[] convexHullVertices_; protected volatile Region<Euclidean2D> convexHullRegion_; private volatile int numRows_, numCols_; private volatile List<XYStagePosition> xyPositions_; private volatile double xyPadding_um_ = 0; protected volatile double boundXMin_, boundXMax_, boundYMin_, boundYMax_; private volatile int boundXPixelMin_, boundXPixelMax_, boundYPixelMin_, boundYPixelMax_; private ExecutorService executor_; protected volatile SingleResolutionInterpolation currentInterpolation_; private SurfaceManager manager_; private Future currentInterpolationTask_; //Objects for wait/notify sync of calcualtions protected Object xyPositionLock_ = new Object(), interpolationLock_ = new Object(), convexHullLock_ = new Object(); public SurfaceInterpolator(String xyDevice, String zDevice) { manager_ = SurfaceManager.getInstance(); name_ = manager_.getNewName(); xyDeviceName_ = xyDevice; zDeviceName_ = zDevice; //store points sorted by z coordinate to easily find the top, for generating slice index 0 position points_ = new TreeSet<Point3d>(new Comparator<Point3d>() { @Override public int compare(Point3d p1, Point3d p2) { if (p1.z > p2.z) { return 1; } else if (p1.z < p2.z) { return -1; } else { if (p1.x > p2.x) { return 1; } else if (p1.x < p2.x) { return -1; } else { if (p1.y > p2.y) { return 1; } else if (p1.y < p2.y) { return -1; } else { return 0; } } } } }); mChain_ = new MonotoneChain(true); executor_ = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "Interpolation calculation thread "); } }); try { int dir = Magellan.getCore().getFocusDirection(zDevice); if (dir > 0) { towardsSampleIsPositive_ = true; } else if (dir < 0) { towardsSampleIsPositive_ = false; } else { throw new Exception(); } } catch (Exception e) { Log.log("Couldn't get focus direction of Z drive. Configre using Tools--Hardware Configuration Wizard"); throw new RuntimeException(); } } @Override public String getXYDevice() { return xyDeviceName_; } public String getZDevice() { return zDeviceName_; } public String getCurrentPixelSizeConfig() { try { return Magellan.getCore().getCurrentPixelSizeConfig(); } catch (Exception ex) { Log.log("couldnt get pixel size config"); throw new RuntimeException(); } } public void delete() { executor_.shutdownNow(); CovariantPairingsManager.getInstance().deletePairsReferencingSurface(this); } @Override public String toString() { return name_; } public String getName() { return name_; } public void rename(String newName) { name_ = newName; } /** * Returns the number of XY positions spanned by the footprint of this surface * @return */ public int getNumPositions() { if (xyPositions_ == null) { return -1; } return xyPositions_.size(); } public double getXYPadding() { return xyPadding_um_; } public void setXYPadding(double pad) { xyPadding_um_ = pad; synchronized (xyPositionLock_) { xyPositions_ = null; } updateXYPositionsOnly(FixedAreaAcquisitionSettings.getStoredTileOverlapPercentage()); } /** * Blocks until convex hull vertices have been calculated * @return * @throws InterruptedException */ public Vector2D[] getConvexHullPoints() throws InterruptedException { // block until convex hull points available synchronized (convexHullLock_) { while (convexHullVertices_ == null) { convexHullLock_.wait(); } return convexHullVertices_; } } @Override public List<XYStagePosition> getXYPositionsNoUpdate() { return xyPositions_; } @Override public List<XYStagePosition> getXYPositions(double overlap) throws InterruptedException { synchronized (xyPositionLock_) { updateXYPositionsOnly(overlap); while (xyPositions_ == null) { xyPositionLock_.wait(); } return xyPositions_; } } public SingleResolutionInterpolation waitForCurentInterpolation() throws InterruptedException { synchronized (interpolationLock_) { if (currentInterpolation_ == null) { while (currentInterpolation_ == null) { interpolationLock_.wait(); } return currentInterpolation_; } return currentInterpolation_; } } public boolean isDefinedAtPosition(XYStagePosition position) { //create square region correpsonding to stage pos Region<Euclidean2D> square = getStagePositionRegion(position); //if convex hull and position have no intersection, delete Region<Euclidean2D> intersection = regionFacotry_.intersection(square, convexHullRegion_); if (intersection.isEmpty()) { return false; } else { return true; } } /** * tests whether any part of XY stage position is lies above the interpolated surface * * @return true if every part of position is above surface, false otherwise */ public boolean isPositionCompletelyAboveSurface(XYStagePosition pos, SurfaceInterpolator surface, double zPos, boolean extrapolate) throws InterruptedException { return testPositionRelativeToSurface(pos, surface, zPos, ABOVE_SURFACE, extrapolate); } /** * tests whether any part of XY stage position is lies above the interpolated surface * * @return true if every part of position is above surface, false otherwise */ public boolean isPositionCompletelyBelowSurface(XYStagePosition pos, SurfaceInterpolator surface, double zPos, boolean extrapolate) throws InterruptedException { return testPositionRelativeToSurface(pos, surface, zPos, BELOW_SURFACE, extrapolate); } /** * test whether XY position is completely abve or completely below surface * @throws InterruptedException */ public boolean testPositionRelativeToSurface(XYStagePosition pos, SurfaceInterpolator surface, double zPos, int mode, boolean extrapolate) throws InterruptedException { //get the corners with padding added in Point2D.Double[] corners = getPositionCornersWithPadding(pos, surface.xyPadding_um_); //First check position corners before going into a more detailed set of test points for (Point2D.Double point : corners) { float interpVal; if (!surface.waitForCurentInterpolation().isInterpDefined(point.x, point.y)) { if (extrapolate) { interpVal = surface.getExtrapolatedValue(point.x, point.y); } else { continue; } } else { interpVal = surface.waitForCurentInterpolation().getInterpolatedValue(point.x, point.y); } if ((towardsSampleIsPositive_ && mode == ABOVE_SURFACE && zPos >= interpVal) || (towardsSampleIsPositive_ && mode == BELOW_SURFACE && zPos <= interpVal) || (!towardsSampleIsPositive_ && mode == ABOVE_SURFACE && zPos <= interpVal) || (!towardsSampleIsPositive_ && mode == BELOW_SURFACE) && zPos >= interpVal) { return false; } } //then check a grid of points spanning entire position //9x9 square of points to check for each position //square is aligned with axes in pixel space, so convert to pixel space to generate test points double xSpan = corners[2].getX() - corners[0].getX(); double ySpan = corners[2].getY() - corners[0].getY(); Point2D.Double pixelSpan = new Point2D.Double(); AffineTransform transform = AffineUtils.getAffineTransform(getCurrentPixelSizeConfig(), 0, 0); try { transform.inverseTransform(new Point2D.Double(xSpan, ySpan), pixelSpan); } catch (NoninvertibleTransformException ex) { Log.log("Problem inverting affine transform"); } outerloop: for (double x = 0; x <= pixelSpan.x; x += pixelSpan.x / (double) NUM_XY_TEST_POINTS) { for (double y = 0; y <= pixelSpan.y; y += pixelSpan.y / (double) NUM_XY_TEST_POINTS) { //convert these abritray pixel coordinates back to stage coordinates double[] transformMaxtrix = new double[6]; transform.getMatrix(transformMaxtrix); transformMaxtrix[4] = corners[0].getX(); transformMaxtrix[5] = corners[0].getY(); //create new transform with translation applied transform = new AffineTransform(transformMaxtrix); Point2D.Double stageCoords = new Point2D.Double(); transform.transform(new Point2D.Double(x, y), stageCoords); //test point for inclusion of position float interpVal; if (!surface.waitForCurentInterpolation().isInterpDefined(stageCoords.x, stageCoords.y)) { if (extrapolate) { interpVal = surface.getExtrapolatedValue(stageCoords.x, stageCoords.y); } else { continue; } } else { interpVal = surface.waitForCurentInterpolation().getInterpolatedValue(stageCoords.x, stageCoords.y); } if ((towardsSampleIsPositive_ && mode == ABOVE_SURFACE && zPos >= interpVal) || (towardsSampleIsPositive_ && mode == BELOW_SURFACE && zPos <= interpVal) || (!towardsSampleIsPositive_ && mode == ABOVE_SURFACE && zPos <= interpVal) || (!towardsSampleIsPositive_ && mode == BELOW_SURFACE) && zPos >= interpVal) { return false; } } } return true; } /** * figure out which of the positions need to be collected at a given slice * Assumes positions_ contains list of all possible positions for fitting * block until interpolation is detailed enough to calculate stage positions * @param zPos */ public ArrayList<XYStagePosition> getXYPositonsAtSlice(double zPos, boolean above) throws InterruptedException { SingleResolutionInterpolation interp = waitForCurentInterpolation(); double overlapPercent = FixedAreaAcquisitionSettings.getStoredTileOverlapPercentage() / 100; int overlapX = (int) (JavaLayerImageConstructor.getInstance().getImageWidth() * overlapPercent); int overlapY = (int) (JavaLayerImageConstructor.getInstance().getImageHeight() * overlapPercent); int tileWidth = JavaLayerImageConstructor.getInstance().getImageWidth() - overlapX; int tileHeight = JavaLayerImageConstructor.getInstance().getImageHeight() - overlapY; while (interp.getPixelsPerInterpPoint() >= Math.max(tileWidth, tileHeight) / NUM_XY_TEST_POINTS) { synchronized (interpolationLock_) { interpolationLock_.wait(); } interp = waitForCurentInterpolation(); } ArrayList<XYStagePosition> positionsAtSlice = new ArrayList<XYStagePosition>(); for (XYStagePosition pos : xyPositions_) { if (!above) { if (!isPositionCompletelyAboveSurface(pos, this, zPos, false)) { //not completely above = below positionsAtSlice.add(pos); } } else { if (!isPositionCompletelyBelowSurface(pos, this, zPos, false)) { // not completely below = above positionsAtSlice.add(pos); } } } return positionsAtSlice; } private static Point2D.Double[] getPositionCornersWithPadding(XYStagePosition pos, double xyPadding) { if (xyPadding == 0) { return pos.getDisplayedTileCorners(); } else { //expand to bigger square to acount for padding //make two lines that criss cross the smaller square Point2D.Double[] corners = pos.getDisplayedTileCorners(); double diagonalLength = new Vector2D(corners[0].x, corners[0].y) .distance(new Vector2D(corners[2].x, corners[2].y)); Vector2D center = new Vector2D(pos.getCenter().x, pos.getCenter().y); Point2D.Double[] paddedCorners = new Point2D.Double[4]; Vector2D c0 = center.add(xyPadding + 0.5 * diagonalLength, new Vector2D(corners[0].x - corners[2].x, corners[0].y - corners[2].y).normalize()); Vector2D c1 = center.add(xyPadding + 0.5 * diagonalLength, new Vector2D(corners[1].x - corners[3].x, corners[1].y - corners[3].y).normalize()); Vector2D c2 = center.add(xyPadding + 0.5 * diagonalLength, new Vector2D(corners[2].x - corners[0].x, corners[2].y - corners[0].y).normalize()); Vector2D c3 = center.add(xyPadding + 0.5 * diagonalLength, new Vector2D(corners[3].x - corners[1].x, corners[3].y - corners[1].y).normalize()); paddedCorners[0] = new Point2D.Double(c0.getX(), c0.getY()); paddedCorners[1] = new Point2D.Double(c1.getX(), c1.getY()); paddedCorners[2] = new Point2D.Double(c2.getX(), c2.getY()); paddedCorners[3] = new Point2D.Double(c3.getX(), c3.getY()); return paddedCorners; } } /** * Create a 2D square region corresponding to the the stage position + any extra padding * @param pos * @return */ private Region<Euclidean2D> getStagePositionRegion(XYStagePosition pos) { Region<Euclidean2D> square; Point2D.Double[] corners = pos.getDisplayedTileCorners(); if (xyPadding_um_ == 0) { square = new PolygonsSet(0.0001, new Vector2D[] { new Vector2D(corners[0].x, corners[0].y), new Vector2D(corners[1].x, corners[1].y), new Vector2D(corners[2].x, corners[2].y), new Vector2D(corners[3].x, corners[3].y) }); } else { //expand to bigger square to acount for padding //make two lines that criss cross the smaller square double diagonalLength = new Vector2D(corners[0].x, corners[0].y) .distance(new Vector2D(corners[2].x, corners[2].y)); Vector2D center = new Vector2D(pos.getCenter().x, pos.getCenter().y); square = new PolygonsSet(0.0001, new Vector2D[] { center.add(xyPadding_um_ + 0.5 * diagonalLength, new Vector2D(corners[0].x - corners[2].x, corners[0].y - corners[2].y) .normalize()), center.add(xyPadding_um_ + 0.5 * diagonalLength, new Vector2D(corners[1].x - corners[3].x, corners[1].y - corners[3].y) .normalize()), center.add(xyPadding_um_ + 0.5 * diagonalLength, new Vector2D(corners[2].x - corners[0].x, corners[2].y - corners[0].y) .normalize()), center.add(xyPadding_um_ + 0.5 * diagonalLength, new Vector2D(corners[3].x - corners[1].x, corners[3].y - corners[1].y) .normalize()) }); } return square.checkPoint(new Vector2D(pos.getCenter().x, pos.getCenter().y)) == Region.Location.OUTSIDE ? regionFacotry_.getComplement(square) : square; } private void calculateConvexHullBounds() { //convert convex hull vertices to pixel offsets in an arbitrary pixel space AffineTransform transform = AffineUtils.getAffineTransform(getCurrentPixelSizeConfig(), 0, 0); boundYPixelMin_ = Integer.MAX_VALUE; boundYPixelMax_ = Integer.MIN_VALUE; boundXPixelMin_ = Integer.MAX_VALUE; boundXPixelMax_ = Integer.MIN_VALUE; boundXMin_ = Double.MAX_VALUE; boundXMax_ = Double.MIN_VALUE; boundYMin_ = Double.MAX_VALUE; boundYMax_ = Double.MIN_VALUE; for (int i = 0; i < convexHullVertices_.length; i++) { //calculate edges of interpolation bounding box //for later use by interpolating function boundXMin_ = Math.min(boundXMin_, convexHullVertices_[i].getX()); boundXMax_ = Math.max(boundXMax_, convexHullVertices_[i].getX()); boundYMin_ = Math.min(boundYMin_, convexHullVertices_[i].getY()); boundYMax_ = Math.max(boundYMax_, convexHullVertices_[i].getY()); //also get pixel bounds of convex hull for fitting of XY positions double dx = convexHullVertices_[i].getX() - convexHullVertices_[0].getX(); double dy = convexHullVertices_[i].getY() - convexHullVertices_[0].getY(); Point2D.Double pixelOffset = new Point2D.Double(); // pixel offset from convex hull vertex 0; try { transform.inverseTransform(new Point2D.Double(dx, dy), pixelOffset); } catch (NoninvertibleTransformException ex) { Log.log("Problem inverting affine transform"); } boundYPixelMin_ = (int) Math.min(boundYPixelMin_, pixelOffset.y); boundYPixelMax_ = (int) Math.max(boundYPixelMax_, pixelOffset.y); boundXPixelMin_ = (int) Math.min(boundXPixelMin_, pixelOffset.x); boundXPixelMax_ = (int) Math.max(boundXPixelMax_, pixelOffset.x); } } protected abstract void interpolateSurface(LinkedList<Point3d> points) throws InterruptedException; /** * calculated ad hoc unlike interpolated values which are cached */ public abstract float getExtrapolatedValue(double x, double y); private void fitXYPositionsToConvexHull(double overlap) throws InterruptedException { int fullTileWidth = JavaLayerImageConstructor.getInstance().getImageWidth(); int fullTileHeight = JavaLayerImageConstructor.getInstance().getImageHeight(); int overlapX = (int) (JavaLayerImageConstructor.getInstance().getImageWidth() * overlap / 100); int overlapY = (int) (JavaLayerImageConstructor.getInstance().getImageHeight() * overlap / 100); int tileWidthMinusOverlap = fullTileWidth - overlapX; int tileHeightMinusOverlap = fullTileHeight - overlapY; int pixelPadding = (int) (xyPadding_um_ / Magellan.getCore().getPixelSizeUm()); numRows_ = (int) Math .ceil((boundYPixelMax_ - boundYPixelMin_ + pixelPadding) / (double) tileHeightMinusOverlap); numCols_ = (int) Math .ceil((boundXPixelMax_ - boundXPixelMin_ + pixelPadding) / (double) tileWidthMinusOverlap); //take center of bounding box and create grid int pixelCenterX = boundXPixelMin_ + (boundXPixelMax_ - boundXPixelMin_) / 2; int pixelCenterY = boundYPixelMin_ + (boundYPixelMax_ - boundYPixelMin_) / 2; AffineTransform transform = AffineUtils.getAffineTransform(getCurrentPixelSizeConfig(), 0, 0); ArrayList<XYStagePosition> positions = new ArrayList<XYStagePosition>(); Point2D.Double gridCenterStageCoords = new Point2D.Double(); transform.transform(new Point2D.Double(pixelCenterX, pixelCenterY), gridCenterStageCoords); gridCenterStageCoords.x += convexHullVertices_[0].getX(); gridCenterStageCoords.y += convexHullVertices_[0].getY(); //set affine transform translation relative to grid center double[] transformMaxtrix = new double[6]; transform.getMatrix(transformMaxtrix); transformMaxtrix[4] = gridCenterStageCoords.x; transformMaxtrix[5] = gridCenterStageCoords.y; //create new transform with translation applied transform = new AffineTransform(transformMaxtrix); //add all positions of rectangle around convex hull for (int col = 0; col < numCols_; col++) { double xPixelOffset = (col - (numCols_ - 1) / 2.0) * (tileWidthMinusOverlap); for (int row = 0; row < numRows_; row++) { if (Thread.interrupted()) { throw new InterruptedException(); } double yPixelOffset = (row - (numRows_ - 1) / 2.0) * (tileHeightMinusOverlap); Point2D.Double pixelPos = new Point2D.Double(xPixelOffset, yPixelOffset); Point2D.Double stagePos = new Point2D.Double(); transform.transform(pixelPos, stagePos); AffineTransform posTransform = AffineUtils.getAffineTransform(getCurrentPixelSizeConfig(), stagePos.x, stagePos.y); positions.add(new XYStagePosition(stagePos, tileWidthMinusOverlap, tileHeightMinusOverlap, fullTileWidth, fullTileHeight, row, col, posTransform)); } } //delete positions squares (+padding) that do not overlap convex hull for (int i = positions.size() - 1; i >= 0; i--) { if (Thread.interrupted()) { throw new InterruptedException(); } XYStagePosition pos = positions.get(i); //create square region correpsonding to stage pos Region<Euclidean2D> square = getStagePositionRegion(pos); //if convex hull and position have no intersection, delete Region<Euclidean2D> intersection = regionFacotry_.intersection(square, convexHullRegion_); if (intersection.isEmpty()) { positions.remove(i); } square.getBoundarySize(); } if (Thread.interrupted()) { throw new InterruptedException(); } synchronized (xyPositionLock_) { xyPositions_ = positions; xyPositionLock_.notifyAll(); } //let manger know new parmas caluclated manager_.updateSurfaceTableAndCombos(); } public synchronized void deleteAllPoints() { points_.clear(); } public synchronized void deletePointsWithinZRange(double zMin, double zMax) { ArrayList<Point3d> toRemove = new ArrayList<Point3d>(); for (Point3d point : points_) { if (point.z >= zMin && point.z <= zMax) { toRemove.add(point); } } for (Point3d point : toRemove) { points_.remove(point); } updateConvexHullAndInterpolate(); manager_.drawSurfaceOverlay(this); } /** * delete closest point within XY tolerance * @param x * @param y * @param toleranceXY radius in stage space */ public synchronized void deleteClosestPoint(double x, double y, double toleranceXY, double zMin, double zMax) { double minDistance = toleranceXY + 1; Point3d minDistancePoint = null; for (Point3d point : points_) { double distance = Math.sqrt((x - point.x) * (x - point.x) + (y - point.y) * (y - point.y)); if (distance < minDistance && point.z >= zMin && point.z <= zMax) { minDistance = distance; minDistancePoint = point; } } //delete if within tolerance if (minDistance < toleranceXY && minDistancePoint != null) { points_.remove(minDistancePoint); } updateConvexHullAndInterpolate(); manager_.drawSurfaceOverlay(this); } public synchronized void addPoint(double x, double y, double z) { points_.add(new Point3d(x, y, z)); //for interpolation updateConvexHullAndInterpolate(); manager_.drawSurfaceOverlay(this); } //redo XY position fitting, but dont need to reinterpolate private void updateXYPositionsOnly(final double overlap) { synchronized (xyPositionLock_) { xyPositions_ = null; } executor_.submit(new Runnable() { @Override public void run() { try { fitXYPositionsToConvexHull(overlap); } catch (InterruptedException ex) { //this won't happen return; } manager_.drawSurfaceOverlay(SurfaceInterpolator.this); } }); } private synchronized void updateConvexHullAndInterpolate() { //duplicate points for use on caluclation thread final LinkedList<Point3d> points = new LinkedList<Point3d>(points_); if (currentInterpolationTask_ != null && !currentInterpolationTask_.isDone()) { //cancel current interpolation because interpolation points have changed, call does not block currentInterpolationTask_.cancel(true); } //don't want one of the get methods returning a null object thinking it has a value synchronized (convexHullLock_) { convexHullVertices_ = null; convexHullRegion_ = null; } synchronized (interpolationLock_) { currentInterpolation_ = null; } synchronized (xyPositionLock_) { xyPositions_ = null; } numRows_ = 0; numCols_ = 0; currentInterpolationTask_ = executor_.submit(new Runnable() { @Override public void run() { if (points.size() > 2) { try { //convert xyPoints to a vector2d for convex hull calculation LinkedList<Vector2D> xyPoints = new LinkedList<Vector2D>(); for (Point3d p : points) { xyPoints.add(new Vector2D(p.x, p.y)); } ConvexHull2D hull = null; hull = mChain_.generate(xyPoints); if (Thread.interrupted()) { throw new InterruptedException(); } synchronized (convexHullLock_) { convexHullVertices_ = hull.getVertices(); convexHullLock_.notifyAll(); } if (Thread.interrupted()) { throw new InterruptedException(); } convexHullRegion_ = hull.createRegion(); if (Thread.interrupted()) { throw new InterruptedException(); } calculateConvexHullBounds(); if (Thread.interrupted()) { throw new InterruptedException(); } //use the most recently set overlap value for display purposes. When it comes time to calc the real thing, //get it from the acquisition settings fitXYPositionsToConvexHull(FixedAreaAcquisitionSettings.getStoredTileOverlapPercentage()); //Interpolate surface as specified by the subclass method interpolateSurface(points); //let manager handle event firing to acquisitions using surface manager_.surfaceUpdated(SurfaceInterpolator.this); } catch (InterruptedException e) { return; } } } }); } /** * block until a higher resolution surface is available. Might experience spurious wakeups * @throws InterruptedException */ public void waitForHigherResolutionInterpolation() throws InterruptedException { synchronized (interpolationLock_) { interpolationLock_.wait(); } } public synchronized Point3d[] getPoints() { return points_.toArray(new Point3d[0]); } /** * return list of surface Data. * @return */ Collection<SurfaceData> getData() { ArrayList<SurfaceData> list = new ArrayList<SurfaceData>(); for (String datumName : SurfaceData.enumerateDataTypes()) { try { list.add(new SurfaceData(this, datumName)); } catch (Exception e) { e.printStackTrace(); //this will never happen } } return list; } }