🐛 Fixes problems with masks rotation and some clipping problems

This commit is contained in:
alonso.torres 2020-12-08 11:35:14 +01:00 committed by Hirunatan
parent 34af5e4563
commit 3a8a212432
10 changed files with 95 additions and 104 deletions

View file

@ -1146,8 +1146,11 @@
ptk/WatchEvent
(watch [_ state stream]
(rx/of (dws/deselect-all)
(dws/select-shape (:id shape))))))
(let [selected (get-in state [:workspace-local :selected])]
(if (selected (:id shape))
(rx/empty)
(rx/of (dws/deselect-all)
(dws/select-shape (:id shape))))))))
(def hide-context-menu
(ptk/reify ::hide-context-menu
@ -1478,14 +1481,20 @@
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val nil}]}]
:val nil}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}]
uchanges [{:type :mod-obj
:page-id page-id
:id (:id group)
:operations [{:type :set
:attr :masked-group?
:val (:masked-group? group)}]}]]
:val (:masked-group? group)}]}
{:type :reg-objects
:page-id page-id
:shapes [(:id group)]}]]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set (:id group))))))))))

View file

@ -77,7 +77,10 @@
(fn []
(let [keys [(events/listen js/window EventType.POPSTATE on-pop-state)
(events/listen js/document EventType.KEYDOWN handle-keydown)
(events/listen js/document EventType.CLICK handle-click-outside)
;; Changing to js/document breaks the color picker
(events/listen (dom/get-root) EventType.CLICK handle-click-outside)
(events/listen js/document EventType.CONTEXTMENU handle-click-outside)]]
#(doseq [key keys]
(events/unlistenByKey key)))))

View file

@ -9,7 +9,6 @@
[rumext.alpha :as mf]
[app.common.uuid :as uuid]
[app.common.geom.shapes :as geom]
[app.main.ui.shapes.group :refer [mask-id-ctx]]
[app.util.object :as obj]))
; The SVG standard does not implement yet the 'stroke-alignment'
@ -25,16 +24,13 @@
elem-name (unchecked-get props "elem-name")
;; {:keys [x y width height]} (geom/shape->rect-shape shape)
{:keys [x y width height]} (:selrect shape)
mask-id (mf/use-ctx mask-id-ctx)
stroke-id (mf/use-var (uuid/next))
stroke-style (:stroke-style shape :none)
stroke-position (:stroke-alignment shape :center)]
(cond
;; Center alignment (or no stroke): the default in SVG
(or (= stroke-style :none) (= stroke-position :center))
[:> elem-name (cond-> (obj/merge! #js {} base-props)
(some? mask-id)
(obj/merge! #js {:mask mask-id}))]
[:> elem-name (obj/merge! #js {} base-props)]
;; Inner alignment: display the shape with double width stroke,
;; and clip the result with the original shape without stroke.
@ -54,15 +50,10 @@
shape-props (-> (obj/merge! #js {} base-props)
(obj/merge! #js {:strokeWidth (* stroke-width 2)
:clipPath (str "url('#" clip-id "')")}))]
(if (nil? mask-id)
[:*
[:> "clipPath" #js {:id clip-id}
[:> elem-name clip-props]]
[:> elem-name shape-props]]
[:g {:mask mask-id}
[:> "clipPath" #js {:id clip-id}
[:> elem-name clip-props]]
[:> elem-name shape-props]]))
[:*
[:> "clipPath" #js {:id clip-id}
[:> elem-name clip-props]]
[:> elem-name shape-props]])
;; Outer alingmnent: display the shape in two layers. One
;; without stroke (only fill), and another one only with stroke
@ -100,17 +91,10 @@
:fill "none"
:fillOpacity 0
:mask (str "url('#" stroke-mask-id "')")}))]
(if (nil? mask-id)
[:*
[:mask {:id mask-id}
[:> elem-name mask-props1]
[:> elem-name mask-props2]]
[:> elem-name shape-props1]
[:> elem-name shape-props2]]
[:g {:mask mask-id}
[:mask {:id stroke-mask-id}
[:> elem-name mask-props1]
[:> elem-name mask-props2]]
[:> elem-name shape-props1]
[:> elem-name shape-props2]])))))
[:*
[:mask {:id stroke-mask-id}
[:> elem-name mask-props1]
[:> elem-name mask-props2]]
[:> elem-name shape-props1]
[:> elem-name shape-props2]]))))

