Struct TextCanvas

Source
pub struct TextCanvas {
    pub output: Surface,
    pub screen: Surface,
    pub buffer: PixelBuffer,
    pub color_buffer: ColorBuffer,
    pub text_buffer: TextBuffer,
    pub is_inverted: bool,
    /* private fields */
}
Expand description

Draw to the terminal like an HTML Canvas.

§Examples

use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

assert_eq!(
    canvas.repr(),
    "Canvas(output=(15×5), screen=(30×20)))"
);

assert_eq!(
    (canvas.w(), canvas.h(), canvas.cx(), canvas.cy()),
    (29, 19, 15, 10),
);

canvas.stroke_line(0, 0, canvas.w(), canvas.h());
canvas.draw_text("hello, world", 1, 2);
assert_eq!(
    canvas.to_string(),
    "\
⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀hello,⠢world⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄
"
);

Fields§

§output: Surface

Properties of the output surface, whose size is given as parameter to the constructor. One unit in width and in height represents exactly one character on the terminal.

§screen: Surface

Properties of the virtual output surface. This surface benefits from the increase in resolution. One unit in width and in height represents one Braille dot. There are 2 dots per character in width, and 4 per character in height. So this surface is 2× wider and 4× higher than the output surface.

§buffer: PixelBuffer

The in-memory pixel buffer. This maps 1-to-1 to the virtual screen. Each pixel is this buffer is either on or off.

§color_buffer: ColorBuffer

The in-memory color buffer. This maps 1-to-1 to the output buffer (and so to the physical screen). This contains color data for characters. Screen dots, being part of one output character, cannot be colored individually. Color is thus less precise than screen pixels. Note that a call to set_color() is valid for both pixels and text, but text embeds color in its own buffer, and does not use this buffer at all. Note also that this buffer is empty until the first call to set_color(). The first call to set_color() initializes the buffer and sets is_colorized() to true.

§text_buffer: TextBuffer

The in-memory text buffer. This maps 1-to-1 to the output buffer (and so to the physical screen). This contains regular text characters. Text is drawn on top of the pixel buffer on a separate layer. Drawing text does not affect pixels. Pixels and text do not share the same color buffer either. Color info is embedded in the text buffer with each character directly. Note also that this buffer is empty until the first call to draw_text(). The first call to draw_text() initializes the buffer and sets is_textual() to True.

§is_inverted: bool

Inverted drawing mode. In inverted mode, functions which usually turn pixels on, will turn them off, and vice-versa.

Implementations§

Source§

impl TextCanvas

Source

pub fn new(width: i32, height: i32) -> Self

Create new TextCanvas.

§Panics

If width and height of canvas are < 1×1.

Also, if the pixel resolution of width or height is larger than 65_535 (realistically, should not happen). This is because we let the user interact with the canvas with i32s. i32 is a more likely user-type (maths, etc.), and allows for negative values. Even though they are never displayed, allowing negative values makes for a nicer API. We handle the complexity for the user.

Examples found in repository?
examples/sines.rs (line 56)
55fn with_fixed_time_step() {
56    let mut canvas = TextCanvas::new(80, 16);
57
58    let mut big_sine = TextCanvas::new(80, 10);
59    let mut medium_sine = TextCanvas::new(80, 6);
60    let mut small_sine = TextCanvas::new(80, 4);
61
62    let f = |x: f64| x.sin();
63
64    let mut i = 0;
65    #[rustfmt::skip]
66    GameLoop::loop_fixed(time::Duration::from_millis(30), &mut || {
67        if i == 108 {
68            // Break out of the loop.
69            return None;
70        }
71
72        let big_offset = f64::from(i) / 350.0 * 37.0;
73        let medium_offset = f64::from(i) / 250.0 * 37.0;
74        let small_offset = f64::from(i) / 150.0 * 37.0;
75
76        canvas.clear();
77
78        big_sine.clear();
79        medium_sine.clear();
80        small_sine.clear();
81
82        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
83        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
84        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
85
86        canvas.merge_canvas(&big_sine, 0, 12);
87        canvas.merge_canvas(&medium_sine, 0, 20);
88        canvas.merge_canvas(&small_sine, 0, 25);
89
90        canvas.draw_text(&format!("  i: {i}"), 0, 0);
91        canvas.draw_text("Fixed", 37, 0);
92
93        i += 1;
94
95        Some(canvas.to_string())
96    });
97}
98
99/// Will loop as fast as possible.
100fn with_variable_time_step() {
101    let mut canvas = TextCanvas::new(80, 16);
102
103    let mut big_sine = TextCanvas::new(80, 10);
104    let mut medium_sine = TextCanvas::new(80, 6);
105    let mut small_sine = TextCanvas::new(80, 4);
106
107    let f = |x: f64| x.sin();
108
109    let mut accumulator = 0.0;
110    #[rustfmt::skip]
111    GameLoop::loop_variable(&mut |delta_time| {
112        if accumulator >= 333.33 {
113            // Break out of the loop.
114            return None;
115        }
116
117        let big_offset = accumulator / 350.0 * 12.0;
118        let medium_offset = accumulator / 250.0 * 12.0;
119        let small_offset = accumulator / 150.0 * 12.0;
120
121        canvas.clear();
122
123        big_sine.clear();
124        medium_sine.clear();
125        small_sine.clear();
126
127        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
128        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
129        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
130
131        canvas.merge_canvas(&big_sine, 0, 12);
132        canvas.merge_canvas(&medium_sine, 0, 20);
133        canvas.merge_canvas(&small_sine, 0, 25);
134
135        canvas.draw_text(&format!(" Δt: {delta_time:.9}"), 0, 0);
136        canvas.draw_text(&format!("acc: {accumulator:>11.7}"), 0, 1);
137        canvas.draw_text("Variable", 36, 0);
138
139        accumulator += 100.0 * delta_time;
140
141        Some(canvas.to_string())
142    });
143}
More examples
Hide additional examples
examples/cube.rs (line 53)
52fn main() {
53    let mut canvas = TextCanvas::new(CANVAS_WIDTH as i32, CANVAS_HEIGHT as i32);
54
55    let camera = Camera::new();
56    let mut cube = Cube::new();
57
58    GameLoop::loop_variable(&mut |delta_time| {
59        canvas.clear();
60
61        let rotate = 1.0 * delta_time;
62        cube.rotate(rotate, rotate, rotate);
63
64        let mut model = camera.project_cube(&cube);
65        for face in &mut model {
66            let mut first: Option<Vec2D> = None;
67            let mut previous: Option<Vec2D> = None;
68            for vertex in face {
69                let [x, y, z] = *vertex;
70
71                let [x, y] = world_to_screen([x, y, z]);
72                *vertex = [x, y, 0.0]; // `z` gets ditched.
73
74                if let Some(previous) = previous {
75                    canvas.stroke_line(
76                        previous[0].trunc() as i32,
77                        previous[1].trunc() as i32,
78                        x.trunc() as i32,
79                        y.trunc() as i32,
80                    );
81                }
82
83                if first.is_none() {
84                    first = Some([x, y]);
85                }
86
87                previous = Some([x, y]);
88            }
89
90            // Close.
91            if let (Some(first), Some(previous)) = (first, previous) {
92                canvas.stroke_line(
93                    previous[0].trunc() as i32,
94                    previous[1].trunc() as i32,
95                    first[0].trunc() as i32,
96                    first[1].trunc() as i32,
97                );
98            }
99        }
100
101        // Don't eat up the whole CPU.
102        std::thread::sleep(std::time::Duration::from_millis(7));
103
104        Some(canvas.to_string())
105    });
106}
Source

pub fn new_auto() -> Result<Self, TextCanvasError>

Create new TextCanvas by reading size from environment.

§Errors

If either or both WIDTH and HEIGHT variables cannot be read from the environment.

Source

pub fn get_default_size() -> (i32, i32)

Default canvas size.

This value is used by TextCanvas::default(), but it may be useful to query it separately.

Source

pub fn get_auto_size() -> Result<(i32, i32), TextCanvasError>

