2021-01-10 14:42:46 +00:00
|
|
|
use crate::{
|
|
|
|
text::{Fonts, Galley, TextStyle},
|
2021-01-25 20:23:24 +00:00
|
|
|
Color32, Mesh, Stroke,
|
2020-05-19 20:28:57 +00:00
|
|
|
};
|
2021-01-10 14:42:46 +00:00
|
|
|
use emath::*;
|
2020-05-19 20:28:57 +00:00
|
|
|
|
2020-12-27 11:57:15 +00:00
|
|
|
/// A paint primitive such as a circle or a piece of text.
|
2020-12-29 00:13:14 +00:00
|
|
|
/// Coordinates are all screen space points (not physical pixels).
|
2021-01-16 17:49:10 +00:00
|
|
|
#[must_use = "Add a Shape to a Painter"]
|
2021-03-13 11:58:17 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
2021-01-10 10:43:01 +00:00
|
|
|
pub enum Shape {
|
2020-07-30 12:11:09 +00:00
|
|
|
/// Paint nothing. This can be useful as a placeholder.
|
|
|
|
Noop,
|
2021-01-10 10:43:01 +00:00
|
|
|
/// Recursively nest more shapes - sometimes a convenience to be able to do.
|
2020-12-29 00:13:14 +00:00
|
|
|
/// For performance reasons it is better to avoid it.
|
2021-01-10 10:43:01 +00:00
|
|
|
Vec(Vec<Shape>),
|
2020-05-19 20:28:57 +00:00
|
|
|
Circle {
|
|
|
|
center: Pos2,
|
2020-08-05 17:45:39 +00:00
|
|
|
radius: f32,
|
2021-01-02 16:02:18 +00:00
|
|
|
fill: Color32,
|
2020-09-01 21:54:21 +00:00
|
|
|
stroke: Stroke,
|
2020-05-19 20:28:57 +00:00
|
|
|
},
|
|
|
|
LineSegment {
|
|
|
|
points: [Pos2; 2],
|
2020-09-01 21:54:21 +00:00
|
|
|
stroke: Stroke,
|
2020-05-19 20:28:57 +00:00
|
|
|
},
|
|
|
|
Path {
|
2020-09-01 18:03:50 +00:00
|
|
|
points: Vec<Pos2>,
|
|
|
|
/// If true, connect the first and last of the points together.
|
|
|
|
/// This is required if `fill != TRANSPARENT`.
|
2020-05-19 20:28:57 +00:00
|
|
|
closed: bool,
|
2021-01-02 16:02:18 +00:00
|
|
|
fill: Color32,
|
2020-09-01 21:54:21 +00:00
|
|
|
stroke: Stroke,
|
2020-05-19 20:28:57 +00:00
|
|
|
},
|
|
|
|
Rect {
|
|
|
|
rect: Rect,
|
2020-12-27 13:16:37 +00:00
|
|
|
/// How rounded the corners are. Use `0.0` for no rounding.
|
2020-05-19 20:28:57 +00:00
|
|
|
corner_radius: f32,
|
2021-01-02 16:02:18 +00:00
|
|
|
fill: Color32,
|
2020-09-01 21:54:21 +00:00
|
|
|
stroke: Stroke,
|
2020-05-19 20:28:57 +00:00
|
|
|
},
|
|
|
|
Text {
|
2021-03-29 19:24:09 +00:00
|
|
|
/// Top left corner of the first character..
|
2020-05-19 20:28:57 +00:00
|
|
|
pos: Pos2,
|
2021-03-29 19:24:09 +00:00
|
|
|
/// The layed out text.
|
2021-03-29 20:30:18 +00:00
|
|
|
galley: std::sync::Arc<Galley>,
|
2021-03-29 19:24:09 +00:00
|
|
|
/// Text color (foreground).
|
2021-01-02 16:02:18 +00:00
|
|
|
color: Color32,
|
2021-03-29 19:24:09 +00:00
|
|
|
/// If true, tilt the letters for a hacky italics effect.
|
2021-01-30 13:48:36 +00:00
|
|
|
fake_italics: bool,
|
2020-05-19 20:28:57 +00:00
|
|
|
},
|
2021-01-25 20:23:24 +00:00
|
|
|
Mesh(Mesh),
|
2020-05-19 20:28:57 +00:00
|
|
|
}
|
|
|
|
|
2020-11-02 16:35:16 +00:00
|
|
|
/// ## Constructors
|
2021-01-10 10:43:01 +00:00
|
|
|
impl Shape {
|
2020-09-13 07:28:54 +00:00
|
|
|
pub fn line_segment(points: [Pos2; 2], stroke: impl Into<Stroke>) -> Self {
|
|
|
|
Self::LineSegment {
|
|
|
|
points,
|
|
|
|
stroke: stroke.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 16:35:16 +00:00
|
|
|
pub fn line(points: Vec<Pos2>, stroke: impl Into<Stroke>) -> Self {
|
|
|
|
Self::Path {
|
|
|
|
points,
|
|
|
|
closed: false,
|
|
|
|
fill: Default::default(),
|
|
|
|
stroke: stroke.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<Stroke>) -> Self {
|
|
|
|
Self::Path {
|
|
|
|
points,
|
|
|
|
closed: true,
|
|
|
|
fill: Default::default(),
|
|
|
|
stroke: stroke.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-02 16:02:18 +00:00
|
|
|
pub fn polygon(points: Vec<Pos2>, fill: impl Into<Color32>, stroke: impl Into<Stroke>) -> Self {
|
2020-11-02 16:35:16 +00:00
|
|
|
Self::Path {
|
|
|
|
points,
|
|
|
|
closed: true,
|
|
|
|
fill: fill.into(),
|
|
|
|
stroke: stroke.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-02 16:02:18 +00:00
|
|
|
pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
|
2020-09-13 07:28:54 +00:00
|
|
|
Self::Circle {
|
|
|
|
center,
|
|
|
|
radius,
|
|
|
|
fill: fill_color.into(),
|
|
|
|
stroke: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn circle_stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
|
|
|
|
Self::Circle {
|
|
|
|
center,
|
|
|
|
radius,
|
|
|
|
fill: Default::default(),
|
|
|
|
stroke: stroke.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-02 16:02:18 +00:00
|
|
|
pub fn rect_filled(rect: Rect, corner_radius: f32, fill_color: impl Into<Color32>) -> Self {
|
2020-09-13 07:28:54 +00:00
|
|
|
Self::Rect {
|
|
|
|
rect,
|
|
|
|
corner_radius,
|
|
|
|
fill: fill_color.into(),
|
|
|
|
stroke: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
2020-10-01 20:25:44 +00:00
|
|
|
|
2021-04-29 17:49:49 +00:00
|
|
|
#[allow(clippy::needless_pass_by_value)]
|
2020-10-01 20:25:44 +00:00
|
|
|
pub fn text(
|
|
|
|
fonts: &Fonts,
|
|
|
|
pos: Pos2,
|
2021-01-10 09:51:16 +00:00
|
|
|
anchor: Align2,
|
2021-04-29 17:49:49 +00:00
|
|
|
text: impl ToString,
|
2020-10-01 20:25:44 +00:00
|
|
|
text_style: TextStyle,
|
2021-01-02 16:02:18 +00:00
|
|
|
color: Color32,
|
2020-10-01 20:25:44 +00:00
|
|
|
) -> Self {
|
2021-04-29 17:49:49 +00:00
|
|
|
let galley = fonts.layout_multiline(text_style, text.to_string(), f32::INFINITY);
|
2021-01-10 09:51:16 +00:00
|
|
|
let rect = anchor.anchor_rect(Rect::from_min_size(pos, galley.size));
|
2020-10-01 20:25:44 +00:00
|
|
|
Self::Text {
|
|
|
|
pos: rect.min,
|
|
|
|
galley,
|
|
|
|
color,
|
2021-01-30 13:48:36 +00:00
|
|
|
fake_italics: false,
|
2020-10-01 20:25:44 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-02 16:35:16 +00:00
|
|
|
}
|
2020-10-21 09:04:22 +00:00
|
|
|
|
2020-11-02 16:35:16 +00:00
|
|
|
/// ## Operations
|
2021-01-10 10:43:01 +00:00
|
|
|
impl Shape {
|
2021-01-25 20:23:24 +00:00
|
|
|
pub fn mesh(mesh: Mesh) -> Self {
|
2021-05-17 20:34:29 +00:00
|
|
|
crate::epaint_assert!(mesh.is_valid());
|
2021-01-25 20:23:24 +00:00
|
|
|
Self::Mesh(mesh)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[deprecated = "Renamed `mesh`"]
|
|
|
|
pub fn triangles(mesh: Mesh) -> Self {
|
|
|
|
Self::mesh(mesh)
|
2020-10-21 09:04:22 +00:00
|
|
|
}
|
2020-10-21 09:09:42 +00:00
|
|
|
|
2021-04-01 20:10:34 +00:00
|
|
|
#[inline(always)]
|
2020-10-21 09:09:42 +00:00
|
|
|
pub fn texture_id(&self) -> super::TextureId {
|
2021-01-25 20:23:24 +00:00
|
|
|
if let Shape::Mesh(mesh) = self {
|
|
|
|
mesh.texture_id
|
2020-10-21 09:09:42 +00:00
|
|
|
} else {
|
|
|
|
super::TextureId::Egui
|
|
|
|
}
|
|
|
|
}
|
2020-11-02 16:41:52 +00:00
|
|
|
|
|
|
|
/// Translate location by this much, in-place
|
|
|
|
pub fn translate(&mut self, delta: Vec2) {
|
|
|
|
match self {
|
2021-01-10 10:43:01 +00:00
|
|
|
Shape::Noop => {}
|
|
|
|
Shape::Vec(shapes) => {
|
|
|
|
for shape in shapes {
|
|
|
|
shape.translate(delta);
|
2020-12-29 00:13:14 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-10 10:43:01 +00:00
|
|
|
Shape::Circle { center, .. } => {
|
2020-11-02 16:41:52 +00:00
|
|
|
*center += delta;
|
|
|
|
}
|
2021-01-10 10:43:01 +00:00
|
|
|
Shape::LineSegment { points, .. } => {
|
2020-11-02 16:41:52 +00:00
|
|
|
for p in points {
|
|
|
|
*p += delta;
|
|
|
|
}
|
|
|
|
}
|
2021-01-10 10:43:01 +00:00
|
|
|
Shape::Path { points, .. } => {
|
2020-11-02 16:41:52 +00:00
|
|
|
for p in points {
|
|
|
|
*p += delta;
|
|
|
|
}
|
|
|
|
}
|
2021-01-10 10:43:01 +00:00
|
|
|
Shape::Rect { rect, .. } => {
|
2020-11-02 16:41:52 +00:00
|
|
|
*rect = rect.translate(delta);
|
|
|
|
}
|
2021-01-10 10:43:01 +00:00
|
|
|
Shape::Text { pos, .. } => {
|
2020-11-02 16:41:52 +00:00
|
|
|
*pos += delta;
|
|
|
|
}
|
2021-01-25 20:23:24 +00:00
|
|
|
Shape::Mesh(mesh) => {
|
|
|
|
mesh.translate(delta);
|
2020-11-02 16:41:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-13 07:28:54 +00:00
|
|
|
}
|