use crate::draw_ctx::FillRule;
use crate::gl_renderer::tess2_bridge::{
agg_path_to_contours, to_tess_winding_rule, try_tessellate,
};
use agg_rust::basics::VertexSource;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct AaTexVertex {
pub pos: [f32; 2],
pub uv: [f32; 2],
}
pub fn tessellate_path_aa_texture<VS: VertexSource>(
path: &mut VS,
fill_rule: FillRule,
) -> Option<(Vec<AaTexVertex>, Vec<u32>)> {
let contours = agg_path_to_contours(path);
if contours.is_empty() {
return None;
}
struct TessOut {
verts: Vec<f32>,
indices: Vec<u32>,
flags: Vec<u8>,
vcount: usize,
}
let out = try_tessellate(
&contours,
to_tess_winding_rule(fill_rule),
"tessellate_path_aa_texture",
|tess| {
Some(TessOut {
verts: tess.vertices().iter().map(|&v| v as f32).collect(),
indices: tess.elements().to_vec(),
flags: tess.edge_flags().to_vec(),
vcount: tess.vertex_count(),
})
},
)?;
let in_verts = &out.verts;
let in_indices = &out.indices;
let edge_flags = &out.flags;
let n_interior = out.vcount;
let n_indices = in_indices.len();
if n_indices == 0 {
return None;
}
let mut out_verts: Vec<AaTexVertex> = Vec::with_capacity(n_indices * 5);
let mut out_indices: Vec<u32> = Vec::with_capacity(n_indices * 3);
let n_tris = n_indices / 3;
for t in 0..n_tris {
let ia = in_indices[t * 3] as usize;
let ib = in_indices[t * 3 + 1] as usize;
let ic = in_indices[t * 3 + 2] as usize;
if ia >= n_interior || ib >= n_interior || ic >= n_interior {
continue;
}
let v0 = [in_verts[ia * 2], in_verts[ia * 2 + 1]];
let v1 = [in_verts[ib * 2], in_verts[ib * 2 + 1]];
let v2 = [in_verts[ic * 2], in_verts[ic * 2 + 1]];
let e0 = edge_flags.get(t * 3).copied().unwrap_or(0);
let e1 = edge_flags.get(t * 3 + 1).copied().unwrap_or(0);
let e2 = edge_flags.get(t * 3 + 2).copied().unwrap_or(0);
match e0 + e1 + e2 {
0 => draw_non_aa_triangle(&mut out_verts, &mut out_indices, v0, v1, v2),
1 => {
if e0 == 1 {
draw_1_edge_triangle(&mut out_verts, &mut out_indices, v0, v1, v2);
} else if e1 == 1 {
draw_1_edge_triangle(&mut out_verts, &mut out_indices, v1, v2, v0);
} else {
draw_1_edge_triangle(&mut out_verts, &mut out_indices, v2, v0, v1);
}
}
2 => {
if e0 == 1 {
if e1 == 1 {
draw_2_edge_triangle(&mut out_verts, &mut out_indices, v0, v1, v2);
} else {
draw_2_edge_triangle(&mut out_verts, &mut out_indices, v2, v0, v1);
}
} else {
draw_2_edge_triangle(&mut out_verts, &mut out_indices, v1, v2, v0);
}
}
3 => draw_3_edge_triangle(&mut out_verts, &mut out_indices, v0, v1, v2),
_ => {}
}
}
Some((out_verts, out_indices))
}
fn draw_non_aa_triangle(
verts: &mut Vec<AaTexVertex>,
indices: &mut Vec<u32>,
p0: [f32; 2],
p1: [f32; 2],
p2: [f32; 2],
) {
let base = verts.len() as u32;
verts.push(AaTexVertex {
pos: p0,
uv: [0.2, 0.25],
});
verts.push(AaTexVertex {
pos: p1,
uv: [0.2, 0.75],
});
verts.push(AaTexVertex {
pos: p2,
uv: [0.9, 0.5],
});
indices.extend_from_slice(&[base, base + 1, base + 2]);
}
fn draw_1_edge_triangle(
verts: &mut Vec<AaTexVertex>,
indices: &mut Vec<u32>,
aa_p0: [f32; 2],
aa_p1: [f32; 2],
non_aa_point: [f32; 2],
) {
if aa_p0 == aa_p1 || aa_p1 == non_aa_point || non_aa_point == aa_p0 {
return;
}
let edge = [aa_p1[0] - aa_p0[0], aa_p1[1] - aa_p0[1]];
let len = (edge[0] * edge[0] + edge[1] * edge[1]).sqrt();
if len < 1e-6 {
return;
}
let edge_n = [edge[0] / len, edge[1] / len];
let mut normal = [edge_n[1], -edge_n[0]];
let dot_n_third =
normal[0] * (non_aa_point[0] - aa_p0[0]) + normal[1] * (non_aa_point[1] - aa_p0[1]);
let edge_dot_p3 = if dot_n_third < 0.0 {
-dot_n_third
} else {
normal = [-normal[0], -normal[1]];
dot_n_third
};
let p0_offset = [aa_p0[0] + normal[0], aa_p0[1] + normal[1]];
let p1_offset = [aa_p1[0] + normal[0], aa_p1[1] + normal[1]];
let inv_1023 = 1.0 / 1023.0_f32;
let tex_p0 = [inv_1023, 0.25];
let tex_p1 = [inv_1023, 0.75];
let tex_p2 = [(1.0 + edge_dot_p3) * inv_1023, 0.25];
let tex_p0_off = [0.0, 0.25];
let tex_p1_off = [0.0, 0.75];
let base = verts.len() as u32;
verts.push(AaTexVertex {
pos: aa_p0,
uv: tex_p0,
});
verts.push(AaTexVertex {
pos: p0_offset,
uv: tex_p0_off,
});
verts.push(AaTexVertex {
pos: p1_offset,
uv: tex_p1_off,
});
verts.push(AaTexVertex {
pos: aa_p1,
uv: tex_p1,
});
verts.push(AaTexVertex {
pos: non_aa_point,
uv: tex_p2,
});
indices.extend_from_slice(&[
base,
base + 1,
base + 2,
base,
base + 2,
base + 3,
base,
base + 3,
base + 4,
]);
}
fn draw_2_edge_triangle(
verts: &mut Vec<AaTexVertex>,
indices: &mut Vec<u32>,
p0: [f32; 2],
p1: [f32; 2],
p2: [f32; 2],
) {
let centroid = [(p0[0] + p1[0] + p2[0]) / 3.0, (p0[1] + p1[1] + p2[1]) / 3.0];
let mid_p0p1 = [(p0[0] + p1[0]) * 0.5, (p0[1] + p1[1]) * 0.5];
let mid_p1p2 = [(p1[0] + p2[0]) * 0.5, (p1[1] + p2[1]) * 0.5];
let inner_p0p1 = [
mid_p0p1[0] + (centroid[0] - mid_p0p1[0]) * 0.001,
mid_p0p1[1] + (centroid[1] - mid_p0p1[1]) * 0.001,
];
let inner_p1p2 = [
mid_p1p2[0] + (centroid[0] - mid_p1p2[0]) * 0.001,
mid_p1p2[1] + (centroid[1] - mid_p1p2[1]) * 0.001,
];
draw_1_edge_triangle(verts, indices, p0, p1, inner_p0p1);
draw_1_edge_triangle(verts, indices, p1, p2, inner_p1p2);
draw_non_aa_triangle(verts, indices, p0, p1, p2);
}
fn draw_3_edge_triangle(
verts: &mut Vec<AaTexVertex>,
indices: &mut Vec<u32>,
p0: [f32; 2],
p1: [f32; 2],
p2: [f32; 2],
) {
let centroid = [(p0[0] + p1[0] + p2[0]) / 3.0, (p0[1] + p1[1] + p2[1]) / 3.0];
draw_1_edge_triangle(verts, indices, p0, p1, centroid);
draw_1_edge_triangle(verts, indices, p1, p2, centroid);
draw_1_edge_triangle(verts, indices, p2, p0, centroid);
}