use super::{ArrowAttrs, ArrowProperties, PathCommand};
fn arrow_builder(x1: f32, y1: f32, x2: f32, y2: f32, arrow_values: ArrowAttrs) -> ArrowProperties {
let line_length = (x2 - x1).hypot(y2 - y1);
let line_angle = (y2 - y1).atan2(x2 - x1);
let bend_angle = arrow_values.bend_angle.to_radians();
let r = line_length.hypot(line_length / bend_angle.tan()) * 0.5;
let wing_angle = arrow_values.head_angle.to_radians() * 0.5;
let wing_scale = arrow_values.head_length / 1.5;
let head_length = wing_scale.min(line_length / 3.);
let end_angle = line_angle + bend_angle;
let left_angle = end_angle + wing_angle;
let right_angle = end_angle - wing_angle;
let x3 = x2 - head_length * left_angle.cos();
let y3 = y2 - head_length * left_angle.sin();
let x4 = x2 - head_length * right_angle.cos();
let y4 = y2 - head_length * right_angle.sin();
let m = -bend_angle.signum() * (x2 - x1).hypot(y2 - y1) * 0.5;
let s = (r * r - m * m).sqrt();
let xi = 0.5 * (m / s * (y1 - y2) + x1 + x2);
let yi = 0.5 * (m / s * (x2 - x1) + y1 + y2);
let path_commands = vec![
PathCommand::MoveTo([x1, y1]),
if r < 1e5 {
PathCommand::ArcTo(([xi, yi], [x2, y2], r))
} else {
PathCommand::LineTo([x2, y2])
},
PathCommand::LineTo([x2, y2]),
PathCommand::MoveTo([x3, y3]),
PathCommand::LineTo([x2, y2]),
PathCommand::LineTo([x4, y4]),
];
ArrowProperties {
path_commands,
stroke_color: arrow_values.stroke_color,
stroke_width: arrow_values.stroke_width,
stroke_opacity: arrow_values.stroke_opacity,
}
}
pub fn arrow_iter<Data>(
values: &[Data],
x1: impl Fn(&Data) -> f32,
y1: impl Fn(&Data) -> f32,
x2: impl Fn(&Data) -> f32,
y2: impl Fn(&Data) -> f32,
arrow_attrs: impl Fn(&Data) -> ArrowAttrs,
) -> impl Iterator<Item = ArrowProperties> {
values.iter().map(move |value| {
arrow_builder(
x1(value),
y1(value),
x2(value),
y2(value),
arrow_attrs(value),
)
})
}
pub fn vector_iter<Data>(
values: &[Data],
x: impl Fn(&Data) -> f32,
y: impl Fn(&Data) -> f32,
length: impl Fn(&Data) -> f32,
rotate: impl Fn(&Data) -> f32,
arrow_attrs: impl Fn(&Data) -> ArrowAttrs,
) -> impl Iterator<Item = ArrowProperties> {
values.iter().map(move |value| {
let line_length = length(value);
let angle = rotate(value);
let cos = angle.cos();
let sin = angle.sin();
let x = x(value);
let y = y(value);
let half = line_length * 0.5;
arrow_builder(
half * cos + x,
-half * sin + y,
-half * cos + x,
half * sin + y,
arrow_attrs(value),
)
})
}