From 3d6c9032739b442a3e6e2244f10e4d835579909a Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 21 Jan 2022 14:48:33 +0100 Subject: [PATCH] :zap: Improve path rendering performance. --- common/src/app/common/geom/shapes/path.cljc | 19 +-- .../app/common/geom/shapes/transforms.cljc | 13 +- common/src/app/common/path/commands.cljc | 4 +- frontend/src/app/main/ui/shapes/path.cljs | 23 ++- frontend/src/app/util/path/format.cljs | 144 +++++++++--------- 5 files changed, 101 insertions(+), 102 deletions(-) diff --git a/common/src/app/common/geom/shapes/path.cljc b/common/src/app/common/geom/shapes/path.cljc index b9fc403ba..38d2e5680 100644 --- a/common/src/app/common/geom/shapes/path.cljc +++ b/common/src/app/common/geom/shapes/path.cljc @@ -376,19 +376,20 @@ set-tr (fn [params px py] - (assoc params - px (+ (get params px) dx) - py (+ (get params py) dy))) + (-> params + (update px + dx) + (update py + dy))) transform-params - (fn [params] + (fn [{:keys [x c1x c2x] :as params}] (cond-> params - (contains? params :x) (set-tr :x :y) - (contains? params :c1x) (set-tr :c1x :c1y) - (contains? params :c2x) (set-tr :c2x :c2y)))] + (some? x) (set-tr :x :y) + (some? c1x) (set-tr :c1x :c1y) + (some? c2x) (set-tr :c2x :c2y)))] - (->> content - (mapv #(d/update-when % :params transform-params))))) + (into [] + (map #(update % :params transform-params)) + content))) (defn transform-content [content transform] diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 56010f196..bfacc7298 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -42,9 +42,9 @@ (defn move "Move the shape relatively to its current position applying the provided delta." - [shape {dx :x dy :y}] - (let [dx (d/check-num dx) - dy (d/check-num dy) + [{:keys [type] :as shape} {dx :x dy :y}] + (let [dx (d/check-num dx) + dy (d/check-num dy) move-vec (gpt/point dx dy)] (-> shape @@ -52,11 +52,8 @@ (update :points move-points move-vec) (d/update-when :x + dx) (d/update-when :y + dy) - (cond-> (= :bool (:type shape)) - (update :bool-content gpa/move-content move-vec)) - (cond-> (= :path (:type shape)) - (update :content gpa/move-content move-vec))))) - + (cond-> (= :bool type) (update :bool-content gpa/move-content move-vec)) + (cond-> (= :path type) (update :content gpa/move-content move-vec))))) ;; --- Absolute Movement diff --git a/common/src/app/common/path/commands.cljc b/common/src/app/common/path/commands.cljc index d787c2457..a79b34676 100644 --- a/common/src/app/common/path/commands.cljc +++ b/common/src/app/common/path/commands.cljc @@ -17,8 +17,8 @@ (command->point command)))) ([command] - (when-not (nil? command) - (let [{{:keys [x y]} :params} command] + (when command + (let [{:keys [x y]} (:params command)] (gpt/point x y))))) diff --git a/frontend/src/app/main/ui/shapes/path.cljs b/frontend/src/app/main/ui/shapes/path.cljs index e62fb46e8..36155fc7e 100644 --- a/frontend/src/app/main/ui/shapes/path.cljs +++ b/frontend/src/app/main/ui/shapes/path.cljs @@ -20,21 +20,18 @@ [props] (let [shape (unchecked-get props "shape") content (:content shape) - pdata (mf/use-memo - (mf/deps content) - (fn [] - (try - (upf/format-path content) - (catch :default e - (log/error :hint "unexpected error on formating path" - :shape-name (:name shape) - :shape-id (:id shape) - :cause e) - "")))) + pdata (mf/with-memo [content] + (try + (upf/format-path content) + (catch :default e + (log/error :hint "unexpected error on formating path" + :shape-name (:name shape) + :shape-id (:id shape) + :cause e) + ""))) props (-> (attrs/extract-style-attrs shape) - (obj/merge! - #js {:d pdata}))] + (obj/set! "d" pdata))] [:& shape-custom-stroke {:shape shape} [:> :path props]])) diff --git a/frontend/src/app/util/path/format.cljs b/frontend/src/app/util/path/format.cljs index 80b1f9214..30c274547 100644 --- a/frontend/src/app/util/path/format.cljs +++ b/frontend/src/app/util/path/format.cljs @@ -8,88 +8,92 @@ (:require [app.common.path.commands :as upc] [app.common.path.subpaths :refer [pt=]] - [cuerdas.core :as str])) + [app.util.array :as arr])) -(defn command->param-list [command] - (let [params (:params command)] - (case (:command command) - (:move-to :line-to :smooth-quadratic-bezier-curve-to) - (str (:x params) "," - (:y params)) +(defn- join-params + ([a] + (js* "\"\"+~{}" a)) + ([a b] + (js* "\"\"+~{}+\",\"+~{}" a b)) + ([a b c] + (js* "\"\"+~{}+\",\"+~{}+\",\"+~{}" a b c)) + ([a b c d] + (js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d)) + ([a b c d e] + (js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d e)) + ([a b c d e f] + (js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d e f)) + ([a b c d e f g] + (js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d e f g))) - :close-path - "" +(defn- translate-params + [command {:keys [x y] :as params}] + (case command + (:move-to :line-to :smooth-quadratic-bezier-curve-to) + (join-params x y) - (:line-to-horizontal :line-to-vertical) - (str (:value params)) + :close-path + "" - :curve-to - (str (:c1x params) "," - (:c1y params) "," - (:c2x params) "," - (:c2y params) "," - (:x params) "," - (:y params)) + (:line-to-horizontal :line-to-vertical) + (:value params) - (:smooth-curve-to :quadratic-bezier-curve-to) - (str (:cx params) "," - (:cy params) "," - (:x params) "," - (:y params)) + :curve-to + (let [{:keys [c1x c1y c2x c2y]} params] + (join-params c1x c1y c2x c2y x y)) - :elliptical-arc - (str (:rx params) "," - (:ry params) "," - (:x-axis-rotation params) "," - (:large-arc-flag params) "," - (:sweep-flag params) "," - (:x params) "," - (:y params)) + (:smooth-curve-to :quadratic-bezier-curve-to) + (let [{:keys [cx cy]} params] + (join-params cx cy x y)) - ""))) + :elliptical-arc + (let [{:keys [rx ry x-axis-rotation large-arc-flag sweep-flag]} params] + (join-params rx ry x-axis-rotation large-arc-flag sweep-flag x y)) -(defn command->string [{:keys [command relative] :as entry}] - (let [command-str (case command - :move-to "M" - :close-path "Z" - :line-to "L" - :line-to-horizontal "H" - :line-to-vertical "V" - :curve-to "C" - :smooth-curve-to "S" - :quadratic-bezier-curve-to "Q" - :smooth-quadratic-bezier-curve-to "T" - :elliptical-arc "A" - "") - command-str (if relative (str/lower command-str) command-str) - param-list (command->param-list entry)] - (str command-str param-list))) + "")) + +(defn- translate-command + [cname] + (case cname + :move-to "M" + :close-path "Z" + :line-to "L" + :line-to-horizontal "H" + :line-to-vertical "V" + :curve-to "C" + :smooth-curve-to "S" + :quadratic-bezier-curve-to "Q" + :smooth-quadratic-bezier-curve-to "T" + :elliptical-arc "A" + "")) -(defn set-point - [command point] - (-> command - (assoc-in [:params :x] (:x point)) - (assoc-in [:params :y] (:y point)))) +(defn- command->string + [{:keys [command relative params]}] + (let [cmd (cond-> (translate-command command) + relative (.toLowerCase)) + prm (translate-params command params)] + (js* "~{} + ~{}" cmd prm))) + +(defn- set-point + [command {:keys [x y]}] + (update command :params assoc :x x :y y)) (defn format-path [content] - (with-out-str - (loop [last-move nil - current (first content) - content (rest content)] + (let [result (make-array (count content))] + (reduce (fn [last-move current] + (let [point (upc/command->point current) + current-move? (= :move-to (:command current)) + last-move (if current-move? point last-move)] - (when (some? current) - (let [point (upc/command->point current) - current-move? (= :move-to (:command current)) - last-move (if current-move? point last-move)] + (if (and (not current-move?) (pt= last-move point)) + (arr/conj! result (command->string (set-point current last-move))) + (arr/conj! result (command->string current))) - (if (and (not current-move?) (pt= last-move point)) - (print (command->string (set-point current last-move))) - (print (command->string current))) + (when (and (not current-move?) (pt= last-move point)) + (arr/conj! result "Z")) - (when (and (not current-move?) (pt= last-move point)) - (print "Z")) - - (recur last-move - (first content) - (rest content))))))) + last-move)) + nil + content) + (.join ^js result "")))