mirror of
https://github.com/ivabus/matrix
synced 2024-12-04 05:55:08 +03:00
Initial commit
Signed-off-by: Ivan Bushchik <ivabus@ivabus.dev>
This commit is contained in:
commit
af12193395
9 changed files with 479 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*Cargo.lock
|
||||
*target/
|
||||
*.DS_Store
|
||||
*.idea/
|
11
.rustfmt.toml
Normal file
11
.rustfmt.toml
Normal 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
3
Cargo.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[workspace]
|
||||
members = ["matrix", "matrix_graphics"]
|
||||
resolver = "2"
|
20
LICENSE
Normal file
20
LICENSE
Normal 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
8
matrix/Cargo.toml
Normal 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
154
matrix/src/lib.rs
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
18
matrix_graphics/Cargo.toml
Normal file
18
matrix_graphics/Cargo.toml
Normal 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
195
matrix_graphics/src/main.rs
Normal 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;
|
||||
}
|
||||
});
|
||||
}
|
66
matrix_graphics/src/structs.rs
Normal file
66
matrix_graphics/src/structs.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue