mirror of
https://github.com/penpot/penpot.git
synced 2025-02-08 16:18:11 -05:00
🐛 Fix problem with inconsistency with border-radius
This commit is contained in:
parent
928128ba2d
commit
e6f8269c0b
5 changed files with 124 additions and 78 deletions
|
@ -11,6 +11,9 @@
|
|||
## 1.13.0-beta
|
||||
|
||||
### :boom: Breaking changes
|
||||
|
||||
- We've changed the behaviour of the border-radius so it works as CSS that [has some limits](https://www.w3.org/TR/css-backgrounds-3/#corner-overlap).
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
- Exporting big files flow [Taiga #2218](https://tree.taiga.io/project/penpot/us/2218)
|
||||
|
@ -49,6 +52,7 @@
|
|||
- Fix problem when importing a SVG with text [#1532](https://github.com/penpot/penpot/issues/1532)
|
||||
- Fix problem when adding shadows to imported text [#Taiga 3057](https://tree.taiga.io/project/penpot/issue/3057)
|
||||
- Fix problem when importing SVG's with uses with overriding properties [#Taiga 2884](https://tree.taiga.io/project/penpot/issue/2884)
|
||||
- Fix inconsistency with radius in SVG an CSS [#1587](https://github.com/penpot/penpot/issues/1587)
|
||||
|
||||
### :arrow_up: Deps updates
|
||||
### :heart: Community contributions by (Thank you!)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.geom.shapes.bool :as gsb]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.geom.shapes.constraints :as gct]
|
||||
[app.common.geom.shapes.corners :as gsc]
|
||||
[app.common.geom.shapes.intersect :as gin]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
|
@ -153,3 +154,7 @@
|
|||
;; Constraints
|
||||
(dm/export gct/default-constraints-h)
|
||||
(dm/export gct/default-constraints-v)
|
||||
|
||||
;; Corners
|
||||
(dm/export gsc/shape-corners-1)
|
||||
(dm/export gsc/shape-corners-4)
|
||||
|
|
46
common/src/app/common/geom/shapes/corners.cljc
Normal file
46
common/src/app/common/geom/shapes/corners.cljc
Normal file
|
@ -0,0 +1,46 @@
|
|||
;; 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.corners)
|
||||
|
||||
(defn fix-radius
|
||||
;; https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
|
||||
;;
|
||||
;; > Corner curves must not overlap: When the sum of any two adjacent border radii exceeds the size of the border box,
|
||||
;; > UAs must proportionally reduce the used values of all border radii until none of them overlap.
|
||||
;;
|
||||
;; > The algorithm for reducing radii is as follows: Let f = min(Li/Si), where i ∈ {top, right, bottom, left}, Si is
|
||||
;; > the sum of the two corresponding radii of the corners on side i, and Ltop = Lbottom = the width of the box, and
|
||||
;; > Lleft = Lright = the height of the box. If f < 1, then all corner radii are reduced by multiplying them by f.
|
||||
([width height r]
|
||||
(let [f (min (/ width (* 2 r))
|
||||
(/ height (* 2 r)))]
|
||||
(if (< f 1)
|
||||
(* r f)
|
||||
r)))
|
||||
|
||||
([width height r1 r2 r3 r4]
|
||||
(let [f (min (/ width (+ r1 r2))
|
||||
(/ height (+ r2 r3))
|
||||
(/ width (+ r3 r4))
|
||||
(/ height (+ r4 r1)))]
|
||||
(if (< f 1)
|
||||
[(* r1 f) (* r2 f) (* r3 f) (* r4 f)]
|
||||
[r1 r2 r3 r4]))))
|
||||
|
||||
(defn shape-corners-1
|
||||
"Retrieve the effective value for the corner given a single value for corner."
|
||||
[{:keys [width height rx] :as shape}]
|
||||
(if (some? rx)
|
||||
(fix-radius width height rx)
|
||||
0))
|
||||
|
||||
(defn shape-corners-4
|
||||
"Retrieve the effective value for the corner given four values for the corners."
|
||||
[{:keys [width height r1 r2 r3 r4]}]
|
||||
(if (and (some? r1) (some? r2) (some? r3) (some? r4))
|
||||
(fix-radius width height r1 r2 r3 r4)
|
||||
[r1 r2 r3 r4]))
|
|
@ -11,9 +11,11 @@
|
|||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.common :as gsc]
|
||||
[app.common.geom.shapes.corners :as gso]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.path.bool :as pb]
|
||||
[app.common.path.commands :as pc]))
|
||||
[app.common.path.commands :as pc]
|
||||
[app.common.spec.radius :as ctr]))
|
||||
|
||||
(def ^:const bezier-circle-c 0.551915024494)
|
||||
|
||||
|
@ -93,7 +95,7 @@
|
|||
|
||||
(defn circle->path
|
||||
"Creates the bezier curves to approximate a circle shape"
|
||||
[x y width height]
|
||||
[{:keys [x y width height]}]
|
||||
(let [mx (+ x (/ width 2))
|
||||
my (+ y (/ height 2))
|
||||
ex (+ x width)
|
||||
|
@ -116,11 +118,12 @@
|
|||
(pc/make-curve-to p4 (assoc p3 :x c1x) (assoc p4 :y c2y))
|
||||
(pc/make-curve-to p1 (assoc p4 :y c1y) (assoc p1 :x c1x))]))
|
||||
|
||||
(defn rect->path
|
||||
"Creates a bezier curve that approximates a rounded corner rectangle"
|
||||
[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))
|
||||
(defn draw-rounded-rect-path
|
||||
([x y width height r]
|
||||
(draw-rounded-rect-path x y width height r r r r))
|
||||
|
||||
([x y width height r1 r2 r3 r4]
|
||||
(let [p1 (gpt/point x (+ y r1))
|
||||
p2 (gpt/point (+ x r1) y)
|
||||
|
||||
p3 (gpt/point (+ width x (- r2)) y)
|
||||
|
@ -144,7 +147,21 @@
|
|||
(conj (pc/make-line-to p7))
|
||||
(cond-> (not= p7 p8)
|
||||
(conj (make-corner-arc p7 p8 :bottom-left r4)))
|
||||
(conj (pc/make-line-to p1)))))
|
||||
(conj (pc/make-line-to p1))))))
|
||||
|
||||
(defn rect->path
|
||||
"Creates a bezier curve that approximates a rounded corner rectangle"
|
||||
[{:keys [x y width height] :as shape}]
|
||||
(case (ctr/radius-mode shape)
|
||||
:radius-1
|
||||
(let [radius (gso/shape-corners-1 shape)]
|
||||
(draw-rounded-rect-path x y width height radius))
|
||||
|
||||
:radius-4
|
||||
(let [[r1 r2 r3 r4] (gso/shape-corners-4 shape)]
|
||||
(draw-rounded-rect-path x y width height r1 r2 r3 r4))
|
||||
|
||||
[]))
|
||||
|
||||
(declare convert-to-path)
|
||||
|
||||
|
@ -192,9 +209,9 @@
|
|||
"Transforms the given shape to a path"
|
||||
([shape]
|
||||
(convert-to-path shape {}))
|
||||
([{:keys [type x y width height r1 r2 r3 r4 rx metadata] :as shape} objects]
|
||||
([{:keys [type metadata] :as shape} objects]
|
||||
(assert (map? objects))
|
||||
(case (:type shape)
|
||||
(case type
|
||||
:group
|
||||
(group-to-path shape objects)
|
||||
|
||||
|
@ -204,8 +221,8 @@
|
|||
(:rect :circle :image :text)
|
||||
(let [new-content
|
||||
(case type
|
||||
:circle (circle->path x y width height)
|
||||
#_:else (rect->path x y width height r1 r2 r3 r4 rx))
|
||||
:circle (circle->path shape)
|
||||
#_:else (rect->path shape))
|
||||
|
||||
;; Apply the transforms that had the shape
|
||||
transform (:transform shape)
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
(ns app.main.ui.shapes.attrs
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.spec.radius :as ctr]
|
||||
[app.common.spec.shape :refer [stroke-caps-line stroke-caps-marker]]
|
||||
[app.main.ui.context :as muc]
|
||||
|
@ -26,49 +28,21 @@
|
|||
|
||||
(->> values (map #(+ % width)) (str/join ","))))
|
||||
|
||||
(defn- truncate-side
|
||||
[shape ra-attr rb-attr dimension-attr]
|
||||
(let [ra (ra-attr shape)
|
||||
rb (rb-attr shape)
|
||||
dimension (dimension-attr shape)]
|
||||
(if (<= (+ ra rb) dimension)
|
||||
[ra rb]
|
||||
[(/ (* ra dimension) (+ ra rb))
|
||||
(/ (* rb dimension) (+ ra rb))])))
|
||||
|
||||
(defn- truncate-radius
|
||||
[shape]
|
||||
(let [[r-top-left r-top-right]
|
||||
(truncate-side shape :r1 :r2 :width)
|
||||
|
||||
[r-right-top r-right-bottom]
|
||||
(truncate-side shape :r2 :r3 :height)
|
||||
|
||||
[r-bottom-right r-bottom-left]
|
||||
(truncate-side shape :r3 :r4 :width)
|
||||
|
||||
[r-left-bottom r-left-top]
|
||||
(truncate-side shape :r4 :r1 :height)]
|
||||
|
||||
[(min r-top-left r-left-top)
|
||||
(min r-top-right r-right-top)
|
||||
(min r-right-bottom r-bottom-right)
|
||||
(min r-bottom-left r-left-bottom)]))
|
||||
|
||||
(defn add-border-radius [attrs shape]
|
||||
(defn add-border-radius [attrs {:keys [x y width height] :as shape}]
|
||||
(case (ctr/radius-mode shape)
|
||||
|
||||
:radius-1
|
||||
(obj/merge! attrs #js {:rx (:rx shape 0)
|
||||
:ry (:ry shape 0)})
|
||||
(let [radius (gsh/shape-corners-1 shape)]
|
||||
(obj/merge! attrs #js {:rx radius :ry radius}))
|
||||
|
||||
:radius-4
|
||||
(let [[r1 r2 r3 r4] (truncate-radius shape)
|
||||
top (- (:width shape) r1 r2)
|
||||
right (- (:height shape) r2 r3)
|
||||
bottom (- (:width shape) r3 r4)
|
||||
left (- (:height shape) r4 r1)]
|
||||
(obj/merge! attrs #js {:d (str "M" (+ (:x shape) r1) "," (:y shape) " "
|
||||
(let [[r1 r2 r3 r4] (gsh/shape-corners-4 shape)
|
||||
top (- width r1 r2)
|
||||
right (- height r2 r3)
|
||||
bottom (- width r3 r4)
|
||||
left (- height r4 r1)]
|
||||
(obj/merge! attrs #js {:d (dm/str
|
||||
"M" (+ x r1) "," y " "
|
||||
"h" top " "
|
||||
"a" r2 "," r2 " 0 0 1 " r2 "," r2 " "
|
||||
"v" right " "
|
||||
|
|
Loading…
Add table
Reference in a new issue