use fltk::{
app,
app::{MouseWheel, redraw},
draw::*,
enums::*,
frame::*,
prelude::*,
valuator::HorNiceSlider,
window::*,
};
use linestring::linestring_2d::{LineString2, SimpleAffine};
use std::{cell::RefCell, rc::Rc};
use vector_traits::{
glam,
glam::vec2,
prelude::{Aabb2, GenericVector2},
};
const HF: i32 = 565;
const WF: i32 = 790;
const FO: i32 = 5;
const H: i32 = 650;
const W: i32 = 800;
const SH: i32 = 25;
type T = f32;
#[derive(Debug, Clone, Copy)]
pub enum GuiMessage {
SliderRdpChanged(f32),
SliderVwChanged(usize),
}
struct SharedData {
lines: Vec<Vec<glam::Vec2>>,
last_message: Option<GuiMessage>,
affine: SimpleAffine<glam::Vec2>,
}
fn main() {
let app = app::App::default().with_scheme(app::Scheme::Gtk);
let mut wind = Window::new(
100,
100,
W,
H + SH,
"Ramer–Douglas–Peucker & Visvalingam–Whyatt simplification + Self intersection test",
);
let mut frame = Frame::new(FO, FO, WF, HF + SH, "");
frame.set_color(Color::White);
frame.set_frame(FrameType::DownBox);
let mut slider_d =
HorNiceSlider::new(FO, FO + HF, WF, SH, "Ramer–Douglas–Peucker distance:0.0");
slider_d.set_value(0.0);
slider_d.set_color(Color::Green);
slider_d.set_frame(FrameType::PlasticUpBox);
let mut slider_n = HorNiceSlider::new(
FO,
FO + HF + SH * 2,
WF,
SH,
"Visvalingam–Whyatt deleted points:0",
);
slider_n.set_value(0.0);
slider_n.set_color(Color::Blue);
slider_n.set_frame(FrameType::PlasticUpBox);
wind.end();
wind.show();
let shared_data_rc = Rc::from(RefCell::from(SharedData {
lines: Vec::new(),
last_message: None,
affine: SimpleAffine::default(),
}));
{
let mut shared_data_bm = shared_data_rc.borrow_mut();
let aabb = <glam::Vec2 as GenericVector2>::Aabb::from_points([
vec2(0.0, 0.0),
vec2(W as f32, H as f32),
]);
shared_data_bm.affine = SimpleAffine::new(&aabb, &aabb).unwrap();
shared_data_bm.affine.scale[1] *= -1.0;
}
#[cfg(not(target_os = "macos"))]
{
set_draw_color(Color::White);
draw_rectf(0, 0, WF, HF);
}
let (sender, receiver) = app::channel::<GuiMessage>();
sender.send(GuiMessage::SliderRdpChanged(0.0));
slider_d.set_callback(move |s| {
let distance = s.value() as T * 400.0;
s.set_label(
(" Ramer–Douglas–Peucker distance:".to_string()
+ &distance.to_string()
+ " ")
.as_str(),
);
sender.send(GuiMessage::SliderRdpChanged(distance));
});
slider_n.set_callback(move |s| {
let number_to_remove = (s.value() as T * 200.0) as usize;
s.set_color(Color::Blue);
s.set_label(
(" Visvalingam–Whyatt deleted points:".to_string()
+ &number_to_remove.to_string()
+ " ")
.as_str(),
);
sender.send(GuiMessage::SliderVwChanged(number_to_remove));
});
add_data(Rc::clone(&shared_data_rc));
let shared_data_c = Rc::clone(&shared_data_rc);
frame.draw(move |_| {
let shared_data_b = shared_data_c.borrow();
let make_line = |line: [T; 4], cross: bool| {
let line = shared_data_b.affine.transform_ab_a(line);
if let Ok(line) = line {
draw_line(
line[0] as i32,
line[1] as i32,
line[2] as i32,
line[3] as i32,
);
if cross {
draw_line(
line[0] as i32 - 2,
line[1] as i32 - 2,
line[0] as i32 + 2,
line[1] as i32 + 2,
);
draw_line(
line[2] as i32 + 2,
line[3] as i32 - 2,
line[2] as i32 - 2,
line[3] as i32 + 2,
);
}
}
};
match shared_data_b.last_message {
Some(GuiMessage::SliderRdpChanged(distance)) => {
set_draw_color(Color::White);
draw_rectf(6, 6, WF - 6, HF - 6);
set_line_style(LineStyle::Solid, 1);
for l in shared_data_b.lines.iter() {
if l.is_self_intersecting().unwrap() {
set_draw_color(Color::Red);
} else {
set_draw_color(Color::Black);
}
for a_line in l.window_iter() {
make_line(
[a_line.start.x, a_line.start.y, a_line.end.x, a_line.end.y],
false,
);
}
let simplified_line = l.simplify_rdp(distance);
if simplified_line.is_self_intersecting().unwrap() {
set_draw_color(Color::Red);
} else {
set_draw_color(Color::Green);
}
for a_line in simplified_line.window_iter() {
make_line(
[a_line.start.x, a_line.start.y, a_line.end.x, a_line.end.y],
true,
);
}
}
}
Some(GuiMessage::SliderVwChanged(number_to_remove)) => {
set_draw_color(Color::White);
draw_rectf(6, 6, WF - 6, HF - 6);
set_line_style(LineStyle::Solid, 1);
for l in shared_data_b.lines.iter() {
if l.is_self_intersecting().unwrap() {
set_draw_color(Color::Red);
} else {
set_draw_color(Color::Black);
}
for a_line in l.window_iter() {
make_line(
[a_line.start.x, a_line.start.y, a_line.end.x, a_line.end.y],
false,
);
}
let simplified_line = l.simplify_vw(number_to_remove);
if simplified_line.is_self_intersecting().unwrap() {
set_draw_color(Color::Red);
} else {
set_draw_color(Color::Blue);
}
for a_line in simplified_line.window_iter() {
make_line(
[a_line.start.x, a_line.start.y, a_line.end.x, a_line.end.y],
true,
);
}
}
}
None => (),
}
});
let shared_data_c = Rc::clone(&shared_data_rc);
let mut mouse_drag: Option<(i32, i32)> = None;
wind.handle(move |_, ev| match ev {
fltk::enums::Event::MouseWheel => {
let event = &app::event_coords();
let mut shared_data_bm = shared_data_c.borrow_mut();
let event_dy = match app::event_dy() {
MouseWheel::Up => 3,
MouseWheel::Down => -3,
_ => 0,
};
let reverse_middle = shared_data_bm
.affine
.transform_ba(glam::Vec2::from([event.0 as T, event.1 as T]));
if reverse_middle.is_err() {
println!("{:?}", reverse_middle.err().unwrap());
return false;
}
let reverse_middle = reverse_middle.unwrap();
if event_dy != 0 {
let scale_mod = 1.01_f32.powf(event_dy as T);
shared_data_bm.affine.scale[0] *= scale_mod;
shared_data_bm.affine.scale[1] *= scale_mod;
}
let new_middle = shared_data_bm.affine.transform_ab(glam::Vec2::from([
reverse_middle[0] as T,
reverse_middle[1] as T,
]));
if new_middle.is_err() {
println!("{:?}", new_middle.err().unwrap());
return false;
}
let new_middle = new_middle.unwrap();
shared_data_bm.affine.b_offset[0] += (event.0 as T) - new_middle[0];
shared_data_bm.affine.b_offset[1] += (event.1 as T) - new_middle[1];
redraw();
true
}
fltk::enums::Event::Drag => {
let event = &app::event_coords();
if mouse_drag.is_none() {
mouse_drag = Some(*event);
} else {
let md = mouse_drag.unwrap();
let mut shared_data_bm = shared_data_c.borrow_mut();
shared_data_bm.affine.b_offset[0] += (event.0 - md.0) as T;
shared_data_bm.affine.b_offset[1] += (event.1 - md.1) as T;
mouse_drag = Some(*event);
redraw();
}
true
}
fltk::enums::Event::Released => {
if mouse_drag.is_some() {
mouse_drag = None;
}
true
}
_ => false,
});
let shared_data_c = Rc::clone(&shared_data_rc);
while app.wait() {
if let Some(msg) = receiver.recv() {
let mut shared_data_bm = shared_data_c.borrow_mut();
shared_data_bm.last_message = Some(msg);
redraw();
}
}
}
fn add_data(data: Rc<RefCell<SharedData>>) {
let mut data_b = data.borrow_mut();
data_b.lines.clear();
let mut line: Vec<glam::Vec2> = Vec::with_capacity(150);
for x in (0..150).skip(1) {
let x = (x as T) / 20.0;
let y: f32 = std::f32::consts::E.powf(-x) * (x * 2.0 * std::f32::consts::PI).cos();
line.push(glam::Vec2::new(50.0 + x * 75.0, 200.0 + y * 300.0));
}
data_b.lines.push(line);
let mut line: Vec<glam::Vec2> = Vec::with_capacity(360);
for angle in (0..358).step_by(2) {
let x: f32 = 400.0
+ (angle as T).to_radians().cos() * 250.0
+ (FO as T) * (angle as T * 8.523).to_radians().sin();
let y: f32 = 400.0
+ (angle as T).to_radians().sin() * 150.0
+ (FO as T) * (angle as T * 2.534).to_radians().cos();
line.push(glam::Vec2::new(x, y));
}
line.push(line[0]);
line.push(glam::Vec2::new(250_f32, 300.0));
data_b.lines.push(line);
}