use crate::layer::{Layer, LayerId};
use std::any::Any;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct HillshadeParams {
pub opacity: f32,
pub highlight_color: [f32; 4],
pub shadow_color: [f32; 4],
pub accent_color: [f32; 4],
pub illumination_direction: f32,
pub illumination_altitude: f32,
pub exaggeration: f32,
}
impl Default for HillshadeParams {
fn default() -> Self {
Self {
opacity: 1.0,
highlight_color: [1.0, 1.0, 1.0, 1.0],
shadow_color: [0.0, 0.0, 0.0, 1.0],
accent_color: [0.42, 0.48, 0.42, 1.0],
illumination_direction: 335.0f32.to_radians(),
illumination_altitude: 45.0f32.to_radians(),
exaggeration: 1.0,
}
}
}
#[derive(Debug, Clone)]
pub struct HillshadeLayer {
id: LayerId,
name: String,
visible: bool,
opacity: f32,
highlight_color: [f32; 4],
shadow_color: [f32; 4],
accent_color: [f32; 4],
illumination_direction_deg: f32,
illumination_altitude_deg: f32,
exaggeration: f32,
}
impl HillshadeLayer {
pub fn new(name: impl Into<String>) -> Self {
Self {
id: LayerId::next(),
name: name.into(),
visible: true,
opacity: 1.0,
highlight_color: [1.0, 1.0, 1.0, 1.0],
shadow_color: [0.0, 0.0, 0.0, 1.0],
accent_color: [0.42, 0.48, 0.42, 1.0],
illumination_direction_deg: 335.0,
illumination_altitude_deg: 45.0,
exaggeration: 1.0,
}
}
#[inline]
pub fn highlight_color(&self) -> [f32; 4] {
self.highlight_color
}
#[inline]
pub fn shadow_color(&self) -> [f32; 4] {
self.shadow_color
}
#[inline]
pub fn accent_color(&self) -> [f32; 4] {
self.accent_color
}
#[inline]
pub fn illumination_direction_deg(&self) -> f32 {
self.illumination_direction_deg
}
#[inline]
pub fn illumination_altitude_deg(&self) -> f32 {
self.illumination_altitude_deg
}
#[inline]
pub fn exaggeration(&self) -> f32 {
self.exaggeration
}
pub fn set_highlight_color(&mut self, color: [f32; 4]) {
self.highlight_color = color;
}
pub fn set_shadow_color(&mut self, color: [f32; 4]) {
self.shadow_color = color;
}
pub fn set_accent_color(&mut self, color: [f32; 4]) {
self.accent_color = color;
}
pub fn set_illumination_direction_deg(&mut self, direction_deg: f32) {
if direction_deg.is_finite() {
self.illumination_direction_deg = direction_deg.rem_euclid(360.0);
}
}
pub fn set_illumination_altitude_deg(&mut self, altitude_deg: f32) {
if altitude_deg.is_finite() {
self.illumination_altitude_deg = altitude_deg.clamp(0.0, 90.0);
}
}
pub fn set_exaggeration(&mut self, exaggeration: f32) {
if exaggeration.is_finite() {
self.exaggeration = exaggeration.max(0.0);
}
}
pub fn effective_params(&self) -> HillshadeParams {
HillshadeParams {
opacity: self.opacity,
highlight_color: self.highlight_color,
shadow_color: self.shadow_color,
accent_color: self.accent_color,
illumination_direction: self.illumination_direction_deg.to_radians(),
illumination_altitude: self.illumination_altitude_deg.to_radians(),
exaggeration: self.exaggeration,
}
}
}
impl Layer for HillshadeLayer {
fn id(&self) -> LayerId {
self.id
}
fn kind(&self) -> crate::layer::LayerKind {
crate::layer::LayerKind::Hillshade
}
fn name(&self) -> &str {
&self.name
}
fn visible(&self) -> bool {
self.visible
}
fn set_visible(&mut self, visible: bool) {
self.visible = visible;
}
fn opacity(&self) -> f32 {
self.opacity
}
fn set_opacity(&mut self, opacity: f32) {
self.opacity = opacity.clamp(0.0, 1.0);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::layer::Layer;
#[test]
fn defaults_match_expected_contract() {
let layer = HillshadeLayer::new("hillshade");
assert_eq!(layer.kind(), crate::layer::LayerKind::Hillshade);
assert_eq!(layer.illumination_direction_deg(), 335.0);
assert_eq!(layer.illumination_altitude_deg(), 45.0);
assert_eq!(layer.exaggeration(), 1.0);
}
#[test]
fn setters_clamp_values() {
let mut layer = HillshadeLayer::new("hillshade");
layer.set_illumination_direction_deg(725.0);
layer.set_illumination_altitude_deg(120.0);
layer.set_exaggeration(-2.0);
assert_eq!(layer.illumination_direction_deg(), 5.0);
assert_eq!(layer.illumination_altitude_deg(), 90.0);
assert_eq!(layer.exaggeration(), 0.0);
}
}