Read canvas size from WIDTH and HEIGHT env variables.

This value is used by TextCanvas::new_auto(), but it may be useful to query it separately.

§Errors

If either or both WIDTH and HEIGHT variables cannot be read from the environment.

Source

pub fn repr(&self) -> String

High-level string representation of the canvas.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

assert_eq!(
    canvas.repr(),
    "Canvas(output=(15×5), screen=(30×20)))"
);
Source

pub fn w(&self) -> i32

Shortcut for width of pixel screen (index of last column).

Source

pub fn uw(&self) -> usize

Shortcut for width of pixel screen (index of last column).

Source

pub fn fw(&self) -> f64

Shortcut for width of pixel screen (index of last column).

Source

pub fn h(&self) -> i32

Shortcut for height of pixel screen (index of last row).

Source

pub fn uh(&self) -> usize

Shortcut for height of pixel screen (index of last row).

Source

pub fn fh(&self) -> f64

Shortcut for height of pixel screen (index of last row).

Source

pub fn cx(&self) -> i32

Shortcut for center-X of pixel screen.

Source

pub fn ucx(&self) -> usize

Shortcut for center-X of pixel screen.

Source

pub fn fcx(&self) -> f64

Shortcut for center-X of pixel screen.

Source

pub fn cy(&self) -> i32

Shortcut for center-Y of pixel screen.

Source

pub fn ucy(&self) -> usize

Shortcut for center-Y of pixel screen.

Source

pub fn fcy(&self) -> f64

Shortcut for center-Y of pixel screen.

Source

pub fn clear(&mut self)

Turn all pixels off and remove color and text.

Note: This method does not drop the color and text buffers, it only clears them. No memory is freed, and all references remain valid (buffers are cleared in-place, not replaced).

Note: clear() is not affected by inverted mode, it works on a lower level.

