mirror of
https://github.com/exyte/Macaw.git
synced 2024-09-21 09:59:10 +03:00
Better Align implementation
This commit is contained in:
parent
26ee61a2c8
commit
49e16e9f52
@ -1,5 +1,44 @@
|
|||||||
public enum Align {
|
open class Align {
|
||||||
case min
|
|
||||||
case mid
|
public static let min: Align = MinAlign()
|
||||||
case max
|
public static let mid: Align = MidAlign()
|
||||||
|
public static let max: Align = MaxAlign()
|
||||||
|
|
||||||
|
open func align(x: Double, y: Double) -> Double {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
open func align(x: CGFloat, y: CGFloat) -> CGFloat {
|
||||||
|
return CGFloat(align(x: x.doubleValue, y: y.doubleValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
open func align(x: Double) -> Double {
|
||||||
|
return align(x: x, y: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func align(x: CGFloat) -> CGFloat {
|
||||||
|
return CGFloat(align(x: x.doubleValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MinAlign : Align {
|
||||||
|
|
||||||
|
override func align(x: Double, y: Double) -> Double {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MidAlign : Align {
|
||||||
|
|
||||||
|
override func align(x: Double, y: Double) -> Double {
|
||||||
|
return x / 2 - y / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MaxAlign : Align {
|
||||||
|
|
||||||
|
override func align(x: Double, y: Double) -> Double {
|
||||||
|
return x - y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,16 +121,7 @@ open class Text: Node {
|
|||||||
NSAttributedStringKey.font: font
|
NSAttributedStringKey.font: font
|
||||||
]
|
]
|
||||||
let textSize = NSString(string: text).size(withAttributes: textAttributes)
|
let textSize = NSString(string: text).size(withAttributes: textAttributes)
|
||||||
var alignmentOffset = 0.0
|
return -align.align(x: textSize.width.doubleValue)
|
||||||
switch align {
|
|
||||||
case .mid:
|
|
||||||
alignmentOffset = (textSize.width / 2).doubleValue
|
|
||||||
case .max:
|
|
||||||
alignmentOffset = textSize.width.doubleValue
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return -alignmentOffset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -115,8 +115,6 @@ class ImageRenderer: NodeRenderer {
|
|||||||
let srcAR = size.width / size.height
|
let srcAR = size.width / size.height
|
||||||
var resultW = w
|
var resultW = w
|
||||||
var resultH = h
|
var resultH = h
|
||||||
var destX = CGFloat(0)
|
|
||||||
var destY = CGFloat(0)
|
|
||||||
if destAR < srcAR {
|
if destAR < srcAR {
|
||||||
// fill all available width and scale height
|
// fill all available width and scale height
|
||||||
resultH = size.height * w / size.width
|
resultH = size.height * w / size.width
|
||||||
@ -124,24 +122,8 @@ class ImageRenderer: NodeRenderer {
|
|||||||
// fill all available height and scale width
|
// fill all available height and scale width
|
||||||
resultW = size.width * h / size.height
|
resultW = size.width * h / size.height
|
||||||
}
|
}
|
||||||
let xalign = image.xAlign
|
let destX = image.xAlign.align(x: w, y: resultW)
|
||||||
switch xalign {
|
let destY = image.yAlign.align(x: h, y: resultH)
|
||||||
case Align.min:
|
|
||||||
destX = 0
|
|
||||||
case Align.mid:
|
|
||||||
destX = w / 2 - resultW / 2
|
|
||||||
case Align.max:
|
|
||||||
destX = w - resultW
|
|
||||||
}
|
|
||||||
let yalign = image.yAlign
|
|
||||||
switch yalign {
|
|
||||||
case Align.min:
|
|
||||||
destY = 0
|
|
||||||
case Align.mid:
|
|
||||||
destY = h / 2 - resultH / 2
|
|
||||||
case Align.max:
|
|
||||||
destY = h - resultH
|
|
||||||
}
|
|
||||||
return CGRect(x: destX, y: destY, width: resultW, height: resultH)
|
return CGRect(x: destX, y: destY, width: resultW, height: resultH)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,26 +141,12 @@ class ImageRenderer: NodeRenderer {
|
|||||||
// fill all available width and scale height
|
// fill all available width and scale height
|
||||||
totalH = size.height * w / size.width
|
totalH = size.height * w / size.width
|
||||||
totalW = w
|
totalW = w
|
||||||
switch image.yAlign {
|
srcY = image.yAlign.align(x: h, y: totalH)
|
||||||
case Align.min:
|
|
||||||
srcY = 0
|
|
||||||
case Align.mid:
|
|
||||||
srcY = -(totalH / 2 - h / 2)
|
|
||||||
case Align.max:
|
|
||||||
srcY = -(totalH - h)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// fill all available height and scale width
|
// fill all available height and scale width
|
||||||
totalW = size.width * h / size.height
|
totalW = size.width * h / size.height
|
||||||
totalH = h
|
totalH = h
|
||||||
switch image.xAlign {
|
srcX = image.xAlign.align(x: w, y: totalW)
|
||||||
case Align.min:
|
|
||||||
srcX = 0
|
|
||||||
case Align.mid:
|
|
||||||
srcX = -(totalW / 2 - w / 2)
|
|
||||||
case Align.max:
|
|
||||||
srcX = -(totalW - w)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return CGRect(x: srcX, y: srcY, width: totalW, height: totalH)
|
return CGRect(x: srcX, y: srcY, width: totalW, height: totalH)
|
||||||
}
|
}
|
||||||
|
@ -145,16 +145,7 @@ class TextRenderer: NodeRenderer {
|
|||||||
NSAttributedStringKey.font: font
|
NSAttributedStringKey.font: font
|
||||||
]
|
]
|
||||||
let textSize = NSString(string: text.text).size(withAttributes: textAttributes)
|
let textSize = NSString(string: text.text).size(withAttributes: textAttributes)
|
||||||
var alignmentOffset = CGFloat(0)
|
return -text.align.align(x: textSize.width)
|
||||||
switch text.align {
|
|
||||||
case Align.mid:
|
|
||||||
alignmentOffset = textSize.width / 2
|
|
||||||
case Align.max:
|
|
||||||
alignmentOffset = textSize.width
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return -alignmentOffset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func getTextColor(_ fill: Fill) -> MColor {
|
fileprivate func getTextColor(_ fill: Fill) -> MColor {
|
||||||
|
@ -11,26 +11,34 @@ import CoreGraphics
|
|||||||
|
|
||||||
open class SVGParser {
|
open class SVGParser {
|
||||||
|
|
||||||
|
fileprivate class ViewBoxParams {
|
||||||
|
var svgSize: Size?
|
||||||
|
var viewBox: Rect?
|
||||||
|
var scalingMode: AspectRatio?
|
||||||
|
var xAligningMode: Align?
|
||||||
|
var yAligningMode: Align?
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse an SVG file identified by the specified bundle, name and file extension.
|
/// Parse an SVG file identified by the specified bundle, name and file extension.
|
||||||
/// - returns: Root node of the corresponding Macaw scene.
|
/// - returns: Root node of the corresponding Macaw scene.
|
||||||
open class func parse(bundle: Bundle, path: String, ofType: String = "svg", transformHelper: TransformHelperProtocol = TransformHelper()) throws -> Node {
|
open class func parse(bundle: Bundle, path: String, ofType: String = "svg") throws -> Node {
|
||||||
guard let fullPath = bundle.path(forResource: path, ofType: ofType) else {
|
guard let fullPath = bundle.path(forResource: path, ofType: ofType) else {
|
||||||
throw SVGParserError.noSuchFile(path: "\(path).\(ofType)")
|
throw SVGParserError.noSuchFile(path: "\(path).\(ofType)")
|
||||||
}
|
}
|
||||||
let text = try String(contentsOfFile: fullPath, encoding: String.Encoding.utf8)
|
let text = try String(contentsOfFile: fullPath, encoding: String.Encoding.utf8)
|
||||||
return try SVGParser.parse(text: text, transformHelper: transformHelper)
|
return try SVGParser.parse(text: text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an SVG file identified by the specified name and file extension.
|
/// Parse an SVG file identified by the specified name and file extension.
|
||||||
/// - returns: Root node of the corresponding Macaw scene.
|
/// - returns: Root node of the corresponding Macaw scene.
|
||||||
open class func parse(path: String, ofType: String = "svg", transformHelper: TransformHelperProtocol = TransformHelper()) throws -> Node {
|
open class func parse(path: String, ofType: String = "svg") throws -> Node {
|
||||||
return try SVGParser.parse(bundle: Bundle.main, path: path, ofType: ofType, transformHelper: transformHelper)
|
return try SVGParser.parse(bundle: Bundle.main, path: path, ofType: ofType)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the specified content of an SVG file.
|
/// Parse the specified content of an SVG file.
|
||||||
/// - returns: Root node of the corresponding Macaw scene.
|
/// - returns: Root node of the corresponding Macaw scene.
|
||||||
open class func parse(text: String, transformHelper: TransformHelperProtocol = TransformHelper()) throws -> Node {
|
open class func parse(text: String) throws -> Node {
|
||||||
return SVGParser(text, transformHelper: transformHelper).parse()
|
return SVGParser(text).parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
let availableStyleAttributes = ["stroke", "stroke-width", "stroke-opacity", "stroke-dasharray", "stroke-linecap", "stroke-linejoin",
|
let availableStyleAttributes = ["stroke", "stroke-width", "stroke-opacity", "stroke-dasharray", "stroke-linecap", "stroke-linejoin",
|
||||||
@ -42,13 +50,6 @@ open class SVGParser {
|
|||||||
fileprivate let xmlString: String
|
fileprivate let xmlString: String
|
||||||
fileprivate let initialPosition: Transform
|
fileprivate let initialPosition: Transform
|
||||||
|
|
||||||
fileprivate var svgSize: Size?
|
|
||||||
fileprivate var viewBox: Rect?
|
|
||||||
fileprivate var scalingMode: ScaleMode?
|
|
||||||
fileprivate var xAligningMode: AlignMode?
|
|
||||||
fileprivate var yAligningMode: AlignMode?
|
|
||||||
fileprivate var transformHelper: TransformHelperProtocol!
|
|
||||||
|
|
||||||
fileprivate var nodes = [Node]()
|
fileprivate var nodes = [Node]()
|
||||||
fileprivate var defNodes = [String: XMLIndexer]()
|
fileprivate var defNodes = [String: XMLIndexer]()
|
||||||
fileprivate var defFills = [String: Fill]()
|
fileprivate var defFills = [String: Fill]()
|
||||||
@ -68,34 +69,38 @@ open class SVGParser {
|
|||||||
|
|
||||||
fileprivate typealias PathCommand = (type: PathCommandType, expression: String, absolute: Bool)
|
fileprivate typealias PathCommand = (type: PathCommandType, expression: String, absolute: Bool)
|
||||||
|
|
||||||
fileprivate init(_ string: String, pos: Transform = Transform(), transformHelper: TransformHelperProtocol = TransformHelper()) {
|
fileprivate init(_ string: String, pos: Transform = Transform()) {
|
||||||
self.xmlString = string
|
self.xmlString = string
|
||||||
self.initialPosition = pos
|
self.initialPosition = pos
|
||||||
self.transformHelper = transformHelper
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func parse() -> Group {
|
fileprivate func parse() -> Group {
|
||||||
let parsedXml = SWXMLHash.parse(xmlString)
|
let parsedXml = SWXMLHash.parse(xmlString)
|
||||||
prepareSvg(parsedXml.children)
|
|
||||||
|
var viewBoxParams: ViewBoxParams?
|
||||||
|
for child in parsedXml.children {
|
||||||
|
if let element = child.element {
|
||||||
|
if element.name == "svg" {
|
||||||
|
viewBoxParams = parseViewBox(element)
|
||||||
|
prepareSvg(child.children)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
parseSvg(parsedXml.children)
|
parseSvg(parsedXml.children)
|
||||||
|
|
||||||
let group = Group(contents: self.nodes, place: initialPosition)
|
let group = Group(contents: self.nodes, place: initialPosition)
|
||||||
addViewBoxClip(toNode: group)
|
if let viewBoxParams = viewBoxParams {
|
||||||
|
addViewBoxClip(toNode: group, viewBoxParams: viewBoxParams)
|
||||||
|
}
|
||||||
return group
|
return group
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func prepareSvg(_ children: [XMLIndexer]) {
|
fileprivate func prepareSvg(_ children: [XMLIndexer]) {
|
||||||
children.forEach { child in
|
children.forEach { child in
|
||||||
if let element = child.element {
|
|
||||||
if element.name == "svg" {
|
|
||||||
parseViewBox(element)
|
|
||||||
prepareSvg(child.children)
|
|
||||||
} else {
|
|
||||||
prepareSvg(child)
|
prepareSvg(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func prepareSvg(_ node: XMLIndexer) {
|
fileprivate func prepareSvg(_ node: XMLIndexer) {
|
||||||
if let element = node.element {
|
if let element = node.element {
|
||||||
@ -123,57 +128,59 @@ open class SVGParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func addViewBoxClip(toNode node: Node) {
|
fileprivate func addViewBoxClip(toNode node: Node, viewBoxParams params: ViewBoxParams) {
|
||||||
|
|
||||||
guard let viewBox = viewBox else { return }
|
guard let viewBox = params.viewBox else { return }
|
||||||
node.clip = viewBox
|
node.clip = viewBox
|
||||||
|
|
||||||
guard let scalingMode = scalingMode else { return }
|
guard let scalingMode = params.scalingMode else { return }
|
||||||
guard let svgSize = svgSize else { return }
|
guard let svgSize = params.svgSize else { return }
|
||||||
|
|
||||||
if scalingMode == .aspectFill {
|
if scalingMode == .slice {
|
||||||
// setup new clipping to slice extra bits
|
// setup new clipping to slice extra bits
|
||||||
node.clip = svgSize.aspectFit(viewBox)
|
node.clip = svgSize.aspectFit(viewBox)
|
||||||
}
|
}
|
||||||
|
|
||||||
transformHelper.scalingMode = scalingMode
|
let transformHelper = TransformHelper(scalingMode: scalingMode, xAligningMode: params.xAligningMode, yAligningMode: params.yAligningMode)
|
||||||
transformHelper.xAligningMode = xAligningMode
|
|
||||||
transformHelper.yAligningMode = yAligningMode
|
|
||||||
node.place = transformHelper.getTransformOf(viewBox, into: Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h))
|
node.place = transformHelper.getTransformOf(viewBox, into: Rect(x: 0, y: 0, w: svgSize.w, h: svgSize.h))
|
||||||
|
|
||||||
// move to (0, 0)
|
// move to (0, 0)
|
||||||
node.place = node.place.move(dx: -viewBox.x, dy: -viewBox.y)
|
node.place = node.place.move(dx: -viewBox.x, dy: -viewBox.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func parseViewBox(_ element: SWXMLHash.XMLElement) {
|
fileprivate func parseViewBox(_ element: SWXMLHash.XMLElement) -> ViewBoxParams {
|
||||||
|
let params = ViewBoxParams()
|
||||||
|
|
||||||
if let w = getDoubleValue(element, attribute: "width"), let h = getDoubleValue(element, attribute: "height") {
|
if let w = getDoubleValue(element, attribute: "width"), let h = getDoubleValue(element, attribute: "height") {
|
||||||
svgSize = Size(w: w, h: h)
|
params.svgSize = Size(w: w, h: h)
|
||||||
}
|
}
|
||||||
if let viewBoxString = element.allAttributes["viewBox"]?.text {
|
if let viewBoxString = element.allAttributes["viewBox"]?.text {
|
||||||
let nums = viewBoxString.components(separatedBy: .whitespaces).map{ Double($0) }
|
let nums = viewBoxString.components(separatedBy: .whitespaces).map{ Double($0) }
|
||||||
if nums.count == 4, let x = nums[0], let y = nums[1], let w = nums[2], let h = nums[3] {
|
if nums.count == 4, let x = nums[0], let y = nums[1], let w = nums[2], let h = nums[3] {
|
||||||
viewBox = Rect(x: x, y: y, w: w, h: h)
|
params.viewBox = Rect(x: x, y: y, w: w, h: h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let contentModeString = element.allAttributes["preserveAspectRatio"]?.text {
|
if let contentModeString = element.allAttributes["preserveAspectRatio"]?.text {
|
||||||
let strings = contentModeString.components(separatedBy: CharacterSet(charactersIn: " "))
|
let strings = contentModeString.components(separatedBy: CharacterSet(charactersIn: " "))
|
||||||
if strings.count == 1 { // none
|
if strings.count == 1 { // none
|
||||||
scalingMode = ScaleMode(rawValue: strings[0])
|
params.scalingMode = parseAspectRatio(strings[0])
|
||||||
return
|
return params
|
||||||
}
|
}
|
||||||
guard strings.count == 2 else { return }
|
guard strings.count == 2 else { return params }
|
||||||
|
|
||||||
let alignString = strings[0]
|
let alignString = strings[0]
|
||||||
var xAlign = alignString.prefix(4).lowercased()
|
var xAlign = alignString.prefix(4).lowercased()
|
||||||
xAlign.remove(at: xAlign.startIndex)
|
xAlign.remove(at: xAlign.startIndex)
|
||||||
xAligningMode = AlignMode(rawValue: xAlign)
|
params.xAligningMode = parseAlign(xAlign)
|
||||||
|
|
||||||
var yAlign = alignString.suffix(4).lowercased()
|
var yAlign = alignString.suffix(4).lowercased()
|
||||||
yAlign.remove(at: yAlign.startIndex)
|
yAlign.remove(at: yAlign.startIndex)
|
||||||
yAligningMode = AlignMode(rawValue: yAlign)
|
params.yAligningMode = parseAlign(yAlign)
|
||||||
|
|
||||||
scalingMode = ScaleMode(rawValue: strings[1])
|
params.scalingMode = parseAspectRatio(strings[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func parseNode(_ node: XMLIndexer, groupStyle: [String: String] = [:]) -> Node? {
|
fileprivate func parseNode(_ node: XMLIndexer, groupStyle: [String: String] = [:]) -> Node? {
|
||||||
@ -341,6 +348,26 @@ open class SVGParser {
|
|||||||
return parseTransformationAttribute(transformAttribute)
|
return parseTransformationAttribute(transformAttribute)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func parseAlign(_ string: String) -> Align {
|
||||||
|
if string == "min" {
|
||||||
|
return .min
|
||||||
|
}
|
||||||
|
if string == "mid" {
|
||||||
|
return .mid
|
||||||
|
}
|
||||||
|
return .max
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func parseAspectRatio(_ string: String) -> AspectRatio {
|
||||||
|
if string == "meet" {
|
||||||
|
return .meet
|
||||||
|
}
|
||||||
|
if string == "slice" {
|
||||||
|
return .slice
|
||||||
|
}
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
var count = 0
|
var count = 0
|
||||||
|
|
||||||
fileprivate func parseTransformationAttribute(_ attributes: String, transform: Transform = Transform()) -> Transform {
|
fileprivate func parseTransformationAttribute(_ attributes: String, transform: Transform = Transform()) -> Transform {
|
||||||
|
@ -159,14 +159,13 @@ open class SVGSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func alignToSVG(_ align: Align) -> String {
|
fileprivate func alignToSVG(_ align: Align) -> String {
|
||||||
switch align {
|
if align === Align.mid {
|
||||||
case .mid:
|
|
||||||
return " text-anchor=\"middle\" "
|
return " text-anchor=\"middle\" "
|
||||||
case .max:
|
|
||||||
return " text-anchor=\"end "
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
if align === Align.max {
|
||||||
|
return " text-anchor=\"end "
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func baselineToSVG(_ baseline: Baseline) -> String {
|
fileprivate func baselineToSVG(_ baseline: Baseline) -> String {
|
||||||
|
@ -56,44 +56,44 @@ open class SVGView: MacawView {
|
|||||||
let svgWidth = nodeBounds.width
|
let svgWidth = nodeBounds.width
|
||||||
let svgHeight = nodeBounds.height
|
let svgHeight = nodeBounds.height
|
||||||
|
|
||||||
let transformHelper = TransformHelper()
|
var scalingMode = AspectRatio.none
|
||||||
transformHelper.scalingMode = .noScaling
|
var xAligningMode = Align.mid
|
||||||
transformHelper.xAligningMode = .mid
|
var yAligningMode = Align.mid
|
||||||
transformHelper.yAligningMode = .mid
|
|
||||||
|
|
||||||
switch self.contentMode {
|
switch contentMode {
|
||||||
case .scaleToFill:
|
case .scaleToFill:
|
||||||
transformHelper.scalingMode = .scaleToFill
|
scalingMode = .none
|
||||||
case .scaleAspectFill:
|
case .scaleAspectFill:
|
||||||
transformHelper.scalingMode = .aspectFill
|
scalingMode = .slice
|
||||||
case .scaleAspectFit:
|
case .scaleAspectFit:
|
||||||
transformHelper.scalingMode = .aspectFit
|
scalingMode = .meet
|
||||||
case .center:
|
case .center:
|
||||||
break
|
break
|
||||||
case .top:
|
case .top:
|
||||||
transformHelper.yAligningMode = .min
|
yAligningMode = .min
|
||||||
case .bottom:
|
case .bottom:
|
||||||
transformHelper.yAligningMode = .max
|
yAligningMode = .max
|
||||||
case .left:
|
case .left:
|
||||||
transformHelper.xAligningMode = .min
|
xAligningMode = .min
|
||||||
case .right:
|
case .right:
|
||||||
transformHelper.xAligningMode = .max
|
xAligningMode = .max
|
||||||
case .topLeft:
|
case .topLeft:
|
||||||
transformHelper.xAligningMode = .min
|
xAligningMode = .min
|
||||||
transformHelper.yAligningMode = .min
|
yAligningMode = .min
|
||||||
case .topRight:
|
case .topRight:
|
||||||
transformHelper.xAligningMode = .max
|
xAligningMode = .max
|
||||||
transformHelper.yAligningMode = .min
|
yAligningMode = .min
|
||||||
case .bottomLeft:
|
case .bottomLeft:
|
||||||
transformHelper.xAligningMode = .min
|
xAligningMode = .min
|
||||||
transformHelper.yAligningMode = .max
|
yAligningMode = .max
|
||||||
case .bottomRight:
|
case .bottomRight:
|
||||||
transformHelper.xAligningMode = .max
|
xAligningMode = .max
|
||||||
transformHelper.yAligningMode = .max
|
yAligningMode = .max
|
||||||
case .redraw:
|
case .redraw:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let transformHelper = TransformHelper(scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode)
|
||||||
svgNode.place = transformHelper.getTransformOf(Rect(x: 0, y: 0, w: Double(svgWidth), h: Double(svgHeight)), into: Rect(cgRect: viewBounds))
|
svgNode.place = transformHelper.getTransformOf(Rect(x: 0, y: 0, w: Double(svgWidth), h: Double(svgHeight)), into: Rect(cgRect: viewBounds))
|
||||||
|
|
||||||
rootNode.contents = [svgNode]
|
rootNode.contents = [svgNode]
|
||||||
|
@ -1,33 +1,25 @@
|
|||||||
|
|
||||||
public enum AlignMode : String {
|
|
||||||
case max
|
|
||||||
case mid
|
|
||||||
case min
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ScaleMode : String {
|
|
||||||
case aspectFit = "meet"
|
|
||||||
case aspectFill = "slice"
|
|
||||||
case scaleToFill = "none"
|
|
||||||
case noScaling = "noScaling"
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol TransformHelperProtocol {
|
public protocol TransformHelperProtocol {
|
||||||
|
|
||||||
var scalingMode: ScaleMode? { get set }
|
static var standard: TransformHelperProtocol { get }
|
||||||
var xAligningMode: AlignMode? { get set }
|
|
||||||
var yAligningMode: AlignMode? { get set }
|
|
||||||
|
|
||||||
func getTransformOf(_ rect: Rect, into rectToFitIn: Rect) -> Transform
|
func getTransformOf(_ rect: Rect, into rectToFitIn: Rect) -> Transform
|
||||||
}
|
}
|
||||||
|
|
||||||
open class TransformHelper: TransformHelperProtocol {
|
open class TransformHelper: TransformHelperProtocol {
|
||||||
|
|
||||||
public var scalingMode: ScaleMode?
|
public let scalingMode: AspectRatio!
|
||||||
public var xAligningMode: AlignMode?
|
public let xAligningMode: Align!
|
||||||
public var yAligningMode: AlignMode?
|
public let yAligningMode: Align!
|
||||||
|
|
||||||
public init() { }
|
public init(scalingMode: AspectRatio, xAligningMode: Align? = Align.min, yAligningMode: Align? = Align.min) {
|
||||||
|
self.scalingMode = scalingMode
|
||||||
|
self.xAligningMode = xAligningMode
|
||||||
|
self.yAligningMode = yAligningMode
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var standard: TransformHelperProtocol {
|
||||||
|
return TransformHelper(scalingMode: .none)
|
||||||
|
}
|
||||||
|
|
||||||
public func getTransformOf(_ rect: Rect, into rectToFitIn: Rect) -> Transform {
|
public func getTransformOf(_ rect: Rect, into rectToFitIn: Rect) -> Transform {
|
||||||
|
|
||||||
@ -41,7 +33,7 @@ open class TransformHelper: TransformHelperProtocol {
|
|||||||
var newHeight = rectToFitIn.h
|
var newHeight = rectToFitIn.h
|
||||||
|
|
||||||
switch scalingMode {
|
switch scalingMode {
|
||||||
case .aspectFit:
|
case .meet:
|
||||||
if heightRatio < widthRatio {
|
if heightRatio < widthRatio {
|
||||||
newWidth = rect.w * heightRatio
|
newWidth = rect.w * heightRatio
|
||||||
} else {
|
} else {
|
||||||
@ -51,7 +43,7 @@ open class TransformHelper: TransformHelperProtocol {
|
|||||||
sx: newWidth / rect.w,
|
sx: newWidth / rect.w,
|
||||||
sy: newHeight / rect.h
|
sy: newHeight / rect.h
|
||||||
)
|
)
|
||||||
case .aspectFill:
|
case .slice:
|
||||||
if heightRatio > widthRatio {
|
if heightRatio > widthRatio {
|
||||||
newWidth = rect.w * heightRatio
|
newWidth = rect.w * heightRatio
|
||||||
} else {
|
} else {
|
||||||
@ -61,47 +53,16 @@ open class TransformHelper: TransformHelperProtocol {
|
|||||||
sx: newWidth / rect.w,
|
sx: newWidth / rect.w,
|
||||||
sy: newHeight / rect.h
|
sy: newHeight / rect.h
|
||||||
)
|
)
|
||||||
case .scaleToFill:
|
case .none:
|
||||||
result = result.scale(
|
result = result.scale(
|
||||||
sx: Double(widthRatio),
|
sx: Double(widthRatio),
|
||||||
sy: Double(heightRatio)
|
sy: Double(heightRatio)
|
||||||
)
|
)
|
||||||
case .noScaling:
|
|
||||||
newWidth = rect.w
|
|
||||||
newHeight = rect.h
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let xAligningMode = xAligningMode else { return result }
|
let dx = xAligningMode.align(x: rectToFitIn.w, y: newWidth) / (newWidth / rect.w)
|
||||||
switch xAligningMode {
|
let dy = yAligningMode.align(x: rectToFitIn.h, y: newHeight) / (newHeight / rect.h)
|
||||||
case .min:
|
result = result.move(dx: dx, dy: dy)
|
||||||
break
|
|
||||||
case .mid:
|
|
||||||
result = result.move(
|
|
||||||
dx: (rectToFitIn.w / 2 - newWidth / 2) / (newWidth / rect.w),
|
|
||||||
dy: 0
|
|
||||||
)
|
|
||||||
case .max:
|
|
||||||
result = result.move(
|
|
||||||
dx: (rectToFitIn.w - newWidth) / (newWidth / rect.w),
|
|
||||||
dy: 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let yAligningMode = yAligningMode else { return result }
|
|
||||||
switch yAligningMode {
|
|
||||||
case .min:
|
|
||||||
break
|
|
||||||
case .mid:
|
|
||||||
result = result.move(
|
|
||||||
dx: 0,
|
|
||||||
dy: (rectToFitIn.h / 2 - newHeight / 2) / (newHeight / rect.h)
|
|
||||||
)
|
|
||||||
case .max:
|
|
||||||
result = result.move(
|
|
||||||
dx: 0,
|
|
||||||
dy: (rectToFitIn.h - newHeight) / (newHeight / rect.h)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user