use super::super::traits::*;
use std::iter;
use std::ops::*;
use curves::*;
use curves::bezier;
use canvas::*;
const MIN_DISTANCE: f64 = 2.0;
const INK_PRESSURE_SCALE: f64 = 50.0;
pub struct InkBrush {
blend_mode: BlendMode,
min_width: f32,
max_width: f32,
scale_up_distance: f32
}
impl InkBrush {
pub fn new(definition: &InkDefinition, drawing_style: BrushDrawingStyle) -> InkBrush {
use BrushDrawingStyle::*;
let blend_mode = match drawing_style {
Draw => BlendMode::SourceOver,
Erase => BlendMode::DestinationOut
};
InkBrush {
blend_mode: blend_mode,
min_width: definition.min_width,
max_width: definition.max_width,
scale_up_distance: definition.scale_up_distance
}
}
}
#[derive(Clone, Copy)]
struct InkCoord {
x: f64,
y: f64,
pressure: f64
}
impl InkCoord {
pub fn pressure(&self) -> f64 { self.pressure }
pub fn set_pressure(&mut self, new_pressure: f64) {
self.pressure = new_pressure;
}
pub fn to_coord2(&self) -> (Coord2, f64) {
(Coord2(self.x, self.y), self.pressure)
}
}
impl<'a> From<&'a RawPoint> for InkCoord {
fn from(src: &'a RawPoint) -> InkCoord {
InkCoord {
x: src.position.0 as f64,
y: src.position.1 as f64,
pressure: (src.pressure as f64)*INK_PRESSURE_SCALE
}
}
}
impl<'a> From<&'a BrushPoint> for InkCoord {
fn from(src: &'a BrushPoint) -> InkCoord {
InkCoord {
x: src.position.0 as f64,
y: src.position.1 as f64,
pressure: (src.width as f64)*INK_PRESSURE_SCALE
}
}
}
impl Add<InkCoord> for InkCoord {
type Output=InkCoord;
#[inline]
fn add(self, rhs: InkCoord) -> InkCoord {
InkCoord {
x: self.x + rhs.x,
y: self.y + rhs.y,
pressure: self.pressure + rhs.pressure
}
}
}
impl Sub<InkCoord> for InkCoord {
type Output=InkCoord;
#[inline]
fn sub(self, rhs: InkCoord) -> InkCoord {
InkCoord {
x: self.x - rhs.x,
y: self.y - rhs.y,
pressure: self.pressure - rhs.pressure
}
}
}
impl Mul<f64> for InkCoord {
type Output=InkCoord;
#[inline]
fn mul(self, rhs: f64) -> InkCoord {
InkCoord {
x: self.x * rhs,
y: self.y * rhs,
pressure: self.pressure * rhs
}
}
}
impl Coordinate for InkCoord {
#[inline]
fn from_components(components: &[f64]) -> InkCoord {
InkCoord { x: components[0], y: components[1], pressure: components[2] }
}
#[inline]
fn origin() -> InkCoord {
InkCoord { x: 0.0, y: 0.0, pressure: 0.0 }
}
#[inline]
fn len() -> usize { 3 }
#[inline]
fn get(&self, index: usize) -> f64 {
match index {
0 => self.x,
1 => self.y,
2 => self.pressure,
_ => panic!("InkCoord only has three components")
}
}
fn from_biggest_components(p1: InkCoord, p2: InkCoord) -> InkCoord {
InkCoord {
x: f64::from_biggest_components(p1.x, p2.x),
y: f64::from_biggest_components(p1.y, p2.y),
pressure: f64::from_biggest_components(p1.pressure, p2.pressure)
}
}
fn from_smallest_components(p1: InkCoord, p2: InkCoord) -> InkCoord {
InkCoord {
x: f64::from_smallest_components(p1.x, p2.x),
y: f64::from_smallest_components(p1.y, p2.y),
pressure: f64::from_smallest_components(p1.pressure, p2.pressure)
}
}
#[inline]
fn distance_to(&self, target: &InkCoord) -> f64 {
let dist_x = target.x-self.x;
let dist_y = target.y-self.y;
let dist_p = target.pressure-self.pressure;
f64::sqrt(dist_x*dist_x + dist_y*dist_y + dist_p*dist_p)
}
#[inline]
fn dot(&self, target: &Self) -> f64 {
self.x*target.x + self.y*target.y + self.pressure*target.pressure
}
}
#[derive(Clone, Copy)]
struct InkCurve {
pub start_point: InkCoord,
pub end_point: InkCoord,
pub control_points: (InkCoord, InkCoord)
}
impl InkCurve {
pub fn from_brush_points(last_point: &BrushPoint, next_point: &BrushPoint) -> InkCurve {
InkCurve {
start_point: InkCoord { x: last_point.position.0 as f64, y: last_point.position.1 as f64, pressure: last_point.width as f64 },
end_point: InkCoord { x: next_point.position.0 as f64, y: next_point.position.1 as f64, pressure: next_point.width as f64 },
control_points: (
InkCoord { x: next_point.cp1.0 as f64, y: next_point.cp1.1 as f64, pressure: next_point.width as f64 },
InkCoord { x: next_point.cp2.0 as f64, y: next_point.cp2.1 as f64, pressure: next_point.width as f64 }
)
}
}
pub fn to_offset_curves(&self, min_width: f64, max_width: f64) -> (Vec<bezier::Curve>, Vec<bezier::Curve>) {
let (start, start_pressure) = self.start_point().to_coord2();
let (end, end_pressure) = self.end_point().to_coord2();
let cp1 = self.control_points.0.to_coord2().0;
let cp2 = self.control_points.1.to_coord2().0;
let start_offset = start_pressure*(max_width-min_width) + min_width;
let end_offset = end_pressure*(max_width-min_width) + min_width;
let base_curve = bezier::Curve::from_points(start, end, cp1, cp2);
let offset_up = bezier::offset(&base_curve, start_offset, end_offset);
let offset_down = bezier::offset(&base_curve, -start_offset, -end_offset);
(offset_up, offset_down)
}
}
impl BezierCurve for InkCurve {
type Point = InkCoord;
fn from_points(start: InkCoord, end: InkCoord, control_point1: InkCoord, control_point2: InkCoord) -> InkCurve {
InkCurve {
start_point: start,
end_point: end,
control_points: (control_point1, control_point2)
}
}
#[inline]
fn start_point(&self) -> InkCoord {
self.start_point
}
#[inline]
fn end_point(&self) -> InkCoord {
self.end_point
}
#[inline]
fn control_points(&self) -> (InkCoord, InkCoord) {
self.control_points
}
}
impl Brush for InkBrush {
fn brush_points_for_raw_points(&self, points: &[RawPoint]) -> Vec<BrushPoint> {
if points.len() <= 2 {
return vec![];
}
let ink_points: Vec<_> = points.iter().map(|point| InkCoord::from(point)).collect();
let mut averaged_points = vec![];
let mut last_point = ink_points[0];
averaged_points.push(last_point);
for point in ink_points.iter().skip(1) {
let distance = last_point.distance_to(point);
if distance < MIN_DISTANCE {
let num_averaged = averaged_points.len();
let current_average = averaged_points[num_averaged-1];
let averaged_point = (current_average + last_point) * 0.5;
averaged_points[num_averaged-1] = averaged_point;
} else {
averaged_points.push(*point);
last_point = *point;
}
}
let mut ink_points = InkCoord::smooth(&averaged_points, &[0.1, 0.25, 0.3, 0.25, 0.1]);
let mut distance = 0.0;
let mut last_point = ink_points[0];
let scale_up_distance = self.scale_up_distance as f64;
for point in ink_points.iter_mut() {
distance += last_point.distance_to(point);
last_point = *point;
if distance > scale_up_distance { break; }
let pressure = point.pressure();
point.set_pressure(pressure * (distance/scale_up_distance));
}
let curve = InkCurve::fit_from_points(&ink_points, 1.0);
let mut brush_points = vec![];
if let Some(curve) = curve {
let start = curve[0].start_point();;
brush_points.push(BrushPoint {
position: (start.x as f32, start.y as f32),
cp1: (0.0, 0.0),
cp2: (0.0, 0.0),
width: (start.pressure/INK_PRESSURE_SCALE) as f32
});
for segment in curve {
let end = segment.end_point();
let (cp1, cp2) = segment.control_points();
brush_points.push(BrushPoint {
position: (end.x as f32, end.y as f32),
cp1: (cp1.x as f32, cp1.y as f32),
cp2: (cp2.x as f32, cp2.y as f32),
width: (end.pressure/INK_PRESSURE_SCALE) as f32
});
}
}
brush_points
}
fn prepare_to_render<'a>(&'a self, properties: &BrushProperties) -> Box<'a+Iterator<Item=Draw>> {
Box::new(vec![
Draw::BlendMode(self.blend_mode),
Draw::FillColor(properties.color.with_alpha(properties.opacity))
].into_iter())
}
fn render_brush<'a>(&'a self, properties: &'a BrushProperties, points: &'a Vec<BrushPoint>) -> Box<'a+Iterator<Item=Draw>> {
let size_ratio = properties.size / self.max_width;
if points.len() < 2 {
return Box::new(iter::empty());
}
let mut curve = vec![];
let mut last_point = &points[0];
for brush_point in points.iter().skip(1) {
curve.push(InkCurve::from_brush_points(last_point, brush_point));
last_point = brush_point;
}
let (upper_curves, lower_curves): (Vec<_>, Vec<_>) = curve.into_iter()
.map(|ink_curve| ink_curve.to_offset_curves((self.min_width*size_ratio) as f64, (self.max_width*size_ratio) as f64))
.unzip();
let Coord2(x, y) = upper_curves[0][0].start_point();
let preamble = vec![
Draw::NewPath,
Draw::Move(x as f32, y as f32)
];
let upper_curves = upper_curves.into_iter()
.flat_map(|curve_list| curve_list.into_iter())
.map(|curve_section| Draw::from(&curve_section));
let Coord2(x, y) = {
let last_section = &lower_curves[lower_curves.len()-1];
let last_curve = &last_section[last_section.len()-1];
last_curve.end_point()
};
let end_cap = Draw::Line(x as f32, y as f32);
let lower_curves = lower_curves.into_iter()
.rev()
.flat_map(|curve_list| curve_list.into_iter().rev())
.map(|curve_section| Draw::from(&curve_section.reverse()));
let finish = Draw::Fill;
let draw_brush = preamble.into_iter()
.chain(upper_curves)
.chain(iter::once(end_cap))
.chain(lower_curves)
.chain(iter::once(finish));
Box::new(draw_brush)
}
fn to_definition(&self) -> (BrushDefinition, BrushDrawingStyle) {
let definition = BrushDefinition::Ink(InkDefinition {
min_width: self.min_width,
max_width: self.max_width,
scale_up_distance: self.scale_up_distance
});
let drawing_style = match self.blend_mode {
BlendMode::DestinationOut => BrushDrawingStyle::Erase,
_ => BrushDrawingStyle::Draw
};
(definition, drawing_style)
}
}