Created
February 22, 2019 21:03
-
-
Save echeipesh/e5f0150ae47d7c8438b8b8e95ced9f02 to your computer and use it in GitHub Desktop.
PixelBounds
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2018 Azavea | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package geotrellis.contrib.vlm | |
import scala.collection.mutable | |
import spire.syntax.cfor._ | |
import spire.math._ | |
import spire.syntax._ | |
import spire.implicits._ | |
case class PixelBounds[A: Integral](colMin: A, rowMin: A, colMax: A, rowMax: A) { | |
def width: A = colMax - colMin + Integral[A].one | |
def height: A = rowMax - rowMin + Integral[A].one | |
def size: A = width * height | |
def isEmpty: Boolean = size == 0 | |
/** | |
* Return true if the present [[PixelBounds]] contains the position | |
* pointed to by the given column and row, otherwise false. | |
* | |
* @param col The column | |
* @param row The row | |
*/ | |
def contains(col: A, row: A): Boolean = | |
(colMin <= col && col <= colMax) && | |
(rowMin <= row && row <= rowMax) | |
/** | |
* Returns true if the present [[PixelBounds]] and the given one | |
* intersect (including their boundaries), otherwise returns false. | |
* | |
* @param other The other PixelBounds | |
*/ | |
def intersects(other: PixelBounds[A]): Boolean = | |
!(colMax < other.colMin || other.colMax < colMin) && | |
!(rowMax < other.rowMin || other.rowMax < rowMin) | |
/** | |
* Creates a new [[PixelBounds]] using a buffer around this | |
* PixelBounds. | |
* | |
* @note This will not buffer past 0 regardless of how much the buffer | |
* falls below it. | |
* | |
* @param bufferSize The amount this PixelBounds should be buffered by. | |
*/ | |
def buffer(bufferSize: Int): PixelBounds[A] = | |
buffer(bufferSize, bufferSize) | |
/** | |
* Creates a new [[PixelBounds]] using a buffer around this | |
* PixelBounds. | |
* | |
* @note This will not buffer past 0 regardless of how much the buffer | |
* falls below it. | |
* | |
* @param colBuffer The amount the cols within this PixelBounds should be buffered. | |
* @param rowBuffer The amount the rows within this PixelBounds should be buffered. | |
* @param clamp Determines whether or not to clamp the PixelBounds to the grid | |
* such that it no value will be under 0; defaults to true. If false, | |
* then the resulting PixelBounds can contain negative values outside | |
* of the grid boundaries. | |
*/ | |
def buffer(colBuffer: A, rowBuffer: A, clamp: Boolean = true): PixelBounds[A] = | |
PixelBounds( | |
if (clamp) Integral[A].max(colMin - colBuffer, 0) else colMin - colBuffer, | |
if (clamp) Integral[A].max(rowMin - rowBuffer, 0) else rowMin - rowBuffer, | |
colMax + colBuffer, | |
rowMax + rowBuffer | |
) | |
/** | |
* Offsets this [[PixelBounds]] to a new location relative to its current | |
* position | |
* | |
* @param boundsOffset The amount the PixelBounds should be shifted. | |
*/ | |
def offset(boundsOffset: A): PixelBounds[A] = | |
offset(boundsOffset, boundsOffset) | |
/** | |
* Offsets this [[PixelBounds]] to a new location relative to its current | |
* position | |
* | |
* @param colOffset The amount the cols should be shifted. | |
* @param rowOffset The amount the rows should be shifted. | |
*/ | |
def offset(colOffset: A, rowOffset: A): PixelBounds[A] = | |
PixelBounds( | |
colMin + colOffset, | |
rowMin + rowOffset, | |
colMax + colOffset, | |
rowMax + rowOffset | |
) | |
/** | |
* Another name for the 'minus' method. | |
* | |
* @param other The other PixelBounds | |
*/ | |
def -(other: PixelBounds[A]): Seq[PixelBounds[A]] = minus(other) | |
/** | |
* Returns the difference of the present [[PixelBounds]] and the | |
* given one. This returns a sequence, because the difference may | |
* consist of more than one PixelBounds. | |
* | |
* @param other The other PixelBounds | |
*/ | |
def minus(other: PixelBounds[A]): Seq[PixelBounds[A]] = | |
if(!intersects(other)) { | |
Seq(this) | |
} else { | |
val overlapColMin = | |
if(colMin < other.colMin) other.colMin | |
else colMin | |
val overlapColMax = | |
if(colMax < other.colMax) colMax | |
else other.colMax | |
val overlapRowMin = | |
if(rowMin < other.rowMin) other.rowMin | |
else rowMin | |
val overlapRowMax = | |
if(rowMax < other.rowMax) rowMax | |
else other.rowMax | |
val result = mutable.ListBuffer[PixelBounds[A]]() | |
// Left cut | |
if(colMin < overlapColMin) { | |
result += PixelBounds(colMin, rowMin, overlapColMin - 1, rowMax) | |
} | |
// Right cut | |
if(overlapColMax < colMax) { | |
result += PixelBounds(overlapColMax + 1, rowMin, colMax, rowMax) | |
} | |
// Top cut | |
if(rowMin < overlapRowMin) { | |
result += PixelBounds(overlapColMin, rowMin, overlapColMax, overlapRowMin - 1) | |
} | |
// Bottom cut | |
if(overlapRowMax < rowMax) { | |
result += PixelBounds(overlapColMin, overlapRowMax + 1, overlapColMax, rowMax) | |
} | |
result | |
} | |
/** | |
* Return the intersection of the present [[PixelBounds]] and the | |
* given [[PixelBounds]]. | |
* | |
* @param other The other PixelBounds | |
*/ | |
def intersection(other: PixelBounds[A]): Option[PixelBounds[A]] = | |
if(!intersects(other)) { | |
None | |
} else { | |
Some( | |
PixelBounds( | |
Integral[A].max(colMin, other.colMin), | |
Integral[A].max(rowMin, other.rowMin), | |
Integral[A].min(colMax, other.colMax), | |
Integral[A].min(rowMax, other.rowMax) | |
) | |
) | |
} | |
/** Return the union of PixelBounds. */ | |
def combine(other: PixelBounds[A]): PixelBounds[A] = | |
PixelBounds( | |
colMin = Integral[A].min(this.colMin, other.colMin), | |
rowMin = Integral[A].min(this.rowMin, other.rowMin), | |
colMax = Integral[A].max(this.colMax, other.colMax), | |
rowMax = Integral[A].max(this.rowMax, other.rowMax) | |
) | |
/** Empty PixelBounds contain nothing, though non empty PixelBounds contains iteslf */ | |
def contains(other: PixelBounds[A]): Boolean = | |
if (colMin == 0 && colMax == 0 && rowMin == 0 && rowMax == 0) | |
false | |
else | |
other.colMin >= colMin && | |
other.rowMin >= rowMin && | |
other.colMax <= colMax && | |
other.rowMax <= rowMax | |
/** Split into windows, covering original PixelBounds */ | |
def split(cols: A, rows: A)(implicit ev: NumberTag[A]): Iterator[PixelBounds[A]] = { | |
for { | |
windowRowMin <- Interval.closed(rowMin, rowMax).iterator(rows) | |
windowColMin <- Interval.closed(colMin, colMax).iterator(cols) | |
} yield { | |
PixelBounds( | |
colMin = windowColMin, | |
rowMin = windowRowMin, | |
colMax = Integral[A].min(windowColMin + cols - 1, colMax), | |
rowMax = Integral[A].min(windowRowMin + rows - 1, rowMax) | |
) | |
} | |
} | |
def toInt: PixelBounds[Int] = { | |
require(colMin <= Int.MaxValue && colMax <= Int.MaxValue | |
&& rowMin <= Int.MaxValue && rowMax <= Int.MaxValue, | |
s"$this exceeds Int.MaxValue") | |
PixelBounds(colMin.toInt, rowMin.toInt, colMax.toInt, rowMax.toInt) | |
} | |
def toLong: PixelBounds[Long] = { | |
PixelBounds(colMin.toLong, rowMin.toLong, colMax.toLong, rowMax.toLong) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment