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 (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.bool :as gsb]
[app.common.geom.shapes.common :as gco] [app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.intersect :as gin] [app.common.geom.shapes.intersect :as gin]
[app.common.geom.shapes.path :as gsp] [app.common.geom.shapes.path :as gsp]
@ -164,3 +165,6 @@
(d/export gin/has-point?) (d/export gin/has-point?)
(d/export gin/has-point-rect?) (d/export gin/has-point-rect?)
(d/export gin/rect-contains-shape?) (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.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.rect :as gpr] [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) (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 (defn line-values
[[from-p to-p] t] [[from-p to-p] t]
(let [move-v (-> (gpt/to-vec from-p to-p) (let [move-v (-> (gpt/to-vec from-p to-p)
@ -670,3 +702,57 @@
(map second) (map second)
(reduce +) (reduce +)
(not= 0)))) (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)
(d/export helpers/get-children-objects) (d/export helpers/get-children-objects)
(d/export helpers/get-object-with-children) (d/export helpers/get-object-with-children)
(d/export helpers/select-children)
(d/export helpers/is-shape-grouped) (d/export helpers/is-shape-grouped)
(d/export helpers/get-parent) (d/export helpers/get-parent)
(d/export helpers/get-parents) (d/export helpers/get-parents)

View file

@ -9,6 +9,7 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.geom.shapes.bool :as gshb]
[app.common.pages.common :refer [component-sync-attrs]] [app.common.pages.common :refer [component-sync-attrs]]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.pages.init :as init] [app.common.pages.init :as init]
@ -156,7 +157,7 @@
(sequence (comp (sequence (comp
(mapcat #(cons % (cph/get-parents % objects))) (mapcat #(cons % (cph/get-parents % objects)))
(map #(get objects %)) (map #(get objects %))
(filter #(= (:type %) :group)) (filter #(contains? #{:group :bool} (:type %)))
(map :id) (map :id)
(distinct)) (distinct))
shapes))) shapes)))
@ -177,6 +178,9 @@
(empty? children) (empty? children)
group group
(= :bool (:type group))
(gshb/update-bool-selrect group children objects)
(:masked-group? group) (:masked-group? group)
(set-mask-selrect group children) (set-mask-selrect group children)

View file

@ -138,6 +138,10 @@
[id objects] [id objects]
(mapv #(get objects %) (cons id (get-children 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 (defn is-shape-grouped
"Checks if a shape is inside a group" "Checks if a shape is inside a group"
[shape-id objects] [shape-id objects]

View file

@ -4,20 +4,19 @@
;; ;;
;; Copyright (c) UXBOX Labs SL ;; Copyright (c) UXBOX Labs SL
(ns app.util.path.bool (ns app.common.path.bool
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.path :as gsp] [app.common.geom.shapes.path :as gsp]
[app.util.path.commands :as upc] [app.common.path.commands :as upc]
[app.util.path.geom :as upg] [app.common.path.subpaths :as ups]))
[app.util.path.subpaths :as ups]))
(defn- split-command (defn- split-command
[cmd values] [cmd values]
(case (:command cmd) (case (:command cmd)
:line-to (upg/split-line-to-ranges (:prev cmd) cmd values) :line-to (gsp/split-line-to-ranges (:prev cmd) cmd values)
:curve-to (upg/split-curve-to-ranges (:prev cmd) cmd values) :curve-to (gsp/split-curve-to-ranges (:prev cmd) cmd values)
[cmd])) [cmd]))
(defn split [seg-1 seg-2] (defn split [seg-1 seg-2]
@ -198,7 +197,7 @@
(gsp/command->point current) (gsp/command->point current)
(conj result (dissoc current :prev))))))) (conj result (dissoc current :prev)))))))
(defn content-bool (defn content-bool-pair
[bool-type content-a content-b] [bool-type content-a content-b]
(let [content-a (add-previous content-a) (let [content-a (add-previous content-a)
@ -218,3 +217,11 @@
(->> (fix-move-to bool-content) (->> (fix-move-to bool-content)
(ups/close-subpaths)))) (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 ;; Copyright (c) UXBOX Labs SL
(ns app.util.path.commands (ns app.common.path.commands
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt])) [app.common.geom.point :as gpt]))

View file

@ -4,22 +4,46 @@
;; ;;
;; Copyright (c) UXBOX Labs SL ;; Copyright (c) UXBOX Labs SL
(ns app.util.path.shapes-to-path (ns app.common.path.shapes-to-path
(: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.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes.common :as gsc]
[app.common.geom.shapes.path :as gsp] [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 ^:const bezier-circle-c 0.551915024494)
(def dissoc-attrs [:x :y :width :height
:rx :ry :r1 :r2 :r3 :r4 (def ^:const dissoc-attrs
:medata]) [:x :y :width :height
(def allowed-transform-types #{:rect :rx :ry :r1 :r2 :r3 :r4
:circle :metadata :shapes])
:image})
(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 (defn make-corner-arc
"Creates a curvle corner for border radius" "Creates a curvle corner for border radius"
@ -86,8 +110,9 @@
(defn rect->path (defn rect->path
"Creates a bezier curve that approximates a rounded corner rectangle" "Creates a bezier curve that approximates a rounded corner rectangle"
[x y width height r1 r2 r3 r4] [x y width height r1 r2 r3 r4 rx]
(let [p1 (gpt/point x (+ y r1)) (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) p2 (gpt/point (+ x r1) y)
p3 (gpt/point (+ width x (- r2)) y) p3 (gpt/point (+ width x (- r2)) y)
@ -113,34 +138,51 @@
(conj (make-corner-arc p7 p8 :bottom-left r4))) (conj (make-corner-arc p7 p8 :bottom-left r4)))
(conj (pc/make-line-to p1))))) (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 (defn convert-to-path
"Transforms the given shape to a 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) (contains? allowed-transform-types type)
(let [r1 (or r1 rx 0) (let [new-content
r2 (or r2 rx 0)
r3 (or r3 rx 0)
r4 (or r4 rx 0)
new-content
(case type (case type
:circle :circle (circle->path x y width height)
(circle->path x y width height) #_:else (rect->path x y width height r1 r2 r3 r4 rx))
(rect->path x y width height r1 r2 r3 r4))
;; Apply the transforms that had the shape ;; Apply the transforms that had the shape
transform (:transform shape) transform (:transform shape)
new-content (cond-> new-content new-content (cond-> new-content
(some? transform) (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 (-> shape
(d/without-keys dissoc-attrs)
(assoc :type :path) (assoc :type :path)
(assoc :content new-content) (assoc :content new-content)
(cond-> (= :image type) (-> (assoc :fill-image metadata) (cond-> (= :image type)
(dissoc :metadata))))) (assoc :fill-image metadata))
(d/without-keys dissoc-attrs)))
:else
;; Do nothing if the shape is not of a correct type ;; Do nothing if the shape is not of a correct type
shape)) shape))

View file

@ -4,11 +4,11 @@
;; ;;
;; Copyright (c) UXBOX Labs SL ;; Copyright (c) UXBOX Labs SL
(ns app.util.path.subpaths (ns app.common.path.subpaths
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.util.path.commands :as upc])) [app.common.path.commands :as upc]))
(defn pt= (defn pt=
"Check if two points are close" "Check if two points are close"

View file

@ -10,6 +10,7 @@
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.pages.changes-builder :as cb] [app.common.pages.changes-builder :as cb]
[app.common.path.shapes-to-path :as stp]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
@ -18,24 +19,6 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[potok.core :as ptk])) [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 (defn selected-shapes
[state] [state]
(let [objects (wsh/lookup-page-objects state)] (let [objects (wsh/lookup-page-objects state)]
@ -47,10 +30,10 @@
(sort-by ::index)))) (sort-by ::index))))
(defn create-bool-data (defn create-bool-data
[type name shapes] [type name shapes objects]
(let [head (first shapes) (let [shapes (mapv #(stp/convert-to-path % objects) shapes)
head-data (select-keys head style-properties) head (first shapes)
selrect (gsh/selection-rect shapes)] head-data (select-keys head stp/style-properties)]
(-> {:id (uuid/next) (-> {:id (uuid/next)
:type :bool :type :bool
:bool-type type :bool-type type
@ -60,7 +43,7 @@
::index (::index head) ::index (::index head)
:shapes []} :shapes []}
(merge head-data) (merge head-data)
(gsh/setup selrect)))) (gsh/update-bool-selrect shapes objects))))
(defn create-bool (defn create-bool
[bool-type] [bool-type]
@ -69,14 +52,14 @@
(watch [it state _] (watch [it state _]
(let [page-id (:current-page-id 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")) base-name (-> bool-type d/name str/capital (str "-1"))
name (-> (dwc/retrieve-used-names objects) name (-> (dwc/retrieve-used-names objects)
(dwc/generate-unique-name base-name)) (dwc/generate-unique-name base-name))
shapes (selected-shapes state)] shapes (selected-shapes state)]
(when-not (empty? shapes) (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) shape-id (:id boolean-data)
changes (-> (cb/empty-changes it page-id) changes (-> (cb/empty-changes it page-id)
(cb/add-obj boolean-data) (cb/add-obj boolean-data)

View file

@ -7,7 +7,10 @@
(ns app.main.data.workspace.path.drawing (ns app.main.data.workspace.path.drawing
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.path :as upg]
[app.common.pages :as cp] [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.common.spec :as us]
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
@ -21,9 +24,6 @@
[app.main.data.workspace.path.undo :as undo] [app.main.data.workspace.path.undo :as undo]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms] [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] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))

View file

@ -8,6 +8,10 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt] [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.changes :as dch]
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
[app.main.data.workspace.path.changes :as changes] [app.main.data.workspace.path.changes :as changes]
@ -19,10 +23,6 @@
[app.main.data.workspace.path.undo :as undo] [app.main.data.workspace.path.undo :as undo]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms] [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] [app.util.path.tools :as upt]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))

View file

@ -10,10 +10,10 @@
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.math :as mth] [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.data.workspace.path.common :as common]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.path.commands :as upc]
[app.util.path.subpaths :as ups]
[potok.core :as ptk])) [potok.core :as ptk]))
(defn end-path-event? [event] (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 (ns app.main.data.workspace.path.state
(:require (:require
[app.common.data :as d] [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 (defn get-path-id
"Retrieves the currently editing path id" "Retrieves the currently editing path id"
@ -31,7 +31,8 @@
[state & ks] [state & ks]
(let [path-loc (get-path-location state) (let [path-loc (get-path-location state)
shape (-> (get-in state path-loc) 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) (if (empty? ks)
shape shape

View file

@ -7,12 +7,12 @@
(ns app.main.data.workspace.path.streams (ns app.main.data.workspace.path.streams
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.path :as upg]
[app.common.math :as mth] [app.common.math :as mth]
[app.main.data.workspace.path.state :as state] [app.main.data.workspace.path.state :as state]
[app.main.snap :as snap] [app.main.snap :as snap]
[app.main.store :as st] [app.main.store :as st]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.path.geom :as upg]
[beicon.core :as rx] [beicon.core :as rx]
[okulary.core :as l] [okulary.core :as l]
[potok.core :as ptk])) [potok.core :as ptk]))

View file

@ -6,13 +6,13 @@
(ns app.main.data.workspace.path.tools (ns app.main.data.workspace.path.tools
(:require (: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.changes :as dch]
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
[app.main.data.workspace.path.changes :as changes] [app.main.data.workspace.path.changes :as changes]
[app.main.data.workspace.path.state :as st] [app.main.data.workspace.path.state :as st]
[app.main.data.workspace.state-helpers :as wsh] [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] [app.util.path.tools :as upt]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))

View file

@ -243,16 +243,34 @@
([ids {:keys [with-modifiers?] ([ids {:keys [with-modifiers?]
:or { with-modifiers? false }}] :or { with-modifiers? false }}]
(l/derived (fn [state] (let [selector
(let [objects (wsh/lookup-page-objects state) (fn [state]
modifiers (:workspace-modifiers state) (let [objects (wsh/lookup-page-objects state)
objects (cond-> objects modifiers (:workspace-modifiers state)
with-modifiers? objects (cond-> objects
(gsh/merge-modifiers modifiers)) with-modifiers?
xform (comp (map #(get objects %)) (gsh/merge-modifiers modifiers))
(remove nil?))] xform (comp (map #(get objects %))
(into [] xform ids))) (remove nil?))]
st/state =))) (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 (def selected-data
(l/derived #(let [selected (wsh/lookup-selected %) (l/derived #(let [selected (wsh/lookup-selected %)

View file

@ -6,11 +6,10 @@
(ns app.main.ui.shapes.bool (ns app.main.ui.shapes.bool
(:require (: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.main.ui.hooks :refer [use-equal-memo]]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.path.bool :as pb]
[app.util.path.shapes-to-path :as stp]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(defn bool-shape (defn bool-shape
@ -20,32 +19,25 @@
[props] [props]
(let [frame (obj/get props "frame") (let [frame (obj/get props "frame")
shape (obj/get props "shape") shape (obj/get props "shape")
childs (obj/get props "childs")] childs (obj/get props "childs")
(when (> (count childs) 1) childs (use-equal-memo childs)
(let [shape-1 (stp/convert-to-path (nth childs 0))
shape-2 (stp/convert-to-path (nth childs 1))
content-1 (use-equal-memo (-> shape-1 gsh/transform-shape :content)) bool-content
content-2 (use-equal-memo (-> shape-2 gsh/transform-shape :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 [:& shape-wrapper {:shape (-> shape
(mf/use-memo (assoc :type :path)
(mf/deps content-1 content-2) (assoc :content bool-content))
#(pb/content-bool (:bool-type shape) content-1 content-2))] :frame frame}])))
[:*
[:& 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"}}])]])))))

View file

@ -10,6 +10,7 @@
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl] [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.shortcuts :as sc]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -147,6 +148,7 @@
do-boolean-difference (st/emitf (dw/create-bool :difference)) do-boolean-difference (st/emitf (dw/create-bool :difference))
do-boolean-intersection (st/emitf (dw/create-bool :intersection)) do-boolean-intersection (st/emitf (dw/create-bool :intersection))
do-boolean-exclude (st/emitf (dw/create-bool :exclude)) 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") [:& menu-entry {:title (tr "workspace.shape.menu.copy")
@ -214,6 +216,9 @@
:shortcut (sc/get-tooltip :start-editing) :shortcut (sc/get-tooltip :start-editing)
:on-click do-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.path")}
[:& menu-entry {:title (tr "workspace.shape.menu.union") [:& menu-entry {:title (tr "workspace.shape.menu.union")
:shortcut (sc/get-tooltip :boolean-union) :shortcut (sc/get-tooltip :boolean-union)
@ -230,8 +235,7 @@
[:& menu-separator] [:& menu-separator]
;; TODO ;; TODO
[:& menu-entry {:title "Flatten"}] [:& menu-entry {:title "Flatten"}]]
[:& menu-entry {:title "Transform to path"}]]
(if (:hidden shape) (if (:hidden shape)
[:& menu-entry {:title (tr "workspace.shape.menu.show") [:& menu-entry {:title (tr "workspace.shape.menu.show")

View file

@ -33,17 +33,11 @@
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
frame (unchecked-get props "frame") frame (unchecked-get props "frame")
childs-ref (mf/use-memo (mf/deps shape) #(refs/objects-by-id (:shapes shape) {:with-modifiers? true})) childs-ref (mf/use-memo
{:keys [selected modifiers]} (mf/deref refs/local-displacement) (mf/deps (:id shape))
#(refs/select-children (:id shape)))
add-modifiers childs (mf/deref childs-ref)]
(fn [{:keys [id] :as shape}]
(cond-> shape
(contains? selected id)
(update :modifiers merge modifiers)))
childs (->> (mf/deref childs-ref)
(mapv add-modifiers))]
[:> shape-container {:shape shape} [:> shape-container {:shape shape}
[:& shape-component [:& shape-component

View file

@ -6,11 +6,11 @@
(ns app.main.ui.workspace.shapes.path (ns app.main.ui.workspace.shapes.path
(:require (:require
[app.common.path.commands :as upc]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.ui.shapes.path :as path] [app.main.ui.shapes.path :as path]
[app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.path.common :as pc] [app.main.ui.workspace.shapes.path.common :as pc]
[app.util.path.commands :as upc]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(mf/defc path-wrapper (mf/defc path-wrapper

View file

@ -8,7 +8,9 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt] [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.data.workspace.path :as drp]
[app.main.snap :as snap] [app.main.snap :as snap]
[app.main.store :as st] [app.main.store :as st]
@ -18,10 +20,7 @@
[app.main.ui.workspace.shapes.path.common :as pc] [app.main.ui.workspace.shapes.path.common :as pc]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.path.commands :as upc]
[app.util.path.format :as upf] [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]] [clojure.set :refer [map-invert]]
[goog.events :as events] [goog.events :as events]
[rumext.alpha :as mf]) [rumext.alpha :as mf])
@ -217,16 +216,16 @@
shape (cond-> shape shape (cond-> shape
(not= :path (:type shape)) (not= :path (:type shape))
ups/convert-to-path (ups/convert-to-path {})
:always :always
hooks/use-equal-memo) hooks/use-equal-memo)
base-content (:content shape) 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 (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)) point->base (->> (map hash-map content-points base-points) (reduce merge))
base->point (map-invert point->base) base->point (map-invert point->base)
@ -269,7 +268,7 @@
ms/mouse-position ms/mouse-position
(mf/deps shape zoom) (mf/deps shape zoom)
(fn [position] (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))))) (reset! hover-point (when (< (gpt/distance position point) (/ 10 zoom)) point)))))
[:g.path-editor {:ref editor-ref} [:g.path-editor {:ref editor-ref}

View file

@ -6,8 +6,8 @@
(ns app.util.path.format (ns app.util.path.format
(:require (:require
[app.util.path.commands :as upc] [app.common.path.commands :as upc]
[app.util.path.subpaths :refer [pt=]] [app.common.path.subpaths :refer [pt=]]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(defn command->param-list [command] (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 (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt] [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.arc-to-curve :refer [a2c]]
[app.util.path.commands :as upc]
[app.util.path.geom :as upg]
[app.util.svg :as usvg] [app.util.svg :as usvg]
[cuerdas.core :as str])) [cuerdas.core :as str]))

View file

@ -8,9 +8,9 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.path :as upg]
[app.common.math :as mth] [app.common.math :as mth]
[app.util.path.commands :as upc] [app.common.path.commands :as upc]
[app.util.path.geom :as upg]
[clojure.set :as set])) [clojure.set :as set]))
(defn remove-line-curves (defn remove-line-curves