From b2d7b3449e133ab5705789010aba5bbedda7730f Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Wed, 25 Jun 2025 10:23:00 +0800 Subject: [PATCH] removed dependency from rmath --- rdraught-ui/Cargo.toml | 1 - rdraught-ui/src/geo2d.rs | 163 +++++---------------------------------- rdraught-ui/src/main.rs | 100 ++++++++++++++++++------ 3 files changed, 94 insertions(+), 170 deletions(-) diff --git a/rdraught-ui/Cargo.toml b/rdraught-ui/Cargo.toml index f50e3e1..8f4c043 100644 --- a/rdraught-ui/Cargo.toml +++ b/rdraught-ui/Cargo.toml @@ -12,7 +12,6 @@ version.workspace = true gtk4.workspace = true gdk4.workspace = true rdraught.workspace = true -rmath = { version="0.1", registry="gitea" } librsvg.workspace = true cairo-rs.workspace = true gio.workspace = true diff --git a/rdraught-ui/src/geo2d.rs b/rdraught-ui/src/geo2d.rs index 015a34f..3ca6ac2 100644 --- a/rdraught-ui/src/geo2d.rs +++ b/rdraught-ui/src/geo2d.rs @@ -1,4 +1,3 @@ -use rmath::SMatrix; use std::clone::Clone; use std::cmp::Eq; use std::cmp::PartialEq; @@ -10,54 +9,56 @@ use std::ops::Mul; use std::ops::Neg; use std::ops::Sub; -pub type Xform = SMatrix; - #[derive(Debug)] -pub struct Point(SMatrix); +pub struct Point { + x: f64, + y: f64, +} impl Point { pub fn x(&self) -> f64 { - self.0[(0, 0)] + self.x } pub fn y(&self) -> f64 { - self.0[(0, 1)] + self.y } pub fn new(x: f64, y: f64) -> Point { - Point(SMatrix::new(|pos| match pos { - (0, 0) => x, - (0, 1) => y, - (0, 2) => 1f64, - _ => 0f64, - })) + Point { x, y } } } impl Add<&Point> for &Point { fn add(self, rhs: &Point) -> Self::Output { - self * &xlate(rhs.x(), rhs.y()) + Point { + x: self.x() + rhs.x(), + y: self.y() + rhs.y(), + } } type Output = Point; } impl Sub<&Point> for &Point { fn sub(self, rhs: &Point) -> Self::Output { - self * &xlate(-rhs.x(), -rhs.y()) + Point { + x: self.x() - rhs.x(), + y: self.y() - rhs.y(), + } } type Output = Point; } impl Mul for &Point { fn mul(self, rhs: f64) -> Self::Output { - Point::new(self.0[(0, 0)] * rhs, self.0[(0, 1)] * rhs) + Point::new(self.x * rhs, self.y * rhs) } type Output = Point; } impl Div for &Point { fn div(self, rhs: f64) -> Self::Output { - Point::new(self.0[(0, 0)] / rhs, self.0[(0, 1)] / rhs) + Point::new(self.x / rhs, self.y / rhs) } type Output = Point; } @@ -72,20 +73,12 @@ impl Neg for &Point { impl PartialEq for Point { fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) + self.x.eq(&other.x) && self.y.eq(&other.y) } } impl Eq for Point {} -impl Mul<&Xform> for &Point { - fn mul(self, rhs: &Xform) -> Self::Output { - Point(self.0 * rhs) - } - - type Output = Point; -} - impl Clone for Point { fn clone(&self) -> Self { *self @@ -96,126 +89,8 @@ impl Copy for Point {} impl Display for Point { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) + write!(f, "Point({}, {})", self.x, self.y) } } -#[derive(Debug, Clone)] -pub struct Rect2d { - tl: Point, - br: Point, -} - -impl Rect2d { - pub fn new(tl: Point, br: Point) -> Rect2d { - Rect2d { tl, br } - } - - pub fn center(&self) -> Point { - &(&self.tl + &self.br) / 2.0 - } - - pub fn tl(&self) -> Point { - self.tl - } - - pub fn br(&self) -> Point { - self.br - } - - pub fn width(&self) -> f64 { - (self.br.x() - self.tl.x()).abs() - } - - pub fn height(&self) -> f64 { - (self.br.y() - self.tl.y()).abs() - } - - pub fn contains(&self, point: &Point) -> bool { - self.tl().x() < point.x() - && self.tl().y() < point.y() - && self.br().x() > point.x() - && self.br().y() > point.y() - } -} - -impl Mul<&Xform> for &Rect2d { - fn mul(self, rhs: &Xform) -> Self::Output { - Rect2d { - tl: Point(self.tl.0 * rhs), - br: Point(self.br.0 * rhs), - } - } - - type Output = Rect2d; -} - -pub fn rot(alpha: f64) -> Xform { - let sa = alpha.sin(); - let ca = alpha.cos(); - Xform::new(|position| match position { - (0, 0) => ca, - (1, 1) => ca, - (1, 0) => -sa, - (0, 1) => sa, - (2, 2) => 1f64, - _ => 0f64, - }) -} - impl Point {} - -pub fn scale(x: f64, y: f64) -> Xform { - Xform::new(|position| match position { - (0, 0) => x, - (1, 1) => y, - (2, 2) => 1f64, - _ => 0f64, - }) -} - -pub fn xlate(x: f64, y: f64) -> Xform { - Xform::new(|position| match position { - (0, 0) => 1f64, - (1, 1) => 1f64, - (2, 2) => 1f64, - (2, 0) => x, - (2, 1) => y, - _ => 0f64, - }) -} - -#[cfg(test)] -mod tests { - - use std::f64::consts::PI; - - use super::Point; - use super::rot; - use super::scale; - use super::xlate; - - #[test] - fn test_xlate() { - let p = Point::new(1.0, 3.0); - let xform = xlate(-1.0, 0.0); - let p2 = &(&(&p * &xform) * &rot(-PI / 2.0)) * &xlate(-2.0, 3.0); - assert!(p == p2); - } - - #[test] - fn test_rotate() { - let p = Point::new(0.0, 3.0); - let p2 = &p * &rot(-PI / 2.0); - assert!((p2.x() - 3.0).abs() < 1e-3); - assert!((p2.y() - 0.0).abs() < 1e-3); - } - - #[test] - fn test_scale() { - let p = Point::new(1.0, 3.0); - let p2 = &p * &scale(2.0, 3.0); - assert!((p2.x() - 2.0).abs() < 1e-3); - assert!((p2.y() - 9.0).abs() < 1e-3); - } -} diff --git a/rdraught-ui/src/main.rs b/rdraught-ui/src/main.rs index d850a00..acbfa16 100644 --- a/rdraught-ui/src/main.rs +++ b/rdraught-ui/src/main.rs @@ -1,13 +1,12 @@ -use gdk4::cairo::{Context as CairoContext, Matrix}; +use gdk4::cairo::{Context as CairoContext, Matrix, Rectangle}; use gtk4::cairo::Error; use gtk4::{self as gtk, gdk::ffi::GDK_BUTTON_PRIMARY}; use gtk4::{DrawingArea, prelude::*}; use rdraught::draughts::{self, DraughtsBoard, DraughtsGame, Move, Piece, Player}; use rdraught::position::Position; -use rmath::NumericalMatrix; mod geo2d; use core::f64::consts::PI; -use geo2d::{Point, Rect2d, Xform, scale, xlate}; +use geo2d::Point; use rsvg::SvgHandle; use std::cell::{Cell, RefCell}; use std::rc::Rc; @@ -17,9 +16,50 @@ const SQUARE_SIZE: f64 = 1.0; const CROWN_RED: &'static [u8] = include_bytes!("crown_red.svg"); const CROWN_WHITE: &'static [u8] = include_bytes!("crown_white.svg"); +fn transform_point(p: &Point, m: &Matrix) -> Point { + let (x, y) = m.transform_point(p.x(), p.y()); + Point::new(x, y) +} + +trait AugmentedRect { + fn from_points(tl: Point, br: Point) -> Rectangle; + fn center(&self) -> Point; + fn tl(&self) -> Point; + fn br(&self) -> Point; + fn contains(&self, p: &Point) -> bool; +} + +impl AugmentedRect for Rectangle { + fn center(&self) -> Point { + Point::new( + self.x() + self.width() / 2.0, + self.y() + self.height() / 2.0, + ) + } + + fn tl(&self) -> Point { + Point::new(self.x(), self.y()) + } + + fn br(&self) -> Point { + Point::new(self.x() + self.width(), self.y() + self.height()) + } + + fn from_points(tl: Point, br: Point) -> Rectangle { + Rectangle::new(tl.x(), tl.y(), br.x() - tl.x(), br.y() - tl.y()) + } + + fn contains(&self, p: &Point) -> bool { + self.x() < p.x() + && self.x() + self.width() > p.x() + && self.y() < p.y() + && self.y() + self.height() > p.y() + } +} + fn draw_piece( cr: &CairoContext, - square: &Rect2d, + square: &Rectangle, piece: Piece, crown_red: &SvgHandle, crown_white: &SvgHandle, @@ -138,7 +178,7 @@ fn on_activate(application: >k::Application) { // Get the allocation information for the widget. let board_width = SQUARE_SIZE * DraughtsBoard::rows() as f64; let board_height = SQUARE_SIZE * DraughtsBoard::columns() as f64; - let board = Rect2d::new(Point::new(0.0, 0.0), Point::new(board_width, board_height)); + let board = Rectangle::from_points(Point::new(0.0, 0.0), Point::new(board_width, board_height)); let board_clone = board.clone(); let crown_red_handle = { let stream = gio::MemoryInputStream::from_bytes(&glib::Bytes::from_static(CROWN_RED)); @@ -162,7 +202,7 @@ fn on_activate(application: >k::Application) { ) .unwrap() }; - let xform = Rc::>::new(RefCell::new(Xform::identity(3))); + let xform = Rc::>::new(RefCell::new(Matrix::identity())); // Set the "draw" function of the drawing area. This callback is called // whenever GTK needs to redraw this widget (for example, on first display or when resized). { @@ -171,27 +211,29 @@ fn on_activate(application: >k::Application) { let selected_piece = selected_piece.clone(); let board_clone = board.clone(); let available_moves = available_moves.clone(); - let get_square_for_position = move |position: &Position, xform: &Xform| -> Rect2d { + let get_square_for_position = move |position: &Position, xform: &Matrix| -> Rectangle { let square_size = SQUARE_SIZE as f64; - let square = Rect2d::new( - &board_clone.tl() - + &Point::new( - (position.col() as f64) * square_size, - ((8 - 1 - position.row()) as f64) * square_size, - ), - &board_clone.tl() - + &Point::new( - ((position.col() + 1) as f64) * square_size, - ((8 - 1 - position.row() + 1) as f64) * square_size, - ), + let p1 = Point::new( + (position.col() as f64) * square_size, + ((8 - 1 - position.row()) as f64) * square_size, ); - &square * &xform + let p2 = &p1 + &Point::new(square_size, square_size); + let square = Rectangle::from_points(&board_clone.tl() + &p1, &board_clone.tl() + &p2); + let tl = transform_point(&square.tl(), xform); + let br = transform_point(&square.br(), xform); + let result = Rectangle::new( + f64::min(tl.x(), br.x()), + f64::min(tl.y(), br.y()), + f64::abs(tl.x() - br.x()), + f64::abs(tl.y() - br.y()), + ); + result }; drawing_area .borrow_mut() .set_draw_func(move |_widget, cr, width, height| { - let screen = Rect2d::new( + let screen = Rectangle::from_points( Point::new(0.0, 0.0), Point::new(width as f64, height as f64), ); @@ -202,9 +244,13 @@ fn on_activate(application: >k::Application) { let screen_center = screen.center(); let board_center = board.center(); let mut xform = xform.borrow_mut(); - *xform = xlate(-board_center.x(), -board_center.y()) - * scale(f, f) - * xlate(screen_center.x(), screen_center.y()); + *xform = Matrix::multiply( + &Matrix::multiply( + &Matrix::new(1.0, 0.0, 0.0, 1.0, -board_center.x(), -board_center.y()), + &Matrix::new(f, 0.0, 0.0, f, 0.0, 0.0), + ), + &Matrix::new(1.0, 0.0, 0.0, 1.0, screen_center.x(), screen_center.y()), + ); // Loop over rows and columns to draw each chessboard cell. for row in 0..DraughtsBoard::rows() { @@ -300,8 +346,12 @@ fn on_activate(application: >k::Application) { gesture.connect_pressed(move |gesture, _, x, y| { gesture.set_state(gtk::EventSequenceState::Claimed); let xform = xform.borrow(); - let inverse = xform.clone().invert(); - let p = &Point::new(x, y) * &inverse; + let inverse = { + let mut m = xform.clone(); + m.invert(); + m + }; + let p = transform_point(&Point::new(x, y), &inverse); if board_clone.contains(&p) { let p = &p - &board_clone.tl(); // println!("Point: {:?}", p);