use crate::conv_stroke::LineJoin;
use crate::path_storage::PathCommand;
use crate::path_storage::Vertex;
use crate::line_interp::LineParameters;
use crate::line_interp::DrawVars;
use crate::SetColor;
use crate::Lines;
use crate::AccurateJoins;
use crate::VertexSource;
use crate::raster::len_i64;
use crate::POLY_SUBPIXEL_SCALE;
pub struct RasterizerOutlineAA<'a,T> where T: SetColor + AccurateJoins + Lines {
pub ren: &'a mut T,
pub start_x: i64,
pub start_y: i64,
pub vertices: Vec<Vertex<i64>>,
pub round_cap: bool,
pub line_join: LineJoin,
}
impl<'a,T> RasterizerOutlineAA<'a, T> where T: SetColor + AccurateJoins + Lines {
pub fn with_renderer(ren: &'a mut T) -> Self {
let line_join = if ren.accurate_join_only() {
LineJoin::MiterAccurate
} else {
LineJoin::Round
};
Self { ren, start_x: 0, start_y: 0, vertices: vec![],
round_cap: false, line_join }
}
pub fn round_cap(&mut self, on: bool) {
self.round_cap = on;
}
pub fn add_path<VS: VertexSource>(&mut self, path: &VS) {
for v in path.xconvert().iter() {
match v.cmd {
PathCommand::MoveTo => self.move_to_d(v.x, v.y),
PathCommand::LineTo => self.line_to_d(v.x, v.y),
PathCommand::Close => self.close(),
PathCommand::Stop => unimplemented!("stop encountered"),
}
}
self.render(false);
}
pub fn conv(&self, v: f64) -> i64 {
(v * POLY_SUBPIXEL_SCALE as f64).round() as i64
}
pub fn move_to_d(&mut self, x: f64, y: f64) {
let x = self.conv(x);
let y = self.conv(y);
self.move_to( x, y );
}
pub fn line_to_d(&mut self, x: f64, y: f64) {
let x = self.conv(x);
let y = self.conv(y);
self.line_to( x, y );
}
pub fn move_to(&mut self, x: i64, y: i64) {
self.start_x = x;
self.start_y = y;
self.vertices.push( Vertex::move_to(x, y) );
}
pub fn line_to(&mut self, x: i64, y: i64) {
let n = self.vertices.len();
if n > 1 {
let v0 = self.vertices[n-1];
let v1 = self.vertices[n-2];
let len = len_i64(&v0,&v1);
if len < POLY_SUBPIXEL_SCALE + POLY_SUBPIXEL_SCALE / 2 {
self.vertices.pop();
}
}
self.vertices.push( Vertex::line_to(x, y) );
}
pub fn close(&mut self) {
}
pub fn cmp_dist_start(d: i64) -> bool { d > 0 }
pub fn cmp_dist_end (d: i64) -> bool { d <= 0 }
pub fn draw_two_points(&mut self) {
debug_assert!(self.vertices.len() == 2);
let p1 = self.vertices.first().unwrap();
let p2 = self.vertices.last().unwrap();
let (x1,y1) = (p1.x, p1.y);
let (x2,y2) = (p2.x, p2.y);
let lprev = len_i64(p1,p2);
let lp = LineParameters::new(x1,y1, x2,y2, lprev);
if self.round_cap {
self.ren.semidot(Self::cmp_dist_start,
x1, y1,
x1 + (y2-y1),
y1 - (x2-x1));
}
self.ren.line3(&lp,
x1 + (y2-y1), y1 - (x2-x1),
x2 + (y2-y1), y2 - (x2-x1));
if self.round_cap {
self.ren.semidot(Self::cmp_dist_end,
x2, y2,
x2 + (y2-y1),
y2 - (x2-x1));
}
}
pub fn draw_three_points(&mut self) {
debug_assert!(self.vertices.len() == 3);
let mut v = self.vertices.iter();
let p1 = v.next().unwrap();
let p2 = v.next().unwrap();
let p3 = v.next().unwrap();
let (x1,y1) = (p1.x, p1.y);
let (x2,y2) = (p2.x, p2.y);
let (x3,y3) = (p3.x, p3.y);
let lprev = len_i64(p1,p2);
let lnext = len_i64(p2,p3);
let lp1 = LineParameters::new(x1, y1, x2, y2, lprev);
let lp2 = LineParameters::new(x2, y2, x3, y3, lnext);
if self.round_cap {
self.ren.semidot(Self::cmp_dist_start,
x1, y1,
x1 + (y2-y1),
y1 - (x2-x1));
}
if self.line_join == LineJoin::Round {
self.ren.line3(&lp1,
x1 + (y2-y1), y1 - (x2-x1),
x2 + (y2-y1), y2 - (x2-x1));
self.ren.pie(x2, y2,
x2 + (y2-y1), y2 - (x2-x1),
x2 + (y3-y2), y2 - (x3-x2));
self.ren.line3(&lp2,
x2 + (y3-y2), y2 - (x3-x2),
x3 + (y3-y2), y3 - (x3-x2));
} else {
let (xb1, yb1) = Self::bisectrix(&lp1, &lp2);
self.ren.line3(&lp1, x1 + (y2-y1), y1 - (x2-x1), xb1, yb1);
self.ren.line3(&lp2, xb1, yb1, x3 + (y3-y2), y3 - (x3-x2));
}
if self.round_cap {
self.ren.semidot(Self::cmp_dist_end,
x3, y3,
x3 + (y3-y2),
y3 - (x3-x2));
}
}
pub fn draw_many_points(&mut self) {
debug_assert!(self.vertices.len() > 3);
let v1 = self.vertices[0];
let x1 = v1.x;
let y1 = v1.y;
let v2 = self.vertices[1];
let x2 = v2.x;
let y2 = v2.y;
let v3 = self.vertices[2];
let v4 = self.vertices[3];
let mut dv = DrawVars::new();
dv.idx = 3;
let lprev = len_i64(&v1,&v2);
dv.lcurr = len_i64(&v2,&v3);
dv.lnext = len_i64(&v3,&v4);
let prev = LineParameters::new(x1,y1, x2, y2, lprev); dv.x1 = v3.x;
dv.y1 = v3.y;
dv.curr = LineParameters::new(x2,y2, dv.x1, dv.y1, dv.lcurr); dv.x2 = v4.x;
dv.y2 = v4.y;
dv.next = LineParameters::new(dv.x1,dv.y1, dv.x2, dv.y2, dv.lnext); dv.xb1 = 0;
dv.xb2 = 0;
dv.yb1 = 0;
dv.yb2 = 0;
dv.flags = match self.line_join {
LineJoin::MiterRevert | LineJoin::Bevel | LineJoin::MiterRound => { 3 },
LineJoin::None => 3,
LineJoin::MiterAccurate => 0,
LineJoin::Miter | LineJoin::Round => {
let mut v = 0;
if prev.diagonal_quadrant() == dv.curr.diagonal_quadrant() {
v |= 1;
}
if dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant() {
v |= 2;
}
v
}
};
if self.round_cap {
self.ren.semidot(Self::cmp_dist_start, x1,y1, x1 + (y2-y1), y1 - (x2-x1));
}
if (dv.flags & 1) == 0 {
if self.line_join == LineJoin::Round {
self.ren.line3(&prev,
x1 + (y2-y1), y1 - (x2-x1),
x2 + (y2-y1), y2 - (x2-x1));
self.ren.pie(prev.x2, prev.y2,
x2 + (y2-y1), y2 - (x2-x1),
dv.curr.x1 + (dv.curr.y2-dv.curr.y1),
dv.curr.y1 + (dv.curr.x2-dv.curr.x1));
} else {
let(xb1, yb1) = Self::bisectrix(&prev, &dv.curr);
self.ren.line3(&prev,
x1 + (y2-y1), y1 - (x2-x1), xb1, yb1);
dv.xb1 = xb1;
dv.yb1 = yb1;
}
} else {
self.ren.line1(&prev, x1 + (y2-y1), y1-(x2-x1));
}
if (dv.flags & 2) == 0 && self.line_join != LineJoin::Round {
let (xb2, yb2) = Self::bisectrix(&dv.curr, &dv.next);
dv.xb2 = xb2;
dv.yb2 = yb2;
}
self.draw(&mut dv, 1, self.vertices.len()-2);
if (dv.flags & 1) == 0 {
if self.line_join == LineJoin::Round {
self.ren.line3(&dv.curr,
dv.curr.x1 + (dv.curr.y2-dv.curr.y1),
dv.curr.y1 - (dv.curr.x2 - dv.curr.x1),
dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
dv.curr.y2 - (dv.curr.x2 - dv.curr.x1));
} else {
self.ren.line3(&dv.curr, dv.xb1, dv.yb1,
dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
dv.curr.y2 - (dv.curr.x2 - dv.curr.x1));
}
} else {
self.ren.line2(&dv.curr,
dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
dv.curr.y2 - (dv.curr.x2 - dv.curr.x1));
}
if self.round_cap {
self.ren.semidot(Self::cmp_dist_end, dv.curr.x2, dv.curr.y2,
dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
dv.curr.y2 - (dv.curr.x2 - dv.curr.x1));
}
}
pub fn render(&mut self, close_polygon: bool) {
if close_polygon {
unimplemented!("no closed polygons yet");
} else {
match self.vertices.len() {
0 | 1 => return,
2 => self.draw_two_points(),
3 => self.draw_three_points(),
_ => self.draw_many_points(),
}
}
self.vertices.clear();
}
pub fn draw(&mut self, dv: &mut DrawVars, start: usize, end: usize) {
for _i in start .. end {
if self.line_join == LineJoin::Round {
dv.xb1 = dv.curr.x1 + (dv.curr.y2 - dv.curr.y1);
dv.yb1 = dv.curr.y1 - (dv.curr.x2 - dv.curr.x1);
dv.xb2 = dv.curr.x2 + (dv.curr.y2 - dv.curr.y1);
dv.yb2 = dv.curr.y2 - (dv.curr.x2 - dv.curr.x1);
}
match dv.flags {
0 => self.ren.line3(&dv.curr, dv.xb1, dv.yb1, dv.xb2, dv.yb2),
1 => self.ren.line2(&dv.curr, dv.xb2, dv.yb2),
2 => self.ren.line1(&dv.curr, dv.xb1, dv.yb1),
3 => self.ren.line0(&dv.curr),
_ => unreachable!("flag value not covered")
}
if self.line_join == LineJoin::Round && (dv.flags & 2) == 0 {
self.ren.pie(dv.curr.x2, dv.curr.y2,
dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
dv.curr.y2 - (dv.curr.x2 - dv.curr.x1),
dv.curr.x2 + (dv.next.y2 - dv.next.y1),
dv.curr.y2 - (dv.next.x2 - dv.next.x1));
}
dv.x1 = dv.x2;
dv.y1 = dv.y2;
dv.lcurr = dv.lnext;
let v0 = self.vertices[dv.idx];
dv.idx += 1;
if dv.idx >= self.vertices.len() {
dv.idx = 0;
}
let v = self.vertices[dv.idx];
dv.x2 = v.x;
dv.y2 = v.y;
dv.lnext = len_i64(&v0,&v);
dv.curr = dv.next;
dv.next = LineParameters::new(dv.x1, dv.y1, dv.x2, dv.y2, dv.lnext);
dv.xb1 = dv.xb2;
dv.yb1 = dv.yb2;
match self.line_join {
LineJoin::Bevel | LineJoin::MiterRevert | LineJoin::MiterRound => dv.flags = 3,
LineJoin::None => dv.flags = 3,
LineJoin::Miter => {
dv.flags >>= 1;
if dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant() {
dv.flags |= 1 << 1;
}
if (dv.flags & 2) == 0 {
let (xb2,yb2) = Self::bisectrix(&dv.curr, &dv.next);
dv.xb2 = xb2;
dv.yb2 = yb2;
}
},
LineJoin::Round => {
dv.flags >>= 1;
if dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant() {
dv.flags |= 1 << 1;
}
},
LineJoin::MiterAccurate => {
dv.flags = 0;
let (xb2,yb2) = Self::bisectrix(&dv.curr, &dv.next);
dv.xb2 = xb2;
dv.yb2 = yb2;
}
}
}
}
pub fn bisectrix(l1: &LineParameters, l2: &LineParameters) -> (i64, i64) {
let k = l2.len as f64 / l1.len as f64;
let mut tx = l2.x2 as f64 - (l2.x1 - l1.x1) as f64 * k;
let mut ty = l2.y2 as f64 - (l2.y1 - l1.y1) as f64 * k;
if ((l2.x2 - l2.x1) as f64 * (l2.y1 - l1.y1) as f64) <
((l2.y2 - l2.y1) as f64 * (l2.x1 - l1.x1) as f64 + 100.0) {
tx -= (tx - l2.x1 as f64) * 2.0;
ty -= (ty - l2.y1 as f64) * 2.0;
}
let dx = tx - l2.x1 as f64;
let dy = ty - l2.y1 as f64;
if ((dx * dx + dy * dy).sqrt() as i64) < POLY_SUBPIXEL_SCALE {
let x = (l2.x1 + l2.x1 + (l2.y1 - l1.y1) + (l2.y2 - l2.y1)) >> 1;
let y = (l2.y1 + l2.y1 - (l2.x1 - l1.x1) - (l2.x2 - l2.x1)) >> 1;
(x,y)
} else {
(tx.round() as i64,ty.round() as i64)
}
}
}