All Downloads are FREE. Search and download functionalities are using the official Maven repository.

geotrellis.raster.op.focal.CursorMask.scala Maven / Gradle / Ivy

The newest version!
package geotrellis.raster.op.focal

import scala.math.{min,max}
import scala.collection.mutable.LinkedList

import Movement._

/**
 * A mask over a cursor. The CursorMask
 * helps the cursor keep track of the state of masking
 * and unmasking of cells between moves.
 */
class CursorMask(d:Int,f:(Int,Int)=>Boolean) {

  /** A fast data type that records the unmasked values
   * of the cursor per cursor row */
  private class MaskSet {
    private var data = Array.ofDim[Int](d*(d+1))

    def add(i:Int,v:Int) = {
      val len = data(i*(d+1)) + 1
      data(i*(d+1) + len) = v
      data(i*(d+1)) = len
    }

    def foreachAt(i:Int)(f:Int=>Unit) = {
      val len = data(i*(d+1))
      if(len > 0) {
        var x = 0
        while(x < len) {
          f(data(i*(d+1) + x + 1))
          x += 1
        }
      }
    }
  }
  
  // Holds data about what cells are unmasked
  private val unmasked = new MaskSet
  private var westColumnUnmasked = Array.ofDim[Int](d+1)
  private var eastColumnUnmasked = Array.ofDim[Int](d+1)

  // Holds data about what cells are masked or unmasked by moving the cursor.
  private val maskedAfterMoveUp = new MaskSet
  private val unmaskedAfterMoveUp = new MaskSet
  private val maskedAfterMoveLeft = new MaskSet
  private val unmaskedAfterMoveLeft = new MaskSet

  // Record the mask values
  var x = 0
  var y = 0
  var len = 0
  var isMasked = false
  var leftIsMasked = false
  while(y < d) {
    x = 0
    len = 0
    while(x < d) {
      isMasked = f(x,y)
      if(!isMasked) {
        unmasked.add(y,x)

        // Record the border columns, they are a special case to be tracked.
        if(x == 0) {
          val len = westColumnUnmasked(0) + 1
          westColumnUnmasked(len) = y
          westColumnUnmasked(0) = len
        }
        if(x == d - 1) {
          val len = eastColumnUnmasked(0) + 1
          eastColumnUnmasked(len) = y
          eastColumnUnmasked(0) = len
        }
      }

      // Check if moving left yeilds a different mask result,
      // so we can tell if moving horizontally has changed the
      // masked value for a cell.
      if(x > 0) {
        val leftIsMasked = f(x-1,y)

        if(leftIsMasked && !isMasked) {
          // Moving left will unmask
          unmaskedAfterMoveLeft.add(y,x)
        } else if(!leftIsMasked && isMasked) {
          // Moving left will mask
          maskedAfterMoveLeft.add(y,x)
        }
      }
      
      //Check if moving up yeilds different mask result
      if(y > 0) {
        val upIsMasked = f(x,y-1)
        
        if(upIsMasked && !isMasked) {
          // Moving up will unmask
          unmaskedAfterMoveUp.add(y,x)
        } else if(!upIsMasked && isMasked) {
          // Moving up will mask
          maskedAfterMoveUp.add(y,x)
        }
      }
      x += 1
    }
    y += 1
  }
 
  def foreachX(row:Int)(f:Int=>Unit) = {
    unmasked.foreachAt(row)(f)
  }
 
  def foreachWestColumn(f:Int=>Unit) = {
    val len = westColumnUnmasked(0)
    if(len > 0) {
      var y = 1
      while(y <= len) {
        f(westColumnUnmasked(y))
        y += 1
      }
    }
  }

  def foreachEastColumn(f:Int=>Unit) = {
    val len = eastColumnUnmasked(0)
    if(len > 0) {
      var y = 1
      while(y <= len) {
        f(eastColumnUnmasked(y))
        y += 1
      }
    }
  }

  private def foreach(xOffset:Int,yOffset:Int,startY:Int,set:MaskSet)(f:(Int,Int)=>Unit) = {
    var y = startY
    while(y < d) {
      set.foreachAt(y) { x => f(x - xOffset, y - yOffset) }
      y += 1
    }
  }

  def foreachMasked(mv:Movement)(f:(Int,Int)=>Unit) {
    mv match {
      case Left => foreach(0,0,0,maskedAfterMoveLeft)(f)
      case Right => foreach(1,0,0,unmaskedAfterMoveLeft)(f)
      case Up => foreach(0,0,1,maskedAfterMoveUp)(f)
      case Down => foreach(0,1,1,unmaskedAfterMoveUp)(f)
      case _ =>
    }
  }

  def foreachUnmasked(mv:Movement)(f:(Int,Int)=>Unit) {
    mv match {
      case Left => foreach(0,0,0,unmaskedAfterMoveLeft)(f)
      case Right => foreach(1,0,0,maskedAfterMoveLeft)(f)
      case Up => foreach(0,0,1,unmaskedAfterMoveUp)(f)
      case Down => foreach(0,1,1,maskedAfterMoveUp)(f)
      case _ =>
    }
  }

  def asciiDraw:String = {
    import scala.collection.mutable.Set
    val s = Set[Int]()
    var r = ""
    for(y <- 0 to d - 1) {
      s.clear()
      unmasked.foreachAt(y) { x => s += x }
      for(x <- 0 to d - 1) {
        if(s.contains(x)) { r += " O " } else { r += " X " }
      }
      r += "\n"
    }
    r
  }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy