use ImageSize;
use interpolation::lerp;
use types::{
Line,
SourceRectangle,
Polygon,
Polygons,
Radius,
Rectangle,
Resolution,
};
use math::{
multiply,
orient,
translate,
Matrix2d,
Scalar,
Vec2d,
};
use radians::Radians;
#[inline(always)]
pub fn tx(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
(m[0][0] * x + m[0][1] * y + m[0][2]) as f32
}
#[inline(always)]
pub fn ty(m: Matrix2d, x: Scalar, y: Scalar) -> f32 {
(m[1][0] * x + m[1][1] * y + m[1][2]) as f32
}
#[inline(always)]
pub fn with_lerp_polygons_tri_list<F>(
m: Matrix2d,
polygons: Polygons,
tween_factor: Scalar,
f: F
)
where
F: FnMut(&[f32])
{
let poly_len = polygons.len() as Scalar;
let tw = tween_factor % 1.0;
let tw = if tw < 0.0 { tw + 1.0 } else { tw };
let tw = tw * poly_len;
let frame = tw as usize;
let next_frame = (frame + 1) % polygons.len();
let p0 = polygons[frame];
let p1 = polygons[next_frame];
let tw = tw - frame as Scalar;
let n = polygons[0].len();
let mut i: usize = 0;
stream_polygon_tri_list(m, || {
if i >= n { return None; }
let j = i;
i += 1;
Some(lerp(&p0[j], &p1[j], &tw))
}, f);
}
#[inline(always)]
pub fn with_ellipse_tri_list<F>(
resolution: Resolution,
m: Matrix2d,
rect: Rectangle,
f: F
)
where
F: FnMut(&[f32])
{
let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
let (cw, ch) = (0.5 * w, 0.5 * h);
let (cx, cy) = (x + cw, y + ch);
let n = resolution;
let mut i = 0;
stream_polygon_tri_list(m, || {
if i >= n { return None; }
let angle = i as Scalar / n as Scalar * <Scalar as Radians>::_360();
i += 1;
Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
}, f);
}
#[inline(always)]
pub fn with_round_border_line_tri_list<F>(
resolution_cap: Resolution,
m: Matrix2d,
line: Line,
round_border_radius: Radius,
f: F
)
where
F: FnMut(&[f32])
{
let radius = round_border_radius;
let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
let (dx, dy) = (x2 - x1, y2 - y1);
let w = (dx * dx + dy * dy).sqrt();
let m = multiply(m, translate([x1, y1]));
let m = multiply(m, orient(dx, dy));
let n = resolution_cap * 2;
let mut i = 0;
stream_polygon_tri_list(m, || {
if i >= n { return None; }
let j = i;
i += 1;
match j {
j if j >= resolution_cap => {
let angle = (j - resolution_cap) as Scalar
/ (resolution_cap - 1) as Scalar * <Scalar as Radians>::_180()
+ <Scalar as Radians>::_180();
let angle = angle + <Scalar as Radians>::_90();
Some([w + angle.cos() * radius, angle.sin() * radius])
},
j => {
let angle = j as Scalar
/ (resolution_cap - 1) as Scalar
* <Scalar as Radians>::_180();
let angle = angle + <Scalar as Radians>::_90();
Some([angle.cos() * radius, angle.sin() * radius])
},
}
}, f);
}
#[inline(always)]
pub fn with_round_rectangle_tri_list<F>(
resolution_corner: Resolution,
m: Matrix2d,
rect: Rectangle,
round_radius: Radius,
f: F
)
where
F: FnMut(&[f32])
{
use vecmath::traits::{ FromPrimitive, Trig };
let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
let radius = round_radius;
let n = resolution_corner * 4;
let mut i = 0;
stream_polygon_tri_list(m, || {
if i >= n { return None; }
let j = i;
i += 1;
match j {
j if j >= resolution_corner * 3 => {
let angle: Scalar = (j - resolution_corner * 3) as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90()
+ <Scalar as FromPrimitive>::from_f64(3.0)
* <Scalar as Radians>::_90();
let (cx, cy) = (x + w - radius, y + radius);
Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
},
j if j >= resolution_corner * 2 => {
let angle = (j - resolution_corner * 2) as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90()
+ <Scalar as Radians>::_180();
let (cx, cy) = (x + radius, y + radius);
Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
},
j if j >= resolution_corner * 1 => {
let angle = (j - resolution_corner) as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90()
+ <Scalar as Radians>::_90();
let (cx, cy) = (x + radius, y + h - radius);
Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
},
j => {
let angle = j as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90();
let (cx, cy) = (x + w - radius, y + h - radius);
Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
},
}
}, f);
}
pub fn stream_polygon_tri_list<E, F>(
m: Matrix2d,
mut polygon: E,
mut f: F
)
where
E: FnMut() -> Option<Vec2d>,
F: FnMut(&[f32])
{
let mut vertices: [f32; 720] = [0.0; 720];
let fp = match polygon() { None => return, Some(val) => val };
let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
let gp = match polygon() { None => return, Some(val) => val };
let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
let mut gx = gx;
let mut gy = gy;
let mut i = 0;
let vertices_per_triangle = 3;
let position_components_per_vertex = 2;
let align_vertices =
vertices_per_triangle
* position_components_per_vertex;
'read_vertices: loop {
let ind_out = i * align_vertices;
vertices[ind_out + 0] = fx;
vertices[ind_out + 1] = fy;
let ind_out = i * align_vertices + 2;
let p =
match polygon() {
None => break 'read_vertices,
Some(val) => val,
};
let x = tx(m, p[0], p[1]);
let y = ty(m, p[0], p[1]);
vertices[ind_out + 0] = gx;
vertices[ind_out + 1] = gy;
vertices[ind_out + 2] = x;
vertices[ind_out + 3] = y;
gx = x;
gy = y;
i += 1;
if i * align_vertices + 2 >= vertices.len() {
f(&vertices[0..i * align_vertices]);
i = 0;
}
}
if i > 0 {
f(&vertices[0..i * align_vertices]);
}
}
#[inline(always)]
pub fn with_ellipse_border_tri_list<F>(
resolution: Resolution,
m: Matrix2d,
rect: Rectangle,
border_radius: Radius,
f: F
)
where
F: FnMut(&[f32])
{
let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
let (cw, ch) = (0.5 * w, 0.5 * h);
let (cw1, ch1) = (cw + border_radius, ch + border_radius);
let (cw2, ch2) = (cw - border_radius, ch - border_radius);
let (cx, cy) = (x + cw, y + ch);
let n = resolution;
let mut i = 0;
stream_quad_tri_list(m, || {
if i > n { return None; }
let angle = i as Scalar / n as Scalar * <Scalar as Radians>::_360();
let cos = angle.cos();
let sin = angle.sin();
i += 1;
Some(([cx + cos * cw1, cy + sin * ch1],
[cx + cos * cw2, cy + sin * ch2]))
}, f);
}
#[inline(always)]
pub fn with_arc_tri_list<F>(
start_radians: Scalar,
end_radians: Scalar,
resolution: Resolution,
m: Matrix2d,
rect: Rectangle,
border_radius: Radius,
f: F
)
where
F: FnMut(&[f32])
{
let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
let (cw, ch) = (0.5 * w, 0.5 * h);
let (cw1, ch1) = (cw + border_radius, ch + border_radius);
let (cw2, ch2) = (cw - border_radius, ch - border_radius);
let (cx, cy) = (x + cw, y + ch);
let n = resolution;
let mut i = 0;
let (nearest_start_radians, nearest_end_radians) = if start_radians < end_radians {
(start_radians, start_radians + (end_radians - start_radians))
} else {
(end_radians, end_radians + (start_radians - end_radians))
};
stream_quad_tri_list(m, || {
if i > n { return None; }
let angle = nearest_start_radians
+ i as Scalar / n as Scalar * <Scalar as Radians>::_360();
if angle > nearest_end_radians {
return None;
}
let cos = angle.cos();
let sin = angle.sin();
i += 1;
Some(([cx + cos * cw1, cy + sin * ch1],
[cx + cos * cw2, cy + sin * ch2]))
}, f);
}
#[inline(always)]
pub fn with_round_rectangle_border_tri_list<F>(
resolution_corner: Resolution,
m: Matrix2d,
rect: Rectangle,
round_radius: Radius,
border_radius: Radius,
f: F
)
where
F: FnMut(&[f32])
{
use vecmath::traits::{ FromPrimitive, Trig };
let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
let radius = round_radius;
let radius1 = round_radius + border_radius;
let radius2 = round_radius - border_radius;
let n = resolution_corner * 4;
let mut i = 0;
stream_quad_tri_list(m, || {
if i > n { return None; }
let j = i;
i += 1;
match j {
j if j == n => {
let (cx, cy) = (x + w - radius, y + h - radius);
Some(([cx + radius1, cy],
[cx + radius2, cy]))
},
j if j >= resolution_corner * 3 => {
let angle: Scalar = (j - resolution_corner * 3) as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90()
+ <Scalar as FromPrimitive>::from_f64(3.0)
* <Scalar as Radians>::_90();
let (cx, cy) = (x + w - radius, y + radius);
let cos = angle.cos();
let sin = angle.sin();
Some(([cx + cos * radius1, cy + sin * radius1],
[cx + cos * radius2, cy + sin * radius2]))
},
j if j >= resolution_corner * 2 => {
let angle = (j - resolution_corner * 2) as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90()
+ <Scalar as Radians>::_180();
let (cx, cy) = (x + radius, y + radius);
let cos = angle.cos();
let sin = angle.sin();
Some(([cx + cos * radius1, cy + sin * radius1],
[cx + cos * radius2, cy + sin * radius2]))
},
j if j >= resolution_corner * 1 => {
let angle = (j - resolution_corner) as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90()
+ <Scalar as Radians>::_90();
let (cx, cy) = (x + radius, y + h - radius);
let cos = angle.cos();
let sin = angle.sin();
Some(([cx + cos * radius1, cy + sin * radius1],
[cx + cos * radius2, cy + sin * radius2]))
},
j => {
let angle = j as Scalar
/ (resolution_corner - 1) as Scalar
* <Scalar as Radians>::_90();
let (cx, cy) = (x + w - radius, y + h - radius);
let cos = angle.cos();
let sin = angle.sin();
Some(([cx + cos * radius1, cy + sin * radius1],
[cx + cos * radius2, cy + sin * radius2]))
},
}
}, f);
}
pub fn stream_quad_tri_list<E, F>(
m: Matrix2d,
mut quad_edge: E,
mut f: F
)
where
E: FnMut() -> Option<(Vec2d, Vec2d)>,
F: FnMut(&[f32])
{
let mut vertices: [f32; 720] = [0.0; 720];
let (fp1, fp2) = match quad_edge() {
None => return,
Some((val1, val2)) => (val1, val2)
};
let (mut fx1, mut fy1) = (
tx(m, fp1[0], fp1[1]),
ty(m, fp1[0], fp1[1])
);
let (mut fx2, mut fy2) = (
tx(m, fp2[0], fp2[1]),
ty(m, fp2[0], fp2[1])
);
let mut i = 0;
let triangles_per_quad = 2;
let vertices_per_triangle = 3;
let position_components_per_vertex = 2;
let align_vertices =
triangles_per_quad
* vertices_per_triangle
* position_components_per_vertex;
loop {
let (gp1, gp2) = match quad_edge() {
None => break,
Some((val1, val2)) => (val1, val2)
};
let (gx1, gy1) = (
tx(m, gp1[0], gp1[1]),
ty(m, gp1[0], gp1[1])
);
let (gx2, gy2) = (
tx(m, gp2[0], gp2[1]),
ty(m, gp2[0], gp2[1])
);
let ind_out = i * align_vertices;
vertices[ind_out + 0] = fx1;
vertices[ind_out + 1] = fy1;
vertices[ind_out + 2] = fx2;
vertices[ind_out + 3] = fy2;
vertices[ind_out + 4] = gx1;
vertices[ind_out + 5] = gy1;
vertices[ind_out + 6] = fx2;
vertices[ind_out + 7] = fy2;
vertices[ind_out + 8] = gx1;
vertices[ind_out + 9] = gy1;
vertices[ind_out + 10] = gx2;
vertices[ind_out + 11] = gy2;
i += 1;
fx1 = gx1;
fy1 = gy1;
fx2 = gx2;
fy2 = gy2;
if i * align_vertices >= vertices.len() {
f(&vertices[0..i * align_vertices]);
i = 0;
}
}
if i > 0 {
f(&vertices[0..i * align_vertices]);
}
}
pub fn with_polygon_tri_list<F>(
m: Matrix2d,
polygon: Polygon,
f: F
)
where
F: FnMut(&[f32])
{
let n = polygon.len();
let mut i = 0;
stream_polygon_tri_list(
m, || {
if i >= n { return None; }
let j = i;
i += 1;
Some(polygon[j])
}, f
);
}
#[inline(always)]
pub fn rect_tri_list_xy(
m: Matrix2d,
rect: Rectangle
) -> [f32; 12] {
let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
let (x2, y2) = (x + w, y + h);
[
tx(m,x,y), ty(m,x,y),
tx(m,x2,y), ty(m,x2,y),
tx(m,x,y2), ty(m,x,y2),
tx(m,x2,y), ty(m,x2,y),
tx(m,x2,y2), ty(m,x2,y2),
tx(m,x,y2), ty(m,x,y2)
]
}
#[inline(always)]
pub fn rect_border_tri_list_xy(
m: Matrix2d,
rect: Rectangle,
border_radius: Radius,
) -> [f32; 48] {
let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
let (w1, h1) = (w + border_radius, h + border_radius);
let (w2, h2) = (w - border_radius, h - border_radius);
let (x11, y11) = (x - border_radius, y - border_radius);
let (x21, y21) = (x + border_radius, y + border_radius);
let (x12, y12) = (x + w1, y + h1);
let (x22, y22) = (x + w2, y + h2);
[
tx(m, x11, y11), ty(m, x11, y11),
tx(m, x12, y11), ty(m, x12, y11),
tx(m, x21, y21), ty(m, x21, y21),
tx(m, x21, y21), ty(m, x21, y21),
tx(m, x12, y11), ty(m, x12, y11),
tx(m, x22, y21), ty(m, x22, y21),
tx(m, x22, y21), ty(m, x22, y21),
tx(m, x12, y11), ty(m, x12, y11),
tx(m, x12, y12), ty(m, x12, y12),
tx(m, x22, y21), ty(m, x22, y21),
tx(m, x12, y12), ty(m, x12, y12),
tx(m, x22, y22), ty(m, x22, y22),
tx(m, x12, y12), ty(m, x12, y12),
tx(m, x22, y22), ty(m, x22, y22),
tx(m, x11, y12), ty(m, x11, y12),
tx(m, x22, y22), ty(m, x22, y22),
tx(m, x11, y12), ty(m, x11, y12),
tx(m, x21, y22), ty(m, x21, y22),
tx(m, x11, y12), ty(m, x11, y12),
tx(m, x21, y21), ty(m, x21, y21),
tx(m, x21, y22), ty(m, x21, y22),
tx(m, x11, y12), ty(m, x11, y12),
tx(m, x11, y11), ty(m, x11, y11),
tx(m, x21, y21), ty(m, x21, y21),
]
}
#[inline(always)]
pub fn rect_tri_list_uv<I: ImageSize>(
image: &I, source_rect: SourceRectangle
) -> [f32; 12] {
let (w, h) = image.get_size();
let (src_x, src_y, src_w, src_h) =
(source_rect[0], source_rect[1], source_rect[2], source_rect[3]);
let x1 = src_x as f32 / w as f32;
let y1 = src_y as f32 / h as f32;
let x2 = (src_w + src_x) as f32 / w as f32;
let y2 = (src_h + src_y) as f32 / h as f32;
[
x1, y1, x2, y1, x1, y2,
x2, y1, x2, y2, x1, y2
]
}