Rendering text in a separate function using piston2d in rust - rust

I am trying to render text in a separate function using piston2d / piston_window. I am able to draw text just fine, but I can't figure out how to pass the appropriate parameters into a separate function.
I have studied What is GlyphCache type in a function to render text in Piston2d and adjusted my code accordingly, but I can't make sense of the error I am getting.
use piston_window::*;
fn main() {
let font = include_bytes!("IBMPlexSans-Regular.ttf");
let opengl = OpenGL::V3_2;
let settings = WindowSettings::new("test", [500, 500])
.graphics_api(opengl)
.fullscreen(false)
.vsync(true)
.exit_on_esc(true);
let mut window: PistonWindow = settings.build().unwrap();
let mut glyphs = Glyphs::from_bytes(
font,
window.create_texture_context(),
TextureSettings::new(),
)
.unwrap();
while let Some(e) = window.next() {
window.draw_2d(&e, |c, gfx, device| {
clear([0.2; 4], gfx);
text::Text::new_color([1.0, 1.0, 1.0, 0.7], 30)
.draw(
"Hi!",
&mut glyphs,
&c.draw_state,
c.transform
.trans(100., 100.),
gfx,
)
.unwrap();
glyphs.factory.encoder.flush(device);
});
}
}
fn render_text(
x: f64,
y: f64,
text: &str,
size: u32,
c: Context,
g: &mut G2d,
glyphs: &mut glyph_cache::rusttype::GlyphCache<GfxFactory, G2dTexture>,
) {
text::Text::new(size)
.draw(text, glyphs, &c.draw_state, c.transform.trans(x, y), g)
.unwrap();
}
I am receiving the following error:
error[E0277]: the trait bound `Texture<gfx_device_gl::Resources>: UpdateTexture<gfx_device_gl::factory::Factory>` is not satisfied
--> src/main.rs:54:10
|
54 | .draw(text, glyphs, &c.draw_state, c.transform.trans(x, y), g)
| ^^^^ the trait `UpdateTexture<gfx_device_gl::factory::Factory>` is not implemented for `Texture<gfx_device_gl::Resources>`
|
= help: the following implementations were found:
<Texture<R> as UpdateTexture<TextureContext<F, R, C>>>
= note: required because of the requirements on the impl of `CharacterCache` for `GlyphCache<'_, gfx_device_gl::factory::Factory, Texture<gfx_device_gl::Resources>>`
I am aware this is probably very piston-specific, but I would be very happy about any pointers.

Just had the same problem. It's almost a year after but documentation for piston_window isn't the best so maybe others will need it.
This worked for me
use piston_window::types::Color;
use piston_window::{text, Context, G2d, Glyphs, Transformed};
pub const text_color: Color = [1.0, 1.0, 1.0, 1.0];
pub fn draw_text(
ctx: &Context,
graphics: &mut G2d,
glyphs: &mut Glyphs,
color: Color,
pos: Position,
text: &str,
) {
text::Text::new_color(color, 20)
.draw(
text,
glyphs,
&ctx.draw_state,
ctx.transform.trans(pos.x as f64, pos.y as f64),
graphics,
)
.unwrap();
}
pub struct Pos {
pub x: f64,
pub y: f64,
}
pub fn main() {
let assets = find_folder::Search::ParentsThenKids(3, 3)
.for_folder("assets")
.unwrap();
let ref font = assets.join("retro-gaming.ttf");
let mut glyphs = window.load_font(font).unwrap();
let size = [500., 500.];
let mut window: PistonWindow = WindowSettings::new("Test", size)
.resizable(false)
.exit_on_esc(true)
.build()
.unwrap();
while let Some(event) = window.next() {
window.draw_2d(&event, |ctx, g, _| {
draw_text(&ctx, g, &mut glyphs, text_color, Pos { x: 0, y: 10 }, "20")
}
}
}
Note that I'm using different "Glyphs" than you are. There's additionally one more dependency in "Cargo.toml" namely find_folder = "0.3.0".
I'm not entirely sure whether the snippet above compiles and works. It is a quick refactor from this commit https://github.com/laszukdawid/rsnake/commit/e6e23563ebdd9c7b972ee17b5d0299e2202358cf.

Related

join vector of non-strings into a string rust

I have a type T that implements display (so it has a .to_string() method)
let my_vec = vec![T(), T(), T()];
println!("{}", my_vec.join(", "));
sadly errors with "trait bounds not satisfied" because the separator, ", ", is not of the same type as the vector's items (I'm pretty sure).
I guess my workaround is then
println!("{}", my_vec.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", "));
But isn't there anything shorter and clearer that I can write out instead of this?
I've just written this function to help me out:
fn join<T, I>(vec: &[T], sep: I) -> String
where
T: std::fmt::Display,
I: std::fmt::Display,
{
vec.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(&sep.to_string())
}
But I'd rather not have to. There must be alternative solutions that: are already built-in, don't require manual implementation, at least don't require the creation of a top-level function that's then only called twice.
AFAIK, your work around is good and sound without non-std libs.
Several cargos provide some helper functions for this issue, for example:
Playground
itertools and joinery
use itertools::Itertools;
use joinery::Joinable;
struct Color {
red: u8,
green: u8,
blue: u8,
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"RGB ({}, {}, {}) 0x{:02X}{:02X}{:02X}",
self.red, self.green, self.blue, self.red, self.green, self.blue
)
}
}
fn main() {
let colors = vec![
Color {
red: 128,
green: 255,
blue: 90,
},
Color {
red: 0,
green: 3,
blue: 254,
},
];
println!(
"{}",
colors
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
);
println!("{}", colors.iter().join(", "));
println!("{}", colors.join_with(", "));
}

