/* * omeis.providers.re.data.PlaneDef * * Copyright 2006 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package omeis.providers.re.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Identifies a <i>2D</i>-plane in the <i>XYZ</i> moving frame of the <i>3D</i> * stack. Tells which plane the wavelengths to render belong to. * * @author Jean-Marie Burel      <a * href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br> * Andrea Falconi      <a * href="mailto:a.falconi@dundee.ac.uk"> a.falconi@dundee.ac.uk</a> * @version 2.2 * @since OME2.2 */ public class PlaneDef implements Serializable { /** The serial number */ private static final long serialVersionUID = -3200013163481587159L; /** Flag to identify an <i>XY</i>-plane. */ public static final int XY = 0; /** Flag to identify an <i>YZ</i>-plane. */ public static final int ZY = 1; /** Flag to identify an <i>XZ</i>-plane. */ public static final int XZ = 2; /** * Tells which kind of plane this object identifies. Set to one of the * {@link #XY}, {@link #XZ} or {@link #ZY} constants defined by this class. */ private int slice; /** * Selects a plane in the set identified by {@link #slice}. Only relevant * in the context of an <i>YZ</i>-plane. */ private int x; /** * Selects a plane in the set identified by {@link #slice}. Only relevant * in the context of an <i>XZ</i>-plane. */ private int y; /** * Selects a plane in the set identified by {@link #slice}. Only relevant * in the context of an <i>XY</i> plane. */ private int z; /** The timepoint. */ private int t; /** The region to retrieve i.e. a rectangular section of the plane. */ private RegionDef region; /** The step size. */ private int stride; /** Render shapes (masks) assosiated with this plane? */ private boolean renderShapes; /** List of shape Ids to render. */ private List<Long> shapeIds; /** * Creates a new instance. This new object will identify the plane belonging * to the set specified by <code>slice</code> and <code>t</code> and * having a <code>0</code> index. Call the appropriate <code>setXXX</code> * method to set another index. * * @param slice * Tells which kind of plane this object identifies. Must be one * of the {@link #XY}, {@link #XZ}, {@link #ZY} constants * defined by this class. * @param t * The selected timepoint. * @throws IllegalArgumentException * If bad arguments are passed in. */ public PlaneDef(int slice, int t) { switch (slice) { case XY: case ZY: case XZ: this.slice = slice; break; default: throw new IllegalArgumentException("Wrong slice identifier: " + slice + "."); } if (t < 0) { throw new IllegalArgumentException("Negative timepoint: " + t + "."); } this.t = t; stride = 0; renderShapes = false; shapeIds = new ArrayList<Long>(); } /** * Selects a plane in the set identified by this object. Can only be called * in the context of an <i>ZY</i>-plane. * * @param x * The plane index. * @throws IllegalArgumentException * If bad arguments are passed in or this is not an <i>ZY</i>-plane. */ public void setX(int x) { // Verify X normal. if (ZY != slice) { throw new IllegalArgumentException( "X index can only be set for ZY planes."); } if (x < 0) { throw new IllegalArgumentException("Negative index: " + x + "."); } this.x = x; } /** * Selects a plane in the set identified by this object. Can only be called * in the context of an <i>XZ</i>-plane. * * @param y * The plane index. * @throws IllegalArgumentException * If bad arguments are passed in or this is not an <i>XZ</i>-plane. */ public void setY(int y) { // Verify Y normal. if (XZ != slice) { throw new IllegalArgumentException( "Y index can only be set for XZ planes."); } if (y < 0) { throw new IllegalArgumentException("Negative index: " + y + "."); } this.y = y; } /** * Selects a plane in the set identified by this object. Can only be called * in the context of an <i>XY</i>-plane. * * @param z * The plane index. * @throws IllegalArgumentException * If bad arguments are passed in or this is not an <i>XY</i>-plane. */ public void setZ(int z) { // Verify Z normal. if (XY != slice) { throw new IllegalArgumentException( "Z index can only be set for XY planes."); } if (z < 0) { throw new IllegalArgumentException("Negative index: " + z + "."); } this.z = z; } /** * Returns an identifier to tell which kind of plane this object identifies. * * @return One of the {@link #XY}, {@link #XZ} or {@link #ZY} constants * defined by this class. */ public int getSlice() { return slice; } /** * Returns the timepoint. * * @return See above. */ public int getT() { return t; } /** * Returns the index of the plane in the set identified by this object. Only * relevant in the case of an <i>YZ</i>-plane. * * @return See above. */ public int getX() { return x; } /** * Returns the index of the plane in the set identified by this object. Only * relevant in the case of an <i>XZ</i>-plane. * * @return See above. */ public int getY() { return y; } /** * Returns the index of the plane in the set identified by this object. Only * relevant in the case of an <i>XY</i>-plane. * * @return See above. */ public int getZ() { return z; } /** * Sets the region to render. * * @param region The region to render. */ public void setRegion(RegionDef region) { this.region = region; } /** * Returns the region to render. * * @return See above. */ public RegionDef getRegion() { return region; } /** * Returns the stride. * * @param stride The value to set. */ public void setStride(int stride) { if (stride < 0) stride = 0; this.stride = stride; } /** * Returns the stride. * * @return See above. */ public int getStride() { return stride; } /** * Returns whether or not the shapes (masks) will be rendered. * * @return See above. */ public boolean getRenderShapes() { return renderShapes; } /** * Sets whether or not to render the shapes (masks). * * @param renderShapes The value to set. */ public void setRenderShapes(boolean renderShapes) { this.renderShapes = renderShapes; } /** * Returns whether or not the shapes (masks) will be rendered. * * @return See above. */ public List<Long> getShapeIds() { return shapeIds; } /** * Sets whether or not to render the shapes (masks). * * @param shapeIds The value to set. */ public void setShapeIds(List<Long> shapeIds) { this.shapeIds = shapeIds; } /** * Overridden to reflect equality of abstract values (data object) as * opposite to object identity. * * @see Object#equals(Object) */ @Override public boolean equals(Object o) { boolean isEqual = false; if (o != null && o instanceof PlaneDef) { // Else can't be equal. PlaneDef other = (PlaneDef) o; if (other.slice == slice && other.t == t) { // Else can't be equal. switch (slice) { case XY: isEqual = other.z == z; break; case ZY: isEqual = other.x == x; break; case XZ: isEqual = other.y == y; break; } } } return isEqual; } /** * Overridden to reflect equality of abstract values (data object) as * opposite to object identity. * * @see Object#hashCode() */ @Override public int hashCode() { // We return f(u, t) = (u % 2^15)*2^15 + (t % 2^15), where u is one out // of x, y, z depending on slice. int u = -1, two15 = 0x8000; // 0x8000 = 2^15 switch (slice) { case XY: u = z; break; case ZY: u = x; break; case XZ: u = y; break; } return u % two15 * two15 + t % two15; } /* * NOTE: The hash code function that we picked, besides being easy and fast * to calculate, has this very nice property: * * (u1, t1) = (u2, t2) <=> f(u1, t1) = f(u2, t2) * * as long as 0 <= u1, t1, u2, t2 < 2^15. Because for all pratical purposes * we can assume a pixels set contains less than 2^15 timepoints and less * than 2^15 sections in each stack, then the above implies in practice: * * a.equals(b) <=> a.hashCode() == b.hashCode() * * where a and b are two instances of PlaneDef and provided that state * didn't change across invocations of the equals and hashCode methods. So * we can basically assume this functions avoids collisions. Even though * this is not a requirement for the hashCode contract, avoiding collisions * makes PlaneDef very "hash table friedly" :) */ /** * Overrides generic <code>toString</code> method to provide a specific * string representation of this object. * * @see Object#toString() */ @Override public String toString() { StringBuffer buf = new StringBuffer(); switch (slice) { case XY: buf.append("Type: XY, "); buf.append("z=" + z); break; case ZY: buf.append("Type: ZY, "); buf.append("x=" + x); break; case XZ: buf.append("Type: XZ, "); buf.append("y=" + y); break; } buf.append(", t=" + t); if (region != null) { buf.append("; Region: " + region.toString()); } buf.append(", renderShapes=" + renderShapes); buf.append(", shapeIds=" + shapeIds); return buf.toString(); } }