///|
pub fn ThermalMask::new(
width~ : Int,
height~ : Int,
cells~ : Array[Bool],
) -> ThermalMask raise ThermalError {
if width <= 0 || height <= 0 || cells.length() != width * height {
raise ThermalError::InvalidDimensions(
width~,
height~,
values=cells.length(),
)
}
{ width, height, cells: cells.copy() }
}
///|
pub fn ThermalMatrix::threshold_mask(
matrix : ThermalMatrix,
min_temp~ : Double,
max_temp? : Double,
) -> ThermalMask {
let cells = matrix.values.map(fn(value) {
if value < min_temp {
false
} else {
match max_temp {
Some(limit) => value <= limit
None => true
}
}
})
{ width: matrix.width, height: matrix.height, cells }
}
///|
pub fn ThermalMask::get(
mask : ThermalMask,
point : ThermalPoint,
) -> Bool raise ThermalError {
if point.x < 0 ||
point.x >= mask.width ||
point.y < 0 ||
point.y >= mask.height {
raise ThermalError::OutOfBounds(
x=point.x,
y=point.y,
width=mask.width,
height=mask.height,
)
}
mask.cells[point.y * mask.width + point.x]
}
///|
fn mask_unsafe_get(mask : ThermalMask, x~ : Int, y~ : Int) -> Bool {
mask.cells[y * mask.width + x]
}
///|
fn any_neighbor(mask : ThermalMask, x~ : Int, y~ : Int, radius : Int) -> Bool {
let x0 = Int::max(0, x - radius)
let x1 = Int::min(mask.width - 1, x + radius)
let y0 = Int::max(0, y - radius)
let y1 = Int::min(mask.height - 1, y + radius)
for yy in y0..<=y1 {
for xx in x0..<=x1 {
if mask_unsafe_get(mask, x=xx, y=yy) {
return true
}
}
}
false
}
///|
fn all_neighbors(mask : ThermalMask, x~ : Int, y~ : Int, radius : Int) -> Bool {
let x0 = Int::max(0, x - radius)
let x1 = Int::min(mask.width - 1, x + radius)
let y0 = Int::max(0, y - radius)
let y1 = Int::min(mask.height - 1, y + radius)
for yy in y0..<=y1 {
for xx in x0..<=x1 {
if !mask_unsafe_get(mask, x=xx, y=yy) {
return false
}
}
}
true
}
///|
pub fn ThermalMask::dilate(
mask : ThermalMask,
radius? : Int = 1,
) -> ThermalMask {
let r = Int::max(1, radius)
let cells : Array[Bool] = []
for y in 0.. ThermalMask {
let r = Int::max(1, radius)
let cells : Array[Bool] = []
for y in 0.. ThermalMask {
mask.erode(radius~).dilate(radius~)
}
///|
pub fn ThermalMask::close(mask : ThermalMask, radius? : Int = 1) -> ThermalMask {
mask.dilate(radius~).erode(radius~)
}
///|
pub fn ThermalMask::count(mask : ThermalMask) -> Int {
mask.cells.count_if(fn(cell) { cell })
}
///|
pub fn ThermalMask::to_ascii(
mask : ThermalMask,
hot? : String = "#",
cold? : String = ".",
) -> String {
let lines : Array[String] = []
for y in 0..