How to use rayon to update a personal struct containing an Array in Rust

CONTEXT
General overview
(Here is the github page with the minimal example of my problem, and the page of my whole project)
I'm very new to rust and I'm trying to simulate the behavior of a fluid in Rust. This is easy: computing large arrays with some functions for each timestep.
I'd like to parallelize the computations done each timestep using rayon. But the compiler doesn't want me to access a mutable struct containing an Array that I want to modify, even if I'm sure that each modification will be on different places in the array: which assure me that it's safe. (I think?).
So my question is: should I use unsafe rust in here? If so, how?
And: is it possible to make Rust understand that it's safe, or to do it properly without unsafe rust?
I saw this post which kind of resembled my problem, but couldn't find a way to use the solution for my problem.
I also tried to put unsafe {...} keywords everywhere, but the compilator still complains...
You may only need to read "Structs" subsection to understand the problem, but I will also put a "Function" subsection, in case it can be important. If you think it might not be necessary, you can skip to "Main function" subsection.
Structs
Here are my structs:
I'd like to keep them that way, as they would give me (I think) more flexibility with setters/getters, but I'm open to change the way it's implemented right now.
#[derive(Debug, PartialEq)]
struct vec2D {pub x: f64, pub y: f64}
#[derive(Debug, PartialEq)]
struct ScalarField2D {
s: Array2<f64>,
}
#[derive(Debug, PartialEq)]
struct VectorField2D {
x: ScalarField2D,
y: ScalarField2D
}
impl ScalarField2D {
// also a constructor new() but not shown for simplicity
fn get_pos(&self, x: usize, y: usize) -> f64
{return self.s[[y,x]];}
fn set_pos(&mut self, x: usize, y: usize, f: f64)
{self.s[[y,x]] = f;}
}
impl VectorField2D {
// also a constructor new() but not shown for simplicity
fn get_pos(&self, x: usize, y: usize) -> vec2D
{let vec_at_pos = vec2D {
x: self.x.get_pos(x, y),
y: self.y.get_pos(x, y)};
return vec_at_pos;}
fn set_pos(&mut self, x: usize, y: usize, vec: &vec2D)
{self.x.set_pos(x, y, vec.x);
self.y.set_pos(x, y, vec.y);}
}
Function
Here is my function: which takes a ScalarField2D struct, and computes a vector called the "gradient" at a particular position of the ScalarField2D array, and then returning this vector as a "vec2D" struct.
// computes the gradient of a scalar field at a given position
fn grad_scalar(a: &ScalarField2D,
x: i32, y: i32,
x_max: i32, y_max: i32) -> vec2D
{
let ip = ((x+1) % x_max) as usize;
// i-1 with Periodic Boundaries
let im = ((x - 1 + x_max) % x_max) as usize;
// j+1 with Periodic Boundaries
let jp = ((y+1) % y_max) as usize;
// j-1 with Periodic Boundaries
let jm = ((y - 1 + y_max) % y_max) as usize;
let (i, j) = (x as usize, y as usize);
let grad = vec2D {
x: (a.get_pos(ip, j) - a.get_pos(im, j))/(2.),
y: (a.get_pos(i, jp) - a.get_pos(i, jm))/(2.)};
return grad;
}
Main function
Here is my problem:
I try to iterate over all the possible x and y using (0..x_max).into_par_iter() (or y_max for y), compute the gradient associated with each position, and then set the value to the ScalarField2D struct using the set_pos method... Here is the main function, and the import commands, and I will show you the error message I get in the next subsection
use ndarray::prelude::*;
use rayon::prelude::*;
fn main() {
let (x_max, y_max) = (2usize, 50usize);
let (x_maxi32, y_maxi32) = (x_max as i32, y_max as i32);
let mut GD_grad_rho = VectorField2D::new(x_max, y_max);
let mut GD_rho = ScalarField2D::new(x_max, y_max);
let x_iterator = (0..x_max).into_par_iter();
x_iterator.map(|xi| {
let y_iterator = (0..y_max).into_par_iter();
y_iterator.map(|yi| {
// unsafe here?
GD_grad_rho
.set_pos(xi, yi,
&grad_scalar(&GD_rho,
xi as i32, yi as i32,
x_maxi32, y_maxi32));
});});
}
Error message
Here is the compilation error I get
error[E0596]: cannot borrow `GD_grad_rho` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:104:13
|
104 | / GD_grad_rho
105 | | .set_pos(xi, yi,
106 | | &grad_scalar(&GD_rho,
107 | | xi as i32, yi as i32,
108 | | x_maxi32, y_maxi32));
| |__________________________________________________________^ cannot borrow as mutable
error[E0596]: cannot borrow `GD_grad_rho` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:101:24
|
101 | y_iterator.map(|yi| {
| ^^^^ cannot borrow as mutable
...
104 | GD_grad_rho
| ----------- mutable borrow occurs due to use of `GD_grad_rho` in closure
For more information about this error, try `rustc --explain E0596`.
error: could not compile `minimal_example_para` due to 2 previous errors
If you want the complete thing, I created a github repo with everything in it.
Tests after susitsm answer
So I did something like this:
use ndarray::prelude::*;
use rayon::prelude::*;
fn grad_scalar(a: &Array2<f64>,
i: usize, j: usize) -> (f64, f64)
{
let array_shape = a.shape();
let imax = array_shape[0];
let jmax = array_shape[1];
// i-1 with Periodic Boundaries
let ip = ((i + 1) % imax);
// i-1 with Periodic Boundaries
let im = ((i + imax) - 1) % imax;
// j+1 with Periodic Boundaries
let jp = ((j + 1) % jmax);
// j-1 with Periodic Boundaries
let jm = ((j + jmax) - 1) % jmax;
let grad_i = (a[[ip, j]] - a[[im, j]])/2.;
let grad_j = (a[[i, jp]] - a[[i, jm]])/2.;
return (grad_i, grad_j);
}
fn main() {
let a = Array::<f64, Ix2>::ones((dim, dim));
let index_list: Vec<(_, _)> = (0..a.len())
.into_par_iter()
.map(|i| (i / a.dim().0, i % a.dim().1))
.collect();
let (r1, r2): (Vec<_>, Vec<_>) = (0..a.len())
.into_par_iter()
.map(|i| (index_list[i].0, index_list[i].1))
.map(|(i, j)| grad_scalar(&a, i, j))
.collect();
let grad_row = Array2::from_shape_vec(a.dim(), r1).unwrap();
let grad_col = Array2::from_shape_vec(a.dim(), r2).unwrap();
}
Which gives me the result I want, even though I wanted to access the values through my Structs. Which is not exactly what I want but we're getting closer
But I wonder about the efficiency for more arrays, I'll post a separate question for that
You can do something like this:
use ndarray::Array2;
use rayon::prelude::*;
fn main() {
let a: Vec<u64> = (0..20000).collect();
let a = Array2::from_shape_vec((100, 200), a).unwrap();
let stuff = |arr, i, j| (i + j, i * j);
let (x, y): (Vec<_>, Vec<_>) = (0..a.len())
.into_par_iter()
.map(|i| (i / a.dim().0, i % a.dim().1))
.map(|(i, j)| stuff(&a, i, j))
.collect();
let grad_x = Array2::from_shape_vec(a.dim(), x);
let grad_y = Array2::from_shape_vec(a.dim(), y);
let grad_vector_field = VectorField2D {
x: ScalarField2D { s: grad_x },
y: ScalarField2D { s: grad_y },
};
}
Or implement the FromParallelIterator<vec2D>
impl FromParallelIterator<vec2D> for VectorField2D {
fn from_par_iter<I>(par_iter: I) -> Self
where I: IntoParallelIterator<Item = T>
{
let (x, y): (Vec<_>, Vec<_>) = par_iter
.into_par_iter()
.map(|vec_2D| {
let vec2D { x, y } = vec_2D;
(x, y)
})
.collect();
Self {
x: ScalarField2D { s: Array2::from_shape_vec(a.dim(), x) },
y: ScalarField2D { s: Array2::from_shape_vec(a.dim(), y) },
}
}
}
This will enable using collect for your type when using parallel iterators
let vector_field: VectorField2D = (0..a.len())
.into_par_iter()
.map(|i| (index_list[i].0, index_list[i].1))
.map(|(i, j)| grad_scalar(&a, i, j))
.collect();

Dynamically create a range in either direction in Rust

I am learning Rust and recently went through an exercise where I had to iterate through numbers that could go in either direction. I tried the below with unexpected results.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
x: i32,
y: i32
}
fn test() {
let p1 = Point { x: 1, y: 8 };
let p2 = Point { x: 3, y: 6 };
let all_x = p1.x..=p2.x;
println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());
let all_y = p1.y..=p2.y;
println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());
let points: Vec<Point> = all_x.zip(all_y).map(|(x, y)| Point { x, y }).collect();
println!("points: {:?}", points);
}
The output was
all_x: [1, 2, 3]
all_y: []
points: []
After some googling I found an explanation and some old answers which basically amount to use (a..b).rev() as needed.
My question is, how do I do this in a dynamic way? If I use an if...else like so
let all_x = if p1.x < p2.x { (p1.x..=p2.x) } else { (p2.x..=p1.x).rev() };
I get a type error because the else is different than the if
|
58 | let all_x = if p1.x < p2.x { (p1.x..=p2.x) }
| - ------------- expected because of this
| _________________|
| |
59 | | else { (p2.x..=p1.x).rev() };
| |____________^^^^^^^^^^^^^^^^^^^_- `if` and `else` have incompatible types
| |
| expected struct `RangeInclusive`, found struct `Rev`
|
= note: expected type `RangeInclusive<_>`
found struct `Rev<RangeInclusive<_>>`
After trying a bunch of different variations on let all_x: dyn Range<Item = i32>, let all_x: dyn Iterator<Item = i32>, etc, the only way I have managed to do this is by turning them into collections and then back to iterators.
let all_x: Vec<i32>;
if p1.x < p2.x { all_x = (p1.x..=p2.x).collect(); }
else { all_x = (p2.x..=p1.x).rev().collect(); }
let all_x = all_x.into_iter();
println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());
let all_y: Vec<i32>;
if p1.y < p2.y { all_y = (p1.y..=p2.y).collect(); }
else { all_y = (p2.y..=p1.y).rev().collect(); }
let all_y = all_y.into_iter();
println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());
which provides the desired outcome
all_x: [1, 2, 3]
all_y: [8, 7, 6]
points: [Point { x: 1, y: 8 }, Point { x: 2, y: 7 }, Point { x: 3, y: 6 }]
but is a bit repetitive, inelegant and I'm assuming not very efficient at large numbers. Is there a better way to handle this situation?
NOTE: Sorry for including the Point struct. I could not get my example to work with x1, x2, etc. Probably a different question for a different post lol.
You can dynamically dispatch it. Wrapping them into a Box and returning a dynamic object, an Iterator in this case. For example:
fn maybe_reverse_range(init: usize, end: usize, reverse: bool) -> Box<dyn Iterator<Item=usize>> {
if reverse {
Box::new((init..end).rev())
} else {
Box::new((init..end))
}
}
Playground
The enum itertools::Either can be used to solve the incompatible type error in the if/else statement. A function like get_range_iter below using Either can reduce the code repetition.
use itertools::Either;
fn get_range_iter(start: i32, end: i32) -> impl Iterator<Item=i32> {
if start < end {
Either::Left(start..=end)
} else {
Either::Right((end..=start).rev())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
x: i32,
y: i32
}
fn main() {
let p1 = Point { x: 1, y: 8 };
let p2 = Point { x: 3, y: 6 };
let all_x = get_range_iter(p1.x, p2.x);
let all_y = get_range_iter(p1.y, p2.y);
println!("all_x: {:?}", all_x.collect::<Vec<_>>());
println!("all_y: {:?}", all_y.collect::<Vec<_>>());
}
Playground

How to add threading to a for loop in Rust?

I'm trying to write a raytracer in Rust. I'm having difficulty getting the for loops to run in parallel. I'm not sure where the problem is, but I can't seem to get anything on the screen. Is this the correct approach or am I completely heading in the wrong direction?
I've tried running the for loops without multi-threading and it does correctly produce output. I've also added loggers to the consumer loop and I'm getting the correct values as well. It just doesn't seem to update the window.
#[derive(Clone, Copy)]
pub struct Pixel {
pub x: usize,
pub y: usize,
pub color: Vec3,
}
let mut buffer : Vec<u32> = vec![0; WIDTH * HEIGHT];
let (tx, rx) = mpsc::channel()
for x in 0..HEIGHT {
let tx_t = tx.clone();
thread::spawn(move || {
for y in 0..WIDTH {
let mut color = cast_ray(x, y); // returns vec3
let pixel = Pixel { x: x, y: y, color: color };
tx_t.send(pixel).unwrap();
}
});
}
for received in rx {
buffer[received.x * WIDTH + received.y] = received.color.x << 16 | received.color.y << 8 | received.color.z;
}
while window.is_open() && !window.is_key_down(Key::Escape) {
window.update_with_buffer(&buffer).unwrap();
}
I'm expecting a few spheres or color to appear on the screen, but it's just black.

How to use math to rotate around the center of a ggez image instead of the top left?

I'm making a game using ggez, using a camera from ggez_goodies.
This camera only has rotation from the top left of an image, but I want it from the center. Is there a good way to position the image so that it rotates around the center instead?
I'd assume you'd just change the position to fix this, what I have currently is just the position
self.image
.draw_camera(
camera,
ctx,
graphics::Point2::new(self.position.0, self.position.1),
self.rotation,
)
.unwrap();
I'm guessing a fix would look sort of like this:
self.image
.draw_camera(
camera,
ctx,
graphics::Point2::new(
self.position.0 + self.rotation_offset.0,
self.position.1 + self.rotation_offset.1,
),
self.rotation,
)
.unwrap();
I think it would be possible with an offset, but I can't figure out how to set the offset based off of the rotation angle.
What offset/change could I make to the position to get the image to rotate based around the center instead of around the top left corner?
In following example:
use ggez;
use ggez::event;
use ggez::graphics;
use ggez::graphics::{DrawMode, MeshBuilder};
use ggez::nalgebra as na;
use ggez::timer::check_update_time;
use ggez::{Context, GameResult};
use std::env;
use std::path;
struct MainState {
rotate: f32,
sprite_batch: graphics::spritebatch::SpriteBatch,
}
impl MainState {
fn new(ctx: &mut Context) -> GameResult<MainState> {
let image = graphics::Image::new(ctx, "/sprite_sheet.png").unwrap();
let batch = graphics::spritebatch::SpriteBatch::new(image);
Ok(MainState {rotate: 0.0, sprite_batch: batch})
}
}
impl event::EventHandler for MainState {
fn update(&mut self, ctx: &mut Context) -> GameResult {
while check_update_time(ctx, 30) {
self.rotate += 0.1;
}
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult {
graphics::clear(ctx, graphics::BLACK);
let mesh = MeshBuilder::new().circle(
DrawMode::fill(),
na::Point2::new(400.0, 300.0),
2.0,
2.0,
graphics::WHITE,
).build(ctx)?;
self.sprite_batch.add(
graphics::DrawParam::new()
.src(graphics::Rect::new(0.0, 0.0, 1.0, 1.0))
.rotation(self.rotate)
.dest(na::Point2::new(400.0, 300.0))
.offset(na::Point2::new(0.5, 0.5)),
);
graphics::draw(
ctx,
&self.sprite_batch,
graphics::DrawParam::new().dest(na::Point2::new(0.0, 0.0)),
)?;
graphics::draw(
ctx,
&mesh,
graphics::DrawParam::new().dest(na::Point2::new(0.0, 0.0)),
)?;
self.sprite_batch.clear();
graphics::present(ctx)?;
Ok(())
}
}
pub fn main() -> GameResult {
let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
let mut path = path::PathBuf::from(manifest_dir);
path.push("resources");
path
} else {
path::PathBuf::from("./resources")
};
let cb = ggez::ContextBuilder::new("oc", "bux")
.add_resource_path(resource_dir)
.window_mode(ggez::conf::WindowMode::default().dimensions(800.0, 600.0));
let (ctx, event_loop) = &mut cb.build()?;
let state = &mut MainState::new(ctx)?;
event::run(ctx, event_loop, state)
}
Where resources/sprite_sheet.png is:
The which do rotation from the center of the image is:
.offset(na::Point2::new(0.5, 0.5))

Resources