├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── curriculum ├── create-something.md ├── first-program.md └── images │ ├── 1-sin-x.png │ ├── 3-sin-x.png │ ├── create-new-file.png │ ├── curve-range.png │ ├── curve.png │ ├── step-1.png │ └── x-y-grid.png ├── images ├── blue_background.png └── white_flake.png ├── project.clj └── src └── drawing ├── core.clj ├── lines.clj ├── practice.clj ├── step1.clj ├── step2.clj ├── step3.clj ├── step4.clj ├── step5.clj └── step6.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | This drawing app is a part of the ClojureBridge 4 | [curriculum](https://github.com/ClojureBridge/curriculum). Updates to 5 | the curriculum are welcome and encouraged! We would not have anything 6 | without the input from the volunteers organizing workshops. 7 | 8 | ## Getting Started 9 | 10 | The curriculum team uses GitHub to review, discuss, and ultimately 11 | accept changes. If you don't have a GitHub account, you'll have to 12 | [sign up](https://github.com/signup/free). If you're new to 13 | [git](http://en.wikipedia.org/wiki/Git_(software)) or collaborating on 14 | shared Git repositories, checkout the 15 | [references](#completely-new-to-git-and-github). 16 | 17 | * [**Fork**](https://github.com/ClojureBridge/drawing/fork) this 18 | repository to your GitHub account. 19 | 20 | * **Clone** your fork to your local computer: 21 | 22 | ```shell 23 | # Run from a terminal environment. 24 | git clone https://github.com//drawing.git 25 | cd drawing/ 26 | ``` 27 | 28 | ## Making Changes 29 | 30 | With the local fork in hand, you're ready to make some changes. 31 | 32 | * First, create a local topic **branch** for your change: 33 | 34 | ```shell 35 | git checkout -b my-new-feature-branch 36 | ``` 37 | 38 | * **Commit** changes to your new branch: 39 | 40 | ```shell 41 | # Create, edit, remove files, etc. 42 | git add . 43 | git commit -m "An informative description of my change." 44 | ``` 45 | 46 | ## Submitting Changes 47 | 48 | After finishing work on your branch, you can submit a 49 | [pull request](https://help.github.com/articles/using-pull-requests/) 50 | for review: 51 | 52 | * **Push** your committed changes from your local fork of the 53 | repository. This sets up a remote branch tracking your local branch: 54 | 55 | ```shell 56 | git push origin -u my-new-feature-branch 57 | ``` 58 | 59 | * Once you're done making your change, you're ready to submit a **pull 60 | request** (PR) to the 61 | [ClojureBridge/drawing](https://github.com/ClojureBridge/drawing) 62 | repository. Pull requests are submitted from your own fork, but will 63 | appear on the upstream repository. After pushing changes to your 64 | fork, you fork's GitHub page will automatically prompt you to submit 65 | a pull request upstream. 66 | 67 | * The ClojureBridge curriculum team will **review** and discuss the 68 | pull request in comments on the PR. Two curriculum team members must 69 | give a thumbs up, then the PR will be accepted. All pull requests 70 | will receive a response, but may need to be updated with new commits 71 | once you've received feedback from the maintainer. 72 | 73 | * After your PR is accepted and merged into `master`, you'll have to 74 | update your own fork. First, set the **upstream** remote repository: 75 | 76 | ```shell 77 | git remote add upstream git://github.com/ClojureBridge/drawing.git 78 | ``` 79 | 80 | and then update your local copy: 81 | 82 | ```shell 83 | git checkout master 84 | git fetch upstream master 85 | git push origin master 86 | ``` 87 | 88 | ## Curriculum Team 89 | 90 | * Wait, why does the curriculum team get to say which PRs get 91 | accepted? I'm glad you asked! If you contribute more than two 92 | patches, you, too, will become part of the curriculum team. 93 | * Curriculum team members are given commit rights to the curriculum. 94 | * Commit rights are meant for approving PRs, not for making direct 95 | commits. 96 | * There is also a 97 | [ClojureBridge curriculum group](https://groups.google.com/forum/#!forum/clojurebridge-curriculum) 98 | for discussing curriculum direction. 99 | 100 | ## Workshop/Chapter Curriculum Forks 101 | 102 | * Workshops or chapters that are using the main ClojureBridge 103 | curriculum should fork the curriculum to their chapter's GitHub (e.g., 104 | [https://github.com/clojurebridge-sf/curriculum](https://github.com/clojurebridge-sf/curriculum)) 105 | * Give teachers commit rights to the chapter's fork of the curriculum. 106 | * The submit pull requests to the main curriculum, if you would like 107 | to contribute the changes back. 108 | 109 | ## Completely New to Git and GitHub 110 | 111 | If this is your first time using Git and GitHub for source code 112 | version control and collaboration, there are some great tutorials on 113 | the web. For Git fundamentals, 114 | [Git Immersion](http://gitimmersion.com/) walks through a lot of basic 115 | usage. [GitHub guides](https://guides.github.com/) also explains 116 | common GitHub collaboration workflows, such as 117 | [forking](https://guides.github.com/activities/forking/). The workflow 118 | described above is sometimes known as the 119 | ["GitHub flow"](https://guides.github.com/introduction/flow/). If you 120 | have any questions, don't hesitate to ask questions to the curriculum 121 | [mailing list](https://groups.google.com/forum/#!forum/clojurebridge-curriculum). 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drawing 2 | 3 | ClojureBridge capstone app using Quil to draw. 4 | 5 | ## Usage 6 | 7 | LightTable - open `lines.clj` and press `Ctrl+Shift+Enter` to evaluate 8 | the file. 9 | 10 | REPL - run `(require 'drawing.lines)`. 11 | 12 | ## Curriculum 13 | 14 | * [Making Your First Program with Quil](curriculum/first-program.md) 15 | * [I Want to Create Something Cool with Quil! (pt. 2)] (curriculum/create-something.md) 16 | 17 | ## Quil and Processing Resources 18 | 19 | * [fun-with-quil](https://github.com/quephird/fun-with-quil) - A repo chock full of very cool Quil sketches from Danielle Kefford 20 | * [Quil](https://github.com/quil/quil) 21 | * [The Nature of Code](http://natureofcode.com/) 22 | * [Processing](https://processing.org/) 23 | 24 | ## License 25 | 26 | Creative Commons License
ClojureBridge Curriculum by ClojureBridge is licensed under a Creative Commons Attribution 4.0 International License. 27 | -------------------------------------------------------------------------------- /curriculum/create-something.md: -------------------------------------------------------------------------------- 1 | I want to create something cool with Quil! 2 | =========================================== 3 | 4 | This is a story of Clara who attended a ClojureBridge workshop recently. 5 | At the workshop, she learned what Clojure is and how to write Clojure code. 6 | That really impressed her, "How functional!" 7 | Also, Clara met an interesting drawing tool, Quil, which is written in Clojure. 8 | When Clara learned how to use Quil, she thought, "I want to create something cool with Quil!" 9 | Here's how Clara developed her own Quil application. 10 | 11 | ## Step 1. Snowflake on a blue background 12 | 13 | Clara considered where to start while looking at her first drawing application. 14 | Then, a small light turned on in her mind, "a white snowflake on a blue background looks nice." 15 | What she wanted to know was how to make a background blue and put a snowflake on it. 16 | Clara already learned how to find the way. It was: 17 | 18 | 1. Go to the API documentation website 19 | 2. Google it 20 | 21 | Following that, Clara went to the Quil API web site, 22 | [http://quil.info/api](http://quil.info/api), and found the 23 | [Loading and Displaying](http://quil.info/api/image/loading-and-displaying) 24 | section. Then, she found the 25 | [background-image](http://quil.info/api/color/setting#background-image) 26 | function. 27 | 28 | Then, she googled and found a StackOverflow question, 29 | [Load/display image in clojure with quil](http://stackoverflow.com/questions/18714941/load-display-image-in-clojure-with-quil), which 30 | looked like helpful. 31 | She also went to ClojureBridge drawing resources section, 32 | [Quil and Processing Resources](https://github.com/ClojureBridge/drawing#quil-and-processing-resources). 33 | Browsing at web sites listed there for a while, she found an interesting Xmas tree 34 | example, 35 | [xmas-tree.clj](https://github.com/quephird/fun-with-quil/blob/master/src/fun_with_quil/animations/xmas-tree.clj), 36 | which displayed a xmas tree read from a file in a window. 37 | "This should be what I want," she delighted in getting those useful search results. 38 | 39 | OK, for now, she got enough information to accomplish step 1. 40 | 41 | At the ClojureBridge workshop, Clara went though the Quil app 42 | tutorial, 43 | [Making Your First Program with Quil](https://github.com/ClojureBridge/drawing/blob/master/curriculum/first-program.md), 44 | so she already had the `drawing` Clojure project. She decided to use 45 | the same project for her own app. 46 | 47 | ### step 1-1: Create a new source file 48 | 49 | Clara added a new file under `src/drawing` directory with the name 50 | `practice.clj`. At this point, her directory structure looks like the 51 | below: 52 | 53 | ``` 54 | drawing 55 | ├── LICENSE 56 | ├── README.md 57 | ├── project.clj 58 | └── src 59 | └── drawing 60 | ├── core.clj 61 | ├── lines.clj 62 | └── practice.clj <-- this file is added in step 1-1 63 | ``` 64 | 65 | ### step 1-2: Add namespace - make the source code clojure-ish 66 | 67 | First, Clojure source code has a namespace declaration, so Clara 68 | copy-pasted `ns` from the top of her `lines.clj` file. But, she 69 | changed the name from `drawing.lines` to `drawing.practice`, because 70 | her new file has the name `practice`. 71 | 72 | At this moment, `practice.clj` looks like this: 73 | 74 | ```clojure 75 | (ns drawing.practice 76 | (:require [quil.core :as q])) 77 | ``` 78 | 79 | ### step 1-3: Add basic Quil code 80 | 81 | Basic Quil code has `setup` and `draw` functions, along with a 82 | `defsketch` macro, which defines the app. Following these Quil rules, 83 | Clara added those things into her `practice.clj`. 84 | 85 | Now, `practice.clj` looks like this: 86 | 87 | ```clojure 88 | (ns drawing.practice 89 | (:require [quil.core :as q])) 90 | 91 | ;; setup and draw functions and q/defsketch are added in step 1-3 92 | (defn setup []) 93 | 94 | (defn draw []) 95 | 96 | (q/defsketch practice 97 | :title "Clara's Quil practice" 98 | :size [500 500] 99 | :setup setup 100 | :draw draw 101 | :features [:keep-on-top]) 102 | ``` 103 | 104 | ### step 1-4: Download and put snowflake and background images 105 | 106 | Looking at the Quil API and the StackOverflow question, Clara learned 107 | that where to put her image files was important. She created a new 108 | directory, `images`, under the top `drawing` directory, and 109 | [put two images there](https://github.com/ClojureBridge/drawing/tree/master/images). 110 | 111 | Now, her directory structure looks like the one below: 112 | 113 | ``` 114 | drawing 115 | ├── LICENSE 116 | ├── README.md 117 | ├── images 118 | │   ├── blue_background.png <-- added in step 1-4 119 | │   └── white_flake.png <-- added in step 1-4 120 | ├── project.clj 121 | └── src 122 | └── drawing 123 | ├── core.clj 124 | ├── lines.clj 125 | └── practice.clj 126 | ``` 127 | 128 | ### step 1-5: Add framework 129 | 130 | So far, the images were ready, the next step was to code using the Quil API. 131 | 132 | Clara was back to the xmas tree example and learned her application needed 133 | one more library to add, `[quil.middleware :as m]` within `:require`. 134 | This was a quite new experience to her. To figure out what's that and how 135 | to use, Clara walked through the document 136 | [Functional mode (fun mode)](https://github.com/quil/quil/wiki/Functional-mode-(fun-mode)). 137 | 138 | When she finished the document, she murmured, "Ha, fun mode, nice 139 | name, isn't it? What I should do here is... to add": 140 | 141 | 1. `[quil.middleware :as m]` in the `ns` form 142 | 2. `:middleware [m/fun-mode]` in the `q/defsketch` form 143 | 3. function argument `state` to `draw` function 144 | 145 | "to my `practice.clj`. OK, let's do it!" 146 | 147 | At this moment, `practice.clj` looks like this: 148 | 149 | ```clojure 150 | (ns drawing.practice 151 | (:require [quil.core :as q] 152 | [quil.middleware :as m])) ;; this line is added in step 1-5 153 | 154 | (defn setup []) 155 | 156 | (defn draw [state]) ;; argument state is added in step 1-5 157 | 158 | (q/defsketch practice 159 | :title "Clara's Quil practice" 160 | :size [500 500] 161 | :setup setup 162 | :draw draw 163 | :features [:keep-on-top] 164 | :middleware [m/fun-mode]) ;; this line is added in step 1-5 165 | ``` 166 | 167 | ### step 1-6: Load snowflake and background images 168 | 169 | Clara already knew the final pieces to load images were: 170 | 171 | 1. in `setup` function, load images and return those as a **state** 172 | 2. in `draw` function, look the contents in `state` argument and draw images 173 | 174 | So, she added a few lines of code to the `setup` and `draw` functions in 175 | her `practice.clj`. She was careful when writing the image filenames 176 | because it should reflect the actual directory structure. 177 | 178 | ### `practice.clj` in step 1 179 | 180 | At this moment, `practice.clj` looks like this: 181 | 182 | ```clojure 183 | (ns drawing.practice 184 | (:require [quil.core :as q] 185 | [quil.middleware :as m])) 186 | 187 | (defn setup [] 188 | ;; these two lines, a map (data structure) is added in step 1-6 189 | {:flake (q/load-image "images/white_flake.png") 190 | :background (q/load-image "images/blue_background.png")} 191 | ) 192 | 193 | (defn draw [state] 194 | ;; q/background-image and q/image functions are added in step 1-6 195 | (q/background-image (:background state)) 196 | (q/image (:flake state) 200 10) 197 | ) 198 | 199 | (q/defsketch practice 200 | :title "Clara's Quil practice" 201 | :size [500 500] 202 | :setup setup 203 | :draw draw 204 | :features [:keep-on-top] 205 | :middleware [m/fun-mode]) 206 | ``` 207 | 208 | When Clara ran this code, she saw this image: 209 | 210 | ![step 1 screenshot](images/step-1.png) 211 | 212 | Woohoo! She made it! 213 | 214 | 215 | #### [bonus] destructuring 216 | 217 | Clojure has a nice feature called, 218 | [destructuring](http://clojurebridge.github.io/community-docs/docs/clojure/destructuring/). 219 | Using the destructuring in a function argument, we can write `draw` function like this: 220 | 221 | ```clojure 222 | (defn draw [{flake :flake background :background}] 223 | (q/background-image background) 224 | (q/image flake 200 10)) 225 | ``` 226 | 227 | Clojurians often use this handy feature. 228 | 229 | 230 | ## Step 2. Snowflake falling down 231 | 232 | Clara was satisfied with the image of the white snowflake on the blue 233 | background. However, that was boring. For the next step, she wanted to 234 | move the snowflake like it was falling down. This needed further Quil 235 | API study and googling. 236 | 237 | As far as she searched, moving some pieces in the image is called 238 | **animation**. The basic idea is: 239 | 240 | - draw the image at some position 241 | - update the position 242 | - draw the image at updated position 243 | 244 | So-called animations repeat these steps again and again. 245 | 246 | ### step 2-1: Add `y` parameter update 247 | 248 | "Well," Clara thought, "What does 'moving the snowflake like it was 249 | falling down' mean in terms of programming?" 250 | 251 | To draw the snowflake, she used Quil's `image` function, described in 252 | the API: 253 | [image](http://quil.info/api/image/loading-and-displaying#image). 254 | 255 | The x and y parameters were 200 and 10 from the upper-left corner, 256 | which was the position she set to draw the snowflake. To make it fall 257 | down, the y parameter should be increased as time goes by. 258 | 259 | ![x and y](images/x-y-grid.png) 260 | 261 | In terms of programming, 'moving the snowflake like it was falling 262 | down' means: 263 | 264 | 1. Set the initial state--for example, `(x, y) = (200, 10)` 265 | 2. Draw the background first, then the snowflake 266 | 3. Update the state - increase the `y` parameter--for example, `(x, y) = (200, 11)` 267 | 4. Draw the background again first, then the snowflake 268 | 5. Repeat 2 and 3, increasing the `y` parameter. 269 | 270 | In her application, "changing state" includes only the `y` 271 | parameter. How could she increment the `y` value by one? 272 | 273 | Yes, Clojure has the `inc` function. This is the function she used. 274 | 275 | To update `y` parameter: 276 | - Add an initial `y` parameter in the map of `setup` function, which 277 | represents **state** 278 | 279 | ```clojure 280 | {:flake (q/load-image "images/white_flake.png") 281 | :background (q/load-image "images/blue_background.png") 282 | :y-param 10} 283 | ``` 284 | 285 | - Add a new function `update` which will increment the `y` parameter by one 286 | 287 | ```clojure 288 | (defn update [state] 289 | ;; updating y paraemter by one 290 | (update-in state [:y-param] inc)) 291 | ``` 292 | 293 | - Add the `update` function in the `q/defsketch` form 294 | 295 | ```clojure 296 | (q/defsketch practice 297 | :title "Clara's Quil practice" 298 | :size [500 500] 299 | :setup setup 300 | :update update 301 | :draw draw 302 | :features [:keep-on-top] 303 | :middleware [m/fun-mode]) 304 | ``` 305 | 306 | ### step 2-2: Draw the image on updated position 307 | 308 | So far, the app got the feature to update `y` parameter; 309 | however, this is not enough to make the snowflake falling down. 310 | The snowflakes should be put on the updated position. 311 | 312 | Clara changed the `draw` function so that `q/image` could have updated 313 | `y` parameter. 314 | 315 | ```clojure 316 | (defn draw [state] 317 | ;; drawing blue background and a snowflake on it 318 | (q/background-image (:background state)) 319 | (q/image (:flake state) 200 (:y-param state))) 320 | ``` 321 | 322 | She added one more function, `(q/smooth)`, to `setup` since this would 323 | make animation move smoothly. 324 | 325 | ### `practice.clj` in step 2 326 | 327 | At this point, `practice.clj` looks like this: 328 | 329 | ```clojure 330 | (ns drawing.practice 331 | (:require [quil.core :as q] 332 | [quil.middleware :as m])) 333 | 334 | (defn setup [] 335 | (q/smooth) ;; added in step 2-2 336 | {:flake (q/load-image "images/white_flake.png") 337 | :background (q/load-image "images/blue_background.png") 338 | :y-param 10} ;; added in step 2-1 339 | ) 340 | 341 | ;; update function is added in step 2-1 342 | (defn update [state] 343 | (update-in state [:y-param] inc)) 344 | 345 | (defn draw [state] 346 | (q/background-image (:background state)) 347 | (q/image (:flake state) 200 (:y-param state)) ;; changed in step 2-2 348 | ) 349 | 350 | (q/defsketch practice 351 | :title "Clara's Quil practice" 352 | :size [500 500] 353 | :setup setup 354 | :update update ;; added in step 2-1 355 | :draw draw 356 | :features [:keep-on-top] 357 | :middleware [m/fun-mode]) 358 | ``` 359 | 360 | When Clara ran this code--hey! She saw the snowflake was falling 361 | down. 362 | 363 | 364 | ## Step 3. Make the snowflake keep falling down from top to bottom 365 | 366 | Clara got a nice Quil app. But, once the snowflake went down beyond 367 | the bottom line, that was it. Only a blue background remained on the 368 | window. So, she wanted it to repeat again and again. 369 | 370 | In other words: if the snowflake reaches the bottom, it should come 371 | back to the top. Then, it should fall down again. 372 | 373 | In terms of programming, what does that mean? 374 | 375 | If the `y` parameter is greater than the height of the image, `y` 376 | parameter should go back to `0`. Otherwise, the `y` parameter should 377 | be incremented by one. That said, there exist two cases to update `y` 378 | parameter. 379 | 380 | Clara recalled `if` was used for 381 | [Flow Control](http://clojurebridge.github.io/curriculum/outline/flow_control.html) 382 | at the ClojureBridge workshop. It looked `if` would handle the two cases 383 | well like this: 384 | 385 | ```clojure 386 | (defn update [state] 387 | (if (>= (:y-param state) (q/height)) ;; y-param is greater than or equal to image height? 388 | (assoc state :y-param 0) ;; true - get it back to the 0 (top) 389 | (update-in state [:y-param] inc) ;; false - update y paraemter by one 390 | )) 391 | ``` 392 | 393 | So, she used `if` to make the snowflake go back to the top in the 394 | update function. 395 | 396 | ### `practice.clj` in step 3 397 | 398 | At this point, `practice.clj` looks like this: 399 | 400 | ```clojure 401 | (ns drawing.practice 402 | (:require [quil.core :as q] 403 | [quil.middleware :as m])) 404 | 405 | (defn setup [] 406 | (q/smooth) 407 | {:flake (q/load-image "images/white_flake.png") 408 | :background (q/load-image "images/blue_background.png") 409 | :y-param 10}) 410 | 411 | (defn update [state] 412 | ;; these three lines were added in step 3 413 | (if (>= (:y-param state) (q/height)) 414 | (assoc state :y-param 0) 415 | (update-in state [:y-param] inc))) 416 | 417 | (defn draw [state] 418 | (q/background-image (:background state)) 419 | (q/image (:flake state) 200 (:y-param state))) 420 | 421 | (q/defsketch practice 422 | :title "Clara's Quil practice" 423 | :size [500 500] 424 | :setup setup 425 | :update update 426 | :draw draw 427 | :features [:keep-on-top] 428 | :middleware [m/fun-mode]) 429 | ``` 430 | 431 | Clara saw the snowflake appeared from the top after it went down below 432 | the bottom line. 433 | 434 | 435 | ## Step 4. Make more snowflakes falling down from top to bottom 436 | 437 | Clara thought, "It's nice to look at the snowflake falls down many 438 | times. But I have only one snowflake. Can I add more?" 439 | She wanted to see more snowflakes falling down: one or more on the left 440 | half, as well as one or more on the right half. 441 | 442 | Again, she needed to express her thoughts by the words of programming 443 | world. Looking at her code already written, she figures out, "This 444 | would be 'draw multiple images with the different `x` parameters.'" 445 | The easiest way would be to copy-paste `(q/image (:flake state) 200 (:y-param state)` 446 | multiple times with the different `x` parameters. For example, 447 | 448 | ```clojure 449 | (q/image (:flake state) 10 (:y-param state)) 450 | (q/image (:flake state) 200 (:y-param state)) 451 | (q/image (:flake state) 390 (:y-param state)) 452 | ``` 453 | 454 | But, for Clara, this did not look nice; she learned a lot about 455 | Clojure and wanted to use what she knew. 456 | 457 | First, she thought about how to keep multiple `x` parameters. She 458 | remembered there was a `Vectors` in the 459 | [Data Structures](http://clojurebridge.github.io/curriculum/outline/data_structures.html) 460 | section, which looked a good fit in this case. 461 | 462 | Here's what she did to add more snowflakes: 463 | 464 | 1. Add a vector which has multiple `x` parameters assigned to a name 465 | with `def`. 466 | 467 | ```clojure 468 | (def x-params [10 200 390]) ;; x parameters for three snowflakes 469 | ``` 470 | 471 | 2. Draw snowflakes as many times as the number of `x`-params using `doseq`. 472 | 473 | ```clojure 474 | (doseq [x x-params] 475 | (q/image (:flake state) x (:y-param state))) 476 | ``` 477 | 478 | * See, [doseq](http://clojurebridge.github.io/curriculum/outline/sequences.html#/3) 479 | 480 | ### `practice.clj` in step 4 481 | 482 | At this point, `practice.clj` looks like this: 483 | 484 | ```clojure 485 | (ns drawing.practice 486 | (:require [quil.core :as q] 487 | [quil.middleware :as m])) 488 | 489 | (def x-params [10 200 390]) ;; added in step 4 490 | 491 | (defn setup [] 492 | (q/smooth) 493 | {:flake (q/load-image "images/white_flake.png") 494 | :background (q/load-image "images/blue_background.png") 495 | :y-param 10}) 496 | 497 | (defn update [state] 498 | (if (>= (:y-param state) (q/height)) 499 | (assoc state :y-param 0) 500 | (update-in state [:y-param] inc))) 501 | 502 | (defn draw [state] 503 | (q/background-image (:background state)) 504 | (doseq [x x-params] ;; two lines were changed 505 | (q/image (:flake state) x (:y-param state)))) ;; in step 4 506 | 507 | (q/defsketch practice 508 | :title "Clara's Quil practice" 509 | :size [500 500] 510 | :setup setup 511 | :update update 512 | :draw draw 513 | :features [:keep-on-top] 514 | :middleware [m/fun-mode]) 515 | ``` 516 | 517 | "Yes!" Clara shouted when she saw three snowflakes kept falling down. 518 | 519 | 520 | ## Step 5. Make snowflakes keep falling down at different speed 521 | 522 | Although her app looked lovely, Clara felt something was not quite 523 | right. In her window, all three snowflakes fell down at the same 524 | speed like a robots' march. It did not look natural. So, she wanted to 525 | make them fall down at different speed. 526 | 527 | Using programming terms, the problem here is that all three snowflakes 528 | share the same `y` parameter. 529 | Given that using multiple `y` parameters would solve the problem--but how? 530 | 531 | ### step 5-1 Change `y` parameters to maps with speeds 532 | 533 | As she used `vector` for the x parameters, the `vector` would be a good data 534 | structure to have different `y` parameters as well. However, this 535 | should not be a simple vector since each snowflake will have two 536 | parameters, height and speed. Having these two, Clara could change the 537 | falling speed of each snowflake. 538 | 539 | Well, she was back to ClojureBridge curriculum and went to 540 | [Data Structures](http://clojurebridge.github.io/curriculum/outline/data_structures.html). 541 | There was a data structure called `Maps` which allowed her to save 542 | multiple parameters. Looking at maps examples, she changed the `y-param` from a 543 | single value to a vector of 3 maps. Also, the keyword was changed from 544 | `y-param` to `y-params`. Now her initial **state** became this: 545 | 546 | ```clojure 547 | {:flake (q/load-image "images/white_flake.png") 548 | :background (q/load-image "images/blue_background.png") 549 | :y-params [{:y 10 :speed 1} {:y 150 :speed 4} {:y 50 :speed 2}]} 550 | ``` 551 | 552 | It was a nice data structure, actually maps in a vector in a map 553 | including outer map. Downside was, her `update` function would not be 554 | simple anymore. What she had to do was updating all `y` values in 555 | the three maps within a vector. 556 | 557 | 558 | ### step 5-2 Update values in maps in the vector 559 | 560 | Thinking both map and vector at the same time was confusing to her, so 561 | she decided to think about map only. Each map has `y` parameter and 562 | `speed`: 563 | 564 | ```clojure 565 | {:y 150 :speed 4} 566 | ``` 567 | as an initial **state**. The very next moment, speed should be added 568 | to y value. As a result, the map should be updated to: 569 | 570 | ```clojure 571 | {:y 154 :speed 4} 572 | ``` 573 | 574 | To accomplish this map update, Clara added `update-y` function: 575 | 576 | ```clojure 577 | (defn update-y 578 | [m] 579 | (let [y (:y m) 580 | speed (:speed m)] 581 | (if (>= y (q/height)) ;; y is greater than or equal to image height? 582 | (assoc m :y 0) ;; true - get it back to the 0 (top) 583 | (update-in m [:y] + speed)))) ;; false - add y value and speed 584 | ``` 585 | 586 | #### [bonus] destructuring 587 | 588 | Using Clojure's destructuring, we can write `update-y` function like 589 | this: 590 | 591 | ```clojure 592 | (defn update-y 593 | [{y :y speed :speed :as m}] 594 | (if (>= y (q/height)) 595 | (assoc m :y 0) 596 | (update-in m [:y] + speed))) 597 | ``` 598 | 599 | As in the code, we can skip let binding. 600 | 601 | 602 | ### step 5-3 Update maps in the vector 603 | 604 | Next step is to update maps in the vector using `update-y` function. 605 | Before writing this part, Clara cut down the problem to focus on 606 | updating a vector: how to update contents in a vector. She remembered 607 | there was a `map` function which allowed her to apply a function to 608 | each element in the vector. 609 | [`map` function](http://clojurebridge.github.io/curriculum/outline/functions.html#/9) 610 | 611 | For example: 612 | 613 | ```clojure 614 | (map inc [1 2 3]) ;=> (2 3 4) 615 | ``` 616 | 617 | > Clojure has a `map` function and `map` data structure. 618 | > Be careful, this is confusing. 619 | > In Python, function is a `map`, data structure is dictionary. 620 | > In Ruby, function is a `map` or `collect`, data structure is hash. 621 | 622 | She tested the function on the insta-REPL: 623 | 624 | ```clojure 625 | (defn update-test 626 | [m] 627 | (let [y (:y m) 628 | speed (:speed m)] 629 | (if (>= y 500) 630 | (assoc m :y 0) 631 | (update-in m [:y] + speed)))) 632 | 633 | (def y-params [{:y 10 :speed 1} {:y 150 :speed 4} {:y 50 :speed 2}]) 634 | 635 | (map update-test y-params) 636 | ;=> ({:y 11, :speed 1} {:y 154, :speed 4} {:y 52, :speed 2}) 637 | ``` 638 | 639 | It looked good, so she got back to her `practice.clj` file to change 640 | `update` function. This function should return the **state** as the 641 | map which includes `:y-params` key with the update vector as a value. 642 | Her `update` function became like this: 643 | 644 | ```clojure 645 | (defn update [state] 646 | (let [y-params (:y-params state)] 647 | (assoc state :y-params (map update-y y-params)))) 648 | ``` 649 | 650 | 651 | ### step 5-4 Update draw function to see maps in the vector 652 | 653 | Another challenge was the `draw` function change to see values in the 654 | maps which were in the vector. 655 | Clara found a couple of ways to repeat something in Clojure. Among 656 | them, she chose `dotimes` and `nth` to repeatedly draw images; the 657 | `nth` function is the one in the curriculum: 658 | [Data Structures](http://clojurebridge.github.io/curriculum/outline/data_structures.html#/6) 659 | 660 | In this case, she knew there were exactly 3 snowflakes, so she changed 661 | the code to draw 3 snowflakes as shown below: 662 | 663 | ```clojure 664 | (let [y-params (:y-params state)] 665 | (dotimes [n 3] 666 | (q/image (:flake state) (nth x-params n) (:y (nth y-params n))))) 667 | ``` 668 | 669 | ### `practice.clj` in step 5 670 | 671 | At this point, her entire `practice.clj` looks like this: 672 | 673 | ```clojure 674 | (ns drawing.practice 675 | (:require [quil.core :as q] 676 | [quil.middleware :as m])) 677 | 678 | (def x-params [10 200 390]) 679 | 680 | (defn setup [] 681 | (q/smooth) 682 | {:flake (q/load-image "images/white_flake.png") 683 | :background (q/load-image "images/blue_background.png") 684 | :y-params [{:y 10 :speed 1} {:y 150 :speed 4} {:y 50 :speed 2}]}) ;; changed in step 5-1 685 | 686 | ;; update-y function was added in step 5-2 687 | (defn update-y 688 | [m] 689 | (let [y (:y m) 690 | speed (:speed m)] 691 | (if (>= y (q/height)) 692 | (assoc m :y 0) 693 | (update-in m [:y] + speed)))) 694 | 695 | (defn update [state] 696 | (let [y-params (:y-params state)] ;; update function 697 | (assoc state :y-params (map update-y y-params)))) ;; was changed in step 5-3 698 | 699 | (defn draw [state] 700 | (q/background-image (:background state)) 701 | (let [y-params (:y-params state)] ;; three lines below were changed in step 5-4 702 | (dotimes [n 3] 703 | (q/image (:flake state) (nth x-params n) (:y (nth y-params n)))))) 704 | 705 | (q/defsketch practice 706 | :title "Clara's Quil practice" 707 | :size [500 500] 708 | :setup setup 709 | :update update 710 | :draw draw 711 | :features [:keep-on-top] 712 | :middleware [m/fun-mode]) 713 | ``` 714 | 715 | When she ran the code, three snowflakes kept falling down at 716 | different speeds. It looked more natural. 717 | 718 | 719 | ## Step 6. Do some "refactoring" 720 | 721 | Clara looked at her `practice.clj` thinking her code got longer for a while. 722 | 723 | Scanning her code from top to bottom again, she thought 724 | "`x-params` may be part of the **state**," for example: 725 | 726 | ```clojure 727 | [{:x 10 :y 10 :speed 1} {:x 200 :y 150 :speed 4} {:x 390 :y 50 :speed 2}] 728 | ``` 729 | 730 | She found that this new data structure was easy to maintain the state of each snowflake. 731 | 732 | To use this new data structure, she changed her `setup` function to 733 | return the initial **state** which included x parameters also. The key 734 | name was changed from `:y-params` to `:params`: 735 | 736 | ```clojure 737 | {:flake (q/load-image "images/white_flake.png") 738 | :background (q/load-image "images/blue_background.png") 739 | :params [{:x 10 :y 10 :speed 1} 740 | {:x 200 :y 150 :speed 4} 741 | {:x 390 :y 50 :speed 2}]} 742 | ``` 743 | 744 | Clara stared at `update-y` function and concluded to leave as it was. 745 | Since existence of `:x` and its value didn't affect updating y value. 746 | The function returned the map with three key-value pairs with updated y value. 747 | 748 | What about `update` function? This needed a little change since key 749 | name was changed from `:y-params` to `:params`. 750 | 751 | ```clojure 752 | (defn update [state] 753 | (let [params (:params state)] 754 | (assoc state :params (map update-y params)))) 755 | ``` 756 | 757 | The `draw` function would have a bigger change since the way to 758 | extract x values was changed. 759 | At first, Clara changed `dotimes` function like this: 760 | 761 | ```clojure 762 | (let [params (:params state)] 763 | (dotimes [n 3] 764 | (q/image (:flake state) (:x (nth params n)) (:y (nth params n))))) 765 | ``` 766 | 767 | But, the exact the same thing, `(nth params n)`, appeared twice. 768 | "Is there anything better to avoid repetition?" she thought. 769 | The answer was easy - use `let` binding within `dotimes` function. 770 | Using `let`, her `dotimes` form turned to: 771 | 772 | ```clojure 773 | (let [params (:params state)] 774 | (dotimes [n 3] 775 | (let [param (nth params n)] 776 | (q/image (:flake state) (:x param) (:y param))))) 777 | ``` 778 | 779 | The last line got much cleaner! 780 | 781 | ### `practice.clj` in step 6 782 | 783 | At this moment, her entire `practice.clj` looks like this: 784 | 785 | ```clojure 786 | (ns drawing.practice 787 | (:require [quil.core :as q] 788 | [quil.middleware :as m])) 789 | 790 | (defn setup [] 791 | (q/smooth) 792 | {:flake (q/load-image "images/white_flake.png") 793 | :background (q/load-image "images/blue_background.png") 794 | :params [{:x 10 :y 10 :speed 1} ;; changed in step 6 795 | {:x 200 :y 150 :speed 4} ;; 796 | {:x 390 :y 50 :speed 2}]}) ;; 797 | 798 | (defn update-y 799 | [m] 800 | (let [y (:y m) 801 | speed (:speed m)] 802 | (if (>= y (q/height)) 803 | (assoc m :y 0) 804 | (update-in m [:y] + speed)))) 805 | 806 | (defn update [state] 807 | (let [params (:params state)] ;; changed to params in step 6 808 | (assoc state :params (map update-y params)))) ;; 809 | 810 | (defn draw [state] 811 | (q/background-image (:background state)) 812 | (let [params (:params state)] ;; changed in step 6 813 | (dotimes [n 3] ;; 814 | (let [param (nth params n)] ;; 815 | (q/image (:flake state) (:x param) (:y param)))))) ;; 816 | 817 | (q/defsketch practice 818 | :title "Clara's Quil practice" 819 | :size [500 500] 820 | :setup setup 821 | :update update 822 | :draw draw 823 | :features [:keep-on-top] 824 | :middleware [m/fun-mode]) 825 | ``` 826 | 827 | She saw the exact same result as the step 5, but her code 828 | looked nicer. This sort of work is often called "refactoring". 829 | 830 | ## Step 7. Make snowflakes swing as falling down 831 | 832 | Clara was getting much familiar with Clojure coding. Her Quil app was 833 | getting much more fantastic, as well! 834 | 835 | It was amusing to look at snowflakes falling down in different speeds. 836 | But, something she didn't like was... all of the snowflakes were 837 | falling straight down. This was much better than marching robots: 838 | however, it would be awesome if snowflakes were swinging left and 839 | right as falling down. 840 | 841 | In the words of programming, the `x` parameter should either increase or 842 | decrease when the value is updated. This means that the `update` 843 | function should update the `x` parameters as well as the `y` 844 | parameters so that snowflakes took a trace like this: 845 | 846 | ![curve to fall down](images/curve.png) 847 | 848 | This would be a big challenge to her. 849 | 850 | ### step 7-1 Add swing parameter to initial **state** 851 | 852 | Clara already knew from her experience on y value: different swing parameters 853 | to three x values would give her nice swing motion. 854 | She changed the initial **state** to have swing parameter like this: 855 | 856 | ```clojure 857 | [{:x 10 :swing 1 :y 10 :speed 1} 858 | {:x 200 :swing 3 :y 100 :speed 4} 859 | {:x 390 :swing 2 :y 50 :speed 2}] 860 | ``` 861 | 862 | The `swing` parameters should work to give different ranges between 863 | leftmost and rightmost of the curve. 864 | 865 | ![swing of curve](images/curve-range.png) 866 | 867 | 868 | Her `setup` function became like this: 869 | 870 | ```clojure 871 | (defn setup [] 872 | (q/smooth) 873 | {:flake (q/load-image "images/white_flake.png") 874 | :background (q/load-image "images/blue_background.png") 875 | :params [{:x 10 :swing 1 :y 10 :speed 1} 876 | {:x 200 :swing 3 :y 100 :speed 4} 877 | {:x 390 :swing 2 :y 50 :speed 2}]}) 878 | ``` 879 | 880 | ### step 7-2 Calculate x values to swing snowflakes 881 | 882 | Updating x values were quite similar to the one for y values. 883 | Like Clara added the `update-y` function, she was about to write 884 | `update-x` function. But she stopped and thought, "How can I calculate 885 | updated x values?" sketching a curve in her mind. 886 | 887 | She went to Quil api document and scanned the functions thinking some 888 | might have helped her. She found 889 | [`sin`](http://quil.info/api/math/trigonometry#sin) function which 890 | was to calculate the sine of an angle. Also she recalled what was the shape 891 | of sine curve. 892 | 893 | The curve she want had roughly the shape of: 894 | 895 | ``` 896 | x = sin(y) 897 | ``` 898 | 899 | If she considered the size of window, a couple more parameters were 900 | needed to make swing look nice, for example: 901 | 902 | ``` 903 | x = x + a * sin(y / b) 904 | ``` 905 | 906 | The parameter `a` exactly works as the `swing` she added to the **state**. 907 | When the `swing` is 1, the curve traces like in the left image. While 908 | the `swing` is 3, the curve becomes the right image. 909 | 910 | ![swing is 1](images/1-sin-x.png) ![swing is 3](images/3-sin-x.png) 911 | 912 | The parameter `b` adjusts distances between peeks. If the value is 913 | small, the snowflake goes left and right busily. On the other hand, if 914 | the value is big, the snowflakes moves loosely. Thinking of the size 915 | of window, 50 would be a good number for `b`. 916 | Given that, the update function would be: 917 | 918 | ``` 919 | x = x + swing * sin(y / 50) 920 | ``` 921 | 922 | Not just update x values, the function should handle the cases x is 923 | smaller than 0 (too left), and x is greater than image width (too 924 | right). When the x value goes to less than `0`, it should take the value of the 925 | image width, so that the snowflake will appear from the right. 926 | Likewise, when the x value goes to more than the image width, it 927 | should have value `0` so that the snowflake will appear from the left. 928 | Reflecting this conditions, her `update-x` function became 929 | like this: 930 | 931 | ```clojure 932 | (defn update-x 933 | [m] 934 | (let [x (:x m) 935 | swing (:swing m) 936 | y (:y m)] 937 | (cond 938 | (< x 0) (assoc m :x (q/width)) ;; too left 939 | (< x (q/width)) (update-in m [:x] + (* swing (q/sin (/ y 50)))) ;; within frame 940 | :else (assoc m :x 0)))) ;; too right 941 | ``` 942 | 943 | In this function, she couldn't use `if` anymore since `if` takes only one 944 | predicate (comparison). Instead of `if`, she used `cond` which allowed 945 | her to handle multiple comparisons. 946 | 947 | 948 | ### step 7-3 Update x and y values in maps in the vector 949 | 950 | Clara got `update-y` and `update-x` functions. The next step would be 951 | to use these functions to update x and y values in maps in the vector. 952 | This would not be a big deal for her anymore. Tricky thing was it 953 | needed after updating y values in maps, it should update x values in 954 | the same maps. Luckily, Clojure's `let` binding treats this sort of 955 | value changes well. Within `let`, each binding is evaluated from the 956 | first to last one by one. The `update` function turned to this: 957 | 958 | ```clojure 959 | (defn update [state] 960 | (let [params (:params state) 961 | params (map update-y params) 962 | params (map update-x params)] 963 | (assoc state :params params))) 964 | ``` 965 | 966 | The first binding assigns params vector to a name, `params`. The second 967 | binding updates y values in the `params` (maps in vector) and assigns to the 968 | name, `params`. The third binding updates x values in the `params` 969 | (maps in vector) and assigns to the name, `params`. When `params` 970 | comes to the body of `let` function, in other words, the line of 971 | `assoc`, both x and y values are already updated. 972 | 973 | These were all to swing the snowflakes. She could use `draw` function 974 | as it was. 975 | 976 | 977 | ### `practice.clj` in step 7 978 | 979 | At this point, her entire `practice.clj` looks like this: 980 | 981 | ```clojure 982 | (ns drawing.practice 983 | (:require [quil.core :as q] 984 | [quil.middleware :as m])) 985 | 986 | (defn setup [] 987 | (q/smooth) 988 | {:flake (q/load-image "images/white_flake.png") 989 | :background (q/load-image "images/blue_background.png") 990 | :params [{:x 10 :swing 1 :y 10 :speed 1} ;; swing was added in step 7-1 991 | {:x 200 :swing 3 :y 100 :speed 4} ;; 992 | {:x 390 :swing 2 :y 50 :speed 2}]}) ;; 993 | 994 | ;; this update-x function was added in step 7-2 995 | (defn update-x 996 | [m] 997 | (let [x (:x m) 998 | swing (:swing m) 999 | y (:y m)] 1000 | (cond 1001 | (< x 0) (assoc m :x (q/width)) 1002 | (< x (q/width)) (update-in m [:x] + (* swing (q/sin (/ y 50)))) 1003 | :else (assoc m :x 0)))) 1004 | 1005 | (defn update-y 1006 | [m] 1007 | (let [y (:y m) 1008 | speed (:speed m)] 1009 | (if (>= y (q/height)) 1010 | (assoc m :y 0) 1011 | (update-in m [:y] + speed)))) 1012 | 1013 | (defn update [state] 1014 | (let [params (:params state) 1015 | params (map update-y params) 1016 | params (map update-x params)] ;; added in step 7-3 1017 | (assoc state :params params))) 1018 | 1019 | (defn draw [state] 1020 | (q/background-image (:background state)) 1021 | (let [params (:params state)] 1022 | (dotimes [n 3] 1023 | (let [param (nth params n)] 1024 | (q/image (:flake state) (:x param) (:y param)))))) 1025 | 1026 | (q/defsketch practice 1027 | :title "Clara's Quil practice" 1028 | :size [500 500] 1029 | :setup setup 1030 | :update update 1031 | :draw draw 1032 | :features [:keep-on-top] 1033 | :middleware [m/fun-mode]) 1034 | ``` 1035 | 1036 | When Clara ran this code, she saw snowflakes were falling down, 1037 | swinging left and right tracing sine curve. 1038 | "Cool!" she shouted with joy. 1039 | 1040 | Still, she could a couple more improvements including refactoring, 1041 | Clara was satisfied with her app. Moreover, she started 1042 | thinking about her next, more advanced app in Clojure! 1043 | 1044 | The End. 1045 | 1046 | 1047 | -------------- 1048 | Snowflake is designed by Freepik, http://www.flaticon.com/packs/snowflakes 1049 | -------------------------------------------------------------------------------- /curriculum/first-program.md: -------------------------------------------------------------------------------- 1 | Making Your First Program with Quil 2 | =================================== 3 | 4 | Now that you know a bit about how to write Clojure code, let's look at 5 | how to create a standalone application. 6 | 7 | In order to do that, you'll first create a *project*. You'll learn how 8 | to organize your project with *namespaces*. You'll also learn how to 9 | specify your project's *dependencies*. Finally, you'll learn how to 10 | *build* your project to create the standalone application. 11 | 12 | ## Create a Project 13 | 14 | Up until now you've been experimenting in a REPL. Unfortunately, all 15 | the work you do in a REPL is lost when you close the REPL. You can 16 | think of a project as a permanent home for your code. You'll be using 17 | a tool called "Leiningen" to help you create and manage your 18 | project. To create a new project, run this command: 19 | 20 | ```clojure 21 | lein new quil drawing 22 | ``` 23 | 24 | This should create a directory structure that looks like this: 25 | 26 | ``` 27 | drawing 28 | ├── LICENSE 29 | ├── README.md 30 | ├── project.clj 31 | └── src 32 | └── drawing 33 | └── core.clj 34 | ``` 35 | 36 | There's nothing inherently special or Clojure-y about this project 37 | skeleton. It's just a convention used by Leiningen. You'll be using 38 | Leiningen to build and run Clojure apps, and Leiningen expects your 39 | app to be laid out this way. Here's the function of each part of the 40 | skeleton: 41 | 42 | - `project.clj` is a configuration file for Leiningen. It helps 43 | Leiningen answer questions like, "What dependencies does this 44 | project have?" and "When this Clojure program runs, what function 45 | should get executed first?" 46 | - `src/drawing/core.clj` is where the Clojure code goes 47 | 48 | This uses a Clojure library, [Quil](https://github.com/quil/quil), that creates drawings called 49 | sketches. 50 | 51 | Now let's go ahead and actually run the Quil sketch. Open up Nightcode 52 | and Import - find the drawing folder and click. Open the file `src/drawing/core.clj` 53 | 54 | On the bottom of the right side: 55 | 56 | 1. click Run with REPL 57 | 2. click Reload File 58 | 59 | Run with REPL may take a while to startup. Once you see the prompt, `user=>`, on the bottom window, you can click Reload. 60 | 61 | A window will pop up and a circle bouncing, hitting walls within. 62 | 63 | You may close the pop up-ed window by clicking a close (X) icon on the top left. 64 | 65 | 66 | ## Modify Project 67 | 68 | Let's create another Quil sketch. In Nightcode, select drawing on the left side of 69 | directory tree. click New File on the top of right side window. 70 | 71 | 72 | ![Create a new file](images/create-new-file.png) 73 | 74 | Enter `lines.clj` as the name. 75 | 76 | 77 | ## Organization 78 | 79 | As your programs get more complex, you'll need to organize them. You 80 | organize your Clojure code by placing related functions and data in 81 | separate files. Clojure expects each file to correspond to a 82 | *namespace*, so you must *declare* a namespace at the top of each 83 | file. 84 | 85 | Until now, you haven't really had to care about namespaces. Namespaces 86 | allow you to define new functions and data structures without worrying 87 | about whether the name you'd like is already taken. For example, you 88 | could create a function named `println` within the custom namespace 89 | `my-special-namespace`, and it would not interfere with Clojure's 90 | built-in `println` function. You can use the *fully-qualified name* 91 | `my-special-namespace/println` to distinguish your function from the 92 | built-in `println`. 93 | 94 | Create a namespace in the file `src/drawing/lines.clj`. Open it, and 95 | type the following: 96 | 97 | ```clojure 98 | (ns drawing.lines) 99 | ``` 100 | 101 | This line establishes that everything you define in this file will be 102 | stored within the `drawing.lines` namespace. 103 | 104 | Before going forward, click Save on the top menu bar. 105 | 106 | 107 | ## Dependencies 108 | 109 | The final part of working with projects is managing their 110 | *dependencies*. Dependencies are just code libraries that others have 111 | written which you can incorporate in your own project. 112 | 113 | To add a dependency, open `project.clj`. You should see a section 114 | which reads 115 | 116 | ```clj 117 | :dependencies [[org.clojure/clojure "1.8.0"] 118 | [quil "2.4.0"]]) 119 | ``` 120 | 121 | This is where our dependencies are listed. All the dependencies we 122 | need for this project are already included. 123 | 124 | In order to use these libraries, we have to _require_ them in our own 125 | project. In `src/drawing/lines.clj`, edit the ns statement you typed 126 | before: 127 | 128 | ```clojure 129 | (ns drawing.lines 130 | (:require [quil.core :as q])) 131 | ``` 132 | 133 | This gives us access to the library we will need to make our project. 134 | 135 | There are a couple of things going on here. First, the `:require` in 136 | `ns` tells Clojure to load other namespaces. The `:as` part of 137 | `:require` creates an *alias* for a namespace, letting you refer to 138 | its definitions without having to type out the entire namespace. For 139 | example, you can use `q/fill` instead of `quil.core/fill`. 140 | 141 | Before going forward, don't forget to save the file. 142 | Click Save on the top menu bar when you change the code. 143 | 144 | 145 | ## Your first real program 146 | 147 | ### Drawing with Quil 148 | 149 | Quil is a Clojure library that provides the powers of [Processing](https://processing.org/), a 150 | tool that allows you to create drawings and animations. We will use 151 | the functions of Quil to create some of our own drawings. 152 | 153 | We will define our own functions, like so... 154 | 155 | ```clojure 156 | (defn draw [] 157 | ; Do some things 158 | ) 159 | ``` 160 | 161 | ... that call functions that Quil provides, like so... 162 | 163 | ```clojure 164 | ; Call the quil background function 165 | (q/background 240) 166 | ``` 167 | 168 | Put it together: 169 | ```clojure 170 | (defn draw [] 171 | ; Call the quil background function 172 | (q/background 240) 173 | ) 174 | ``` 175 | 176 | In order to create a drawing (or sketch in Quil lingo) with Quil, you 177 | have to define the `setup`, `draw`, and `sketch` functions. `setup` is 178 | where you set the stage for your drawing. `draw` happens repeatedly, 179 | so that is where the action of your drawing happens. `sketch` is the 180 | stage itself. Let's define these functions together, and you will see 181 | what they do. 182 | 183 | In Nightcode, in the lines.clj file, add the following after the 184 | closing parenthesis of the ns statement from before. 185 | 186 | ```clojure 187 | (defn setup [] 188 | 189 | (q/frame-rate 30) 190 | 191 | (q/color-mode :rgb) 192 | 193 | (q/stroke 255 0 0)) 194 | ``` 195 | 196 | This is the `setup` function that sets the stage for the 197 | drawing. First, we call quil's `frame-rate` function to say that the 198 | drawing should be redrawn 30 times per second. We put `q/` in front to 199 | say that this is `frame-rate` from quil. Look up at the ns 200 | statement. Since it says `:as q`, we can use q as a short hand for 201 | quil, and `library-name/function-name` is the way you call a function 202 | from a library. 203 | 204 | Second, we set the color mode to RGB. 205 | 206 | Third, we set the color of the lines we will draw with `stroke`. The 207 | code 255 0 0 represents red. You can [look up RGB codes](http://xona.com/colorlist/) for other 208 | colors if you would like to try something else. 209 | 210 | In Nightcode, in the lines.clj file, add the following after the 211 | closing parenthesis of the setup function. 212 | 213 | ```clojure 214 | (defn draw [] 215 | 216 | (q/line 0 0 (q/mouse-x) (q/mouse-y)) 217 | 218 | (q/line 200 0 (q/mouse-x) (q/mouse-y)) 219 | 220 | (q/line 0 200 (q/mouse-x) (q/mouse-y)) 221 | 222 | (q/line 200 200 (q/mouse-x) (q/mouse-y))) 223 | ``` 224 | 225 | Here we call the quil `line` function four times. We also call two 226 | functions repeatedly as the arguments to the `line` function: 227 | `mouse-x` and `mouse-y`. These get the current position (x and y 228 | coordinates on a 2d plane) of the mouse. The `line` function takes 229 | four arguments - two sets of x, y coordinates. The first x and y are 230 | the starting position of the line. The second x and y are the ending 231 | position of the line. So we start each of these lines at a fixed 232 | position, then end them wherever the mouse is when the sketch is 233 | drawn. 234 | 235 | ```clojure 236 | (q/defsketch hello-lines 237 | 238 | :title "You can see lines" 239 | 240 | :size [500 500] 241 | 242 | :setup setup 243 | 244 | :draw draw 245 | 246 | :features [:keep-on-top]) 247 | ``` 248 | 249 | This is our sketch. You can set attributes of the sketch such as the 250 | title and size. You also tell it what are the names of the setup and 251 | draw functions. These have to match exactly the function names we used 252 | above. The last line is to make our drawing app window keep on top 253 | of everything else. 254 | 255 | Now click - Run with REPL - Reload File - which evaluates the file. 256 | Your drawing should appear. 257 | 258 | If not, try - Save file - Stop - Run with REPL - Reload File. 259 | 260 | 261 | ### Exercise: Rainbow lines 262 | 263 | Update your drawing so that: 264 | 265 | * the lines are a different color 266 | * the title is different 267 | * the lines start at a different place 268 | 269 | Bonus: Make each of the four lines a different color. 270 | 271 | Bonus #2: Change the color of the lines based on the mouse position. 272 | 273 | Hint: You can browse the [Quil API](http://quil.info/api) for ideas and function definitions. 274 | 275 | Hint: You may think this helpful: the [Quil Cheatsheet](https://github.com/ClojureBridge/curriculum/blob/gh-pages/outline/cheatsheet-quil.md) to see selected APIs for ClojureBridge curriculum. 276 | -------------------------------------------------------------------------------- /curriculum/images/1-sin-x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/curriculum/images/1-sin-x.png -------------------------------------------------------------------------------- /curriculum/images/3-sin-x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/curriculum/images/3-sin-x.png -------------------------------------------------------------------------------- /curriculum/images/create-new-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/curriculum/images/create-new-file.png -------------------------------------------------------------------------------- /curriculum/images/curve-range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/curriculum/images/curve-range.png -------------------------------------------------------------------------------- /curriculum/images/curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/curriculum/images/curve.png -------------------------------------------------------------------------------- /curriculum/images/step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/curriculum/images/step-1.png -------------------------------------------------------------------------------- /curriculum/images/x-y-grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/curriculum/images/x-y-grid.png -------------------------------------------------------------------------------- /images/blue_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/images/blue_background.png -------------------------------------------------------------------------------- /images/white_flake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClojureBridge/drawing/5feabef315c4f61b316f38a42f5024e9da76bc41/images/white_flake.png -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject drawing "0.1.0-SNAPSHOT" 2 | 3 | :description "ClojureBridge capstone app that uses Quil for drawing" 4 | 5 | :url "http://clojurebridge.org" 6 | 7 | :license {:name "Creative Commons Attribution 4.0 International License" 8 | :url "https://creativecommons.org/licenses/by/4.0/legalcode"} 9 | 10 | :dependencies [[org.clojure/clojure "1.7.0"] 11 | [quil "2.2.6"]]) 12 | -------------------------------------------------------------------------------- /src/drawing/core.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.core 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (defn setup [] 6 | ; Set frame rate to 30 frames per second. 7 | (q/frame-rate 30) 8 | ; Set color mode to HSB (HSV) instead of default RGB. 9 | (q/color-mode :hsb) 10 | ; setup function returns initial state. It contains 11 | ; circle color and position. 12 | {:color 0 13 | :angle 0}) 14 | 15 | (defn update [state] 16 | ; Update sketch state by changing circle color and position. 17 | {:color (mod (+ (:color state) 0.7) 255) 18 | :angle (+ (:angle state) 0.1)}) 19 | 20 | (defn draw [state] 21 | ; Clear the sketch by filling it with light-grey color. 22 | (q/background 240) 23 | ; Set circle color. 24 | (q/fill (:color state) 255 255) 25 | ; Calculate x and y coordinates of the circle. 26 | (let [angle (:angle state) 27 | x (* 150 (q/cos angle)) 28 | y (* 150 (q/sin angle))] 29 | ; Move origin point to the center of the sketch. 30 | (q/with-translation [(/ (q/width) 2) 31 | (/ (q/height) 2)] 32 | ; Draw the circle. 33 | (q/ellipse x y 100 100)))) 34 | 35 | (q/defsketch circles 36 | :title "You spin my circle right round" 37 | :size [500 500] 38 | ; setup function called only once, during sketch initialization. 39 | :setup setup 40 | ; update is called on each iteration before draw is called. 41 | ; It updates sketch state. 42 | :update update 43 | :draw draw 44 | :features [:keep-on-top] 45 | ; This sketch uses functional-mode middleware. 46 | ; Check quil wiki for more info about middlewares and particularly 47 | ; fun-mode. 48 | :middleware [m/fun-mode]) 49 | -------------------------------------------------------------------------------- /src/drawing/lines.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.lines 2 | (:require [quil.core :as q])) 3 | 4 | (defn setup [] 5 | ; Set frame rate to 30 frames per second. 6 | (q/frame-rate 30) 7 | ; Set color mode to RGB 8 | (q/color-mode :rgb) 9 | ; Set color of line to Red 10 | (q/stroke 255 0 0)) 11 | 12 | (defn mouse-color [x y] 13 | "Calculate a color based on two values" 14 | 15 | ;TODO: Take a look at B (of RGB). Right now it is just 0. 16 | [(mod x 255) (mod y 255) 0]) 17 | 18 | (defn draw [] 19 | 20 | ; Throughout the draw function, get the position of the 21 | ; mouse with mouse-x and mouse-y 22 | 23 | ; Set the line color based on mouse position 24 | (apply q/stroke (mouse-color (q/mouse-x) (q/mouse-y))) 25 | 26 | ; Draw a line from the origin to the position of the mouse 27 | (q/line 0 0 (q/mouse-x) (q/mouse-y)) 28 | ; Draw a line from 200 pixels right from the top left 29 | ; to the position of the mouse 30 | (q/line 200 0 (q/mouse-x) (q/mouse-y)) 31 | ; Draw a line from 200 pixels down from the top left 32 | ; to the position of the mouse 33 | (q/line 0 200 (q/mouse-x) (q/mouse-y)) 34 | ; Draw a line from 200 pixels right and down from the top left 35 | ; to the position of the mouse 36 | (q/line 200 200 (q/mouse-x) (q/mouse-y))) 37 | 38 | (q/defsketch hello-lines 39 | ; Set the title of the sketch 40 | :title "You can see lines" 41 | ; Set the size of the sketch 42 | :size [500 500] 43 | ; setup function is called setup 44 | :setup setup 45 | ; draw function is called draw 46 | :draw draw 47 | :features [:keep-on-top]) 48 | -------------------------------------------------------------------------------- /src/drawing/practice.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.practice 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (defn setup [] 6 | ;; loading two images 7 | (q/smooth) 8 | {:flake (q/load-image "images/white_flake.png") 9 | :background (q/load-image "images/blue_background.png") 10 | :params [{:x 10 :swing 1 :y 10 :speed 1} 11 | {:x 200 :swing 3 :y 100 :speed 4} 12 | {:x 390 :swing 2 :y 50 :speed 2}]}) 13 | 14 | (defn update-x 15 | [m] 16 | (let [x (:x m) 17 | swing (:swing m) 18 | y (:y m)] 19 | (cond 20 | (< x 0) (assoc m :x (q/width)) ;; too left 21 | (< x (q/width)) (update-in m [:x] + (* swing (q/sin (/ y 50)))) ;; within frame 22 | :else (assoc m :x 0)))) ;; too right 23 | 24 | (defn update-y 25 | [m] 26 | (let [y (:y m) 27 | speed (:speed m)] 28 | (if (>= y (q/height)) ;; y is greater than or equal to image height? 29 | (assoc m :y 0) ;; true - get it back to the 0 (top) 30 | (update-in m [:y] + speed)))) ;; false - add y value and speed 31 | 32 | (defn update [state] 33 | (let [params (:params state) 34 | params (map update-y params) 35 | params (map update-x params)] 36 | (assoc state :params params))) 37 | 38 | (defn draw [state] 39 | ;; drawing blue background and mutiple snowflakes on it 40 | (q/background-image (:background state)) 41 | (let [params (:params state)] 42 | (dotimes [n 3] 43 | (let [param (nth params n)] 44 | (q/image (:flake state) (:x param) (:y param)))))) 45 | 46 | (q/defsketch practice 47 | :title "Clara's Quil practice" 48 | :size [500 500] 49 | :setup setup 50 | :update update 51 | :draw draw 52 | :features [:keep-on-top] 53 | :middleware [m/fun-mode]) 54 | -------------------------------------------------------------------------------- /src/drawing/step1.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.practice 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (defn setup [] 6 | ;; loading two images 7 | {:flake (q/load-image "images/white_flake.png") 8 | :background (q/load-image "images/blue_background.png")}) 9 | 10 | (defn draw [{flake :flake background :background}] 11 | ;; drawing blue background and a snowflake on it 12 | (q/background-image background) 13 | (q/image flake 200 10)) 14 | 15 | (q/defsketch practice 16 | :title "Clara's Quil practice" 17 | :size [500 500] 18 | :setup setup 19 | :draw draw 20 | :features [:keep-on-top] 21 | :middleware [m/fun-mode]) 22 | -------------------------------------------------------------------------------- /src/drawing/step2.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.step2 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (defn setup [] 6 | ;; loading two images 7 | (q/smooth) 8 | (q/frame-rate 60) 9 | {:flake (q/load-image "images/white_flake.png") 10 | :background (q/load-image "images/blue_background.png") 11 | :y-param 10}) 12 | 13 | (defn update [state] 14 | ;; updating y paraemter by one 15 | (update-in state [:y-param] inc)) 16 | 17 | (defn draw [state] 18 | ;; drawing blue background and a snowflake on it 19 | (q/background-image (:background state)) 20 | (q/image (:flake state) 200 (:y-param state))) 21 | 22 | (q/defsketch practice 23 | :title "Clara's Quil practice" 24 | :size [500 500] 25 | :setup setup 26 | :update update 27 | :draw draw 28 | :features [:keep-on-top] 29 | :middleware [m/fun-mode]) 30 | -------------------------------------------------------------------------------- /src/drawing/step3.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.step3 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (defn setup [] 6 | ;; loading two images 7 | (q/smooth) 8 | (q/frame-rate 60) 9 | {:flake (q/load-image "images/white_flake.png") 10 | :background (q/load-image "images/blue_background.png") 11 | :y-param 10}) 12 | 13 | (defn update [state] 14 | (if (>= (:y-param state) (q/height)) ;; y-param is greater than or equal to image height? 15 | (assoc state :y-param 0) ;; true - get it back to the 0 (top) 16 | (update-in state [:y-param] inc) ;; false - updating y paraemter by one 17 | )) 18 | 19 | (defn draw [state] 20 | ;; drawing blue background and a snowflake on it 21 | (q/background-image (:background state)) 22 | (q/image (:flake state) 200 (:y-param state))) 23 | 24 | (q/defsketch practice 25 | :title "Clara's Quil practice" 26 | :size [500 500] 27 | :setup setup 28 | :update update 29 | :draw draw 30 | :features [:keep-on-top] 31 | :middleware [m/fun-mode]) 32 | -------------------------------------------------------------------------------- /src/drawing/step4.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.step4 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (def x-params [10 200 390]) ;; x parameters for three snowflakes 6 | 7 | (defn setup [] 8 | ;; loading two images 9 | (q/smooth) 10 | (q/frame-rate 60) 11 | {:flake (q/load-image "images/white_flake.png") 12 | :background (q/load-image "images/blue_background.png") 13 | :y-param 10}) 14 | 15 | (defn update [state] 16 | (if (>= (:y-param state) (q/height)) ;; y-param is greater than or equal to image height? 17 | (assoc state :y-param 0) ;; true - get it back to the 0 (top) 18 | (update-in state [:y-param] inc) ;; false - updating y paraemter by one 19 | )) 20 | 21 | (defn draw [state] 22 | ;; drawing blue background and mutiple snowflakes on it 23 | (q/background-image (:background state)) 24 | (doseq [x x-params] 25 | (q/image (:flake state) x (:y-param state)))) 26 | 27 | (q/defsketch practice 28 | :title "Clara's Quil practice" 29 | :size [500 500] 30 | :setup setup 31 | :update update 32 | :draw draw 33 | :features [:keep-on-top] 34 | :middleware [m/fun-mode]) 35 | -------------------------------------------------------------------------------- /src/drawing/step5.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.step5 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (def x-params [10 200 390]) ;; x parameters for three snowflakes 6 | 7 | (defn setup [] 8 | ;; loading two images 9 | (q/smooth) 10 | (q/frame-rate 60) 11 | {:flake (q/load-image "images/white_flake.png") 12 | :background (q/load-image "images/blue_background.png") 13 | :y-params [{:y 10 :speed 1} {:y 150 :speed 4} {:y 50 :speed 2}]}) 14 | 15 | #_(defn update-y 16 | [m] 17 | (let [y (:y m) 18 | speed (:speed m)] 19 | (if (>= y (q/height)) ;; y is greater than or equal to image height? 20 | (assoc m :y 0) ;; true - get it back to the 0 (top) 21 | (update-in m [:y] + speed)))) ;; false - add y value and speed 22 | 23 | (defn update-y 24 | [{y :y speed :speed :as m}] 25 | (if (>= y (q/height)) ;; y is greater than or equal to image height? 26 | (assoc m :y 0) ;; true - get it back to the 0 (top) 27 | (update-in m [:y] + speed))) ;; false - add y value and speed 28 | 29 | (defn update [state] 30 | (let [y-params (:y-params state)] 31 | (assoc state :y-params (map update-y y-params)))) 32 | 33 | (defn draw [state] 34 | ;; drawing blue background and mutiple snowflakes on it 35 | (q/background-image (:background state)) 36 | (let [y-params (:y-params state)] 37 | (dotimes [n 3] 38 | (q/image (:flake state) (nth x-params n) (:y (nth y-params n)))))) 39 | 40 | (q/defsketch practice 41 | :title "Clara's Quil practice" 42 | :size [500 500] 43 | :setup setup 44 | :update update 45 | :draw draw 46 | :features [:keep-on-top] 47 | :middleware [m/fun-mode]) 48 | -------------------------------------------------------------------------------- /src/drawing/step6.clj: -------------------------------------------------------------------------------- 1 | (ns drawing.step6 2 | (:require [quil.core :as q] 3 | [quil.middleware :as m])) 4 | 5 | (defn setup [] 6 | ;; loading two images 7 | (q/smooth) 8 | (q/frame-rate 60) 9 | {:flake (q/load-image "images/white_flake.png") 10 | :background (q/load-image "images/blue_background.png") 11 | :params [{:x 10 :y 10 :speed 1} 12 | {:x 200 :y 150 :speed 4} 13 | {:x 390 :y 50 :speed 2}]}) 14 | 15 | (defn update-y 16 | [m] 17 | (let [y (:y m) 18 | speed (:speed m)] 19 | (if (>= y (q/height)) ;; y is greater than or equal to image height? 20 | (assoc m :y 0) ;; true - get it back to the 0 (top) 21 | (update-in m [:y] + speed)))) ;; false - add y value and speed 22 | 23 | (defn update [state] 24 | (let [params (:params state)] 25 | (assoc state :params (map update-y params)))) 26 | 27 | (defn draw [state] 28 | ;; drawing blue background and mutiple snowflakes on it 29 | (q/background-image (:background state)) 30 | (let [params (:params state)] 31 | (dotimes [n 3] 32 | (let [param (nth params n)] 33 | (q/image (:flake state) (:x param) (:y param)))))) 34 | 35 | #_(defn draw [state] 36 | ;; drawing blue background and mutiple snowflakes on it 37 | (q/background-image (:background state)) 38 | (let [params (:params state)] 39 | (dotimes [n 3] 40 | (q/image (:flake state) (:x (nth params n)) (:y (nth params n)))))) 41 | 42 | (q/defsketch practice 43 | :title "Clara's Quil practice" 44 | :size [500 500] 45 | :setup setup 46 | :update update 47 | :draw draw 48 | :features [:keep-on-top] 49 | :middleware [m/fun-mode]) 50 | --------------------------------------------------------------------------------