├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── demo ├── public │ └── index.html └── src │ └── demo.cljs ├── doc ├── img │ ├── frame00.png │ ├── frame01.png │ ├── frame02.png │ ├── frame03.png │ ├── frame04.png │ ├── frame05.png │ ├── frame06.png │ ├── frame07.png │ ├── frame08.png │ ├── frame09.png │ ├── frame10.png │ ├── frame11.png │ ├── frame12.png │ ├── frame13.png │ ├── frame14.png │ ├── frame15.png │ ├── frame16.png │ ├── frame17.png │ ├── frame18.png │ └── frame19.png └── intro.md ├── project.clj ├── src ├── cljc │ └── clojure_turtle │ │ └── core.cljc └── cljs │ └── clojure_turtle │ └── macros.cljc ├── test └── clojure_turtle │ └── core_test.clj └── wiki ├── Animation.md ├── Contributors.md └── Home.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | figwheel_server.log 11 | demo/public/js -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 6 | (CLA), which you can do online. The CLA is necessary mainly because you own the 7 | copyright to your changes, even after your contribution becomes part of our 8 | codebase, so we need your permission to use and distribute your code. We also 9 | need to be sure of various other things—for instance that you'll tell us if you 10 | know that your code infringes on other people's patents. You don't have to sign 11 | the CLA until after you've submitted your code for review and a member has 12 | approved it, but you must do it before we can put your code into our codebase. 13 | Before you start working on a larger contribution, you should get in touch with 14 | us first through the issue tracker with your idea so that we can help out and 15 | possibly guide you. Coordinating up front makes it much easier to avoid 16 | frustration later on. 17 | 18 | ### Code reviews 19 | All submissions, including submissions by project members, require review. We 20 | use Github pull requests for this purpose. 21 | 22 | ### The small print 23 | Contributions made by corporations are covered by a different agreement than 24 | the one above, the Software Grant and Corporate Contributor License Agreement. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2015 Google. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clojure-turtle 2 | 3 | clojure-turtle is a Clojure library that implements the Logo programming language in a Clojure context. [Quil](https://github.com/quil/quil/) is used for rendering. 4 | 5 | Logo is a simple language that is useful in introducing programming to beginners, especially young ones. Logo also happens to be a dialect of Lisp. clojure-turtle tries to maintain those beneficial aspects of Logo while using Clojure and Clojure syntax. The goal is to make learning programming and/or Clojure easier by disguising powerful concepts with fun! 6 | 7 | ## Artifacts 8 | 9 | `clojure-turtle` artifacts are [released to Clojars](https://clojars.org/com.google/clojure-turtle). 10 | 11 | If you are using Maven, add the following repository definition to your `pom.xml`: 12 | 13 | ``` xml 14 | 15 | clojars.org 16 | http://clojars.org/repo 17 | 18 | ``` 19 | 20 | ### The Most Recent Release 21 | 22 | With Leiningen: 23 | 24 | ``` clj 25 | [com.google/clojure-turtle "0.3.0"] 26 | ``` 27 | 28 | With Maven: 29 | 30 | ``` xml 31 | 32 | com.google 33 | clojure-turtle 34 | 0.3.0 35 | 36 | ``` 37 | 38 | ## Installation 39 | 40 | First, [install Leiningen](https://github.com/technomancy/leiningen). 41 | 42 | Second, run a REPL session that has clojure-turtle loaded inside it. This can be done in a couple of ways: 43 | * [Create a new Leiningen project](https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md) and [add the clojure-turtle dependency](https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md#artifact-ids-groups-and-versions) into `project.clj`. Then run `lein repl`. 44 | * Use git to clone the [clojure-turtle repository](https://github.com/google/clojure-turtle), move into the `clojure-turtle` working directory, then run `lein repl`. 45 | 46 | ## Usage 47 | 48 | Load the `clojure-turtle.core` namespace. 49 | 50 | ```clojure 51 | (use 'clojure-turtle.core) 52 | ; WARNING: repeat already refers to: #'clojure.core/repeat in namespace: user, being replaced by: #'clojure-turtle.core/repeat 53 | ;=> nil 54 | ``` 55 | The symbol `repeat` is overridden to behave more like the Logo function, but the Clojure core function is still available as `clojure.core/repeat`. 56 | 57 | Now load a new window that shows our Quil sketch using the `new-window` form. The sketch is where our turtle lives and operates. 58 | 59 | ```clojure 60 | user=> (new-window {:size [300 480]}) 61 | ;=> #'user/example 62 | ``` 63 | 64 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame00.png) 65 | 66 | 67 | ### forward, back, right, left 68 | 69 | It's `forward`, `back`, `right`, and `left` as in Logo. Go forward and back by a length (in pixels). Right and left turn the turtle by an angle, in degrees. It's Clojure syntax, so 'executing commands' (function calls) are done within parentheses. 70 | 71 | ```clojure 72 | (forward 30) 73 | ;=> # 74 | ``` 75 | 76 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame01.png) 77 | 78 | 79 | ```clojure 80 | (right 90) 81 | ; # 82 | ``` 83 | 84 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame02.png) 85 | 86 | ### repeat, all 87 | 88 | `repeat` is like the Logo function, or like Clojure's `repeatedly`. Going from the Logo syntax to clojure-turtle's syntax for `repeat`, commands that are being repeated are put within parentheses notation. The square brackets that group the repeated commands are replaced with `(all ... )`. The equivalent of the Logo `REPEAT 3 [FORWARD 30 RIGHT 90]` would be 89 | 90 | ```clojure 91 | (repeat 3 (all (forward 30) (right 90))) 92 | ;=> # 93 | ``` 94 | 95 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame03.png) 96 | 97 | 98 | Let's see how we can simplify this. 99 | 100 | 101 | ```clojure 102 | (def side (all (forward 30) (right 90))) 103 | ;=> #'user/side 104 | (left 90) 105 | ;=> # 106 | ``` 107 | 108 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame04.png) 109 | 110 | 111 | ```clojure 112 | (repeat 4 side) 113 | ;=> # 114 | ``` 115 | 116 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame05.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame06.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame07.png) 141 | 142 | 143 | ```clojure 144 | (left 45) 145 | (repeat 4 square-and-turn) 146 | ``` 147 | 148 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame08.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame09.png) 162 | 163 | ```clojure 164 | (pendown) 165 | (repeat 4 (all (forward 160) (right 90))) 166 | ``` 167 | 168 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame10.png) 169 | 170 | 171 | ```clojure 172 | (setxy -100 0) 173 | ``` 174 | 175 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame11.png) 176 | 177 | 178 | ```clojure 179 | (setheading 225) 180 | ``` 181 | 182 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame12.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame13.png) 193 | 194 | ```clojure 195 | (home) 196 | ``` 197 | 198 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame14.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame15.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame16.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame17.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame18.png) 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 | ![](https://github.com/google/clojure-turtle/blob/master/doc/img/frame19.png) 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://pixabay.com/static/uploads/photo/2015/10/01/21/39/background-image-967820_960_720.jpg)](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/) --------------------------------------------------------------------------------