Optimize meshing

This commit is contained in:
Emil Ernerfeldt 2020-05-11 13:11:01 +02:00
parent 6bee26ca59
commit 4fcea59929
8 changed files with 89 additions and 46 deletions

View file

@ -157,8 +157,8 @@ fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) {
)); ));
// Draw a minus: // Draw a minus:
ui.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::LineSegment {
points: vec![ points: [
pos2(small_icon_rect.left(), small_icon_rect.center().y), pos2(small_icon_rect.left(), small_icon_rect.center().y),
pos2(small_icon_rect.right(), small_icon_rect.center().y), pos2(small_icon_rect.right(), small_icon_rect.center().y),
], ],
@ -168,8 +168,8 @@ fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) {
if !state.open { if !state.open {
// Draw it as a plus: // Draw it as a plus:
ui.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::LineSegment {
points: vec![ points: [
pos2(small_icon_rect.center().x, small_icon_rect.top()), pos2(small_icon_rect.center().x, small_icon_rect.top()),
pos2(small_icon_rect.center().x, small_icon_rect.bottom()), pos2(small_icon_rect.center().x, small_icon_rect.bottom()),
], ],

View file

@ -256,7 +256,7 @@ fn paint_resize_corner(ui: &mut Ui, interact: &InteractInfo) {
while w < 12.0 { while w < 12.0 {
ui.add_paint_cmd(PaintCmd::line_segment( ui.add_paint_cmd(PaintCmd::line_segment(
(pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)), [pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)],
color, color,
width, width,
)); ));

View file

@ -8,6 +8,7 @@ pub struct Vec2 {
pub y: f32, pub y: f32,
} }
#[inline(always)]
pub fn vec2(x: f32, y: f32) -> Vec2 { pub fn vec2(x: f32, y: f32) -> Vec2 {
Vec2 { x, y } Vec2 { x, y }
} }
@ -39,6 +40,7 @@ impl Vec2 {
} }
} }
#[inline(always)]
pub fn rot90(self) -> Self { pub fn rot90(self) -> Self {
vec2(self.y, -self.x) vec2(self.y, -self.x)
} }
@ -212,6 +214,13 @@ pub fn pos2(x: f32, y: f32) -> Pos2 {
} }
impl Pos2 { impl Pos2 {
pub fn to_vec2(self) -> Vec2 {
Vec2 {
x: self.x,
y: self.y,
}
}
pub fn dist(self: Self, other: Self) -> f32 { pub fn dist(self: Self, other: Self) -> f32 {
(self - other).length() (self - other).length()
} }

View file

@ -147,6 +147,7 @@ impl Path {
self.0.clear(); self.0.clear();
} }
#[inline(always)]
pub fn add_point(&mut self, pos: Pos2, normal: Vec2) { pub fn add_point(&mut self, pos: Pos2, normal: Vec2) {
self.0.push(PathPoint { pos, normal }); self.0.push(PathPoint { pos, normal });
} }
@ -160,22 +161,33 @@ impl Path {
} }
} }
pub fn add_line_segment(&mut self, points: [Pos2; 2]) {
let normal = (points[1] - points[0]).normalized().rot90();
self.add_point(points[0], normal);
self.add_point(points[1], normal);
}
pub fn add_line(&mut self, points: &[Pos2]) { pub fn add_line(&mut self, points: &[Pos2]) {
let n = points.len(); let n = points.len();
assert!(n >= 2); assert!(n >= 2);
self.add_point(points[0], (points[1] - points[0]).normalized().rot90()); if n == 2 {
for i in 1..n - 1 { // Common case optimization:
let n0 = (points[i] - points[i - 1]).normalized().rot90(); self.add_line_segment([points[0], points[1]]);
let n1 = (points[i + 1] - points[i]).normalized().rot90(); } else {
let v = (n0 + n1) / 2.0; self.add_point(points[0], (points[1] - points[0]).normalized().rot90());
let normal = v / v.length_sq(); for i in 1..n - 1 {
self.add_point(points[i], normal); // TODO: handle VERY sharp turns better let n0 = (points[i] - points[i - 1]).normalized().rot90();
let n1 = (points[i + 1] - points[i]).normalized().rot90();
let v = (n0 + n1) / 2.0;
let normal = v / v.length_sq();
self.add_point(points[i], normal); // TODO: handle VERY sharp turns better
}
self.add_point(
points[n - 1],
(points[n - 1] - points[n - 2]).normalized().rot90(),
);
} }
self.add_point(
points[n - 1],
(points[n - 1] - points[n - 2]).normalized().rot90(),
);
} }
pub fn add_rectangle(&mut self, rect: Rect) { pub fn add_rectangle(&mut self, rect: Rect) {
@ -387,7 +399,16 @@ pub fn paint_path(
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, out_mesh: &mut Mesh) { /// path: only used to reuse memory
pub fn mesh_command(
path: &mut Path,
options: MesherOptions,
fonts: &Fonts,
command: PaintCmd,
out_mesh: &mut Mesh,
) {
path.clear();
match command { match command {
PaintCmd::Circle { PaintCmd::Circle {
center, center,
@ -395,7 +416,6 @@ pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, ou
outline, outline,
radius, radius,
} => { } => {
let mut path = Path::default();
path.add_circle(center, radius); path.add_circle(center, radius);
if let Some(color) = fill_color { if let Some(color) = fill_color {
fill_closed_path(out_mesh, options, &path.0, color); fill_closed_path(out_mesh, options, &path.0, color);
@ -414,14 +434,21 @@ pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, ou
PaintCmd::Mesh(mesh) => { PaintCmd::Mesh(mesh) => {
out_mesh.append(&mesh); out_mesh.append(&mesh);
} }
PaintCmd::Line { PaintCmd::LineSegment {
points,
color,
width,
} => {
path.add_line_segment(points);
paint_path(out_mesh, options, Open, &path.0, color, width);
}
PaintCmd::LinePath {
points, points,
color, color,
width, width,
} => { } => {
let n = points.len(); let n = points.len();
if n >= 2 { if n >= 2 {
let mut path = Path::default();
path.add_line(&points); path.add_line(&points);
paint_path(out_mesh, options, Open, &path.0, color, width); paint_path(out_mesh, options, Open, &path.0, color, width);
} }
@ -462,7 +489,6 @@ pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, ou
rect.min = rect.min.max(pos2(-1e7, -1e7)); rect.min = rect.min.max(pos2(-1e7, -1e7));
rect.max = rect.max.min(pos2(1e7, 1e7)); rect.max = rect.max.min(pos2(1e7, 1e7));
let mut path = Path::default();
path.add_rounded_rectangle(rect, corner_radius); path.add_rounded_rectangle(rect, corner_radius);
if let Some(fill_color) = fill_color { if let Some(fill_color) = fill_color {
fill_closed_path(out_mesh, options, &path.0, fill_color); fill_closed_path(out_mesh, options, &path.0, fill_color);
@ -512,6 +538,8 @@ pub fn mesh_paint_commands(
fonts: &Fonts, fonts: &Fonts,
commands: Vec<(Rect, PaintCmd)>, commands: Vec<(Rect, PaintCmd)>,
) -> Vec<(Rect, Mesh)> { ) -> Vec<(Rect, Mesh)> {
let mut reused_path = Path::default();
let mut batches = PaintBatches::default(); let mut batches = PaintBatches::default();
for (clip_rect, cmd) in commands { for (clip_rect, cmd) in commands {
// TODO: cull(clip_rect, cmd) // TODO: cull(clip_rect, cmd)
@ -522,6 +550,7 @@ pub fn mesh_paint_commands(
if options.debug_paint_clip_rects && !clip_rect.is_empty() { if options.debug_paint_clip_rects && !clip_rect.is_empty() {
let out_mesh = &mut batches.last_mut().unwrap().1; let out_mesh = &mut batches.last_mut().unwrap().1;
mesh_command( mesh_command(
&mut reused_path,
options, options,
fonts, fonts,
PaintCmd::Rect { PaintCmd::Rect {
@ -536,7 +565,7 @@ pub fn mesh_paint_commands(
} }
let out_mesh = &mut batches.last_mut().unwrap().1; let out_mesh = &mut batches.last_mut().unwrap().1;
mesh_command(options, fonts, cmd, out_mesh); mesh_command(&mut reused_path, options, fonts, cmd, out_mesh);
} }
batches batches

View file

@ -78,7 +78,12 @@ pub enum PaintCmd {
outline: Option<Outline>, outline: Option<Outline>,
radius: f32, radius: f32,
}, },
Line { LineSegment {
points: [Pos2; 2],
color: Color,
width: f32,
},
LinePath {
points: Vec<Pos2>, points: Vec<Pos2>,
color: Color, color: Color,
width: f32, width: f32,
@ -111,9 +116,9 @@ pub enum PaintCmd {
} }
impl PaintCmd { impl PaintCmd {
pub fn line_segment(seg: (Pos2, Pos2), color: Color, width: f32) -> Self { pub fn line_segment(points: [Pos2; 2], color: Color, width: f32) -> Self {
Self::Line { Self::LineSegment {
points: vec![seg.0, seg.1], points,
color, color,
width, width,
} }

View file

@ -369,17 +369,17 @@ impl Ui {
if too_wide { if too_wide {
self.add_paint_cmd(PaintCmd::line_segment( self.add_paint_cmd(PaintCmd::line_segment(
(rect.left_top(), rect.left_bottom()), [rect.left_top(), rect.left_bottom()],
color, color,
width, width,
)); ));
self.add_paint_cmd(PaintCmd::line_segment( self.add_paint_cmd(PaintCmd::line_segment(
(rect.left_center(), rect.right_center()), [rect.left_center(), rect.right_center()],
color, color,
width, width,
)); ));
self.add_paint_cmd(PaintCmd::line_segment( self.add_paint_cmd(PaintCmd::line_segment(
(rect.right_top(), rect.right_bottom()), [rect.right_top(), rect.right_bottom()],
color, color,
width, width,
)); ));
@ -387,17 +387,17 @@ impl Ui {
if too_high { if too_high {
self.add_paint_cmd(PaintCmd::line_segment( self.add_paint_cmd(PaintCmd::line_segment(
(rect.left_top(), rect.right_top()), [rect.left_top(), rect.right_top()],
color, color,
width, width,
)); ));
self.add_paint_cmd(PaintCmd::line_segment( self.add_paint_cmd(PaintCmd::line_segment(
(rect.center_top(), rect.center_bottom()), [rect.center_top(), rect.center_bottom()],
color, color,
width, width,
)); ));
self.add_paint_cmd(PaintCmd::line_segment( self.add_paint_cmd(PaintCmd::line_segment(
(rect.left_bottom(), rect.right_bottom()), [rect.left_bottom(), rect.right_bottom()],
color, color,
width, width,
)); ));
@ -594,11 +594,11 @@ impl Ui {
let line_start = child_rect.min - indent * 0.5; let line_start = child_rect.min - indent * 0.5;
let line_start = line_start.round(); // TODO: round to pixel instead let line_start = line_start.round(); // TODO: round to pixel instead
let line_end = pos2(line_start.x, line_start.y + size.y - 8.0); let line_end = pos2(line_start.x, line_start.y + size.y - 8.0);
self.add_paint_cmd(PaintCmd::Line { self.add_paint_cmd(PaintCmd::line_segment(
points: vec![line_start, line_end], [line_start, line_end],
color: gray(150, 255), gray(150, 255),
width: self.style.line_width, self.style.line_width,
}); ));
self.reserve_space(indent + size, None) self.reserve_space(indent + size, None)
} }

View file

@ -160,11 +160,11 @@ impl Widget for Hyperlink {
let y = ui.round_to_pixel(y); let y = ui.round_to_pixel(y);
let min_x = pos.x + fragment.min_x(); let min_x = pos.x + fragment.min_x();
let max_x = pos.x + fragment.max_x(); let max_x = pos.x + fragment.max_x();
ui.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::line_segment(
points: vec![pos2(min_x, y), pos2(max_x, y)], [pos2(min_x, y), pos2(max_x, y)],
color, color,
width: ui.style().line_width, ui.style().line_width,
}); ));
} }
} }
@ -284,7 +284,7 @@ impl<'a> Widget for Checkbox<'a> {
let stroke_color = ui.style().interact(&interact).stroke_color; let stroke_color = ui.style().interact(&interact).stroke_color;
if *self.checked { if *self.checked {
ui.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::LinePath {
points: vec![ points: vec![
pos2(small_icon_rect.left(), small_icon_rect.center().y), pos2(small_icon_rect.left(), small_icon_rect.center().y),
pos2(small_icon_rect.center().x, small_icon_rect.bottom()), pos2(small_icon_rect.center().x, small_icon_rect.bottom()),
@ -422,7 +422,7 @@ impl Widget for Separator {
Direction::Horizontal => { Direction::Horizontal => {
let interact = ui.reserve_space(vec2(self.min_spacing, available_space.y), None); let interact = ui.reserve_space(vec2(self.min_spacing, available_space.y), None);
( (
vec![ [
pos2(interact.rect.center().x, interact.rect.top() - extra), pos2(interact.rect.center().x, interact.rect.top() - extra),
pos2(interact.rect.center().x, interact.rect.bottom() + extra), pos2(interact.rect.center().x, interact.rect.bottom() + extra),
], ],
@ -432,7 +432,7 @@ impl Widget for Separator {
Direction::Vertical => { Direction::Vertical => {
let interact = ui.reserve_space(vec2(available_space.x, self.min_spacing), None); let interact = ui.reserve_space(vec2(available_space.x, self.min_spacing), None);
( (
vec![ [
pos2(interact.rect.left() - extra, interact.rect.center().y), pos2(interact.rect.left() - extra, interact.rect.center().y),
pos2(interact.rect.right() + extra, interact.rect.center().y), pos2(interact.rect.right() + extra, interact.rect.center().y),
], ],
@ -440,7 +440,7 @@ impl Widget for Separator {
) )
} }
}; };
ui.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::LineSegment {
points, points,
color: self.color, color: self.color,
width: self.line_width, width: self.line_width,

View file

@ -96,7 +96,7 @@ impl<'t> Widget for TextEdit<'t> {
interact.rect.min interact.rect.min
}; };
ui.add_paint_cmd(PaintCmd::line_segment( ui.add_paint_cmd(PaintCmd::line_segment(
(cursor_pos, cursor_pos + vec2(0.0, line_spacing)), [cursor_pos, cursor_pos + vec2(0.0, line_spacing)],
color::WHITE, color::WHITE,
ui.style().text_cursor_width, ui.style().text_cursor_width,
)); ));