Initial commit

Signed-off-by: Ivan Bushchik <ivabus@ivabus.dev>
This commit is contained in:
Ivan Bushchik 2023-03-11 20:26:57 +03:00
commit af12193395
No known key found for this signature in database
GPG key ID: 9F6DDABE11A2674D
9 changed files with 479 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*Cargo.lock
*target/
*.DS_Store
*.idea/

11
.rustfmt.toml Normal file
View file

@ -0,0 +1,11 @@
edition = "2021"
hard_tabs = true
merge_derives = true
reorder_imports = true
reorder_modules = true
use_field_init_shorthand = true
use_small_heuristics = "Off"
#wrap_comments = true
#comment_width = 80
#indent_style = "Block"
#wrap_comments = true

3
Cargo.toml Normal file
View file

@ -0,0 +1,3 @@
[workspace]
members = ["matrix", "matrix_graphics"]
resolver = "2"

20
LICENSE Normal file
View file

@ -0,0 +1,20 @@
MIT License
Copyright (c) 2023 Ivan Bushchik
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

8
matrix/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "matrix"
version = "0.1.0"
edition = "2021"
license = "MIT"
[dependencies]
rand = "0.8.5"

154
matrix/src/lib.rs Normal file
View file

@ -0,0 +1,154 @@
// AUGGGGHHHHHHHHHHHHH
// I WANT TO USE GENERICS
// BUT I DON'T KNOW HOW TO USE THEM PROPERLY
use rand::{Rng, thread_rng};
fn check_valid(a: &Vec<Vec<f64>>) -> bool {
let len = a[0].len();
for i in a {
if i.len() != len {
return false;
}
}
true
}
pub fn sum(a: &Vec<Vec<f64>>, b: &Vec<Vec<f64>>) -> Option<Vec<Vec<f64>>> {
if !(check_valid(&a) && check_valid(&b)) {
return None;
}
if a.len() != b.len() {
return None;
}
let mut c: Vec<Vec<f64>> = Vec::new();
for i in 0..a.len() {
c.push(vec![]);
for j in 0..a[0].len() {
c[i].push(a[i][j] + b[i][j]);
}
}
Some(c)
}
pub fn mult(a: &Vec<Vec<f64>>, b: &Vec<Vec<f64>>) -> Option<Vec<Vec<f64>>> {
if !(check_valid(&a) && check_valid(&b)) {
return None;
}
if a[0].len() != b.len() {
return None;
}
let m = a[0].len();
let mut c: Vec<Vec<f64>> = Vec::new();
for i in 0..a.len() {
c.push(vec![]);
for j in 0..b[0].len() {
let mut s = 0.;
for r in 0..m {
s += a[i][r] * b[r][j];
}
c[i].push(s as f64);
}
}
Some(c)
}
#[allow(dead_code)]
fn gen_matrix(i: usize, j: usize) -> Option<Vec<Vec<f64>>> {
if !(i > 0 && j > 0) {
return None;
}
let mut rng = thread_rng();
let mut m: Vec<Vec<f64>> = Vec::new();
for a in 0..i {
m.push(vec![]);
for _ in 0..j {
m[a].push(rng.gen_range(-100..100) as f64);
}
}
Some(m)
}
#[cfg(test)]
mod test {
use rand::{Rng, thread_rng};
#[test]
fn basic_sum() {
let a = vec![vec![1., 2.], vec![3., 4.]];
let b = vec![vec![5., 6.], vec![7., 8.]];
assert_eq!(crate::sum(&a, &b).unwrap(), vec![vec![6., 8.], vec![10., 12.]])
}
#[test]
fn basic_mult() {
let a = vec![vec![3., -1., 2.], vec![4., 2., 0.], vec![-5., 6., 1.]];
let b = vec![vec![8., 1.], vec![7., 2.], vec![2., -3.]];
assert_eq!(crate::mult(&a, &b).unwrap(), vec![vec![21., -5.], vec![46., 8.], vec![4., 4.]])
}
#[test]
fn mv_sum() {
let mut rng = thread_rng();
let (s1, s2) = (rng.gen_range(2..500), rng.gen_range(2..500));
let a = crate::gen_matrix(s1, s2).unwrap();
let b = crate::gen_matrix(s1, s2).unwrap();
assert_eq!(crate::sum(&a, &b).unwrap(), crate::sum(&b, &a).unwrap())
}
#[test]
#[ignore]
fn mv_mult() {
let a = crate::gen_matrix(10, 1).unwrap();
let b = crate::gen_matrix(1, 10).unwrap();
let ab = crate::mult(&a, &b);
let ba = crate::mult(&b, &a);
if ab != None && ba != None {
assert!(ab.unwrap() == ba.unwrap())
} else {
assert!(false)
}
}
#[test]
fn comb_sum() {
let mut rng = thread_rng();
let (s1, s2) = (rng.gen_range(2..500), rng.gen_range(2..500));
let a = crate::gen_matrix(s1, s2).unwrap();
let b = crate::gen_matrix(s1, s2).unwrap();
let c = crate::gen_matrix(s1, s2).unwrap();
assert_eq!(
crate::sum(&crate::sum(&a, &b).unwrap(), &c).unwrap(),
crate::sum(&a, &crate::sum(&b, &c).unwrap()).unwrap()
)
}
#[test]
fn comb_mult() {
let mut rng = thread_rng();
let (s1, s2, s3, s4) = (
rng.gen_range(2..500),
rng.gen_range(2..500),
rng.gen_range(2..500),
rng.gen_range(2..500),
);
let a = crate::gen_matrix(s1, s2).unwrap();
let b = crate::gen_matrix(s2, s3).unwrap();
let c = crate::gen_matrix(s3, s4).unwrap();
assert_eq!(
crate::mult(&crate::mult(&a, &b).unwrap(), &c).unwrap(),
crate::mult(&a, &crate::mult(&b, &c).unwrap()).unwrap()
)
}
}

