src/stats/Centroids.js
-
-
- import Stat from "./Stat.js"
- import PixelsByCell from "./PixelsByCell.js"
-
- /** This Stat computes the centroid of a cell. When the cell resides on a torus, the
- centroid may be well outside the cell, and other stats may be preferable (e.g.
- {@link CentroidsWithTorusCorrection}).
-
- @example
- * let CPM = require( "path/to/build" )
- *
- * // Make a CPM, seed two cells, run a little, and get their centroids
- * let C = new CPM.CPM( [100,100], {
- * T:20,
- * torus:[false,false],
- * J:[[0,20],[20,10]],
- * V:[0,200],
- * LAMBDA_V:[0,2]
- * } )
- * let gm = new CPM.GridManipulator( C )
- * gm.seedCell(1)
- * gm.seedCell(1)
- * for( let t = 0; t < 100; t++ ){ C.timeStep() }
- *
- * C.getStat( CPM.Centroids )
- */
- class Centroids extends Stat {
-
- /** The set model method of class CentroidsWithTorusCorrection.
- @param {GridBasedModel} M - the model to compute centroids on. */
- set model( M ){
-
- /** The model to compute centroids on.
- @type {GridBasedModel}*/
- this.M = M
-
- /* Check if the grid has a torus; if so, warn that this method may not be
- appropriate. */
- let torus = false
- for( let d = 0; d < this.M.ndim; d++ ){
- if( this.M.grid.torus[d] ){
- torus = true
- break
- }
- }
-
- if(torus){
- // eslint-disable-next-line no-console
- console.warn( "Your model grid has a torus, and the 'Centroids' stat is not compatible with torus! Consider using 'CentroidsWithTorusCorrection' instead." )
- }
-
- // Half the grid dimensions; if pixels with the same cellid are further apart,
- // we assume they are on the border of the grid and that we need to correct
- // their positions to compute the centroid.
- /** @ignore */
- this.halfsize = new Array( this.M.ndim).fill(0)
- for( let i = 0 ; i < this.M.ndim ; i ++ ){
- this.halfsize[i] = this.M.extents[i]/2
- }
- }
- /** @ignore */
- constructor( conf ){
- super(conf)
- }
- /** This method computes the centroid of a specific cell.
- @param {CellId} cellid the unique cell id of the cell to get centroid of.
- @param {CellArrayObject} cellpixels object produced by {@link PixelsByCell},
- with keys for each cellid
- and as corresponding value the pixel coordinates of their pixels.
- @returns {ArrayCoordinate} coordinate of the centroid.
- */
- computeCentroidOfCell( cellid, cellpixels ){
-
- //let cellpixels = this.M.getStat( PixelsByCell )
-
- const pixels = cellpixels[ cellid ]
-
- // cvec will contain the x, y, (z) coordinate of the centroid.
- // Loop over the dimensions to compute each element separately.
- let cvec = new Array(this.M.ndim).fill(0)
- for( let dim = 0 ; dim < this.M.ndim ; dim ++ ){
-
- let mi = 0.
- // Loop over the pixels;
- // compute mean position per dimension with online algorithm
- for( let j = 0 ; j < pixels.length ; j ++ ){
- // Check distance of current pixel to the accumulated mean in this dim.
- // Check if this distance is greater than half the grid size in this
- // dimension; if so, this indicates that the cell has moved to the
- // other end of the grid because of the torus. Note that this only
- // holds AFTER the first pixel (so for j > 0), when we actually have
- // an idea of where the cell is.
- let dx = pixels[j][dim] - mi
- // Update the mean with the appropriate weight.
- mi += dx/(j+1)
- }
- // Set the mean position in the cvec vector.
- cvec[dim] = mi
- }
- return cvec
-
- }
-
- /** Compute centroids for all cells on the grid.
- @return {CellObject} with an {@link ArrayCoordinate} of the centroid for each cell
- on the grid (see {@link computeCentroidOfCell}). */
- compute(){
- // Get object with arrays of pixels for each cell on the grid, and get
- // the array for the current cell.
- let cellpixels = this.M.getStat( PixelsByCell )
-
- // Create an object for the centroids. Add the centroid array for each cell.
- let centroids = {}
- for( let cid of this.M.cellIDs() ){
- centroids[cid] = this.computeCentroidOfCell( cid, cellpixels )
- }
- return centroids
- }
- }
-
- export default Centroids