Create grid from selection

This commit is contained in:
alonso.torres 2023-10-24 16:58:28 +02:00
parent 02399add7a
commit 351f7fd1bb
17 changed files with 395 additions and 234 deletions

View file

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.geom.shapes.modifiers
(ns app.common.geom.modifiers
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]

View file

@ -15,7 +15,6 @@
[app.common.geom.shapes.constraints :as gct]
[app.common.geom.shapes.corners :as gsc]
[app.common.geom.shapes.intersect :as gsi]
[app.common.geom.shapes.modifiers :as gsm]
[app.common.geom.shapes.path :as gsp]
[app.common.geom.shapes.transforms :as gtr]
[app.common.math :as mth]))
@ -203,8 +202,5 @@
(dm/export gsc/shape-corners-1)
(dm/export gsc/shape-corners-4)
;; Modifiers
(dm/export gsm/set-objects-modifiers)
;; Rect
(dm/export grc/rect->points)

View file

@ -10,7 +10,8 @@
[app.common.geom.shapes.flex-layout.bounds :as fbo]
[app.common.geom.shapes.flex-layout.drop-area :as fdr]
[app.common.geom.shapes.flex-layout.layout-data :as fld]
[app.common.geom.shapes.flex-layout.modifiers :as fmo]))
[app.common.geom.shapes.flex-layout.modifiers :as fmo]
[app.common.geom.shapes.flex-layout.params :as fp]))
(dm/export fbo/layout-content-bounds)
(dm/export fbo/layout-content-points)
@ -19,3 +20,4 @@
(dm/export fdr/get-drop-areas)
(dm/export fld/calc-layout-data)
(dm/export fmo/layout-child-modifiers)
(dm/export fp/calculate-params)

View file

