use {Align, Color, Dimensions, FontSize, Point, Rect, Scalar};
use graph::{self, Graph};
use std;
use text;
use theme::Theme;
use widget::{self, primitive, Widget};
pub struct Primitives<'a> {
crop_stack: Vec<(widget::Id, Rect)>,
depth_order: std::slice::Iter<'a, widget::Id>,
graph: &'a Graph,
theme: &'a Theme,
fonts: &'a text::font::Map,
window_rect: Rect,
points: Vec<Point>,
positioned_glyphs: Vec<text::PositionedGlyph>,
}
#[derive(Clone)]
pub struct OwnedPrimitives {
primitives: Vec<OwnedPrimitive>,
points: Vec<Point>,
max_glyphs: usize,
line_infos: Vec<text::line::Info>,
texts_string: String,
}
pub trait PrimitiveWalker {
fn next_primitive(&mut self) -> Option<Primitive>;
}
impl<'a> PrimitiveWalker for Primitives<'a> {
fn next_primitive(&mut self) -> Option<Primitive> {
self.next()
}
}
impl<'a> PrimitiveWalker for WalkOwnedPrimitives<'a> {
fn next_primitive(&mut self) -> Option<Primitive> {
self.next()
}
}
pub struct Primitive<'a> {
pub id: widget::Id,
pub kind: PrimitiveKind<'a>,
pub scizzor: Rect,
pub rect: Rect,
}
pub enum PrimitiveKind<'a> {
Rectangle {
color: Color
},
Polygon {
color: Color,
points: &'a [Point],
},
Lines {
color: Color,
cap: primitive::line::Cap,
thickness: Scalar,
points: &'a [Point],
},
Image {
color: Option<Color>,
source_rect: Option<Rect>,
},
Text {
color: Color,
text: Text<'a>,
font_id: text::font::Id,
},
Other(&'a graph::Container),
}
pub struct Text<'a> {
positioned_glyphs: &'a mut Vec<text::PositionedGlyph>,
window_dim: Dimensions,
text: &'a str,
line_infos: &'a [text::line::Info],
font: &'a text::Font,
font_size: FontSize,
rect: Rect,
x_align: Align,
y_align: Align,
line_spacing: Scalar,
}
#[derive(Clone)]
struct OwnedPrimitive {
id: widget::Id,
kind: OwnedPrimitiveKind,
scizzor: Rect,
rect: Rect,
}
#[derive(Clone)]
enum OwnedPrimitiveKind {
Rectangle {
color: Color,
},
Polygon {
color: Color,
point_range: std::ops::Range<usize>,
},
Lines {
color: Color,
cap: primitive::line::Cap,
thickness: Scalar,
point_range: std::ops::Range<usize>,
},
Image {
color: Option<Color>,
source_rect: Option<Rect>,
},
Text {
color: Color,
font_id: text::font::Id,
text: OwnedText,
},
}
#[derive(Clone)]
struct OwnedText {
str_byte_range: std::ops::Range<usize>,
line_infos_range: std::ops::Range<usize>,
window_dim: Dimensions,
font: text::Font,
font_size: FontSize,
rect: Rect,
x_align: Align,
y_align: Align,
line_spacing: Scalar,
}
pub struct WalkOwnedPrimitives<'a> {
primitives: std::slice::Iter<'a, OwnedPrimitive>,
points: &'a [Point],
line_infos: &'a [text::line::Info],
texts_str: &'a str,
positioned_glyphs: Vec<text::PositionedGlyph>,
}
impl<'a> Text<'a> {
pub fn positioned_glyphs(self, dpi_factor: f32) -> &'a [text::PositionedGlyph] {
let Text {
positioned_glyphs,
window_dim,
text,
line_infos,
font,
font_size,
rect,
x_align,
y_align,
line_spacing,
} = self;
let trans_x = |x: Scalar| (x + window_dim[0] / 2.0) * dpi_factor as Scalar;
let trans_y = |y: Scalar| ((-y) + window_dim[1] / 2.0) * dpi_factor as Scalar;
let line_infos = line_infos.iter().cloned();
let lines = line_infos.clone().map(|info| &text[info.byte_range()]);
let line_rects = text::line::rects(line_infos, font_size, rect,
x_align, y_align, line_spacing);
positioned_glyphs.clear();
let scale = text::pt_to_scale((font_size as f32 * dpi_factor) as FontSize);
for (line, line_rect) in lines.zip(line_rects) {
let (x, y) = (trans_x(line_rect.left()) as f32, trans_y(line_rect.bottom()) as f32);
let point = text::rt::Point { x: x, y: y };
positioned_glyphs.extend(font.layout(line, scale, point).map(|g| g.standalone()));
}
positioned_glyphs
}
}
const CIRCLE_RESOLUTION: usize = 50;
const NUM_POINTS: usize = CIRCLE_RESOLUTION + 1;
impl<'a> Primitives<'a> {
pub fn new(graph: &'a Graph,
depth_order: &'a [widget::Id],
theme: &'a Theme,
fonts: &'a text::font::Map,
window_dim: Dimensions) -> Self
{
Primitives {
crop_stack: Vec::new(),
depth_order: depth_order.iter(),
graph: graph,
theme: theme,
fonts: fonts,
window_rect: Rect::from_xy_dim([0.0, 0.0], window_dim),
points: vec![[0.0, 0.0]; NUM_POINTS],
positioned_glyphs: Vec::new(),
}
}
pub fn next(&mut self) -> Option<Primitive> {
let Primitives {
ref mut crop_stack,
ref mut depth_order,
ref mut points,
ref mut positioned_glyphs,
graph,
theme,
fonts,
window_rect,
} = *self;
while let Some(widget) = next_widget(depth_order, graph, crop_stack, window_rect) {
use widget::primitive::point_path::{State as PointPathState, Style as PointPathStyle};
use widget::primitive::shape::polygon::{State as PolygonState};
use widget::primitive::shape::Style as ShapeStyle;
let (id, scizzor, container) = widget;
let rect = container.rect;
fn state_type_id<W>() -> std::any::TypeId
where W: Widget,
{
std::any::TypeId::of::<W::State>()
}
if container.type_id == state_type_id::<widget::Rectangle>() {
if let Some(rectangle) = container.unique_widget_state::<widget::Rectangle>() {
let graph::UniqueWidgetState { ref style, .. } = *rectangle;
let color = style.get_color(theme);
match *style {
ShapeStyle::Fill(_) => {
let kind = PrimitiveKind::Rectangle { color: color };
return Some(new_primitive(id, kind, scizzor, rect));
},
ShapeStyle::Outline(ref line_style) => {
let (l, r, b, t) = rect.l_r_b_t();
points[0] = [l, b];
points[1] = [l, t];
points[2] = [r, t];
points[3] = [r, b];
points[4] = [l, b];
let cap = line_style.get_cap(theme);
let thickness = line_style.get_thickness(theme);
let kind = PrimitiveKind::Lines {
color: color,
cap: cap,
thickness: thickness,
points: &points[..5],
};
return Some(new_primitive(id, kind, scizzor, rect));
},
}
}
} else if container.type_id == state_type_id::<widget::Oval>() {
if let Some(oval) = container.unique_widget_state::<widget::Oval>() {
use std::f64::consts::PI;
let graph::UniqueWidgetState { ref style, .. } = *oval;
let (x, y, w, h) = rect.x_y_w_h();
let t = 2.0 * PI / CIRCLE_RESOLUTION as Scalar;
let hw = w / 2.0;
let hh = h / 2.0;
let f = |i: Scalar| [x + hw * (t*i).cos(), y + hh * (t*i).sin()];
for i in 0..NUM_POINTS {
points[i] = f(i as f64);
}
let color = style.get_color(theme);
let points = &mut points[..NUM_POINTS];
match *style {
ShapeStyle::Fill(_) => {
let kind = PrimitiveKind::Polygon { color: color, points: points };
return Some(new_primitive(id, kind, scizzor, rect));
},
ShapeStyle::Outline(ref line_style) => {
let cap = line_style.get_cap(theme);
let thickness = line_style.get_thickness(theme);
let kind = PrimitiveKind::Lines {
color: color,
cap: cap,
thickness: thickness,
points: points,
};
return Some(new_primitive(id, kind, scizzor, rect));
},
}
}
} else if container.type_id == std::any::TypeId::of::<PolygonState>() {
use widget::primitive::shape::Style;
if let Some(polygon) = container.state_and_style::<PolygonState, Style>() {
let graph::UniqueWidgetState { ref state, ref style } = *polygon;
let color = style.get_color(theme);
let points = &state.points[..];
match *style {
ShapeStyle::Fill(_) => {
let kind = PrimitiveKind::Polygon { color: color, points: points };
return Some(new_primitive(id, kind, scizzor, rect));
},
ShapeStyle::Outline(ref line_style) => {
let cap = line_style.get_cap(theme);
let thickness = line_style.get_thickness(theme);
let kind = PrimitiveKind::Lines {
color: color,
cap: cap,
thickness: thickness,
points: points,
};
return Some(new_primitive(id, kind, scizzor, rect));
},
}
}
} else if container.type_id == state_type_id::<widget::Line>() {
if let Some(line) = container.unique_widget_state::<widget::Line>() {
let graph::UniqueWidgetState { ref state, ref style } = *line;
let color = style.get_color(theme);
let cap = style.get_cap(theme);
let thickness = style.get_thickness(theme);
points[0] = state.start;
points[1] = state.end;
let points = &points[..2];
let kind = PrimitiveKind::Lines {
color: color,
cap: cap,
thickness: thickness,
points: points,
};
return Some(new_primitive(id, kind, scizzor, rect));
}
} else if container.type_id == std::any::TypeId::of::<PointPathState>() {
if let Some(point_path) = container.state_and_style::<PointPathState, PointPathStyle>() {
let graph::UniqueWidgetState { ref state, ref style } = *point_path;
let color = style.get_color(theme);
let cap = style.get_cap(theme);
let thickness = style.get_thickness(theme);
let points = &state.points[..];
let kind = PrimitiveKind::Lines {
color: color,
cap: cap,
thickness: thickness,
points: points,
};
return Some(new_primitive(id, kind, scizzor, rect));
}
} else if container.type_id == state_type_id::<widget::Text>() {
if let Some(text) = container.unique_widget_state::<widget::Text>() {
let graph::UniqueWidgetState { ref state, ref style } = *text;
let font_id = match style.font_id(theme).or_else(|| fonts.ids().next()) {
Some(id) => id,
None => continue,
};
let font = match fonts.get(font_id) {
Some(font) => font,
None => continue,
};
let color = style.color(theme);
let font_size = style.font_size(theme);
let line_spacing = style.line_spacing(theme);
let x_align = style.text_align(theme);
let y_align = Align::End;
let text = Text {
positioned_glyphs: positioned_glyphs,
window_dim: window_rect.dim(),
text: &state.string,
line_infos: &state.line_infos,
font: font,
font_size: font_size,
rect: rect,
x_align: x_align,
y_align: y_align,
line_spacing: line_spacing,
};
let kind = PrimitiveKind::Text {
color: color,
text: text,
font_id: font_id,
};
return Some(new_primitive(id, kind, scizzor, rect));
}
} else if container.type_id == state_type_id::<widget::Image>() {
use widget::primitive::image::{State, Style};
if let Some(image) = container.state_and_style::<State, Style>() {
let graph::UniqueWidgetState { ref state, ref style } = *image;
let color = style.maybe_color(theme);
let kind = PrimitiveKind::Image {
color: color,
source_rect: state.src_rect,
};
return Some(new_primitive(id, kind, scizzor, rect));
}
} else {
let kind = PrimitiveKind::Other(container);
return Some(new_primitive(id, kind, scizzor, rect));
}
}
None
}
pub fn owned(mut self) -> OwnedPrimitives {
let mut primitives = Vec::with_capacity(self.depth_order.len());
let mut primitive_points = Vec::new();
let mut primitive_line_infos = Vec::new();
let mut texts_string = String::new();
let mut max_glyphs = 0;
while let Some(Primitive { id, rect, scizzor, kind }) = self.next() {
let new = |kind| OwnedPrimitive {
id: id,
rect: rect,
scizzor: scizzor,
kind: kind,
};
match kind {
PrimitiveKind::Rectangle { color } => {
let kind = OwnedPrimitiveKind::Rectangle { color: color };
primitives.push(new(kind));
},
PrimitiveKind::Polygon { color, points } => {
let start = primitive_points.len();
primitive_points.extend(points.iter().cloned());
let end = primitive_points.len();
let kind = OwnedPrimitiveKind::Polygon {
color: color,
point_range: start..end,
};
primitives.push(new(kind));
},
PrimitiveKind::Lines { color, cap, thickness, points } => {
let start = primitive_points.len();
primitive_points.extend(points.iter().cloned());
let end = primitive_points.len();
let kind = OwnedPrimitiveKind::Lines {
color: color,
cap: cap,
thickness: thickness,
point_range: start..end,
};
primitives.push(new(kind));
},
PrimitiveKind::Image { color, source_rect } => {
let kind = OwnedPrimitiveKind::Image {
color: color,
source_rect: source_rect,
};
primitives.push(new(kind));
},
PrimitiveKind::Text { color, font_id, text } => {
let Text {
window_dim,
text,
line_infos,
font,
font_size,
rect,
x_align,
y_align,
line_spacing,
..
} = text;
max_glyphs = std::cmp::max(max_glyphs, text.len());
let start_str_byte = texts_string.len();
texts_string.push_str(text);
let end_str_byte = texts_string.len();
let start_line_info_idx = primitive_line_infos.len();
primitive_line_infos.extend(line_infos.iter().cloned());
let end_line_info_idx = primitive_line_infos.len();
let owned_text = OwnedText {
str_byte_range: start_str_byte..end_str_byte,
line_infos_range: start_line_info_idx..end_line_info_idx,
window_dim: window_dim,
font: font.clone(),
font_size: font_size,
rect: rect,
x_align: x_align,
y_align: y_align,
line_spacing: line_spacing,
};
let kind = OwnedPrimitiveKind::Text {
color: color,
font_id: font_id,
text: owned_text,
};
primitives.push(new(kind));
},
PrimitiveKind::Other(_) => (),
}
}
OwnedPrimitives {
primitives: primitives,
points: primitive_points,
max_glyphs: max_glyphs,
line_infos: primitive_line_infos,
texts_string: texts_string,
}
}
}
impl OwnedPrimitives {
pub fn walk(&self) -> WalkOwnedPrimitives {
let OwnedPrimitives {
ref primitives,
ref points,
ref line_infos,
ref texts_string,
max_glyphs,
} = *self;
WalkOwnedPrimitives {
primitives: primitives.iter(),
points: points,
line_infos: line_infos,
texts_str: texts_string,
positioned_glyphs: Vec::with_capacity(max_glyphs),
}
}
}
impl<'a> WalkOwnedPrimitives<'a> {
pub fn next(&mut self) -> Option<Primitive> {
let WalkOwnedPrimitives {
ref mut primitives,
ref mut positioned_glyphs,
points,
line_infos,
texts_str,
} = *self;
primitives.next().map(move |&OwnedPrimitive { id, rect, scizzor, ref kind }| {
let new = |kind| Primitive {
id: id,
rect: rect,
scizzor: scizzor,
kind: kind,
};
match *kind {
OwnedPrimitiveKind::Rectangle { color } => {
let kind = PrimitiveKind::Rectangle { color: color };
new(kind)
},
OwnedPrimitiveKind::Polygon { color, ref point_range } => {
let kind = PrimitiveKind::Polygon {
color: color,
points: &points[point_range.clone()],
};
new(kind)
},
OwnedPrimitiveKind::Lines { color, cap, thickness, ref point_range } => {
let kind = PrimitiveKind::Lines {
color: color,
cap: cap,
thickness: thickness,
points: &points[point_range.clone()],
};
new(kind)
},
OwnedPrimitiveKind::Text { color, font_id, ref text } => {
let OwnedText {
ref str_byte_range,
ref line_infos_range,
ref font,
window_dim,
font_size,
rect,
x_align,
y_align,
line_spacing,
} = *text;
let text_str = &texts_str[str_byte_range.clone()];
let line_infos = &line_infos[line_infos_range.clone()];
let text = Text {
positioned_glyphs: positioned_glyphs,
window_dim: window_dim,
text: text_str,
line_infos: line_infos,
font: font,
font_size: font_size,
rect: rect,
x_align: x_align,
y_align: y_align,
line_spacing: line_spacing,
};
let kind = PrimitiveKind::Text {
color: color,
font_id: font_id,
text: text,
};
new(kind)
},
OwnedPrimitiveKind::Image { color, source_rect } => {
let kind = PrimitiveKind::Image {
color: color,
source_rect: source_rect,
};
new(kind)
},
}
})
}
}
fn new_primitive(id: widget::Id, kind: PrimitiveKind, scizzor: Rect, rect: Rect) -> Primitive {
Primitive {
id: id,
kind: kind,
scizzor: scizzor,
rect: rect,
}
}
fn next_widget<'a>(depth_order: &mut std::slice::Iter<widget::Id>,
graph: &'a Graph,
crop_stack: &mut Vec<(widget::Id, Rect)>,
window_rect: Rect) -> Option<(widget::Id, Rect, &'a graph::Container)>
{
while let Some(&id) = depth_order.next() {
let container = match graph.widget(id) {
Some(container) => container,
None => continue,
};
while let Some(&(crop_parent_idx, _)) = crop_stack.last() {
if graph.does_recursive_depth_edge_exist(crop_parent_idx, id) {
break;
} else {
crop_stack.pop();
}
}
let scizzor = crop_stack.last().map(|&(_, scizzor)| scizzor).unwrap_or(window_rect);
if container.crop_kids {
let scizzor_rect = container.kid_area.rect.overlap(scizzor)
.unwrap_or_else(|| Rect::from_xy_dim([0.0, 0.0], [0.0, 0.0]));
crop_stack.push((id, scizzor_rect));
}
let is_visible = container.rect.overlap(window_rect).is_some()
&& graph::algo::cropped_area_of_widget(graph, id).is_some();
if !is_visible {
continue;
}
return Some((id, scizzor, container));
}
None
}