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/// ![turtle moving forward](https://github.com/sunjay/turtle/raw/9240f8890d1032a0033ec5c5338a10ffa942dc21/forward.gif)
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&deg;          | `0.0`             |
456    /// | North              | 90.0&deg;         | `PI/2`            |
457    /// | West               | 180.0&deg;        | `PI`              |
458    /// | South              | 270.0&deg;        | `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&deg;          | `0.0`             |
497    /// | North              | 90.0&deg;         | `PI/2`            |
498    /// | West               | 180.0&deg;        | `PI`              |
499    /// | South              | 270.0&deg;        | `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    /// ![turtle pen thickness](https://github.com/sunjay/turtle/raw/9240f8890d1032a0033ec5c5338a10ffa942dc21/docs/assets/images/docs/pen_thickness.png)
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    /// ![turtle pen color](https://github.com/sunjay/turtle/raw/9240f8890d1032a0033ec5c5338a10ffa942dc21/docs/assets/images/docs/colored_circle.png)
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    /// ![turtle fill example](https://github.com/sunjay/turtle/raw/9240f8890d1032a0033ec5c5338a10ffa942dc21/docs/assets/images/docs/red_circle.png)
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    /// ![turtle clear before click](https://github.com/sunjay/turtle/raw/9240f8890d1032a0033ec5c5338a10ffa942dc21/docs/assets/images/docs/clear_before_click.png)
950    ///
951    /// Once you click on the screen, the drawings will be cleared:
952    ///
953    /// ![turtle clear before click](https://github.com/sunjay/turtle/raw/9240f8890d1032a0033ec5c5338a10ffa942dc21/docs/assets/images/docs/clear_after_click.png)
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}