mirror of
https://github.com/emilk/egui.git
synced 2026-06-27 15:13:12 -04:00
Shape refactor (#705)
* More introspection stats about vertices/indices etc * more serde derive * #[inline] to Shape constructors * Introduce RectShape * Introduce CircleShape * Introduce PathShape * More serde derive * impl Copy for RectShape and CircleShape * Simplify some code * More serde derive * Add helpers for appending more input or output * Serde derives for RawInput * Rename Fonts::from_definitions to Fonts::new * Add Output::take * refactor EguiGlium slightly * Derive PartialEq for RawInput * Improve egui::util::History interface * tweaks * Improve History filter: add minimum length * Calculate galley bounding rect * tessellator: cull line segments and paths * tessellator: cull meshes * Fix bug in History bandwidth estimator
This commit is contained in:
@@ -87,7 +87,7 @@ pub use {
|
||||
color::{Color32, Rgba},
|
||||
mesh::{Mesh, Mesh16, Vertex},
|
||||
shadow::Shadow,
|
||||
shape::{Shape, TextShape},
|
||||
shape::{CircleShape, PathShape, RectShape, Shape, TextShape},
|
||||
stats::PaintStats,
|
||||
stroke::Stroke,
|
||||
tessellator::{TessellationOptions, Tessellator},
|
||||
@@ -95,6 +95,8 @@ pub use {
|
||||
texture_atlas::{Texture, TextureAtlas},
|
||||
};
|
||||
|
||||
pub use emath::{pos2, vec2, Pos2, Rect, Vec2};
|
||||
|
||||
pub use ahash;
|
||||
pub use emath;
|
||||
|
||||
@@ -106,6 +108,7 @@ pub const WHITE_UV: emath::Pos2 = emath::pos2(0.0, 0.0);
|
||||
|
||||
/// What texture to use in a [`Mesh`] mesh.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum TextureId {
|
||||
/// The egui font texture.
|
||||
/// If you don't want to use a texture, pick this and the [`WHITE_UV`] for uv-coord.
|
||||
@@ -122,14 +125,6 @@ impl Default for TextureId {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PaintRect {
|
||||
pub rect: emath::Rect,
|
||||
/// How rounded the corners are. Use `0.0` for no rounding.
|
||||
pub corner_radius: f32,
|
||||
pub fill: Color32,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
/// A [`Shape`] within a clip rectangle.
|
||||
///
|
||||
/// Everything is using logical points.
|
||||
@@ -146,6 +141,7 @@ pub struct ClippedShape(
|
||||
///
|
||||
/// Everything is using logical points.
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct ClippedMesh(
|
||||
/// Clip / scissor rectangle.
|
||||
/// Only show the part of the [`Mesh`] that falls within this.
|
||||
|
||||
@@ -6,6 +6,7 @@ use emath::*;
|
||||
/// Should be friendly to send to GPU as is.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Vertex {
|
||||
/// Logical pixel coordinates (points).
|
||||
/// (0,0) is the top left corner of the screen.
|
||||
@@ -22,6 +23,7 @@ pub struct Vertex {
|
||||
|
||||
/// Textured triangles in two dimensions.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Mesh {
|
||||
/// Draw as triangles (i.e. the length is always multiple of three).
|
||||
///
|
||||
|
||||
@@ -52,12 +52,11 @@ impl Shadow {
|
||||
let Self { extrusion, color } = *self;
|
||||
|
||||
use crate::tessellator::*;
|
||||
let rect = PaintRect {
|
||||
rect: rect.expand(0.5 * extrusion),
|
||||
corner_radius: corner_radius + 0.5 * extrusion,
|
||||
fill: color,
|
||||
stroke: Default::default(),
|
||||
};
|
||||
let rect = RectShape::filled(
|
||||
rect.expand(0.5 * extrusion),
|
||||
corner_radius + 0.5 * extrusion,
|
||||
color,
|
||||
);
|
||||
let mut tessellator = Tessellator::from_options(TessellationOptions {
|
||||
aa_size: extrusion,
|
||||
anti_alias: true,
|
||||
|
||||
@@ -14,32 +14,13 @@ pub enum Shape {
|
||||
/// Recursively nest more shapes - sometimes a convenience to be able to do.
|
||||
/// For performance reasons it is better to avoid it.
|
||||
Vec(Vec<Shape>),
|
||||
Circle {
|
||||
center: Pos2,
|
||||
radius: f32,
|
||||
fill: Color32,
|
||||
stroke: Stroke,
|
||||
},
|
||||
Circle(CircleShape),
|
||||
LineSegment {
|
||||
points: [Pos2; 2],
|
||||
stroke: Stroke,
|
||||
},
|
||||
Path {
|
||||
points: Vec<Pos2>,
|
||||
/// If true, connect the first and last of the points together.
|
||||
/// This is required if `fill != TRANSPARENT`.
|
||||
closed: bool,
|
||||
/// Fill is only supported for convex polygons.
|
||||
fill: Color32,
|
||||
stroke: Stroke,
|
||||
},
|
||||
Rect {
|
||||
rect: Rect,
|
||||
/// How rounded the corners are. Use `0.0` for no rounding.
|
||||
corner_radius: f32,
|
||||
fill: Color32,
|
||||
stroke: Stroke,
|
||||
},
|
||||
Path(PathShape),
|
||||
Rect(RectShape),
|
||||
Text(TextShape),
|
||||
Mesh(Mesh),
|
||||
}
|
||||
@@ -48,6 +29,7 @@ pub enum Shape {
|
||||
impl Shape {
|
||||
/// A line between two points.
|
||||
/// More efficient than calling [`Self::line`].
|
||||
#[inline]
|
||||
pub fn line_segment(points: [Pos2; 2], stroke: impl Into<Stroke>) -> Self {
|
||||
Self::LineSegment {
|
||||
points,
|
||||
@@ -58,23 +40,15 @@ impl Shape {
|
||||
/// A line through many points.
|
||||
///
|
||||
/// Use [`Self::line_segment`] instead if your line only connects two points.
|
||||
#[inline]
|
||||
pub fn line(points: Vec<Pos2>, stroke: impl Into<Stroke>) -> Self {
|
||||
Self::Path {
|
||||
points,
|
||||
closed: false,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
Self::Path(PathShape::line(points, stroke))
|
||||
}
|
||||
|
||||
/// A line that closes back to the start point again.
|
||||
#[inline]
|
||||
pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<Stroke>) -> Self {
|
||||
Self::Path {
|
||||
points,
|
||||
closed: true,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
Self::Path(PathShape::closed_line(points, stroke))
|
||||
}
|
||||
|
||||
/// Turn a line into equally spaced dots.
|
||||
@@ -102,53 +76,33 @@ impl Shape {
|
||||
}
|
||||
|
||||
/// A convex polygon with a fill and optional stroke.
|
||||
#[inline]
|
||||
pub fn convex_polygon(
|
||||
points: Vec<Pos2>,
|
||||
fill: impl Into<Color32>,
|
||||
stroke: impl Into<Stroke>,
|
||||
) -> Self {
|
||||
Self::Path {
|
||||
points,
|
||||
closed: true,
|
||||
fill: fill.into(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
Self::Path(PathShape::convex_polygon(points, fill, stroke))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
|
||||
Self::Circle {
|
||||
center,
|
||||
radius,
|
||||
fill: fill_color.into(),
|
||||
stroke: Default::default(),
|
||||
}
|
||||
Self::Circle(CircleShape::filled(center, radius, fill_color))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn circle_stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
|
||||
Self::Circle {
|
||||
center,
|
||||
radius,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
Self::Circle(CircleShape::stroke(center, radius, stroke))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rect_filled(rect: Rect, corner_radius: f32, fill_color: impl Into<Color32>) -> Self {
|
||||
Self::Rect {
|
||||
rect,
|
||||
corner_radius,
|
||||
fill: fill_color.into(),
|
||||
stroke: Default::default(),
|
||||
}
|
||||
Self::Rect(RectShape::filled(rect, corner_radius, fill_color))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rect_stroke(rect: Rect, corner_radius: f32, stroke: impl Into<Stroke>) -> Self {
|
||||
Self::Rect {
|
||||
rect,
|
||||
corner_radius,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
Self::Rect(RectShape::stroke(rect, corner_radius, stroke))
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
@@ -165,6 +119,7 @@ impl Shape {
|
||||
Self::galley(rect.min, galley)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn galley(pos: Pos2, galley: std::sync::Arc<Galley>) -> Self {
|
||||
TextShape::new(pos, galley).into()
|
||||
}
|
||||
@@ -172,7 +127,165 @@ impl Shape {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// How to draw some text on screen.
|
||||
/// How to paint a circle.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct CircleShape {
|
||||
pub center: Pos2,
|
||||
pub radius: f32,
|
||||
pub fill: Color32,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
impl CircleShape {
|
||||
#[inline]
|
||||
pub fn filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
|
||||
Self {
|
||||
center,
|
||||
radius,
|
||||
fill: fill_color.into(),
|
||||
stroke: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
|
||||
Self {
|
||||
center,
|
||||
radius,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CircleShape> for Shape {
|
||||
#[inline(always)]
|
||||
fn from(shape: CircleShape) -> Self {
|
||||
Self::Circle(shape)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// A path which can be stroked and/or filled (if closed).
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct PathShape {
|
||||
pub points: Vec<Pos2>,
|
||||
/// If true, connect the first and last of the points together.
|
||||
/// This is required if `fill != TRANSPARENT`.
|
||||
pub closed: bool,
|
||||
/// Fill is only supported for convex polygons.
|
||||
pub fill: Color32,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
impl PathShape {
|
||||
/// A line through many points.
|
||||
///
|
||||
/// Use [`Shape::line_segment`] instead if your line only connects two points.
|
||||
#[inline]
|
||||
pub fn line(points: Vec<Pos2>, stroke: impl Into<Stroke>) -> Self {
|
||||
PathShape {
|
||||
points,
|
||||
closed: false,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A line that closes back to the start point again.
|
||||
#[inline]
|
||||
pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<Stroke>) -> Self {
|
||||
PathShape {
|
||||
points,
|
||||
closed: true,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A convex polygon with a fill and optional stroke.
|
||||
#[inline]
|
||||
pub fn convex_polygon(
|
||||
points: Vec<Pos2>,
|
||||
fill: impl Into<Color32>,
|
||||
stroke: impl Into<Stroke>,
|
||||
) -> Self {
|
||||
PathShape {
|
||||
points,
|
||||
closed: true,
|
||||
fill: fill.into(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen-space bounding rectangle.
|
||||
#[inline]
|
||||
pub fn bounding_rect(&self) -> Rect {
|
||||
Rect::from_points(&self.points).expand(self.stroke.width)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathShape> for Shape {
|
||||
#[inline(always)]
|
||||
fn from(shape: PathShape) -> Self {
|
||||
Self::Path(shape)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// How to paint a rectangle.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct RectShape {
|
||||
pub rect: Rect,
|
||||
/// How rounded the corners are. Use `0.0` for no rounding.
|
||||
pub corner_radius: f32,
|
||||
pub fill: Color32,
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
impl RectShape {
|
||||
#[inline]
|
||||
pub fn filled(rect: Rect, corner_radius: f32, fill_color: impl Into<Color32>) -> Self {
|
||||
Self {
|
||||
rect,
|
||||
corner_radius,
|
||||
fill: fill_color.into(),
|
||||
stroke: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn stroke(rect: Rect, corner_radius: f32, stroke: impl Into<Stroke>) -> Self {
|
||||
Self {
|
||||
rect,
|
||||
corner_radius,
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen-space bounding rectangle.
|
||||
#[inline]
|
||||
pub fn bounding_rect(&self) -> Rect {
|
||||
self.rect.expand(self.stroke.width)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RectShape> for Shape {
|
||||
#[inline(always)]
|
||||
fn from(shape: RectShape) -> Self {
|
||||
Self::Rect(shape)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// How to paint some text on screen.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TextShape {
|
||||
/// Top left corner of the first character.
|
||||
@@ -206,12 +319,18 @@ impl TextShape {
|
||||
angle: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen-space bounding rectangle.
|
||||
#[inline]
|
||||
pub fn bounding_rect(&self) -> Rect {
|
||||
self.galley.mesh_bounds.translate(self.pos.to_vec2())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextShape> for Shape {
|
||||
#[inline(always)]
|
||||
fn from(text_shape: TextShape) -> Self {
|
||||
Self::Text(text_shape)
|
||||
fn from(shape: TextShape) -> Self {
|
||||
Self::Text(shape)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +378,7 @@ fn dashes_from_line(
|
||||
let new_point = start + vector * (position_on_segment / segment_length);
|
||||
if drawing_dash {
|
||||
// This is the end point.
|
||||
if let Shape::Path { points, .. } = shapes.last_mut().unwrap() {
|
||||
if let Shape::Path(PathShape { points, .. }) = shapes.last_mut().unwrap() {
|
||||
points.push(new_point);
|
||||
}
|
||||
position_on_segment += gap_length;
|
||||
@@ -272,7 +391,7 @@ fn dashes_from_line(
|
||||
}
|
||||
// If the segment ends and the dash is not finished, add the segment's end point.
|
||||
if drawing_dash {
|
||||
if let Shape::Path { points, .. } = shapes.last_mut().unwrap() {
|
||||
if let Shape::Path(PathShape { points, .. }) = shapes.last_mut().unwrap() {
|
||||
points.push(end);
|
||||
}
|
||||
}
|
||||
@@ -305,21 +424,21 @@ impl Shape {
|
||||
shape.translate(delta);
|
||||
}
|
||||
}
|
||||
Shape::Circle { center, .. } => {
|
||||
*center += delta;
|
||||
Shape::Circle(circle_shape) => {
|
||||
circle_shape.center += delta;
|
||||
}
|
||||
Shape::LineSegment { points, .. } => {
|
||||
for p in points {
|
||||
*p += delta;
|
||||
}
|
||||
}
|
||||
Shape::Path { points, .. } => {
|
||||
for p in points {
|
||||
Shape::Path(path_shape) => {
|
||||
for p in &mut path_shape.points {
|
||||
*p += delta;
|
||||
}
|
||||
}
|
||||
Shape::Rect { rect, .. } => {
|
||||
*rect = rect.translate(delta);
|
||||
Shape::Rect(rect_shape) => {
|
||||
rect_shape.rect = rect_shape.rect.translate(delta);
|
||||
}
|
||||
Shape::Text(text_shape) => {
|
||||
text_shape.pos += delta;
|
||||
|
||||
@@ -9,20 +9,20 @@ pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) {
|
||||
adjust_colors(shape, adjust_color)
|
||||
}
|
||||
}
|
||||
Shape::Circle { fill, stroke, .. } => {
|
||||
adjust_color(fill);
|
||||
adjust_color(&mut stroke.color);
|
||||
Shape::Circle(circle_shape) => {
|
||||
adjust_color(&mut circle_shape.fill);
|
||||
adjust_color(&mut circle_shape.stroke.color);
|
||||
}
|
||||
Shape::LineSegment { stroke, .. } => {
|
||||
adjust_color(&mut stroke.color);
|
||||
}
|
||||
Shape::Path { fill, stroke, .. } => {
|
||||
adjust_color(fill);
|
||||
adjust_color(&mut stroke.color);
|
||||
Shape::Path(path_shape) => {
|
||||
adjust_color(&mut path_shape.fill);
|
||||
adjust_color(&mut path_shape.stroke.color);
|
||||
}
|
||||
Shape::Rect { fill, stroke, .. } => {
|
||||
adjust_color(fill);
|
||||
adjust_color(&mut stroke.color);
|
||||
Shape::Rect(rect_shape) => {
|
||||
adjust_color(&mut rect_shape.fill);
|
||||
adjust_color(&mut rect_shape.stroke.color);
|
||||
}
|
||||
Shape::Text(text_shape) => {
|
||||
if let Some(override_text_color) = &mut text_shape.override_text_color {
|
||||
|
||||
@@ -163,6 +163,9 @@ pub struct PaintStats {
|
||||
pub shape_mesh: AllocInfo,
|
||||
pub shape_vec: AllocInfo,
|
||||
|
||||
pub text_shape_vertices: AllocInfo,
|
||||
pub text_shape_indices: AllocInfo,
|
||||
|
||||
/// Number of separate clip rectangles
|
||||
pub clipped_meshes: AllocInfo,
|
||||
pub vertices: AllocInfo,
|
||||
@@ -195,11 +198,16 @@ impl PaintStats {
|
||||
Shape::Noop | Shape::Circle { .. } | Shape::LineSegment { .. } | Shape::Rect { .. } => {
|
||||
Default::default()
|
||||
}
|
||||
Shape::Path { points, .. } => {
|
||||
self.shape_path += AllocInfo::from_slice(points);
|
||||
Shape::Path(path_shape) => {
|
||||
self.shape_path += AllocInfo::from_slice(&path_shape.points);
|
||||
}
|
||||
Shape::Text(text_shape) => {
|
||||
self.shape_text += AllocInfo::from_galley(&text_shape.galley);
|
||||
|
||||
for row in &text_shape.galley.rows {
|
||||
self.text_shape_indices += AllocInfo::from_slice(&row.visuals.mesh.indices);
|
||||
self.text_shape_vertices += AllocInfo::from_slice(&row.visuals.mesh.vertices);
|
||||
}
|
||||
}
|
||||
Shape::Mesh(mesh) => {
|
||||
self.shape_mesh += AllocInfo::from_mesh(mesh);
|
||||
|
||||
@@ -26,6 +26,12 @@ impl Stroke {
|
||||
color: color.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// True if width is zero or color is transparent
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.width <= 0.0 || self.color == Color32::TRANSPARENT
|
||||
}
|
||||
}
|
||||
|
||||
impl<Color> From<(f32, Color)> for Stroke
|
||||
|
||||
@@ -541,12 +541,12 @@ impl Tessellator {
|
||||
self.tessellate_shape(tex_size, shape, out)
|
||||
}
|
||||
}
|
||||
Shape::Circle {
|
||||
Shape::Circle(CircleShape {
|
||||
center,
|
||||
radius,
|
||||
fill,
|
||||
stroke,
|
||||
} => {
|
||||
}) => {
|
||||
if radius <= 0.0 {
|
||||
return;
|
||||
}
|
||||
@@ -563,70 +563,45 @@ impl Tessellator {
|
||||
self.scratchpad_path.stroke_closed(stroke, options, out);
|
||||
}
|
||||
Shape::Mesh(mesh) => {
|
||||
if mesh.is_valid() {
|
||||
out.append(mesh);
|
||||
} else {
|
||||
if !mesh.is_valid() {
|
||||
crate::epaint_assert!(false, "Invalid Mesh in Shape::Mesh");
|
||||
return;
|
||||
}
|
||||
|
||||
if options.coarse_tessellation_culling && !clip_rect.intersects(mesh.calc_bounds())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
out.append(mesh);
|
||||
}
|
||||
Shape::LineSegment { points, stroke } => {
|
||||
if stroke.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if options.coarse_tessellation_culling
|
||||
&& !clip_rect
|
||||
.intersects(Rect::from_two_pos(points[0], points[1]).expand(stroke.width))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.scratchpad_path.clear();
|
||||
self.scratchpad_path.add_line_segment(points);
|
||||
self.scratchpad_path.stroke_open(stroke, options, out);
|
||||
}
|
||||
Shape::Path {
|
||||
points,
|
||||
closed,
|
||||
fill,
|
||||
stroke,
|
||||
} => {
|
||||
if points.len() >= 2 {
|
||||
self.scratchpad_path.clear();
|
||||
if closed {
|
||||
self.scratchpad_path.add_line_loop(&points);
|
||||
} else {
|
||||
self.scratchpad_path.add_open_points(&points);
|
||||
}
|
||||
|
||||
if fill != Color32::TRANSPARENT {
|
||||
crate::epaint_assert!(
|
||||
closed,
|
||||
"You asked to fill a path that is not closed. That makes no sense."
|
||||
);
|
||||
self.scratchpad_path.fill(fill, options, out);
|
||||
}
|
||||
let typ = if closed {
|
||||
PathType::Closed
|
||||
} else {
|
||||
PathType::Open
|
||||
};
|
||||
self.scratchpad_path.stroke(typ, stroke, options, out);
|
||||
}
|
||||
Shape::Path(path_shape) => {
|
||||
self.tessellate_path(path_shape, out);
|
||||
}
|
||||
Shape::Rect {
|
||||
rect,
|
||||
corner_radius,
|
||||
fill,
|
||||
stroke,
|
||||
} => {
|
||||
let rect = PaintRect {
|
||||
rect,
|
||||
corner_radius,
|
||||
fill,
|
||||
stroke,
|
||||
};
|
||||
self.tessellate_rect(&rect, out);
|
||||
Shape::Rect(rect_shape) => {
|
||||
self.tessellate_rect(&rect_shape, out);
|
||||
}
|
||||
Shape::Text(text_shape) => {
|
||||
if options.debug_paint_text_rects {
|
||||
let rect = text_shape.galley.rect.translate(text_shape.pos.to_vec2());
|
||||
self.tessellate_rect(
|
||||
&PaintRect {
|
||||
rect: Rect::from_min_size(text_shape.pos, text_shape.galley.size())
|
||||
.expand(0.5),
|
||||
corner_radius: 2.0,
|
||||
fill: Default::default(),
|
||||
stroke: (0.5, Color32::GREEN).into(),
|
||||
},
|
||||
&RectShape::stroke(rect.expand(0.5), 2.0, (0.5, Color32::GREEN)),
|
||||
out,
|
||||
);
|
||||
}
|
||||
@@ -635,8 +610,48 @@ impl Tessellator {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tessellate_rect(&mut self, rect: &PaintRect, out: &mut Mesh) {
|
||||
let PaintRect {
|
||||
pub(crate) fn tessellate_path(&mut self, path_shape: PathShape, out: &mut Mesh) {
|
||||
if path_shape.points.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.options.coarse_tessellation_culling
|
||||
&& !path_shape.bounding_rect().intersects(self.clip_rect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let PathShape {
|
||||
points,
|
||||
closed,
|
||||
fill,
|
||||
stroke,
|
||||
} = path_shape;
|
||||
|
||||
self.scratchpad_path.clear();
|
||||
if closed {
|
||||
self.scratchpad_path.add_line_loop(&points);
|
||||
} else {
|
||||
self.scratchpad_path.add_open_points(&points);
|
||||
}
|
||||
|
||||
if fill != Color32::TRANSPARENT {
|
||||
crate::epaint_assert!(
|
||||
closed,
|
||||
"You asked to fill a path that is not closed. That makes no sense."
|
||||
);
|
||||
self.scratchpad_path.fill(fill, self.options, out);
|
||||
}
|
||||
let typ = if closed {
|
||||
PathType::Closed
|
||||
} else {
|
||||
PathType::Open
|
||||
};
|
||||
self.scratchpad_path.stroke(typ, stroke, self.options, out);
|
||||
}
|
||||
|
||||
pub(crate) fn tessellate_rect(&mut self, rect: &RectShape, out: &mut Mesh) {
|
||||
let RectShape {
|
||||
mut rect,
|
||||
corner_radius,
|
||||
fill,
|
||||
@@ -803,12 +818,11 @@ pub fn tessellate_shapes(
|
||||
tessellator.clip_rect = Rect::EVERYTHING;
|
||||
tessellator.tessellate_shape(
|
||||
tex_size,
|
||||
Shape::Rect {
|
||||
rect: *clip_rect,
|
||||
corner_radius: 0.0,
|
||||
fill: Default::default(),
|
||||
stroke: Stroke::new(2.0, Color32::from_rgb(150, 255, 150)),
|
||||
},
|
||||
Shape::rect_stroke(
|
||||
*clip_rect,
|
||||
0.0,
|
||||
Stroke::new(2.0, Color32::from_rgb(150, 255, 150)),
|
||||
),
|
||||
mesh,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -118,7 +118,6 @@ pub struct FontDefinitions {
|
||||
///
|
||||
/// `epaint` has built-in-default for these,
|
||||
/// but you can override them if you like.
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
pub font_data: BTreeMap<String, FontData>,
|
||||
|
||||
/// Which fonts (names) to use for each [`FontFamily`].
|
||||
@@ -219,7 +218,7 @@ pub struct Fonts {
|
||||
}
|
||||
|
||||
impl Fonts {
|
||||
pub fn from_definitions(pixels_per_point: f32, definitions: FontDefinitions) -> Self {
|
||||
pub fn new(pixels_per_point: f32, definitions: FontDefinitions) -> Self {
|
||||
assert!(
|
||||
0.0 < pixels_per_point && pixels_per_point < 100.0,
|
||||
"pixels_per_point out of range: {}",
|
||||
@@ -278,6 +277,11 @@ impl Fonts {
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated = "Renamed to Fonts::new"]
|
||||
pub fn from_definitions(pixels_per_point: f32, definitions: FontDefinitions) -> Self {
|
||||
Self::new(pixels_per_point, definitions)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn pixels_per_point(&self) -> f32 {
|
||||
self.pixels_per_point
|
||||
|
||||
@@ -333,11 +333,13 @@ fn galley_from_rows(fonts: &Fonts, job: Arc<LayoutJob>, mut rows: Vec<Row>) -> G
|
||||
|
||||
let format_summary = format_summary(&job);
|
||||
|
||||
let mut mesh_bounds = Rect::NOTHING;
|
||||
let mut num_vertices = 0;
|
||||
let mut num_indices = 0;
|
||||
|
||||
for row in &mut rows {
|
||||
row.visuals = tessellate_row(fonts, &job, &format_summary, row);
|
||||
mesh_bounds = mesh_bounds.union(row.visuals.mesh_bounds);
|
||||
num_vertices += row.visuals.mesh.vertices.len();
|
||||
num_indices += row.visuals.mesh.indices.len();
|
||||
}
|
||||
@@ -348,6 +350,7 @@ fn galley_from_rows(fonts: &Fonts, job: Arc<LayoutJob>, mut rows: Vec<Row>) -> G
|
||||
job,
|
||||
rows,
|
||||
rect,
|
||||
mesh_bounds,
|
||||
num_vertices,
|
||||
num_indices,
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use emath::*;
|
||||
///
|
||||
/// Pass this to [`Fonts::layout_job]` or [`crate::text::layout`].
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct LayoutJob {
|
||||
/// The complete text of this job, referenced by `LayoutSection`.
|
||||
pub text: String, // TODO: Cow<'static, str>
|
||||
@@ -136,6 +137,7 @@ impl std::hash::Hash for LayoutJob {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct LayoutSection {
|
||||
/// Can be used for first row indentation.
|
||||
pub leading_space: f32,
|
||||
@@ -161,6 +163,7 @@ impl std::hash::Hash for LayoutSection {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct TextFormat {
|
||||
pub style: TextStyle,
|
||||
/// Text color
|
||||
@@ -225,6 +228,10 @@ pub struct Galley {
|
||||
/// * [`Align::RIGHT`]: rect.right() == 0.0
|
||||
pub rect: Rect,
|
||||
|
||||
/// Tight bounding box around all the meshes in all the rows.
|
||||
/// Can be used for culling.
|
||||
pub mesh_bounds: Rect,
|
||||
|
||||
/// Total number of vertices in all the row meshes.
|
||||
pub num_vertices: usize,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user