@ -0,0 +1,97 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.geom.shapes.flex-layout.params
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.common :as gco]
[app.common.math :as mth]
[app.common.types.shape-tree :as ctt]))
(defn calculate-params
"Given the shapes calculate its flex parameters (horizontal vs vertical, gaps, etc)"
([objects shapes]
(calculate-params objects shapes nil))
([objects shapes parent]
(when (d/not-empty? shapes)
(let [points
(->> shapes
(map :id)
(ctt/sort-z-index objects)
(map (comp gco/shape->center (d/getf objects))))
start (first points)
end (reduce (fn [acc p] (gpt/add acc (gpt/to-vec start p))) points)
angle (gpt/signed-angle-with-other
(gpt/to-vec start end)
(gpt/point 1 0))
angle (mod angle 360)
t1 (min (abs (- angle 0)) (abs (- angle 360)))
t2 (abs (- angle 90))
t3 (abs (- angle 180))
t4 (abs (- angle 270))
tmin (min t1 t2 t3 t4)
direction
(cond
(mth/close? tmin t1) :row
(mth/close? tmin t2) :column-reverse
(mth/close? tmin t3) :row-reverse
(mth/close? tmin t4) :column)
selrects (->> shapes
(mapv :selrect))
min-x (->> selrects
(mapv #(min (:x1 %) (:x2 %)))
(apply min))
max-x (->> selrects
(mapv #(max (:x1 %) (:x2 %)))
(apply max))
all-width (->> selrects
(map :width)
(reduce +))
column-gap (if (and (> (count shapes) 1)
(or (= direction :row) (= direction :row-reverse)))
(/ (- (- max-x min-x) all-width)
(dec (count shapes)))
0)
min-y (->> selrects
(mapv #(min (:y1 %) (:y2 %)))
(apply min))
max-y (->> selrects
(mapv #(max (:y1 %) (:y2 %)))
(apply max))
all-height (->> selrects
(map :height)
(reduce +))
row-gap (if (and (> (count shapes) 1)
(or (= direction :column) (= direction :column-reverse)))
(/ (- (- max-y min-y) all-height)
(dec (count shapes)))
0)
layout-gap {:row-gap (max row-gap 0)
:column-gap (max column-gap 0)}
parent-selrect (:selrect parent)
padding (when (and (not (nil? parent)) (> (count shapes) 0))
{:p1 (min (- min-y (:y1 parent-selrect)) (- (:y2 parent-selrect) max-y))
:p2 (min (- min-x (:x1 parent-selrect)) (- (:x2 parent-selrect) max-x))})]
(cond-> {:layout-flex-dir direction :layout-gap layout-gap}
(not (nil? padding))
(assoc :layout-padding {:p1 (:p1 padding)
:p2 (:p2 padding)
:p3 (:p1 padding)
:p4 (:p2 padding)}))))))

View file

@ -9,6 +9,7 @@
[app.common.data.macros :as dm]
[app.common.geom.shapes.grid-layout.bounds :as glpb]
[app.common.geom.shapes.grid-layout.layout-data :as glld]
[app.common.geom.shapes.grid-layout.params :as glpr]
[app.common.geom.shapes.grid-layout.positions :as glp]))
(dm/export glld/calc-layout-data)
@ -19,3 +20,4 @@
(dm/export glp/cell-bounds)
(dm/export glpb/layout-content-points)
(dm/export glpb/layout-content-bounds)
(dm/export glpr/calculate-params)

View file

@ -590,7 +590,7 @@
[{:keys [origin row-tracks column-tracks shape-cells]} _transformed-parent-bounds [_ child]]
(let [grid-cell (get shape-cells (:id child))]
(when (some? grid-cell)
(when (and (some? grid-cell) (d/not-empty? grid-cell))
(let [column (nth column-tracks (dec (:column grid-cell)) nil)
row (nth row-tracks (dec (:row grid-cell)) nil)

View file

@ -0,0 +1,166 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.geom.shapes.grid-layout.params
(:require
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.points :as gpo]
[app.common.math :as mth]
[app.common.types.shape.layout :as ctl]
[clojure.set :as set]))
;; Small functions to help with ranges
(defn rect->range
"Creates ranges"
[axis rect]
(let [start (get (gpt/point rect) axis)]
(if (= axis :x)
[start (+ start (:width rect))]
[start (+ start (:height rect))])))
(defn overlaps-range?
"Return true if the ranges overlaps in the given axis"
[axis [start-a end-a] rect]
(let [[start-b end-b] (rect->range axis rect)]
(or (< start-a start-b end-a)
(< start-b start-a end-b)
(mth/close? start-a start-b)
(mth/close? end-a end-b))))
(defn join-range
"Creates a new range given the rect"
[axis [start-a end-a :as range] rect]
(if (not range)
(rect->range axis rect)
(let [[start-b end-b] (rect->range axis rect)]
[(min start-a start-b) (max end-a end-b)])))
(defn size-range
[[start end]]
(- end start))
(defn calculate-tracks
"Given the geometry and the axis calculates the tracks for the given shapes"
[axis shapes-by-axis]
(loop [pending (seq shapes-by-axis)
result []
index 1
current-track #{}
current-range nil]
(if pending
(let [[next-shape rect :as next-shape+rects] (first pending)]
(if (or (not current-range) (overlaps-range? axis current-range rect))
;; Add shape to current row
(let [current-track (conj current-track (:id next-shape))
current-range (join-range axis current-range rect)]
(recur (next pending) result index current-track current-range))
;; New row
(recur (next pending)
(conj result {:index index
:shapes current-track
:size (size-range current-range)})
(inc index)
#{(:id next-shape)}
(rect->range axis rect))))
;; Add the still ongoing row
(conj result {:index index
:shapes current-track
:size (size-range current-range)}))))
(defn assign-shape-cells
"Create cells for the defined tracks and assign the shapes to these cells"
[params rows cols]
(let [assign-cell
(fn [[params auto?] row column]
(let [row-num (:index row)
column-num (:index column)
cell (ctl/cell-by-row-column params row-num column-num)
shape (first (set/intersection (:shapes row) (:shapes column)))
auto? (and auto? (some? shape))]
[(cond-> params
(some? shape)
(assoc-in [:layout-grid-cells (:id cell) :shapes] [shape])
(not auto?)
(assoc-in [:layout-grid-cells (:id cell) :position] :manual))
auto?]))
[params _]
(->> rows
(reduce
(fn [result row]
(->> cols
(reduce
#(assign-cell %1 row %2)
result)))
[params true]))]
params))
(defn calculate-params
"Given the shapes calculate its grid parameters (horizontal vs vertical, gaps, etc)"
([objects shapes]
(calculate-params objects shapes nil))
([_objects shapes parent]
(if (empty? shapes)
(-> {:layout-grid-columns [{:type :auto} {:type :auto}]
:layout-grid-rows [{:type :auto} {:type :auto}]}
(ctl/create-cells [1 1 2 2]))
(let [all-shapes-rect (gco/shapes->rect shapes)
shapes+bounds
(->> shapes
(map #(vector % (grc/points->rect (get % :points)))))
shapes-by-x
(->> shapes+bounds
(sort-by (comp :x second)))
shapes-by-y
(->> shapes+bounds
(sort-by (comp :y second)))
cols (calculate-tracks :x shapes-by-x)
rows (calculate-tracks :y shapes-by-y)
num-cols (count cols)
num-rows (count rows)
total-cols-width (->> cols (reduce #(+ %1 (:size %2)) 0))
total-rows-height (->> rows (reduce #(+ %1 (:size %2)) 0))
column-gap
(if (= num-cols 1)
0
(/ (- (:width all-shapes-rect) total-cols-width) (dec num-cols)))
row-gap
(if (= num-rows 1)
0
(/ (- (:height all-shapes-rect) total-rows-height) (dec num-rows)))
layout-grid-rows (mapv (constantly (array-map :type :auto)) rows)
layout-grid-columns (mapv (constantly (array-map :type :auto)) cols)
parent-childs-vector (gpt/to-vec (gpo/origin (:points parent)) (gpt/point all-shapes-rect))
p-left (:x parent-childs-vector)
p-top (:y parent-childs-vector)]
(-> {:layout-grid-columns layout-grid-columns
:layout-grid-rows layout-grid-rows
:layout-gap {:row-gap row-gap
:column-gap column-gap}
:layout-padding {:p1 p-top :p2 p-left :p3 p-top :p4 p-left}
:layout-grid-dir (if (> num-cols num-rows) :row :column)}
(ctl/create-cells [1 1 num-cols num-rows])
(assign-shape-cells rows cols))))))

View file

@ -373,7 +373,7 @@
(cond-> objects
@changed?
(assoc-in [parent-id :shapes] new-shapes))))
(d/assoc-in-when [parent-id :shapes] new-shapes))))
check-modify-component
(fn [data]

View file

@ -227,8 +227,7 @@
([objects ids {:keys [bottom-frames?] :as options
:or {bottom-frames? false}}]
(letfn [
(comp [id-a id-b]
(letfn [(comp [id-a id-b]
(cond
(= id-a id-b)
0