1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Creating a blank window is all well and good, but drawing something to it is even better.
//!
//! Rendering in Quicksilver usually takes the form:
//! ```no_run
//! # use quicksilver::{graphics::{Background, Drawable}, lifecycle::Window};
//! # fn func(window: &mut Window, some_drawable: impl Drawable, some_background: Background) {
//! window.draw(&some_drawable, some_background);
//! # }
//! ```
//! `Drawable` is a trait which allows an object to determine how to lay out some points to draw,
//! like a rectangle or a circle. A Background is what to fill those points with, like a solid
//! color or an image. For example, drawing a red rectangle with a top-left coordinate of (50, 50),
//! a width of 100, and a height of 200 would look like:
//! ```no_run
//! # use quicksilver::{geom::{Rectangle}, graphics::{Background, Color, Drawable}, lifecycle::Window};
//! # fn func(window: &mut Window) {
//! let area = Rectangle::new((50, 50), (100, 200));
//! let background = Background::Col(Color::RED);
//! window.draw(&area, background);
//! # }
//! ```
//! If we wanted to switch out our rectangle for a Circle with a center at (100, 100) and a radius
//! of 50, we could do:
//! ```no_run
//! # use quicksilver::{geom::{Circle, Rectangle}, graphics::{Background, Color, Drawable}, lifecycle::Window};
//! # fn func(window: &mut Window) {
//! let area = Circle::new((100, 100), 50);
//! let background = Background::Col(Color::RED);
//! window.draw(&area, background);
//! # }
//! ```
//! The next step is actually integrating some drawing code into our blank window:
//! ```no_run
//! extern crate quicksilver;
//!
//! use quicksilver::{
//! Result,
//! geom::{Rectangle, Vector}, // We'll need to import Rectangle now
//! graphics::{Background, Color}, // Also Background and Color
//! lifecycle::{State, Window, run}
//! };
//!
//! struct Screen;
//!
//! impl State for Screen {
//! fn new() -> Result<Screen> {
//! Ok(Screen)
//! }
//!
//! fn draw(&mut self, window: &mut Window) -> Result<()> {
//! // Clear the contents of the window to a white background
//! window.clear(Color::WHITE)?;
//! // Draw a red rectangle
//! window.draw(&Rectangle::new((50, 50), (100, 200)), Background::Col(Color::RED));
//! Ok(())
//! }
//! }
//!
//! fn main() {
//! run::<Screen>("Hello World", Vector::new(800, 600), Default::default());
//! }
//! ```
//! We've made two changes from the previous example: imported `Rectangle`, `Background`, and
//! `Color`, as well as implementing the `draw` function. By default, `draw` will be called by the
//! host environment whenever the screen refreshes. First we clear out the window's previous
//! contents, then we draw a red rectangle.
//!
//! If we want the rectangle to be smaller, or bigger, or a different color, the code we wrote is
//! sufficient. Just tweak the input values and you could have a blue Rectangle that's twice as
//! big. But how could we do a rotation, or efficiently do a complex scaling or translation
//! operation? The answer is the `Transform` struct. If you're familiar with matrix math or linear
//! algebra, `Transform` is a 3x3 transformation matrix. If you don't know the underlying math,
//! worry not! There are 4 main ways to create a transform:
//!
//! - `Transform::IDENTITY`: Create a Transform that does nothing. When you apply this transform,
//! everything will look exactly the same
//! - `Transform::rotate(angle)`: Create a Transform that rotates counter-clockwise by a given
//! amount of degrees
//! - `Transform::translate(vector)`: Create a Transform that moves an object by a given vector
//! - `Transform::scale(vector)`: Create a Transform with a given x and y axis scale factor
//!
//! We combine Transform objects using the `*` operator, with the last transform in a chain being
//! applied first. This means that
//! ```no_run
//! # use quicksilver::geom::Transform;
//! Transform::rotate(30) * Transform::translate((0, -6));
//! ```
//! first translates an object up six pixels and then rotates it by 30 degrees.
//!
//! The last drawing concept for now is z-ordering. Sometimes you don't want to draw objects to the
//! screen in the order they're drawn, but with some other sorting method. Here you use z-ordering:
//! an object with a higher z value gets drawn on top of an object with a lower z value.
//!
//! If you want to use a transform or z-ordering, you need to use the more advanced draw function,
//! which takes the form:
//! ```no_run
//! # use quicksilver::{geom::{Transform}, graphics::{Background, Drawable}, lifecycle::Window};
//! # fn func(window: &mut Window, some_drawable: impl Drawable, some_background: Background,
//! # some_transform_value: Transform, some_z_value: f32) {
//! window.draw_ex(&some_drawable, some_background, some_transform_value, some_z_value);
//! # }
//! ```
//! Armed with Transform values, we can turn our little red rectangle into a little red diamond:
//! ```no_run
//! extern crate quicksilver;
//!
//! use quicksilver::{
//! Result,
//! geom::{Rectangle, Transform, Vector}, // Now we need Transform
//! graphics::{Background, Color},
//! lifecycle::{State, Window, run}
//! };
//!
//! struct Screen;
//!
//! impl State for Screen {
//! fn new() -> Result<Screen> {
//! Ok(Screen)
//! }
//!
//! fn draw(&mut self, window: &mut Window) -> Result<()> {
//! window.clear(Color::WHITE)?;
//! // Draw a red diamond
//! window.draw_ex(
//! &Rectangle::new((50, 50), (50, 50)),
//! Background::Col(Color::RED),
//! Transform::rotate(45), // Rotate by 45 degrees
//! 0 // we don't really care about the Z value
//! );
//! Ok(())
//! }
//! }
//!
//! fn main() {
//! run::<Screen>("Hello World", Vector::new(800, 600), Default::default());
//! }
//! ```
//! Quicksilver gives you a number of `Drawable` objects to work with by default: `Rectangle`,
//! `Vector`, `Circle`, `Line`, and `Triangle`. Most applications will only ever need these, or
//! even just a subset of these, but you can feel free to define your own `Drawable` objects. This
//! is covered later in the `mesh` tutorial.