diff --git a/block-lang/.github/workflows/mdbook.yml b/block-lang/.github/workflows/mdbook.yml new file mode 100644 index 0000000..f6d5fcb --- /dev/null +++ b/block-lang/.github/workflows/mdbook.yml @@ -0,0 +1,35 @@ +name: Deploy +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install mdbook + run: | + mkdir mdbook + curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + - name: Deploy GitHub Pages + run: | + # This assumes your book is in the root of your repository. + # Just add a `cd` here if you need to change to another directory. + mdbook build + git worktree add gh-pages + git config user.name "Deploy from CI" + git config user.email "" + cd gh-pages + # Delete the ref to avoid keeping history. + git update-ref -d refs/heads/gh-pages + rm -rf * + mv ../docs/ . + sed '/^\/docs\/$/d' .gitignore -i + git add . + git commit -m "Deploy $GITHUB_SHA to gh-pages" + git push --force --set-upstream origin gh-pages diff --git a/block-lang/.gitignore b/block-lang/.gitignore index 3dd439d..0d77843 100644 --- a/block-lang/.gitignore +++ b/block-lang/.gitignore @@ -13,3 +13,5 @@ # Added by cargo /target + +/docs/ diff --git a/block-lang/.vscode/settings.json b/block-lang/.vscode/settings.json index 431a2ff..9469be5 100644 --- a/block-lang/.vscode/settings.json +++ b/block-lang/.vscode/settings.json @@ -1,4 +1,7 @@ { - "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix", - "rust-analyzer.server.extraEnv": {"RUSTUP_TOOLCHAIN": "nightly"} + "rust-analyzer.server.extraEnv": { + "RUSTUP_TOOLCHAIN": "nightly" + }, + "nixEnvSelector.suggestion": false, + "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix" } \ No newline at end of file diff --git a/block-lang/Cargo.lock b/block-lang/Cargo.lock index 671b1bd..ff49b49 100644 --- a/block-lang/Cargo.lock +++ b/block-lang/Cargo.lock @@ -534,6 +534,7 @@ dependencies = [ [[package]] name = "bevy_mouse_tracking_plugin" version = "0.2.1" +source = "git+https://github.com/zyansheep/bevy-mouse-tracking#7b358c66feffc38b44d0d1b2d479057914888b04" dependencies = [ "bevy", ] @@ -541,6 +542,7 @@ dependencies = [ [[package]] name = "bevy_pancam" version = "0.3.0" +source = "git+https://github.com/zyansheep/bevy_pancam#e3e3f3e0824f21a6a38c6f59df74c635fcab3867" dependencies = [ "bevy", ] @@ -915,6 +917,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-lang" +version = "0.1.0" +dependencies = [ + "anyhow", + "ariadne", + "bevy", + "bevy_mouse_tracking_plugin", + "bevy_pancam", + "bevy_prototype_lyon", + "bytecheck", + "chumsky", + "hashdb", + "iyes_loopless", + "rkyv", + "rustyline", + "thiserror", +] + [[package]] name = "bs58" version = "0.4.0" @@ -3573,25 +3594,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" -[[package]] -name = "tmp-lang" -version = "0.1.0" -dependencies = [ - "anyhow", - "ariadne", - "bevy", - "bevy_mouse_tracking_plugin", - "bevy_pancam", - "bevy_prototype_lyon", - "bytecheck", - "chumsky", - "hashdb", - "iyes_loopless", - "rkyv", - "rustyline", - "thiserror", -] - [[package]] name = "toml" version = "0.5.9" diff --git a/block-lang/Cargo.toml b/block-lang/Cargo.toml index 41ca0d8..036ea6e 100644 --- a/block-lang/Cargo.toml +++ b/block-lang/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "tmp-lang" +name = "block-lang" version = "0.1.0" edition = "2021" -default-run = "tmp-lang" +default-run = "block-lang" [[bin]] name = "cli" @@ -29,16 +29,12 @@ anyhow = "1.0.58" bytecheck = "0.6.8" rkyv = { version = "0.7.39", features = ["validation"] } thiserror = "1.0.31" -bevy_pancam = { version = "*" } -bevy_mouse_tracking_plugin = { version = "*" } +bevy_pancam = { git = "https://github.com/zyansheep/bevy_pancam" } +bevy_mouse_tracking_plugin = { git = "https://github.com/zyansheep/bevy-mouse-tracking" } iyes_loopless = "0.6.1" rustyline = "10.0.0" bevy_prototype_lyon = "0.5.0" -[patch.crates-io] -bevy_mouse_tracking_plugin = { path = "vendor/bevy-mouse-tracking" } -bevy_pancam = { path = "vendor/bevy_pancam" } - # Stuff for Zyansheep, I use NixOS btw :) [package.metadata.nix] build = true @@ -51,6 +47,7 @@ nativeBuildInputs = [ "pkgconfig", "cmake", "mold", + "rust-analyzer", ] buildInputs = [ # Window and Input diff --git a/block-lang/README.md b/block-lang/README.md index 9916926..04189ed 100644 --- a/block-lang/README.md +++ b/block-lang/README.md @@ -1,6 +1,7 @@ -# tmp-lang +# Block Lang +*Functional Blocks* -Some unwritten language... +See the website: https://zyansheep.github.io/tmp-lang/ ## General idea @@ -10,12 +11,8 @@ Lambda calculus, but a visual drag-n-drop application, a bit like MIT Scratch Putting blocks together counts as assembly -The beautiful part, is animating the reductions +The beautiful part, is animating the reductions (Not finished yet) +Gui: `cargo run --release` -## How to run: - -```bash -cargo run --release -``` -(may need some graphics libraries, should be fine on windows) +Cli: `cargo run --package cli --release` diff --git a/block-lang/assets/Application.png b/block-lang/assets/AppState=Formed.png similarity index 100% rename from block-lang/assets/Application.png rename to block-lang/assets/AppState=Formed.png diff --git a/block-lang/assets/AppState=None.png b/block-lang/assets/AppState=None.png new file mode 100644 index 0000000..0c38c70 Binary files /dev/null and b/block-lang/assets/AppState=None.png differ diff --git a/block-lang/assets/AppState=Placed.png b/block-lang/assets/AppState=Placed.png new file mode 100644 index 0000000..3ed187e Binary files /dev/null and b/block-lang/assets/AppState=Placed.png differ diff --git a/block-lang/assets/AppState=Slotted.png b/block-lang/assets/AppState=Slotted.png new file mode 100644 index 0000000..583ed95 Binary files /dev/null and b/block-lang/assets/AppState=Slotted.png differ diff --git a/block-lang/assets/LamState=Connected.png b/block-lang/assets/LamState=Connected.png new file mode 100644 index 0000000..19ec5ca Binary files /dev/null and b/block-lang/assets/LamState=Connected.png differ diff --git a/block-lang/assets/LambdaEmpty.png b/block-lang/assets/LamState=Formed.png similarity index 100% rename from block-lang/assets/LambdaEmpty.png rename to block-lang/assets/LamState=Formed.png diff --git a/block-lang/assets/LambdaDot.png b/block-lang/assets/LamState=FormedConnected.png similarity index 100% rename from block-lang/assets/LambdaDot.png rename to block-lang/assets/LamState=FormedConnected.png diff --git a/block-lang/assets/LamState=None.png b/block-lang/assets/LamState=None.png new file mode 100644 index 0000000..b697154 Binary files /dev/null and b/block-lang/assets/LamState=None.png differ diff --git a/block-lang/assets/LamState=Placed.png b/block-lang/assets/LamState=Placed.png new file mode 100644 index 0000000..5f0a16b Binary files /dev/null and b/block-lang/assets/LamState=Placed.png differ diff --git a/block-lang/assets/Lambda.png b/block-lang/assets/Lambda.png deleted file mode 100644 index 0d983c5..0000000 Binary files a/block-lang/assets/Lambda.png and /dev/null differ diff --git a/block-lang/assets/VariableBound.png b/block-lang/assets/VarState=Connected.png similarity index 100% rename from block-lang/assets/VariableBound.png rename to block-lang/assets/VarState=Connected.png diff --git a/block-lang/assets/Variable.png b/block-lang/assets/VarState=None.png similarity index 100% rename from block-lang/assets/Variable.png rename to block-lang/assets/VarState=None.png diff --git a/block-lang/assets/VariableDot.png b/block-lang/assets/VarState=Placed.png similarity index 100% rename from block-lang/assets/VariableDot.png rename to block-lang/assets/VarState=Placed.png diff --git a/block-lang/book.toml b/block-lang/book.toml new file mode 100644 index 0000000..75ebbf5 --- /dev/null +++ b/block-lang/book.toml @@ -0,0 +1,9 @@ +[book] +authors = ["SimonFJ20", "Zyansheep"] +language = "en" +multilingual = false +src = "pages" +title = "Block Lang Documentation" + +[build] +build-dir = "docs" diff --git a/block-lang/pages/SUMMARY.md b/block-lang/pages/SUMMARY.md new file mode 100644 index 0000000..55af90c --- /dev/null +++ b/block-lang/pages/SUMMARY.md @@ -0,0 +1,5 @@ +# Summary + +- [Introduction](./introduction.md) +- [Build instructions](./build_instruction.md) +- [How 2 use](./how_to_use.md) diff --git a/block-lang/pages/build_instruction.md b/block-lang/pages/build_instruction.md new file mode 100644 index 0000000..580cd54 --- /dev/null +++ b/block-lang/pages/build_instruction.md @@ -0,0 +1,10 @@ + +# Build instruction + +`cargo run` + +Or + +`cargo run --features bevy/dynamic` to avoid having to recompile bevy. + + diff --git a/block-lang/pages/how_to_use.md b/block-lang/pages/how_to_use.md new file mode 100644 index 0000000..0780f46 --- /dev/null +++ b/block-lang/pages/how_to_use.md @@ -0,0 +1,23 @@ + +# How 2 use + +This program is for making lambda calculus expressions. + +Lambda consists of three elements +* Variables `x` is the green block +* Functions `λx.x` is the red block +* Applications `(λx.x) x` is the purple block + +## Step by step example + +Press `a` to make an application. The block will now follow your mouse cursor, until you click somewhere on the canvas. + +Now press `f` ~~to pay respect~~ to make a function, and place it on the left side of the application. + +Then press `v` to make a variable and place it on the top-half of the function. + +Hover on the varible, press `c`, and then click the function block. Now the variable is bound to the function. + +Make another function, place it on the right side of the application and insert a variable on the bottom. + +Now hover the mouse directly on the frame of the application and press `r`. That will read the expression, show it in text form on the sidebar. It will also reduce the expression, and show the reduced expression in text form, below the original expression. diff --git a/block-lang/pages/introduction.md b/block-lang/pages/introduction.md new file mode 100644 index 0000000..f370995 --- /dev/null +++ b/block-lang/pages/introduction.md @@ -0,0 +1,14 @@ + +# Block Lang + +MIT Scratch, but for lambda calculus, because we all have trouble finding the 'λ'-key, when trying to type it. + +## General idea + +Lambda calculus, but a visual drag-n-drop application, a bit like MIT Scratch + +## Theme: Beautiful assembly + +Putting blocks together counts as assembly + +The beautiful part, is animating the reductions (Not finished yet) diff --git a/block-lang/src/block.rs b/block-lang/src/block.rs index 4a84c89..3a0917f 100644 --- a/block-lang/src/block.rs +++ b/block-lang/src/block.rs @@ -4,40 +4,44 @@ use std::f32::consts::{FRAC_1_SQRT_2, FRAC_PI_2, PI}; use bevy::prelude::*; -use crate::{expr::{Binding, Expr}, mouseover::{HoverState, TopHover}, placing::Placing}; +use crate::{expr::{BindTree, Expr}, mouseover::{HoverState, TopHover}, placing::Placing}; -/* pub enum Binding { - None, - End, - Branch(Box, Box) -} +/// Associated `Entity` with a variable +pub type BindEntityTree = BindTree<'static, Entity>; -#[derive(Component, Default)] -pub enum Expr { - Function { bind: Binding, expr: Option }, - Application { func: Option, args: Option }, - #[default] - Variable, -} */ +#[derive(Clone, Copy, Debug)] +pub enum PartialForm { + Func, Args +} #[derive(Component, Clone, Debug)] pub enum WrappedExpr { - Variable { bound: Option }, + Variable { formed: (&'static Expr<'static>, &'static BindEntityTree) }, Lambda { - bind_entity: Option, expr_entity: Option, - formed: Option>, + is_bound: bool, + formed: Option<(&'static Expr<'static>, &'static BindEntityTree)>, }, Application { func_entity: Option, args_entity: Option, - formed: Option>, + partial_formed: Option<(&'static Expr<'static>, &'static BindEntityTree, PartialForm)>, + formed: Option<(&'static Expr<'static>, &'static BindEntityTree)>, } } impl WrappedExpr { - pub const APPLICATION: WrappedExpr = WrappedExpr::Application { func_entity: None, args_entity: None, formed: None }; - pub const LAMBDA: WrappedExpr = WrappedExpr::Lambda { bind_entity: None, expr_entity: None, formed: None }; - pub const VARIABLE: WrappedExpr = WrappedExpr::Variable { bound: None }; + pub const APPLICATION: WrappedExpr = WrappedExpr::Application { func_entity: None, args_entity: None, partial_formed: None, formed: None }; + pub const LAMBDA: WrappedExpr = WrappedExpr::Lambda { is_bound: false, expr_entity: None, formed: None }; + pub const VARIABLE: WrappedExpr = WrappedExpr::Variable { formed: (Expr::VAR, BindTree::NONE) }; + pub fn unform(&mut self) { + match self { + Self::Application { formed, partial_formed, .. } => { + *formed = None; *partial_formed = None; + } + Self::Lambda { formed, .. } => *formed = None, + _ => {}, + } + } } impl Default for WrappedExpr { fn default() -> Self { Self::VARIABLE } } @@ -80,12 +84,16 @@ impl ObjectData { } pub fn gen_texture(expr: &WrappedExpr, asset_server: &AssetServer) -> Handle { match expr { - WrappedExpr::Variable { .. } => asset_server.load("VariableDot.png"), - WrappedExpr::Variable { bound: Some(_) } => asset_server.load("VariableBound.png"), - WrappedExpr::Lambda { expr_entity: None, formed: None, .. } => asset_server.load("Lambda.png"), - WrappedExpr::Lambda { expr_entity: Some(_), formed: None, .. } => asset_server.load("LambdaEmpty.png"), - WrappedExpr::Lambda { formed: Some(_), .. } => asset_server.load("LambdaDot.png"), - WrappedExpr::Application { .. } => asset_server.load("Application.png"), + WrappedExpr::Variable { formed: (_, BindEntityTree::End(_)) } => asset_server.load("VarState=Connected.png"), + WrappedExpr::Variable { .. } => asset_server.load("VarState=Placed.png"), + WrappedExpr::Lambda { formed: Some(_), is_bound: true, .. } => asset_server.load("LamState=FormedConnected.png"), + WrappedExpr::Lambda { formed: Some(_), .. } => asset_server.load("LamState=Formed.png"), + WrappedExpr::Lambda { expr_entity: Some(_), is_bound: true, .. } => asset_server.load("LamState=Connected.png"), + WrappedExpr::Lambda { expr_entity: Some(_), .. } => asset_server.load("LamState=Placed.png"), + WrappedExpr::Lambda { expr_entity: None, .. } => asset_server.load("LamState=None.png"), + WrappedExpr::Application { formed: Some(_), .. } => asset_server.load("AppState=Formed.png"), + WrappedExpr::Application { func_entity: Some(_), args_entity: Some(_), .. } => asset_server.load("AppState=Slotted.png"), + WrappedExpr::Application { .. } => asset_server.load("AppState=Placed.png"), } } pub fn gen_transform(&self, z_loc: f32) -> Transform { diff --git a/block-lang/src/block_to_expr.rs b/block-lang/src/block_to_expr.rs index 5d2762d..3f3063c 100644 --- a/block-lang/src/block_to_expr.rs +++ b/block-lang/src/block_to_expr.rs @@ -1,11 +1,10 @@ use crate::{expr::Expr, block::WrappedExpr}; - -pub fn block_to_expr(wrapped: &WrappedExpr) -> Result<&Expr<'static>, String> { +pub fn block_to_expr(wrapped: &WrappedExpr) -> Result<&Expr<'static>, Box> { match wrapped { - WrappedExpr::Variable { bound } => Ok(&Expr::Variable), - WrappedExpr::Lambda { bind_entity, expr_entity, formed: Some(expr) } => Ok(expr), - WrappedExpr::Application { func_entity, args_entity, formed: Some(expr) } => Ok(expr), - _ => Err("malformed expression".into()), + WrappedExpr::Variable { formed: (expr, _) } + | WrappedExpr::Lambda { formed: Some((expr, _)), .. } + | WrappedExpr::Application { formed: Some((expr, _)), .. } => Ok(expr), + _ => Err(format!("unformed expression for {wrapped:?}"))?, } } diff --git a/block-lang/src/cli.rs b/block-lang/src/cli.rs index 7bf0dba..cce5139 100644 --- a/block-lang/src/cli.rs +++ b/block-lang/src/cli.rs @@ -20,7 +20,7 @@ fn cli_editor() { use parse::Command; use rustyline::Editor; - println!("tmp-lang cli editor!"); + println!("block-lang cli editor!"); let mut editor = Editor::<()>::new().unwrap(); if editor.load_history(".editor_history").is_err() {} diff --git a/block-lang/src/expr/bind.rs b/block-lang/src/expr/bind.rs index e437f70..8d6a59f 100644 --- a/block-lang/src/expr/bind.rs +++ b/block-lang/src/expr/bind.rs @@ -17,12 +17,8 @@ pub enum Binding<'a> { None, End, Branch( - #[with(HashType)] - #[omit_bounds] - &'a Binding<'a>, - #[with(HashType)] - #[omit_bounds] - &'a Binding<'a>, + #[with(HashType)] #[omit_bounds] &'a Binding<'a>, + #[with(HashType)] #[omit_bounds] &'a Binding<'a>, ), } impl<'a> Binding<'a> { @@ -72,7 +68,7 @@ pub enum BindTreeError { InvalidSplit, } -// Associates a type with +// Associates a value with various parts of an `Expr` #[derive(Clone, Hash, PartialEq, Debug)] pub enum BindTree<'a, T: TypeStorable> { None, @@ -89,58 +85,39 @@ impl<'a, 'e, T: TypeStorable + 'e> BindTree<'a, T> { }) } fn branch_new(left: &'a Self, right: &'a Self) -> Self { - if let (BindTree::None, BindTree::None) = (left, right) { - BindTree::None - } else { - BindTree::Branch(left, right) - } + if let (BindTree::None, BindTree::None) = (left, right) { BindTree::None } + else { BindTree::Branch(left, right) } } - pub fn branch(left: &'a Self, right: &'a Self, binds: &'a impl TypeStore<'a>) -> &'a Self { - binds.add(Self::branch_new(left, right)) + pub fn branch(left: &'a Self, right: &'a Self, trees: &'a impl TypeStore<'a>) -> &'a Self { + trees.add(Self::branch_new(left, right)) } - pub fn left(&'a self, binds: &'a impl TypeStore<'a>) -> &'a Self { - Self::branch(self, BindTree::NONE, binds) + pub fn left(&'a self, trees: &'a impl TypeStore<'a>) -> &'a Self { + Self::branch(self, BindTree::NONE, trees) } - pub fn right(&'a self, binds: &'a impl TypeStore<'a>) -> &'a Self { - Self::branch(BindTree::NONE, self, binds) + pub fn right(&'a self, trees: &'a impl TypeStore<'a>) -> &'a Self { + Self::branch(BindTree::NONE, self, trees) } - pub fn end(val: T, binds: &'a impl TypeStore<'a>) -> &'a Self { - binds.add(BindTree::End(val)) - } -} - -impl<'a, T: fmt::Display + TypeStorable> fmt::Display for BindTree<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - BindTree::Branch(BindTree::None, right) => write!(f, ">{}", right)?, - BindTree::Branch(left, BindTree::None) => write!(f, "<{}", left)?, - BindTree::Branch(left, right) => write!(f, "({},{})", left, right)?, - BindTree::End(val) => write!(f, "{}", val)?, - BindTree::None => write!(f, "N")?, - } - Ok(()) + pub fn end(val: T, trees: &'a impl TypeStore<'a>) -> &'a Self { + trees.add(BindTree::End(val)) } -} - -/// BindTree that can represent multiple lambda abstractions at once -pub type BindSubTree<'a> = BindTree<'a, usize>; -impl<'a, 'e> BindSubTree<'a> { /// Add PointerTree to ReplaceTree at certain abstraction level - pub fn push_binding(self: &mut &'a Self, binds: &'a impl TypeStore<'a>, level: usize, pointer: &'e Binding<'e>) -> Result<(), BindTreeError> { - *self = match (*self, pointer) { - // If ReplaceTree is None, fill in pointer + pub fn push_binding(self: &mut &'a Self, trees: &'a impl TypeStore<'a>, end: T, binds: &'e Binding<'e>) -> Result<(), BindTreeError> + where T: Clone, + { + *self = match (*self, binds) { + // If ReplaceTree is None, fill in binds (tree, Binding::None) => tree, - (BindTree::None, Binding::End) => Self::end(level, binds), + (BindTree::None, Binding::End) => Self::end(end, trees), (BindTree::None, Binding::Branch(l, r)) => { let (mut left, mut right) = (Self::NONE, Self::NONE); - left.push_binding(binds, level, l)?; - right.push_binding(binds, level, r)?; - Self::branch(left, right, binds) + left.push_binding(trees, end.clone(), l)?; + right.push_binding(trees, end, r)?; + Self::branch(left, right, trees) } (BindTree::Branch(mut left, mut right), Binding::Branch(l, r)) => { - left.push_binding(binds, level, l)?; - right.push_binding(binds, level, r)?; - Self::branch(left, right, binds) + left.push_binding(trees, end.clone(), l)?; + right.push_binding(trees, end, r)?; + Self::branch(left, right, trees) } (BindTree::End(_), _) => return Err(BindTreeError::AlreadyBound), (_, Binding::End) => return Err(BindTreeError::InvalidBindLocation), @@ -148,15 +125,17 @@ impl<'a, 'e> BindSubTree<'a> { Ok(()) } /// Constructs PointerTree from ReplaceTree at certain abstraction level - pub fn pop_binding(self: &mut &'a Self, binds: &'a impl TypeStore<'a>, level: usize, ptrs: &'e impl TypeStore<'e>) -> Result<&'e Binding<'e>, BindTreeError> { + pub fn pop_binding(self: &mut &'a Self, trees: &'a impl TypeStore<'a>, end: &T, binds: &'e impl TypeStore<'e>) -> Result<&'e Binding<'e>, BindTreeError> + where T: PartialEq, + { Ok(match self { BindTree::Branch(mut l, mut r) => { - let left = l.pop_binding(binds, level, ptrs)?; - let right = r.pop_binding(binds, level, ptrs)?; - *self = Self::branch(l, r, binds); - Binding::branch_reduce(left, right, ptrs) + let left = l.pop_binding(trees, end, binds)?; + let right = r.pop_binding(trees, end, binds)?; + *self = Self::branch(l, r, trees); + Binding::branch_reduce(left, right, binds) } - BindTree::End(count) if level == *count => { + BindTree::End(count) if *end == *count => { *self = Self::NONE; Binding::END } @@ -165,33 +144,22 @@ impl<'a, 'e> BindSubTree<'a> { } } -/// BindTree that can associate an Expr with bound variables -pub type BindTypeTree<'a, 'e> = BindTree<'a, &'e Expr<'e>>; -impl<'a, 'e> BindTypeTree<'a, 'e> { - // Push Binding and type Expr onto BindTypeTree - pub fn push_binding(self: &mut &'a Self, bind: &'e Binding<'e>, bind_type: &'e Expr<'e>, binds: &'a impl TypeStore<'a>) -> Result<(), BindTreeError> { - *self = match (*self, bind) { - // If ReplaceTree is None, fill in pointer - (tree, Binding::None) => tree, - (BindTree::None, Binding::End) => Self::end(bind_type, binds), - (BindTree::None, Binding::Branch(l, r)) => { - let (mut left, mut right) = (Self::NONE, Self::NONE); - left.push_binding(l, bind_type, binds)?; - right.push_binding(r, bind_type, binds)?; - Self::branch(left, right, binds) - } - (BindTree::Branch(mut left, mut right), Binding::Branch(l, r)) => { - left.push_binding(l, bind_type, binds)?; - right.push_binding(r, bind_type, binds)?; - Self::branch(left, right, binds) - } - (BindTree::End(_), _) => return Err(BindTreeError::AlreadyBound), - (_, Binding::End) => return Err(BindTreeError::InvalidBindLocation), - }; +impl<'a, T: fmt::Display + TypeStorable> fmt::Display for BindTree<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BindTree::Branch(BindTree::None, right) => write!(f, ">{}", right)?, + BindTree::Branch(left, BindTree::None) => write!(f, "<{}", left)?, + BindTree::Branch(left, right) => write!(f, "({},{})", left, right)?, + BindTree::End(val) => write!(f, "{}", val)?, + BindTree::None => write!(f, "N")?, + } Ok(()) } } +/// BindTree that can represent multiple lambda abstractions at once +pub type BindSubTree<'a> = BindTree<'a, usize>; + // Index = 0 means no lambda history (tree should BindTree::None) // Index > 0 means there is history #[derive(PartialEq, Debug, Clone)] @@ -200,8 +168,9 @@ pub struct BindIndex<'a> { pub tree: &'a BindSubTree<'a>, } -impl<'a, 'e> BindIndex<'a> { +impl<'a> BindIndex<'a> { pub const DEFAULT: BindIndex<'a> = BindIndex::new(0, BindTree::NONE); + /// Create new BindIndex pub const fn new(index: usize, tree: &'a BindSubTree<'a>) -> Self { Self { index, tree } } @@ -211,43 +180,41 @@ impl<'a, 'e> BindIndex<'a> { Ok((BindIndex::new(self.index, left), BindIndex::new(self.index, right))) } // Join two BindIndexs of same index - pub fn join(left: BindIndex<'a>, right: BindIndex<'a>, binds: &'a impl TypeStore<'a>) -> BindIndex<'a> { + pub fn join(left: BindIndex<'a>, right: BindIndex<'a>, trees: &'a impl TypeStore<'a>) -> BindIndex<'a> { debug_assert_eq!(left.index, right.index); - BindIndex::new(left.index, BindTree::branch(left.tree, right.tree, binds)) + BindIndex::new(left.index, BindTree::branch(left.tree, right.tree, trees)) } /// Push Binding onto BindIndex - pub fn push_binding(&mut self, pointer: &'e Binding<'e>, binds: &'a impl TypeStore<'a>) -> Result<(), BindTreeError> { + pub fn push_binding<'e>(&mut self, binds: &'e Binding<'e>, trees: &'a impl TypeStore<'a>) -> Result<(), BindTreeError> { let BindIndex { index, tree } = self; *index += 1; - tree.push_binding(binds, *index, pointer)?; + tree.push_binding(trees, *index, binds)?; Ok(()) } /// Pop Binding from BindIndex - pub fn pop_binding(&mut self, binds: &'a impl TypeStore<'a>, ptrs: &'e impl TypeStore<'e>) -> Result<&'e Binding<'e>, LambdaError> { - let BindIndex { index, tree } = self; + pub fn pop_binding<'e>(&mut self, trees: &'a impl TypeStore<'a>, binds: &'e impl TypeStore<'e>) -> Result<&'e Binding<'e>, LambdaError> { + let BindIndex::<'a> { index, tree } = self; if *index == 0 { return Err(LambdaError::BindingLevelMismatch); } - let ret = tree.pop_binding(binds, *index, ptrs)?; + let ret = tree.pop_binding(trees, index, binds)?; *index -= 1; Ok(ret) } /// Build BindIndex from nested Lambda expressions - pub fn push_lambda(&mut self, expr: &'a Expr<'a>, binds: &'a impl TypeStore<'a>) -> Result<&'a Expr<'a>, BindTreeError> { - Ok(if let Expr::Lambda { bind: pointer_tree, expr } = expr { - let pushed_expr = self.push_lambda(expr, binds)?; - self.push_binding(pointer_tree, binds)?; + pub fn push_lambda<'e>(&mut self, expr: &'e Expr<'e>, trees: &'a impl TypeStore<'a>) -> Result<&'e Expr<'e>, BindTreeError> { + Ok(if let Expr::Lambda { bind: binds_tree, expr } = expr { + let pushed_expr = self.push_lambda(expr, trees)?; + self.push_binding(binds_tree, trees)?; pushed_expr - } else { - &expr - }) + } else { &expr }) } /// Creates nested Lambda expressions from BindIndex #[allow(dead_code)] - pub fn pop_lambda(&mut self, expr: &'e Expr<'e>, binds: &'a impl TypeStore<'a>, exprs: &'e impl TypeStore<'e>) -> Result<&'e Expr<'e>, LambdaError> { - let pointer_tree = self.pop_binding(binds, exprs)?; - let popped_expr = if self.index == 0 { &expr } else { self.pop_lambda(expr, binds, exprs)? }; - Ok(Expr::lambda(pointer_tree, popped_expr, exprs)) + pub fn pop_lambda<'e>(&mut self, expr: &'e Expr<'e>, trees: &'a impl TypeStore<'a>, exprs: &'e impl TypeStore<'e>) -> Result<&'e Expr<'e>, LambdaError> { + let binds_tree = self.pop_binding(trees, exprs)?; + let popped_expr = if self.index == 0 { &expr } else { self.pop_lambda(expr, trees, exprs)? }; + Ok(Expr::lambda(binds_tree, popped_expr, exprs)) } } @@ -256,55 +223,3 @@ impl<'a> fmt::Display for BindIndex<'a> { write!(f, "{}/{}", self.tree, self.index) } } - -#[test] -fn test_replace_tree() { - use crate::name::NamespaceMut; - use hashdb::LinkArena; - - use Binding as B; - let binds = &LinkArena::new(); - let exprs = &LinkArena::new(); - let mut r = BindIndex::DEFAULT; - println!("start: [{}]", r); - - let lambda = crate::parse::parse("[x y z w] x (y z) w", &mut NamespaceMut::new(), exprs).unwrap(); - println!("lambda: {}", lambda); - let expr = r.push_lambda(&lambda, binds).unwrap(); - println!("after push: {} : {}", expr, r); - let lambda_2 = r.pop_lambda(expr, binds, exprs).unwrap(); - println!("after pop: {}", lambda_2); - assert_eq!(lambda, lambda_2); - - // Test Split & Join - let r = BindIndex::DEFAULT; - test_split(binds, r).unwrap(); - - let _pts = &LinkArena::new(); - let mut r = BindIndex::DEFAULT; - r.push_binding(&B::END, binds).unwrap(); - test_split(binds, r).unwrap_err(); - - // let r = &mut ReduceArena(, 0); - // test_split(r, db).unwrap(); // This will error - let mut r = BindIndex::DEFAULT; - r.push_binding(&B::left(B::END, exprs), binds).unwrap(); - test_split(binds, r).unwrap(); - - let mut r = BindIndex::DEFAULT; - r.push_binding(&B::branch(B::right(B::END, exprs), B::END, exprs), binds).unwrap(); - test_split(binds, r).unwrap(); -} -#[allow(dead_code)] -fn test_split<'a>(binds: &'a impl TypeStore<'a>, r: BindIndex<'a>) -> Result<(), BindTreeError> { - print!("split [{}] ", r); - let (left, right) = r.split().map_err(|e| { - println!("split err: {}", e); - e - })?; - print!(" - ([{}] [{}])", left, right); - let r_after = BindIndex::join(left, right, binds); - println!(" = [{}]", r); - assert_eq!(r, r_after); - Ok(()) -} diff --git a/block-lang/src/main.rs b/block-lang/src/main.rs index a165bfc..358f4c4 100644 --- a/block-lang/src/main.rs +++ b/block-lang/src/main.rs @@ -3,10 +3,10 @@ use bevy::prelude::*; use bevy_mouse_tracking_plugin::{MainCamera, MousePosPlugin, MousePosWorld}; use bevy_pancam::{PanCam, PanCamPlugin}; use bevy_prototype_lyon::prelude::*; -use block::{ObjectData, Orientation, WrappedExpr}; +use block::{BindEntityTree, ObjectData, Orientation, PartialForm, WrappedExpr}; use block_to_expr::block_to_expr; -use expr::{Binding, Expr}; -use hashdb::LinkArena; +use expr::{Binding, Expr, BindTree}; +use hashdb::{LinkArena, TypeStore}; use mouseover::{BottomHover, HoverState, TopHover}; use placing::place_expr; @@ -43,14 +43,15 @@ fn main() { .add_system_set(SystemSet::on_update(AppState::PlacingObject).with_system(placing::placing_system)) - .add_system_set(SystemSet::on_update(AppState::WiringObject).with_system(wiring_system).with_system(connecting_system)) + .add_system_set(SystemSet::on_update(AppState::WiringObject).with_system(wiring_system)) + .add_system_set(SystemSet::on_exit(AppState::WiringObject).with_system(connecting_system)) .add_system(block::data_update).add_system(block::expr_update).add_system(block::hover_update) .add_system(mouseover::mouseover_system) .add_system(state_change_detect) .add_system(ui::button_system) .add_system(bevy::window::exit_on_window_close_system) - .add_system(exprs_forming_system) + .add_system(exprs_forming_system).add_system(reform_system) .init_resource::() .run(); } @@ -153,13 +154,6 @@ fn block_input( } } - -// Component that travels from Variable to Lambda and once it gets there, it changes the state. -#[derive(Component)] -struct WireFinder { - bind: Binding<'static>, -} - #[derive(Debug, Clone, Copy)] enum PortType { Lambda, @@ -184,34 +178,57 @@ struct ActiveWire; #[derive(Component, Debug, Clone)] struct FormConnection(Entity, PortType); +#[derive(Component, Debug, Clone)] +struct TriggerReform; + fn connecting_system( mut commands: Commands, - mut objects: Query<(Entity, &mut ObjectData, &mut WrappedExpr, &FormConnection)> + mut objects: Query<(Entity, &mut ObjectData, &mut WrappedExpr, &FormConnection), Added> ) { for (entity, data, mut expr, conn) in objects.iter_mut() { match (&mut *expr, conn.1) { - (WrappedExpr::Variable { bound }, PortType::Variable) => *bound = Some(conn.0), - (WrappedExpr::Lambda { bind_entity, .. }, PortType::Lambda) => *bind_entity = Some(conn.0), + (WrappedExpr::Variable { formed: (_, bind_tree) }, PortType::Variable) => { + *bind_tree = BindTree::end(conn.0, &LeakStore); + commands.entity(entity).insert(TriggerReform); + debug!("Set bind {entity:?} : {:?} to {:?}", *expr, conn.0); + }, + (WrappedExpr::Lambda { is_bound, .. }, PortType::Lambda) => { + *is_bound = true; + commands.entity(entity).insert(TriggerReform); + debug!("Set bind {entity:?} : {:?} to {:?}", *expr, conn.0); + }, _ => { error!("Invalid connection") } } commands.entity(entity).remove::(); } } +fn reform_system(mut commands: Commands, mut objects: Query<(Entity, &ObjectData, &mut WrappedExpr), Added>) { + for (entity, data, mut expr) in objects.iter_mut() { + debug!("Unformed: {:?}", entity); + expr.unform(); // Remove formed fields + commands.entity(entity).remove::().remove::(); + if let WrappedExpr::Variable { .. } = *expr { commands.entity(entity).insert(Formed); } + if let Some(parent) = data.parent { + commands.entity(parent).insert(TriggerReform).remove::(); + } + } +} // System for wiring things up fn wiring_system( mut commands: Commands, mut app_state: ResMut>, - mut state: ResMut, - mut top_hover: Query<(Entity, &ObjectData, &WrappedExpr, &HoverState), With>, + // mut state: ResMut, + mut top_hover: Query<(Entity, &ObjectData, &WrappedExpr), With>, mut mouse: ResMut>, mut keyboard: ResMut>, mut wire: Query<(Entity, &mut Wire, Option<&mut Path>), With>, mouse_pos: Res, ) { - if let Ok((entity, mut wire, mut path)) = wire.get_single_mut() { + // Start wiring if there is an active wire + if let Ok((wire_entity, mut wire, mut path)) = wire.get_single_mut() { wire.end = Vec2::new(mouse_pos.x, mouse_pos.y); - if let Ok((entity, data, expr, state)) = top_hover.get_single_mut() { + if let Ok((entity, data, expr)) = top_hover.get_single_mut() { if mouse.clear_just_pressed(MouseButton::Left) { match (expr, wire.port) { (WrappedExpr::Variable { .. }, PortType::Lambda) | @@ -220,20 +237,22 @@ fn wiring_system( commands.entity(entity).insert(FormConnection(wire.from, wire.port.swap())); wire.end = data.location; app_state.pop().unwrap(); + commands.entity(wire_entity).remove::(); + debug!("Wired {:?} to {:?}:({expr:?})", wire.from, entity) } _ => {}, } } } if keyboard.clear_just_pressed(KeyCode::Escape) { - commands.entity(entity).despawn(); + commands.entity(wire_entity).despawn(); app_state.pop().unwrap(); } // Build line let mut path_builder = PathBuilder::new(); - path_builder.move_to(wire.start); - path_builder.line_to(wire.end); + path_builder.move_to(Vec2::ZERO); + path_builder.line_to(wire.end - wire.start); let line = path_builder.build(); if let Some(path) = &mut path { **path = line; @@ -241,43 +260,95 @@ fn wiring_system( commands.spawn().insert_bundle(GeometryBuilder::build_as( &line, DrawMode::Stroke(StrokeMode::new(Color::BLACK, 10.0)), - Transform::from_xyz(0.0, 0.0, 1000.0), + Transform::from_translation(wire.start.extend(1000.0)), )); } } } +// *I like to leak it leak it, I like to leak it leak it, I like to leak it leak it, I like to, LEAK IT* +struct LeakStore; +impl TypeStore<'static> for LeakStore { + fn add(&self, val: T) -> &'static T { + Box::leak(Box::new(val)) + } +} +#[derive(Component)] +struct Formed; + fn exprs_forming_system( - mut paramset: ParamSet<(Query<(&ObjectData, &mut WrappedExpr, Entity)>, Query<(&ObjectData, &mut WrappedExpr, Entity)>)> + mut commands: Commands, + formed: Query<(Entity, &ObjectData, &mut WrappedExpr), (With, With)>, + mut unformed: Query<(Entity, &mut WrappedExpr), Without>, ) { - let mut exprs: Vec<(Expr, Entity)> = Vec::new(); - for (data, w_expr, entity) in paramset.p0().iter() { - match w_expr { - WrappedExpr::Variable { bound: Some(_) } => { - exprs.push((Expr::Variable, entity)); - } - WrappedExpr::Application {formed: Some(expr), ..} - | WrappedExpr::Lambda {formed: Some(expr), ..} => { - exprs.push((expr.clone(), entity)); - } - _ => {} - } - } - for (data, mut w_expr, entity) in paramset.p1().iter_mut() { - let mut expr_option: Option<&Expr> = None; - for (e_expr, e_entity) in exprs.iter_mut() { - if entity == *e_entity { - expr_option = Some(e_expr); - } - } - if let Some(expr) = expr_option { - match &mut (*w_expr) { - WrappedExpr::Application { formed, .. } - | WrappedExpr::Lambda { formed, .. } => { - *formed = Some(expr.clone()); + for (f_entity, data, f_wexpr) in formed.iter() { + if let WrappedExpr::Variable { formed: (f_expr, mut f_bind_tree) } + | WrappedExpr::Lambda { formed: Some((f_expr, mut f_bind_tree)), .. } + | WrappedExpr::Application { formed: Some((f_expr, mut f_bind_tree)), .. } = *f_wexpr { + if let Some(Ok((entity, mut wexpr))) = data.parent.map(|parent|unformed.get_mut(parent)) { + match &mut *wexpr { + WrappedExpr::Lambda { + expr_entity: Some(expr_entity), + formed, .. + } if *expr_entity == f_entity => { + let bind = f_bind_tree.pop_binding(&LeakStore, &entity, &LeakStore).unwrap(); + debug!("Using bind: {:?}", bind); + *formed = Some((LeakStore.add(Expr::Lambda { bind, expr: f_expr }), f_bind_tree)); + commands.entity(entity).insert(Formed); + info!("Entity {:?} formed expression: {:?}", entity, *formed); + } + WrappedExpr::Application { + func_entity: Some(func_entity), + args_entity: Some(args_entity), + partial_formed: Some((partial_expr, partial_tree, partial_form)), + formed, + } if formed.is_none() => match partial_form { + PartialForm::Func if *args_entity == f_entity => { + *formed = Some(( + Expr::app(*partial_expr, f_expr, &LeakStore), + BindEntityTree::branch(*partial_tree, f_bind_tree, &LeakStore) + )); + commands.entity(entity).insert(Formed); + info!("Entity {:?} formed expression: {:?}", entity, *formed); + } + PartialForm::Args if *func_entity == f_entity => { + *formed = Some(( + Expr::app(f_expr, *partial_expr, &LeakStore), + BindEntityTree::branch(f_bind_tree, *partial_tree, &LeakStore) + )); + commands.entity(entity).insert(Formed); + info!("Entity {:?} formed expression: {:?}", entity, *formed); + } + _ => { warn!("Entity {:?} Couldn't form partial form with expression {:?}", entity, *wexpr); } + } + WrappedExpr::Application { + func_entity: Some(func_entity), + args_entity: _, + partial_formed, + formed: None, + } if partial_formed.is_none() && *func_entity == f_entity => { + *partial_formed = Some((f_expr, f_bind_tree, PartialForm::Func)); + info!("Entity {:?} formed partial expression for: {:?}", entity, PartialForm::Func); + } + WrappedExpr::Application { + func_entity: _, + args_entity: Some(args_entity), + partial_formed, + formed: None, + } if partial_formed.is_none() && *args_entity == f_entity => { + *partial_formed = Some((f_expr, f_bind_tree, PartialForm::Args)); + info!("Entity {:?} formed partial expression for: {:?}", entity, PartialForm::Args); + } + WrappedExpr::Variable { .. } => { + commands.entity(entity).insert(Formed); + warn!("Variable not set to Formed for some reason"); + } + _ => { warn!("Entity {:?} couldn't form expression: {:?}", entity, *wexpr); } } - _ => {} } + } else { + warn!("Entity {f_entity:?} did not have formed field but had Formed component"); + commands.entity(f_entity).remove::(); } } } \ No newline at end of file diff --git a/block-lang/src/parse.rs b/block-lang/src/parse.rs index eea6c86..6ecc52e 100644 --- a/block-lang/src/parse.rs +++ b/block-lang/src/parse.rs @@ -80,7 +80,7 @@ fn parser<'e: 'b, 'b, B: TypeStore<'b>, E: TypeStore<'e>>(namespace: &'b Namespa }); 0..symbols.len() }).then(expr.clone()).foldr(|_, (lam_expr, mut bind_tree)| { - let binding = bind_tree.pop_binding(binds, bind_map.pop_bind(), exprs).expect("failed to pop lambda"); + let binding = bind_tree.pop_binding(binds, &bind_map.pop_bind(), exprs).expect("failed to pop lambda"); (Expr::lambda(binding, lam_expr, exprs), bind_tree) }).labelled("lambda"); diff --git a/block-lang/src/placing.rs b/block-lang/src/placing.rs index a70067e..decaad7 100644 --- a/block-lang/src/placing.rs +++ b/block-lang/src/placing.rs @@ -5,7 +5,7 @@ use std::f32::consts::FRAC_1_SQRT_2; use bevy::prelude::*; use bevy_mouse_tracking_plugin::{MainCamera, MousePosWorld}; -use crate::{AppState, GameState, block::{WrappedExpr, Object, ObjectData, Orientation}, mouseover::{HoverState, Side, TopHover}}; +use crate::{AppState, Formed, GameState, block::{WrappedExpr, Object, ObjectData, Orientation}, mouseover::{HoverState, Side, TopHover}}; #[derive(Component, Default, Clone)] pub struct Placing; @@ -16,7 +16,7 @@ pub fn place_expr( state: &mut GameState, expr: WrappedExpr, ) { - info!("Placing: {:?}", expr); + debug!("Placing: {:?}", expr); match app_state.current() { AppState::Default => { state.just_pressed = true; // Prevent a single mouse click @@ -59,6 +59,7 @@ pub fn placing_system( *expr = new_expr; } + let mut follow_mouse = true; if let Ok((h_entity, mut h_data, mut h_expr, HoverState::Yes { side, .. })) = top_hover.get_single_mut() { // Make sure we can place block if let Some((side, expr_slot)) = match (&mut *h_expr, side) { @@ -75,6 +76,7 @@ pub fn placing_system( } (_, _) => None, } { + follow_mouse = false; let size = (h_data.size * FRAC_1_SQRT_2) * 0.90; data.orientation = h_data.orientation.swap(); data.size = size; @@ -91,7 +93,10 @@ pub fn placing_system( // Place block inside another block if mouse.clear_just_pressed(MouseButton::Left) { - state.just_pressed = true; + // Variables are formed expressions + if let WrappedExpr::Variable { .. } = *expr { commands.entity(entity).insert(Formed); } + debug!("Placed inside {:?} on {:?} side: {:?}, {:?}", h_entity, side, entity, expr); + *expr_slot = Some(entity); data.parent = Some(h_entity); // commands.entity(h_entity).add_child(entity); // DONT DO THIS, YOUR LIFE WILL BE PAINNNN @@ -100,13 +105,18 @@ pub fn placing_system( return; } } - } else { + } + if follow_mouse { data.size = camera_proj.iter().next().unwrap().scale * 300.0; // Scale block-to-place with size data.location = Vec2::new(mouse_pos.x, mouse_pos.y); // Move block-to-place to mouse cursor data.orientation = state.placing_orientation; // Set orientation based on game state // Place block on blank canvas (if there are no objects in scene) if mouse.clear_just_pressed(MouseButton::Left) && top_hover.is_empty() { + // Variables are formed expressions + if let WrappedExpr::Variable { .. } = *expr { commands.entity(entity).insert(Formed); } + debug!("Placed on canvas {:?}, {:?}", entity, expr); + state.just_pressed = true; commands.entity(entity).remove::().insert(HoverState::No); app_state.pop().unwrap(); diff --git a/block-lang/src/ui.rs b/block-lang/src/ui.rs index 60fdc24..6334b0d 100644 --- a/block-lang/src/ui.rs +++ b/block-lang/src/ui.rs @@ -64,7 +64,7 @@ pub fn ui_setup(mut commands: Commands, asset_server: Res) { build_button( parent, - asset_server.load("VariableDot.png"), + asset_server.load("VarState=None.png"), |commands, app_state, state| { place_expr(commands, app_state, state, WrappedExpr::VARIABLE); info!("Added variable"); @@ -72,7 +72,7 @@ pub fn ui_setup(mut commands: Commands, asset_server: Res) { ); build_button( parent, - asset_server.load("Lambda.png"), + asset_server.load("LamState=None.png"), |commands, app_state, state| { place_expr( commands, @@ -85,7 +85,7 @@ pub fn ui_setup(mut commands: Commands, asset_server: Res) { ); build_button( parent, - asset_server.load("Application.png"), + asset_server.load("AppState=None.png"), |commands, app_state, state| { place_expr( commands, diff --git a/block-lang/tmp-lang b/block-lang/tmp-lang deleted file mode 160000 index 7ed3533..0000000 --- a/block-lang/tmp-lang +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7ed35338791af0486dc22623ab65459238ec2ece diff --git a/block-lang/vendor/bevy-mouse-tracking/.github/workflows/ci.yml b/block-lang/vendor/bevy-mouse-tracking/.github/workflows/ci.yml deleted file mode 100644 index 25d61e0..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/.github/workflows/ci.yml +++ /dev/null @@ -1,67 +0,0 @@ -on: [pull_request] - -name: CI - -jobs: - - test: - name: Test Suite - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - - fmt: - name: Rustfmt - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - clippy: - name: Clippy - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: rustup component add clippy - - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -- -D warnings - - readme: - name: README - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: cargo install cargo-rdme - - uses: actions-rs/cargo@v1 - with: - command: rdme - args: --check diff --git a/block-lang/vendor/bevy-mouse-tracking/.gitignore b/block-lang/vendor/bevy-mouse-tracking/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/block-lang/vendor/bevy-mouse-tracking/Cargo.toml b/block-lang/vendor/bevy-mouse-tracking/Cargo.toml deleted file mode 100644 index f74ac26..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "bevy_mouse_tracking_plugin" -description = "A plugin for effortless mouse tracking in the bevy game engine." -version = "0.2.1" -authors = ["JoJoJet "] -edition = "2021" -license = "MIT" -repository = "https://github.com/JoJoJet/bevy-mouse-tracking" -keywords = ["gamedev", "bevy", "mouse", "input"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies.bevy] -version = "0.7.*" -default-features = false -features = [ "render" ] - -[dev-dependencies.bevy] -version = "0.7.*" -default-features = false -features = [ "render", "png", "bevy_winit" ] diff --git a/block-lang/vendor/bevy-mouse-tracking/README.md b/block-lang/vendor/bevy-mouse-tracking/README.md deleted file mode 100644 index 4b0795c..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/README.md +++ /dev/null @@ -1,180 +0,0 @@ -# bevy_mouse_tracking_plugin - - - -[![CI](https://github.com/JoJoJet/bevy-mouse-tracking/actions/workflows/ci.yml/badge.svg)](https://github.com/JoJoJet/bevy-mouse-tracking/workflows/ci.yml) -[![bevy_mouse_tracking on crates.io](https://img.shields.io/crates/v/bevy_mouse_tracking_plugin.svg)](https://crates.io/crates/bevy_mouse_tracking_plugin) -[![bevy_mouse_tracking docs](https://img.shields.io/badge/docs-docs.rs-orange.svg)](https://docs.rs/bevy_mouse_tracking_plugin) - -Tracking the mouse in `bevy` is kind of annoying. -You gotta use [`Events`], and [`EventReader`]s, and even then, they only -get called when the mouse actually *moves*. - -[`Events`]: bevy::ecs::event::Events -[`EventReader`]: bevy::ecs::event::EventReader - -This crate aims to make this as easy as possible, by providing a -static [resource](bevy::ecs::system::Res) that tracks the mouse position every frame. - -This crate also supports more complex use cases such as multiple cameras, which are discussed further down. - -## Basics - -First, add the plugin to your app: - -```rust -use bevy::prelude::*; -use bevy_mouse_tracking_plugin::MousePosPlugin; - -App::new() - .add_plugins(DefaultPlugins) - .add_plugin(MousePosPlugin::SingleCamera); -``` - -Now, you can access the resource in your [`System`]s: - -[`System`]: bevy::ecs::system::System - -```rust -use bevy_mouse_tracking_plugin::MousePos; -fn dbg_mouse(mouse: Res) { - eprintln!("{}", *mouse); -} -``` -...and don't forget to add the system to your app: -```rust - .add_plugin(MousePosPlugin::SingleCamera) - .add_system(dbg_mouse); - -``` - -This will print the screen-space location of the mouse on every frame. - -However, we can do better than just screen-space: we support automatic -transformation to world-space coordinates via the [`MousePosWorld`] resource. - -```rust -use bevy_mouse_tracking_plugin::MousePosWorld; -fn dbg_world(mouse: Res) { - eprintln!("{}", *mouse); -} -``` - -This will print the world-space location of the mouse on every frame. -Note that this is only supported for two-dimensional, orthographic camera, -but pull requests for 3D support are welcome! - -## Multiple cameras - -You may notice that if you try to use this plugin in an app that has multiple cameras, it crashes! - -```rust - -App::new() - .add_plugins(DefaultPlugins) - .add_plugin(MousePosPlugin::SingleCamera) - .add_startup_system(setup) - .run(); - -fn setup(mut commands: Commands) { - commands.spawn_bundle(OrthographicCameraBundle::new_2d()); - commands.spawn_bundle(UiCameraBundle::default()); -} -``` - -This panics with the following output: - -```text -thread 'main' panicked at 'cannot identify main camera -- consider adding the MainCamera component to one of the cameras', src\mouse_pos.rs:163:13 -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -``` - -This is because the plugin doesn't know which of the two cameras to use when figuring out -the values of the `MousePos` and `MousePosWorld` resources. Let's take the panic message's advice. - -```rust - commands.spawn_bundle(OrthographicCameraBundle::new_2d()) - .insert(MainCamera); // added this line - commands.spawn_bundle(UiCameraBundle::default()); -``` - -### Queries - -If you want to get mouse tracking information relative to each camera individually, -simply [query](bevy::ecs::system::Query) for a `MousePos` or `MousePosWorld` as a -_component_ instead of as a resource. - -```rust - -App::new() - // plugins omitted... - .add_system(dbg_for_each); - -fn dbg_for_each(mouse_pos: Query<&MousePosWorld>) { - for pos in mouse_pos.iter() { - // This prints the mouse position twice per frame: - // once relative to the UI camera, and once relative to the physical camera. - eprintln!("{}", *pos); - } -} -``` - -If you want the mouse position for a specific camera, you can add query filters as always. -Note that as of `bevy 0.6`, the only way to tell the difference between a UI camera and -an orthographic camera is by checking for the [`Frustum`] component. - -[`Frustum`]: bevy::render::primitives::Frustum - -```rust - - -use bevy::render::primitives::Frustum; -fn dbg_ui_pos(mouse_pos: Query<&MousePosWorld, Without>) { - // query for the UI camera, which doesn't have a Frustum component. - let pos = mouse_pos.single(); - eprintln!("{}", *pos); -} -``` - -### No main camera - -Let's say you have multiple cameras in your app, and you want to treat them all equally, -without declaring any one of them as the main camera. -Change the plugin to this: - -```rust -App::new() - .add_plugins(DefaultPlugins) - .add_plugin(MousePosPlugin::MultiCamera) // SingleCamera -> MultiCamera - .add_startup_system(setup) - // ... - -``` - -Now, you can add as many cameras as you want, without having to worry about marking any -of them as the main camera. -Note that `MousePos` and `MousePosWorld` will no longer be accessible as global resources --- you can only access them by `Query`ing camera entities. - -## Mouse motion - -This crate supports a resource that tracks mouse motion, via [`MouseMotionPlugin`]. -The motion can be accessed from any system in a [`MouseMotion`] resource. - -[`Res`]: bevy::ecs::system::Res - -## Crate name - -As a final aside: the name of this crate is intentionally verbose. -This is because I didn't want to steal a crate name, especially since -it is very likely that this crate will eventually be made redundant by -future updates to `bevy`. -I recommend renaming the crate in your `Cargo.toml`: -```toml -[dependencies] -mouse_tracking = { package = "bevy_mouse_tracking_plugin", version = "..." } -``` - - - -License: MIT diff --git a/block-lang/vendor/bevy-mouse-tracking/assets/FiraMono-Medium.ttf b/block-lang/vendor/bevy-mouse-tracking/assets/FiraMono-Medium.ttf deleted file mode 100755 index 1e95ced..0000000 Binary files a/block-lang/vendor/bevy-mouse-tracking/assets/FiraMono-Medium.ttf and /dev/null differ diff --git a/block-lang/vendor/bevy-mouse-tracking/assets/cursor.png b/block-lang/vendor/bevy-mouse-tracking/assets/cursor.png deleted file mode 100644 index 7c3721f..0000000 Binary files a/block-lang/vendor/bevy-mouse-tracking/assets/cursor.png and /dev/null differ diff --git a/block-lang/vendor/bevy-mouse-tracking/assets/origin.png b/block-lang/vendor/bevy-mouse-tracking/assets/origin.png deleted file mode 100644 index ca83cce..0000000 Binary files a/block-lang/vendor/bevy-mouse-tracking/assets/origin.png and /dev/null differ diff --git a/block-lang/vendor/bevy-mouse-tracking/examples/screen.rs b/block-lang/vendor/bevy-mouse-tracking/examples/screen.rs deleted file mode 100644 index 55ed526..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/examples/screen.rs +++ /dev/null @@ -1,67 +0,0 @@ -use bevy::prelude::*; - -use bevy_mouse_tracking_plugin::{MousePos, MousePosPlugin}; - -#[derive(Component)] -struct Cursor; - -#[derive(Component)] -struct Hud; - -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .insert_resource(ClearColor(Color::BLACK)) - .insert_resource(WindowDescriptor::default()) - .add_plugin(MousePosPlugin::SingleCamera) - .add_startup_system(setup) - .add_system(bevy::input::system::exit_on_esc_system) - .add_system(run) - .run(); -} - -fn setup(mut commands: Commands, asset_server: Res, window: Res) { - // Spawn a Camera - commands.spawn_bundle(OrthographicCameraBundle::new_2d()); - - // Reference for the origin - commands.spawn_bundle(SpriteBundle { - texture: asset_server.load("origin.png"), - ..Default::default() - }); - - // Hud - let font = asset_server.load("FiraMono-Medium.ttf"); - let style = TextStyle { - font, - font_size: 24.0, - color: Color::ORANGE, - }; - let alignment = TextAlignment { - vertical: VerticalAlign::Top, - horizontal: HorizontalAlign::Left, - }; - let (win_width, win_height) = (window.width, window.height); - let (hud_x, hud_y) = (win_width / 2. * -1., win_height / 2.); - let translation = Vec3::new(hud_x, hud_y, 0.); - let transform = Transform::from_translation(translation); - let value = "Mouse: (-, -)".to_string(); - - commands - .spawn_bundle(Text2dBundle { - text: Text::with_section(value, style.clone(), alignment), - transform, - ..Default::default() - }) - .insert(Hud); -} - -fn run(mouse_pos: Res, mut hud_text: Query<&mut Text, With>) { - let hud_value = format!("Mouse: ({}, {})", mouse_pos.x, mouse_pos.y,); - - if let Some(mut hud_text) = hud_text.iter_mut().next() { - hud_text.sections.first_mut().unwrap().value = hud_value.clone(); - } else { - println!("No Hud Found"); - } -} diff --git a/block-lang/vendor/bevy-mouse-tracking/examples/world.rs b/block-lang/vendor/bevy-mouse-tracking/examples/world.rs deleted file mode 100644 index f26a1bc..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/examples/world.rs +++ /dev/null @@ -1,87 +0,0 @@ -use bevy::prelude::*; - -use bevy_mouse_tracking_plugin::{MousePos, MousePosPlugin, MousePosWorld}; - -#[derive(Component)] -struct Cursor; - -#[derive(Component)] -struct Hud; - -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .insert_resource(ClearColor(Color::BLACK)) - .insert_resource(WindowDescriptor::default()) - .add_plugin(MousePosPlugin::SingleCamera) - .add_startup_system(setup) - .add_system(bevy::input::system::exit_on_esc_system) - .add_system(run) - .run(); -} - -fn setup(mut commands: Commands, asset_server: Res, window: Res) { - // Spawn a Camera - let mut camera_bundle = OrthographicCameraBundle::new_2d(); - camera_bundle.orthographic_projection.scale = 0.5; // works fine with non-unit scaling. - commands.spawn_bundle(camera_bundle); - - // Reference for the origin - commands.spawn_bundle(SpriteBundle { - texture: asset_server.load("origin.png"), - ..Default::default() - }); - - // Reference for the mouse position - commands - .spawn_bundle(SpriteBundle { - texture: asset_server.load("cursor.png"), - ..Default::default() - }) - .insert(Cursor); - - // Hud - let font = asset_server.load("FiraMono-Medium.ttf"); - let style = TextStyle { - font, - font_size: 24.0, - color: Color::ORANGE, - }; - let alignment = TextAlignment { - vertical: VerticalAlign::Top, - horizontal: HorizontalAlign::Left, - }; - let (win_width, win_height) = (window.width, window.height); - let (hud_x, hud_y) = (win_width / 2. * -1., win_height / 2.); - let translation = Vec3::new(hud_x, hud_y, 0.); - let transform = Transform::from_translation(translation); - let value = "Screen: (-, -)\nWorld: (-, -)".to_string(); - - commands - .spawn_bundle(Text2dBundle { - text: Text::with_section(value, style.clone(), alignment), - transform, - ..Default::default() - }) - .insert(Hud); -} - -fn run( - mouse_screen_pos: Res, - mouse_world_pos: Res, - mut hud_text: Query<&mut Text, With>, - mut cursor: Query<&mut Transform, With>, -) { - let hud_value = format!( - "Screen: ({}, {})\nWorld: ({}, {})", - mouse_screen_pos.x, mouse_screen_pos.y, mouse_world_pos.x, mouse_world_pos.y, - ); - - if let Some(mut hud_text) = hud_text.iter_mut().next() { - hud_text.sections.first_mut().unwrap().value = hud_value.clone(); - } - - if let Some(mut cursor_transform) = cursor.iter_mut().next() { - cursor_transform.translation = Vec3::new(mouse_world_pos.x, mouse_world_pos.y, 0.); - } -} diff --git a/block-lang/vendor/bevy-mouse-tracking/src/lib.rs b/block-lang/vendor/bevy-mouse-tracking/src/lib.rs deleted file mode 100644 index d2a9d84..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/src/lib.rs +++ /dev/null @@ -1,213 +0,0 @@ -//! [![CI](https://github.com/JoJoJet/bevy-mouse-tracking/actions/workflows/ci.yml/badge.svg)](https://github.com/JoJoJet/bevy-mouse-tracking/workflows/ci.yml) -//! [![bevy_mouse_tracking on crates.io](https://img.shields.io/crates/v/bevy_mouse_tracking_plugin.svg)](https://crates.io/crates/bevy_mouse_tracking_plugin) -//! [![bevy_mouse_tracking docs](https://img.shields.io/badge/docs-docs.rs-orange.svg)](https://docs.rs/bevy_mouse_tracking_plugin) -//! -//! Tracking the mouse in `bevy` is kind of annoying. -//! You gotta use [`Events`], and [`EventReader`]s, and even then, they only -//! get called when the mouse actually *moves*. -//! -//! [`Events`]: bevy::ecs::event::Events -//! [`EventReader`]: bevy::ecs::event::EventReader -//! -//! This crate aims to make this as easy as possible, by providing a -//! static [resource](bevy::ecs::system::Res) that tracks the mouse position every frame. -//! -//! This crate also supports more complex use cases such as multiple cameras, which are discussed further down. -//! -//! # Basics -//! -//! First, add the plugin to your app: -//! -//! ``` -//! use bevy::prelude::*; -//! use bevy_mouse_tracking_plugin::MousePosPlugin; -//! -//! App::new() -//! .add_plugins(DefaultPlugins) -//! .add_plugin(MousePosPlugin::SingleCamera); -//! ``` -//! -//! Now, you can access the resource in your [`System`]s: -//! -//! [`System`]: bevy::ecs::system::System -//! -//! ``` -//! # use bevy::prelude::*; -//! use bevy_mouse_tracking_plugin::MousePos; -//! fn dbg_mouse(mouse: Res) { -//! eprintln!("{}", *mouse); -//! } -//! ``` -//! ...and don't forget to add the system to your app: -//! ``` -//! # use bevy::prelude::*; -//! # use bevy_mouse_tracking_plugin::MousePosPlugin; -//! # App::new() -//! # .add_plugins(DefaultPlugins) -//! .add_plugin(MousePosPlugin::SingleCamera) -//! .add_system(dbg_mouse); -//! -//! # fn dbg_mouse() { } -//! ``` -//! -//! This will print the screen-space location of the mouse on every frame. -//! -//! However, we can do better than just screen-space: we support automatic -//! transformation to world-space coordinates via the [`MousePosWorld`] resource. -//! -//! ``` -//! # use bevy::prelude::*; -//! use bevy_mouse_tracking_plugin::MousePosWorld; -//! fn dbg_world(mouse: Res) { -//! eprintln!("{}", *mouse); -//! } -//! ``` -//! -//! This will print the world-space location of the mouse on every frame. -//! Note that this is only supported for two-dimensional, orthographic camera, -//! but pull requests for 3D support are welcome! -//! -//! # Multiple cameras -//! -//! You may notice that if you try to use this plugin in an app that has multiple cameras, it crashes! -//! -//! ```should_panic -//! # use bevy::prelude::*; -//! # use bevy_mouse_tracking_plugin::MousePosPlugin; -//! -//! App::new() -//! .add_plugins(DefaultPlugins) -//! .add_plugin(MousePosPlugin::SingleCamera) -//! .add_startup_system(setup) -//! .run(); -//! -//! fn setup(mut commands: Commands) { -//! commands.spawn_bundle(OrthographicCameraBundle::new_2d()); -//! commands.spawn_bundle(UiCameraBundle::default()); -//! } -//! ``` -//! -//! This panics with the following output: -//! -//! ```text -//! thread 'main' panicked at 'cannot identify main camera -- consider adding the MainCamera component to one of the cameras', src\mouse_pos.rs:163:13 -//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -//! ``` -//! -//! This is because the plugin doesn't know which of the two cameras to use when figuring out -//! the values of the `MousePos` and `MousePosWorld` resources. Let's take the panic message's advice. -//! -//! ``` -//! # use bevy::prelude::*; -//! # use bevy_mouse_tracking_plugin::{MousePosPlugin, MainCamera}; -//! # App::new() -//! # .add_plugins(DefaultPlugins) -//! # .add_plugin(MousePosPlugin::SingleCamera) -//! # .add_startup_system(setup) -//! # .update(); -//! # fn setup(mut commands: Commands) { -//! commands.spawn_bundle(OrthographicCameraBundle::new_2d()) -//! .insert(MainCamera); // added this line -//! commands.spawn_bundle(UiCameraBundle::default()); -//! # } -//! ``` -//! -//! ## Queries -//! -//! If you want to get mouse tracking information relative to each camera individually, -//! simply [query](bevy::ecs::system::Query) for a `MousePos` or `MousePosWorld` as a -//! _component_ instead of as a resource. -//! -//! ``` -//! # use bevy::prelude::*; -//! # use bevy_mouse_tracking_plugin::{MousePosPlugin, MainCamera, MousePosWorld}; -//! -//! App::new() -//! // plugins omitted... -//! # .add_plugins(DefaultPlugins) -//! # .add_plugin(MousePosPlugin::SingleCamera) -//! .add_system(dbg_for_each); -//! -//! fn dbg_for_each(mouse_pos: Query<&MousePosWorld>) { -//! for pos in mouse_pos.iter() { -//! // This prints the mouse position twice per frame: -//! // once relative to the UI camera, and once relative to the physical camera. -//! eprintln!("{}", *pos); -//! } -//! } -//! ``` -//! -//! If you want the mouse position for a specific camera, you can add query filters as always. -//! Note that as of `bevy 0.6`, the only way to tell the difference between a UI camera and -//! an orthographic camera is by checking for the [`Frustum`] component. -//! -//! [`Frustum`]: bevy::render::primitives::Frustum -//! -//! ``` -//! # use bevy::prelude::*; -//! # use bevy_mouse_tracking_plugin::{MousePosPlugin, MainCamera, MousePosWorld}; -//! -//! # App::new() -//! # .add_plugins(DefaultPlugins) -//! # .add_plugin(MousePosPlugin::SingleCamera) -//! # .add_system(dbg_ui_pos); -//! -//! use bevy::render::primitives::Frustum; -//! fn dbg_ui_pos(mouse_pos: Query<&MousePosWorld, Without>) { -//! // query for the UI camera, which doesn't have a Frustum component. -//! let pos = mouse_pos.single(); -//! eprintln!("{}", *pos); -//! } -//! ``` -//! -//! ## No main camera -//! -//! Let's say you have multiple cameras in your app, and you want to treat them all equally, -//! without declaring any one of them as the main camera. -//! Change the plugin to this: -//! -//! ``` -//! # use bevy::prelude::*; -//! # use bevy_mouse_tracking_plugin::{MousePosPlugin}; -//! App::new() -//! .add_plugins(DefaultPlugins) -//! .add_plugin(MousePosPlugin::MultiCamera) // SingleCamera -> MultiCamera -//! .add_startup_system(setup) -//! // ... -//! # .update(); -//! -//! # fn setup(mut commands: Commands) { -//! # commands.spawn_bundle(OrthographicCameraBundle::new_2d()); -//! # commands.spawn_bundle(UiCameraBundle::default()); -//! # } -//! ``` -//! -//! Now, you can add as many cameras as you want, without having to worry about marking any -//! of them as the main camera. -//! Note that `MousePos` and `MousePosWorld` will no longer be accessible as global resources -//! -- you can only access them by `Query`ing camera entities. -//! -//! # Mouse motion -//! -//! This crate supports a resource that tracks mouse motion, via [`MouseMotionPlugin`]. -//! The motion can be accessed from any system in a [`MouseMotion`] resource. -//! -//! [`Res`]: bevy::ecs::system::Res -//! -//! # Crate name -//! -//! As a final aside: the name of this crate is intentionally verbose. -//! This is because I didn't want to steal a crate name, especially since -//! it is very likely that this crate will eventually be made redundant by -//! future updates to `bevy`. -//! I recommend renaming the crate in your `Cargo.toml`: -//! ```toml -//! [dependencies] -//! mouse_tracking = { package = "bevy_mouse_tracking_plugin", version = "..." } -//! ``` - -mod mouse_pos; -pub use mouse_pos::{MainCamera, MousePos, MousePosPlugin, MousePosWorld}; - -mod mouse_motion; -pub use mouse_motion::{MouseMotion, MouseMotionPlugin}; diff --git a/block-lang/vendor/bevy-mouse-tracking/src/mouse_motion.rs b/block-lang/vendor/bevy-mouse-tracking/src/mouse_motion.rs deleted file mode 100644 index d3688c2..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/src/mouse_motion.rs +++ /dev/null @@ -1,18 +0,0 @@ -use bevy::prelude::*; - -/// Plugin that tracks mouse motion. -pub struct MouseMotionPlugin; - -pub use bevy::input::mouse::MouseMotion; - -impl bevy::app::Plugin for MouseMotionPlugin { - fn build(&self, app: &mut bevy::app::App) { - app.insert_resource(MouseMotion { delta: Vec2::ZERO }); - app.add_system_to_stage(CoreStage::First, update_mouse_motion); - } -} - -fn update_mouse_motion(mut events: EventReader, mut res: ResMut) { - let delta = events.iter().fold(Vec2::ZERO, |acc, e| acc + e.delta); - *res = MouseMotion { delta }; -} diff --git a/block-lang/vendor/bevy-mouse-tracking/src/mouse_pos.rs b/block-lang/vendor/bevy-mouse-tracking/src/mouse_pos.rs deleted file mode 100644 index 1dd18fe..0000000 --- a/block-lang/vendor/bevy-mouse-tracking/src/mouse_pos.rs +++ /dev/null @@ -1,225 +0,0 @@ -use std::{fmt::Display, ops::Deref}; - -use bevy::prelude::*; - -/// Plugin that tracks the mouse location. -pub enum MousePosPlugin { - /// Configuration for apps that have a single main camera. - /// Provides global Resources for [`MousePos`] and [`MousePosWorld`]. - SingleCamera, - /// Configuration for apps that have multiple cameras which must be handled separately. - MultiCamera, -} - -impl Plugin for MousePosPlugin { - fn build(&self, app: &mut App) { - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)] - enum MouseSystem { - ScreenPos, - WorldPos, - FindMain, - } - - // System to add mouse tracking components. - // Runs once at the end of each frame. This means that no cameras will have - // mouse tracking components until after the first frame. - // This might cause some issues, but it's probably for the best since, - // during the first frame, nothing has been rendered yet. - app.add_system_to_stage(CoreStage::PostUpdate, add_pos_components); - - app.add_system_to_stage(CoreStage::First, update_pos.label(MouseSystem::ScreenPos)); - app.add_system_to_stage( - CoreStage::First, - update_pos_ortho - .label(MouseSystem::WorldPos) - .after(MouseSystem::ScreenPos), - ); - - match self { - Self::SingleCamera => { - app.insert_resource(MousePos(Default::default())); - app.insert_resource(MousePosWorld(Default::default())); - app.init_resource::(); - - // system to update the current main camera - app.add_system_set_to_stage( - CoreStage::First, - SystemSet::new() - .label(MouseSystem::FindMain) - .with_run_criteria(main_camera_changed) - .with_system(find_main_camera), - ); - // - app.add_system_to_stage( - CoreStage::First, - update_resources - .after(MouseSystem::WorldPos) - .after(MouseSystem::FindMain), - ); - } - Self::MultiCamera => {} - } - } -} - -/// The location of the mouse in screenspace. -/// This will be updated every frame during [`CoreStage::First`]. Any systems that rely -/// on this should come after `CoreStage::First`. -#[derive(Debug, Clone, Copy, PartialEq, Component)] -pub struct MousePos(Vec2); - -impl Deref for MousePos { - type Target = Vec2; - fn deref(&self) -> &Vec2 { - &self.0 - } -} - -impl Display for MousePos { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -fn add_pos_components( - cameras1: Query<(Entity, &Camera), Without>, - cameras2: Query, Without)>, - windows: Res, - mut commands: Commands, -) { - for (e, camera) in cameras1.iter() { - if let RenderTarget::Window(window_id) = camera.target { - // get the initial position of the cursor. - let position = windows - .get(window_id) - .and_then(|w| w.cursor_position()) - .unwrap_or_default(); - commands.entity(e).insert(MousePos(position)); - } - } - for cam in cameras2.iter() { - commands - .entity(cam) - .insert(MousePosWorld(Default::default())); - } -} - -fn update_pos( - mut movement: EventReader, - mut cameras: Query<(&Camera, &mut MousePos)>, -) { - for &CursorMoved { id, position } in movement.iter() { - // find all cameras corresponding to the window on which the cursor moved. - for (_, mut pos) in cameras - .iter_mut() - .filter(|(c, ..)| c.target == RenderTarget::Window(id)) - { - pos.0 = position; - } - } -} - -/// The location of the mouse in worldspace. -/// This will be updated every frame during [`CoreStage::First`]. Any systems that rely -/// on this should come after `CoreStage::First`. -#[derive(Debug, Clone, Copy, PartialEq, Component)] -pub struct MousePosWorld(Vec3); - -impl Display for MousePosWorld { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.0.fmt(f) - } -} - -impl Deref for MousePosWorld { - type Target = Vec3; - fn deref(&self) -> &Vec3 { - &self.0 - } -} - -fn update_pos_ortho( - mut tracking: Query<(Entity, &mut MousePosWorld, &MousePos), Changed>, - cameras: Query<(&GlobalTransform, &OrthographicProjection)>, -) { - for (camera, mut world, screen) in tracking.iter_mut() { - let (camera, proj) = cameras - .get(camera) - .expect("only orthographic cameras are supported"); - let offset = Vec2::new(proj.left, proj.bottom); - - // Must multiply by projection scale before applying camera global transform - // Otherwise you get weird offset mouse positions when both scaling and panning the camera. - world.0 = camera.mul_vec3(((screen.0 + offset) * proj.scale).extend(0.0)); - } -} - -/// Marker component for the primary camera in the world. -/// If only one camera exists, this is optional. -#[derive(Debug, Clone, Copy, Component)] -pub struct MainCamera; - -/// Resource that caches the main camera, so it doesn't need to be looked up every frame. -/// This is an implementation detail and thus should not be part of the public api. -#[derive(Debug, Default)] -struct MainCameraStore(Option); - -// only run when the candidates for the main camera change. -use bevy::ecs::schedule::ShouldRun; -use bevy::render::camera::RenderTarget; - -fn main_camera_changed( - cam: Query>, - main: Query>, -) -> ShouldRun { - if !cam.is_empty() || !main.is_empty() { - ShouldRun::Yes - } else { - ShouldRun::No - } -} - -fn find_main_camera( - mut main_store: ResMut, - cameras: Query<(Entity, Option<&MainCamera>), With>, -) { - use bevy::ecs::system::QuerySingleError; - main_store.0 = match cameras.get_single() { - Ok((e, ..)) => Some(e), - Err(QuerySingleError::NoEntities(_)) => { - // no main camera exists - None - } - Err(QuerySingleError::MultipleEntities(_)) => { - // try to disambiguate - let mut mains = cameras.iter().filter_map(|(e, main)| main.and(Some(e))); - let main = mains.next().unwrap_or_else(|| { - panic!("cannot identify main camera -- consider adding the MainCamera component to one of the cameras") - }); - if mains.next().is_some() { - panic!("only one camera may be marked with the MainCamera component"); - } - Some(main) - } - } -} - -fn update_resources( - mut screen_res: ResMut, - mut world_res: ResMut, - main: Res, - screen: Query<&MousePos, Changed>, - world: Query<&MousePosWorld, Changed>, -) { - let main = match main.0 { - Some(m) => m, - None => return, // no main camera, try again next frame - }; - // update the global resources if the components for the main camera changed. - if let Ok(screen) = screen.get(main) { - screen_res.0 = screen.0; - } - if let Ok(world) = world.get(main) { - world_res.0 = world.0; - } -} diff --git a/block-lang/vendor/bevy_pancam/.gitignore b/block-lang/vendor/bevy_pancam/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/block-lang/vendor/bevy_pancam/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/block-lang/vendor/bevy_pancam/Cargo.toml b/block-lang/vendor/bevy_pancam/Cargo.toml deleted file mode 100644 index 8c6c946..0000000 --- a/block-lang/vendor/bevy_pancam/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "bevy_pancam" -version = "0.3.0" -edition = "2021" -license = "MIT OR Apache-2.0" -readme = "readme.md" -authors = ["Johan Helsing "] -description = "A camera that allows panning by dragging with the mouse" -keywords = ["gamedev", "bevy"] -categories = ["game-development"] -repository = "https://github.com/johanhelsing/bevy_pancam" - -[dependencies] -bevy = { version = "0.7", features = ["render"], default-features = false } - -[dev-dependencies] -bevy = { version = "0.7", features = ["render"] } -rand = "0.8" diff --git a/block-lang/vendor/bevy_pancam/LICENSE b/block-lang/vendor/bevy_pancam/LICENSE deleted file mode 100644 index 45dd01d..0000000 --- a/block-lang/vendor/bevy_pancam/LICENSE +++ /dev/null @@ -1,6 +0,0 @@ -bevy_pancam is dual-licensed under either - -* MIT License (./LICENSE-MIT or http://opensource.org/licenses/MIT) -* Apache License, Version 2.0 (./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - -at your option. diff --git a/block-lang/vendor/bevy_pancam/LICENSE-APACHE b/block-lang/vendor/bevy_pancam/LICENSE-APACHE deleted file mode 100644 index d9a10c0..0000000 --- a/block-lang/vendor/bevy_pancam/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/block-lang/vendor/bevy_pancam/LICENSE-MIT b/block-lang/vendor/bevy_pancam/LICENSE-MIT deleted file mode 100644 index 9cf1062..0000000 --- a/block-lang/vendor/bevy_pancam/LICENSE-MIT +++ /dev/null @@ -1,19 +0,0 @@ -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/block-lang/vendor/bevy_pancam/examples/simple.rs b/block-lang/vendor/bevy_pancam/examples/simple.rs deleted file mode 100644 index fa58861..0000000 --- a/block-lang/vendor/bevy_pancam/examples/simple.rs +++ /dev/null @@ -1,38 +0,0 @@ -use bevy::prelude::*; -use bevy_pancam::{PanCam, PanCamPlugin}; -use rand::prelude::random; - -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .add_plugin(PanCamPlugin::default()) - .add_startup_system(setup) - .run(); -} - -fn setup(mut commands: Commands) { - commands - .spawn_bundle(OrthographicCameraBundle::new_2d()) - .insert(PanCam::default()); - - let n = 20; - let spacing = 50.; - let offset = spacing * n as f32 / 2.; - let custom_size = Some(Vec2::new(spacing, spacing)); - for x in 0..n { - for y in 0..n { - let x = x as f32 * spacing - offset; - let y = y as f32 * spacing - offset; - let color = Color::hsl(240., random::() * 0.3, random::() * 0.3); - commands.spawn_bundle(SpriteBundle { - sprite: Sprite { - color, - custom_size, - ..default() - }, - transform: Transform::from_xyz(x, y, 0.), - ..default() - }); - } - } -} \ No newline at end of file diff --git a/block-lang/vendor/bevy_pancam/examples/toggle.rs b/block-lang/vendor/bevy_pancam/examples/toggle.rs deleted file mode 100644 index f57a1ef..0000000 --- a/block-lang/vendor/bevy_pancam/examples/toggle.rs +++ /dev/null @@ -1,47 +0,0 @@ -use bevy::prelude::*; -use bevy_pancam::{PanCam, PanCamPlugin}; -use rand::prelude::random; - -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .add_plugin(PanCamPlugin::default()) - .add_startup_system(setup) - .add_system(toggle_key) - .run(); -} - -fn setup(mut commands: Commands) { - commands - .spawn_bundle(OrthographicCameraBundle::new_2d()) - .insert(PanCam::default()); - - let n = 20; - let spacing = 50.; - let offset = spacing * n as f32 / 2.; - let custom_size = Some(Vec2::new(spacing, spacing)); - for x in 0..n { - for y in 0..n { - let x = x as f32 * spacing - offset; - let y = y as f32 * spacing - offset; - let color = Color::hsl(240., random::() * 0.3, random::() * 0.3); - commands.spawn_bundle(SpriteBundle { - sprite: Sprite { - color, - custom_size, - ..default() - }, - transform: Transform::from_xyz(x, y, 0.), - ..default() - }); - } - } -} - -fn toggle_key(mut query: Query<&mut PanCam>, keys: Res>) { - if keys.just_pressed(KeyCode::Space) { - for mut pancam in query.iter_mut() { - pancam.enabled = !pancam.enabled; - } - } -} diff --git a/block-lang/vendor/bevy_pancam/readme.md b/block-lang/vendor/bevy_pancam/readme.md deleted file mode 100644 index c92e593..0000000 --- a/block-lang/vendor/bevy_pancam/readme.md +++ /dev/null @@ -1,54 +0,0 @@ -# bevy_pancam - -[![crates.io](https://img.shields.io/crates/v/bevy_pancam.svg)](https://crates.io/crates/bevy_pancam) -![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg) -[![crates.io](https://img.shields.io/crates/d/bevy_pancam.svg)](https://crates.io/crates/bevy_pancam) -[![docs.rs](https://img.shields.io/docsrs/bevy_pancam)](https://docs.rs/bevy_pancam) - -A 2d-camera plugin for bevy that works with orthographic cameras. - -The motivation is that this could be used for something like a map editor for a 2D game. - -## Controls - -Behaves similarly to common online map applications: - -- Click and drag to move the camera -- Scroll to zoom - -## Usage - -Add the plugin to your app - -```rust -App::build() - .add_plugins(DefaultPlugins) - .add_plugin(PanCamPlugin::default()); -``` - -```rust -commands.spawn_bundle(OrthographicCameraBundle::new_2d()) - .insert(PanCam::default()); -``` - -See the [`simple`](./examples/simple.rs) example. - -## Bevy Version Support - -The `main` branch targets the latest bevy release. - -I intend to support the `main` branch of Bevy in the `bevy-main` branch. - -|bevy|bevy_pancam| -|---|---| -|0.7|0.3, main| -|0.6|0.2| -|0.5|0.1| - -## License - -MIT or Apache-2.0 - -## Contributions - -PRs welcome! \ No newline at end of file diff --git a/block-lang/vendor/bevy_pancam/src/lib.rs b/block-lang/vendor/bevy_pancam/src/lib.rs deleted file mode 100644 index f00969e..0000000 --- a/block-lang/vendor/bevy_pancam/src/lib.rs +++ /dev/null @@ -1,101 +0,0 @@ -use bevy::{ - input::mouse::{MouseScrollUnit, MouseWheel}, - prelude::*, - render::camera::OrthographicProjection, -}; - -#[derive(Default)] -pub struct PanCamPlugin; - -impl Plugin for PanCamPlugin { - fn build(&self, app: &mut App) { - app.add_system(camera_movement).add_system(camera_zoom); - } -} - -// Zoom doesn't work on bevy 0.5 due to: https://github.com/bevyengine/bevy/pull/2015 -fn camera_zoom( - mut query: Query<(&PanCam, &mut OrthographicProjection, &mut Transform)>, - mut scroll_events: EventReader, - windows: Res, -) { - let pixels_per_line = 100.; // Maybe make configurable? - let scroll = scroll_events - .iter() - .map(|ev| match ev.unit { - MouseScrollUnit::Pixel => ev.y, - MouseScrollUnit::Line => ev.y * pixels_per_line, - }) - .sum::(); - - if scroll == 0. { - return; - } - - let window = windows.get_primary().unwrap(); - let window_size = Vec2::new(window.width(), window.height()); - let mouse_normalized_screen_pos = (window.cursor_position().unwrap() / window_size) * 2. - Vec2::ONE; - - for (cam, mut proj, mut pos) in query.iter_mut() { - if cam.enabled { - let old_scale = proj.scale; - proj.scale = (proj.scale * (1. + -scroll * 0.001)).max(0.00001); - - if cam.track_mouse { - let proj_size = Vec2::new(proj.right, proj.top); - let mouse_world_pos = pos.translation.truncate() + mouse_normalized_screen_pos * proj_size * old_scale; - pos.translation = (mouse_world_pos - mouse_normalized_screen_pos * proj_size * proj.scale).extend(pos.translation.z); - } - } - } -} - -fn camera_movement( - windows: Res, - mouse_buttons: Res>, - mut query: Query<(&PanCam, &mut Transform, &OrthographicProjection)>, - mut last_pos: Local>, -) { - let window = windows.get_primary().unwrap(); - - // Use position instead of MouseMotion, otherwise we don't get acceleration movement - let current_pos = match window.cursor_position() { - Some(current_pos) => current_pos, - None => return, - }; - let delta = current_pos - last_pos.unwrap_or(current_pos); - - for (cam, mut transform, projection) in query.iter_mut() { - if cam.enabled - && cam - .grab_buttons - .iter() - .any(|btn| mouse_buttons.pressed(*btn)) - { - let scaling = Vec2::new( - window.width() / (projection.right - projection.left), - window.height() / (projection.top - projection.bottom), - ) * projection.scale; - - transform.translation -= (delta * scaling).extend(0.); - } - } - *last_pos = Some(current_pos); -} - -#[derive(Component)] -pub struct PanCam { - pub grab_buttons: Vec, - pub enabled: bool, - pub track_mouse: bool, -} - -impl Default for PanCam { - fn default() -> Self { - Self { - grab_buttons: vec![MouseButton::Left, MouseButton::Right, MouseButton::Middle], - enabled: true, - track_mouse: false, - } - } -}