package gdsc.smlm.search;
import gdsc.core.utils.Maths;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2016 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* 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 3 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
/**
* Specify the dimensions for a search
*/
public class FixedDimension implements Cloneable, Dimension
{
public final double min;
public final double max;
public final double lower;
public final double upper;
public final double minIncrement;
public final boolean active;
/**
* Instantiates a new inactive search dimension. The centre is set to zero.
*/
public FixedDimension()
{
this(0);
}
/**
* Instantiates a new inactive search dimension. The centre can be set to any value.
*
* @param centre
* the centre
*/
public FixedDimension(double centre)
{
this(centre, centre, 0);
}
/**
* Instantiates a new search dimension.
*
* @param min
* the minimum of the range
* @param max
* the maximum of the range
* @param minIncrement
* the min increment to use around the centre
*/
public FixedDimension(double min, double max, double minIncrement)
{
this(min, max, minIncrement, min, max);
}
/**
* Instantiates a new search dimension.
*
* @param min
* the minimum of the range
* @param max
* the maximum of the range
* @param minIncrement
* the min increment to use around the centre
* @param lower
* the current lower bound of the range (will be clipped to min/max)
* @param upper
* the current upper bound of the range (will be clipped to min/max)
*/
public FixedDimension(double min, double max, double minIncrement, double lower, double upper)
{
if (isInvalid(min))
throw new IllegalArgumentException("Min is not a valid number: " + min);
if (isInvalid(max))
throw new IllegalArgumentException("Max is not a valid number: " + max);
if (isInvalid(lower))
throw new IllegalArgumentException("Lower is not a valid number: " + lower);
if (isInvalid(upper))
throw new IllegalArgumentException("Upper is not a valid number: " + upper);
if (isInvalid(minIncrement))
throw new IllegalArgumentException("Min increment is not a valid number: " + minIncrement);
if (max < min)
throw new IllegalArgumentException("Max is less than min");
this.active = min < max;
if (minIncrement < 0)
throw new IllegalArgumentException("Min increment is negative: " + minIncrement);
if (upper < lower)
throw new IllegalArgumentException("Upper is less than lower");
// if (upper < min || upper > max)
// throw new IllegalArgumentException("Upper is outside min/max range");
// if (lower < min || lower > max)
// throw new IllegalArgumentException("Lower is outside min/max range");
// Clip to range
lower = Math.min(max, Math.max(lower, min));
upper = Math.min(max, Math.max(upper, min));
// We round to the min increment so that the values returned should be identical if the centre is moved by a factor of the increment.
this.minIncrement = minIncrement;
this.min = round(min);
this.max = round(max);
this.lower = round(lower);
this.upper = round(upper);
}
/**
* Creates a new fixed dimension, respecting the current min/max and the increment settings. If the current search
* dimension is not active then an inactive dimension is returned centred between the lower and upper bounds.
*
* @see gdsc.smlm.search.Dimension#create(double, double)
*/
public FixedDimension create(double lower, double upper)
{
if (!active)
return new FixedDimension((upper + lower) / 2);
if (lower < min)
lower = min;
if (upper > max)
upper = max;
return new FixedDimension(min, max, minIncrement, lower, upper);
}
/**
* Creates a new search dimension, respecting the current settings.
*
* @param nIncrement
* the number of increments to use around the centre
* @return the search dimension
*/
public SearchDimension create(int nIncrement)
{
if (nIncrement <= 0)
{
// Compute the maximum number of increments to cover the range from the centre
nIncrement = (int) Math.ceil(Math.ceil((max - min) / minIncrement) / 2);
}
return new SearchDimension(min, max, minIncrement, nIncrement, getLower(), getUpper());
}
private static boolean isInvalid(double d)
{
return Double.isNaN(d) || Double.isInfinite(d);
}
/**
* Round the value to the nearest min increment. If min increment is zero no rounding is performed.
*
* @param value
* the value
* @return the rounded value
*/
public double round(double value)
{
if (canRound())
return Maths.round(value, minIncrement);
return value;
}
/**
* If the dimension is not active or min increment is zero no rounding is performed.
*
* @see gdsc.smlm.search.Dimension#canRound()
*/
public boolean canRound()
{
return (active && minIncrement != 0);
}
/**
* Gets the centre of the range in the dimension
*
* @return the centre of the range in the dimension
*/
public double getCentre()
{
return round((upper + lower) / 2);
}
/**
* Gets the current lower bound of the range
*
* @return the current lower bound of the range
*/
public double getLower()
{
return lower;
}
/**
* Gets the current upper bound of the range
*
* @return the current upper bound of the range
*/
public double getUpper()
{
return upper;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.search.Dimension#isAtBounds(double)
*/
public boolean isAtBounds(double v)
{
return (v <= lower || v >= upper);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#clone()
*/
@Override
public FixedDimension clone()
{
try
{
return (FixedDimension) super.clone();
}
catch (CloneNotSupportedException e)
{
return null;
}
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.search.Dimension#getMin()
*/
public double getMin()
{
return min;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.search.Dimension#getMax()
*/
public double getMax()
{
return max;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.search.Dimension#isActive()
*/
public boolean isActive()
{
return active;
}
}