View file

@ -18,56 +18,54 @@
(mf/defc linear-gradient [{:keys [id gradient shape]}]
(let [{:keys [x y width height]} shape]
[:defs
[:linearGradient {:id id
:x1 (:start-x gradient)
:y1 (:start-y gradient)
:x2 (:end-x gradient)
:y2 (:end-y gradient)}
(for [{:keys [offset color opacity]} (:stops gradient)]
[:stop {:key (str id "-stop-" offset)
:offset (or offset 0)
:stop-color color
:stop-opacity opacity}])]]))
[:linearGradient {:id id
:x1 (:start-x gradient)
:y1 (:start-y gradient)
:x2 (:end-x gradient)
:y2 (:end-y gradient)}
(for [{:keys [offset color opacity]} (:stops gradient)]
[:stop {:key (str id "-stop-" offset)
:offset (or offset 0)
:stop-color color
:stop-opacity opacity}])]))
(mf/defc radial-gradient [{:keys [id gradient shape]}]
(let [{:keys [x y width height]} shape]
[:defs
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
(+ y (* height (:start-y gradient))))
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
(* height (:start-y gradient)))
(gpt/point (* width (:end-x gradient))
(* height (:end-y gradient))))
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
(+ y (* height (:start-y gradient))))
angle (gpt/angle gradient-vec
(gpt/point 1 0))
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
(* height (:start-y gradient)))
(gpt/point (* width (:end-x gradient))
(* height (:end-y gradient))))
shape-height-vec (gpt/point 0 (/ height 2))
angle (gpt/angle gradient-vec
(gpt/point 1 0))
scale-factor-y (/ (gpt/length gradient-vec) (/ height 2))
scale-factor-x (* scale-factor-y (:width gradient))
shape-height-vec (gpt/point 0 (/ height 2))
scale-vec (gpt/point (* scale-factor-y (/ height 2))
(* scale-factor-x (/ width 2)))
scale-factor-y (/ (gpt/length gradient-vec) (/ height 2))
scale-factor-x (* scale-factor-y (:width gradient))
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
tr-rotate (str/fmt "rotate(%s)" angle)
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)]
[:radialGradient {:id id
:cx 0
:cy 0
:r 1
:gradientUnits "userSpaceOnUse"
:gradientTransform transform}
(for [{:keys [offset color opacity]} (:stops gradient)]
[:stop {:key (str id "-stop-" offset)
:offset (or offset 0)
:stop-color color
:stop-opacity opacity}])])]))
scale-vec (gpt/point (* scale-factor-y (/ height 2))
(* scale-factor-x (/ width 2)))
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
tr-rotate (str/fmt "rotate(%s)" angle)
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)]
[:radialGradient {:id id
:cx 0
:cy 0
:r 1
:gradientUnits "userSpaceOnUse"
:gradientTransform transform}
(for [{:keys [offset color opacity]} (:stops gradient)]
[:stop {:key (str id "-stop-" offset)
:offset (or offset 0)
:stop-color color
:stop-opacity opacity}])])))
(mf/defc gradient
{::mf/wrap-props false}

View file

@ -14,17 +14,16 @@
[app.main.ui.shapes.attrs :as attrs]
[app.common.geom.shapes :as geom]))
(def mask-id-ctx (mf/create-context nil))
(defn group-shape
[shape-wrapper]
(mf/fnc group-shape
{::mf/wrap-props false}
[props]
(let [frame (unchecked-get props "frame")
shape (unchecked-get props "shape")
childs (unchecked-get props "childs")
expand-mask (unchecked-get props "expand-mask")
(let [frame (unchecked-get props "frame")
shape (unchecked-get props "shape")
childs (unchecked-get props "childs")
expand-mask (unchecked-get props "expand-mask")
pointer-events (unchecked-get props "pointer-events")
mask (if (and (:masked-group? shape) (not expand-mask))
(first childs)
nil)
@ -33,7 +32,9 @@
childs)
{:keys [id x y width height]} shape
transform (geom/transform-matrix shape)]
[:g
[:g.group {:pointer-events pointer-events
:mask (when (and mask (not expand-mask))
(str/fmt "url(#%s)" (:id mask)))}
(when mask
[:defs
[:mask {:id (:id mask)
@ -41,11 +42,10 @@
:height height}
[:& shape-wrapper {:frame frame
:shape mask}]]])
[:& (mf/provider mask-id-ctx) {:value (str/fmt "url(#%s)" (:id mask))}
(for [item childs]
[:& shape-wrapper {:frame frame
:shape item
:key (:id item)}])]
])))
(for [item childs]
[:& shape-wrapper {:frame frame
:shape item
:key (:id item)}])])))

