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
impl TextCanvas
Sourcepub fn new(width: i32, height: i32) -> Self
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 i32
s. 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?
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
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}
Sourcepub fn new_auto() -> Result<Self, TextCanvasError>
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.
Sourcepub fn get_default_size() -> (i32, i32)
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.
Sourcepub fn get_auto_size() -> Result<(i32, i32), TextCanvasError>
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.
Sourcepub fn repr(&self) -> String
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)))"
);
Sourcepub fn clear(&mut self)
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?
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
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}
Sourcepub fn fill(&mut self)
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.
Sourcepub fn invert(&mut self)
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.
Sourcepub fn is_colorized(&self) -> bool
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());
Sourcepub fn is_textual(&self) -> bool
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());
Sourcepub fn set_color(&mut self, color: &Color)
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"
);
Sourcepub fn get_pixel(&self, x: i32, y: i32) -> Option<bool>
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).
Sourcepub fn set_pixel(&mut self, x: i32, y: i32, state: bool)
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.
Sourcepub fn draw_text(&mut self, text: &str, x: i32, y: i32)
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?
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}
pub fn draw_text_vertical(&mut self, text: &str, x: i32, y: i32)
Sourcepub fn merge_text(&mut self, text: &str, x: i32, y: i32)
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.
pub fn merge_text_vertical(&mut self, text: &str, x: i32, y: i32)
Sourcepub fn iter_buffer(&self) -> IterPixelBuffer<i32> ⓘ
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.
Sourcepub fn uiter_buffer(&self) -> IterPixelBuffer<usize> ⓘ
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.
impl TextCanvas
Implementation of drawing primitives.
Sourcepub fn stroke_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32)
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?
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}
Sourcepub fn stroke_rect(&mut self, x: i32, y: i32, width: i32, height: i32)
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(),
"\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⠒⠒⠒⠒⠒⠒⠒⠒⠒⡆⠀⠀
⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀
⠀⠀⠸⠤⠤⠤⠤⠤⠤⠤⠤⠤⠇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Sourcepub fn frame(&mut self)
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(),
"\
⡏⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢹
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸
⣇⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣸
"
);
Sourcepub fn fill_rect(&mut self, x: i32, y: i32, width: i32, height: i32)
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(),
"\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⣶⣶⣶⣶⣶⣶⣶⣶⣶⡆⠀⠀
⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀
⠀⠀⠸⠿⠿⠿⠿⠿⠿⠿⠿⠿⠇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Sourcepub fn stroke_triangle(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
x3: i32,
y3: i32,
)
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(),
"\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⠢⠤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢸⠀⠀⠀⠈⠉⢒⡢⠄⠀⠀⠀⠀
⠀⠀⡇⠀⣀⠤⠔⠊⠁⠀⠀⠀⠀⠀⠀
⠀⠀⠓⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Sourcepub fn fill_triangle(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
x3: i32,
y3: i32,
)
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(),
"\
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢰⣦⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⢸⣿⣿⣿⣿⣿⣶⡦⠄⠀⠀⠀⠀
⠀⠀⣿⣿⣿⠿⠟⠋⠁⠀⠀⠀⠀⠀⠀
⠀⠀⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
);
Sourcepub fn stroke_circle(&mut self, x: i32, y: i32, radius: i32)
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(),
"\
⠀⠀⠀⠀⠀⠀⣀⣀⣀⡀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⡠⠊⠀⠀⠀⠈⠢⡀⠀⠀⠀
⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀
⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⡠⠃⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠒⠒⠒⠊⠀⠀⠀⠀⠀
"
);
Sourcepub fn fill_circle(&mut self, x: i32, y: i32, radius: i32)
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(),
"\
⠀⠀⠀⠀⠀⠀⣀⣀⣀⡀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣦⡀⠀⠀⠀
⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀
⠀⠀⠀⠀⠻⣿⣿⣿⣿⣿⡿⠃⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠛⠛⠛⠋⠀⠀⠀⠀⠀
"
);
Sourcepub fn stroke_ngon(
&mut self,
x: i32,
y: i32,
radius: i32,
sides: i32,
angle: f64,
)
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.
Sourcepub fn draw_canvas(&mut self, canvas: &Self, dx: i32, dy: i32)
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(),
"\
⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠊
⠀⠀⠀⠑⡏⠉⠉⠉⠉⠉⢹⠊⠀⠀⠀
⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀
⠀⠀⠀⡠⣇⣀⣀⣀⣀⣀⣸⢄⠀⠀⠀
⡠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄
"
);
Sourcepub fn merge_canvas(&mut self, canvas: &Self, dx: i32, dy: i32)
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?
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}