mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 09:08:31 -05:00
✨ Updates selrects, groups to path
This commit is contained in:
parent
1db2895606
commit
6fd35ae5d9
28 changed files with 327 additions and 239 deletions
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.bool :as gsb]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.geom.shapes.intersect :as gin]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
|
@ -164,3 +165,6 @@
|
|||
(d/export gin/has-point?)
|
||||
(d/export gin/has-point-rect?)
|
||||
(d/export gin/rect-contains-shape?)
|
||||
|
||||
;; Bool
|
||||
(d/export gsb/update-bool-selrect)
|
||||
|
|
25
common/src/app/common/geom/shapes/bool.cljc
Normal file
25
common/src/app/common/geom/shapes/bool.cljc
Normal file
|
@ -0,0 +1,25 @@
|
|||
;; 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.geom.shapes.bool
|
||||
(:require
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
[app.common.path.bool :as pb]
|
||||
[app.common.path.shapes-to-path :as stp]))
|
||||
|
||||
(defn update-bool-selrect
|
||||
[shape children objects]
|
||||
|
||||
(let [selrect (->> children
|
||||
(map #(stp/convert-to-path % objects))
|
||||
(mapv :content)
|
||||
(pb/content-bool (:bool-type shape))
|
||||
(gsp/content->selrect))
|
||||
points (gpr/rect->points selrect)]
|
||||
(-> shape
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points))))
|
|
@ -10,10 +10,42 @@
|
|||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
[app.common.math :as mth]))
|
||||
[app.common.math :as mth]
|
||||
[app.common.path.commands :as upc]))
|
||||
|
||||
(def ^:const curve-curve-precision 0.1)
|
||||
|
||||
(defn calculate-opposite-handler
|
||||
"Given a point and its handler, gives the symetric handler"
|
||||
[point handler]
|
||||
(let [handler-vector (gpt/to-vec point handler)]
|
||||
(gpt/add point (gpt/negate handler-vector))))
|
||||
|
||||
(defn opposite-handler
|
||||
"Calculates the coordinates of the opposite handler"
|
||||
[point handler]
|
||||
(let [phv (gpt/to-vec point handler)]
|
||||
(gpt/add point (gpt/negate phv))))
|
||||
|
||||
(defn opposite-handler-keep-distance
|
||||
"Calculates the coordinates of the opposite handler but keeping the old distance"
|
||||
[point handler old-opposite]
|
||||
(let [old-distance (gpt/distance point old-opposite)
|
||||
phv (gpt/to-vec point handler)
|
||||
phv2 (gpt/multiply
|
||||
(gpt/unit (gpt/negate phv))
|
||||
(gpt/point old-distance))]
|
||||
(gpt/add point phv2)))
|
||||
|
||||
(defn content->points
|
||||
"Returns the points in the given content"
|
||||
[content]
|
||||
(->> content
|
||||
(map #(when (-> % :params :x)
|
||||
(gpt/point (-> % :params :x) (-> % :params :y))))
|
||||
(remove nil?)
|
||||
(into [])))
|
||||
|
||||
(defn line-values
|
||||
[[from-p to-p] t]
|
||||
(let [move-v (-> (gpt/to-vec from-p to-p)
|
||||
|
@ -670,3 +702,57 @@
|
|||
(map second)
|
||||
(reduce +)
|
||||
(not= 0))))
|
||||
|
||||
(defn split-line-to
|
||||
"Given a point and a line-to command will create a two new line-to commands
|
||||
that will split the original line into two given a value between 0-1"
|
||||
[from-p cmd t-val]
|
||||
(let [to-p (upc/command->point cmd)
|
||||
sp (gpt/lerp from-p to-p t-val)]
|
||||
[(upc/make-line-to sp) cmd]))
|
||||
|
||||
(defn split-curve-to
|
||||
"Given the point and a curve-to command will split the curve into two new
|
||||
curve-to commands given a value between 0-1"
|
||||
[from-p cmd t-val]
|
||||
(let [params (:params cmd)
|
||||
end (gpt/point (:x params) (:y params))
|
||||
h1 (gpt/point (:c1x params) (:c1y params))
|
||||
h2 (gpt/point (:c2x params) (:c2y params))
|
||||
[[_ to1 h11 h21]
|
||||
[_ to2 h12 h22]] (curve-split from-p end h1 h2 t-val)]
|
||||
[(upc/make-curve-to to1 h11 h21)
|
||||
(upc/make-curve-to to2 h12 h22)]))
|
||||
|
||||
(defn split-line-to-ranges
|
||||
"Splits a line into several lines given the points in `values`
|
||||
for example (split-line-to-ranges p c [0 0.25 0.5 0.75 1] will split
|
||||
the line into 4 lines"
|
||||
[from-p cmd values]
|
||||
(let [to-p (upc/command->point cmd)]
|
||||
(->> (conj values 1)
|
||||
(mapv (fn [val]
|
||||
(-> (gpt/lerp from-p to-p val)
|
||||
#_(gpt/round 2)
|
||||
(upc/make-line-to)))))))
|
||||
|
||||
(defn split-curve-to-ranges
|
||||
"Splits a curve into several curves given the points in `values`
|
||||
for example (split-curve-to-ranges p c [0 0.25 0.5 0.75 1] will split
|
||||
the curve into 4 curves that draw the same curve"
|
||||
[from-p cmd values]
|
||||
(if (empty? values)
|
||||
[cmd]
|
||||
(let [to-p (upc/command->point cmd)
|
||||
params (:params cmd)
|
||||
h1 (gpt/point (:c1x params) (:c1y params))
|
||||
h2 (gpt/point (:c2x params) (:c2y params))
|
||||
|
||||
values-set (->> (conj values 1) (into (sorted-set)))]
|
||||
(->> (d/with-prev values-set)
|
||||
(mapv
|
||||
(fn [[t1 t0]]
|
||||
(let [t0 (if (nil? t0) 0 t0)
|
||||
[_ to-p h1' h2'] (subcurve-range from-p to-p h1 h2 t0 t1)]
|
||||
(upc/make-curve-to (-> to-p #_(gpt/round 2)) h1' h2'))))))))
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
(d/export helpers/get-children)
|
||||
(d/export helpers/get-children-objects)
|
||||
(d/export helpers/get-object-with-children)
|
||||
(d/export helpers/select-children)
|
||||
(d/export helpers/is-shape-grouped)
|
||||
(d/export helpers/get-parent)
|
||||
(d/export helpers/get-parents)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.bool :as gshb]
|
||||
[app.common.pages.common :refer [component-sync-attrs]]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.pages.init :as init]
|
||||
|
@ -156,7 +157,7 @@
|
|||
(sequence (comp
|
||||
(mapcat #(cons % (cph/get-parents % objects)))
|
||||
(map #(get objects %))
|
||||
(filter #(= (:type %) :group))
|
||||
(filter #(contains? #{:group :bool} (:type %)))
|
||||
(map :id)
|
||||
(distinct))
|
||||
shapes)))
|
||||
|
@ -177,6 +178,9 @@
|
|||
(empty? children)
|
||||
group
|
||||
|
||||
(= :bool (:type group))
|
||||
(gshb/update-bool-selrect group children objects)
|
||||
|
||||
(:masked-group? group)
|
||||
(set-mask-selrect group children)
|
||||
|
||||
|
|
|
@ -138,6 +138,10 @@
|
|||
[id objects]
|
||||
(mapv #(get objects %) (cons id (get-children id objects))))
|
||||
|
||||
(defn select-children [id objects]
|
||||
(->> (get-children id objects)
|
||||
(select-keys objects)))
|
||||
|
||||
(defn is-shape-grouped
|
||||
"Checks if a shape is inside a group"
|
||||
[shape-id objects]
|
||||
|
|
|
@ -4,20 +4,19 @@
|
|||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.util.path.bool
|
||||
(ns app.common.path.bool
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.geom :as upg]
|
||||
[app.util.path.subpaths :as ups]))
|
||||
[app.common.path.commands :as upc]
|
||||
[app.common.path.subpaths :as ups]))
|
||||
|
||||
(defn- split-command
|
||||
[cmd values]
|
||||
(case (:command cmd)
|
||||
:line-to (upg/split-line-to-ranges (:prev cmd) cmd values)
|
||||
:curve-to (upg/split-curve-to-ranges (:prev cmd) cmd values)
|
||||
:line-to (gsp/split-line-to-ranges (:prev cmd) cmd values)
|
||||
:curve-to (gsp/split-curve-to-ranges (:prev cmd) cmd values)
|
||||
[cmd]))
|
||||
|
||||
(defn split [seg-1 seg-2]
|
||||
|
@ -198,7 +197,7 @@
|
|||
(gsp/command->point current)
|
||||
(conj result (dissoc current :prev)))))))
|
||||
|
||||
(defn content-bool
|
||||
(defn content-bool-pair
|
||||
[bool-type content-a content-b]
|
||||
|
||||
(let [content-a (add-previous content-a)
|
||||
|
@ -218,3 +217,11 @@
|
|||
|
||||
(->> (fix-move-to bool-content)
|
||||
(ups/close-subpaths))))
|
||||
|
||||
(defn content-bool
|
||||
[bool-type contents]
|
||||
;; We apply the boolean operation in to each pair and the result to the next
|
||||
;; element
|
||||
(->> contents
|
||||
(reduce (partial content-bool-pair bool-type))
|
||||
(into [])))
|
|
@ -4,7 +4,7 @@
|
|||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.util.path.commands
|
||||
(ns app.common.path.commands
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]))
|
|
@ -4,22 +4,46 @@
|
|||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.util.path.shapes-to-path
|
||||
(ns app.common.path.shapes-to-path
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.common :as gsc]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.util.path.commands :as pc]))
|
||||
[app.common.path.commands :as pc]))
|
||||
|
||||
(def bezier-circle-c 0.551915024494)
|
||||
(def dissoc-attrs [:x :y :width :height
|
||||
:rx :ry :r1 :r2 :r3 :r4
|
||||
:medata])
|
||||
(def allowed-transform-types #{:rect
|
||||
:circle
|
||||
:image})
|
||||
(def ^:const bezier-circle-c 0.551915024494)
|
||||
|
||||
(def ^:const dissoc-attrs
|
||||
[:x :y :width :height
|
||||
:rx :ry :r1 :r2 :r3 :r4
|
||||
:metadata :shapes])
|
||||
|
||||
(def ^:const allowed-transform-types
|
||||
#{:rect
|
||||
:circle
|
||||
:image
|
||||
:group})
|
||||
|
||||
(def ^:const style-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
|
||||
:shadow
|
||||
:blur])
|
||||
|
||||
(defn make-corner-arc
|
||||
"Creates a curvle corner for border radius"
|
||||
|
@ -86,8 +110,9 @@
|
|||
|
||||
(defn rect->path
|
||||
"Creates a bezier curve that approximates a rounded corner rectangle"
|
||||
[x y width height r1 r2 r3 r4]
|
||||
(let [p1 (gpt/point x (+ y r1))
|
||||
[x y width height r1 r2 r3 r4 rx]
|
||||
(let [[r1 r2 r3 r4] (->> [r1 r2 r3 r4] (mapv #(or % rx 0)))
|
||||
p1 (gpt/point x (+ y r1))
|
||||
p2 (gpt/point (+ x r1) y)
|
||||
|
||||
p3 (gpt/point (+ width x (- r2)) y)
|
||||
|
@ -113,34 +138,51 @@
|
|||
(conj (make-corner-arc p7 p8 :bottom-left r4)))
|
||||
(conj (pc/make-line-to p1)))))
|
||||
|
||||
(declare convert-to-path)
|
||||
|
||||
(defn group-to-path
|
||||
[group objects]
|
||||
|
||||
(let [xform (comp (map #(get objects %))
|
||||
(map #(-> (convert-to-path % objects))))
|
||||
|
||||
child-as-paths (into [] xform (:shapes group))
|
||||
head (first child-as-paths)
|
||||
head-data (select-keys head style-properties)
|
||||
content (into [] (mapcat :content) child-as-paths)]
|
||||
|
||||
(-> group
|
||||
(assoc :type :path)
|
||||
(assoc :content content)
|
||||
(merge head-data)
|
||||
(d/without-keys dissoc-attrs))))
|
||||
|
||||
(defn convert-to-path
|
||||
"Transforms the given shape to a path"
|
||||
[{:keys [type x y width height r1 r2 r3 r4 rx metadata] :as shape}]
|
||||
[{:keys [type x y width height r1 r2 r3 r4 rx metadata] :as shape} objects]
|
||||
(assert (map? objects))
|
||||
(cond
|
||||
(= (:type shape) :group)
|
||||
(group-to-path shape objects)
|
||||
|
||||
(if (contains? allowed-transform-types type)
|
||||
(let [r1 (or r1 rx 0)
|
||||
r2 (or r2 rx 0)
|
||||
r3 (or r3 rx 0)
|
||||
r4 (or r4 rx 0)
|
||||
|
||||
new-content
|
||||
(contains? allowed-transform-types type)
|
||||
(let [new-content
|
||||
(case type
|
||||
:circle
|
||||
(circle->path x y width height)
|
||||
(rect->path x y width height r1 r2 r3 r4))
|
||||
:circle (circle->path x y width height)
|
||||
#_:else (rect->path x y width height r1 r2 r3 r4 rx))
|
||||
|
||||
;; Apply the transforms that had the shape
|
||||
transform (:transform shape)
|
||||
new-content (cond-> new-content
|
||||
(some? transform)
|
||||
(gsp/transform-content (gmt/transform-in (gsh/center-shape shape) transform)))]
|
||||
(gsp/transform-content (gmt/transform-in (gsc/center-shape shape) transform)))]
|
||||
|
||||
(-> shape
|
||||
(d/without-keys dissoc-attrs)
|
||||
(assoc :type :path)
|
||||
(assoc :content new-content)
|
||||
(cond-> (= :image type) (-> (assoc :fill-image metadata)
|
||||
(dissoc :metadata)))))
|
||||
(cond-> (= :image type)
|
||||
(assoc :fill-image metadata))
|
||||
(d/without-keys dissoc-attrs)))
|
||||
:else
|
||||
;; Do nothing if the shape is not of a correct type
|
||||
shape))
|
||||
|
|
@ -4,11 +4,11 @@
|
|||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.util.path.subpaths
|
||||
(ns app.common.path.subpaths
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.util.path.commands :as upc]))
|
||||
[app.common.path.commands :as upc]))
|
||||
|
||||
(defn pt=
|
||||
"Check if two points are close"
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.changes-builder :as cb]
|
||||
[app.common.path.shapes-to-path :as stp]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
|
@ -18,24 +19,6 @@
|
|||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(def ^:const style-properties
|
||||
[:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-gradient
|
||||
:fill-color-ref-file
|
||||
:fill-color-ref-id
|
||||
:stroke-color
|
||||
:stroke-color-ref-file
|
||||
:stroke-color-ref-id
|
||||
:stroke-opacity
|
||||
:stroke-style
|
||||
:stroke-width
|
||||
:stroke-alignment
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
:shadow
|
||||
:blur])
|
||||
|
||||
(defn selected-shapes
|
||||
[state]
|
||||
(let [objects (wsh/lookup-page-objects state)]
|
||||
|
@ -47,10 +30,10 @@
|
|||
(sort-by ::index))))
|
||||
|
||||
(defn create-bool-data
|
||||
[type name shapes]
|
||||
(let [head (first shapes)
|
||||
head-data (select-keys head style-properties)
|
||||
selrect (gsh/selection-rect shapes)]
|
||||
[type name shapes objects]
|
||||
(let [shapes (mapv #(stp/convert-to-path % objects) shapes)
|
||||
head (first shapes)
|
||||
head-data (select-keys head stp/style-properties)]
|
||||
(-> {:id (uuid/next)
|
||||
:type :bool
|
||||
:bool-type type
|
||||
|
@ -60,7 +43,7 @@
|
|||
::index (::index head)
|
||||
:shapes []}
|
||||
(merge head-data)
|
||||
(gsh/setup selrect))))
|
||||
(gsh/update-bool-selrect shapes objects))))
|
||||
|
||||
(defn create-bool
|
||||
[bool-type]
|
||||
|
@ -69,14 +52,14 @@
|
|||
|
||||
(watch [it state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
objects (wsh/lookup-page-objects state)
|
||||
base-name (-> bool-type d/name str/capital (str "-1"))
|
||||
name (-> (dwc/retrieve-used-names objects)
|
||||
(dwc/generate-unique-name base-name))
|
||||
shapes (selected-shapes state)]
|
||||
|
||||
(when-not (empty? shapes)
|
||||
(let [boolean-data (create-bool-data bool-type name shapes)
|
||||
(let [boolean-data (create-bool-data bool-type name shapes objects)
|
||||
shape-id (:id boolean-data)
|
||||
changes (-> (cb/empty-changes it page-id)
|
||||
(cb/add-obj boolean-data)
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
(ns app.main.data.workspace.path.drawing
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.path.commands :as upc]
|
||||
[app.common.path.shapes-to-path :as upsp]
|
||||
[app.common.spec :as us]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
|
@ -21,9 +24,6 @@
|
|||
[app.main.data.workspace.path.undo :as undo]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.geom :as upg]
|
||||
[app.util.path.shapes-to-path :as upsp]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.path.commands :as upc]
|
||||
[app.common.path.shapes-to-path :as upsp]
|
||||
[app.common.path.subpaths :as ups]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.path.changes :as changes]
|
||||
|
@ -19,10 +23,6 @@
|
|||
[app.main.data.workspace.path.undo :as undo]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.geom :as upg]
|
||||
[app.util.path.shapes-to-path :as upsp]
|
||||
[app.util.path.subpaths :as ups]
|
||||
[app.util.path.tools :as upt]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.path.commands :as upc]
|
||||
[app.common.path.subpaths :as ups]
|
||||
[app.main.data.workspace.path.common :as common]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.subpaths :as ups]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(defn end-path-event? [event]
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
;; 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.main.data.workspace.path.shapes-to-path
|
||||
(:require
|
||||
[app.common.path.shapes-to-path :as upsp]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(defn convert-selected-to-path []
|
||||
(ptk/reify ::convert-selected-to-path
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (wsh/lookup-selected state)]
|
||||
(rx/of (dch/update-shapes selected #(upsp/convert-to-path % objects)))))))
|
|
@ -7,7 +7,7 @@
|
|||
(ns app.main.data.workspace.path.state
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.util.path.shapes-to-path :as upsp]))
|
||||
[app.common.path.shapes-to-path :as upsp]))
|
||||
|
||||
(defn get-path-id
|
||||
"Retrieves the currently editing path id"
|
||||
|
@ -31,7 +31,8 @@
|
|||
[state & ks]
|
||||
(let [path-loc (get-path-location state)
|
||||
shape (-> (get-in state path-loc)
|
||||
(upsp/convert-to-path))]
|
||||
;; Empty map because we know the current shape will not have children
|
||||
(upsp/convert-to-path {}))]
|
||||
|
||||
(if (empty? ks)
|
||||
shape
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
(ns app.main.data.workspace.path.streams
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.path.state :as state]
|
||||
[app.main.snap :as snap]
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.path.geom :as upg]
|
||||
[beicon.core :as rx]
|
||||
[okulary.core :as l]
|
||||
[potok.core :as ptk]))
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
(ns app.main.data.workspace.path.tools
|
||||
(:require
|
||||
[app.common.path.shapes-to-path :as upsp]
|
||||
[app.common.path.subpaths :as ups]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.path.changes :as changes]
|
||||
[app.main.data.workspace.path.state :as st]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.util.path.shapes-to-path :as upsp]
|
||||
[app.util.path.subpaths :as ups]
|
||||
[app.util.path.tools :as upt]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
|
|
@ -243,16 +243,34 @@
|
|||
|
||||
([ids {:keys [with-modifiers?]
|
||||
:or { with-modifiers? false }}]
|
||||
(l/derived (fn [state]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
modifiers (:workspace-modifiers state)
|
||||
objects (cond-> objects
|
||||
with-modifiers?
|
||||
(gsh/merge-modifiers modifiers))
|
||||
xform (comp (map #(get objects %))
|
||||
(remove nil?))]
|
||||
(into [] xform ids)))
|
||||
st/state =)))
|
||||
(let [selector
|
||||
(fn [state]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
modifiers (:workspace-modifiers state)
|
||||
objects (cond-> objects
|
||||
with-modifiers?
|
||||
(gsh/merge-modifiers modifiers))
|
||||
xform (comp (map #(get objects %))
|
||||
(remove nil?))]
|
||||
(into [] xform ids)))]
|
||||
(l/derived selector st/state =))))
|
||||
|
||||
(defn select-children [id]
|
||||
(let [selector
|
||||
(fn [state]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
children (cp/select-children id objects)
|
||||
modifiers (-> (:workspace-modifiers state))
|
||||
|
||||
{selected :selected disp-modifiers :modifiers}
|
||||
(-> (:workspace-local state)
|
||||
(select-keys [:modifiers :selected]))
|
||||
|
||||
modifiers (merge modifiers
|
||||
(into #{} (map #(vector % disp-modifiers)) selected))]
|
||||
|
||||
(gsh/merge-modifiers children modifiers)))]
|
||||
(l/derived selector st/state =)))
|
||||
|
||||
(def selected-data
|
||||
(l/derived #(let [selected (wsh/lookup-selected %)
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
|
||||
(ns app.main.ui.shapes.bool
|
||||
(:require
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.path.bool :as pb]
|
||||
[app.common.path.shapes-to-path :as stp]
|
||||
[app.main.ui.hooks :refer [use-equal-memo]]
|
||||
[app.util.object :as obj]
|
||||
[app.util.path.bool :as pb]
|
||||
[app.util.path.shapes-to-path :as stp]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn bool-shape
|
||||
|
@ -20,32 +19,25 @@
|
|||
[props]
|
||||
(let [frame (obj/get props "frame")
|
||||
shape (obj/get props "shape")
|
||||
childs (obj/get props "childs")]
|
||||
childs (obj/get props "childs")
|
||||
|
||||
(when (> (count childs) 1)
|
||||
(let [shape-1 (stp/convert-to-path (nth childs 0))
|
||||
shape-2 (stp/convert-to-path (nth childs 1))
|
||||
childs (use-equal-memo childs)
|
||||
|
||||
content-1 (use-equal-memo (-> shape-1 gsh/transform-shape :content))
|
||||
content-2 (use-equal-memo (-> shape-2 gsh/transform-shape :content))
|
||||
bool-content
|
||||
(mf/use-memo
|
||||
(mf/deps childs)
|
||||
(fn []
|
||||
(->> shape
|
||||
:shapes
|
||||
(map #(get childs %))
|
||||
(map #(stp/convert-to-path % childs))
|
||||
(mapv :content)
|
||||
(pb/content-bool (:bool-type shape)))))]
|
||||
|
||||
content
|
||||
(mf/use-memo
|
||||
(mf/deps content-1 content-2)
|
||||
#(pb/content-bool (:bool-type shape) content-1 content-2))]
|
||||
|
||||
[:*
|
||||
[:& shape-wrapper {:shape (-> shape
|
||||
(assoc :type :path)
|
||||
(assoc :content content))
|
||||
:frame frame}]
|
||||
|
||||
#_[:g
|
||||
(for [point (app.util.path.geom/content->points content)]
|
||||
[:circle {:cx (:x point)
|
||||
:cy (:y point)
|
||||
:r 1
|
||||
:style {:fill "blue"}}])]])))))
|
||||
[:& shape-wrapper {:shape (-> shape
|
||||
(assoc :type :path)
|
||||
(assoc :content bool-content))
|
||||
:frame frame}])))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.path.shapes-to-path :as dwpe]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -147,6 +148,7 @@
|
|||
do-boolean-difference (st/emitf (dw/create-bool :difference))
|
||||
do-boolean-intersection (st/emitf (dw/create-bool :intersection))
|
||||
do-boolean-exclude (st/emitf (dw/create-bool :exclude))
|
||||
do-transform-to-path (st/emitf (dwpe/convert-selected-to-path))
|
||||
]
|
||||
[:*
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.copy")
|
||||
|
@ -214,6 +216,9 @@
|
|||
:shortcut (sc/get-tooltip :start-editing)
|
||||
:on-click do-start-editing}])
|
||||
|
||||
[:& menu-entry {:title "Transform to path"
|
||||
:on-click do-transform-to-path}]
|
||||
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.path")}
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.union")
|
||||
:shortcut (sc/get-tooltip :boolean-union)
|
||||
|
@ -230,8 +235,7 @@
|
|||
|
||||
[:& menu-separator]
|
||||
;; TODO
|
||||
[:& menu-entry {:title "Flatten"}]
|
||||
[:& menu-entry {:title "Transform to path"}]]
|
||||
[:& menu-entry {:title "Flatten"}]]
|
||||
|
||||
(if (:hidden shape)
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.show")
|
||||
|
|
|
@ -33,17 +33,11 @@
|
|||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
|
||||
childs-ref (mf/use-memo (mf/deps shape) #(refs/objects-by-id (:shapes shape) {:with-modifiers? true}))
|
||||
{:keys [selected modifiers]} (mf/deref refs/local-displacement)
|
||||
childs-ref (mf/use-memo
|
||||
(mf/deps (:id shape))
|
||||
#(refs/select-children (:id shape)))
|
||||
|
||||
add-modifiers
|
||||
(fn [{:keys [id] :as shape}]
|
||||
(cond-> shape
|
||||
(contains? selected id)
|
||||
(update :modifiers merge modifiers)))
|
||||
|
||||
childs (->> (mf/deref childs-ref)
|
||||
(mapv add-modifiers))]
|
||||
childs (mf/deref childs-ref)]
|
||||
|
||||
[:> shape-container {:shape shape}
|
||||
[:& shape-component
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
(ns app.main.ui.workspace.shapes.path
|
||||
(:require
|
||||
[app.common.path.commands :as upc]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.shapes.path :as path]
|
||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||
[app.main.ui.workspace.shapes.path.common :as pc]
|
||||
[app.util.path.commands :as upc]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(mf/defc path-wrapper
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as gshp]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.path.commands :as upc]
|
||||
[app.common.path.shapes-to-path :as ups]
|
||||
[app.main.data.workspace.path :as drp]
|
||||
[app.main.snap :as snap]
|
||||
[app.main.store :as st]
|
||||
|
@ -18,10 +20,7 @@
|
|||
[app.main.ui.workspace.shapes.path.common :as pc]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.format :as upf]
|
||||
[app.util.path.geom :as upg]
|
||||
[app.util.path.shapes-to-path :as ups]
|
||||
[clojure.set :refer [map-invert]]
|
||||
[goog.events :as events]
|
||||
[rumext.alpha :as mf])
|
||||
|
@ -217,16 +216,16 @@
|
|||
|
||||
shape (cond-> shape
|
||||
(not= :path (:type shape))
|
||||
ups/convert-to-path
|
||||
(ups/convert-to-path {})
|
||||
|
||||
:always
|
||||
hooks/use-equal-memo)
|
||||
|
||||
base-content (:content shape)
|
||||
base-points (mf/use-memo (mf/deps base-content) #(->> base-content upg/content->points))
|
||||
base-points (mf/use-memo (mf/deps base-content) #(->> base-content gsp/content->points))
|
||||
|
||||
content (upc/apply-content-modifiers base-content content-modifiers)
|
||||
content-points (mf/use-memo (mf/deps content) #(->> content upg/content->points))
|
||||
content-points (mf/use-memo (mf/deps content) #(->> content gsp/content->points))
|
||||
|
||||
point->base (->> (map hash-map content-points base-points) (reduce merge))
|
||||
base->point (map-invert point->base)
|
||||
|
@ -269,7 +268,7 @@
|
|||
ms/mouse-position
|
||||
(mf/deps shape zoom)
|
||||
(fn [position]
|
||||
(when-let [point (gshp/path-closest-point shape position)]
|
||||
(when-let [point (gsp/path-closest-point shape position)]
|
||||
(reset! hover-point (when (< (gpt/distance position point) (/ 10 zoom)) point)))))
|
||||
|
||||
[:g.path-editor {:ref editor-ref}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
(ns app.util.path.format
|
||||
(:require
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.subpaths :refer [pt=]]
|
||||
[app.common.path.commands :as upc]
|
||||
[app.common.path.subpaths :refer [pt=]]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn command->param-list [command]
|
||||
|
|
|
@ -1,97 +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.util.path.geom
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as gshp]
|
||||
[app.util.path.commands :as upc]))
|
||||
|
||||
(defn calculate-opposite-handler
|
||||
"Given a point and its handler, gives the symetric handler"
|
||||
[point handler]
|
||||
(let [handler-vector (gpt/to-vec point handler)]
|
||||
(gpt/add point (gpt/negate handler-vector))))
|
||||
|
||||
(defn split-line-to
|
||||
"Given a point and a line-to command will create a two new line-to commands
|
||||
that will split the original line into two given a value between 0-1"
|
||||
[from-p cmd t-val]
|
||||
(let [to-p (upc/command->point cmd)
|
||||
sp (gpt/lerp from-p to-p t-val)]
|
||||
[(upc/make-line-to sp) cmd]))
|
||||
|
||||
(defn split-curve-to
|
||||
"Given the point and a curve-to command will split the curve into two new
|
||||
curve-to commands given a value between 0-1"
|
||||
[from-p cmd t-val]
|
||||
(let [params (:params cmd)
|
||||
end (gpt/point (:x params) (:y params))
|
||||
h1 (gpt/point (:c1x params) (:c1y params))
|
||||
h2 (gpt/point (:c2x params) (:c2y params))
|
||||
[[_ to1 h11 h21]
|
||||
[_ to2 h12 h22]] (gshp/curve-split from-p end h1 h2 t-val)]
|
||||
[(upc/make-curve-to to1 h11 h21)
|
||||
(upc/make-curve-to to2 h12 h22)]))
|
||||
|
||||
(defn split-line-to-ranges
|
||||
"Splits a line into several lines given the points in `values`
|
||||
for example (split-line-to-ranges p c [0 0.25 0.5 0.75 1] will split
|
||||
the line into 4 lines"
|
||||
[from-p cmd values]
|
||||
(let [to-p (upc/command->point cmd)]
|
||||
(->> (conj values 1)
|
||||
(mapv (fn [val]
|
||||
(-> (gpt/lerp from-p to-p val)
|
||||
#_(gpt/round 2)
|
||||
(upc/make-line-to)))))))
|
||||
|
||||
(defn split-curve-to-ranges
|
||||
"Splits a curve into several curves given the points in `values`
|
||||
for example (split-curve-to-ranges p c [0 0.25 0.5 0.75 1] will split
|
||||
the curve into 4 curves that draw the same curve"
|
||||
[from-p cmd values]
|
||||
(if (empty? values)
|
||||
[cmd]
|
||||
(let [to-p (upc/command->point cmd)
|
||||
params (:params cmd)
|
||||
h1 (gpt/point (:c1x params) (:c1y params))
|
||||
h2 (gpt/point (:c2x params) (:c2y params))
|
||||
|
||||
values-set (->> (conj values 1) (into (sorted-set)))]
|
||||
(->> (d/with-prev values-set)
|
||||
(mapv
|
||||
(fn [[t1 t0]]
|
||||
(let [t0 (if (nil? t0) 0 t0)
|
||||
[_ to-p h1' h2'] (gshp/subcurve-range from-p to-p h1 h2 t0 t1)]
|
||||
(upc/make-curve-to (-> to-p #_(gpt/round 2)) h1' h2'))))))))
|
||||
|
||||
(defn opposite-handler
|
||||
"Calculates the coordinates of the opposite handler"
|
||||
[point handler]
|
||||
(let [phv (gpt/to-vec point handler)]
|
||||
(gpt/add point (gpt/negate phv))))
|
||||
|
||||
(defn opposite-handler-keep-distance
|
||||
"Calculates the coordinates of the opposite handler but keeping the old distance"
|
||||
[point handler old-opposite]
|
||||
(let [old-distance (gpt/distance point old-opposite)
|
||||
phv (gpt/to-vec point handler)
|
||||
phv2 (gpt/multiply
|
||||
(gpt/unit (gpt/negate phv))
|
||||
(gpt/point old-distance))]
|
||||
(gpt/add point phv2)))
|
||||
|
||||
(defn content->points
|
||||
"Returns the points in the given content"
|
||||
[content]
|
||||
(->> content
|
||||
(map #(when (-> % :params :x)
|
||||
(gpt/point (-> % :params :x) (-> % :params :y))))
|
||||
(remove nil?)
|
||||
(into [])))
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.path.commands :as upc]
|
||||
[app.util.path.arc-to-curve :refer [a2c]]
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.geom :as upg]
|
||||
[app.util.svg :as usvg]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as upg]
|
||||
[app.common.math :as mth]
|
||||
[app.util.path.commands :as upc]
|
||||
[app.util.path.geom :as upg]
|
||||
[app.common.path.commands :as upc]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(defn remove-line-curves
|
||||
|
|
Loading…
Add table
Reference in a new issue