♻️ Ensure a correct usage of concat/into operations.

This commit is contained in:
Andrey Antukh
2021-11-30 19:02:27 +01:00
parent 4ad34ab5c8
commit 8a2a1d6d70
43 changed files with 446 additions and 373 deletions

View File

@@ -6,7 +6,7 @@
(ns app.common.data
"Data manipulation and query helper functions."
(:refer-clojure :exclude [concat read-string hash-map merge name])
(:refer-clojure :exclude [read-string hash-map merge name])
#?(:cljs
(:require-macros [app.common.data]))
(:require
@@ -60,19 +60,37 @@
m)
(dissoc m k)))
(defn concat
[& colls]
(loop [result (transient (first colls))
colls (next colls)]
(defn- transient-concat
[c1 colls]
(loop [result (transient c1)
colls colls]
(if colls
(recur (reduce conj! result (first colls))
(next colls))
(persistent! result))))
(defn concat-set
([] #{})
([c1]
(if (set? c1) c1 (into #{} c1)))
([c1 & more]
(if (set? c1)
(transient-concat c1 more)
(transient-concat #{} (cons c1 more)))))
(defn concat-vec
([] [])
([c1]
(if (vector? c1) c1 (into [] c1)))
([c1 & more]
(if (vector? c1)
(transient-concat c1 more)
(transient-concat [] (cons c1 more)))))
(defn preconj
[coll elem]
(assert (vector? coll))
(concat [elem] coll))
(into [elem] coll))
(defn enumerate
([items] (enumerate items 0))
@@ -144,10 +162,15 @@
(reduce #(dissoc! %1 %2) (transient data) keys))))
(defn remove-at-index
"Takes a vector and returns a vector with an element in the
specified index removed."
[v index]
(vec (core/concat
(subvec v 0 index)
(subvec v (inc index)))))
;; The subvec function returns a SubVector type that is an vector
;; but does not have transient impl, because of this, we need to
;; pass an explicit vector as first argument.
(concat-vec []
(subvec v 0 index)
(subvec v (inc index))))
(defn zip [col1 col2]
(map vector col1 col2))
@@ -433,18 +456,18 @@
(str maybe-keyword)))))
(defn with-next
"Given a collectin will return a new collection where each element
is paried with the next item in the collection
(with-next (range 5)) => [[0 1] [1 2] [2 3] [3 4] [4 nil]"
"Given a collection will return a new collection where each element
is paired with the next item in the collection
(with-next (range 5)) => [[0 1] [1 2] [2 3] [3 4] [4 nil]]"
[coll]
(map vector
coll
(concat [] (rest coll) [nil])))
(concat (rest coll) [nil])))
(defn with-prev
"Given a collectin will return a new collection where each element
is paried with the previous item in the collection
(with-prev (range 5)) => [[0 nil] [1 0] [2 1] [3 2] [4 3]"
"Given a collection will return a new collection where each element
is paired with the previous item in the collection
(with-prev (range 5)) => [[0 nil] [1 0] [2 1] [3 2] [4 3]]"
[coll]
(map vector
coll
@@ -453,12 +476,12 @@
(defn with-prev-next
"Given a collection will return a new collection where every item is paired
with the previous and the next item of a collection
(with-prev-next (range 5)) => [[0 nil 1] [1 0 2] [2 1 3] [3 2 4] [4 3 nil]"
(with-prev-next (range 5)) => [[0 nil 1] [1 0 2] [2 1 3] [3 2 4] [4 3 nil]]"
[coll]
(map vector
coll
(concat [nil] coll)
(concat [] (rest coll) [nil])))
(concat (rest coll) [nil])))
(defn prefix-keyword
"Given a keyword and a prefix will return a new keyword with the prefix attached

View File

@@ -6,7 +6,6 @@
(ns app.common.geom.align
(:require
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[clojure.spec.alpha :as s]))
@@ -16,11 +15,15 @@
(declare calc-align-pos)
;; TODO: revisit on how to reuse code and dont have this function
;; duplicated because the implementation right now differs from the
;; original function.
;; Duplicated from pages/helpers to remove cyclic dependencies
(defn- get-children [id objects]
(let [shapes (vec (get-in objects [id :shapes]))]
(if shapes
(d/concat shapes (mapcat #(get-children % objects) shapes))
(into shapes (mapcat #(get-children % objects)) shapes)
[])))
(defn- recursive-move

View File

@@ -100,7 +100,7 @@
(defn curve-tangent
"Retrieve the tangent vector to the curve in the point `t`"
[[start end h1 h2] t]
(let [coords [[(:x start) (:x h1) (:x h2) (:x end)]
[(:y start) (:y h1) (:y h2) (:y end)]]
@@ -316,15 +316,13 @@
:line-to [prev-point (command->point command)]
;; We return the bezier extremities
:curve-to (d/concat
[prev-point
(command->point command)]
(let [curve [prev-point
(command->point command)
(command->point command :c1)
(command->point command :c2)]]
(->> (curve-extremities curve)
(mapv #(curve-values curve %)))))
:curve-to (into [prev-point (command->point command)]
(let [curve [prev-point
(command->point command)
(command->point command :c1)
(command->point command :c2)]]
(->> (curve-extremities curve)
(map #(curve-values curve %)))))
[])
selrect (gpr/points->selrect points)]
(-> selrect
@@ -342,20 +340,19 @@
(command->point command)]
;; We return the bezier extremities
:curve-to (d/concat
[(command->point prev)
(command->point command)]
(let [curve [(command->point prev)
(command->point command)
(command->point command :c1)
(command->point command :c2)]]
(->> (curve-extremities curve)
(mapv #(curve-values curve %)))))
:curve-to (into [(command->point prev)
(command->point command)]
(let [curve [(command->point prev)
(command->point command)
(command->point command :c1)
(command->point command :c2)]]
(->> (curve-extremities curve)
(map #(curve-values curve %)))))
[]))
extremities (mapcat calc-extremities
content
(d/concat [nil] content))
(concat [nil] content))
selrect (gpr/points->selrect extremities)]
@@ -410,14 +407,16 @@
(let [initial (first segments)
lines (rest segments)]
(d/concat [{:command :move-to
:params (select-keys initial [:x :y])}]
(->> lines
(mapv #(hash-map :command :line-to
:params (select-keys % [:x :y]))))
(d/concat-vec
[{:command :move-to
:params (select-keys initial [:x :y])}]
(when closed?
[{:command :close-path}])))))
(->> lines
(map #(hash-map :command :line-to
:params (select-keys % [:x :y]))))
(when closed?
[{:command :close-path}])))))
(defonce num-segments 10)
@@ -770,7 +769,7 @@
ts-3 (check-range c1-half c1-to c2-from c2-half)
ts-4 (check-range c1-half c1-to c2-half c2-to)]
(d/concat [] ts-1 ts-2 ts-3 ts-4)))))))
(d/concat-vec ts-1 ts-2 ts-3 ts-4)))))))
(remove-close-ts [{cp1 :p1 cp2 :p2}]
(fn [{:keys [p1 p2]}]

View File

@@ -214,7 +214,7 @@
not-mask-shapes (without-obj shapes mask-id)
new-index (if (nil? index) nil (max (dec index) 0))
new-shapes (insert-items other-ids new-index not-mask-shapes)]
(d/concat [mask-id] new-shapes))))
(into [mask-id] new-shapes))))
(add-to-parent [parent index shapes]
(let [parent (-> parent

View File

@@ -97,7 +97,7 @@
(let [old-obj (get objects id)
new-obj (update-fn old-obj)
attrs (or attrs (d/concat #{} (keys old-obj) (keys new-obj)))
attrs (or attrs (d/concat-set (keys old-obj) (keys new-obj)))
{rops :rops uops :uops}
(reduce #(generate-operation %1 %2 old-obj new-obj ignore-geometry?)

View File

@@ -103,7 +103,6 @@
"Retrieve all children ids recursively for a given object. The
children's order will be breadth first."
[id objects]
(loop [result (transient [])
pending (transient [])
next id]
@@ -214,10 +213,10 @@
[objects index ids]
(let [[before after] (split-at index objects)
p? (set ids)]
(d/concat []
(remove p? before)
ids
(remove p? after))))
(d/concat-vec []
(remove p? before)
ids
(remove p? after))))
(defn append-at-the-end
[prev-ids ids]
@@ -233,24 +232,25 @@
([objects {:keys [include-frames? include-frame-children?]
:or {include-frames? false
include-frame-children? true}}]
(let [lookup #(get objects %)
root (lookup uuid/zero)
(let [lookup #(get objects %)
root (lookup uuid/zero)
root-children (:shapes root)
lookup-shapes
(fn [result id]
(if (nil? id)
result
(let [obj (lookup id)
typ (:type obj)
(let [obj (lookup id)
typ (:type obj)
children (:shapes obj)]
(cond-> result
(or (not= :frame typ) include-frames?)
(d/concat [obj])
(conj obj)
(and (= :frame typ) include-frame-children?)
(d/concat (map lookup children))))))]
(into (map lookup) children)))))]
(reduce lookup-shapes [] root-children))))
@@ -299,15 +299,13 @@
(some? (:shapes object))
(assoc :shapes (mapv :id new-direct-children)))
new-object (update-new-object new-object object)
new-objects (d/concat [new-object] new-children)
updated-object (update-original-object object new-object)
new-object (update-new-object new-object object)
new-objects (into [new-object] new-children)
updated-object (update-original-object object new-object)
updated-objects (if (identical? object updated-object)
updated-children
(d/concat [updated-object] updated-children))]
(into [updated-object] updated-children))]
[new-object new-objects updated-objects])
@@ -320,9 +318,9 @@
(recur
(next child-ids)
(d/concat new-direct-children [new-child])
(d/concat new-children new-child-objects)
(d/concat updated-children updated-child-objects))))))))
(into new-direct-children [new-child])
(into new-children new-child-objects)
(into updated-children updated-child-objects))))))))
(defn indexed-shapes
"Retrieves a list with the indexes for each element in the layer tree.

View File

@@ -12,28 +12,25 @@
[clojure.set :as set]))
(defn calculate-frame-z-index [z-index frame-id objects]
(let [is-frame? (fn [id] (= :frame (get-in objects [id :type])))
(let [is-frame? (fn [id] (= :frame (get-in objects [id :type])))
frame-shapes (->> objects (vals) (filterv #(= (:frame-id %) frame-id)))
children (or (get-in objects [frame-id :shapes]) [])]
children (or (get-in objects [frame-id :shapes]) [])]
(if (empty? children)
z-index
(loop [current (peek children)
pending (pop children)
current-idx (count frame-shapes)
z-index z-index]
(let [children (get-in objects [current :shapes])
(let [children (get-in objects [current :shapes])
is-frame? (is-frame? current)
pending (if (not is-frame?)
(d/concat pending children)
pending)]
pending (if (not is-frame?)
(d/concat-vec pending children)
pending)]
(if (empty? pending)
(-> z-index
(assoc current current-idx))
(assoc z-index current current-idx)
(recur (peek pending)
(pop pending)
(dec current-idx)

View File

@@ -213,26 +213,25 @@
;; Pick all segments in content-a that are not inside content-b
;; Pick all segments in content-b that are not inside content-a
(let [content
(d/concat
[]
(concat
(->> content-a-split (filter #(not (contains-segment? % content-b))))
(->> content-b-split (filter #(not (contains-segment? % content-a)))))
;; Overlapping segments should be added when they are part of the border
border-content
(->> content-b-split
(filterv #(and (contains-segment? % content-a)
(overlap-segment? % content-a-split)
(not (inside-segment? % content)))))]
(filter #(and (contains-segment? % content-a)
(overlap-segment? % content-a-split)
(not (inside-segment? % content)))))]
(d/concat content border-content)))
;; Ensure that the output is always a vector
(d/concat-vec content border-content)))
(defn create-difference [content-a content-a-split content-b content-b-split]
;; Pick all segments in content-a that are not inside content-b
;; Pick all segments in content b that are inside content-a
;; removing overlapping
(d/concat
[]
(d/concat-vec
(->> content-a-split (filter #(not (contains-segment? % content-b))))
;; Reverse second content so we can have holes inside other shapes
@@ -243,15 +242,14 @@
(defn create-intersection [content-a content-a-split content-b content-b-split]
;; Pick all segments in content-a that are inside content-b
;; Pick all segments in content-b that are inside content-a
(d/concat
[]
(d/concat-vec
(->> content-a-split (filter #(contains-segment? % content-b)))
(->> content-b-split (filter #(contains-segment? % content-a)))))
(defn create-exclusion [content-a content-b]
;; Pick all segments
(d/concat [] content-a content-b))
(d/concat-vec content-a content-b))
(defn fix-move-to

View File

@@ -31,23 +31,22 @@
:blur])
(def style-properties
(d/concat
style-group-properties
[:fill-color
:fill-opacity
:fill-color-gradient
:fill-color-ref-file
:fill-color-ref-id
:fill-image
:stroke-color
:stroke-color-ref-file
:stroke-color-ref-id
:stroke-opacity
:stroke-style
:stroke-width
:stroke-alignment
:stroke-cap-start
:stroke-cap-end]))
(into style-group-properties
[:fill-color
:fill-opacity
:fill-color-gradient
:fill-color-ref-file
:fill-color-ref-id
:fill-image
:stroke-color
:stroke-color-ref-file
:stroke-color-ref-id
:stroke-opacity
:stroke-style
:stroke-width
:stroke-alignment
:stroke-cap-start
:stroke-cap-end]))
(defn make-corner-arc
"Creates a curvle corner for border radius"

View File

@@ -90,7 +90,7 @@
[subpath other]
(assert (pt= (:to subpath) (:from other)))
(-> subpath
(update :data d/concat (rest (:data other)))
(update :data d/concat-vec (rest (:data other)))
(assoc :to (:to other))))
(defn- merge-paths