001/**
002 * 
003 */
004package plugins.kernel.roi.descriptor.measure;
005
006import icy.math.UnitUtil.UnitPrefix;
007import icy.roi.ROI;
008import icy.roi.ROI3D;
009import icy.roi.ROIDescriptor;
010import icy.sequence.Sequence;
011import icy.sequence.SequenceEvent;
012import icy.sequence.SequenceEvent.SequenceEventSourceType;
013import icy.util.StringUtil;
014
015/**
016 * Volume ROI descriptor class (see {@link ROIDescriptor})
017 * 
018 * @author Stephane
019 */
020public class ROIVolumeDescriptor extends ROIDescriptor
021{
022    public static final String ID = "Volume";
023
024    public ROIVolumeDescriptor()
025    {
026        super(ID, "Volume", Double.class);
027    }
028
029    @Override
030    public String getDescription()
031    {
032        return "Volume";
033    }
034
035    @Override
036    public String getUnit(Sequence sequence)
037    {
038        if (sequence != null)
039            return sequence.getBestPixelSizeUnit(3, 3).toString() + "m3";
040
041        return UnitPrefix.MICRO.toString() + "m3";
042    }
043
044    @Override
045    public boolean needRecompute(SequenceEvent change)
046    {
047        final SequenceEventSourceType sourceType = change.getSourceType();
048
049        if (sourceType == SequenceEventSourceType.SEQUENCE_DATA)
050            return true;
051        if (sourceType == SequenceEventSourceType.SEQUENCE_META)
052        {
053            final String metaName = (String) change.getSource();
054
055            return StringUtil.isEmpty(metaName) || StringUtil.equals(metaName, Sequence.ID_PIXEL_SIZE_X)
056                    || StringUtil.equals(metaName, Sequence.ID_PIXEL_SIZE_Y)
057                    || StringUtil.equals(metaName, Sequence.ID_PIXEL_SIZE_Z);
058        }
059
060        return false;
061    }
062
063    @Override
064    public Object compute(ROI roi, Sequence sequence) throws UnsupportedOperationException
065    {
066        return Double.valueOf(computeVolume(roi, sequence));
067    }
068
069    /**
070     * Computes and returns the volume expressed in the unit of the descriptor (see {@link #getUnit(Sequence)}) for the
071     * specified ROI.<br>
072     * It may thrown an <code>UnsupportedOperationException</code> if the operation is not supported for that ROI.
073     * 
074     * @param roi
075     *        the ROI on which we want to compute the volume
076     * @param sequence
077     *        an optional sequence where the pixel size can be retrieved
078     * @return the volume expressed in the unit of the descriptor (see {@link #getUnit(Sequence)})
079     * @throws UnsupportedOperationException
080     *         if the operation is not supported for this ROI
081     */
082    public static double computeVolume(ROI roi, Sequence sequence) throws UnsupportedOperationException
083    {
084        return computeVolume(ROIInteriorDescriptor.computeInterior(roi), roi, sequence);
085    }
086
087    /**
088     * Computes and returns the volume from a given number of interior points expressed in the
089     * unit of the descriptor (see {@link #getUnit(Sequence)}) for the specified sequence and ROI.<br>
090     * It may returns <code>Double.Nan</code> if the operation is not supported for that ROI.
091     * 
092     * @param interiorPoints
093     *        the number of interior points (override the ROI value)
094     * @param roi
095     *        the ROI we want to compute the volume
096     * @param sequence
097     *        the input sequence used to retrieve operation unit by using pixel size
098     *        information.
099     * @return the volume expressed in the unit of the descriptor (see {@link #getUnit(Sequence)})
100     * @throws UnsupportedOperationException
101     *         if the operation is not supported for this ROI
102     */
103    public static double computeVolume(double interiorPoints, ROI roi, Sequence sequence)
104            throws UnsupportedOperationException
105    {
106        try
107        {
108            // we restrict to ROI3D only
109            if (!(roi instanceof ROI3D))
110                throw new UnsupportedOperationException();
111
112            return ROIInteriorDescriptor.computeInterior(interiorPoints, roi, sequence, 3);
113        }
114        catch (UnsupportedOperationException e)
115        {
116            throw new UnsupportedOperationException("Can't process " + ID + " calculation for ROI: '" + roi.getName()
117                    + "'");
118        }
119    }
120}