Examples found in repository?
examples/sines.rs (line 76)
55fn with_fixed_time_step() {
56    let mut canvas = TextCanvas::new(80, 16);
57
58    let mut big_sine = TextCanvas::new(80, 10);
59    let mut medium_sine = TextCanvas::new(80, 6);
60    let mut small_sine = TextCanvas::new(80, 4);
61
62    let f = |x: f64| x.sin();
63
64    let mut i = 0;
65    #[rustfmt::skip]
66    GameLoop::loop_fixed(time::Duration::from_millis(30), &mut || {
67        if i == 108 {
68            // Break out of the loop.
69            return None;
70        }
71
72        let big_offset = f64::from(i) / 350.0 * 37.0;
73        let medium_offset = f64::from(i) / 250.0 * 37.0;
74        let small_offset = f64::from(i) / 150.0 * 37.0;
75
76        canvas.clear();
77
78        big_sine.clear();
79        medium_sine.clear();
80        small_sine.clear();
81
82        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
83        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
84        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
85
86        canvas.merge_canvas(&big_sine, 0, 12);
87        canvas.merge_canvas(&medium_sine, 0, 20);
88        canvas.merge_canvas(&small_sine, 0, 25);
89
90        canvas.draw_text(&format!("  i: {i}"), 0, 0);
91        canvas.draw_text("Fixed", 37, 0);
92
93        i += 1;
94
95        Some(canvas.to_string())
96    });
97}
98
99/// Will loop as fast as possible.
100fn with_variable_time_step() {
101    let mut canvas = TextCanvas::new(80, 16);
102
103    let mut big_sine = TextCanvas::new(80, 10);
104    let mut medium_sine = TextCanvas::new(80, 6);
105    let mut small_sine = TextCanvas::new(80, 4);
106
107    let f = |x: f64| x.sin();
108
109    let mut accumulator = 0.0;
110    #[rustfmt::skip]
111    GameLoop::loop_variable(&mut |delta_time| {
112        if accumulator >= 333.33 {
113            // Break out of the loop.
114            return None;
115        }
116
117        let big_offset = accumulator / 350.0 * 12.0;
118        let medium_offset = accumulator / 250.0 * 12.0;
119        let small_offset = accumulator / 150.0 * 12.0;
120
121        canvas.clear();
122
123        big_sine.clear();
124        medium_sine.clear();
125        small_sine.clear();
126
127        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
128        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
129        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
130
131        canvas.merge_canvas(&big_sine, 0, 12);
132        canvas.merge_canvas(&medium_sine, 0, 20);
133        canvas.merge_canvas(&small_sine, 0, 25);
134
135        canvas.draw_text(&format!(" Δt: {delta_time:.9}"), 0, 0);
136        canvas.draw_text(&format!("acc: {accumulator:>11.7}"), 0, 1);
137        canvas.draw_text("Variable", 36, 0);
138
139        accumulator += 100.0 * delta_time;
140
141        Some(canvas.to_string())
142    });
143}
More examples
Hide additional examples
examples/cube.rs (line 59)
52fn main() {
53    let mut canvas = TextCanvas::new(CANVAS_WIDTH as i32, CANVAS_HEIGHT as i32);
54
55    let camera = Camera::new();
56    let mut cube = Cube::new();
57
58    GameLoop::loop_variable(&mut |delta_time| {
59        canvas.clear();
60
61        let rotate = 1.0 * delta_time;
62        cube.rotate(rotate, rotate, rotate);
63
64        let mut model = camera.project_cube(&cube);
65        for face in &mut model {
66            let mut first: Option<Vec2D> = None;
67            let mut previous: Option<Vec2D> = None;
68            for vertex in face {
69                let [x, y, z] = *vertex;
70
71                let [x, y] = world_to_screen([x, y, z]);
72                *vertex = [x, y, 0.0]; // `z` gets ditched.
73
74                if let Some(previous) = previous {
75                    canvas.stroke_line(
76                        previous[0].trunc() as i32,
77                        previous[1].trunc() as i32,
78                        x.trunc() as i32,
79                        y.trunc() as i32,
80                    );
81                }
82
83                if first.is_none() {
84                    first = Some([x, y]);
85                }
86
87                previous = Some([x, y]);
88            }
89
90            // Close.
91            if let (Some(first), Some(previous)) = (first, previous) {
92                canvas.stroke_line(
93                    previous[0].trunc() as i32,
94                    previous[1].trunc() as i32,
95                    first[0].trunc() as i32,
96                    first[1].trunc() as i32,
97                );
98            }
99        }
100
101        // Don't eat up the whole CPU.
102        std::thread::sleep(std::time::Duration::from_millis(7));
103
104        Some(canvas.to_string())
105    });
106}
Source

pub fn fill(&mut self)

Turn all pixels on.

This does not affect the color and text buffers.

Note: fill() is not affected by inverted mode, it works on a lower level.

Source

pub fn invert(&mut self)

Invert drawing mode.

In inverted mode, functions that usually turn pixels on, will turn them off, and vice versa. This can be used to cut out shapes for instance.

Source

pub fn is_colorized(&self) -> bool

Whether the canvas can contain colors.

Note: This does not mean that any colors are displayed. This only means the color buffer is active.

§Examples
use textcanvas::{Color, TextCanvas};

let mut canvas = TextCanvas::new(15, 5);

assert!(!canvas.is_colorized());

canvas.set_color(&Color::new().fix());  // Buffer is initialized.
assert!(canvas.is_colorized());
Source

pub fn is_textual(&self) -> bool

Whether the canvas can contain text.

Note: This does not mean that any text is displayed. This only means the text buffer is active.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

assert!(!canvas.is_textual());

canvas.draw_text("", 0, 0);  // Buffer is initialized.
assert!(canvas.is_textual());
Source

pub fn set_color(&mut self, color: &Color)

Set context color.

§Examples
use textcanvas::{Color, TextCanvas};

let mut canvas = TextCanvas::new(3, 1);
let green = Color::new().bright_green().fix();

canvas.set_color(&green);
assert!(canvas.is_colorized());

canvas.draw_text("foo", 0, 0);
assert_eq!(
    canvas.to_string(),
    "\x1b[0;92mf\x1b[0m\x1b[0;92mo\x1b[0m\x1b[0;92mo\x1b[0m\n"
);
Source

pub fn get_pixel(&self, x: i32, y: i32) -> Option<bool>

Get the state of a screen pixel.

Some(true) if the pixel is turned on, Some(false) if it is turned off, and None if the coordinates are outside the bounds of the buffer.

§Arguments
  • x - Screen X (high resolution).
  • y - Screen Y (high resolution).
Source

pub fn set_pixel(&mut self, x: i32, y: i32, state: bool)

Set the state of a screen pixel.

Note: Coordinates outside the screen bounds are ignored.

Note: Turning a pixel off also removes color. This side effect does not affect text, as text has a separate color buffer.

§Arguments
  • x - Screen X (high resolution).
  • y - Screen Y (high resolution).
  • state - true means on, false means off.
Source

pub fn draw_text(&mut self, text: &str, x: i32, y: i32)

Draw text onto the canvas.

Note: Spaces are transparent (you see pixels through). But drawing spaces over text erases the text beneath. If you want to keep the text, use the merge_text() method.

Note: Coordinates outside the screen bounds are ignored.

Note: Text is rendered on top of pixels, as a separate layer.

Note: set_color() works for text as well, but text does not share its color buffer with pixels.

Examples found in repository?
examples/sines.rs (line 90)
55fn with_fixed_time_step() {
56    let mut canvas = TextCanvas::new(80, 16);
57
58    let mut big_sine = TextCanvas::new(80, 10);
59    let mut medium_sine = TextCanvas::new(80, 6);
60    let mut small_sine = TextCanvas::new(80, 4);
61
62    let f = |x: f64| x.sin();
63
64    let mut i = 0;
65    #[rustfmt::skip]
66    GameLoop::loop_fixed(time::Duration::from_millis(30), &mut || {
67        if i == 108 {
68            // Break out of the loop.
69            return None;
70        }
71
72        let big_offset = f64::from(i) / 350.0 * 37.0;
73        let medium_offset = f64::from(i) / 250.0 * 37.0;
74        let small_offset = f64::from(i) / 150.0 * 37.0;
75
76        canvas.clear();
77
78        big_sine.clear();
79        medium_sine.clear();
80        small_sine.clear();
81
82        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
83        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
84        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
85
86        canvas.merge_canvas(&big_sine, 0, 12);
87        canvas.merge_canvas(&medium_sine, 0, 20);
88        canvas.merge_canvas(&small_sine, 0, 25);
89
90        canvas.draw_text(&format!("  i: {i}"), 0, 0);
91        canvas.draw_text("Fixed", 37, 0);
92
93        i += 1;
94
95        Some(canvas.to_string())
96    });
97}
98
99/// Will loop as fast as possible.
100fn with_variable_time_step() {
101    let mut canvas = TextCanvas::new(80, 16);
102
103    let mut big_sine = TextCanvas::new(80, 10);
104    let mut medium_sine = TextCanvas::new(80, 6);
105    let mut small_sine = TextCanvas::new(80, 4);
106
107    let f = |x: f64| x.sin();
108
109    let mut accumulator = 0.0;
110    #[rustfmt::skip]
111    GameLoop::loop_variable(&mut |delta_time| {
112        if accumulator >= 333.33 {
113            // Break out of the loop.
114            return None;
115        }
116
117        let big_offset = accumulator / 350.0 * 12.0;
118        let medium_offset = accumulator / 250.0 * 12.0;
119        let small_offset = accumulator / 150.0 * 12.0;
120
121        canvas.clear();
122
123        big_sine.clear();
124        medium_sine.clear();
125        small_sine.clear();
126
127        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
128        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
129        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
130
131        canvas.merge_canvas(&big_sine, 0, 12);
132        canvas.merge_canvas(&medium_sine, 0, 20);
133        canvas.merge_canvas(&small_sine, 0, 25);
134
135        canvas.draw_text(&format!(" Δt: {delta_time:.9}"), 0, 0);
136        canvas.draw_text(&format!("acc: {accumulator:>11.7}"), 0, 1);
137        canvas.draw_text("Variable", 36, 0);
138
139        accumulator += 100.0 * delta_time;
140
141        Some(canvas.to_string())
142    });
143}
Source

