Paths improvements

This commit is contained in:
alonso.torres 2021-03-24 17:28:57 +01:00 committed by Andrey Antukh
parent 1773de88f5
commit 0756de25f8
2 changed files with 88 additions and 53 deletions

View file

@ -24,6 +24,12 @@
shape (unchecked-get props "shape") shape (unchecked-get props "shape")
color (unchecked-get props "color") color (unchecked-get props "color")
transform (gsh/transform-matrix shape) transform (gsh/transform-matrix shape)
path? (= :path (:type shape))
path-data
(mf/use-memo
(mf/deps shape)
#(when path? (ugp/content->path (:content shape))))
{:keys [id x y width height]} shape {:keys [id x y width height]} shape
outline-type (case (:type shape) outline-type (case (:type shape)
@ -45,7 +51,7 @@
:ry (/ height 2)} :ry (/ height 2)}
:path :path
{:d (ugp/content->path (:content shape)) {:d path-data
:transform nil} :transform nil}
{:x x {:x x

View file

@ -22,9 +22,8 @@
(let [handler-vector (gpt/to-vec point handler)] (let [handler-vector (gpt/to-vec point handler)]
(gpt/add point (gpt/negate handler-vector)))) (gpt/add point (gpt/negate handler-vector))))
;;;
(defn simplify (defn simplify
"Simplifies a drawing done with the pen tool"
([points] ([points]
(simplify points 0.1)) (simplify points 0.1))
([points tolerance] ([points tolerance]
@ -68,26 +67,41 @@
(cond-> result (cond-> result
(not (empty? current)) (conj current)))))) (not (empty? current)) (conj current))))))
(defn command->param-list [{:keys [command params]}] (defn command->param-list [command]
(case command (let [params (:params command)]
(:move-to :line-to :smooth-quadratic-bezier-curve-to) (case (:command command)
(let [{:keys [x y]} params] [x y]) (:move-to :line-to :smooth-quadratic-bezier-curve-to)
(str (:x params) ","
(:y params))
:close-path :close-path
[] ""
(:line-to-horizontal :line-to-vertical) (:line-to-horizontal :line-to-vertical)
(let [{:keys [value]} params] [value]) (str (:value params))
:curve-to :curve-to
(let [{:keys [c1x c1y c2x c2y x y]} params] [c1x c1y c2x c2y x y]) (str (:c1x params) ","
(:c1y params) ","
(:c2x params) ","
(:c2y params) ","
(:x params) ","
(:y params))
(:smooth-curve-to :quadratic-bezier-curve-to) (:smooth-curve-to :quadratic-bezier-curve-to)
(let [{:keys [cx cy x y]} params] [cx cy x y]) (str (:cx params) ","
(:cy params) ","
(:x params) ","
(:y params))
:elliptical-arc :elliptical-arc
(let [{:keys [rx ry x-axis-rotation large-arc-flag sweep-flag x y]} params] (str (:rx params) ","
[rx ry x-axis-rotation large-arc-flag sweep-flag x y]))) (:ry params) ","
(:x-axis-rotation params) ","
(:large-arc-flag params) ","
(:sweep-flag params) ","
(:x params) ","
(:y params)))))
;; Path specification ;; Path specification
;; https://www.w3.org/TR/SVG11/paths.html ;; https://www.w3.org/TR/SVG11/paths.html
@ -97,10 +111,15 @@
(let [relative (str/starts-with? cmd "m") (let [relative (str/starts-with? cmd "m")
param-list (extract-params cmd [[:x :number] param-list (extract-params cmd [[:x :number]
[:y :number]])] [:y :number]])]
(for [params param-list]
{:command :move-to (d/concat [{:command :move-to
:relative relative :relative relative
:params params}))) :params (first param-list)}]
(for [params (rest param-list)]
{:command :line-to
:relative relative
:params params}))))
(defmethod parse-command "Z" [cmd] (defmethod parse-command "Z" [cmd]
[{:command :close-path}]) [{:command :close-path}])
@ -156,7 +175,7 @@
:params params}))) :params params})))
(defmethod parse-command "Q" [cmd] (defmethod parse-command "Q" [cmd]
(let [relative (str/starts-with? cmd "s") (let [relative (str/starts-with? cmd "q")
param-list (extract-params cmd [[:cx :number] param-list (extract-params cmd [[:cx :number]
[:cy :number] [:cy :number]
[:x :number] [:x :number]
@ -203,7 +222,7 @@
:elliptical-arc "A") :elliptical-arc "A")
command-str (if relative (str/lower command-str) command-str) command-str (if relative (str/lower command-str) command-str)
param-list (command->param-list entry)] param-list (command->param-list entry)]
(str/fmt "%s%s" command-str (str/join " " param-list)))) (str command-str param-list)))
(defn cmd-pos [prev-pos {:keys [relative params]}] (defn cmd-pos [prev-pos {:keys [relative params]}]
(let [{:keys [x y] :or {x (:x prev-pos) y (:y prev-pos)}} params] (let [{:keys [x y] :or {x (:x prev-pos) y (:y prev-pos)}} params]
@ -253,31 +272,35 @@
"Removes some commands and convert relative to absolute coordinates" "Removes some commands and convert relative to absolute coordinates"
[commands] [commands]
(let [simplify-command (let [simplify-command
;; prev-cc : previous command control point for cubic beziers ;; prev-pos : previous position for the current path. Necesary for relative commands
;; prev-qc : previous command control point for quadratic curves ;; prev-start : previous move-to necesary for Z commands
(fn [[pos result prev-cc prev-qc] [command prev]] ;; prev-cc : previous command control point for cubic beziers
(let [command ;; prev-qc : previous command control point for quadratic curves
(fn [[result prev-pos prev-start prev-cc prev-qc] [command prev]]
(let [command (assoc command :prev-pos prev-pos)
command
(cond-> command (cond-> command
(:relative command) (:relative command)
(-> (assoc :relative false) (-> (assoc :relative false)
(d/update-in-when [:params :c1x] + (:x pos)) (d/update-in-when [:params :c1x] + (:x prev-pos))
(d/update-in-when [:params :c1y] + (:y pos)) (d/update-in-when [:params :c1y] + (:y prev-pos))
(d/update-in-when [:params :c2x] + (:x pos)) (d/update-in-when [:params :c2x] + (:x prev-pos))
(d/update-in-when [:params :c2y] + (:y pos)) (d/update-in-when [:params :c2y] + (:y prev-pos))
(d/update-in-when [:params :cx] + (:x pos)) (d/update-in-when [:params :cx] + (:x prev-pos))
(d/update-in-when [:params :cy] + (:y pos)) (d/update-in-when [:params :cy] + (:y prev-pos))
(d/update-in-when [:params :x] + (:x pos)) (d/update-in-when [:params :x] + (:x prev-pos))
(d/update-in-when [:params :y] + (:y pos)) (d/update-in-when [:params :y] + (:y prev-pos))
(cond-> (cond->
(= :line-to-horizontal (:command command)) (= :line-to-horizontal (:command command))
(d/update-in-when [:params :value] + (:x pos)) (d/update-in-when [:params :value] + (:x prev-pos))
(= :line-to-vertical (:command command)) (= :line-to-vertical (:command command))
(d/update-in-when [:params :value] + (:y pos))))) (d/update-in-when [:params :value] + (:y prev-pos)))))
params (:params command) params (:params command)
orig-command command orig-command command
@ -288,33 +311,33 @@
(-> (assoc :command :line-to) (-> (assoc :command :line-to)
(update :params dissoc :value) (update :params dissoc :value)
(assoc-in [:params :x] (:value params)) (assoc-in [:params :x] (:value params))
(assoc-in [:params :y] (:y pos))) (assoc-in [:params :y] (:y prev-pos)))
(= :line-to-vertical (:command command)) (= :line-to-vertical (:command command))
(-> (assoc :command :line-to) (-> (assoc :command :line-to)
(update :params dissoc :value) (update :params dissoc :value)
(assoc-in [:params :y] (:value params)) (assoc-in [:params :y] (:value params))
(assoc-in [:params :x] (:x pos))) (assoc-in [:params :x] (:x prev-pos)))
(= :smooth-curve-to (:command command)) (= :smooth-curve-to (:command command))
(-> (assoc :command :curve-to) (-> (assoc :command :curve-to)
(update :params dissoc :cx :cy) (update :params dissoc :cx :cy)
(update :params merge (smooth->curve command pos prev-cc))) (update :params merge (smooth->curve command prev-pos prev-cc)))
(= :quadratic-bezier-curve-to (:command command)) (= :quadratic-bezier-curve-to (:command command))
(-> (assoc :command :curve-to) (-> (assoc :command :curve-to)
(update :params dissoc :cx :cy) (update :params dissoc :cx :cy)
(update :params merge (quadratic->curve pos (gpt/point params) (gpt/point (:cx params) (:cy params))))) (update :params merge (quadratic->curve prev-pos (gpt/point params) (gpt/point (:cx params) (:cy params)))))
(= :smooth-quadratic-bezier-curve-to (:command command)) (= :smooth-quadratic-bezier-curve-to (:command command))
(-> (assoc :command :curve-to) (-> (assoc :command :curve-to)
(update :params merge (quadratic->curve pos (gpt/point params) (calculate-opposite-handler pos prev-qc))))) (update :params merge (quadratic->curve prev-pos (gpt/point params) (calculate-opposite-handler prev-pos prev-qc)))))
result (if (= :elliptical-arc (:command command)) result (if (= :elliptical-arc (:command command))
(d/concat result (arc->beziers pos command)) (d/concat result (arc->beziers prev-pos command))
(conj result command)) (conj result command))
prev-cc (case (:command orig-command) next-cc (case (:command orig-command)
:smooth-curve-to :smooth-curve-to
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy])) (gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
@ -326,23 +349,29 @@
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y]))) (gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
prev-qc (case (:command orig-command) next-qc (case (:command orig-command)
:quadratic-bezier-curve-to :quadratic-bezier-curve-to
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy])) (gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
:smooth-quadratic-bezier-curve-to :smooth-quadratic-bezier-curve-to
(calculate-opposite-handler pos prev-qc) (calculate-opposite-handler prev-pos prev-qc)
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))] (gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
[(cmd-pos pos command) result prev-cc prev-qc]))
next-pos (if (= :close-path (:command command))
prev-start
(cmd-pos prev-pos command))
next-start (if (= :move-to (:command command)) next-pos prev-start)]
[result next-pos next-start next-cc next-qc]))
start (first commands) start (first commands)
start-pos (gpt/point (:params start))] start-pos (gpt/point (:params start))]
(->> (map vector (rest commands) commands) (->> (map vector (rest commands) commands)
(reduce simplify-command [start-pos [start] start-pos start-pos]) (reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
(second)))) (first))))
(defn path->content [string] (defn path->content [string]
(let [clean-string (-> string (let [clean-string (-> string
@ -357,7 +386,7 @@
(defn content->path [content] (defn content->path [content]
(->> content (->> content
(map command->string) (mapv command->string)
(str/join ""))) (str/join "")))
(defn make-curve-params (defn make-curve-params