(ns heyarne.rect-packing.core (:require [reagent.core :as r] [reagent.dom :as dom] [thi.ng.geom.core :as geom] [thi.ng.geom.rect :as rect] [thi.ng.geom.vector :as v])) (defonce state (r/atom {:rects [] :frame (rect/rect 500 500)})) (comment (swap! state update :rects empty) (swap! state update :rects conj (rect/rect 200 200)) ;; fill with random rectangles (swap! state assoc :rects (repeatedly 20 #(rect/rect (+ 50 (rand-int 50)) (+ 50 (rand-int 50))))) ) (defn pack-rects-naive "Sorts all rectangles by height and places them next to each other into frame, starting at the top left." [frame rects] (let [rects (sort-by (comp - geom/height) rects)] (-> (reduce (fn [acc rect] ;; a word on "top" vs "bottom": the thi.ng coordinate system ;; works like the ones you know from scool, i.e. [0 1] is ;; above [0 0]. the svg coordinate system uses screen ;; coordinates, where [0 0] is at the top left. if you see ;; rect/bottom we're actually looking at the top edge that is ;; drawn in the svg. (let [last-placed (or (last (:result acc)) (rect/rect 0 0)) moved-right (geom/translate rect [(rect/right last-placed) (rect/bottom (:row-start acc))])] ;; do we still have enough space to the right? (if (<= (rect/right moved-right) (rect/right frame)) ;; if yes, everything is bon (update acc :result conj moved-right) ;; if no, move to the bottom (let [moved-bottom (geom/translate rect [0 (rect/top (:row-start acc))])] (-> (assoc acc :row-start moved-bottom) (update :result conj moved-bottom)))))) {:row-start (first rects) :result []} rects) :result))) #_(pack-rects-naive (:frame @state) (:rects @state)) (defn main [] (let [{:keys [frame rects]} @state sorted (map-indexed vector (pack-rects-naive frame rects))] [:main [:h1 "Visualization"] [:svg.visualization {:viewBox "-0.5 -0.5 501 501" :xmlns "http://www.w3.org/2000/svg"} [:rect {:width (geom/width frame) :height (geom/height frame)}] (for [[idx rect] sorted] ^{:key idx} [:rect {:width (geom/width rect) :height (geom/height rect) :x (-> rect :p :x) :y (-> rect :p :y)}])] [:h2 "Rectangles"] [:ul (for [[idx rect] sorted] ^{:key idx} [:li [:pre (pr-str rect)]])]])) (defn ^:dev/after-load init [] (println "Initializing…") (dom/render [main] (.querySelector js/document "#app"))) (defonce app (init)) (+ 1 2)