114 | ```
115 |
116 | 
117 |
118 | As you just saw above, we can take the instructions that we pass into `repeat`, give them a single name, and refer to that name to get the same effect.
119 |
120 | Let's simplify further.
121 |
122 | ```clojure
123 | (def square (all (repeat 4 side)))
124 | (left 90)
125 | (square)
126 | ```
127 |
128 | 
129 |
130 |
131 | So given a named set of instructions, we can invoke the instructions by putting the name in parentheses just like we do for functions like `forward`, `left`, and `repeat`
132 |
133 | ```clojure
134 | (def square-and-turn (all (square) (left 90)))
135 | (left 90)
136 | (square-and-turn)
137 |
138 | ```
139 |
140 | 
141 |
142 |
143 | ```clojure
144 | (left 45)
145 | (repeat 4 square-and-turn)
146 | ```
147 |
148 | 
149 |
150 |
151 | ### penup, pendown, setxy, setheading
152 |
153 | The turtle has a pen that it drags along where it goes, creating a drawing. We can pick the pen up and put the pen down when we need to draw unconnected lines. `setxy` also teleports the turtle without drawing. `setheading` turns the turtle in an exact direction.
154 |
155 | ```clojure
156 | (penup)
157 | (forward 113)
158 | (right 135)
159 | ```
160 |
161 | 
162 |
163 | ```clojure
164 | (pendown)
165 | (repeat 4 (all (forward 160) (right 90)))
166 | ```
167 |
168 | 
169 |
170 |
171 | ```clojure
172 | (setxy -100 0)
173 | ```
174 |
175 | 
176 |
177 |
178 | ```clojure
179 | (setheading 225)
180 | ```
181 |
182 | 
183 |
184 | ### clean, home
185 |
186 | `clean` erases all drawing. `home` brings the turtle to its original position and direction.
187 |
188 | ```clojure
189 | (clean)
190 | ```
191 |
192 | 
193 |
194 | ```clojure
195 | (home)
196 | ```
197 |
198 | 
199 |
200 | ### color
201 |
202 | Color can be set for the turtle. A color is specified by a vector of
203 | size 1, 3, or 4.
204 |
205 | A three-element color vector has 3 integers for the red,
206 | green, and blue components of the color (in that order) in the range
207 | 0 - 255. (See
208 | [this page](https://en.wikipedia.org/wiki/Web_colors#X11_color_names)
209 | for examples of specifying color in terms of RGB values.)
210 |
211 | ```clojure
212 | (def octagon (all (repeat 8 (all (forward 30) (right 45)))))
213 | (color [0 0 255])
214 | (octagon)
215 | ```
216 |
217 | 
218 |
219 | The turtle sprite (the triangle representing the turtle) will be drawn
220 | in the same color as the turtle's pen.
221 |
222 | ```clojure
223 | (repeat 12 (all (octagon) (right 30)))
224 | ```
225 |
226 | 
227 |
228 | We can also use our color value to fill the interior of shapes that we
229 | draw. To draw shapes will a fill color, we first have to indicate
230 | when we start and when we end drawing the shape. For that, we use the
231 | `start-fill` and `end-fill` commands. Every line segment that the turtle
232 | draws in between `start-fill` and `end-fill` is assumed to form the
233 | perimeter of the shape.
234 |
235 | Let us define `filled-octagon` as the combination of commands to draw a filled
236 | octagon. In between the `start-fill` and `end-fill` that demarcate our
237 | fill shape, we will use our `octagon` function to draw the perimeter of the
238 | octagon that we want filled.
239 |
240 | ```clojure
241 | (def filled-octagon (all (start-fill) (octagon) (end-fill)))
242 | ```
243 |
244 | If a four-element color vector is given for a color, then the 4th value is known
245 | as the "alpha" value. In clojure-turtle, the alpha value is also an
246 | integer that ranges from 0 to 255. The value 0 represents full
247 | transparency, and 255 represents full opacity.
248 |
249 | ```clojure
250 | (color [255 255 0 100])
251 | ```
252 |
253 | We will want to draw 4 octagons that
254 | overlap, so we will create a `points` vector of 4 x,y-coordinates from
255 | which we will start drawing
256 | each octagon.
257 |
258 | ```clojure
259 | (def points [[-11 -11] [-62 -11] [-36 14] [-36 -36]])
260 | ```
261 |
262 | For now, let's retrieve only the first of the four points,
263 | set our position to that first point, and then draw our first octagon
264 | from there.
265 |
266 | ```clojure
267 | (let [point-1 (first points)
268 | x (first point-1)
269 | y (second point-1)]
270 | (setxy x y)
271 | (filled-octagon))
272 | ```
273 |
274 | 
275 |
276 | Next, we will draw our the remaining 3 octagons. Since we will
277 | perform similar actions in repetition, let's create a function to
278 | store the behavior we want to repeat.
279 |
280 | ```clojure
281 | (defn filled-octagon-from-point
282 | [point]
283 | (let [x (first point)
284 | y (second point)]
285 | (setxy x y)
286 | (filled-octagon)))
287 | ```
288 |
289 | Given a point in the form `[x y]`, the function
290 | `filled-octagon-from-point` will draw a filled octagon starting at
291 | (x,y). We label the positions (indices) of a vector starting from 0,
292 | so the 1st element is as position 0, the 3rd element is at position 2,
293 | and the 4th element is at position 3.
294 |
295 | ```clojure
296 | ;; octagon starting from 2nd point:
297 | (filled-octagon-from-point (second points))
298 | ;; ... from 3rd point:
299 | (filled-octagon-from-point (nth points 2))
300 | ;; ... from 4th point:
301 | (filled-octagon-from-point (nth points 3))
302 | ```
303 |
304 | 
305 |
306 | A color vector of size 1 creates a grayscale color ranging from black
307 | to white. The grayscale
308 | color is equivalent to using a 3-element RGB color vector where the
309 | values for red, green, and blue are the same.
310 |
311 | ```clojure
312 | (color [0])
313 | (home)
314 | ```
315 |
316 | 
317 |
318 | ### wait and animation
319 |
320 | We can use `wait` to make the turtle pause. The number passed to wait
321 | indicates how many milliseconds the turtle should wait (1 millisecond
322 | = 0.001 seconds = 1 / 1000 seconds).
323 |
324 | ```clojure
325 | (clean)
326 | (home)
327 | (def stop-and-go (all (forward 30) (wait 2000) (right 90) (forward 30)))
328 | (stop-and-go)
329 | ```
330 |
331 | Computers today are fast. If we use `wait` to slow them down, we can
332 | watch the turtle move and perceive motion.
333 |
334 | ```clojure
335 | (clean)
336 | (home)
337 |
338 | (defn slower-octagon
339 | []
340 | (repeat 8 (fn []
341 | (forward 30)
342 | (right 45)
343 | (wait 100))))
344 | (repeat 12 (all (slower-octagon) (right 30)))
345 | ```
346 |
347 | What happens when you combine `wait` with `clean`? If we repeatedly
348 | draw, wait, and clean images in a loop, we can create the effect of
349 | motion! See the [Animation page](https://github.com/google/clojure-turtle/wiki/Animation) for more
350 | information and examples.
351 |
352 | ### What next?
353 |
354 | clojure-turtle uses Quil, which uses [Processing](https://processing.org/). clojure-turtle also has the full power and fun of Clojure available to it, too.
355 |
356 | What do you get when you enter the following?
357 |
358 | ```clojure
359 | (defn square-by-length
360 | [side-length]
361 | (repeat 4 (all (forward side-length) (right 90))))
362 |
363 | (square-by-length 10)
364 | (square-by-length 20)
365 | ```
366 |
367 | ```clojure
368 | (def lengths [40 50 60])
369 | (map square-by-length lengths)
370 | ```
371 |
372 | ```clojure
373 | (defn times-2
374 | [x]
375 | (* 2 x))
376 |
377 | (right 90)
378 | (map square-by-length (map times-2 lengths))
379 |
380 | (right 90)
381 | (->> lengths
382 | (map times-2)
383 | (map square-by-length))
384 | ```
385 |
386 | ```clojure
387 | (defn polygon-side
388 | [num-sides side-length]
389 | (forward side-length)
390 | (right (/ 360 num-sides)))
391 |
392 | (defn polygon
393 | [num-sides side-length]
394 | (repeat num-sides (all (polygon-side num-sides side-length))))
395 |
396 | (clean)
397 | (right 180)
398 | (polygon 5 20)
399 |
400 | (def side-counts [6 7 8 10 12])
401 | (def lengths (reverse [30 40 50 60 70]))
402 | (map polygon side-counts lengths)
403 | ```
404 |
405 | ```clojure
406 | (defn rand-side
407 | []
408 | (forward (rand-int 50))
409 | (setheading (rand-int 360)))
410 |
411 | (fn? rand-side)
412 | (fn? side)
413 |
414 | (clean)
415 | (home)
416 | (repeat 4 side)
417 | (repeat 100 rand-side)
418 | ```
419 |
420 | What possibilities exist when you incorporate the full power of Clojure? What can you create?
421 |
422 | ## Using the ClojureScript Version
423 |
424 | The same codebase in `clojure-turtle` can be compiled to JS and used in a JS runtime in addition to JVM bytecode. A demo of the JS version can be executed by first running the command:
425 |
426 | ```clojure
427 | lein figwheel
428 | ```
429 |
430 | Where the output may look like:
431 | ```
432 | $ lein figwheel
433 | Figwheel: Starting server at http://localhost:3449
434 | Figwheel: Watching build - dev
435 | Compiling "demo/public/js/main.js" from ["src" "demo/src"]...
436 | Successfully compiled "demo/public/js/main.js" in 16.311 seconds.
437 | Launching ClojureScript REPL for build: dev
438 | ...
439 | ```
440 |
441 | Then, in your browser, visit the URL in the terminal output from the command -- in this example, it is http://localhost:3449. You will see a webpage load with the Quil canvas containing the turtle. Back in your terminal, Figwheel will load a ClojureScript REPL that is connected to the webpage (more precisely, the browser REPL running in the webpage). In the ClojureScript REPL, run:
442 |
443 | ```
444 | cljs.user=> (ns clojure-turtle.core)
445 | cljs.user=> (require '[clojure-turtle.macros :refer-macros [repeat all]])
446 | ```
447 |
448 | Now, the above Logo/`clojure-turtle` commands can be issued in the CLJS REPL as described above, with the result visible in the Figwheel-connected browser page.
449 |
450 | ## Mailing List
451 |
452 | Join the [clojure-turtle mailing list](https://groups.google.com/forum/#!forum/clojure-turtle) to post questions and receive announcements.
453 |
454 | ## How to Contribute
455 |
456 | Interested in contributing code to the project? We would love to have
457 | your help!
458 |
459 | Before you can contribute, you should first read the
460 | [page on contributing](./CONTRIBUTING.md) and agree to the Contributor
461 | License Agreement. Signing the CLA can be done online and is fast.
462 | This is a one-time process.
463 |
464 | Thereafter, contributions can be initiated through a [pull
465 | request](https://help.github.com/articles/using-pull-requests/).
466 |
467 | ## License
468 |
469 | Distributed under the Apache 2 license.
470 |
471 | ### Disclaimer
472 |
473 | This is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google.
474 |
475 | ### Dependencies
476 |
477 | Quil is distributed under the Eclipse Public License either version 1.0 (or at your option) any later version.
478 |
479 | The official Processing.org's jars, used as dependencies of Quil, are distributed under LGPL and their code can be found on http://processing.org/
480 |
--------------------------------------------------------------------------------
/demo/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Clojure-Turtle example. Control me with your Figwheel REPL.
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo/src/demo.cljs:
--------------------------------------------------------------------------------
1 | (ns clojure-turtle.demo
2 | (:require [clojure-turtle.core :as turtle :include-macros true]))
3 |
4 | (.log js/console "Creating turtle")
5 |
6 | (turtle/new-window {:size [320 300]})
7 |
--------------------------------------------------------------------------------
/doc/img/frame00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame00.png
--------------------------------------------------------------------------------
/doc/img/frame01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame01.png
--------------------------------------------------------------------------------
/doc/img/frame02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame02.png
--------------------------------------------------------------------------------
/doc/img/frame03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame03.png
--------------------------------------------------------------------------------
/doc/img/frame04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame04.png
--------------------------------------------------------------------------------
/doc/img/frame05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame05.png
--------------------------------------------------------------------------------
/doc/img/frame06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame06.png
--------------------------------------------------------------------------------
/doc/img/frame07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame07.png
--------------------------------------------------------------------------------
/doc/img/frame08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame08.png
--------------------------------------------------------------------------------
/doc/img/frame09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame09.png
--------------------------------------------------------------------------------
/doc/img/frame10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame10.png
--------------------------------------------------------------------------------
/doc/img/frame11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame11.png
--------------------------------------------------------------------------------
/doc/img/frame12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame12.png
--------------------------------------------------------------------------------
/doc/img/frame13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame13.png
--------------------------------------------------------------------------------
/doc/img/frame14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame14.png
--------------------------------------------------------------------------------
/doc/img/frame15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame15.png
--------------------------------------------------------------------------------
/doc/img/frame16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame16.png
--------------------------------------------------------------------------------
/doc/img/frame17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame17.png
--------------------------------------------------------------------------------
/doc/img/frame18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame18.png
--------------------------------------------------------------------------------
/doc/img/frame19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/clojure-turtle/8ce2b5d25199e9e8ad08d395b16e8445dff5d55b/doc/img/frame19.png
--------------------------------------------------------------------------------
/doc/intro.md:
--------------------------------------------------------------------------------
1 | # Introduction to clojure-turtle
2 |
3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)
4 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject com.google/clojure-turtle "0.4.0-SNAPSHOT"
2 | :description "A Clojure library that implements the Logo programming language in a Clojure context"
3 | :url "https://github.com/google/clojure-turtle"
4 | :license {:name "Apache License, Version 2.0"
5 | :url "http://www.apache.org/licenses/LICENSE-2.0.html"}
6 | :scm {:name "git"
7 | :url "https://github.com/google/clojure-turtle"}
8 | :repositories [["releases" {:url "https://clojars.org/repo/"}]]
9 | :deploy-repositories [["clojars" {:creds :gpg}]]
10 | :pom-addition [:developers [:developer
11 | [:name "Elango Cheran"]
12 | [:email "elango@google.com"]
13 | [:timezone "-8"]]]
14 | :dependencies [[org.clojure/clojure "1.7.0"]
15 | [org.clojure/clojurescript "1.7.170"]
16 | [quil "2.2.6"]]
17 |
18 | :source-paths ["src/cljc" "src/cljs"]
19 |
20 | :profiles {:dev {:plugins [[lein-figwheel "0.5.0-6"]
21 | [lein-cljsbuild "1.1.2"]]
22 | :resource-paths ["demo/public"]
23 | :cljsbuild
24 | {:builds
25 | [{:id "dev"
26 | :source-paths ["src/cljc" "src/cljs" "demo/src"]
27 | :figwheel {}
28 | :compiler {:main "clojure-turtle.demo"
29 | :source-map true
30 | :source-map-timestamp true
31 | :optimizations :none
32 | :output-to "demo/public/js/main.js"
33 | :output-dir "demo/public/js/out"
34 | :asset-path "js/out"}}]}}}
35 |
36 | :figwheel {:http-server-root ""
37 | :repl true}
38 |
39 | :jar-exclusions [#"\.cljx|\.swp|\.swo|\.DS_Store"])
40 |
--------------------------------------------------------------------------------
/src/cljc/clojure_turtle/core.cljc:
--------------------------------------------------------------------------------
1 | ;; Copyright 2014 Google Inc. All Rights Reserved.
2 |
3 | ;; Licensed under the Apache License, Version 2.0 (the "License");
4 | ;; you may not use this file except in compliance with the License.
5 | ;; You may obtain a copy of the License at
6 |
7 | ;; http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | ;; Unless required by applicable law or agreed to in writing, software
10 | ;; distributed under the License is distributed on an "AS IS" BASIS,
11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | ;; See the License for the specific language governing permissions and
13 | ;; limitations under the License.
14 |
15 | (ns clojure-turtle.core
16 | (:refer-clojure :exclude [repeat])
17 | #?(:clj
18 | (:require [quil.core :as q])
19 | :cljs
20 | (:require [quil.core :as q :include-macros true]
21 | [clojure-turtle.macros :refer-macros [repeat all]]))
22 | #?(:clj
23 | (:import java.util.Date)))
24 |
25 |
26 |
27 | ;;
28 | ;; constants
29 | ;;
30 |
31 | (def ^{:doc "The default color to be used (ex: if color is not specified)"}
32 | DEFAULT-COLOR [0 0 0])
33 |
34 | ;;
35 | ;; records
36 | ;;
37 |
38 | (defrecord Turtle [x y angle pen color fill commands start-from]
39 | ;; both for Clojure and ClojureScript, override the behavior of the
40 | ;; str fn / .toString method to "restore" the default .toString
41 | ;; behavior for the entire Turtle record data, instead of just
42 | ;; returning whatever pr-str returns
43 | Object
44 | (toString [turt] (str (select-keys turt (keys turt)))))
45 |
46 | ;;
47 | ;; record printing definitions
48 | ;;
49 |
50 | (defn pr-str-turtle
51 | "This method determines what gets returned when passing a Turtle record instance to pr-str, which in turn affects what gets printed at the REPL"
52 | [turt]
53 | (pr-str
54 | (letfn [(format-key [key]
55 | {key
56 | #?(:clj (float (/ (bigint (* (get turt key) 10)) 10))
57 | :cljs (/ (Math/round (* (get turt key) 10)) 10))})]
58 | (merge (select-keys turt [:pen :color :fll])
59 | (format-key :x)
60 | (format-key :y)
61 | (format-key :angle)))))
62 |
63 | #?(:clj (defmethod print-method Turtle [turt writer]
64 | (.write writer (pr-str-turtle turt)))
65 | :cljs (extend-type Turtle
66 | IPrintWithWriter
67 | (-pr-writer [turt writer _]
68 | (-write writer (pr-str-turtle turt)))))
69 |
70 | ;;
71 | ;; fns - turtle fns
72 | ;;
73 |
74 | (defn new-turtle
75 | "Returns an entity that represents a turtle."
76 | []
77 | (atom (map->Turtle {:x 0
78 | :y 0
79 | :angle 90
80 | :pen true
81 | :color DEFAULT-COLOR
82 | :fill false
83 | :commands []
84 | :start-from {:x 0
85 | :y 0}})))
86 |
87 | (def ^{:doc "The default turtle entity used when no turtle is specified for an operation."}
88 | turtle (new-turtle))
89 |
90 | (defn alter-turtle
91 | "A helper function used in the implementation of basic operations to abstract
92 | out the interface of applying a function to a turtle entity."
93 | [turt-state f]
94 | (swap! turt-state f)
95 | turt-state)
96 |
97 | ;;
98 | ;; fns - colors and visual effects
99 | ;;
100 |
101 | (defn make-opaque
102 | "Take a color vector, as passed to the `color` fn, and return a color vector
103 | in the form [red blue green alpha], where all RGB and alpha values are integers
104 | in the range 0-255 inclusive. In order to make the color vector represent full
105 | opacity, the alpha value will be 255."
106 | [color-vec]
107 | (let [rgb-vec (case (count color-vec)
108 | 1 (clojure.core/repeat 3 (first color-vec))
109 | 3 color-vec
110 | 4 (take 3 color-vec))
111 | rgba-vec (concat rgb-vec [255])]
112 | rgba-vec))
113 |
114 | (defn color
115 | "Set the turtle's color using [red green blue].
116 | RGB values are in the range 0 to 255, inclusive."
117 | ([c]
118 | (color turtle c))
119 | ([turt-state c]
120 | (letfn [(alter-fn [t] (-> t
121 | (assoc :color c)
122 | (update-in [:commands] conj [:color c])))]
123 | (alter-turtle turt-state alter-fn))))
124 |
125 | ;;
126 | ;; fns - basic Logo commands
127 | ;;
128 |
129 | (defn right
130 | "Rotate the turtle turt clockwise by ang degrees."
131 | ([ang]
132 | (right turtle ang))
133 | ([turt-state ang]
134 | ;; the local fn add-angle will increment the angle but keep the
135 | ;; resulting angle in the range [0,360), in degrees.
136 | (letfn [(add-angle
137 | [{:keys [angle] :as t}]
138 | (let [new-angle (-> angle
139 | (- ang)
140 | (mod 360))]
141 | (-> t
142 | (assoc :angle new-angle)
143 | (update-in [:commands] conj [:setheading new-angle]))))]
144 | (alter-turtle turt-state add-angle))))
145 |
146 | (defn left
147 | "Same as right, but turns the turtle counter-clockwise."
148 | ([ang]
149 | (right (* -1 ang)))
150 | ([turt-state ang]
151 | (right turt-state (* -1 ang))))
152 |
153 | (def deg->radians q/radians)
154 |
155 | (def radians->deg q/degrees)
156 |
157 | (def atan q/atan)
158 |
159 | (defn forward
160 | "Move the turtle turt forward in the direction that it is facing by length len."
161 | ([len]
162 | (forward turtle len))
163 | ([turt-state len]
164 | ;; Convert the turtle's polar coordinates (angle + radius) into
165 | ;; Cartesian coordinates (x,y) for display purposes
166 | (let [rads (deg->radians (get @turt-state :angle))
167 | dx (* len (Math/cos rads))
168 | dy (* len (Math/sin rads))
169 | alter-fn (fn [t] (-> t
170 | (update-in [:x] + dx)
171 | (update-in [:y] + dy)
172 | (update-in [:commands] conj [:translate [dx dy]])))]
173 | (alter-turtle turt-state alter-fn))))
174 |
175 | (defn back
176 | "Same as forward, but move the turtle backwards, which is opposite of the direction it is facing."
177 | ([len]
178 | (forward (* -1 len)))
179 | ([turt-state len]
180 | (forward turt-state (* -1 len))))
181 |
182 | (defn penup
183 | "Instruct the turtle to pick its pen up. Subsequent movements will not draw to screen until the pen is put down again."
184 | ([]
185 | (penup turtle))
186 | ([turt-state]
187 | (letfn [(alter-fn [t] (-> t
188 | (assoc :pen false)
189 | (update-in [:commands] conj [:pen false])))]
190 | (alter-turtle turt-state alter-fn))))
191 |
192 | (defn pendown
193 | "Instruct the turtle to put its pen down. Subsequent movements will draw to screen."
194 | ([]
195 | (pendown turtle))
196 | ([turt-state]
197 | (letfn [(alter-fn [t] (-> t
198 | (assoc :pen true)
199 | (update-in [:commands] conj [:pen true])))]
200 | (alter-turtle turt-state alter-fn))))
201 |
202 | (defn start-fill
203 | "Make the turtle fill the area created by his subsequent moves, until end-fill is called."
204 | ([]
205 | (start-fill turtle))
206 | ([turt-state]
207 | (letfn [(alter-fn [t]
208 | (-> t
209 | (assoc :fill true)
210 | (update-in [:commands] conj [:start-fill])))]
211 | (alter-turtle turt-state alter-fn))))
212 |
213 | (defn end-fill
214 | "Stop filling the area of turtle moves. Must be called start-fill."
215 | ([]
216 | (end-fill turtle))
217 | ([turt-state]
218 | (letfn [(alter-fn [t]
219 | (-> t
220 | (assoc :fill false)
221 | (update-in [:commands] conj [:end-fill])))]
222 | (alter-turtle turt-state alter-fn))))
223 |
224 | #?(:clj
225 | (defmacro all
226 | "This macro was created to substitute for the purpose served by the square brackets in Logo
227 | in a call to REPEAT. This macro returns a no-argument function that, when invoked, executes
228 | the commands described in the body inside the macro call/form.
229 | (Haskell programmers refer to the type of function returned a 'thunk'.)"
230 | [& body]
231 | `(fn []
232 | (do
233 | ~@ body))))
234 |
235 | #?(:clj
236 | (defmacro repeat
237 | "A macro to translate the purpose of the Logo REPEAT function."
238 | [n & body]
239 | `(let [states# (repeatedly ~n ~@body)]
240 | (dorun
241 | states#)
242 | (last states#))))
243 |
244 | (defn wait
245 | "Sleeps for ms miliseconds. Can be used in a repeat to show commands execute in real time"
246 | [ms]
247 | (letfn [(get-time []
248 | #?(:clj (.getTime (Date.))
249 | :cljs (.getTime (js/Date.))))]
250 | (let [initial-time (get-time)]
251 | (while (< (get-time) (+ initial-time ms))))))
252 |
253 | (defn clean
254 | "Clear the lines state, which effectively clears the drawing canvas."
255 | ([]
256 | (clean turtle))
257 | ([turt-state]
258 | (letfn [(alter-fn
259 | [t]
260 | (let [curr-pos-map (select-keys t [:x :y])]
261 | (-> t
262 | (assoc :commands [])
263 | (assoc :start-from curr-pos-map))))]
264 | (alter-turtle turt-state alter-fn))))
265 |
266 | (defn setxy
267 | "Set the position of turtle turt to x-coordinate x and y-coordinate y."
268 | ([x y]
269 | (setxy turtle x y))
270 | ([turt-state x y]
271 | (let [pen-down? (get @turt-state :pen)]
272 | (letfn [(alter-fn [t]
273 | (-> t
274 | (assoc :x x)
275 | (assoc :y y)
276 | (update-in [:commands] conj [:setxy [x y]])))]
277 | (alter-turtle turt-state alter-fn)))))
278 |
279 | (defn setheading
280 | "Set the direction which the turtle is facing, given in degrees, where 0 is to the right,
281 | 90 is up, 180 is left, and 270 is down."
282 | ([ang]
283 | (setheading turtle ang))
284 | ([turt-state ang]
285 | (letfn [(alter-fn [t] (-> t
286 | (assoc :angle ang)
287 | (update-in [:commands] conj [:setheading ang])))]
288 | (alter-turtle turt-state alter-fn))))
289 |
290 | (defn home
291 | "Set the turtle at coordinates (0,0), facing up (heading = 90 degrees)"
292 | ([]
293 | (home turtle))
294 | ([turt-state]
295 | (setxy turt-state 0 0)
296 | (setheading turt-state 90)))
297 |
298 | ;;
299 | ;; fns - (Quil-based) rendering and graphics
300 | ;;
301 |
302 | (defn reset-rendering
303 | "A helper function for the Quil rendering function."
304 | []
305 | (q/background 200) ;; Set the background colour to
306 | ;; a nice shade of grey.
307 | (q/stroke-weight 1))
308 |
309 | (defn setup
310 | "A helper function for the Quil rendering function."
311 | []
312 | (q/smooth) ;; Turn on anti-aliasing
313 | ;; Allow q/* functions to be used from the REPL
314 | #?(:cljs
315 | (js/setTimeout #(set! quil.sketch/*applet* (q/get-sketch-by-id "turtle-canvas")) 5))
316 | (reset-rendering))
317 |
318 | (defn get-turtle-sprite
319 | "A helper function that draws the triangle that represents the turtle onto the screen."
320 | ([]
321 | (get-turtle-sprite turtle))
322 | ([turt]
323 | (let [
324 | ;; set up a copy of the turtle to draw the triangle that
325 | ;; will represent / show the turtle on the graphics canvas
326 | short-leg 5
327 | long-leg 12
328 | hypoteneuse (Math/sqrt (+ (* short-leg short-leg)
329 | (* long-leg long-leg)))
330 | large-angle (-> (/ long-leg short-leg)
331 | atan
332 | radians->deg)
333 | small-angle (- 90 large-angle)
334 | turt-copy-state (-> (atom turt)
335 | pendown
336 | clean)
337 | current-color (:color turt)
338 | opaque-color (make-opaque current-color)]
339 | ;; Use the turtle copy to step through the commands required
340 | ;; to draw the triangle that represents the turtle. the
341 | ;; turtle copy will be used for the commands stored within it.
342 | (do
343 | (-> turt-copy-state
344 | (setxy (:x turt) (:y turt)) ;teleport to the current position (turtle's centre)
345 | (penup)
346 | (back (/ long-leg 3)) ;move backwards to the centre of the turtle's base
347 | (pendown)
348 | (color opaque-color)
349 | (right 90)
350 | (forward short-leg)
351 | (left (- 180 large-angle))
352 | (forward hypoteneuse)
353 | (left (- 180 (* 2 small-angle)))
354 | (forward hypoteneuse)
355 | (left (- 180 large-angle))
356 | (forward short-leg)
357 | (left 90)))
358 | ;; now return the turtle copy
359 | turt-copy-state)))
360 |
361 | (defn draw-turtle-commands
362 | "Takes a seq of turtle commands and converts them into Quil commands to draw
363 | onto the canvas"
364 | [turt]
365 | (let [new-turtle @(new-turtle)
366 | start-from-pos (get turt :start-from)
367 | new-turtle-with-start (-> new-turtle
368 | (assoc :x (:x start-from-pos))
369 | (assoc :y (:y start-from-pos)))]
370 | (loop [t new-turtle-with-start
371 | commands (:commands turt)]
372 | (if (empty? commands)
373 | t
374 | (let [next-cmd (first commands)
375 | cmd-name (first next-cmd)
376 | cmd-vals (rest next-cmd)
377 | rest-cmds (rest commands)]
378 | (case cmd-name
379 | :color (let [c (first cmd-vals)]
380 | (apply q/stroke c)
381 | (apply q/fill c)
382 | (recur (assoc t :color c) rest-cmds))
383 | :setxy (let [[x y] (first cmd-vals)]
384 | (recur (assoc t :x x :y y) rest-cmds))
385 | :setheading (recur (assoc t :angle (first cmd-vals)) rest-cmds)
386 | :translate (let [x (:x t)
387 | y (:y t)
388 | [dx dy] (first cmd-vals)
389 | new-x (+ x dx)
390 | new-y (+ y dy)]
391 | (when (:pen t)
392 | (q/line x y new-x new-y)
393 | (when (:fill t)
394 | (q/vertex x y)
395 | (q/vertex new-x new-y)))
396 | (recur (assoc t :x new-x :y new-y) rest-cmds))
397 | :pen (recur (assoc t :pen (first cmd-vals)) rest-cmds)
398 | :start-fill (do (when-not (:fill t)
399 | (q/begin-shape))
400 | (recur (assoc t :fill true) rest-cmds))
401 | :end-fill (do (when (:fill t)
402 | (q/end-shape))
403 | (recur (assoc t :fill false) rest-cmds))
404 | t))))))
405 |
406 | (defn draw-turtle
407 | "The function passed to Quil for doing rendering."
408 | [turt-state]
409 | ;; Use push-matrix to apply a transformation to the graphing plane.
410 | (q/push-matrix)
411 | ;; By default, positive x is to the right, positive y is down.
412 | ;; Here, we tell Quil to move the origin (0,0) to the center of the window.
413 | (q/translate (/ (q/width) 2) (/ (q/height) 2))
414 | (reset-rendering)
415 | ;; Apply another transformation to the canvas.
416 | (q/push-matrix)
417 | ;; Flip the coordinates horizontally -- converts programmers'
418 | ;; x-/y-axes into mathematicians' x-/y-axes
419 | (q/scale 1.0 -1.0)
420 | ;; Set the default colors for line stroke and shape fill
421 | (apply q/stroke DEFAULT-COLOR)
422 | (apply q/fill DEFAULT-COLOR)
423 | ;; Draw the lines of where the turtle has been.
424 | (draw-turtle-commands @turt-state)
425 | ;; Draw the sprite (triangle) representing the turtle itself.
426 | (let [sprite (get-turtle-sprite @turt-state)]
427 | (draw-turtle-commands @sprite))
428 | ;; Undo the graphing plane transformation in Quil/Processing
429 | (q/pop-matrix)
430 | (q/pop-matrix))
431 |
432 | (defn draw
433 | "The function passed to Quil for doing rendering."
434 | []
435 | (draw-turtle turtle))
436 |
437 | (defmacro if-cljs
438 | "Executes `then` clause iff generating ClojureScript code. Stolen from Prismatic code.
439 | Ref. http://goo.gl/DhhhSN, http://goo.gl/Bhdyna."
440 | [then else]
441 | (if (:ns &env) ; nil when compiling for Clojure, nnil for ClojureScript
442 | then else))
443 |
444 | (defmacro new-window
445 | "Opens up a new window that shows the turtle rendering canvas. In CLJS it will render
446 | to a new HTML5 canvas object. An optional config map can be provided, where the key
447 | :title indicates the window title (clj), the :size key indicates a vector of 2 values
448 | indicating the width and height of the window."
449 | [& [config]]
450 | `(if-cljs
451 | ~(let [default-config {:size [323 200]}
452 | {:keys [host size]} (merge default-config config)]
453 | `(do
454 | (quil.sketch/add-canvas "turtle-canvas")
455 | (q/defsketch ~'example
456 | :host "turtle-canvas"
457 | :setup setup
458 | :draw draw
459 | :size ~size)))
460 | ~(let [default-config {:title "Watch the turtle go!"
461 | :size [323 200]}
462 | {:keys [title size]} (merge default-config config)]
463 | `(q/defsketch ~'example
464 | :title ~title
465 | :setup setup
466 | :draw draw
467 | :size ~size))))
468 |
--------------------------------------------------------------------------------
/src/cljs/clojure_turtle/macros.cljc:
--------------------------------------------------------------------------------
1 | (ns clojure-turtle.macros
2 | (:refer-clojure :exclude [repeat]))
3 |
4 | (defmacro all
5 | "This macro was created to substitute for the purpose served by the square brackets in Logo
6 | in a call to REPEAT. This macro returns a no-argument function that, when invoked, executes
7 | the commands described in the body inside the macro call/form.
8 | (Haskell programmers refer to the type of function returned a 'thunk'.)"
9 | [& body]
10 | `(fn []
11 | (do
12 | ~@ body)))
13 |
14 | (defmacro repeat
15 | "A macro to translate the purpose of the Logo REPEAT function."
16 | [n & body]
17 | `(let [states# (repeatedly ~n ~@body)]
18 | (dorun
19 | states#)
20 | (last states#)))
21 |
--------------------------------------------------------------------------------
/test/clojure_turtle/core_test.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright 2014 Google Inc. All Rights Reserved.
2 |
3 | ;; Licensed under the Apache License, Version 2.0 (the "License");
4 | ;; you may not use this file except in compliance with the License.
5 | ;; You may obtain a copy of the License at
6 |
7 | ;; http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | ;; Unless required by applicable law or agreed to in writing, software
10 | ;; distributed under the License is distributed on an "AS IS" BASIS,
11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | ;; See the License for the specific language governing permissions and
13 | ;; limitations under the License.
14 |
15 | (ns clojure-turtle.core-test
16 | (:require [clojure.test :refer :all]
17 | [clojure-turtle.core :refer :all]))
18 |
19 | (deftest a-test
20 | (testing "FIXME, I fail."
21 | (is (= 0 1))))
22 |
--------------------------------------------------------------------------------
/wiki/Animation.md:
--------------------------------------------------------------------------------
1 | Animation in clojure-turtle is primarily handled using the `wait` command, which can allow drawings to unfold in stages. Coupled with the `clean` command, one can define distinct frames, drawing an image, waiting, then cleaning and starting over.
2 |
3 | ##How to use Wait
4 | Wait takes a number and sleeps the program for that many milliseconds. 1000 Milliseconds is one second, so the following code will sleep for one second.
5 |
6 | ```clojure
7 | (wait 1000)
8 | ```
9 |
10 | When animating computer graphics frame-rates that are multiples of 30 are good, because they sync up with the refresh-rate of most monitors. 60 fps is the standard benchmark value for many video games, so we will work with that value for the rest of this example. (1000 milliseconds / second) / (60 frames / second) = 16.666 milliseconds per frame, so a wait time of 17 will produce about 60 frames per second. Note that if you are used to animating in 30 fps a wait time of 33.333 will produce that frame rate.
11 |
12 | ##How to make a simple object move
13 | Suppose we want to make a star move around in a circle. To do this we first need a function that draws our star. This way we can move the turtle around and simply call (Star) once each frame. The following code draws a five-pointed star with the vertical edge in the direction of the turtle:
14 |
15 | ```clojure
16 | (def star (all (repeat 5 (all (forward 100) (left 144))))
17 | ```
18 |
19 | It just so happens that the internal angles of a five-pointed star are all 144 degrees, so a simple 5-stage loop generates the desired shape. The end-result should look something like this:
20 |
21 | (image will go here once I know how, for now heres a [link](https://drive.google.com/file/d/0BxEsaFmDRvM1WlZHWHZLU0JiUDQ/view?usp=sharing))
22 |
23 | Now that we have or object we need to make it move. To do this we will need something like the following pseudo-code:
24 |
25 | ```clojure
26 | (repeat n (all (draw) (wait) (move-commands) (clean) )
27 | ```
28 |
29 | We assume that the computers internal logic happens instantaneously, so the only thing that visibly makes it to the screen is what is drawn at the moment `(wait)` is called. Nothing that happens in the move commands will ever make it to the screen, because it is cleaned and then drawn over instantaneously, before the program pauses long enough to make it visible.
30 |
31 | Let's start with the move commands. We said we wanted the star to move in a circle, so lets use a good approximation of a circle:
32 |
33 | ```clojure
34 | (forward 10) (left 10)
35 | ```
36 |
37 | This is not actually a circle but it has many small sides so it is close enough for our purposes. Each side is of length 10, which means that our star will be moving at a rate of 10px/frame * 60 frame/sec = 600 pixles per second. That is fairly fast, and if you want to make it move at a more reasonable speed something like 5px/frame or even just 1 might be more better. Since this is a circle, just changing the side-length will alter the dimensions of the circular path, but we won't worry about that now.
38 |
39 | The wait command we already worked out would be (wait 17), and (clean) is just (clean), so we have basically everything. All we have to do is plug in our draw function that we defined earlier and we get:
40 |
41 | For n, use the number of frames you want the animation to last for. In my example I used 1000 frames, which is 1000 frames / (60 frames/second) = 16.666 seconds of animation.
42 |
43 | ```clojure
44 | (repeat 1000 (all (star) (wait 17) (forward 10) (left 10) (clean) )
45 | ```
46 |
47 | Run the command and it should look something like this:
48 |
49 | [](https://www.youtube.com/watch?v=HQRPSCNzCTg)
--------------------------------------------------------------------------------
/wiki/Contributors.md:
--------------------------------------------------------------------------------
1 | The following members have contributed to the wiki pages for clojure-turtle:
2 |
3 | _(in alphabetical order)_
4 |
5 | * @atrus159
6 | * @echeran
7 |
8 |
9 |
10 |
11 | The following members have contributed to the code for clojure-turtle:
12 |
13 | _(in alphabetical order)_
14 |
15 | * @atrus159
16 | * @echeran
17 | * @jeisses
18 | * @nikai3d
--------------------------------------------------------------------------------
/wiki/Home.md:
--------------------------------------------------------------------------------
1 | # clojure-turtle Wiki Home
2 |
3 | The following is a collection of resources that are useful to users of `clojure-turtle`. Some materials are geared for students who may be learning programming through Logo. Some information is available for instructors and developers to understand the decisions made in order to blend Logo with Clojure.
4 |
5 | The wiki pages are a work-in-progress, and if you are interested, we would appreciate [your contributions](https://github.com/google/clojure-turtle#how-to-contribute), too!
6 |
7 | ## Pages
8 |
9 | * Created works gallery
10 | * [Recursion](recursion)
11 | * [Animation](animation)
12 |
13 | ## Contributors
14 |
15 | Share a kind thought for the [contributors](contributors) who have brought you clojure-turtle.
16 |
17 | ## End matter
18 |
19 | Wiki pages are released under a [Creative Commons 4.0 Attribution license](http://creativecommons.org/licenses/by/4.0/)
--------------------------------------------------------------------------------