1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-09-20 01:18:59 +03:00

Add public extensions to convert Macaw primitives to system CG primitives

This commit is contained in:
Yuri Strot 2018-05-14 20:34:16 +07:00
parent 23fe575a31
commit 9c97666620
15 changed files with 146 additions and 128 deletions

View File

@ -1,42 +0,0 @@
import Foundation
#if os(iOS)
import UIKit
#endif
public extension Rect {
convenience init(cgRect: CGRect) {
self.init(
x: Double(cgRect.origin.x),
y: Double(cgRect.origin.y),
w: Double(cgRect.size.width),
h: Double(cgRect.size.height))
}
func cgRect() -> CGRect {
return CGRect(x: self.x, y: self.y, width: self.w, height: self.h)
}
func applyTransform(_ transform: Transform) -> Rect {
// TODO: Rewrite using math
let cgTransform = RenderUtils.mapTransform(transform)
return Rect(cgRect: self.cgRect().applying(cgTransform))
}
public func description() -> String {
return "x: \(self.x) y:\(self.y) w:\(self.w) h:\(self.h)"
}
}
public extension Point {
public func cgPoint() -> CGPoint {
return CGPoint(x: self.x, y: self.y)
}
public func description() -> String {
return "x: \(self.x) y:\(self.y)"
}
}

View File

