mirror of
https://github.com/penpot/penpot.git
synced 2026-06-09 08:52:05 +00:00
🐛 Fix path export crop when stroke has arrow/marker caps
This commit is contained in:
parent
9911ff7959
commit
8d3516d06d
@ -11,7 +11,8 @@
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.rect :as grc]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.path :as path]))
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.stroke :as cts]))
|
||||
|
||||
(defn shape-stroke-margin
|
||||
[shape stroke-width]
|
||||
@ -105,6 +106,19 @@
|
||||
(grc/points->rect))]
|
||||
(get-rect-filter-bounds srect filters blur-value ignore-shadow-margin?)))))
|
||||
|
||||
(def ^:private stroke-margin-multiplier 4.25)
|
||||
|
||||
(defn- stroke-cap-marker-margin
|
||||
[strokes open-path?]
|
||||
(if open-path?
|
||||
(->> strokes
|
||||
(filter (fn [s]
|
||||
(or (cts/stroke-caps-marker (:stroke-cap-start s))
|
||||
(cts/stroke-caps-marker (:stroke-cap-end s)))))
|
||||
(map #(* stroke-margin-multiplier (:stroke-width % 0)))
|
||||
(reduce d/max 0))
|
||||
0))
|
||||
|
||||
(defn calculate-padding
|
||||
([shape]
|
||||
(calculate-padding shape false false))
|
||||
@ -127,6 +141,11 @@
|
||||
0
|
||||
(shape-stroke-margin shape stroke-width))
|
||||
|
||||
stroke-cap-margin
|
||||
(if ignore-margin?
|
||||
0
|
||||
(stroke-cap-marker-margin strokes open-path?))
|
||||
|
||||
shadow-width
|
||||
(->> (:shadow shape)
|
||||
(remove :hidden)
|
||||
@ -149,8 +168,8 @@
|
||||
shadow-width
|
||||
(if ignore-shadow-margin? 0 shadow-width)]
|
||||
|
||||
{:horizontal (mth/ceil (+ stroke-margin shadow-width))
|
||||
:vertical (mth/ceil (+ stroke-margin shadow-height))})))
|
||||
{:horizontal (mth/ceil (+ stroke-margin stroke-cap-margin shadow-width))
|
||||
:vertical (mth/ceil (+ stroke-margin stroke-cap-margin shadow-height))})))
|
||||
|
||||
(defn- add-padding
|
||||
[bounds padding]
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
[app.common.types.shape.layout :as ctsl]
|
||||
[app.common.types.shape.shadow :as ctss]
|
||||
[app.common.types.shape.text :as ctsx]
|
||||
[app.common.types.stroke :as stroke]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.variant :as ctv]
|
||||
@ -61,8 +62,8 @@
|
||||
(map->Shape attrs))
|
||||
:clj (map->Shape attrs)))
|
||||
|
||||
(def stroke-caps-line #{:round :square})
|
||||
(def stroke-caps-marker #{:line-arrow :triangle-arrow :square-marker :circle-marker :diamond-marker})
|
||||
(def stroke-caps-line stroke/stroke-caps-line)
|
||||
(def stroke-caps-marker stroke/stroke-caps-marker)
|
||||
(def stroke-caps (conj (set/union stroke-caps-line stroke-caps-marker) nil))
|
||||
|
||||
(def shape-types
|
||||
|
||||
@ -12,6 +12,9 @@
|
||||
;; SCHEMAS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def stroke-caps-line #{:round :square})
|
||||
(def stroke-caps-marker #{:line-arrow :triangle-arrow :square-marker :circle-marker :diamond-marker})
|
||||
|
||||
(def default-stroke
|
||||
{:stroke-alignment :inner
|
||||
:stroke-style :solid
|
||||
|
||||
@ -6,9 +6,71 @@
|
||||
|
||||
(ns common-tests.geom-shapes-strokes-test
|
||||
(:require
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.geom.shapes.strokes :as gss]
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.shape :as cts]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(defn- make-open-path-with-stroke-cap
|
||||
[cap-type stroke-width]
|
||||
(cts/setup-shape
|
||||
{:type :path
|
||||
:content (path/content [{:command :move-to :params {:x 0 :y 100}}
|
||||
{:command :curve-to :params {:x 200 :y 100 :c1x 0 :c1y -50 :c2x 200 :c2y -50}}])
|
||||
:strokes [{:stroke-color "#FF0000"
|
||||
:stroke-width stroke-width
|
||||
:stroke-alignment :center
|
||||
:stroke-cap-start cap-type
|
||||
:stroke-cap-end cap-type}]}))
|
||||
|
||||
(t/deftest stroke-cap-marker-padding-test
|
||||
(t/testing "Path with triangle-arrow caps should have enough padding to contain markers"
|
||||
;; For triangle-arrow with strokeWidth=3:
|
||||
;; markerHeight=8.5, viewBox 0 0 3 6, scale=min(8.5/3,8.5/6)*3=4.25
|
||||
;; lateral extent = 3 * 4.25 = 12.75 user units per side
|
||||
;; So padding should be > 12 user units
|
||||
(let [shape (make-open-path-with-stroke-cap :triangle-arrow 3)
|
||||
padding (gsb/calculate-padding shape)]
|
||||
(t/is (> (:horizontal padding) 12)
|
||||
"Horizontal padding should accommodate triangle-arrow marker lateral extent")
|
||||
(t/is (> (:vertical padding) 12)
|
||||
"Vertical padding should accommodate triangle-arrow marker lateral extent")))
|
||||
|
||||
(t/testing "Path with line-arrow caps should have enough padding to contain markers"
|
||||
(let [shape (make-open-path-with-stroke-cap :line-arrow 3)
|
||||
padding (gsb/calculate-padding shape)]
|
||||
(t/is (> (:horizontal padding) 12)
|
||||
"Horizontal padding should accommodate line-arrow marker lateral extent")
|
||||
(t/is (> (:vertical padding) 12)
|
||||
"Vertical padding should accommodate line-arrow marker lateral extent")))
|
||||
|
||||
(t/testing "Path with diamond-marker caps should have enough padding"
|
||||
;; diamond-marker: markerWidth=6, viewBox 0 0 6 6, scale=min(6/6,6/6)*3=3
|
||||
;; lateral extent = 3 * 3 = 9 user units per side
|
||||
(let [shape (make-open-path-with-stroke-cap :diamond-marker 3)
|
||||
padding (gsb/calculate-padding shape)]
|
||||
(t/is (> (:horizontal padding) 8)
|
||||
"Horizontal padding should accommodate diamond-marker lateral extent")
|
||||
(t/is (> (:vertical padding) 8)
|
||||
"Vertical padding should accommodate diamond-marker lateral extent")))
|
||||
|
||||
(t/testing "Closed path with marker caps should not have extra marker padding"
|
||||
(let [shape (cts/setup-shape
|
||||
{:type :path
|
||||
:content (path/content [{:command :move-to :params {:x 0 :y 0}}
|
||||
{:command :line-to :params {:x 100 :y 0}}
|
||||
{:command :close-path}])
|
||||
:strokes [{:stroke-color "#FF0000"
|
||||
:stroke-width 3
|
||||
:stroke-alignment :center
|
||||
:stroke-cap-start :triangle-arrow
|
||||
:stroke-cap-end :triangle-arrow}]})
|
||||
padding (gsb/calculate-padding shape)]
|
||||
;; Closed path: marker caps don't apply, so padding stays small
|
||||
(t/is (<= (:horizontal padding) 5)
|
||||
"Closed path should not have extra marker padding"))))
|
||||
|
||||
(t/deftest update-stroke-width-test
|
||||
(t/testing "Scale a stroke by 2"
|
||||
(let [stroke {:stroke-width 4 :stroke-color "#000"}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user