├── resources ├── gen.png ├── knot │ ├── t.png │ ├── cross.png │ ├── empty.png │ ├── line.png │ ├── corner.png │ └── data.json ├── tiled-gen.png └── citytemplate.png ├── doc └── intro.md ├── .gitignore ├── test └── wavefunctioncollapse │ └── core_test.clj ├── project.clj ├── CHANGELOG.md ├── LICENSE ├── README.md └── src └── wavefunctioncollapse └── core.clj /resources/gen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/gen.png -------------------------------------------------------------------------------- /resources/knot/t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/knot/t.png -------------------------------------------------------------------------------- /resources/knot/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/knot/cross.png -------------------------------------------------------------------------------- /resources/knot/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/knot/empty.png -------------------------------------------------------------------------------- /resources/knot/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/knot/line.png -------------------------------------------------------------------------------- /resources/tiled-gen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/tiled-gen.png -------------------------------------------------------------------------------- /resources/citytemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/citytemplate.png -------------------------------------------------------------------------------- /resources/knot/corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allison-casey/wavefunctioncollapse-clj/HEAD/resources/knot/corner.png -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to wavefunctioncollapse 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .#* 2 | /target 3 | /classes 4 | /checkouts 5 | profiles.clj 6 | pom.xml 7 | pom.xml.asc 8 | *.jar 9 | *.class 10 | /.lein-* 11 | /.nrepl-port 12 | .hgignore 13 | .hg/ 14 | -------------------------------------------------------------------------------- /test/wavefunctioncollapse/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns wavefunctioncollapse.core-test 2 | (:require [clojure.test :refer :all] 3 | [wavefunctioncollapse.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject wavefunctioncollapse "0.1.6" 2 | :description "Bitmap & tilemap generation from a single example with the help of ideas from quantum mechanics." 3 | :url "https://github.com/sjcasey21/wavefunctioncollapse-clj" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.10.0"] 7 | [com.github.sjcasey21/wavefunctioncollapse "0.2.2"]] 8 | :plugins [[lein-codox "0.10.7"]] 9 | :profiles {:dev {:dependencies [[cheshire "5.9.0"] 10 | [net.mikera/imagez "0.12.0"]]}} 11 | :repl-options {:init-ns wavefunctioncollapse.core}) 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2019-12-04 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2019-12-04 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/wavefunctioncollapse/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/wavefunctioncollapse/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Steven Casey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wavefunctioncollapse 2 | 3 | [![cljdoc badge](https://cljdoc.org/badge/wavefunctioncollapse/wavefunctioncollapse)](https://cljdoc.org/d/wavefunctioncollapse/wavefunctioncollapse/CURRENT) 4 | 5 | Bitmap and tilemap generation from a single example with the help of ideas from quantum mechanics. Java port of mxgmn's wavefunctioncollapse library. 6 | 7 | Javascript port of mxgmn's [WaveFunctionCollapse](https://github.com/mxgmn/WaveFunctionCollapse "WaveFunctionCollapse") library. 8 | 9 | ## Usage 10 | 11 | This library provides clojure bindings for the java wavefunctioncollapse library found [here](https://github.com/sjcasey21/wavefunctioncollapse "here"). 12 | 13 | To Install, add `[wavefunctioncollapse "0.1.6"]` to your lein dependencies. 14 | 15 | For more information check out the [docs](https://cljdoc.org/d/wavefunctioncollapse/wavefunctioncollapse/CURRENT "here") . 16 | 17 | ## Quickstart 18 | 19 | ```clojure 20 | (require [wavefunctioncollapse.core :refer [overlapping-model simple-tiled-model]] 21 | [mikera.image.core :as img] 22 | [cheshire.core :as c]) 23 | 24 | (overlapping-model 25 | (img/load-image "resources/citytemplate.png") 26 | 3 27 | 64 28 | 64 29 | :periodic-output true) 30 | 31 | (def config (-> "resources/knot/data.json" slurp (c/parse-string true))) 32 | (def tilenames (for [tile (:tiles config)] (:name tile))) 33 | (def images (zipmap tilenames 34 | (map #(img/load-image (str "resources/knot/" % ".png")) tilenames))) 35 | (simple-tiled-model 36 | config 37 | images 38 | 32 39 | 40 40 | :subset :fabric) 41 | ``` 42 | 43 | ## License 44 | 45 | Release under the MIT license. See LICENSE for the full license. 46 | -------------------------------------------------------------------------------- /resources/knot/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "tiles": [ 3 | { 4 | "name": "corner", 5 | "symmetry": "L" 6 | }, 7 | { 8 | "name": "cross", 9 | "symmetry": "I" 10 | }, 11 | { 12 | "name": "empty", 13 | "symmetry": "X" 14 | }, 15 | { 16 | "name": "line", 17 | "symmetry": "I" 18 | }, 19 | { 20 | "name": "t", 21 | "symmetry": "T" 22 | } 23 | ], 24 | "neighbors": [ 25 | { 26 | "left": "corner 1", 27 | "right": "empty" 28 | }, 29 | { 30 | "left": "corner", 31 | "right": "cross" 32 | }, 33 | { 34 | "left": "corner", 35 | "right": "cross 1" 36 | }, 37 | { 38 | "left": "corner", 39 | "right": "line" 40 | }, 41 | { 42 | "left": "corner 1", 43 | "right": "line 1" 44 | }, 45 | { 46 | "left": "corner", 47 | "right": "t 2" 48 | }, 49 | { 50 | "left": "corner", 51 | "right": "t 3" 52 | }, 53 | { 54 | "left": "corner", 55 | "right": "t" 56 | }, 57 | { 58 | "left": "corner 1", 59 | "right": "t 1" 60 | }, 61 | { 62 | "left": "corner 1", 63 | "right": "corner 3" 64 | }, 65 | { 66 | "left": "corner 1", 67 | "right": "corner" 68 | }, 69 | { 70 | "left": "corner", 71 | "right": "corner 1" 72 | }, 73 | { 74 | "left": "corner", 75 | "right": "corner 2" 76 | }, 77 | { 78 | "left": "cross", 79 | "right": "cross" 80 | }, 81 | { 82 | "left": "cross", 83 | "right": "cross 1" 84 | }, 85 | { 86 | "left": "cross 1", 87 | "right": "cross 1" 88 | }, 89 | { 90 | "left": "cross", 91 | "right": "line" 92 | }, 93 | { 94 | "left": "cross 1", 95 | "right": "line" 96 | }, 97 | { 98 | "left": "cross", 99 | "right": "t" 100 | }, 101 | { 102 | "left": "cross", 103 | "right": "t 3" 104 | }, 105 | { 106 | "left": "cross 1", 107 | "right": "t" 108 | }, 109 | { 110 | "left": "cross 1", 111 | "right": "t 3" 112 | }, 113 | { 114 | "left": "empty", 115 | "right": "empty" 116 | }, 117 | { 118 | "left": "empty", 119 | "right": "line 1" 120 | }, 121 | { 122 | "left": "empty", 123 | "right": "t 1" 124 | }, 125 | { 126 | "left": "line", 127 | "right": "line" 128 | }, 129 | { 130 | "left": "line 1", 131 | "right": "line 1" 132 | }, 133 | { 134 | "left": "line", 135 | "right": "t" 136 | }, 137 | { 138 | "left": "line 1", 139 | "right": "t 1" 140 | }, 141 | { 142 | "left": "line", 143 | "right": "t 3" 144 | }, 145 | { 146 | "left": "t 1", 147 | "right": "t 3" 148 | }, 149 | { 150 | "left": "t", 151 | "right": "t" 152 | }, 153 | { 154 | "left": "t 2", 155 | "right": "t" 156 | }, 157 | { 158 | "left": "t 1", 159 | "right": "t" 160 | }, 161 | { 162 | "left": "t 3", 163 | "right": "t 1" 164 | } 165 | ], 166 | "subsets": { 167 | "standard": ["corner", "cross", "empty", "line"], 168 | "dense": ["corner", "cross", "line"], 169 | "crossless": ["corner", "empty", "line"], 170 | "te": ["t", "empty"], 171 | "t": ["t"], 172 | "cl": ["corner", "line"], 173 | "ce": ["corner", "empty"], 174 | "c": ["corner"], 175 | "fabric": ["cross", "line"], 176 | "dense-fabric": ["cross"] 177 | }, 178 | "tilesize": 10 179 | } 180 | -------------------------------------------------------------------------------- /src/wavefunctioncollapse/core.clj: -------------------------------------------------------------------------------- 1 | (ns wavefunctioncollapse.core 2 | (:require [clojure.spec.alpha :as s] 3 | [clojure.walk :refer [stringify-keys]]) 4 | (:import (com.github.sjcasey21.wavefunctioncollapse 5 | OverlappingModel 6 | SimpleTiledModel) 7 | (java.awt.image BufferedImage))) 8 | 9 | (s/check-asserts true) 10 | 11 | (s/def ::data #(instance? BufferedImage %)) 12 | (s/def ::N int?) 13 | (s/def ::width int?) 14 | (s/def ::height int?) 15 | (s/def ::periodic-input boolean?) 16 | (s/def ::periodic-output boolean?) 17 | (s/def ::symmetry (s/and int? #(<= 0 % 8))) 18 | (s/def ::ground int?) 19 | (s/def ::seed (s/and int? #(<= Integer/MIN_VALUE % Integer/MAX_VALUE))) 20 | (s/def ::limit pos-int?) 21 | 22 | (s/fdef overlapping-model 23 | :args (s/cat :data ::data 24 | :N ::N 25 | :width ::width 26 | :height ::height 27 | :kwargs (s/keys* :req-un [::periodic-input 28 | ::periodic-output 29 | ::symmetry 30 | ::ground 31 | ::seed 32 | ::limit])) 33 | :ret #(instance? BufferedImage %)) 34 | 35 | (defn overlapping-model 36 | "Generates a new image using the Overlapping Model. 37 | 38 | ## Positional Arguments 39 | `data` A `BufferedImage` object to use as the initial sample. 40 | `N` The size of the patterns. 41 | `width` Width of the output generation in pixels. 42 | `height` Heght of the output generation in pixels. 43 | 44 | ## Keyword Arguments 45 | `periodic-input` Whether or not the input is periodic. 46 | `periodic-output` Whether or not the output should be periodic. 47 | `symmetry` Allowed symmetries from 1 (no symmetry) to 8 (all mirrored / rotated). 48 | `ground` Id of the specific pattern to use as the bottom of the generation. 49 | `seed` Seed to use for the random generator. 50 | `limit` Maximum number of iterations before finishing." 51 | [data 52 | N 53 | width 54 | height 55 | & {:keys 56 | [periodic-input periodic-output symmetry ground seed limit] 57 | :or 58 | {periodic-input true 59 | periodic-output false 60 | symmetry 8 61 | ground 0 62 | seed (rand-int Integer/MAX_VALUE) 63 | limit 0}}] 64 | (let [model (OverlappingModel. 65 | data 66 | N 67 | width 68 | height 69 | periodic-input 70 | periodic-output 71 | symmetry 72 | ground) 73 | result (.run model seed limit) 74 | graphics (.graphics model)] 75 | {:seed seed 76 | :image graphics})) 77 | 78 | (s/def ::tilesize int?) 79 | 80 | (s/def ::name string?) 81 | (s/def ::symmetry (s/and string? #{"L" "T" "I" "\\" "X"})) 82 | (s/def ::tile (s/keys :req-un [::name ::symmetry])) 83 | (s/def ::tiles (s/coll-of ::tile)) 84 | 85 | (s/def ::left string?) 86 | (s/def ::right string?) 87 | (s/def ::neighbor (s/keys :req-un [::left ::right])) 88 | (s/def ::neighbors (s/coll-of ::neighbor)) 89 | 90 | (s/def ::subset (s/or :k keyword? :s string?)) 91 | (s/def ::subsets (s/map-of ::subset (s/coll-of string?))) 92 | 93 | (s/def ::config (s/keys :req-un [::tilesize ::tiles ::neighbors] 94 | :opt-un [::subsets])) 95 | 96 | (s/def ::periodic boolean?) 97 | (s/def ::black boolean?) 98 | (s/def ::unique boolean?) 99 | 100 | (s/fdef simple-tiled-model 101 | :args (s/cat :config ::config 102 | :width ::width 103 | :height ::height 104 | :kwargs (s/keys 105 | :opt-un 106 | [::subset ::periodic ::black ::unique ::seed ::limit])) 107 | :ret #(instance? BufferedImage %)) 108 | 109 | (defn simple-tiled-model 110 | "Generate a new image using the Simple Tiled Model. 111 | 112 | ## Positional Arguments 113 | `config` Tiles, subset and constraint definitions. 114 | `images` Map of `BufferedImage`'s keyed by their `tilename`. 115 | `width` Output width in number of tiles. 116 | `height` Output height in number of tiles. 117 | 118 | ## Keyword Arguments 119 | `subset` Name of the subset defined in `config` to use. 120 | `periodic` Whether or not the output should be periodic. 121 | `black` 122 | `unique` 123 | `seed` Seed to use for the random generator. 124 | `limit` Maximum number of iterations before finishing." 125 | [config 126 | images 127 | width 128 | height 129 | & {:keys [subset periodic black unique seed limit] 130 | :or {subset nil 131 | periodic false 132 | black false 133 | unique false 134 | seed (rand-int Integer/MAX_VALUE) 135 | limit 0}}] 136 | (s/assert ::config config) 137 | (let [{:keys [tilesize tiles neighbors subsets]} config 138 | tiles (map stringify-keys tiles) 139 | neighbors (map stringify-keys neighbors) 140 | subsets (reduce-kv (fn [m k v] (assoc m (name k) (into-array v))) {} subsets) 141 | subset (if subset (name subset)) 142 | model (SimpleTiledModel. 143 | tilesize 144 | tiles 145 | neighbors 146 | subsets 147 | images 148 | subset 149 | width 150 | height 151 | periodic 152 | black 153 | unique) 154 | result (.run model seed limit) 155 | graphics (.graphics model)] 156 | {:seed seed 157 | :image graphics})) 158 | --------------------------------------------------------------------------------