├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE.md ├── README.md ├── doc └── examples │ ├── cesaro-koch-fractal.svg │ ├── fass-curve.svg │ ├── fractal-plant.svg │ ├── heighways-dragon.svg │ ├── hilberts-space-filling-curve.svg │ ├── joined-cross-curve.svg │ ├── koch-blocks.svg │ ├── koch-crystal.svg │ ├── koch-curve.svg │ ├── koch-islands.svg │ ├── koch-ring.svg │ ├── koch-snowflake.svg │ ├── peano-gosper-curve.svg │ ├── penrose-tiling.svg │ ├── quadratic-koch-island.svg │ ├── shrub.svg │ ├── sierpinski-curve.svg │ ├── sierpinski-median-curve.svg │ ├── sierpinski-triangle.svg │ └── tree.svg ├── project.clj ├── resources ├── 718874645349273600.json ├── 718980437436071936.json └── 719139020870000641.json ├── src └── lindenmayer │ ├── compiler.clj │ ├── explorer.clj │ ├── explorer │ ├── bot.clj │ ├── botany.clj │ ├── presets.clj │ └── reptiles.clj │ └── handler.clj └── test └── lindenmayer └── compiler_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib/ 4 | /classes/ 5 | /target/ 6 | .lein-failures 7 | .lein-deps-sum 8 | .lein-repl-history 9 | .lein-cljsbuild-compiler-0/ 10 | resources/public/cljs/ 11 | hs_err_pid*.log 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | lein: 2.8.1 3 | install: 4 | # Get recent node: 5 | - . $HOME/.nvm/nvm.sh 6 | - nvm install stable 7 | - nvm use stable 8 | - npm install 9 | before_script: 10 | - npm install -g eclint 11 | - eclint check $(git ls-files) 12 | - lein install 13 | - lein cljfmt check 14 | script: 15 | - lein with-profile +dev cloverage --coveralls 16 | - curl -F 'json_file=@target/coverage/coveralls.json' 'https://coveralls.io/api/v1/jobs' 17 | jdk: 18 | - oraclejdk8 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM frolvlad/alpine-oraclejdk8:latest 2 | MAINTAINER Richard Hull 3 | 4 | ENV LEIN_ROOT 1 5 | 6 | RUN apk add --update wget ca-certificates bash && \ 7 | wget -q "https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein" \ 8 | -O /usr/local/bin/lein && \ 9 | chmod 0755 /usr/local/bin/lein && \ 10 | lein && \ 11 | apk del wget ca-certificates && \ 12 | rm -rf /tmp/* /var/cache/apk/* 13 | 14 | RUN mkdir -p /usr/src/app 15 | WORKDIR /usr/src/app 16 | COPY . /usr/src/app 17 | RUN \ 18 | lein deps && \ 19 | lein ring uberjar && \ 20 | rm -rf target/classes ~/.m2 21 | 22 | EXPOSE 3000 23 | ENTRYPOINT ["java", "-jar", "target/lindenmayer-systems-0.2.1-standalone.jar"] -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Richard Hull 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lindenmayer Systems 2 | [![Build Status](https://secure.travis-ci.org/rm-hull/lindenmayer-systems.svg)](http://travis-ci.org/rm-hull/lindenmayer-systems) 3 | [![Coverage Status](https://coveralls.io/repos/rm-hull/lindenmayer-systems/badge.svg?branch=master)](https://coveralls.io/r/rm-hull/lindenmayer-systems?branch=master) 4 | [![Dependencies Status](https://versions.deps.co/rm-hull/lindenmayer-systems/status.svg)](https://versions.deps.co/rm-hull/lindenmayer-systems) 5 | [![Docker Pulls](https://img.shields.io/docker/pulls/richardhull/lindenmayer-systems.svg?maxAge=2592000)](https://hub.docker.com/r/richardhull/lindenmayer-systems/) 6 | [![Maintenance](https://img.shields.io/maintenance/yes/2018.svg?maxAge=2592000)]() 7 | 8 | > **NOTE** This project is currently undergoing significant rework 9 | 10 | An L-System or Lindenmayer system is a parallel rewriting system, namely a 11 | variant of a formal grammar, most famously used to model the growth 12 | processes of plant development, but also able to model the morphology of 13 | a variety of organisms. 14 | 15 | This project is written using Clojure, demonstrating L-System 16 | re-writing, deployed to Heroku: 17 | 18 | Pre-defined examples can be generated using these links: 19 | 20 | #### [Sierpinski Curve](http://lindenmayer-systems.destructuring-bind.org/explorer/sierpinski-curve) 21 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/sierpinski-curve.svg) 22 | 23 | #### [Sierpinski Triangle](http://lindenmayer-systems.destructuring-bind.org/explorer/sierpinski-triangle) 24 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/sierpinski-triangle.svg) 25 | 26 | #### [Sierpinski Median Curve](http://lindenmayer-systems.destructuring-bind.org/explorer/sierpinski-median-curve) 27 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/sierpinski-median-curve.svg) 28 | 29 | #### [Koch Curve](http://lindenmayer-systems.destructuring-bind.org/explorer/koch-curve) 30 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/koch-curve.svg) 31 | 32 | #### [Koch Snowflake](http://lindenmayer-systems.destructuring-bind.org/explorer/koch-snowflake) 33 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/koch-snowflake.svg) 34 | 35 | #### [Koch Ring](http://lindenmayer-systems.destructuring-bind.org/explorer/koch-ring) 36 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/koch-ring.svg) 37 | 38 | #### [Koch Blocks](http://lindenmayer-systems.destructuring-bind.org/explorer/koch-blocks) 39 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/koch-blocks.svg) 40 | 41 | #### [Koch Islands](http://lindenmayer-systems.destructuring-bind.org/explorer/koch-islands) 42 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/koch-islands.svg) 43 | 44 | #### [Koch Crystal](http://lindenmayer-systems.destructuring-bind.org/explorer/koch-crystal) 45 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/koch-crystal.svg) 46 | 47 | #### [Quadratic Koch Island](http://lindenmayer-systems.destructuring-bind.org/explorer/quadratic-koch-island) 48 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/quadratic-koch-island.svg) 49 | 50 | #### [Cesaro-Koch Fractal](http://lindenmayer-systems.destructuring-bind.org/explorer/cesaro-koch-fractal) 51 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/cesaro-koch-fractal.svg) 52 | 53 | #### [Joined-Cross Curve](http://lindenmayer-systems.destructuring-bind.org/explorer/joined-cross-curve) 54 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/joined-cross-curve.svg) 55 | 56 | #### [Penrose Tiling](http://lindenmayer-systems.destructuring-bind.org/explorer/penrose-tiling) 57 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/penrose-tiling.svg) 58 | 59 | #### [Peano-Gosper Curve](http://lindenmayer-systems.destructuring-bind.org/explorer/peano-gosper-curve) 60 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/peano-gosper-curve.svg) 61 | 62 | #### [Hilbert's Space-filling Curve](http://lindenmayer-systems.destructuring-bind.org/explorer/hilberts-space-filling-curve) 63 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/hilberts-space-filling-curve.svg) 64 | 65 | #### [FASS Curve](http://lindenmayer-systems.destructuring-bind.org/explorer/fass-curve) 66 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/fass-curve.svg) 67 | 68 | #### [Heighway's Dragon](http://lindenmayer-systems.destructuring-bind.org/explorer/heighways-dragon) 69 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/heighways-dragon.svg) 70 | 71 | #### [Shrub](http://lindenmayer-systems.destructuring-bind.org/explorer/shrub) 72 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/shrub.svg) 73 | 74 | #### [Fractal Plant](http://lindenmayer-systems.destructuring-bind.org/explorer/fractal-plant) 75 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/fractal-plant.svg) 76 | 77 | #### [Tree](http://lindenmayer-systems.destructuring-bind.org/explorer/tree) 78 | ![SVG](https://rawgithub.com/rm-hull/lindenmayer-systems/master/doc/examples/tree.svg) 79 | 80 | ## Pre-requisites 81 | 82 | You will need [Leiningen](https://github.com/technomancy/leiningen) 2.8.1 or above installed. 83 | 84 | ## Building 85 | 86 | To build and start the service locally, run: 87 | 88 | $ cd lindenmayer-systems 89 | $ lein deps 90 | $ lein ring server-headless 91 | 92 | To build and run a standalone jar: 93 | 94 | $ lein ring uberjar 95 | $ java -jar target/lindenmayer-systems-0.2.1-standalone.jar 96 | 97 | In both instances, the webapp starts on http://localhost:3000. 98 | 99 | ### Docker image 100 | 101 | A docker image is available as [richardhull/lindenmayer-systems](https://hub.docker.com/r/richardhull/lindenmayer-systems), 102 | and can be downloaded and started with: 103 | 104 | $ docker pull richardhull/lindenmayer-systems 105 | $ docker run --name lindenmayer-systems -d -p 3000:3000 richardhull/lindenmayer-systems 106 | 107 | ## TODO 108 | 109 | * UI improvements to allow editing and a last-created list 110 | 111 | ## Implementation Notes 112 | 113 | TODO 114 | 115 | ## References 116 | 117 | * [The Algorithmic Beauty of Plants](http://algorithmicbotany.org/papers/abop/abop.pdf) [PDF] 118 | * [Project Euler #220: Heighway Dragon](http://projecteuler.net/problem=220) 119 | * [Project Euler #226: A Scoop of Blancmange](http://projecteuler.net/problem=226) 120 | * [Project Euler #230: Fibonacci Words](http://projecteuler.net/problem=230) 121 | * https://en.wikipedia.org/wiki/Dragon_curve 122 | * https://en.wikipedia.org/wiki/L-system 123 | * http://www.kevs3d.co.uk/dev/lsystems/ 124 | * http://teethgrinder.co.uk/canvas/l-system-1.html 125 | * http://paulbourke.net/fractals/lsys/ 126 | * http://canonical.org/~kragen/named-msgs/reptile-lsystems 127 | 128 | 129 | ## License 130 | 131 | ### The MIT License (MIT) 132 | 133 | Copyright (c) 2016 Richard Hull 134 | 135 | Permission is hereby granted, free of charge, to any person obtaining a copy of 136 | this software and associated documentation files (the "Software"), to deal in 137 | the Software without restriction, including without limitation the rights to 138 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 139 | the Software, and to permit persons to whom the Software is furnished to do so, 140 | subject to the following conditions: 141 | 142 | The above copyright notice and this permission notice shall be included in all 143 | copies or substantial portions of the Software. 144 | 145 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 146 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 147 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 148 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 149 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 150 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 151 | 152 | -------------------------------------------------------------------------------- /doc/examples/koch-blocks.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/examples/koch-crystal.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/examples/koch-islands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/examples/peano-gosper-curve.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/examples/quadratic-koch-island.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/examples/sierpinski-triangle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject lindenmayer-systems "0.2.1" 2 | :description "An L-System explorer in Clojure" 3 | :url "http://lindenmayer-systems.destructuring-bind.org" 4 | :license { 5 | :name "The MIT License (MIT)" 6 | :url "http://opensource.org/licenses/MIT"} 7 | :dependencies [ 8 | [org.clojure/clojure "1.9.0"] 9 | [org.clojure/data.json "0.2.6"] 10 | [com.taoensso/timbre "4.10.0"] 11 | [compojure "1.6.1"] 12 | [ring "1.7.0"] 13 | [hiccup "1.0.5"] 14 | [ring-logger-timbre "0.7.6"] 15 | [metrics-clojure-ring "2.10.0"] 16 | [rm-hull/ring-gzip-middleware "0.1.7"] 17 | [rm-hull/turtle "0.1.11"] 18 | [rm-hull/ring-cede "0.1.0"]] 19 | :scm {:url "git@github.com:rm-hull/lindenmayer-systems.git"} 20 | :ring { 21 | :handler lindenmayer.handler/app} 22 | :source-paths ["src"] 23 | :resource-paths ["resources"] 24 | :jar-exclusions [#"(?:^|/).git"] 25 | :uberjar-exclusions [#"\.SF" #"\.RSA" #"\.DSA"] 26 | :min-lein-version "2.8.1" 27 | :profiles { 28 | :uberjar {:aot :all} 29 | :dev { 30 | :global-vars {*warn-on-reflection* true} 31 | :dependencies [ 32 | [org.clojure/test.check "0.9.0"]] 33 | :plugins [ 34 | [lein-cloverage "1.0.13"] 35 | [lein-cljfmt "0.6.1"] 36 | [lein-ring "0.12.4"]]}}) 37 | -------------------------------------------------------------------------------- /resources/718874645349273600.json: -------------------------------------------------------------------------------- 1 | { 2 | "attribution":"https://twitter.com/LSystemBot/status/718874645349273600", 3 | "start":"NFN", 4 | "rules":{ 5 | "F":"FHF-HHFHN", 6 | "H":"N" 7 | }, 8 | "a":90, 9 | "iter":7 10 | } -------------------------------------------------------------------------------- /resources/718980437436071936.json: -------------------------------------------------------------------------------- 1 | { 2 | "attribution":"https://twitter.com/LSystemBot/status/718980437436071936", 3 | "start": "JJB", 4 | "rules":{ 5 | "F":"F-++", 6 | "J":"FJ[F]JF" 7 | }, 8 | "a":45, 9 | "iter":7 10 | } -------------------------------------------------------------------------------- /resources/719139020870000641.json: -------------------------------------------------------------------------------- 1 | { 2 | "attribution":"https://twitter.com/LSystemBot/status/719139020870000641", 3 | "color":"#e275e2", 4 | "start":"FB", 5 | "rules":{ 6 | "F":"-A-", 7 | "X":"FAB", 8 | "G":"K[BFKBG]X", 9 | "K":"KAB", 10 | "B":"GGK", 11 | "A":"F-F[+F]" 12 | }, 13 | "a":60, 14 | "iter":6 15 | } -------------------------------------------------------------------------------- /src/lindenmayer/compiler.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.compiler) 2 | 3 | (defn make-symbol-table [angle distance] 4 | {\- [:right angle] 5 | \+ [:left angle] 6 | \^ [:fwd distance] 7 | \~ [:pen :up :fwd distance :pen :down] 8 | \| [:right 180] 9 | \[ :save 10 | \] :restore 11 | \# :color-index 12 | \0 0 13 | \1 1 14 | \2 2 15 | \3 3 16 | \4 4 17 | \5 5 18 | \6 6 19 | \7 7 20 | \8 8 21 | \9 9}) 22 | 23 | (defn split-on-assignment [symbols] 24 | [(first symbols) (vec (drop 2 symbols))]) 25 | 26 | (defn make-converter [symbol-table] 27 | (fn [rule] 28 | (mapv #(get symbol-table % (symbol (str %))) rule))) 29 | 30 | (defn builder [converter rules] 31 | (->> 32 | rules 33 | (map (comp split-on-assignment converter)) 34 | (into (array-map)))) 35 | 36 | (defmacro l-system [axiom constants rules angle distance] 37 | (let [symbol-table (make-symbol-table angle distance) 38 | convert (make-converter symbol-table) 39 | rules (builder convert rules) 40 | params (keys rules) 41 | init-args (map convert constants)] 42 | `(letfn [(seq0# [~@params] 43 | (cons ~(convert axiom) (lazy-seq (seq0# ~@(vals rules)))))] 44 | (seq0# ~@init-args)))) 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/lindenmayer/explorer.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.explorer 2 | (:require 3 | [compojure.core :refer [defroutes GET]] 4 | [turtle.core :refer [draw!]] 5 | [turtle.renderer.bitmap :refer [->img]] 6 | [turtle.renderer.vector :refer [->svg]] 7 | [lindenmayer.explorer.botany :as botany] 8 | [lindenmayer.explorer.bot :as bot] 9 | [lindenmayer.explorer.presets :as core] 10 | [lindenmayer.explorer.reptiles :as reptiles])) 11 | 12 | (def presets 13 | (merge 14 | botany/arboria 15 | bot/interesting 16 | core/fractals 17 | reptiles/rep-4)) 18 | 19 | (defn- explore [id] 20 | (draw! ->svg (presets id) [1000 600])) 21 | 22 | (defroutes routes 23 | (GET "/explorer/:id" [id] 24 | (explore (keyword id))) 25 | 26 | (GET "/random" [] 27 | (explore (rand-nth (keys presets))))) 28 | -------------------------------------------------------------------------------- /src/lindenmayer/explorer/bot.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.explorer.bot 2 | (:require 3 | [lindenmayer.compiler :refer [l-system]])) 4 | 5 | (def interesting {:t1 6 | (first 7 | (drop 8 | 7 9 | (l-system 10 | "F" 11 | ("^") 12 | ("F=FF-F") 13 | 90 14 | 30))) 15 | 16 | :t2 17 | (first 18 | (drop 19 | 6 20 | (l-system 21 | "FB" 22 | ("^" "" "" "" "" "") 23 | ("F=-A-" "X=FAB" "G=K[BFKBG]X" "K=KAB" "B=GGK" "A=F-F[+F]") 24 | 60 25 | 40))) 26 | 27 | :t3 28 | (first 29 | (drop 30 | 8 31 | (l-system 32 | "DOO" 33 | ("^" "" "" "" "" "") 34 | ("F=DAD-DO" "Y=+DDF" "O=YA" "W=FOO" "A=FFO+--WF" "D=") 35 | 90 36 | 40))) 37 | 38 | :t4 39 | (first 40 | (drop 41 | 7 42 | (l-system 43 | "M" 44 | ("^" "" "") 45 | ("F=MF" "H=H+F" "M=[]-FFMM") 46 | 60 47 | 50))) 48 | 49 | :t5 50 | (first 51 | (drop 52 | 6 53 | (l-system 54 | "IHIF" 55 | ("^" "" "" "" "") 56 | ("F=[H-]" "I=FHF" "J=[+]I" "S=[]" "H=+SHJFF") 57 | 60 58 | 30))) 59 | 60 | :t6 61 | (first 62 | (drop 63 | 11 64 | (l-system 65 | "FKKI" 66 | ("^" "" "") 67 | ("F=++IKKF-+" "I=[]F" "K=") 68 | 60 69 | 60))) 70 | 71 | :t7 72 | (first 73 | (drop 74 | 7 75 | (l-system 76 | "ZZZF" 77 | ("^" "") 78 | ("F=[]-+-ZFFF" "Z=FFF") 79 | 90 80 | 40))) 81 | 82 | :t8 83 | (first 84 | (drop 85 | 9 86 | (l-system 87 | "NNF" 88 | ("^" "") 89 | ("F=[-]" "N=F-FNF-NFF") 90 | 90 91 | 30))) 92 | 93 | :t9 94 | (first 95 | (drop 96 | 3 97 | (l-system 98 | "WFF" 99 | ("^" "" "") 100 | ("F=[]+[RFWWF]" "W=F+FF" "R=") 101 | 90 102 | 30)))}) 103 | 104 | -------------------------------------------------------------------------------- /src/lindenmayer/explorer/botany.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.explorer.botany 2 | (:require 3 | [lindenmayer.compiler :refer [l-system]])) 4 | 5 | (def arboria {:shrub 6 | (first 7 | (drop 8 | 5 9 | (l-system 10 | "F" 11 | ("^") 12 | ("F=#8F[+F]F[-F][F]") 13 | 21.3 14 | 25))) 15 | 16 | :tree 17 | (first 18 | (drop 19 | 4 20 | (l-system 21 | "F" 22 | ("^") 23 | ("F=#8FF-[#3-F+F+F]+[#9+F-F-F]") 24 | 22.5 25 | 40))) 26 | 27 | :fractal-plant 28 | (first 29 | (drop 30 | 7 31 | (l-system 32 | "X" 33 | ("" "^") 34 | ("X=#9F-[[X]+]+F[+FX]-X" "F=FF") 35 | 25 36 | 8)))}) 37 | -------------------------------------------------------------------------------- /src/lindenmayer/explorer/presets.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.explorer.presets 2 | (:require 3 | [lindenmayer.compiler :refer [l-system]])) 4 | 5 | (def fractals {:heighways-dragon 6 | (first 7 | (drop 8 | 13 9 | (l-system 10 | "^X" 11 | ("" "") 12 | ("X=X+Y^" "Y=^X-Y") 13 | 90 14 | 15))) 15 | 16 | :koch-curve 17 | (first 18 | (drop 19 | 5 20 | (l-system 21 | "-F" 22 | ("^") 23 | ("F=F+F-F-F+F") 24 | 90 25 | 10))) 26 | 27 | :koch-snowflake 28 | (first 29 | (drop 30 | 5 31 | (l-system 32 | "-F--F--F" 33 | ("^") 34 | ("F=F+F--F+F") 35 | 60 36 | 10))) 37 | 38 | :quadratic-koch-island 39 | (first 40 | (drop 41 | 2 42 | (l-system 43 | "F-F-F-F" 44 | ("^") 45 | ("F=F+FF-FF-F-F+F+FF-F-F+F+FF+FF-F") 46 | 90 47 | 15))) 48 | 49 | :koch-crystal 50 | (first 51 | (drop 52 | 4 53 | (l-system 54 | "F-F-F-F" 55 | ("^") 56 | ("F=FF-F--F-F") 57 | 90 58 | 15))) 59 | 60 | :koch-ring 61 | (first 62 | (drop 63 | 4 64 | (l-system 65 | "F-F-F-F" 66 | ("^") 67 | ("F=FF-F-F-F-F-F+F") 68 | 90 69 | 15))) 70 | 71 | :koch-blocks 72 | (first 73 | (drop 74 | 3 75 | (l-system 76 | "F-F-F-F" 77 | ("^") 78 | ("F=FF-F+F-F-FF") 79 | 90 80 | 30))) 81 | 82 | :koch-islands 83 | (first 84 | (drop 85 | 2 86 | (l-system 87 | "F+F+F+F" 88 | ("^" "~") 89 | ("F=F+f-FF+F+FF+Ff+FF-f+FF-F-FF-Ff-FFF" "f=ffffff") 90 | 90 91 | 15))) 92 | 93 | :cesaro-koch-fractal 94 | (first 95 | (drop 96 | 6 97 | (l-system 98 | "F" 99 | ("^") 100 | ("F=F+F--F+F") 101 | 85 102 | 30))) 103 | 104 | :fass-curve 105 | (first 106 | (drop 107 | 4 108 | (l-system 109 | "-L" 110 | ("" "" "^") 111 | ("L=LF+RFR+FL-F-LFLFL-FRFR+" "R=-LFLF+RFRFR+F+RF-LFL-FR" "F=F") 112 | 90 113 | 15))) 114 | 115 | :peano-gosper-curve 116 | (first 117 | (drop 118 | 4 119 | (l-system 120 | "A" 121 | ("^" "^") 122 | ("A=A-B--B+A++AA+B-" "B=+A-BB--B-A++A+B") 123 | 60 124 | 30))) 125 | 126 | :peano-curve 127 | (first 128 | (drop 129 | 4 130 | (l-system 131 | "F" 132 | ("^") 133 | ("F=F+F-F-F-F+F+F+F-F") 134 | 90 135 | 30))) 136 | 137 | :square-curve 138 | (first 139 | (drop 140 | 5 141 | (l-system 142 | "XF+F+X" 143 | ("" "^") 144 | ("X=XF-F+F-XF+F+XF-F+F-X" "F=^") 145 | 90 146 | 30))) 147 | 148 | :sierpinski-curve 149 | (first 150 | (drop 151 | 8 152 | (l-system 153 | "A" 154 | ("^" "^") 155 | ("A=B-A-B" "B=A+B+A") 156 | 60 10))) 157 | 158 | :sierpinski-triangle 159 | (first 160 | (drop 161 | 6 162 | (l-system 163 | "F-G-G" 164 | ("^" "^") 165 | ("F=F-G+F+G-F" "G=GG") 166 | 120 167 | 40))) 168 | 169 | :sierpinski-median-curve 170 | (first 171 | (drop 172 | 11 173 | (l-system 174 | "L--^--L--^" 175 | ("" "") 176 | ("L=+R-^-R+" "R=-L+^+L-") 177 | 45 178 | 15))) 179 | 180 | :hilberts-space-filling-curve 181 | (first 182 | (drop 183 | 6 184 | (l-system 185 | "X" 186 | ("" "") 187 | ("X=-Y^+X^X+^Y-" "Y=+X^-Y^Y-^X+") 188 | 90 189 | 20))) 190 | 191 | :sierpinski-carpet 192 | (first 193 | (drop 194 | 4 195 | (l-system 196 | "F" 197 | ("^" "^") 198 | ("F=F+F-F-F-G+F+F+F-F" "G=GGG") 199 | 90 20))) 200 | 201 | :lace 202 | (first 203 | (drop 204 | 8 205 | (l-system 206 | "W" 207 | ("" "" "" "") 208 | ("W=+++X--^--Z^X+", "X=---W++^++Y^W-", "Y=+Z^X--^--Z+++", "Z=-Y^W++^++Y---") 209 | 30 210 | 10))) 211 | 212 | :joined-cross-curve 213 | (first 214 | (drop 215 | 3 216 | (l-system 217 | "XYXYXYX+XYXYXYX+XYXYXYX+XYXYXYX" 218 | ("^" "" "") 219 | ("F=" "X=FX+FX+FXFY-FY-" "Y=+FX+FXFY-FY-FY") 220 | 90 221 | 20))) 222 | 223 | :penrose-tiling 224 | (first 225 | (drop 226 | 5 227 | (l-system 228 | "[B]++[B]++[B]++[B]++[B]" 229 | ("^" "^" "^" "^" "") 230 | ("A=CE++DE----BE[-CE----AE]++" "B=+CE--DE[---AE--BE]+" "C=-AE++BE[+++CE++DE]-" "D=--CE++++AE[+DE++++BE]--BE" "E=") 231 | 36 232 | 60)))}) 233 | -------------------------------------------------------------------------------- /src/lindenmayer/explorer/reptiles.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.explorer.reptiles 2 | (:require 3 | [lindenmayer.compiler :refer [l-system]])) 4 | 5 | ; based on http://canonical.org/~kragen/named-msgs/reptile-lsystems 6 | 7 | (def rep-4 {:reptile-1 8 | (first 9 | (drop 10 | 5 11 | (l-system 12 | "R" 13 | ("^" "" "") 14 | ("F=GG" "G=GG" "R=FF+FF[+G|R]FF+FF[+G|R]FF+FF+FF-F[+R]RF+") 15 | 90 16 | 50))) 17 | 18 | :reptile-1a 19 | (first 20 | (drop 21 | 1 22 | (l-system 23 | "++X++F-F-FF-FF-F-F" 24 | ("^" "" "") 25 | ("F=FF" "G=GG" "X=X[G+G-X][-G+G-X][+G+G-X][++F-F-F[+F]F-F[+F]F-F-F]") 26 | 90 27 | 50))) 28 | 29 | :reptile-2 30 | (first 31 | (drop 32 | 4 33 | (l-system 34 | "Q" 35 | ("^" "" "") 36 | ("F=FF" "P=FFF|Q|PFFF+FFFF+FFF|Q|F+PFF-FF+FF+" "Q=FFF|P|QFFF-FFFF-FFF|P|F-QFF+FF-FF-") 37 | 90 38 | 50))) 39 | 40 | :reptile-3 41 | (first 42 | (drop 43 | 5 44 | (l-system 45 | "R" 46 | ("^" "" "") 47 | ("F=FF" "R=F+R-F-FFF+L-F+FF+FFF+L-FFF+F+R-FFF+FF+" "L=F-L+F+FFF-R+F-FF-FFF-R+FFF-F-L+FFF-FF-") 48 | 90 49 | 50))) 50 | 51 | :reptile-4 52 | (first 53 | (drop 54 | 4 55 | (l-system 56 | "R" 57 | ("^" "") 58 | ("F=FF" "R=++FFR+FFR+FFR++FFFRF") 59 | 60 60 | 61 | 50)))}) 62 | -------------------------------------------------------------------------------- /src/lindenmayer/handler.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.handler 2 | (:require 3 | [compojure.core :refer [defroutes]] 4 | [compojure.handler :as handler] 5 | [compojure.route :as route] 6 | [ring.middleware.params :refer [wrap-params]] 7 | [ring.middleware.gzip :refer [wrap-gzip]] 8 | [hiccup.middleware :refer [wrap-base-url]] 9 | [ring.logger.timbre :as logger.timbre] 10 | [metrics.ring.expose :refer [expose-metrics-as-json]] 11 | [metrics.ring.instrument :refer [instrument]] 12 | [lindenmayer.explorer :as explorer])) 13 | 14 | (defroutes app-routes 15 | explorer/routes 16 | (route/not-found "You step in the stream, but the water has moved on.")) 17 | 18 | (def app 19 | (-> 20 | app-routes 21 | (logger.timbre/wrap-with-logger) 22 | (expose-metrics-as-json) 23 | (instrument) 24 | (wrap-base-url) 25 | (wrap-params) 26 | (wrap-gzip) 27 | (handler/api))) 28 | 29 | -------------------------------------------------------------------------------- /test/lindenmayer/compiler_test.clj: -------------------------------------------------------------------------------- 1 | (ns lindenmayer.compiler-test 2 | (:require 3 | [clojure.test :refer :all] 4 | [lindenmayer.compiler :refer :all])) 5 | 6 | (deftest check-split-on-assignment 7 | (let [symbol-table (make-symbol-table 30 10) 8 | convert (make-converter symbol-table)] 9 | (is (= ((comp split-on-assignment convert) "A=B-A-B") 10 | ['A ['B [:right 30] 'A [:right 30] 'B]])))) 11 | 12 | (deftest check-builder 13 | (let [symbol-table (make-symbol-table 30 10) 14 | convert (make-converter symbol-table)] 15 | (is (= (builder convert ["X=X+Y^", "Y=^X-Y"]) 16 | {'X ['X [:left 30] 'Y [:fwd 10]] 17 | 'Y [[:fwd 10] 'X [:right 30] 'Y]})))) 18 | 19 | ; ============================================== 20 | ; Sierpinski Triangle 21 | ; start : A 22 | ; rules : (A → B−A−B), (B → A+B+A) 23 | ; angle : 60° 24 | ; ============================================== 25 | ;(macroexpand-1 '(l-system "A" ("^" "^") ("A=B-A-B" "B=A+B+A") 60 10)) 26 | ;(flatten (nth (l-system "A" ("^" "^") ("A=B-A-B" "B=A+B+A") 60 10) 5)) 27 | ; ============================================== 28 | ; Koch Curve 29 | ; start : F 30 | ; rules : (F → F+F−F−F+F) 31 | ; ============================================== 32 | ;(macroexpand-1 '(l-system "-F" ("^") ("F=F+F-F-F+F"))) 33 | ;(take 5 (l-system "-F" ("^") ("F=F+F-F-F+F"))) 34 | ; ============================================== 35 | ; Dragon 36 | ; start : FX 37 | ; rules : (X → X+YF), (Y → FX-Y) 38 | ; ============================================== 39 | ;(macroexpand-1 '(l-system "X" ("^" "") ("X=X+Y^" "Y=^X-Y"))) 40 | ;(take 5 (l-system "X" ("^" "") ("X=X+Y^" "Y=^X-Y"))) 41 | ; ============================================== 42 | ; Fractal plant 43 | ; start : X 44 | ; rules : (X → F-[[X]+X]+F[+FX]-X), (F → FF) 45 | ; angle : 25° 46 | ; ============================================== 47 | ;(macroexpand-1 '(l-system "X" ("" "^") ("X=F-[[X]+]+F[+FX]-X" "F=FF"))) 48 | ;(take 5 (l-system "X" ("" "^") ("X=F-[[X]+]+F[+FX]-X" "F=FF"))) 49 | ;(macroexpand-1 '(l-system "[B]++[B]++[B]++[B]++[B]" ("^" "^" "^" "^" "") ("A=CE++DE----BE[-CE----AE]++" "B=+CE--DE[---AE--BE]+" "C=-AE++BE[+++CE++DE]-" "D=--CE++++AE[+DE++++BE]--BE" "E="))) 50 | ;(take 5 (l-system "[B]++[B]++[B]++[B]++[B]" ("^" "^" "^" "^" "") ("A=CE++DE----BE[-CE----AE]++" "B=+CE--DE[---AE--BE]+" "C=-AE++BE[+++CE++DE]-" "D=--CE++++AE[+DE++++BE]--BE" "E="))) 51 | 52 | --------------------------------------------------------------------------------