0
Fork 0
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:
alonso.torres 2021-09-17 10:50:35 +02:00
parent 1db2895606
commit 6fd35ae5d9
28 changed files with 327 additions and 239 deletions

View file

@ -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)

View 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))))

View file

@ -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'))))))))

View file

@ -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)

View file

@ -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)

View file

@ -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]

View file

@ -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 [])))

View file

@ -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]))

View file

@ -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))

View file

@ -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"

View file

@ -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)

View file

@ -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]))

View file

@ -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]))

View file

@ -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]

View file

@ -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)))))))

View file

@ -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

View file

@ -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]))

View file

@ -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]))

View file

@ -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 %)

View file

@ -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}])))

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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}

View file

@ -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]

View file

@ -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 [])))

View file

@ -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]))

View file

@ -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