Last active
June 14, 2021 23:34
-
-
Save bhauman/1f8b59927ff183e80bb141215e715777 to your computer and use it in GitHub Desktop.
Generating a random ceramic tile layout for my shower in CLJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns ^:figwheel-hooks tilelayout.core | |
(:require | |
[goog.dom :as gdom] | |
[goog.crypt.base64 :as base64] | |
[goog.events :as gevent] | |
[clojure.string :as string] | |
[cljs.reader :refer [read-string]]) | |
(:import [goog History])) | |
;; TILE LAYOUT GENERATION | |
(def HEIGHT 13) | |
(def WIDTH (+ 5 7 5)) | |
(def colors [:arancio | |
:giallo | |
:rosso | |
:blu | |
:azzurrio | |
:acquamarina | |
:bianco | |
:bianco | |
:cenere | |
:cenere]) | |
(defn taken-colors [{:keys [x y]} filled-pos-index] | |
(fn [col] | |
(let [neighbors (cond-> [{:x x :y (dec y)} | |
{:x (dec x) :y y} | |
;; don't allow identical neighbors two tiles away | |
{:x x :y (dec (dec y))} | |
{:x (dec (dec x)) :y y}] | |
(not (or (#{:cenere :bianco} col) | |
(< (rand) 0.1))) | |
;; dissallow identical corners | |
(concat [{:x (dec x) :y (dec y)} | |
{:x (dec x) :y (inc y)} | |
{:x (inc x) :y (dec y)}]))] | |
((->> neighbors | |
(keep (fn [{:keys [x y]}] | |
(get-in filled-pos-index [x y]))) | |
set) | |
col)))) | |
(defn neighbor-constrained-color-lookup-index | |
[positions avail-colors color-index-accum] | |
(if (empty? positions) | |
color-index-accum | |
(let [[p & ps] positions | |
[unused-neighbor-matching-colors [uniq-color & colors-left]] | |
(split-with (taken-colors p color-index-accum) avail-colors)] | |
(recur ps | |
(concat unused-neighbor-matching-colors colors-left) | |
(assoc-in color-index-accum [(:x p) (:y p)] uniq-color))))) | |
(defn gen-positions [width-range height-range] | |
(for [y height-range | |
x width-range] | |
{:x x :y y})) | |
(defn gen-vertical-striped-positions [stripe-width width-range height-range] | |
(mapcat #(gen-positions % height-range) | |
(partition-all stripe-width width-range))) | |
(defn random-tile-layout [] | |
(let [positions (->> | |
;; IMPORTANT for even distribution order the | |
;; positions in vertical stripes with a width that | |
;; is the square root of the the number of unique | |
;; tiles, in this case 10 unique tiles so the width | |
;; of our vertical stripes is 3 | |
(gen-vertical-striped-positions | |
(js/Math.trunc (js/Math.sqrt (count colors))) | |
(range WIDTH) (range HEIGHT)) | |
(remove | |
(set (concat | |
;; window | |
(gen-positions (range 6 8) (range 1 6)) | |
;; bench | |
(gen-positions (range 2 7) (range 11 13))))))] | |
(neighbor-constrained-color-lookup-index | |
positions | |
;; IMPORTANT for even distribution don't shuffle colors in total! | |
(mapcat shuffle (repeat colors)) | |
{}))) | |
;; DISPLAY | |
(declare draw-walls) | |
(defonce hist | |
(doto (History. false) | |
(.setEnabled true) | |
(gevent/listen goog.history.EventType.NAVIGATE | |
#(some->> (.-token %) | |
base64/decodeString | |
read-string | |
(draw-walls WIDTH HEIGHT))))) | |
(defn register-draw-event [positions-with-colors] | |
(when positions-with-colors | |
(.setToken hist (base64/encodeString (pr-str positions-with-colors))))) | |
(defn draw-walls [width height positions-with-colors] | |
(letfn [(dom [klass] (gdom/createDom "div" #js {:class klass}))] | |
(doto (gdom/getElement "app") | |
(gdom/removeChildren) | |
(gdom/append | |
(doto (gdom/createDom "button" #js{} "next") | |
(.addEventListener | |
"click" | |
#(register-draw-event (random-tile-layout))))) | |
(gdom/append | |
(doto (dom "wall") | |
(gdom/append (gdom/createTable height width false)) | |
(gdom/append (dom "cover top-wall")) | |
(gdom/append (dom "cover left-wall-1")) | |
(gdom/append (dom "cover left-wall-2")) | |
(gdom/append (dom "cover right-wall-1")) | |
(gdom/append (dom "cover right-wall-2")) | |
(gdom/append (dom "cover window-cover")) | |
(gdom/append (dom "cover bench-cover"))))) | |
(doseq [[{:keys [x y]} elem] (map vector | |
(gen-positions (range width) (range height)) | |
(gdom/getElementsByTagName "td"))] | |
(gdom/append elem (dom "cell")) | |
(when-let [color (some-> positions-with-colors | |
(get-in [x y]) | |
name)] | |
(set! (.-className elem) color)))) | |
(js/console.log "Color frequency") | |
(->> positions-with-colors | |
vals | |
(mapcat vals) | |
frequencies | |
prn) | |
positions-with-colors) | |
(defn setup [tok] | |
(if (or (nil? tok) (string/blank? tok)) | |
(random-tile-layout) | |
(let [data (read-string (base64/decodeString tok))] | |
(draw-walls WIDTH HEIGHT data) | |
nil))) | |
(register-draw-event (setup (.getToken hist))) | |
;; specify reload hook with ^;after-load metadata | |
(defn ^:after-load on-reload [] | |
(register-draw-event (random-tile-layout))) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.wall { | |
font-size: 0.3em; | |
position:relative; | |
} | |
.cover { | |
position: absolute; | |
background-color: rgba(255,255,255,1); | |
} | |
.left-wall-1 { | |
top: 0px; | |
left: 0px; | |
width: 3em; | |
height: 600px; | |
} | |
.left-wall-2 { | |
top: 0px; | |
left: 203px; | |
width: 4.4em; | |
height: 600px; | |
} | |
.right-wall-2 { | |
top: 0px; | |
left: 748px; | |
width: 3em; | |
height: 600px; | |
} | |
.right-wall-1 { | |
top: 0px; | |
left: 539px; | |
width: 4.4em; | |
height: 600px; | |
} | |
.window-cover { | |
top: 39px; | |
left: 243px; | |
width: 150px; | |
height: 252px; | |
} | |
.top-wall { | |
top: 0px; | |
left: 0px; | |
width: 100%; | |
height: 3em; | |
} | |
.bench-cover { | |
top: 476px; | |
left: 90px; | |
width: 245px; | |
height: 110px; | |
} | |
.cell { | |
width: 8.5em; | |
height: 8.5em; | |
border-color: grey; | |
} | |
.arancio { | |
background-color: rgb(255, 137, 75); | |
} | |
.giallo { | |
background-color: rgb(255, 207, 92); | |
} | |
.rosso { | |
background-color: rgb(196, 56, 61); | |
} | |
.blu { | |
background-color: rgb(14,40,95); | |
} | |
.azzurrio { | |
background-color: rgb(62, 163, 187); | |
} | |
.acquamarina { | |
background-color: rgb(165, 219, 171); | |
} | |
.smeraldo { | |
background-color: rgb(28,85,77); | |
} | |
.bianco { | |
background-color: rgb(242, 241, 233); | |
} | |
.nero { | |
background-color: rgb(28,32,31); | |
} | |
.cenere { | |
background-color: rgb(232, 230, 225); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment