/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2013 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.env.rnd.roi; import java.awt.Point; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import omero.gateway.Gateway; import omero.gateway.SecurityContext; import omero.gateway.exception.DataSourceException; import omero.gateway.facility.RawDataFacility; import omero.gateway.rnd.DataSink; import omero.gateway.rnd.Plane2D; import org.openmicroscopy.shoola.util.roi.figures.ROIFigure; import org.openmicroscopy.shoola.util.roi.model.ROIShape; import omero.gateway.model.PixelsData; /** * Iterates over the pixels contained in an <code>ROIShape</code> or an * <code>ROI</code>. * The iteration advances in point by point through each * {@link ROIFigure} contained in the {@link ROIShape} — the image data is * obviously assumed to be in XYZWT order. Each instance of this class is * bound to a given pixels set; however the same instance can be used to iterate * multiple {@link ROIShape}s over said pixels set. * {@link PointIteratorObserver}s * are attached to an instance of this class before an iteration starts so to * get notified of every iterated pixels value. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * @since OME3.0 */ class PointIterator { /** Gateway to the raw data of the pixels set bound to this iterator. */ private Gateway gw; /** The number of z-sections. */ private int sizeZ; /** The number of timepoints. */ private int sizeT; /** The number of pixels along the x-axis. */ private int sizeX; /** The number of pixels along the y-axis. */ private int sizeY; /** The number of channels. */ private int sizeC; private PixelsData pixels; /** * All currently registered {@link PointIteratorObserver}s. * This is a set (no duplicates) and mustn't contain <code>null</code>s. */ private Set<PointIteratorObserver> observers; /** Notifies all observers, if any, that the iteration has started. */ private void notifyIterationStart() { PointIteratorObserver obs; Iterator<PointIteratorObserver> i = observers.iterator(); while (i.hasNext()) { obs =i.next(); obs.iterationStarted(); } } /** Notifies all observers, if any, that the iteration has finished. */ private void notifyIterationEnd() { PointIteratorObserver obs; Iterator<PointIteratorObserver> i = observers.iterator(); while (i.hasNext()) { obs = i.next(); obs.iterationFinished(); } } /** * Notifies all observers, if any, that a 2D selection within a given plane * is about to be iterated. * * @param z The z coordinate (stack frame) of the plane. * @param w The w coordinate (channel) of the plane. * @param t The t coordinate (timepoint) of the plane. * @param pointsCount How many points are contained in the selection that * is about to be iterated. */ private void notifyPlaneStart(int z, int w, int t, int pointsCount) { PointIteratorObserver obs; Iterator<PointIteratorObserver> i = observers.iterator(); while (i.hasNext()) { obs = i.next(); obs.onStartPlane(z, w, t, pointsCount); } } /** * Notifies all observers, if any, of the current pixel being iterated. * * @param pixelValue The value of the current pixel. * @param z The z coordinate (stack frame) of the plane. * @param w The w coordinate (channel) of the plane. * @param t The t coordinate (timepoint) of the plane. * @param loc The location of the pixelValue on the 2D-selection. */ private void notifyValue(double pixelValue, int z, int w, int t, Point loc) { PointIteratorObserver obs; Iterator<PointIteratorObserver> i = observers.iterator(); while (i.hasNext()) { obs = i.next(); obs.update(pixelValue, z, w, t, loc); } } /** * Notifies all observers, if any, that a 2D selection within a given plane * has been iterated. * * @param z The z coordinate (stack frame) of the plane. * @param w The w coordinate (channel) of the plane. * @param t The t coordinate (timepoint) of the plane. * @param pointsCount How many points are contained in the selection that * has been iterated. */ private void notifyPlaneEnd(int z, int w, int t, int pointsCount) { PointIteratorObserver obs; Iterator<PointIteratorObserver> i = observers.iterator(); while (i.hasNext()) { obs = i.next(); obs.onEndPlane(z, w, t, pointsCount); } } /** * Returns <code>true</code> if the passed coordinates are valid, * <code>false</code> otherwise. * * @param x The x-coordinate of the point. * @param y The y-coordinate of the point. * @return See above. */ private boolean isValidPoint(int x, int y) { if (x < 0 || x >= sizeX) return false; if (y < 0 || y >= sizeY) return false; return true; } /** * Creates a new instance to iterate over the pixels set accessible through * <code>source</code>. * * @param source Gateway to the raw data of the pixels set this iterator * will work on. Mustn't be <code>null</code>. * @param sizeZ The number of z-sections. * @param sizeT The number of timepoints. * @param sizeC The number of channels. * @param sizeX The number of pixels along the x-axis. * @param sizeY The number of pixels along the y-axis. */ PointIterator(Gateway gw, PixelsData pixels) { if (gw == null) throw new NullPointerException("No source."); this.gw = gw; this.pixels = pixels; this.sizeZ = pixels.getSizeZ(); this.sizeC = pixels.getSizeC(); this.sizeT = pixels.getSizeT(); this.sizeX = pixels.getSizeX(); this.sizeY = pixels.getSizeY(); observers = new HashSet<PointIteratorObserver>(); } /** * Adds an iteration observer to the notification list. * This method will do nothing if the specified observer is already in the * list. This means it is not possible for an observer to subscribe twice. * * @param observer The observer to add. Mustn't be <code>null</code>. */ void register(PointIteratorObserver observer) { if (observer == null) throw new NullPointerException("No observer."); observers.add(observer); } /** * Removes an iteration observer from the notification list. * * @param observer The observer to remove. Mustn't be <code>null</code>. */ void remove(PointIteratorObserver observer) { if (observer == null) throw new NullPointerException("No observer."); observers.remove(observer); } /** Removes all iteration observers from the notification list. */ void clearNotificationList() { observers = new HashSet<PointIteratorObserver>(); } /** * Iterates over the pixels contained in <code>roi</code>. * The pixel values come from the pixels set that was bound to this * iterator at creation time. * All registered {@link PointIteratorObserver}s get notified of every * iterated pixels value. * * @param ctx The security context. * @param shape The shape to analyze. Mustn't be <code>null</code>. * @param points The collection of points contained in the shape. * @param w The selected channel. * @param close Pass <code></code> * @throws DataSourceException If an error occurs while retrieving plane * data from the pixels source. */ public void iterate(SecurityContext ctx, ROIShape shape, List<Point> points, int w, boolean close) throws DataSourceException { if (shape == null) throw new NullPointerException("No shapes."); if (w < 0 || w >= sizeC) throw new NullPointerException("Channel not valid."); RawDataFacility rf = null; try { rf = gw.getFacility(RawDataFacility.class); } catch (ExecutionException e) { throw new DataSourceException(e); } notifyIterationStart(); try { int z = shape.getZ(); int t = shape.getT(); if (z >= 0 && z < sizeZ && t >= 0 && t < sizeT) { notifyPlaneStart(z, w, t, points.size()); Plane2D data = rf.getPlane(ctx, pixels, z, t, w, close); double value; int length = 0; int x1, x2; Iterator<Point> i = points.iterator(); Point p; while (i.hasNext()) { p = i.next(); x1 = p.x; x2 = p.y; if (isValidPoint(x1, x2)) { value = data.getPixelValue(x1, x2); notifyValue(value, z, w, t, p); length++; } } notifyPlaneEnd(z, w, t, length); } } finally { //Give the observers a chance to clean up even when //something goes wrong. notifyIterationEnd(); } } /** * Iterates over the pixels contained in <code>roi</code>. * The pixel values come from the pixels set that was bound to this * iterator at creation time. * All registered {@link PointIteratorObserver}s get notified of every * iterated pixels value. * * @param ctx The security context. * @param shape The shape to analyze. Mustn't be <code>null</code>. * @param points The collection of points contained in the shape. * @param w The selected channel. * @throws DataSourceException If an error occurs while retrieving plane * data from the pixels source. */ public void iterate(SecurityContext ctx, ROIShape shape, List<Point> points, int w) throws DataSourceException { iterate(ctx, shape, points, w, true); } }