View file

@ -13,7 +13,6 @@
[app.config :as cfg]
[app.common.geom.shapes :as geom]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.group :refer [mask-id-ctx]]
[app.util.object :as obj]
[app.main.ui.context :as muc]
[app.main.data.fetch :as df]
@ -27,7 +26,6 @@
{:keys [id x y width height rotation metadata]} shape
uri (cfg/resolve-media-path (:path metadata))
embed-resources? (mf/use-ctx muc/embed-ctx)
mask-id (mf/use-ctx mask-id-ctx)
data-uri (mf/use-state (when (not embed-resources?) uri))]
(mf/use-effect
@ -45,8 +43,7 @@
:transform transform
:width width
:height height
:preserveAspectRatio "none"
:mask mask-id}))]
:preserveAspectRatio "none"}))]
(if (nil? @data-uri)
[:> "rect" (obj/merge!
props

View file

@ -13,7 +13,6 @@
[rumext.alpha :as mf]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
[app.main.ui.shapes.group :refer [mask-id-ctx]]
[app.common.geom.shapes :as geom]
[app.util.object :as obj]
[app.util.geom.path :as ugp]))
@ -27,7 +26,6 @@
background? (unchecked-get props "background?")
;; {:keys [id x y width height]} (geom/shape->rect-shape shape)
{:keys [id x y width height]} (:selrect shape)
mask-id (mf/use-ctx mask-id-ctx)
transform (geom/transform-matrix shape)
pdata (ugp/content->path (:content shape))
props (-> (attrs/extract-style-attrs shape)
@ -35,7 +33,7 @@
#js {:transform transform
:d pdata}))]
(if background?
[:g {:mask mask-id}
[:g
[:path {:stroke "transparent"
:fill "transparent"
:stroke-width "20px"
@ -45,6 +43,5 @@
:elem-name "path"}]]
[:& shape-custom-stroke {:shape shape
:base-props props
:mask mask-id
:elem-name "path"}])))

View file

@ -12,7 +12,6 @@
[cuerdas.core :as str]
[rumext.alpha :as mf]
[app.main.ui.context :as muc]
[app.main.ui.shapes.group :refer [mask-id-ctx]]
[app.common.data :as d]
[app.common.geom.shapes :as geom]
[app.common.geom.matrix :as gmt]
@ -90,7 +89,6 @@
(let [shape (unchecked-get props "shape")
selected? (unchecked-get props "selected?")
grow-type (:grow-type shape)
mask-id (mf/use-ctx mask-id-ctx)
{:keys [id x y width height content]} shape]
[:foreignObject {:x x
:y y
@ -99,7 +97,6 @@
:transform (geom/transform-matrix shape)
:width (if (#{:auto-width} grow-type) 10000 width)
:height (if (#{:auto-height :auto-width} grow-type) 10000 height)
:mask mask-id
:ref ref
:pointer-events "none"}
[:& text-content {:shape shape

View file

@ -80,7 +80,8 @@
{:frame frame
:shape shape
:childs childs
:expand-mask is-mask-selected?}]
:expand-mask is-mask-selected?
:pointer-events (when (not is-child-selected?) "none")}]
(when-not is-child-selected?
[:rect.group-actions