pub fn draw_text_vertical(&mut self, text: &str, x: i32, y: i32)

Source

pub fn merge_text(&mut self, text: &str, x: i32, y: i32)

Merge text onto the canvas.

This is the same as draw_text(), but spaces do not erase text underneath.

Source

pub fn merge_text_vertical(&mut self, text: &str, x: i32, y: i32)

Source

pub fn iter_buffer(&self) -> IterPixelBuffer<i32>

Iterate over all cells of the pixel buffer.

Yields all X/Y coordinates, left-right, top-bottom.

Values are i32, this is meant to be used with the public API.

Source

pub fn uiter_buffer(&self) -> IterPixelBuffer<usize>

Iterate over all cells of the pixel buffer.

Yields all X/Y coordinates, left-right, top-bottom.

Values are usize, this is meant to index directly into the buffer.

Source§

impl TextCanvas

Implementation of drawing primitives.

Source

pub fn stroke_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32)

Stroke line.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.stroke_line(5, 5, 25, 15);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠐⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠉⠒⠤⣀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠒⠤⣀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Examples found in repository?
examples/cube.rs (lines 75-80)
52fn main() {
53    let mut canvas = TextCanvas::new(CANVAS_WIDTH as i32, CANVAS_HEIGHT as i32);
54
55    let camera = Camera::new();
56    let mut cube = Cube::new();
57
58    GameLoop::loop_variable(&mut |delta_time| {
59        canvas.clear();
60
61        let rotate = 1.0 * delta_time;
62        cube.rotate(rotate, rotate, rotate);
63
64        let mut model = camera.project_cube(&cube);
65        for face in &mut model {
66            let mut first: Option<Vec2D> = None;
67            let mut previous: Option<Vec2D> = None;
68            for vertex in face {
69                let [x, y, z] = *vertex;
70
71                let [x, y] = world_to_screen([x, y, z]);
72                *vertex = [x, y, 0.0]; // `z` gets ditched.
73
74                if let Some(previous) = previous {
75                    canvas.stroke_line(
76                        previous[0].trunc() as i32,
77                        previous[1].trunc() as i32,
78                        x.trunc() as i32,
79                        y.trunc() as i32,
80                    );
81                }
82
83                if first.is_none() {
84                    first = Some([x, y]);
85                }
86
87                previous = Some([x, y]);
88            }
89
90            // Close.
91            if let (Some(first), Some(previous)) = (first, previous) {
92                canvas.stroke_line(
93                    previous[0].trunc() as i32,
94                    previous[1].trunc() as i32,
95                    first[0].trunc() as i32,
96                    first[1].trunc() as i32,
97                );
98            }
99        }
100
101        // Don't eat up the whole CPU.
102        std::thread::sleep(std::time::Duration::from_millis(7));
103
104        Some(canvas.to_string())
105    });
106}
Source

pub fn stroke_rect(&mut self, x: i32, y: i32, width: i32, height: i32)

Stroke rectangle.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.stroke_rect(5, 5, 20, 10);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⠒⠒⠒⠒⠒⠒⠒⠒⠒⡆⠀⠀
⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀
⠀⠀⠸⠤⠤⠤⠤⠤⠤⠤⠤⠤⠇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Source

pub fn frame(&mut self)

Draw a border around the canvas.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.frame();

assert_eq!(
    canvas.to_string(),
    "\
⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸
"
);
Source

pub fn fill_rect(&mut self, x: i32, y: i32, width: i32, height: i32)

Fill rectangle.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.fill_rect(5, 5, 20, 10);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⣶⣶⣶⣶⣶⣶⣶⣶⣶⡆⠀⠀
⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀
⠀⠀⠸⠿⠿⠿⠿⠿⠿⠿⠿⠿⠇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Source

pub fn stroke_triangle( &mut self, x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32, )

Stroke triangle.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.stroke_triangle(5, 5, 20, 10, 4, 17);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⠢⠤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢸⠀⠀⠀⠈⠉⢒⡢⠄⠀⠀⠀⠀
⠀⠀⡇⠀⣀⠤⠔⠊⠁⠀⠀⠀⠀⠀⠀
⠀⠀⠓⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Source

