use std::convert::From;
use conrod_core::{self as conrod, position::Scalar as ConrodScalar, Positionable, Widget};
use plotters_backend::{
text_anchor, BackendColor, BackendCoord, BackendStyle, BackendTextStyle, DrawingBackend,
DrawingErrorKind,
};
use crate::error::ConrodBackendError;
use crate::graph::ConrodBackendReusableGraph;
use crate::triangulate;
use crate::utils::{color, convert, path, position, shape};
pub struct ConrodBackend<'a, 'b> {
ui: &'a mut conrod::UiCell<'b>,
size: (u32, u32),
parent: conrod::widget::Id,
font: conrod::text::font::Id,
graph: &'a mut ConrodBackendReusableGraph,
}
impl<'a, 'b> ConrodBackend<'a, 'b> {
pub fn new(
ui: &'a mut conrod::UiCell<'b>,
size: (u32, u32),
parent: conrod::widget::Id,
font: conrod::text::font::Id,
graph: &'a mut ConrodBackendReusableGraph,
) -> Self {
graph.prepare();
Self {
ui,
parent,
font,
size,
graph,
}
}
}
impl<'a, 'b> DrawingBackend for ConrodBackend<'a, 'b> {
type ErrorType = ConrodBackendError;
fn get_size(&self) -> (u32, u32) {
self.size
}
fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<ConrodBackendError>> {
Ok(())
}
fn present(&mut self) -> Result<(), DrawingErrorKind<ConrodBackendError>> {
Ok(())
}
fn draw_pixel(
&mut self,
_point: BackendCoord,
_color: BackendColor,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
Ok(())
}
fn draw_line<S: BackendStyle>(
&mut self,
from: BackendCoord,
to: BackendCoord,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
if let Some(position) = position::PositionParent::from(&self.ui, self.parent) {
let line_style = conrod::widget::primitive::line::Style::solid()
.color(color::Color::from(&style.color()).into())
.thickness(style.stroke_width() as ConrodScalar);
conrod::widget::line::Line::abs_styled(
position.abs_point_conrod_scalar(&from),
position.abs_point_conrod_scalar(&to),
line_style,
)
.top_left_of(self.parent)
.set(self.graph.line.next(&mut self.ui), &mut self.ui);
Ok(())
} else {
Err(DrawingErrorKind::DrawingError(
ConrodBackendError::NoParentPosition,
))
}
}
fn draw_rect<S: BackendStyle>(
&mut self,
upper_left: BackendCoord,
bottom_right: BackendCoord,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
let rectangle_style = if fill {
conrod::widget::primitive::shape::Style::fill_with(
color::Color::from(&style.color()).into(),
)
} else {
conrod::widget::primitive::shape::Style::outline_styled(
conrod::widget::primitive::line::Style::new()
.color(color::Color::from(&style.color()).into())
.thickness(style.stroke_width() as ConrodScalar),
)
};
conrod::widget::rectangle::Rectangle::styled(
[
(bottom_right.0 - upper_left.0) as ConrodScalar,
(bottom_right.1 - upper_left.1) as ConrodScalar,
],
rectangle_style,
)
.top_left_with_margins_on(
self.parent,
upper_left.1 as ConrodScalar,
upper_left.0 as ConrodScalar,
)
.set(self.graph.rect.next(&mut self.ui), &mut self.ui);
Ok(())
}
fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
path: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
if let Some(position) = position::PositionParent::from(&self.ui, self.parent) {
let line_style = conrod::widget::primitive::line::Style::solid()
.color(color::Color::from(&style.color()).into())
.thickness(style.stroke_width() as ConrodScalar);
conrod::widget::point_path::PointPath::abs_styled(
path.into_iter()
.map(|point| position.abs_point_conrod_scalar(&point))
.collect::<Vec<conrod::position::Point>>(),
line_style,
)
.top_left_of(self.parent)
.set(self.graph.path.next(&mut self.ui), &mut self.ui);
Ok(())
} else {
Err(DrawingErrorKind::DrawingError(
ConrodBackendError::NoParentPosition,
))
}
}
fn draw_circle<S: BackendStyle>(
&mut self,
center: BackendCoord,
radius: u32,
style: &S,
fill: bool,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
let circle_style = if fill {
conrod::widget::primitive::shape::Style::fill_with(
color::Color::from(&style.color()).into(),
)
} else {
conrod::widget::primitive::shape::Style::outline_styled(
conrod::widget::primitive::line::Style::new()
.color(color::Color::from(&style.color()).into())
.thickness(style.stroke_width() as ConrodScalar),
)
};
conrod::widget::circle::Circle::styled(radius as ConrodScalar, circle_style)
.top_left_with_margins_on(
self.parent,
(center.1 - radius as i32) as ConrodScalar,
(center.0 - radius as i32) as ConrodScalar,
)
.set(self.graph.circle.next(&mut self.ui), &mut self.ui);
Ok(())
}
fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(
&mut self,
vert: I,
style: &S,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
if let Some(position) = position::PositionParent::from(&self.ui, self.parent) {
let simplified_path: Vec<_> = path::PathSimplifier::from(
vert.into_iter()
.map(|vertex| position.abs_point_path_simplifier(&vertex)),
)
.collect();
if let Ok(mut shape_splitter) = shape::ShapeSplitter::try_from(&simplified_path) {
let polygon_style = conrod::widget::primitive::shape::Style::fill_with(
color::Color::from(&style.color()).into(),
);
for shape_points in shape_splitter.collect() {
if shape_points.len() >= 3 {
let triangles = triangulate::triangulate_points(shape_points.iter());
for index in 0..triangles.size() {
conrod::widget::polygon::Polygon::abs_styled(
triangles.get_triangle(index).points.iter().copied(),
polygon_style,
)
.top_left_of(self.parent)
.set(self.graph.fill.next(&mut self.ui), &mut self.ui);
}
}
}
}
Ok(())
} else {
Err(DrawingErrorKind::DrawingError(
ConrodBackendError::NoParentPosition,
))
}
}
fn draw_text<S: BackendTextStyle>(
&mut self,
text: &str,
style: &S,
pos: BackendCoord,
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
let (text_width_estimated, font_size_final) = convert::font_style(text, style.size());
let mut text_style = conrod::widget::primitive::text::Style::default();
text_style.color = Some(color::Color::from(&style.color()).into());
text_style.font_id = Some(Some(self.font));
text_style.font_size = Some(font_size_final);
text_style.justify = Some(match style.anchor().h_pos {
text_anchor::HPos::Left => conrod::text::Justify::Left,
text_anchor::HPos::Right => conrod::text::Justify::Right,
text_anchor::HPos::Center => conrod::text::Justify::Center,
});
conrod::widget::Text::new(text)
.with_style(text_style)
.top_left_with_margins_on(
self.parent,
pos.1 as ConrodScalar - (style.size() / 2.0 + 1.0),
pos.0 as ConrodScalar - text_width_estimated,
)
.set(self.graph.text.next(&mut self.ui), &mut self.ui);
Ok(())
}
fn estimate_text_size<S: BackendTextStyle>(
&self,
text: &str,
style: &S,
) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {
let (text_width_estimated, text_height_estimated) = convert::font_style(text, style.size());
Ok((text_width_estimated as u32, text_height_estimated))
}
fn blit_bitmap(
&mut self,
_pos: BackendCoord,
(_iw, _ih): (u32, u32),
_src: &[u8],
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
Ok(())
}
}