@ -39,7 +39,7 @@ class AnimationCache {
let calculatedBounds = customBounds ?? node.bounds()
if let shapeBounds = calculatedBounds {
let cgRect = shapeBounds.cgRect()
let cgRect = shapeBounds.toCG()
let origFrame = CGRect(x: 0.0, y: 0.0,
width: round(cgRect.width),
@ -54,7 +54,7 @@ class AnimationCache {
layer.renderTransform = CGAffineTransform(translationX: -1.0 * cgRect.origin.x, y: -1.0 * cgRect.origin.y)
let nodeTransform = RenderUtils.mapTransform(AnimationUtils.absolutePosition(node))
let nodeTransform = AnimationUtils.absolutePosition(node).toCG()
layer.transform = CATransform3DMakeAffineTransform(nodeTransform)
// Clip
@ -100,7 +100,7 @@ class AnimationCache {
let origBounds = Rect(x: 0.0, y: 0.0, w: 1.0, h: 1.0)
let startTransform = animFunc(0.0)
let startBounds = origBounds.applyTransform(startTransform)
let startBounds = origBounds.applying(startTransform)
var startArea = startBounds.w * startBounds.h
// zero scale protection
@ -113,7 +113,7 @@ class AnimationCache {
let step = 0.1
while t <= 1.0 {
let currentTransform = animFunc(t)
let currentBounds = origBounds.applyTransform(currentTransform)
let currentBounds = origBounds.applying(currentTransform)
let currentArea = currentBounds.w * currentBounds.h
if maxArea < currentArea {
maxArea = currentArea

View File

@ -76,12 +76,12 @@ func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anim
animation.onProgressUpdate?(t)
}
layer.path = RenderUtils.toCGPath(fromLocus)
layer.path = fromLocus.toCGPath()
// Stroke
if let stroke = mutatingShape.stroke {
if let color = stroke.fill as? Color {
layer.strokeColor = RenderUtils.mapColor(color)
layer.strokeColor = color.toCG()
} else {
layer.strokeColor = MColor.black.cgColor
}
@ -97,7 +97,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anim
// Fill
if let color = mutatingShape.fill as? Color {
layer.fillColor = RenderUtils.mapColor(color)
layer.fillColor = color.toCG()
} else {
layer.fillColor = MColor.clear.cgColor
}
@ -112,8 +112,8 @@ func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anim
fileprivate func pathAnimation(from: Locus, to: Locus, duration: Double, renderTransform: CGAffineTransform) -> CAAnimation {
var transform = renderTransform
let fromPath = RenderUtils.toCGPath(from).copy(using: &transform)
let toPath = RenderUtils.toCGPath(to).copy(using: &transform)
let fromPath = from.toCGPath().copy(using: &transform)
let toPath = to.toCGPath().copy(using: &transform)
let animation = CABasicAnimation(keyPath: "path")
animation.fromValue = fromPath

View File

@ -87,12 +87,12 @@ func addShapeAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animati
animation.onProgressUpdate?(t)
}
layer.path = RenderUtils.toCGPath(fromShape.form)
layer.path = fromShape.form.toCGPath()
// Stroke
if let stroke = shape.stroke {
if let color = stroke.fill as? Color {
layer.strokeColor = RenderUtils.mapColor(color)
layer.strokeColor = color.toCG()
} else {
layer.strokeColor = MColor.black.cgColor
}
@ -108,7 +108,7 @@ func addShapeAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animati
// Fill
if let color = shape.fill as? Color {
layer.fillColor = RenderUtils.mapColor(color)
layer.fillColor = color.toCG()
} else {
layer.fillColor = MColor.clear.cgColor
}
@ -127,8 +127,8 @@ fileprivate func generateShapeAnimation(from: Shape, to: Shape, duration: Double
// Shape
// Path
var transform = renderTransform
let fromPath = RenderUtils.toCGPath(from.form).copy(using: &transform)
let toPath = RenderUtils.toCGPath(to.form).copy(using: &transform)
let fromPath = from.form.toCGPath().copy(using: &transform)
let toPath = to.form.toCGPath().copy(using: &transform)
let pathAnimation = CABasicAnimation(keyPath: "path")
pathAnimation.fromValue = fromPath
@ -143,8 +143,8 @@ fileprivate func generateShapeAnimation(from: Shape, to: Shape, duration: Double
if fromFillColor != toFillColor {
let fillAnimation = CABasicAnimation(keyPath: "fillColor")
fillAnimation.fromValue = RenderUtils.mapColor(fromFillColor)
fillAnimation.toValue = RenderUtils.mapColor(toFillColor)
fillAnimation.fromValue = fromFillColor.toCG()
fillAnimation.toValue = toFillColor.toCG()
fillAnimation.duration = duration
group.animations?.append(fillAnimation)
@ -170,8 +170,8 @@ fileprivate func generateShapeAnimation(from: Shape, to: Shape, duration: Double
if fromStrokeColor != toStrokeColor {
let strokeColorAnimation = CABasicAnimation(keyPath: "strokeColor")
strokeColorAnimation.fromValue = RenderUtils.mapColor(fromStrokeColor)
strokeColorAnimation.toValue = RenderUtils.mapColor(toStrokeColor)
strokeColorAnimation.fromValue = fromStrokeColor.toCG()
strokeColorAnimation.toValue = toStrokeColor.toCG()
strokeColorAnimation.duration = duration
group.animations?.append(strokeColorAnimation)

View File

@ -103,7 +103,7 @@ func transformAnimationByFunc(_ node: Node, valueFunc: (Double) -> Transform, du
timeValues.append(dt)
let value = AnimationUtils.absoluteTransform(node, pos: valueFunc(offset + dt))
let cgValue = CATransform3DMakeAffineTransform(RenderUtils.mapTransform(value))
let cgValue = CATransform3DMakeAffineTransform(value.toCG())
transformValues.append(cgValue)
}

View File

@ -69,7 +69,7 @@ open class Group: Node {
var union: Rect?
contents.forEach { node in
guard let nodeBounds = node.bounds()?.applyTransform(node.place) else {
guard let nodeBounds = node.bounds()?.applying(node.place) else {
return
}

View File

@ -127,10 +127,6 @@ extension MBezierPath {
close()
}
func reversing() -> MBezierPath {
return self.reversed
}
func addLine(to: NSPoint) {
self.line(to: to)
}

View File

@ -79,7 +79,7 @@ class NodeRenderer {
return
}
ctx.cgContext!.concatenate(RenderUtils.mapTransform(node.place))
ctx.cgContext!.concatenate(node.place.toCG())
applyClip()
directRender(force: force, opacity: node.opacity * opacity)
}
@ -117,9 +117,9 @@ class NodeRenderer {
ctx.restoreGState()
}
ctx.concatenate(RenderUtils.mapTransform(place))
ctx.concatenate(place.toCG())
applyClip()
let loc = location.applying(RenderUtils.mapTransform(inverted))
let loc = location.applying(inverted.toCG())
let result = doFindNodeAt(location: CGPoint(x: loc.x, y: loc.y), ctx: ctx)
return result
}

View File

@ -7,29 +7,6 @@ import AppKit
#endif
class RenderUtils {
class func mapColor(_ color: Color) -> CGColor {
let red = CGFloat(Double(color.r()) / 255.0)
let green = CGFloat(Double(color.g()) / 255.0)
let blue = CGFloat(Double(color.b()) / 255.0)
let alpha = CGFloat(Double(color.a()) / 255.0)
return MColor(red: red, green: green, blue: blue, alpha: alpha).cgColor
}
class func mapTransform(_ t: Transform) -> CGAffineTransform {
return CGAffineTransform(a: CGFloat(t.m11), b: CGFloat(t.m12), c: CGFloat(t.m21),
d: CGFloat(t.m22), tx: CGFloat(t.dx), ty: CGFloat(t.dy))
}
class func mapLineJoin(_ join: LineJoin?) -> CGLineJoin {
switch join {
case LineJoin.round?:
return CGLineJoin.round
case LineJoin.bevel?:
return CGLineJoin.bevel
default:
return CGLineJoin.miter
}
}
class func mapLineJoinToString(_ join: LineJoin?) -> String {
switch join {
@ -42,17 +19,6 @@ class RenderUtils {
}
}
class func mapLineCap(_ cap: LineCap?) -> CGLineCap {
switch cap {
case LineCap.round?:
return CGLineCap.round
case LineCap.square?:
return CGLineCap.square
default:
return CGLineCap.butt
}
}
class func mapLineCapToString(_ cap: LineCap?) -> String {
switch cap {
case LineCap.round?:
@ -154,7 +120,7 @@ class RenderUtils {
class func toBezierPath(_ locus: Locus) -> MBezierPath {
if let round = locus as? RoundRect {
let corners = CGSize(width: CGFloat(round.rx), height: CGFloat(round.ry))
return MBezierPath(roundedRect: newCGRect(round.rect), byRoundingCorners:
return MBezierPath(roundedRect: round.rect.toCG(), byRoundingCorners:
MRectCorner.allCorners, cornerRadii: corners)
} else if let arc = locus as? Arc {
if arc.ellipse.rx == arc.ellipse.ry {
@ -177,9 +143,9 @@ class RenderUtils {
} else if let polygon = locus as? Polyline {
return pointsToPath(polygon.points)
} else if let rect = locus as? Rect {
return MBezierPath(rect: rect.cgRect())
return MBezierPath(rect: rect.toCG())
} else if let circle = locus as? Circle {
return MBezierPath(ovalIn: circle.bounds().cgRect())
return MBezierPath(ovalIn: circle.bounds().toCG())
} else if let path = locus as? Path {
return toBezierPath(path)
}
@ -583,8 +549,4 @@ class RenderUtils {
class func num2bool(_ double: Double) -> Bool {
return double > 0.5 ? true : false
}
fileprivate class func newCGRect(_ rect: Rect) -> CGRect {
return CGRect(x: CGFloat(rect.x), y: CGFloat(rect.y), width: CGFloat(rect.w), height: CGFloat(rect.h))
}
}

View File

@ -99,7 +99,7 @@ class ShapeRenderer: NodeRenderer {
let ry = ellipse.ry
ctx.addEllipse(in: CGRect(x: cx - rx, y: cy - ry, width: rx * 2, height: ry * 2))
} else {
ctx.addPath(RenderUtils.toCGPath(locus))
ctx.addPath(locus.toCGPath())
}
}
@ -141,7 +141,7 @@ class ShapeRenderer: NodeRenderer {
}
if let fillColor = fill as? Color {
let color = RenderUtils.applyOpacity(fillColor, opacity: opacity)
ctx!.setFillColor(RenderUtils.mapColor(color))
ctx!.setFillColor(color.toCG())
} else if let gradient = fill as? Gradient {
drawGradient(gradient, ctx: ctx, opacity: opacity)
} else {
@ -170,8 +170,8 @@ class ShapeRenderer: NodeRenderer {
fileprivate func setStrokeAttributes(_ stroke: Stroke, ctx: CGContext?) {
ctx!.setLineWidth(CGFloat(stroke.width))
ctx!.setLineJoin(RenderUtils.mapLineJoin(stroke.join))
ctx!.setLineCap(RenderUtils.mapLineCap(stroke.cap))
ctx!.setLineJoin(stroke.join.toCG())
ctx!.setLineCap(stroke.cap.toCG())
if !stroke.dashes.isEmpty {
ctx?.setLineDash(phase: CGFloat(stroke.offset),
lengths: stroke.dashes.map{ CGFloat($0) })
@ -183,7 +183,7 @@ class ShapeRenderer: NodeRenderer {
return
}
let color = RenderUtils.applyOpacity(strokeColor, opacity: opacity)
ctx!.setStrokeColor(RenderUtils.mapColor(color))
ctx!.setStrokeColor(color.toCG())
}
fileprivate func gradientStroke(_ stroke: Stroke, ctx: CGContext?, opacity: Double) {
@ -201,7 +201,7 @@ class ShapeRenderer: NodeRenderer {
for stop in gradient.stops {
stops.append(CGFloat(stop.offset))
let color = RenderUtils.applyOpacity(stop.color, opacity: opacity)
colors.append(RenderUtils.mapColor(color))
colors.append(color.toCG())
}
if let gradient = gradient as? LinearGradient {

View File

@ -59,7 +59,7 @@ class TextRenderer: NodeRenderer {
}
override func doFindNodeAt(location: CGPoint, ctx: CGContext) -> Node? {
guard let contains = node()?.bounds()?.cgRect().contains(location) else {
guard let contains = node()?.bounds()?.toCG().contains(location) else {
return .none
}
@ -149,9 +149,9 @@ class TextRenderer: NodeRenderer {
if let color = fill as? Color {
#if os(iOS)
return MColor(cgColor: RenderUtils.mapColor(color))
return MColor(cgColor: color.toCG())
#elseif os(OSX)
return MColor(cgColor: RenderUtils.mapColor(color)) ?? .black
return MColor(cgColor: color.toCG()) ?? .black
#endif
}

View File

@ -1065,7 +1065,7 @@ open class SVGParser {
if let gradientTransform = element.allAttributes["gradientTransform"]?.text {
let transform = parseTransformationAttribute(gradientTransform)
let cgTransform = RenderUtils.mapTransform(transform)
let cgTransform = transform.toCG()
let point1 = CGPoint(x: x1, y: y1).applying(cgTransform)
x1 = point1.x.doubleValue
@ -1127,7 +1127,7 @@ open class SVGParser {
if let gradientTransform = element.allAttributes["gradientTransform"]?.text {
let transform = parseTransformationAttribute(gradientTransform)
let cgTransform = RenderUtils.mapTransform(transform)
let cgTransform = transform.toCG()
let point1 = CGPoint(x: cx, y: cy).applying(cgTransform)
cx = point1.x.doubleValue

View File

@ -90,7 +90,7 @@ open class SVGView: MacawView {
}
let contentLayout = SVGContentLayout(scalingMode: scalingMode, xAligningMode: xAligningMode, yAligningMode: yAligningMode)
svgNode.place = contentLayout.layout(rect: nodeBounds, into: Rect(cgRect: bounds))
svgNode.place = contentLayout.layout(rect: nodeBounds, into: bounds.toMacaw())
rootNode.contents = [svgNode]
node = rootNode

View File

@ -0,0 +1,102 @@
//
// CGMappings
// Created by Yuri Strot on 5/14/18.
//
import Foundation
#if os(iOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif
public extension Color {
public func toCG() -> CGColor {
let red = CGFloat(Double(r()) / 255.0)
let green = CGFloat(Double(g()) / 255.0)
let blue = CGFloat(Double(b()) / 255.0)
let alpha = CGFloat(Double(a()) / 255.0)
return MColor(red: red, green: green, blue: blue, alpha: alpha).cgColor
}
}
public extension Transform {
public func toCG() -> CGAffineTransform {
return CGAffineTransform(a: CGFloat(m11), b: CGFloat(m12), c: CGFloat(m21),
d: CGFloat(m22), tx: CGFloat(dx), ty: CGFloat(dy))
}
}
public extension LineJoin {
public func toCG() -> CGLineJoin {
switch self {
case .round:
return .round
case .bevel:
return CGLineJoin.bevel
default:
return CGLineJoin.miter
}
}
}
public extension LineCap {
public func toCG() -> CGLineCap {
switch self {
case .round:
return CGLineCap.round
case .square:
return CGLineCap.square
default:
return CGLineCap.butt
}
}
}
public extension Rect {
public func toCG() -> CGRect {
return CGRect(x: self.x, y: self.y, width: self.w, height: self.h)
}
func applying(_ t: Transform) -> Rect {
return toCG().applying(t.toCG()).toMacaw()
}
}
public extension CGRect {
public func toMacaw() -> Rect {
return Rect(x: Double(origin.x),
y: Double(origin.y),
w: Double(size.width),
h: Double(size.height))
}
}
public extension Point {
public func toCG() -> CGPoint {
return CGPoint(x: self.x, y: self.y)
}
}
public extension Locus {
public func toCGPath() -> CGPath {
return RenderUtils.toCGPath(self)
}
}

View File

@ -19,7 +19,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
didSet {
if let canvas = node as? SVGCanvas, let layout = canvas.layout as? SVGNodeLayout {
layout.layout(node: canvas, in: Rect(cgRect: bounds))
layout.layout(node: canvas, in: bounds.toMacaw())
}
nodesMap.add(node, view: self)
@ -205,7 +205,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
}
let inverted = node.place.invert()!
let loc = location.applying(RenderUtils.mapTransform(inverted))
let loc = location.applying(inverted.toCG())
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
let point = TouchPoint(id: id, location: Point(x: Double(loc.x), y: Double(loc.y)))
@ -248,7 +248,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
let currentTouch = touches[currentIndex]
let location = CGPoint(x: currentTouch.x, y: currentTouch.y)
let inverted = currentNode.place.invert()!
let loc = location.applying(RenderUtils.mapTransform(inverted))
let loc = location.applying(inverted.toCG())
let point = TouchPoint(id: currentTouch.id, location: Point(x: Double(loc.x), y: Double(loc.y)))
points.append(point)
}
@ -277,7 +277,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
let inverted = node.place.invert()!
let location = CGPoint(x: touch.x, y: touch.y)
let loc = location.applying(RenderUtils.mapTransform(inverted))
let loc = location.applying(inverted.toCG())
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
let point = TouchPoint(id: id, location: Point(x: Double(loc.x), y: Double(loc.y)))
let touchEvent = TouchEvent(node: node, points: [point])
@ -328,7 +328,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
foundNodes.forEach { node in
let inverted = node.place.invert()!
let loc = location.applying(RenderUtils.mapTransform(inverted))
let loc = location.applying(inverted.toCG())
let event = TapEvent(node: node, location: Point(x: Double(loc.x), y: Double(loc.y)))
node.handleTap(event)
}
@ -365,7 +365,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
foundNodes.forEach { node in
let inverted = node.place.invert()!
let loc = location.applying(RenderUtils.mapTransform(inverted))
let loc = location.applying(inverted.toCG())
let event = TapEvent(node: node, location: Point(x: Double(loc.x), y: Double(loc.y)))
node.handleLongTap(event, touchBegan: recognizer.state == .began)
}