From e2c4d9650d6ebe45621337384ab6e712296c7530 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Mon, 7 May 2018 16:14:06 +0700 Subject: [PATCH] Add NodeLayout --- Source/svg/SVGCanvas.swift | 4 +- Source/svg/SVGParser.swift | 8 +-- Source/utils/SvgContentLayout.swift | 44 ++------------- Source/utils/SvgNodeLayout.swift | 88 +++++++++++++++++++++++++++++ Source/views/MacawView.swift | 36 +----------- 5 files changed, 100 insertions(+), 80 deletions(-) create mode 100644 Source/utils/SvgNodeLayout.swift diff --git a/Source/svg/SVGCanvas.swift b/Source/svg/SVGCanvas.swift index 4f17a0a2..dfdaa990 100644 --- a/Source/svg/SVGCanvas.swift +++ b/Source/svg/SVGCanvas.swift @@ -7,9 +7,9 @@ class SVGCanvas: Group { - let layout: ContentLayout + let layout: NodeLayout - public init(layout: ContentLayout, contents: [Node] = []) { + public init(layout: NodeLayout, contents: [Node] = []) { self.layout = layout super.init(contents: contents) } diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index a4f1852f..f3e74a13 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -69,7 +69,7 @@ open class SVGParser { fileprivate func parse() -> Group { let parsedXml = SWXMLHash.parse(xmlString) - var layout: ContentLayout? + var layout: NodeLayout? for child in parsedXml.children { if let element = child.element { if element.name == "svg" { @@ -119,7 +119,7 @@ open class SVGParser { } } - fileprivate func parseViewBox(_ element: SWXMLHash.XMLElement) -> SvgContentLayout? { + fileprivate func parseViewBox(_ element: SWXMLHash.XMLElement) -> SvgNodeLayout? { var svgDimensions: Dimensions? if let w = getDimensionValue(element, attribute: "width"), let h = getDimensionValue(element, attribute: "height") { svgDimensions = Dimensions(width: w, height: h) @@ -143,7 +143,7 @@ open class SVGParser { let strings = contentModeString.components(separatedBy: CharacterSet(charactersIn: " ")) if strings.count == 1 { // none scalingMode = parseAspectRatio(strings[0]) - return SvgContentLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode) + return SvgNodeLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode) } guard strings.count == 2 else { fatalError("Invalid content mode") } @@ -159,7 +159,7 @@ open class SVGParser { scalingMode = parseAspectRatio(strings[1]) } - return SvgContentLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode) + return SvgNodeLayout(svgDimensions: svgDimensions, viewBox: viewBox, scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode) } fileprivate func parseNode(_ node: XMLIndexer, groupStyle: [String: String] = [:]) -> Node? { diff --git a/Source/utils/SvgContentLayout.swift b/Source/utils/SvgContentLayout.swift index 9041751a..08afb49e 100644 --- a/Source/utils/SvgContentLayout.swift +++ b/Source/utils/SvgContentLayout.swift @@ -1,27 +1,4 @@ -enum Dimension { - case percent(Double) - case pixels(Double) - - init(percent: Double) { - self = .percent(percent) - } - - init(pixels: Double) { - self = .pixels(pixels) - } -} - -class Dimensions { - let width: Dimension - let height: Dimension - - public init(width: Dimension, height: Dimension) { - self.width = width - self.height = height - } -} - public protocol ContentLayout { static var standard: ContentLayout { get } @@ -30,24 +7,11 @@ public protocol ContentLayout { class SvgContentLayout: ContentLayout { - public let scalingMode: AspectRatio - public let xAligningMode: Align - public let yAligningMode: Align + let scalingMode: AspectRatio + let xAligningMode: Align + let yAligningMode: Align - let svgDimensions: Dimensions? - let viewBox: Rect? - - public init(scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) { - self.svgDimensions = .none - self.viewBox = .none - self.scalingMode = scalingMode ?? .meet - self.xAligningMode = xAligningMode ?? .mid - self.yAligningMode = yAligningMode ?? .mid - } - - init(svgDimensions: Dimensions? = .none, viewBox: Rect? = .none, scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) { - self.svgDimensions = svgDimensions - self.viewBox = viewBox + init(scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) { self.scalingMode = scalingMode ?? .meet self.xAligningMode = xAligningMode ?? .mid self.yAligningMode = yAligningMode ?? .mid diff --git a/Source/utils/SvgNodeLayout.swift b/Source/utils/SvgNodeLayout.swift new file mode 100644 index 00000000..d10ad4bf --- /dev/null +++ b/Source/utils/SvgNodeLayout.swift @@ -0,0 +1,88 @@ + +enum Dimension { + case percent(Double) + case pixels(Double) + + init(percent: Double) { + self = .percent(percent) + } + + init(pixels: Double) { + self = .pixels(pixels) + } +} + +class Dimensions { + let width: Dimension + let height: Dimension + + public init(width: Dimension, height: Dimension) { + self.width = width + self.height = height + } +} + +public protocol NodeLayout { + + func layout(in rect: Rect) -> (Transform, Locus?) +} + +class SvgNodeLayout: NodeLayout { + + let svgDimensions: Dimensions? + let viewBox: Rect? + let scalingMode: AspectRatio + let xAligningMode: Align + let yAligningMode: Align + + init(svgDimensions: Dimensions? = .none, viewBox: Rect? = .none, scalingMode: AspectRatio? = .meet, xAligningMode: Align? = .mid, yAligningMode: Align? = .mid) { + self.svgDimensions = svgDimensions + self.viewBox = viewBox + self.scalingMode = scalingMode ?? .meet + self.xAligningMode = xAligningMode ?? .mid + self.yAligningMode = yAligningMode ?? .mid + } + + public func layout(in rect: Rect) -> (Transform, Locus?) { + + var clip: Locus? = .none + var transform = Transform.identity + + guard let dimensions = svgDimensions else { + return (transform, clip) + } + + let width = dimensionToPixels(dimensions.width, framePixels: rect.w) + let height = dimensionToPixels(dimensions.height, framePixels: rect.h) + let svgSize = Size(w: width, h: height) + + if let viewBox = self.viewBox { + clip = viewBox + } + let viewBox = self.viewBox ?? Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h) + + if scalingMode === AspectRatio.slice { + // setup new clipping to slice extra bits + let newSize = AspectRatio.meet.fit(size: svgSize, into: viewBox) + let newX = viewBox.x + xAligningMode.align(outer: viewBox.w, inner: newSize.w) + let newY = viewBox.y + yAligningMode.align(outer: viewBox.h, inner: newSize.h) + clip = Rect(x: newX, y: newY, w: newSize.w, h: newSize.h) + } + + let contentLayout = SvgContentLayout(scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode) + transform = contentLayout.layout(rect: viewBox, into: Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h)) + + // move to (0, 0) + transform = transform.move(dx: -viewBox.x, dy: -viewBox.y) + return (transform, clip) + } +} + +fileprivate func dimensionToPixels(_ dimension: Dimension, framePixels: Double) -> Double { + switch(dimension) { + case let .percent(percent): + return framePixels * percent / 100.0 + case let .pixels(pixels): + return pixels + } +} diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 1a53afba..7d546f1e 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -18,31 +18,8 @@ open class MacawView: MView, MGestureRecognizerDelegate { } didSet { - if let canvas = node as? SVGCanvas, let params = canvas.layout as? SvgContentLayout, let dimensions = params.svgDimensions { - - let width = dimensionToPixels(dimensions.width, framePixels: Double(frame.width)) - let height = dimensionToPixels(dimensions.height, framePixels: Double(frame.height)) - let svgSize = Size(w: width, h: height) - - let scalingMode = params.scalingMode - if let viewBox = params.viewBox { - canvas.clip = viewBox - } - let viewBox = params.viewBox ?? Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h) - - if scalingMode === AspectRatio.slice { - // setup new clipping to slice extra bits - let newSize = AspectRatio.meet.fit(size: svgSize, into: viewBox) - let newX = viewBox.x + params.xAligningMode.align(outer: viewBox.w, inner: newSize.w) - let newY = viewBox.y + params.yAligningMode.align(outer: viewBox.h, inner: newSize.h) - node.clip = Rect(x: newX, y: newY, w: newSize.w, h: newSize.h) - } - - let contentLayout = SvgContentLayout(scalingMode: scalingMode, xAligningMode: params.xAligningMode, yAligningMode: params.yAligningMode) - node.place = contentLayout.layout(rect: viewBox, into: Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h)) - - // move to (0, 0) - node.place = node.place.move(dx: -viewBox.x, dy: -viewBox.y) + if let canvas = node as? SVGCanvas, let layout = canvas.layout as? SvgNodeLayout { + (canvas.place, canvas.clip) = layout.layout(in: Rect(cgRect: bounds)) } nodesMap.add(node, view: self) @@ -200,15 +177,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { guard let ctx = context.cgContext else { return .none } return renderer?.findNodeAt(location: location, ctx: ctx) } - - fileprivate func dimensionToPixels(_ dimension: Dimension, framePixels: Double) -> Double { - switch(dimension) { - case let .percent(percent): - return framePixels * percent / 100.0 - case let .pixels(pixels): - return pixels - } - } // MARK: - Touches