mirror of
https://github.com/penpot/penpot.git
synced 2025-07-31 20:08:21 +02:00
✨ Import paths as native shapes
This commit is contained in:
parent
741d67c30b
commit
19febde547
28 changed files with 921 additions and 209 deletions
203
frontend/src/app/util/a2c.js
Normal file
203
frontend/src/app/util/a2c.js
Normal file
|
@ -0,0 +1,203 @@
|
|||
/**
|
||||
* Arc to Bezier curves transformer
|
||||
*
|
||||
* Is a modified and google closure complatible version of the a2c
|
||||
* functions by https://github.com/fontello/svgpath
|
||||
*
|
||||
* @author UXBOX Labs SL
|
||||
* @license MIT License <https://opensource.org/licenses/MIT>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
goog.provide("app.util.a2c");
|
||||
|
||||
// https://raw.githubusercontent.com/fontello/svgpath/master/lib/a2c.js
|
||||
goog.scope(function() {
|
||||
const self = app.util.a2c;
|
||||
|
||||
var TAU = Math.PI * 2;
|
||||
|
||||
/* eslint-disable space-infix-ops */
|
||||
|
||||
// Calculate an angle between two unit vectors
|
||||
//
|
||||
// Since we measure angle between radii of circular arcs,
|
||||
// we can use simplified math (without length normalization)
|
||||
//
|
||||
function unit_vector_angle(ux, uy, vx, vy) {
|
||||
var sign = (ux * vy - uy * vx < 0) ? -1 : 1;
|
||||
var dot = ux * vx + uy * vy;
|
||||
|
||||
// Add this to work with arbitrary vectors:
|
||||
// dot /= Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy);
|
||||
|
||||
// rounding errors, e.g. -1.0000000000000002 can screw up this
|
||||
if (dot > 1.0) { dot = 1.0; }
|
||||
if (dot < -1.0) { dot = -1.0; }
|
||||
|
||||
return sign * Math.acos(dot);
|
||||
}
|
||||
|
||||
|
||||
// Convert from endpoint to center parameterization,
|
||||
// see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
||||
//
|
||||
// Return [cx, cy, theta1, delta_theta]
|
||||
//
|
||||
function get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi) {
|
||||
// Step 1.
|
||||
//
|
||||
// Moving an ellipse so origin will be the middlepoint between our two
|
||||
// points. After that, rotate it to line up ellipse axes with coordinate
|
||||
// axes.
|
||||
//
|
||||
var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
|
||||
var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
|
||||
|
||||
var rx_sq = rx * rx;
|
||||
var ry_sq = ry * ry;
|
||||
var x1p_sq = x1p * x1p;
|
||||
var y1p_sq = y1p * y1p;
|
||||
|
||||
// Step 2.
|
||||
//
|
||||
// Compute coordinates of the centre of this ellipse (cx', cy')
|
||||
// in the new coordinate system.
|
||||
//
|
||||
var radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq);
|
||||
|
||||
if (radicant < 0) {
|
||||
// due to rounding errors it might be e.g. -1.3877787807814457e-17
|
||||
radicant = 0;
|
||||
}
|
||||
|
||||
radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq);
|
||||
radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1);
|
||||
|
||||
var cxp = radicant * rx/ry * y1p;
|
||||
var cyp = radicant * -ry/rx * x1p;
|
||||
|
||||
// Step 3.
|
||||
//
|
||||
// Transform back to get centre coordinates (cx, cy) in the original
|
||||
// coordinate system.
|
||||
//
|
||||
var cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2;
|
||||
var cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2;
|
||||
|
||||
// Step 4.
|
||||
//
|
||||
// Compute angles (theta1, delta_theta).
|
||||
//
|
||||
var v1x = (x1p - cxp) / rx;
|
||||
var v1y = (y1p - cyp) / ry;
|
||||
var v2x = (-x1p - cxp) / rx;
|
||||
var v2y = (-y1p - cyp) / ry;
|
||||
|
||||
var theta1 = unit_vector_angle(1, 0, v1x, v1y);
|
||||
var delta_theta = unit_vector_angle(v1x, v1y, v2x, v2y);
|
||||
|
||||
if (fs === 0 && delta_theta > 0) {
|
||||
delta_theta -= TAU;
|
||||
}
|
||||
if (fs === 1 && delta_theta < 0) {
|
||||
delta_theta += TAU;
|
||||
}
|
||||
|
||||
return [ cx, cy, theta1, delta_theta ];
|
||||
}
|
||||
|
||||
//
|
||||
// Approximate one unit arc segment with bézier curves,
|
||||
// see http://math.stackexchange.com/questions/873224
|
||||
//
|
||||
function approximate_unit_arc(theta1, delta_theta) {
|
||||
var alpha = 4/3 * Math.tan(delta_theta/4);
|
||||
|
||||
var x1 = Math.cos(theta1);
|
||||
var y1 = Math.sin(theta1);
|
||||
var x2 = Math.cos(theta1 + delta_theta);
|
||||
var y2 = Math.sin(theta1 + delta_theta);
|
||||
|
||||
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) {
|
||||
var sin_phi = Math.sin(phi * TAU / 360);
|
||||
var cos_phi = Math.cos(phi * TAU / 360);
|
||||
|
||||
// Make sure radii are valid
|
||||
//
|
||||
var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
|
||||
var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
|
||||
|
||||
if (x1p === 0 && y1p === 0) {
|
||||
// we're asked to draw line to itself
|
||||
return [];
|
||||
}
|
||||
|
||||
if (rx === 0 || ry === 0) {
|
||||
// one of the radii is zero
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
// Compensate out-of-range radii
|
||||
//
|
||||
rx = Math.abs(rx);
|
||||
ry = Math.abs(ry);
|
||||
|
||||
var lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
|
||||
if (lambda > 1) {
|
||||
rx *= Math.sqrt(lambda);
|
||||
ry *= Math.sqrt(lambda);
|
||||
}
|
||||
|
||||
|
||||
// Get center parameters (cx, cy, theta1, delta_theta)
|
||||
//
|
||||
var cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi);
|
||||
|
||||
var result = [];
|
||||
var theta1 = cc[2];
|
||||
var delta_theta = cc[3];
|
||||
|
||||
// Split an arc to multiple segments, so each segment
|
||||
// will be less than τ/4 (= 90°)
|
||||
//
|
||||
var segments = Math.max(Math.ceil(Math.abs(delta_theta) / (TAU / 4)), 1);
|
||||
delta_theta /= segments;
|
||||
|
||||
for (var i = 0; i < segments; i++) {
|
||||
result.push(approximate_unit_arc(theta1, delta_theta));
|
||||
theta1 += delta_theta;
|
||||
}
|
||||
|
||||
// We have a bezier approximation of a unit circle,
|
||||
// now need to transform back to the original ellipse
|
||||
//
|
||||
return result.map(function (curve) {
|
||||
for (var i = 0; i < curve.length; i += 2) {
|
||||
var x = curve[i + 0];
|
||||
var y = curve[i + 1];
|
||||
|
||||
// scale
|
||||
x *= rx;
|
||||
y *= ry;
|
||||
|
||||
// rotate
|
||||
var xp = cos_phi*x - sin_phi*y;
|
||||
var yp = sin_phi*x + cos_phi*y;
|
||||
|
||||
// translate
|
||||
curve[i + 0] = xp + cc[0];
|
||||
curve[i + 1] = yp + cc[1];
|
||||
}
|
||||
|
||||
return curve;
|
||||
});
|
||||
}
|
||||
|
||||
self.a2c = a2c;
|
||||
});
|
|
@ -116,6 +116,11 @@
|
|||
(= id :multiple)
|
||||
(= file-id :multiple)))
|
||||
|
||||
(defn color? [^string color-str]
|
||||
(and (not (nil? color-str))
|
||||
(not (empty? color-str))
|
||||
(gcolor/isValidColor color-str)))
|
||||
|
||||
(defn parse-color [^string color-str]
|
||||
(let [result (gcolor/parse color-str)]
|
||||
(str (.-hex ^js result))))
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
|
||||
(ns app.util.geom.path
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[app.common.data :as cd]
|
||||
[app.util.data :as d]
|
||||
[app.common.data :as cd]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.util.geom.path-impl-simplify :as impl-simplify]))
|
||||
[app.util.a2c :refer [a2c]]
|
||||
[app.util.data :as d]
|
||||
[app.util.geom.path-impl-simplify :as impl-simplify]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn simplify
|
||||
([points]
|
||||
|
@ -28,23 +29,42 @@
|
|||
|
||||
;; 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 #"([+-]?(([1-9]\d*(\.\d+)?)|(\.\d+)|0|1))")
|
||||
(def num-regex #"[+-]?(\d+(\.\d+)?|\.\d+)")
|
||||
|
||||
(def flag-regex #"[01]")
|
||||
|
||||
(defn coord-n [size]
|
||||
(re-pattern (str "(?i)[a-z]\\s*"
|
||||
(->> (range size)
|
||||
(map #(identity num-regex))
|
||||
(str/join "\\s+")))))
|
||||
(defn fix-dot-number [val]
|
||||
(if (str/starts-with? val ".")
|
||||
(str "0" val)
|
||||
val))
|
||||
|
||||
(defn extract-params [cmd-str extract-commands]
|
||||
(loop [result []
|
||||
extract-idx 0
|
||||
current {}
|
||||
remain (-> cmd-str (subs 1) (str/trim))]
|
||||
|
||||
(defn parse-params [cmd-str num-params]
|
||||
(let [fix-starting-dot (fn [arg] (str/replace arg #"([^\d]|^)\." "$10."))]
|
||||
(->> (re-seq num-regex cmd-str)
|
||||
(map first)
|
||||
(map fix-starting-dot)
|
||||
(map d/read-string)
|
||||
(partition num-params))))
|
||||
(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 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
|
||||
(not (empty? current)) (conj current))))))
|
||||
|
||||
(defn command->param-list [{:keys [command params]}]
|
||||
(case command
|
||||
|
@ -73,96 +93,99 @@
|
|||
|
||||
(defmethod parse-command "M" [cmd]
|
||||
(let [relative (str/starts-with? cmd "m")
|
||||
params (parse-params cmd 2)]
|
||||
(for [[x y] params]
|
||||
param-list (extract-params cmd [[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :move-to
|
||||
:relative relative
|
||||
:params {:x x :y y}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "Z" [cmd]
|
||||
[{:command :close-path}])
|
||||
|
||||
(defmethod parse-command "L" [cmd]
|
||||
(let [relative (str/starts-with? cmd "l")
|
||||
params (parse-params cmd 2)]
|
||||
(for [[x y] params]
|
||||
param-list (extract-params cmd [[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :line-to
|
||||
:relative relative
|
||||
:params {:x x :y y}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "H" [cmd]
|
||||
(let [relative (str/starts-with? cmd "h")
|
||||
params (parse-params cmd 1)]
|
||||
(for [[value] params]
|
||||
param-list (extract-params cmd [[:value :number]])]
|
||||
(for [params param-list]
|
||||
{:command :line-to-horizontal
|
||||
:relative relative
|
||||
:params {:value value}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "V" [cmd]
|
||||
(let [relative (str/starts-with? cmd "v")
|
||||
params (parse-params cmd 1)]
|
||||
(for [[value] params]
|
||||
param-list (extract-params cmd [[:value :number]])]
|
||||
(for [params param-list]
|
||||
{:command :line-to-vertical
|
||||
:relative relative
|
||||
:params {:value value}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "C" [cmd]
|
||||
(let [relative (str/starts-with? cmd "c")
|
||||
params (parse-params cmd 6)]
|
||||
(for [[c1x c1y c2x c2y x y] params]
|
||||
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 {:c1x c1x
|
||||
:c1y c1y
|
||||
:c2x c2x
|
||||
:c2y c2y
|
||||
:x x
|
||||
:y y}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "S" [cmd]
|
||||
(let [relative (str/starts-with? cmd "s")
|
||||
params (parse-params cmd 4)]
|
||||
(for [[cx cy x y] params]
|
||||
param-list (extract-params cmd [[:c1x :number]
|
||||
[:c2y :number]
|
||||
[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :smooth-curve-to
|
||||
:relative relative
|
||||
:params {:cx cx
|
||||
:cy cy
|
||||
:x x
|
||||
:y y}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "Q" [cmd]
|
||||
(let [relative (str/starts-with? cmd "s")
|
||||
params (parse-params cmd 4)]
|
||||
(for [[cx cy x y] params]
|
||||
param-list (extract-params cmd [[:c1x :number]
|
||||
[:c1y :number]
|
||||
[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :quadratic-bezier-curve-to
|
||||
:relative relative
|
||||
:params {:cx cx
|
||||
:cy cy
|
||||
:x x
|
||||
:y y}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "T" [cmd]
|
||||
(let [relative (str/starts-with? cmd "t")
|
||||
params (parse-params cmd (coord-n 2))]
|
||||
(for [[cx cy x y] params]
|
||||
param-list (extract-params cmd [[:x :number]
|
||||
[:y :number]])]
|
||||
(for [params param-list]
|
||||
{:command :smooth-quadratic-bezier-curve-to
|
||||
:relative relative
|
||||
:params {:x x
|
||||
:y y}})))
|
||||
:params params})))
|
||||
|
||||
(defmethod parse-command "A" [cmd]
|
||||
(let [relative (str/starts-with? cmd "a")
|
||||
params (parse-params cmd 7)]
|
||||
(for [[rx ry x-axis-rotation large-arc-flag sweep-flag x y] params]
|
||||
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 {:rx rx
|
||||
:ry ry
|
||||
:x-axis-rotation x-axis-rotation
|
||||
:large-arc-flag large-arc-flag
|
||||
:sweep-flag sweep-flag
|
||||
:x x
|
||||
:y y}})))
|
||||
:params params})))
|
||||
|
||||
(defn command->string [{:keys [command relative params] :as entry}]
|
||||
(let [command-str (case command
|
||||
|
@ -185,6 +208,21 @@
|
|||
(contains? params :y))
|
||||
(gpt/point params)))
|
||||
|
||||
(defn arc->beziers [prev 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} (:params prev)
|
||||
{:keys [rx ry x-axis-rotation large-arc-flag sweep-flag x y]} (:params command)
|
||||
result (a2c 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]
|
||||
|
@ -208,11 +246,13 @@
|
|||
(:relative command)
|
||||
(-> (assoc :relative false)
|
||||
(cd/update-in-when [:params :x] + (:x pos))
|
||||
(cd/update-in-when [:params :y] + (:y pos)))
|
||||
|
||||
|
||||
)]
|
||||
[(cmd-pos command) (conj result command)]))
|
||||
(cd/update-in-when [:params :y] + (:y pos))))
|
||||
|
||||
result #_(conj result command)
|
||||
(if (= :elliptical-arc (:command command))
|
||||
(cd/concat result (arc->beziers prev command))
|
||||
(conj result command))]
|
||||
[(cmd-pos command) result]))
|
||||
|
||||
start (first commands)
|
||||
start-pos (cmd-pos start)]
|
||||
|
|
|
@ -11,9 +11,16 @@
|
|||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.data :as cd]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defonce replace-regex #"[^#]*#([^)\s]+).*")
|
||||
(defonce replace-regex #"#([^\W]+)")
|
||||
|
||||
(defn extract-ids [val]
|
||||
(->> (re-seq replace-regex val)
|
||||
(mapv second)))
|
||||
|
||||
(defn clean-attrs
|
||||
"Transforms attributes to their react equivalent"
|
||||
|
@ -36,34 +43,39 @@
|
|||
(into {})))
|
||||
|
||||
(map-fn [[key val]]
|
||||
(cond
|
||||
(= key :class) [:className val]
|
||||
(and (= key :style) (string? val)) [key (format-styles val)]
|
||||
:else (vector (transform-key key) val)))]
|
||||
(let [key (keyword key)]
|
||||
(cond
|
||||
(= key :class) [:className val]
|
||||
(and (= key :style) (string? val)) [key (format-styles val)]
|
||||
:else (vector (transform-key key) val))))]
|
||||
|
||||
(->> attrs
|
||||
(map map-fn)
|
||||
(into {}))))
|
||||
|
||||
(defn update-attr-ids
|
||||
"Replaces the ids inside a property"
|
||||
[attrs replace-fn]
|
||||
(letfn [(update-ids [key val]
|
||||
(cond
|
||||
(map? val)
|
||||
(cd/mapm update-ids val)
|
||||
|
||||
(= key :id)
|
||||
(replace-fn val)
|
||||
|
||||
:else
|
||||
(let [replace-id
|
||||
(fn [result it]
|
||||
(str/replace result it (replace-fn it)))]
|
||||
(reduce replace-id val (extract-ids val)))))]
|
||||
(cd/mapm update-ids attrs)))
|
||||
|
||||
(defn replace-attrs-ids
|
||||
"Replaces the ids inside a property"
|
||||
[attrs ids-mapping]
|
||||
(if (and ids-mapping (not (empty? ids-mapping)))
|
||||
(letfn [(replace-ids [key val]
|
||||
(cond
|
||||
(map? val)
|
||||
(cd/mapm replace-ids val)
|
||||
|
||||
(and (= key :id) (contains? ids-mapping val))
|
||||
(get ids-mapping val)
|
||||
|
||||
:else
|
||||
(let [[_ from-id] (re-matches replace-regex val)]
|
||||
(if (and from-id (contains? ids-mapping from-id))
|
||||
(str/replace val from-id (get ids-mapping from-id))
|
||||
val))))]
|
||||
(cd/mapm replace-ids attrs))
|
||||
|
||||
(update-attr-ids attrs (fn [id] (get ids-mapping id id)))
|
||||
;; Ids-mapping is null
|
||||
attrs))
|
||||
|
||||
|
@ -74,3 +86,83 @@
|
|||
element-id (assoc element-id (str (uuid/next))))]
|
||||
(reduce visit-node result (:content node))))]
|
||||
(visit-node {} content)))
|
||||
|
||||
(defn extract-defs [{:keys [tag content] :as node}]
|
||||
|
||||
(if-not (map? node)
|
||||
[{} node]
|
||||
(letfn [(def-tag? [{:keys [tag]}] (= tag :defs))
|
||||
|
||||
(assoc-node [result node]
|
||||
(assoc result (-> node :attrs :id) node))
|
||||
|
||||
(node-data [node]
|
||||
(->> (:content node) (reduce assoc-node {})))]
|
||||
|
||||
(let [current-def (->> content
|
||||
(filterv def-tag?)
|
||||
(map node-data)
|
||||
(reduce merge))
|
||||
result (->> content
|
||||
(filter (comp not def-tag?))
|
||||
(map extract-defs))
|
||||
|
||||
current-def (->> result (map first) (reduce merge current-def))
|
||||
content (->> result (mapv second))]
|
||||
|
||||
[current-def (assoc node :content content)]))))
|
||||
|
||||
(defn find-attr-references [attrs]
|
||||
(->> attrs
|
||||
(mapcat (fn [[_ attr-value]] (extract-ids attr-value)))))
|
||||
|
||||
(defn find-node-references [node]
|
||||
(let [current (->> (find-attr-references (:attrs node)) (into #{}))
|
||||
children (->> (:content node) (map find-node-references) (flatten) (into #{}))]
|
||||
(-> (cd/concat current children)
|
||||
(vec))))
|
||||
|
||||
(defn find-def-references [defs references]
|
||||
(loop [result (into #{} references)
|
||||
checked? #{}
|
||||
to-check (first references)
|
||||
pending (rest references)]
|
||||
|
||||
(cond
|
||||
(nil? to-check)
|
||||
result
|
||||
|
||||
(checked? to-check)
|
||||
(recur result
|
||||
checked?
|
||||
(first pending)
|
||||
(rest pending))
|
||||
|
||||
:else
|
||||
(let [node (get defs to-check)
|
||||
new-refs (find-node-references node)]
|
||||
(recur (cd/concat result new-refs)
|
||||
(conj checked? to-check)
|
||||
(first pending)
|
||||
(rest pending))))))
|
||||
|
||||
(defn svg-transform-matrix [shape]
|
||||
(if (:svg-viewbox shape)
|
||||
(let [{svg-x :x
|
||||
svg-y :y
|
||||
svg-width :width
|
||||
svg-height :height} (:svg-viewbox shape)
|
||||
{:keys [x y width height]} (:selrect shape)
|
||||
|
||||
scale-x (/ width svg-width)
|
||||
scale-y (/ height svg-height)]
|
||||
|
||||
(gmt/multiply
|
||||
(gmt/matrix)
|
||||
(gsh/transform-matrix shape)
|
||||
(gmt/translate-matrix (gpt/point (- x (* scale-x svg-x)) (- y (* scale-y svg-y))))
|
||||
(gmt/scale-matrix (gpt/point scale-x scale-y))))
|
||||
|
||||
;; :else
|
||||
(gmt/matrix)))
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue