diff options
author | Jon Santmyer <jon@jonsantmyer.com> | 2024-09-30 12:05:43 -0400 |
---|---|---|
committer | Jon Santmyer <jon@jonsantmyer.com> | 2024-09-30 12:05:43 -0400 |
commit | 4feb3a8fb81fa648c2c5f7a72029ff5b54cf0dcb (patch) | |
tree | 3f3025d22a9fe142690e04813c69f337cced3367 /projects/learning-rust | |
parent | ad94bcef3a643e1a96a9e7e5390132a8cd4f4256 (diff) | |
download | website-master.tar.gz website-master.tar.bz2 website-master.zip |
Diffstat (limited to 'projects/learning-rust')
-rw-r--r-- | projects/learning-rust/1.html | 58 | ||||
-rw-r--r-- | projects/learning-rust/2.html | 102 | ||||
-rw-r--r-- | projects/learning-rust/index.html | 6 |
3 files changed, 144 insertions, 22 deletions
diff --git a/projects/learning-rust/1.html b/projects/learning-rust/1.html index 840a968..be6bfa7 100644 --- a/projects/learning-rust/1.html +++ b/projects/learning-rust/1.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <head> - <title>1: Solar Systems</title> + <title>1: Orbitals</title> <link rel="stylesheet" href="/style.css"/> <link rel="stylesheet" href="/prism/prism.css"/> @@ -9,22 +9,33 @@ </head> <body> <div class="window"> - <div class="window-title"><h2 style="padding: 0px; margin: 0px;">Learning Rust : Solar Systems</h2></div> + <div class="window-title"><h2 style="padding: 0px; margin: 0px;">Learning Rust : Orbitals</h2></div> <i>How do I store non-owning references to collection members?</i> <div class="window-content"> <a class="link-button" href="/projects/learning-rust"><</a> + <a class="link-button" href="/projects/learning-rust">Home</a> + <a class="link-button" href="/projects/learning-rust/2.html">></a> <p>I want to make a data-driven viewer for the solar system, as part of a game inspired by <a href="https://www.aurora2.pentarch.org/">Aurora 4x</a> but Rust is an entirely different beast from C.</p> + <p>So let's start with the types i'll use:</p> + <pre><code class="language-rust"> +pub const GRAVITATIONAL_CONSTANT : f64 = 6.67430e-11; + +pub type Second = u64; +pub type Meter = f64; +pub type Rad = f64; +pub type Kilogram = f64; + </code></pre> <p>I have prior experience writing systems like this, so I figured it would be as easy as:</p> <pre><code class="language-rust"> pub struct Orbital { //Some optional non-owning reference to the parent orbital - mass : f64, - semi_major_axis : f64, - eccentricity : f64, - inclination : f64, - long_asc_node : f64, - argument_periapsis : f64, - mean_anomaly_epoch : f64 + mass : Kilogram, + semi_major_axis : Rad, + eccentricity : Rad, + inclination : Rad, + long_asc_node : Rad, + argument_periapsis : Rad, + mean_anomaly_epoch : Rad } </code></pre> <p>The question then is: How do we store the reference to the parent orbital?</p> @@ -36,33 +47,38 @@ pub struct Orbital { <p>But not every part of the update function needs the parent orbital's position, so I can split the update into two parts:</p> <p>1: Update the orbital elements relative to the parent object</p> <p>2: Use the parent orbital's position to transform the relative coordinates into absolute coordinates</p> + <p>All the tick method needs is an immutable parent object to calculate the standard gravitational parameter of the one-body system being emulated.</p> <p>So here's what I came up with.</p> <pre><code class="language-rust"> pub struct Orbital { - parent_index : usize, - //Temporary state variables for later update. - rel_pos : (f64, f64, f64) + origin : Option<usize>, + //Euler angles for a given time (T) relative to origin + rel_pos : Option<(Second, Meter, Rad, Rad, Rad)> + //Cartesian coordinates for a given time (T) relative to (0, 0, 0) + abs_pos : Option<(Second, Meter, Meter, Meter)> //Orbital parameters - mass : f64, - semi_major_axis : f64, - eccentricity : f64, - inclination : f64, - long_asc_node : f64, - argument_periapsis : f64, - mean_anomaly_epoch : f64 + mass : Kilogram, + semi_major_axis : Meter, + eccentricity : Rad, + inclination : Rad, + long_asc_node : Rad, + argument_periapsis : Rad, + mean_anomaly_epoch : Rad } //... impl Orbital { //... - pub fn update_rel(&mut self) -> Orbital { + //Update relative + pub fn tick(&mut self, &system: System, t: Second) { //... } - pub fn update(&self, &system : System) -> (f64, f64, f64) { + pub fn pos(&self, &system : System, t: Second) -> (Meter, Meter, Meter) { //... } } </code></pre> + <p>Now comes the daunting task of storing our orbitals in a usable manner. Ideally, I want to iterate through the list of orbitals in a way that also lets me access other members of the orbital list from the tick and pos functions. It's a lot harder than it should be.</p> </div> </div> </body> diff --git a/projects/learning-rust/2.html b/projects/learning-rust/2.html new file mode 100644 index 0000000..c09b3f2 --- /dev/null +++ b/projects/learning-rust/2.html @@ -0,0 +1,102 @@ +<!DOCTYPE html> +<html> +<head> + <title>2: Solar Systems</title> + + <link rel="stylesheet" href="/style.css"/> + <link rel="stylesheet" href="/prism/prism.css"/> + <script src="/prism/prism.js"></script> +</head> +<body> + <div class="window"> + <div class="window-title"><h2 style="padding: 0px; margin: 0px;">Learning Rust : Solar Systems</h2></div> + <i>Everything is implicit and I don't know the exact types I'm working with.</i> + <div class="window-content"> + <a class="link-button" href="/projects/learning-rust/1.html"><</a> + <a class="link-button" href="/projects/learning-rust">Home</a> + <a class="link-button" href="/projects/learning-rust/3.html">></a> + <div/> + <p>So, now that I have a struct for defining orbitals, I can begin work on a container for these orbitals. I can store the orbitals in a vector, and iterate through the list of orbitals to tick and update them for a given time. We start with the following concept code:</p> + <pre><code class="language-rust"> +struct System +{ + orbitals : Vec<Orbital> +} + +impl System +{ + pub fn tick(&self, t : Second) + { + let orbitals_len = self.orbitals.len(); + for i in 0..orbitals_len { + let orbital = self.orbitals[i]; + orbital.tick(self, t); + } + } +} + </code></pre> + <p>This simple code leads me to my first major hurdle when dealing with Rust: Borrows against a container take the entire object, not just the element at a given index. Because of this quirk, I cannot borrow the parent orbital to calculate parts of my orbital state vectors. Since my "tick" function needs the parent orbital to calculate the gravitational parameter. Here's the code that fuels this problem:</p> + <pre><code class="language-rust"> +pub fn tick(&mut self, system: &System) -> () +{ + ... + let origin_index = self.origin.unwrap(); + let origin_orbital = system.orbitals[origin_index]; + let origin_mass = origin_orbital.mass; + ... +} + </code></pre> + <p>Because I can't borrow from the orbital vector more than once, I can't get the mass of the origin orbital.</p> + <p>Just to be clear, I understand why the borrow checker takes the entire vector when taking an element. If the element gets deleted or changed or the vector gets added to, then any other reference to the vector's elements must be re-evaluated. To properly borrow multiple mutable elements, I have to somehow split the vector in such a way that both the origin and ticking orbital are in seperate arrays.</p> + <p>Based on this information, and some help from the internet, here is what I came up with for accessing the parent-orbital pair</p> + <pre><code class="language-rust"> +fn orbital_set(&self, orbitals : &mut [Orbital], i : usize) + -> (&mut Orbital, Option<&mut Orbital>) +{ + //The orbital vector must first be converted to a mutable span, so that I can access the raw data. + assert!(orbitals.len() < i); + unsafe { + //From the orbital span, take a raw pointer from the given index, cast to a pointer to a mutable Orbital, and dereference. + let orbital = &mut *(orbitals.get_unchecked_mut(i) as *mut Orbital); + //Assuming the orbital is a valid object, check for the parent orbital. + match orbital.origin() { + Some(origin_i) => { + //Make sure the origin index does not break rules. + assert!(origin_i != i, "Orbital cannot orbit itself"); + assert!(origin_i < orbitals.len()); + let origin = &mut *(orbitals.get_unchecked_mut(i) as *mut Orbital) + (orbital, Some(origin)) + }, + None => (orbital, None) + } + } +} + </code></pre> + <p>I hate the unsafe block, but this is the only solution that I could find that had the characteristics that I wanted. Applied to the global tick method it looks like this:</p> + <pre><code class="language-rust"> +pub fn tick(&mut self) -> () +{ + let orbitals = &mut self.orbitals.borrow_mut()[..]; + for i in 0..orbitals.len() { + let orbital_set = self.orbital_set(orbitals, i); + match orbital_set.1 { + Some(origin) => { orbital_set.0.tick(origin, t); } + None => {} + } + } +} + </code></pre> + <p>where the orbital tick method looks like:</p> + <pre><code class="language-rust"> +pub fn tick(&mut self, origin : &mut Orbital, t : Second) +{ + ... + let origin_mass = origin.mass; + ... +} + </code></pre> + <p>Now the challenge will be drawing all of this information on the screen!</p> + </div> + </div> +</body> +</html> diff --git a/projects/learning-rust/index.html b/projects/learning-rust/index.html index b890e85..080c4d6 100644 --- a/projects/learning-rust/index.html +++ b/projects/learning-rust/index.html @@ -22,8 +22,12 @@ <a class="link-button" href="https://git.jonsantmyer.com/systemic">Source</a> <p style="display: inline-block"> Requires nix and cargo. depends on <a href="https://www.raylib.com/">Raylib</a></p> <div/> - <a class="link-button" href="1.html">1: Solar Systems</a> + <a class="link-button" href="1.html">1: Orbitals</a> <p style="display: inline-block"> : How do I store non-owning references to other objects?</p> + + <div/> + <a class="link-button" href="2.html">2: Systems</a> + <p style="display: inline-block"> : Why does borrowing a mutable element of Vec borrow the entire Vec?</p> </div> </div> </body> |