mirror of
https://github.com/penpot/penpot.git
synced 2025-06-08 06:21:38 +02:00
🎉 Add library page for components on migration
This commit is contained in:
parent
bdcbe46d0d
commit
ce09ea6eb5
10 changed files with 150 additions and 252 deletions
|
@ -173,6 +173,10 @@
|
||||||
[data]
|
[data]
|
||||||
(into {} (remove (comp nil? second)) data))
|
(into {} (remove (comp nil? second)) data))
|
||||||
|
|
||||||
|
(defn vec-without-nils
|
||||||
|
[coll]
|
||||||
|
(into [] (remove nil?) coll))
|
||||||
|
|
||||||
(defn without-qualified
|
(defn without-qualified
|
||||||
[data]
|
[data]
|
||||||
(into {} (remove (comp qualified-keyword? first)) data))
|
(into {} (remove (comp qualified-keyword? first)) data))
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[app.common.pages.changes :as ch]
|
[app.common.pages.changes :as ch]
|
||||||
[app.common.pages.changes-spec :as pcs]
|
[app.common.pages.changes-spec :as pcs]
|
||||||
[app.common.pages.init :as init]
|
[app.common.pages.init :as init]
|
||||||
|
[app.common.types.page :as ctp]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.types.page :as ctp]
|
[app.common.types.page :as ctp]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
@ -179,8 +180,7 @@
|
||||||
|
|
||||||
(assert (nil? (:current-component-id file)))
|
(assert (nil? (:current-component-id file)))
|
||||||
(let [page-id (or (:id data) (uuid/next))
|
(let [page-id (or (:id data) (uuid/next))
|
||||||
page (-> init/empty-page-data
|
page (-> (ctp/make-empty-page page-id "Page-1")
|
||||||
(assoc :id page-id)
|
|
||||||
(d/deep-merge data))]
|
(d/deep-merge data))]
|
||||||
(-> file
|
(-> file
|
||||||
(commit-change
|
(commit-change
|
||||||
|
|
|
@ -41,11 +41,21 @@
|
||||||
|
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
|
(defn bounding-box
|
||||||
|
"Returns a rect that wraps the shape after all transformations applied."
|
||||||
|
[shape]
|
||||||
|
; TODO: perhaps we need to store this calculation in a shape attribute
|
||||||
|
(gpr/points->rect (:points shape)))
|
||||||
|
|
||||||
(defn left-bound
|
(defn left-bound
|
||||||
|
"Returns the lowest x coord of the shape BEFORE applying transformations."
|
||||||
|
; TODO: perhaps some day we want after transformations, but for the
|
||||||
|
; moment it's enough as is now.
|
||||||
[shape]
|
[shape]
|
||||||
(get shape :x (:x (:selrect shape)))) ; Paths don't have :x attribute
|
(get shape :x (:x (:selrect shape)))) ; Paths don't have :x attribute
|
||||||
|
|
||||||
(defn top-bound
|
(defn top-bound
|
||||||
|
"Returns the lowest y coord of the shape BEFORE applying transformations."
|
||||||
[shape]
|
[shape]
|
||||||
(get shape :y (:y (:selrect shape)))) ; Paths don't have :y attribute
|
(get shape :y (:y (:selrect shape)))) ; Paths don't have :y attribute
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
[app.common.pages.init :as init]
|
[app.common.pages.init :as init]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.pages.changes-spec :as pcs]
|
[app.common.pages.changes-spec :as pcs]
|
||||||
[app.common.types.shape :as cts]))
|
[app.common.types.page :as ctp]
|
||||||
|
[app.common.types.pages-list :as ctpl]
|
||||||
|
[app.common.types.shape :as cts]
|
||||||
|
[app.common.types.shape-tree :as ctst]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Specific helpers
|
;; Specific helpers
|
||||||
|
@ -28,10 +31,6 @@
|
||||||
[coll o]
|
[coll o]
|
||||||
(into [] (filter #(not= % o)) coll))
|
(into [] (filter #(not= % o)) coll))
|
||||||
|
|
||||||
(defn vec-without-nils
|
|
||||||
[coll]
|
|
||||||
(into [] (remove nil?) coll))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Page Transformation Changes
|
;; Page Transformation Changes
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -74,44 +73,9 @@
|
||||||
|
|
||||||
(defmethod process-change :add-obj
|
(defmethod process-change :add-obj
|
||||||
[data {:keys [id obj page-id component-id frame-id parent-id index ignore-touched]}]
|
[data {:keys [id obj page-id component-id frame-id parent-id index ignore-touched]}]
|
||||||
(letfn [(update-parent-shapes [shapes]
|
(let [update-container
|
||||||
;; Ensure that shapes is always a vector.
|
(fn [container]
|
||||||
(let [shapes (into [] shapes)]
|
(ctst/add-shape id obj container frame-id parent-id index ignore-touched))]
|
||||||
(cond
|
|
||||||
(some #{id} shapes)
|
|
||||||
shapes
|
|
||||||
|
|
||||||
(nil? index)
|
|
||||||
(conj shapes id)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(cph/insert-at-index shapes index [id]))))
|
|
||||||
|
|
||||||
(update-parent [parent]
|
|
||||||
(-> parent
|
|
||||||
(update :shapes update-parent-shapes)
|
|
||||||
(update :shapes vec-without-nils)
|
|
||||||
(cond-> (and (:shape-ref parent)
|
|
||||||
(not= (:id parent) frame-id)
|
|
||||||
(not ignore-touched))
|
|
||||||
(-> (update :touched cph/set-touched-group :shapes-group)
|
|
||||||
(dissoc :remote-synced?)))))
|
|
||||||
|
|
||||||
;; TODO: this looks wrong, why we allow nil values?
|
|
||||||
(update-objects [objects parent-id]
|
|
||||||
(if (and (or (nil? parent-id) (contains? objects parent-id))
|
|
||||||
(or (nil? frame-id) (contains? objects frame-id)))
|
|
||||||
(-> objects
|
|
||||||
(assoc id (-> obj
|
|
||||||
(assoc :frame-id frame-id)
|
|
||||||
(assoc :parent-id parent-id)
|
|
||||||
(assoc :id id)))
|
|
||||||
(update parent-id update-parent))
|
|
||||||
objects))
|
|
||||||
|
|
||||||
(update-container [data]
|
|
||||||
(let [parent-id (or parent-id frame-id)]
|
|
||||||
(update data :objects update-objects parent-id)))]
|
|
||||||
|
|
||||||
(if page-id
|
(if page-id
|
||||||
(d/update-in-when data [:pages-index page-id] update-container)
|
(d/update-in-when data [:pages-index page-id] update-container)
|
||||||
|
@ -237,7 +201,7 @@
|
||||||
;; We need to ensure that no `nil` in the
|
;; We need to ensure that no `nil` in the
|
||||||
;; shapes list after adding all the
|
;; shapes list after adding all the
|
||||||
;; incoming shapes to the parent.
|
;; incoming shapes to the parent.
|
||||||
(update :shapes vec-without-nils))]
|
(update :shapes d/vec-without-nils))]
|
||||||
(cond-> parent
|
(cond-> parent
|
||||||
(and (:shape-ref parent) (= (:type parent) :group) (not ignore-touched))
|
(and (:shape-ref parent) (= (:type parent) :group) (not ignore-touched))
|
||||||
(-> (update :touched cph/set-touched-group :shapes-group)
|
(-> (update :touched cph/set-touched-group :shapes-group)
|
||||||
|
@ -258,7 +222,7 @@
|
||||||
|
|
||||||
(-> objects
|
(-> objects
|
||||||
(d/update-in-when [pid :shapes] without-obj sid)
|
(d/update-in-when [pid :shapes] without-obj sid)
|
||||||
(d/update-in-when [pid :shapes] vec-without-nils)
|
(d/update-in-when [pid :shapes] d/vec-without-nils)
|
||||||
(cond-> component? (d/update-when pid #(-> %
|
(cond-> component? (d/update-when pid #(-> %
|
||||||
(update :touched cph/set-touched-group :shapes-group)
|
(update :touched cph/set-touched-group :shapes-group)
|
||||||
(dissoc :remote-synced?)))))))))
|
(dissoc :remote-synced?)))))))))
|
||||||
|
@ -323,22 +287,11 @@
|
||||||
[data {:keys [id name page]}]
|
[data {:keys [id name page]}]
|
||||||
(when (and id name page)
|
(when (and id name page)
|
||||||
(ex/raise :type :conflict
|
(ex/raise :type :conflict
|
||||||
:hint "name or page should be provided, never both"))
|
:hint "id+name or page should be provided, never both"))
|
||||||
(letfn [(conj-if-not-exists [pages id]
|
(let [page (if (and (string? name) (uuid? id))
|
||||||
(cond-> pages
|
(ctp/make-empty-page id name)
|
||||||
(not (d/seek #(= % id) pages))
|
page)]
|
||||||
(conj id)))]
|
(ctpl/add-page data page)))
|
||||||
(if (and (string? name) (uuid? id))
|
|
||||||
(let [page (assoc init/empty-page-data
|
|
||||||
:id id
|
|
||||||
:name name)]
|
|
||||||
(-> data
|
|
||||||
(update :pages conj-if-not-exists id)
|
|
||||||
(update :pages-index assoc id page)))
|
|
||||||
|
|
||||||
(-> data
|
|
||||||
(update :pages conj-if-not-exists (:id page))
|
|
||||||
(update :pages-index assoc (:id page) page)))))
|
|
||||||
|
|
||||||
(defmethod process-change :mod-page
|
(defmethod process-change :mod-page
|
||||||
[data {:keys [id name]}]
|
[data {:keys [id name]}]
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
[app.common.colors :as clr]
|
[app.common.colors :as clr]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
(def file-version 19)
|
(def file-version 20)
|
||||||
(def default-color clr/gray-20)
|
(def default-color clr/gray-20)
|
||||||
(def root uuid/zero)
|
(def root uuid/zero)
|
||||||
|
|
||||||
|
|
|
@ -1,186 +0,0 @@
|
||||||
;; 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) UXBOX Labs SL
|
|
||||||
|
|
||||||
(ns app.common.pages.init
|
|
||||||
(:require
|
|
||||||
[app.common.colors :as clr]
|
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.exceptions :as ex]
|
|
||||||
[app.common.geom.shapes :as gsh]
|
|
||||||
[app.common.pages.common :refer [file-version default-color]]
|
|
||||||
[app.common.uuid :as uuid]))
|
|
||||||
|
|
||||||
(def root uuid/zero)
|
|
||||||
|
|
||||||
(def empty-page-data
|
|
||||||
{:options {}
|
|
||||||
:name "Page-1"
|
|
||||||
:objects
|
|
||||||
{root
|
|
||||||
{:id root
|
|
||||||
:type :frame
|
|
||||||
:name "Root Frame"}}})
|
|
||||||
|
|
||||||
(def empty-file-data
|
|
||||||
{:version file-version
|
|
||||||
:pages []
|
|
||||||
:pages-index {}})
|
|
||||||
|
|
||||||
(def default-shape-attrs
|
|
||||||
{})
|
|
||||||
|
|
||||||
(def default-frame-attrs
|
|
||||||
{:frame-id uuid/zero
|
|
||||||
:fills [{:fill-color clr/white
|
|
||||||
:fill-opacity 1}]
|
|
||||||
:strokes []
|
|
||||||
:shapes []
|
|
||||||
:hide-fill-on-export false})
|
|
||||||
|
|
||||||
(def ^:private minimal-shapes
|
|
||||||
[{:type :rect
|
|
||||||
:name "Rect-1"
|
|
||||||
:fills [{:fill-color default-color
|
|
||||||
:fill-opacity 1}]
|
|
||||||
:strokes []
|
|
||||||
:rx 0
|
|
||||||
:ry 0}
|
|
||||||
|
|
||||||
{:type :image
|
|
||||||
:rx 0
|
|
||||||
:ry 0
|
|
||||||
:fills []
|
|
||||||
:strokes []}
|
|
||||||
|
|
||||||
{:type :circle
|
|
||||||
:name "Circle-1"
|
|
||||||
:fills [{:fill-color default-color
|
|
||||||
:fill-opacity 1}]
|
|
||||||
:strokes []}
|
|
||||||
|
|
||||||
{:type :path
|
|
||||||
:name "Path-1"
|
|
||||||
:fills []
|
|
||||||
:strokes [{:stroke-style :solid
|
|
||||||
:stroke-alignment :center
|
|
||||||
:stroke-width 2
|
|
||||||
:stroke-color clr/black
|
|
||||||
:stroke-opacity 1}]}
|
|
||||||
|
|
||||||
{:type :frame
|
|
||||||
:name "Board-1"
|
|
||||||
:fills [{:fill-color clr/white
|
|
||||||
:fill-opacity 1}]
|
|
||||||
:strokes []
|
|
||||||
:stroke-style :none
|
|
||||||
:stroke-alignment :center
|
|
||||||
:stroke-width 0
|
|
||||||
:stroke-color clr/black
|
|
||||||
:stroke-opacity 0
|
|
||||||
:rx 0
|
|
||||||
:ry 0}
|
|
||||||
|
|
||||||
{:type :text
|
|
||||||
:name "Text-1"
|
|
||||||
:content nil}
|
|
||||||
|
|
||||||
{:type :svg-raw}])
|
|
||||||
|
|
||||||
(def empty-selrect
|
|
||||||
{:x 0 :y 0
|
|
||||||
:x1 0 :y1 0
|
|
||||||
:x2 0.01 :y2 0.01
|
|
||||||
:width 0.01 :height 0.01})
|
|
||||||
|
|
||||||
(defn make-minimal-shape
|
|
||||||
[type]
|
|
||||||
(let [type (cond (= type :curve) :path
|
|
||||||
:else type)
|
|
||||||
shape (d/seek #(= type (:type %)) minimal-shapes)]
|
|
||||||
(when-not shape
|
|
||||||
(ex/raise :type :assertion
|
|
||||||
:code :shape-type-not-implemented
|
|
||||||
:context {:type type}))
|
|
||||||
|
|
||||||
(cond-> shape
|
|
||||||
:always
|
|
||||||
(assoc :id (uuid/next))
|
|
||||||
|
|
||||||
(not= :path (:type shape))
|
|
||||||
(assoc :x 0
|
|
||||||
:y 0
|
|
||||||
:width 0.01
|
|
||||||
:height 0.01
|
|
||||||
:selrect {:x 0
|
|
||||||
:y 0
|
|
||||||
:x1 0
|
|
||||||
:y1 0
|
|
||||||
:x2 0.01
|
|
||||||
:y2 0.01
|
|
||||||
:width 0.01
|
|
||||||
:height 0.01}))))
|
|
||||||
|
|
||||||
(defn make-minimal-group
|
|
||||||
[frame-id selection-rect group-name]
|
|
||||||
{:id (uuid/next)
|
|
||||||
:type :group
|
|
||||||
:name group-name
|
|
||||||
:shapes []
|
|
||||||
:frame-id frame-id
|
|
||||||
:x (:x selection-rect)
|
|
||||||
:y (:y selection-rect)
|
|
||||||
:width (:width selection-rect)
|
|
||||||
:height (:height selection-rect)})
|
|
||||||
|
|
||||||
(defn make-file-data
|
|
||||||
([file-id]
|
|
||||||
(make-file-data file-id (uuid/next)))
|
|
||||||
|
|
||||||
([file-id page-id]
|
|
||||||
(let [pd (assoc empty-page-data
|
|
||||||
:id page-id
|
|
||||||
:name "Page-1")]
|
|
||||||
(-> empty-file-data
|
|
||||||
(assoc :id file-id)
|
|
||||||
(update :pages conj page-id)
|
|
||||||
(update :pages-index assoc page-id pd)))))
|
|
||||||
|
|
||||||
(defn setup-rect-selrect
|
|
||||||
"Initializes the selrect and points for a shape"
|
|
||||||
[shape]
|
|
||||||
(let [selrect (gsh/rect->selrect shape)
|
|
||||||
points (gsh/rect->points shape)]
|
|
||||||
(-> shape
|
|
||||||
(assoc :selrect selrect
|
|
||||||
:points points))))
|
|
||||||
|
|
||||||
(defn- setup-rect
|
|
||||||
"A specialized function for setup rect-like shapes."
|
|
||||||
[shape {:keys [x y width height]}]
|
|
||||||
(-> shape
|
|
||||||
(assoc :x x :y y :width width :height height)
|
|
||||||
(setup-rect-selrect)))
|
|
||||||
|
|
||||||
(defn- setup-image
|
|
||||||
[{:keys [metadata] :as shape} props]
|
|
||||||
(-> (setup-rect shape props)
|
|
||||||
(assoc
|
|
||||||
:proportion (/ (:width metadata)
|
|
||||||
(:height metadata))
|
|
||||||
:proportion-lock true)))
|
|
||||||
|
|
||||||
(defn setup-shape
|
|
||||||
"A function that initializes the first coordinates for
|
|
||||||
the shape. Used mainly for draw operations."
|
|
||||||
([props]
|
|
||||||
(setup-shape {:type :rect} props))
|
|
||||||
|
|
||||||
([shape props]
|
|
||||||
(case (:type shape)
|
|
||||||
:image (setup-image shape props)
|
|
||||||
(setup-rect shape props))))
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.geom.shapes.path :as gsp]
|
[app.common.geom.shapes.path :as gsp]
|
||||||
[app.common.geom.shapes.text :as gsht]
|
[app.common.geom.shapes.text :as gsht]
|
||||||
|
@ -15,6 +16,10 @@
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.container :as ctc]
|
||||||
|
[app.common.types.page :as ctp]
|
||||||
|
[app.common.types.pages-list :as ctpl]
|
||||||
|
[app.common.types.shape-tree :as ctst]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
@ -432,5 +437,72 @@
|
||||||
(update :pages-index d/update-vals update-container)
|
(update :pages-index d/update-vals update-container)
|
||||||
(update :components d/update-vals update-container))))
|
(update :components d/update-vals update-container))))
|
||||||
|
|
||||||
|
(defmethod migrate 20
|
||||||
|
[data]
|
||||||
|
(let [page-id (uuid/next)
|
||||||
|
|
||||||
|
components (->> (:components data)
|
||||||
|
vals
|
||||||
|
(sort-by :name))
|
||||||
|
|
||||||
|
add-library-page
|
||||||
|
(fn [data]
|
||||||
|
(let [page (ctp/make-empty-page page-id "Library page")]
|
||||||
|
(-> data
|
||||||
|
(ctpl/add-page page))))
|
||||||
|
|
||||||
|
add-main-instance
|
||||||
|
(fn [data component position]
|
||||||
|
(let [page (ctpl/get-page data page-id)
|
||||||
|
|
||||||
|
[new-shape new-shapes]
|
||||||
|
(ctc/instantiate-component page
|
||||||
|
component
|
||||||
|
(:id data)
|
||||||
|
position)
|
||||||
|
|
||||||
|
add-shape
|
||||||
|
(fn [data shape]
|
||||||
|
(update-in data [:pages-index page-id]
|
||||||
|
#(ctst/add-shape (:id shape)
|
||||||
|
shape
|
||||||
|
%
|
||||||
|
(:frame-id shape)
|
||||||
|
(:parent-id shape)
|
||||||
|
nil ; <- As shapes are ordered, we can safely add each
|
||||||
|
true))) ; one at the end of the parent's children list.
|
||||||
|
|
||||||
|
update-component
|
||||||
|
(fn [component]
|
||||||
|
(assoc component
|
||||||
|
:main-instance-id (:id new-shape)
|
||||||
|
:main-instance-page page-id))]
|
||||||
|
|
||||||
|
(as-> data $
|
||||||
|
(reduce add-shape $ new-shapes)
|
||||||
|
(update-in $ [:components (:id component)] update-component))))
|
||||||
|
|
||||||
|
add-instance-grid
|
||||||
|
(fn [data components]
|
||||||
|
(let [position-seq (ctst/generate-shape-grid
|
||||||
|
(map cph/get-component-root components)
|
||||||
|
50)]
|
||||||
|
(loop [data data
|
||||||
|
components-seq (seq components)
|
||||||
|
position-seq position-seq]
|
||||||
|
(let [component (first components-seq)
|
||||||
|
position (first position-seq)]
|
||||||
|
(if (nil? component)
|
||||||
|
data
|
||||||
|
(recur (add-main-instance data component position)
|
||||||
|
(rest components-seq)
|
||||||
|
(rest position-seq)))))))]
|
||||||
|
|
||||||
|
(if (empty? components)
|
||||||
|
data
|
||||||
|
(-> data
|
||||||
|
(add-library-page)
|
||||||
|
(add-instance-grid components)))))
|
||||||
|
|
||||||
;; TODO: pending to do a migration for delete already not used fill
|
;; TODO: pending to do a migration for delete already not used fill
|
||||||
;; and stroke props. This should be done for >1.14.x version.
|
;; and stroke props. This should be done for >1.14.x version.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
;; --- Grid options
|
;; --- Grid options
|
||||||
|
@ -95,6 +96,23 @@
|
||||||
(s/def ::page
|
(s/def ::page
|
||||||
(s/keys :req-un [::id ::name ::objects ::options]))
|
(s/keys :req-un [::id ::name ::objects ::options]))
|
||||||
|
|
||||||
|
;; --- Initialization
|
||||||
|
|
||||||
|
(def root uuid/zero)
|
||||||
|
|
||||||
|
(def empty-page-data
|
||||||
|
{:options {}
|
||||||
|
:objects {root
|
||||||
|
{:id root
|
||||||
|
:type :frame
|
||||||
|
:name "Root Frame"}}})
|
||||||
|
|
||||||
|
(defn make-empty-page
|
||||||
|
[id name]
|
||||||
|
(assoc empty-page-data
|
||||||
|
:id id
|
||||||
|
:name name))
|
||||||
|
|
||||||
;; --- Helpers for flow
|
;; --- Helpers for flow
|
||||||
|
|
||||||
(defn rename-flow
|
(defn rename-flow
|
||||||
|
|
|
@ -272,10 +272,13 @@
|
||||||
original ones.
|
original ones.
|
||||||
|
|
||||||
Returns the cloned object, the list of all new objects (including
|
Returns the cloned object, the list of all new objects (including
|
||||||
the cloned one), and possibly a list of original objects modified."
|
the cloned one), and possibly a list of original objects modified.
|
||||||
|
|
||||||
|
The list of objects are returned in tree traversal order, respecting
|
||||||
|
the order of the children of each parent."
|
||||||
|
|
||||||
([object parent-id objects update-new-object]
|
([object parent-id objects update-new-object]
|
||||||
(clone-object object parent-id objects update-new-object identity))
|
(clone-object object parent-id objects update-new-object (fn [object _] object)))
|
||||||
|
|
||||||
([object parent-id objects update-new-object update-original-object]
|
([object parent-id objects update-new-object update-original-object]
|
||||||
(let [new-id (uuid/next)]
|
(let [new-id (uuid/next)]
|
||||||
|
@ -316,3 +319,27 @@
|
||||||
(into new-children new-child-objects)
|
(into new-children new-child-objects)
|
||||||
(into updated-children updated-child-objects))))))))
|
(into updated-children updated-child-objects))))))))
|
||||||
|
|
||||||
|
(defn generate-shape-grid
|
||||||
|
"Generate a sequence of positions that lays out the list of
|
||||||
|
shapes in a grid of equal-sized rows and columns."
|
||||||
|
[shapes gap]
|
||||||
|
(let [shapes-bounds (map gsh/bounding-box shapes)
|
||||||
|
|
||||||
|
grid-size (mth/ceil (mth/sqrt (count shapes)))
|
||||||
|
row-size (+ (apply max (map :height shapes-bounds))
|
||||||
|
gap)
|
||||||
|
column-size (+ (apply max (map :width shapes-bounds))
|
||||||
|
gap)
|
||||||
|
|
||||||
|
next-pos (fn [position]
|
||||||
|
(let [counter (inc (:counter (meta position)))
|
||||||
|
row (quot counter grid-size)
|
||||||
|
column (mod counter grid-size)
|
||||||
|
new-pos (gpt/point (* column column-size)
|
||||||
|
(* row row-size))]
|
||||||
|
(with-meta new-pos
|
||||||
|
{:counter counter})))]
|
||||||
|
(iterate next-pos
|
||||||
|
(with-meta (gpt/point 0 0)
|
||||||
|
{:counter 0}))))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue