turtle/turtle.rs
1use std::cell::RefCell;
2use std::fmt::Debug;
3use std::rc::Rc;
4use std::thread;
5use std::time::Duration;
6
7#[cfg(not(target_arch = "wasm32"))]
8use crate::event::{Event, MouseButton};
9use crate::radians::{self, Radians};
10use crate::turtle_window::TurtleWindow;
11use crate::{Color, Drawing, Point, Speed};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14enum AngleUnit {
15 Degrees,
16 Radians,
17}
18
19impl AngleUnit {
20 fn to_radians(self, angle: Angle) -> Radians {
21 match self {
22 AngleUnit::Degrees => Radians::from_degrees_value(angle),
23 AngleUnit::Radians => Radians::from_radians_value(angle),
24 }
25 }
26
27 fn to_angle(self, angle: Radians) -> Angle {
28 match self {
29 AngleUnit::Degrees => angle.to_degrees(),
30 AngleUnit::Radians => angle.to_radians(),
31 }
32 }
33}
34
35/// Any distance value (positive or negative)
36pub type Distance = f64;
37
38/// An angle value without a unit
39///
40/// The unit with which this angle will be interpreted depends on whether the Turtle is set to use
41/// degrees or radians. See the [`use_degrees()`](struct.Turtle.html#method.use_degrees) or
42/// [`use_radians()`](struct.Turtle.html#method.use_radians) methods for more information.
43pub type Angle = f64;
44
45/// A turtle with a pen attached to its tail
46///
47/// **The idea:** You control a turtle with a pen tied to its tail. As it moves
48/// across the screen, it draws the path that it follows. You can use this to draw
49/// any picture you want just by moving the turtle across the screen.
50///
51/// 
52///
53/// See the documentation for the methods below to learn about the different drawing commands you
54/// can use with the turtle.
55pub struct Turtle {
56 window: Rc<RefCell<TurtleWindow>>,
57 drawing: Drawing,
58 angle_unit: AngleUnit,
59}
60
61impl Default for Turtle {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67impl Turtle {
68 /// Create a new turtle.
69 ///
70 /// This will immediately open a new window with the turtle at the center. As each line in
71 /// your program runs, the turtle shown in the window will update.
72 ///
73 /// ```rust,no_run
74 /// # #![allow(unused_variables, unused_mut)]
75 /// use turtle::Turtle;
76 ///
77 /// fn main() {
78 /// let mut turtle = Turtle::new();
79 /// // Do things with the turtle...
80 /// }
81 /// ```
82 ///
83 /// **Note:** If you do not create the `Turtle` right at the beginning of `main()`, call
84 /// [`turtle::start()`](fn.start.html) in order to avoid any problems.
85 pub fn new() -> Turtle {
86 let window = Rc::new(RefCell::new(TurtleWindow::new()));
87 Turtle {
88 window: window.clone(),
89 drawing: Drawing::with_window(window),
90 angle_unit: AngleUnit::Degrees,
91 }
92 }
93
94 /// Move the turtle forward by the given amount of `distance`. If the pen is down, the turtle
95 /// will draw a line as it moves.
96 ///
97 /// The turtle takes very small steps (measured in "pixels"). So if you want it to move more,
98 /// use a bigger value to make the turtle walk further.
99 /// The `distance` can be a negative value in which case the turtle will move backward.
100 ///
101 /// # Example
102 ///
103 /// ```rust
104 /// # use turtle::Turtle;
105 /// # let mut turtle = Turtle::new();
106 /// // Move forward 10 tiny turtle steps, drawing a line as you move
107 /// turtle.forward(10.0);
108 ///
109 /// // Move forward 100 tiny turtle steps, drawing a much longer line
110 /// turtle.forward(100.0);
111 ///
112 /// // Move backward 223 tiny turtle steps, without drawing anything
113 /// turtle.pen_up();
114 /// turtle.forward(-223.0);
115 /// # assert_eq!(turtle.position().y.round(), -113.0);
116 /// ```
117 pub fn forward(&mut self, distance: Distance) {
118 self.window.borrow_mut().forward(distance);
119 }
120
121 /// Move the turtle backwards by the given amount of `distance`. If the pen is down, the turtle
122 /// will draw a line as it moves.
123 ///
124 /// The turtle takes very small steps (measured in "pixels"). So if you want it to move more,
125 /// use a bigger value to make the turtle walk further.
126 /// The `distance` can be a negative value in which case the turtle will move forward.
127 ///
128 /// # Example
129 ///
130 /// ```rust
131 /// # use turtle::Turtle;
132 /// # let mut turtle = Turtle::new();
133 /// // Move backward 10 tiny turtle steps, drawing a line as you move
134 /// turtle.backward(10.0);
135 ///
136 /// // Move backward 100 tiny turtle steps, drawing a much longer line
137 /// turtle.backward(100.0);
138 ///
139 /// // Move forward 179 tiny turtle steps, without drawing anything
140 /// turtle.pen_up();
141 /// turtle.backward(-179.0);
142 /// # assert_eq!(turtle.position().y.round(), 69.0);
143 /// ```
144 pub fn backward(&mut self, distance: Distance) {
145 // Moving backwards is essentially moving forwards with a negative distance
146 self.window.borrow_mut().forward(-distance);
147 }
148
149 /// Instruct the turtle to turn right (clockwise) by the given angle. Since the turtle rotates
150 /// in place, its position will not change and it will not draw anything while it turns.
151 ///
152 /// The `angle` parameter is a floating point number that represents how much you want the
153 /// turtle to rotate.
154 /// The unit of `angle` is "degrees" by default. You can change that by using the
155 /// [`use_degrees()`](struct.Turtle.html#method.use_degrees) or
156 /// [`use_radians()`](struct.Turtle.html#method.use_radians) methods.
157 ///
158 /// # Example
159 ///
160 /// ```rust
161 /// # use turtle::*;
162 /// # let mut turtle = Turtle::new();
163 /// // rotate right by 30 degrees
164 /// turtle.right(30.0);
165 ///
166 /// // rotate right by 1 radian (57.2957795 degrees)
167 /// turtle.use_radians();
168 /// turtle.right(1.0);
169 /// // Use PI for precise angles in radians
170 /// use std::f64::consts::PI;
171 /// // This is the same as turning 45.0 degrees
172 /// turtle.right(PI/4.0);
173 /// # // Calculate the angle that should result from the above rotations
174 /// # let expected = (90f64 - 30f64).to_radians() - 1.0 - PI/4.0;
175 /// # // Need to properly normalize the angle so that it can be checked
176 /// # // We only perform a normalization in `right`, and not `left` because the angle resulting
177 /// # // from the rotations is negative.
178 /// # let expected = expected - (2.0*PI) * (expected / (2.0*PI)).floor();
179 /// # let expected = (expected * 1e5).trunc();
180 /// # assert_eq!((turtle.heading() * 1e5).trunc(), expected);
181 /// ```
182 pub fn right(&mut self, angle: Angle) {
183 let angle = self.angle_unit.to_radians(angle);
184 self.window.borrow_mut().rotate(angle, true);
185 }
186
187 /// Instruct the turtle to turn left (counterclockwise) by the given angle. Since the turtle
188 /// rotates in place, its position will not change and it will not draw anything while it
189 /// turns.
190 ///
191 /// The `angle` parameter is a floating point number that represents how much you want the
192 /// turtle to rotate.
193 /// The unit of `angle` is "degrees" by default. You can change that by using the
194 /// [`use_degrees()`](struct.Turtle.html#method.use_degrees) or
195 /// [`use_radians()`](struct.Turtle.html#method.use_radians) methods.
196 ///
197 /// # Example
198 ///
199 /// ```rust
200 /// # use turtle::*;
201 /// # let mut turtle = Turtle::new();
202 /// // rotate left by 30 degrees
203 /// turtle.left(30.0);
204 ///
205 /// // rotate left by 1 radian (57.2957795 degrees)
206 /// turtle.use_radians();
207 /// turtle.left(1.0);
208 /// // Use PI for precise angles in radians
209 /// use std::f64::consts::PI;
210 /// // This is the same as turning 45.0 degrees
211 /// turtle.left(PI/4.0);
212 /// # assert_eq!(
213 /// # (turtle.heading() * 1e5).trunc(),
214 /// # (((90f64 + 30f64).to_radians() + 1.0 + PI/4.0) * 1e5).trunc()
215 /// # );
216 /// ```
217 pub fn left(&mut self, angle: Angle) {
218 let angle = self.angle_unit.to_radians(angle);
219 self.window.borrow_mut().rotate(angle, false);
220 }
221
222 /// Waits for the specified number of seconds before executing the next command.
223 ///
224 /// ```rust,no_run
225 /// # use turtle::*;
226 /// # let mut turtle = Turtle::new();
227 /// turtle.forward(100.0);
228 /// turtle.wait(2.0);
229 /// // The turtle will stop for 2 seconds before proceeding to this line
230 /// turtle.forward(50.0);
231 /// ```
232 pub fn wait(&mut self, secs: f64) {
233 if !secs.is_normal() {
234 return;
235 }
236 thread::sleep(Duration::from_millis((secs * 1000.0) as u64));
237 }
238
239 /// Retrieve a read-only reference to the drawing.
240 ///
241 /// See the documentation for the [`Drawing` struct](struct.Drawing.html) for a complete
242 /// listing of the information that you can retrieve from the drawing.
243 pub fn drawing(&self) -> &Drawing {
244 &self.drawing
245 }
246
247 /// Retrieve a mutable reference to the drawing
248 ///
249 /// See the documentation for the [`Drawing` struct](struct.Drawing.html) for a complete
250 /// listing of the ways that you can manipulate the drawing.
251 pub fn drawing_mut(&mut self) -> &mut Drawing {
252 &mut self.drawing
253 }
254
255 /// Returns the current speed of the turtle.
256 ///
257 /// ```rust
258 /// # use turtle::*;
259 /// # let mut turtle = Turtle::new();
260 /// turtle.set_speed(8);
261 /// assert_eq!(turtle.speed(), 8);
262 /// ```
263 ///
264 /// See the documentation for the [`Speed` struct](struct.Speed.html) for more information.
265 pub fn speed(&self) -> Speed {
266 self.window.borrow().fetch_turtle().speed
267 }
268
269 /// Set the turtle's movement and rotation speed to the given value. A higher value will make
270 /// the turtle's walking and turning animations faster.
271 ///
272 /// You can pass either a number or certain strings like `"slow"`, `"normal"`, and `"fast"`.
273 /// See the documentation for the [`Speed` struct](struct.Speed.html) for all of the different
274 /// options as well as the valid range of numbers that can be used for speeds.
275 ///
276 /// ```rust,no_run
277 /// # use turtle::*;
278 /// # let mut turtle = Turtle::new();
279 /// turtle.set_speed("normal");
280 /// turtle.set_speed("fast");
281 /// turtle.set_speed(2);
282 /// turtle.set_speed(12);
283 /// turtle.set_speed("slower");
284 /// // Constructing a Speed directly works too, but the syntax above is often more convenient
285 /// turtle.set_speed(Speed::from(2));
286 /// ```
287 ///
288 /// Any invalid string or numeric value outside of the valid range will cause the program to
289 /// `panic!` at runtime.
290 ///
291 /// # Moving Instantly
292 ///
293 /// Setting the speed to `"instant"` results in no animation. The turtle moves instantly
294 /// and turns instantly. This is often used to position the turtle before you start to draw
295 /// something. You can set the speed to instant, move the turtle into the position you want to
296 /// start your drawing from and then set the speed back to `"normal"`.
297 ///
298 /// ```rust,no_run
299 /// # use turtle::*;
300 /// let mut turtle = Turtle::new();
301 /// turtle.set_speed("instant");
302 /// // Move to a position 300 steps to the left of the start position
303 /// turtle.right(90.0);
304 /// turtle.backward(300.0);
305 ///
306 /// // The turtle is in position we want it to start at,
307 /// // so let's set the speed back to normal
308 /// turtle.set_speed("normal");
309 /// // Start drawing from here...
310 /// ```
311 ///
312 /// # Conversion Traits
313 ///
314 /// So how does this method work? Why can it accept completely different types as input to the
315 /// same function?
316 ///
317 /// Using this method is an excellent way to learn about the conversion traits [`From`] and
318 /// [`Into`]. This method takes a *generic type* as its speed parameter. By specifying the type
319 /// as `S: Into<Speed>`, we are telling the Rust compiler that we want to accept any type
320 /// that can be converted into a [`Speed`].
321 ///
322 /// ```rust,no_run
323 /// # use turtle::*;
324 /// # struct T {speed: Speed} impl T {
325 /// // This is (essentially) how Turtle::set_speed is implemented
326 /// fn set_speed<S: Into<Speed>>(&mut self, speed: S) {
327 /// // Calling `.into()` converts the value of `speed` into type `Speed`.
328 /// // The `.into()` method is defined in the `Into` trait and is implemented by `Speed`
329 /// // for `i32` and `&str`
330 /// // `S: Into<Speed>` makes the compiler guarantee that this method exists and returns
331 /// // exactly the type that we expect
332 /// let speed: Speed = speed.into();
333 /// self.speed = speed;
334 /// }
335 /// # }
336 ///
337 /// // This makes it possible to pass in any type that can be converted into a `Speed`
338 /// fn main() {
339 /// let mut turtle = Turtle::new();
340 ///
341 /// // The following works because `Speed` defined a conversion from `i32`
342 /// turtle.set_speed(1);
343 ///
344 /// // The following works because `Speed` defined a conversion from `&str`
345 /// turtle.set_speed("fast");
346 /// }
347 /// ```
348 ///
349 /// The [`Speed`] documentation describes the different types that it can be converted from in
350 /// detail. For other types and in other crates where this may not be explicitly documented,
351 /// you can always find this information by looking for implementations of the [`From`] trait.
352 ///
353 /// [`Speed`] implements [`From`] for several types:
354 ///
355 /// * [`impl From<&str> for Speed`](struct.Speed.html#impl-From%3C%26%27a%20str%3E)
356 /// * [`impl From<i32> for Speed`](struct.Speed.html#impl-From%3Ci32%3E)
357 /// * etc.
358 ///
359 /// Why look for [`From`] and not [`Into`]? It turns out that the Rust compiler knows a rule that says
360 /// "if some type A can be converted **from** type B, type B can be converted **into** type A."
361 /// That is why most types only implement [`From`] and leave [`Into`] to automatically be
362 /// derived based on the rule.
363 /// See the documentation of the [`Into`] trait for the "blanket implementation" which defines
364 /// that rule.
365 ///
366 /// [`Speed`]: struct.Speed.html
367 /// [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
368 /// [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html
369 pub fn set_speed<S: Into<Speed>>(&mut self, speed: S) {
370 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.speed = speed.into());
371 }
372
373 /// Returns the turtle's current location (x, y)
374 ///
375 /// ```rust
376 /// # use turtle::*;
377 /// # let mut turtle = Turtle::new();
378 /// turtle.forward(100.0);
379 /// let pos = turtle.position();
380 /// assert_eq!(pos.round(), Point {x: 0.0, y: 100.0});
381 /// ```
382 pub fn position(&self) -> Point {
383 self.window.borrow().fetch_turtle().position
384 }
385
386 /// Moves the turtle directly to the given position. See the [`Point` struct](struct.Point.html)
387 /// documentation for more information.
388 ///
389 /// If the pen is down, this will draw a line. The turtle will not turn to face the direction
390 /// in which it is moving. It's heading will stay the same.
391 /// Use [`set_speed()`](struct.Turtle.html#method.set_speed) to control the animation speed.
392 ///
393 /// ```rust
394 /// # use turtle::*;
395 /// # let mut turtle = Turtle::new();
396 /// let heading = turtle.heading();
397 /// assert_eq!(turtle.position(), Point {x: 0.0, y: 0.0});
398 /// turtle.go_to([100.0, -150.0]);
399 /// // The heading has not changed, but the turtle has moved to the new position
400 /// assert_eq!(turtle.heading(), heading);
401 /// assert_eq!(turtle.position(), Point {x: 100.0, y: -150.0});
402 /// ```
403 pub fn go_to<P: Into<Point>>(&mut self, position: P) {
404 self.window.borrow_mut().go_to(position.into());
405 }
406
407 /// Goes to the given x-coordinate, keeping the y-coordinate and heading of the turtle the
408 /// same. See [`go_to()`](struct.Turtle.html#method.go_to) for more information.
409 pub fn set_x(&mut self, x: f64) {
410 let y = self.position().y;
411 self.go_to([x, y]);
412 }
413
414 /// Goes to the given y-coordinate, keeping the x-coordinate and heading of the turtle the
415 /// same. See [`go_to()`](struct.Turtle.html#method.go_to) for more information.
416 pub fn set_y(&mut self, y: f64) {
417 let x = self.position().x;
418 self.go_to([x, y]);
419 }
420
421 /// Moves instantaneously to the origin and resets the heading to face north.
422 ///
423 /// ```rust
424 /// # use turtle::*;
425 /// let mut turtle = Turtle::new();
426 /// let start_position = turtle.position().round();
427 /// let start_heading = turtle.heading().round();
428 /// turtle.right(55.0);
429 /// turtle.forward(127.0);
430 /// assert_ne!(turtle.heading().round(), start_heading);
431 /// assert_ne!(turtle.position().round(), start_position);
432 /// turtle.home();
433 /// assert_eq!(turtle.heading().round(), start_heading);
434 /// assert_eq!(turtle.position().round(), start_position);
435 /// ```
436 pub fn home(&mut self) {
437 self.window.borrow_mut().with_turtle_mut(|turtle| {
438 turtle.position = Point::origin();
439 turtle.heading = radians::PI / 2.0;
440 });
441 }
442
443 /// Returns the turtle's current heading.
444 ///
445 /// The unit of the returned angle is degrees by default, but can be set using the
446 /// [`use_degrees()`](struct.Turtle.html#method.use_degrees) or
447 /// [`use_radians()`](struct.Turtle.html#method.use_radians) methods.
448 ///
449 /// The heading is relative to the positive x axis (east). When first created, the turtle
450 /// starts facing north. That means that its heading is 90.0 degrees. The following chart
451 /// contains many common directions and their angles.
452 ///
453 /// | Cardinal Direction | Heading (degrees) | Heading (radians) |
454 /// | ------------------ | ----------------- | ----------------- |
455 /// | East | 0.0° | `0.0` |
456 /// | North | 90.0° | `PI/2` |
457 /// | West | 180.0° | `PI` |
458 /// | South | 270.0° | `3*PI/2` |
459 ///
460 /// You can test the result of `heading()` with these values to see if the turtle is facing
461 /// a certain direction.
462 ///
463 /// ```rust
464 /// # use turtle::*;
465 /// // Turtles start facing north
466 /// let mut turtle = Turtle::new();
467 /// // The rounding is to account for floating-point error
468 /// assert_eq!(turtle.heading().round(), 90.0);
469 /// turtle.right(31.0);
470 /// assert_eq!(turtle.heading().round(), 59.0);
471 /// turtle.left(193.0);
472 /// assert_eq!(turtle.heading().round(), 252.0);
473 /// turtle.left(130.0);
474 /// // Angles should not exceed 360.0
475 /// assert_eq!(turtle.heading().round(), 22.0);
476 /// ```
477 pub fn heading(&self) -> Angle {
478 let heading = self.window.borrow().fetch_turtle().heading;
479 self.angle_unit.to_angle(heading)
480 }
481
482 /// Rotate the turtle so that its heading is the given angle.
483 ///
484 /// The unit of `angle` is degrees by default, but can be set using the
485 /// [`use_degrees()`](struct.Turtle.html#method.use_degrees) or
486 /// [`use_radians()`](struct.Turtle.html#method.use_radians) methods.
487 ///
488 /// The turtle will attempt to rotate as little as possible in order to reach the given heading
489 /// (between -180 and 179 degrees).
490 /// Use [`set_speed()`](struct.Turtle.html#method.set_speed) to control the animation speed.
491 ///
492 /// Here are some common directions in degrees and radians:
493 ///
494 /// | Cardinal Direction | Heading (degrees) | Heading (radians) |
495 /// | ------------------ | ----------------- | ----------------- |
496 /// | East | 0.0° | `0.0` |
497 /// | North | 90.0° | `PI/2` |
498 /// | West | 180.0° | `PI` |
499 /// | South | 270.0° | `3*PI/2` |
500 ///
501 /// See [`heading()`](struct.Turtle.html#method.heading) for more information.
502 ///
503 /// # Example
504 ///
505 /// ```rust
506 /// # use turtle::*;
507 /// // Turtles start facing north
508 /// let mut turtle = Turtle::new();
509 /// // The rounding is to account for floating-point error
510 /// assert_eq!(turtle.heading().round(), 90.0);
511 /// turtle.set_heading(31.0);
512 /// assert_eq!(turtle.heading().round(), 31.0);
513 /// turtle.set_heading(293.0);
514 /// assert_eq!(turtle.heading().round(), 293.0);
515 /// turtle.set_heading(1.0);
516 /// assert_eq!(turtle.heading().round(), 1.0);
517 /// // Angles should not exceed 360.0, even when we set them to values larger than that
518 /// turtle.set_heading(367.0);
519 /// assert_eq!(turtle.heading().round(), 7.0);
520 /// ```
521 pub fn set_heading(&mut self, angle: Angle) {
522 let angle = self.angle_unit.to_radians(angle);
523 let heading = self.window.borrow().fetch_turtle().heading;
524 // Find the amount we need to turn to reach the target heading based on our current heading
525 let angle = angle - heading;
526 // Normalize the angle to be between -180 and 179 so that we rotate as little as possible
527 // Formula from: https://stackoverflow.com/a/24234924/551904
528 let angle = angle - radians::TWO_PI * ((angle + radians::PI) / radians::TWO_PI).floor();
529 self.window.borrow_mut().rotate(angle, false);
530 }
531
532 /// Returns true if `Angle` values will be interpreted as degrees.
533 ///
534 /// See [`use_degrees()`](struct.Turtle.html#method.use_degrees) for more information.
535 pub fn is_using_degrees(&self) -> bool {
536 self.angle_unit == AngleUnit::Degrees
537 }
538
539 /// Returns true if `Angle` values will be interpreted as radians.
540 ///
541 /// See [`use_radians()`](struct.Turtle.html#method.use_degrees) for more information.
542 pub fn is_using_radians(&self) -> bool {
543 self.angle_unit == AngleUnit::Radians
544 }
545
546 /// Change the angle unit to degrees.
547 ///
548 /// ```rust
549 /// # use turtle::*;
550 /// # let mut turtle = Turtle::new();
551 /// # turtle.use_radians();
552 /// assert!(!turtle.is_using_degrees());
553 /// turtle.use_degrees();
554 /// assert!(turtle.is_using_degrees());
555 ///
556 /// // This will now be interpreted as 1.0 degree
557 /// turtle.right(1.0);
558 /// ```
559 pub fn use_degrees(&mut self) {
560 self.angle_unit = AngleUnit::Degrees;
561 }
562
563 /// Change the angle unit to radians.
564 ///
565 /// ```rust
566 /// # use turtle::*;
567 /// # let mut turtle = Turtle::new();
568 /// assert!(!turtle.is_using_radians());
569 /// turtle.use_radians();
570 /// assert!(turtle.is_using_radians());
571 ///
572 /// // This will now be interpreted as 1.0 radian
573 /// turtle.right(1.0);
574 /// ```
575 pub fn use_radians(&mut self) {
576 self.angle_unit = AngleUnit::Radians;
577 }
578
579 /// Return true if pen is down, false if it’s up.
580 ///
581 /// ```rust
582 /// # use turtle::*;
583 /// # let mut turtle = Turtle::new();
584 /// assert!(turtle.is_pen_down());
585 /// turtle.pen_up();
586 /// assert!(!turtle.is_pen_down());
587 /// turtle.pen_down();
588 /// assert!(turtle.is_pen_down());
589 /// ```
590 pub fn is_pen_down(&self) -> bool {
591 self.window.borrow().fetch_turtle().pen.enabled
592 }
593
594 /// Pull the pen down so that the turtle draws while moving.
595 ///
596 /// ```rust
597 /// # use turtle::*;
598 /// # let mut turtle = Turtle::new();
599 /// # turtle.pen_up();
600 /// assert!(!turtle.is_pen_down());
601 /// // This will move the turtle, but not draw any lines
602 /// turtle.forward(100.0);
603 /// turtle.pen_down();
604 /// assert!(turtle.is_pen_down());
605 /// // The turtle will now draw lines again
606 /// turtle.forward(100.0);
607 /// ```
608 pub fn pen_down(&mut self) {
609 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.pen.enabled = true);
610 }
611
612 /// Pick the pen up so that the turtle does not draw while moving
613 ///
614 /// ```rust
615 /// # use turtle::*;
616 /// # let mut turtle = Turtle::new();
617 /// assert!(turtle.is_pen_down());
618 /// // The turtle will move and draw a line
619 /// turtle.forward(100.0);
620 /// turtle.pen_up();
621 /// assert!(!turtle.is_pen_down());
622 /// // Now, the turtle will move, but not draw anything
623 /// turtle.forward(100.0);
624 /// ```
625 pub fn pen_up(&mut self) {
626 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.pen.enabled = false);
627 }
628
629 /// Returns the size (thickness) of the pen. The thickness is measured in pixels.
630 ///
631 /// ```rust
632 /// # use turtle::*;
633 /// # let mut turtle = Turtle::new();
634 /// turtle.set_pen_size(25.0);
635 /// assert_eq!(turtle.pen_size(), 25.0);
636 /// ```
637 ///
638 /// See [`set_pen_size()`](struct.Turtle.html#method.set_pen_size) for more details.
639 pub fn pen_size(&self) -> f64 {
640 self.window.borrow().fetch_turtle().pen.thickness
641 }
642
643 /// Sets the thickness of the pen to the given size. The thickness is measured in pixels.
644 ///
645 /// The turtle's pen has a flat tip. The value you set the pen's size to will change the
646 /// width of the stroke created by the turtle as it moves. See the example below for more
647 /// about what this means.
648 ///
649 /// # Example
650 ///
651 /// ```rust,no_run
652 /// use turtle::Turtle;
653 ///
654 /// fn main() {
655 /// let mut turtle = Turtle::new();
656 ///
657 /// turtle.pen_up();
658 /// turtle.right(90.0);
659 /// turtle.backward(300.0);
660 /// turtle.pen_down();
661 ///
662 /// turtle.set_pen_color("#2196F3"); // blue
663 /// turtle.set_pen_size(1.0);
664 /// turtle.forward(200.0);
665 ///
666 /// turtle.set_pen_color("#f44336"); // red
667 /// turtle.set_pen_size(50.0);
668 /// turtle.forward(200.0);
669 ///
670 /// turtle.set_pen_color("#4CAF50"); // green
671 /// turtle.set_pen_size(100.0);
672 /// turtle.forward(200.0);
673 /// }
674 /// ```
675 ///
676 /// This will produce the following:
677 ///
678 /// 
679 ///
680 /// Notice that while the turtle travels in a straight line, it produces different thicknesses
681 /// of lines which appear like large rectangles.
682 pub fn set_pen_size(&mut self, thickness: f64) {
683 assert!(
684 thickness >= 0.0 && thickness.is_finite(),
685 "Invalid thickness: {}. The pen thickness must be greater than or equal to zero",
686 thickness
687 );
688 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.pen.thickness = thickness);
689 }
690
691 /// Returns the color of the pen.
692 ///
693 /// ```rust
694 /// # use turtle::*;
695 /// # let mut turtle = Turtle::new();
696 /// turtle.set_pen_color("blue");
697 /// assert_eq!(turtle.pen_color(), "blue".into());
698 /// ```
699 ///
700 /// See the [`color` module](color/index.html) for more information about colors.
701 pub fn pen_color(&self) -> Color {
702 self.window.borrow().fetch_turtle().pen.color
703 }
704
705 /// Sets the color of the pen to the given color.
706 ///
707 /// Any type that can be converted into a color can be passed into this function.
708 /// See the [`color` module](color/index.html) for more information.
709 ///
710 /// # Example
711 ///
712 /// ```rust,no_run
713 /// use turtle::Turtle;
714 ///
715 /// fn main() {
716 /// let mut turtle = Turtle::new();
717 /// turtle.drawing_mut().set_background_color("light grey");
718 /// turtle.set_pen_size(3.0);
719 ///
720 /// let colors = ["red", "green", "blue"];
721 ///
722 /// for i in 0..36 {
723 /// turtle.set_pen_color(colors[i % colors.len()]);
724 /// turtle.forward(25.0);
725 /// turtle.right(10.0);
726 /// }
727 /// }
728 /// ```
729 ///
730 /// This will produce the following:
731 ///
732 /// 
733 pub fn set_pen_color<C: Into<Color> + Copy + Debug>(&mut self, color: C) {
734 let pen_color = color.into();
735 assert!(
736 pen_color.is_valid(),
737 "Invalid color: {:?}. See the color module documentation for more information.",
738 color
739 );
740 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.pen.color = pen_color);
741 }
742
743 /// Returns the current fill color.
744 ///
745 /// This will be used to fill the shape when
746 /// [`begin_fill()`](struct.Turtle.html#method.begin_fill) and
747 /// [`end_fill()`](struct.Turtle.html#method.end_fill) are called.
748 ///
749 /// ```rust
750 /// # use turtle::*;
751 /// # let mut turtle = Turtle::new();
752 /// turtle.set_fill_color("coral");
753 /// assert_eq!(turtle.fill_color(), "coral".into());
754 /// ```
755 ///
756 /// See the [`color` module](color/index.html) for more information about colors.
757 pub fn fill_color(&self) -> Color {
758 self.window.borrow().fetch_turtle().fill_color
759 }
760
761 /// Sets the fill color to the given color.
762 ///
763 /// **Note:** The fill color must be set **before** `begin_fill()` is called in order to be
764 /// used when filling the shape.
765 ///
766 /// Any type that can be converted into a color can be passed into this function.
767 /// See the [`color` module](color/index.html) for more information.
768 ///
769 /// # Example
770 ///
771 /// See [`begin_fill()`](struct.Turtle.html#method.begin_fill) for an example.
772 pub fn set_fill_color<C: Into<Color> + Copy + Debug>(&mut self, color: C) {
773 let fill_color = color.into();
774 assert!(
775 fill_color.is_valid(),
776 "Invalid color: {:?}. See the color module documentation for more information.",
777 color
778 );
779 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.fill_color = fill_color);
780 }
781
782 /// Return true if the turtle is currently filling the shape drawn
783 /// by its movements.
784 ///
785 /// ```rust
786 /// # use turtle::*;
787 /// # let mut turtle = Turtle::new();
788 /// assert!(!turtle.is_filling());
789 /// turtle.begin_fill();
790 /// assert!(turtle.is_filling());
791 /// turtle.end_fill();
792 /// assert!(!turtle.is_filling());
793 /// ```
794 ///
795 /// See [`begin_fill()`](struct.Turtle.html#method.begin_fill) for more
796 /// information and an example.
797 pub fn is_filling(&self) -> bool {
798 self.window.borrow().fetch_turtle().is_filling
799 }
800
801 /// Begin filling the shape drawn by the turtle's movements.
802 ///
803 /// **Rule of thumb:** For every call to [`begin_fill()`](struct.Turtle.html#method.begin_fill),
804 /// there should be a corresponding call to [`end_fill()`](struct.Turtle.html#method.end_fill).
805 ///
806 /// # Example
807 ///
808 /// The following example will draw a circle filled with the color red and then a square with
809 /// no fill.
810 ///
811 /// **Note:** The fill color must be set **before** `begin_fill()` is called in order to be
812 /// used when filling the shape.
813 ///
814 /// ```rust,no_run
815 /// use turtle::Turtle;
816 ///
817 /// fn main() {
818 /// let mut turtle = Turtle::new();
819 /// turtle.right(90.0);
820 /// turtle.set_pen_size(3.0);
821 ///
822 /// turtle.set_pen_color("blue");
823 /// turtle.set_fill_color("red");
824 /// turtle.begin_fill();
825 /// for _ in 0..360 {
826 /// turtle.forward(2.0);
827 /// turtle.right(1.0);
828 /// }
829 /// turtle.end_fill();
830 ///
831 /// turtle.set_pen_color("green");
832 /// turtle.forward(120.0);
833 /// for _ in 0..3 {
834 /// turtle.right(90.0);
835 /// turtle.forward(240.0);
836 /// }
837 /// turtle.right(90.0);
838 /// turtle.forward(120.0);
839 /// }
840 /// ```
841 ///
842 /// This will result in the following:
843 ///
844 /// 
845 pub fn begin_fill(&mut self) {
846 self.window.borrow_mut().begin_fill();
847 }
848
849 /// Stop filling the shape drawn by the turtle's movements.
850 ///
851 /// **Rule of thumb:** For every call to [`begin_fill()`](struct.Turtle.html#method.begin_fill),
852 /// there should be a corresponding call to [`end_fill()`](struct.Turtle.html#method.end_fill).
853 ///
854 /// See [`begin_fill()`](struct.Turtle.html#method.begin_fill) for more information.
855 pub fn end_fill(&mut self) {
856 self.window.borrow_mut().end_fill();
857 }
858
859 /// Returns true if the turtle is visible.
860 ///
861 /// ```rust
862 /// # use turtle::*;
863 /// let mut turtle = Turtle::new();
864 /// assert!(turtle.is_visible());
865 /// turtle.hide();
866 /// assert!(!turtle.is_visible());
867 /// turtle.show();
868 /// assert!(turtle.is_visible());
869 /// ```
870 pub fn is_visible(&self) -> bool {
871 self.window.borrow().fetch_turtle().visible
872 }
873
874 /// Makes the turtle invisible. The shell will not be shown, but drawings will continue.
875 ///
876 /// Useful for some complex drawings.
877 ///
878 /// ```rust
879 /// # use turtle::*;
880 /// # let mut turtle = Turtle::new();
881 /// assert!(turtle.is_visible());
882 /// turtle.hide();
883 /// assert!(!turtle.is_visible());
884 /// ```
885 pub fn hide(&mut self) {
886 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.visible = false);
887 }
888
889 /// Makes the turtle visible.
890 ///
891 /// ```rust
892 /// # use turtle::*;
893 /// # let mut turtle = Turtle::new();
894 /// # turtle.hide();
895 /// assert!(!turtle.is_visible());
896 /// turtle.show();
897 /// assert!(turtle.is_visible());
898 /// ```
899 pub fn show(&mut self) {
900 self.window.borrow_mut().with_turtle_mut(|turtle| turtle.visible = true);
901 }
902
903 /// Delete the turtle's drawings from the screen, re-center the turtle and reset all of the
904 /// turtle's state (speed, color, etc.) back to the default.
905 ///
906 /// ```rust
907 /// # use turtle::*;
908 /// # let mut turtle = Turtle::new();
909 /// turtle.left(43.0);
910 /// turtle.forward(289.0);
911 /// turtle.set_pen_color("red");
912 /// turtle.drawing_mut().set_background_color("green");
913 /// let position = turtle.position();
914 /// let heading = turtle.heading();
915 /// turtle.reset();
916 /// assert_eq!(turtle.heading(), 90.0);
917 /// assert_eq!(turtle.position(), Point {x: 0.0, y: 0.0});
918 /// assert_ne!(turtle.pen_color(), "red".into());
919 /// assert_ne!(turtle.drawing().background_color(), "green".into());
920 /// ```
921 pub fn reset(&mut self) {
922 self.clear();
923 self.window.borrow_mut().with_turtle_mut(|turtle| *turtle = Default::default());
924 self.window.borrow_mut().with_drawing_mut(|drawing| *drawing = Default::default());
925 }
926
927 /// Delete the turtle's drawings from the screen.
928 ///
929 /// Does not move turtle. Position, speed and heading of the turtle are not affected. The
930 /// background color and any other settings (pen color, size, etc.) all remain the same.
931 ///
932 /// # Example
933 ///
934 /// ```rust,no_run
935 /// use turtle::Turtle;
936 ///
937 /// fn main() {
938 /// let mut turtle = Turtle::new();
939 /// turtle.right(32.0);
940 /// turtle.forward(150.0);
941 ///
942 /// turtle.wait_for_click();
943 /// turtle.clear();
944 /// }
945 /// ```
946 ///
947 /// This will produce the following:
948 ///
949 /// 
950 ///
951 /// Once you click on the screen, the drawings will be cleared:
952 ///
953 /// 
954 pub fn clear(&mut self) {
955 self.window.borrow_mut().clear();
956 }
957
958 /// Rotates the turtle to face the given point. See the [`Point` struct](struct.Point.html)
959 /// documentation for more information.
960 ///
961 /// If the coordinates are the same as the turtle's current position, no rotation takes place.
962 /// Always rotates the least amount necessary in order to face the given point.
963 ///
964 /// # Example
965 ///
966 /// ```rust,no_run
967 /// use turtle::Turtle;
968 ///
969 /// fn main() {
970 /// let mut turtle = Turtle::new();
971 /// // moving the turtle to the bottom on the screen in the middle
972 /// turtle.pen_up();
973 /// turtle.go_to([0.0, -300.0]);
974 /// turtle.set_heading(90.0);
975 /// turtle.pen_down();
976 ///
977 /// // the turtle will go up following an oscillating point
978 /// let mut i: f64 = 0.0;
979 /// // choosing an arbitrary constant to multiply
980 /// // the cos function, result between -5000 and 5000
981 /// let c = 5000.0;
982 /// // just draw a few full cicles
983 /// while i < 15.0 {
984 /// let f = (i).cos()*c;
985 /// // following the oscillating point above at y=1000
986 /// turtle.turn_towards([f, 1000.0]);
987 /// // going forward for a small amount
988 /// turtle.forward(1.0);
989 /// // incrementing the angle
990 /// i = i + 0.01;
991 /// }
992 /// }
993 /// ```
994 pub fn turn_towards<P: Into<Point>>(&mut self, target: P) {
995 let target: Point = target.into();
996 let position = self.position();
997
998 // If the target is (approximately) on the turtle don't turn
999 if (target - position).is_not_normal() {
1000 return;
1001 }
1002
1003 let heading = self.window.borrow().fetch_turtle().heading;
1004
1005 // Calculate the target angle to reach
1006 let angle = (target - position).atan2();
1007 let angle = Radians::from_radians_value(angle);
1008 // Calculate how much turning will be needed (angle - heading)
1009 // And clamp it make sure the turtle doesn't turn more than 360 degrees
1010 let angle = (angle - heading) % radians::TWO_PI;
1011 // Try to rotate as little as possible
1012 let angle = if angle.abs() > radians::PI {
1013 // Use signum to make sure the angle has the right sign
1014 // And the turtle turns the right way
1015 -angle.signum() * (radians::TWO_PI - angle.abs())
1016 } else {
1017 angle
1018 };
1019 self.window.borrow_mut().rotate(angle, false);
1020 }
1021
1022 /// Convenience function that waits for a click to occur before returning.
1023 ///
1024 /// Useful for when you want the turtle to wait for the user to click before continuing. Use
1025 /// this to force the turtle to wait before it starts drawing at the beginning of your program.
1026 ///
1027 /// This method uses [`poll_event()`](struct.Drawing.html#method.poll_event) internally and
1028 /// ignores any other events that take place before the click is received.
1029 ///
1030 /// # Example
1031 ///
1032 /// ```rust,no_run
1033 /// use turtle::Turtle;
1034 ///
1035 /// fn main() {
1036 /// let mut turtle = Turtle::new();
1037 /// turtle.wait_for_click();
1038 /// // The turtle will wait for the screen to be clicked before continuing
1039 /// turtle.forward(100.0);
1040 /// }
1041 /// ```
1042 #[cfg(not(target_arch = "wasm32"))] //FIXME: Port to WASM
1043 pub fn wait_for_click(&mut self) {
1044 loop {
1045 if let Some(Event::MouseButtonReleased(MouseButton::Left)) = self.drawing_mut().poll_event() {
1046 break;
1047 }
1048
1049 // Sleep for ~1 frame (at 120fps) to avoid pegging the CPU.
1050 thread::sleep(Duration::from_millis(1000 / 120));
1051 }
1052 }
1053}
1054
1055#[cfg(test)]
1056mod tests {
1057 use super::*;
1058
1059 #[test]
1060 fn is_using_radians_degrees() {
1061 // is_using_radians and is_using_degrees should be inverses of each other
1062 let mut turtle = Turtle::new();
1063 assert!(!turtle.is_using_radians());
1064 assert!(turtle.is_using_degrees());
1065 turtle.use_radians();
1066 assert!(turtle.is_using_radians());
1067 assert!(!turtle.is_using_degrees());
1068 turtle.use_degrees();
1069 assert!(!turtle.is_using_radians());
1070 assert!(turtle.is_using_degrees());
1071 }
1072
1073 #[test]
1074 fn clear_leaves_position_and_heading() {
1075 let mut turtle = Turtle::new();
1076 assert_eq!(turtle.position(), Point::origin());
1077 assert_eq!(turtle.heading(), 90.0);
1078 turtle.forward(100.0);
1079 turtle.set_heading(51.0);
1080 turtle.clear();
1081 // The rounding is to account for floating-point error
1082 assert_eq!(turtle.position().round(), Point { x: 0.0, y: 100.0 });
1083 assert_eq!(turtle.heading(), 51.0);
1084 }
1085
1086 #[test]
1087 fn turn_towards() {
1088 let mut turtle = Turtle::new();
1089
1090 // Turn from each cardinal direction to each cardinal direction
1091 for n in 0..16 as u32 {
1092 let original_angle = radians::TWO_PI * n as f64 / 16.0;
1093 for i in 0..16 as u32 {
1094 turtle.turn_towards([original_angle.cos(), original_angle.sin()]);
1095 assert_eq!(turtle.heading().ceil(), original_angle.to_degrees().ceil());
1096
1097 let target_angle = radians::TWO_PI * i as f64 / 16.0;
1098 turtle.turn_towards([target_angle.cos(), target_angle.sin()]);
1099 assert_eq!(turtle.heading().ceil(), target_angle.to_degrees().ceil());
1100 }
1101 }
1102 }
1103
1104 #[test]
1105 #[should_panic(expected = "Invalid thickness: -10. The pen thickness must be greater than or equal to zero")]
1106 fn set_pen_size_rejects_negative() {
1107 let mut turtle = Turtle::new();
1108 turtle.set_pen_size(-10.0);
1109 }
1110
1111 #[test]
1112 #[should_panic(expected = "Invalid thickness: NaN. The pen thickness must be greater than or equal to zero")]
1113 fn set_pen_size_rejects_nan() {
1114 let mut turtle = Turtle::new();
1115 turtle.set_pen_size(::std::f64::NAN);
1116 }
1117
1118 #[test]
1119 #[should_panic(expected = "Invalid thickness: inf. The pen thickness must be greater than or equal to zero")]
1120 fn set_pen_size_rejects_inf() {
1121 let mut turtle = Turtle::new();
1122 turtle.set_pen_size(::std::f64::INFINITY);
1123 }
1124
1125 #[test]
1126 #[should_panic(expected = "Invalid thickness: -inf. The pen thickness must be greater than or equal to zero")]
1127 fn set_pen_size_rejects_neg_inf() {
1128 let mut turtle = Turtle::new();
1129 turtle.set_pen_size(-::std::f64::INFINITY);
1130 }
1131
1132 #[test]
1133 #[should_panic(
1134 expected = "Invalid color: Color { red: NaN, green: 0.0, blue: 0.0, alpha: 0.0 }. See the color module documentation for more information."
1135 )]
1136 fn rejects_invalid_pen_color() {
1137 let mut turtle = Turtle::new();
1138 turtle.set_pen_color(Color {
1139 red: ::std::f64::NAN,
1140 green: 0.0,
1141 blue: 0.0,
1142 alpha: 0.0,
1143 });
1144 }
1145
1146 #[test]
1147 #[should_panic(
1148 expected = "Invalid color: Color { red: NaN, green: 0.0, blue: 0.0, alpha: 0.0 }. See the color module documentation for more information."
1149 )]
1150 fn rejects_invalid_fill_color() {
1151 let mut turtle = Turtle::new();
1152 turtle.set_fill_color(Color {
1153 red: ::std::f64::NAN,
1154 green: 0.0,
1155 blue: 0.0,
1156 alpha: 0.0,
1157 });
1158 }
1159}