Commit f06b625998d4e914e63ec854a24b47e52357cd37
1 parent
45d1af9c
Add first simple triable animation based on fractionals
Showing
5 changed files
with
301 additions
and
2 deletions
... | ... | @@ -20,13 +20,19 @@ |
20 | 20 | // |
21 | 21 | use std::cmp; |
22 | 22 | use std::fmt::{Formatter, Display, Result}; |
23 | +use std::sync::mpsc; | |
23 | 24 | |
24 | 25 | pub trait Easel { |
25 | - fn canvas(self) -> dyn Canvas; | |
26 | + //fn canvas(&mut self, width :u16, height :u16) -> Option<&dyn Canvas>; | |
26 | 27 | } |
27 | 28 | |
28 | 29 | pub trait Canvas { |
29 | - fn draw(self, c :&dyn Drawable, ofs :Coordinate); | |
30 | + fn init_events(&self); | |
31 | + fn start_events(&self, tx :mpsc::Sender<i32>); | |
32 | + | |
33 | + fn clear(&mut self); | |
34 | + fn draw(&mut self, c :&dyn Drawable, ofs :Coordinate); | |
35 | + fn show(&self); | |
30 | 36 | } |
31 | 37 | |
32 | 38 | pub trait Drawable { | ... | ... |
... | ... | @@ -23,6 +23,9 @@ use std::f64::consts::PI as FPI; |
23 | 23 | use std::fmt::Display; |
24 | 24 | use std::num::TryFromIntError; |
25 | 25 | use std::ops::{Add,Sub,Neg,Mul,Div}; |
26 | +use std::sync::mpsc; | |
27 | +use std::time; | |
28 | +use std::thread; | |
26 | 29 | |
27 | 30 | use fractional::continuous::Continuous; |
28 | 31 | use fractional::easel::{ Coordinate, Coordinates, Drawable, Line, Polyline |
... | ... | @@ -32,6 +35,9 @@ use fractional::trigonometry::Trig; |
32 | 35 | use fractional::vector::{Vector}; |
33 | 36 | use fractional::transform::{TMatrix, translate, rotate_x, rotate_y, rotate_z, rotate_v}; |
34 | 37 | |
38 | +use fractional::xcb::XcbEasel; | |
39 | +use fractional::easel::Canvas; | |
40 | + | |
35 | 41 | fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> { |
36 | 42 | let r = v.iter().fold(0, |acc, x| acc + x); |
37 | 43 | let l = i64::try_from(v.len())?; |
... | ... | @@ -325,4 +331,54 @@ fn main() { |
325 | 331 | _transform2(); |
326 | 332 | println!(); |
327 | 333 | _line(); |
334 | + | |
335 | + let xcb = XcbEasel::new().unwrap(); | |
336 | + let mut canvas = xcb.canvas(151, 151).unwrap(); | |
337 | + | |
338 | + canvas.set_title("Something..."); | |
339 | + canvas.init_events(); | |
340 | + | |
341 | + let (tx, rx) = mpsc::channel(); | |
342 | + let tx1 = mpsc::Sender::clone(&tx); | |
343 | + canvas.start_events(tx); | |
344 | + | |
345 | + let i = Vector(Fractional( 0,1), Fractional(-35,1), Fractional(0,1)); | |
346 | + let j = Vector(Fractional( 30,1), Fractional( 17,1), Fractional(0,1)); | |
347 | + let k = Vector(Fractional(-30,1), Fractional( 17,1), Fractional(0,1)); | |
348 | + | |
349 | + fn to_i32(x :Fractional) -> i32 { | |
350 | + let Fractional(n, d) = x; | |
351 | + (n / d + if (n % d).abs() < (n / 2).abs() { 0 } else { 1 }) as i32 | |
352 | + } | |
353 | + | |
354 | + thread::spawn(move || { | |
355 | + loop { | |
356 | + tx1.send(0).unwrap(); | |
357 | + thread::sleep(time::Duration::from_millis(10)); | |
358 | + } | |
359 | + }); | |
360 | + | |
361 | + let mut deg :i32 = 0; | |
362 | + | |
363 | + for x in rx { | |
364 | + match x { | |
365 | + 1 => break, | |
366 | + _ => { | |
367 | + let rot :TMatrix<Fractional> = rotate_z(deg); | |
368 | + let Vector(ix, iy, _) = rot.apply(&i); | |
369 | + let Vector(jx, jy, _) = rot.apply(&j); | |
370 | + let Vector(kx, ky, _) = rot.apply(&k); | |
371 | + | |
372 | + let pg = Polygon( | |
373 | + Coordinates(vec!( Coordinate(to_i32(ix), to_i32(iy)) | |
374 | + , Coordinate(to_i32(jx), to_i32(jy)) | |
375 | + , Coordinate(to_i32(kx), to_i32(ky)) ))); | |
376 | + | |
377 | + canvas.clear(); | |
378 | + canvas.draw(&pg, Coordinate(75,75)); | |
379 | + canvas.show(); | |
380 | + deg = (deg + 3) % 359; | |
381 | + }, | |
382 | + } | |
383 | + } | |
328 | 384 | } | ... | ... |
fractional/src/xcb.rs
0 → 100644
1 | +// | |
2 | +// XCB implementation for drawing some stuff... | |
3 | +// | |
4 | +// Georg Hopp <georg@steffers.org> | |
5 | +// | |
6 | +// Copyright © 2019 Georg Hopp | |
7 | +// | |
8 | +// This program is free software: you can redistribute it and/or modify | |
9 | +// it under the terms of the GNU General Public License as published by | |
10 | +// the Free Software Foundation, either version 3 of the License, or | |
11 | +// (at your option) any later version. | |
12 | +// | |
13 | +// This program is distributed in the hope that it will be useful, | |
14 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | +// GNU General Public License for more details. | |
17 | +// | |
18 | +// You should have received a copy of the GNU General Public License | |
19 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | +// | |
21 | +extern crate xcb; | |
22 | + | |
23 | +use std::sync::Arc; | |
24 | +use std::ptr; //::{null, null_mut}; | |
25 | +use std::thread; | |
26 | +use std::sync::mpsc; | |
27 | + | |
28 | +use crate::easel::{Easel, Canvas, Drawable, Coordinate, Coordinates}; | |
29 | + | |
30 | +#[derive(Clone)] | |
31 | +pub struct XcbEasel (Arc<xcb::Connection>, i32); | |
32 | + | |
33 | +pub struct XcbCanvas<'a> { conn :Arc<xcb::Connection> | |
34 | + , width :u16 | |
35 | + , height :u16 | |
36 | + , window :u32 | |
37 | + , pixmap :u32 | |
38 | + , gc :u32 | |
39 | + , shm :&'a mut [u32] } | |
40 | + | |
41 | +impl XcbEasel { | |
42 | + pub fn new() -> Result<XcbEasel, xcb::ConnError> { | |
43 | + let (conn, num) = xcb::Connection::connect(None)?; | |
44 | + | |
45 | + Ok(XcbEasel(Arc::new(conn), num)) | |
46 | + } | |
47 | + | |
48 | + pub fn setup(&self) -> xcb::Setup { | |
49 | + let XcbEasel(conn, _) = self; | |
50 | + conn.get_setup() | |
51 | + } | |
52 | + | |
53 | + pub fn screen(&self) -> Option<xcb::Screen> { | |
54 | + let XcbEasel(_, num) = self; | |
55 | + self.setup().roots().nth(*num as usize) | |
56 | + } | |
57 | + | |
58 | + pub fn canvas(&self, width :u16, height :u16) -> Option<XcbCanvas> { | |
59 | + let Self(conn, _) = self; | |
60 | + let conn = conn.clone(); | |
61 | + let screen = match self.screen() { | |
62 | + None => return None, | |
63 | + Some(screen) => screen, | |
64 | + }; | |
65 | + | |
66 | + let shmseg = conn.generate_id(); | |
67 | + let gc = conn.generate_id(); | |
68 | + let pixmap = conn.generate_id(); | |
69 | + let window = conn.generate_id(); | |
70 | + | |
71 | + xcb::create_window( &conn, xcb::COPY_FROM_PARENT as u8, window | |
72 | + , screen.root(), 0, 0, width, width, 0 | |
73 | + , xcb::WINDOW_CLASS_INPUT_OUTPUT as u16 | |
74 | + , screen.root_visual() | |
75 | + , &[(xcb::CW_BACK_PIXEL, screen.white_pixel())] ); | |
76 | + | |
77 | + xcb::create_gc( &conn, gc, screen.root() | |
78 | + , &[ (xcb::GC_FOREGROUND, screen.black_pixel()) | |
79 | + , (xcb::GC_GRAPHICS_EXPOSURES, 0) ] ); | |
80 | + | |
81 | + let (shmid, shm) = getshm((width * height) as usize); | |
82 | + xcb::shm::attach(&conn, shmseg, shmid as u32, false); | |
83 | + unsafe { libc::shmctl(shmid, libc::IPC_RMID, ptr::null_mut()); } | |
84 | + | |
85 | + xcb::shm::create_pixmap( &conn, pixmap, window, width, height | |
86 | + , screen.root_depth(), shmseg, 0 ); | |
87 | + | |
88 | + xcb::map_window(&conn, window); | |
89 | + conn.flush(); | |
90 | + | |
91 | + Some(XcbCanvas{ conn: conn | |
92 | + , width: width | |
93 | + , height: height | |
94 | + , window: window | |
95 | + , pixmap: pixmap | |
96 | + , gc: gc | |
97 | + , shm: shm } ) | |
98 | + } | |
99 | +} | |
100 | + | |
101 | +impl<'a> XcbCanvas<'a> { | |
102 | + pub fn set_title(&self, title :&str) { | |
103 | + let c = xcb::change_property_checked( &self.conn | |
104 | + , xcb::PROP_MODE_REPLACE as u8 | |
105 | + , self.window | |
106 | + , xcb::ATOM_WM_NAME | |
107 | + , xcb::ATOM_STRING | |
108 | + , 8 | |
109 | + , title.as_bytes() ); | |
110 | + if self.conn.has_error().is_err() || c.request_check().is_err() { | |
111 | + println!("Error setting title"); | |
112 | + } | |
113 | + } | |
114 | +} | |
115 | + | |
116 | +fn getshm<'a>(size :usize) -> (i32, &'a mut [u32]) { | |
117 | + use std::slice::from_raw_parts_mut; | |
118 | + | |
119 | + unsafe { | |
120 | + let id = libc::shmget( libc::IPC_PRIVATE | |
121 | + , size * 4 | |
122 | + , libc::IPC_CREAT | 0o744 ); | |
123 | + let ptr = libc::shmat(id, ptr::null(), 0); | |
124 | + (id as i32, from_raw_parts_mut(ptr as *mut u32, size)) | |
125 | + } | |
126 | +} | |
127 | + | |
128 | +impl Easel for XcbEasel {} | |
129 | + | |
130 | +impl<'a> Canvas for XcbCanvas<'a> { | |
131 | + fn init_events(&self) { | |
132 | + let mask = [( xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | |
133 | + | xcb::EVENT_MASK_KEY_PRESS | |
134 | + | xcb::EVENT_MASK_STRUCTURE_NOTIFY | |
135 | + | xcb::EVENT_MASK_PROPERTY_CHANGE )]; | |
136 | + xcb::change_window_attributes(&self.conn, self.window, &mask); | |
137 | + self.conn.flush(); | |
138 | + } | |
139 | + | |
140 | + fn start_events(&self, tx :mpsc::Sender<i32>) { | |
141 | + let conn = self.conn.clone(); | |
142 | + let window = self.window; | |
143 | + let pixmap = self.pixmap; | |
144 | + let gc = self.gc; | |
145 | + let width = self.width; | |
146 | + let height = self.height; | |
147 | + | |
148 | + thread::spawn(move || { | |
149 | + loop { | |
150 | + let event = conn.wait_for_event(); | |
151 | + | |
152 | + match event { | |
153 | + None => break, | |
154 | + Some(event) => { | |
155 | + match event.response_type() & !0x80 { | |
156 | + xcb::PROPERTY_NOTIFY => { | |
157 | + let prop_notify :&xcb::PropertyNotifyEvent | |
158 | + = unsafe { xcb::cast_event(&event) }; | |
159 | + | |
160 | + if prop_notify.atom() == xcb::ATOM_WM_NAME { | |
161 | + // retrieving title | |
162 | + let cookie | |
163 | + = xcb::get_property( &conn | |
164 | + , false | |
165 | + , window | |
166 | + , xcb::ATOM_WM_NAME | |
167 | + , xcb::ATOM_STRING | |
168 | + , 0, 1024 ); | |
169 | + | |
170 | + if let Ok(reply) = cookie.get_reply() { | |
171 | + let r = reply.value(); | |
172 | + let r = std::str::from_utf8(r).unwrap(); | |
173 | + | |
174 | + println!("title changed to: {}", r); | |
175 | + } | |
176 | + } | |
177 | + }, | |
178 | + | |
179 | + xcb::EXPOSE => { | |
180 | + xcb::copy_area( &conn, pixmap, window, gc | |
181 | + , 0, 0, 0, 0 | |
182 | + , width, height ); | |
183 | + conn.flush(); | |
184 | + }, | |
185 | + | |
186 | + xcb::KEY_PRESS => { | |
187 | + let key_press: &xcb::KeyPressEvent | |
188 | + = unsafe { xcb::cast_event(&event) }; | |
189 | + | |
190 | + println!( "Key '{}' pressed" | |
191 | + , key_press.detail() ); | |
192 | + | |
193 | + // Q (on qwerty) | |
194 | + if key_press.detail() == 0x18 { | |
195 | + tx.send(1).unwrap(); | |
196 | + break; | |
197 | + } | |
198 | + }, | |
199 | + | |
200 | + _ => {}, | |
201 | + } | |
202 | + }, | |
203 | + } | |
204 | + } | |
205 | + }); | |
206 | + } | |
207 | + | |
208 | + fn clear(&mut self) { | |
209 | + unsafe { | |
210 | + let ptr = self.shm.as_mut_ptr(); | |
211 | + ptr::write_bytes( ptr, 0 | |
212 | + , self.width as usize * self.height as usize); | |
213 | + } | |
214 | + } | |
215 | + | |
216 | + fn draw(&mut self, d :&dyn Drawable, ofs :Coordinate) { | |
217 | + let Coordinates(c) = d.plot(); | |
218 | + let Coordinate(xofs, yofs) = ofs; | |
219 | + | |
220 | + for Coordinate(x, y) in c { | |
221 | + let idx :usize = ((y+yofs)*(self.width as i32)+x+xofs) as usize; | |
222 | + self.shm[idx] = 0xFFFFFF; | |
223 | + } | |
224 | + } | |
225 | + | |
226 | + fn show(&self) { | |
227 | + xcb::copy_area( &self.conn, self.pixmap, self.window, self.gc | |
228 | + , 0, 0, 0, 0 | |
229 | + , self.width, self.height ); | |
230 | + self.conn.flush(); | |
231 | + } | |
232 | +} | ... | ... |
Please
register
or
login
to post a comment