pub fn fill_triangle( &mut self, x1: i32, y1: i32, x2: i32, y2: i32, x3: i32, y3: i32, )

Fill triangle.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.fill_triangle(5, 5, 20, 10, 4, 17);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⣦⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢸⣿⣿⣿⣿⣿⣶⡦⠄⠀⠀⠀⠀
⠀⠀⣿⣿⣿⠿⠟⠋⠁⠀⠀⠀⠀⠀⠀
⠀⠀⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Source

pub fn stroke_circle(&mut self, x: i32, y: i32, radius: i32)

Stroke circle.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.stroke_circle(canvas.cx(), canvas.cy(), 7);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⣀⣀⣀⡀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⡠⠊⠀⠀⠀⠈⠢⡀⠀⠀⠀
⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀
⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⡠⠃⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠒⠒⠒⠊⠀⠀⠀⠀⠀
"
);
Source

pub fn fill_circle(&mut self, x: i32, y: i32, radius: i32)

Fill circle.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.fill_circle(canvas.cx(), canvas.cy(), 7);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⣀⣀⣀⡀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣦⡀⠀⠀⠀
⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀
⠀⠀⠀⠀⠻⣿⣿⣿⣿⣿⡿⠃⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠛⠛⠛⠋⠀⠀⠀⠀⠀
"
);
Source

pub fn stroke_ngon( &mut self, x: i32, y: i32, radius: i32, sides: i32, angle: f64, )

Stroke n-gon.

§Examples
use textcanvas::TextCanvas;
use std::f64::consts::PI;

let mut canvas = TextCanvas::new(15, 5);

canvas.stroke_ngon(canvas.cx(), canvas.cy(), 7, 5, PI / 2.0);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢀⡠⠊⠁⠉⠢⣀⠀⠀⠀⠀
⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀
⠀⠀⠀⠀⠀⢇⠀⠀⠀⢀⠎⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠀⠀⠀⠀⠀
"
);
§Panics

Panics if sides < 3.

Source

pub fn fill_ngon(&mut self, x: i32, y: i32, radius: i32, sides: i32, angle: f64)

Fill n-gon.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);

canvas.fill_ngon(canvas.cx(), canvas.cy(), 7, 4, 0.0);

assert_eq!(
    canvas.to_string(),
    "\
⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢀⣴⣿⣷⣄⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢴⣿⣿⣿⣿⣿⣷⠄⠀⠀⠀
⠀⠀⠀⠀⠀⠙⢿⣿⣿⠟⠁⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠙⠁⠀⠀⠀⠀⠀⠀
"
);
§Panics

Panics if sides < 3.

Source

pub fn draw_canvas(&mut self, canvas: &Self, dx: i32, dy: i32)

Draw another canvas onto the current canvas.

The other canvas completely overrides the current canvas where it is drawn (but it does not affect the portions where it is not drawn).

Note: Inverted mode has no effect here, this is a low level copy-paste.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);
canvas.stroke_line(0, 0, canvas.w(), canvas.h());
canvas.stroke_line(0, canvas.h(), canvas.w(), 0);

let mut overlay = TextCanvas::new(7, 3);
overlay.frame();

canvas.draw_canvas(&overlay, 8, 4);

assert_eq!(
    canvas.to_string(),
    "\
⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠊
⠀⠀⠀⠑⡏⠉⠉⠉⠉⠉⢹⠊⠀⠀⠀
⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀
⠀⠀⠀⡠⣇⣀⣀⣀⣀⣀⣸⢄⠀⠀⠀
⡠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄
"
);
Source

pub fn merge_canvas(&mut self, canvas: &Self, dx: i32, dy: i32)

Merge another canvas with the current canvas.

The other canvas is merged with the current canvas. That is, pixels that are turned on get draw, but those that are off are ignored.

Note: Inverted mode has no effect here, this is a low level copy-paste.

§Examples
use textcanvas::TextCanvas;

