mod butterfly;
mod cartesian;
mod color;
mod cone;
pub(crate) mod contour;
mod floodlight_cartesian;
mod heatmap;
mod isocandela;
mod isolux;
mod labels;
mod polar;
mod projection;
mod svg;
mod watchface;
pub use butterfly::{ButterflyDiagram, ButterflyWing, CPlaneData};
pub use cartesian::{CartesianCurve, CartesianDiagram, CartesianPoint};
pub use color::{heatmap_color, hsl_to_rgb, Color, ColorPalette};
pub use cone::{ConeDiagram, ConeIlluminanceRow, ConeIlluminanceTable};
pub use floodlight_cartesian::{
FloodlightCartesianDiagram, FloodlightCurve, FloodlightPoint, YScale,
};
pub use heatmap::{HeatmapCell, HeatmapDiagram};
pub use isocandela::{IsocandelaCell, IsocandelaContour, IsocandelaDiagram};
pub use isolux::{IsoluxCell, IsoluxContour, IsoluxDiagram, IsoluxParams};
pub use labels::DiagramLabels;
pub use polar::{PolarCurve, PolarDiagram, PolarPoint};
pub use projection::IsometricProjection;
pub use svg::{ConeDiagramLabels, DetailLevel, SvgLabels, SvgTheme};
pub use watchface::WatchFaceStyle;
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Point2D {
pub x: f64,
pub y: f64,
}
impl Point2D {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DiagramScale {
pub max_intensity: f64,
pub scale_max: f64,
pub grid_values: Vec<f64>,
}
impl DiagramScale {
pub fn from_max_intensity(max_intensity: f64, num_divisions: usize) -> Self {
let scale_max = if max_intensity > 0.0 {
let step = if max_intensity > 1000.0 {
500.0
} else if max_intensity > 100.0 {
50.0
} else {
20.0
};
step * (max_intensity / step).ceil()
} else {
100.0
};
let grid_values: Vec<f64> = (1..=num_divisions)
.map(|i| scale_max * (i as f64) / (num_divisions as f64))
.collect();
Self {
max_intensity,
scale_max,
grid_values,
}
}
pub fn nice_step(max_value: f64, target_ticks: usize) -> f64 {
if max_value <= 0.0 || target_ticks == 0 {
return 1.0;
}
let rough_step = max_value / target_ticks as f64;
let magnitude = 10.0_f64.powf(rough_step.log10().floor());
let residual = rough_step / magnitude;
let nice = if residual <= 1.5 {
1.0
} else if residual <= 3.0 {
2.0
} else if residual <= 7.0 {
5.0
} else {
10.0
};
nice * magnitude
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diagram_scale() {
let scale = DiagramScale::from_max_intensity(450.0, 5);
assert_eq!(scale.scale_max, 450.0);
assert_eq!(scale.grid_values.len(), 5);
let scale2 = DiagramScale::from_max_intensity(451.0, 5);
assert_eq!(scale2.scale_max, 500.0);
}
#[test]
fn test_nice_step() {
assert!((DiagramScale::nice_step(100.0, 5) - 20.0).abs() < 0.01);
assert!((DiagramScale::nice_step(1000.0, 5) - 200.0).abs() < 0.01);
}
}