From 74447442b8fa5a6a0a67b1efc618026ee9de2622 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Dec 2023 00:28:49 +0100 Subject: [PATCH] :sparkles: Add several improvements to svg path parser tests And properly reorganize legacy implementations --- .../app/common/svg/path}/arc_to_bezier.js | 13 +- .../app/common/svg/path/legacy_parser1.cljs | 325 ++++++++++++++++++ .../path/{legacy.cljc => legacy_parser2.cljc} | 28 +- common/test/common_tests/svg_path_test.cljc | 190 +++++++++- 4 files changed, 519 insertions(+), 37 deletions(-) rename common/{test/common_tests => src/app/common/svg/path}/arc_to_bezier.js (93%) create mode 100644 common/src/app/common/svg/path/legacy_parser1.cljs rename common/src/app/common/svg/path/{legacy.cljc => legacy_parser2.cljc} (97%) diff --git a/common/test/common_tests/arc_to_bezier.js b/common/src/app/common/svg/path/arc_to_bezier.js similarity index 93% rename from common/test/common_tests/arc_to_bezier.js rename to common/src/app/common/svg/path/arc_to_bezier.js index bc2c1a843..39dc8d447 100644 --- a/common/test/common_tests/arc_to_bezier.js +++ b/common/src/app/common/svg/path/arc_to_bezier.js @@ -2,7 +2,8 @@ * Arc to Bezier curves transformer * * Is a modified and google closure compatible version of the a2c - * functions by https://github.com/fontello/svgpath + * functions by https://github.com/fontello/svgpath used as reference + * implementation for tests * * @author KALEIDOS INC * @license MIT License @@ -10,11 +11,11 @@ "use strict"; -goog.provide("common_tests.arc_to_bezier"); +goog.provide("app.common.svg.path.arc_to_bezier"); // https://raw.githubusercontent.com/fontello/svgpath/master/lib/a2c.js goog.scope(function() { - const self = common_tests.arc_to_bezier; + const self = app.common.svg.path.arc_to_bezier; var TAU = Math.PI * 2; @@ -123,7 +124,7 @@ goog.scope(function() { return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ]; } - function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) { + function calculate_beziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) { var sin_phi = Math.sin(phi * TAU / 360); var cos_phi = Math.cos(phi * TAU / 360); @@ -132,6 +133,8 @@ goog.scope(function() { var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2; var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2; + // console.log("L", x1p, y1p) + if (x1p === 0 && y1p === 0) { // we're asked to draw line to itself return []; @@ -204,5 +207,5 @@ goog.scope(function() { }); } - self.a2c = a2c; + self.calculateBeziers = calculate_beziers; }); diff --git a/common/src/app/common/svg/path/legacy_parser1.cljs b/common/src/app/common/svg/path/legacy_parser1.cljs new file mode 100644 index 000000000..70acc5824 --- /dev/null +++ b/common/src/app/common/svg/path/legacy_parser1.cljs @@ -0,0 +1,325 @@ +;; 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.common.svg.path.legacy-parser1 + "The first SVG Path parser implementation. + + Written in a mix of CLJS and JS code and used in production until + 1.19, used mainly for tests." + (:require + [app.common.data :as d] + [app.common.geom.point :as gpt] + [app.common.geom.shapes.path :as upg] + [app.common.svg :as csvg] + [app.common.svg.path.arc-to-bezier :as a2b] + [app.common.svg.path.command :as upc] + [cuerdas.core :as str])) + +(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*") + +;; Matches numbers for path values allows values like... -.01, 10, +12.22 +;; 0 and 1 are special because can refer to flags +(def num-regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?") + +(def flag-regex #"[01]") + +(defn extract-params [cmd-str extract-commands] + (loop [result [] + extract-idx 0 + current {} + remain (-> cmd-str (subs 1) (str/trim))] + + (let [[param type] (nth extract-commands extract-idx) + regex (case type + :flag flag-regex + #_:number num-regex) + match (re-find regex remain)] + + (if match + (let [value (-> match first csvg/fix-dot-number d/read-string) + remain (str/replace-first remain regex "") + current (assoc current param value) + extract-idx (inc extract-idx) + [result current extract-idx] + (if (>= extract-idx (count extract-commands)) + [(conj result current) {} 0] + [result current extract-idx])] + (recur result + extract-idx + current + remain)) + (cond-> result + (seq current) (conj current)))))) + +;; Path specification +;; https://www.w3.org/TR/SVG11/paths.html +(defmulti parse-command (comp str/upper first)) + +(defmethod parse-command "M" [cmd] + (let [relative (str/starts-with? cmd "m") + param-list (extract-params cmd [[:x :number] + [:y :number]])] + + (into [{:command :move-to + :relative relative + :params (first param-list)}] + + (for [params (rest param-list)] + {:command :line-to + :relative relative + :params params})))) + +(defmethod parse-command "Z" [_] + [{:command :close-path}]) + +(defmethod parse-command "L" [cmd] + (let [relative (str/starts-with? cmd "l") + param-list (extract-params cmd [[:x :number] + [:y :number]])] + (for [params param-list] + {:command :line-to + :relative relative + :params params}))) + +(defmethod parse-command "H" [cmd] + (let [relative (str/starts-with? cmd "h") + param-list (extract-params cmd [[:value :number]])] + (for [params param-list] + {:command :line-to-horizontal + :relative relative + :params params}))) + +(defmethod parse-command "V" [cmd] + (let [relative (str/starts-with? cmd "v") + param-list (extract-params cmd [[:value :number]])] + (for [params param-list] + {:command :line-to-vertical + :relative relative + :params params}))) + +(defmethod parse-command "C" [cmd] + (let [relative (str/starts-with? cmd "c") + param-list (extract-params cmd [[:c1x :number] + [:c1y :number] + [:c2x :number] + [:c2y :number] + [:x :number] + [:y :number]]) + ] + (for [params param-list] + {:command :curve-to + :relative relative + :params params}))) + +(defmethod parse-command "S" [cmd] + (let [relative (str/starts-with? cmd "s") + param-list (extract-params cmd [[:cx :number] + [:cy :number] + [:x :number] + [:y :number]])] + (for [params param-list] + {:command :smooth-curve-to + :relative relative + :params params}))) + +(defmethod parse-command "Q" [cmd] + (let [relative (str/starts-with? cmd "q") + param-list (extract-params cmd [[:cx :number] + [:cy :number] + [:x :number] + [:y :number]])] + (for [params param-list] + {:command :quadratic-bezier-curve-to + :relative relative + :params params}))) + +(defmethod parse-command "T" [cmd] + (let [relative (str/starts-with? cmd "t") + param-list (extract-params cmd [[:x :number] + [:y :number]])] + (for [params param-list] + {:command :smooth-quadratic-bezier-curve-to + :relative relative + :params params}))) + +(defmethod parse-command "A" [cmd] + (let [relative (str/starts-with? cmd "a") + param-list (extract-params cmd [[:rx :number] + [:ry :number] + [:x-axis-rotation :number] + [:large-arc-flag :flag] + [:sweep-flag :flag] + [:x :number] + [:y :number]])] + (for [params param-list] + {:command :elliptical-arc + :relative relative + :params params}))) + +(defn smooth->curve + [{:keys [params]} pos handler] + (let [{c1x :x c1y :y} (upg/calculate-opposite-handler pos handler)] + {:c1x c1x + :c1y c1y + :c2x (:cx params) + :c2y (:cy params)})) + +(defn quadratic->curve + [sp ep cp] + (let [cp1 (-> (gpt/to-vec sp cp) + (gpt/scale (/ 2 3)) + (gpt/add sp)) + + cp2 (-> (gpt/to-vec ep cp) + (gpt/scale (/ 2 3)) + (gpt/add ep))] + + {:c1x (:x cp1) + :c1y (:y cp1) + :c2x (:x cp2) + :c2y (:y cp2)})) + +(defn arc->beziers* + [from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation] + (a2b/calculateBeziers from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation)) + +(defn arc->beziers [from-p command] + (let [to-command + (fn [[_ _ c1x c1y c2x c2y x y]] + {:command :curve-to + :relative (:relative command) + :params {:c1x c1x :c1y c1y + :c2x c2x :c2y c2y + :x x :y y}}) + + {from-x :x from-y :y} from-p + {:keys [rx ry x-axis-rotation large-arc-flag sweep-flag x y]} (:params command) + result (arc->beziers* from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation)] + (mapv to-command result))) + +(defn simplify-commands + "Removes some commands and convert relative to absolute coordinates" + [commands] + (let [simplify-command + ;; prev-pos : previous position for the current path. Necessary for relative commands + ;; prev-start : previous move-to necessary for Z commands + ;; prev-cc : previous command control point for cubic beziers + ;; 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 + (:relative command) + (-> (assoc :relative false) + (d/update-in-when [:params :c1x] + (:x prev-pos)) + (d/update-in-when [:params :c1y] + (:y prev-pos)) + + (d/update-in-when [:params :c2x] + (:x prev-pos)) + (d/update-in-when [:params :c2y] + (:y prev-pos)) + + (d/update-in-when [:params :cx] + (:x prev-pos)) + (d/update-in-when [:params :cy] + (:y prev-pos)) + + (d/update-in-when [:params :x] + (:x prev-pos)) + (d/update-in-when [:params :y] + (:y prev-pos)) + + (cond-> + (= :line-to-horizontal (:command command)) + (d/update-in-when [:params :value] + (:x prev-pos)) + + (= :line-to-vertical (:command command)) + (d/update-in-when [:params :value] + (:y prev-pos))))) + + params (:params command) + orig-command command + + command + (cond-> command + (= :line-to-horizontal (:command command)) + (-> (assoc :command :line-to) + (update :params dissoc :value) + (assoc-in [:params :x] (:value params)) + (assoc-in [:params :y] (:y prev-pos))) + + (= :line-to-vertical (:command command)) + (-> (assoc :command :line-to) + (update :params dissoc :value) + (assoc-in [:params :y] (:value params)) + (assoc-in [:params :x] (:x prev-pos))) + + (= :smooth-curve-to (:command command)) + (-> (assoc :command :curve-to) + (update :params dissoc :cx :cy) + (update :params merge (smooth->curve command prev-pos prev-cc))) + + (= :quadratic-bezier-curve-to (:command command)) + (-> (assoc :command :curve-to) + (update :params dissoc :cx :cy) + (update :params merge (quadratic->curve prev-pos (gpt/point params) (gpt/point (:cx params) (:cy params))))) + + (= :smooth-quadratic-bezier-curve-to (:command command)) + (-> (assoc :command :curve-to) + (update :params merge (quadratic->curve prev-pos (gpt/point params) (upg/calculate-opposite-handler prev-pos prev-qc))))) + + result (if (= :elliptical-arc (:command command)) + (into result (arc->beziers prev-pos command)) + (conj result command)) + + next-cc (case (:command orig-command) + :smooth-curve-to + (gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy])) + + :curve-to + (gpt/point (get-in orig-command [:params :c2x]) (get-in orig-command [:params :c2y])) + + (:line-to-horizontal :line-to-vertical) + (gpt/point (get-in command [:params :x]) (get-in command [:params :y])) + + (gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y]))) + + next-qc (case (:command orig-command) + :quadratic-bezier-curve-to + (gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy])) + + :smooth-quadratic-bezier-curve-to + (upg/calculate-opposite-handler prev-pos prev-qc) + + (gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y]))) + + next-pos (if (= :close-path (:command command)) + prev-start + (upc/command->point 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 (cond-> start + (:relative start) + (assoc :relative false)) + + start-pos (gpt/point (:params start))] + + (->> (map vector (rest commands) commands) + (reduce simplify-command [[start] start-pos start-pos start-pos start-pos]) + (first)))) + +(defn parse [path-str] + (if (empty? path-str) + path-str + (let [clean-path-str + (-> path-str + (str/trim) + ;; Change "commas" for spaces + (str/replace #"," " ") + ;; Remove all consecutive spaces + (str/replace #"\s+" " ")) + commands (re-seq commands-regex clean-path-str)] + (-> (mapcat parse-command commands) + (simplify-commands))))) + diff --git a/common/src/app/common/svg/path/legacy.cljc b/common/src/app/common/svg/path/legacy_parser2.cljc similarity index 97% rename from common/src/app/common/svg/path/legacy.cljc rename to common/src/app/common/svg/path/legacy_parser2.cljc index 594905906..fab3f8102 100644 --- a/common/src/app/common/svg/path/legacy.cljc +++ b/common/src/app/common/svg/path/legacy_parser2.cljc @@ -4,9 +4,11 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.common.svg.path.legacy - "The first svg path parser implementation in pure clojure, used as reference impl - and for tests." +(ns app.common.svg.path.legacy-parser2 + "The second SVG Path parser implementation. + + Written in crossplatform CLJC code. Used meanwhile a hight + performance parser is developed in the 1.20 version." (:require [app.common.data :as d] [app.common.geom.point :as gpt] @@ -16,7 +18,6 @@ [app.common.svg.path.command :as upc] [cuerdas.core :as str])) - (def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*") (def regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?") @@ -296,10 +297,10 @@ y1p (+ (/ (* (- sin-phi) (- x1 x2)) 2) (/ (* cos-phi (- y1 y2)) 2))] - (if (or (zero? x1p) - (zero? y1p) - (zero? rx) - (zero? ry)) + (if (or (and (zero? x1p) + (zero? y1p)) + (and (zero? rx) + (zero? ry))) [] (let [ rx (mth/abs rx) @@ -462,19 +463,10 @@ (reduce simplify-command [[start] start-pos start-pos start-pos start-pos]) (first)))) - (defn parse [path-str] (if (empty? path-str) path-str (let [commands (re-seq commands-regex path-str)] (->> (mapcat parse-command commands) - (simplify-commands) - (map (fn [segment] - ;; (prn "LEGACY:" segment) - segment)))))) - - - - - + (simplify-commands))))) diff --git a/common/test/common_tests/svg_path_test.cljc b/common/test/common_tests/svg_path_test.cljc index 3fb89c432..19140f91e 100644 --- a/common/test/common_tests/svg_path_test.cljc +++ b/common/test/common_tests/svg_path_test.cljc @@ -10,9 +10,9 @@ [app.common.pprint :as pp] [app.common.math :as mth] [app.common.svg.path :as svg.path] - [app.common.svg.path.legacy :as svg.path.legacy] + [app.common.svg.path.legacy-parser2 :as svg.path.legacy2] [clojure.test :as t] - #?(:cljs [common-tests.arc-to-bezier :as impl]))) + #?(:cljs [app.common.svg.path.legacy-parser2 :as svg.path.legacy1]))) (t/deftest parse-test-1 (let [data (str "m -994.563 4564.1423 149.3086 -52.8821 30.1828 " @@ -23,14 +23,25 @@ result1 (->> (svg.path/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %))))) - result2 (->> (svg.path.legacy/parse data) + result2 (->> (svg.path.legacy2/parse data) (mapv (fn [entry] - (update entry :params #(into (sorted-map) %)))))] + (update entry :params #(into (sorted-map) %))))) + + result3 #?(:cljs (->> (svg.path.legacy1/parse data) + (mapv (fn [entry] + (update entry :params #(into (sorted-map) %))))) + :clj nil)] (t/is (= 15 (count result1) (count result2))) + + #?(:cljs + (t/is (= 15 + (count result1) + (count result3)))) + (dotimes [i (count result1)] (let [item1 (nth result1 i) item2 (nth result2 i)] @@ -40,6 +51,14 @@ (t/is (= (:params item1) (:params item2))) + #?(:cljs + (let [item3 (nth result3 i)] + (t/is (= (:command item1) + (:command item3))) + (t/is (= (:params item1) + (:params item3))))) + + #_(println "------------------------") #_(pp/pprint (dissoc item1 :relative)) #_(pp/pprint (dissoc item2 :prev-pos :relative)))))) @@ -92,7 +111,7 @@ result1 (->> (svg.path/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %))))) - result2 (->> (svg.path.legacy/parse data) + result2 (->> (svg.path.legacy2/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %)))))] @@ -108,7 +127,6 @@ (t/is (= (:command item1) (:command item2))) - ;; (println "================" (:command item1)) ;; (pp/pprint (:params item1)) ;; (println "---------") @@ -124,7 +142,7 @@ result1 (->> (svg.path/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %))))) - result2 (->> (svg.path.legacy/parse data) + result2 (->> (svg.path.legacy2/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %)))))] @@ -203,7 +221,7 @@ result1 (->> (svg.path/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %))))) - result2 (->> (svg.path.legacy/parse data) + result2 (->> (svg.path.legacy2/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %)))))] @@ -256,7 +274,7 @@ result1 (->> (svg.path/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %))))) - result2 (->> (svg.path.legacy/parse data) + result2 (->> (svg.path.legacy2/parse data) (mapv (fn [entry] (update entry :params #(into (sorted-map) %)))))] @@ -279,6 +297,61 @@ (t/is (mth/close? v (get-in item2 [:params k]) 0.000000001)) ))))) +(t/deftest parse-test-6 + (let [data (str "M3.078 3.548v16.9a.5.5 0 0 0 1 0v-16.9a.5.5 0 0 0-1 0ZM18.422 11.5" + "H7.582a2.5 2.5 0 0 1-2.5-2.5V6.565a2.5 2.5 0 0 1 2.5-2.5" + "h10.84a2.5 2.5 0 0 1 2.5 2.5V9a2.5 2.5 0 0 1-2.5 2.5Z" + "M7.582 5.065a1.5 1.5 0 0 0-1.5 1.5V9a1.5 1.5 0 0 0 1.5 1.5" + "h10.84a1.5 1.5 0 0 0 1.5-1.5V6.565a1.5 1.5 0 0 0-1.5-1.5Z" + "M13.451 19.938H7.582a2.5 2.5 0 0 1-2.5-2.5V15" + "a2.5 2.5 0 0 1 2.5-2.5h5.869a2.5 2.5 0 0 1 2.5 2.5v2.436" + "a2.5 2.5 0 0 1-2.5 2.502ZM7.582 13.5a1.5 1.5 0 0 0-1.5 1.5v2.436" + "a1.5 1.5 0 0 0 1.5 1.5h5.869a1.5 1.5 0 0 0 1.5-1.5V15" + "a1.5 1.5 0 0 0-1.5-1.5Z") + + result1 (->> (svg.path/parse data) + (mapv (fn [entry] + (update entry :params #(into (sorted-map) %))))) + result2 (->> (svg.path.legacy2/parse data) + (mapv (fn [entry] + (update entry :params #(into (sorted-map) %)))))] + + (t/is (= 47 + (count result1) + (count result2))) + + ;; (pp/pprint result1 {:length 100}) + ;; (pp/pprint result2 {:length 50}) + + (dotimes [i (count result1)] + (let [item1 (nth result1 i) + item2 (nth result2 i) + ] + + (t/is (= (:command item1) + (:command item2))) + + (doseq [[k v] (:params item1)] + (t/is (mth/close? v (get-in item2 [:params k]) 0.000000001)) + ))) + + #?(:cljs + (let [result3 (svg.path.legacy1/parse data)] + (t/is (= 47 + (count result1) + (count result3))) + + (dotimes [i (count result1)] + (let [item1 (nth result1 i) + item3 (nth result2 i)] + + (t/is (= (:command item1) + (:command item3))) + + (t/is (= (:params item1) + (:params item3))))))))) + + (t/deftest arc-to-bezier-1 (let [expected1 [-1.6697754290362354e-13 -5.258016244624741e-13 @@ -316,7 +389,7 @@ (nth expected2 (+ i 2)) 0.0000000001)))) - (let [[result1 result2 :as total] (svg.path.legacy/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)] + (let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)] (t/is (= (count total) 2)) (dotimes [i (count result1)] @@ -327,7 +400,96 @@ (dotimes [i (count result2)] (t/is (mth/close? (nth result2 i) (nth expected2 i) - 0.000000000001)))))) + 0.000000000001)))) + + #?(:cljs + (let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)] + (t/is (= (count total) 2)) + + (dotimes [i (count result1)] + (t/is (mth/close? (nth result1 i) + (nth expected1 i) + 0.000000000001))) + + (dotimes [i (count result2)] + (t/is (mth/close? (nth result2 i) + (nth expected2 i) + 0.000000000001))))) + + )) + +(t/deftest arc-to-bezier-2 + (let [expected1 [3.0779999999999994, + 20.448, + 3.0780000082296834, + 20.724142369096132, + 3.3018576309038683, + 20.94799998509884, + 3.5779999999999994, + 20.94799998509884] + + expected2 [3.5779999999999994, + 20.94799998509884, + 3.854142369096131, + 20.94799998509884, + 4.077999991770315, + 20.724142369096132, + 4.077999999999999, + 20.448]] + + (let [[result1 result2 :as total] (->> (svg.path/arc->beziers 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0) + (mapv (fn [segment] + (vec (.-params segment)))))] + (t/is (= (count total) 2)) + ;; (println "================" 11111111) + ;; (pp/pprint expected1 {:width 50}) + ;; (println "------------") + ;; (pp/pprint result1 {:width 50}) + + (dotimes [i (count result1)] + (t/is (mth/close? (nth result1 i) + (nth expected1 (+ i 2)) + 0.0000000001))) + + (dotimes [i (count result2)] + (t/is (mth/close? (nth result2 i) + (nth expected2 (+ i 2)) + 0.0000000001)))) + + (let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)] + (t/is (= (count total) 2)) + + ;; (println "================" 11111111) + ;; (pp/pprint expected1 {:width 50}) + ;; (println "------------") + ;; (pp/pprint (vec result1) {:width 50}) + + (dotimes [i (count result1)] + (t/is (mth/close? (nth result1 i) + (nth expected1 i) + 0.000000000001))) + + (dotimes [i (count result2)] + (t/is (mth/close? (nth result2 i) + (nth expected2 i) + 0.000000000001)))) + + #?(:cljs + (let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)] + (t/is (= (count total) 2)) + + (dotimes [i (count result1)] + (t/is (mth/close? (nth result1 i) + (nth expected1 i) + 0.000000000001))) + + (dotimes [i (count result2)] + (t/is (mth/close? (nth result2 i) + (nth expected2 i) + 0.000000000001))))) + + )) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -357,14 +519,14 @@ "59.9137 -301.293 -1.0595 -51.375 25.7186 -261.0492 -7.706 ") pattern [[:x :number] [:y :number]]] - (t/is (= expected (svg.path.legacy/extract-params cmdstr pattern))))) + (t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern))))) (t/deftest extract-params-legacy-2 (let [expected [{:x -994.563, :y 4564.1423 :r 0}] cmdstr (str "m -994.563 4564.1423 0") pattern [[:x :number] [:y :number] [:r :flag]]] - (t/is (= expected (svg.path.legacy/extract-params cmdstr pattern))))) + (t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern))))) (t/deftest extract-params-legacy-3 (let [cmdstr (str "a1.42 1.42 0 00-1.415-1.416 1.42 1.42 0 00-1.416 1.416 " @@ -382,7 +544,7 @@ [:sweep-flag :flag] [:x :number] [:y :number]] - result (svg.path.legacy/extract-params cmdstr pattern)] + result (svg.path.legacy2/extract-params cmdstr pattern)] (t/is (= (nth result 0) (nth expected 0)))