swhitty / SwiftDraw

@@ -50,7 +50,11 @@
Loading
50 50
    func format(_ coordinates: DOM.Coordinate...) -> String {
51 51
      return coordinates.map { format(Double($0)) }.joined(separator: delimeter.rawValue)
52 52
    }
53 -
    
53 +
54 +
    func format(_ coordinates: Double...) -> String {
55 +
      return coordinates.map { format($0) }.joined(separator: delimeter.rawValue)
56 +
    }
57 +
54 58
    func format(_ c: Double) -> String {
55 59
      switch precision {
56 60
      case .capped(let max):

@@ -0,0 +1,81 @@
Loading
1 +
//
2 +
//  Renderer.SVG.swift
3 +
//  SwiftDraw
4 +
//
5 +
//  Created by Simon Whitty on 7/03/22.
6 +
//  Copyright 2022 Simon Whitty
7 +
//
8 +
//  Distributed under the permissive zlib license
9 +
//  Get the latest version from here:
10 +
//
11 +
//  https://github.com/swhitty/SwiftDraw
12 +
//
13 +
//  This software is provided 'as-is', without any express or implied
14 +
//  warranty.  In no event will the authors be held liable for any damages
15 +
//  arising from the use of this software.
16 +
//
17 +
//  Permission is granted to anyone to use this software for any purpose,
18 +
//  including commercial applications, and to alter it and redistribute it
19 +
//  freely, subject to the following restrictions:
20 +
//
21 +
//  1. The origin of this software must not be misrepresented; you must not
22 +
//  claim that you wrote the original software. If you use this software
23 +
//  in a product, an acknowledgment in the product documentation would be
24 +
//  appreciated but is not required.
25 +
//
26 +
//  2. Altered source versions must be plainly marked as such, and must not be
27 +
//  misrepresented as being the original software.
28 +
//
29 +
//  3. This notice may not be removed or altered from any source distribution.
30 +
//
31 +
32 +
public struct SVGRenderer {
33 +
34 +
    /// Expand a Path by applying the supplied transformation
35 +
    /// - Parameters:
36 +
    ///   - path: SVG path data `M x y L x y ...`
37 +
    ///   - transform: SVG transform commands `translate(10, 20) roate(30)`
38 +
    /// - Returns: SVG path data with transform applied  `M x y L x y ...`
39 +
    public static func makeExpanded(path: String,
40 +
                                    transform: String,
41 +
                                    precision: Int = 4) throws -> String {
42 +
43 +
        let domPath = try XMLParser().parsePath(from: path)
44 +
        let layerPath = try LayerTree.Builder.createPath(from: domPath)
45 +
        let domTransform = try XMLParser().parseTransform(transform)
46 +
        let matrix = LayerTree.Builder
47 +
            .createTransforms(from: domTransform)
48 +
            .toMatrix()
49 +
50 +
        return makeDOM(path: layerPath.applying(matrix: matrix), precision: precision)
51 +
    }
52 +
53 +
    static func makeDOM(path: LayerTree.Path, precision: Int) -> String {
54 +
        let formatter = XMLFormatter.CoordinateFormatter(delimeter: .comma,
55 +
                                                         precision: .capped(max: precision))
56 +
        return path.segments
57 +
            .map { makeDOM(segment: $0, formatter: formatter) }
58 +
            .joined(separator: " ")
59 +
    }
60 +
61 +
    static func makeDOM(segment: LayerTree.Path.Segment,
62 +
                        formatter: XMLFormatter.CoordinateFormatter) -> String {
63 +
        switch segment {
64 +
        case .move(let point):
65 +
            let point = formatter.format(point.x, point.y)
66 +
            return "M\(point)"
67 +
        case .line(let point):
68 +
            let point = formatter.format(point.x, point.y)
69 +
            return "L\(point)"
70 +
        case .cubic(let p, let cp1, let cp2):
71 +
            let p = formatter.format(p.x, p.y)
72 +
            let cp1 = formatter.format(cp1.x, cp1.y)
73 +
            let cp2 = formatter.format(cp2.x, cp2.y)
74 +
            return "C\(cp1) \(cp2) \(p)"
75 +
        case .close:
76 +
            return "Z"
77 +
        }
78 +
    }
79 +
}
80 +
81 +

@@ -102,3 +102,31 @@
Loading
102 102
    }
103 103
  }
104 104
}
105 +
106 +
107 +
extension LayerTree.Path {
108 +
109 +
    func applying(matrix: LayerTree.Transform.Matrix) -> LayerTree.Path {
110 +
        LayerTree.Path(
111 +
            segments.map { $0.applying(matrix: matrix) }
112 +
        )
113 +
    }
114 +
}
115 +
116 +
extension LayerTree.Path.Segment {
117 +
118 +
    func applying(matrix: LayerTree.Transform.Matrix) -> Self {
119 +
        switch self {
120 +
        case .move(let point):
121 +
            return .move(to: matrix.transform(point: point))
122 +
        case .line(let point):
123 +
            return .line(to: matrix.transform(point: point))
124 +
        case .cubic(let p, let cp1, let cp2):
125 +
            return .cubic(to: matrix.transform(point: p),
126 +
                          control1: matrix.transform(point: cp1),
127 +
                          control2: matrix.transform(point: cp2))
128 +
        case .close:
129 +
            return .close
130 +
        }
131 +
    }
132 +
}

@@ -97,3 +97,21 @@
Loading
97 97
                                      ty: (t.tx * m.b) + (t.ty * m.d) + m.ty)
98 98
  }
99 99
}
100 +
101 +
extension LayerTree.Transform.Matrix {
102 +
103 +
    func transform(point: LayerTree.Point) -> LayerTree.Point {
104 +
        LayerTree.Point(
105 +
            (a * point.x) + (c * point.y) + tx,
106 +
            (b * point.x) + (d * point.y) + ty
107 +
        )
108 +
    }
109 +
}
110 +
111 +
extension Array where Element == LayerTree.Transform {
112 +
    func toMatrix() -> LayerTree.Transform.Matrix {
113 +
        reversed().reduce(LayerTree.Transform.identity.toMatrix()) {
114 +
            $0.concatenated($1.toMatrix())
115 +
        }
116 +
    }
117 +
}
Files Coverage
SwiftDraw 76.11%
Project Totals (59 files) 76.11%
1
ignore:
2
  - "SwiftDrawTests"
3
  - "CommandLine"
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading