├── .gitignore ├── LICENSE ├── README.md ├── bocko.jpg ├── project.clj └── src └── bocko ├── core.cljc └── swing.clj /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | pom.xml.asc 3 | *jar 4 | /lib/ 5 | /classes/ 6 | /target/ 7 | /checkouts/ 8 | .lein-deps-sum 9 | .lein-repl-history 10 | .lein-plugins/ 11 | .lein-failures 12 | .nrepl-port 13 | .idea 14 | bocko.iml 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bocko 2 | 3 | A small library making it extremely simple to play around with low-res graphics from Clojure, as well as from ClojureScript on [iOS](https://github.com/mfikes/bocko-ios), [Android](https://github.com/nvbn/bocko-android)[1](#bockoandroid), and an [HTML canvas](https://github.com/mfikes/bocko-canvas). ([Read the blog post.](http://blog.fikesfarm.com/posts/2015-05-24-bocko-low-res-clojure-graphics.html)) 4 | 5 | 6 | 7 | # Usage 8 | 9 | ``` 10 | lein new bocko my-project 11 | ``` 12 | 13 | Then find a short `README.md` at the top-level of the generated project, explaining how to get up and running. 14 | 15 | # Detailed Usage 16 | 17 | ```clojure 18 | (require '[bocko.core :refer :all]) 19 | 20 | (plot 2 3) ;; plots a point on the screen 21 | 22 | (color :pink) ;; changes the color to pink 23 | (plot 5 5) 24 | 25 | (scrn 5 5) ;; => :pink 26 | 27 | (hlin 3 9 10) ;; draws a horizontal line 28 | 29 | (clear) ;; clears screen 30 | ``` 31 | 32 | The commands comprise `color`, `plot`, `scrn`, `hlin`, `vlin`, and `clear`. 33 | 34 | # Demo 35 | 36 | Watch a demo to see it in action: 37 | 38 | [![Bocko Demo](http://img.youtube.com/vi/piJPrP3BKIk/0.jpg)](http://www.youtube.com/watch?v=piJPrP3BKIk "Bocko Clojure simple graphics") 39 | 40 | # Examples 41 | 42 | Draw an American flag: 43 | ```clojure 44 | ;; Draw 13 stripes cycling over red/white 45 | 46 | (doseq [[n c] (take 13 47 | (map vector (range) (cycle [:red :white])))] 48 | (color c) 49 | (let [x1 10 50 | x2 25 51 | y (+ 10 n)] 52 | (hlin x1 x2 y))) 53 | 54 | ;; Fill in a dark blue field in the corner 55 | 56 | (color :dark-blue) 57 | (doseq [x (range 10 19) 58 | y (range 10 17)] 59 | (plot x y)) 60 | 61 | ;; Add some stars to the field by skipping by 2 62 | 63 | (color :white) 64 | (doseq [x (range 11 19 2) 65 | y (range 11 17 2)] 66 | (plot x y)) 67 | ``` 68 | 69 | Display all the colors: 70 | 71 | ```clojure 72 | (doseq [[c n] (map vector 73 | [:black :red :dark-blue :purple 74 | :dark-green :dark-gray :medium-blue :light-blue 75 | :brown :orange :light-gray :pink 76 | :light-green :yellow :aqua :white] 77 | (range))] 78 | (color c) 79 | (let [x' (* 10 (rem n 4)) 80 | y' (* 10 (quot n 4))] 81 | (doseq [x (range x' (+ 10 x')) 82 | y (range y' (+ 10 y'))] 83 | (plot x y)))) 84 | ``` 85 | 86 | 87 | Animated bouncing ball using `loop`/`recur`: 88 | ```clojure 89 | (loop [x 5 y 23 vx 1 vy 1] 90 | ; First determine new location and velocity, 91 | ; reversing direction if bouncing off edge. 92 | (let [x' (+ x vx) 93 | y' (+ y vy) 94 | vx' (if (< 0 x' 39) vx (- vx)) 95 | vy' (if (< 0 y' 39) vy (- vy))] 96 | ; Erase drawing at previous location 97 | (color :black) 98 | (plot x y) 99 | ; Draw ball in new location 100 | (color :dark-blue) 101 | (plot x' y') 102 | ; Sleep a little and then loop around again 103 | (Thread/sleep 50) 104 | (recur x' y' vx' vy'))) 105 | ``` 106 | 107 | Random colors and locations: 108 | ```clojure 109 | (loop [] 110 | (let [c (rand-nth [:black :red :dark-blue :purple 111 | :dark-green :dark-gray :medium-blue :light-blue 112 | :brown :orange :light-gray :pink 113 | :light-green :yellow :aqua :white]) 114 | x (rand-int 40) 115 | y (rand-int 40)] 116 | (color c) 117 | (plot x y) 118 | (Thread/sleep 1) 119 | (recur))) 120 | ``` 121 | 122 | Game-of-Life Glider: (Credit: [Christophe Grand](http://clj-me.cgrand.net/2011/08/19/conways-game-of-life/)) 123 | ``` 124 | (defn neighbours [[x y]] 125 | (for [dx [-1 0 1] dy (if (zero? dx) [-1 1] [-1 0 1])] 126 | [(+ dx x) (+ dy y)])) 127 | 128 | (defn step [cells] 129 | (set (for [[loc n] (frequencies (mapcat neighbours cells)) 130 | :when (or (= n 3) (and (= n 2) (cells loc)))] 131 | loc))) 132 | 133 | (loop [board #{[0 2] [1 0] [1 2] [2 1] [2 2]}] 134 | (clear) 135 | (run! (partial apply plot) board) 136 | (Thread/sleep 100) 137 | (recur (step board))) 138 | ``` 139 | 140 | # Multi-threading 141 | 142 | You can use Bocko from multiple threads. The underlying canvas is thread-safe. 143 | 144 | In that scenario, establishing thread-local bindings for the `*color*` dynamic var will allow each thread to plot independently. For example, the following code will not interfere with the color being used for plotting in other threads: 145 | 146 | ```clojure 147 | (future 148 | (binding [*color* :orange] 149 | (plot 3 3) ; Will plot an orange point 150 | (set! *color* :aqua) 151 | (plot 4 4))) ; Will plot an aqua point 152 | ``` 153 | 154 | In fact, a form like `(color :red)` is just a simple wrapper that will set a thread-local binding if one is in effect, otherwise it will set the root binding. 155 | 156 | Here is an example. This makes use of thread-local bindings and "does the right thing": 157 | 158 | ```clojure 159 | (do 160 | 161 | ;; Repeatedly display all the colors 162 | 163 | (future 164 | (loop [] 165 | (clear) 166 | (doseq [[c n] (map vector 167 | [:black :red :dark-blue :purple 168 | :dark-green :dark-gray :medium-blue :light-blue 169 | :brown :orange :light-gray :pink 170 | :light-green :yellow :aqua :white] 171 | (range))] 172 | (binding [*color* c] 173 | (let [x' (* 10 (rem n 4)) 174 | y' (* 10 (quot n 4))] 175 | (doseq [x (range x' (+ 10 x')) 176 | y (range y' (+ 10 y'))] 177 | (plot x y) 178 | (Thread/sleep 1))))) 179 | (recur))) 180 | 181 | ;; Add a bouncing ball 182 | 183 | (future 184 | (loop [x 5 y 23 vx 1 vy 1] 185 | ; First determine new location and velocity, 186 | ; reversing direction if bouncing off edge. 187 | (let [x' (+ x vx) 188 | y' (+ y vy) 189 | vx' (if (< 0 x' 39) vx (- vx)) 190 | vy' (if (< 0 y' 39) vy (- vy))] 191 | ; Erase drawing at previous location 192 | (binding [*color* :black] 193 | (plot x y)) 194 | ; Draw ball in new location 195 | (binding [*color* :dark-blue] 196 | (plot x' y')) 197 | ; Sleep a little and then loop around again 198 | (Thread/sleep 50) 199 | (recur x' y' vx' vy'))))) 200 | ``` 201 | 202 | This, on the other hand, illustrates contention / interference with the color being used for plotting: 203 | 204 | ```clojure 205 | (do 206 | 207 | ;; Repeatedly display all the colors 208 | 209 | (future 210 | (loop [] 211 | (clear) 212 | (doseq [[c n] (map vector 213 | [:black :red :dark-blue :purple 214 | :dark-green :dark-gray :medium-blue :light-blue 215 | :brown :orange :light-gray :pink 216 | :light-green :yellow :aqua :white] 217 | (range))] 218 | (color c) 219 | (let [x' (* 10 (rem n 4)) 220 | y' (* 10 (quot n 4))] 221 | (doseq [x (range x' (+ 10 x')) 222 | y (range y' (+ 10 y'))] 223 | (plot x y) 224 | (Thread/sleep 1)))) 225 | (recur))) 226 | 227 | ;; Add a bouncing ball 228 | 229 | (future 230 | (loop [x 5 y 23 vx 1 vy 1] 231 | ; First determine new location and velocity, 232 | ; reversing direction if bouncing off edge. 233 | (let [x' (+ x vx) 234 | y' (+ y vy) 235 | vx' (if (< 0 x' 39) vx (- vx)) 236 | vy' (if (< 0 y' 39) vy (- vy))] 237 | ; Erase drawing at previous location 238 | (color :black) 239 | (plot x y) 240 | ; Draw ball in new location 241 | (color :dark-blue) 242 | (plot x' y') 243 | ; Sleep a little and then loop around again 244 | (Thread/sleep 50) 245 | (recur x' y' vx' vy'))))) 246 | ``` 247 | 248 | 249 | # License 250 | 251 | Copyright © 2015–2016 Mike Fikes and Contributors 252 | 253 | Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version. 254 | 255 | # Footnotes 256 | 257 | 1: Bocko for Android is by [Vladimir Iakovlev](https://github.com/nvbn). 258 | -------------------------------------------------------------------------------- /bocko.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mfikes/bocko/2889b2529d7549d56e1000900d996b47fa2da0eb/bocko.jpg -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject bocko "1.0.0" 2 | :description "Simple imperative graphics" 3 | :url "https://github.com/mfikes/bocko" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.8.0"]]) 7 | -------------------------------------------------------------------------------- /src/bocko/core.cljc: -------------------------------------------------------------------------------- 1 | (ns bocko.core) 2 | 3 | (def ^:private ^:const width 40) 4 | (def ^:private ^:const height 40) 5 | (def ^:private ^:const pixel-width 28) 6 | (def ^:private ^:const pixel-height 16) 7 | (def ^:private clear-color :black) 8 | (def ^:private default-color :white) 9 | (def ^:private clear-screen (vec (repeat height (vec (repeat width clear-color))))) 10 | 11 | (defonce ^:private raster (atom clear-screen)) 12 | 13 | (def ^:private color-map 14 | {:black [0 0 0] 15 | :red [157 9 102] 16 | :dark-blue [42 42 229] 17 | :purple [199 52 255] 18 | :dark-green [0 118 26] 19 | :dark-gray [128 128 128] 20 | :medium-blue [13 161 255] 21 | :light-blue [170 170 255] 22 | :brown [85 85 0] 23 | :orange [242 94 0] 24 | :light-gray [192 192 192] 25 | :pink [255 137 229] 26 | :light-green [56 203 0] 27 | :yellow [213 213 26] 28 | :aqua [98 246 153] 29 | :white [255 255 254]}) 30 | 31 | (defonce ^:private create-canvas-fn (atom nil)) 32 | 33 | (defn set-create-canvas 34 | "Sets a function that creates a 'canvas'. The function will 35 | be passed the color-map, the raster atom, and raster width and 36 | height and desires pixel-width and pixel-height." 37 | [f] 38 | (reset! create-canvas-fn f)) 39 | 40 | (defonce ^:private canvas 41 | (delay (@create-canvas-fn color-map raster width height pixel-width pixel-height))) 42 | 43 | ;; If we are in Clojure, set up a Swing canvas 44 | #?(:clj 45 | (set-create-canvas 46 | (fn [color-map raster width height pixel-width pixel-height] 47 | (require 'bocko.swing) 48 | (let [make-panel (eval 'bocko.swing/make-panel)] 49 | (make-panel color-map raster width height pixel-width pixel-height))))) 50 | 51 | (defn clear 52 | "Clears this screen." 53 | [] 54 | (force canvas) 55 | (reset! raster clear-screen) 56 | nil) 57 | 58 | (defonce ^:dynamic 59 | ^{:doc "The color used for plotting."} 60 | *color* default-color) 61 | 62 | (set-validator! #'*color* 63 | (fn [c] (contains? color-map c))) 64 | 65 | (defn color 66 | "Sets the color for plotting. 67 | 68 | The color must be one of the following: 69 | 70 | :black :red :dark-blue :purple 71 | :dark-green :dark-gray :medium-blue :light-blue 72 | :brown :orange :light-gray :pink 73 | :light-green :yellow :aqua :white" 74 | [c] 75 | {:pre [(keyword? c) 76 | (c #{:black :red :dark-blue :purple 77 | :dark-green :dark-gray :medium-blue :light-blue 78 | :brown :orange :light-gray :pink 79 | :light-green :yellow :aqua :white})]} 80 | (force canvas) 81 | #?(:clj (if (thread-bound? #'*color*) 82 | (set! *color* c) 83 | (alter-var-root #'*color* (constantly c))) 84 | :cljs (set! *color* c)) 85 | nil) 86 | 87 | (defn- plot* 88 | [r x y c] 89 | (assoc-in r [x y] c)) 90 | 91 | (defn plot 92 | "Plots a point at a given x and y. 93 | 94 | Both x and y must be between 0 and 39." 95 | [x y] 96 | {:pre [(integer? x) (integer? y) (<= 0 x 39) (<= 0 y 39)]} 97 | (force canvas) 98 | (swap! raster plot* x y *color*) 99 | nil) 100 | 101 | (defn- lin 102 | [r a1 a2 b c f] 103 | (if (< a2 a1) 104 | (lin r a2 a1 b c f) 105 | (reduce (fn [r x] 106 | (assoc-in r (f [x b]) c)) 107 | r 108 | (range a1 (inc a2))))) 109 | 110 | (defn- hlin* 111 | [r x1 x2 y c] 112 | (lin r x1 x2 y c identity)) 113 | 114 | (defn hlin 115 | "Plots a horizontal line from x1 to x2 at a given y. 116 | 117 | The x and y numbers must be between 0 and 39." 118 | [x1 x2 y] 119 | {:pre [(integer? x1) (integer? x2) (integer? y) (<= 0 x1 39) (<= 0 x2 39) (<= 0 y 39)]} 120 | (force canvas) 121 | (swap! raster hlin* x1 x2 y *color*) 122 | nil) 123 | 124 | (defn- vlin* 125 | [r y1 y2 x c] 126 | (lin r y1 y2 x c reverse)) 127 | 128 | (defn vlin 129 | "Plots a vertical line from y1 to y2 at a given x. 130 | 131 | The x and y numbers must be between 0 and 39." 132 | [y1 y2 x] 133 | {:pre [(integer? y1) (integer? y2) (integer? x) (<= 0 y1 39) (<= 0 y2 39) (<= 0 x 39)]} 134 | (force canvas) 135 | (swap! raster vlin* y1 y2 x *color*) 136 | nil) 137 | 138 | (defn- scrn* 139 | [r x y] 140 | (get-in r [x y])) 141 | 142 | (defn scrn 143 | "Gets the color at a given x and y. 144 | 145 | Both x and y must be between 0 and 39." 146 | [x y] 147 | {:pre [(integer? x) (integer? y) (<= 0 x 39) (<= 0 y 39)]} 148 | (force canvas) 149 | (scrn* @raster x y)) -------------------------------------------------------------------------------- /src/bocko/swing.clj: -------------------------------------------------------------------------------- 1 | (ns bocko.swing 2 | (:import (java.awt Dimension Color) 3 | (javax.swing JPanel JFrame))) 4 | 5 | (defn- rgb->color 6 | [[r g b]] 7 | (Color. r g b)) 8 | 9 | (defn make-panel 10 | [color-map raster width height pixel-width pixel-height] 11 | (let [frame (JFrame. "Bocko") 12 | rgb->color (memoize rgb->color) 13 | paint-point (fn [x y c g] 14 | (.setColor g (rgb->color (c color-map))) 15 | (.fillRect g 16 | (* x pixel-width) (* y pixel-height) 17 | pixel-width pixel-height)) 18 | panel (proxy [JPanel] [] 19 | (paintComponent [g] 20 | (proxy-super paintComponent g) 21 | (let [r @raster] 22 | (doseq [x (range width) 23 | y (range height)] 24 | (paint-point x y (get-in r [x y]) g)))) 25 | (getPreferredSize [] 26 | (Dimension. 27 | (* width pixel-width) 28 | (* height pixel-height))))] 29 | (doto frame 30 | (.add panel) 31 | (.pack) 32 | (.setVisible true)) 33 | (add-watch raster :monitor 34 | (fn [_ _ o n] 35 | (when (not= o n) 36 | (.repaint panel)))) 37 | panel)) 38 | --------------------------------------------------------------------------------