From 850ef80139680300e154e90416d132c33e776991 Mon Sep 17 00:00:00 2001 From: Jeremy Dormitzer Date: Fri, 8 Nov 2019 23:34:45 -0500 Subject: [PATCH] Implement more flexible state management --- deps.edn | 3 +- src/three_playground/core.cljs | 50 ++++++++++++++++++++-------- test/three_playground/core_test.cljs | 9 ++++- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/deps.edn b/deps.edn index 8a49d9e..d795e19 100644 --- a/deps.edn +++ b/deps.edn @@ -1,5 +1,6 @@ {:deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.clojure/clojurescript {:mvn/version "1.10.520"}} + org.clojure/clojurescript {:mvn/version "1.10.520"} + binaryage/oops {:mvn/version "0.7.0"}} :paths ["src" "resources"] :aliases {:fig {:extra-deps {com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"} diff --git a/src/three_playground/core.cljs b/src/three_playground/core.cljs index 2488d88..b416ae6 100644 --- a/src/three_playground/core.cljs +++ b/src/three_playground/core.cljs @@ -1,6 +1,8 @@ (ns ^:figwheel-hooks three-playground.core (:require [goog.dom :as gdom] + [goog.object :as obj] + [oops.core :refer [oset!+]] [three])) (defn get-app-element [] @@ -24,31 +26,53 @@ (.setSize renderer (.-innerWidth js/window) (.-innerHeight js/window)) (.add scene cube) (atom {:running true - :cube {:rotation {:x 0 :y 0}} :renderer renderer - :scene scene - :camera {:position {:z 5}} - :entities {:cube cube + :entity-state {:cube {:rotation {:x 0 :y 0}} + :camera {:position {:z 5}}} + :entities {:scene scene + :cube cube :camera camera}}))) (defonce app-state (init-state)) (defn update-state [state] (-> state - (assoc-in [:cube :rotation :x] - (+ (get-in state [:cube :rotation :x]) - 0.01)) - (assoc-in [:cube :rotation :y] - (+ (get-in state [:cube :rotation :y]) + (assoc-in [:entity-state :cube :rotation :x] + (+ (get-in state [:entity-state :cube :rotation :x]) + 0.03)) + (assoc-in [:entity-state :cube :rotation :y] + (+ (get-in state [:entity-state :cube :rotation :y]) 0.01)))) +(defn map->paths [m] + (if (map? m) + (vec + (mapcat (fn [[k v]] + (let [sub (map->paths v) + nested (map #(into [k] %) (filter (comp not empty?) sub))] + (if (seq nested) + nested + [[k]]))) + m)) + [])) + +(defn update-entities! [state] + (doseq [[id entity] (:entities state)] + (let [target-state (get-in state [:entity-state id])] + (when target-state + (doseq [path (map->paths target-state)] + (let [str-path (map name path) + current (apply aget entity str-path) + new (get-in target-state path)] + (when-not (= current new) + (oset!+ entity str-path new)))))))) + (defn render [state] - (let [{:keys [renderer scene]} state + (let [renderer (:renderer state) + scene (get-in state [:entities :scene]) camera (get-in state [:entities :camera]) cube (get-in state [:entities :cube])] - (set! (-> camera (.-position) (.-z)) (get-in state [:camera :position :z])) - (set! (-> cube (.-rotation) (.-x)) (get-in state [:cube :rotation :x])) - (set! (-> cube (.-rotation) (.-y)) (get-in state [:cube :rotation :y])) + (update-entities! state) (.render renderer scene camera))) (defn the-loop [] diff --git a/test/three_playground/core_test.cljs b/test/three_playground/core_test.cljs index 659b5de..761498c 100644 --- a/test/three_playground/core_test.cljs +++ b/test/three_playground/core_test.cljs @@ -1 +1,8 @@ -(ns three-playground.core-test) +(ns three-playground.core-test + (:require [cljs.test :refer-macros [deftest testing is]] + [three-playground.core :as core])) + +(deftest test-map->paths + (testing "Basic case" + (is (= (core/map->paths {:foo {:bar 1 :baz 2} :qux 3}) + [[:foo :bar] [:foo :baz] [:qux]]))))