rust piston rectangle function does not work within the loop - rust

I want to draw a lot of cells based on 2d array in rust piston library. I try to edit hello world example and have function like so:
fn render(&mut self, args: &RenderArgs) {
const GREEN: [f32; 4] = [0.0f32, 1.0f32, 0.0f32, 1.0f32];
const RED: [f32; 4] = [1.0f32, 0.0f32, 0.0f32, 1.0f32];
let square = rectangle::square(0.0, 0.0, 10.0);
let field = &mut self.field;
self.gl.draw(args.viewport(), |c, gl| -> () {
clear(GREEN, gl);
for x_y_item in field {
let item = x_y_item.2;
if item == 7 {
let x = x_y_item.0;
let y = x_y_item.1;
rectangle(
RED,
square,
c.transform.trans(x as f64 * 10.0, y as f64 * 10.0),
gl,
);
}
}
rectangle(
RED,
square,
c.transform.trans(0 as f64 * 10.0, 0 as f64 * 10.0),
gl,
);
})
}
sadly it doesn't work within the loop.
as in it works fine if I debug print the variables and hardcode them one by one outside of the loop but inside - it just doesn't draw anything, yet doesn't crash.
Update:
I was asked to provide the code to clarify what field is and so it can be runed, so here it is:
Field:
pub struct Grid {
cursor: i32,
body: Vec<Vec<i32>>,
height: i32,
width: i32,
}
impl Grid {
pub fn new(width: i32, height: i32) -> Self {
let mut body: Vec<Vec<i32>> = Vec::with_capacity(height as usize);
let mut temp: Vec<i32> = Vec::with_capacity(width as usize);
for i in 0..width {
temp.push(0);
}
for i in 0..height {
body.push(temp.clone());
}
println!("W: {}, TW:{}, H:{}, TH: {}",width,body[0].len() ,height, body.len());
return Self {cursor: 1 ,width : width, height : height, body : body};
}
pub fn fill(&mut self, value: i32) {
for y in 0..self.height-1 {
for x in 0..self.width - 1 {
self.body[y as usize][x as usize] = value;
}
}
}
pub fn inject(&mut self, pos: (i32,i32), arg: i32) {
self.body[(pos.1 - 1) as usize][(pos.0 - 1) as usize] = arg;
}
pub fn extract(&mut self, pos: (i32,i32)) -> i32 {
return self.body[(pos.1 - 1) as usize][(pos.0 - 1) as usize];
}
pub fn pinpoint(&mut self, val: i32) -> Vec<[i32;2]> {
let mut ret: Vec<[i32;2]> = Vec::new();
for y in 0..self.height {
for x in 0..self.width {
if self.body[y as usize][x as usize] == val {
ret.push([x as i32 + 1,y as i32 + 1]);
}
}
}
return ret;
}
pub fn d_show(&mut self) {
let mut counter: i32 = 1;
for row in self.body.iter() {
println!("{}. {:?}",counter,*row);
counter += 1;
}
}
}
impl Iterator for Grid {
type Item = (i32,i32,i32);
fn next(&mut self) -> Option<Self::Item> {
if self.cursor == self.width * self.height {
return None;
} else {
let x: i32 = self.cursor - (self.cursor/self.width)*self.width;
let y: i32 = self.cursor/self.width;
let val: i32 = 0;
self.cursor += 1;
return Some((x,y,self.body[y as usize][x as usize]));
}
}
}
Main:
extern crate glutin_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate piston;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventSettings, Events};
use piston::input::{RenderArgs, RenderEvent, UpdateArgs, UpdateEvent};
use piston::window::WindowSettings;
use graphics::*;
mod arr2d;
use crate::arr2d::Grid;
pub struct State {
field: arr2d::Grid,
gl: GlGraphics,
}
impl State {
fn render(&mut self, args: &RenderArgs) {
const GREEN: [f32;4] = [0.0f32,1.0f32,0.0f32,1.0f32];
const RED: [f32;4] = [1.0f32,0.0f32,0.0f32,1.0f32];
let square = rectangle::square(0.0,0.0,10.0);
let field = &mut self.field;
self.gl.draw(args.viewport(), |c, gl| -> () {
clear(GREEN,gl);
for x_y_item in field {
let item = x_y_item.2;
if item == 7 {
let x = x_y_item.0;
let y = x_y_item.1;
rectangle(RED, square, c.transform.trans(x as f64 * 10.0, y as f64 * 10.0), gl);
}
}
rectangle(RED, square, c.transform.trans(0 as f64 *10.0,0 as f64 *10.0), gl);
})
}
fn update(&mut self, args: &UpdateArgs) {
}
fn new(gl: OpenGL ,w: i32,h: i32) -> Self {
let mut body = Grid::new(w,h);
body.inject((4,4),7);
body.inject((4,5),7);
body.inject((4,6),7);
return Self{gl: GlGraphics::new(gl), field: body};
}
}
fn main() {
let open_gl = OpenGL::V3_2;
let mut window : Window = WindowSettings::new("Spinning Square",[200,200])
.opengl(open_gl)
.exit_on_esc(true)
.build()
.unwrap();
let mut app = State::new(open_gl,20,20);
let mut events = Events::new(EventSettings::new());
while let Some(e) = events.next(&mut window) {
if let Some(args) = e.render_args() {
app.render(&args);
}
if let Some(args) = e.update_args() {
app.update(&args);
}
}
}
Update #2:
I did more research - and created much simpler version of the code based on other tutorials and examples, but that issue still seem to persist:
extern crate piston;
use piston_window::*;
use std::ffi::OsString;
use std::path::Path;
mod tileSet;
mod arr2d;
fn main() {
let mut window: PistonWindow = WindowSettings::new("Hello Piston!", [640, 480]).exit_on_esc(true)
.build()
.unwrap();
let mut tilez: tileSet::Textures = tileSet::Textures::from_directory(OsString::from("C:\\Users\\grass\\Desktop\\codes\\Rust\\ghandalf\\assets"),
window.create_texture_context(),
|x: &Path| -> bool {
return x.to_str().unwrap().ends_with(".png");
}
);
let mut field: arr2d::Grid = arr2d::Grid::new(40,40);
field.fill(0);
let mut image: Image = Image::new();
while let Some(event) = window.next() {
window.draw_2d(&event, |context, graphics, _device| {
clear([1.0; 4], graphics);
for (x,y,item) in &mut field {
let tile: String = match item {
0 => "tile0.png".to_string(),
_ => "tile1.png".to_string()
};
image.draw(tilez.get(tile).unwrap(), &DrawState::default(), context.transform.trans(40.0,40.0), graphics); //empty screen here
}
image.draw(tilez.get(tile).unwrap(), &DrawState::default(), context.transform.trans(40.0,40.0), graphics); //works fine here
});
}
}
I tried using hardcoded variables, debugging whether the loop iterates, check if any variable is out of scope but it doesn't seem to give the answer. I think that it has to do with some open gl concept like clearing the screen per iteration perhaps?
It doesn't help that compiler doesn't throw an error.

Yeah, I'm retarded - I forgot to reset iterator implied for arr2d -_-, so it was drawing but was over within 1 frame.

Related

How to traverse and consume a vector in given order? [duplicate]

For example, I have a Vec<String> and an array storing indexes.
let src = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let idx_arr = [2_usize, 0, 1];
The indexes stored in idx_arr comes from the range 0..src.len(), without repetition or omission.
I want to move the elements in src to another container in the given order, until the vector is completely consumed. For example,
let iter = into_iter_in_order(src, &idx_arr);
for s in iter {
// s: String
}
// or
consume_vec_in_order(src, &idx_arr, |s| {
// s: String
});
If the type of src can be changed to Vec<Option<String>>, things will be much easier, just use src[i].take(). However, it cannot.
Edit:
"Another container" refers to any container, such as a queue or hash set. Reordering in place is not the answer to the problem. It introduces the extra time cost of O(n). The ideal method should be 0-cost.
Not sure if my algorithm satisfies your requirements but here I have an algorithm that can consume the provided vector in-order without initializing a new temporary vector, which is more efficient for a memory.
fn main() {
let src = &mut vec!["a".to_string(), "b".to_string(), "c".to_string(), "d".to_string()];
let idx_arr = [2_usize, 3, 1, 0];
consume_vector_in_order(src, idx_arr.to_vec());
println!("{:?}", src); // d , c , a , b
}
// In-place consume vector in order
fn consume_vector_in_order<T>(v: &mut Vec<T>, inds: Vec<usize>) -> &mut Vec<T>
where
T: Default,
{
let mut i: usize = 0;
let mut temp_inds = inds.to_vec();
while i < inds.to_vec().len() {
let s_index = temp_inds[i];
if s_index != i {
let new_index = temp_inds[s_index];
temp_inds.swap(s_index, new_index);
v.swap(s_index, new_index);
} else {
i += 1;
}
}
v
}
You can use the technique found in How to sort a Vec by indices? (using my answer in particular) since that can reorder the data in-place from the indices, and then its just simple iteration:
fn consume_vec_in_order<T>(mut vec: Vec<T>, order: &[usize], mut cb: impl FnMut(T)) {
sort_by_indices(&mut vec, order.to_owned());
for elem in vec {
cb(elem);
}
}
Full example available on the playground.
Edit:
An ideal method, but needs to access unstable features and functions not exposed by the standard library.
use std::alloc::{Allocator, RawVec};
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop};
use std::ptr::{self, NonNull};
#[inline]
unsafe fn into_iter_in_order<'a, T, A: Allocator>(
vec: Vec<T, A>,
order: &'a [usize],
) -> IntoIter<'a, T, A> {
unsafe {
let mut vec = ManuallyDrop::new(vec);
let cap = vec.capacity();
let alloc = ManuallyDrop::new(ptr::read(vec.allocator()));
let ptr = order.as_ptr();
let end = ptr.add(order.len());
IntoIter {
buf: NonNull::new_unchecked(vec.as_mut_ptr()),
_marker_1: PhantomData,
cap,
alloc,
ptr,
end,
_marker_2: PhantomData,
}
}
}
struct IntoIter<'a, T, A: Allocator> {
buf: NonNull<T>,
_marker_1: PhantomData<T>,
cap: usize,
alloc: ManuallyDrop<A>,
ptr: *const usize,
end: *const usize,
_marker_2: PhantomData<&'a usize>,
}
impl<T, A: Allocator> Iterator for IntoIter<T, A> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
if self.ptr == self.end {
None
} else {
let idx = unsafe { *self.ptr };
self.ptr = unsafe { self.ptr.add(1) };
if T::IS_ZST {
Some(unsafe { mem::zeroed() })
} else {
Some(unsafe { ptr::read(self.buf.as_ptr().add(idx)) })
}
}
}
}
impl<#[may_dangle] T, A: Allocator> Drop for IntoIter<T, A> {
fn drop(&mut self) {
struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter<T, A>);
impl<T, A: Allocator> Drop for DropGuard<'_, T, A> {
fn drop(&mut self) {
unsafe {
// `IntoIter::alloc` is not used anymore after this and will be dropped by RawVec
let alloc = ManuallyDrop::take(&mut self.0.alloc);
// RawVec handles deallocation
let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc);
}
}
}
let guard = DropGuard(self);
// destroy the remaining elements
unsafe {
while self.ptr != self.end {
let idx = *self.ptr;
self.ptr = self.ptr.add(1);
let p = if T::IS_ZST {
self.buf.as_ptr().wrapping_byte_add(idx)
} else {
self.buf.as_ptr().add(idx)
};
ptr::drop_in_place(p);
}
}
// now `guard` will be dropped and do the rest
}
}
Example:
let src = vec![
"0".to_string(),
"1".to_string(),
"2".to_string(),
"3".to_string(),
"4".to_string(),
];
let mut dst = vec![];
let iter = unsafe { into_iter_in_order(src, &[2, 1, 3, 0, 4]) };
for s in iter {
dst.push(s);
}
assert_eq!(dst, vec!["2", "1", "3", "0", "4"]);
My previous answer:
use std::mem;
use std::ptr;
pub unsafe fn consume_vec_in_order<T>(vec: Vec<T>, order: &[usize], mut cb: impl FnMut(T)) {
// Check whether `order` contains all numbers in 0..len without repetition
// or omission.
if cfg!(debug_assertions) {
use std::collections::HashSet;
let n = order.len();
if n != vec.len() {
panic!("The length of `order` is not equal to that of `vec`.");
}
let mut set = HashSet::<usize>::new();
for &idx in order {
if idx >= n {
panic!("`{idx}` in the `order` is out of range (0..{n}).");
} else if set.contains(&idx) {
panic!("`order` contains the repeated element `{idx}`");
} else {
set.insert(idx);
}
}
}
unsafe {
for &idx in order {
let s = ptr::read(vec.get_unchecked(idx));
cb(s);
}
vec.set_len(0);
}
}
Example:
let src = vec![
"0".to_string(),
"1".to_string(),
"2".to_string(),
"3".to_string(),
"4".to_string(),
];
let mut dst = vec![];
consume_vec_in_order(
src,
&[2, 1, 3, 0, 4],
|elem| dst.push(elem),
);
assert_eq!(dst, vec!["2", "1", "3", "0", "4"]);

Adding a closure as a struct method

I'm trying to do this: where calling modify() will update the r with the environment variables.
I read about closures but don't know how to add a closure to a struct.
fn main() {
let r = Rect::new();
let width:u32 = 10;
let height:u32 = 10;
r.modify();
}
struct Rect{
width:u32,
height:u32
}
impl Rect{
fn new()->Self{
Self{
width:0,
height:0,
}
}
fn modify(&mut self){
self.width = ...;
self.height = ...;
}
}
You can do what you want, try it!
fn main() {
let mut r = Rect::new();
let width: u32 = 10;
let height: u32 = 11;
let cls = || (width, height);
r.modify(cls);
dbg! {r};
}
#[derive(Debug)]
struct Rect {
width: u32,
height: u32,
}
impl Rect {
fn new() -> Self {
Self {
width: 0,
height: 0,
}
}
fn modify<T>(&mut self, cls: T)
where
T: Fn() -> (u32, u32)
{
self.width = cls().0;
self.height = cls().1;
}
}

SHA3-512 NIST FIPS PUB 202 in rust

I'm attempting to implement SHA3-512 the way it is done by NIST in FIPS PUB 202:
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf.
It works whenever I plug in the test vector's bit values + "01"(look for HERE in code), and for the blank value test case.
Example working test vector:
https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-512_Msg5.pdf
For some reason, whenever string values such as "abc" are plugged in, the hash function fails to work. Despite the conversion from string to binary string being fine, I believe the issue is with the padding function but I cant see where I messed up.
/* Keccak */
struct Keccak<'a> {
b: usize,
d: usize,
r: usize,
l: usize,
w: usize,
delim: &'a str,
}
impl Keccak<'_> {
pub fn new(digest: usize, bits: usize, delim: &str) -> Keccak {
Keccak {
b: bits,
d: digest,
r: (bits - digest * 2),
w: bits / 25,
l: (((bits / 25) as f32).log2()) as usize,
delim,
}
}
pub fn hash(&mut self, msg: &mut String) {
self.sponge(msg);
}
// SPONGE the provided string of binary m
fn sponge(&mut self, msg: &mut String) {
// Pad the message using pad10*1
// HERE: let mut msg = &mut String::from("01");
let len = msg.len() as isize;
self.pad101(msg, self.r as isize, len);
// Let n be size of P
let n = msg.len() / self.r;
// Create vector s of size b/8, each value is a byte
let mut s: Vec<String> = vec!["0".repeat(8); self.b / 8];
// Xor s and p for every p in P
let c = self.b - self.r;
for i in 0..n {
// let p be the message of len(r) + 0^c of size b
let p = format!("{}{}", &msg[i * self.r..(i + 1) * self.r], "0".repeat(c));
// Xor s and p in byte sized intervals, then do a round of keccakf
s = self.keccackf(
&(0..p.len())
.step_by(8)
.map(|x| {
format!(
"{:08?}",
p[x..x + 8].parse::<u32>().unwrap() ^ s[x / 8].parse::<u32>().unwrap()
)
})
.collect::<Vec<String>>()
.join(""),
);
let mut z = String::new();
while z.len() <= self.d {
let mut str = s.join("");
str.truncate(self.r);
z += &str;
s = self.keccackf(&(s.join("")));
}
z.truncate(self.d);
self.from_bin_string(&z);
}
}
// Convert string m to state array
fn to_state(&self, msg: &str) -> Vec<Vec<Vec<u8>>> {
let mut s: Vec<Vec<Vec<u8>>> = Vec::new();
for x in 0..5 {
let mut row = Vec::new();
for y in 0..5 {
let mut column = Vec::new();
for z in 0..self.w {
let i = self.w * (5 * y + x) + z;
column.push(msg[i..i + 1].parse::<u8>().unwrap());
}
row.push(column)
}
s.push(row);
}
s
}
// Convert state array to vector of string
fn from_state(&self, state: Vec<Vec<Vec<u8>>>) -> Vec<String> {
let mut result: Vec<String> = Vec::new();
let mut temp = String::new();
for i in 0..5 {
for j in 0..5 {
for w in 0..self.w {
temp += &format!("{}", state[j][i][w]);
if (temp.len() == 8) {
result.push(temp.clone());
temp = String::new();
}
}
}
}
result
}
fn theta(&self, state: &mut Vec<Vec<Vec<u8>>>) {
// C NIST
fn c(x: usize, z: usize, state: &Vec<Vec<Vec<u8>>>) -> u8 {
state[x][0][z] ^ state[x][1][z] ^ state[x][2][z] ^ state[x][3][z] ^ state[x][4][z]
}
// D NIST
fn d(x: usize, z: usize, state: &Vec<Vec<Vec<u8>>>, w: isize) -> u8 {
c(((x as isize) - 1).rem_euclid(5) as usize, z, state)
^ c(
(x + 1).rem_euclid(5),
((z as isize) - 1).rem_euclid(w) as usize,
state,
)
}
// Let s be a'
let mut s = vec![vec![vec![0; self.w]; 5]; 5];
// Save xor'd values into a'
for x in 0..5 {
for y in 0..5 {
for z in 0..self.w {
s[x][y][z] = state[x][y][z] ^ d(x, z, &state, self.w as isize);
}
}
}
s.clone_into(state);
}
fn rho(&self, state: &mut Vec<Vec<Vec<u8>>>) {
// Let s be a'
let mut s = vec![vec![vec![0; self.w]; 5]; 5];
// Set all z values of a' = a
state[0][0].clone_into(&mut s[0][0]);
// Let coords represent (x, y)
let mut coords = [1, 0];
for t in 0..24 {
for z in 0..self.w {
s[coords[0]][coords[1]][z] = state[coords[0]][coords[1]]
[((z as isize) - (t + 1) * (t + 2) / 2).rem_euclid(self.w as isize) as usize];
}
coords = [coords[1], (2 * coords[0] + 3 * coords[1]).rem_euclid(5)];
}
s.clone_into(state);
}
fn pi(&self, state: &mut Vec<Vec<Vec<u8>>>) {
let mut s: Vec<Vec<Vec<u8>>> = Vec::new();
for x in 0..5 {
let mut row = Vec::new();
for y in 0..5 {
let mut column = Vec::new();
for z in 0..self.w {
column.push(state[((x as usize) + 3 * y).rem_euclid(5)][x][z]);
}
row.push(column)
}
s.push(row);
}
s.clone_into(state)
}
fn chi(&self, state: &mut Vec<Vec<Vec<u8>>>) {
let mut s: Vec<Vec<Vec<u8>>> = Vec::new();
for x in 0..5 {
let mut row = Vec::new();
for y in 0..5 {
let mut column = Vec::new();
for z in 0..self.w {
column.push(
state[x][y][z]
^ ((state[(x + 1).rem_euclid(5)][y][z] ^ 1)
* state[(x + 2).rem_euclid(5)][y][z]),
);
}
row.push(column)
}
s.push(row);
}
s.clone_into(state)
}
fn rc(&self, t: usize) -> u8 {
if t.rem_euclid(255) == 0 {
return 1;
}
let mut r = std::collections::VecDeque::new();
r.extend(
"10000000"
.chars()
.map(|x| x.to_string().parse::<u8>().unwrap()),
);
for _ in 0..t.rem_euclid(255) {
r.push_front(0);
r[0] ^= r[8];
r[4] ^= r[8];
r[5] ^= r[8];
r[6] ^= r[8];
r.truncate(8);
}
return r[0];
}
fn iota(&self, state: &mut Vec<Vec<Vec<u8>>>, round: usize) {
let mut r: Vec<u8> = vec![0; self.w];
for j in 0..=self.l {
r[((2 as isize).pow(j as u32) - 1) as usize] = self.rc(j + 7 * round);
}
for z in 0..self.w {
state[0][0][z] ^= r[z];
}
}
fn keccackf(&self, msg: &str) -> Vec<String> {
let mut state = self.to_state(msg);
// Go through all the rounds
for r in 0..(12 + 2 * self.l) {
self.theta(&mut state);
self.rho(&mut state);
self.pi(&mut state);
self.chi(&mut state);
self.iota(&mut state, r);
}
let res = self.from_state(state);
res
}
// Pad10*1
fn pad101(&mut self, msg: &mut String, x: isize, m: isize) {
let j = (-m - 2).rem_euclid(x);
msg.push_str("1");
msg.push_str(&"0".repeat(j as usize));
msg.push('1');
}
// For nist test vectors
fn from_bin_string(&self, str: &str) {
let z = (0..str.len())
.step_by(8)
.map(|i| (&str[i..i + 8]).chars().rev().collect::<String>())
.collect::<Vec<String>>();
for i in 0..z.len() {
print!("{:02x}", u32::from_str_radix(&z[i], 2).unwrap());
}
}
}
/* Sha3 */
pub struct SHA3<'a> {
kec: Option<Keccak<'a>>,
}
impl SHA3<'_> {
// Creates new hash variable with default hash function, currently SHA512
pub fn new() -> SHA3<'static> {
SHA3 { kec: None }
}
// Sha512
fn SHA512(&mut self, mut msg: String) {
match self.kec.as_mut() {
// Provided kec hash msg
Some(kec) => kec.hash(&mut msg),
// Otherwise create new kec and call function again
None => {
// Creating new keccack struct, l & w & state are defined in NIST
self.kec = Some(Keccak::new(512, 1600, "0110"));
self.SHA512(msg)
}
}
}
// Return hashed vector
pub fn hash(&mut self, m: &str) {
self.SHA512(self.to_bin_string(m) + "01");
}
// Return hexdigest output
pub fn hexdigest(&self, m: &str) {}
// Convert string slice to binary string
fn to_bin_string(&self, str: &str) -> String {
String::from(str)
.as_bytes()
.into_iter()
.map(|x| format!("{:08b}", x))
.collect::<Vec<String>>()
.join("")
}
}

Is there a better way to pass a callback function to a struct in Rust?

I'm building a game engine / CPU 3D renderer to learn the basics of rendering following a course.
I'm trying to organize the code so that things are encapsulated and I can provide a public API that's easy to use.
For this, I want to provide this engine.on_update(|&mut engine| {}) method so users can pass in their own code and use the engine in there.
This callback is then executed as part of the engine's update loop.
Here is what this looks like now (I trimmed the code to make this easier as its already quite long)
This is the engine module.
use std::time::{Duration, Instant};
use sdl2::{
event::Event,
keyboard::Keycode,
pixels::PixelFormatEnum,
render::{Canvas, Texture, UpdateTextureError},
video::Window,
EventPump,
};
use crate::buffer::{ClearAuto, ClearColor, ColorBuffer, Drawable};
use crate::utils::NumOption;
pub struct EngineConfigParams {
pub window_title: Option<String>,
pub width: Option<usize>,
pub height: Option<usize>,
pub clear_color: Option<u32>,
pub fps: Option<u32>,
}
impl Default for EngineConfigParams {
fn default() -> Self {
EngineConfigParams {
window_title: None,
width: None,
height: None,
clear_color: None,
fps: None,
}
}
}
pub struct EngineConfig {
window_title: String,
width: usize,
height: usize,
clear_color: u32,
fps: u32,
}
impl EngineConfig {
pub fn new(params: EngineConfigParams) -> Self {
let default = EngineConfig::default();
EngineConfig {
window_title: params.window_title.unwrap_or(default.window_title),
width: params.width.unwrap_gt_or(0, default.width),
height: params.height.unwrap_gt_or(0, default.height),
clear_color: params.clear_color.unwrap_or(default.clear_color),
fps: params.fps.unwrap_gt_or(0, default.fps),
}
}
pub fn window_title(&self) -> &String {
&self.window_title
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn clear_color(&self) -> u32 {
self.clear_color
}
pub fn fps(&self) -> u32 {
self.fps
}
}
impl Default for EngineConfig {
fn default() -> Self {
EngineConfig {
window_title: "3D renderer".to_string(),
width: 800,
height: 600,
clear_color: 0xFF000000,
fps: 60,
}
}
}
type EngineUpdateFn<'a> = &'a mut dyn FnMut(&mut EngineCore);
pub struct Engine<'a> {
core: EngineCore,
update: Option<EngineUpdateFn<'a>>,
previous_frame_time: Instant,
target_frame_time: Duration,
}
impl<'a> Engine<'a> {
pub fn build(mut config: EngineConfig) -> Engine<'a> {
let ctx = sdl2::init().unwrap();
let video = ctx.video().unwrap();
match video.display_mode(0, 0) {
Ok(mode) => {
config.width = mode.w as usize;
config.height = mode.h as usize;
println!("Display mode: {:?}", mode);
}
Err(e) => eprintln!(
"Failed to get display mode: {}, using default width and height",
e
),
};
let width = config.width;
let height = config.height;
let window = video
.window(&config.window_title, width as u32, height as u32)
.borderless()
.position_centered()
.fullscreen()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
let color_buffer = ColorBuffer::new(width, height);
let event_pump = ctx.event_pump().unwrap();
println!("WindowCtx w: {} h: {}", width, height);
let fps = config.fps;
Engine {
core: EngineCore {
config,
canvas,
color_buffer,
event_pump,
},
update: None,
previous_frame_time: Instant::now(),
target_frame_time: Duration::new(0, 1_000_000_000u32 / fps),
}
}
pub fn config(&self) -> &EngineConfig {
&self.core.config
}
pub fn on_update(&mut self, f: EngineUpdateFn<'a>) {
self.update = Some(f);
self.update();
}
pub fn user_update(&mut self) {
self.update.as_mut().unwrap()(&mut self.core);
}
pub fn update(&mut self) {
self.target_frame_time = Duration::new(0, 1_000_000_000u32 / self.core.config.fps);
let texture_creator = self.core.canvas.texture_creator();
let mut texture = texture_creator
.create_texture(
PixelFormatEnum::ARGB8888,
sdl2::render::TextureAccess::Streaming,
self.core.config.width as u32,
self.core.config.height as u32,
)
.unwrap();
let mut running = true;
while running {
self.previous_frame_time = Instant::now();
running = self.core.process_input();
self.user_update();
self.core.render_buffer(&mut texture).unwrap();
self.core.clear();
let now = Instant::now();
let frame_time = now - self.previous_frame_time;
println!(
"Time this frame {}ms {} FPS",
frame_time.as_millis(),
1000u128 / frame_time.as_millis()
);
if frame_time.as_nanos() < self.target_frame_time.as_nanos() {
::std::thread::sleep(self.target_frame_time - frame_time);
}
}
}
}
pub struct EngineCore {
config: EngineConfig,
canvas: Canvas<Window>,
color_buffer: ColorBuffer,
event_pump: EventPump,
}
impl EngineCore {
fn process_input(&mut self) -> bool {
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
println!("Received quit event, shutting down");
return false;
}
_ => {}
}
}
true
}
fn render_buffer(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
self.copy_buffer_to_canvas(texture)?;
self.canvas.present();
Ok(())
}
fn copy_buffer_to_canvas(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
texture.update(None, self.color_buffer.pixels(), self.config.width * 4)?;
self.canvas.copy(texture, None, None).unwrap();
Ok(())
}
pub fn config(&self) -> &EngineConfig {
&self.config
}
}
impl ClearAuto for EngineCore {
fn clear(&mut self) {
self.color_buffer.clear(self.config.clear_color);
}
}
impl Drawable for EngineCore {
fn draw_grid(&mut self, spacing: usize, color: Option<u32>) {
self.color_buffer.draw_grid(spacing, color);
}
fn draw_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: u32) {
self.color_buffer.draw_rect(x, y, width, height, color);
}
fn draw_point(&mut self, x: usize, y: usize, color: u32) {
self.color_buffer.draw_point(x, y, color);
}
}
And in main.rs (Again trimmed of the mesh/points manipulations)
use renderer3d::prelude::*;
use vecx::Vec3;
pub fn main() {
let mut eng = Engine::build(EngineConfig {
window_title: "3d Renderer".to_string(),
width: 800,
height: 600,
clear_color: 0xFF000000,
});
let points = build_cube();
let fov = 640.0;
let cam_pos = Vec3(0.0, 0.0, -5.0);
let mut rotation = Vec3(0.0, 0.0, 0.0);
println!("Start update");
eng.on_update(&mut |eng: &mut Engine| {
eng.draw_grid(10, Some(0xFF333333));
rotation = rotation + Vec3(0.01, 0.02, 0.0);
let mut points = transform_points(&points, rotation);
points = project_points(&points, cam_pos, fov);
points.iter().for_each(|point| {
let mut x = point.x();
let mut y = point.y();
x += eng.config().width as f64 / 2.0;
y += eng.config().height as f64 / 2.0;
eng.draw_rect(x as usize, y as usize, 4, 4, 0xFFFF0000);
});
});
}
As you can see, it's important that this callback can access and mutate variables from the outer scope.
I've been through a lot of trial and error and I ended up finding something that works but the fact I have to do unsafe code makes me wonder if that's really the correct way to do this.
Especially I'm worried that I'm trying to design this too much as I would in other less safe/restrictive languages and that I'm missing how to design this properly in Rust.
Update
Here is the full final code after applying the accepted answer's solution
engine module
use std::time::{Duration, Instant};
use sdl2::{
event::Event,
keyboard::Keycode,
pixels::PixelFormatEnum,
render::{Canvas, Texture, UpdateTextureError},
video::Window,
EventPump,
};
use crate::buffer::{ClearAuto, ClearColor, ColorBuffer, Drawable};
use crate::utils::NumOption;
pub struct EngineConfigParams {
pub window_title: Option<String>,
pub width: Option<usize>,
pub height: Option<usize>,
pub clear_color: Option<u32>,
pub fps: Option<u32>,
}
impl Default for EngineConfigParams {
fn default() -> Self {
EngineConfigParams {
window_title: None,
width: None,
height: None,
clear_color: None,
fps: None,
}
}
}
pub struct EngineConfig {
window_title: String,
width: usize,
height: usize,
clear_color: u32,
fps: u32,
}
impl EngineConfig {
pub fn new(params: EngineConfigParams) -> Self {
let default = EngineConfig::default();
EngineConfig {
window_title: params.window_title.unwrap_or(default.window_title),
width: params.width.unwrap_gt_or(0, default.width),
height: params.height.unwrap_gt_or(0, default.height),
clear_color: params.clear_color.unwrap_or(default.clear_color),
fps: params.fps.unwrap_gt_or(0, default.fps),
}
}
pub fn window_title(&self) -> &String {
&self.window_title
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
}
impl Default for EngineConfig {
fn default() -> Self {
EngineConfig {
window_title: "3D renderer".to_string(),
width: 800,
height: 600,
clear_color: 0xFF000000,
fps: 60,
}
}
}
type EngineUpdateFn<'a> = &'a mut dyn FnMut(&mut EngineCore);
pub struct Engine<'a> {
core: EngineCore,
update: Option<EngineUpdateFn<'a>>,
previous_frame_time: Instant,
target_frame_time: Duration,
}
impl<'a> Engine<'a> {
pub fn build(mut config: EngineConfig) -> Engine<'a> {
let ctx = sdl2::init().unwrap();
let video = ctx.video().unwrap();
config = EngineConfig {
width: config.width,
height: config.height,
..EngineConfig::default()
};
match video.display_mode(0, 0) {
Ok(mode) => {
config.width = mode.w as usize;
config.height = mode.h as usize;
println!("Display mode: {:?}", mode);
}
Err(e) => eprintln!(
"Failed to get display mode: {}, using default width and height",
e
),
};
let width = config.width;
let height = config.height;
let window = video
.window(&config.window_title, width as u32, height as u32)
.borderless()
.position_centered()
.fullscreen()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
let color_buffer = ColorBuffer::new(width, height);
let event_pump = ctx.event_pump().unwrap();
println!("WindowCtx w: {} h: {}", width, height);
let fps = config.fps;
Engine {
core: EngineCore {
config,
canvas,
color_buffer,
event_pump,
},
update: None,
previous_frame_time: Instant::now(),
target_frame_time: Duration::new(0, 1_000_000_000u32 / fps),
}
}
pub fn config(&self) -> &EngineConfig {
&self.core.config
}
pub fn on_update(&mut self, f: EngineUpdateFn<'a>) {
self.update = Some(f);
self.update();
}
pub fn user_update(&mut self) {
self.update.as_mut().unwrap()(&mut self.core);
}
pub fn update(&mut self) {
self.target_frame_time = Duration::new(0, 1_000_000_000u32 / self.core.config.fps);
let texture_creator = self.core.canvas.texture_creator();
let mut texture = texture_creator
.create_texture(
PixelFormatEnum::ARGB8888,
sdl2::render::TextureAccess::Streaming,
self.core.config.width as u32,
self.core.config.height as u32,
)
.unwrap();
let mut running = true;
while running {
self.previous_frame_time = Instant::now();
running = self.core.process_input();
self.user_update();
self.core.render_buffer(&mut texture).unwrap();
self.core.clear();
let now = Instant::now();
let frame_time = now - self.previous_frame_time;
println!(
"Time this frame {}ms {} FPS",
frame_time.as_millis(),
1000u128 / frame_time.as_millis()
);
if frame_time.as_nanos() < self.target_frame_time.as_nanos() {
::std::thread::sleep(self.target_frame_time - frame_time);
}
}
}
}
pub struct EngineCore {
config: EngineConfig,
canvas: Canvas<Window>,
color_buffer: ColorBuffer,
event_pump: EventPump,
}
impl EngineCore {
fn process_input(&mut self) -> bool {
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
println!("Received quit event, shutting down");
return false;
}
_ => {}
}
}
true
}
fn render_buffer(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
self.copy_buffer_to_canvas(texture)?;
self.canvas.present();
Ok(())
}
fn copy_buffer_to_canvas(&mut self, texture: &mut Texture) -> Result<(), UpdateTextureError> {
texture.update(None, self.color_buffer.pixels(), self.config.width * 4)?;
self.canvas.copy(texture, None, None).unwrap();
Ok(())
}
pub fn config(&self) -> &EngineConfig {
&self.config
}
}
impl ClearAuto for EngineCore {
fn clear(&mut self) {
self.color_buffer.clear(self.config.clear_color);
}
}
impl Drawable for EngineCore {
fn draw_grid(&mut self, spacing: usize, color: Option<u32>) {
self.color_buffer.draw_grid(spacing, color);
}
fn draw_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: u32) {
self.color_buffer.draw_rect(x, y, width, height, color);
}
fn draw_point(&mut self, x: usize, y: usize, color: u32) {
self.color_buffer.draw_point(x, y, color);
}
}
main.rs
use renderer3d::{prelude::*, Mesh};
use vecx::Vec3;
pub fn main() {
let mut eng = Engine::build(EngineConfig::new(EngineConfigParams {
window_title: Some("3d Renderer".to_string()),
..EngineConfigParams::default()
}));
let cube = Mesh::cube();
let fov = 640.0;
let cam_pos = Vec3(0.0, 0.0, -5.0);
let mut rotation = Vec3(0.0, 0.0, 0.0);
println!("Start update");
eng.on_update(&mut |eng| {
eng.draw_grid(10, Some(0xFF333333));
rotation = rotation + Vec3(0.01, 0.02, 0.0);
let mut points = cube
.face_vertices()
.map(|fv| transform_points(&fv, rotation))
.flatten()
.collect();
points = project_points(&points, cam_pos, fov);
points.iter().for_each(|point| {
let mut x = point.x();
let mut y = point.y();
x += eng.config().width() as f64 / 2.0;
y += eng.config().height() as f64 / 2.0;
eng.draw_rect(x as usize, y as usize, 4, 4, 0xFFFF0000);
});
});
}
fn transform_points(points: &Vec<Vec3>, rotation: Vec3) -> Vec<Vec3> {
points.iter().map(|p| p.rot(rotation)).collect()
}
fn project_points(points: &Vec<Vec3>, cam_pos: Vec3, fov: f64) -> Vec<Vec3> {
points
.iter()
.map(|point| {
let z = point.z() + cam_pos.z();
let mut x = point.x() / z;
let mut y = point.y() / z;
x *= fov;
y *= fov;
Vec3(x, y, z)
})
.collect()
}
The problem is not the variables from the outer scope, it's the fact that the callback has mutable access to Engine but it itself comes from engine, so there are two coexisting mutable references. Theoretically, it could for example access the captured variables both by the data pointer in Engine and by using them directly from different threads, causing a data race.
Since you probably don't need access to the callback inside the callback, I would split Engine into two structs: one will contain the callback and the other, and have an update() and on_update() methods, and the other will include all other stuff. This way, the callback will take a mutable reference to the inner struct, which it can - since it is not contained within, but will still be able to call all engine's functions.

Implement Iterator in Rust

I'm making an iterator in rust. In the next() method I want to extract next_element as current_element which will be returned, and create new Term instance and set it as next_element:
pub struct Term {
pub coefficient: f64,
pub index: i32,
pub exponent: f64,
}
pub struct SeriesIterator {
next_element: Term,
}
impl Iterator for SeriesIterator {
type Item = Term;
fn next(&mut self) -> Option<Self::Item> {
let current_element = self.next_element;
self.next_element = Term {
coefficient: 1.0,
index: current_element.index + 1,
exponent: f64::from(current_element.index + 1),
};
Some(current_element)
}
}
How to move ownership of next_element into current_element which will finally be moved outside with the return of next()?
cannot move out of `self.next_element` which is behind a mutable reference
|
| let current_element = self.next_element;
| ^^^^^^^^^^^^^^^^
Using std::mem::replace, try this:
fn next(&mut self) -> Option<Self::Item> {
let index = self.next_element.index + 1;
Some(std::mem::replace(
&mut self.next_element,
Term {
coefficient: 1.0,
index: index,
exponent: f64::from(index),
},
))
}
Using #[derive(Clone, Copy)]:
fn next(&mut self) -> Option<Self::Item> {
let current_element = self.next_element; // copy
self.next_element.coefficient = 1.0;
self.next_element.index += 1;
self.next_element.exponent = f64::from(self.next_element.index);
Some(current_element)
}
Try this working example:
#[derive(Clone, Copy)]
pub struct Term {
pub coefficient: f64,
pub index: i32,
pub exponent: f64,
}
pub struct SeriesIterator {
next_element: Term,
}
impl Iterator for SeriesIterator {
type Item = Term;
fn next(&mut self) -> Option<Self::Item> {
let current_element = self.next_element; // copy
self.next_element.coefficient = 1.0;
self.next_element.index += 1;
self.next_element.exponent = f64::from(self.next_element.index);
Some(current_element)
}
}
fn main() {
let mut t = SeriesIterator {
next_element: Term {
coefficient: 1.0,
index: 0,
exponent: 1.0,
},
};
let next = t.next().unwrap();
println!("{}", next.index); // 0
println!("{}", t.next_element.index); // 1
let next = t.next().unwrap();
println!("{}", next.index); // 1
println!("{}", t.next_element.index); // 2
}
Output:
0
1
1
2

Resources