Merge pull request #1276 from penpot/bugfixes

Bugfixes
This commit is contained in:
Andrey Antukh 2021-10-13 13:40:07 +02:00 committed by GitHub
commit 925058467f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 217 additions and 151 deletions

View file

@ -3,10 +3,15 @@
## :rocket: Next ## :rocket: Next
### :boom: Breaking changes ### :boom: Breaking changes
- Some stroke-caps can change behaviour
- Text display bug fix could potentialy make some texts jump a line
### :sparkles: New features ### :sparkles: New features
- Add boolean shapes: intersections, unions, difference and exclusions. - Add boolean shapes: intersections, unions, difference and exclusions.
- Add advanced prototyping [#244](https://tree.taiga.io/project/penpot/us/244). - Add advanced prototyping [#244](https://tree.taiga.io/project/penpot/us/244).
- Change order of the teams menu so it's in the joined time order
### :bug: Bugs fixed ### :bug: Bugs fixed
@ -26,12 +31,15 @@
- Fix zoom context menu in viewer [Taiga #2041](https://tree.taiga.io/project/penpot/issue/2041) - Fix zoom context menu in viewer [Taiga #2041](https://tree.taiga.io/project/penpot/issue/2041)
- Fix stroke caps adjustments in relation with stroke size [Taiga #2123](https://tree.taiga.io/project/penpot/issue/2123) - Fix stroke caps adjustments in relation with stroke size [Taiga #2123](https://tree.taiga.io/project/penpot/issue/2123)
- Fix problem duplicating paths [Taiga #2147](https://tree.taiga.io/project/penpot/issue/2147) - Fix problem duplicating paths [Taiga #2147](https://tree.taiga.io/project/penpot/issue/2147)
- Fix problem inheriting attributes from SVG root when importing [Taiga #2124](https://tree.taiga.io/project/penpot/issue/2124)
- Fix problem with lines and inside/outside stroke [Taiga #2146](https://tree.taiga.io/project/penpot/issue/2146)
- Add stroke width in selection calculation [Taiga #2146](https://tree.taiga.io/project/penpot/issue/2146)
- Fix shift+wheel to horizontal scrolling in MacOS [#1217](https://github.com/penpot/penpot/issues/1217)
- Fix path stroke is not working properly with high thickness [Taiga #2154](https://tree.taiga.io/project/penpot/issue/2154)
- Fix bug with transformation operations [Taiga #2155](https://tree.taiga.io/project/penpot/issue/2155)
- Fix bug in firefox when a text box is inside a mask [Taiga #2152](https://tree.taiga.io/project/penpot/issue/2152)
### :arrow_up: Deps updates ### :arrow_up: Deps updates
### :boom: Breaking changes
- Some stroke-caps can change behaviour
- Text display bug fix could potentialy make some texts jump a line
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)

View file

@ -58,7 +58,7 @@
join team as t on (t.id = tp.team_id) join team as t on (t.id = tp.team_id)
where t.deleted_at is null where t.deleted_at is null
and tp.profile_id = ? and tp.profile_id = ?
order by t.created_at asc") order by tp.created_at asc")
(defn retrieve-teams (defn retrieve-teams
[conn profile-id] [conn profile-id]

View file

@ -160,6 +160,7 @@
;; PATHS ;; PATHS
(d/export gsp/content->selrect) (d/export gsp/content->selrect)
(d/export gsp/transform-content) (d/export gsp/transform-content)
(d/export gsp/open-path?)
;; Intersection ;; Intersection
(d/export gin/overlaps?) (d/export gin/overlaps?)

View file

@ -284,12 +284,19 @@
(defn overlaps? (defn overlaps?
"General case to check for overlaping between shapes and a rectangle" "General case to check for overlaping between shapes and a rectangle"
[shape rect] [shape rect]
(let [stroke-width (/ (or (:stroke-width shape) 0) 2)
rect (-> rect
(update :x - stroke-width)
(update :y - stroke-width)
(update :width + (* 2 stroke-width))
(update :height + (* 2 stroke-width))
)]
(or (not shape) (or (not shape)
(let [path? (= :path (:type shape)) (let [path? (= :path (:type shape))
circle? (= :circle (:type shape))] circle? (= :circle (:type shape))]
(and (overlaps-rect-points? rect (:points shape)) (and (overlaps-rect-points? rect (:points shape))
(or (not path?) (overlaps-path? shape rect)) (or (not path?) (overlaps-path? shape rect))
(or (not circle?) (overlaps-ellipse? shape rect)))))) (or (not circle?) (overlaps-ellipse? shape rect)))))))
(defn has-point-rect? (defn has-point-rect?
[rect point] [rect point]

View file

@ -126,8 +126,8 @@
(let [tangent (curve-tangent curve t)] (let [tangent (curve-tangent curve t)]
(cond (cond
(> (:y tangent) 0) 1 (> (:y tangent) 0) -1
(< (:y tangent) 0) -1 (< (:y tangent) 0) 1
:else 0))) :else 0)))
(defn curve-split (defn curve-split
@ -822,30 +822,27 @@
(let [selrect (content->selrect content) (let [selrect (content->selrect content)
ray-line [point (gpt/point (inc (:x point)) (:y point))] ray-line [point (gpt/point (inc (:x point)) (:y point))]
closed-subpaths closed-content
(into []
(comp (filter sp/is-closed?)
(mapcat :data))
(->> content (->> content
(sp/close-subpaths) (sp/close-subpaths)
(sp/get-subpaths) (sp/get-subpaths)))
(filterv sp/is-closed?))
cast-ray cast-ray
(fn [cmd] (fn [cmd]
(case (:command cmd) (case (:command cmd)
:line-to (ray-line-intersect point (command->line cmd)) :line-to (ray-line-intersect point (command->line cmd))
:curve-to (ray-curve-intersect ray-line (command->bezier cmd)) :curve-to (ray-curve-intersect ray-line (command->bezier cmd))
#_:else [])) #_:else []))]
is-point-in-subpath? (and (gpr/contains-point? selrect point)
(fn [subpath] (->> closed-content
(and (gpr/contains-point? (content->selrect (:data subpath)) point)
(->> (:data subpath)
(mapcat cast-ray) (mapcat cast-ray)
(map second) (map second)
(reduce +) (reduce +)
(not= 0))))] (not= 0)))))
(and (gpr/contains-point? selrect point)
(some is-point-in-subpath? closed-subpaths))))
(defn split-line-to (defn split-line-to
"Given a point and a line-to command will create a two new line-to commands "Given a point and a line-to command will create a two new line-to commands
@ -948,3 +945,14 @@
(gsc/transform-points points-center transform-inverse) (gsc/transform-points points-center transform-inverse)
(gpr/points->selrect))] (gpr/points->selrect))]
[points selrect])) [points selrect]))
(defn open-path?
[shape]
(and (= :path (:type shape))
(not (->> shape
:content
(sp/close-subpaths)
(sp/get-subpaths)
(every? sp/is-closed?)))))

View file

@ -73,9 +73,15 @@
(fn [subpaths current] (fn [subpaths current]
(let [is-move? (= :move-to (:command current)) (let [is-move? (= :move-to (:command current))
last-idx (dec (count subpaths))] last-idx (dec (count subpaths))]
(if is-move? (cond
is-move?
(conj subpaths (make-subpath current)) (conj subpaths (make-subpath current))
(update subpaths last-idx add-subpath-command current))))]
(>= last-idx 0)
(update subpaths last-idx add-subpath-command current)
:else
subpaths)))]
(->> content (->> content
(reduce reduce-subpath [])))) (reduce reduce-subpath []))))

View file

@ -432,6 +432,7 @@
(ptk/reify ::duplicate-selected (ptk/reify ::duplicate-selected
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(when (nil? (get-in state [:workspace-local :transform]))
(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 page-id)
selected (wsh/lookup-selected state) selected (wsh/lookup-selected state)
@ -461,7 +462,7 @@
:undo-changes uchanges :undo-changes uchanges
:origin it}) :origin it})
(select-shapes selected) (select-shapes selected)
(memorize-duplicated id-original id-duplicated)))))) (memorize-duplicated id-original id-duplicated)))))))
(defn change-hover-state (defn change-hover-state
[id value] [id value]

View file

@ -178,7 +178,8 @@
:y (+ y offset-y)} :y (+ y offset-y)}
(gsh/setup-selrect) (gsh/setup-selrect)
(assoc :svg-attrs (-> (:attrs svg-data) (assoc :svg-attrs (-> (:attrs svg-data)
(dissoc :viewBox :xmlns)))))) (dissoc :viewBox :xmlns)
(d/without-keys usvg/inheritable-props))))))
(defn create-group [name frame-id svg-data {:keys [attrs]}] (defn create-group [name frame-id svg-data {:keys [attrs]}]
(let [svg-transform (usvg/parse-transform (:transform attrs)) (let [svg-transform (usvg/parse-transform (:transform attrs))
@ -387,7 +388,7 @@
(setup-stroke))) (setup-stroke)))
children (cond->> (:content element-data) children (cond->> (:content element-data)
(= tag :g) (or (= tag :g) (= tag :svg))
(mapv #(usvg/inherit-attributes attrs %)))] (mapv #(usvg/inherit-attributes attrs %)))]
[shape children])))) [shape children]))))
@ -487,11 +488,15 @@
;; Creates the root shape ;; Creates the root shape
changes (dwc/add-shape-changes page-id objects selected root-shape false) changes (dwc/add-shape-changes page-id objects selected root-shape false)
root-attrs (-> (:attrs svg-data)
(usvg/format-styles))
;; Reduces the children to create the changes to add the children shapes ;; Reduces the children to create the changes to add the children shapes
[_ [rchanges uchanges]] [_ [rchanges uchanges]]
(reduce (partial add-svg-child-changes page-id objects selected frame-id root-id svg-data) (reduce (partial add-svg-child-changes page-id objects selected frame-id root-id svg-data)
[unames changes] [unames changes]
(d/enumerate (:content svg-data))) (d/enumerate (->> (:content svg-data)
(mapv #(usvg/inherit-attributes root-attrs %)))))
reg-objects-action {:type :reg-objects reg-objects-action {:type :reg-objects
:page-id page-id :page-id page-id

View file

@ -70,10 +70,7 @@
(defn- fix-init-point (defn- fix-init-point
"Fix the initial point so the resizes are accurate" "Fix the initial point so the resizes are accurate"
[initial handler shape] [initial handler shape]
(let [{:keys [x y width height]} (:selrect shape) (let [{:keys [x y width height]} (:selrect shape)]
{:keys [rotation]} shape
rotation (or rotation 0)]
(if (= rotation 0)
(cond-> initial (cond-> initial
(contains? #{:left :top-left :bottom-left} handler) (contains? #{:left :top-left :bottom-left} handler)
(assoc :x x) (assoc :x x)
@ -85,8 +82,7 @@
(assoc :y y) (assoc :y y)
(contains? #{:bottom :bottom-right :bottom-left} handler) (contains? #{:bottom :bottom-right :bottom-left} handler)
(assoc :y (+ y height))) (assoc :y (+ y height)))))
initial)))
(defn finish-transform [] (defn finish-transform []
(ptk/reify ::finish-transform (ptk/reify ::finish-transform
@ -285,10 +281,19 @@
(letfn [(resize [shape initial layout [point lock? center? point-snap]] (letfn [(resize [shape initial layout [point lock? center? point-snap]]
(let [{:keys [width height]} (:selrect shape) (let [{:keys [width height]} (:selrect shape)
{:keys [rotation]} shape {:keys [rotation]} shape
shape-center (gsh/center-shape shape)
shape-transform (:transform shape (gmt/matrix))
shape-transform-inverse (:transform-inverse shape (gmt/matrix))
rotation (or rotation 0) rotation (or rotation 0)
initial (gsh/transform-point-center initial shape-center shape-transform-inverse)
initial (fix-init-point initial handler shape) initial (fix-init-point initial handler shape)
point (gsh/transform-point-center (if (= rotation 0) point-snap point)
shape-center shape-transform-inverse)
shapev (-> (gpt/point width height)) shapev (-> (gpt/point width height))
scale-text (:scale-text layout) scale-text (:scale-text layout)
@ -300,8 +305,7 @@
handler-mult (let [[x y] (handler-multipliers handler)] (gpt/point x y)) handler-mult (let [[x y] (handler-multipliers handler)] (gpt/point x y))
;; Difference between the origin point in the coordinate system of the rotation ;; Difference between the origin point in the coordinate system of the rotation
deltav (-> (gpt/to-vec initial (if (= rotation 0) point-snap point)) deltav (-> (gpt/to-vec initial point)
(gpt/transform (gmt/rotate-matrix (- rotation)))
(gpt/multiply handler-mult)) (gpt/multiply handler-mult))
;; Resize vector ;; Resize vector
@ -317,24 +321,23 @@
scalev) scalev)
;; Resize origin point given the selected handler ;; Resize origin point given the selected handler
origin (handler-resize-origin (:selrect shape) handler) handler-origin (handler-resize-origin (:selrect shape) handler)
shape-center (gsh/center-shape shape)
shape-transform (:transform shape (gmt/matrix))
shape-transform-inverse (:transform-inverse shape (gmt/matrix))
;; If we want resize from center, displace the shape ;; If we want resize from center, displace the shape
;; so it is still centered after resize. ;; so it is still centered after resize.
displacement (when center? displacement
(when center?
(-> shape-center (-> shape-center
(gpt/subtract origin) (gpt/subtract handler-origin)
(gpt/multiply scalev) (gpt/multiply scalev)
(gpt/add origin) (gpt/add handler-origin)
(gpt/subtract shape-center) (gpt/subtract shape-center)
(gpt/multiply (gpt/point -1 -1)) (gpt/multiply (gpt/point -1 -1))
(gpt/transform shape-transform))) (gpt/transform shape-transform)))
origin (cond-> (gsh/transform-point-center origin shape-center shape-transform) resize-origin
(cond-> (gsh/transform-point-center handler-origin shape-center shape-transform)
(some? displacement) (some? displacement)
(gpt/add displacement)) (gpt/add displacement))
@ -344,7 +347,7 @@
(rx/of (set-modifiers ids (rx/of (set-modifiers ids
{:displacement displacement {:displacement displacement
:resize-vector scalev :resize-vector scalev
:resize-origin origin :resize-origin resize-origin
:resize-transform shape-transform :resize-transform shape-transform
:resize-scale-text scale-text :resize-scale-text scale-text
:resize-transform-inverse shape-transform-inverse})))) :resize-transform-inverse shape-transform-inverse}))))

View file

@ -25,22 +25,24 @@
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(defn bounds (defn calc-bounds
[object objects] [object objects]
(if (= :group (:type object))
(let [children-bounds
(into []
(comp (map #(get objects %))
(map #(bounds % objects)))
(:shapes object))]
(gsh/join-rects children-bounds))
(let [padding (filters/calculate-padding object)] (let [xf-get-bounds (comp (map #(get objects %)) (map #(calc-bounds % objects)))
padding (filters/calculate-padding object)
obj-bounds
(-> (filters/get-filters-bounds object) (-> (filters/get-filters-bounds object)
(update :x - padding) (update :x - padding)
(update :y - padding) (update :y - padding)
(update :width + (* 2 padding)) (update :width + (* 2 padding))
(update :height + (* 2 padding)))))) (update :height + (* 2 padding)))]
(if (= :group (:type object))
(->> (:shapes object)
(into [obj-bounds] xf-get-bounds)
(gsh/join-rects))
obj-bounds)))
(mf/defc object-svg (mf/defc object-svg
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
@ -64,7 +66,7 @@
objects (reduce updt-fn objects mod-ids) objects (reduce updt-fn objects mod-ids)
object (get objects object-id) object (get objects object-id)
{:keys [x y width height] :as bs} (bounds object objects) {:keys [x y width height] :as bs} (calc-bounds object objects)
[_ _ width height :as coords] (->> [x y width height] (map #(* % zoom))) [_ _ width height :as coords] (->> [x y width height] (map #(* % zoom)))
vbox (str/join " " coords) vbox (str/join " " coords)

View file

@ -14,12 +14,14 @@
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
(defn- stroke-type->dasharray (defn- stroke-type->dasharray
[style] [width style]
(case style (let [values (case style
:mixed "5,5,1,5" :mixed [5 5 1 5]
:dotted "5,5" :dotted [5 5]
:dashed "10,10" :dashed [10 10]
nil)) nil)]
(->> values (map #(+ % width)) (str/join ","))))
(defn- truncate-side (defn- truncate-side
[shape ra-attr rb-attr dimension-attr] [shape ra-attr rb-attr dimension-attr]
@ -102,10 +104,11 @@
(defn add-stroke [attrs shape render-id] (defn add-stroke [attrs shape render-id]
(let [stroke-style (:stroke-style shape :none) (let [stroke-style (:stroke-style shape :none)
stroke-color-gradient-id (str "stroke-color-gradient_" render-id)] stroke-color-gradient-id (str "stroke-color-gradient_" render-id)
stroke-width (:stroke-width shape 1)]
(if (not= stroke-style :none) (if (not= stroke-style :none)
(let [stroke-attrs (let [stroke-attrs
(cond-> {:strokeWidth (:stroke-width shape 1)} (cond-> {:strokeWidth stroke-width}
(:stroke-color-gradient shape) (:stroke-color-gradient shape)
(assoc :stroke (str/format "url(#%s)" stroke-color-gradient-id)) (assoc :stroke (str/format "url(#%s)" stroke-color-gradient-id))
@ -118,7 +121,7 @@
(assoc :strokeOpacity (:stroke-opacity shape nil)) (assoc :strokeOpacity (:stroke-opacity shape nil))
(not= stroke-style :svg) (not= stroke-style :svg)
(assoc :strokeDasharray (stroke-type->dasharray stroke-style)) (assoc :strokeDasharray (stroke-type->dasharray stroke-width stroke-style))
;; For simple line caps we use svg stroke-line-cap attribute. This ;; For simple line caps we use svg stroke-line-cap attribute. This
;; only works if all caps are the same and we are not using the tricks ;; only works if all caps are the same and we are not using the tricks

View file

@ -7,6 +7,7 @@
(ns app.main.ui.shapes.custom-stroke (ns app.main.ui.shapes.custom-stroke
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.geom.shapes :as gsh]
[app.main.ui.context :as muc] [app.main.ui.context :as muc]
[app.util.object :as obj] [app.util.object :as obj]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -145,6 +146,8 @@
(mf/defc stroke-defs (mf/defc stroke-defs
[{:keys [shape render-id]}] [{:keys [shape render-id]}]
(when (and (= (:type shape) :path)
(gsh/open-path? shape))
(cond (cond
(and (= :inner (:stroke-alignment shape :center)) (and (= :inner (:stroke-alignment shape :center))
(> (:stroke-width shape 0) 0)) (> (:stroke-width shape 0) 0))
@ -160,7 +163,7 @@
(some? (:stroke-cap-end shape))) (some? (:stroke-cap-end shape)))
(= (:stroke-alignment shape) :center)) (= (:stroke-alignment shape) :center))
[:& cap-markers {:shape shape [:& cap-markers {:shape shape
:render-id render-id}])) :render-id render-id}])))
;; Outer alingmnent: display the shape in two layers. One ;; Outer alingmnent: display the shape in two layers. One
;; without stroke (only fill), and another one only with stroke ;; without stroke (only fill), and another one only with stroke
@ -253,15 +256,17 @@
stroke-position (:stroke-alignment shape :center) stroke-position (:stroke-alignment shape :center)
has-stroke? (and (> stroke-width 0) has-stroke? (and (> stroke-width 0)
(not= stroke-style :none)) (not= stroke-style :none))
closed? (or (not= :path (:type shape))
(not (gsh/open-path? shape)))
inner? (= :inner stroke-position) inner? (= :inner stroke-position)
outer? (= :outer stroke-position)] outer? (= :outer stroke-position)]
(cond (cond
(and has-stroke? inner?) (and has-stroke? inner? closed?)
[:& inner-stroke {:shape shape} [:& inner-stroke {:shape shape}
child] child]
(and has-stroke? outer?) (and has-stroke? outer? closed?)
[:& outer-stroke {:shape shape} [:& outer-stroke {:shape shape}
child] child]

View file

@ -27,13 +27,23 @@
[(first childs) (rest childs)] [(first childs) (rest childs)]
[nil childs]) [nil childs])
;; We need to separate mask and clip into two because a bug in Firefox
;; breaks when the group has clip+mask+foreignObject
;; Clip and mask separated will work in every platform
; Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1734805
[clip-wrapper clip-props]
(if masked-group?
["g" (-> (obj/new)
(obj/set! "clipPath" (clip-url render-id mask)))]
[mf/Fragment nil])
[mask-wrapper mask-props] [mask-wrapper mask-props]
(if masked-group? (if masked-group?
["g" (-> (obj/new) ["g" (-> (obj/new)
(obj/set! "clipPath" (clip-url render-id mask))
(obj/set! "mask" (mask-url render-id mask)))] (obj/set! "mask" (mask-url render-id mask)))]
[mf/Fragment nil])] [mf/Fragment nil])]
[:> clip-wrapper clip-props
[:> mask-wrapper mask-props [:> mask-wrapper mask-props
(when masked-group? (when masked-group?
[:> render-mask #js {:frame frame :mask mask}]) [:> render-mask #js {:frame frame :mask mask}])
@ -41,7 +51,7 @@
(for [item childs] (for [item childs]
[:& shape-wrapper {:frame frame [:& shape-wrapper {:frame frame
:shape item :shape item
:key (:id item)}])])))) :key (:id item)}])]]))))

View file

@ -363,12 +363,14 @@
delta-y (-> (.-deltaY ^js event) delta-y (-> (.-deltaY ^js event)
(* unit) (* unit)
(/ zoom)) (/ zoom))
delta-x (-> (.-deltaX ^js event) delta-x (-> (.-deltaX ^js event)
(* unit) (* unit)
(/ zoom))] (/ zoom))]
(dom/prevent-default event) (dom/prevent-default event)
(dom/stop-propagation event) (dom/stop-propagation event)
(if (kbd/shift? event) (if (and (not (cfg/check-platform? :macos)) ;; macos sends delta-x automaticaly, don't need to do it
(kbd/shift? event))
(st/emit! (dw/update-viewport-position {:x #(+ % delta-y)})) (st/emit! (dw/update-viewport-position {:x #(+ % delta-y)}))
(st/emit! (dw/update-viewport-position {:x #(+ % delta-x) (st/emit! (dw/update-viewport-position {:x #(+ % delta-x)
:y #(+ % delta-y)}))))))))) :y #(+ % delta-y)})))))))))

View file

@ -229,7 +229,12 @@
current-transform (mf/deref refs/current-transform) current-transform (mf/deref refs/current-transform)
selrect (:selrect shape) selrect (:selrect shape)
transform (geom/transform-matrix shape {:no-flip true})] transform (geom/transform-matrix shape {:no-flip true})
rotation (-> (gpt/point 1 0)
(gpt/transform (:transform shape))
(gpt/angle)
(mod 360))]
(when (not (#{:move :rotate} current-transform)) (when (not (#{:move :rotate} current-transform))
[:g.controls {:pointer-events (if disable-handlers "none" "visible")} [:g.controls {:pointer-events (if disable-handlers "none" "visible")}
@ -249,7 +254,7 @@
:on-rotate on-rotate :on-rotate on-rotate
:on-resize (partial on-resize position) :on-resize (partial on-resize position)
:transform transform :transform transform
:rotation (:rotation shape) :rotation rotation
:color color :color color
:overflow-text overflow-text} :overflow-text overflow-text}
props (map->obj (merge common-props props))] props (map->obj (merge common-props props))]