Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions DOM/Sources/DOM.Use.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ package extension Array<DOM.GraphicsElement> {
if element.id == id {
return element
}
if let container = element as? any ContainerElement {
return container.childElements.firstGraphicsElement(with: id)
if let container = element as? any ContainerElement,
let found = container.childElements.firstGraphicsElement(with: id) {
return found
}
}
return nil
Expand Down
18 changes: 18 additions & 0 deletions DOM/Tests/UseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@ import Testing
@Suite("Use Tests")
struct UseTests {

@Test
func firstGraphicsElementSearchesPastContainers() throws {
let svg = DOM.SVG(width: 100, height: 100)

// Place a group (container) before the target element
let group = DOM.Group()
group.childElements = [DOM.Circle(cx: 0, cy: 0, r: 5)]

let target = DOM.Rect(width: 10, height: 10)
target.id = "target"

svg.childElements = [group, target]

// Should find "target" even though a container (group) comes first
let found = svg.firstGraphicsElement(with: "target")
#expect(found?.id == "target")
}

@Test
func use() throws {
var node = ["xlink:href": "#line2", "href": "#line1"]
Expand Down
3 changes: 2 additions & 1 deletion SwiftDraw/Sources/LayerTree/LayerTree.Builder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ extension LayerTree.Builder {

func makePattern(for element: DOM.Pattern) -> LayerTree.Pattern {
let frame = LayerTree.Rect(x: 0, y: 0, width: element.width, height: element.height)
let pattern = LayerTree.Pattern(frame: frame)
let contentUnits: LayerTree.PatternUnits = element.patternContentUnits == .objectBoundingBox ? .objectBoundingBox : .userSpaceOnUse
let pattern = LayerTree.Pattern(frame: frame, contentUnits: contentUnits)
pattern.contents = element.childElements.compactMap { .layer(makeLayer(from: $0, inheriting: .init())) }
return pattern
}
Expand Down
16 changes: 14 additions & 2 deletions SwiftDraw/Sources/LayerTree/LayerTree.CommandGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,24 @@ extension LayerTree {
commands.append(.fill(path, rule: rule))
}
case .pattern(let fillPattern):
var resolvedPattern = fillPattern
if fillPattern.contentUnits == .objectBoundingBox {
let bounds = provider.getBounds(from: shape)
let scaledFrame = LayerTree.Rect(
x: fillPattern.frame.x * bounds.width + bounds.x,
y: fillPattern.frame.y * bounds.height + bounds.y,
width: fillPattern.frame.width * bounds.width,
height: fillPattern.frame.height * bounds.height
)
resolvedPattern = LayerTree.Pattern(frame: scaledFrame)
resolvedPattern.contents = fillPattern.contents
}
var patternCommands = [RendererCommand<P.Types>]()
for contents in fillPattern.contents {
for contents in resolvedPattern.contents {
patternCommands.append(contentsOf: renderCommands(for: contents, colorConverter: colorConverter))
}

let pattern = provider.createPattern(from: fillPattern, contents: patternCommands)
let pattern = provider.createPattern(from: resolvedPattern, contents: patternCommands)
let rule = provider.createFillRule(from: fill.rule)
commands.append(.setFillPattern(pattern))
commands.append(.fill(path, rule: rule))
Expand Down
12 changes: 10 additions & 2 deletions SwiftDraw/Sources/LayerTree/LayerTree.Pattern.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,31 @@

extension LayerTree {

enum PatternUnits: Hashable {
case userSpaceOnUse
case objectBoundingBox
}

final class Pattern: Hashable {

var frame: LayerTree.Rect
var contentUnits: PatternUnits
var contents: [LayerTree.Layer.Contents]

init(frame: LayerTree.Rect) {
init(frame: LayerTree.Rect, contentUnits: PatternUnits = .userSpaceOnUse) {
self.frame = frame
self.contentUnits = contentUnits
self.contents = []
}

func hash(into hasher: inout Hasher) {
frame.hash(into: &hasher)
contentUnits.hash(into: &hasher)
contents.hash(into: &hasher)
}

static func == (lhs: LayerTree.Pattern, rhs: LayerTree.Pattern) -> Bool {
return lhs.frame == rhs.frame && lhs.contents == rhs.contents
return lhs.frame == rhs.frame && lhs.contentUnits == rhs.contentUnits && lhs.contents == rhs.contents
}
}
}
22 changes: 22 additions & 0 deletions SwiftDraw/Tests/LayerTree/LayerTree.BuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ final class LayerTreeBuilderTests: XCTestCase {
XCTAssertEqual(pattern.contents, [.layer(expected)])
}

func testDOMPatternObjectBoundingBox() {
let builder = LayerTree.Builder(svg: DOM.SVG(width: 100, height: 100))

var element = DOM.Pattern(id: "p1", width: 1, height: 1)
element.patternContentUnits = .objectBoundingBox
element.childElements = [DOM.Circle(cx: 0, cy: 0, r: 5)]

let pattern = builder.makePattern(for: element)
XCTAssertEqual(pattern.contentUnits, LayerTree.PatternUnits.objectBoundingBox)
XCTAssertEqual(pattern.frame, LayerTree.Rect(x: 0, y: 0, width: 1, height: 1))
}

func testDOMPatternUserSpaceOnUse() {
let builder = LayerTree.Builder(svg: DOM.SVG(width: 100, height: 100))

var element = DOM.Pattern(id: "p2", width: 20, height: 20)
element.childElements = [DOM.Circle(cx: 10, cy: 10, r: 5)]

let pattern = builder.makePattern(for: element)
XCTAssertEqual(pattern.contentUnits, LayerTree.PatternUnits.userSpaceOnUse)
}

func testStrokeAttributes() {
var state = LayerTree.Builder.State()
state.stroke = .color(.rgbf(1.0, 0.0, 0.0, 1.0))
Expand Down