Move egui/math into new crate emath

This commit is contained in:
Emil Ernerfeldt 2021-01-10 11:37:47 +01:00
parent aee1474b6e
commit a0b0f36d29
38 changed files with 187 additions and 97 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. * Center window titles.
* Tweak size and alignment of some emojis to match other text. * Tweak size and alignment of some emojis to match other text.
* Rename feature "serde" to "persistence".
### Fixed 🐛 ### Fixed 🐛

8
Cargo.lock generated
View file

@ -642,6 +642,7 @@ version = "0.7.0"
dependencies = [ dependencies = [
"ahash", "ahash",
"atomic_refcell", "atomic_refcell",
"emath",
"parking_lot", "parking_lot",
"rusttype", "rusttype",
"serde", "serde",
@ -703,6 +704,13 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "emath"
version = "0.7.0"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "epi" name = "epi"
version = "0.7.0" version = "0.7.0"

View file

@ -5,6 +5,7 @@ members = [
"egui_glium", "egui_glium",
"egui_web", "egui_web",
"egui", "egui",
"emath",
"epi", "epi",
] ]

View file

@ -19,6 +19,8 @@ include = [
[lib] [lib]
[dependencies] [dependencies]
emath = { path = "../emath" }
ahash = { version = "0.6", features = ["std"], default-features = false } ahash = { version = "0.6", features = ["std"], default-features = false }
atomic_refcell = { version = "0.1", optional = true } # Used instead of parking_lot when you are always using Egui in a single thread. About as fast as parking_lot. Panics on multi-threaded use of egui::Context. atomic_refcell = { version = "0.1", optional = true } # Used instead of parking_lot when you are always using Egui in a single thread. About as fast as parking_lot. Panics on multi-threaded use of egui::Context.
parking_lot = { version = "0.11", optional = true } # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios parking_lot = { version = "0.11", optional = true } # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios
@ -27,6 +29,7 @@ serde = { version = "1", features = ["derive", "rc"], optional = true }
[features] [features]
default = ["atomic_refcell", "default_fonts"] default = ["atomic_refcell", "default_fonts"]
persistence = ["serde", "emath/serde"]
# If set, egui will use `include_bytes!` to bundle some fonts. # If set, egui will use `include_bytes!` to bundle some fonts.
# If you plan on specifying your own fonts you may disable this feature. # If you plan on specifying your own fonts you may disable this feature.

View file

@ -8,7 +8,7 @@ use crate::*;
/// State that is persisted between frames /// State that is persisted between frames
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct State { pub(crate) struct State {
/// Last known pos /// Last known pos
pub pos: Pos2, pub pos: Pos2,

View file

@ -7,8 +7,8 @@ use crate::{
}; };
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub(crate) struct State { pub(crate) struct State {
open: bool, open: bool,
@ -114,7 +114,7 @@ pub(crate) fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) {
let rect = Rect::from_center_size(rect.center(), vec2(rect.width(), rect.height()) * 0.75); let rect = Rect::from_center_size(rect.center(), vec2(rect.width(), rect.height()) * 0.75);
let mut points = vec![rect.left_top(), rect.right_top(), rect.center_bottom()]; let mut points = vec![rect.left_top(), rect.right_top(), rect.center_bottom()];
use std::f32::consts::TAU; use std::f32::consts::TAU;
let rotation = Rot2::from_angle(remap(openness, 0.0..=1.0, -TAU / 4.0..=0.0)); let rotation = math::Rot2::from_angle(remap(openness, 0.0..=1.0, -TAU / 4.0..=0.0));
for p in &mut points { for p in &mut points {
*p = rect.center() + rotation * (*p - rect.center()); *p = rect.center() + rotation * (*p - rect.center());
} }

View file

@ -1,7 +1,7 @@
use crate::*; use crate::*;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct State { pub(crate) struct State {
/// This is the size that the user has picked by dragging the resize handles. /// This is the size that the user has picked by dragging the resize handles.
/// This may be smaller and/or larger than the actual size. /// This may be smaller and/or larger than the actual size.

View file

@ -1,8 +1,8 @@
use crate::*; use crate::*;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub(crate) struct State { pub(crate) struct State {
/// Positive offset means scrolling down/right /// Positive offset means scrolling down/right
offset: Vec2, offset: Vec2,
@ -10,7 +10,7 @@ pub(crate) struct State {
show_scroll: bool, show_scroll: bool,
/// Momentum, used for kinetic scrolling /// Momentum, used for kinetic scrolling
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub vel: Vec2, pub vel: Vec2,
/// Mouse offset relative to the top of the handle when started moving the handle. /// Mouse offset relative to the top of the handle when started moving the handle.
scroll_start_offset_from_top: Option<f32>, scroll_start_offset_from_top: Option<f32>,
@ -177,7 +177,7 @@ impl Prepared {
// We take the scroll target so only this ScrollArea will use it. // We take the scroll target so only this ScrollArea will use it.
let scroll_target = content_ui.ctx().frame_state().scroll_target.take(); let scroll_target = content_ui.ctx().frame_state().scroll_target.take();
if let Some((scroll_y, align)) = scroll_target { if let Some((scroll_y, align)) = scroll_target {
let center_factor = align.scroll_center_factor(); let center_factor = align.to_factor();
let top = content_ui.min_rect().top(); let top = content_ui.min_rect().top();
let visible_range = top..=top + content_ui.clip_rect().height(); let visible_range = top..=top + content_ui.clip_rect().height();

View file

@ -720,7 +720,7 @@ impl TitleBar {
self.title_label = self.title_label.text_color(style.fg_stroke.color); self.title_label = self.title_label.text_color(style.fg_stroke.color);
let full_top_rect = Rect::from_x_y_ranges(self.rect.x_range(), self.min_rect.y_range()); let full_top_rect = Rect::from_x_y_ranges(self.rect.x_range(), self.min_rect.y_range());
let text_pos = align::center_size_in_rect(self.title_galley.size, full_top_rect); let text_pos = math::align::center_size_in_rect(self.title_galley.size, full_top_rect);
let text_pos = text_pos.left_top() - 2.0 * Vec2::Y; // HACK: center on x-height of text (looks better) let text_pos = text_pos.left_top() - 2.0 * Vec2::Y; // HACK: center on x-height of text (looks better)
self.title_label self.title_label
.paint_galley(ui, text_pos, self.title_galley); .paint_galley(ui, text_pos, self.title_galley);

View file

@ -28,7 +28,7 @@ use std::hash::Hash;
/// Then there are widgets that need no identifiers at all, like labels, /// Then there are widgets that need no identifiers at all, like labels,
/// because they have no state nor are interacted with. /// because they have no state nor are interacted with.
#[derive(Clone, Copy, Hash, Eq, PartialEq)] #[derive(Clone, Copy, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Id(u64); pub struct Id(u64);
impl Id { impl Id {

View file

@ -1,6 +1,5 @@
//! uis for egui types. //! uis for egui types.
use crate::{ use crate::{
math::*,
paint::{self, PaintCmd, Texture, Triangles}, paint::{self, PaintCmd, Texture, Triangles},
*, *,
}; };

View file

@ -4,7 +4,7 @@ use crate::{math::Rect, paint::PaintCmd, Id, *};
/// Different layer categories /// Different layer categories
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub enum Order { pub enum Order {
/// Painted behind all floating windows /// Painted behind all floating windows
Background, Background,
@ -40,7 +40,7 @@ impl Order {
/// An identifier for a paint layer. /// An identifier for a paint layer.
/// Also acts as an identifier for [`Area`]:s. /// Also acts as an identifier for [`Area`]:s.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct LayerId { pub struct LayerId {
pub order: Order, pub order: Order,
pub id: Id, pub id: Id,

View file

@ -66,8 +66,8 @@ impl Region {
/// Layout direction, one of `LeftToRight`, `RightToLeft`, `TopDown`, `BottomUp`. /// Layout direction, one of `LeftToRight`, `RightToLeft`, `TopDown`, `BottomUp`.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "persistence", serde(rename_all = "snake_case"))]
pub enum Direction { pub enum Direction {
LeftToRight, LeftToRight,
RightToLeft, RightToLeft,
@ -95,7 +95,7 @@ impl Direction {
/// The layout of a [`Ui`][`crate::Ui`], e.g. "vertical & centered". /// The layout of a [`Ui`][`crate::Ui`], e.g. "vertical & centered".
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
// #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] // #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Layout { pub struct Layout {
/// Main axis direction /// Main axis direction
main_dir: Direction, main_dir: Direction,

View file

@ -86,7 +86,6 @@ mod input;
mod introspection; mod introspection;
mod layers; mod layers;
mod layout; mod layout;
pub mod math;
mod memory; mod memory;
pub mod menu; pub mod menu;
pub mod paint; pub mod paint;
@ -97,6 +96,8 @@ mod ui;
pub mod util; pub mod util;
pub mod widgets; pub mod widgets;
pub use emath as math;
pub use { pub use {
containers::*, containers::*,
context::{Context, CtxRef}, context::{Context, CtxRef},
@ -104,7 +105,7 @@ pub use {
input::*, input::*,
layers::*, layers::*,
layout::*, layout::*,
math::*, math::{clamp, lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rect, Vec2},
memory::Memory, memory::Memory,
paint::{ paint::{
color, Color32, FontDefinitions, FontFamily, PaintCmd, PaintJobs, Rgba, Stroke, TextStyle, color, Color32, FontDefinitions, FontFamily, PaintCmd, PaintJobs, Rgba, Stroke, TextStyle,

View file

@ -18,50 +18,50 @@ use crate::{
/// ///
/// If you want this to persist when closing your app you should serialize `Memory` and store it. /// If you want this to persist when closing your app you should serialize `Memory` and store it.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Memory { pub struct Memory {
pub(crate) options: Options, pub(crate) options: Options,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) interaction: Interaction, pub(crate) interaction: Interaction,
// states of various types of widgets // states of various types of widgets
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>, pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) menu_bar: HashMap<Id, menu::BarState>, pub(crate) menu_bar: HashMap<Id, menu::BarState>,
pub(crate) resize: HashMap<Id, resize::State>, pub(crate) resize: HashMap<Id, resize::State>,
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>, pub(crate) scroll_areas: HashMap<Id, scroll_area::State>,
pub(crate) text_edit: HashMap<Id, text_edit::State>, pub(crate) text_edit: HashMap<Id, text_edit::State>,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) window_interaction: Option<window::WindowInteraction>, pub(crate) window_interaction: Option<window::WindowInteraction>,
/// For temporary edit of e.g. a slider value. /// For temporary edit of e.g. a slider value.
/// Couples with [`Interaction::kb_focus_id`]. /// Couples with [`Interaction::kb_focus_id`].
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) temp_edit_string: Option<String>, pub(crate) temp_edit_string: Option<String>,
pub(crate) areas: Areas, pub(crate) areas: Areas,
/// Used by color picker /// Used by color picker
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) color_cache: Cache<Color32, Hsva>, pub(crate) color_cache: Cache<Color32, Hsva>,
/// Which popup-window is open (if any)? /// Which popup-window is open (if any)?
/// Could be a combo box, color picker, menu etc. /// Could be a combo box, color picker, menu etc.
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
popup: Option<Id>, popup: Option<Id>,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
everything_is_visible: bool, everything_is_visible: bool,
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub(crate) struct Options { pub(crate) struct Options {
/// The default style for new `Ui`:s. /// The default style for new `Ui`:s.
pub(crate) style: std::sync::Arc<Style>, pub(crate) style: std::sync::Arc<Style>,
@ -304,8 +304,8 @@ impl Memory {
/// Keeps track of `Area`s, which are free-floating `Ui`s. /// Keeps track of `Area`s, which are free-floating `Ui`s.
/// These `Area`s can be in any `Order`. /// These `Area`s can be in any `Order`.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Areas { pub struct Areas {
areas: HashMap<Id, area::State>, areas: HashMap<Id, area::State>,
/// Top is last /// Top is last

View file

@ -8,7 +8,7 @@ use crate::math::clamp;
/// Internally this uses 0-255 gamma space `sRGBA` color with premultiplied alpha. /// Internally this uses 0-255 gamma space `sRGBA` color with premultiplied alpha.
/// Alpha channel is in linear space. /// Alpha channel is in linear space.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Color32(pub(crate) [u8; 4]); pub struct Color32(pub(crate) [u8; 4]);
impl std::ops::Index<usize> for Color32 { impl std::ops::Index<usize> for Color32 {
@ -130,7 +130,7 @@ impl Color32 {
/// 0-1 linear space `RGBA` color with premultiplied alpha. /// 0-1 linear space `RGBA` color with premultiplied alpha.
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Rgba(pub(crate) [f32; 4]); pub struct Rgba(pub(crate) [f32; 4]);
impl std::ops::Index<usize> for Rgba { impl std::ops::Index<usize> for Rgba {

View file

@ -192,7 +192,7 @@ impl PaintCmd {
/// Describes the width and color of a line. /// Describes the width and color of a line.
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Stroke { pub struct Stroke {
pub width: f32, pub width: f32,
pub color: Color32, pub color: Color32,

View file

@ -14,8 +14,8 @@ use super::{
// TODO: rename // TODO: rename
/// One of a few categories of styles of text, e.g. body, button or heading. /// One of a few categories of styles of text, e.g. body, button or heading.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "persistence", serde(rename_all = "snake_case"))]
pub enum TextStyle { pub enum TextStyle {
/// Used when small text is needed. /// Used when small text is needed.
Small, Small,
@ -45,8 +45,8 @@ impl TextStyle {
/// Which style of font: [`Monospace`][`FontFamily::Monospace`] or [`Proportional`][`FontFamily::Proportional`]. /// Which style of font: [`Monospace`][`FontFamily::Monospace`] or [`Proportional`][`FontFamily::Proportional`].
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "persistence", serde(rename_all = "snake_case"))]
pub enum FontFamily { pub enum FontFamily {
/// A font where each character is the same width (`w` is the same width as `i`). /// A font where each character is the same width (`w` is the same width as `i`).
Monospace, Monospace,
@ -81,15 +81,15 @@ fn rusttype_font_from_font_data(name: &str, data: &FontData) -> rusttype::Font<'
/// ctx.set_fonts(fonts); /// ctx.set_fonts(fonts);
/// ``` /// ```
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct FontDefinitions { pub struct FontDefinitions {
/// List of font names and their definitions. /// List of font names and their definitions.
/// The definition must be the contents of either a `.ttf` or `.otf` font file. /// The definition must be the contents of either a `.ttf` or `.otf` font file.
/// ///
/// Egui has built-in-default for these, /// Egui has built-in-default for these,
/// but you can override them if you like. /// but you can override them if you like.
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub font_data: BTreeMap<String, FontData>, pub font_data: BTreeMap<String, FontData>,
/// Which fonts (names) to use for each [`FontFamily`]. /// Which fonts (names) to use for each [`FontFamily`].

View file

@ -20,7 +20,7 @@ use crate::math::{pos2, NumExt, Rect, Vec2};
/// Character cursor /// Character cursor
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct CCursor { pub struct CCursor {
/// Character offset (NOT byte offset!). /// Character offset (NOT byte offset!).
pub index: usize, pub index: usize,
@ -71,7 +71,7 @@ impl std::ops::Sub<usize> for CCursor {
/// Row Cursor /// Row Cursor
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct RCursor { pub struct RCursor {
/// 0 is first row, and so on. /// 0 is first row, and so on.
/// Note that a single paragraph can span multiple rows. /// Note that a single paragraph can span multiple rows.
@ -86,7 +86,7 @@ pub struct RCursor {
/// Paragraph Cursor /// Paragraph Cursor
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct PCursor { pub struct PCursor {
/// 0 is first paragraph, and so on. /// 0 is first paragraph, and so on.
/// Note that a single paragraph can span multiple rows. /// Note that a single paragraph can span multiple rows.
@ -118,7 +118,7 @@ impl PartialEq for PCursor {
/// pcursor/rcursor can also point to after the end of the paragraph/row. /// pcursor/rcursor can also point to after the end of the paragraph/row.
/// Does not implement `PartialEq` because you must think which cursor should be equivalent. /// Does not implement `PartialEq` because you must think which cursor should be equivalent.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Cursor { pub struct Cursor {
pub ccursor: CCursor, pub ccursor: CCursor,
pub rcursor: RCursor, pub rcursor: RCursor,

View file

@ -1,7 +1,7 @@
use super::*; use super::*;
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Shadow { pub struct Shadow {
// The shadow extends this much outside the rect. // The shadow extends this much outside the rect.
pub extrusion: f32, pub extrusion: f32,

View file

@ -446,8 +446,8 @@ use self::PathType::{Closed, Open};
/// Tessellation quality options /// Tessellation quality options
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct TessellationOptions { pub struct TessellationOptions {
/// Size of a pixel in points, e.g. 0.5 /// Size of a pixel in points, e.g. 0.5
pub aa_size: f32, pub aa_size: f32,

View file

@ -1,3 +1,5 @@
//! Egui theme (spacing, colors, etc).
#![allow(clippy::if_same_then_else)] #![allow(clippy::if_same_then_else)]
use crate::{ use crate::{
@ -9,8 +11,8 @@ use crate::{
/// Specifies the look and feel of a [`Ui`]. /// Specifies the look and feel of a [`Ui`].
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Style { pub struct Style {
/// Default `TextStyle` for normal text (i.e. for `Label` and `TextEdit`). /// Default `TextStyle` for normal text (i.e. for `Label` and `TextEdit`).
pub body_text_style: TextStyle, pub body_text_style: TextStyle,
@ -39,8 +41,8 @@ impl Style {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Spacing { pub struct Spacing {
/// Horizontal and vertical spacing between widgets /// Horizontal and vertical spacing between widgets
pub item_spacing: Vec2, pub item_spacing: Vec2,
@ -95,8 +97,8 @@ impl Spacing {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Interaction { pub struct Interaction {
/// Mouse must be the close to the side of a window to resize /// Mouse must be the close to the side of a window to resize
pub resize_grab_radius_side: f32, pub resize_grab_radius_side: f32,
@ -106,8 +108,8 @@ pub struct Interaction {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Visuals { pub struct Visuals {
/// Override default text color for all text. /// Override default text color for all text.
/// ///
@ -168,16 +170,16 @@ impl Visuals {
/// Selected text, selected elements etc /// Selected text, selected elements etc
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Selection { pub struct Selection {
pub bg_fill: Color32, pub bg_fill: Color32,
pub stroke: Stroke, pub stroke: Stroke,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Widgets { pub struct Widgets {
/// For an interactive widget that is being interacted with /// For an interactive widget that is being interacted with
pub active: WidgetVisuals, pub active: WidgetVisuals,
@ -207,7 +209,7 @@ impl Widgets {
/// bg = background, fg = foreground. /// bg = background, fg = foreground.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct WidgetVisuals { pub struct WidgetVisuals {
/// Background color of widget /// Background color of widget
pub bg_fill: Color32, pub bg_fill: Color32,

View file

@ -5,7 +5,7 @@ use crate::{lerp, math::Rect, Align, CtxRef, Id, LayerId, Ui};
/// What Egui emits each frame. /// What Egui emits each frame.
/// The backend should use this. /// The backend should use this.
#[derive(Clone, Default)] #[derive(Clone, Default)]
// #[cfg_attr(feature = "serde", derive(serde::Serialize))] // #[cfg_attr(feature = "persistence", derive(serde::Serialize))]
pub struct Output { pub struct Output {
/// Set the cursor to this icon. /// Set the cursor to this icon.
pub cursor_icon: CursorIcon, pub cursor_icon: CursorIcon,
@ -26,8 +26,8 @@ pub struct Output {
/// ///
/// Egui emits a `CursorIcond` in [`Output`] each frame as a request to the integration. /// Egui emits a `CursorIcond` in [`Output`] each frame as a request to the integration.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
// #[cfg_attr(feature = "serde", derive(serde::Serialize))] // #[cfg_attr(feature = "persistence", derive(serde::Serialize))]
// #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] // #[cfg_attr(feature = "persistence", serde(rename_all = "snake_case"))]
pub enum CursorIcon { pub enum CursorIcon {
Default, Default,
/// Pointing hand, used for e.g. web links /// Pointing hand, used for e.g. web links
@ -187,7 +187,7 @@ impl Response {
/// }); /// });
/// ``` /// ```
pub fn scroll_to_me(&self, align: Align) { pub fn scroll_to_me(&self, align: Align) {
let scroll_target = lerp(self.rect.y_range(), align.scroll_center_factor()); let scroll_target = lerp(self.rect.y_range(), align.to_factor());
self.ctx.frame_state().scroll_target = Some((scroll_target, align)); self.ctx.frame_state().scroll_target = Some((scroll_target, align));
} }
} }
@ -254,7 +254,7 @@ impl std::ops::BitOrAssign for Response {
/// What sort of interaction is a widget sensitive to? /// What sort of interaction is a widget sensitive to?
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
// #[cfg_attr(feature = "serde", derive(serde::Serialize))] // #[cfg_attr(feature = "persistence", derive(serde::Serialize))]
pub struct Sense { pub struct Sense {
/// buttons, sliders, windows ... /// buttons, sliders, windows ...
pub click: bool, pub click: bool,

View file

@ -1,7 +1,7 @@
use std::collections::VecDeque; use std::collections::VecDeque;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Settings { pub struct Settings {
/// Maximum number of undos. /// Maximum number of undos.
/// If your state is resource intensive, you should keep this low. /// If your state is resource intensive, you should keep this low.
@ -48,7 +48,7 @@ impl Default for Settings {
/// Rule 1) will make sure an undo point is not created until you _stop_ dragging that slider. /// Rule 1) will make sure an undo point is not created until you _stop_ dragging that slider.
/// Rule 2) will make sure that you will get some undo points even if you are constantly changing the state. /// Rule 2) will make sure that you will get some undo points even if you are constantly changing the state.
#[derive(Clone, Default)] #[derive(Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct Undoer<State> { pub struct Undoer<State> {
settings: Settings, settings: Settings,
@ -57,7 +57,7 @@ pub struct Undoer<State> {
/// The latest undo point may (often) be the current state. /// The latest undo point may (often) be the current state.
undos: VecDeque<State>, undos: VecDeque<State>,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
flux: Option<Flux<State>>, flux: Option<Flux<State>>,
} }

View file

@ -149,7 +149,7 @@ impl<'a> Widget for DragValue<'a> {
let auto_decimals = (aim_rad / speed.abs()).log10().ceil().at_least(0.0) as usize; let auto_decimals = (aim_rad / speed.abs()).log10().ceil().at_least(0.0) as usize;
let max_decimals = max_decimals.unwrap_or(auto_decimals + 2); let max_decimals = max_decimals.unwrap_or(auto_decimals + 2);
let auto_decimals = clamp(auto_decimals, min_decimals..=max_decimals); let auto_decimals = clamp(auto_decimals, min_decimals..=max_decimals);
let value_text = format_with_decimals_in_range(value, auto_decimals..=max_decimals); let value_text = math::format_with_decimals_in_range(value, auto_decimals..=max_decimals);
let kb_edit_id = ui.auto_id_with("edit"); let kb_edit_id = ui.auto_id_with("edit");
let is_kb_editing = ui.memory().has_kb_focus(kb_edit_id); let is_kb_editing = ui.memory().has_kb_focus(kb_edit_id);
@ -193,7 +193,7 @@ impl<'a> Widget for DragValue<'a> {
let delta_value = speed * delta_points; let delta_value = speed * delta_points;
if delta_value != 0.0 { if delta_value != 0.0 {
let new_value = value + delta_value as f64; let new_value = value + delta_value as f64;
let new_value = round_to_decimals(new_value, auto_decimals); let new_value = math::round_to_decimals(new_value, auto_decimals);
let new_value = clamp(new_value, range); let new_value = clamp(new_value, range);
set(&mut value_function, new_value); set(&mut value_function, new_value);
// TODO: To make use or `smart_aim` for `DragValue` we need to store some state somewhere, // TODO: To make use or `smart_aim` for `DragValue` we need to store some state somewhere,

View file

@ -2,7 +2,7 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use crate::{math::NumExt, paint::*, widgets::Label, *}; use crate::{paint::*, widgets::Label, *};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -209,7 +209,7 @@ impl<'a> Slider<'a> {
fn set_value(&mut self, mut value: f64) { fn set_value(&mut self, mut value: f64) {
if let Some(max_decimals) = self.max_decimals { if let Some(max_decimals) = self.max_decimals {
value = round_to_decimals(value, max_decimals); value = math::round_to_decimals(value, max_decimals);
} }
set(&mut self.get_set_value, value); set(&mut self.get_set_value, value);
} }
@ -366,13 +366,13 @@ impl<'a> Slider<'a> {
let auto_decimals = clamp(auto_decimals, min_decimals..=max_decimals); let auto_decimals = clamp(auto_decimals, min_decimals..=max_decimals);
if min_decimals == max_decimals { if min_decimals == max_decimals {
format_with_minimum_decimals(value, max_decimals) math::format_with_minimum_decimals(value, max_decimals)
} else if value == 0.0 { } else if value == 0.0 {
"0".to_owned() "0".to_owned()
} else if range == 0.0 { } else if range == 0.0 {
value.to_string() value.to_string()
} else { } else {
format_with_decimals_in_range(value, auto_decimals..=max_decimals) math::format_with_decimals_in_range(value, auto_decimals..=max_decimals)
} }
} }
} }

View file

@ -1,17 +1,17 @@
use crate::{paint::*, util::undoer::Undoer, *}; use crate::{paint::*, util::undoer::Undoer, *};
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub(crate) struct State { pub(crate) struct State {
cursorp: Option<CursorPair>, cursorp: Option<CursorPair>,
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
undoer: Undoer<(CCursorPair, String)>, undoer: Undoer<(CCursorPair, String)>,
} }
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
struct CursorPair { struct CursorPair {
/// When selecting with a mouse, this is where the mouse was released. /// When selecting with a mouse, this is where the mouse was released.
/// When moving with e.g. shift+arrows, this is what moves. /// When moving with e.g. shift+arrows, this is what moves.
@ -75,7 +75,7 @@ impl CursorPair {
} }
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
struct CCursorPair { struct CCursorPair {
/// When selecting with a mouse, this is where the mouse was released. /// When selecting with a mouse, this is where the mouse was released.
/// When moving with e.g. shift+arrows, this is what moves. /// When moving with e.g. shift+arrows, this is what moves.

View file

@ -144,8 +144,8 @@ impl FractalClock {
]; ];
let hand_rotors = [ let hand_rotors = [
hands[0].length * Rot2::from_angle(hand_rotations[0]), hands[0].length * math::Rot2::from_angle(hand_rotations[0]),
hands[1].length * Rot2::from_angle(hand_rotations[1]), hands[1].length * math::Rot2::from_angle(hand_rotations[1]),
]; ];
#[derive(Clone, Copy)] #[derive(Clone, Copy)]

View file

@ -35,7 +35,7 @@ default = []
http = ["ureq"] http = ["ureq"]
persistence = [ persistence = [
"directories-next", "directories-next",
"egui/serde", "egui/persistence",
"epi/serde_json", "epi/serde_json",
"epi/serde", "epi/serde",
"serde_json", "serde_json",

24
emath/Cargo.toml Normal file
View file

@ -0,0 +1,24 @@
[package]
name = "emath"
version = "0.7.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Minimal 2D math library for GUI work"
edition = "2018"
homepage = "https://github.com/emilk/egui"
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/emilk/egui"
categories = ["gui", "math"]
keywords = ["gui", "math"]
include = [
"**/*.rs",
"Cargo.toml",
]
[lib]
[dependencies]
serde = { version = "1", features = ["derive"], optional = true }
[features]
default = []

5
emath/README.md Normal file
View file

@ -0,0 +1,5 @@
# emath - Egui Math Library
A bare-bones 2D math library with types and functions useful for GUI building.
Made for [`egui`](https://github.com/emilk/egui/).

View file

@ -1,6 +1,6 @@
//! One- and two-dimensional alignment ([`Align::Center`], [`LEFT_TOP`] etc). //! One- and two-dimensional alignment ([`Align::Center`], [`Align2::LEFT_TOP`] etc).
use crate::math::*; use crate::*;
/// left/center/right or top/center/bottom alignment for e.g. anchors and `Layout`s. /// left/center/right or top/center/bottom alignment for e.g. anchors and `Layout`s.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -35,7 +35,8 @@ impl Align {
Self::Max Self::Max
} }
pub(crate) fn scroll_center_factor(&self) -> f32 { /// Convert `Min => 0.0`, `Center => 0.5` or `Max => 1.0`.
pub fn to_factor(&self) -> f32 {
match self { match self {
Self::Min => 0.0, Self::Min => 0.0,
Self::Center => 0.5, Self::Center => 0.5,
@ -52,6 +53,7 @@ impl Default for Align {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// Two-dimension alignment, e.g. [`Align2::LEFT_TOP`].
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
@ -79,7 +81,7 @@ impl Align2 {
/// Used e.g. to anchor a piece of text to a part of the rectangle. /// Used e.g. to anchor a piece of text to a part of the rectangle.
/// Give a position within the rect, specified by the aligns /// Give a position within the rect, specified by the aligns
pub(crate) fn anchor_rect(self, rect: Rect) -> Rect { pub fn anchor_rect(self, rect: Rect) -> Rect {
let x = match self.x() { let x = match self.x() {
Align::Min => rect.left(), Align::Min => rect.left(),
Align::Center => rect.left() - 0.5 * rect.width(), Align::Center => rect.left() - 0.5 * rect.width(),

View file

@ -1,8 +1,55 @@
//! Vectors, positions, rectangles etc. //! Vectors, positions, rectangles etc.
//! //!
//! Conventions (unless otherwise specified): //! Conventions (unless otherwise specified):
//!
//! * All angles are in radians //! * All angles are in radians
//! * All metrics are in points (logical pixels) //! * X+ is right and Y+ is down.
//! * (0,0) is left top.
//! * Dimension order is always `x y`
#![cfg_attr(not(debug_assertions), deny(warnings))] // Forbid warnings in release builds
#![forbid(unsafe_code)]
#![warn(
clippy::all,
clippy::await_holding_lock,
clippy::dbg_macro,
clippy::doc_markdown,
clippy::empty_enum,
clippy::enum_glob_use,
clippy::exit,
clippy::filter_map_next,
clippy::fn_params_excessive_bools,
clippy::if_let_mutex,
clippy::imprecise_flops,
clippy::inefficient_to_string,
clippy::linkedlist,
clippy::lossy_float_literal,
clippy::macro_use_imports,
clippy::match_on_vec_items,
clippy::match_wildcard_for_single_variants,
clippy::mem_forget,
clippy::mismatched_target_os,
clippy::missing_errors_doc,
clippy::missing_safety_doc,
clippy::needless_borrow,
clippy::needless_continue,
clippy::needless_pass_by_value,
clippy::option_option,
clippy::pub_enum_variant_names,
clippy::rest_pat_in_fully_bound_structs,
clippy::todo,
clippy::unimplemented,
clippy::unnested_or_patterns,
clippy::verbose_file_reads,
future_incompatible,
missing_crate_level_docs,
missing_doc_code_examples,
// missing_docs,
nonstandard_style,
rust_2018_idioms,
unused_doc_comments,
)]
#![allow(clippy::manual_range_contains)]
use std::ops::{Add, Div, Mul, RangeInclusive, Sub}; use std::ops::{Add, Div, Mul, RangeInclusive, Sub};
@ -130,14 +177,11 @@ pub fn round_to_decimals(value: f64, decimal_places: usize) -> f64 {
.unwrap_or(value) .unwrap_or(value)
} }
pub(crate) fn format_with_minimum_decimals(value: f64, decimals: usize) -> String { pub fn format_with_minimum_decimals(value: f64, decimals: usize) -> String {
format_with_decimals_in_range(value, decimals..=6) format_with_decimals_in_range(value, decimals..=6)
} }
pub(crate) fn format_with_decimals_in_range( pub fn format_with_decimals_in_range(value: f64, decimal_range: RangeInclusive<usize>) -> String {
value: f64,
decimal_range: RangeInclusive<usize>,
) -> String {
let min_decimals = *decimal_range.start(); let min_decimals = *decimal_range.start();
let max_decimals = *decimal_range.end(); let max_decimals = *decimal_range.end();
debug_assert!(min_decimals <= max_decimals); debug_assert!(min_decimals <= max_decimals);

View file

@ -1,6 +1,6 @@
use std::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign}; use std::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign};
use crate::math::*; use crate::*;
/// A position on screen. /// A position on screen.
/// ///

View file

@ -1,6 +1,6 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use crate::math::*; use crate::*;
/// A rectangular region of space. /// A rectangular region of space.
/// ///

View file

@ -1,6 +1,6 @@
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, RangeInclusive, Sub, SubAssign}; use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, RangeInclusive, Sub, SubAssign};
use crate::math::*; use crate::*;
/// A vector has a direction and length. /// A vector has a direction and length.
/// A [`Vec2`] is often used to represent a size. /// A [`Vec2`] is often used to represent a size.