View file

@ -0,0 +1,18 @@
[package]
name = "matrix_graphics"
version = "0.1.0"
edition = "2021"
license = "MIT"
[features]
optimize = ["log/release_max_level_warn"]
default = ["optimize"]
[dependencies]
env_logger = "0.10"
log = "0.4"
matrix = { version = "0.1.0", path = "../matrix" }
pixels = "0.11.0"
winit = "0.28.1"
winit_input_helper = "0.14.0"
bresenham = "0.1.1"

195
matrix_graphics/src/main.rs Normal file
View file

@ -0,0 +1,195 @@
use std::time::Instant;
use log::error;
use pixels::{PixelsBuilder, SurfaceTexture};
use winit::dpi::LogicalSize;
use winit::event::{Event, VirtualKeyCode};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{WindowBuilder, WindowButtons};
use winit_input_helper::WinitInputHelper;
use crate::structs::Line;
mod structs;
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
const POLYGON: usize = 7;
// POLYGON >= 2
const CENTER_X: u32 = 320;
const CENTER_Y: u32 = 240;
const RADIUS: u32 = 100;
const PI: f64 = std::f64::consts::PI;
const DEFAULT_ANGLE: f64 = 0.;
impl Default for Line {
fn default() -> Self {
Line {
start_x: 0.,
start_y: 0.,
end_x: 0.,
end_y: 0.,
rotate_angle: 0.,
rotate_center_x: 0.,
rotate_center_y: 0.,
}
}
}
fn create_polygon() -> [Line; POLYGON] {
let mut it: [Line; POLYGON] = [Line::default(); POLYGON];
let mut dots: Vec<(f64, f64)> = Vec::new();
for i in 0..POLYGON {
dots.push((
CENTER_X as f64 + RADIUS as f64 * (2. * PI * i as f64 / POLYGON as f64).cos(),
CENTER_Y as f64 + RADIUS as f64 * (2. * PI * i as f64 / POLYGON as f64).sin(),
));
}
for i in 0..POLYGON {
let next = if i + 1 == POLYGON {
0
} else {
i + 1
};
it[i] = Line::new(
dots[i].0,
dots[i].1,
dots[next].0,
dots[next].1,
DEFAULT_ANGLE,
CENTER_X as f64,
CENTER_Y as f64,
);
}
it
}
// Most based render, that goes by existing frame bitmap and just expands it to RGBA.
fn render(frame: &mut [u8], bitmap: &[[bool; WIDTH as usize]; HEIGHT as usize]) {
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
let x = i % WIDTH as usize;
let y = i / WIDTH as usize;
if bitmap[y][x] {
pixel.copy_from_slice(&[0xff, 0xff, 0xff, 0xff]);
} else {
pixel.copy_from_slice(&[0x00, 0x00, 0x00, 0xff]);
}
}
}
fn main() {
env_logger::init();
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
let window = {
let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
WindowBuilder::new()
.with_title("matrix_graphics")
.with_enabled_buttons(WindowButtons::CLOSE.union(WindowButtons::MINIMIZE))
.with_inner_size(size)
.with_min_inner_size(size)
.with_max_inner_size(size)
.build(&event_loop)
.unwrap()
};
let mut pixels = {
let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
PixelsBuilder::new(WIDTH, HEIGHT, surface_texture).enable_vsync(true).build().unwrap()
};
let mut objects = create_polygon();
let mut frame_count = 0;
let mut now = Instant::now();
let mut bitmap: [[bool; WIDTH as usize]; HEIGHT as usize] =
[[false; WIDTH as usize]; HEIGHT as usize];
event_loop.run(move |event, _, control_flow| {
match event {
Event::MainEventsCleared => {
render(pixels.get_frame_mut(), &bitmap);
if let Err(err) = pixels.render() {
error!("pixels.render() failed: {err}");
*control_flow = ControlFlow::Exit;
return;
}
}
_ => {}
}
// Handle input events
if input.update(&event) {
if input.key_pressed(VirtualKeyCode::Escape)
|| input.close_requested()
|| input.destroyed()
{
*control_flow = ControlFlow::Exit;
return;
}
if input.key_pressed(VirtualKeyCode::F) {
println!("Average FPS: {}", frame_count as f64 / now.elapsed().as_secs_f64());
frame_count = 0;
now = Instant::now();
}
if input.key_held(VirtualKeyCode::Equals) {
for i in &mut objects {
i.rotate_angle += std::f64::consts::PI / 360.;
}
}
if input.key_held(VirtualKeyCode::Space) {
for i in &mut objects {
i.rotate_angle = 0.;
}
}
if input.key_held(VirtualKeyCode::Minus) {
for i in &mut objects {
i.rotate_angle -= std::f64::consts::PI / 360.;
}
}
if input.key_held(VirtualKeyCode::Up) {
for i in &mut objects {
i.start_y -= 1.;
i.end_y -= 1.;
i.rotate_center_y -= 1.;
}
}
if input.key_held(VirtualKeyCode::Down) {
for i in &mut objects {
i.start_y += 1.;
i.end_y += 1.;
i.rotate_center_y += 1.;
}
}
if input.key_held(VirtualKeyCode::Left) {
for i in &mut objects {
i.start_x -= 1.;
i.end_x -= 1.;
i.rotate_center_x -= 1.;
}
}
if input.key_held(VirtualKeyCode::Right) {
for i in &mut objects {
i.start_x += 1.;
i.end_x += 1.;
i.rotate_center_x += 1.;
}
}
for i in &mut objects {
i.update();
}
bitmap = [[false; WIDTH as usize]; HEIGHT as usize];
for l in 0..objects.len() {
let current_dots = objects[l].get_points();
for (x, y) in current_dots {
if x >= 0 && y >= 0 && x < WIDTH as isize && y < HEIGHT as isize {
bitmap[y as usize][x as usize] = true;
}
}
}
frame_count += 1;
}
});
}

View file

@ -0,0 +1,66 @@
use bresenham;
#[derive(Copy, Clone)]
pub struct Line {
pub start_x: f64,
pub start_y: f64,
pub end_x: f64,
pub end_y: f64,
pub rotate_angle: f64,
pub rotate_center_x: f64,
pub rotate_center_y: f64,
}
impl Line {
pub fn new(
start_x: f64,
start_y: f64,
end_x: f64,
end_y: f64,
rotate_angle: f64,
rotate_center_x: f64,
rotate_center_y: f64,
) -> Line {
Line {
start_x,
start_y,
end_x,
end_y,
rotate_angle,
rotate_center_x,
rotate_center_y,
}
}
pub fn get_points(&self) -> bresenham::Bresenham {
bresenham::Bresenham::new(
(self.start_x as isize, self.start_y as isize),
(self.end_x as isize, self.end_y as isize),
)
}
fn rotate(&mut self) {
let matrix_rotate = vec![
vec![self.rotate_angle.cos(), -self.rotate_angle.sin()],
vec![self.rotate_angle.sin(), self.rotate_angle.cos()],
];
let matrix_start = vec![
vec![(self.start_x - self.rotate_center_x) as f64],
vec![(self.start_y - self.rotate_center_y) as f64],
];
let matrix_end = vec![
vec![(self.end_x - self.rotate_center_x) as f64],
vec![(self.end_y - self.rotate_center_y) as f64],
];
let res_start = matrix::mult(&matrix_rotate, &matrix_start).unwrap();
let res_end = matrix::mult(&matrix_rotate, &matrix_end).unwrap();
(self.start_x, self.start_y) =
(res_start[0][0] + self.rotate_center_x, res_start[1][0] + self.rotate_center_y);
(self.end_x, self.end_y) =
(res_end[0][0] + self.rotate_center_x, res_end[1][0] + self.rotate_center_y);
}
pub fn update(&mut self) {
if self.rotate_angle != 0. {
self.rotate();
}
}
}