let mut canvas = TextCanvas::new(15, 5);
canvas.stroke_line(0, 0, canvas.w(), canvas.h());
canvas.stroke_line(0, canvas.h(), canvas.w(), 0);

let mut overlay = TextCanvas::new(7, 3);
overlay.frame();

canvas.merge_canvas(&overlay, 8, 4);

assert_eq!(
    canvas.to_string(),
    "\
⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠊
⠀⠀⠀⠑⡯⣉⠉⠉⠉⣉⢽⠊⠀⠀⠀
⠀⠀⠀⠀⡇⠀⡱⠶⢎⠀⢸⠀⠀⠀⠀
⠀⠀⠀⡠⣗⣉⣀⣀⣀⣉⣺⢄⠀⠀⠀
⡠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄
"
);
Examples found in repository?
examples/sines.rs (line 86)
55fn with_fixed_time_step() {
56    let mut canvas = TextCanvas::new(80, 16);
57
58    let mut big_sine = TextCanvas::new(80, 10);
59    let mut medium_sine = TextCanvas::new(80, 6);
60    let mut small_sine = TextCanvas::new(80, 4);
61
62    let f = |x: f64| x.sin();
63
64    let mut i = 0;
65    #[rustfmt::skip]
66    GameLoop::loop_fixed(time::Duration::from_millis(30), &mut || {
67        if i == 108 {
68            // Break out of the loop.
69            return None;
70        }
71
72        let big_offset = f64::from(i) / 350.0 * 37.0;
73        let medium_offset = f64::from(i) / 250.0 * 37.0;
74        let small_offset = f64::from(i) / 150.0 * 37.0;
75
76        canvas.clear();
77
78        big_sine.clear();
79        medium_sine.clear();
80        small_sine.clear();
81
82        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
83        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
84        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
85
86        canvas.merge_canvas(&big_sine, 0, 12);
87        canvas.merge_canvas(&medium_sine, 0, 20);
88        canvas.merge_canvas(&small_sine, 0, 25);
89
90        canvas.draw_text(&format!("  i: {i}"), 0, 0);
91        canvas.draw_text("Fixed", 37, 0);
92
93        i += 1;
94
95        Some(canvas.to_string())
96    });
97}
98
99/// Will loop as fast as possible.
100fn with_variable_time_step() {
101    let mut canvas = TextCanvas::new(80, 16);
102
103    let mut big_sine = TextCanvas::new(80, 10);
104    let mut medium_sine = TextCanvas::new(80, 6);
105    let mut small_sine = TextCanvas::new(80, 4);
106
107    let f = |x: f64| x.sin();
108
109    let mut accumulator = 0.0;
110    #[rustfmt::skip]
111    GameLoop::loop_variable(&mut |delta_time| {
112        if accumulator >= 333.33 {
113            // Break out of the loop.
114            return None;
115        }
116
117        let big_offset = accumulator / 350.0 * 12.0;
118        let medium_offset = accumulator / 250.0 * 12.0;
119        let small_offset = accumulator / 150.0 * 12.0;
120
121        canvas.clear();
122
123        big_sine.clear();
124        medium_sine.clear();
125        small_sine.clear();
126
127        Plot::function(&mut big_sine, -10.0 + big_offset, 10.0 + big_offset, &f);
128        Plot::function(&mut medium_sine, -10.0 + medium_offset, 10.0 + medium_offset, &f);
129        Plot::function(&mut small_sine, -10.0 + small_offset, 10.0 + small_offset, &f);
130
131        canvas.merge_canvas(&big_sine, 0, 12);
132        canvas.merge_canvas(&medium_sine, 0, 20);
133        canvas.merge_canvas(&small_sine, 0, 25);
134
135        canvas.draw_text(&format!(" Δt: {delta_time:.9}"), 0, 0);
136        canvas.draw_text(&format!("acc: {accumulator:>11.7}"), 0, 1);
137        canvas.draw_text("Variable", 36, 0);
138
139        accumulator += 100.0 * delta_time;
140
141        Some(canvas.to_string())
142    });
143}

Trait Implementations§

Source§

impl Debug for TextCanvas

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TextCanvas

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Display for TextCanvas

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.