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 java.util.Arrays; import org.apache.commons.math3.random.JDKRandomGenerator; import org.apache.commons.math3.random.RandomDataGenerator; import org.apache.commons.math3.random.RandomGenerator; /** * Populates an image with well spaced unary or binary localisations. * <p> * Creates a grid layout using cells of the specified size within the image area. Each cell can have one or two * localisations. The first localisation is placed within the central 50% of the cell. The second localisation (if * present) is placed randomly up to a maximum distance away. When all cells have been sampled then no more * localisations are generated. */ public class GridDistribution implements SpatialDistribution { private RandomGenerator randomGenerator; private RandomDataGenerator dataGenerator; private int size, cellSize; private double pBinary; private double minBinaryDistance, maxBinaryDistance; private double min, depth; private int cell = -1; private int nCellsPerRow, nCells; private double[] previous = null; /** * Create a distribution with the binary spots placed from 0 - distance * * @param size * @param depth * @param cellSize * @param pBinary * @param binaryDistance */ public GridDistribution(int size, double depth, int cellSize, double pBinary, double binaryDistance) { this(size, depth, cellSize, pBinary, 0, binaryDistance, null); } /** * Create a distribution with the binary spots placed from min - max distance * * @param size * @param depth * @param cellSize * @param pBinary * @param minBinaryDistance * @param maxBinaryDistance */ public GridDistribution(int size, double depth, int cellSize, double pBinary, double minBinaryDistance, double maxBinaryDistance) { this(size, depth, cellSize, pBinary, minBinaryDistance, maxBinaryDistance, null); } /** * Create a distribution with the binary spots placed from min - max distance * * @param size * @param depth * @param cellSize * @param pBinary * @param minBinaryDistance * @param maxBinaryDistance * @param randomGenerator */ public GridDistribution(int size, double depth, int cellSize, double pBinary, double minBinaryDistance, double maxBinaryDistance, RandomGenerator randomGenerator) { if (size < 1) throw new IllegalArgumentException("Size must be above zero"); if (size < cellSize) throw new IllegalArgumentException("Size must be >= cell size"); if (pBinary < 0 || pBinary > 1) throw new IllegalArgumentException("Probability must be between 0 and 1"); if (maxBinaryDistance < 0) throw new IllegalArgumentException("Max distance must be positive"); if (minBinaryDistance > maxBinaryDistance) throw new IllegalArgumentException("Min distance must be below max distance"); if (randomGenerator == null) randomGenerator = new JDKRandomGenerator(); this.randomGenerator = randomGenerator; this.dataGenerator = new RandomDataGenerator(randomGenerator); this.size = size; this.min = -depth / 2; this.depth = depth; this.cellSize = cellSize; this.pBinary = pBinary; this.minBinaryDistance = minBinaryDistance; this.maxBinaryDistance = maxBinaryDistance; nCellsPerRow = size / cellSize; nCells = nCellsPerRow * nCellsPerRow; } /* * (non-Javadoc) * * @see gdsc.smlm.model.SpatialDistribution#next() */ public double[] next() { if (previous != null) { // See if a binary localisation should be created near the previous spot if (randomGenerator.nextDouble() < pBinary) { double[] xyz = Arrays.copyOf(previous, 3); // Create a random unit vector double x = dataGenerator.nextGaussian(0, 1); double y = dataGenerator.nextGaussian(0, 1); double z = dataGenerator.nextGaussian(0, 1); final double length = Math.sqrt(x * x + y * y + z * z); if (length != 0) { // Shift by a random distance final double distance = (maxBinaryDistance == minBinaryDistance) ? maxBinaryDistance : dataGenerator.nextUniform(minBinaryDistance, maxBinaryDistance, true); final double d = distance / length; x *= d; y *= d; z *= d; } xyz[0] += x; xyz[1] += y; xyz[2] += z; previous = null; return xyz; } } previous = null; // See if any more localisations will fit in the grid if (++cell < nCells) { int cellx = cell % nCellsPerRow; int celly = cell / nCellsPerRow; previous = new double[3]; // Ensure the centre of the distribution is [0,0,0] previous[0] = cellx * cellSize - size / 2 + cellSize * dataGenerator.nextUniform(0.25, 0.75); previous[1] = celly * cellSize - size / 2 + cellSize * dataGenerator.nextUniform(0.25, 0.75); previous[2] = min + randomGenerator.nextDouble() * depth; } return previous; } /* * (non-Javadoc) * * @see gdsc.smlm.model.SpatialDistribution#isWithin(double[]) */ public boolean isWithin(double[] xyz) { for (int i = 0; i < xyz.length; i++) if (xyz[i] < 0 || xyz[i] > size) return false; return true; } /* * (non-Javadoc) * * @see gdsc.smlm.model.SpatialDistribution#isWithinXY(double[]) */ public boolean isWithinXY(double[] xyz) { for (int i = 0; i < 2; i++) if (xyz[i] < 0 || xyz[i] > size) return false; return true; } /* * (non-Javadoc) * * @see gdsc.smlm.model.SpatialDistribution#initialise(double[]) */ public void initialise(double[] xyz) { // Ignore } }