├── .gitignore ├── pga ├── src │ ├── lib.rs │ └── main.rs └── Cargo.toml ├── generated ├── src │ ├── lib.rs │ ├── pga2d │ │ ├── mod.rs │ │ ├── traits.rs │ │ ├── vec2.rs │ │ ├── vec3.rs │ │ ├── rotor.rs │ │ └── line.rs │ └── pga3d │ │ ├── mod.rs │ │ ├── traits.rs │ │ ├── moment3.rs │ │ └── vec3.rs └── Cargo.toml ├── Cargo.toml ├── reference ├── Cargo.toml └── src │ └── bin │ └── ref_pga2d.rs ├── generator ├── Cargo.toml ├── templates │ ├── lib.rs │ └── traits.rs ├── src │ ├── markdown.rs │ ├── documentation.rs │ ├── explicit.rs │ ├── blade.rs │ ├── main.rs │ ├── types.rs │ ├── typ.rs │ ├── rust.rs │ ├── typify.rs │ ├── lib.rs │ ├── expr.rs │ ├── grammars.rs │ ├── sblade.rs │ ├── simplify.rs │ └── gen.rs └── tests │ └── test.rs ├── run.sh ├── LICENSE-MIT ├── README.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.pdf 3 | -------------------------------------------------------------------------------- /pga/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod manual_pga2d; 2 | -------------------------------------------------------------------------------- /generated/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pga2d; 2 | pub mod pga3d; 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "generated", 4 | "generator", 5 | ] 6 | -------------------------------------------------------------------------------- /generated/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generated" 3 | version = "0.1.0" 4 | authors = ["Emil Ernerfeldt "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | derive_more = "0.99" 9 | -------------------------------------------------------------------------------- /reference/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reference" 3 | version = "0.1.0" 4 | authors = ["Emil Ernerfeldt "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | -------------------------------------------------------------------------------- /pga/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pga" 3 | version = "0.1.0" 4 | authors = ["Emil Ernerfeldt "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | derive_more = "0.99" 11 | itertools = "0.8" 12 | -------------------------------------------------------------------------------- /generator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Emil Ernerfeldt "] 3 | edition = "2018" 4 | license = "MIT OR Apache-2.0" 5 | name = "generator" 6 | version = "0.1.0" 7 | 8 | [dependencies] 9 | derive_more = "0.99" 10 | indexmap = "1" 11 | itertools = "0.8" 12 | pico-args = "0.3" 13 | strum = "0.18" 14 | strum_macros = "0.18" 15 | -------------------------------------------------------------------------------- /generator/templates/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod blades; 2 | pub mod traits; 3 | 4 | // ---------------------------------------------------------------------------- 5 | // Geometric Algebra definition helpers: 6 | 7 | /// Special zero type for completeness, and better error messages. 8 | /// If you get this in an error message, it is because you multiplied 9 | /// two dimensions that always results in zero. 10 | 11 | pub struct Zero {} 12 | 13 | // ---------------------------------------------------------------------------- 14 | -------------------------------------------------------------------------------- /pga/src/main.rs: -------------------------------------------------------------------------------- 1 | use pga::manual_pga2d::*; 2 | 3 | fn main() { 4 | // let l1 = Line::from_euclidean(1.0, 0.0, 0.0); 5 | let l1 = Line { 6 | e0: E0(0.0), 7 | e1: E1(1.0), 8 | e2: E2(0.0), 9 | }; 10 | // let l2 = Line::from_euclidean(0.5f64.sqrt(), -0.5f64.sqrt(), 0.0); 11 | let l2 = Line { 12 | e0: E0(0.0), 13 | e1: E1(-0.5f64.sqrt()), 14 | e2: E2(0.5f64.sqrt()), 15 | }; 16 | let t = l1 * l2; // 90 deg rotate 17 | let p = Point::from_euclidean(3.0, 2.0); 18 | dbg!(t); 19 | dbg!(p); 20 | dbg!(t.sandwich(p)); 21 | // dbg!(t * p * t.reverse()); 22 | } 23 | -------------------------------------------------------------------------------- /generated/src/pga2d/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blades; 2 | pub mod traits; 3 | 4 | // ---------------------------------------------------------------------------- 5 | // Geometric Algebra definition helpers: 6 | 7 | /// Special zero type for completeness, and better error messages. 8 | /// If you get this in an error message, it is because you multiplied 9 | /// two dimensions that always results in zero. 10 | 11 | pub struct Zero {} 12 | 13 | // ---------------------------------------------------------------------------- 14 | 15 | // Types: 16 | pub mod line; 17 | pub mod motor; 18 | pub mod rotor; 19 | pub mod vec2; 20 | pub mod vec3; 21 | 22 | pub use self::{blades::*, line::*, motor::*, rotor::*, traits::*, vec2::*, vec3::*}; 23 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit -o nounset -o pipefail 3 | 4 | cd "$( dirname "${BASH_SOURCE[0]}" )" 5 | 6 | echo "[workspace] 7 | members = [ 8 | \"generator\", 9 | ]" > Cargo.toml 10 | 11 | rm -rf generated/src/pga2d generated/src/pga3d 12 | 13 | cargo run -q -- --grammar pga2d --out_dir generated/src/pga2d 14 | cargo run -q -- --grammar pga3d --out_dir generated/src/pga3d 15 | 16 | echo "Testing generated code:" 17 | 18 | echo "[workspace] 19 | members = [ 20 | \"generated\", 21 | \"generator\", 22 | ]" > Cargo.toml 23 | 24 | cargo fmt 25 | 26 | cargo check -q --all-features 27 | cargo test -q --all-features 28 | cargo clippy -q 29 | cargo clean -q --doc && cargo doc --no-deps 30 | 31 | echo "All OK" 32 | -------------------------------------------------------------------------------- /generated/src/pga3d/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blades; 2 | pub mod traits; 3 | 4 | // ---------------------------------------------------------------------------- 5 | // Geometric Algebra definition helpers: 6 | 7 | /// Special zero type for completeness, and better error messages. 8 | /// If you get this in an error message, it is because you multiplied 9 | /// two dimensions that always results in zero. 10 | 11 | pub struct Zero {} 12 | 13 | // ---------------------------------------------------------------------------- 14 | 15 | // Types: 16 | pub mod line3; 17 | pub mod moment3; 18 | pub mod motor3; 19 | pub mod plane; 20 | pub mod rotor3; 21 | pub mod vec3; 22 | pub mod vec4; 23 | 24 | pub use self::{blades::*, line3::*, moment3::*, motor3::*, plane::*, rotor3::*, traits::*, vec3::*, vec4::*}; 25 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2021 Emil Ernerfeldt 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /generator/src/markdown.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use itertools::{izip, Itertools}; 4 | 5 | pub fn table(headers: impl IntoIterator, rows: impl IntoIterator>) -> String { 6 | let headers: Vec = headers.into_iter().collect(); 7 | let rows: Vec> = rows.into_iter().collect(); 8 | 9 | let mut col_widths: Vec = headers.iter().map(String::len).collect(); 10 | for row in &rows { 11 | assert_eq!(row.len(), col_widths.len()); 12 | for (col_idx, cell) in row.iter().enumerate() { 13 | col_widths[col_idx] = col_widths[col_idx].max(cell.len()); 14 | } 15 | } 16 | 17 | let mut s = vec![]; 18 | write!( 19 | &mut s, 20 | "| {} |\n", 21 | izip!(&headers, &col_widths) 22 | .map(|(header, width)| format!("{: String) -> String { 9 | markdown::table( 10 | chain(Some("Op \\ Blade".to_owned()), unit_blades.iter().cloned().map(rust)), 11 | Unary::iter().map(|unary| { 12 | chain( 13 | Some(unary.short_description().to_owned()), 14 | unit_blades.iter().map(|blade| rust(Expr::unary(unary, blade.clone()))), 15 | ) 16 | .collect() 17 | }), 18 | ) 19 | } 20 | 21 | pub fn multiplication_tables(unit_blades: &[Expr], rust: &impl Fn(Expr) -> String) -> String { 22 | Product::iter() 23 | .map(|prod| { 24 | format!( 25 | "### {} multiplication table\n\n{}\n", 26 | prod.trait_name(), 27 | multiplication_table(unit_blades, Product::Geometric, rust) 28 | ) 29 | }) 30 | .join("\n") 31 | } 32 | 33 | pub fn multiplication_table(unit_blades: &[Expr], product: Product, rust: &impl Fn(Expr) -> String) -> String { 34 | markdown::table( 35 | chain(Some("".to_owned()), unit_blades.iter().cloned().map(rust)), 36 | unit_blades.iter().map(|l| { 37 | chain( 38 | Some(rust(l.clone())), 39 | unit_blades 40 | .iter() 41 | .map(|r| rust(Expr::Prod(product, vec![l.clone(), r.clone()]))), 42 | ) 43 | .collect() 44 | }), 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /generator/src/explicit.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | impl Expr { 4 | /// Turn `y ^ x` into `-XY(y.0 * x.0)` so we can easier see sign changes, 5 | /// and easier copy/paste code 6 | #[must_use] 7 | pub fn explicit(self, t: &Types, g: &Grammar) -> Expr { 8 | match self { 9 | Expr::Var { .. } => self, 10 | Expr::Vec(_) => self, 11 | Expr::Unary(unary, expr) => Expr::Unary(unary, expr.explicit(t, g).into()), 12 | Expr::Term(expr, scalar) => Expr::Term(expr.explicit(t, g).into(), scalar), 13 | Expr::Sum(terms) => Expr::Sum(terms.into_iter().map(|e| e.explicit(t, g)).collect()), 14 | Expr::Prod(product, factors) => { 15 | let factors: Vec = factors.into_iter().map(|e| e.explicit(t, g)).collect(); 16 | explicit_product(product, &factors, t, g).unwrap_or_else(|| Expr::Prod(product, factors)) 17 | } 18 | Expr::StructInstance(si) => Expr::StructInstance(StructInstance { 19 | struct_name: si.struct_name, 20 | strct: si.strct, 21 | members: si 22 | .members 23 | .into_iter() 24 | .map(|(name, expr)| (name, expr.explicit(t, g))) 25 | .collect(), 26 | }), 27 | } 28 | } 29 | } 30 | 31 | /// Turn `y ^ x` into `-XY(y.0 * x.0)` 32 | fn explicit_product(product: Product, factors: &[Expr], t: &Types, g: &Grammar) -> Option { 33 | if factors.len() < 2 { 34 | return None; 35 | } 36 | let mut names = Vec::with_capacity(factors.len()); 37 | let mut sblades = Vec::with_capacity(factors.len()); 38 | for factor in factors { 39 | if let Expr::Var { name, typ, .. } = factor { 40 | let sblade = typ.clone().into_sblade()?; 41 | names.push(format!("{}.0", name)); 42 | sblades.push(sblade); 43 | } else { 44 | return None; 45 | } 46 | } 47 | 48 | let product_sblade = SBlade::product(product, &sblades, g); 49 | let (sign, type_name) = t.get_sblade(&product_sblade)?; 50 | assert!(sign.abs() == 1); 51 | 52 | use itertools::Itertools; 53 | 54 | let var = Expr::Var { 55 | order: 0, 56 | name: format!("{}({})", type_name, names.iter().join(" * ")), 57 | typ: Type::SBlade(product_sblade), 58 | }; 59 | 60 | if sign == -1 { 61 | Some(Expr::Term(var.into(), -1)) 62 | } else { 63 | Some(var) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /generated/src/pga2d/traits.rs: -------------------------------------------------------------------------------- 1 | /// A value multiplied by its complement is the pseudoscalar. 2 | pub trait LCompl { 3 | type Output; 4 | 5 | /// Left compliment. 6 | /// self.lcompl() * self == pseudo-scalar 7 | fn lcompl(self) -> Self::Output; 8 | } 9 | 10 | /// A value multiplied by its complement is the pseudoscalar. 11 | pub trait RCompl { 12 | type Output; 13 | 14 | /// Right compliment. 15 | /// self * self.rcompl() == pseudo-scalar 16 | /// e0 * e0.rcompl() = e0 * e12 = e012 17 | /// e1.rcompl() = e20 = -e02 18 | fn rcompl(self) -> Self::Output; 19 | } 20 | 21 | /// Reverse the order of the vector indices: 22 | /// e1.rev() = e1 23 | /// e12.rev() = e21 = -e12 24 | /// e012.rev() = e210 = -e012 25 | /// Used for sandwich products 26 | pub trait Reverse { 27 | fn rev(self) -> Self; 28 | } 29 | 30 | pub trait AntiReverse { 31 | /// self.lcompl().rev().rcompl() 32 | fn arev(self) -> Self; 33 | } 34 | 35 | /// x.squared() = x * x 36 | /// Note that all values square to a scalar (could always be zero though). 37 | pub trait Square { 38 | fn square(self) -> f64; 39 | } 40 | 41 | /// The geometric product, a.k.a. normal multiplication. 42 | /// a.geometric(b) = a.dot(b) + a.wedge(b) 43 | pub trait Geometric { 44 | type Output; 45 | fn geometric(self, e: Rhs) -> Self::Output; 46 | } 47 | 48 | /// The anti-geometric product, i.e. the dual version of the geometric product. 49 | /// self.antigeometric(other) = self.lcompl().geometric(other.lcompl()).rcompl() 50 | /// Introduced in http://terathon.com/blog/projective-geometric-algebra-done-right/ 51 | pub trait AntiGeometric { 52 | type Output; 53 | fn anti_geometric(self, e: Rhs) -> Self::Output; 54 | } 55 | 56 | /// The dot product, a.k.a. the inner product. 57 | /// The commutative part of the geometric product. 58 | /// Signifies a metric of how alike two values are. 59 | /// Orthogonal values always dot to zero. 60 | pub trait Dot { 61 | type Output; 62 | fn dot(self, e: Rhs) -> Self::Output; 63 | } 64 | 65 | /// The wedge product, a.k.a. the outer product. 66 | /// x.wedge(y) = x ^ y 67 | /// The anti-commutative (a ^ b = - b ^ a) part of the geometric product. 68 | /// Signifies how unlike two things are. x^x = 0 69 | /// In dual PGA this is the MEET operator, used to intersect two things (e.g. a line ^ plane = point). 70 | pub trait Wedge { 71 | type Output; 72 | fn wedge(self, e: Rhs) -> Self::Output; 73 | } 74 | 75 | /// The regressive product, a.k.a. the anti-wedge product, a.k.a. exterior antiproduct. 76 | /// This is the dual version of the regressive product. 77 | /// x.regressive(y) = x & y = !(!x ^ !y) 78 | /// In dual PHA this is the JOIN operator, used to join two things, e.g. point & line = plane. 79 | pub trait AntiWedge { 80 | type Output; 81 | fn anti_wedge(self, e: Rhs) -> Self::Output; 82 | } 83 | -------------------------------------------------------------------------------- /generated/src/pga3d/traits.rs: -------------------------------------------------------------------------------- 1 | /// A value multiplied by its complement is the pseudoscalar. 2 | pub trait LCompl { 3 | type Output; 4 | 5 | /// Left compliment. 6 | /// self.lcompl() * self == pseudo-scalar 7 | fn lcompl(self) -> Self::Output; 8 | } 9 | 10 | /// A value multiplied by its complement is the pseudoscalar. 11 | pub trait RCompl { 12 | type Output; 13 | 14 | /// Right compliment. 15 | /// self * self.rcompl() == pseudo-scalar 16 | /// e0 * e0.rcompl() = e0 * e12 = e012 17 | /// e1.rcompl() = e20 = -e02 18 | fn rcompl(self) -> Self::Output; 19 | } 20 | 21 | /// Reverse the order of the vector indices: 22 | /// e1.rev() = e1 23 | /// e12.rev() = e21 = -e12 24 | /// e012.rev() = e210 = -e012 25 | /// Used for sandwich products 26 | pub trait Reverse { 27 | fn rev(self) -> Self; 28 | } 29 | 30 | pub trait AntiReverse { 31 | /// self.lcompl().rev().rcompl() 32 | fn arev(self) -> Self; 33 | } 34 | 35 | /// x.squared() = x * x 36 | /// Note that all values square to a scalar (could always be zero though). 37 | pub trait Square { 38 | fn square(self) -> f64; 39 | } 40 | 41 | /// The geometric product, a.k.a. normal multiplication. 42 | /// a.geometric(b) = a.dot(b) + a.wedge(b) 43 | pub trait Geometric { 44 | type Output; 45 | fn geometric(self, e: Rhs) -> Self::Output; 46 | } 47 | 48 | /// The anti-geometric product, i.e. the dual version of the geometric product. 49 | /// self.antigeometric(other) = self.lcompl().geometric(other.lcompl()).rcompl() 50 | /// Introduced in http://terathon.com/blog/projective-geometric-algebra-done-right/ 51 | pub trait AntiGeometric { 52 | type Output; 53 | fn anti_geometric(self, e: Rhs) -> Self::Output; 54 | } 55 | 56 | /// The dot product, a.k.a. the inner product. 57 | /// The commutative part of the geometric product. 58 | /// Signifies a metric of how alike two values are. 59 | /// Orthogonal values always dot to zero. 60 | pub trait Dot { 61 | type Output; 62 | fn dot(self, e: Rhs) -> Self::Output; 63 | } 64 | 65 | /// The wedge product, a.k.a. the outer product. 66 | /// x.wedge(y) = x ^ y 67 | /// The anti-commutative (a ^ b = - b ^ a) part of the geometric product. 68 | /// Signifies how unlike two things are. x^x = 0 69 | /// In dual PGA this is the MEET operator, used to intersect two things (e.g. a line ^ plane = point). 70 | pub trait Wedge { 71 | type Output; 72 | fn wedge(self, e: Rhs) -> Self::Output; 73 | } 74 | 75 | /// The regressive product, a.k.a. the anti-wedge product, a.k.a. exterior antiproduct. 76 | /// This is the dual version of the regressive product. 77 | /// x.regressive(y) = x & y = !(!x ^ !y) 78 | /// In dual PHA this is the JOIN operator, used to join two things, e.g. point & line = plane. 79 | pub trait AntiWedge { 80 | type Output; 81 | fn anti_wedge(self, e: Rhs) -> Self::Output; 82 | } 83 | -------------------------------------------------------------------------------- /generator/templates/traits.rs: -------------------------------------------------------------------------------- 1 | /// A value multiplied by its complement is the pseudoscalar. 2 | pub trait LCompl { 3 | type Output; 4 | 5 | /// Left compliment. 6 | /// self.lcompl() * self == pseudo-scalar 7 | fn lcompl(self) -> Self::Output; 8 | } 9 | 10 | /// A value multiplied by its complement is the pseudoscalar. 11 | pub trait RCompl { 12 | type Output; 13 | 14 | /// Right compliment. 15 | /// self * self.rcompl() == pseudo-scalar 16 | /// e0 * e0.rcompl() = e0 * e12 = e012 17 | /// e1.rcompl() = e20 = -e02 18 | fn rcompl(self) -> Self::Output; 19 | } 20 | 21 | /// Reverse the order of the vector indices: 22 | /// e1.rev() = e1 23 | /// e12.rev() = e21 = -e12 24 | /// e012.rev() = e210 = -e012 25 | /// Used for sandwich products 26 | pub trait Reverse { 27 | fn rev(self) -> Self; 28 | } 29 | 30 | pub trait AntiReverse { 31 | /// self.lcompl().rev().rcompl() 32 | fn arev(self) -> Self; 33 | } 34 | 35 | /// x.squared() = x * x 36 | /// Note that all values square to a scalar (could always be zero though). 37 | pub trait Square { 38 | fn square(self) -> f64; 39 | } 40 | 41 | /// The geometric product, a.k.a. normal multiplication. 42 | /// a.geometric(b) = a.dot(b) + a.wedge(b) 43 | pub trait Geometric { 44 | type Output; 45 | fn geometric(self, e: Rhs) -> Self::Output; 46 | } 47 | 48 | /// The anti-geometric product, i.e. the dual version of the geometric product. 49 | /// self.antigeometric(other) = self.lcompl().geometric(other.lcompl()).rcompl() 50 | /// Introduced in http://terathon.com/blog/projective-geometric-algebra-done-right/ 51 | pub trait AntiGeometric { 52 | type Output; 53 | fn anti_geometric(self, e: Rhs) -> Self::Output; 54 | } 55 | 56 | /// The dot product, a.k.a. the inner product. 57 | /// The commutative part of the geometric product. 58 | /// Signifies a metric of how alike two values are. 59 | /// Orthogonal values always dot to zero. 60 | pub trait Dot { 61 | type Output; 62 | fn dot(self, e: Rhs) -> Self::Output; 63 | } 64 | 65 | /// The wedge product, a.k.a. the outer product. 66 | /// x.wedge(y) = x ^ y 67 | /// The anti-commutative (a ^ b = - b ^ a) part of the geometric product. 68 | /// Signifies how unlike two things are. x^x = 0 69 | /// In dual PGA this is the MEET operator, used to intersect two things (e.g. a line ^ plane = point). 70 | pub trait Wedge { 71 | type Output; 72 | fn wedge(self, e: Rhs) -> Self::Output; 73 | } 74 | 75 | /// The regressive product, a.k.a. the anti-wedge product, a.k.a. exterior antiproduct. 76 | /// This is the dual version of the regressive product. 77 | /// x.regressive(y) = x & y = !(!x ^ !y) 78 | /// In dual PHA this is the JOIN operator, used to join two things, e.g. point & line = plane. 79 | pub trait AntiWedge { 80 | type Output; 81 | fn anti_wedge(self, e: Rhs) -> Self::Output; 82 | } 83 | -------------------------------------------------------------------------------- /generator/src/blade.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// ``` text 4 | /// Blade(vec![]) = scalar 5 | /// Blade(vec![0]) = e0 6 | /// Blade(vec![0, 2]) = e02 7 | /// ``` 8 | /// Always sorted, always unique. 9 | #[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] 10 | pub struct Blade(Vec); 11 | 12 | impl Blade { 13 | pub fn scalar() -> Self { 14 | Blade(vec![]) 15 | } 16 | 17 | pub fn vec(vi: VecIdx) -> Self { 18 | Self(vec![vi]) 19 | } 20 | 21 | pub fn pseudo_scalar(g: &Grammar) -> Self { 22 | Self::from_sorted(g.vecs().collect()) 23 | } 24 | 25 | pub fn from_sorted(vecs: Vec) -> Self { 26 | assert!(is_sorted(&vecs)); 27 | assert!(!has_adjacent_copies(&vecs)); 28 | Self(vecs) 29 | } 30 | 31 | pub fn is_scalar(&self) -> bool { 32 | self.0.is_empty() 33 | } 34 | 35 | pub fn has_vec(&self, needle: VecIdx) -> bool { 36 | self.0.iter().any(|&vi| vi == needle) 37 | } 38 | 39 | /// 0 for scalar, 1 for vector, 2 for multivector etc. 40 | pub fn grade(&self) -> usize { 41 | self.0.len() 42 | } 43 | 44 | pub fn vecs(&self) -> &[VecIdx] { 45 | &self.0 46 | } 47 | 48 | /// Left compliment. 49 | /// self.lcompl() * self == pseudo-scalar 50 | pub fn lcompl(&self, g: &Grammar) -> SBlade { 51 | let compliment: Vec = g.vecs().filter(|&vi| !self.has_vec(vi)).collect(); 52 | let mut all_vecs = compliment.clone(); 53 | all_vecs.append(&mut self.0.clone()); 54 | let scaled_pseudoscalar = SBlade::from_unsorted(&all_vecs); 55 | assert_eq!(scaled_pseudoscalar.blade, Blade::pseudo_scalar(g)); 56 | SBlade { 57 | sign: scaled_pseudoscalar.sign, 58 | blade: Self::from_sorted(compliment), 59 | } 60 | } 61 | 62 | /// Right compliment. 63 | /// self * self.rcompl() == pseudo-scalar 64 | /// e0 * e0.rcompl() = e0 * e12 = e012 65 | /// e1.rcompl() = e20 = -e02 66 | pub fn rcompl(&self, g: &Grammar) -> SBlade { 67 | let compliment: Vec = g.vecs().filter(|&vi| !self.has_vec(vi)).collect(); 68 | let mut all_vecs = self.0.clone(); 69 | all_vecs.append(&mut compliment.clone()); 70 | let scaled_pseudoscalar = SBlade::from_unsorted(&all_vecs); 71 | assert_eq!(scaled_pseudoscalar.blade, Blade::pseudo_scalar(g)); 72 | SBlade { 73 | sign: scaled_pseudoscalar.sign, 74 | blade: Self::from_sorted(compliment), 75 | } 76 | } 77 | } 78 | 79 | impl std::ops::Index for Blade { 80 | type Output = VecIdx; 81 | 82 | fn index(&self, idx: usize) -> &Self::Output { 83 | &self.0[idx] 84 | } 85 | } 86 | 87 | fn has_adjacent_copies(b: &[VecIdx]) -> bool { 88 | for i in 1..b.len() { 89 | if b[i - 1] == b[i] { 90 | return true; 91 | } 92 | } 93 | false 94 | } 95 | 96 | fn is_sorted(b: &[VecIdx]) -> bool { 97 | for i in 1..b.len() { 98 | if b[i - 1] > b[i] { 99 | return false; 100 | } 101 | } 102 | true 103 | } 104 | 105 | impl std::fmt::Debug for Blade { 106 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 107 | use itertools::Itertools; 108 | if self.0.is_empty() { 109 | // Real/Scalar 110 | "s".fmt(f) 111 | } else { 112 | format!("e{}", self.0.iter().join("")).fmt(f) 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /generator/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::BTreeSet, error::Error, fs, io::prelude::*, path::Path}; 2 | 3 | use itertools::Itertools; 4 | 5 | use generator::{gen::*, *}; 6 | 7 | fn main() -> Result<(), Box> { 8 | let mut args = pico_args::Arguments::from_env(); 9 | 10 | let grammar: String = args.value_from_str(["-g", "--grammar"])?; 11 | let out_dir: String = args.value_from_str(["-o", "--out_dir"])?; 12 | let out_dir = Path::new(&out_dir); 13 | 14 | let (grammar, types) = match grammar.as_str() { 15 | "pga2d" => generator::grammars::pga2d(), 16 | "pga3d" => generator::grammars::pga3d(), 17 | _ => panic!("Unknown grammar: '{}'", grammar), 18 | }; 19 | 20 | let settings = Settings::default(); 21 | let gen = Generator { 22 | grammar, 23 | types, 24 | settings, 25 | ro: RustOptions::rust(), 26 | }; 27 | 28 | if false { 29 | // Test: 30 | let line = gen.types.get_struct("Line"); 31 | let point = gen.types.get_struct("Point"); 32 | dbg!(strct::struct_product_type_signature( 33 | &gen, 34 | &("Line", line), 35 | &("Point", point), 36 | Product::Dot 37 | )); 38 | panic!("Planned"); 39 | } 40 | 41 | fs::create_dir_all(out_dir)?; 42 | 43 | let mut mod_file_contents = include_str!("../templates/lib.rs").to_owned(); 44 | 45 | let mut mods = BTreeSet::new(); 46 | mods.insert("traits".to_string()); 47 | mods.insert("blades".to_string()); 48 | 49 | write_file(include_str!("../templates/traits.rs"), &out_dir.join("traits.rs"))?; 50 | write_file(&blades::file(&gen), &out_dir.join("blades.rs"))?; 51 | 52 | mod_file_contents += "\n// Types:\n"; 53 | for (struct_name, strct) in gen.types.structs() { 54 | let mod_name = struct_name.to_ascii_lowercase(); 55 | let file_name = format!("{}.rs", mod_name); 56 | let file_contents = strct::file(&gen, struct_name, strct); 57 | write_file(&file_contents, &out_dir.join(file_name))?; 58 | mod_file_contents += &format!("pub mod {};\n", mod_name); 59 | mods.insert(mod_name); 60 | } 61 | 62 | mod_file_contents += &format!( 63 | "\npub use self::{{\n{}\n}};\n", 64 | mods.iter().map(|mod_name| format!(" {}::*,", mod_name)).join("\n") 65 | ); 66 | 67 | write_file(&mod_file_contents, &out_dir.join("mod.rs"))?; 68 | 69 | Ok(()) 70 | } 71 | 72 | fn write_file(unformatted_contents: &str, final_path: &Path) -> Result<(), Box> { 73 | let temp_file_path = std::env::temp_dir().join("temp.rs"); 74 | fs::File::create(&temp_file_path)?.write_all(unformatted_contents.as_bytes())?; 75 | cargo_fmt(&temp_file_path)?; 76 | 77 | let formatted_contents = fs::read_to_string(temp_file_path)?; 78 | 79 | // Only write file if it has actually changed: 80 | if matches!(fs::read_to_string(final_path), Ok(existing_contents) if existing_contents == formatted_contents) { 81 | // eprintln!("No change to '{}'", final_path.display()); 82 | return Ok(()); 83 | } 84 | 85 | fs::File::create(&final_path)?.write_all(formatted_contents.as_bytes())?; 86 | eprintln!("New file written to '{}'", final_path.display()); 87 | 88 | Ok(()) 89 | } 90 | 91 | fn cargo_fmt(path: &Path) -> Result<(), Box> { 92 | std::process::Command::new("cargo") 93 | .arg("fmt") 94 | .arg("--") 95 | .arg(path) 96 | .output()?; 97 | Ok(()) 98 | } 99 | -------------------------------------------------------------------------------- /generator/src/types.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | 3 | use crate::*; 4 | 5 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 6 | pub struct StructMember { 7 | pub name: String, 8 | pub typ: Type, 9 | } 10 | 11 | pub type Struct = IndexMap; 12 | 13 | /// In order of preference (first match). 14 | #[derive(Clone, Debug, Default)] 15 | pub struct Types { 16 | // types: Vec, 17 | types: IndexMap, 18 | 19 | /// Maps struct names to their declarations 20 | structs: IndexMap, 21 | 22 | /// Maps blades to cannocial sign and name, 23 | /// e.g. [0,2] => -"e20" 24 | blades: IndexMap, 25 | } 26 | 27 | impl Types { 28 | pub fn insert_blade(&mut self, name: &str, sblade: SBlade) { 29 | self.blades 30 | .insert(sblade.blade.clone(), (sblade.sign, name.to_string())); 31 | self.types.insert(name.to_string(), Type::SBlade(sblade)); 32 | } 33 | 34 | pub fn insert_struct(&mut self, name: &str, members: &[(&str, &str)]) { 35 | let strct: Struct = members 36 | .iter() 37 | .map(|(member, type_name)| { 38 | ( 39 | member.to_string(), 40 | StructMember { 41 | name: type_name.to_string(), 42 | typ: self.get(type_name).clone(), 43 | }, 44 | ) 45 | }) 46 | .collect(); 47 | self.structs.insert(name.to_string(), strct.clone()); 48 | 49 | let struct_type = strct.into_iter().map(|(key, val)| (key, val.typ)).collect(); 50 | self.types.insert(name.to_string(), Type::Struct(struct_type)); 51 | } 52 | 53 | pub fn get(&self, name: &str) -> &Type { 54 | self.types 55 | .get(name) 56 | .unwrap_or_else(|| panic!("Failed to find type '{}'", name)) 57 | } 58 | 59 | pub fn get_struct(&self, name: &str) -> &Struct { 60 | self.structs 61 | .get(name) 62 | .unwrap_or_else(|| panic!("Failed to find struct '{}'", name)) 63 | } 64 | 65 | /// Maps blades to cannocial sign and name, 66 | pub fn get_blade(&self, blade: &Blade) -> Option<&(i32, String)> { 67 | self.blades.get(blade) 68 | } 69 | 70 | /// Maps signed blades to cannocial sign and name, 71 | pub fn get_sblade(&self, sblade: &SBlade) -> Option<(i32, &str)> { 72 | let (sign, output_sblade_name) = self.get_blade(&sblade.blade)?; 73 | Some((sblade.sign * sign, output_sblade_name)) 74 | } 75 | 76 | pub fn type_name(&self, typ: &Type) -> &str { 77 | match typ { 78 | Type::SBlade(sblade) if sblade.is_zero() => "Zero", 79 | Type::SBlade(sblade) => self.get_blade(&sblade.blade).unwrap().1.as_str(), 80 | Type::Struct(_) => self 81 | .structs() 82 | .find(|(_, strct)| &Type::strct(strct) == typ) 83 | .map(|(name, _)| name) 84 | .unwrap(), 85 | _ => todo!("Get name of type {:?}", typ), 86 | } 87 | } 88 | 89 | pub fn sblades(&self) -> Vec<(&str, SBlade)> { 90 | self.blades 91 | .iter() 92 | .map(|(blade, (sign, name))| (name.as_str(), SBlade::signed_blade(*sign, blade.clone()))) 93 | .collect() 94 | } 95 | 96 | pub fn unit_blades(&self) -> Vec { 97 | self.sblades() 98 | .iter() 99 | .map(|(_name, sblade)| Expr::sblade(sblade)) 100 | .collect() 101 | } 102 | 103 | pub fn structs(&self) -> impl Iterator { 104 | self.structs.iter().map(|(name, strct)| (name.as_str(), strct)) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /generator/src/typ.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// A type is some sort of multivector. 4 | /// A value is a linear combination of types. 5 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 6 | pub enum Type { 7 | /// Special type: a value that is always the magnitude 1. 8 | /// This is useful for describing a normalized point with {x: X, y: Y, z: Z, w: One(W)} 9 | Constant(SBlade), 10 | /// Has a sign so that we can normalize e20 to -e02 11 | SBlade(SBlade), 12 | /// named members 13 | Struct(Vec<(String, Type)>), 14 | } 15 | 16 | impl Type { 17 | pub fn zero() -> Self { 18 | Type::SBlade(SBlade::zero()) 19 | } 20 | 21 | /// Special type: a value that is always the scalar value 1. 22 | /// This is useful for describing a normalized point with {x: X, y: Y, z: Z, w: 1} 23 | pub fn constant(sblade: SBlade) -> Self { 24 | Type::Constant(sblade) 25 | } 26 | 27 | pub fn scalar() -> Self { 28 | Type::SBlade(SBlade::scalar()) 29 | } 30 | 31 | pub fn vec(vi: VecIdx) -> Self { 32 | Type::SBlade(SBlade::vec(vi)) 33 | } 34 | 35 | pub fn strct(s: &Struct) -> Self { 36 | Self::Struct(s.iter().map(|(name, mem)| (name.clone(), mem.typ.clone())).collect()) 37 | } 38 | 39 | pub fn is_zero(&self) -> bool { 40 | match self { 41 | Type::Constant(sb) => sb.is_zero(), 42 | Type::SBlade(sb) => sb.is_zero(), 43 | Type::Struct(_) => false, 44 | } 45 | } 46 | 47 | pub fn is_negative(&self) -> bool { 48 | match self { 49 | Type::Constant(sb) => sb.is_negative(), 50 | Type::SBlade(sb) => sb.is_negative(), 51 | Type::Struct(_) => todo!(), 52 | } 53 | } 54 | 55 | pub fn is_blade(&self, blade: &Blade) -> bool { 56 | match self { 57 | Type::Constant(_) => todo!(), 58 | Type::SBlade(sb) => sb.blade == *blade, 59 | Type::Struct(_) => false, 60 | } 61 | } 62 | 63 | pub fn into_sblade(self) -> Option { 64 | match self { 65 | Type::Constant(sb) => Some(sb), 66 | Type::SBlade(sb) => Some(sb), 67 | Type::Struct(_) => None, 68 | } 69 | } 70 | 71 | pub fn unit(&self) -> Expr { 72 | match self { 73 | Type::Constant(sblade) | Type::SBlade(sblade) => Expr::sblade(sblade), 74 | _ => todo!(), 75 | } 76 | } 77 | 78 | pub fn unary(&self, unary: Unary, g: Option<&Grammar>) -> Option { 79 | match self { 80 | Type::Constant(sblade) | Type::SBlade(sblade) => Some(Type::SBlade(sblade.unary(unary, g?))), 81 | _ => todo!("{:?}.{}()", self, unary.name()), 82 | } 83 | } 84 | } 85 | 86 | impl Expr { 87 | pub fn typ(&self, g: Option<&Grammar>) -> Option { 88 | match self { 89 | Expr::Var { typ, .. } => Some(typ.clone()), 90 | Expr::Term(_, 0) => Some(Type::zero()), 91 | Expr::Term(expr, _) => expr.typ(g), 92 | Expr::Vec(vi) => Some(Type::vec(*vi)), 93 | Expr::Unary(unary, expr) => expr.typ(g).and_then(|t| t.unary(*unary, g)), 94 | Expr::Sum(terms) => { 95 | if terms.is_empty() { 96 | Some(Type::zero()) 97 | } else { 98 | let mut types = std::collections::BTreeSet::new(); 99 | for e in terms { 100 | types.insert(e.typ(g)?); 101 | } 102 | assert!(!types.is_empty()); 103 | if types.len() == 1 { 104 | Some(types.into_iter().next().unwrap()) 105 | } else { 106 | None // TODO: could be a struct? 107 | } 108 | } 109 | } 110 | Expr::Prod(product, factors) => product_type(*product, factors, g), 111 | Expr::StructInstance(StructInstance { strct, .. }) => Some(Type::strct(strct)), 112 | } 113 | } 114 | } 115 | 116 | fn product_type(product: Product, factors: &[Expr], g: Option<&Grammar>) -> Option { 117 | if factors.is_empty() { 118 | Some(Type::scalar()) // TODO: Type::One ? 119 | } else if factors.len() == 1 { 120 | factors[0].typ(g) 121 | } else { 122 | let types: Option> = factors.iter().map(|f| f.typ(g)).collect(); 123 | let types = types?; 124 | if types.iter().any(Type::is_zero) { 125 | return Some(Type::zero()); 126 | } 127 | let sblades: Option> = types.into_iter().map(Type::into_sblade).collect(); 128 | let sblades = sblades?; 129 | 130 | if let Some(g) = g { 131 | Some(Type::SBlade(SBlade::product(product, &sblades, g))) 132 | } else { 133 | None 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /generator/src/rust.rs: -------------------------------------------------------------------------------- 1 | ///! Module for formatting as rust code 2 | use itertools::Itertools; 3 | 4 | use crate::*; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct RustOptions { 8 | /// Output "a ^ b" if true, else "a.wedge(b)" 9 | pub operators: bool, 10 | 11 | /// "Default::default()" or "0" 12 | pub zero_expr: String, 13 | } 14 | 15 | impl RustOptions { 16 | pub fn rust() -> Self { 17 | Self { 18 | operators: false, 19 | zero_expr: "Default::default()".to_owned(), 20 | } 21 | } 22 | pub fn readable() -> Self { 23 | Self { 24 | operators: true, 25 | zero_expr: "0".to_owned(), 26 | } 27 | } 28 | } 29 | 30 | struct RustExpr(Precedence, String); 31 | 32 | #[derive(Eq, Ord, PartialEq, PartialOrd)] 33 | enum Precedence { 34 | Sum, 35 | Product, 36 | Atom, 37 | } 38 | 39 | impl RustExpr { 40 | fn atom(s: impl ToString) -> Self { 41 | RustExpr(Precedence::Atom, s.to_string()) 42 | } 43 | 44 | fn enclose_if_less(&self, p: Precedence) -> String { 45 | if self.0 < p { 46 | format!("({})", self.1) 47 | } else { 48 | self.1.clone() 49 | } 50 | } 51 | } 52 | 53 | impl Expr { 54 | pub fn rust(&self, ro: &RustOptions) -> String { 55 | self.rust_expr(ro).1 56 | } 57 | 58 | // Helper for tests: output with operators (a ^ b) 59 | pub fn rust_concise(&self) -> String { 60 | self.rust(&RustOptions::readable()) 61 | } 62 | 63 | fn rust_expr(&self, ro: &RustOptions) -> RustExpr { 64 | match self { 65 | Expr::Var { name, .. } => RustExpr::atom(name), 66 | Expr::Vec(vi) => { 67 | // You should call expr.typify() before .rust(ro) to get more readable vector names 68 | RustExpr::atom(format!("_e{}", vi.0)) 69 | } 70 | Expr::Term(expr, s) => { 71 | if expr.is_one() { 72 | RustExpr::atom(s) 73 | } else if *s == -1 { 74 | RustExpr( 75 | Precedence::Product, 76 | format!("-{}", expr.rust_expr(ro).enclose_if_less(Precedence::Product)), 77 | ) 78 | } else { 79 | RustExpr( 80 | Precedence::Product, 81 | format!("{} * {}", s, expr.rust_expr(ro).enclose_if_less(Precedence::Product)), 82 | ) 83 | } 84 | } 85 | Expr::Unary(unary, expr) => RustExpr::atom(format!( 86 | "{}.{}()", 87 | expr.rust_expr(ro).enclose_if_less(Precedence::Atom), 88 | unary.name() 89 | )), 90 | Expr::Sum(terms) => { 91 | if terms.is_empty() { 92 | RustExpr::atom("0") 93 | } else if terms.len() == 1 { 94 | terms[0].rust_expr(ro) 95 | } else { 96 | // RustExpr(Precedence::Sum, terms.iter().map(|term| term.rust(ro)).join(" + ")) 97 | let mut s = terms[0].rust(ro); 98 | for t in &terms[1..] { 99 | if t.is_negation() { 100 | s += &format!(" - {}", t.clone().negate().rust(ro)); 101 | } else { 102 | s += &format!(" + {}", t.rust(ro)); 103 | } 104 | } 105 | RustExpr(Precedence::Sum, s) 106 | } 107 | } 108 | Expr::Prod(product, factors) => { 109 | if factors.is_empty() { 110 | match product { 111 | Product::Geometric | Product::Wedge | Product::Dot => RustExpr::atom("1"), 112 | _ => todo!(), 113 | } 114 | } else if factors.len() == 1 { 115 | factors[0].rust_expr(ro) 116 | } else { 117 | if ro.operators { 118 | let operator = format!(" {} ", product.symbol()); 119 | RustExpr( 120 | Precedence::Product, 121 | factors 122 | .iter() 123 | .map(|factor| factor.rust_expr(ro).enclose_if_less(Precedence::Product)) 124 | .join(&operator), 125 | ) 126 | } else { 127 | let mut code = factors[0].rust_expr(ro).enclose_if_less(Precedence::Atom); 128 | for factor in factors.iter().skip(1) { 129 | code += &format!(".{}({})", product.trait_function_name(), factor.rust(ro)) 130 | } 131 | RustExpr(Precedence::Atom, code) 132 | } 133 | } 134 | } 135 | Expr::StructInstance(StructInstance { 136 | struct_name, members, .. 137 | }) => { 138 | let maxw = members.iter().map(|(name, _)| name.len()).max().unwrap_or_default(); 139 | RustExpr::atom(format!( 140 | "{} {{\n{}\n}}", 141 | struct_name, 142 | indent( 143 | &members 144 | .iter() 145 | .map(|(name, expr)| { 146 | let code = if expr.is_zero() { 147 | ro.zero_expr.clone() 148 | } else { 149 | expr.rust(ro) 150 | }; 151 | format!("{:maxw$}: {},", name, code, maxw = maxw) 152 | }) 153 | .join("\n") 154 | ) 155 | )) 156 | } 157 | } 158 | } 159 | } 160 | 161 | pub fn indent(s: &str) -> String { 162 | with_line_prefixes(" ", s) 163 | } 164 | 165 | pub fn indent_n(n: usize, s: &str) -> String { 166 | with_line_prefixes(&" ".repeat(n), s) 167 | } 168 | 169 | pub fn with_line_prefixes(prefix: &str, s: &str) -> String { 170 | s.lines().map(|line| format!("{}{}", prefix, line)).join("\n") 171 | } 172 | -------------------------------------------------------------------------------- /generator/src/typify.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use crate::*; 4 | 5 | /// Sum of the values, grouped by their types. 6 | type Value = BTreeMap; 7 | 8 | // fn show_value(v: &Value) -> String { 9 | // use itertools::Itertools; 10 | // format!( 11 | // "{{\n{}\n}}", 12 | // v.iter() 13 | // .map(|(k, v)| format!(" {:6?}: {},", k, v.rust_concise())) 14 | // .join("\n") 15 | // ) 16 | // } 17 | 18 | impl Expr { 19 | /// Detect named types and replace those expressions with named versions. 20 | pub fn typify(mut self, t: &Types, g: &Grammar) -> Self { 21 | if let Expr::Sum(terms) = &self { 22 | // eprintln!("typify Sum: {}", self.rust()); 23 | if let Some(value) = as_value(&terms, Some(g)) { 24 | if let Some(s) = find_struct(&value, t) { 25 | // eprintln!("typify Sum {} as struct {}", self.rust(), s.struct_name); 26 | self = Expr::StructInstance(s); 27 | } 28 | } 29 | } 30 | 31 | // A blade? 32 | if let Some(sblade) = self.as_sblade(g) { 33 | // eprintln!("typify sblade: {}", self.rust()); 34 | if sblade.is_zero() { 35 | self = Expr::zero(); 36 | } else if sblade.is_scalar() { 37 | self = Expr::scalar(sblade.sign); 38 | } else if let Some((canon_sign, canon_name)) = t.get_blade(&sblade.blade) { 39 | let canon_sign = *canon_sign; 40 | let canon_type = Type::SBlade(SBlade { 41 | sign: canon_sign, 42 | blade: sblade.blade.clone(), 43 | }); 44 | let order = sblade.grade(); // TODO 45 | let blade_var = Expr::var(order, &canon_name, &canon_type); 46 | let scalar = sblade.sign * canon_sign; 47 | self = match scalar { 48 | 0 => Expr::zero(), 49 | 1 => blade_var, 50 | _ => Expr::Term(blade_var.into(), scalar), 51 | }; 52 | } 53 | } 54 | 55 | match self { 56 | Expr::Var { .. } | Expr::Vec(_) => self, 57 | Expr::Term(expr, s) => Expr::Term(expr.typify(t, g).into(), s), 58 | Expr::Unary(unary, expr) => Expr::Unary(unary, expr.typify(t, g).into()), 59 | Expr::Sum(terms) => Expr::Sum(terms.into_iter().map(|e| e.typify(t, g)).collect()), 60 | Expr::Prod(prod, factors) => Expr::Prod(prod, factors.into_iter().map(|e| e.typify(t, g)).collect()), 61 | Expr::StructInstance(StructInstance { 62 | struct_name, 63 | strct, 64 | members, 65 | }) => Expr::StructInstance(StructInstance { 66 | struct_name, 67 | strct, 68 | members: members.into_iter().map(|(name, e)| (name, e.typify(t, g))).collect(), 69 | }), 70 | } 71 | } 72 | } 73 | 74 | fn as_value(terms: &[Expr], g: Option<&Grammar>) -> Option { 75 | let mut parts: BTreeMap> = Default::default(); 76 | for term in terms { 77 | // eprintln!("as_value {} typ: {:?}", term.rust(), term.typ(g)); 78 | let typ = term.typ(g)?; 79 | if !typ.is_zero() { 80 | match typ { 81 | Type::Constant(sblade) | Type::SBlade(sblade) => { 82 | let term = if sblade.is_negative() { 83 | term.clone().negate() 84 | } else { 85 | term.clone() 86 | }; 87 | parts.entry(sblade.blade).or_default().push(term); 88 | } 89 | Type::Struct { .. } => { 90 | return None; 91 | } 92 | } 93 | } 94 | } 95 | 96 | Some( 97 | parts 98 | .into_iter() 99 | .map(|(typ, terms)| (typ, Expr::Sum(terms).simplify(g))) 100 | .collect(), 101 | ) 102 | } 103 | 104 | fn find_struct(sum: &Value, t: &Types) -> Option { 105 | if sum.is_empty() { 106 | return None; // zero: no struct for this! 107 | } 108 | if sum.len() <= 1 { 109 | return None; // Not really a struct 110 | } 111 | 112 | // eprintln!("find_struct for {}", show_value(sum)); 113 | 114 | for (name, strct) in t.structs() { 115 | if let Some(instance) = as_struct_instance(name, strct, &sum) { 116 | return Some(instance); 117 | } 118 | } 119 | None 120 | } 121 | 122 | fn as_struct_instance(struct_name: &str, strct: &Struct, value: &Value) -> Option { 123 | if value.keys().all(|b| is_blade_in_struct(strct, b)) { 124 | Some(StructInstance { 125 | struct_name: struct_name.to_owned(), 126 | strct: strct.clone(), 127 | members: strct 128 | .iter() 129 | .map(|(name, mem)| (name.to_string(), find_term(&mem.typ, value).unwrap_or_else(Expr::zero))) 130 | .collect(), 131 | }) 132 | } else { 133 | None 134 | } 135 | } 136 | 137 | fn is_blade_in_struct(strct: &Struct, blade: &Blade) -> bool { 138 | strct.iter().any(|(_, mem)| mem.typ.is_blade(blade)) 139 | } 140 | 141 | fn find_term(needle: &Type, value: &Value) -> Option { 142 | if let Some(needle) = needle.clone().into_sblade() { 143 | for (blade, expr) in value { 144 | if &needle.blade == blade { 145 | return Some(match needle.sign { 146 | 1 => expr.clone().simplify(None), 147 | -1 => expr.clone().negate().simplify(None), 148 | _ => unreachable!(), 149 | }); 150 | } 151 | } 152 | } 153 | None 154 | } 155 | -------------------------------------------------------------------------------- /generator/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod blade; 2 | pub mod documentation; 3 | mod explicit; 4 | mod expr; 5 | pub mod gen; 6 | pub mod grammars; 7 | pub mod markdown; 8 | mod rust; 9 | mod sblade; 10 | mod simplify; 11 | mod typ; 12 | mod types; 13 | mod typify; 14 | 15 | pub use {blade::*, expr::*, rust::*, sblade::*, typ::*, types::*}; 16 | 17 | /// Which base vector (e0, e1 or e2?) 18 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, derive_more::Display)] 19 | pub struct VecIdx(pub usize); 20 | 21 | /// Types of distributative unary operations 22 | #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, strum_macros::EnumIter)] 23 | pub enum Unary { 24 | /// Right compliment. 25 | /// The right compliment of a blade B is defined so that 26 | /// B * RCompl(B) = PseudoScalar 27 | /// distributive: RCompl(a + b) = RCompl(a) + RCompl(b) 28 | RCompl, 29 | 30 | /// Left compliment. 31 | /// The left compliment of a blade B is defined so that 32 | /// LCompl(B) * B = PseudoScalar 33 | /// distributive: LCompl(a + b) = LCompl(a) + LCompl(b) 34 | LCompl, 35 | 36 | /// Reverse the order of the vector indices: 37 | /// e1.reverse() = e1 38 | /// e12.reverse() = e21 = -e12 39 | /// e012.reverse() = e210 = -e012 40 | /// Used for sandwich products 41 | Reverse, 42 | 43 | /// x.anti_reverse() == 44 | /// x.lcompl().reverse().rcompl() 45 | /// x.rcompl().reverse().lcompl() 46 | /// Used for anti-sandwich-products 47 | AntiReverse, 48 | } 49 | 50 | #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, strum_macros::EnumIter)] 51 | pub enum Product { 52 | /// Geom = Inner + Outer = Dot + Wedge 53 | Geometric, 54 | /// Geometric Antiproduct = rcompl(lcompl(a) * lcompl(b)) 55 | AntiGeometric, 56 | 57 | /// Inner / dot product. 58 | /// The commutative part of the geometric product. 59 | /// Measures sameness of blades. 60 | Dot, 61 | 62 | /// Outer (progressive) product. Moves to a higher dimension. Join. 63 | /// The anti-commutative part of the geometric product. 64 | /// Measures difference between blades. 65 | Wedge, 66 | 67 | /// Regressive. Reduces the dimensionality. Meet. 68 | AntiWedge, 69 | } 70 | 71 | /// what you get when you square the input vectors, 72 | /// e.g. [0, 1, 1] would specify the 2d gpa of e0^2=0 e1^2=1 e2^2=1 73 | pub struct Grammar(pub Vec); 74 | 75 | // ---------------------------------------------------------------------------- 76 | 77 | impl Grammar { 78 | pub fn square_geom(&self, v: VecIdx) -> i32 { 79 | self.0[v.0] 80 | } 81 | 82 | /// What do we get when we multiply the given base vector with itself using the given product? 83 | pub fn square_with(&self, product: Product, v: VecIdx) -> Option { 84 | match product { 85 | Product::Geometric => Some(self.0[v.0]), 86 | Product::AntiGeometric => None, // TODO 87 | Product::Dot => Some(self.0[v.0]), 88 | Product::Wedge => Some(0), 89 | Product::AntiWedge => Some(0), 90 | } 91 | } 92 | 93 | /// Number of base vectors, i.e. the dimensionaltiy of the grammar 94 | pub fn num_vecs(&self) -> usize { 95 | self.0.len() 96 | } 97 | 98 | pub fn vecs(&self) -> impl Iterator { 99 | (0..self.num_vecs()).map(VecIdx) 100 | } 101 | } 102 | 103 | impl Unary { 104 | pub fn name(self) -> &'static str { 105 | match self { 106 | Unary::LCompl => "lcompl", 107 | Unary::RCompl => "rcompl", 108 | Unary::Reverse => "rev", 109 | Unary::AntiReverse => "arev", 110 | } 111 | } 112 | 113 | pub fn short_description(self) -> &'static str { 114 | match self { 115 | Unary::LCompl => "Left complement", 116 | Unary::RCompl => "Right complement", 117 | Unary::Reverse => "Reverse", 118 | Unary::AntiReverse => "Anti-reverse", 119 | } 120 | } 121 | 122 | /// whan undoes this operation? 123 | pub fn undoer(self) -> Self { 124 | match self { 125 | Unary::LCompl => Unary::RCompl, 126 | Unary::RCompl => Unary::LCompl, 127 | Unary::Reverse => Unary::Reverse, 128 | Unary::AntiReverse => Unary::AntiReverse, 129 | } 130 | } 131 | 132 | pub fn trait_name(self) -> &'static str { 133 | match self { 134 | Unary::LCompl => "LCompl", 135 | Unary::RCompl => "RCompl", 136 | Unary::Reverse => "Reverse", 137 | Unary::AntiReverse => "AntiReverse", 138 | } 139 | } 140 | 141 | pub fn trait_function_name(self) -> &'static str { 142 | match self { 143 | Unary::LCompl => "lcompl", 144 | Unary::RCompl => "rcompl", 145 | Unary::Reverse => "rev", 146 | Unary::AntiReverse => "arev", 147 | } 148 | } 149 | 150 | pub fn trait_has_output_type(self) -> bool { 151 | matches!(self, Unary::LCompl | Unary::RCompl) 152 | } 153 | } 154 | 155 | impl Product { 156 | pub fn symbol(self) -> &'static str { 157 | match self { 158 | Product::Geometric => "*", 159 | Product::AntiGeometric => "!*", // TODO 160 | Product::Dot => "|", 161 | Product::Wedge => "^", 162 | Product::AntiWedge => "&", 163 | } 164 | } 165 | 166 | pub fn trait_name(self) -> &'static str { 167 | match self { 168 | Product::Geometric => "Geometric", 169 | Product::AntiGeometric => "AntiGeometric", 170 | Product::Dot => "Dot", 171 | Product::Wedge => "Wedge", 172 | Product::AntiWedge => "AntiWedge", 173 | } 174 | } 175 | 176 | pub fn trait_function_name(self) -> &'static str { 177 | match self { 178 | Product::Geometric => "geometric", // TODO: shorten 179 | Product::AntiGeometric => "anti_geometric", // TODO: shorten 180 | Product::Dot => "dot", 181 | Product::Wedge => "wedge", 182 | Product::AntiWedge => "anti_wedge", 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /generator/src/expr.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | /// A value expression, e.g. `5 + X ^ (2 * Y - 1)` 4 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 5 | pub enum Expr { 6 | /// Variable of a given type 7 | Var { 8 | /// This is the sort order when rearranging expressions. 9 | /// You can use this to always place "self" to the left of "rhs", for instance. 10 | order: usize, 11 | name: String, 12 | typ: Type, 13 | }, 14 | // A unit length base vector 15 | Vec(VecIdx), 16 | 17 | /// Indicates a scalar times something. 18 | /// Wedge(vec![X, 3, Y, 4]) simplifies to Term(Wedge(vec![X, Y]), 12) 19 | /// In its simplest form, the scalar is never 0 or 1 20 | /// 0 == Sum(vec![]) 21 | /// 1 == Prod(_, vec![]) 22 | Term(Box, i32), 23 | 24 | /// Unary operation, e.g. a complement or a reverse 25 | Unary(Unary, Box), 26 | 27 | /// 0 == Sum(vec![]) 28 | Sum(Vec), 29 | 30 | /// 1 == Prod(_, vec![]) 31 | Prod(Product, Vec), 32 | 33 | /// An instance of a struct, e.g. `Point { x: ..., y: ... }` etc. 34 | StructInstance(StructInstance), 35 | } 36 | 37 | /// An instance of a struct, e.g. `Point { x: ..., y: ... }` etc. 38 | #[derive(Clone, Debug, Eq, PartialEq)] 39 | pub struct StructInstance { 40 | pub struct_name: String, 41 | pub strct: Struct, 42 | pub members: Vec<(String, Expr)>, 43 | } 44 | 45 | impl StructInstance { 46 | pub fn count_zeros(&self) -> usize { 47 | self.members.iter().filter(|(_, expr)| expr.is_zero()).count() 48 | } 49 | } 50 | 51 | impl std::cmp::Ord for StructInstance { 52 | fn cmp(&self, other: &StructInstance) -> std::cmp::Ordering { 53 | self.members.cmp(&other.members) 54 | } 55 | } 56 | 57 | impl std::cmp::PartialOrd for StructInstance { 58 | fn partial_cmp(&self, other: &StructInstance) -> Option { 59 | Some(self.cmp(other)) 60 | } 61 | } 62 | 63 | impl Expr { 64 | pub fn zero() -> Expr { 65 | Expr::Sum(vec![]) 66 | } 67 | 68 | pub fn one() -> Expr { 69 | Expr::Prod(Product::Geometric, vec![]) 70 | } 71 | 72 | pub fn scalar(s: i32) -> Self { 73 | match s { 74 | 0 => Self::zero(), 75 | 1 => Self::one(), 76 | s => Expr::Term(Expr::one().into(), s), 77 | } 78 | } 79 | 80 | pub fn vec(vi: VecIdx) -> Self { 81 | Expr::Vec(vi) 82 | } 83 | 84 | /// One magnitude sblade 85 | pub fn sblade(sblade: &SBlade) -> Self { 86 | let expr = match sblade.blade.grade() { 87 | 0 => Expr::one(), 88 | 1 => Expr::Vec(sblade.blade[0]), 89 | _ => Expr::wedge(sblade.blade.vecs().iter().copied().map(Expr::Vec).collect()), 90 | }; 91 | match sblade.sign { 92 | -1 => expr.negate(), 93 | 0 => Expr::zero(), 94 | 1 => expr, 95 | _ => unreachable!(), 96 | } 97 | } 98 | 99 | pub fn var(order: usize, name: impl ToString, typ: &Type) -> Self { 100 | Expr::Var { 101 | order, 102 | name: name.to_string(), 103 | typ: typ.clone(), 104 | } 105 | } 106 | 107 | pub fn unary(unary: Unary, expr: Expr) -> Self { 108 | Expr::Unary(unary, expr.into()) 109 | } 110 | 111 | pub fn geometric(factors: Vec) -> Self { 112 | Expr::Prod(Product::Geometric, factors) 113 | } 114 | 115 | pub fn dot(factors: Vec) -> Self { 116 | Expr::Prod(Product::Dot, factors) 117 | } 118 | 119 | /// outer product 120 | pub fn wedge(factors: Vec) -> Self { 121 | Expr::Prod(Product::Wedge, factors) 122 | } 123 | 124 | /// also known as the regressive product 125 | pub fn antiwedge(factors: Vec) -> Self { 126 | Expr::Prod(Product::AntiWedge, factors) 127 | } 128 | 129 | /// Note: self must be simplified 130 | pub fn is_zero(&self) -> bool { 131 | self == &Self::zero() 132 | } 133 | 134 | /// Note: self must be simplified 135 | pub fn is_one(&self) -> bool { 136 | match self { 137 | Expr::Prod(Product::Geometric, factors) if factors.is_empty() => true, 138 | _ => false, 139 | } 140 | } 141 | 142 | pub fn is_negation(&self) -> bool { 143 | match self { 144 | Expr::Term(_op, s) => *s < 0, 145 | _ => false, 146 | } 147 | } 148 | 149 | pub fn negate(self) -> Self { 150 | match self { 151 | Expr::Term(expr, -1) => *expr, 152 | Expr::Term(expr, s) => Expr::Term(expr, -s), 153 | expr => Expr::Term(expr.into(), -1), 154 | } 155 | } 156 | 157 | pub fn as_scalar(&self) -> Option { 158 | match self { 159 | Expr::Term(expr, s) if expr.is_one() => Some(*s), 160 | _ => None, 161 | } 162 | } 163 | 164 | /// Returns this Expr in terms of a multiple of a blade, if possible 165 | pub fn as_sblade(&self, g: &Grammar) -> Option { 166 | match self { 167 | Expr::Var { .. } => None, 168 | Expr::Vec(vi) => Some(SBlade::vec(*vi)), 169 | Expr::Term(expr, s) => { 170 | if let Some(sblade) = expr.as_sblade(g) { 171 | Some(*s * sblade) 172 | } else { 173 | None 174 | } 175 | } 176 | Expr::Unary(unary, expr) => Some(expr.as_sblade(g)?.unary(*unary, g)), 177 | Expr::Sum(terms) => { 178 | if terms.is_empty() { 179 | Some(SBlade::zero()) 180 | } else if terms.len() == 1 { 181 | terms[0].as_sblade(g) 182 | } else { 183 | None // assuming we are simplified 184 | } 185 | } 186 | Expr::Prod(product, factors) => { 187 | let sblades: Option> = factors.iter().map(|f| f.as_sblade(g)).collect(); 188 | let sblades = sblades?; 189 | Some(SBlade::product(*product, &sblades, g)) 190 | } 191 | Expr::StructInstance { .. } => None, 192 | } 193 | } 194 | } 195 | 196 | #[cfg(test)] 197 | mod tests { 198 | use super::*; 199 | 200 | use crate::sblade::tests::sb; 201 | 202 | #[test] 203 | fn test_as_sblade() { 204 | let grammar = Grammar(vec![0, 1, 1, 1]); 205 | let g = &grammar; 206 | let v0 = VecIdx(0); 207 | let v1 = VecIdx(1); 208 | // let v2 = VecIdx(2); 209 | // let v3 = VecIdx(3); 210 | 211 | // assert_eq!(Expr::dot(vec![Expr::vec(v0), Expr::vec(v0)]).as_sblade(g), Some(sb("0"))); 212 | // assert_eq!(Expr::dot(vec![Expr::vec(v1), Expr::vec(v1)]).as_sblade(g), Some(sb("1"))); 213 | assert_eq!( 214 | Expr::wedge(vec![Expr::vec(v0), Expr::vec(v0)]).as_sblade(g), 215 | Some(sb("0")) 216 | ); 217 | assert_eq!( 218 | Expr::wedge(vec![Expr::vec(v1), Expr::vec(v1)]).as_sblade(g), 219 | Some(sb("0")) 220 | ); 221 | assert_eq!( 222 | Expr::geometric(vec![Expr::vec(v0), Expr::vec(v0)]).as_sblade(g), 223 | Some(sb("0")) 224 | ); 225 | assert_eq!( 226 | Expr::geometric(vec![Expr::vec(v1), Expr::vec(v1)]).as_sblade(g), 227 | Some(sb("s")) 228 | ); 229 | assert_eq!( 230 | Expr::wedge(vec![Expr::vec(v0), Expr::vec(v1)]).as_sblade(g), 231 | Some(sb("e01")) 232 | ); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /generator/src/grammars.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub fn pga2d() -> (Grammar, Types) { 4 | let g = Grammar(vec![1, 1, 0]); 5 | let mut t = Types::default(); 6 | let x = VecIdx(0); 7 | let y = VecIdx(1); 8 | let w = VecIdx(2); 9 | t.insert_blade("S", SBlade::scalar()); 10 | t.insert_blade("X", SBlade::vec(x)); 11 | t.insert_blade("Y", SBlade::vec(y)); 12 | t.insert_blade("W", SBlade::vec(w)); 13 | t.insert_blade("YW", SBlade::from_unsorted(&[y, w])); 14 | t.insert_blade("WX", SBlade::from_unsorted(&[w, x])); 15 | t.insert_blade("XY", SBlade::from_unsorted(&[x, y])); 16 | t.insert_blade("XYW", SBlade::from_unsorted(&[x, y, w])); 17 | 18 | // TODO: Point { x: X, y: Y, z: Z, w: 1) 19 | t.insert_struct("Vec2", &[("x", "X"), ("y", "Y")]); 20 | t.insert_struct("Vec3", &[("x", "X"), ("y", "Y"), ("w", "W")]); 21 | 22 | // TODO: verify if these are Plücker coordinates 23 | t.insert_struct( 24 | "Line", 25 | &[ 26 | // dir : 27 | ("dx", "YW"), 28 | ("dy", "WX"), 29 | // offset / moment: 30 | ("m", "XY"), 31 | ], 32 | ); 33 | 34 | // TODO: is this correct? 35 | // t.insert_struct("Translator", &[("s", "S"), ("yw", "YW"), ("wx", "WX")]); 36 | t.insert_struct("Rotor", &[("s", "S"), ("xy", "XY")]); 37 | t.insert_struct("Motor", &[("s", "S"), ("yw", "YW"), ("wx", "WX"), ("xy", "XY")]); 38 | // TODO: Is this a Motor? Or a Transform? 39 | // t.insert_struct( 40 | // "Transform", 41 | // &[("s", "S"), ("yw", "YW"), ("wx", "WX"), ("xy", "XY"), ("xyw", "XYW")], 42 | // ); 43 | 44 | (g, t) 45 | } 46 | 47 | /// Using the Eric Lengyel system, but with X,Y,Z,W instead of e1,e2,e3,e4 48 | /// See http://terathon.com/blog/projective-geometric-algebra-done-right/ 49 | pub fn pga3d() -> (Grammar, Types) { 50 | let g = Grammar(vec![1, 1, 1, 0]); 51 | let mut t = Types::default(); 52 | let x = VecIdx(0); 53 | let y = VecIdx(1); 54 | let z = VecIdx(2); 55 | let w = VecIdx(3); 56 | 57 | t.insert_blade("S", SBlade::scalar()); 58 | t.insert_blade("X", SBlade::vec(x)); 59 | t.insert_blade("Y", SBlade::vec(y)); 60 | t.insert_blade("Z", SBlade::vec(z)); 61 | t.insert_blade("W", SBlade::vec(w)); 62 | t.insert_blade("WX", SBlade::from_unsorted(&[w, x])); 63 | t.insert_blade("WY", SBlade::from_unsorted(&[w, y])); 64 | t.insert_blade("WZ", SBlade::from_unsorted(&[w, z])); 65 | t.insert_blade("YZ", SBlade::from_unsorted(&[y, z])); 66 | t.insert_blade("ZX", SBlade::from_unsorted(&[z, x])); 67 | t.insert_blade("XY", SBlade::from_unsorted(&[x, y])); 68 | t.insert_blade("YZW", SBlade::from_unsorted(&[y, z, w])); 69 | t.insert_blade("ZXW", SBlade::from_unsorted(&[z, x, w])); 70 | t.insert_blade("XYW", SBlade::from_unsorted(&[x, y, w])); 71 | // t.insert_blade("ZYX", SBlade::from_unsorted(&[z, y, x])); 72 | t.insert_blade("XYZ", SBlade::from_unsorted(&[x, y, z])); 73 | t.insert_blade("XYZW", SBlade::from_unsorted(&[x, y, z, w])); 74 | // ----------------------------------- 75 | // 2D 76 | 77 | if false { 78 | // TODO: Point2 { x: X, y: Y, w: 1) 79 | t.insert_struct("Vec2", &[("x", "X"), ("y", "Y")]); 80 | 81 | // Plücker coordinates 82 | t.insert_struct( 83 | "Line2", 84 | &[ 85 | // Dir: 86 | ("dx", "WX"), 87 | ("dy", "WY"), 88 | // offset / moment: 89 | ("m", "XY"), 90 | ], 91 | ); 92 | 93 | // t.insert_struct("Translator2", &[("s", "S"), ("wy", "WY"), ("wx", "WX")]); 94 | t.insert_struct("Rotor2", &[("s", "S"), ("xy", "XY")]); 95 | t.insert_struct("Motor2", &[("s", "S"), ("wy", "WY"), ("wx", "WX"), ("xy", "XY")]); 96 | } 97 | 98 | // ----------------------------------- 99 | // 3D 100 | 101 | // TODO: Point3 { x: X, y: Y, z: Z, w: 1) 102 | t.insert_struct("Vec3", &[("x", "X"), ("y", "Y"), ("z", "Z")]); 103 | t.insert_struct("Vec4", &[("x", "X"), ("y", "Y"), ("z", "Z"), ("w", "W")]); 104 | 105 | // The result of Vec3 ^ Vec3, which is numerically identical to a cross product. 106 | // Use this to represent e.g. a normal. 107 | t.insert_struct("Moment3", &[("mx", "YZ"), ("my", "ZX"), ("mz", "XY")]); 108 | 109 | // Plücker coordinates 110 | t.insert_struct( 111 | "Line3", 112 | &[ 113 | // dir: 114 | ("vx", "WX"), 115 | ("vy", "WY"), 116 | ("vz", "WZ"), 117 | // moment: 118 | ("mx", "YZ"), 119 | ("my", "ZX"), 120 | ("mz", "XY"), 121 | ], 122 | ); 123 | 124 | // t.insert_struct("Plane", &[("nx", "YZW"), ("ny", "ZXW"), ("nz", "XYW"), ("d", "ZYX")]); 125 | t.insert_struct("Plane", &[("nx", "YZW"), ("ny", "ZXW"), ("nz", "XYW"), ("d", "XYZ")]); 126 | 127 | // t.insert_struct( 128 | // "Translator3", 129 | // &[("x", "YZ"), ("y", "ZX"), ("z", "XY"), ("xyzw", "XYZW")], 130 | // ); 131 | // Quaternion 132 | t.insert_struct("Rotor3", &[("x", "WX"), ("y", "WY"), ("z", "WZ"), ("w", "XYZW")]); 133 | 134 | // Dual quaternion 135 | t.insert_struct( 136 | "Motor3", 137 | &[ 138 | ("rx", "WX"), 139 | ("ry", "WY"), 140 | ("rz", "WZ"), 141 | ("rw", "XYZW"), 142 | ("ux", "YZW"), 143 | ("uy", "ZXW"), 144 | ("uz", "XYW"), 145 | ("uw", "S"), 146 | ], 147 | ); 148 | 149 | // ----------------------------------- 150 | 151 | (g, t) 152 | } 153 | 154 | /// Using the naming convention of Eric Lengyel. 155 | /// See http://terathon.com/blog/projective-geometric-algebra-done-right/ 156 | pub fn pga3d_lengyel() -> (Grammar, Types) { 157 | let g = Grammar(vec![1, 1, 1, 0]); 158 | let mut t = Types::default(); 159 | 160 | let e1 = VecIdx(0); 161 | let e2 = VecIdx(1); 162 | let e3 = VecIdx(2); 163 | let e4 = VecIdx(3); 164 | t.insert_blade("s", SBlade::scalar()); 165 | 166 | t.insert_blade("e1", SBlade::vec(e1)); 167 | t.insert_blade("e2", SBlade::vec(e2)); 168 | t.insert_blade("e3", SBlade::vec(e3)); 169 | t.insert_blade("e4", SBlade::vec(e4)); 170 | t.insert_blade("e41", SBlade::from_unsorted(&[e4, e1])); 171 | t.insert_blade("e42", SBlade::from_unsorted(&[e4, e2])); 172 | t.insert_blade("e43", SBlade::from_unsorted(&[e4, e3])); 173 | t.insert_blade("e23", SBlade::from_unsorted(&[e2, e3])); 174 | t.insert_blade("e31", SBlade::from_unsorted(&[e3, e1])); 175 | t.insert_blade("e12", SBlade::from_unsorted(&[e1, e2])); 176 | t.insert_blade("e234", SBlade::from_unsorted(&[e2, e3, e4])); 177 | t.insert_blade("e314", SBlade::from_unsorted(&[e3, e1, e4])); 178 | t.insert_blade("e124", SBlade::from_unsorted(&[e1, e2, e4])); 179 | t.insert_blade("e321", SBlade::from_unsorted(&[e3, e2, e1])); 180 | t.insert_blade("E4", SBlade::from_unsorted(&[e1, e2, e3, e4])); 181 | 182 | t.insert_struct("Point", &[("x", "e1"), ("y", "e2"), ("z", "e3"), ("w", "e4")]); 183 | 184 | // Plücker coordinates 185 | t.insert_struct( 186 | "Line", 187 | &[ 188 | ("vx", "e41"), 189 | ("vy", "e42"), 190 | ("vz", "e43"), 191 | ("mx", "e23"), 192 | ("my", "e31"), 193 | ("mz", "e12"), 194 | ], 195 | ); 196 | 197 | t.insert_struct( 198 | "Plane", 199 | &[("nx", "e234"), ("ny", "e314"), ("nz", "e124"), ("d", "e321")], 200 | ); 201 | 202 | (g, t) 203 | } 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Projective Geometric Algebra 2 | 3 | This library is a Rust code generator, generating the mathematics you need for a geometric algebra library. 4 | 5 | I made it mostly to teach myself the fundamentals of PGA. 6 | 7 | Consider it an experiment. 8 | 9 | # Inspiration 10 | [The Geometric Algebra course at Siggraph 2019](https://www.youtube.com/watch?v=tX4H_ctggYo) 11 | https://bivector.net/index.html 12 | 13 | Foundation of Game Engine Development, Volume 1, by Eric Lengyel 14 | http://terathon.com/blog/projective-geometric-algebra-done-right/ 15 | http://terathon.com/pga_lengyel.pdf 16 | 17 | 18 | ## 3D example - projective 19 | 20 | **Vector names**: `X Y Z W` 21 | 22 | **Grammar**: `X²=1 Y²=1 Z²=1 W²=0` 23 | 24 | **Types**: 25 | 26 | | Name | Blades | Alternative | Interpretation | 27 | | ------------ | ----------------------------- | ----------- | ---------------------------------------- | 28 | | Vec3 | `X Y Z` | | direction 29 | | Vec4 | `X Y Z W` | | homogenous point 30 | | Point3 | `X Y Z W=1` | | normalized point 31 | | Line3 | `WX WY WZ YZ ZX XY` | | direction + moment of a Plücker line 32 | | Plane | `YZW ZXW XYW ZYX` | !(X Y Z W) | normal + offset 33 | | Translator3 | `YZ ZX XY XYZW` | | translation primitive 34 | | Rotor3 | `WX WY WZ XYZW` | | rotation primitive, a.k.a. quaternion 35 | | Motor3 | `WX WY WZ XYZW YZW ZXW XYW S` | | translator + rotor, a.k.a. dual quaternion 36 | 37 | From the above definition, this library generates all the operations that can be done on these types. For instance, it will autmatically realize that `Point3 ^ Point3 -> Line3` (wedging two points gives the line that goes through those points) and `Plane V Line3 -> Vec4` (the antiwedge of a plane and a line is the point where the plane and line interesect). 38 | 39 | The generated code uses newtypes for all vectors and blades, so that `x = y;` wouldn't compile (since `x` and `y` coordinates run along different vectors). 40 | 41 | # Details 42 | 43 | ## Algebra 44 | You first specify the the algebra by the signs of the product of the base vectors, e.g. `0++` for 2d projective geometric algebra, which would be the base vectors `e0²=0, e1²=1, e2²=1`. 45 | 46 | You can also give your base vectors more descriptive names, e.g. `X/Y/Z/W` for your standard homogeneous 3D PGA. 47 | 48 | From these all products are generated, creating bivectors (`XY`, `YZ` etc), trivectors (`XYZ` etc), and so on. Together with the `R`eal type they make up the *blades* of the system. All values are a linear combination of the blades, e.g. `0.5 + 2*X - 42*XZ`. 49 | 50 | ### Named blade groups (types) 51 | We can give names to groups of blades, for instance naming `Vec3={X,Y,Z}`, `Point3={X,Y,Z,W}`, `Line3={WX, WY, WZ, YZ, ZX, XY}` ([Plücker coordinates](https://en.wikipedia.org/wiki/Pl%C3%BCcker_coordinates)), `Plane={YZW,ZXW,XYW,ZYX}` etc. 52 | 53 | Note that a value can have multiple types. For instance, in the example above, any `Vec3` or also a `Point3` with `W=0` (an infinite point). The types thus form a Venn diagram. 54 | 55 | These types will be combined against each other for all operations, unary (like the dual) as well as binary (multiplication, dot product, wedge, regressive, ...). The generator will notice what dimensions (blades) will be the output, and deduce a type name form that. For instance, `Point3 ^ Point3 -> Line3` (wedging two points gives you the line that goes through both points) or `Plane V Line3 -> Point3` (the antiwedge of a plane and a line is the point where the plane and line interesect). 56 | 57 | 58 | # A very brief introduction to Geometric Algebra 59 | As a programmer, my view of Geometric Algebra is as a type safe superset of linear algebra that unifies many differents parts of the standard 3D programming toolset into one theory. Using GA we can combine vectors, points, plücker lines, planes, translators, rotors (quaternions) and motors (dual quaternions) into one framework. This library generates the code for these primitves and all valid operations you can do using them. 60 | 61 | ## Notation 62 | 63 | Geometric Algebra (GA) is great and very general. We will focus first on the interesting subset of 3D Projective Geometric Algebra (PGA). This is where you denote point and vectors with an extra projective dimension `w` and define `w=0` as directions and `w=1` as cartesian coordinates. Thus a vector is expressed as `[x, y, z, w]`. 64 | 65 | In textbook geometric algebra, the base vectors are given the names `e1/e2/e3/e4` (or sometimes `e0/e1/e2/e3`). Due to this unfamiliarity and inconsistency I prefer to simply rename them to the familiar names `X/Y/Z/W`. 66 | 67 | So, I use this notations: 68 | 69 | 2D PGA: 70 | 71 | | Class | Blades | Description | | 72 | | --------- | --------- | ----------- | -- | 73 | | Scalar | S | Numbers | The set of real numbers 74 | | Vectors | X Y W | Directions | Orthogonal dimensions in projective space 75 | | Bivectors | YW WX XY | Lines | Orthogonal planes with normals `X Y W` 76 | | Trivector | XYW | Area | 77 | 78 | I will use lower case letters for `x,y,z,w` for values (e.g. `3.14`) and upper case letters `X,Y,Z,W` for the basis vectors, which can be thought of as the *type* of the value. 79 | 80 | ## Vectors and planes 81 | Consider two arrows on the ground, both with heir names engraved on them: `e` and `n`, and they happen to be pointing east and north. What can you do with two arrows? 82 | 83 | ``` 84 | e ^ 85 | ------> | 86 | n| 87 | | 88 | ``` 89 | 90 | One thing you can do with them, is to stick them together at their bases. We call this *wedging*: 91 | 92 | ``` 93 | ^ 94 | | 95 | n| 96 | +------> 97 | e 98 | ``` 99 | 100 | In geometric algebra, we write this as `n^e`. If one arrow has length 2 and the other length 3 we get `2N^3E` = 6N^E = 6NE`. This represents the plane spanned by the two vectors, with the magnitude representing the area of the parallelogram spanned by the vectors. 101 | 102 | 103 | ## Thinking in geometric algebra. 104 | The value `x` means "How much along the `X` axis?". 105 | 106 | `XY`: How much area do I have in the plane spanned by `XY` plane, i.e. the plane pointing along the positive `Z` axis. 107 | 108 | Say you have three orthogonal vectors which are the sides of a cube. What is the volume of them together? 109 | 110 | Classical thinking would get you `|v0|*|v1|*|v2|`. In GA, you instead think of the dimensions involved. 111 | 112 | Each vector has three scalars: `{x: X, y: Y, z: Z}` (`w=0` since these are directions). 113 | We want to get to the volume, which has dimension `XYZ`. We want to from low dimension (directions) to a higher one (volume), so what do we do? We wedge them together! Wedging is joining many lower-dimensions things (lines) into one higher dimension things (volume). So the answer is `v0^v1^v2`. And then you are done! 114 | -------------------------------------------------------------------------------- /generator/src/sblade.rs: -------------------------------------------------------------------------------- 1 | use itertools::chain; 2 | 3 | use crate::*; 4 | 5 | /// A blade type with a sign. This is useful so we can express e20 = -e02. 6 | /// Can be both a type (-e02) and a value (42 * e123) 7 | #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] 8 | pub struct SBlade { 9 | pub blade: Blade, 10 | pub sign: i32, 11 | } 12 | 13 | impl SBlade { 14 | pub fn zero() -> Self { 15 | SBlade { 16 | sign: 0, 17 | blade: Blade::scalar(), // could be any blade really 18 | } 19 | } 20 | 21 | pub fn one() -> Self { 22 | SBlade { 23 | sign: 1, 24 | blade: Blade::scalar(), 25 | } 26 | } 27 | 28 | pub fn scalar() -> Self { 29 | SBlade::unit(Blade::scalar()) 30 | } 31 | 32 | pub fn pseudo_scalar(g: &Grammar) -> Self { 33 | Self::unit(Blade::pseudo_scalar(g)) 34 | } 35 | 36 | pub fn vec(vi: VecIdx) -> Self { 37 | SBlade::unit(Blade::vec(vi)) 38 | } 39 | 40 | pub fn from_sorted(vecs: &[VecIdx]) -> Self { 41 | SBlade { 42 | sign: 1, 43 | blade: Blade::from_sorted(vecs.to_vec()), 44 | } 45 | } 46 | 47 | pub fn from_unsorted(vecs: &[VecIdx]) -> Self { 48 | let (sign, vecs) = sort_vecs(vecs.to_vec()); 49 | SBlade { 50 | sign, 51 | blade: Blade::from_sorted(vecs), 52 | } 53 | } 54 | 55 | /// One times the given blade 56 | pub fn unit(blade: Blade) -> Self { 57 | Self { sign: 1, blade } 58 | } 59 | 60 | pub fn signed_blade(sign: i32, blade: Blade) -> Self { 61 | Self { sign, blade } 62 | } 63 | 64 | pub fn is_zero(&self) -> bool { 65 | self.sign == 0 66 | } 67 | 68 | pub fn is_negative(&self) -> bool { 69 | self.sign < 0 70 | } 71 | 72 | pub fn is_scalar(&self) -> bool { 73 | self.sign == 0 || self.blade.is_scalar() 74 | } 75 | 76 | pub fn grade(&self) -> usize { 77 | self.blade.grade() 78 | } 79 | 80 | pub fn negate(self) -> SBlade { 81 | SBlade { 82 | blade: self.blade, 83 | sign: -self.sign, 84 | } 85 | } 86 | 87 | /// Left compliment. 88 | /// self.lcompl() * self == pseudo-scalar 89 | pub fn lcompl(&self, g: &Grammar) -> Self { 90 | self.sign * self.blade.lcompl(g) 91 | } 92 | 93 | /// Right compliment. 94 | /// self * self.rcompl() == pseudo-scalar 95 | /// e0 * e0.rcompl() = e0 * e12 = e012 96 | /// e1.rcompl() = e20 = -e02 97 | pub fn rcompl(&self, g: &Grammar) -> Self { 98 | self.sign * self.blade.rcompl(g) 99 | } 100 | 101 | /// Reverse the order of the vector indices: 102 | /// e1.reverse() = e1 103 | /// e12.reverse() = e21 = -e12 104 | /// e012.reverse() = e210 = -e012 105 | /// Used for sandwich products 106 | pub fn reverse(mut self) -> Self { 107 | let r = self.grade(); 108 | if r > 1 { 109 | // After reversing the order, we want to sort again. 110 | let num_swaps = r * (r - 1) / 2; 111 | if num_swaps % 2 == 1 { 112 | // Odd number of swaps => sign change 113 | self.sign = -self.sign; 114 | } 115 | } 116 | self 117 | } 118 | 119 | pub fn anti_reverse(&self, g: &Grammar) -> Self { 120 | self.lcompl(g).reverse().rcompl(g) 121 | } 122 | 123 | pub fn unary(&self, unary: Unary, g: &Grammar) -> Self { 124 | match unary { 125 | Unary::LCompl => self.lcompl(g), 126 | Unary::RCompl => self.rcompl(g), 127 | Unary::Reverse => self.clone().reverse(), 128 | Unary::AntiReverse => self.anti_reverse(g), 129 | } 130 | } 131 | 132 | /// geometric product (normal multiplication) 133 | pub fn geometric_product(&self, other: &SBlade, g: &Grammar) -> Self { 134 | let mut sign = self.sign * other.sign; 135 | let mut vecs: Vec = chain(self.blade.vecs(), other.blade.vecs()).copied().collect(); 136 | sign *= sort_vecs_inplace(&mut vecs); 137 | sign *= collapse_adjacent(&mut vecs, g); 138 | if sign == 0 { 139 | Self::zero() 140 | } else { 141 | SBlade { 142 | sign, 143 | blade: Blade::from_sorted(vecs), 144 | } 145 | } 146 | } 147 | 148 | pub fn geometric_antiproduct(&self, other: &SBlade, g: &Grammar) -> Self { 149 | self.lcompl(g).geometric_product(&other.lcompl(g), g).rcompl(g) 150 | } 151 | 152 | /// dot / inner product 153 | pub fn dot_product(&self, other: &SBlade, g: &Grammar) -> Self { 154 | // The dot product is the K grade of the geometric product, 155 | // where K is the absolute difference in grades between the operands. 156 | let k = ((self.grade() as i64) - (other.grade() as i64)).abs() as usize; 157 | let prod = self.geometric_product(other, g); 158 | if prod.blade.grade() > k { 159 | Self::zero() 160 | } else { 161 | prod 162 | } 163 | } 164 | 165 | /// outer / wedge 166 | pub fn wedge_product(&self, other: &SBlade, g: &Grammar) -> Self { 167 | let k = self.grade() + other.grade(); 168 | let prod = self.geometric_product(other, g); 169 | if prod.blade.grade() < k { 170 | Self::zero() 171 | } else { 172 | prod 173 | } 174 | } 175 | 176 | /// regressive product 177 | pub fn antiwedge_product(&self, other: &SBlade, g: &Grammar) -> Self { 178 | self.lcompl(g).wedge_product(&other.lcompl(g), g).rcompl(g) 179 | } 180 | 181 | pub fn binary_product(a: &SBlade, product: Product, b: &SBlade, g: &Grammar) -> Self { 182 | match product { 183 | Product::Geometric => a.geometric_product(b, g), 184 | Product::AntiGeometric => a.geometric_antiproduct(b, g), 185 | Product::Dot => a.dot_product(b, g), 186 | Product::Wedge => a.wedge_product(b, g), 187 | Product::AntiWedge => a.antiwedge_product(b, g), 188 | } 189 | } 190 | 191 | pub fn product(product: Product, operands: &[SBlade], g: &Grammar) -> Self { 192 | if operands.is_empty() { 193 | SBlade::one() // TODO: is this correct for antiwedge? 194 | } else { 195 | let mut result = operands[0].clone(); 196 | for operand in operands.iter().skip(1) { 197 | result = Self::binary_product(&result, product, operand, g); 198 | } 199 | result 200 | } 201 | } 202 | } 203 | 204 | impl std::ops::Mul for i32 { 205 | type Output = SBlade; 206 | fn mul(self, right: SBlade) -> Self::Output { 207 | SBlade { 208 | sign: self * right.sign, 209 | blade: right.blade, 210 | } 211 | } 212 | } 213 | 214 | /// Sort the vector indices, keeping track of all sign changes. 215 | #[must_use] 216 | fn sort_vecs(mut vecs: Vec) -> (i32, Vec) { 217 | let sign = sort_vecs_inplace(&mut vecs); 218 | (sign, vecs) 219 | } 220 | 221 | #[must_use] 222 | fn sort_vecs_inplace(vecs: &mut [VecIdx]) -> i32 { 223 | // Multiplication is anti-commutative so each time we swap we need to flip the sign. 224 | // So bubble-sort! 225 | let mut sign = 1; 226 | for _ in 0..vecs.len() { 227 | for i in 1..vecs.len() { 228 | if vecs[i - 1] > vecs[i] { 229 | vecs.swap(i - 1, i); 230 | sign = -sign; 231 | } 232 | } 233 | } 234 | sign 235 | } 236 | 237 | // Collapse adjacent identical vector indices using the given grammar 238 | #[must_use] 239 | pub fn collapse_adjacent(vecs: &mut Vec, g: &Grammar) -> i32 { 240 | let mut sign = 1; 241 | let mut new_bases = vec![]; 242 | for vi in vecs.iter() { 243 | if new_bases.last() == Some(vi) { 244 | sign *= g.square_geom(*vi); 245 | new_bases.pop(); 246 | } else { 247 | new_bases.push(*vi); 248 | } 249 | } 250 | *vecs = new_bases; 251 | sign 252 | } 253 | 254 | impl std::fmt::Debug for SBlade { 255 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 256 | match self.sign { 257 | 1 => format!("{:?}", self.blade).fmt(f), 258 | 0 => "0".fmt(f), 259 | -1 => format!("-{:?}", self.blade).fmt(f), 260 | sign => format!("{}*{:?}", sign, self.blade).fmt(f), 261 | } 262 | } 263 | } 264 | 265 | #[cfg(test)] 266 | pub mod tests { 267 | use super::*; 268 | 269 | /// Parse a signed blade 270 | pub fn sb(s: &str) -> SBlade { 271 | fn idx_from_char(c: char) -> VecIdx { 272 | VecIdx(c.to_digit(10).unwrap() as usize) 273 | } 274 | 275 | if s == "0" { 276 | SBlade::zero() 277 | } else if s == "s" { 278 | SBlade::scalar() 279 | } else if s.starts_with('e') { 280 | let vecs: Vec = s[1..].chars().map(idx_from_char).collect(); 281 | SBlade::from_unsorted(&vecs) 282 | } else { 283 | panic!("Expected 'e' followed by digits (e.g. e21), found '{}'", s) 284 | } 285 | } 286 | 287 | #[test] 288 | fn test_sblade() { 289 | let grammar = Grammar(vec![0, 1, 1, 1]); 290 | let g = &grammar; 291 | let v0 = VecIdx(0); 292 | let v1 = VecIdx(1); 293 | let v2 = VecIdx(2); 294 | // let v3 = VecIdx(3); 295 | let s = SBlade::scalar(); 296 | let e0 = SBlade::vec(v0); 297 | let e1 = SBlade::vec(v1); 298 | let e2 = SBlade::vec(v2); 299 | // let e3 = SBlade::vec(v3); 300 | assert_eq!(SBlade::product(Product::Wedge, &[e0, e1, e2], g), sb("e012")); 301 | 302 | assert_eq!(s.rcompl(g), SBlade::pseudo_scalar(g)); 303 | assert_eq!(sb("e0").rcompl(g), sb("e123")); 304 | assert_eq!(sb("e1").rcompl(g), sb("e203")); 305 | assert_eq!(sb("e01").rcompl(g), sb("e23")); 306 | assert_eq!(SBlade::pseudo_scalar(g).rcompl(g), s); 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /generated/src/pga2d/vec2.rs: -------------------------------------------------------------------------------- 1 | //! # Vec2 2 | //! 3 | //! ## Operations 4 | //! ```text 5 | //! Vec2.geometric(Vec2) -> Rotor 6 | //! Vec2.dot(Vec2) -> S 7 | //! Vec2.geometric(Vec3) -> Motor 8 | //! Vec3.geometric(Vec2) -> Motor 9 | //! Vec2.anti_geometric(Vec3) -> Vec2 10 | //! Vec3.anti_geometric(Vec2) -> Vec2 11 | //! Vec2.dot(Vec3) -> S 12 | //! Vec3.dot(Vec2) -> S 13 | //! Vec2.wedge(Vec3) -> Line 14 | //! Vec3.wedge(Vec2) -> Line 15 | //! Vec2.anti_geometric(Line) -> Rotor 16 | //! Line.anti_geometric(Vec2) -> Rotor 17 | //! Vec2.dot(Line) -> Vec3 18 | //! Line.dot(Vec2) -> Vec3 19 | //! Vec2.wedge(Line) -> XYW 20 | //! Line.wedge(Vec2) -> XYW 21 | //! Vec2.anti_wedge(Line) -> S 22 | //! Line.anti_wedge(Vec2) -> S 23 | //! Vec2.geometric(Rotor) -> Vec2 24 | //! Rotor.geometric(Vec2) -> Vec2 25 | //! Vec2.dot(Rotor) -> Vec2 26 | //! Rotor.dot(Vec2) -> Vec2 27 | //! Vec2.wedge(Rotor) -> Vec2 28 | //! Rotor.wedge(Vec2) -> Vec2 29 | //! Vec2.anti_geometric(Motor) -> Rotor 30 | //! Motor.anti_geometric(Vec2) -> Rotor 31 | //! Vec2.dot(Motor) -> Vec3 32 | //! Motor.dot(Vec2) -> Vec3 33 | //! Vec2.anti_wedge(Motor) -> S 34 | //! Motor.anti_wedge(Vec2) -> S 35 | //! ``` 36 | 37 | use super::*; 38 | 39 | #[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, derive_more::Neg, derive_more::Add, derive_more::Sub)] 40 | pub struct Vec2 { 41 | pub x: X, 42 | pub y: Y, 43 | } 44 | 45 | // --------------------------------------------------------------------- 46 | 47 | impl RCompl for Vec2 { 48 | type Output = Line; 49 | fn rcompl(self) -> Self::Output { 50 | Line { 51 | dx: self.x.rcompl(), 52 | dy: self.y.rcompl(), 53 | m: Default::default(), 54 | } 55 | } 56 | } 57 | 58 | impl LCompl for Vec2 { 59 | type Output = Line; 60 | fn lcompl(self) -> Self::Output { 61 | Line { 62 | dx: self.x.lcompl(), 63 | dy: self.y.lcompl(), 64 | m: Default::default(), 65 | } 66 | } 67 | } 68 | 69 | impl Reverse for Vec2 { 70 | fn rev(self) -> Self { 71 | Vec2 { x: self.x, y: self.y } 72 | } 73 | } 74 | 75 | impl AntiReverse for Vec2 { 76 | fn arev(self) -> Self { 77 | Vec2 { x: -self.x, y: -self.y } 78 | } 79 | } 80 | 81 | // --------------------------------------------------------------------- 82 | // Vec2 OP Vec2: 83 | 84 | // Vec2.geometric(Vec2) -> Rotor 85 | impl Geometric for Vec2 { 86 | type Output = Rotor; 87 | fn geometric(self, rhs: Vec2) -> Self::Output { 88 | // Rotor { 89 | // s : S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0), 90 | // xy: XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 91 | // } 92 | Rotor { 93 | s: self.x.geometric(rhs.x) + self.y.geometric(rhs.y), 94 | xy: self.x.geometric(rhs.y) - self.y.geometric(rhs.x), 95 | } 96 | } 97 | } 98 | 99 | // Omitted: Vec2 anti_geometric Vec2 = 0 (unnamed type) 100 | 101 | // Vec2.dot(Vec2) -> S 102 | impl Dot for Vec2 { 103 | type Output = S; 104 | fn dot(self, rhs: Vec2) -> Self::Output { 105 | // S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0) 106 | self.x.dot(rhs.x) + self.y.dot(rhs.y) 107 | } 108 | } 109 | 110 | // Omitted: Vec2 wedge Vec2 = self.x ^ rhs.y + self.y ^ rhs.x (unnamed type) 111 | // Omitted: Vec2 anti_wedge Vec2 = 0 (unnamed type) 112 | 113 | // --------------------------------------------------------------------- 114 | // Vec2 OP Vec3: 115 | 116 | // Vec2.geometric(Vec3) -> Motor 117 | impl Geometric for Vec2 { 118 | type Output = Motor; 119 | fn geometric(self, rhs: Vec3) -> Self::Output { 120 | // Motor { 121 | // s : S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0), 122 | // yw: YW(self.y.0 * rhs.w.0), 123 | // wx: WX(self.x.0 * rhs.w.0), 124 | // xy: XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 125 | // } 126 | Motor { 127 | s: self.x.geometric(rhs.x) + self.y.geometric(rhs.y), 128 | yw: self.y.geometric(rhs.w), 129 | wx: -self.x.geometric(rhs.w), 130 | xy: self.x.geometric(rhs.y) - self.y.geometric(rhs.x), 131 | } 132 | } 133 | } 134 | 135 | // Vec2.anti_geometric(Vec3) -> Vec2 136 | impl AntiGeometric for Vec2 { 137 | type Output = Vec2; 138 | fn anti_geometric(self, rhs: Vec3) -> Self::Output { 139 | // Vec2 { 140 | // x: X(self.y.0 * rhs.w.0), 141 | // y: Y(self.x.0 * rhs.w.0), 142 | // } 143 | Vec2 { 144 | x: -self.y.anti_geometric(rhs.w), 145 | y: self.x.anti_geometric(rhs.w), 146 | } 147 | } 148 | } 149 | 150 | // Vec2.dot(Vec3) -> S 151 | impl Dot for Vec2 { 152 | type Output = S; 153 | fn dot(self, rhs: Vec3) -> Self::Output { 154 | // S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0) 155 | self.x.dot(rhs.x) + self.y.dot(rhs.y) 156 | } 157 | } 158 | 159 | // Vec2.wedge(Vec3) -> Line 160 | impl Wedge for Vec2 { 161 | type Output = Line; 162 | fn wedge(self, rhs: Vec3) -> Self::Output { 163 | // Line { 164 | // dx: YW(self.y.0 * rhs.w.0), 165 | // dy: WX(self.x.0 * rhs.w.0), 166 | // m : XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 167 | // } 168 | Line { 169 | dx: self.y.wedge(rhs.w), 170 | dy: -self.x.wedge(rhs.w), 171 | m: self.x.wedge(rhs.y) - self.y.wedge(rhs.x), 172 | } 173 | } 174 | } 175 | 176 | // Omitted: Vec2 anti_wedge Vec3 = 0 (unnamed type) 177 | 178 | // --------------------------------------------------------------------- 179 | // Vec2 OP Line: 180 | 181 | // Omitted: Vec2 geometric Line = self.x * rhs.dx + self.x * rhs.dy + self.x * rhs.m + self.y * rhs.dx + self.y * rhs.dy + self.y * rhs.m (unnamed type) 182 | 183 | // Vec2.anti_geometric(Line) -> Rotor 184 | impl AntiGeometric for Vec2 { 185 | type Output = Rotor; 186 | fn anti_geometric(self, rhs: Line) -> Self::Output { 187 | // Rotor { 188 | // s : S(self.x.0 * rhs.dx.0) + S(self.y.0 * rhs.dy.0), 189 | // xy: XY(self.x.0 * rhs.dy.0) + XY(self.y.0 * rhs.dx.0), 190 | // } 191 | Rotor { 192 | s: self.x.anti_geometric(rhs.dx) + self.y.anti_geometric(rhs.dy), 193 | xy: -self.x.anti_geometric(rhs.dy) + self.y.anti_geometric(rhs.dx), 194 | } 195 | } 196 | } 197 | 198 | // Vec2.dot(Line) -> Vec3 199 | impl Dot for Vec2 { 200 | type Output = Vec3; 201 | fn dot(self, rhs: Line) -> Self::Output { 202 | // Vec3 { 203 | // x: X(self.y.0 * rhs.m.0), 204 | // y: Y(self.x.0 * rhs.m.0), 205 | // w: W(self.x.0 * rhs.dy.0) + W(self.y.0 * rhs.dx.0), 206 | // } 207 | Vec3 { 208 | x: -self.y.dot(rhs.m), 209 | y: self.x.dot(rhs.m), 210 | w: -self.x.dot(rhs.dy) + self.y.dot(rhs.dx), 211 | } 212 | } 213 | } 214 | 215 | // Vec2.wedge(Line) -> XYW 216 | impl Wedge for Vec2 { 217 | type Output = XYW; 218 | fn wedge(self, rhs: Line) -> Self::Output { 219 | // XYW(self.x.0 * rhs.dx.0) + XYW(self.y.0 * rhs.dy.0) 220 | self.x.wedge(rhs.dx) + self.y.wedge(rhs.dy) 221 | } 222 | } 223 | 224 | // Vec2.anti_wedge(Line) -> S 225 | impl AntiWedge for Vec2 { 226 | type Output = S; 227 | fn anti_wedge(self, rhs: Line) -> Self::Output { 228 | // S(self.x.0 * rhs.dx.0) + S(self.y.0 * rhs.dy.0) 229 | self.x.anti_wedge(rhs.dx) + self.y.anti_wedge(rhs.dy) 230 | } 231 | } 232 | 233 | // --------------------------------------------------------------------- 234 | // Vec2 OP Rotor: 235 | 236 | // Vec2.geometric(Rotor) -> Vec2 237 | impl Geometric for Vec2 { 238 | type Output = Vec2; 239 | fn geometric(self, rhs: Rotor) -> Self::Output { 240 | // Vec2 { 241 | // x: X(self.x.0 * rhs.s.0) + X(self.y.0 * rhs.xy.0), 242 | // y: Y(self.x.0 * rhs.xy.0) + Y(self.y.0 * rhs.s.0), 243 | // } 244 | Vec2 { 245 | x: self.x.geometric(rhs.s) - self.y.geometric(rhs.xy), 246 | y: self.x.geometric(rhs.xy) + self.y.geometric(rhs.s), 247 | } 248 | } 249 | } 250 | 251 | // Omitted: Vec2 anti_geometric Rotor = 0 (unnamed type) 252 | 253 | // Vec2.dot(Rotor) -> Vec2 254 | impl Dot for Vec2 { 255 | type Output = Vec2; 256 | fn dot(self, rhs: Rotor) -> Self::Output { 257 | // Vec2 { 258 | // x: X(self.x.0 * rhs.s.0) + X(self.y.0 * rhs.xy.0), 259 | // y: Y(self.x.0 * rhs.xy.0) + Y(self.y.0 * rhs.s.0), 260 | // } 261 | Vec2 { 262 | x: self.x.dot(rhs.s) - self.y.dot(rhs.xy), 263 | y: self.x.dot(rhs.xy) + self.y.dot(rhs.s), 264 | } 265 | } 266 | } 267 | 268 | // Vec2.wedge(Rotor) -> Vec2 269 | impl Wedge for Vec2 { 270 | type Output = Vec2; 271 | fn wedge(self, rhs: Rotor) -> Self::Output { 272 | // Vec2 { 273 | // x: X(self.x.0 * rhs.s.0), 274 | // y: Y(self.y.0 * rhs.s.0), 275 | // } 276 | Vec2 { 277 | x: self.x.wedge(rhs.s), 278 | y: self.y.wedge(rhs.s), 279 | } 280 | } 281 | } 282 | 283 | // Omitted: Vec2 anti_wedge Rotor = 0 (unnamed type) 284 | 285 | // --------------------------------------------------------------------- 286 | // Vec2 OP Motor: 287 | 288 | // Omitted: Vec2 geometric Motor = self.x * rhs.s + self.x * rhs.wx + self.x * rhs.xy + self.x * rhs.yw + self.y * rhs.s + self.y * rhs.wx + self.y * rhs.xy + self.y * rhs.yw (unnamed type) 289 | 290 | // Vec2.anti_geometric(Motor) -> Rotor 291 | impl AntiGeometric for Vec2 { 292 | type Output = Rotor; 293 | fn anti_geometric(self, rhs: Motor) -> Self::Output { 294 | // Rotor { 295 | // s : S(self.x.0 * rhs.yw.0) + S(self.y.0 * rhs.wx.0), 296 | // xy: XY(self.x.0 * rhs.wx.0) + XY(self.y.0 * rhs.yw.0), 297 | // } 298 | Rotor { 299 | s: self.x.anti_geometric(rhs.yw) + self.y.anti_geometric(rhs.wx), 300 | xy: -self.x.anti_geometric(rhs.wx) + self.y.anti_geometric(rhs.yw), 301 | } 302 | } 303 | } 304 | 305 | // Vec2.dot(Motor) -> Vec3 306 | impl Dot for Vec2 { 307 | type Output = Vec3; 308 | fn dot(self, rhs: Motor) -> Self::Output { 309 | // Vec3 { 310 | // x: X(self.x.0 * rhs.s.0) + X(self.y.0 * rhs.xy.0), 311 | // y: Y(self.x.0 * rhs.xy.0) + Y(self.y.0 * rhs.s.0), 312 | // w: W(self.x.0 * rhs.wx.0) + W(self.y.0 * rhs.yw.0), 313 | // } 314 | Vec3 { 315 | x: self.x.dot(rhs.s) - self.y.dot(rhs.xy), 316 | y: self.x.dot(rhs.xy) + self.y.dot(rhs.s), 317 | w: -self.x.dot(rhs.wx) + self.y.dot(rhs.yw), 318 | } 319 | } 320 | } 321 | 322 | // Omitted: Vec2 wedge Motor = self.x ^ rhs.s + self.x ^ rhs.yw + self.y ^ rhs.s + self.y ^ rhs.wx (unnamed type) 323 | 324 | // Vec2.anti_wedge(Motor) -> S 325 | impl AntiWedge for Vec2 { 326 | type Output = S; 327 | fn anti_wedge(self, rhs: Motor) -> Self::Output { 328 | // S(self.x.0 * rhs.yw.0) + S(self.y.0 * rhs.wx.0) 329 | self.x.anti_wedge(rhs.yw) + self.y.anti_wedge(rhs.wx) 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /generator/src/simplify.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | // Used when simplifying sums: 4 | #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 5 | struct Term { 6 | /// expr first, so we sort on that 7 | expr: Expr, 8 | scalar: i32, 9 | } 10 | 11 | impl Term { 12 | pub fn from_op(expr: Expr) -> Term { 13 | match expr { 14 | Expr::Term(expr, scalar) => Term { expr: *expr, scalar }, 15 | expr => Term { expr, scalar: 1 }, 16 | } 17 | } 18 | 19 | pub fn into_op(self) -> Expr { 20 | if self.scalar == 0 { 21 | Expr::zero() 22 | } else if self.scalar == 1 { 23 | self.expr 24 | } else { 25 | Expr::Term(self.expr.into(), self.scalar) 26 | } 27 | } 28 | } 29 | 30 | impl Expr { 31 | #[must_use] 32 | pub fn simplify(self, g: Option<&Grammar>) -> Expr { 33 | match self { 34 | Expr::Var { 35 | typ: Type::Constant(sblade), 36 | .. 37 | } => Expr::sblade(&sblade), 38 | Expr::Var { 39 | order, 40 | name, 41 | typ: Type::Struct(members), 42 | } => Expr::Sum( 43 | members 44 | .into_iter() 45 | .map(|(mem_name, typ)| Expr::var(order, format!("{}.{}", name, mem_name), &typ)) 46 | .collect(), 47 | ) 48 | .simplify(g), 49 | Expr::Var { .. } => self, 50 | Expr::Vec(_) => self, 51 | Expr::Unary(unary, expr) => { 52 | let expr = expr.simplify(g); 53 | 54 | // Check if the unary op is just a sign change (for reverse and anti-reverse): 55 | if let Some(g) = g { 56 | if let Some(typ) = expr.typ(Some(g)).and_then(Type::into_sblade) { 57 | let unary_typ = typ.unary(unary, g); 58 | if unary_typ.sign == 0 { 59 | return Expr::zero(); 60 | } else if typ.blade == unary_typ.blade { 61 | return match typ.sign * unary_typ.sign { 62 | 1 => expr, // unary op is a no-op, 63 | -1 => Expr::Term(expr.into(), -1).simplify(Some(g)), // unary op is a sign change 64 | _ => unreachable!(), 65 | }; 66 | } 67 | } 68 | } 69 | 70 | match expr { 71 | Expr::Term(expr, scalar) => { 72 | // (e * s).unary() = e.unary() * s 73 | Expr::Term(Expr::Unary(unary, expr).into(), scalar).simplify(g) 74 | } 75 | 76 | // e.g. x.lcompl().rcompl() => x 77 | Expr::Unary(inner_unary, expr) if inner_unary == unary.undoer() => *expr, 78 | 79 | // distributive property 80 | // (a + b).unary() = a.unary() + b.unary() 81 | Expr::Sum(terms) => { 82 | Expr::Sum(terms.into_iter().map(|t| Expr::Unary(unary, t.into())).collect()).simplify(g) 83 | } 84 | 85 | expr => Expr::Unary(unary, expr.into()), 86 | } 87 | } 88 | 89 | Expr::Term(expr, mut scalar) => { 90 | let expr: Expr = match expr.simplify(g) { 91 | Expr::Term(inner_op, inner_scalar) => { 92 | scalar *= inner_scalar; 93 | *inner_op 94 | } 95 | expr => expr, 96 | }; 97 | 98 | if scalar == 0 || expr.is_zero() { 99 | Self::zero() 100 | } else if scalar == 1 { 101 | expr 102 | } else if let Expr::Sum(terms) = expr { 103 | // (a + b) * s = a * s + b * s 104 | Expr::Sum(terms.into_iter().map(|expr| Expr::Term(expr.into(), scalar)).collect()).simplify(g) 105 | } else { 106 | Expr::Term(expr.into(), scalar) 107 | } 108 | } 109 | Expr::Sum(terms) => { 110 | // use itertools::Itertools; 111 | // eprintln!("simplify sums input: {:?}", terms.iter().map(Expr::rust).join(" + ")); 112 | let mut terms: Vec = terms 113 | .into_iter() 114 | .flat_map(|term| match term.simplify(g) { 115 | Expr::Sum(terms) => terms, 116 | expr => vec![expr], 117 | }) 118 | .collect(); 119 | terms.retain(|f| !f.is_zero()); 120 | 121 | // eprintln!("simplify sums PRE-sort {:?}", terms.iter().map(Expr::rust).join(" + ")); 122 | terms = sort_and_join_terms(terms, g); 123 | // eprintln!("simplify sums POST-sort {:?}", terms.iter().map(Expr::rust).join(" + ")); 124 | 125 | if terms.is_empty() { 126 | Expr::zero() 127 | } else if terms.len() == 1 { 128 | terms.remove(0) 129 | } else { 130 | Expr::Sum(terms) 131 | } 132 | } 133 | Expr::Prod(product, mut factors) => { 134 | for fac in &mut factors { 135 | fac.simplify_inplace(g); 136 | } 137 | 138 | // look for a sum for expansion: 139 | for (i, fac) in factors.iter().enumerate() { 140 | if let Expr::Sum(terms) = fac { 141 | let terms = terms.clone(); 142 | return Expr::Sum( 143 | terms 144 | .into_iter() 145 | .map(|term| { 146 | factors[i] = term; 147 | Expr::Prod(product, factors.clone()) 148 | }) 149 | .collect(), 150 | ) 151 | .simplify(g); 152 | } 153 | } 154 | 155 | simplify_product(product, factors, g) 156 | } 157 | Expr::StructInstance(si) => Expr::StructInstance(StructInstance { 158 | struct_name: si.struct_name, 159 | strct: si.strct, 160 | members: si 161 | .members 162 | .into_iter() 163 | .map(|(name, expr)| (name, expr.simplify(g))) 164 | .collect(), 165 | }), 166 | } 167 | } 168 | 169 | pub fn simplify_inplace(&mut self, g: Option<&Grammar>) { 170 | *self = std::mem::replace(self, Expr::zero()).simplify(g); 171 | } 172 | 173 | fn into_factors(self, desired_product_type: Product) -> Vec { 174 | match self { 175 | Expr::Term(expr, scalar) => { 176 | let mut factors = expr.into_factors(desired_product_type); 177 | factors.push(Expr::scalar(scalar)); 178 | factors 179 | } 180 | Expr::Prod(product, factors) if product == desired_product_type => factors, 181 | expr => vec![expr], 182 | } 183 | } 184 | } 185 | 186 | #[must_use] 187 | fn sort_and_join_terms(terms: Vec, _g: Option<&Grammar>) -> Vec { 188 | // Convert into sum-of-products: 189 | let mut terms: Vec = terms.into_iter().map(Term::from_op).collect(); 190 | terms.sort(); 191 | 192 | // Join adjacent: 193 | let mut collapsed_terms: Vec = vec![]; 194 | for new_term in terms { 195 | if let Some(last_term) = collapsed_terms.last_mut() { 196 | if last_term.expr == new_term.expr { 197 | last_term.scalar += new_term.scalar; 198 | if last_term.scalar == 0 { 199 | collapsed_terms.pop(); 200 | } 201 | continue; 202 | } 203 | } 204 | 205 | collapsed_terms.push(new_term); 206 | } 207 | 208 | collapsed_terms.into_iter().map(Term::into_op).collect() 209 | } 210 | 211 | #[must_use] 212 | fn simplify_product(product: Product, factors: Vec, g: Option<&Grammar>) -> Expr { 213 | // eprintln!("simplify_product {:?} {:?}", product, factors); 214 | let mut new_scalar = 1; 215 | let mut new_factors = vec![]; 216 | 217 | for fac in factors { 218 | for fac in fac.into_factors(product) { 219 | if let Some(scalar) = fac.as_scalar() { 220 | new_scalar *= scalar; 221 | } else { 222 | new_factors.push(fac); 223 | } 224 | } 225 | } 226 | let mut scalar = new_scalar; 227 | let mut factors = new_factors; 228 | 229 | // eprintln!("simplify_product {} * {:?} {:?}", scalar, product, factors); 230 | 231 | scalar *= sort_factors(product, &mut factors, g); 232 | if let Some(g) = g { 233 | scalar *= collapse_factors(product, &mut factors, g); 234 | } 235 | 236 | if false { 237 | if let Some(g) = g { 238 | scalar *= sort_factors_to_avoid_sign_flips(product, &mut factors, g); 239 | } 240 | } 241 | 242 | // eprintln!("simplify_product {} * {:?} {:?}", scalar, product, factors); 243 | 244 | if scalar == 0 { 245 | Expr::zero() 246 | } else if scalar == 1 { 247 | if factors.is_empty() { 248 | match product { 249 | Product::Geometric | Product::Wedge | Product::Dot => Expr::one(), 250 | _ => Expr::Prod(product, factors), // TODO 251 | } 252 | } else if factors.len() == 1 { 253 | factors.remove(0) 254 | } else { 255 | Expr::Prod(product, factors) 256 | } 257 | } else { 258 | Expr::Term(Expr::Prod(product, factors).into(), scalar).simplify(g) 259 | } 260 | } 261 | 262 | #[must_use] 263 | fn sort_factors(product: Product, factors: &mut [Expr], g: Option<&Grammar>) -> i32 { 264 | // Any sign-change due to swapping 265 | let mut sign = 1; 266 | 267 | // Bubble-sort: 268 | while { 269 | let mut did_swap = false; 270 | for i in 1..factors.len() { 271 | if factors[i - 1] > factors[i] { 272 | // We want to swap them. Can we? 273 | let lt = factors[i - 1].typ(g); 274 | let rt = factors[i].typ(g); 275 | if let Some(sign_change) = commutativeness(product, lt, rt, g) { 276 | factors.swap(i - 1, i); 277 | did_swap = true; 278 | sign *= sign_change; 279 | } 280 | } 281 | } 282 | did_swap 283 | } {} 284 | 285 | sign 286 | } 287 | 288 | /// Rewrite `y*x` to `-x*y` to avoid implicit sign change in product. 289 | /// This is so that generated code is easier to read and copy-paste elsewhere. 290 | #[must_use] 291 | fn sort_factors_to_avoid_sign_flips(product: Product, factors: &mut [Expr], g: &Grammar) -> i32 { 292 | // Any sign-change due to swapping 293 | let mut sign = 1; 294 | 295 | // Bubble-sort: 296 | while { 297 | let mut did_swap = false; 298 | for i in 1..factors.len() { 299 | let lt = factors[i - 1].typ(Some(g)); 300 | let rt = factors[i].typ(Some(g)); 301 | 302 | if swapping_order_would_remove_sign(product, lt, rt, g) == Some(true) { 303 | factors.swap(i - 1, i); 304 | did_swap = true; 305 | sign *= -1; 306 | } 307 | } 308 | did_swap 309 | } {} 310 | 311 | sign 312 | } 313 | 314 | fn swapping_order_would_remove_sign(product: Product, l: Option, r: Option, g: &Grammar) -> Option { 315 | let l = l?.into_sblade()?; 316 | let r = r?.into_sblade()?; 317 | let lr = SBlade::binary_product(&l, product, &r, g); 318 | let rl = SBlade::binary_product(&r, product, &l, g); 319 | assert_eq!(lr.is_zero(), rl.is_zero()); 320 | assert_eq!(lr.blade, rl.blade); 321 | assert_eq!(lr.sign.abs(), rl.sign.abs()); 322 | 323 | Some(lr.sign == -1 && rl.sign == 1) 324 | } 325 | 326 | #[must_use] 327 | fn collapse_factors(product: Product, factors: &mut Vec, g: &Grammar) -> i32 { 328 | // Any sign-change due to squaring of base vectors 329 | let mut sign = 1; 330 | 331 | let mut i = 0; 332 | while i + 1 < factors.len() { 333 | if factors[i] == factors[i + 1] { 334 | if let Expr::Vec(vi) = factors[i] { 335 | if let Some(s) = g.square_with(product, vi) { 336 | sign *= s; 337 | factors.remove(i); 338 | factors.remove(i); 339 | continue; 340 | } 341 | } 342 | } 343 | 344 | if let (Some(Type::SBlade(lb)), Some(Type::SBlade(rb))) = (factors[i].typ(Some(g)), factors[i + 1].typ(Some(g))) 345 | { 346 | if SBlade::binary_product(&lb, product, &rb, g).is_zero() { 347 | factors.clear(); 348 | return 0; 349 | } 350 | } 351 | 352 | i += 1; 353 | } 354 | 355 | sign 356 | } 357 | 358 | /// Can we swap these two factors, and if so what is the sign change? 359 | fn commutativeness(product: Product, l: Option, r: Option, g: Option<&Grammar>) -> Option { 360 | let l = l?.into_sblade()?; 361 | let r = r?.into_sblade()?; 362 | let lr = SBlade::binary_product(&l, product, &r, g?); 363 | let rl = SBlade::binary_product(&r, product, &l, g?); 364 | assert_eq!(lr.is_zero(), rl.is_zero()); 365 | assert_eq!(lr.blade, rl.blade); 366 | assert_eq!(lr.sign.abs(), rl.sign.abs()); 367 | if lr.is_zero() { 368 | Some(0) 369 | } else if lr.sign == rl.sign { 370 | Some(1) 371 | } else { 372 | Some(-1) 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /generated/src/pga2d/vec3.rs: -------------------------------------------------------------------------------- 1 | //! # Vec3 2 | //! 3 | //! ## Operations 4 | //! ```text 5 | //! Vec3.geometric(Vec3) -> Motor 6 | //! Vec3.dot(Vec3) -> S 7 | //! Vec3.wedge(Vec3) -> Line 8 | //! Vec3.geometric(Vec2) -> Motor 9 | //! Vec2.geometric(Vec3) -> Motor 10 | //! Vec3.anti_geometric(Vec2) -> Vec2 11 | //! Vec2.anti_geometric(Vec3) -> Vec2 12 | //! Vec3.dot(Vec2) -> S 13 | //! Vec2.dot(Vec3) -> S 14 | //! Vec3.wedge(Vec2) -> Line 15 | //! Vec2.wedge(Vec3) -> Line 16 | //! Vec3.anti_geometric(Line) -> Motor 17 | //! Line.anti_geometric(Vec3) -> Motor 18 | //! Vec3.dot(Line) -> Vec3 19 | //! Line.dot(Vec3) -> Vec3 20 | //! Vec3.wedge(Line) -> XYW 21 | //! Line.wedge(Vec3) -> XYW 22 | //! Vec3.anti_wedge(Line) -> S 23 | //! Line.anti_wedge(Vec3) -> S 24 | //! Vec3.anti_geometric(Rotor) -> Rotor 25 | //! Rotor.anti_geometric(Vec3) -> Rotor 26 | //! Vec3.dot(Rotor) -> Vec3 27 | //! Rotor.dot(Vec3) -> Vec3 28 | //! Vec3.anti_wedge(Rotor) -> S 29 | //! Rotor.anti_wedge(Vec3) -> S 30 | //! Vec3.anti_geometric(Motor) -> Motor 31 | //! Motor.anti_geometric(Vec3) -> Motor 32 | //! Vec3.dot(Motor) -> Vec3 33 | //! Motor.dot(Vec3) -> Vec3 34 | //! Vec3.anti_wedge(Motor) -> S 35 | //! Motor.anti_wedge(Vec3) -> S 36 | //! ``` 37 | 38 | use super::*; 39 | 40 | #[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, derive_more::Neg, derive_more::Add, derive_more::Sub)] 41 | pub struct Vec3 { 42 | pub x: X, 43 | pub y: Y, 44 | pub w: W, 45 | } 46 | 47 | // --------------------------------------------------------------------- 48 | 49 | impl RCompl for Vec3 { 50 | type Output = Line; 51 | fn rcompl(self) -> Self::Output { 52 | Line { 53 | dx: self.x.rcompl(), 54 | dy: self.y.rcompl(), 55 | m: self.w.rcompl(), 56 | } 57 | } 58 | } 59 | 60 | impl LCompl for Vec3 { 61 | type Output = Line; 62 | fn lcompl(self) -> Self::Output { 63 | Line { 64 | dx: self.x.lcompl(), 65 | dy: self.y.lcompl(), 66 | m: self.w.lcompl(), 67 | } 68 | } 69 | } 70 | 71 | impl Reverse for Vec3 { 72 | fn rev(self) -> Self { 73 | Vec3 { 74 | x: self.x, 75 | y: self.y, 76 | w: self.w, 77 | } 78 | } 79 | } 80 | 81 | impl AntiReverse for Vec3 { 82 | fn arev(self) -> Self { 83 | Vec3 { 84 | x: -self.x, 85 | y: -self.y, 86 | w: -self.w, 87 | } 88 | } 89 | } 90 | 91 | // --------------------------------------------------------------------- 92 | // Vec3 OP Vec2: 93 | 94 | // Vec3.geometric(Vec2) -> Motor 95 | impl Geometric for Vec3 { 96 | type Output = Motor; 97 | fn geometric(self, rhs: Vec2) -> Self::Output { 98 | // Motor { 99 | // s : S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0), 100 | // yw: YW(self.w.0 * rhs.y.0), 101 | // wx: WX(self.w.0 * rhs.x.0), 102 | // xy: XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 103 | // } 104 | Motor { 105 | s: self.x.geometric(rhs.x) + self.y.geometric(rhs.y), 106 | yw: -self.w.geometric(rhs.y), 107 | wx: self.w.geometric(rhs.x), 108 | xy: self.x.geometric(rhs.y) - self.y.geometric(rhs.x), 109 | } 110 | } 111 | } 112 | 113 | // Vec3.anti_geometric(Vec2) -> Vec2 114 | impl AntiGeometric for Vec3 { 115 | type Output = Vec2; 116 | fn anti_geometric(self, rhs: Vec2) -> Self::Output { 117 | // Vec2 { 118 | // x: X(self.w.0 * rhs.y.0), 119 | // y: Y(self.w.0 * rhs.x.0), 120 | // } 121 | Vec2 { 122 | x: self.w.anti_geometric(rhs.y), 123 | y: -self.w.anti_geometric(rhs.x), 124 | } 125 | } 126 | } 127 | 128 | // Vec3.dot(Vec2) -> S 129 | impl Dot for Vec3 { 130 | type Output = S; 131 | fn dot(self, rhs: Vec2) -> Self::Output { 132 | // S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0) 133 | self.x.dot(rhs.x) + self.y.dot(rhs.y) 134 | } 135 | } 136 | 137 | // Vec3.wedge(Vec2) -> Line 138 | impl Wedge for Vec3 { 139 | type Output = Line; 140 | fn wedge(self, rhs: Vec2) -> Self::Output { 141 | // Line { 142 | // dx: YW(self.w.0 * rhs.y.0), 143 | // dy: WX(self.w.0 * rhs.x.0), 144 | // m : XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 145 | // } 146 | Line { 147 | dx: -self.w.wedge(rhs.y), 148 | dy: self.w.wedge(rhs.x), 149 | m: self.x.wedge(rhs.y) - self.y.wedge(rhs.x), 150 | } 151 | } 152 | } 153 | 154 | // Omitted: Vec3 anti_wedge Vec2 = 0 (unnamed type) 155 | 156 | // --------------------------------------------------------------------- 157 | // Vec3 OP Vec3: 158 | 159 | // Vec3.geometric(Vec3) -> Motor 160 | impl Geometric for Vec3 { 161 | type Output = Motor; 162 | fn geometric(self, rhs: Vec3) -> Self::Output { 163 | // Motor { 164 | // s : S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0), 165 | // yw: YW(self.w.0 * rhs.y.0) + YW(self.y.0 * rhs.w.0), 166 | // wx: WX(self.w.0 * rhs.x.0) + WX(self.x.0 * rhs.w.0), 167 | // xy: XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 168 | // } 169 | Motor { 170 | s: self.x.geometric(rhs.x) + self.y.geometric(rhs.y), 171 | yw: -self.w.geometric(rhs.y) + self.y.geometric(rhs.w), 172 | wx: self.w.geometric(rhs.x) - self.x.geometric(rhs.w), 173 | xy: self.x.geometric(rhs.y) - self.y.geometric(rhs.x), 174 | } 175 | } 176 | } 177 | 178 | // Omitted: Vec3 anti_geometric Vec3 = self.w !* rhs.w + self.w !* rhs.x + self.w !* rhs.y + self.x !* rhs.w + self.y !* rhs.w (unnamed type) 179 | 180 | // Vec3.dot(Vec3) -> S 181 | impl Dot for Vec3 { 182 | type Output = S; 183 | fn dot(self, rhs: Vec3) -> Self::Output { 184 | // S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0) 185 | self.x.dot(rhs.x) + self.y.dot(rhs.y) 186 | } 187 | } 188 | 189 | // Vec3.wedge(Vec3) -> Line 190 | impl Wedge for Vec3 { 191 | type Output = Line; 192 | fn wedge(self, rhs: Vec3) -> Self::Output { 193 | // Line { 194 | // dx: YW(self.w.0 * rhs.y.0) + YW(self.y.0 * rhs.w.0), 195 | // dy: WX(self.w.0 * rhs.x.0) + WX(self.x.0 * rhs.w.0), 196 | // m : XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 197 | // } 198 | Line { 199 | dx: -self.w.wedge(rhs.y) + self.y.wedge(rhs.w), 200 | dy: self.w.wedge(rhs.x) - self.x.wedge(rhs.w), 201 | m: self.x.wedge(rhs.y) - self.y.wedge(rhs.x), 202 | } 203 | } 204 | } 205 | 206 | // Omitted: Vec3 anti_wedge Vec3 = 0 (unnamed type) 207 | 208 | // --------------------------------------------------------------------- 209 | // Vec3 OP Line: 210 | 211 | // Omitted: Vec3 geometric Line = self.w * rhs.m + self.x * rhs.dx + self.x * rhs.dy + self.x * rhs.m + self.y * rhs.dx + self.y * rhs.dy + self.y * rhs.m (unnamed type) 212 | 213 | // Vec3.anti_geometric(Line) -> Motor 214 | impl AntiGeometric for Vec3 { 215 | type Output = Motor; 216 | fn anti_geometric(self, rhs: Line) -> Self::Output { 217 | // Motor { 218 | // s : S(self.w.0 * rhs.m.0) + S(self.x.0 * rhs.dx.0) + S(self.y.0 * rhs.dy.0), 219 | // yw: YW(self.w.0 * rhs.dy.0), 220 | // wx: WX(self.w.0 * rhs.dx.0), 221 | // xy: XY(self.x.0 * rhs.dy.0) + XY(self.y.0 * rhs.dx.0), 222 | // } 223 | Motor { 224 | s: self.w.anti_geometric(rhs.m) + self.x.anti_geometric(rhs.dx) + self.y.anti_geometric(rhs.dy), 225 | yw: self.w.anti_geometric(rhs.dy), 226 | wx: -self.w.anti_geometric(rhs.dx), 227 | xy: -self.x.anti_geometric(rhs.dy) + self.y.anti_geometric(rhs.dx), 228 | } 229 | } 230 | } 231 | 232 | // Vec3.dot(Line) -> Vec3 233 | impl Dot for Vec3 { 234 | type Output = Vec3; 235 | fn dot(self, rhs: Line) -> Self::Output { 236 | // Vec3 { 237 | // x: X(self.y.0 * rhs.m.0), 238 | // y: Y(self.x.0 * rhs.m.0), 239 | // w: W(self.x.0 * rhs.dy.0) + W(self.y.0 * rhs.dx.0), 240 | // } 241 | Vec3 { 242 | x: -self.y.dot(rhs.m), 243 | y: self.x.dot(rhs.m), 244 | w: -self.x.dot(rhs.dy) + self.y.dot(rhs.dx), 245 | } 246 | } 247 | } 248 | 249 | // Vec3.wedge(Line) -> XYW 250 | impl Wedge for Vec3 { 251 | type Output = XYW; 252 | fn wedge(self, rhs: Line) -> Self::Output { 253 | // XYW(self.w.0 * rhs.m.0) + XYW(self.x.0 * rhs.dx.0) + XYW(self.y.0 * rhs.dy.0) 254 | self.w.wedge(rhs.m) + self.x.wedge(rhs.dx) + self.y.wedge(rhs.dy) 255 | } 256 | } 257 | 258 | // Vec3.anti_wedge(Line) -> S 259 | impl AntiWedge for Vec3 { 260 | type Output = S; 261 | fn anti_wedge(self, rhs: Line) -> Self::Output { 262 | // S(self.w.0 * rhs.m.0) + S(self.x.0 * rhs.dx.0) + S(self.y.0 * rhs.dy.0) 263 | self.w.anti_wedge(rhs.m) + self.x.anti_wedge(rhs.dx) + self.y.anti_wedge(rhs.dy) 264 | } 265 | } 266 | 267 | // --------------------------------------------------------------------- 268 | // Vec3 OP Rotor: 269 | 270 | // Omitted: Vec3 geometric Rotor = self.w * rhs.s + self.w * rhs.xy + self.x * rhs.s + self.x * rhs.xy + self.y * rhs.s + self.y * rhs.xy (unnamed type) 271 | 272 | // Vec3.anti_geometric(Rotor) -> Rotor 273 | impl AntiGeometric for Vec3 { 274 | type Output = Rotor; 275 | fn anti_geometric(self, rhs: Rotor) -> Self::Output { 276 | // Rotor { 277 | // s : S(self.w.0 * rhs.xy.0), 278 | // xy: XY(self.w.0 * rhs.s.0), 279 | // } 280 | Rotor { 281 | s: self.w.anti_geometric(rhs.xy), 282 | xy: -self.w.anti_geometric(rhs.s), 283 | } 284 | } 285 | } 286 | 287 | // Vec3.dot(Rotor) -> Vec3 288 | impl Dot for Vec3 { 289 | type Output = Vec3; 290 | fn dot(self, rhs: Rotor) -> Self::Output { 291 | // Vec3 { 292 | // x: X(self.x.0 * rhs.s.0) + X(self.y.0 * rhs.xy.0), 293 | // y: Y(self.x.0 * rhs.xy.0) + Y(self.y.0 * rhs.s.0), 294 | // w: W(self.w.0 * rhs.s.0), 295 | // } 296 | Vec3 { 297 | x: self.x.dot(rhs.s) - self.y.dot(rhs.xy), 298 | y: self.x.dot(rhs.xy) + self.y.dot(rhs.s), 299 | w: self.w.dot(rhs.s), 300 | } 301 | } 302 | } 303 | 304 | // Omitted: Vec3 wedge Rotor = self.w ^ rhs.s + self.w ^ rhs.xy + self.x ^ rhs.s + self.y ^ rhs.s (unnamed type) 305 | 306 | // Vec3.anti_wedge(Rotor) -> S 307 | impl AntiWedge for Vec3 { 308 | type Output = S; 309 | fn anti_wedge(self, rhs: Rotor) -> Self::Output { 310 | // S(self.w.0 * rhs.xy.0) 311 | self.w.anti_wedge(rhs.xy) 312 | } 313 | } 314 | 315 | // --------------------------------------------------------------------- 316 | // Vec3 OP Motor: 317 | 318 | // Omitted: Vec3 geometric Motor = self.w * rhs.s + self.w * rhs.xy + self.x * rhs.s + self.x * rhs.wx + self.x * rhs.xy + self.x * rhs.yw + self.y * rhs.s + self.y * rhs.wx + self.y * rhs.xy + self.y * rhs.yw (unnamed type) 319 | 320 | // Vec3.anti_geometric(Motor) -> Motor 321 | impl AntiGeometric for Vec3 { 322 | type Output = Motor; 323 | fn anti_geometric(self, rhs: Motor) -> Self::Output { 324 | // Motor { 325 | // s : S(self.w.0 * rhs.xy.0) + S(self.x.0 * rhs.yw.0) + S(self.y.0 * rhs.wx.0), 326 | // yw: YW(self.w.0 * rhs.wx.0), 327 | // wx: WX(self.w.0 * rhs.yw.0), 328 | // xy: XY(self.w.0 * rhs.s.0) + XY(self.x.0 * rhs.wx.0) + XY(self.y.0 * rhs.yw.0), 329 | // } 330 | Motor { 331 | s: self.w.anti_geometric(rhs.xy) + self.x.anti_geometric(rhs.yw) + self.y.anti_geometric(rhs.wx), 332 | yw: self.w.anti_geometric(rhs.wx), 333 | wx: -self.w.anti_geometric(rhs.yw), 334 | xy: -self.w.anti_geometric(rhs.s) - self.x.anti_geometric(rhs.wx) + self.y.anti_geometric(rhs.yw), 335 | } 336 | } 337 | } 338 | 339 | // Vec3.dot(Motor) -> Vec3 340 | impl Dot for Vec3 { 341 | type Output = Vec3; 342 | fn dot(self, rhs: Motor) -> Self::Output { 343 | // Vec3 { 344 | // x: X(self.x.0 * rhs.s.0) + X(self.y.0 * rhs.xy.0), 345 | // y: Y(self.x.0 * rhs.xy.0) + Y(self.y.0 * rhs.s.0), 346 | // w: W(self.w.0 * rhs.s.0) + W(self.x.0 * rhs.wx.0) + W(self.y.0 * rhs.yw.0), 347 | // } 348 | Vec3 { 349 | x: self.x.dot(rhs.s) - self.y.dot(rhs.xy), 350 | y: self.x.dot(rhs.xy) + self.y.dot(rhs.s), 351 | w: self.w.dot(rhs.s) - self.x.dot(rhs.wx) + self.y.dot(rhs.yw), 352 | } 353 | } 354 | } 355 | 356 | // Omitted: Vec3 wedge Motor = self.w ^ rhs.s + self.w ^ rhs.xy + self.x ^ rhs.s + self.x ^ rhs.yw + self.y ^ rhs.s + self.y ^ rhs.wx (unnamed type) 357 | 358 | // Vec3.anti_wedge(Motor) -> S 359 | impl AntiWedge for Vec3 { 360 | type Output = S; 361 | fn anti_wedge(self, rhs: Motor) -> Self::Output { 362 | // S(self.w.0 * rhs.xy.0) + S(self.x.0 * rhs.yw.0) + S(self.y.0 * rhs.wx.0) 363 | self.w.anti_wedge(rhs.xy) + self.x.anti_wedge(rhs.yw) + self.y.anti_wedge(rhs.wx) 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /generator/tests/test.rs: -------------------------------------------------------------------------------- 1 | use generator::{documentation::*, *}; 2 | 3 | #[cfg(test)] 4 | macro_rules! assert_eq_ignoring_whitespace { 5 | ($l:expr, $r:expr) => {{ 6 | let l = $l; 7 | let r = $r; 8 | if tokenize(&l) != tokenize(&r) { 9 | panic!("Got this:\n{}\n\nExpected this:\n{}", l, r); 10 | } 11 | }}; 12 | ($l:expr, $r:expr, $msg:expr) => {{ 13 | let l = $l; 14 | let r = $r; 15 | if tokenize(&l) != tokenize(&r) { 16 | panic!("{}. Got this:\n{}\n\nExpected this:\n{}", $msg, l, r); 17 | } 18 | }}; 19 | } 20 | 21 | fn tokenize(s: &str) -> Vec<&str> { 22 | s.trim().split_ascii_whitespace().collect() 23 | } 24 | 25 | #[test] 26 | fn test_pga3d_lengyel() { 27 | let (g, t) = grammars::pga3d_lengyel(); 28 | let rust = |expr: Expr| expr.simplify(Some(&g)).typify(&t, &g).rust_concise(); 29 | let unit_blades = t.unit_blades(); 30 | // println!("{}", multiplication_tables(&unit_blades, &rust)); 31 | 32 | assert_eq_ignoring_whitespace!( 33 | unary_table(&unit_blades, &rust), 34 | r" 35 | | Op \ Blade | 1 | e1 | e2 | e3 | e4 | e41 | e42 | e43 | e23 | e31 | e12 | e234 | e314 | e124 | e321 | E4 | 36 | | ---------------- | -- | ----- | ----- | ----- | ----- | ---- | ---- | ---- | ---- | ---- | ---- | ----- | ----- | ----- | ----- | -- | 37 | | Right complement | E4 | e234 | e314 | e124 | e321 | -e23 | -e31 | -e12 | -e41 | -e42 | -e43 | -e1 | -e2 | -e3 | -e4 | 1 | 38 | | Left complement | E4 | -e234 | -e314 | -e124 | -e321 | -e23 | -e31 | -e12 | -e41 | -e42 | -e43 | e1 | e2 | e3 | e4 | 1 | 39 | | Reverse | 1 | e1 | e2 | e3 | e4 | -e41 | -e42 | -e43 | -e23 | -e31 | -e12 | -e234 | -e314 | -e124 | -e321 | E4 | 40 | | Anti-reverse | 1 | -e1 | -e2 | -e3 | -e4 | -e41 | -e42 | -e43 | -e23 | -e31 | -e12 | e234 | e314 | e124 | e321 | E4 | 41 | " 42 | ); 43 | 44 | assert_eq_ignoring_whitespace!( 45 | multiplication_table(&unit_blades, Product::Geometric, &rust), 46 | r" 47 | | | 1 | e1 | e2 | e3 | e4 | e41 | e42 | e43 | e23 | e31 | e12 | e234 | e314 | e124 | e321 | E4 | 48 | | ---- | ---- | ----- | ----- | ----- | ---- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ---- | 49 | | 1 | 1 | e1 | e2 | e3 | e4 | e41 | e42 | e43 | e23 | e31 | e12 | e234 | e314 | e124 | e321 | E4 | 50 | | e1 | e1 | 1 | e12 | -e31 | -e41 | -e4 | -e124 | e314 | -e321 | -e3 | e2 | E4 | e43 | -e42 | -e23 | e234 | 51 | | e2 | e2 | -e12 | 1 | e23 | -e42 | e124 | -e4 | -e234 | e3 | -e321 | -e1 | -e43 | E4 | e41 | -e31 | e314 | 52 | | e3 | e3 | e31 | -e23 | 1 | -e43 | -e314 | e234 | -e4 | -e2 | e1 | -e321 | e42 | -e41 | E4 | -e12 | e124 | 53 | | e4 | e4 | e41 | e42 | e43 | 0 | 0 | 0 | 0 | e234 | e314 | e124 | 0 | 0 | 0 | E4 | 0 | 54 | | e41 | e41 | e4 | e124 | -e314 | 0 | 0 | 0 | 0 | -E4 | -e43 | e42 | 0 | 0 | 0 | -e234 | 0 | 55 | | e42 | e42 | -e124 | e4 | e234 | 0 | 0 | 0 | 0 | e43 | -E4 | -e41 | 0 | 0 | 0 | -e314 | 0 | 56 | | e43 | e43 | e314 | -e234 | e4 | 0 | 0 | 0 | 0 | -e42 | e41 | -E4 | 0 | 0 | 0 | -e124 | 0 | 57 | | e23 | e23 | -e321 | -e3 | e2 | e234 | -E4 | -e43 | e42 | -1 | -e12 | e31 | -e4 | -e124 | e314 | e1 | e41 | 58 | | e31 | e31 | e3 | -e321 | -e1 | e314 | e43 | -E4 | -e41 | e12 | -1 | -e23 | e124 | -e4 | -e234 | e2 | e42 | 59 | | e12 | e12 | -e2 | e1 | -e321 | e124 | -e42 | e41 | -E4 | -e31 | e23 | -1 | -e314 | e234 | -e4 | e3 | e43 | 60 | | e234 | e234 | -E4 | -e43 | e42 | 0 | 0 | 0 | 0 | -e4 | -e124 | e314 | 0 | 0 | 0 | e41 | 0 | 61 | | e314 | e314 | e43 | -E4 | -e41 | 0 | 0 | 0 | 0 | e124 | -e4 | -e234 | 0 | 0 | 0 | e42 | 0 | 62 | | e124 | e124 | -e42 | e41 | -E4 | 0 | 0 | 0 | 0 | -e314 | e234 | -e4 | 0 | 0 | 0 | e43 | 0 | 63 | | e321 | e321 | -e23 | -e31 | -e12 | -E4 | e234 | e314 | e124 | e1 | e2 | e3 | -e41 | -e42 | -e43 | -1 | e4 | 64 | | E4 | E4 | -e234 | -e314 | -e124 | 0 | 0 | 0 | 0 | e41 | e42 | e43 | 0 | 0 | 0 | -e4 | 0 | 65 | " 66 | ); 67 | 68 | assert_eq_ignoring_whitespace!( 69 | multiplication_table(&unit_blades, Product::AntiGeometric, &rust), 70 | r" 71 | | | 1 | e1 | e2 | e3 | e4 | e41 | e42 | e43 | e23 | e31 | e12 | e234 | e314 | e124 | e321 | E4 | 72 | | ---- | ----- | ----- | ----- | ----- | ---- | ----- | ----- | ----- | ---- | ---- | ---- | ----- | ----- | ----- | ---- | ---- | 73 | | 1 | 0 | 0 | 0 | 0 | e321 | e23 | e31 | e12 | 0 | 0 | 0 | e1 | e2 | e3 | 0 | 1 | 74 | | e1 | 0 | 0 | 0 | 0 | -e23 | -e321 | e3 | -e2 | 0 | 0 | 0 | 1 | -e12 | e31 | 0 | e1 | 75 | | e2 | 0 | 0 | 0 | 0 | -e31 | -e3 | -e321 | e1 | 0 | 0 | 0 | e12 | 1 | -e23 | 0 | e2 | 76 | | e3 | 0 | 0 | 0 | 0 | -e12 | e2 | -e1 | -e321 | 0 | 0 | 0 | -e31 | e23 | 1 | 0 | e3 | 77 | | e4 | -e321 | e23 | e31 | e12 | -E4 | e234 | e314 | e124 | -e1 | -e2 | -e3 | -e41 | -e42 | -e43 | 1 | e4 | 78 | | e41 | e23 | -e321 | e3 | -e2 | e234 | -E4 | e43 | -e42 | -1 | e12 | -e31 | -e4 | e124 | -e314 | e1 | e41 | 79 | | e42 | e31 | -e3 | -e321 | e1 | e314 | -e43 | -E4 | e41 | -e12 | -1 | e23 | -e124 | -e4 | e234 | e2 | e42 | 80 | | e43 | e12 | e2 | -e1 | -e321 | e124 | e42 | -e41 | -E4 | e31 | -e23 | -1 | e314 | -e234 | -e4 | e3 | e43 | 81 | | e23 | 0 | 0 | 0 | 0 | e1 | -1 | e12 | -e31 | 0 | 0 | 0 | -e321 | e3 | -e2 | 0 | e23 | 82 | | e31 | 0 | 0 | 0 | 0 | e2 | -e12 | -1 | e23 | 0 | 0 | 0 | -e3 | -e321 | e1 | 0 | e31 | 83 | | e12 | 0 | 0 | 0 | 0 | e3 | e31 | -e23 | -1 | 0 | 0 | 0 | e2 | -e1 | -e321 | 0 | e12 | 84 | | e234 | -e1 | -1 | e12 | -e31 | -e41 | -e4 | e124 | -e314 | e321 | -e3 | e2 | E4 | -e43 | e42 | e23 | e234 | 85 | | e314 | -e2 | -e12 | -1 | e23 | -e42 | -e124 | -e4 | e234 | e3 | e321 | -e1 | e43 | E4 | -e41 | e31 | e314 | 86 | | e124 | -e3 | e31 | -e23 | -1 | -e43 | e314 | -e234 | -e4 | -e2 | e1 | e321 | -e42 | e41 | E4 | e12 | e124 | 87 | | e321 | 0 | 0 | 0 | 0 | -1 | e1 | e2 | e3 | 0 | 0 | 0 | -e23 | -e31 | -e12 | 0 | e321 | 88 | | E4 | 1 | e1 | e2 | e3 | e4 | e41 | e42 | e43 | e23 | e31 | e12 | e234 | e314 | e124 | e321 | E4 | 89 | " 90 | ); 91 | 92 | // Normalized equclidean point 93 | let point = Type::Struct(vec![ 94 | ("x".to_string(), t.get("e1").clone()), 95 | ("y".to_string(), t.get("e2").clone()), 96 | ("z".to_string(), t.get("e3").clone()), 97 | ( 98 | "w".to_string(), 99 | Type::constant(t.get("e4").clone().into_sblade().unwrap()), 100 | ), 101 | ]); 102 | assert_eq_ignoring_whitespace!( 103 | rust(Expr::wedge(vec![Expr::var(0, "p", &point), Expr::var(1, "q", &point)])), 104 | " 105 | Line { 106 | vx: -p.x ^ e4 + q.x ^ e4, 107 | vy: -p.y ^ e4 + q.y ^ e4, 108 | vz: -p.z ^ e4 + q.z ^ e4, 109 | mx: p.y ^ q.z - p.z ^ q.y, 110 | my: -p.x ^ q.z + p.z ^ q.x, 111 | mz: p.x ^ q.y - p.y ^ q.x, 112 | } 113 | " 114 | ); 115 | } 116 | 117 | #[test] 118 | fn test_pga2d() { 119 | let (g, t) = grammars::pga2d(); 120 | let rust = |expr: Expr| expr.simplify(Some(&g)).typify(&t, &g).rust_concise(); 121 | 122 | // let unit_blades = t.unit_blades(); 123 | // println!("{}", multiplication_tables(&unit_blades, &rust)); 124 | 125 | assert_eq_ignoring_whitespace!( 126 | t.get("WX").unit().rust_concise(), 127 | "-_e0 ^ _e2", 128 | "Ugly output without typify" 129 | ); 130 | assert_eq_ignoring_whitespace!(rust(t.get("WX").unit()), "WX"); 131 | 132 | assert_eq_ignoring_whitespace!( 133 | Expr::dot(vec![t.get("XY").unit()]).simplify(Some(&g)).rust_concise(), 134 | "_e0 ^ _e1" 135 | ); 136 | assert_eq_ignoring_whitespace!(rust(Expr::dot(vec![t.get("XY").unit(), Expr::one()])), "XY"); 137 | 138 | let x_type = t.get("X"); 139 | let y_type = t.get("Y"); 140 | assert_eq_ignoring_whitespace!(rust(x_type.unit()), "X"); 141 | assert_eq_ignoring_whitespace!(rust(Expr::wedge(vec![x_type.unit(), y_type.unit()])), "XY"); 142 | let expr = Expr::Sum(vec![ 143 | Expr::wedge(vec![x_type.unit(), y_type.unit()]), 144 | Expr::wedge(vec![y_type.unit(), x_type.unit()]), 145 | ]); 146 | assert_eq_ignoring_whitespace!( 147 | expr.rust_concise(), 148 | "_e0 ^ _e1 + _e1 ^ _e0", 149 | "Hard to read without running typify" 150 | ); 151 | assert_eq_ignoring_whitespace!(rust(expr), "0"); 152 | 153 | let point = t.get("Vec3"); 154 | assert_eq_ignoring_whitespace!( 155 | rust(Expr::wedge(vec![Expr::var(0, "l", point), Expr::var(1, "r", point)])), 156 | r" 157 | Line { 158 | dx: -l.w ^ r.y + l.y ^ r.w, 159 | dy: l.w ^ r.x - l.x ^ r.w, 160 | m : l.x ^ r.y - l.y ^ r.x, 161 | }" 162 | .trim() 163 | ); 164 | 165 | let line = t.get("Line"); 166 | assert_eq_ignoring_whitespace!( 167 | rust(Expr::antiwedge(vec![Expr::var(0, "l", line), Expr::var(1, "r", line)])), 168 | r" 169 | Vec3 { 170 | x: l.dy & r.m - l.m & r.dy, 171 | y: -l.dx & r.m + l.m & r.dx, 172 | w: l.dx & r.dy - l.dy & r.dx, 173 | } 174 | " 175 | .trim() 176 | ); 177 | 178 | assert_eq_ignoring_whitespace!( 179 | rust(Expr::geometric(vec![ 180 | Expr::var(0, "p", point), 181 | Expr::var(0, "p", point) 182 | ])), 183 | r"p.x * p.x + p.y * p.y" 184 | ); 185 | 186 | assert_eq_ignoring_whitespace!( 187 | rust(Expr::geometric(vec![Expr::var(0, "l", line), Expr::var(0, "l", line)])), 188 | r"l.m * l.m" 189 | ); 190 | } 191 | 192 | #[test] 193 | fn test_pga2d_rcompl() { 194 | let (g, t) = grammars::pga2d(); 195 | let rust = |expr: Expr| expr.simplify(Some(&g)).typify(&t, &g).rust_concise(); 196 | let point = t.get("Vec3"); 197 | let expr = Expr::unary(Unary::RCompl, Expr::var(0, "l", point)); 198 | dbg!(expr.clone().rust_concise()); 199 | dbg!(expr.clone().simplify(Some(&g)).rust_concise()); 200 | assert_eq_ignoring_whitespace!( 201 | rust(expr), 202 | r" 203 | Line { 204 | dx: l.x.rcompl(), 205 | dy: l.y.rcompl(), 206 | m : l.w.rcompl(), 207 | }" 208 | .trim() 209 | ); 210 | } 211 | 212 | #[test] 213 | fn test_generator() { 214 | let (grammar, types) = generator::grammars::pga3d(); 215 | let settings = gen::Settings::default(); 216 | let gen = gen::Generator { 217 | grammar, 218 | types, 219 | settings, 220 | ro: RustOptions::rust(), 221 | }; 222 | 223 | let point = gen.types.get_struct("Vec4"); 224 | let code = gen::strct::impl_struct_product(&gen, &("Vec4", &point), &("Vec4", &point), Product::Wedge); 225 | assert_eq_ignoring_whitespace!( 226 | code, 227 | r" 228 | // Vec4.wedge(Vec4) -> Line3 229 | impl Wedge for Vec4 { 230 | type Output = Line3; 231 | fn wedge(self, rhs: Vec4) -> Self::Output { 232 | // Line3 { 233 | // vx: WX(self.w.0 * rhs.x.0) + WX(self.x.0 * rhs.w.0), 234 | // vy: WY(self.w.0 * rhs.y.0) + WY(self.y.0 * rhs.w.0), 235 | // vz: WZ(self.w.0 * rhs.z.0) + WZ(self.z.0 * rhs.w.0), 236 | // mx: YZ(self.y.0 * rhs.z.0) + YZ(self.z.0 * rhs.y.0), 237 | // my: ZX(self.x.0 * rhs.z.0) + ZX(self.z.0 * rhs.x.0), 238 | // mz: XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 239 | // } 240 | Line3 { 241 | vx: self.w.wedge(rhs.x) - self.x.wedge(rhs.w), 242 | vy: self.w.wedge(rhs.y) - self.y.wedge(rhs.w), 243 | vz: self.w.wedge(rhs.z) - self.z.wedge(rhs.w), 244 | mx: self.y.wedge(rhs.z) - self.z.wedge(rhs.y), 245 | my: -self.x.wedge(rhs.z) + self.z.wedge(rhs.x), 246 | mz: self.x.wedge(rhs.y) - self.y.wedge(rhs.x), 247 | } 248 | } 249 | } 250 | " 251 | ); 252 | } 253 | -------------------------------------------------------------------------------- /generated/src/pga2d/rotor.rs: -------------------------------------------------------------------------------- 1 | //! # Rotor 2 | //! 3 | //! ## Operations 4 | //! ```text 5 | //! Rotor.geometric(Rotor) -> Rotor 6 | //! Rotor.dot(Rotor) -> Rotor 7 | //! Rotor.wedge(Rotor) -> Rotor 8 | //! Rotor.geometric(Vec2) -> Vec2 9 | //! Vec2.geometric(Rotor) -> Vec2 10 | //! Rotor.dot(Vec2) -> Vec2 11 | //! Vec2.dot(Rotor) -> Vec2 12 | //! Rotor.wedge(Vec2) -> Vec2 13 | //! Vec2.wedge(Rotor) -> Vec2 14 | //! Rotor.anti_geometric(Vec3) -> Rotor 15 | //! Vec3.anti_geometric(Rotor) -> Rotor 16 | //! Rotor.dot(Vec3) -> Vec3 17 | //! Vec3.dot(Rotor) -> Vec3 18 | //! Rotor.anti_wedge(Vec3) -> S 19 | //! Vec3.anti_wedge(Rotor) -> S 20 | //! Rotor.geometric(Line) -> Motor 21 | //! Line.geometric(Rotor) -> Motor 22 | //! Rotor.anti_geometric(Line) -> Vec2 23 | //! Line.anti_geometric(Rotor) -> Vec2 24 | //! Rotor.dot(Line) -> Motor 25 | //! Line.dot(Rotor) -> Motor 26 | //! Rotor.wedge(Line) -> Line 27 | //! Line.wedge(Rotor) -> Line 28 | //! Rotor.anti_wedge(Line) -> Vec2 29 | //! Line.anti_wedge(Rotor) -> Vec2 30 | //! Rotor.geometric(Motor) -> Motor 31 | //! Motor.geometric(Rotor) -> Motor 32 | //! Rotor.anti_geometric(Motor) -> Vec2 33 | //! Motor.anti_geometric(Rotor) -> Vec2 34 | //! Rotor.dot(Motor) -> Motor 35 | //! Motor.dot(Rotor) -> Motor 36 | //! Rotor.wedge(Motor) -> Motor 37 | //! Motor.wedge(Rotor) -> Motor 38 | //! Rotor.anti_wedge(Motor) -> Vec2 39 | //! Motor.anti_wedge(Rotor) -> Vec2 40 | //! ``` 41 | 42 | use super::*; 43 | 44 | #[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, derive_more::Neg, derive_more::Add, derive_more::Sub)] 45 | pub struct Rotor { 46 | pub s: S, 47 | pub xy: XY, 48 | } 49 | 50 | // --------------------------------------------------------------------- 51 | // Omitted: Rotor.rcompl() -> self.s.rcompl() + self.xy.rcompl() 52 | // Omitted: Rotor.lcompl() -> self.s.lcompl() + self.xy.lcompl() 53 | 54 | impl Reverse for Rotor { 55 | fn rev(self) -> Self { 56 | Rotor { 57 | s: self.s, 58 | xy: -self.xy, 59 | } 60 | } 61 | } 62 | 63 | impl AntiReverse for Rotor { 64 | fn arev(self) -> Self { 65 | Rotor { 66 | s: -self.s, 67 | xy: self.xy, 68 | } 69 | } 70 | } 71 | 72 | // --------------------------------------------------------------------- 73 | // Rotor OP Vec2: 74 | 75 | // Rotor.geometric(Vec2) -> Vec2 76 | impl Geometric for Rotor { 77 | type Output = Vec2; 78 | fn geometric(self, rhs: Vec2) -> Self::Output { 79 | // Vec2 { 80 | // x: X(self.s.0 * rhs.x.0) + X(self.xy.0 * rhs.y.0), 81 | // y: Y(self.s.0 * rhs.y.0) + Y(self.xy.0 * rhs.x.0), 82 | // } 83 | Vec2 { 84 | x: self.s.geometric(rhs.x) + self.xy.geometric(rhs.y), 85 | y: self.s.geometric(rhs.y) - self.xy.geometric(rhs.x), 86 | } 87 | } 88 | } 89 | 90 | // Omitted: Rotor anti_geometric Vec2 = 0 (unnamed type) 91 | 92 | // Rotor.dot(Vec2) -> Vec2 93 | impl Dot for Rotor { 94 | type Output = Vec2; 95 | fn dot(self, rhs: Vec2) -> Self::Output { 96 | // Vec2 { 97 | // x: X(self.s.0 * rhs.x.0) + X(self.xy.0 * rhs.y.0), 98 | // y: Y(self.s.0 * rhs.y.0) + Y(self.xy.0 * rhs.x.0), 99 | // } 100 | Vec2 { 101 | x: self.s.dot(rhs.x) + self.xy.dot(rhs.y), 102 | y: self.s.dot(rhs.y) - self.xy.dot(rhs.x), 103 | } 104 | } 105 | } 106 | 107 | // Rotor.wedge(Vec2) -> Vec2 108 | impl Wedge for Rotor { 109 | type Output = Vec2; 110 | fn wedge(self, rhs: Vec2) -> Self::Output { 111 | // Vec2 { 112 | // x: X(self.s.0 * rhs.x.0), 113 | // y: Y(self.s.0 * rhs.y.0), 114 | // } 115 | Vec2 { 116 | x: self.s.wedge(rhs.x), 117 | y: self.s.wedge(rhs.y), 118 | } 119 | } 120 | } 121 | 122 | // Omitted: Rotor anti_wedge Vec2 = 0 (unnamed type) 123 | 124 | // --------------------------------------------------------------------- 125 | // Rotor OP Vec3: 126 | 127 | // Omitted: Rotor geometric Vec3 = self.s * rhs.w + self.s * rhs.x + self.s * rhs.y + self.xy * rhs.w + self.xy * rhs.x + self.xy * rhs.y (unnamed type) 128 | 129 | // Rotor.anti_geometric(Vec3) -> Rotor 130 | impl AntiGeometric for Rotor { 131 | type Output = Rotor; 132 | fn anti_geometric(self, rhs: Vec3) -> Self::Output { 133 | // Rotor { 134 | // s : S(self.xy.0 * rhs.w.0), 135 | // xy: XY(self.s.0 * rhs.w.0), 136 | // } 137 | Rotor { 138 | s: self.xy.anti_geometric(rhs.w), 139 | xy: -self.s.anti_geometric(rhs.w), 140 | } 141 | } 142 | } 143 | 144 | // Rotor.dot(Vec3) -> Vec3 145 | impl Dot for Rotor { 146 | type Output = Vec3; 147 | fn dot(self, rhs: Vec3) -> Self::Output { 148 | // Vec3 { 149 | // x: X(self.s.0 * rhs.x.0) + X(self.xy.0 * rhs.y.0), 150 | // y: Y(self.s.0 * rhs.y.0) + Y(self.xy.0 * rhs.x.0), 151 | // w: W(self.s.0 * rhs.w.0), 152 | // } 153 | Vec3 { 154 | x: self.s.dot(rhs.x) + self.xy.dot(rhs.y), 155 | y: self.s.dot(rhs.y) - self.xy.dot(rhs.x), 156 | w: self.s.dot(rhs.w), 157 | } 158 | } 159 | } 160 | 161 | // Omitted: Rotor wedge Vec3 = self.s ^ rhs.w + self.s ^ rhs.x + self.s ^ rhs.y + self.xy ^ rhs.w (unnamed type) 162 | 163 | // Rotor.anti_wedge(Vec3) -> S 164 | impl AntiWedge for Rotor { 165 | type Output = S; 166 | fn anti_wedge(self, rhs: Vec3) -> Self::Output { 167 | // S(self.xy.0 * rhs.w.0) 168 | self.xy.anti_wedge(rhs.w) 169 | } 170 | } 171 | 172 | // --------------------------------------------------------------------- 173 | // Rotor OP Line: 174 | 175 | // Rotor.geometric(Line) -> Motor 176 | impl Geometric for Rotor { 177 | type Output = Motor; 178 | fn geometric(self, rhs: Line) -> Self::Output { 179 | // Motor { 180 | // s : S(self.xy.0 * rhs.m.0), 181 | // yw: YW(self.s.0 * rhs.dx.0) + YW(self.xy.0 * rhs.dy.0), 182 | // wx: WX(self.s.0 * rhs.dy.0) + WX(self.xy.0 * rhs.dx.0), 183 | // xy: XY(self.s.0 * rhs.m.0), 184 | // } 185 | Motor { 186 | s: -self.xy.geometric(rhs.m), 187 | yw: self.s.geometric(rhs.dx) + self.xy.geometric(rhs.dy), 188 | wx: self.s.geometric(rhs.dy) - self.xy.geometric(rhs.dx), 189 | xy: self.s.geometric(rhs.m), 190 | } 191 | } 192 | } 193 | 194 | // Rotor.anti_geometric(Line) -> Vec2 195 | impl AntiGeometric for Rotor { 196 | type Output = Vec2; 197 | fn anti_geometric(self, rhs: Line) -> Self::Output { 198 | // Vec2 { 199 | // x: X(self.s.0 * rhs.dx.0) + X(self.xy.0 * rhs.dy.0), 200 | // y: Y(self.s.0 * rhs.dy.0) + Y(self.xy.0 * rhs.dx.0), 201 | // } 202 | Vec2 { 203 | x: self.s.anti_geometric(rhs.dx) - self.xy.anti_geometric(rhs.dy), 204 | y: self.s.anti_geometric(rhs.dy) + self.xy.anti_geometric(rhs.dx), 205 | } 206 | } 207 | } 208 | 209 | // Rotor.dot(Line) -> Motor 210 | impl Dot for Rotor { 211 | type Output = Motor; 212 | fn dot(self, rhs: Line) -> Self::Output { 213 | // Motor { 214 | // s : S(self.xy.0 * rhs.m.0), 215 | // yw: YW(self.s.0 * rhs.dx.0), 216 | // wx: WX(self.s.0 * rhs.dy.0), 217 | // xy: XY(self.s.0 * rhs.m.0), 218 | // } 219 | Motor { 220 | s: -self.xy.dot(rhs.m), 221 | yw: self.s.dot(rhs.dx), 222 | wx: self.s.dot(rhs.dy), 223 | xy: self.s.dot(rhs.m), 224 | } 225 | } 226 | } 227 | 228 | // Rotor.wedge(Line) -> Line 229 | impl Wedge for Rotor { 230 | type Output = Line; 231 | fn wedge(self, rhs: Line) -> Self::Output { 232 | // Line { 233 | // dx: YW(self.s.0 * rhs.dx.0), 234 | // dy: WX(self.s.0 * rhs.dy.0), 235 | // m : XY(self.s.0 * rhs.m.0), 236 | // } 237 | Line { 238 | dx: self.s.wedge(rhs.dx), 239 | dy: self.s.wedge(rhs.dy), 240 | m: self.s.wedge(rhs.m), 241 | } 242 | } 243 | } 244 | 245 | // Rotor.anti_wedge(Line) -> Vec2 246 | impl AntiWedge for Rotor { 247 | type Output = Vec2; 248 | fn anti_wedge(self, rhs: Line) -> Self::Output { 249 | // Vec2 { 250 | // x: X(self.xy.0 * rhs.dy.0), 251 | // y: Y(self.xy.0 * rhs.dx.0), 252 | // } 253 | Vec2 { 254 | x: -self.xy.anti_wedge(rhs.dy), 255 | y: self.xy.anti_wedge(rhs.dx), 256 | } 257 | } 258 | } 259 | 260 | // --------------------------------------------------------------------- 261 | // Rotor OP Rotor: 262 | 263 | // Rotor.geometric(Rotor) -> Rotor 264 | impl Geometric for Rotor { 265 | type Output = Rotor; 266 | fn geometric(self, rhs: Rotor) -> Self::Output { 267 | // Rotor { 268 | // s : S(self.s.0 * rhs.s.0) + S(self.xy.0 * rhs.xy.0), 269 | // xy: XY(self.s.0 * rhs.xy.0) + XY(self.xy.0 * rhs.s.0), 270 | // } 271 | Rotor { 272 | s: self.s.geometric(rhs.s) - self.xy.geometric(rhs.xy), 273 | xy: self.s.geometric(rhs.xy) + self.xy.geometric(rhs.s), 274 | } 275 | } 276 | } 277 | 278 | // Omitted: Rotor anti_geometric Rotor = 0 (unnamed type) 279 | 280 | // Rotor.dot(Rotor) -> Rotor 281 | impl Dot for Rotor { 282 | type Output = Rotor; 283 | fn dot(self, rhs: Rotor) -> Self::Output { 284 | // Rotor { 285 | // s : S(self.s.0 * rhs.s.0) + S(self.xy.0 * rhs.xy.0), 286 | // xy: XY(self.s.0 * rhs.xy.0) + XY(self.xy.0 * rhs.s.0), 287 | // } 288 | Rotor { 289 | s: self.s.dot(rhs.s) - self.xy.dot(rhs.xy), 290 | xy: self.s.dot(rhs.xy) + self.xy.dot(rhs.s), 291 | } 292 | } 293 | } 294 | 295 | // Rotor.wedge(Rotor) -> Rotor 296 | impl Wedge for Rotor { 297 | type Output = Rotor; 298 | fn wedge(self, rhs: Rotor) -> Self::Output { 299 | // Rotor { 300 | // s : S(self.s.0 * rhs.s.0), 301 | // xy: XY(self.s.0 * rhs.xy.0) + XY(self.xy.0 * rhs.s.0), 302 | // } 303 | Rotor { 304 | s: self.s.wedge(rhs.s), 305 | xy: self.s.wedge(rhs.xy) + self.xy.wedge(rhs.s), 306 | } 307 | } 308 | } 309 | 310 | // Omitted: Rotor anti_wedge Rotor = 0 (unnamed type) 311 | 312 | // --------------------------------------------------------------------- 313 | // Rotor OP Motor: 314 | 315 | // Rotor.geometric(Motor) -> Motor 316 | impl Geometric for Rotor { 317 | type Output = Motor; 318 | fn geometric(self, rhs: Motor) -> Self::Output { 319 | // Motor { 320 | // s : S(self.s.0 * rhs.s.0) + S(self.xy.0 * rhs.xy.0), 321 | // yw: YW(self.s.0 * rhs.yw.0) + YW(self.xy.0 * rhs.wx.0), 322 | // wx: WX(self.s.0 * rhs.wx.0) + WX(self.xy.0 * rhs.yw.0), 323 | // xy: XY(self.s.0 * rhs.xy.0) + XY(self.xy.0 * rhs.s.0), 324 | // } 325 | Motor { 326 | s: self.s.geometric(rhs.s) - self.xy.geometric(rhs.xy), 327 | yw: self.s.geometric(rhs.yw) + self.xy.geometric(rhs.wx), 328 | wx: self.s.geometric(rhs.wx) - self.xy.geometric(rhs.yw), 329 | xy: self.s.geometric(rhs.xy) + self.xy.geometric(rhs.s), 330 | } 331 | } 332 | } 333 | 334 | // Rotor.anti_geometric(Motor) -> Vec2 335 | impl AntiGeometric for Rotor { 336 | type Output = Vec2; 337 | fn anti_geometric(self, rhs: Motor) -> Self::Output { 338 | // Vec2 { 339 | // x: X(self.s.0 * rhs.yw.0) + X(self.xy.0 * rhs.wx.0), 340 | // y: Y(self.s.0 * rhs.wx.0) + Y(self.xy.0 * rhs.yw.0), 341 | // } 342 | Vec2 { 343 | x: self.s.anti_geometric(rhs.yw) - self.xy.anti_geometric(rhs.wx), 344 | y: self.s.anti_geometric(rhs.wx) + self.xy.anti_geometric(rhs.yw), 345 | } 346 | } 347 | } 348 | 349 | // Rotor.dot(Motor) -> Motor 350 | impl Dot for Rotor { 351 | type Output = Motor; 352 | fn dot(self, rhs: Motor) -> Self::Output { 353 | // Motor { 354 | // s : S(self.s.0 * rhs.s.0) + S(self.xy.0 * rhs.xy.0), 355 | // yw: YW(self.s.0 * rhs.yw.0), 356 | // wx: WX(self.s.0 * rhs.wx.0), 357 | // xy: XY(self.s.0 * rhs.xy.0) + XY(self.xy.0 * rhs.s.0), 358 | // } 359 | Motor { 360 | s: self.s.dot(rhs.s) - self.xy.dot(rhs.xy), 361 | yw: self.s.dot(rhs.yw), 362 | wx: self.s.dot(rhs.wx), 363 | xy: self.s.dot(rhs.xy) + self.xy.dot(rhs.s), 364 | } 365 | } 366 | } 367 | 368 | // Rotor.wedge(Motor) -> Motor 369 | impl Wedge for Rotor { 370 | type Output = Motor; 371 | fn wedge(self, rhs: Motor) -> Self::Output { 372 | // Motor { 373 | // s : S(self.s.0 * rhs.s.0), 374 | // yw: YW(self.s.0 * rhs.yw.0), 375 | // wx: WX(self.s.0 * rhs.wx.0), 376 | // xy: XY(self.s.0 * rhs.xy.0) + XY(self.xy.0 * rhs.s.0), 377 | // } 378 | Motor { 379 | s: self.s.wedge(rhs.s), 380 | yw: self.s.wedge(rhs.yw), 381 | wx: self.s.wedge(rhs.wx), 382 | xy: self.s.wedge(rhs.xy) + self.xy.wedge(rhs.s), 383 | } 384 | } 385 | } 386 | 387 | // Rotor.anti_wedge(Motor) -> Vec2 388 | impl AntiWedge for Rotor { 389 | type Output = Vec2; 390 | fn anti_wedge(self, rhs: Motor) -> Self::Output { 391 | // Vec2 { 392 | // x: X(self.xy.0 * rhs.wx.0), 393 | // y: Y(self.xy.0 * rhs.yw.0), 394 | // } 395 | Vec2 { 396 | x: -self.xy.anti_wedge(rhs.wx), 397 | y: self.xy.anti_wedge(rhs.yw), 398 | } 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /reference/src/bin/ref_pga2d.rs: -------------------------------------------------------------------------------- 1 | // Written by a generator written by enki. 2 | #![allow(unused_imports)] 3 | #![allow(dead_code)] 4 | #![allow(non_upper_case_globals)] 5 | #![allow(non_snake_case)] 6 | #![allow(non_camel_case_types)] 7 | 8 | use std::fmt; 9 | use std::ops::{Add, BitAnd, BitOr, BitXor, Index, IndexMut, Mul, Not, Sub}; 10 | 11 | type float_t = f64; 12 | 13 | // use std::f64::consts::PI; 14 | const PI: float_t = 3.14159265358979323846; 15 | 16 | const basis: &'static [&'static str] = &["1", "e0", "e1", "e2", "e01", "e20", "e12", "e012"]; 17 | const basis_count: usize = basis.len(); 18 | 19 | #[derive(Default, Clone, Copy, PartialEq)] 20 | struct PGA2D { 21 | mvec: [float_t; basis_count], 22 | } 23 | 24 | impl PGA2D { 25 | pub const fn zero() -> Self { 26 | Self { 27 | mvec: [0.0; basis_count], 28 | } 29 | } 30 | 31 | pub const fn new(f: float_t, idx: usize) -> Self { 32 | let mut ret = Self::zero(); 33 | ret.mvec[idx] = f; 34 | ret 35 | } 36 | } 37 | 38 | // basis vectors are available as global constants. 39 | const e0: PGA2D = PGA2D::new(1.0, 1); 40 | const e1: PGA2D = PGA2D::new(1.0, 2); 41 | const e2: PGA2D = PGA2D::new(1.0, 3); 42 | const e01: PGA2D = PGA2D::new(1.0, 4); 43 | const e20: PGA2D = PGA2D::new(1.0, 5); 44 | const e12: PGA2D = PGA2D::new(1.0, 6); 45 | const e012: PGA2D = PGA2D::new(1.0, 7); 46 | 47 | impl Index for PGA2D { 48 | type Output = float_t; 49 | 50 | fn index<'a>(&'a self, index: usize) -> &'a Self::Output { 51 | &self.mvec[index] 52 | } 53 | } 54 | 55 | impl IndexMut for PGA2D { 56 | fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Self::Output { 57 | &mut self.mvec[index] 58 | } 59 | } 60 | 61 | impl fmt::Debug for PGA2D { 62 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 | let mut n = 0; 64 | let ret = self 65 | .mvec 66 | .iter() 67 | .enumerate() 68 | .filter_map(|(i, &coeff)| { 69 | if coeff > 0.00001 || coeff < -0.00001 { 70 | n = 1; 71 | Some(format!( 72 | "{}{}", 73 | format!("{:.*}", 7, coeff).trim_end_matches('0').trim_end_matches('.'), 74 | if i > 0 { basis[i] } else { "" } 75 | )) 76 | } else { 77 | None 78 | } 79 | }) 80 | .collect::>() 81 | .join(" + "); 82 | if n == 0 { 83 | write!(f, "0") 84 | } else { 85 | write!(f, "{}", ret) 86 | } 87 | } 88 | } 89 | 90 | impl fmt::Display for PGA2D { 91 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 92 | let mut n = 0; 93 | let ret = self 94 | .mvec 95 | .iter() 96 | .enumerate() 97 | .filter_map(|(i, &coeff)| { 98 | if coeff > 0.00001 || coeff < -0.00001 { 99 | n = 1; 100 | Some(format!( 101 | "{}{}", 102 | format!("{:.*}", 7, coeff).trim_end_matches('0').trim_end_matches('.'), 103 | if i > 0 { basis[i] } else { "" } 104 | )) 105 | } else { 106 | None 107 | } 108 | }) 109 | .collect::>() 110 | .join(" + "); 111 | if n == 0 { 112 | write!(f, "0") 113 | } else { 114 | write!(f, "{}", ret) 115 | } 116 | } 117 | } 118 | 119 | // Reverse 120 | // Reverse the order of the basis blades. 121 | impl PGA2D { 122 | pub fn Reverse(self: Self) -> PGA2D { 123 | let mut res = PGA2D::zero(); 124 | let a = self; 125 | res[0] = a[0]; 126 | res[1] = a[1]; 127 | res[2] = a[2]; 128 | res[3] = a[3]; 129 | res[4] = -a[4]; 130 | res[5] = -a[5]; 131 | res[6] = -a[6]; 132 | res[7] = -a[7]; 133 | res 134 | } 135 | } 136 | 137 | // Dual 138 | // Poincare duality operator. 139 | impl PGA2D { 140 | pub fn Dual(self: Self) -> PGA2D { 141 | let mut res = PGA2D::zero(); 142 | let a = self; 143 | res[0] = a[7]; 144 | res[1] = a[6]; 145 | res[2] = a[5]; 146 | res[3] = a[4]; 147 | res[4] = a[3]; 148 | res[5] = a[2]; 149 | res[6] = a[1]; 150 | res[7] = a[0]; 151 | res 152 | } 153 | } 154 | 155 | impl Not for PGA2D { 156 | type Output = PGA2D; 157 | 158 | fn not(self: Self) -> PGA2D { 159 | let mut res = PGA2D::zero(); 160 | let a = self; 161 | res[0] = a[7]; 162 | res[1] = a[6]; 163 | res[2] = a[5]; 164 | res[3] = a[4]; 165 | res[4] = a[3]; 166 | res[5] = a[2]; 167 | res[6] = a[1]; 168 | res[7] = a[0]; 169 | res 170 | } 171 | } 172 | 173 | // Conjugate 174 | // Clifford Conjugation 175 | impl PGA2D { 176 | pub fn Conjugate(self: Self) -> PGA2D { 177 | let mut res = PGA2D::zero(); 178 | let a = self; 179 | res[0] = a[0]; 180 | res[1] = -a[1]; 181 | res[2] = -a[2]; 182 | res[3] = -a[3]; 183 | res[4] = -a[4]; 184 | res[5] = -a[5]; 185 | res[6] = -a[6]; 186 | res[7] = a[7]; 187 | res 188 | } 189 | } 190 | 191 | // Involute 192 | // Main involution 193 | impl PGA2D { 194 | pub fn Involute(self: Self) -> PGA2D { 195 | let mut res = PGA2D::zero(); 196 | let a = self; 197 | res[0] = a[0]; 198 | res[1] = -a[1]; 199 | res[2] = -a[2]; 200 | res[3] = -a[3]; 201 | res[4] = a[4]; 202 | res[5] = a[5]; 203 | res[6] = a[6]; 204 | res[7] = -a[7]; 205 | res 206 | } 207 | } 208 | 209 | // Mul 210 | // The geometric product. 211 | impl Mul for PGA2D { 212 | type Output = PGA2D; 213 | 214 | fn mul(self: PGA2D, b: PGA2D) -> PGA2D { 215 | let mut res = PGA2D::zero(); 216 | let a = self; 217 | res[0] = b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6]; 218 | res[1] = b[1] * a[0] + b[0] * a[1] - b[4] * a[2] + b[5] * a[3] + b[2] * a[4] 219 | - b[3] * a[5] 220 | - b[7] * a[6] 221 | - b[6] * a[7]; 222 | res[2] = b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6]; 223 | res[3] = b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6]; 224 | res[4] = b[4] * a[0] + b[2] * a[1] - b[1] * a[2] + b[7] * a[3] + b[0] * a[4] + b[6] * a[5] - b[5] * a[6] 225 | + b[3] * a[7]; 226 | res[5] = b[5] * a[0] - b[3] * a[1] + b[7] * a[2] + b[1] * a[3] - b[6] * a[4] 227 | + b[0] * a[5] 228 | + b[4] * a[6] 229 | + b[2] * a[7]; 230 | res[6] = b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6]; 231 | res[7] = 232 | b[7] * a[0] 233 | + b[6] * a[1] + b[5] * a[2] 234 | + b[4] * a[3] + b[3] * a[4] 235 | + b[2] * a[5] + b[1] * a[6] 236 | + b[0] * a[7]; 237 | res 238 | } 239 | } 240 | 241 | // Wedge 242 | // The outer product. (MEET) 243 | impl BitXor for PGA2D { 244 | type Output = PGA2D; 245 | 246 | fn bitxor(self: PGA2D, b: PGA2D) -> PGA2D { 247 | let mut res = PGA2D::zero(); 248 | let a = self; 249 | res[0] = b[0] * a[0]; 250 | res[1] = b[1] * a[0] + b[0] * a[1]; 251 | res[2] = b[2] * a[0] + b[0] * a[2]; 252 | res[3] = b[3] * a[0] + b[0] * a[3]; 253 | res[4] = b[4] * a[0] + b[2] * a[1] - b[1] * a[2] + b[0] * a[4]; 254 | res[5] = b[5] * a[0] - b[3] * a[1] + b[1] * a[3] + b[0] * a[5]; 255 | res[6] = b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6]; 256 | res[7] = 257 | b[7] * a[0] 258 | + b[6] * a[1] + b[5] * a[2] 259 | + b[4] * a[3] + b[3] * a[4] 260 | + b[2] * a[5] + b[1] * a[6] 261 | + b[0] * a[7]; 262 | res 263 | } 264 | } 265 | 266 | // Vee 267 | // The regressive product. (JOIN) 268 | impl BitAnd for PGA2D { 269 | type Output = PGA2D; 270 | 271 | fn bitand(self: PGA2D, b: PGA2D) -> PGA2D { 272 | let mut res = PGA2D::zero(); 273 | let a = self; 274 | res[7] = b[7] * a[7]; 275 | res[6] = b[6] * a[7] + b[7] * a[6]; 276 | res[5] = b[5] * a[7] + b[7] * a[5]; 277 | res[4] = b[4] * a[7] + b[7] * a[4]; 278 | res[3] = b[3] * a[7] + b[5] * a[6] - b[6] * a[5] + b[7] * a[3]; 279 | res[2] = b[2] * a[7] - b[4] * a[6] + b[6] * a[4] + b[7] * a[2]; 280 | res[1] = b[1] * a[7] + b[4] * a[5] - b[5] * a[4] + b[7] * a[1]; 281 | res[0] = 282 | b[0] * a[7] 283 | + b[1] * a[6] + b[2] * a[5] 284 | + b[3] * a[4] + b[4] * a[3] 285 | + b[5] * a[2] + b[6] * a[1] 286 | + b[7] * a[0]; 287 | res 288 | } 289 | } 290 | 291 | // Dot 292 | // The inner product. 293 | impl BitOr for PGA2D { 294 | type Output = PGA2D; 295 | 296 | fn bitor(self: PGA2D, b: PGA2D) -> PGA2D { 297 | let mut res = PGA2D::zero(); 298 | let a = self; 299 | res[0] = b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6]; 300 | res[1] = b[1] * a[0] + b[0] * a[1] - b[4] * a[2] + b[5] * a[3] + b[2] * a[4] 301 | - b[3] * a[5] 302 | - b[7] * a[6] 303 | - b[6] * a[7]; 304 | res[2] = b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6]; 305 | res[3] = b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6]; 306 | res[4] = b[4] * a[0] + b[7] * a[3] + b[0] * a[4] + b[3] * a[7]; 307 | res[5] = b[5] * a[0] + b[7] * a[2] + b[0] * a[5] + b[2] * a[7]; 308 | res[6] = b[6] * a[0] + b[0] * a[6]; 309 | res[7] = b[7] * a[0] + b[0] * a[7]; 310 | res 311 | } 312 | } 313 | 314 | // Add 315 | // Multivector addition 316 | impl Add for PGA2D { 317 | type Output = PGA2D; 318 | 319 | fn add(self: PGA2D, b: PGA2D) -> PGA2D { 320 | let mut res = PGA2D::zero(); 321 | let a = self; 322 | res[0] = a[0] + b[0]; 323 | res[1] = a[1] + b[1]; 324 | res[2] = a[2] + b[2]; 325 | res[3] = a[3] + b[3]; 326 | res[4] = a[4] + b[4]; 327 | res[5] = a[5] + b[5]; 328 | res[6] = a[6] + b[6]; 329 | res[7] = a[7] + b[7]; 330 | res 331 | } 332 | } 333 | 334 | // Sub 335 | // Multivector subtraction 336 | impl Sub for PGA2D { 337 | type Output = PGA2D; 338 | 339 | fn sub(self: PGA2D, b: PGA2D) -> PGA2D { 340 | let mut res = PGA2D::zero(); 341 | let a = self; 342 | res[0] = a[0] - b[0]; 343 | res[1] = a[1] - b[1]; 344 | res[2] = a[2] - b[2]; 345 | res[3] = a[3] - b[3]; 346 | res[4] = a[4] - b[4]; 347 | res[5] = a[5] - b[5]; 348 | res[6] = a[6] - b[6]; 349 | res[7] = a[7] - b[7]; 350 | res 351 | } 352 | } 353 | 354 | // smul 355 | // scalar/multivector multiplication 356 | impl Mul for float_t { 357 | type Output = PGA2D; 358 | 359 | fn mul(self: float_t, b: PGA2D) -> PGA2D { 360 | let mut res = PGA2D::zero(); 361 | let a = self; 362 | res[0] = a * b[0]; 363 | res[1] = a * b[1]; 364 | res[2] = a * b[2]; 365 | res[3] = a * b[3]; 366 | res[4] = a * b[4]; 367 | res[5] = a * b[5]; 368 | res[6] = a * b[6]; 369 | res[7] = a * b[7]; 370 | res 371 | } 372 | } 373 | 374 | // muls 375 | // multivector/scalar multiplication 376 | impl Mul for PGA2D { 377 | type Output = PGA2D; 378 | 379 | fn mul(self: PGA2D, b: float_t) -> PGA2D { 380 | let mut res = PGA2D::zero(); 381 | let a = self; 382 | res[0] = a[0] * b; 383 | res[1] = a[1] * b; 384 | res[2] = a[2] * b; 385 | res[3] = a[3] * b; 386 | res[4] = a[4] * b; 387 | res[5] = a[5] * b; 388 | res[6] = a[6] * b; 389 | res[7] = a[7] * b; 390 | res 391 | } 392 | } 393 | 394 | // sadd 395 | // scalar/multivector addition 396 | impl Add for float_t { 397 | type Output = PGA2D; 398 | 399 | fn add(self: float_t, b: PGA2D) -> PGA2D { 400 | let mut res = PGA2D::zero(); 401 | let a = self; 402 | res[0] = a + b[0]; 403 | res[1] = b[1]; 404 | res[2] = b[2]; 405 | res[3] = b[3]; 406 | res[4] = b[4]; 407 | res[5] = b[5]; 408 | res[6] = b[6]; 409 | res[7] = b[7]; 410 | res 411 | } 412 | } 413 | 414 | // adds 415 | // multivector/scalar addition 416 | impl Add for PGA2D { 417 | type Output = PGA2D; 418 | 419 | fn add(self: PGA2D, b: float_t) -> PGA2D { 420 | let mut res = PGA2D::zero(); 421 | let a = self; 422 | res[0] = a[0] + b; 423 | res[1] = a[1]; 424 | res[2] = a[2]; 425 | res[3] = a[3]; 426 | res[4] = a[4]; 427 | res[5] = a[5]; 428 | res[6] = a[6]; 429 | res[7] = a[7]; 430 | res 431 | } 432 | } 433 | 434 | impl PGA2D { 435 | pub fn norm(self: Self) -> float_t { 436 | let scalar_part = (self * self.Conjugate())[0]; 437 | 438 | scalar_part.abs().sqrt() 439 | } 440 | 441 | pub fn inorm(self: Self) -> float_t { 442 | self.Dual().norm() 443 | } 444 | 445 | pub fn normalized(self: Self) -> Self { 446 | self * (1.0 / self.norm()) 447 | } 448 | } 449 | 450 | fn main() { 451 | // let l1 = 0.0 * e0 + 1.0 * e1 + 0.0 * e2; // x=0 452 | // let l2 = 0.0 * e0 + -0.5f64.sqrt() * e1 + 0.5f64.sqrt() * e2; // x-y=0 453 | // let t = l1 * l2; 454 | // let p = 3.0 * e01 + 2.0 * e20 + e12; // x=2, y=3 455 | // dbg!(&t); 456 | // dbg!(&p); 457 | // dbg!(t * p * t.Reverse()); 458 | 459 | // let t = (2.0 * e12 + 3.0 * e20 + 5.0 * e01); 460 | // let p = (2.0 * e12 + 3.0 * e20 + 5.0 * e01); 461 | 462 | // dbg! | (7.0 * e0 + 11.0 * e1 + 13.0 * e2)); 463 | 464 | // dbg!((2.0 * e12 + 3.0 * e20 + 5.0 * e01) | (7.0 * e0 + 11.0 * e1 + 13.0 * e2)); 465 | 466 | // dbg!(e12 * e12); 467 | 468 | let s = PGA2D::new(1.0, 0); 469 | let blades = vec![ 470 | s, 471 | e0.clone(), 472 | e1.clone(), 473 | e2.clone(), 474 | e01.clone(), 475 | e20.clone(), 476 | e12.clone(), 477 | e012.clone(), 478 | ]; 479 | 480 | println!(); 481 | println!("Geometric product multiplication table (left side * top row):"); 482 | for &a in &blades { 483 | print!(" "); 484 | for &b in &blades { 485 | print!("{:<8}", (a * b).to_string()); 486 | } 487 | println!(); 488 | } 489 | 490 | println!(); 491 | println!("Inner / dot product multiplication table (left side | top row):"); 492 | for &a in &blades { 493 | print!(" "); 494 | for &b in &blades { 495 | print!("{:<8}", (a | b).to_string()); 496 | } 497 | println!(); 498 | } 499 | 500 | println!(); 501 | println!("Outer product multiplication table (left side ^ top row):"); 502 | for &a in &blades { 503 | print!(" "); 504 | for &b in &blades { 505 | print!("{:<8}", (a ^ b).to_string()); 506 | } 507 | println!(); 508 | } 509 | 510 | println!(); 511 | println!("Regressive product multiplication table (right side & bottom row):"); 512 | for &a in &blades { 513 | print!(" "); 514 | for &b in &blades { 515 | print!("{:<8}", (a & b).to_string()); 516 | } 517 | println!(); 518 | } 519 | } 520 | -------------------------------------------------------------------------------- /generated/src/pga2d/line.rs: -------------------------------------------------------------------------------- 1 | //! # Line 2 | //! 3 | //! ## Operations 4 | //! ```text 5 | //! Line.geometric(Line) -> Motor 6 | //! Line.dot(Line) -> S 7 | //! Line.anti_wedge(Line) -> Vec3 8 | //! Line.anti_geometric(Vec2) -> Rotor 9 | //! Vec2.anti_geometric(Line) -> Rotor 10 | //! Line.dot(Vec2) -> Vec3 11 | //! Vec2.dot(Line) -> Vec3 12 | //! Line.wedge(Vec2) -> XYW 13 | //! Vec2.wedge(Line) -> XYW 14 | //! Line.anti_wedge(Vec2) -> S 15 | //! Vec2.anti_wedge(Line) -> S 16 | //! Line.anti_geometric(Vec3) -> Motor 17 | //! Vec3.anti_geometric(Line) -> Motor 18 | //! Line.dot(Vec3) -> Vec3 19 | //! Vec3.dot(Line) -> Vec3 20 | //! Line.wedge(Vec3) -> XYW 21 | //! Vec3.wedge(Line) -> XYW 22 | //! Line.anti_wedge(Vec3) -> S 23 | //! Vec3.anti_wedge(Line) -> S 24 | //! Line.geometric(Rotor) -> Motor 25 | //! Rotor.geometric(Line) -> Motor 26 | //! Line.anti_geometric(Rotor) -> Vec2 27 | //! Rotor.anti_geometric(Line) -> Vec2 28 | //! Line.dot(Rotor) -> Motor 29 | //! Rotor.dot(Line) -> Motor 30 | //! Line.wedge(Rotor) -> Line 31 | //! Rotor.wedge(Line) -> Line 32 | //! Line.anti_wedge(Rotor) -> Vec2 33 | //! Rotor.anti_wedge(Line) -> Vec2 34 | //! Line.geometric(Motor) -> Motor 35 | //! Motor.geometric(Line) -> Motor 36 | //! Line.dot(Motor) -> Motor 37 | //! Motor.dot(Line) -> Motor 38 | //! Line.wedge(Motor) -> Line 39 | //! Motor.wedge(Line) -> Line 40 | //! Line.anti_wedge(Motor) -> Vec3 41 | //! Motor.anti_wedge(Line) -> Vec3 42 | //! ``` 43 | 44 | use super::*; 45 | 46 | #[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, derive_more::Neg, derive_more::Add, derive_more::Sub)] 47 | pub struct Line { 48 | pub dx: YW, 49 | pub dy: WX, 50 | pub m: XY, 51 | } 52 | 53 | // --------------------------------------------------------------------- 54 | 55 | impl RCompl for Line { 56 | type Output = Vec3; 57 | fn rcompl(self) -> Self::Output { 58 | Vec3 { 59 | x: self.dx.rcompl(), 60 | y: self.dy.rcompl(), 61 | w: self.m.rcompl(), 62 | } 63 | } 64 | } 65 | 66 | impl LCompl for Line { 67 | type Output = Vec3; 68 | fn lcompl(self) -> Self::Output { 69 | Vec3 { 70 | x: self.dx.lcompl(), 71 | y: self.dy.lcompl(), 72 | w: self.m.lcompl(), 73 | } 74 | } 75 | } 76 | 77 | impl Reverse for Line { 78 | fn rev(self) -> Self { 79 | Line { 80 | dx: -self.dx, 81 | dy: -self.dy, 82 | m: -self.m, 83 | } 84 | } 85 | } 86 | 87 | impl AntiReverse for Line { 88 | fn arev(self) -> Self { 89 | Line { 90 | dx: self.dx, 91 | dy: self.dy, 92 | m: self.m, 93 | } 94 | } 95 | } 96 | 97 | // --------------------------------------------------------------------- 98 | // Line OP Vec2: 99 | 100 | // Omitted: Line geometric Vec2 = self.dx * rhs.x + self.dx * rhs.y + self.dy * rhs.x + self.dy * rhs.y + self.m * rhs.x + self.m * rhs.y (unnamed type) 101 | 102 | // Line.anti_geometric(Vec2) -> Rotor 103 | impl AntiGeometric for Line { 104 | type Output = Rotor; 105 | fn anti_geometric(self, rhs: Vec2) -> Self::Output { 106 | // Rotor { 107 | // s : S(self.dx.0 * rhs.x.0) + S(self.dy.0 * rhs.y.0), 108 | // xy: XY(self.dx.0 * rhs.y.0) + XY(self.dy.0 * rhs.x.0), 109 | // } 110 | Rotor { 111 | s: self.dx.anti_geometric(rhs.x) + self.dy.anti_geometric(rhs.y), 112 | xy: -self.dx.anti_geometric(rhs.y) + self.dy.anti_geometric(rhs.x), 113 | } 114 | } 115 | } 116 | 117 | // Line.dot(Vec2) -> Vec3 118 | impl Dot for Line { 119 | type Output = Vec3; 120 | fn dot(self, rhs: Vec2) -> Self::Output { 121 | // Vec3 { 122 | // x: X(self.m.0 * rhs.y.0), 123 | // y: Y(self.m.0 * rhs.x.0), 124 | // w: W(self.dx.0 * rhs.y.0) + W(self.dy.0 * rhs.x.0), 125 | // } 126 | Vec3 { 127 | x: self.m.dot(rhs.y), 128 | y: -self.m.dot(rhs.x), 129 | w: -self.dx.dot(rhs.y) + self.dy.dot(rhs.x), 130 | } 131 | } 132 | } 133 | 134 | // Line.wedge(Vec2) -> XYW 135 | impl Wedge for Line { 136 | type Output = XYW; 137 | fn wedge(self, rhs: Vec2) -> Self::Output { 138 | // XYW(self.dx.0 * rhs.x.0) + XYW(self.dy.0 * rhs.y.0) 139 | self.dx.wedge(rhs.x) + self.dy.wedge(rhs.y) 140 | } 141 | } 142 | 143 | // Line.anti_wedge(Vec2) -> S 144 | impl AntiWedge for Line { 145 | type Output = S; 146 | fn anti_wedge(self, rhs: Vec2) -> Self::Output { 147 | // S(self.dx.0 * rhs.x.0) + S(self.dy.0 * rhs.y.0) 148 | self.dx.anti_wedge(rhs.x) + self.dy.anti_wedge(rhs.y) 149 | } 150 | } 151 | 152 | // --------------------------------------------------------------------- 153 | // Line OP Vec3: 154 | 155 | // Omitted: Line geometric Vec3 = self.dx * rhs.x + self.dx * rhs.y + self.dy * rhs.x + self.dy * rhs.y + self.m * rhs.w + self.m * rhs.x + self.m * rhs.y (unnamed type) 156 | 157 | // Line.anti_geometric(Vec3) -> Motor 158 | impl AntiGeometric for Line { 159 | type Output = Motor; 160 | fn anti_geometric(self, rhs: Vec3) -> Self::Output { 161 | // Motor { 162 | // s : S(self.dx.0 * rhs.x.0) + S(self.dy.0 * rhs.y.0) + S(self.m.0 * rhs.w.0), 163 | // yw: YW(self.dy.0 * rhs.w.0), 164 | // wx: WX(self.dx.0 * rhs.w.0), 165 | // xy: XY(self.dx.0 * rhs.y.0) + XY(self.dy.0 * rhs.x.0), 166 | // } 167 | Motor { 168 | s: self.dx.anti_geometric(rhs.x) + self.dy.anti_geometric(rhs.y) + self.m.anti_geometric(rhs.w), 169 | yw: -self.dy.anti_geometric(rhs.w), 170 | wx: self.dx.anti_geometric(rhs.w), 171 | xy: -self.dx.anti_geometric(rhs.y) + self.dy.anti_geometric(rhs.x), 172 | } 173 | } 174 | } 175 | 176 | // Line.dot(Vec3) -> Vec3 177 | impl Dot for Line { 178 | type Output = Vec3; 179 | fn dot(self, rhs: Vec3) -> Self::Output { 180 | // Vec3 { 181 | // x: X(self.m.0 * rhs.y.0), 182 | // y: Y(self.m.0 * rhs.x.0), 183 | // w: W(self.dx.0 * rhs.y.0) + W(self.dy.0 * rhs.x.0), 184 | // } 185 | Vec3 { 186 | x: self.m.dot(rhs.y), 187 | y: -self.m.dot(rhs.x), 188 | w: -self.dx.dot(rhs.y) + self.dy.dot(rhs.x), 189 | } 190 | } 191 | } 192 | 193 | // Line.wedge(Vec3) -> XYW 194 | impl Wedge for Line { 195 | type Output = XYW; 196 | fn wedge(self, rhs: Vec3) -> Self::Output { 197 | // XYW(self.dx.0 * rhs.x.0) + XYW(self.dy.0 * rhs.y.0) + XYW(self.m.0 * rhs.w.0) 198 | self.dx.wedge(rhs.x) + self.dy.wedge(rhs.y) + self.m.wedge(rhs.w) 199 | } 200 | } 201 | 202 | // Line.anti_wedge(Vec3) -> S 203 | impl AntiWedge for Line { 204 | type Output = S; 205 | fn anti_wedge(self, rhs: Vec3) -> Self::Output { 206 | // S(self.dx.0 * rhs.x.0) + S(self.dy.0 * rhs.y.0) + S(self.m.0 * rhs.w.0) 207 | self.dx.anti_wedge(rhs.x) + self.dy.anti_wedge(rhs.y) + self.m.anti_wedge(rhs.w) 208 | } 209 | } 210 | 211 | // --------------------------------------------------------------------- 212 | // Line OP Line: 213 | 214 | // Line.geometric(Line) -> Motor 215 | impl Geometric for Line { 216 | type Output = Motor; 217 | fn geometric(self, rhs: Line) -> Self::Output { 218 | // Motor { 219 | // s : S(self.m.0 * rhs.m.0), 220 | // yw: YW(self.dy.0 * rhs.m.0) + YW(self.m.0 * rhs.dy.0), 221 | // wx: WX(self.dx.0 * rhs.m.0) + WX(self.m.0 * rhs.dx.0), 222 | // xy: Default::default(), 223 | // } 224 | Motor { 225 | s: -self.m.geometric(rhs.m), 226 | yw: -self.dy.geometric(rhs.m) + self.m.geometric(rhs.dy), 227 | wx: self.dx.geometric(rhs.m) - self.m.geometric(rhs.dx), 228 | xy: Default::default(), 229 | } 230 | } 231 | } 232 | 233 | // Omitted: Line anti_geometric Line = self.dx !* rhs.dx + self.dx !* rhs.dy + self.dx !* rhs.m + self.dy !* rhs.dx + self.dy !* rhs.dy + self.dy !* rhs.m + self.m !* rhs.dx + self.m !* rhs.dy (unnamed type) 234 | 235 | // Line.dot(Line) -> S 236 | impl Dot for Line { 237 | type Output = S; 238 | fn dot(self, rhs: Line) -> Self::Output { 239 | // -S(self.m.0 * rhs.m.0) 240 | self.m.dot(rhs.m) 241 | } 242 | } 243 | 244 | // Omitted: Line wedge Line = 0 (unnamed type) 245 | 246 | // Line.anti_wedge(Line) -> Vec3 247 | impl AntiWedge for Line { 248 | type Output = Vec3; 249 | fn anti_wedge(self, rhs: Line) -> Self::Output { 250 | // Vec3 { 251 | // x: X(self.dy.0 * rhs.m.0) + X(self.m.0 * rhs.dy.0), 252 | // y: Y(self.dx.0 * rhs.m.0) + Y(self.m.0 * rhs.dx.0), 253 | // w: W(self.dx.0 * rhs.dy.0) + W(self.dy.0 * rhs.dx.0), 254 | // } 255 | Vec3 { 256 | x: self.dy.anti_wedge(rhs.m) - self.m.anti_wedge(rhs.dy), 257 | y: -self.dx.anti_wedge(rhs.m) + self.m.anti_wedge(rhs.dx), 258 | w: self.dx.anti_wedge(rhs.dy) - self.dy.anti_wedge(rhs.dx), 259 | } 260 | } 261 | } 262 | 263 | // --------------------------------------------------------------------- 264 | // Line OP Rotor: 265 | 266 | // Line.geometric(Rotor) -> Motor 267 | impl Geometric for Line { 268 | type Output = Motor; 269 | fn geometric(self, rhs: Rotor) -> Self::Output { 270 | // Motor { 271 | // s : S(self.m.0 * rhs.xy.0), 272 | // yw: YW(self.dx.0 * rhs.s.0) + YW(self.dy.0 * rhs.xy.0), 273 | // wx: WX(self.dx.0 * rhs.xy.0) + WX(self.dy.0 * rhs.s.0), 274 | // xy: XY(self.m.0 * rhs.s.0), 275 | // } 276 | Motor { 277 | s: -self.m.geometric(rhs.xy), 278 | yw: self.dx.geometric(rhs.s) - self.dy.geometric(rhs.xy), 279 | wx: self.dx.geometric(rhs.xy) + self.dy.geometric(rhs.s), 280 | xy: self.m.geometric(rhs.s), 281 | } 282 | } 283 | } 284 | 285 | // Line.anti_geometric(Rotor) -> Vec2 286 | impl AntiGeometric for Line { 287 | type Output = Vec2; 288 | fn anti_geometric(self, rhs: Rotor) -> Self::Output { 289 | // Vec2 { 290 | // x: X(self.dx.0 * rhs.s.0) + X(self.dy.0 * rhs.xy.0), 291 | // y: Y(self.dx.0 * rhs.xy.0) + Y(self.dy.0 * rhs.s.0), 292 | // } 293 | Vec2 { 294 | x: self.dx.anti_geometric(rhs.s) + self.dy.anti_geometric(rhs.xy), 295 | y: -self.dx.anti_geometric(rhs.xy) + self.dy.anti_geometric(rhs.s), 296 | } 297 | } 298 | } 299 | 300 | // Line.dot(Rotor) -> Motor 301 | impl Dot for Line { 302 | type Output = Motor; 303 | fn dot(self, rhs: Rotor) -> Self::Output { 304 | // Motor { 305 | // s : S(self.m.0 * rhs.xy.0), 306 | // yw: YW(self.dx.0 * rhs.s.0), 307 | // wx: WX(self.dy.0 * rhs.s.0), 308 | // xy: XY(self.m.0 * rhs.s.0), 309 | // } 310 | Motor { 311 | s: -self.m.dot(rhs.xy), 312 | yw: self.dx.dot(rhs.s), 313 | wx: self.dy.dot(rhs.s), 314 | xy: self.m.dot(rhs.s), 315 | } 316 | } 317 | } 318 | 319 | // Line.wedge(Rotor) -> Line 320 | impl Wedge for Line { 321 | type Output = Line; 322 | fn wedge(self, rhs: Rotor) -> Self::Output { 323 | // Line { 324 | // dx: YW(self.dx.0 * rhs.s.0), 325 | // dy: WX(self.dy.0 * rhs.s.0), 326 | // m : XY(self.m.0 * rhs.s.0), 327 | // } 328 | Line { 329 | dx: self.dx.wedge(rhs.s), 330 | dy: self.dy.wedge(rhs.s), 331 | m: self.m.wedge(rhs.s), 332 | } 333 | } 334 | } 335 | 336 | // Line.anti_wedge(Rotor) -> Vec2 337 | impl AntiWedge for Line { 338 | type Output = Vec2; 339 | fn anti_wedge(self, rhs: Rotor) -> Self::Output { 340 | // Vec2 { 341 | // x: X(self.dy.0 * rhs.xy.0), 342 | // y: Y(self.dx.0 * rhs.xy.0), 343 | // } 344 | Vec2 { 345 | x: self.dy.anti_wedge(rhs.xy), 346 | y: -self.dx.anti_wedge(rhs.xy), 347 | } 348 | } 349 | } 350 | 351 | // --------------------------------------------------------------------- 352 | // Line OP Motor: 353 | 354 | // Line.geometric(Motor) -> Motor 355 | impl Geometric for Line { 356 | type Output = Motor; 357 | fn geometric(self, rhs: Motor) -> Self::Output { 358 | // Motor { 359 | // s : S(self.m.0 * rhs.xy.0), 360 | // yw: YW(self.dx.0 * rhs.s.0) + YW(self.dy.0 * rhs.xy.0) + YW(self.m.0 * rhs.wx.0), 361 | // wx: WX(self.dx.0 * rhs.xy.0) + WX(self.dy.0 * rhs.s.0) + WX(self.m.0 * rhs.yw.0), 362 | // xy: XY(self.m.0 * rhs.s.0), 363 | // } 364 | Motor { 365 | s: -self.m.geometric(rhs.xy), 366 | yw: self.dx.geometric(rhs.s) - self.dy.geometric(rhs.xy) + self.m.geometric(rhs.wx), 367 | wx: self.dx.geometric(rhs.xy) + self.dy.geometric(rhs.s) - self.m.geometric(rhs.yw), 368 | xy: self.m.geometric(rhs.s), 369 | } 370 | } 371 | } 372 | 373 | // Omitted: Line anti_geometric Motor = self.dx !* rhs.s + self.dx !* rhs.wx + self.dx !* rhs.xy + self.dx !* rhs.yw + self.dy !* rhs.s + self.dy !* rhs.wx + self.dy !* rhs.xy + self.dy !* rhs.yw + self.m !* rhs.wx + self.m !* rhs.yw (unnamed type) 374 | 375 | // Line.dot(Motor) -> Motor 376 | impl Dot for Line { 377 | type Output = Motor; 378 | fn dot(self, rhs: Motor) -> Self::Output { 379 | // Motor { 380 | // s : S(self.m.0 * rhs.xy.0), 381 | // yw: YW(self.dx.0 * rhs.s.0), 382 | // wx: WX(self.dy.0 * rhs.s.0), 383 | // xy: XY(self.m.0 * rhs.s.0), 384 | // } 385 | Motor { 386 | s: -self.m.dot(rhs.xy), 387 | yw: self.dx.dot(rhs.s), 388 | wx: self.dy.dot(rhs.s), 389 | xy: self.m.dot(rhs.s), 390 | } 391 | } 392 | } 393 | 394 | // Line.wedge(Motor) -> Line 395 | impl Wedge for Line { 396 | type Output = Line; 397 | fn wedge(self, rhs: Motor) -> Self::Output { 398 | // Line { 399 | // dx: YW(self.dx.0 * rhs.s.0), 400 | // dy: WX(self.dy.0 * rhs.s.0), 401 | // m : XY(self.m.0 * rhs.s.0), 402 | // } 403 | Line { 404 | dx: self.dx.wedge(rhs.s), 405 | dy: self.dy.wedge(rhs.s), 406 | m: self.m.wedge(rhs.s), 407 | } 408 | } 409 | } 410 | 411 | // Line.anti_wedge(Motor) -> Vec3 412 | impl AntiWedge for Line { 413 | type Output = Vec3; 414 | fn anti_wedge(self, rhs: Motor) -> Self::Output { 415 | // Vec3 { 416 | // x: X(self.dy.0 * rhs.xy.0) + X(self.m.0 * rhs.wx.0), 417 | // y: Y(self.dx.0 * rhs.xy.0) + Y(self.m.0 * rhs.yw.0), 418 | // w: W(self.dx.0 * rhs.wx.0) + W(self.dy.0 * rhs.yw.0), 419 | // } 420 | Vec3 { 421 | x: self.dy.anti_wedge(rhs.xy) - self.m.anti_wedge(rhs.wx), 422 | y: -self.dx.anti_wedge(rhs.xy) + self.m.anti_wedge(rhs.yw), 423 | w: self.dx.anti_wedge(rhs.wx) - self.dy.anti_wedge(rhs.yw), 424 | } 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /generated/src/pga3d/moment3.rs: -------------------------------------------------------------------------------- 1 | //! # Moment3 2 | //! 3 | //! ## Operations 4 | //! ```text 5 | //! Moment3.dot(Moment3) -> S 6 | //! Moment3.dot(Vec3) -> Vec3 7 | //! Vec3.dot(Moment3) -> Vec3 8 | //! Moment3.wedge(Vec3) -> XYZ 9 | //! Vec3.wedge(Moment3) -> XYZ 10 | //! Moment3.anti_geometric(Vec4) -> Vec3 11 | //! Vec4.anti_geometric(Moment3) -> Vec3 12 | //! Moment3.dot(Vec4) -> Vec3 13 | //! Vec4.dot(Moment3) -> Vec3 14 | //! Moment3.wedge(Vec4) -> Plane 15 | //! Vec4.wedge(Moment3) -> Plane 16 | //! Moment3.dot(Line3) -> S 17 | //! Line3.dot(Moment3) -> S 18 | //! Moment3.wedge(Line3) -> XYZW 19 | //! Line3.wedge(Moment3) -> XYZW 20 | //! Moment3.anti_wedge(Line3) -> S 21 | //! Line3.anti_wedge(Moment3) -> S 22 | //! Moment3.dot(Plane) -> Vec4 23 | //! Plane.dot(Moment3) -> Vec4 24 | //! Moment3.anti_wedge(Plane) -> Vec3 25 | //! Plane.anti_wedge(Moment3) -> Vec3 26 | //! Moment3.geometric(Rotor3) -> Rotor3 27 | //! Rotor3.geometric(Moment3) -> Rotor3 28 | //! Moment3.dot(Rotor3) -> Line3 29 | //! Rotor3.dot(Moment3) -> Line3 30 | //! Moment3.wedge(Rotor3) -> XYZW 31 | //! Rotor3.wedge(Moment3) -> XYZW 32 | //! 33 | //! ``` 34 | 35 | use super::*; 36 | 37 | #[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, derive_more::Neg, derive_more::Add, derive_more::Sub)] 38 | pub struct Moment3 { 39 | pub mx: YZ, 40 | pub my: ZX, 41 | pub mz: XY, 42 | } 43 | 44 | // --------------------------------------------------------------------- 45 | 46 | impl RCompl for Moment3 { 47 | type Output = Line3; 48 | fn rcompl(self) -> Self::Output { 49 | Line3 { 50 | vx: -self.mx.rcompl(), 51 | vy: -self.my.rcompl(), 52 | vz: -self.mz.rcompl(), 53 | mx: Default::default(), 54 | my: Default::default(), 55 | mz: Default::default(), 56 | } 57 | } 58 | } 59 | 60 | impl LCompl for Moment3 { 61 | type Output = Line3; 62 | fn lcompl(self) -> Self::Output { 63 | Line3 { 64 | vx: -self.mx.lcompl(), 65 | vy: -self.my.lcompl(), 66 | vz: -self.mz.lcompl(), 67 | mx: Default::default(), 68 | my: Default::default(), 69 | mz: Default::default(), 70 | } 71 | } 72 | } 73 | 74 | impl Reverse for Moment3 { 75 | fn rev(self) -> Self { 76 | Moment3 { 77 | mx: -self.mx, 78 | my: -self.my, 79 | mz: -self.mz, 80 | } 81 | } 82 | } 83 | 84 | impl AntiReverse for Moment3 { 85 | fn arev(self) -> Self { 86 | Moment3 { 87 | mx: -self.mx, 88 | my: -self.my, 89 | mz: -self.mz, 90 | } 91 | } 92 | } 93 | 94 | // --------------------------------------------------------------------- 95 | // Moment3 OP Vec3: 96 | 97 | // Omitted: Moment3 geometric Vec3 = self.mx * rhs.x + self.mx * rhs.y + self.mx * rhs.z + self.my * rhs.x + self.my * rhs.y + self.my * rhs.z + self.mz * rhs.x + self.mz * rhs.y + self.mz * rhs.z (unnamed type) 98 | // Omitted: Moment3 anti_geometric Vec3 = 0 (unnamed type) 99 | 100 | // Moment3.dot(Vec3) -> Vec3 101 | impl Dot for Moment3 { 102 | type Output = Vec3; 103 | fn dot(self, rhs: Vec3) -> Self::Output { 104 | // Vec3 { 105 | // x: X(self.my.0 * rhs.z.0) + X(self.mz.0 * rhs.y.0), 106 | // y: Y(self.mx.0 * rhs.z.0) + Y(self.mz.0 * rhs.x.0), 107 | // z: Z(self.mx.0 * rhs.y.0) + Z(self.my.0 * rhs.x.0), 108 | // } 109 | Vec3 { 110 | x: -self.my.dot(rhs.z) + self.mz.dot(rhs.y), 111 | y: self.mx.dot(rhs.z) - self.mz.dot(rhs.x), 112 | z: -self.mx.dot(rhs.y) + self.my.dot(rhs.x), 113 | } 114 | } 115 | } 116 | 117 | // Moment3.wedge(Vec3) -> XYZ 118 | impl Wedge for Moment3 { 119 | type Output = XYZ; 120 | fn wedge(self, rhs: Vec3) -> Self::Output { 121 | // XYZ(self.mx.0 * rhs.x.0) + XYZ(self.my.0 * rhs.y.0) + XYZ(self.mz.0 * rhs.z.0) 122 | self.mx.wedge(rhs.x) + self.my.wedge(rhs.y) + self.mz.wedge(rhs.z) 123 | } 124 | } 125 | 126 | // Omitted: Moment3 anti_wedge Vec3 = 0 (unnamed type) 127 | 128 | // --------------------------------------------------------------------- 129 | // Moment3 OP Vec4: 130 | 131 | // Omitted: Moment3 geometric Vec4 = self.mx * rhs.w + self.mx * rhs.x + self.mx * rhs.y + self.mx * rhs.z + self.my * rhs.w + self.my * rhs.x + self.my * rhs.y + self.my * rhs.z + self.mz * rhs.w + self.mz * rhs.x + self.mz * rhs.y + self.mz * rhs.z (unnamed type) 132 | 133 | // Moment3.anti_geometric(Vec4) -> Vec3 134 | impl AntiGeometric for Moment3 { 135 | type Output = Vec3; 136 | fn anti_geometric(self, rhs: Vec4) -> Self::Output { 137 | // Vec3 { 138 | // x: X(self.mx.0 * rhs.w.0), 139 | // y: Y(self.my.0 * rhs.w.0), 140 | // z: Z(self.mz.0 * rhs.w.0), 141 | // } 142 | Vec3 { 143 | x: self.mx.anti_geometric(rhs.w), 144 | y: self.my.anti_geometric(rhs.w), 145 | z: self.mz.anti_geometric(rhs.w), 146 | } 147 | } 148 | } 149 | 150 | // Moment3.dot(Vec4) -> Vec3 151 | impl Dot for Moment3 { 152 | type Output = Vec3; 153 | fn dot(self, rhs: Vec4) -> Self::Output { 154 | // Vec3 { 155 | // x: X(self.my.0 * rhs.z.0) + X(self.mz.0 * rhs.y.0), 156 | // y: Y(self.mx.0 * rhs.z.0) + Y(self.mz.0 * rhs.x.0), 157 | // z: Z(self.mx.0 * rhs.y.0) + Z(self.my.0 * rhs.x.0), 158 | // } 159 | Vec3 { 160 | x: -self.my.dot(rhs.z) + self.mz.dot(rhs.y), 161 | y: self.mx.dot(rhs.z) - self.mz.dot(rhs.x), 162 | z: -self.mx.dot(rhs.y) + self.my.dot(rhs.x), 163 | } 164 | } 165 | } 166 | 167 | // Moment3.wedge(Vec4) -> Plane 168 | impl Wedge for Moment3 { 169 | type Output = Plane; 170 | fn wedge(self, rhs: Vec4) -> Self::Output { 171 | // Plane { 172 | // nx: YZW(self.mx.0 * rhs.w.0), 173 | // ny: ZXW(self.my.0 * rhs.w.0), 174 | // nz: XYW(self.mz.0 * rhs.w.0), 175 | // d : XYZ(self.mx.0 * rhs.x.0) + XYZ(self.my.0 * rhs.y.0) + XYZ(self.mz.0 * rhs.z.0), 176 | // } 177 | Plane { 178 | nx: self.mx.wedge(rhs.w), 179 | ny: self.my.wedge(rhs.w), 180 | nz: self.mz.wedge(rhs.w), 181 | d: self.mx.wedge(rhs.x) + self.my.wedge(rhs.y) + self.mz.wedge(rhs.z), 182 | } 183 | } 184 | } 185 | 186 | // Omitted: Moment3 anti_wedge Vec4 = 0 (unnamed type) 187 | 188 | // --------------------------------------------------------------------- 189 | // Moment3 OP Moment3: 190 | 191 | // Omitted: Moment3 geometric Moment3 = self.mx * rhs.mx + self.mx * rhs.my + self.mx * rhs.mz + self.my * rhs.mx + self.my * rhs.my + self.my * rhs.mz + self.mz * rhs.mx + self.mz * rhs.my + self.mz * rhs.mz (unnamed type) 192 | // Omitted: Moment3 anti_geometric Moment3 = 0 (unnamed type) 193 | 194 | // Moment3.dot(Moment3) -> S 195 | impl Dot for Moment3 { 196 | type Output = S; 197 | fn dot(self, rhs: Moment3) -> Self::Output { 198 | // -S(self.mx.0 * rhs.mx.0) - S(self.my.0 * rhs.my.0) - S(self.mz.0 * rhs.mz.0) 199 | self.mx.dot(rhs.mx) + self.my.dot(rhs.my) + self.mz.dot(rhs.mz) 200 | } 201 | } 202 | 203 | // Omitted: Moment3 wedge Moment3 = 0 (unnamed type) 204 | // Omitted: Moment3 anti_wedge Moment3 = 0 (unnamed type) 205 | 206 | // --------------------------------------------------------------------- 207 | // Moment3 OP Line3: 208 | 209 | // Omitted: Moment3 geometric Line3 = self.mx * rhs.mx + self.mx * rhs.my + self.mx * rhs.mz + self.mx * rhs.vx + self.mx * rhs.vy + self.mx * rhs.vz + self.my * rhs.mx + self.my * rhs.my + self.my * rhs.mz + self.my * rhs.vx + self.my * rhs.vy + self.my * rhs.vz + self.mz * rhs.mx + self.mz * rhs.my + self.mz * rhs.mz + self.mz * rhs.vx + self.mz * rhs.vy + self.mz * rhs.vz (unnamed type) 210 | // Omitted: Moment3 anti_geometric Line3 = self.mx !* rhs.vx + self.mx !* rhs.vy + self.mx !* rhs.vz + self.my !* rhs.vx + self.my !* rhs.vy + self.my !* rhs.vz + self.mz !* rhs.vx + self.mz !* rhs.vy + self.mz !* rhs.vz (unnamed type) 211 | 212 | // Moment3.dot(Line3) -> S 213 | impl Dot for Moment3 { 214 | type Output = S; 215 | fn dot(self, rhs: Line3) -> Self::Output { 216 | // -S(self.mx.0 * rhs.mx.0) - S(self.my.0 * rhs.my.0) - S(self.mz.0 * rhs.mz.0) 217 | self.mx.dot(rhs.mx) + self.my.dot(rhs.my) + self.mz.dot(rhs.mz) 218 | } 219 | } 220 | 221 | // Moment3.wedge(Line3) -> XYZW 222 | impl Wedge for Moment3 { 223 | type Output = XYZW; 224 | fn wedge(self, rhs: Line3) -> Self::Output { 225 | // -XYZW(self.mx.0 * rhs.vx.0) - XYZW(self.my.0 * rhs.vy.0) - XYZW(self.mz.0 * rhs.vz.0) 226 | self.mx.wedge(rhs.vx) + self.my.wedge(rhs.vy) + self.mz.wedge(rhs.vz) 227 | } 228 | } 229 | 230 | // Moment3.anti_wedge(Line3) -> S 231 | impl AntiWedge for Moment3 { 232 | type Output = S; 233 | fn anti_wedge(self, rhs: Line3) -> Self::Output { 234 | // -S(self.mx.0 * rhs.vx.0) - S(self.my.0 * rhs.vy.0) - S(self.mz.0 * rhs.vz.0) 235 | self.mx.anti_wedge(rhs.vx) + self.my.anti_wedge(rhs.vy) + self.mz.anti_wedge(rhs.vz) 236 | } 237 | } 238 | 239 | // --------------------------------------------------------------------- 240 | // Moment3 OP Plane: 241 | 242 | // Omitted: Moment3 geometric Plane = self.mx * rhs.d + self.mx * rhs.nx + self.mx * rhs.ny + self.mx * rhs.nz + self.my * rhs.d + self.my * rhs.nx + self.my * rhs.ny + self.my * rhs.nz + self.mz * rhs.d + self.mz * rhs.nx + self.mz * rhs.ny + self.mz * rhs.nz (unnamed type) 243 | // Omitted: Moment3 anti_geometric Plane = self.mx !* rhs.nx + self.mx !* rhs.ny + self.mx !* rhs.nz + self.my !* rhs.nx + self.my !* rhs.ny + self.my !* rhs.nz + self.mz !* rhs.nx + self.mz !* rhs.ny + self.mz !* rhs.nz (unnamed type) 244 | 245 | // Moment3.dot(Plane) -> Vec4 246 | impl Dot for Moment3 { 247 | type Output = Vec4; 248 | fn dot(self, rhs: Plane) -> Self::Output { 249 | // Vec4 { 250 | // x: X(self.mx.0 * rhs.d.0), 251 | // y: Y(self.my.0 * rhs.d.0), 252 | // z: Z(self.mz.0 * rhs.d.0), 253 | // w: W(self.mx.0 * rhs.nx.0) + W(self.my.0 * rhs.ny.0) + W(self.mz.0 * rhs.nz.0), 254 | // } 255 | Vec4 { 256 | x: -self.mx.dot(rhs.d), 257 | y: -self.my.dot(rhs.d), 258 | z: -self.mz.dot(rhs.d), 259 | w: -self.mx.dot(rhs.nx) - self.my.dot(rhs.ny) - self.mz.dot(rhs.nz), 260 | } 261 | } 262 | } 263 | 264 | // Omitted: Moment3 wedge Plane = 0 (unnamed type) 265 | 266 | // Moment3.anti_wedge(Plane) -> Vec3 267 | impl AntiWedge for Moment3 { 268 | type Output = Vec3; 269 | fn anti_wedge(self, rhs: Plane) -> Self::Output { 270 | // Vec3 { 271 | // x: X(self.my.0 * rhs.nz.0) + X(self.mz.0 * rhs.ny.0), 272 | // y: Y(self.mx.0 * rhs.nz.0) + Y(self.mz.0 * rhs.nx.0), 273 | // z: Z(self.mx.0 * rhs.ny.0) + Z(self.my.0 * rhs.nx.0), 274 | // } 275 | Vec3 { 276 | x: self.my.anti_wedge(rhs.nz) - self.mz.anti_wedge(rhs.ny), 277 | y: -self.mx.anti_wedge(rhs.nz) + self.mz.anti_wedge(rhs.nx), 278 | z: self.mx.anti_wedge(rhs.ny) - self.my.anti_wedge(rhs.nx), 279 | } 280 | } 281 | } 282 | 283 | // --------------------------------------------------------------------- 284 | // Moment3 OP Rotor3: 285 | 286 | // Moment3.geometric(Rotor3) -> Rotor3 287 | impl Geometric for Moment3 { 288 | type Output = Rotor3; 289 | fn geometric(self, rhs: Rotor3) -> Self::Output { 290 | // Rotor3 { 291 | // x: WX(self.mx.0 * rhs.w.0) + WX(self.my.0 * rhs.z.0) + WX(self.mz.0 * rhs.y.0), 292 | // y: WY(self.mx.0 * rhs.z.0) + WY(self.my.0 * rhs.w.0) + WY(self.mz.0 * rhs.x.0), 293 | // z: WZ(self.mx.0 * rhs.y.0) + WZ(self.my.0 * rhs.x.0) + WZ(self.mz.0 * rhs.w.0), 294 | // w: XYZW(self.mx.0 * rhs.x.0) + XYZW(self.my.0 * rhs.y.0) + XYZW(self.mz.0 * rhs.z.0), 295 | // } 296 | Rotor3 { 297 | x: self.mx.geometric(rhs.w) - self.my.geometric(rhs.z) + self.mz.geometric(rhs.y), 298 | y: self.mx.geometric(rhs.z) + self.my.geometric(rhs.w) - self.mz.geometric(rhs.x), 299 | z: -self.mx.geometric(rhs.y) + self.my.geometric(rhs.x) + self.mz.geometric(rhs.w), 300 | w: -self.mx.geometric(rhs.x) - self.my.geometric(rhs.y) - self.mz.geometric(rhs.z), 301 | } 302 | } 303 | } 304 | 305 | // Omitted: Moment3 anti_geometric Rotor3 = self.mx !* rhs.w + self.mx !* rhs.x + self.mx !* rhs.y + self.mx !* rhs.z + self.my !* rhs.w + self.my !* rhs.x + self.my !* rhs.y + self.my !* rhs.z + self.mz !* rhs.w + self.mz !* rhs.x + self.mz !* rhs.y + self.mz !* rhs.z (unnamed type) 306 | // Omitted: Moment3 dot Rotor3 = Line3 { vx: self.mx | rhs.w, vy: self.my | rhs.w, vz: self.mz | rhs.w, mx: 0, my: 0, mz: 0, } (too many zeros) 307 | 308 | // Moment3.wedge(Rotor3) -> XYZW 309 | impl Wedge for Moment3 { 310 | type Output = XYZW; 311 | fn wedge(self, rhs: Rotor3) -> Self::Output { 312 | // -XYZW(self.mx.0 * rhs.x.0) - XYZW(self.my.0 * rhs.y.0) - XYZW(self.mz.0 * rhs.z.0) 313 | self.mx.wedge(rhs.x) + self.my.wedge(rhs.y) + self.mz.wedge(rhs.z) 314 | } 315 | } 316 | 317 | // Omitted: Moment3 anti_wedge Rotor3 = self.mx & rhs.w + self.mx & rhs.x + self.my & rhs.w + self.my & rhs.y + self.mz & rhs.w + self.mz & rhs.z (unnamed type) 318 | 319 | // --------------------------------------------------------------------- 320 | // Moment3 OP Motor3: 321 | 322 | // Omitted: Moment3 geometric Motor3 = self.mx * rhs.rw + self.mx * rhs.rx + self.mx * rhs.ry + self.mx * rhs.rz + self.mx * rhs.uw + self.mx * rhs.ux + self.mx * rhs.uy + self.mx * rhs.uz + self.my * rhs.rw + self.my * rhs.rx + self.my * rhs.ry + self.my * rhs.rz + self.my * rhs.uw + self.my * rhs.ux + self.my * rhs.uy + self.my * rhs.uz + self.mz * rhs.rw + self.mz * rhs.rx + self.mz * rhs.ry + self.mz * rhs.rz + self.mz * rhs.uw + self.mz * rhs.ux + self.mz * rhs.uy + self.mz * rhs.uz (unnamed type) 323 | // Omitted: Moment3 anti_geometric Motor3 = self.mx !* rhs.rw + self.mx !* rhs.rx + self.mx !* rhs.ry + self.mx !* rhs.rz + self.mx !* rhs.ux + self.mx !* rhs.uy + self.mx !* rhs.uz + self.my !* rhs.rw + self.my !* rhs.rx + self.my !* rhs.ry + self.my !* rhs.rz + self.my !* rhs.ux + self.my !* rhs.uy + self.my !* rhs.uz + self.mz !* rhs.rw + self.mz !* rhs.rx + self.mz !* rhs.ry + self.mz !* rhs.rz + self.mz !* rhs.ux + self.mz !* rhs.uy + self.mz !* rhs.uz (unnamed type) 324 | // Omitted: Moment3 dot Motor3 = self.mx | rhs.rw + self.mx | rhs.uw + self.mx | rhs.ux + self.my | rhs.rw + self.my | rhs.uw + self.my | rhs.uy + self.mz | rhs.rw + self.mz | rhs.uw + self.mz | rhs.uz (unnamed type) 325 | // Omitted: Moment3 wedge Motor3 = self.mx ^ rhs.rx + self.mx ^ rhs.uw + self.my ^ rhs.ry + self.my ^ rhs.uw + self.mz ^ rhs.rz + self.mz ^ rhs.uw (unnamed type) 326 | // Omitted: Moment3 anti_wedge Motor3 = self.mx & rhs.rw + self.mx & rhs.rx + self.mx & rhs.uy + self.mx & rhs.uz + self.my & rhs.rw + self.my & rhs.ry + self.my & rhs.ux + self.my & rhs.uz + self.mz & rhs.rw + self.mz & rhs.rz + self.mz & rhs.ux + self.mz & rhs.uy (unnamed type) 327 | -------------------------------------------------------------------------------- /generated/src/pga3d/vec3.rs: -------------------------------------------------------------------------------- 1 | //! # Vec3 2 | //! 3 | //! ## Operations 4 | //! ```text 5 | //! Vec3.dot(Vec3) -> S 6 | //! Vec3.wedge(Vec3) -> Moment3 7 | //! Vec3.anti_geometric(Vec4) -> Moment3 8 | //! Vec4.anti_geometric(Vec3) -> Moment3 9 | //! Vec3.dot(Vec4) -> S 10 | //! Vec4.dot(Vec3) -> S 11 | //! Vec3.wedge(Vec4) -> Line3 12 | //! Vec4.wedge(Vec3) -> Line3 13 | //! Vec3.dot(Moment3) -> Vec3 14 | //! Moment3.dot(Vec3) -> Vec3 15 | //! Vec3.wedge(Moment3) -> XYZ 16 | //! Moment3.wedge(Vec3) -> XYZ 17 | //! Vec3.dot(Line3) -> Vec4 18 | //! Line3.dot(Vec3) -> Vec4 19 | //! Vec3.wedge(Line3) -> Plane 20 | //! Line3.wedge(Vec3) -> Plane 21 | //! Vec3.dot(Plane) -> Line3 22 | //! Plane.dot(Vec3) -> Line3 23 | //! Vec3.wedge(Plane) -> XYZW 24 | //! Plane.wedge(Vec3) -> XYZW 25 | //! Vec3.anti_wedge(Plane) -> S 26 | //! Plane.anti_wedge(Vec3) -> S 27 | //! Vec3.wedge(Rotor3) -> Plane 28 | //! Rotor3.wedge(Vec3) -> Plane 29 | //! Vec3.anti_wedge(Rotor3) -> Vec3 30 | //! Rotor3.anti_wedge(Vec3) -> Vec3 31 | //! 32 | //! ``` 33 | 34 | use super::*; 35 | 36 | #[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, derive_more::Neg, derive_more::Add, derive_more::Sub)] 37 | pub struct Vec3 { 38 | pub x: X, 39 | pub y: Y, 40 | pub z: Z, 41 | } 42 | 43 | // --------------------------------------------------------------------- 44 | 45 | impl RCompl for Vec3 { 46 | type Output = Plane; 47 | fn rcompl(self) -> Self::Output { 48 | Plane { 49 | nx: self.x.rcompl(), 50 | ny: self.y.rcompl(), 51 | nz: self.z.rcompl(), 52 | d: Default::default(), 53 | } 54 | } 55 | } 56 | 57 | impl LCompl for Vec3 { 58 | type Output = Plane; 59 | fn lcompl(self) -> Self::Output { 60 | Plane { 61 | nx: -self.x.lcompl(), 62 | ny: -self.y.lcompl(), 63 | nz: -self.z.lcompl(), 64 | d: Default::default(), 65 | } 66 | } 67 | } 68 | 69 | impl Reverse for Vec3 { 70 | fn rev(self) -> Self { 71 | Vec3 { 72 | x: self.x, 73 | y: self.y, 74 | z: self.z, 75 | } 76 | } 77 | } 78 | 79 | impl AntiReverse for Vec3 { 80 | fn arev(self) -> Self { 81 | Vec3 { 82 | x: -self.x, 83 | y: -self.y, 84 | z: -self.z, 85 | } 86 | } 87 | } 88 | 89 | // --------------------------------------------------------------------- 90 | // Vec3 OP Vec3: 91 | 92 | // Omitted: Vec3 geometric Vec3 = self.x * rhs.x + self.x * rhs.y + self.x * rhs.z + self.y * rhs.x + self.y * rhs.y + self.y * rhs.z + self.z * rhs.x + self.z * rhs.y + self.z * rhs.z (unnamed type) 93 | // Omitted: Vec3 anti_geometric Vec3 = 0 (unnamed type) 94 | 95 | // Vec3.dot(Vec3) -> S 96 | impl Dot for Vec3 { 97 | type Output = S; 98 | fn dot(self, rhs: Vec3) -> Self::Output { 99 | // S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0) + S(self.z.0 * rhs.z.0) 100 | self.x.dot(rhs.x) + self.y.dot(rhs.y) + self.z.dot(rhs.z) 101 | } 102 | } 103 | 104 | // Vec3.wedge(Vec3) -> Moment3 105 | impl Wedge for Vec3 { 106 | type Output = Moment3; 107 | fn wedge(self, rhs: Vec3) -> Self::Output { 108 | // Moment3 { 109 | // mx: YZ(self.y.0 * rhs.z.0) + YZ(self.z.0 * rhs.y.0), 110 | // my: ZX(self.x.0 * rhs.z.0) + ZX(self.z.0 * rhs.x.0), 111 | // mz: XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 112 | // } 113 | Moment3 { 114 | mx: self.y.wedge(rhs.z) - self.z.wedge(rhs.y), 115 | my: -self.x.wedge(rhs.z) + self.z.wedge(rhs.x), 116 | mz: self.x.wedge(rhs.y) - self.y.wedge(rhs.x), 117 | } 118 | } 119 | } 120 | 121 | // Omitted: Vec3 anti_wedge Vec3 = 0 (unnamed type) 122 | 123 | // --------------------------------------------------------------------- 124 | // Vec3 OP Vec4: 125 | 126 | // Omitted: Vec3 geometric Vec4 = self.x * rhs.w + self.x * rhs.x + self.x * rhs.y + self.x * rhs.z + self.y * rhs.w + self.y * rhs.x + self.y * rhs.y + self.y * rhs.z + self.z * rhs.w + self.z * rhs.x + self.z * rhs.y + self.z * rhs.z (unnamed type) 127 | 128 | // Vec3.anti_geometric(Vec4) -> Moment3 129 | impl AntiGeometric for Vec3 { 130 | type Output = Moment3; 131 | fn anti_geometric(self, rhs: Vec4) -> Self::Output { 132 | // Moment3 { 133 | // mx: YZ(self.x.0 * rhs.w.0), 134 | // my: ZX(self.y.0 * rhs.w.0), 135 | // mz: XY(self.z.0 * rhs.w.0), 136 | // } 137 | Moment3 { 138 | mx: -self.x.anti_geometric(rhs.w), 139 | my: -self.y.anti_geometric(rhs.w), 140 | mz: -self.z.anti_geometric(rhs.w), 141 | } 142 | } 143 | } 144 | 145 | // Vec3.dot(Vec4) -> S 146 | impl Dot for Vec3 { 147 | type Output = S; 148 | fn dot(self, rhs: Vec4) -> Self::Output { 149 | // S(self.x.0 * rhs.x.0) + S(self.y.0 * rhs.y.0) + S(self.z.0 * rhs.z.0) 150 | self.x.dot(rhs.x) + self.y.dot(rhs.y) + self.z.dot(rhs.z) 151 | } 152 | } 153 | 154 | // Vec3.wedge(Vec4) -> Line3 155 | impl Wedge for Vec3 { 156 | type Output = Line3; 157 | fn wedge(self, rhs: Vec4) -> Self::Output { 158 | // Line3 { 159 | // vx: WX(self.x.0 * rhs.w.0), 160 | // vy: WY(self.y.0 * rhs.w.0), 161 | // vz: WZ(self.z.0 * rhs.w.0), 162 | // mx: YZ(self.y.0 * rhs.z.0) + YZ(self.z.0 * rhs.y.0), 163 | // my: ZX(self.x.0 * rhs.z.0) + ZX(self.z.0 * rhs.x.0), 164 | // mz: XY(self.x.0 * rhs.y.0) + XY(self.y.0 * rhs.x.0), 165 | // } 166 | Line3 { 167 | vx: -self.x.wedge(rhs.w), 168 | vy: -self.y.wedge(rhs.w), 169 | vz: -self.z.wedge(rhs.w), 170 | mx: self.y.wedge(rhs.z) - self.z.wedge(rhs.y), 171 | my: -self.x.wedge(rhs.z) + self.z.wedge(rhs.x), 172 | mz: self.x.wedge(rhs.y) - self.y.wedge(rhs.x), 173 | } 174 | } 175 | } 176 | 177 | // Omitted: Vec3 anti_wedge Vec4 = 0 (unnamed type) 178 | 179 | // --------------------------------------------------------------------- 180 | // Vec3 OP Moment3: 181 | 182 | // Omitted: Vec3 geometric Moment3 = self.x * rhs.mx + self.x * rhs.my + self.x * rhs.mz + self.y * rhs.mx + self.y * rhs.my + self.y * rhs.mz + self.z * rhs.mx + self.z * rhs.my + self.z * rhs.mz (unnamed type) 183 | // Omitted: Vec3 anti_geometric Moment3 = 0 (unnamed type) 184 | 185 | // Vec3.dot(Moment3) -> Vec3 186 | impl Dot for Vec3 { 187 | type Output = Vec3; 188 | fn dot(self, rhs: Moment3) -> Self::Output { 189 | // Vec3 { 190 | // x: X(self.y.0 * rhs.mz.0) + X(self.z.0 * rhs.my.0), 191 | // y: Y(self.x.0 * rhs.mz.0) + Y(self.z.0 * rhs.mx.0), 192 | // z: Z(self.x.0 * rhs.my.0) + Z(self.y.0 * rhs.mx.0), 193 | // } 194 | Vec3 { 195 | x: -self.y.dot(rhs.mz) + self.z.dot(rhs.my), 196 | y: self.x.dot(rhs.mz) - self.z.dot(rhs.mx), 197 | z: -self.x.dot(rhs.my) + self.y.dot(rhs.mx), 198 | } 199 | } 200 | } 201 | 202 | // Vec3.wedge(Moment3) -> XYZ 203 | impl Wedge for Vec3 { 204 | type Output = XYZ; 205 | fn wedge(self, rhs: Moment3) -> Self::Output { 206 | // XYZ(self.x.0 * rhs.mx.0) + XYZ(self.y.0 * rhs.my.0) + XYZ(self.z.0 * rhs.mz.0) 207 | self.x.wedge(rhs.mx) + self.y.wedge(rhs.my) + self.z.wedge(rhs.mz) 208 | } 209 | } 210 | 211 | // Omitted: Vec3 anti_wedge Moment3 = 0 (unnamed type) 212 | 213 | // --------------------------------------------------------------------- 214 | // Vec3 OP Line3: 215 | 216 | // Omitted: Vec3 geometric Line3 = self.x * rhs.mx + self.x * rhs.my + self.x * rhs.mz + self.x * rhs.vx + self.x * rhs.vy + self.x * rhs.vz + self.y * rhs.mx + self.y * rhs.my + self.y * rhs.mz + self.y * rhs.vx + self.y * rhs.vy + self.y * rhs.vz + self.z * rhs.mx + self.z * rhs.my + self.z * rhs.mz + self.z * rhs.vx + self.z * rhs.vy + self.z * rhs.vz (unnamed type) 217 | // Omitted: Vec3 anti_geometric Line3 = self.x !* rhs.vx + self.x !* rhs.vy + self.x !* rhs.vz + self.y !* rhs.vx + self.y !* rhs.vy + self.y !* rhs.vz + self.z !* rhs.vx + self.z !* rhs.vy + self.z !* rhs.vz (unnamed type) 218 | 219 | // Vec3.dot(Line3) -> Vec4 220 | impl Dot for Vec3 { 221 | type Output = Vec4; 222 | fn dot(self, rhs: Line3) -> Self::Output { 223 | // Vec4 { 224 | // x: X(self.y.0 * rhs.mz.0) + X(self.z.0 * rhs.my.0), 225 | // y: Y(self.x.0 * rhs.mz.0) + Y(self.z.0 * rhs.mx.0), 226 | // z: Z(self.x.0 * rhs.my.0) + Z(self.y.0 * rhs.mx.0), 227 | // w: W(self.x.0 * rhs.vx.0) + W(self.y.0 * rhs.vy.0) + W(self.z.0 * rhs.vz.0), 228 | // } 229 | Vec4 { 230 | x: -self.y.dot(rhs.mz) + self.z.dot(rhs.my), 231 | y: self.x.dot(rhs.mz) - self.z.dot(rhs.mx), 232 | z: -self.x.dot(rhs.my) + self.y.dot(rhs.mx), 233 | w: -self.x.dot(rhs.vx) - self.y.dot(rhs.vy) - self.z.dot(rhs.vz), 234 | } 235 | } 236 | } 237 | 238 | // Vec3.wedge(Line3) -> Plane 239 | impl Wedge for Vec3 { 240 | type Output = Plane; 241 | fn wedge(self, rhs: Line3) -> Self::Output { 242 | // Plane { 243 | // nx: YZW(self.y.0 * rhs.vz.0) + YZW(self.z.0 * rhs.vy.0), 244 | // ny: ZXW(self.x.0 * rhs.vz.0) + ZXW(self.z.0 * rhs.vx.0), 245 | // nz: XYW(self.x.0 * rhs.vy.0) + XYW(self.y.0 * rhs.vx.0), 246 | // d : XYZ(self.x.0 * rhs.mx.0) + XYZ(self.y.0 * rhs.my.0) + XYZ(self.z.0 * rhs.mz.0), 247 | // } 248 | Plane { 249 | nx: -self.y.wedge(rhs.vz) + self.z.wedge(rhs.vy), 250 | ny: self.x.wedge(rhs.vz) - self.z.wedge(rhs.vx), 251 | nz: -self.x.wedge(rhs.vy) + self.y.wedge(rhs.vx), 252 | d: self.x.wedge(rhs.mx) + self.y.wedge(rhs.my) + self.z.wedge(rhs.mz), 253 | } 254 | } 255 | } 256 | 257 | // Omitted: Vec3 anti_wedge Line3 = 0 (unnamed type) 258 | 259 | // --------------------------------------------------------------------- 260 | // Vec3 OP Plane: 261 | 262 | // Omitted: Vec3 geometric Plane = self.x * rhs.d + self.x * rhs.nx + self.x * rhs.ny + self.x * rhs.nz + self.y * rhs.d + self.y * rhs.nx + self.y * rhs.ny + self.y * rhs.nz + self.z * rhs.d + self.z * rhs.nx + self.z * rhs.ny + self.z * rhs.nz (unnamed type) 263 | // Omitted: Vec3 anti_geometric Plane = self.x !* rhs.nx + self.x !* rhs.ny + self.x !* rhs.nz + self.y !* rhs.nx + self.y !* rhs.ny + self.y !* rhs.nz + self.z !* rhs.nx + self.z !* rhs.ny + self.z !* rhs.nz (unnamed type) 264 | 265 | // Vec3.dot(Plane) -> Line3 266 | impl Dot for Vec3 { 267 | type Output = Line3; 268 | fn dot(self, rhs: Plane) -> Self::Output { 269 | // Line3 { 270 | // vx: WX(self.y.0 * rhs.nz.0) + WX(self.z.0 * rhs.ny.0), 271 | // vy: WY(self.x.0 * rhs.nz.0) + WY(self.z.0 * rhs.nx.0), 272 | // vz: WZ(self.x.0 * rhs.ny.0) + WZ(self.y.0 * rhs.nx.0), 273 | // mx: YZ(self.x.0 * rhs.d.0), 274 | // my: ZX(self.y.0 * rhs.d.0), 275 | // mz: XY(self.z.0 * rhs.d.0), 276 | // } 277 | Line3 { 278 | vx: self.y.dot(rhs.nz) - self.z.dot(rhs.ny), 279 | vy: -self.x.dot(rhs.nz) + self.z.dot(rhs.nx), 280 | vz: self.x.dot(rhs.ny) - self.y.dot(rhs.nx), 281 | mx: self.x.dot(rhs.d), 282 | my: self.y.dot(rhs.d), 283 | mz: self.z.dot(rhs.d), 284 | } 285 | } 286 | } 287 | 288 | // Vec3.wedge(Plane) -> XYZW 289 | impl Wedge for Vec3 { 290 | type Output = XYZW; 291 | fn wedge(self, rhs: Plane) -> Self::Output { 292 | // XYZW(self.x.0 * rhs.nx.0) + XYZW(self.y.0 * rhs.ny.0) + XYZW(self.z.0 * rhs.nz.0) 293 | self.x.wedge(rhs.nx) + self.y.wedge(rhs.ny) + self.z.wedge(rhs.nz) 294 | } 295 | } 296 | 297 | // Vec3.anti_wedge(Plane) -> S 298 | impl AntiWedge for Vec3 { 299 | type Output = S; 300 | fn anti_wedge(self, rhs: Plane) -> Self::Output { 301 | // S(self.x.0 * rhs.nx.0) + S(self.y.0 * rhs.ny.0) + S(self.z.0 * rhs.nz.0) 302 | self.x.anti_wedge(rhs.nx) + self.y.anti_wedge(rhs.ny) + self.z.anti_wedge(rhs.nz) 303 | } 304 | } 305 | 306 | // --------------------------------------------------------------------- 307 | // Vec3 OP Rotor3: 308 | 309 | // Omitted: Vec3 geometric Rotor3 = self.x * rhs.w + self.x * rhs.x + self.x * rhs.y + self.x * rhs.z + self.y * rhs.w + self.y * rhs.x + self.y * rhs.y + self.y * rhs.z + self.z * rhs.w + self.z * rhs.x + self.z * rhs.y + self.z * rhs.z (unnamed type) 310 | // Omitted: Vec3 anti_geometric Rotor3 = self.x !* rhs.w + self.x !* rhs.x + self.x !* rhs.y + self.x !* rhs.z + self.y !* rhs.w + self.y !* rhs.x + self.y !* rhs.y + self.y !* rhs.z + self.z !* rhs.w + self.z !* rhs.x + self.z !* rhs.y + self.z !* rhs.z (unnamed type) 311 | // Omitted: Vec3 dot Rotor3 = self.x | rhs.w + self.x | rhs.x + self.y | rhs.w + self.y | rhs.y + self.z | rhs.w + self.z | rhs.z (unnamed type) 312 | 313 | // Vec3.wedge(Rotor3) -> Plane 314 | impl Wedge for Vec3 { 315 | type Output = Plane; 316 | fn wedge(self, rhs: Rotor3) -> Self::Output { 317 | // Plane { 318 | // nx: YZW(self.y.0 * rhs.z.0) + YZW(self.z.0 * rhs.y.0), 319 | // ny: ZXW(self.x.0 * rhs.z.0) + ZXW(self.z.0 * rhs.x.0), 320 | // nz: XYW(self.x.0 * rhs.y.0) + XYW(self.y.0 * rhs.x.0), 321 | // d : Default::default(), 322 | // } 323 | Plane { 324 | nx: -self.y.wedge(rhs.z) + self.z.wedge(rhs.y), 325 | ny: self.x.wedge(rhs.z) - self.z.wedge(rhs.x), 326 | nz: -self.x.wedge(rhs.y) + self.y.wedge(rhs.x), 327 | d: Default::default(), 328 | } 329 | } 330 | } 331 | 332 | // Vec3.anti_wedge(Rotor3) -> Vec3 333 | impl AntiWedge for Vec3 { 334 | type Output = Vec3; 335 | fn anti_wedge(self, rhs: Rotor3) -> Self::Output { 336 | // Vec3 { 337 | // x: X(self.x.0 * rhs.w.0), 338 | // y: Y(self.y.0 * rhs.w.0), 339 | // z: Z(self.z.0 * rhs.w.0), 340 | // } 341 | Vec3 { 342 | x: self.x.anti_wedge(rhs.w), 343 | y: self.y.anti_wedge(rhs.w), 344 | z: self.z.anti_wedge(rhs.w), 345 | } 346 | } 347 | } 348 | 349 | // --------------------------------------------------------------------- 350 | // Vec3 OP Motor3: 351 | 352 | // Omitted: Vec3 geometric Motor3 = self.x * rhs.rw + self.x * rhs.rx + self.x * rhs.ry + self.x * rhs.rz + self.x * rhs.uw + self.x * rhs.ux + self.x * rhs.uy + self.x * rhs.uz + self.y * rhs.rw + self.y * rhs.rx + self.y * rhs.ry + self.y * rhs.rz + self.y * rhs.uw + self.y * rhs.ux + self.y * rhs.uy + self.y * rhs.uz + self.z * rhs.rw + self.z * rhs.rx + self.z * rhs.ry + self.z * rhs.rz + self.z * rhs.uw + self.z * rhs.ux + self.z * rhs.uy + self.z * rhs.uz (unnamed type) 353 | // Omitted: Vec3 anti_geometric Motor3 = self.x !* rhs.rw + self.x !* rhs.rx + self.x !* rhs.ry + self.x !* rhs.rz + self.x !* rhs.ux + self.x !* rhs.uy + self.x !* rhs.uz + self.y !* rhs.rw + self.y !* rhs.rx + self.y !* rhs.ry + self.y !* rhs.rz + self.y !* rhs.ux + self.y !* rhs.uy + self.y !* rhs.uz + self.z !* rhs.rw + self.z !* rhs.rx + self.z !* rhs.ry + self.z !* rhs.rz + self.z !* rhs.ux + self.z !* rhs.uy + self.z !* rhs.uz (unnamed type) 354 | // Omitted: Vec3 dot Motor3 = self.x | rhs.rw + self.x | rhs.rx + self.x | rhs.uw + self.x | rhs.uy + self.x | rhs.uz + self.y | rhs.rw + self.y | rhs.ry + self.y | rhs.uw + self.y | rhs.ux + self.y | rhs.uz + self.z | rhs.rw + self.z | rhs.rz + self.z | rhs.uw + self.z | rhs.ux + self.z | rhs.uy (unnamed type) 355 | // Omitted: Vec3 wedge Motor3 = self.x ^ rhs.ry + self.x ^ rhs.rz + self.x ^ rhs.uw + self.x ^ rhs.ux + self.y ^ rhs.rx + self.y ^ rhs.rz + self.y ^ rhs.uw + self.y ^ rhs.uy + self.z ^ rhs.rx + self.z ^ rhs.ry + self.z ^ rhs.uw + self.z ^ rhs.uz (unnamed type) 356 | // Omitted: Vec3 anti_wedge Motor3 = self.x & rhs.rw + self.x & rhs.ux + self.y & rhs.rw + self.y & rhs.uy + self.z & rhs.rw + self.z & rhs.uz (unnamed type) 357 | -------------------------------------------------------------------------------- /generator/src/gen.rs: -------------------------------------------------------------------------------- 1 | use {itertools::Itertools, strum::IntoEnumIterator}; 2 | 3 | use crate::{documentation::*, *}; 4 | 5 | const CODE_SEPARATOR: &str = "// ---------------------------------------------------------------------"; 6 | 7 | pub struct Settings { 8 | pub float_type: String, 9 | } 10 | 11 | impl Default for Settings { 12 | fn default() -> Self { 13 | Self { 14 | float_type: "f64".to_string(), 15 | } 16 | } 17 | } 18 | 19 | pub struct Generator { 20 | pub grammar: Grammar, 21 | pub types: Types, 22 | pub settings: Settings, 23 | pub ro: RustOptions, 24 | } 25 | 26 | impl Generator { 27 | fn rust(&self, expr: Expr) -> String { 28 | expr.simplify(Some(&self.grammar)) 29 | .typify(&self.types, &self.grammar) 30 | .rust(&self.ro) 31 | } 32 | } 33 | 34 | pub mod blades { 35 | use super::*; 36 | 37 | pub fn file(gen: &Generator) -> String { 38 | let documentation = with_line_prefixes("//! ", &documentation(gen).trim()); 39 | format!( 40 | "\ 41 | {}\n\n\ 42 | use derive_more::{{Add, Mul, Neg, Sub}};\n\ 43 | \n\ 44 | use super::*;\n\ 45 | \n\ 46 | {}\n\n\ 47 | {}\n\ 48 | {}\n\n\ 49 | {}\n\ 50 | {}\n", 51 | documentation, 52 | declare_blades(gen), 53 | CODE_SEPARATOR, 54 | impl_blade_unaryops(gen), 55 | CODE_SEPARATOR, 56 | impl_blade_products(gen), 57 | ) 58 | } 59 | 60 | fn documentation(gen: &Generator) -> String { 61 | let rust = |expr| gen.rust(expr); 62 | let unit_blades = gen.types.unit_blades(); 63 | format!( 64 | "\ 65 | # Blade types\n\ 66 | The blades that make up this geometric algebra.\n\ 67 | \n\ 68 | ## Unary operations\n\ 69 | {}\n\ 70 | \n\ 71 | ## Multiplication tables\n\ 72 | {}\n\ 73 | ", 74 | unary_table(&unit_blades, &rust), 75 | multiplication_tables(&unit_blades, &rust) 76 | ) 77 | } 78 | 79 | fn declare_blades(gen: &Generator) -> String { 80 | gen.types 81 | .sblades() 82 | .iter() 83 | .map(|(name, sb)| declare_blade(gen, name, sb)) 84 | .join("\n\n") 85 | } 86 | 87 | fn declare_blade(gen: &Generator, name: &str, sb: &SBlade) -> String { 88 | let is_scalar = sb.grade() == 0; 89 | let is_pseudoscalar = sb.grade() == gen.grammar.num_vecs(); 90 | 91 | let mut code = String::new(); 92 | if is_scalar { 93 | code += "/// The scalar type (real numbers).\n"; 94 | } else if is_pseudoscalar { 95 | code += "/// The pseudo-scalar.\n" 96 | }; 97 | 98 | let squares_to = sb.geometric_product(&sb, &gen.grammar); 99 | assert!(squares_to.is_scalar()); 100 | let squares_to = squares_to.sign; 101 | code += &format!("/// Squares to {}.\n", squares_to); 102 | 103 | let mut derives = "Copy, Clone, Debug, Default, PartialEq, PartialOrd, Neg, Add, Sub".to_string(); 104 | if is_scalar { 105 | derives += ", Mul"; // NOTE: NOT the pseudoscalar (it may scale to zero) 106 | } 107 | code += &format!("#[derive({})]\n", derives); 108 | 109 | format!("{}pub struct {}(pub {});", code, name, gen.settings.float_type) 110 | } 111 | 112 | fn impl_blade_unaryops(gen: &Generator) -> String { 113 | Unary::iter() 114 | .map(|unary| { 115 | format!( 116 | "// impl {} for blades:\n\n{}", 117 | unary.trait_name(), 118 | gen.types 119 | .sblades() 120 | .iter() 121 | .map(|(sblade_name, sblade)| { impl_blade_unary(gen, sblade_name, sblade, unary) }) 122 | .join("\n\n") 123 | ) 124 | }) 125 | .join(&format!("\n\n{}\n", CODE_SEPARATOR)) 126 | } 127 | 128 | fn impl_blade_unary(gen: &Generator, sblade_name: &str, sblade: &SBlade, unary: Unary) -> String { 129 | let result_type = sblade.unary(unary, &gen.grammar); 130 | 131 | if result_type.is_zero() { 132 | format!(" // Omitted: {}.{}() -> 0", sblade_name, unary.trait_function_name(),) 133 | } else { 134 | let (sign, output_sblade_name) = gen 135 | .types 136 | .get_sblade(&result_type) 137 | .unwrap_or_else(|| panic!("unknown blade: {:?}", result_type.blade)); 138 | assert_eq!(sign.abs(), 1); 139 | 140 | let sign = if sign == -1 { "-" } else { "" }; 141 | 142 | if unary.trait_has_output_type() { 143 | format!( 144 | r" 145 | impl {Trait} for {sblade_name} {{ 146 | type Output = {Output}; 147 | fn {function_name}(self) -> Self::Output {{ 148 | {Output}({sign} self.0) 149 | }} 150 | }} 151 | ", 152 | sblade_name = sblade_name, 153 | Trait = unary.trait_name(), 154 | function_name = unary.trait_function_name(), 155 | Output = output_sblade_name, 156 | sign = sign, 157 | ) 158 | } else { 159 | format!( 160 | r" 161 | impl {Trait} for {sblade_name} {{ 162 | fn {function_name}(self) -> Self {{ 163 | {sign} self 164 | }} 165 | }} 166 | ", 167 | sblade_name = sblade_name, 168 | Trait = unary.trait_name(), 169 | function_name = unary.trait_function_name(), 170 | sign = sign, 171 | ) 172 | } 173 | } 174 | } 175 | 176 | fn impl_blade_products(gen: &Generator) -> String { 177 | Product::iter() 178 | .map(|prod| { 179 | format!( 180 | "// impl {} for blades:\n\n{}", 181 | prod.trait_name(), 182 | impl_product_for_blades(gen, prod) 183 | ) 184 | }) 185 | .join(&format!("\n\n{}\n", CODE_SEPARATOR)) 186 | } 187 | 188 | fn impl_product_for_blades(gen: &Generator, product: Product) -> String { 189 | gen.types 190 | .sblades() 191 | .iter() 192 | .map(|lhs| { 193 | gen.types 194 | .sblades() 195 | .iter() 196 | .map(|rhs| impl_blade_product(gen, lhs, rhs, product)) 197 | .join("\n\n") 198 | }) 199 | .join("\n\n") 200 | } 201 | 202 | fn impl_blade_product(gen: &Generator, lhs: &(&str, SBlade), rhs: &(&str, SBlade), product: Product) -> String { 203 | let product_type = SBlade::product(product, &[lhs.1.clone(), rhs.1.clone()], &gen.grammar); 204 | 205 | if product_type.is_zero() { 206 | format!( 207 | r" 208 | impl {Trait}<{Rhs}> for {Lhs} {{ 209 | type Output = Zero; 210 | fn {function_name}(self, _rhs: {Rhs}) -> Self::Output {{ 211 | Zero {{}} 212 | }} 213 | }} 214 | ", 215 | Lhs = lhs.0, 216 | Rhs = rhs.0, 217 | Trait = product.trait_name(), 218 | function_name = product.trait_function_name(), 219 | ) 220 | } else { 221 | let (sign, output_sblade_name) = gen 222 | .types 223 | .get_sblade(&product_type) 224 | .unwrap_or_else(|| panic!("unknown blade: {:?}", product_type.blade)); 225 | assert_eq!(sign.abs(), 1); 226 | 227 | format!( 228 | r" 229 | impl {Trait}<{Rhs}> for {Lhs} {{ 230 | type Output = {Output}; 231 | fn {function_name}(self, rhs: {Rhs}) -> Self::Output {{ 232 | {Output}({sign} self.0 * rhs.0) 233 | }} 234 | }} 235 | ", 236 | Lhs = lhs.0, 237 | Rhs = rhs.0, 238 | Trait = product.trait_name(), 239 | function_name = product.trait_function_name(), 240 | Output = output_sblade_name, 241 | sign = if sign == -1 { "-" } else { "" } 242 | ) 243 | } 244 | } 245 | } 246 | 247 | pub mod strct { 248 | use super::*; 249 | 250 | pub fn file(gen: &Generator, struct_name: &str, strct: &Struct) -> String { 251 | let documentation = with_line_prefixes("//! ", &documentation(gen, struct_name, strct).trim()); 252 | 253 | let unaryops = Unary::iter() 254 | .map(|unary| impl_struct_unary(gen, struct_name, strct, unary)) 255 | .join("\n"); 256 | 257 | let binops = gen 258 | .types 259 | .structs() 260 | .map(|(rhs_name, rhs_struct)| { 261 | format!( 262 | "// {} OP {}:\n\n{}\n", 263 | struct_name, 264 | rhs_name, 265 | Product::iter() 266 | .map(|prod| impl_struct_product(gen, &(struct_name, strct), &(rhs_name, rhs_struct), prod)) 267 | .join("\n") 268 | ) 269 | }) 270 | .join(&format!("\n{}\n", CODE_SEPARATOR)); 271 | 272 | format!( 273 | "\ 274 | {}\n\n\ 275 | use super::*;\n\n\ 276 | {}\n\ 277 | {}\n\ 278 | {}\n\ 279 | {}\n\ 280 | {}\n", 281 | documentation, 282 | declare_struct(gen, struct_name, strct), 283 | CODE_SEPARATOR, 284 | unaryops, 285 | CODE_SEPARATOR, 286 | binops, 287 | ) 288 | } 289 | 290 | fn documentation(gen: &Generator, struct_name: &str, strct: &Struct) -> String { 291 | let homo_ops = Product::iter() 292 | .filter_map(|product| { 293 | struct_product_type_signature(gen, &(struct_name, strct), &(struct_name, strct), product) 294 | }) 295 | .join("\n"); 296 | 297 | let hetero_ops = gen 298 | .types 299 | .structs() 300 | .filter_map(|(other_struct_name, other_strct)| { 301 | if struct_name == other_struct_name { 302 | None 303 | } else { 304 | Some( 305 | Product::iter() 306 | .flat_map(|product| { 307 | itertools::chain( 308 | struct_product_type_signature( 309 | gen, 310 | &(struct_name, strct), 311 | &(other_struct_name, other_strct), 312 | product, 313 | ), 314 | struct_product_type_signature( 315 | gen, 316 | &(other_struct_name, other_strct), 317 | &(struct_name, strct), 318 | product, 319 | ), 320 | ) 321 | }) 322 | .join("\n"), 323 | ) 324 | } 325 | }) 326 | .join("\n"); 327 | 328 | format!( 329 | "\ 330 | # {}\n\n\ 331 | ## Operations\n\ 332 | ```text\n\ 333 | {}\n\ 334 | {}\n\ 335 | ```\n\ 336 | ", 337 | struct_name, homo_ops, hetero_ops 338 | ) 339 | } 340 | 341 | fn declare_struct(_gen: &Generator, struct_name: &str, strct: &Struct) -> String { 342 | // TODO: we can only implement Add, Sub if the struct has no Type::Constant 343 | let derives = 344 | "Copy, Clone, Debug, Default, PartialEq, PartialOrd, derive_more::Neg, derive_more::Add, derive_more::Sub\n"; 345 | let members = strct 346 | .iter() 347 | .map(|(member_name, member_type)| format!(" pub {}: {},", member_name, member_type.name)) 348 | .join("\n"); 349 | format!( 350 | "\ 351 | #[derive({})]\n\ 352 | pub struct {} {{\n\ 353 | {}\n\ 354 | }}\n ", 355 | derives, struct_name, members 356 | ) 357 | } 358 | 359 | pub fn impl_struct_unary(gen: &Generator, struct_name: &str, strct: &Struct, unary: Unary) -> String { 360 | let var = Expr::var(0, "self", &Type::strct(strct)); 361 | let expr = Expr::unary(unary, var); 362 | let expr = expr.simplify(Some(&gen.grammar)).typify(&gen.types, &gen.grammar); 363 | let code = expr.rust(&gen.ro); 364 | match type_name(gen, &expr) { 365 | Some(output_type_name) => { 366 | if unary.trait_has_output_type() { 367 | format!( 368 | r" 369 | impl {Trait} for {struct_name} {{ 370 | type Output = {Output}; 371 | fn {function_name}(self) -> Self::Output {{ 372 | {code} 373 | }} 374 | }} 375 | ", 376 | struct_name = struct_name, 377 | Trait = unary.trait_name(), 378 | function_name = unary.trait_function_name(), 379 | Output = output_type_name, 380 | code = code, 381 | ) 382 | } else { 383 | format!( 384 | r" 385 | impl {Trait} for {struct_name} {{ 386 | fn {function_name}(self) -> Self {{ 387 | {code} 388 | }} 389 | }} 390 | ", 391 | struct_name = struct_name, 392 | Trait = unary.trait_name(), 393 | function_name = unary.trait_function_name(), 394 | code = code, 395 | ) 396 | } 397 | } 398 | None => format!( 399 | "// Omitted: {}.{}() -> {}", 400 | struct_name, 401 | unary.trait_function_name(), 402 | code.replace('\n', " ") 403 | ), 404 | } 405 | } 406 | 407 | pub fn struct_product_type_signature( 408 | gen: &Generator, 409 | lhs: &(&str, &Struct), 410 | rhs: &(&str, &Struct), 411 | product: Product, 412 | ) -> Option { 413 | let factors = vec![ 414 | Expr::var(0, lhs.0, &Type::strct(lhs.1)), 415 | Expr::var(1, rhs.0, &Type::strct(rhs.1)), 416 | ]; 417 | let input_expr = Expr::Prod(product, factors); 418 | let input_code = input_expr.rust(&gen.ro); 419 | let output_expr = input_expr.simplify(Some(&gen.grammar)).typify(&gen.types, &gen.grammar); 420 | let type_name = type_name(gen, &output_expr)?; 421 | Some(format!("{} -> {}", input_code, type_name)) 422 | } 423 | 424 | pub fn impl_struct_product( 425 | gen: &Generator, 426 | lhs: &(&str, &Struct), 427 | rhs: &(&str, &Struct), 428 | product: Product, 429 | ) -> String { 430 | let factors = vec![ 431 | Expr::var(0, "self", &Type::strct(lhs.1)), 432 | Expr::var(1, "rhs", &Type::strct(rhs.1)), // TODO: name this snake_case(rhs.0) iff lhs type != rhs.type 433 | ]; 434 | let expr = Expr::Prod(product, factors); 435 | let expr = expr.simplify(Some(&gen.grammar)); 436 | let expr = expr.typify(&gen.types, &gen.grammar); 437 | 438 | if let Expr::StructInstance(si) = &expr { 439 | if si.count_zeros() > 1 { 440 | // For instance, `Vec3 ^ Vec3 -> Line3`, but the Line has zero direction, 441 | // which makes very little sense. 442 | return format!( 443 | "// Omitted: {} {} {} = {} (too many zeros)", 444 | lhs.0, 445 | product.trait_function_name(), 446 | rhs.0, 447 | expr.rust(&RustOptions::readable()).replace('\n', " ") 448 | ); 449 | } 450 | } 451 | 452 | match type_name(gen, &expr) { 453 | Some(output_type_name) => { 454 | let explicit = expr 455 | .clone() 456 | .typify(&gen.types, &gen.grammar) 457 | .explicit(&gen.types, &gen.grammar) 458 | .simplify(Some(&gen.grammar)) 459 | .rust(&gen.ro); 460 | 461 | let code = expr.rust(&gen.ro); 462 | 463 | let code = format!("{}\n{}", with_line_prefixes("// ", &explicit), code); 464 | let code = rust::indent_n(2, &code); 465 | 466 | format!( 467 | r" 468 | // {comment} 469 | impl {Trait}<{Rhs}> for {Lhs} {{ 470 | type Output = {Output}; 471 | fn {function_name}(self, rhs: {Rhs}) -> Self::Output {{ 472 | {code} 473 | }} 474 | }} 475 | ", 476 | comment = struct_product_type_signature(gen, lhs, rhs, product).unwrap(), 477 | Lhs = lhs.0, 478 | Rhs = rhs.0, 479 | Trait = product.trait_name(), 480 | function_name = product.trait_function_name(), 481 | Output = output_type_name, 482 | code = code, 483 | ) 484 | } 485 | None => format!( 486 | "// Omitted: {} {} {} = {} (unnamed type)", 487 | lhs.0, 488 | product.trait_function_name(), 489 | rhs.0, 490 | expr.rust(&RustOptions::readable()).replace('\n', " ") 491 | ), 492 | } 493 | } 494 | } 495 | 496 | /// Returns None if the type is Zero or unknown 497 | fn type_name(gen: &Generator, expr: &Expr) -> Option { 498 | // println!("type_name({})", expr.rust(&gen.ro)); 499 | let output_type = expr.typ(Some(&gen.grammar)); 500 | // println!("type_name({}) output_type: {:?}", expr.rust(&gen.ro), output_type); 501 | let output_type = output_type?; 502 | if output_type.is_zero() { 503 | None 504 | } else { 505 | Some(gen.types.type_name(&output_type).to_owned()) 506 | } 507 | } 508 | --------------------------------------------------------------------------------