package gdsc.smlm.model;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2013 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.
*---------------------------------------------------------------------------*/
import org.apache.commons.math3.random.JDKRandomGenerator;
import org.apache.commons.math3.random.RandomGenerator;
/**
* Samples uniformly from the specified spherical volume
*/
public class SphericalDistribution implements SpatialDistribution
{
private final double radius, r2, range;
private RandomGenerator randomGenerator;
private boolean useRejectionMethod = true;
private final double[] origin = new double[3];
public SphericalDistribution(double radius)
{
this(radius, null);
}
public SphericalDistribution(double radius, RandomGenerator randomGenerator)
{
if (randomGenerator == null)
randomGenerator = new JDKRandomGenerator();
if (radius < 0)
throw new IllegalArgumentException("Radius must be positive: {0}");
this.radius = radius;
this.r2 = radius * radius;
this.range = 2 * radius;
this.randomGenerator = randomGenerator;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.model.SpatialDistribution#next()
*/
public double[] next()
{
double[] xyz = new double[3];
if (radius > 0)
{
// See: http://math.stackexchange.com/questions/87230/
// picking-random-points-in-the-volume-of-sphere-with-uniform-probability
if (useRejectionMethod)
{
// -=-=-=-
// Rejection method:
// Sample from a cube and then check if within a sphere
// -=-=-=-
double d2 = 0;
do
{
for (int i = 0; i < 3; i++)
{
//xyz[i] = randomGenerator.nextDouble() * ((randomGenerator.nextBoolean()) ? -radius : radius);
// Avoid extra call to the random generator
xyz[i] = randomGenerator.nextDouble() * range - radius;
}
d2 = xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2];
} while (d2 > r2);
}
else
{
// -=-=-=-
// Transformation method:
// Generate a random point on the surface of the sphere and then sample within.
// -=-=-=-
// Generate a random unit vector: X1, X2, X3 sampled with mean 0 and variance 1
for (int i = 0; i < 3; i++)
{
xyz[i] = randomGenerator.nextGaussian();
}
// Calculate the distance: RsU^1/3 / length
final double d = (radius * Math.pow(randomGenerator.nextDouble(), 0.3333333333)) /
Math.sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2]);
for (int i = 0; i < 3; i++)
xyz[i] *= d;
}
}
return xyz;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.model.SpatialDistribution#isWithin(double[])
*/
public boolean isWithin(double[] xyz)
{
final double[] delta = { xyz[0] - origin[0], xyz[1] - origin[1], xyz[2] - origin[2] };
return (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]) < r2;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.model.SpatialDistribution#isWithinXY(double[])
*/
public boolean isWithinXY(double[] xyz)
{
final double[] delta = { xyz[0] - origin[0], xyz[1] - origin[1] };
return (delta[0] * delta[0] + delta[1] * delta[1]) < r2;
}
/**
* @return If true then sample from the distribution using the rejection method. The alternative is a transformation
* method.
*/
public boolean isUseRejectionMethod()
{
return useRejectionMethod;
}
/**
* @param useRejectionMethod
* If true then sample from the distribution using the rejection method. The alternative is a
* transformation
* method.
*/
public void setUseRejectionMethod(boolean useRejectionMethod)
{
this.useRejectionMethod = useRejectionMethod;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.model.SpatialDistribution#initialise(double[])
*/
public void initialise(double[] xyz)
{
if (xyz != null && xyz.length > 2)
{
for (int i = 0; i < 3; i++)
origin[i] = xyz[i];
}
}
}