Rename PaintCmd to Shape

This commit is contained in:
Emil Ernerfeldt 2021-01-10 11:43:01 +01:00
parent a0b0f36d29
commit fb2317c993
33 changed files with 225 additions and 235 deletions

View file

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Center window titles.
* Tweak size and alignment of some emojis to match other text.
* Rename `PaintCmd` to `Shape`.
* Rename feature "serde" to "persistence".
### Fixed 🐛
@ -163,7 +164,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Refactored the interface for `egui::app::App`.
* Windows are now constrained to the screen.
* `Context::begin_frame()` no longer returns a `Ui`. Instead put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
* `Context::end_frame()` now returns paint commands that need to be converted to triangles with `Context::tessellate()`.
* `Context::end_frame()` now returns shapes that need to be converted to triangles with `Context::tessellate()`.
* Anti-aliasing is now off by default in debug builds.
### Removed 🔥

View file

@ -173,8 +173,8 @@ loop {
let raw_input: egui::RawInput = my_integration.gather_input();
egui_ctx.begin_frame(raw_input);
my_app.ui(&mut egui_ctx); // add panels, windows and widgets to `egui_ctx` here
let (output, paint_commands) = egui_ctx.end_frame();
let paint_jobs = egui_ctx.tessellate(paint_commands); // create triangles to paint
let (output, shapes) = egui_ctx.end_frame();
let paint_jobs = egui_ctx.tessellate(shapes); // create triangles to paint
my_integration.paint(paint_jobs);
my_integration.set_cursor_icon(output.cursor_icon);
// Also see `egui::Output` for more

View file

@ -1,7 +1,7 @@
use std::hash::Hash;
use crate::{
paint::{PaintCmd, TextStyle},
paint::{Shape, TextStyle},
widgets::Label,
*,
};
@ -119,7 +119,7 @@ pub(crate) fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) {
*p = rect.center() + rotation * (*p - rect.center());
}
ui.painter().add(PaintCmd::closed_line(points, stroke));
ui.painter().add(Shape::closed_line(points, stroke));
}
/// A header which can be collapsed/expanded, revealing a contained [`Ui`] region.
@ -203,7 +203,7 @@ impl CollapsingHeader {
state.toggle(ui);
}
let bg_index = ui.painter().add(PaintCmd::Noop);
let bg_index = ui.painter().add(Shape::Noop);
{
let (mut icon_rect, _) = ui.style().spacing.icon_rectangles(header_response.rect);
@ -229,7 +229,7 @@ impl CollapsingHeader {
painter.set(
bg_index,
PaintCmd::Rect {
Shape::Rect {
rect: header_response.rect,
corner_radius: ui.style().interact(&header_response).corner_radius,
fill: ui.style().interact(&header_response).bg_fill,

View file

@ -1,4 +1,4 @@
use crate::{paint::PaintCmd, style::WidgetVisuals, *};
use crate::{paint::Shape, style::WidgetVisuals, *};
/// A drop-down selection menu with a descriptive label.
///
@ -119,7 +119,7 @@ fn button_frame(
let margin = ui.style().spacing.button_padding;
let outer_rect_bounds = ui.available_rect_before_wrap();
let inner_rect = outer_rect_bounds.shrink2(margin);
let where_to_put_background = ui.painter().add(PaintCmd::Noop);
let where_to_put_background = ui.painter().add(Shape::Noop);
let mut content_ui = ui.child_ui(inner_rect, *ui.layout());
add_contents(&mut content_ui);
@ -131,7 +131,7 @@ fn button_frame(
ui.painter().set(
where_to_put_background,
PaintCmd::Rect {
Shape::Rect {
rect: outer_rect,
corner_radius: visuals.corner_radius,
fill: visuals.bg_fill,
@ -149,7 +149,7 @@ fn paint_icon(painter: &Painter, rect: Rect, visuals: &WidgetVisuals) {
rect.center(),
vec2(rect.width() * 0.7, rect.height() * 0.45),
);
painter.add(PaintCmd::closed_line(
painter.add(Shape::closed_line(
vec![rect.left_top(), rect.right_top(), rect.center_bottom()],
visuals.fg_stroke,
));

View file

@ -1,6 +1,6 @@
//! Frame container
use crate::{layers::PaintCmdIdx, paint::*, *};
use crate::{layers::ShapeIdx, paint::*, *};
/// Adds a rectangular frame and background to some [`Ui`].
#[derive(Clone, Copy, Debug, Default, PartialEq)]
@ -95,13 +95,13 @@ impl Frame {
pub struct Prepared {
pub frame: Frame,
outer_rect_bounds: Rect,
where_to_put_background: PaintCmdIdx,
where_to_put_background: ShapeIdx,
pub content_ui: Ui,
}
impl Frame {
pub fn begin(self, ui: &mut Ui) -> Prepared {
let where_to_put_background = ui.painter().add(PaintCmd::Noop);
let where_to_put_background = ui.painter().add(Shape::Noop);
let outer_rect_bounds = ui.available_rect_before_wrap();
let inner_rect = outer_rect_bounds.shrink2(self.margin);
let content_ui = ui.child_ui(inner_rect, *ui.layout());
@ -141,7 +141,7 @@ impl Prepared {
..
} = self;
let frame_cmd = PaintCmd::Rect {
let frame_shape = Shape::Rect {
rect: outer_rect,
corner_radius: frame.corner_radius,
fill: frame.fill,
@ -149,13 +149,13 @@ impl Prepared {
};
if frame.shadow == Default::default() {
ui.painter().set(where_to_put_background, frame_cmd);
ui.painter().set(where_to_put_background, frame_shape);
} else {
let shadow = frame.shadow.tessellate(outer_rect, frame.corner_radius);
let shadow = PaintCmd::Triangles(shadow);
let shadow = Shape::Triangles(shadow);
ui.painter().set(
where_to_put_background,
PaintCmd::Vec(vec![shadow, frame_cmd]),
Shape::Vec(vec![shadow, frame_shape]),
)
};

View file

@ -284,7 +284,7 @@ impl Resize {
if self.with_stroke && corner_response.is_some() {
let rect = Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size);
let rect = rect.expand(2.0); // breathing room for content
ui.painter().add(paint::PaintCmd::Rect {
ui.painter().add(paint::Shape::Rect {
rect,
corner_radius: 3.0,
fill: Default::default(),

View file

@ -325,7 +325,7 @@ impl Prepared {
let visuals = ui.style().interact(&response);
ui.painter().add(paint::PaintCmd::Rect {
ui.painter().add(paint::Shape::Rect {
rect: outer_scroll_rect,
corner_radius,
fill: ui.style().visuals.dark_bg_color,
@ -334,7 +334,7 @@ impl Prepared {
// stroke: visuals.bg_stroke,
});
ui.painter().add(paint::PaintCmd::Rect {
ui.painter().add(paint::Shape::Rect {
rect: handle_rect.expand(-2.0),
corner_radius,
fill: visuals.fg_fill,

View file

@ -623,7 +623,7 @@ fn paint_frame_interaction(
points.push(pos2(max.x, min.y + cr));
points.push(pos2(max.x, max.y - cr));
}
ui.painter().add(PaintCmd::line(points, visuals.bg_stroke));
ui.painter().add(Shape::line(points, visuals.bg_stroke));
}
// ----------------------------------------------------------------------------

View file

@ -592,10 +592,10 @@ impl Context {
/// Call at the end of each frame.
/// Returns what has happened this frame (`Output`) as well as what you need to paint.
/// You can transform the returned paint commands into triangles with a call to
/// You can transform the returned shapes into triangles with a call to
/// `Context::tessellate`.
#[must_use]
pub fn end_frame(&self) -> (Output, Vec<(Rect, PaintCmd)>) {
pub fn end_frame(&self) -> (Output, Vec<(Rect, Shape)>) {
if self.input.wants_repaint() {
self.request_repaint();
}
@ -608,25 +608,21 @@ impl Context {
output.needs_repaint = true;
}
let paint_commands = self.drain_paint_lists();
(output, paint_commands)
let shapes = self.drain_paint_lists();
(output, shapes)
}
fn drain_paint_lists(&self) -> Vec<(Rect, PaintCmd)> {
fn drain_paint_lists(&self) -> Vec<(Rect, Shape)> {
let memory = self.memory();
self.graphics().drain(memory.areas.order()).collect()
}
/// Tessellate the given paint commands into triangle meshes.
pub fn tessellate(&self, paint_commands: Vec<(Rect, PaintCmd)>) -> PaintJobs {
/// Tessellate the given shapes into triangle meshes.
pub fn tessellate(&self, shapes: Vec<(Rect, Shape)>) -> PaintJobs {
let mut tessellation_options = self.memory().options.tessellation_options;
tessellation_options.aa_size = 1.0 / self.pixels_per_point();
let paint_stats = PaintStats::from_paint_commands(&paint_commands); // TODO: internal allocations
let paint_jobs = tessellator::tessellate_paint_commands(
paint_commands,
tessellation_options,
self.fonts(),
);
let paint_stats = PaintStats::from_shapes(&shapes); // TODO: internal allocations
let paint_jobs = tessellator::tessellate_shapes(shapes, tessellation_options, self.fonts());
*self.paint_stats.lock() = paint_stats.with_paint_jobs(&paint_jobs);
paint_jobs
}

View file

@ -1,6 +1,6 @@
//! uis for egui types.
use crate::{
paint::{self, PaintCmd, Texture, Triangles},
paint::{self, Shape, Texture, Triangles},
*,
};
@ -25,7 +25,7 @@ impl Texture {
[pos2(0.0, 0.0), pos2(1.0, 1.0)].into(),
Color32::WHITE,
);
ui.painter().add(PaintCmd::triangles(triangles));
ui.painter().add(Shape::triangles(triangles));
let (tex_w, tex_h) = (self.width as f32, self.height as f32);
@ -49,7 +49,7 @@ impl Texture {
);
let mut triangles = Triangles::default();
triangles.add_rect_with_uv(zoom_rect, uv_rect, Color32::WHITE);
ui.painter().add(PaintCmd::triangles(triangles));
ui.painter().add(Shape::triangles(triangles));
});
}
}

View file

@ -1,6 +1,6 @@
use ahash::AHashMap;
use crate::{math::Rect, paint::PaintCmd, Id, *};
use crate::{math::Rect, paint::Shape, Id, *};
/// Different layer categories
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
@ -70,47 +70,48 @@ impl LayerId {
}
}
/// A unique identifier of a specific [`PaintCmd`] in a [`PaintList`].
/// A unique identifier of a specific [`Shape`] in a [`PaintList`].
#[derive(Clone, Copy, PartialEq)]
pub struct PaintCmdIdx(usize);
pub struct ShapeIdx(usize);
/// A list of [`PaintCmd`]s paired with a clip rectangle.
/// A list of [`Shape`]s paired with a clip rectangle.
#[derive(Clone, Default)]
pub struct PaintList(Vec<(Rect, PaintCmd)>);
pub struct PaintList(Vec<(Rect, Shape)>);
impl PaintList {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Returns the index of the new command that can be used with `PaintList::set`.
pub fn add(&mut self, clip_rect: Rect, cmd: PaintCmd) -> PaintCmdIdx {
let idx = PaintCmdIdx(self.0.len());
self.0.push((clip_rect, cmd));
/// Returns the index of the new [`Shape`] that can be used with `PaintList::set`.
pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {
let idx = ShapeIdx(self.0.len());
self.0.push((clip_rect, shape));
idx
}
pub fn extend(&mut self, clip_rect: Rect, mut cmds: Vec<PaintCmd>) {
self.0.extend(cmds.drain(..).map(|cmd| (clip_rect, cmd)))
pub fn extend(&mut self, clip_rect: Rect, mut shapes: Vec<Shape>) {
self.0
.extend(shapes.drain(..).map(|shape| (clip_rect, shape)))
}
/// Modify an existing command.
/// Modify an existing [`Shape`].
///
/// Sometimes you want to paint a frame behind some contents, but don't know how large the frame needs to be
/// until the contents have been added, and therefor also painted to the `PaintList`.
///
/// The solution is to allocate a `PaintCmd` using `let idx = paint_list.add(cr, PaintCmd::Noop);`
/// The solution is to allocate a `Shape` using `let idx = paint_list.add(cr, Shape::Noop);`
/// and then later setting it using `paint_list.set(idx, cr, frame);`.
pub fn set(&mut self, idx: PaintCmdIdx, clip_rect: Rect, cmd: PaintCmd) {
pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {
assert!(idx.0 < self.0.len());
self.0[idx.0] = (clip_rect, cmd);
self.0[idx.0] = (clip_rect, shape);
}
/// Translate each paint-command and clip rectangle by this much, in-place
/// Translate each [`Shape`] and clip rectangle by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
for (clip_rect, cmd) in &mut self.0 {
for (clip_rect, shape) in &mut self.0 {
*clip_rect = clip_rect.translate(delta);
cmd.translate(delta);
shape.translate(delta);
}
}
}
@ -128,8 +129,8 @@ impl GraphicLayers {
pub fn drain(
&mut self,
area_order: &[LayerId],
) -> impl ExactSizeIterator<Item = (Rect, PaintCmd)> {
let mut all_commands: Vec<_> = Default::default();
) -> impl ExactSizeIterator<Item = (Rect, Shape)> {
let mut all_shapes: Vec<_> = Default::default();
for &order in &Order::ALL {
let order_map = &mut self.0[order as usize];
@ -142,18 +143,18 @@ impl GraphicLayers {
// First do the layers part of area_order:
for layer_id in area_order {
if layer_id.order == order {
if let Some(commands) = order_map.get_mut(&layer_id.id) {
all_commands.extend(commands.0.drain(..));
if let Some(shapes) = order_map.get_mut(&layer_id.id) {
all_shapes.extend(shapes.0.drain(..));
}
}
}
// Also draw areas that are missing in `area_order`:
for commands in order_map.values_mut() {
all_commands.extend(commands.0.drain(..));
for shapes in order_map.values_mut() {
all_shapes.extend(shapes.0.drain(..));
}
}
all_commands.into_iter()
all_shapes.into_iter()
}
}

View file

@ -26,8 +26,8 @@
//! let raw_input: egui::RawInput = my_integration.gather_input();
//! egui_ctx.begin_frame(raw_input);
//! my_app.ui(&egui_ctx); // add panels, windows and widgets to `egui_ctx` here
//! let (output, paint_commands) = egui_ctx.end_frame();
//! let paint_jobs = egui_ctx.tessellate(paint_commands); // create triangles to paint
//! let (output, shapes) = egui_ctx.end_frame();
//! let paint_jobs = egui_ctx.tessellate(shapes); // create triangles to paint
//! my_integration.paint(paint_jobs);
//! my_integration.set_cursor_icon(output.cursor_icon);
//! // Also see `egui::Output` for more
@ -108,7 +108,7 @@ pub use {
math::{clamp, lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rect, Vec2},
memory::Memory,
paint::{
color, Color32, FontDefinitions, FontFamily, PaintCmd, PaintJobs, Rgba, Stroke, TextStyle,
color, Color32, FontDefinitions, FontFamily, PaintJobs, Rgba, Shape, Stroke, TextStyle,
Texture, TextureId,
},
painter::Painter,

View file

@ -1,21 +1,21 @@
//! 2D graphics/rendering. Fonts, textures, color, geometry, tessellation etc.
pub mod color;
pub mod command;
pub mod font;
pub mod fonts;
mod galley;
mod shadow;
pub mod shape;
pub mod stats;
pub mod tessellator;
mod texture_atlas;
pub use {
color::{Color32, Rgba},
command::{PaintCmd, Stroke},
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
galley::*,
shadow::Shadow,
shape::{Shape, Stroke},
stats::PaintStats,
tessellator::{
PaintJob, PaintJobs, TessellationOptions, TextureId, Triangles, Vertex, WHITE_UV,

View file

@ -6,12 +6,12 @@ use {
/// A paint primitive such as a circle or a piece of text.
/// Coordinates are all screen space points (not physical pixels).
#[derive(Clone, Debug)]
pub enum PaintCmd {
pub enum Shape {
/// Paint nothing. This can be useful as a placeholder.
Noop,
/// Recursively nest more paint commands - sometimes a convenience to be able to do.
/// Recursively nest more shapes - sometimes a convenience to be able to do.
/// For performance reasons it is better to avoid it.
Vec(Vec<PaintCmd>),
Vec(Vec<Shape>),
Circle {
center: Pos2,
radius: f32,
@ -49,7 +49,7 @@ pub enum PaintCmd {
}
/// ## Constructors
impl PaintCmd {
impl Shape {
pub fn line_segment(points: [Pos2; 2], stroke: impl Into<Stroke>) -> Self {
Self::LineSegment {
points,
@ -141,14 +141,14 @@ impl PaintCmd {
}
/// ## Operations
impl PaintCmd {
impl Shape {
pub fn triangles(triangles: Triangles) -> Self {
debug_assert!(triangles.is_valid());
Self::Triangles(triangles)
}
pub fn texture_id(&self) -> super::TextureId {
if let PaintCmd::Triangles(triangles) = self {
if let Shape::Triangles(triangles) = self {
triangles.texture_id
} else {
super::TextureId::Egui
@ -158,32 +158,32 @@ impl PaintCmd {
/// Translate location by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
match self {
PaintCmd::Noop => {}
PaintCmd::Vec(commands) => {
for command in commands {
command.translate(delta);
Shape::Noop => {}
Shape::Vec(shapes) => {
for shape in shapes {
shape.translate(delta);
}
}
PaintCmd::Circle { center, .. } => {
Shape::Circle { center, .. } => {
*center += delta;
}
PaintCmd::LineSegment { points, .. } => {
Shape::LineSegment { points, .. } => {
for p in points {
*p += delta;
}
}
PaintCmd::Path { points, .. } => {
Shape::Path { points, .. } => {
for p in points {
*p += delta;
}
}
PaintCmd::Rect { rect, .. } => {
Shape::Rect { rect, .. } => {
*rect = rect.translate(delta);
}
PaintCmd::Text { pos, .. } => {
Shape::Text { pos, .. } => {
*pos += delta;
}
PaintCmd::Triangles(triangles) => {
Shape::Triangles(triangles) => {
triangles.translate(delta);
}
}

View file

@ -54,16 +54,16 @@ impl std::ops::AddAssign for AllocInfo {
}
impl AllocInfo {
// pub fn from_paint_cmd(cmd: &PaintCmd) -> Self {
// match cmd {
// PaintCmd::Noop
// PaintCmd::Vec(commands) => Self::from_paint_commands(commands)
// | PaintCmd::Circle { .. }
// | PaintCmd::LineSegment { .. }
// | PaintCmd::Rect { .. } => Self::default(),
// PaintCmd::Path { points, .. } => Self::from_slice(points),
// PaintCmd::Text { galley, .. } => Self::from_galley(galley),
// PaintCmd::Triangles(triangles) => Self::from_triangles(triangles),
// pub fn from_shape(shape: &Shape) -> Self {
// match shape {
// Shape::Noop
// Shape::Vec(shapes) => Self::from_shapes(shapes)
// | Shape::Circle { .. }
// | Shape::LineSegment { .. }
// | Shape::Rect { .. } => Self::default(),
// Shape::Path { points, .. } => Self::from_slice(points),
// Shape::Text { galley, .. } => Self::from_galley(galley),
// Shape::Triangles(triangles) => Self::from_triangles(triangles),
// }
// }
@ -137,11 +137,11 @@ impl AllocInfo {
#[derive(Clone, Copy, Default)]
pub struct PaintStats {
primitives: AllocInfo,
cmd_text: AllocInfo,
cmd_path: AllocInfo,
cmd_mesh: AllocInfo,
cmd_vec: AllocInfo,
shapes: AllocInfo,
shape_text: AllocInfo,
shape_path: AllocInfo,
shape_mesh: AllocInfo,
shape_vec: AllocInfo,
/// Number of separate clip rectangles
jobs: AllocInfo,
@ -150,40 +150,39 @@ pub struct PaintStats {
}
impl PaintStats {
pub fn from_paint_commands(paint_commands: &[(Rect, PaintCmd)]) -> Self {
pub fn from_shapes(shapes: &[(Rect, Shape)]) -> Self {
let mut stats = Self::default();
stats.cmd_path.element_size = ElementSize::Heterogenous; // nicer display later
stats.cmd_vec.element_size = ElementSize::Heterogenous; // nicer display later
stats.shape_path.element_size = ElementSize::Heterogenous; // nicer display later
stats.shape_vec.element_size = ElementSize::Heterogenous; // nicer display later
stats.primitives = AllocInfo::from_slice(paint_commands);
for (_, cmd) in paint_commands {
stats.add(cmd);
stats.shapes = AllocInfo::from_slice(shapes);
for (_, shape) in shapes {
stats.add(shape);
}
stats
}
fn add(&mut self, cmd: &PaintCmd) {
match cmd {
PaintCmd::Vec(paint_commands) => {
// self += PaintStats::from_paint_commands(&paint_commands); // TODO
self.primitives += AllocInfo::from_slice(paint_commands);
self.cmd_vec += AllocInfo::from_slice(paint_commands);
for cmd in paint_commands {
self.add(cmd);
fn add(&mut self, shape: &Shape) {
match shape {
Shape::Vec(shapes) => {
// self += PaintStats::from_shapes(&shapes); // TODO
self.shapes += AllocInfo::from_slice(shapes);
self.shape_vec += AllocInfo::from_slice(shapes);
for shape in shapes {
self.add(shape);
}
}
PaintCmd::Noop
| PaintCmd::Circle { .. }
| PaintCmd::LineSegment { .. }
| PaintCmd::Rect { .. } => Default::default(),
PaintCmd::Path { points, .. } => {
self.cmd_path += AllocInfo::from_slice(points);
Shape::Noop | Shape::Circle { .. } | Shape::LineSegment { .. } | Shape::Rect { .. } => {
Default::default()
}
PaintCmd::Text { galley, .. } => {
self.cmd_text += AllocInfo::from_galley(galley);
Shape::Path { points, .. } => {
self.shape_path += AllocInfo::from_slice(points);
}
PaintCmd::Triangles(triangles) => {
self.cmd_mesh += AllocInfo::from_triangles(triangles);
Shape::Text { galley, .. } => {
self.shape_text += AllocInfo::from_galley(galley);
}
Shape::Triangles(triangles) => {
self.shape_mesh += AllocInfo::from_triangles(triangles);
}
}
}
@ -198,10 +197,10 @@ impl PaintStats {
}
// pub fn total(&self) -> AllocInfo {
// self.primitives
// + self.cmd_text
// + self.cmd_path
// + self.cmd_mesh
// self.shapes
// + self.shape_text
// + self.shape_path
// + self.shape_mesh
// + self.jobs
// + self.vertices
// + self.indices
@ -211,7 +210,7 @@ impl PaintStats {
impl PaintStats {
pub fn ui(&self, ui: &mut crate::Ui) {
ui.label(
"Egui generates intermediate level primitives like circles and text. \
"Egui generates intermediate level shapes like circles and text. \
These are later tessellated into triangles.",
);
ui.advance_cursor(10.0);
@ -219,24 +218,24 @@ impl PaintStats {
ui.style_mut().body_text_style = TextStyle::Monospace;
let Self {
primitives,
cmd_text,
cmd_path,
cmd_mesh,
cmd_vec,
shapes,
shape_text,
shape_path,
shape_mesh,
shape_vec,
jobs,
vertices,
indices,
} = self;
ui.label("Intermediate:");
primitives
.label(ui, "primitives")
shapes
.label(ui, "shapes")
.on_hover_text("Boxes, circles, etc");
cmd_text.label(ui, "text");
cmd_path.label(ui, "paths");
cmd_mesh.label(ui, "meshes");
cmd_vec.label(ui, "nested");
shape_text.label(ui, "text");
shape_path.label(ui, "paths");
shape_mesh.label(ui, "meshes");
shape_vec.label(ui, "nested");
ui.advance_cursor(10.0);
ui.label("Tessellated:");

View file

@ -1,6 +1,6 @@
//! Converts graphics primitives into textured triangles.
//!
//! This module converts lines, circles, text and more represented by [`PaintCmd`]
//! This module converts lines, circles, text and more represented by [`Shape`]
//! into textured triangles represented by [`Triangles`].
#![allow(clippy::identity_op)]
@ -687,31 +687,26 @@ impl Tessellator {
}
}
/// Tessellate a single [`PaintCmd`] into a [`Triangles`].
/// Tessellate a single [`Shape`] into a [`Triangles`].
///
/// * `command`: the command to tessellate
/// * `shape`: the shape to tessellate
/// * `options`: tessellation quality
/// * `fonts`: font source when tessellating text
/// * `out`: where the triangles are put
/// * `scratchpad_path`: if you plan to run `tessellate_paint_command`
/// * `scratchpad_path`: if you plan to run `tessellate_shape`
/// many times, pass it a reference to the same `Path` to avoid excessive allocations.
pub fn tessellate_paint_command(
&mut self,
fonts: &Fonts,
command: PaintCmd,
out: &mut Triangles,
) {
pub fn tessellate_shape(&mut self, fonts: &Fonts, shape: Shape, out: &mut Triangles) {
let clip_rect = self.clip_rect;
let options = self.options;
match command {
PaintCmd::Noop => {}
PaintCmd::Vec(vec) => {
for command in vec {
self.tessellate_paint_command(fonts, command, out)
match shape {
Shape::Noop => {}
Shape::Vec(vec) => {
for shape in vec {
self.tessellate_shape(fonts, shape, out)
}
}
PaintCmd::Circle {
Shape::Circle {
center,
radius,
fill,
@ -733,20 +728,20 @@ impl Tessellator {
fill_closed_path(&path.0, fill, options, out);
stroke_path(&path.0, Closed, stroke, options, out);
}
PaintCmd::Triangles(triangles) => {
Shape::Triangles(triangles) => {
if triangles.is_valid() {
out.append(triangles);
} else {
debug_assert!(false, "Invalid Triangles in PaintCmd::Triangles");
debug_assert!(false, "Invalid Triangles in Shape::Triangles");
}
}
PaintCmd::LineSegment { points, stroke } => {
Shape::LineSegment { points, stroke } => {
let path = &mut self.scratchpad_path;
path.clear();
path.add_line_segment(points);
stroke_path(&path.0, Open, stroke, options, out);
}
PaintCmd::Path {
Shape::Path {
points,
closed,
fill,
@ -772,7 +767,7 @@ impl Tessellator {
stroke_path(&path.0, typ, stroke, options, out);
}
}
PaintCmd::Rect {
Shape::Rect {
rect,
corner_radius,
fill,
@ -786,7 +781,7 @@ impl Tessellator {
};
self.tessellate_rect(&rect, out);
}
PaintCmd::Text {
Shape::Text {
pos,
galley,
text_style,
@ -872,7 +867,7 @@ impl Tessellator {
let c = chars.next().unwrap();
if self.options.coarse_tessellation_culling && !is_line_visible {
// culling individual lines of text is important, since a single `PaintCmd::Text`
// culling individual lines of text is important, since a single `Shape::Text`
// can span hundreds of lines.
continue;
}
@ -899,29 +894,29 @@ impl Tessellator {
}
}
/// Turns [`PaintCmd`]:s into sets of triangles.
/// Turns [`Shape`]:s into sets of triangles.
///
/// The given commands will be painted back-to-front (painters algorithm).
/// The given shapes will be painted back-to-front (painters algorithm).
/// They will be batched together by clip rectangle.
///
/// * `commands`: the command to tessellate
/// * `shapes`: the shape to tessellate
/// * `options`: tessellation quality
/// * `fonts`: font source when tessellating text
///
/// ## Returns
/// A list of clip rectangles with matching [`Triangles`].
pub fn tessellate_paint_commands(
commands: Vec<(Rect, PaintCmd)>,
pub fn tessellate_shapes(
shapes: Vec<(Rect, Shape)>,
options: TessellationOptions,
fonts: &Fonts,
) -> Vec<(Rect, Triangles)> {
let mut tessellator = Tessellator::from_options(options);
let mut jobs = PaintJobs::default();
for (clip_rect, cmd) in commands {
for (clip_rect, shape) in shapes {
let start_new_job = match jobs.last() {
None => true,
Some(job) => job.0 != clip_rect || job.1.texture_id != cmd.texture_id(),
Some(job) => job.0 != clip_rect || job.1.texture_id != shape.texture_id(),
};
if start_new_job {
@ -930,15 +925,15 @@ pub fn tessellate_paint_commands(
let out = &mut jobs.last_mut().unwrap().1;
tessellator.clip_rect = clip_rect;
tessellator.tessellate_paint_command(fonts, cmd, out);
tessellator.tessellate_shape(fonts, shape, out);
}
if options.debug_paint_clip_rects {
for (clip_rect, triangles) in &mut jobs {
tessellator.clip_rect = Rect::everything();
tessellator.tessellate_paint_command(
tessellator.tessellate_shape(
fonts,
PaintCmd::Rect {
Shape::Rect {
rect: *clip_rect,
corner_radius: 0.0,
fill: Default::default(),

View file

@ -1,7 +1,7 @@
use crate::{
layers::PaintCmdIdx,
layers::ShapeIdx,
math::{Align2, Pos2, Rect, Vec2},
paint::{Fonts, Galley, PaintCmd, Stroke, TextStyle},
paint::{Fonts, Galley, Shape, Stroke, TextStyle},
Color32, CtxRef, LayerId,
};
@ -10,7 +10,7 @@ use crate::{
/// All coordinates are screen coordinates in the unit points (one point can consist of many physical pixels).
#[derive(Clone)]
pub struct Painter {
/// Source of fonts and destination of paint commands
/// Source of fonts and destination of shapes
ctx: CtxRef,
/// Where we paint
@ -106,26 +106,26 @@ impl Painter {
/// It is up to the caller to make sure there is room for this.
/// Can be used for free painting.
/// NOTE: all coordinates are screen coordinates!
pub fn add(&self, paint_cmd: PaintCmd) -> PaintCmdIdx {
pub fn add(&self, shape: Shape) -> ShapeIdx {
self.ctx
.graphics()
.list(self.layer_id)
.add(self.clip_rect, paint_cmd)
.add(self.clip_rect, shape)
}
pub fn extend(&self, cmds: Vec<PaintCmd>) {
pub fn extend(&self, shapes: Vec<Shape>) {
self.ctx
.graphics()
.list(self.layer_id)
.extend(self.clip_rect, cmds);
.extend(self.clip_rect, shapes);
}
/// Modify an existing command.
pub fn set(&self, idx: PaintCmdIdx, cmd: PaintCmd) {
/// Modify an existing [`Shape`].
pub fn set(&self, idx: ShapeIdx, shape: Shape) {
self.ctx
.graphics()
.list(self.layer_id)
.set(idx, self.clip_rect, cmd)
.set(idx, self.clip_rect, shape)
}
}
@ -143,7 +143,7 @@ impl Painter {
let galley = font.layout_multiline(format!("🔥 {}", text), f32::INFINITY);
let rect = Align2::LEFT_TOP.anchor_rect(Rect::from_min_size(pos, galley.size));
let frame_rect = rect.expand(2.0);
self.add(PaintCmd::Rect {
self.add(Shape::Rect {
rect: frame_rect,
corner_radius: 0.0,
fill: Color32::from_black_alpha(240),
@ -157,7 +157,7 @@ impl Painter {
/// # Paint different primitives
impl Painter {
pub fn line_segment(&self, points: [Pos2; 2], stroke: impl Into<Stroke>) {
self.add(PaintCmd::LineSegment {
self.add(Shape::LineSegment {
points,
stroke: stroke.into(),
});
@ -170,7 +170,7 @@ impl Painter {
fill_color: impl Into<Color32>,
stroke: impl Into<Stroke>,
) {
self.add(PaintCmd::Circle {
self.add(Shape::Circle {
center,
radius,
fill: fill_color.into(),
@ -179,7 +179,7 @@ impl Painter {
}
pub fn circle_filled(&self, center: Pos2, radius: f32, fill_color: impl Into<Color32>) {
self.add(PaintCmd::Circle {
self.add(Shape::Circle {
center,
radius,
fill: fill_color.into(),
@ -188,7 +188,7 @@ impl Painter {
}
pub fn circle_stroke(&self, center: Pos2, radius: f32, stroke: impl Into<Stroke>) {
self.add(PaintCmd::Circle {
self.add(Shape::Circle {
center,
radius,
fill: Default::default(),
@ -203,7 +203,7 @@ impl Painter {
fill_color: impl Into<Color32>,
stroke: impl Into<Stroke>,
) {
self.add(PaintCmd::Rect {
self.add(Shape::Rect {
rect,
corner_radius,
fill: fill_color.into(),
@ -212,7 +212,7 @@ impl Painter {
}
pub fn rect_filled(&self, rect: Rect, corner_radius: f32, fill_color: impl Into<Color32>) {
self.add(PaintCmd::Rect {
self.add(Shape::Rect {
rect,
corner_radius,
fill: fill_color.into(),
@ -221,7 +221,7 @@ impl Painter {
}
pub fn rect_stroke(&self, rect: Rect, corner_radius: f32, stroke: impl Into<Stroke>) {
self.add(PaintCmd::Rect {
self.add(Shape::Rect {
rect,
corner_radius,
fill: Default::default(),
@ -266,7 +266,7 @@ impl Painter {
/// Paint text that has already been layed out in a `Galley`.
pub fn galley(&self, pos: Pos2, galley: Galley, text_style: TextStyle, color: Color32) {
self.add(PaintCmd::Text {
self.add(Shape::Text {
pos,
galley,
text_style,

View file

@ -923,7 +923,7 @@ impl Ui {
(ret, response)
}
/// Redirect paint commands to another paint layer.
/// Redirect shapes to another paint layer.
pub fn with_layer_id<R>(
&mut self,
layer_id: LayerId,

View file

@ -196,7 +196,7 @@ impl<'a> Widget for Checkbox<'a> {
rect.center().y - 0.5 * galley.size.y,
);
let (small_icon_rect, big_icon_rect) = ui.style().spacing.icon_rectangles(rect);
ui.painter().add(PaintCmd::Rect {
ui.painter().add(Shape::Rect {
rect: big_icon_rect,
corner_radius: visuals.corner_radius,
fill: visuals.bg_fill,
@ -205,7 +205,7 @@ impl<'a> Widget for Checkbox<'a> {
if *checked {
// Check mark:
ui.painter().add(PaintCmd::line(
ui.painter().add(Shape::line(
vec![
pos2(small_icon_rect.left(), small_icon_rect.center().y),
pos2(small_icon_rect.center().x, small_icon_rect.bottom()),
@ -289,7 +289,7 @@ impl Widget for RadioButton {
let painter = ui.painter();
painter.add(PaintCmd::Circle {
painter.add(Shape::Circle {
center: big_icon_rect.center(),
radius: big_icon_rect.width() / 2.0,
fill: visuals.bg_fill,
@ -297,7 +297,7 @@ impl Widget for RadioButton {
});
if checked {
painter.add(PaintCmd::Circle {
painter.add(Shape::Circle {
center: small_icon_rect.center(),
radius: small_icon_rect.width() / 3.0,
fill: visuals.fg_stroke.color, // Intentional to use stroke and not fill

View file

@ -37,7 +37,7 @@ fn background_checkers(painter: &Painter, rect: Rect) {
);
std::mem::swap(&mut top_color, &mut bottom_color);
}
painter.add(PaintCmd::triangles(triangles));
painter.add(Shape::triangles(triangles));
}
pub fn show_color(ui: &mut Ui, color: impl Into<Color32>, desired_size: Vec2) -> Response {
@ -47,7 +47,7 @@ pub fn show_color(ui: &mut Ui, color: impl Into<Color32>, desired_size: Vec2) ->
fn show_srgba(ui: &mut Ui, srgba: Color32, desired_size: Vec2) -> Response {
let (rect, response) = ui.allocate_at_least(desired_size, Sense::hover());
background_checkers(ui.painter(), rect);
ui.painter().add(PaintCmd::Rect {
ui.painter().add(Shape::Rect {
rect,
corner_radius: 2.0,
fill: srgba,
@ -61,7 +61,7 @@ fn color_button(ui: &mut Ui, color: Color32) -> Response {
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
let visuals = ui.style().interact(&response);
background_checkers(ui.painter(), rect);
ui.painter().add(PaintCmd::Rect {
ui.painter().add(Shape::Rect {
rect,
corner_radius: visuals.corner_radius.at_most(2.0),
fill: color,
@ -103,7 +103,7 @@ fn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color
triangles.add_triangle(2 * i + 1, 2 * i + 2, 2 * i + 3);
}
}
ui.painter().add(PaintCmd::triangles(triangles));
ui.painter().add(Shape::triangles(triangles));
}
ui.painter().rect_stroke(rect, 0.0, visuals.bg_stroke); // outline
@ -113,7 +113,7 @@ fn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color
let x = lerp(rect.left()..=rect.right(), *value);
let r = rect.height() / 4.0;
let picked_color = color_at(*value);
ui.painter().add(PaintCmd::polygon(
ui.painter().add(Shape::polygon(
vec![
pos2(x - r, rect.bottom()),
pos2(x + r, rect.bottom()),
@ -164,7 +164,7 @@ fn color_slider_2d(
}
}
}
ui.painter().add(PaintCmd::triangles(triangles)); // fill
ui.painter().add(Shape::triangles(triangles)); // fill
ui.painter().rect_stroke(rect, 0.0, visuals.bg_stroke); // outline
@ -172,7 +172,7 @@ fn color_slider_2d(
let x = lerp(rect.left()..=rect.right(), *x_value);
let y = lerp(rect.bottom()..=rect.top(), *y_value);
let picked_color = color_at(*x_value, *y_value);
ui.painter().add(PaintCmd::Circle {
ui.painter().add(Shape::Circle {
center: pos2(x, y),
radius: rect.width() / 12.0,
fill: picked_color,

View file

@ -59,14 +59,14 @@ impl Image {
if *bg_fill != Default::default() {
let mut triangles = Triangles::default();
triangles.add_colored_rect(rect, *bg_fill);
ui.painter().add(PaintCmd::triangles(triangles));
ui.painter().add(Shape::triangles(triangles));
}
{
// TODO: builder pattern for Triangles
let mut triangles = Triangles::with_texture(*texture_id);
triangles.add_rect_with_uv(rect, *uv, *tint);
ui.painter().add(PaintCmd::triangles(triangles));
ui.painter().add(Shape::triangles(triangles));
}
}
}

View file

@ -277,14 +277,14 @@ impl<'a> Slider<'a> {
);
let marker_center_x = self.x_from_value(value, x_range);
ui.painter().add(PaintCmd::Rect {
ui.painter().add(Shape::Rect {
rect: rail_rect,
corner_radius: rail_radius,
fill: ui.style().visuals.widgets.inactive.bg_fill,
stroke: ui.style().visuals.widgets.inactive.bg_stroke,
});
ui.painter().add(PaintCmd::Circle {
ui.painter().add(Shape::Circle {
center: pos2(marker_center_x, rail_rect.center().y),
radius: handle_radius(rect),
fill: ui.style().interact(response).fg_fill,

View file

@ -216,7 +216,7 @@ impl<'t> Widget for TextEdit<'t> {
let margin = Vec2::splat(2.0);
let frame_rect = ui.available_rect_before_wrap();
let content_rect = frame_rect.shrink2(margin);
let where_to_put_background = ui.painter().add(PaintCmd::Noop);
let where_to_put_background = ui.painter().add(Shape::Noop);
let mut content_ui = ui.child_ui(content_rect, *ui.layout());
let response = self.content_ui(&mut content_ui);
let frame_rect = Rect::from_min_max(frame_rect.min, content_ui.min_rect().max + margin);
@ -226,7 +226,7 @@ impl<'t> Widget for TextEdit<'t> {
let frame_rect = response.rect;
ui.painter().set(
where_to_put_background,
PaintCmd::Rect {
Shape::Rect {
rect: frame_rect,
corner_radius: visuals.corner_radius,
fill: ui.style().visuals.dark_bg_color,

View file

@ -36,11 +36,9 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let mut demo_windows = egui_demo_lib::DemoWindows::default();
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
let (_, paint_commands) = ctx.end_frame();
let (_, shapes) = ctx.end_frame();
c.bench_function("tessellate", |b| {
b.iter(|| ctx.tessellate(paint_commands.clone()))
});
c.bench_function("tessellate", |b| b.iter(|| ctx.tessellate(shapes.clone())));
}
{

View file

@ -300,7 +300,7 @@ fn vertex_gradient(ui: &mut Ui, bg_fill: Color32, gradient: &Gradient) -> Respon
if bg_fill != Default::default() {
let mut triangles = Triangles::default();
triangles.add_colored_rect(rect, bg_fill);
ui.painter().add(PaintCmd::triangles(triangles));
ui.painter().add(Shape::triangles(triangles));
}
{
let n = gradient.0.len();
@ -317,7 +317,7 @@ fn vertex_gradient(ui: &mut Ui, bg_fill: Color32, gradient: &Gradient) -> Respon
triangles.add_triangle(2 * i + 1, 2 * i + 2, 2 * i + 3);
}
}
ui.painter().add(PaintCmd::triangles(triangles));
ui.painter().add(Shape::triangles(triangles));
}
response
}

View file

@ -34,7 +34,7 @@ impl super::View for DancingStrings {
let desired_size = ui.available_width() * vec2(1.0, 0.35);
let (_id, rect) = ui.allocate_space(desired_size);
let mut cmds = vec![];
let mut shapes = vec![];
for &mode in &[2, 3, 5] {
let mode = mode as f32;
@ -55,13 +55,13 @@ impl super::View for DancingStrings {
.collect();
let thickness = 10.0 / mode;
cmds.push(paint::PaintCmd::line(
shapes.push(paint::Shape::line(
points,
Stroke::new(thickness, Color32::from_additive_luminance(196)),
));
}
ui.painter().extend(cmds);
ui.painter().extend(shapes);
});
ui.add(crate::__egui_github_link_file!());
}

View file

@ -43,7 +43,7 @@ pub fn drop_target<R>(
let outer_rect_bounds = ui.available_rect_before_wrap();
let inner_rect = outer_rect_bounds.shrink2(margin);
let where_to_put_background = ui.painter().add(PaintCmd::Noop);
let where_to_put_background = ui.painter().add(Shape::Noop);
let mut content_ui = ui.child_ui(inner_rect, *ui.layout());
let ret = body(&mut content_ui);
let outer_rect = Rect::from_min_max(outer_rect_bounds.min, content_ui.min_rect().max + margin);
@ -61,7 +61,7 @@ pub fn drop_target<R>(
ui.painter().set(
where_to_put_background,
PaintCmd::Rect {
Shape::Rect {
corner_radius: style.corner_radius,
fill: style.bg_fill,
stroke: style.bg_stroke,

View file

@ -52,7 +52,7 @@ impl Painting {
for line in &self.lines {
if line.len() >= 2 {
let points: Vec<Pos2> = line.iter().map(|p| rect.min + *p).collect();
painter.add(PaintCmd::line(points, self.stroke));
painter.add(Shape::line(points, self.stroke));
}
}
}

View file

@ -69,7 +69,7 @@ impl FrameHistory {
let (rect, response) = ui.allocate_at_least(size, Sense::hover());
let style = ui.style().noninteractive();
let mut cmds = vec![PaintCmd::Rect {
let mut shapes = vec![Shape::Rect {
rect,
corner_radius: style.corner_radius,
fill: ui.style().visuals.dark_bg_color,
@ -82,13 +82,13 @@ impl FrameHistory {
if let Some(mouse_pos) = ui.input().mouse.pos {
if rect.contains(mouse_pos) {
let y = mouse_pos.y;
cmds.push(PaintCmd::line_segment(
shapes.push(Shape::line_segment(
[pos2(rect.left(), y), pos2(rect.right(), y)],
line_stroke,
));
let cpu_usage = remap(y, rect.bottom_up_range(), 0.0..=graph_top_cpu_usage);
let text = format!("{:.1} ms", 1e3 * cpu_usage);
cmds.push(PaintCmd::text(
shapes.push(Shape::text(
ui.fonts(),
pos2(rect.left(), y),
egui::Align2::LEFT_BOTTOM,
@ -108,17 +108,17 @@ impl FrameHistory {
let x = remap(age, history.max_age()..=0.0, rect.x_range());
let y = remap_clamp(cpu_usage, 0.0..=graph_top_cpu_usage, rect.bottom_up_range());
cmds.push(PaintCmd::line_segment(
shapes.push(Shape::line_segment(
[pos2(x, rect.bottom()), pos2(x, y)],
line_stroke,
));
if cpu_usage < graph_top_cpu_usage {
cmds.push(PaintCmd::circle_filled(pos2(x, y), radius, circle_color));
shapes.push(Shape::circle_filled(pos2(x, y), radius, circle_color));
}
}
ui.painter().extend(cmds);
ui.painter().extend(shapes);
response
}

View file

@ -98,8 +98,8 @@ fn test_egui_e2e() {
for _ in 0..NUM_FRAMES {
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx);
let (_output, paint_commands) = ctx.end_frame();
let paint_jobs = ctx.tessellate(paint_commands);
let (_output, shapes) = ctx.end_frame();
let paint_jobs = ctx.tessellate(shapes);
assert!(!paint_jobs.is_empty());
}
}

View file

@ -184,7 +184,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
*ctx.memory() = saved_memory; // We don't want to remember that windows were huge.
ctx.clear_animations();
let (egui_output, _paint_commands) = ctx.end_frame();
let (egui_output, _shapes) = ctx.end_frame();
handle_output(egui_output, &display, clipboard.as_mut());
// TODO: handle app_output
// eprintln!("Warmed up in {} ms", warm_up_start.elapsed().as_millis())
@ -211,8 +211,8 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
}
.build();
app.update(&ctx, &mut frame);
let (egui_output, paint_commands) = ctx.end_frame();
let paint_jobs = ctx.tessellate(paint_commands);
let (egui_output, shapes) = ctx.end_frame();
let paint_jobs = ctx.tessellate(shapes);
let frame_time = (Instant::now() - frame_start).as_secs_f64() as f32;
previous_frame_time = Some(frame_time);

View file

@ -38,8 +38,8 @@ impl WebBackend {
.take()
.expect("unmatched calls to begin_frame/end_frame");
let (output, paint_commands) = self.ctx.end_frame();
let paint_jobs = self.ctx.tessellate(paint_commands);
let (output, shapes) = self.ctx.end_frame();
let paint_jobs = self.ctx.tessellate(shapes);
let now = now_sec();
self.previous_frame_time = Some((now - frame_start) as f32);

View file

@ -67,7 +67,7 @@ pub trait App {
/// If `true` a warm-up call to [`Self::update`] will be issued where
/// `ctx.memory().everything_is_visible()` will be set to `true`.
///
/// In this warm-up call, all paint commands will be ignored.
/// In this warm-up call, all painted shapes will be ignored.
fn warm_up_enabled(&self) -> bool {
false
}