penpot/frontend/src/app/util/path/format.cljs
2024-07-23 16:55:32 +02:00

138 lines
4.0 KiB
Clojure

;; 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) KALEIDOS INC
(ns app.util.path.format
(:require
[app.common.svg.path.command :as upc]
[app.common.svg.path.subpath :refer [pt=]]
[app.util.array :as arr]))
;; TODO: move to common
(def path-precision 3)
(defn- join-params
([a]
(js* "\"\"+~{}"
(.toFixed a path-precision)))
([a b]
(js* "\"\"+~{}+\",\"+~{}"
(.toFixed a path-precision)
(.toFixed b path-precision)))
([a b c]
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}"
(.toFixed a path-precision)
(.toFixed b path-precision)
(.toFixed c path-precision)))
([a b c d]
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
(.toFixed a path-precision)
(.toFixed b path-precision)
(.toFixed c path-precision)
(.toFixed d path-precision)))
([a b c d e]
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
(.toFixed a path-precision)
(.toFixed b path-precision)
(.toFixed c path-precision)
(.toFixed d path-precision)
(.toFixed e path-precision)))
([a b c d e f]
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
(.toFixed a path-precision)
(.toFixed b path-precision)
(.toFixed c path-precision)
(.toFixed d path-precision)
(.toFixed e path-precision)
(.toFixed f path-precision)))
([a b c d e f g]
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
(.toFixed a path-precision)
(.toFixed b path-precision)
(.toFixed c path-precision)
(.toFixed d path-precision)
(.toFixed e path-precision)
(.toFixed f path-precision)
(.toFixed g path-precision))))
(defn- translate-params
[command {:keys [x y] :as params}]
(case command
(:move-to :line-to :smooth-quadratic-bezier-curve-to)
(join-params x y)
:close-path
""
(:line-to-horizontal :line-to-vertical)
(:value params)
:curve-to
(let [{:keys [c1x c1y c2x c2y]} params]
(join-params (or c1x x) (or c1y y) (or c2x x) (or c2y y) x y))
(: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- 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- 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]
(try
(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)]
(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)))
(when (and (not current-move?) (pt= last-move point))
(arr/conj! result "Z"))
last-move))
nil
content)
(.join ^js result ""))
(catch :default err
(.error js/console err)
nil)))