use std::cmp;
use std::cmp::Ordering;
use std::cmp::Ordering::*;
use std::fmt;
use geom::Vec2;
use mask::Mask;
use path::FillRule;
use fixed::Fixed;
type Vid = u16;
#[derive(Clone, Copy, PartialEq, Debug)]
enum FigDir {
Forward,
Reverse,
}
fn opposite(dir: FigDir) -> FigDir {
match dir {
FigDir::Forward => FigDir::Reverse,
FigDir::Reverse => FigDir::Forward,
}
}
struct SubFig {
start : Vid, n_points : Vid, done : bool, }
#[derive(Debug)]
struct Edge {
v1 : Vid, y0f : Option<i32>, y1f : Option<i32>, dir : FigDir, step_pix : Fixed, islope : Fixed, x_bot : Fixed, min_x : Fixed, max_x : Fixed, }
pub struct Fig {
points : Vec<Vec2>, subs : Vec<SubFig>, }
struct Scanner<'a> {
fig : &'a Fig, mask : &'a mut Mask, sgn_area : &'a mut [i16], edges : Vec<Edge>, dir : FigDir, rule : FillRule, y_now : Fixed, y_prev : Fixed, y_bot : Fixed, }
fn cmp_fixed(a: f32, b: f32) -> Ordering {
Fixed::from(a).cmp(&Fixed::from(b))
}
fn line_of(f: Fixed) -> i32 {
(f - Fixed::EPSILON).into()
}
impl SubFig {
fn new(start: Vid) -> SubFig {
SubFig {
start : start,
n_points : 0 as Vid,
done : false,
}
}
fn next(&self, vid: Vid, dir: FigDir) -> Vid {
match dir {
FigDir::Forward => {
let v = vid + 1 as Vid;
if v < self.start + self.n_points {
v
} else {
self.start
}
},
FigDir::Reverse => {
if vid > self.start {
vid - 1 as Vid
} else {
self.start + self.n_points - 1 as Vid
}
},
}
}
}
impl Edge {
fn new(v0: Vid, v1: Vid, p0: Vec2, p1: Vec2, dir: FigDir) -> Edge {
debug_assert!(v0 != v1);
let dx = Fixed::from(p1.x - p0.x); let dy = Fixed::from(p1.y - p0.y); debug_assert!(dy > Fixed::ZERO);
let step_pix = Edge::calculate_step(dx, dy);
let islope = dx / dy;
let y0 = Fixed::from(p0.y);
let y1 = Fixed::from(p1.y);
let y0f = if y0.fract() > Fixed::ZERO { Some(y0.into()) } else { None };
let y1f = if y1.fract() > Fixed::ZERO { Some(y1.into()) } else { None };
let fm = (y0.ceil() - y0) * islope;
let x_bot = fm + Fixed::from(p0.x);
Edge {
v1 : v1,
y0f : y0f,
y1f : y1f,
dir : dir,
step_pix : step_pix,
islope : islope,
x_bot : x_bot,
min_x : Fixed::ZERO,
max_x : Fixed::ZERO,
}
}
fn calculate_step(dx: Fixed, dy: Fixed) -> Fixed {
if dx != Fixed::ZERO {
cmp::min(Fixed::ONE, (dy / dx).abs())
} else {
Fixed::ZERO
}
}
fn is_partial(&self, y: i32) -> bool {
(if let Some(y0) = self.y0f { y == y0 } else { false }) ||
(if let Some(y1) = self.y1f { y == y1 } else { false })
}
fn calculate_x_limits_partial(&mut self, ypb: Fixed, ynb: Fixed) {
let xt = self.x_bot - self.islope * ypb;
let xb = self.x_bot - self.islope * ynb;
self.set_x_limits(xt, xb);
}
fn calculate_x_limits_full(&mut self) {
let xt = self.x_bot - self.islope;
let xb = self.x_bot;
self.set_x_limits(xt, xb);
}
fn set_x_limits(&mut self, xt: Fixed, xb: Fixed) {
self.min_x = cmp::min(xt, xb);
self.max_x = cmp::max(xt, xb);
}
fn min_pix(&self) -> i32 {
self.min_x.into()
}
fn max_pix(&self) -> i32 {
self.max_x.into()
}
fn first_cov(&self) -> Fixed {
let r = if self.min_pix() == self.max_pix() {
(Fixed::ONE - self.x_mid().fract())
} else {
(Fixed::ONE - self.min_x.fract()) * Fixed::HALF
};
self.step_cov(r)
}
fn step_cov(&self, r: Fixed) -> Fixed {
if self.step_pix > Fixed::ZERO {
r * self.step_pix
} else {
r
}
}
fn x_mid(&self) -> Fixed {
self.max_x.avg(self.min_x)
}
fn scan_area(&self, dir: FigDir, cov_full: i16, area: &mut [i16]) {
let w = area.len() as i32;
let ed = if self.dir == dir { 1i16 } else { -1i16 };
let s_0 = pixel_cov(self.first_cov());
let s_n = pixel_cov(self.step_cov(Fixed::ONE));
debug_assert!(s_n > 0);
let mut cc = s_0;
let mut cov = 0i16;
let mut x = self.min_pix();
while x < w && cov < cov_full {
let c = cmp::min(cc, cov_full - cov);
cov += c;
area[cmp::max(0, x) as usize] += c * ed;
cc = s_n;
x += 1;
}
}
}
impl fmt::Debug for Fig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for sub in &self.subs {
write!(f, "sub {}+{} ", sub.start, sub.n_points)?;
for v in sub.start..(sub.start + sub.n_points) {
write!(f, "{:?} ", self.get_point(v))?;
}
}
Ok(())
}
}
impl Fig {
pub fn new() -> Fig {
let points = Vec::with_capacity(1024);
let mut subs = Vec::with_capacity(16);
subs.push(SubFig::new(0 as Vid));
Fig { points, subs }
}
fn sub_current(&mut self) -> &mut SubFig {
self.subs.last_mut().unwrap()
}
fn sub_add(&mut self) {
let vid = self.points.len() as Vid;
self.subs.push(SubFig::new(vid));
}
fn sub_add_point(&mut self) {
let sub = self.sub_current();
sub.n_points += 1;
}
fn sub_is_done(&self) -> bool {
self.subs.last().unwrap().done
}
fn sub_set_done(&mut self) {
let start = { self.sub_current().start };
let pt = self.get_point(start);
let c = self.coincident(pt);
if c { self.points.pop(); }
let sub = self.sub_current();
debug_assert!(sub.n_points > 0);
sub.done = true;
if c { sub.n_points -= 1; }
}
fn sub_at(&self, vid: Vid) -> &SubFig {
for sub in self.subs.iter() {
if vid < sub.start + sub.n_points {
return sub;
}
}
unreachable!();
}
fn next(&self, vid: Vid, dir: FigDir) -> Vid {
let sub = self.sub_at(vid);
sub.next(vid, dir)
}
fn next_y(&self, vid: Vid, dir: FigDir) -> Vid {
let py = self.get_y(vid);
let sub = self.sub_at(vid);
let mut v = sub.next(vid, dir);
while v != vid {
let y = self.get_y(v);
if cmp_fixed(py, y) != Equal {
return v;
}
v = sub.next(v, dir);
}
vid
}
fn next_edge(&self, vid: Vid, dir: FigDir) -> Vid {
let pp = self.get_point(vid);
let sub = self.sub_at(vid);
let mut v = sub.next(vid, dir);
while v != vid {
let p = self.get_point(v);
if p.x < pp.x || cmp_fixed(pp.y, p.y) != Equal {
return v;
}
v = sub.next(v, dir);
}
vid
}
fn same_y(&self, vid: Vid, dir: FigDir) -> Vid {
let py = self.get_y(vid);
let sub = self.sub_at(vid);
let mut vp = vid;
let mut v = sub.next(vid, dir);
while v != vid {
let y = self.get_y(v);
if cmp_fixed(py, y) != Equal {
return vp;
}
vp = v;
v = sub.next(v, dir);
}
vid
}
fn get_dir(&self, vid: Vid) -> FigDir {
let p = self.get_point(vid);
let p0 = self.get_point(self.next(vid, FigDir::Forward));
let p1 = self.get_point(self.next(vid, FigDir::Reverse));
if (p1 - p).widdershins(p0 - p) {
FigDir::Forward
} else {
FigDir::Reverse
}
}
fn get_point(&self, vid: Vid) -> Vec2 {
self.points[vid as usize]
}
fn get_y(&self, vid: Vid) -> f32 {
self.get_point(vid).y
}
pub fn add_point(&mut self, pt: Vec2) {
let n_pts = self.points.len();
if n_pts < Vid::max_value() as usize {
let done = self.sub_is_done();
if done {
self.sub_add();
}
if done || !self.coincident(pt) {
self.points.push(pt);
self.sub_add_point();
}
}
}
fn coincident(&self, pt: Vec2) -> bool {
if let Some(p) = self.points.last() {
pt == *p
} else {
false
}
}
pub fn close(&mut self) {
if self.points.len() > 0 {
self.sub_set_done();
}
}
fn compare_vids(&self, v0: Vid, v1: Vid) -> Ordering {
let p0 = self.get_point(v0);
let p1 = self.get_point(v1);
match cmp_fixed(p0.y, p1.y) {
Less => Less,
Greater => Greater,
Equal => {
p0.x.partial_cmp(&p1.x).unwrap_or(Equal)
},
}
}
pub fn fill(&self, mask: &mut Mask, sgn_area: &mut [i16], rule: FillRule) {
let n_points = self.points.len() as Vid;
if n_points > 0 {
debug_assert!(self.sub_is_done());
let mut vids: Vec<Vid> = (0 as Vid..n_points).collect();
vids.sort_by(|a,b| self.compare_vids(*a, *b));
let dir = self.get_dir(vids[0]);
let mut scan = Scanner::new(self, mask, sgn_area, dir, rule);
for vid in vids {
if scan.is_complete() {
break;
}
scan.scan_vertex(vid);
}
scan.scan_accumulate();
}
}
}
impl<'a> Scanner<'a> {
fn new(fig: &'a Fig, mask: &'a mut Mask, sgn_area: &'a mut [i16],
dir: FigDir, rule: FillRule) -> Scanner<'a>
{
assert!(mask.width() <= sgn_area.len() as u32);
let y_bot = Fixed::from(mask.height() as i32);
let edges = Vec::with_capacity(16);
Scanner {
fig : fig,
mask : mask,
sgn_area : sgn_area,
edges : edges,
dir : dir,
rule : rule,
y_now : Fixed::ZERO,
y_prev : Fixed::ZERO,
y_bot : y_bot,
}
}
fn scan_vertex(&mut self, vid: Vid) {
let y = self.get_y(vid);
let y_vtx = Fixed::from(y);
if self.edges.len() > 0 {
self.scan_to_y(y_vtx);
} else {
self.y_now = y_vtx;
self.y_prev = y_vtx;
}
self.update_edges(vid);
}
fn get_y(&self, vid: Vid) -> f32 {
self.fig.get_y(vid)
}
fn scan_to_y(&mut self, y_vtx: Fixed) {
while self.y_now < y_vtx && !self.is_complete() {
if self.is_line_bottom() {
self.scan_accumulate();
}
self.y_prev = self.y_now;
self.y_now = cmp::min(y_vtx, self.y_now.floor() + Fixed::ONE);
if self.is_next_line() {
self.advance_edges();
}
if self.y_now > Fixed::ZERO {
if self.is_partial() {
self.scan_partial();
}
if self.is_line_bottom() {
self.scan_full();
}
}
}
}
fn is_complete(&self) -> bool {
line_of(self.y_now) >= line_of(self.y_bot)
}
fn is_line_bottom(&self) -> bool {
self.y_now.fract() == Fixed::ZERO
}
fn is_next_line(&self) -> bool {
line_of(self.y_now) > line_of(self.y_prev)
}
fn advance_edges(&mut self) {
for e in self.edges.iter_mut() {
e.x_bot = e.x_bot + e.islope;
}
}
fn is_partial(&self) -> bool {
(self.y_now - self.y_prev) < Fixed::ONE
}
fn scan_partial(&mut self) {
let cov_full = self.scan_coverage();
debug_assert!(cov_full <= 256);
if cov_full <= 0 {
return;
}
let y = line_of(self.y_now);
let y_bot = self.y_now.ceil();
let ypb = y_bot - self.y_prev;
let ynb = y_bot - self.y_now;
let mut area = &mut self.sgn_area;
for e in self.edges.iter_mut() {
if e.is_partial(y) {
e.calculate_x_limits_partial(ypb, ynb);
e.scan_area(self.dir, cov_full, &mut area);
}
}
}
fn scan_full(&mut self) {
let y = line_of(self.y_now);
let mut area = &mut self.sgn_area;
for e in self.edges.iter_mut() {
if !e.is_partial(y) {
e.calculate_x_limits_full();
e.scan_area(self.dir, 256i16, &mut area);
}
}
}
fn scan_accumulate(&mut self) {
if self.y_now > Fixed::ZERO && self.y_now <= self.y_bot {
let y = line_of(self.y_now) as u32;
self.mask.scan_accumulate(self.sgn_area, y, self.rule);
}
}
fn scan_coverage(&self) -> i16 {
debug_assert!(self.y_now > self.y_prev);
debug_assert!(self.y_now <= self.y_prev + Fixed::ONE);
let scan_now = pixel_cov(self.y_now.fract());
let scan_prev = pixel_cov(self.y_prev.fract());
if scan_now == scan_prev && self.y_now.fract() > Fixed::ZERO {
0
} else if scan_now > scan_prev {
scan_now - scan_prev
} else {
256 + scan_now - scan_prev
}
}
fn update_edges(&mut self, vid: Vid) {
let vp = self.fig.next_edge(vid, FigDir::Reverse);
let vn = self.fig.next_edge(vid, FigDir::Forward);
if (vp != vid) && (vn != vid) {
let y = self.get_y(vid);
let cp = cmp_fixed(self.get_y(vp), y);
let cn = cmp_fixed(self.get_y(vn), y);
match (cp, cn) {
(Less, Less) => self.edge_merge(vid),
(Greater, Greater) => self.edge_split(vp, vn),
_ => self.edge_regular(vid),
}
}
}
fn edge_merge(&mut self, vid: Vid) {
let fig = &self.fig;
let mut i = self.edges.len();
while i > 0 {
i -= 1;
let v1 = self.edges[i].v1;
if (fig.same_y(v1, FigDir::Forward) == vid) ||
(fig.same_y(v1, FigDir::Reverse) == vid)
{
self.edges.remove(i);
}
}
}
fn edge_split(&mut self, v0: Vid, v1: Vid) {
let fig = &self.fig;
let v0u = fig.next(v0, FigDir::Forward); let p0u = fig.get_point(v0u); let p0 = fig.get_point(v0); self.edges.push(Edge::new(v0u, v0, p0u, p0, FigDir::Reverse));
let v1u = fig.next(v1, FigDir::Reverse); let p1u = fig.get_point(v1u); let p1 = fig.get_point(v1); self.edges.push(Edge::new(v1u, v1, p1u, p1, FigDir::Forward));
}
fn edge_regular(&mut self, vid: Vid) {
let fig = &self.fig;
for e in self.edges.iter_mut() {
if vid == e.v1 {
let dir = e.dir;
let vn = fig.next_y(vid, dir); let v = fig.next_y(vn, opposite(dir)); let p = fig.get_point(v);
let pn = fig.get_point(vn);
*e = Edge::new(v, vn, p, pn, dir);
break;
}
}
}
}
fn pixel_cov(fcov: Fixed) -> i16 {
debug_assert!(fcov >= Fixed::ZERO && fcov <= Fixed::ONE);
let n: i32 = (fcov << 8).round().into();
n as i16
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn compare_fixed() {
assert!(cmp_fixed(0.0, 0.0) == Ordering::Equal);
assert!(cmp_fixed(0.0, 0.00001) == Ordering::Equal);
assert!(cmp_fixed(0.0, 0.0001) == Ordering::Less);
assert!(cmp_fixed(0.0, -0.0001) == Ordering::Greater);
}
#[test]
fn fig_3x3() {
let mut m = Mask::new(3, 3);
let mut s = vec!(0; 3);
let mut f = Fig::new();
f.add_point(Vec2::new(0.0, 0.0));
f.add_point(Vec2::new(3.0, 3.0));
f.add_point(Vec2::new(0.0, 3.0));
f.close();
f.fill(&mut m, &mut s, FillRule::NonZero);
assert!([128, 0, 0, 255, 128, 0, 255, 255, 128] == m.pixels());
}
#[test]
fn fig_9x1() {
let mut m = Mask::new(9, 1);
let mut s = vec!(0; 16);
let mut f = Fig::new();
f.add_point(Vec2::new(0.0, 0.0));
f.add_point(Vec2::new(9.0, 1.0));
f.add_point(Vec2::new(0.0, 1.0));
f.close();
f.fill(&mut m, &mut s, FillRule::NonZero);
assert!([242, 214, 186, 158, 130, 102, 74, 46, 18] == m.pixels());
}
}