From d9a5b27e94972757da4a088d338fd945f78b9e02 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Fri, 14 Feb 2020 14:13:53 +0700 Subject: [PATCH 01/48] Add correct zoom-scroll handling --- .../AnimationsHierarchyViewController.swift | 28 ++++++++++++++++++- .../Cache/AnimationCache.swift | 2 +- .../MorphingGenerator.swift | 2 +- .../OpacityGenerator.swift | 2 +- .../ShapeAnimationGenerator.swift | 2 +- .../TransformGenerator.swift | 2 +- Source/views/MacawView.swift | 13 +++++---- 7 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Example/Example/Examples/AnimationsHierarchy/AnimationsHierarchyViewController.swift b/Example/Example/Examples/AnimationsHierarchy/AnimationsHierarchyViewController.swift index 394c4ebc..e1f554d3 100644 --- a/Example/Example/Examples/AnimationsHierarchy/AnimationsHierarchyViewController.swift +++ b/Example/Example/Examples/AnimationsHierarchy/AnimationsHierarchyViewController.swift @@ -13,6 +13,9 @@ class AnimationsHierarchyViewController: UIViewController { @IBOutlet weak var animView: MacawView! + var startCallbacks: [()->()] = [] + var stopCallbacks: [()->()] = [] + override func viewDidLoad() { super.viewDidLoad() @@ -20,6 +23,22 @@ class AnimationsHierarchyViewController: UIViewController { animView.zoom.enable() } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + startCallbacks.forEach { + $0() + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + stopCallbacks.forEach { + $0() + } + } + func createTree(height: Int) -> Node { let rect = Rect(w: 10, h: 10) @@ -52,7 +71,14 @@ class AnimationsHierarchyViewController: UIViewController { func createLeaf(childForm: Locus, xDelta: Double, yDelta: Double) -> Group { let inset = childForm.bounds().w / 2 let leaf = Shape(form: childForm, fill: Color.teal, place: .move(dx: -inset, dy: -inset)) - leaf.placeVar.animation(angle: 2 * .pi, during: 5).cycle().play() + + let animation = leaf.placeVar.animation(angle: 2 * .pi, during: 5).cycle() + startCallbacks.append({ + animation.play() + }) + stopCallbacks.append({ + animation.stop() + }) let leafGroup = [leaf].group(place: .move(dx: xDelta, dy: yDelta)) leaf.onTap { _ in diff --git a/Source/animation/types/animation_generators/Cache/AnimationCache.swift b/Source/animation/types/animation_generators/Cache/AnimationCache.swift index d54b2c7a..b049f19d 100644 --- a/Source/animation/types/animation_generators/Cache/AnimationCache.swift +++ b/Source/animation/types/animation_generators/Cache/AnimationCache.swift @@ -8,7 +8,7 @@ import AppKit class AnimationUtils { - class func layerForNodeRenderer(_ renderer: NodeRenderer, _ context: AnimationContext, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true) -> ShapeLayer { + class func layerForNodeRenderer(_ renderer: NodeRenderer, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true) -> ShapeLayer { let node = renderer.node if let cachedLayer = renderer.layer { diff --git a/Source/animation/types/animation_generators/MorphingGenerator.swift b/Source/animation/types/animation_generators/MorphingGenerator.swift index 212cbd9d..09d54277 100644 --- a/Source/animation/types/animation_generators/MorphingGenerator.swift +++ b/Source/animation/types/animation_generators/MorphingGenerator.swift @@ -30,7 +30,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte let toLocus = morphingAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0) let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration() - let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation, shouldRenderContent: false) + let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false) // Creating proper animation let generatedAnimation = pathAnimation( diff --git a/Source/animation/types/animation_generators/OpacityGenerator.swift b/Source/animation/types/animation_generators/OpacityGenerator.swift index 71e9441c..9bcdde1b 100644 --- a/Source/animation/types/animation_generators/OpacityGenerator.swift +++ b/Source/animation/types/animation_generators/OpacityGenerator.swift @@ -58,7 +58,7 @@ func addOpacityAnimation(_ animation: BasicAnimation, _ context: AnimationContex completion() } - let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation) + let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation) let animationId = animation.ID layer.add(generatedAnimation, forKey: animationId) animation.removeFunc = { [weak layer] in diff --git a/Source/animation/types/animation_generators/ShapeAnimationGenerator.swift b/Source/animation/types/animation_generators/ShapeAnimationGenerator.swift index 2d3dd9dd..b64023ca 100644 --- a/Source/animation/types/animation_generators/ShapeAnimationGenerator.swift +++ b/Source/animation/types/animation_generators/ShapeAnimationGenerator.swift @@ -30,7 +30,7 @@ func addShapeAnimation(_ animation: BasicAnimation, _ context: AnimationContext, let toShape = shapeAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0) let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration() - let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation, shouldRenderContent: false) + let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false) // Creating proper animation let generatedAnimation = generateShapeAnimation(context, diff --git a/Source/animation/types/animation_generators/TransformGenerator.swift b/Source/animation/types/animation_generators/TransformGenerator.swift index 98612d1c..bf91385c 100644 --- a/Source/animation/types/animation_generators/TransformGenerator.swift +++ b/Source/animation/types/animation_generators/TransformGenerator.swift @@ -22,7 +22,7 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont let transactionsDisabled = CATransaction.disableActions() CATransaction.setDisableActions(true) - let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation, shouldRenderContent: true) + let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: true) // Creating proper animation let generatedAnimation = transformAnimationByFunc(transformAnimation, diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 0b918eb9..ae01a108 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -142,8 +142,14 @@ open class MacawView: MView, MGestureRecognizerDelegate { } private func onZoomChange(t: Transform) { + // TODO: actually we should track all changes placeManager.setZoom(place: t) - self.setNeedsDisplay() + placeManager.setLayout(place: layoutHelper.getTransform(renderer!, contentLayout, bounds.size.toMacaw())) + + if let viewLayer = mLayer { + let deltaTransform = CATransform3DMakeAffineTransform(self.place.toCG()) + viewLayer.transform = CATransform3DConcat(viewLayer.transform, deltaTransform) + } } func initializeView() { @@ -195,11 +201,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { return } renderer.calculateZPositionRecursively() - - // TODO: actually we should track all changes - placeManager.setLayout(place: layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw())) - - ctx.concatenate(self.place.toCG()) renderer.render(in: ctx, force: false, opacity: node.opacity) } From bac14c1e85c024fac6acd235d4d52b2f103e49fe Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Tue, 17 Mar 2020 17:16:18 +0700 Subject: [PATCH 02/48] Fixes for examples --- Source/animation/AnimationImpl.swift | 11 ++++++----- Source/animation/AnimationProducer.swift | 4 ++-- Source/animation/types/AnimationSequence.swift | 12 ++++-------- .../CombinationAnimationGenerator.swift | 2 ++ .../animation_generators/MorphingGenerator.swift | 1 + .../animation_generators/OpacityGenerator.swift | 10 ++++++++++ .../animation_generators/TransformGenerator.swift | 10 ++++++++++ 7 files changed, 35 insertions(+), 15 deletions(-) diff --git a/Source/animation/AnimationImpl.swift b/Source/animation/AnimationImpl.swift index 0b79032e..a6fca1ab 100644 --- a/Source/animation/AnimationImpl.swift +++ b/Source/animation/AnimationImpl.swift @@ -24,11 +24,8 @@ class BasicAnimation: Animation { weak var node: Node? { didSet { - node?.animations.append(self) - if let group = node as? Group { - for node in group.contents { - node.animations.append(self) - } + if !(self is CombineAnimation || self is AnimationSequence || self is EmptyAnimation) { + node?.animations.append(self) } } } @@ -104,6 +101,8 @@ class BasicAnimation: Animation { } removeFunc?() + node?.animations.removeAll { $0 === self } + nodeRenderer?.freeLayer() } override open func pause() { @@ -115,6 +114,8 @@ class BasicAnimation: Animation { } removeFunc?() + node?.animations.removeAll { $0 === self } + nodeRenderer?.freeLayer() } override func state() -> AnimationState { diff --git a/Source/animation/AnimationProducer.swift b/Source/animation/AnimationProducer.swift index b6c8887d..85728e26 100644 --- a/Source/animation/AnimationProducer.swift +++ b/Source/animation/AnimationProducer.swift @@ -143,9 +143,9 @@ class AnimationProducer { } // MARK: - Sequence animation - func addAnimationSequence(_ animationSequnce: Animation, + func addAnimationSequence(_ animationSequence: Animation, _ context: AnimationContext) { - guard let sequence = animationSequnce as? AnimationSequence else { + guard let sequence = animationSequence as? AnimationSequence else { return } diff --git a/Source/animation/types/AnimationSequence.swift b/Source/animation/types/AnimationSequence.swift index abb711e7..951e0bac 100644 --- a/Source/animation/types/AnimationSequence.swift +++ b/Source/animation/types/AnimationSequence.swift @@ -27,21 +27,17 @@ internal class AnimationSequence: BasicAnimation { open override func stop() { super.stop() - guard let active = animations.first(where: { $0.isActive() }) else { - return + animations.forEach { animation in + animation.stop() } - - active.stop() } open override func pause() { super.pause() - guard let active = animations.first(where: { $0.isActive() }) else { - return + animations.forEach { animation in + animation.pause() } - - active.pause() } open override func play() { diff --git a/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift b/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift index ee38fa7d..d600b4a5 100644 --- a/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift +++ b/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift @@ -116,6 +116,7 @@ extension AnimationProducer { func addCombineAnimation(_ combineAnimation: Animation, _ context: AnimationContext) { guard let combine = combineAnimation as? CombineAnimation, let renderer = combine.nodeRenderer, + let node = combine.node, let view = renderer.view else { return } @@ -176,6 +177,7 @@ extension AnimationProducer { } combine.removeFunc = { + node.animations.removeAll { $0 === combine } animations.forEach { animation in animation.removeFunc?() } diff --git a/Source/animation/types/animation_generators/MorphingGenerator.swift b/Source/animation/types/animation_generators/MorphingGenerator.swift index 09d54277..b077c26b 100644 --- a/Source/animation/types/animation_generators/MorphingGenerator.swift +++ b/Source/animation/types/animation_generators/MorphingGenerator.swift @@ -94,6 +94,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte let animationId = animation.ID layer.add(generatedAnimation, forKey: animationId) animation.removeFunc = { [weak layer] in + shape.animations.removeAll { $0 === animation } layer?.removeAnimation(forKey: animationId) } diff --git a/Source/animation/types/animation_generators/OpacityGenerator.swift b/Source/animation/types/animation_generators/OpacityGenerator.swift index 9bcdde1b..dc58b087 100644 --- a/Source/animation/types/animation_generators/OpacityGenerator.swift +++ b/Source/animation/types/animation_generators/OpacityGenerator.swift @@ -47,6 +47,15 @@ func addOpacityAnimation(_ animation: BasicAnimation, _ context: AnimationContex node.opacityVar.value = opacityAnimation.getVFunc()(1.0) } + CATransaction.begin() + CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) + renderer.layer?.animationLayer.opacity = Float(node.opacity) + CATransaction.commit() + + if !animation.paused { + animation.removeFunc?() + } + renderer.freeLayer() if !animation.cycled && @@ -62,6 +71,7 @@ func addOpacityAnimation(_ animation: BasicAnimation, _ context: AnimationContex let animationId = animation.ID layer.add(generatedAnimation, forKey: animationId) animation.removeFunc = { [weak layer] in + node.animations.removeAll { $0 === animation } layer?.removeAnimation(forKey: animationId) } diff --git a/Source/animation/types/animation_generators/TransformGenerator.swift b/Source/animation/types/animation_generators/TransformGenerator.swift index bf91385c..5e45377d 100644 --- a/Source/animation/types/animation_generators/TransformGenerator.swift +++ b/Source/animation/types/animation_generators/TransformGenerator.swift @@ -54,6 +54,15 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont node.placeVar.value = transformAnimation.getVFunc()(1.0) } + CATransaction.begin() + CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) + renderer.layer?.animationLayer.transform = CATransform3DMakeAffineTransform(node.place.toCG()) + CATransaction.commit() + + if !animation.paused { + animation.removeFunc?() + } + renderer.freeLayer() if !animation.cycled && @@ -68,6 +77,7 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont let animationId = animation.ID layer.add(generatedAnimation, forKey: animationId) animation.removeFunc = { [weak layer] in + node.animations.removeAll { $0 === animation } layer?.removeAnimation(forKey: animationId) } From bb5e9e0e395a402177d72ca4173f2843b88787c0 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Tue, 17 Mar 2020 17:43:17 +0700 Subject: [PATCH 03/48] Fix --- Source/animation/types/AnimationSequence.swift | 12 ++++++++---- Source/svg/SVGParser.swift | 6 +++--- Source/views/MacawView.swift | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Source/animation/types/AnimationSequence.swift b/Source/animation/types/AnimationSequence.swift index 951e0bac..abb711e7 100644 --- a/Source/animation/types/AnimationSequence.swift +++ b/Source/animation/types/AnimationSequence.swift @@ -27,17 +27,21 @@ internal class AnimationSequence: BasicAnimation { open override func stop() { super.stop() - animations.forEach { animation in - animation.stop() + guard let active = animations.first(where: { $0.isActive() }) else { + return } + + active.stop() } open override func pause() { super.pause() - animations.forEach { animation in - animation.pause() + guard let active = animations.first(where: { $0.isActive() }) else { + return } + + active.pause() } open override func play() { diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index e4bf1630..f3e0a7c6 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -465,7 +465,7 @@ open class SVGParser { if let units = element.allAttributes["patternContentUnits"]?.text, units == "objectBoundingBox" { contentUserSpace = false } - + var contentNode: Node? if pattern.children.isEmpty { if let parentPattern = parentPattern { @@ -483,7 +483,7 @@ open class SVGParser { } contentNode = Group(contents: shapes) } - + return UserSpacePattern(content: contentNode!, bounds: bounds, userSpace: userSpace, @@ -1599,7 +1599,7 @@ open class SVGParser { guard let element = stop.element else { return .none } - + var offset: Double = 0 // This is default value, value can be omitted if let parsedOffset = getDoubleValueFromPercentage(element, attribute: "offset") { offset = parsedOffset diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index ae01a108..d5279252 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -147,7 +147,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { placeManager.setLayout(place: layoutHelper.getTransform(renderer!, contentLayout, bounds.size.toMacaw())) if let viewLayer = mLayer { - let deltaTransform = CATransform3DMakeAffineTransform(self.place.toCG()) + let deltaTransform = CATransform3DMakeAffineTransform(self.place.toCG()) viewLayer.transform = CATransform3DConcat(viewLayer.transform, deltaTransform) } } From 0f96b24db1a33063f018a489a7e2dc47bba3afb0 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Fri, 10 Apr 2020 15:58:19 +0700 Subject: [PATCH 04/48] Split zoom and layout matrices --- Source/views/MacawView.swift | 24 ++++++++++++------------ Source/views/MacawZoom.swift | 16 ++++++++++++++-- Source/views/ShapeLayer.swift | 3 --- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index d5279252..bb2a13a4 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -141,17 +141,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { self.init(node: Group(), coder: aDecoder) } - private func onZoomChange(t: Transform) { - // TODO: actually we should track all changes - placeManager.setZoom(place: t) - placeManager.setLayout(place: layoutHelper.getTransform(renderer!, contentLayout, bounds.size.toMacaw())) - - if let viewLayer = mLayer { - let deltaTransform = CATransform3DMakeAffineTransform(self.place.toCG()) - viewLayer.transform = CATransform3DConcat(viewLayer.transform, deltaTransform) - } - } - func initializeView() { self.contentLayout = .none self.context = RenderContext(view: self) @@ -187,6 +176,12 @@ open class MacawView: MView, MGestureRecognizerDelegate { setNeedsDisplay() } + private func onZoomChange(t: Transform) { + if let viewLayer = mLayer { + viewLayer.transform = CATransform3DMakeAffineTransform(t.toCG()) + } + } + override open func draw(_ rect: CGRect) { context.cgContext = MGraphicsGetCurrentContext() guard let ctx = context.cgContext else { @@ -200,6 +195,11 @@ open class MacawView: MView, MGestureRecognizerDelegate { guard let renderer = renderer else { return } + + // TODO: actually we should track all changes + placeManager.setLayout(place: layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw())) + ctx.concatenate(self.place.toCG()) + renderer.calculateZPositionRecursively() renderer.render(in: ctx, force: false, opacity: node.opacity) } @@ -335,7 +335,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { private func convert(touches: Set) -> [MTouchEvent] { return touches.map { touch -> MTouchEvent in - let location = touch.location(in: self) + let location = touch.applyCurrentLayerTransform(self) let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) } diff --git a/Source/views/MacawZoom.swift b/Source/views/MacawZoom.swift index 1a9532cd..8ffd2701 100644 --- a/Source/views/MacawZoom.swift +++ b/Source/views/MacawZoom.swift @@ -81,6 +81,9 @@ open class MacawZoom { } private func getNewZoom() -> ZoomData { + if !trackMove && !trackScale && !trackRotate { + return zoomData + } if touches.isEmpty || (touches.count == 1 && !trackMove) { return zoomData } @@ -144,7 +147,7 @@ fileprivate class TouchData { let point: Point convenience init(touch: MTouch, in view: MView) { - self.init(touch: touch, point: touch.location(in: view).toMacaw()) + self.init(touch: touch, point: touch.applyCurrentLayerTransform(view)) } init(touch: MTouch, point: Point) { @@ -153,7 +156,16 @@ fileprivate class TouchData { } func current(in view: MView) -> Point { - return touch.location(in: view).toMacaw() + return touch.applyCurrentLayerTransform(view) } } + +extension MTouch { + + func applyCurrentLayerTransform(_ view: MView) -> Point { + let layerTransform = CATransform3DGetAffineTransform(view.mLayer!.transform).toMacaw() + let location = self.location(in: view).toMacaw() + return layerTransform.apply(to: location) + } +} diff --git a/Source/views/ShapeLayer.swift b/Source/views/ShapeLayer.swift index 2551bdc5..8ffab1f2 100644 --- a/Source/views/ShapeLayer.swift +++ b/Source/views/ShapeLayer.swift @@ -17,9 +17,6 @@ class ShapeLayer: CAShapeLayer { return } - let renderContext = RenderContext(view: .none) - renderContext.cgContext = ctx - renderer?.directRender(in: ctx, force: isForceRenderingEnabled) } } From 9e317d78d947cb0229cdfb23b65d2b8708311990 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Tue, 14 Apr 2020 14:30:20 +0700 Subject: [PATCH 05/48] Add transparent view for touches --- .../CombinationAnimationGenerator.swift | 1 - Source/views/MacawView.swift | 60 ++++++++++++++++--- Source/views/MacawZoom.swift | 24 +++----- 3 files changed, 59 insertions(+), 26 deletions(-) diff --git a/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift b/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift index 8165361e..9a6f92eb 100644 --- a/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift +++ b/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift @@ -115,7 +115,6 @@ extension AnimationProducer { // MARK: - Combine animation func addCombineAnimation(_ combineAnimation: Animation, _ context: AnimationContext) { guard let combine = combineAnimation as? CombineAnimation, - let renderer = combine.nodeRenderer, let node = combine.node else { return } diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index bb2a13a4..59a0c91f 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -10,6 +10,31 @@ import AppKit /// MacawView is a main class used to embed Macaw scene into your Cocoa UI. /// You could create your own view extended from MacawView with predefined scene. /// + +internal class TouchInterceptionView: MView { + weak var macawView: MacawView? + + open override func touchesBegan(_ touches: Set, with event: MEvent?) { + super.touchesBegan(touches, with: event) + macawView?.mTouchesBegan(touches, with: event) + } + + open override func touchesMoved(_ touches: Set, with event: MEvent?) { + super.touchesMoved(touches, with: event) + macawView?.mTouchesMoved(touches, with: event) + } + + open override func touchesEnded(_ touches: Set, with event: MEvent?) { + super.touchesEnded(touches, with: event) + macawView?.mTouchesEnded(touches, with: event) + } + + override open func touchesCancelled(_ touches: Set, with event: MEvent?) { + super.touchesCancelled(touches, with: event) + macawView?.mTouchesCancelled(touches, with: event) + } +} + open class MacawView: MView, MGestureRecognizerDelegate { /// Scene root node @@ -53,6 +78,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { } private let placeManager = RootPlaceManager() + internal let touchInterceptionView = TouchInterceptionView() override open var frame: CGRect { didSet { @@ -133,18 +159,36 @@ open class MacawView: MView, MGestureRecognizerDelegate { public override init(frame: CGRect) { super.init(frame: frame) zoom.initialize(view: self, onChange: onZoomChange) - - initializeView() } @objc public convenience required init?(coder aDecoder: NSCoder) { self.init(node: Group(), coder: aDecoder) } + open override func didMoveToWindow() { + super.didMoveToWindow() + + initializeView() + } + func initializeView() { self.contentLayout = .none self.context = RenderContext(view: self) + if let superview = self.superview { + self.isUserInteractionEnabled = false + touchInterceptionView.isMultipleTouchEnabled = true + touchInterceptionView.macawView = self + touchInterceptionView.backgroundColor = .clear + superview.insertSubview(touchInterceptionView, aboveSubview: self) + + touchInterceptionView.translatesAutoresizingMaskIntoConstraints = false + touchInterceptionView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true + touchInterceptionView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true + touchInterceptionView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true + touchInterceptionView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + } + let tapRecognizer = MTapGestureRecognizer(target: self, action: #selector(MacawView.handleTap)) let longTapRecognizer = MLongPressGestureRecognizer(target: self, action: #selector(MacawView.handleLongTap(recognizer:))) let panRecognizer = MPanGestureRecognizer(target: self, action: #selector(MacawView.handlePan)) @@ -163,12 +207,12 @@ open class MacawView: MView, MGestureRecognizerDelegate { rotationRecognizer.cancelsTouchesInView = false pinchRecognizer.cancelsTouchesInView = false - self.removeGestureRecognizers() - self.addGestureRecognizer(tapRecognizer) - self.addGestureRecognizer(longTapRecognizer) - self.addGestureRecognizer(panRecognizer) - self.addGestureRecognizer(rotationRecognizer) - self.addGestureRecognizer(pinchRecognizer) + touchInterceptionView.removeGestureRecognizers() + touchInterceptionView.addGestureRecognizer(tapRecognizer) + touchInterceptionView.addGestureRecognizer(longTapRecognizer) + touchInterceptionView.addGestureRecognizer(panRecognizer) + touchInterceptionView.addGestureRecognizer(rotationRecognizer) + touchInterceptionView.addGestureRecognizer(pinchRecognizer) } open override func layoutSubviews() { diff --git a/Source/views/MacawZoom.swift b/Source/views/MacawZoom.swift index 8ffd2701..33296d26 100644 --- a/Source/views/MacawZoom.swift +++ b/Source/views/MacawZoom.swift @@ -16,7 +16,7 @@ import AppKit open class MacawZoom { - private var view: MView! + private var view: MacawView! private var onChange: ((Transform) -> Void)! private var touches = [TouchData]() private var zoomData = ZoomData() @@ -50,7 +50,7 @@ open class MacawZoom { onChange(zoomData.transform()) } - func initialize(view: MView, onChange: @escaping ((Transform) -> Void)) { + func initialize(view: MacawView, onChange: @escaping ((Transform) -> Void)) { self.view = view self.onChange = onChange } @@ -96,15 +96,7 @@ open class MacawZoom { let e2 = touches[1].current(in: view) let scale = trackScale ? e1.distance(to: e2) / s1.distance(to: s2) : 1 let a = trackRotate ? (e1 - e2).angle() - (s1 - s2).angle() : 0 - var offset = Size.zero - if trackMove { - let sina = sin(a) - let cosa = cos(a) - let w = e1.x - scale * (s1.x * cosa - s1.y * sina) - let h = e1.y - scale * (s1.x * sina + s1.y * cosa) - offset = Size(w: w, h: h) - } - return ZoomData(offset: offset, scale: scale, angle: a).combine(with: zoomData) + return ZoomData(offset: .zero, scale: scale, angle: a).combine(with: zoomData) } } @@ -146,7 +138,7 @@ fileprivate class TouchData { let touch: MTouch let point: Point - convenience init(touch: MTouch, in view: MView) { + convenience init(touch: MTouch, in view: MacawView) { self.init(touch: touch, point: touch.applyCurrentLayerTransform(view)) } @@ -155,7 +147,7 @@ fileprivate class TouchData { self.point = point } - func current(in view: MView) -> Point { + func current(in view: MacawView) -> Point { return touch.applyCurrentLayerTransform(view) } @@ -163,9 +155,7 @@ fileprivate class TouchData { extension MTouch { - func applyCurrentLayerTransform(_ view: MView) -> Point { - let layerTransform = CATransform3DGetAffineTransform(view.mLayer!.transform).toMacaw() - let location = self.location(in: view).toMacaw() - return layerTransform.apply(to: location) + func applyCurrentLayerTransform(_ view: MacawView) -> Point { + return self.location(in: view.touchInterceptionView).toMacaw() } } From 2aed94aec2102504e44cb4cf6f33da9e5304ae52 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 16 Apr 2020 15:46:02 +0700 Subject: [PATCH 06/48] Split on MacawView and DrawingView --- Source/animation/AnimationProducer.swift | 2 +- Source/export/MacawView+PDF.swift | 4 +- Source/render/GroupRenderer.swift | 2 +- Source/render/ImageRenderer.swift | 2 +- Source/render/NodeRenderer.swift | 4 +- Source/render/RenderContext.swift | 4 +- Source/render/RenderUtils.swift | 2 +- Source/render/ShapeRenderer.swift | 2 +- Source/render/TextRenderer.swift | 2 +- Source/views/MacawView.swift | 295 +++++++++++++---------- Source/views/MacawZoom.swift | 11 +- 11 files changed, 188 insertions(+), 142 deletions(-) diff --git a/Source/animation/AnimationProducer.swift b/Source/animation/AnimationProducer.swift index 9a37fcd1..7ae54475 100644 --- a/Source/animation/AnimationProducer.swift +++ b/Source/animation/AnimationProducer.swift @@ -198,7 +198,7 @@ class AnimationProducer { } // MARK: - Stored animation - func addStoredAnimations(_ node: Node, _ view: MacawView) { + func addStoredAnimations(_ node: Node, _ view: DrawingView) { addStoredAnimations(node, AnimationContext()) } diff --git a/Source/export/MacawView+PDF.swift b/Source/export/MacawView+PDF.swift index b723ec69..e54a1f17 100644 --- a/Source/export/MacawView+PDF.swift +++ b/Source/export/MacawView+PDF.swift @@ -27,8 +27,8 @@ public extension MacawView { y: -size.height / bounds.height ) - context.cgContext = ctx - renderer?.render(in: ctx, force: false, opacity: node.opacity) + drawingView.context.cgContext = ctx + drawingView.renderer?.render(in: ctx, force: false, opacity: node.opacity) ctx.endPDFPage() } diff --git a/Source/render/GroupRenderer.swift b/Source/render/GroupRenderer.swift index 420c9628..2d572327 100644 --- a/Source/render/GroupRenderer.swift +++ b/Source/render/GroupRenderer.swift @@ -13,7 +13,7 @@ class GroupRenderer: NodeRenderer { return group } - init(group: Group, view: MacawView?, parentRenderer: GroupRenderer? = nil) { + init(group: Group, view: DrawingView?, parentRenderer: GroupRenderer? = nil) { self.group = group super.init(node: group, view: view, parentRenderer: parentRenderer) updateRenderers() diff --git a/Source/render/ImageRenderer.swift b/Source/render/ImageRenderer.swift index c9a571cc..693c96d8 100644 --- a/Source/render/ImageRenderer.swift +++ b/Source/render/ImageRenderer.swift @@ -17,7 +17,7 @@ class ImageRenderer: NodeRenderer { return image } - init(image: Image, view: MacawView?, parentRenderer: GroupRenderer? = nil) { + init(image: Image, view: DrawingView?, parentRenderer: GroupRenderer? = nil) { self.image = image super.init(node: image, view: view, parentRenderer: parentRenderer) } diff --git a/Source/render/NodeRenderer.swift b/Source/render/NodeRenderer.swift index 4f5a7cd6..11a8b8b8 100644 --- a/Source/render/NodeRenderer.swift +++ b/Source/render/NodeRenderer.swift @@ -22,7 +22,7 @@ class CachedLayer { class NodeRenderer { - weak var view: MacawView? + weak var view: DrawingView? var sceneLayer: CALayer? { return view?.mLayer } @@ -71,7 +71,7 @@ class NodeRenderer { fatalError("Unsupported") } - init(node: Node, view: MacawView?, parentRenderer: GroupRenderer? = nil) { + init(node: Node, view: DrawingView?, parentRenderer: GroupRenderer? = nil) { self.view = view self.parentRenderer = parentRenderer diff --git a/Source/render/RenderContext.swift b/Source/render/RenderContext.swift index 92c85092..82759f0f 100644 --- a/Source/render/RenderContext.swift +++ b/Source/render/RenderContext.swift @@ -5,10 +5,10 @@ import UIKit #endif class RenderContext { - weak var view: MacawView? + weak var view: DrawingView? var cgContext: CGContext? - init(view: MacawView?) { + init(view: DrawingView?) { self.view = view self.cgContext = nil } diff --git a/Source/render/RenderUtils.swift b/Source/render/RenderUtils.swift index b2166717..9c8dec10 100644 --- a/Source/render/RenderUtils.swift +++ b/Source/render/RenderUtils.swift @@ -16,7 +16,7 @@ class RenderUtils { return p } - class func createNodeRenderer(_ node: Node, view: MacawView?, parentRenderer: GroupRenderer? = nil) -> NodeRenderer { + class func createNodeRenderer(_ node: Node, view: DrawingView?, parentRenderer: GroupRenderer? = nil) -> NodeRenderer { if let group = node as? Group { return GroupRenderer(group: group, view: view, parentRenderer: parentRenderer) } else if let shape = node as? Shape { diff --git a/Source/render/ShapeRenderer.swift b/Source/render/ShapeRenderer.swift index 69f5f417..48345d86 100644 --- a/Source/render/ShapeRenderer.swift +++ b/Source/render/ShapeRenderer.swift @@ -10,7 +10,7 @@ class ShapeRenderer: NodeRenderer { var shape: Shape - init(shape: Shape, view: MacawView?, parentRenderer: GroupRenderer? = nil) { + init(shape: Shape, view: DrawingView?, parentRenderer: GroupRenderer? = nil) { self.shape = shape super.init(node: shape, view: view, parentRenderer: parentRenderer) } diff --git a/Source/render/TextRenderer.swift b/Source/render/TextRenderer.swift index 292e0f3a..c34d373c 100644 --- a/Source/render/TextRenderer.swift +++ b/Source/render/TextRenderer.swift @@ -13,7 +13,7 @@ class TextRenderer: NodeRenderer { return text } - init(text: Text, view: MacawView?, parentRenderer: GroupRenderer? = nil) { + init(text: Text, view: DrawingView?, parentRenderer: GroupRenderer? = nil) { self.text = text super.init(node: text, view: view, parentRenderer: parentRenderer) } diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 59a0c91f..fa8f838f 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -11,31 +11,178 @@ import AppKit /// You could create your own view extended from MacawView with predefined scene. /// -internal class TouchInterceptionView: MView { - weak var macawView: MacawView? +open class MacawView: MView { + + internal var drawingView = DrawingView() + + public let zoom = MacawZoom() + + open var node: Node { + get { return drawingView.node } + set { drawingView.node = newValue } + } + + open var contentLayout: ContentLayout { + get { return drawingView.contentLayout } + set { drawingView.contentLayout = newValue } + } + + open override var contentMode: MViewContentMode { + get { return drawingView.contentMode } + set { drawingView.contentMode = newValue } + } + + open var place: Transform { + get { return drawingView.place } + } + + open var placeVar: Variable { + get { return drawingView.placeVar } + } + + open override var frame: CGRect { + get { return drawingView.frame } + set { drawingView.frame = newValue } + } + + override open var intrinsicContentSize: CGSize { + get { return drawingView.intrinsicContentSize } + } + + #if os(OSX) + open override var layer: CALayer? { + didSet { + guard self.layer != nil else { + return + } + initializeView() + + drawingView.renderer = RenderUtils.createNodeRenderer(node, view: self) + } + } + #endif + + @objc public convenience required init?(coder aDecoder: NSCoder) { + self.init(node: Group(), coder: aDecoder) + } + + @objc public init?(node: Node, coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + if let drawingView = DrawingView(node: node, coder: aDecoder) { + self.drawingView = drawingView + } + + zoom.initialize(view: self, onChange: onZoomChange) + } + + public convenience init(node: Node, frame: CGRect) { + self.init(frame: frame) + + self.drawingView = DrawingView(node: node, frame: frame) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + + self.drawingView = DrawingView(frame: frame) + + zoom.initialize(view: self, onChange: onZoomChange) + } + + private func onZoomChange(t: Transform) { + if let viewLayer = drawingView.mLayer { + viewLayer.transform = CATransform3DMakeAffineTransform(t.toCG()) + } + } + + open override func didMoveToWindow() { + super.didMoveToWindow() + + initializeView() + } + + func initializeView() { + + if !self.subviews.contains(drawingView) { + self.backgroundColor = .white + self.clipsToBounds = true + self.addSubview(drawingView) + drawingView.backgroundColor = .white + drawingView.isUserInteractionEnabled = false + drawingView.initializeView() + + drawingView.translatesAutoresizingMaskIntoConstraints = false + drawingView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true + drawingView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true + drawingView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true + drawingView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + } + + let tapRecognizer = MTapGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleTap(recognizer:))) + let longTapRecognizer = MLongPressGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleLongTap(recognizer:))) + let panRecognizer = MPanGestureRecognizer(target: drawingView, action: #selector(DrawingView.handlePan)) + let rotationRecognizer = MRotationGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleRotation)) + let pinchRecognizer = MPinchGestureRecognizer(target: drawingView, action: #selector(DrawingView.handlePinch)) + + tapRecognizer.delegate = drawingView + longTapRecognizer.delegate = drawingView + panRecognizer.delegate = drawingView + rotationRecognizer.delegate = drawingView + pinchRecognizer.delegate = drawingView + + tapRecognizer.cancelsTouchesInView = false + longTapRecognizer.cancelsTouchesInView = false + panRecognizer.cancelsTouchesInView = false + rotationRecognizer.cancelsTouchesInView = false + pinchRecognizer.cancelsTouchesInView = false + + self.removeGestureRecognizers() + self.addGestureRecognizer(tapRecognizer) + self.addGestureRecognizer(longTapRecognizer) + self.addGestureRecognizer(panRecognizer) + self.addGestureRecognizer(rotationRecognizer) + self.addGestureRecognizer(pinchRecognizer) + } open override func touchesBegan(_ touches: Set, with event: MEvent?) { super.touchesBegan(touches, with: event) - macawView?.mTouchesBegan(touches, with: event) + zoom.touchesBegan(touches) + + drawingView.touchesBegan(touchPoints: convert(touches: touches)) } open override func touchesMoved(_ touches: Set, with event: MEvent?) { super.touchesMoved(touches, with: event) - macawView?.mTouchesMoved(touches, with: event) + zoom.touchesMoved(touches) + + drawingView.touchesMoved(touchPoints: convert(touches: touches)) } open override func touchesEnded(_ touches: Set, with event: MEvent?) { super.touchesEnded(touches, with: event) - macawView?.mTouchesEnded(touches, with: event) + zoom.touchesEnded(touches) + + drawingView.touchesEnded(touchPoints: convert(touches: touches)) } override open func touchesCancelled(_ touches: Set, with event: MEvent?) { - super.touchesCancelled(touches, with: event) - macawView?.mTouchesCancelled(touches, with: event) + super.touchesEnded(touches, with: event) + zoom.touchesEnded(touches) + + drawingView.touchesEnded(touchPoints: convert(touches: touches)) + } + + private func convert(touches: Set) -> [MTouchEvent] { + return touches.map { touch -> MTouchEvent in + let location = touch.location(in: self).toMacaw() + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } } } -open class MacawView: MView, MGestureRecognizerDelegate { +internal class DrawingView: MView, MGestureRecognizerDelegate { /// Scene root node open var node: Node = Group() { @@ -67,8 +214,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } - public let zoom = MacawZoom() - public var place: Transform { return placeManager.placeVar.value } @@ -77,9 +222,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { return placeManager.placeVar } - private let placeManager = RootPlaceManager() - internal let touchInterceptionView = TouchInterceptionView() - override open var frame: CGRect { didSet { super.frame = frame @@ -94,6 +236,14 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } + override open var intrinsicContentSize: CGSize { + if let bounds = node.bounds { + return bounds.size().toCG() + } else { + return CGSize(width: MNoIntrinsicMetric(), height: MNoIntrinsicMetric()) + } + } + override open func didMoveToSuperview() { super.didMoveToSuperview() @@ -104,14 +254,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { animationProducer.addStoredAnimations(node, self) } - override open var intrinsicContentSize: CGSize { - if let bounds = node.bounds { - return bounds.size().toCG() - } else { - return CGSize(width: MNoIntrinsicMetric(), height: MNoIntrinsicMetric()) - } - } - + private let placeManager = RootPlaceManager() private let layoutHelper = LayoutHelper() var touchesMap = [MTouchEvent: [NodePath]]() @@ -124,24 +267,17 @@ open class MacawView: MView, MGestureRecognizerDelegate { var toRender = true var frameSetFirstTime = false - #if os(OSX) - open override var layer: CALayer? { - didSet { - guard self.layer != nil else { - return - } - initializeView() - - self.renderer = RenderUtils.createNodeRenderer(node, view: self) - } + func initializeView() { + self.contentLayout = .none + self.context = RenderContext(view: self) + } + + @objc public convenience required init?(coder aDecoder: NSCoder) { + self.init(node: Group(), coder: aDecoder) } - #endif @objc public init?(node: Node, coder aDecoder: NSCoder) { super.init(coder: aDecoder) - zoom.initialize(view: self, onChange: onZoomChange) - - initializeView() self.node = node self.renderer = RenderUtils.createNodeRenderer(node, view: self) @@ -158,61 +294,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { public override init(frame: CGRect) { super.init(frame: frame) - zoom.initialize(view: self, onChange: onZoomChange) - } - - @objc public convenience required init?(coder aDecoder: NSCoder) { - self.init(node: Group(), coder: aDecoder) - } - - open override func didMoveToWindow() { - super.didMoveToWindow() - - initializeView() - } - - func initializeView() { - self.contentLayout = .none - self.context = RenderContext(view: self) - - if let superview = self.superview { - self.isUserInteractionEnabled = false - touchInterceptionView.isMultipleTouchEnabled = true - touchInterceptionView.macawView = self - touchInterceptionView.backgroundColor = .clear - superview.insertSubview(touchInterceptionView, aboveSubview: self) - - touchInterceptionView.translatesAutoresizingMaskIntoConstraints = false - touchInterceptionView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true - touchInterceptionView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true - touchInterceptionView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true - touchInterceptionView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true - } - - let tapRecognizer = MTapGestureRecognizer(target: self, action: #selector(MacawView.handleTap)) - let longTapRecognizer = MLongPressGestureRecognizer(target: self, action: #selector(MacawView.handleLongTap(recognizer:))) - let panRecognizer = MPanGestureRecognizer(target: self, action: #selector(MacawView.handlePan)) - let rotationRecognizer = MRotationGestureRecognizer(target: self, action: #selector(MacawView.handleRotation)) - let pinchRecognizer = MPinchGestureRecognizer(target: self, action: #selector(MacawView.handlePinch)) - - tapRecognizer.delegate = self - longTapRecognizer.delegate = self - panRecognizer.delegate = self - rotationRecognizer.delegate = self - pinchRecognizer.delegate = self - - tapRecognizer.cancelsTouchesInView = false - longTapRecognizer.cancelsTouchesInView = false - panRecognizer.cancelsTouchesInView = false - rotationRecognizer.cancelsTouchesInView = false - pinchRecognizer.cancelsTouchesInView = false - - touchInterceptionView.removeGestureRecognizers() - touchInterceptionView.addGestureRecognizer(tapRecognizer) - touchInterceptionView.addGestureRecognizer(longTapRecognizer) - touchInterceptionView.addGestureRecognizer(panRecognizer) - touchInterceptionView.addGestureRecognizer(rotationRecognizer) - touchInterceptionView.addGestureRecognizer(pinchRecognizer) } open override func layoutSubviews() { @@ -220,12 +301,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { setNeedsDisplay() } - private func onZoomChange(t: Transform) { - if let viewLayer = mLayer { - viewLayer.transform = CATransform3DMakeAffineTransform(t.toCG()) - } - } - override open func draw(_ rect: CGRect) { context.cgContext = MGraphicsGetCurrentContext() guard let ctx = context.cgContext else { @@ -274,10 +349,8 @@ open class MacawView: MView, MGestureRecognizerDelegate { } // MARK: - Touches - override func mTouchesBegan(_ touches: Set, with event: MEvent?) { - zoom.touchesBegan(touches) + func touchesBegan(touchPoints: [MTouchEvent]) { - let touchPoints = convert(touches: touches) if !self.node.shouldCheckForPressed() && !self.node.shouldCheckForMoved() && !self.node.shouldCheckForReleased () { @@ -325,8 +398,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } - override func mTouchesMoved(_ touches: Set, with event: MEvent?) { - zoom.touchesMoved(touches) + func touchesMoved(touchPoints: [MTouchEvent]) { if !self.node.shouldCheckForMoved() { return } @@ -335,7 +407,6 @@ open class MacawView: MView, MGestureRecognizerDelegate { return } - let touchPoints = convert(touches: touches) touchesOfNode.keys.forEach { currentNode in guard let initialTouches = touchesOfNode[currentNode] else { return @@ -369,30 +440,12 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } - override func mTouchesCancelled(_ touches: Set, with event: MEvent?) { - touchesEnded(touches: touches) - } - - override func mTouchesEnded(_ touches: Set, with event: MEvent?) { - touchesEnded(touches: touches) - } - - private func convert(touches: Set) -> [MTouchEvent] { - return touches.map { touch -> MTouchEvent in - let location = touch.applyCurrentLayerTransform(self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - } - - private func touchesEnded(touches: Set) { - zoom.touchesEnded(touches) + func touchesEnded(touchPoints: [MTouchEvent]) { guard let _ = renderer else { return } let invertedViewPlace = self.place.invert() - let touchPoints = convert(touches: touches) for touch in touchPoints { touchesMap[touch]?.forEach { nodePath in diff --git a/Source/views/MacawZoom.swift b/Source/views/MacawZoom.swift index 33296d26..677cb6ba 100644 --- a/Source/views/MacawZoom.swift +++ b/Source/views/MacawZoom.swift @@ -139,7 +139,7 @@ fileprivate class TouchData { let point: Point convenience init(touch: MTouch, in view: MacawView) { - self.init(touch: touch, point: touch.applyCurrentLayerTransform(view)) + self.init(touch: touch, point: touch.location(in: view).toMacaw()) } init(touch: MTouch, point: Point) { @@ -148,14 +148,7 @@ fileprivate class TouchData { } func current(in view: MacawView) -> Point { - return touch.applyCurrentLayerTransform(view) + return touch.location(in: view).toMacaw() } } - -extension MTouch { - - func applyCurrentLayerTransform(_ view: MacawView) -> Point { - return self.location(in: view.touchInterceptionView).toMacaw() - } -} From 9a73bc012fd265c28b93d6d6563ef4f049e31a3e Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 16 Apr 2020 16:11:20 +0700 Subject: [PATCH 07/48] Fix compile errors --- Source/views/MacawView.swift | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index fa8f838f..f7a02dd2 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -49,6 +49,11 @@ open class MacawView: MView { get { return drawingView.intrinsicContentSize } } + internal var renderer: NodeRenderer? { + get { return drawingView.renderer } + set { drawingView.renderer = newValue } + } + #if os(OSX) open override var layer: CALayer? { didSet { @@ -57,7 +62,7 @@ open class MacawView: MView { } initializeView() - drawingView.renderer = RenderUtils.createNodeRenderer(node, view: self) + renderer = RenderUtils.createNodeRenderer(node, view: drawingView) } } #endif @@ -96,8 +101,8 @@ open class MacawView: MView { } } - open override func didMoveToWindow() { - super.didMoveToWindow() + open override func didMoveToSuperview() { + super.didMoveToSuperview() initializeView() } @@ -145,29 +150,29 @@ open class MacawView: MView { self.addGestureRecognizer(pinchRecognizer) } - open override func touchesBegan(_ touches: Set, with event: MEvent?) { - super.touchesBegan(touches, with: event) + open override func mTouchesBegan(_ touches: Set, with event: MEvent?) { + super.mTouchesBegan(touches, with: event) zoom.touchesBegan(touches) drawingView.touchesBegan(touchPoints: convert(touches: touches)) } - open override func touchesMoved(_ touches: Set, with event: MEvent?) { - super.touchesMoved(touches, with: event) + open override func mTouchesMoved(_ touches: Set, with event: MEvent?) { + super.mTouchesMoved(touches, with: event) zoom.touchesMoved(touches) drawingView.touchesMoved(touchPoints: convert(touches: touches)) } - open override func touchesEnded(_ touches: Set, with event: MEvent?) { - super.touchesEnded(touches, with: event) + open override func mTouchesEnded(_ touches: Set, with event: MEvent?) { + super.mTouchesEnded(touches, with: event) zoom.touchesEnded(touches) drawingView.touchesEnded(touchPoints: convert(touches: touches)) } - override open func touchesCancelled(_ touches: Set, with event: MEvent?) { - super.touchesEnded(touches, with: event) + override open func mTouchesCancelled(_ touches: Set, with event: MEvent?) { + super.mTouchesCancelled(touches, with: event) zoom.touchesEnded(touches) drawingView.touchesEnded(touchPoints: convert(touches: touches)) From bb47def33747190deb0768c479f404d6a0236472 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Fri, 17 Apr 2020 15:55:40 +0700 Subject: [PATCH 08/48] Fix some examples --- .../CombinationAnimationGenerator.swift | 7 ++- Source/views/MacawView.swift | 52 +++++++------------ 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift b/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift index 9a6f92eb..6815bcb0 100644 --- a/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift +++ b/Source/animation/types/animation_generators/CombinationAnimationGenerator.swift @@ -115,13 +115,16 @@ extension AnimationProducer { // MARK: - Combine animation func addCombineAnimation(_ combineAnimation: Animation, _ context: AnimationContext) { guard let combine = combineAnimation as? CombineAnimation, + let _ = combine.nodeRenderer, let node = combine.node else { return } var animations = combine.animations - let childAnimations = createChildAnimations(combine) as! [BasicAnimation] - animations.append(contentsOf: childAnimations) + if let _ = combine.node?.bounds, let _ = combine.toNodes.group().bounds { + let childAnimations = createChildAnimations(combine) as! [BasicAnimation] + animations.append(contentsOf: childAnimations) + } // Reversing if combine.autoreverses { diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index f7a02dd2..2dddec6d 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -74,9 +74,8 @@ open class MacawView: MView { @objc public init?(node: Node, coder aDecoder: NSCoder) { super.init(coder: aDecoder) - if let drawingView = DrawingView(node: node, coder: aDecoder) { - self.drawingView = drawingView - } + self.node = node + self.renderer = RenderUtils.createNodeRenderer(node, view: drawingView) zoom.initialize(view: self, onChange: onZoomChange) } @@ -84,14 +83,13 @@ open class MacawView: MView { public convenience init(node: Node, frame: CGRect) { self.init(frame: frame) - self.drawingView = DrawingView(node: node, frame: frame) + self.node = node + self.renderer = RenderUtils.createNodeRenderer(node, view: drawingView) } public override init(frame: CGRect) { super.init(frame: frame) - self.drawingView = DrawingView(frame: frame) - zoom.initialize(view: self, onChange: onZoomChange) } @@ -107,21 +105,27 @@ open class MacawView: MView { initializeView() } + open override func layoutSubviews() { + super.layoutSubviews() + + drawingView.frame = self.bounds + } + func initializeView() { if !self.subviews.contains(drawingView) { - self.backgroundColor = .white - self.clipsToBounds = true + if self.backgroundColor == nil { + self.backgroundColor = .white + } + drawingView.removeFromSuperview() self.addSubview(drawingView) - drawingView.backgroundColor = .white - drawingView.isUserInteractionEnabled = false + drawingView.backgroundColor = .clear drawingView.initializeView() - drawingView.translatesAutoresizingMaskIntoConstraints = false - drawingView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true - drawingView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true - drawingView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true - drawingView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + #if os(iOS) + self.clipsToBounds = true + drawingView.isUserInteractionEnabled = false + #endif } let tapRecognizer = MTapGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleTap(recognizer:))) @@ -277,24 +281,8 @@ internal class DrawingView: MView, MGestureRecognizerDelegate { self.context = RenderContext(view: self) } - @objc public convenience required init?(coder aDecoder: NSCoder) { - self.init(node: Group(), coder: aDecoder) - } - - @objc public init?(node: Node, coder aDecoder: NSCoder) { + @objc public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) - - self.node = node - self.renderer = RenderUtils.createNodeRenderer(node, view: self) - backgroundColor = .white - } - - public convenience init(node: Node, frame: CGRect) { - self.init(frame: frame) - - self.node = node - self.renderer = RenderUtils.createNodeRenderer(node, view: self) - backgroundColor = .white } public override init(frame: CGRect) { From 496ef399d1b80ed9a6ed24460e0bd49576a14a3a Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Mon, 20 Apr 2020 13:18:40 +0700 Subject: [PATCH 09/48] Fix layer inset issue --- Source/views/MacawView.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 2dddec6d..a28b57f8 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -40,9 +40,11 @@ open class MacawView: MView { get { return drawingView.placeVar } } - open override var frame: CGRect { - get { return drawingView.frame } - set { drawingView.frame = newValue } + override open var frame: CGRect { + didSet { + super.frame = frame + drawingView.frame = frame + } } override open var intrinsicContentSize: CGSize { From 5c0f812f0c8638a89540d5d7e08f14e460244bf0 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Mon, 20 Apr 2020 14:18:16 +0700 Subject: [PATCH 10/48] Fix for liquid swipe --- Source/views/MacawView.swift | 42 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index a28b57f8..582db76a 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -11,7 +11,7 @@ import AppKit /// You could create your own view extended from MacawView with predefined scene. /// -open class MacawView: MView { +open class MacawView: MView, MGestureRecognizerDelegate { internal var drawingView = DrawingView() @@ -80,6 +80,7 @@ open class MacawView: MView { self.renderer = RenderUtils.createNodeRenderer(node, view: drawingView) zoom.initialize(view: self, onChange: onZoomChange) + initializeView() } public convenience init(node: Node, frame: CGRect) { @@ -93,6 +94,7 @@ open class MacawView: MView { super.init(frame: frame) zoom.initialize(view: self, onChange: onZoomChange) + initializeView() } private func onZoomChange(t: Transform) { @@ -101,12 +103,6 @@ open class MacawView: MView { } } - open override func didMoveToSuperview() { - super.didMoveToSuperview() - - initializeView() - } - open override func layoutSubviews() { super.layoutSubviews() @@ -136,11 +132,11 @@ open class MacawView: MView { let rotationRecognizer = MRotationGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleRotation)) let pinchRecognizer = MPinchGestureRecognizer(target: drawingView, action: #selector(DrawingView.handlePinch)) - tapRecognizer.delegate = drawingView - longTapRecognizer.delegate = drawingView - panRecognizer.delegate = drawingView - rotationRecognizer.delegate = drawingView - pinchRecognizer.delegate = drawingView + tapRecognizer.delegate = self + longTapRecognizer.delegate = self + panRecognizer.delegate = self + rotationRecognizer.delegate = self + pinchRecognizer.delegate = self tapRecognizer.cancelsTouchesInView = false longTapRecognizer.cancelsTouchesInView = false @@ -191,9 +187,19 @@ open class MacawView: MView { return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) } } + + // MARK: - MGestureRecognizerDelegate + + public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldReceive touch: MTouch) -> Bool { + return true + } + + public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: MGestureRecognizer) -> Bool { + return true + } } -internal class DrawingView: MView, MGestureRecognizerDelegate { +internal class DrawingView: MView { /// Scene root node open var node: Node = Group() { @@ -659,16 +665,6 @@ internal class DrawingView: MView, MGestureRecognizerDelegate { recognizersMap.removeValue(forKey: recognizer) } } - - // MARK: - MGestureRecognizerDelegate - - public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldReceive touch: MTouch) -> Bool { - return true - } - - public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: MGestureRecognizer) -> Bool { - return true - } } class LayoutHelper { From 67b7bb132fed03418e10c57bea470d7adf179171 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Tue, 21 Apr 2020 13:59:37 +0700 Subject: [PATCH 11/48] Fix for animation hierarchy movement --- Source/views/MacawView.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 582db76a..d957b3e8 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -103,23 +103,22 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } - open override func layoutSubviews() { - super.layoutSubviews() - - drawingView.frame = self.bounds - } - func initializeView() { if !self.subviews.contains(drawingView) { if self.backgroundColor == nil { self.backgroundColor = .white } - drawingView.removeFromSuperview() self.addSubview(drawingView) drawingView.backgroundColor = .clear drawingView.initializeView() + drawingView.translatesAutoresizingMaskIntoConstraints = false + drawingView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true + drawingView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true + drawingView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true + drawingView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + #if os(iOS) self.clipsToBounds = true drawingView.isUserInteractionEnabled = false From 0341e6d84e3524612232441f355711ceb215aeb8 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 30 Apr 2020 13:25:09 +0700 Subject: [PATCH 12/48] Return public find node at --- Source/views/MacawView.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index d957b3e8..c46838e9 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -97,6 +97,10 @@ open class MacawView: MView, MGestureRecognizerDelegate { initializeView() } + public final func findNodeAt(location: CGPoint) -> Node? { + return drawingView.findNodeAt(location: location) + } + private func onZoomChange(t: Transform) { if let viewLayer = drawingView.mLayer { viewLayer.transform = CATransform3DMakeAffineTransform(t.toCG()) @@ -230,11 +234,11 @@ internal class DrawingView: MView { } } - public var place: Transform { + var place: Transform { return placeManager.placeVar.value } - public var placeVar: Variable { + var placeVar: Variable { return placeManager.placeVar } @@ -323,7 +327,7 @@ internal class DrawingView: MView { renderer.render(in: ctx, force: false, opacity: node.opacity) } - public final func findNodeAt(location: CGPoint) -> Node? { + final func findNodeAt(location: CGPoint) -> Node? { guard let ctx = context.cgContext else { return .none } From bd5e868daca30f5bde78cef52fde6f951b4c3512 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Wed, 29 Apr 2020 15:45:26 +0700 Subject: [PATCH 13/48] Fix polyline and polygon points parsing Handling of numbers in scientific notation in `parsePoints` used to be incorrect, because a number could be broken by an exponent minus sign, resulting in incorrect values. E.g. '2.5e-1' -> '2.5e', '-1'. --- Source/svg/SVGParser.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index f3e0a7c6..5bb08a77 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -986,15 +986,14 @@ open class SVGParser { fileprivate func parsePoints(_ pointsString: String) -> [Double] { var resultPoints: [Double] = [] - let pointPairs = pointsString.replacingOccurrences(of: "-", with: " -").components(separatedBy: " ") - pointPairs.forEach { pointPair in - let points = pointPair.components(separatedBy: ",") - points.forEach { point in - if let resultPoint = Double(point) { - resultPoints.append(resultPoint) - } + let scanner = Scanner(string: pointsString) + while !scanner.isAtEnd { + var resultPoint: Double = 0 + if scanner.scanDouble(&resultPoint) { + resultPoints.append(resultPoint) } + _ = scanner.scanCharacters(from: [","], into: nil) } if resultPoints.count % 2 == 1 { From 195f828f77410e0c18a9adbfa03d3462afebcbe0 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 28 May 2020 15:34:37 +0700 Subject: [PATCH 14/48] Add toMacaw for CGPath --- Source/utils/CGMappings.swift | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Source/utils/CGMappings.swift b/Source/utils/CGMappings.swift index e3acc84b..1f727bd8 100644 --- a/Source/utils/CGMappings.swift +++ b/Source/utils/CGMappings.swift @@ -153,3 +153,52 @@ public extension Node { } } + +extension CGPath { + + public func toMacaw() -> Path { + + func createPathSegment(type: PathSegmentType, points: UnsafeMutablePointer, count: Int) -> PathSegment { + + var data = [Double]() + for index in 0.. Void) { + typealias Body = @convention(block) (CGPathElement) -> Void + func callback(info: UnsafeMutableRawPointer?, element: UnsafePointer) { + let body = unsafeBitCast(info, to: Body.self) + body(element.pointee) + } + let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self) + self.apply(info: unsafeBody, function: callback) + } +} From 8d6607c307fd918d6e25647422b89ca91c25fde7 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 28 May 2020 16:15:53 +0700 Subject: [PATCH 15/48] Fix #688: Review fixes --- Source/utils/CGMappings.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/utils/CGMappings.swift b/Source/utils/CGMappings.swift index 1f727bd8..a7a35173 100644 --- a/Source/utils/CGMappings.swift +++ b/Source/utils/CGMappings.swift @@ -154,9 +154,18 @@ public extension Node { } -extension CGPath { +extension UIBezierPath { public func toMacaw() -> Path { + let fillRule: FillRule = self.usesEvenOddFillRule ? .evenodd : .nonzero + return self.cgPath.toMacaw(fillRule: fillRule) + } + +} + +extension CGPath { + + public func toMacaw(fillRule: FillRule = .nonzero) -> Path { func createPathSegment(type: PathSegmentType, points: UnsafeMutablePointer, count: Int) -> PathSegment { @@ -184,7 +193,7 @@ extension CGPath { case .closeSubpath: segment = PathSegment(type: .z) @unknown default: - fatalError() + fatalError("Unknown element type: \(element.type)") } segments.append(segment) }) From 9abede7354bf6fbb2507b7e67abde205e5901745 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 28 May 2020 19:11:43 +0700 Subject: [PATCH 16/48] Fix mac build --- Source/utils/CGMappings.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/utils/CGMappings.swift b/Source/utils/CGMappings.swift index a7a35173..45a96873 100644 --- a/Source/utils/CGMappings.swift +++ b/Source/utils/CGMappings.swift @@ -154,7 +154,7 @@ public extension Node { } -extension UIBezierPath { +extension MBezierPath { public func toMacaw() -> Path { let fillRule: FillRule = self.usesEvenOddFillRule ? .evenodd : .nonzero From e5d93909df61644d816af461d9fd1254e13dad77 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Fri, 29 May 2020 12:24:22 +0700 Subject: [PATCH 17/48] More fixes for mac --- Source/platform/iOS/Common_iOS.swift | 6 ++++++ Source/platform/macOS/Common_macOS.swift | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Source/platform/iOS/Common_iOS.swift b/Source/platform/iOS/Common_iOS.swift index 219342fe..9e2f21e6 100644 --- a/Source/platform/iOS/Common_iOS.swift +++ b/Source/platform/iOS/Common_iOS.swift @@ -99,4 +99,10 @@ extension UIScreen { } } +extension UIBezierPath { + var usesEvenOddFillRule: Bool { + return self.usesEvenOddFillRule + } +} + #endif diff --git a/Source/platform/macOS/Common_macOS.swift b/Source/platform/macOS/Common_macOS.swift index b987e957..d870d1f2 100644 --- a/Source/platform/macOS/Common_macOS.swift +++ b/Source/platform/macOS/Common_macOS.swift @@ -184,4 +184,10 @@ extension NSWindow { } } +extension NSBezierPath { + var usesEvenOddFillRule: Bool { + return self.windingRule == .evenOdd + } +} + #endif From d89e2047fee13d97b213ade4d32e356e3bde4924 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Wed, 3 Jun 2020 14:17:17 +0700 Subject: [PATCH 18/48] Fix #692: Consider layout transform in findNode --- .../types/animation_generators/Cache/AnimationCache.swift | 3 +++ Source/views/MacawView.swift | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/animation/types/animation_generators/Cache/AnimationCache.swift b/Source/animation/types/animation_generators/Cache/AnimationCache.swift index b049f19d..0dbcb49b 100644 --- a/Source/animation/types/animation_generators/Cache/AnimationCache.swift +++ b/Source/animation/types/animation_generators/Cache/AnimationCache.swift @@ -150,6 +150,9 @@ class AnimationUtils { } parent = parent?.parentRenderer } + if let viewPlace = renderer.view?.place { + uncachedParentsPlace = uncachedParentsPlace.concat(with: viewPlace) + } return uncachedParentsPlace } } diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index c46838e9..be4becd0 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -346,10 +346,11 @@ internal class DrawingView: MView { defer { MGraphicsEndImageContext() } - guard let ctx = MGraphicsGetCurrentContext() else { + guard let ctx = MGraphicsGetCurrentContext(), let inverted = self.place.invert() else { return .none } - return doFindNode(location: location, ctx: ctx) + let loc = location.applying(inverted.toCG()) + return doFindNode(location: loc, ctx: ctx) } // MARK: - Touches From 65e3fe35f0494f6f72af24f2b406b08b43279cf4 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Wed, 3 Jun 2020 17:38:55 +0700 Subject: [PATCH 19/48] Fix #695: Wrong vector rotation --- Source/model/geom2d/Transform.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/model/geom2d/Transform.swift b/Source/model/geom2d/Transform.swift index 206d137a..b0e1f217 100644 --- a/Source/model/geom2d/Transform.swift +++ b/Source/model/geom2d/Transform.swift @@ -128,9 +128,9 @@ public final class Transform { } public func apply(to: Point) -> Point { - let x2 = m11 * to.x + m12 * to.x + dx - let y2 = m21 * to.y + m22 * to.y + dy - return Point(x: x2, y: y2) + let x = m11 * to.x + m12 * to.y + dx + let y = m21 * to.x + m22 * to.y + dy + return Point(x: x, y: y) } public func invert() -> Transform? { From e63e6f24072d8429b8b4265d64dcf2d2d6616ed9 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 00:39:39 +0700 Subject: [PATCH 20/48] Prevent percent from being parsed as unit --- Source/svg/SVGParser.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 5bb08a77..f59baeb3 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -1649,12 +1649,12 @@ open class SVGParser { } fileprivate func dimensionFromString(_ string: String) -> SVGLength? { + if string.hasSuffix("%"), let value = Double(string.dropLast()) { + return SVGLength(percent: value) + } if let value = doubleFromString(string) { return SVGLength(pixels: value) } - if string.hasSuffix("%") { - return SVGLength(percent: Double(string.dropLast())!) - } return .none } From 1101d4fddc6dca475b16824895a407729ea19785 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 01:23:00 +0700 Subject: [PATCH 21/48] Parse numbers avoiding regex matching --- Source/svg/SVGParser.swift | 77 ++++++++++++++++++++------- Source/svg/SVGParserRegexHelper.swift | 14 ----- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index f59baeb3..7d23aa45 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -1659,30 +1659,27 @@ open class SVGParser { } fileprivate func doubleFromString(_ string: String) -> Double? { - if let doubleValue = Double(string) { - return doubleValue - } if string == "none" { return 0 } - guard let matcher = SVGParserRegexHelper.getUnitsIdenitifierMatcher() else { - return .none - } - let fullRange = NSRange(location: 0, length: string.count) - if let match = matcher.firstMatch(in: string, options: .reportCompletion, range: fullRange) { - let unitString = (string as NSString).substring(with: match.range(at: 1)) - let numberString = String(string.dropLast(unitString.count)) - let value = Double(numberString) ?? 0 - switch unitString { - case "px" : - return value - default: - print("SVG parsing error. Unit \(unitString) not supported") - return value - } - } - return .none + let scanner = Scanner(string: string) + let value = scanner.scannedDouble() + let unit = scanner.scannedCharacters(from: .unitCharacters) + + if !scanner.isAtEnd { + let junk = scanner.scannedUpToCharacters(from: []) ?? "" + print("Found trailing junk \"\(junk)\" in string \"\(string)\".") + return .none + } + + switch unit { + case nil, "px": + return value + default: + print("SVG parsing error. Unit \"\(unit ?? "")\" is not supported") + return value + } } fileprivate func getDoubleValueFromPercentage(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? { @@ -2189,3 +2186,43 @@ fileprivate enum SVGKeys { static let color = "color" static let currentColor = "currentColor" } + +fileprivate extension Scanner { + /// A version of `scanDouble()`, available for an earlier OS. + func scannedDouble() -> Double? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanDouble() + } else { + var double: Double = 0 + return scanDouble(&double) ? double : nil + } + } + + /// A version of `scanCharacters(from:)`, available for an earlier OS. + func scannedCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanCharacters(from: set) + } else { + var string: NSString? = nil + return scanCharacters(from: set, into: &string) ? string as String? : nil + } + } + + /// A version of `scanUpToCharacters(from:)`, available for an earlier OS. + func scannedUpToCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToCharacters(from: set) + } else { + var string: NSString? = nil + return scanUpToCharacters(from: set, into: &string) ? string as String? : nil + } + } +} + +fileprivate extension CharacterSet { + /// Latin alphabet characters. + static let latinAlphabet = CharacterSet(charactersIn: "a"..."z") + .union(CharacterSet(charactersIn: "A"..."Z")) + + static let unitCharacters = CharacterSet.latinAlphabet +} diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index d50e0cc8..d251b9c3 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -6,13 +6,11 @@ class SVGParserRegexHelper { fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" fileprivate static let maskIdenitifierPattern = "url\\(#((?s:.*))\\)" - fileprivate static let unitsIdenitifierPattern = "([a-zA-Z]+)$" fileprivate static var transformMatcher: NSRegularExpression? fileprivate static var transformAttributeMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? fileprivate static var maskIdenitifierMatcher: NSRegularExpression? - fileprivate static var unitsMatcher: NSRegularExpression? class func getTransformAttributeMatcher() -> NSRegularExpression? { if self.transformAttributeMatcher == nil { @@ -57,16 +55,4 @@ class SVGParserRegexHelper { } return self.maskIdenitifierMatcher } - - class func getUnitsIdenitifierMatcher() -> NSRegularExpression? { - if unitsMatcher == nil { - do { - unitsMatcher = try NSRegularExpression(pattern: unitsIdenitifierPattern, options: .caseInsensitive) - } catch { - - } - } - return unitsMatcher - } - } From c94d2d9256c1bf44b46998bcf7acceaba37790fb Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 01:31:52 +0700 Subject: [PATCH 22/48] Remove an unused regular expression --- Source/svg/SVGParserRegexHelper.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index d251b9c3..e1a7c4ef 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -5,12 +5,10 @@ class SVGParserRegexHelper { fileprivate static let transformAttributePattern = "([a-z]+)\\(((\\-?\\d+\\.?\\d*e?\\-?\\d*\\s*,?\\s*)+)\\)" fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" - fileprivate static let maskIdenitifierPattern = "url\\(#((?s:.*))\\)" fileprivate static var transformMatcher: NSRegularExpression? fileprivate static var transformAttributeMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? - fileprivate static var maskIdenitifierMatcher: NSRegularExpression? class func getTransformAttributeMatcher() -> NSRegularExpression? { if self.transformAttributeMatcher == nil { @@ -44,15 +42,4 @@ class SVGParserRegexHelper { } return self.textElementMatcher } - - class func getMaskIdenitifierMatcher() -> NSRegularExpression? { - if self.maskIdenitifierMatcher == nil { - do { - self.maskIdenitifierMatcher = try NSRegularExpression(pattern: maskIdenitifierPattern, options: .caseInsensitive) - } catch { - - } - } - return self.maskIdenitifierMatcher - } } From b0196e217d238ab0a491474b2c6a4f4819c979b1 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 01:51:36 +0700 Subject: [PATCH 23/48] Parse transform attributes using a scanner --- Source/svg/SVGParser.swift | 60 +++++++++++++++++---------- Source/svg/SVGParserRegexHelper.swift | 13 ------ 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 7d23aa45..10853e6b 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -534,21 +534,27 @@ open class SVGParser { fileprivate func parseTransformationAttribute(_ attributes: String, transform: Transform = Transform()) -> Transform { - guard let matcher = SVGParserRegexHelper.getTransformAttributeMatcher() else { - return transform - } + // Transform attribute regular grammar (whitespace characters are ignored): + // ([a-zA-Z]+)\(((-?\d+\.?\d*e?-?\d*,?)+)\) + // Group (1) is an attribute name. + // Group (2) is comma-separated numbers. - let attributes = attributes.replacingOccurrences(of: "\n", with: "") - var finalTransform = transform - let fullRange = NSRange(location: 0, length: attributes.count) + var transform = transform + let scanner = Scanner(string: attributes) - if let matchedAttribute = matcher.firstMatch(in: attributes, options: .reportCompletion, range: fullRange) { + stopParse: while !scanner.isAtEnd { + guard let attributeName = scanner.scannedCharacters(from: .transformationAttributeCharacters), + scanner.scanString("(", into: nil), + let valuesString = scanner.scannedUpToString(")"), + scanner.scanString(")", into: nil) else { + break stopParse + } - let attributeName = (attributes as NSString).substring(with: matchedAttribute.range(at: 1)) - let values = parseTransformValues((attributes as NSString).substring(with: matchedAttribute.range(at: 2))) + let values = parseTransformValues(valuesString) if values.isEmpty { return transform } + switch attributeName { case "translate": if let x = Double(values[0]) { @@ -556,7 +562,7 @@ open class SVGParser { if values.indices.contains(1) { y = Double(values[1]) ?? 0 } - finalTransform = transform.move(dx: x, dy: y) + transform = transform.move(dx: x, dy: y) } case "scale": if let x = Double(values[0]) { @@ -564,27 +570,27 @@ open class SVGParser { if values.indices.contains(1) { y = Double(values[1]) ?? x } - finalTransform = transform.scale(sx: x, sy: y) + transform = transform.scale(sx: x, sy: y) } case "rotate": if let angle = Double(values[0]) { if values.count == 1 { - finalTransform = transform.rotate(angle: degreesToRadians(angle)) + transform = transform.rotate(angle: degreesToRadians(angle)) } else if values.count == 3 { if let x = Double(values[1]), let y = Double(values[2]) { - finalTransform = transform.move(dx: x, dy: y).rotate(angle: degreesToRadians(angle)).move(dx: -x, dy: -y) + transform = transform.move(dx: x, dy: y).rotate(angle: degreesToRadians(angle)).move(dx: -x, dy: -y) } } } case "skewX": if let x = Double(values[0]) { let v = tan((x * Double.pi) / 180.0) - finalTransform = transform.shear(shx: v, shy: 0) + transform = transform.shear(shx: v, shy: 0) } case "skewY": if let y = Double(values[0]) { let y = tan((y * Double.pi) / 180.0) - finalTransform = transform.shear(shx: 0, shy: y) + transform = transform.shear(shx: 0, shy: y) } case "matrix": if values.count != 6 { @@ -595,18 +601,14 @@ open class SVGParser { let dx = Double(values[4]), let dy = Double(values[5]) { let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) - finalTransform = transform.concat(with: transformMatrix) + transform = transform.concat(with: transformMatrix) } default: - break + break stopParse } - let rangeToRemove = NSRange(location: 0, - length: matchedAttribute.range.location + matchedAttribute.range.length) - let newAttributeString = (attributes as NSString).replacingCharacters(in: rangeToRemove, with: "") - return parseTransformationAttribute(newAttributeString, transform: finalTransform) - } else { - return transform } + + return transform } /// Parse an RGB @@ -2217,6 +2219,16 @@ fileprivate extension Scanner { return scanUpToCharacters(from: set, into: &string) ? string as String? : nil } } + + /// A version of `scanUpToString(_:)`, available for an earlier OS. + func scannedUpToString(_ substring: String) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToString(substring) + } else { + var string: NSString? = nil + return scanUpTo(substring, into: &string) ? string as String? : nil + } + } } fileprivate extension CharacterSet { @@ -2225,4 +2237,6 @@ fileprivate extension CharacterSet { .union(CharacterSet(charactersIn: "A"..."Z")) static let unitCharacters = CharacterSet.latinAlphabet + + static let transformationAttributeCharacters = CharacterSet.latinAlphabet } diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index e1a7c4ef..6b2109f4 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -2,25 +2,12 @@ import Foundation class SVGParserRegexHelper { - fileprivate static let transformAttributePattern = "([a-z]+)\\(((\\-?\\d+\\.?\\d*e?\\-?\\d*\\s*,?\\s*)+)\\)" fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" fileprivate static var transformMatcher: NSRegularExpression? - fileprivate static var transformAttributeMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? - class func getTransformAttributeMatcher() -> NSRegularExpression? { - if self.transformAttributeMatcher == nil { - do { - self.transformAttributeMatcher = try NSRegularExpression(pattern: transformAttributePattern, options: .caseInsensitive) - } catch { - - } - } - return self.transformAttributeMatcher - } - class func getTransformMatcher() -> NSRegularExpression? { if self.transformMatcher == nil { do { From 974b2d012c5f973e70d996da90b0d3aad5f8f6ac Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 02:04:15 +0700 Subject: [PATCH 24/48] Parse transform values using a scanner --- Source/svg/SVGParser.swift | 104 +++++++++++++------------- Source/svg/SVGParserRegexHelper.swift | 14 ---- 2 files changed, 52 insertions(+), 66 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 10853e6b..6fbf69f6 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -557,52 +557,51 @@ open class SVGParser { switch attributeName { case "translate": - if let x = Double(values[0]) { - var y: Double = 0 - if values.indices.contains(1) { - y = Double(values[1]) ?? 0 - } - transform = transform.move(dx: x, dy: y) - } + let x = values[0] + var y: Double = 0 + if values.indices ~= 1 { + y = values[1] + } + transform = transform.move(dx: x, dy: y) case "scale": - if let x = Double(values[0]) { - var y: Double = x - if values.indices.contains(1) { - y = Double(values[1]) ?? x - } - transform = transform.scale(sx: x, sy: y) - } + let x = values[0] + var y: Double = x + if values.indices ~= 1 { + y = values[1] + } + transform = transform.scale(sx: x, sy: y) case "rotate": - if let angle = Double(values[0]) { - if values.count == 1 { - transform = transform.rotate(angle: degreesToRadians(angle)) - } else if values.count == 3 { - if let x = Double(values[1]), let y = Double(values[2]) { - transform = transform.move(dx: x, dy: y).rotate(angle: degreesToRadians(angle)).move(dx: -x, dy: -y) - } - } - } + let angle = values[0] + if values.count == 1 { + transform = transform.rotate(angle: degreesToRadians(angle)) + } else if values.count == 3 { + let x = values[1] + let y = values[2] + transform = transform + .move(dx: x, dy: y) + .rotate(angle: degreesToRadians(angle)) + .move(dx: -x, dy: -y) + } case "skewX": - if let x = Double(values[0]) { - let v = tan((x * Double.pi) / 180.0) - transform = transform.shear(shx: v, shy: 0) - } + let x = values[0] + let v = tan((x * Double.pi) / 180.0) + transform = transform.shear(shx: v, shy: 0) case "skewY": - if let y = Double(values[0]) { - let y = tan((y * Double.pi) / 180.0) - transform = transform.shear(shx: 0, shy: y) - } + let y = values[0] + let v = tan((y * Double.pi) / 180.0) + transform = transform.shear(shx: 0, shy: v) case "matrix": if values.count != 6 { return transform } - if let m11 = Double(values[0]), let m12 = Double(values[1]), - let m21 = Double(values[2]), let m22 = Double(values[3]), - let dx = Double(values[4]), let dy = Double(values[5]) { - - let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) - transform = transform.concat(with: transformMatrix) - } + let m11 = values[0] + let m12 = values[1] + let m21 = values[2] + let m22 = values[3] + let dx = values[4] + let dy = values[5] + let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) + transform = transform.concat(with: transformMatrix) default: break stopParse } @@ -641,20 +640,21 @@ open class SVGParser { b: Int(blue.rounded(.up))) } - fileprivate func parseTransformValues(_ values: String, collectedValues: [String] = []) -> [String] { - guard let matcher = SVGParserRegexHelper.getTransformMatcher() else { - return collectedValues - } - var updatedValues: [String] = collectedValues - let fullRange = NSRange(location: 0, length: values.count) - if let matchedValue = matcher.firstMatch(in: values, options: .reportCompletion, range: fullRange) { - let value = (values as NSString).substring(with: matchedValue.range) - updatedValues.append(value) - let rangeToRemove = NSRange(location: 0, length: matchedValue.range.location + matchedValue.range.length) - let newValues = (values as NSString).replacingCharacters(in: rangeToRemove, with: "") - return parseTransformValues(newValues, collectedValues: updatedValues) - } - return updatedValues + fileprivate func parseTransformValues(_ values: String) -> [Double] { + // Parse comma-separated list of numbers. + var collectedValues: [Double] = [] + let scanner = Scanner(string: values) + + while !scanner.isAtEnd { + if let value = scanner.scannedDouble() { + collectedValues.append(value) + } else { + break + } + _ = scanner.scanString(",", into: nil) + } + + return collectedValues } fileprivate func getStyleAttributes(_ groupAttributes: [String: String], diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift index 6b2109f4..d55cd182 100644 --- a/Source/svg/SVGParserRegexHelper.swift +++ b/Source/svg/SVGParserRegexHelper.swift @@ -2,23 +2,9 @@ import Foundation class SVGParserRegexHelper { - fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*" fileprivate static let textElementPattern = "((?s:.*))<\\/text>" - - fileprivate static var transformMatcher: NSRegularExpression? fileprivate static var textElementMatcher: NSRegularExpression? - class func getTransformMatcher() -> NSRegularExpression? { - if self.transformMatcher == nil { - do { - self.transformMatcher = try NSRegularExpression(pattern: transformPattern, options: .caseInsensitive) - } catch { - - } - } - return self.transformMatcher - } - class func getTextElementMatcher() -> NSRegularExpression? { if self.textElementMatcher == nil { do { From 08c6ae17a65e4ad3ab2cb209b3a72e2ebf527a32 Mon Sep 17 00:00:00 2001 From: Tristan Date: Fri, 5 Jun 2020 16:33:32 -0400 Subject: [PATCH 25/48] update ios support to 9.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f27a1ed6..701e28c3 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ We're working hard to provide full documentation. Currently you can take a look ## Requirements -* iOS 8.0+ +* iOS 9.0+ * Mac OS X 10.11+ * Xcode 7.3+ From 016225e7032773777229821fdf32924e6f9ef903 Mon Sep 17 00:00:00 2001 From: Tristan Date: Fri, 5 Jun 2020 16:34:28 -0400 Subject: [PATCH 26/48] Update README_zh.md --- README_zh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_zh.md b/README_zh.md index b625267b..7024ce03 100644 --- a/README_zh.md +++ b/README_zh.md @@ -70,7 +70,7 @@ class MyView: MacawView { ## 系统要求 -* iOS 8.0+ +* iOS 9.0+ * Mac OS X 10.11+ * Xcode 7.3+ From 22233257edd21d8e663a02f25da98395efb84d67 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 19:10:32 +0700 Subject: [PATCH 27/48] Do not use regular expressions and double XML parsing for tspans --- Source/svg/SVGParser.swift | 198 +++++++++++--------------- Source/svg/SVGParserRegexHelper.swift | 18 --- 2 files changed, 81 insertions(+), 135 deletions(-) delete mode 100644 Source/svg/SVGParserRegexHelper.swift diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 6fbf69f6..25b6b9d8 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -1044,28 +1044,19 @@ open class SVGParser { fontWeight: fontWeight, pos: pos) } else { - guard let matcher = SVGParserRegexHelper.getTextElementMatcher() else { - return .none - } - let elementString = element.description - let fullRange = NSRange(location: 0, length: elementString.count) - if let match = matcher.firstMatch(in: elementString, options: .reportCompletion, range: fullRange) { - let tspans = (elementString as NSString).substring(with: match.range(at: 1)) - let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, - y: getDoubleValue(element, attribute: "y") ?? 0) - let collectedTspans = collectTspans(tspans, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: rect) - return Group(contents: collectedTspans, place: pos, tag: getTag(element)) - } + let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, + y: getDoubleValue(element, attribute: "y") ?? 0) + let collectedTspans = collectTspans(element.children, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: rect) + return Group(contents: collectedTspans, place: pos, tag: getTag(element)) } - return .none } fileprivate func anchorToAlign(_ textAnchor: String?) -> Align { @@ -1105,9 +1096,7 @@ open class SVGParser { // REFACTOR - fileprivate func collectTspans(_ tspan: String, - collectedTspans: [Node] = [], - withWhitespace: Bool = false, + fileprivate func collectTspans(_ contents: [XMLContent], textAnchor: String?, fill: Fill?, stroke: Stroke?, @@ -1116,99 +1105,78 @@ open class SVGParser { fontSize: Int?, fontWeight: String?, bounds: Rect) -> [Node] { - let fullString = tspan.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) as NSString - // exit recursion - if fullString.isEqual(to: "") { - return collectedTspans - } - var collection = collectedTspans - let tagRange = fullString.range(of: " element - let closingTagRange = fullString.range(of: "".lowercased()) - let tspanString = fullString.substring(to: closingTagRange.location + closingTagRange.length) - let tspanXml = SWXMLHash.parse(tspanString) - guard let indexer = tspanXml.children.first, - let text = parseTspan(indexer, - withWhitespace: withWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: bounds, - previousCollectedTspan: collection.last) else { + var collection: [Node] = [] + var bounds = bounds + // Whether to add a space before the next non-whitespace-only text. + var addWhitespace = false + // Whether to preserve leading whitespaces before the next text + // by adding a single space prefix. + var preserveWhitespace = false - // skip this element if it can't be parsed - return collectTspans(fullString.substring(from: closingTagRange.location + closingTagRange.length), - collectedTspans: collectedTspans, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: bounds) - } - collection.append(text) - let nextString = fullString.substring(from: closingTagRange.location + closingTagRange.length) as NSString - var withWhitespace = false - if nextString.rangeOfCharacter(from: CharacterSet.whitespacesAndNewlines).location == 0 { - withWhitespace = true - } - return collectTspans(fullString.substring(from: closingTagRange.location + closingTagRange.length), - collectedTspans: collection, - withWhitespace: withWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h)) + for element in contents { + let text: Text? + if let textElement = element as? TextElement { + // parse as regular text element + let textString = textElement.text + let hasLeadingWhitespace = textString.first?.isWhitespace == true + let hasTrailingWhitespace = textString.last?.isWhitespace == true + + var trimmedString = textString.trimmingCharacters(in: .whitespacesAndNewlines) + let isWhitespaceOnly = trimmedString.isEmpty + + if hasLeadingWhitespace && preserveWhitespace && !isWhitespaceOnly { + trimmedString = " " + trimmedString + } + + addWhitespace = preserveWhitespace && hasTrailingWhitespace + preserveWhitespace = false + + if trimmedString.isEmpty { + continue + } + + let place = Transform().move(dx: bounds.x + bounds.w, dy: bounds.y) + + text = Text(text: trimmedString, + font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), + fill: fill, + stroke: stroke, + align: anchorToAlign(textAnchor), + baseline: .alphabetic, + place: place, + opacity: opacity) + } else if let tspanElement = element as? XMLElement, + tspanElement.name == "tspan" { + // parse as element + // ultimately skip it if it cannot be parsed + text = parseTspan(tspanElement, + withWhitespace: addWhitespace, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: bounds, + previousCollectedTspan: collection.last) + preserveWhitespace = true + addWhitespace = false + } else { + print("Skipped an unexpected element type: \(type(of: element)).") + text = nil + } + + if let text = text { + collection.append(text) + bounds = Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h) + } } - // parse as regular text element - var textString: NSString - if tagRange.location >= fullString.length { - textString = fullString - } else { - textString = fullString.substring(to: tagRange.location) as NSString - } - var nextStringWhitespace = false - var trimmedString = textString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) - if trimmedString.count != textString.length { - nextStringWhitespace = true - } - trimmedString = withWhitespace ? " \(trimmedString)" : trimmedString - let text = Text(text: trimmedString, - font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), - fill: fill, - stroke: stroke, - align: anchorToAlign(textAnchor), - baseline: .alphabetic, - place: Transform().move(dx: bounds.x + bounds.w, dy: bounds.y), opacity: opacity) - collection.append(text) - if tagRange.location >= fullString.length { // leave recursion - return collection - } - return collectTspans(fullString.substring(from: tagRange.location), - collectedTspans: collection, - withWhitespace: nextStringWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h)) + + return collection } - fileprivate func parseTspan(_ tspan: XMLIndexer, + fileprivate func parseTspan(_ element: XMLElement, withWhitespace: Bool = false, textAnchor: String?, fill: Fill?, @@ -1220,10 +1188,6 @@ open class SVGParser { bounds: Rect, previousCollectedTspan: Node?) -> Text? { - guard let element = tspan.element else { - return .none - } - let string = element.text var shouldAddWhitespace = withWhitespace let pos = getTspanPosition(element, diff --git a/Source/svg/SVGParserRegexHelper.swift b/Source/svg/SVGParserRegexHelper.swift deleted file mode 100644 index d55cd182..00000000 --- a/Source/svg/SVGParserRegexHelper.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -class SVGParserRegexHelper { - - fileprivate static let textElementPattern = "((?s:.*))<\\/text>" - fileprivate static var textElementMatcher: NSRegularExpression? - - class func getTextElementMatcher() -> NSRegularExpression? { - if self.textElementMatcher == nil { - do { - self.textElementMatcher = try NSRegularExpression(pattern: textElementPattern, options: .caseInsensitive) - } catch { - - } - } - return self.textElementMatcher - } -} From e53ca25b11688c32b1c049d5de5d3f6e93bcb51d Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 19:26:36 +0700 Subject: [PATCH 28/48] Use spaces instead of tabs --- Source/svg/SVGParser.swift | 362 ++++++++++++++++++------------------- 1 file changed, 181 insertions(+), 181 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 25b6b9d8..aaf9cbe0 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -534,21 +534,21 @@ open class SVGParser { fileprivate func parseTransformationAttribute(_ attributes: String, transform: Transform = Transform()) -> Transform { - // Transform attribute regular grammar (whitespace characters are ignored): - // ([a-zA-Z]+)\(((-?\d+\.?\d*e?-?\d*,?)+)\) - // Group (1) is an attribute name. - // Group (2) is comma-separated numbers. + // Transform attribute regular grammar (whitespace characters are ignored): + // ([a-zA-Z]+)\(((-?\d+\.?\d*e?-?\d*,?)+)\) + // Group (1) is an attribute name. + // Group (2) is comma-separated numbers. - var transform = transform - let scanner = Scanner(string: attributes) + var transform = transform + let scanner = Scanner(string: attributes) - stopParse: while !scanner.isAtEnd { - guard let attributeName = scanner.scannedCharacters(from: .transformationAttributeCharacters), - scanner.scanString("(", into: nil), - let valuesString = scanner.scannedUpToString(")"), - scanner.scanString(")", into: nil) else { - break stopParse - } + stopParse: while !scanner.isAtEnd { + guard let attributeName = scanner.scannedCharacters(from: .transformationAttributeCharacters), + scanner.scanString("(", into: nil), + let valuesString = scanner.scannedUpToString(")"), + scanner.scanString(")", into: nil) else { + break stopParse + } let values = parseTransformValues(valuesString) if values.isEmpty { @@ -558,50 +558,50 @@ open class SVGParser { switch attributeName { case "translate": let x = values[0] - var y: Double = 0 - if values.indices ~= 1 { - y = values[1] - } - transform = transform.move(dx: x, dy: y) + var y: Double = 0 + if values.indices ~= 1 { + y = values[1] + } + transform = transform.move(dx: x, dy: y) case "scale": let x = values[0] - var y: Double = x - if values.indices ~= 1 { - y = values[1] - } - transform = transform.scale(sx: x, sy: y) + var y: Double = x + if values.indices ~= 1 { + y = values[1] + } + transform = transform.scale(sx: x, sy: y) case "rotate": let angle = values[0] - if values.count == 1 { - transform = transform.rotate(angle: degreesToRadians(angle)) - } else if values.count == 3 { - let x = values[1] - let y = values[2] - transform = transform - .move(dx: x, dy: y) - .rotate(angle: degreesToRadians(angle)) - .move(dx: -x, dy: -y) - } + if values.count == 1 { + transform = transform.rotate(angle: degreesToRadians(angle)) + } else if values.count == 3 { + let x = values[1] + let y = values[2] + transform = transform + .move(dx: x, dy: y) + .rotate(angle: degreesToRadians(angle)) + .move(dx: -x, dy: -y) + } case "skewX": let x = values[0] - let v = tan((x * Double.pi) / 180.0) - transform = transform.shear(shx: v, shy: 0) + let v = tan((x * Double.pi) / 180.0) + transform = transform.shear(shx: v, shy: 0) case "skewY": let y = values[0] - let v = tan((y * Double.pi) / 180.0) - transform = transform.shear(shx: 0, shy: v) + let v = tan((y * Double.pi) / 180.0) + transform = transform.shear(shx: 0, shy: v) case "matrix": if values.count != 6 { return transform } let m11 = values[0] let m12 = values[1] - let m21 = values[2] - let m22 = values[3] - let dx = values[4] - let dy = values[5] - let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) - transform = transform.concat(with: transformMatrix) + let m21 = values[2] + let m22 = values[3] + let dx = values[4] + let dy = values[5] + let transformMatrix = Transform(m11: m11, m12: m12, m21: m21, m22: m22, dx: dx, dy: dy) + transform = transform.concat(with: transformMatrix) default: break stopParse } @@ -641,20 +641,20 @@ open class SVGParser { } fileprivate func parseTransformValues(_ values: String) -> [Double] { - // Parse comma-separated list of numbers. - var collectedValues: [Double] = [] - let scanner = Scanner(string: values) + // Parse comma-separated list of numbers. + var collectedValues: [Double] = [] + let scanner = Scanner(string: values) - while !scanner.isAtEnd { - if let value = scanner.scannedDouble() { - collectedValues.append(value) - } else { - break - } - _ = scanner.scanString(",", into: nil) - } + while !scanner.isAtEnd { + if let value = scanner.scannedDouble() { + collectedValues.append(value) + } else { + break + } + _ = scanner.scanString(",", into: nil) + } - return collectedValues + return collectedValues } fileprivate func getStyleAttributes(_ groupAttributes: [String: String], @@ -1044,18 +1044,18 @@ open class SVGParser { fontWeight: fontWeight, pos: pos) } else { - let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, - y: getDoubleValue(element, attribute: "y") ?? 0) - let collectedTspans = collectTspans(element.children, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: rect) - return Group(contents: collectedTspans, place: pos, tag: getTag(element)) + let rect = Rect(x: getDoubleValue(element, attribute: "x") ?? 0, + y: getDoubleValue(element, attribute: "y") ?? 0) + let collectedTspans = collectTspans(element.children, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: rect) + return Group(contents: collectedTspans, place: pos, tag: getTag(element)) } } @@ -1105,72 +1105,72 @@ open class SVGParser { fontSize: Int?, fontWeight: String?, bounds: Rect) -> [Node] { - var collection: [Node] = [] - var bounds = bounds - // Whether to add a space before the next non-whitespace-only text. - var addWhitespace = false - // Whether to preserve leading whitespaces before the next text - // by adding a single space prefix. - var preserveWhitespace = false + var collection: [Node] = [] + var bounds = bounds + // Whether to add a space before the next non-whitespace-only text. + var addWhitespace = false + // Whether to preserve leading whitespaces before the next text + // by adding a single space prefix. + var preserveWhitespace = false - for element in contents { - let text: Text? - if let textElement = element as? TextElement { - // parse as regular text element - let textString = textElement.text - let hasLeadingWhitespace = textString.first?.isWhitespace == true - let hasTrailingWhitespace = textString.last?.isWhitespace == true + for element in contents { + let text: Text? + if let textElement = element as? TextElement { + // parse as regular text element + let textString = textElement.text + let hasLeadingWhitespace = textString.first?.isWhitespace == true + let hasTrailingWhitespace = textString.last?.isWhitespace == true - var trimmedString = textString.trimmingCharacters(in: .whitespacesAndNewlines) - let isWhitespaceOnly = trimmedString.isEmpty + var trimmedString = textString.trimmingCharacters(in: .whitespacesAndNewlines) + let isWhitespaceOnly = trimmedString.isEmpty - if hasLeadingWhitespace && preserveWhitespace && !isWhitespaceOnly { - trimmedString = " " + trimmedString - } + if hasLeadingWhitespace && preserveWhitespace && !isWhitespaceOnly { + trimmedString = " " + trimmedString + } - addWhitespace = preserveWhitespace && hasTrailingWhitespace - preserveWhitespace = false + addWhitespace = preserveWhitespace && hasTrailingWhitespace + preserveWhitespace = false - if trimmedString.isEmpty { - continue - } + if trimmedString.isEmpty { + continue + } - let place = Transform().move(dx: bounds.x + bounds.w, dy: bounds.y) + let place = Transform().move(dx: bounds.x + bounds.w, dy: bounds.y) - text = Text(text: trimmedString, - font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), - fill: fill, - stroke: stroke, - align: anchorToAlign(textAnchor), - baseline: .alphabetic, - place: place, - opacity: opacity) - } else if let tspanElement = element as? XMLElement, - tspanElement.name == "tspan" { - // parse as element - // ultimately skip it if it cannot be parsed - text = parseTspan(tspanElement, - withWhitespace: addWhitespace, - textAnchor: textAnchor, - fill: fill, - stroke: stroke, - opacity: opacity, - fontName: fontName, - fontSize: fontSize, - fontWeight: fontWeight, - bounds: bounds, - previousCollectedTspan: collection.last) - preserveWhitespace = true - addWhitespace = false - } else { - print("Skipped an unexpected element type: \(type(of: element)).") - text = nil - } + text = Text(text: trimmedString, + font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), + fill: fill, + stroke: stroke, + align: anchorToAlign(textAnchor), + baseline: .alphabetic, + place: place, + opacity: opacity) + } else if let tspanElement = element as? XMLElement, + tspanElement.name == "tspan" { + // parse as element + // ultimately skip it if it cannot be parsed + text = parseTspan(tspanElement, + withWhitespace: addWhitespace, + textAnchor: textAnchor, + fill: fill, + stroke: stroke, + opacity: opacity, + fontName: fontName, + fontSize: fontSize, + fontWeight: fontWeight, + bounds: bounds, + previousCollectedTspan: collection.last) + preserveWhitespace = true + addWhitespace = false + } else { + print("Skipped an unexpected element type: \(type(of: element)).") + text = nil + } - if let text = text { - collection.append(text) - bounds = Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h) - } + if let text = text { + collection.append(text) + bounds = Rect(x: bounds.x, y: bounds.y, w: bounds.w + text.bounds.w, h: bounds.h) + } } return collection @@ -1629,23 +1629,23 @@ open class SVGParser { return 0 } - let scanner = Scanner(string: string) - let value = scanner.scannedDouble() - let unit = scanner.scannedCharacters(from: .unitCharacters) + let scanner = Scanner(string: string) + let value = scanner.scannedDouble() + let unit = scanner.scannedCharacters(from: .unitCharacters) - if !scanner.isAtEnd { - let junk = scanner.scannedUpToCharacters(from: []) ?? "" - print("Found trailing junk \"\(junk)\" in string \"\(string)\".") - return .none - } + if !scanner.isAtEnd { + let junk = scanner.scannedUpToCharacters(from: []) ?? "" + print("Found trailing junk \"\(junk)\" in string \"\(string)\".") + return .none + } - switch unit { - case nil, "px": - return value - default: - print("SVG parsing error. Unit \"\(unit ?? "")\" is not supported") - return value - } + switch unit { + case nil, "px": + return value + default: + print("SVG parsing error. Unit \"\(unit ?? "")\" is not supported") + return value + } } fileprivate func getDoubleValueFromPercentage(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? { @@ -2154,53 +2154,53 @@ fileprivate enum SVGKeys { } fileprivate extension Scanner { - /// A version of `scanDouble()`, available for an earlier OS. - func scannedDouble() -> Double? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanDouble() - } else { - var double: Double = 0 - return scanDouble(&double) ? double : nil - } - } + /// A version of `scanDouble()`, available for an earlier OS. + func scannedDouble() -> Double? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanDouble() + } else { + var double: Double = 0 + return scanDouble(&double) ? double : nil + } + } - /// A version of `scanCharacters(from:)`, available for an earlier OS. - func scannedCharacters(from set: CharacterSet) -> String? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanCharacters(from: set) - } else { - var string: NSString? = nil - return scanCharacters(from: set, into: &string) ? string as String? : nil - } - } + /// A version of `scanCharacters(from:)`, available for an earlier OS. + func scannedCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanCharacters(from: set) + } else { + var string: NSString? = nil + return scanCharacters(from: set, into: &string) ? string as String? : nil + } + } - /// A version of `scanUpToCharacters(from:)`, available for an earlier OS. - func scannedUpToCharacters(from set: CharacterSet) -> String? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanUpToCharacters(from: set) - } else { - var string: NSString? = nil - return scanUpToCharacters(from: set, into: &string) ? string as String? : nil - } - } + /// A version of `scanUpToCharacters(from:)`, available for an earlier OS. + func scannedUpToCharacters(from set: CharacterSet) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToCharacters(from: set) + } else { + var string: NSString? = nil + return scanUpToCharacters(from: set, into: &string) ? string as String? : nil + } + } - /// A version of `scanUpToString(_:)`, available for an earlier OS. - func scannedUpToString(_ substring: String) -> String? { - if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { - return scanUpToString(substring) - } else { - var string: NSString? = nil - return scanUpTo(substring, into: &string) ? string as String? : nil - } - } + /// A version of `scanUpToString(_:)`, available for an earlier OS. + func scannedUpToString(_ substring: String) -> String? { + if #available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { + return scanUpToString(substring) + } else { + var string: NSString? = nil + return scanUpTo(substring, into: &string) ? string as String? : nil + } + } } fileprivate extension CharacterSet { - /// Latin alphabet characters. - static let latinAlphabet = CharacterSet(charactersIn: "a"..."z") - .union(CharacterSet(charactersIn: "A"..."Z")) + /// Latin alphabet characters. + static let latinAlphabet = CharacterSet(charactersIn: "a"..."z") + .union(CharacterSet(charactersIn: "A"..."Z")) - static let unitCharacters = CharacterSet.latinAlphabet + static let unitCharacters = CharacterSet.latinAlphabet - static let transformationAttributeCharacters = CharacterSet.latinAlphabet + static let transformationAttributeCharacters = CharacterSet.latinAlphabet } From 708a0ab16ba1bf08c6264cc70634db8e469ed3e2 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 21:11:05 +0700 Subject: [PATCH 29/48] Scan an optional comma between transformation attributes --- Source/svg/SVGParser.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index aaf9cbe0..6c79ea89 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -550,6 +550,9 @@ open class SVGParser { break stopParse } + // Skip an optional comma after ")". + _ = scanner.scanString(",", into: nil) + let values = parseTransformValues(valuesString) if values.isEmpty { return transform From 7574dfbfb1ca6462bbbbbe0477e0321de69aa117 Mon Sep 17 00:00:00 2001 From: Petrov Anatoly Date: Sat, 6 Jun 2020 21:22:32 +0700 Subject: [PATCH 30/48] Update the project file --- Macaw.xcodeproj/project.pbxproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Macaw.xcodeproj/project.pbxproj b/Macaw.xcodeproj/project.pbxproj index 10d2385e..84cd4c12 100644 --- a/Macaw.xcodeproj/project.pbxproj +++ b/Macaw.xcodeproj/project.pbxproj @@ -233,7 +233,6 @@ 57614B661F83D15600875933 /* PinchEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E10C1E3B393900D1CB28 /* PinchEvent.swift */; }; 57614B671F83D15600875933 /* ContentsInterpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57A27BCE1E44C4EC0057BD3A /* ContentsInterpolation.swift */; }; 57614B681F83D15600875933 /* GroupRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E13E1E3B393900D1CB28 /* GroupRenderer.swift */; }; - 57614B691F83D15600875933 /* SVGParserRegexHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */; }; 57614B6B1F83D15600875933 /* NSTimer+Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14E1E3B393900D1CB28 /* NSTimer+Closure.swift */; }; 57614B6C1F83D15600875933 /* SWXMLHash+TypeConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 572CEFC51E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift */; }; 57614B6D1F83D15600875933 /* AnimationSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FF1E3B393900D1CB28 /* AnimationSequence.swift */; }; @@ -330,7 +329,6 @@ 57E5E1AA1E3B393900D1CB28 /* SVGConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1461E3B393900D1CB28 /* SVGConstants.swift */; }; 57E5E1AB1E3B393900D1CB28 /* SVGParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1471E3B393900D1CB28 /* SVGParser.swift */; }; 57E5E1AC1E3B393900D1CB28 /* SVGParserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1481E3B393900D1CB28 /* SVGParserError.swift */; }; - 57E5E1AD1E3B393900D1CB28 /* SVGParserRegexHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */; }; 57E5E1AE1E3B393900D1CB28 /* SVGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14A1E3B393900D1CB28 /* SVGView.swift */; }; 57E5E1AF1E3B393900D1CB28 /* CAAnimationClosure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14C1E3B393900D1CB28 /* CAAnimationClosure.swift */; }; 57E5E1B01E3B393900D1CB28 /* CGFloat+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E14D1E3B393900D1CB28 /* CGFloat+Double.swift */; }; @@ -950,7 +948,6 @@ 57E5E1461E3B393900D1CB28 /* SVGConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGConstants.swift; sourceTree = ""; }; 57E5E1471E3B393900D1CB28 /* SVGParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGParser.swift; sourceTree = ""; }; 57E5E1481E3B393900D1CB28 /* SVGParserError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGParserError.swift; sourceTree = ""; }; - 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGParserRegexHelper.swift; sourceTree = ""; }; 57E5E14A1E3B393900D1CB28 /* SVGView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGView.swift; sourceTree = ""; }; 57E5E14C1E3B393900D1CB28 /* CAAnimationClosure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAAnimationClosure.swift; sourceTree = ""; }; 57E5E14D1E3B393900D1CB28 /* CGFloat+Double.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGFloat+Double.swift"; sourceTree = ""; }; @@ -1731,7 +1728,6 @@ 5B1A8C7520A15F7300E5FFAE /* SVGNodeLayout.swift */, 57E5E1471E3B393900D1CB28 /* SVGParser.swift */, 57E5E1481E3B393900D1CB28 /* SVGParserError.swift */, - 57E5E1491E3B393900D1CB28 /* SVGParserRegexHelper.swift */, C4820B171F458D0E008CE0FF /* SVGSerializer.swift */, 57E5E14A1E3B393900D1CB28 /* SVGView.swift */, ); @@ -2811,7 +2807,6 @@ 57614B671F83D15600875933 /* ContentsInterpolation.swift in Sources */, 57614B681F83D15600875933 /* GroupRenderer.swift in Sources */, 5B6E192820AC58F900454E7E /* RadialGradient.swift in Sources */, - 57614B691F83D15600875933 /* SVGParserRegexHelper.swift in Sources */, 5B6E192A20AC58F900454E7E /* Align.swift in Sources */, 57614B6B1F83D15600875933 /* NSTimer+Closure.swift in Sources */, 5B6E193620AC58F900454E7E /* Stop.swift in Sources */, @@ -2954,7 +2949,6 @@ 57A27BCF1E44C4EC0057BD3A /* ContentsInterpolation.swift in Sources */, 57E5E1A31E3B393900D1CB28 /* GroupRenderer.swift in Sources */, 5B6E192720AC58F900454E7E /* RadialGradient.swift in Sources */, - 57E5E1AD1E3B393900D1CB28 /* SVGParserRegexHelper.swift in Sources */, 5B6E192920AC58F900454E7E /* Align.swift in Sources */, 57E5E1B11E3B393900D1CB28 /* NSTimer+Closure.swift in Sources */, 30FF4962215CE97300FF653C /* MCAMediaTimingFillMode_iOS.swift in Sources */, From 954fd0e41575fa41fadc46db6942e11b68c0c885 Mon Sep 17 00:00:00 2001 From: Yuri Strot Date: Tue, 9 Jun 2020 22:21:01 +0700 Subject: [PATCH 31/48] Use the latest Xcode in Travis config --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ddaa8b2c..e6d6286c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ language: swift -osx_image: xcode10.2 +osx_image: xcode11.5 branches: only: - master script: - - set -o pipefail && xcodebuild test -project Macaw.xcodeproj -scheme 'Macaw iOS' -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,OS=12.2,name=iPhone X' | xcpretty; + - set -o pipefail && xcodebuild test -project Macaw.xcodeproj -scheme 'Macaw iOS' -sdk iphonesimulator13.5 ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,OS=13.5,name=iPhone X' | xcpretty; - set -o pipefail && xcodebuild test -project Macaw.xcodeproj -scheme 'MacawOSX' ONLY_ACTIVE_ARCH=NO | xcpretty; From ef7bdbc807620a754d1b8258d9fd958d503f44ef Mon Sep 17 00:00:00 2001 From: Yuri Strot Date: Tue, 9 Jun 2020 22:33:20 +0700 Subject: [PATCH 32/48] Change device used for Travis build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e6d6286c..784378d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ branches: - master script: - - set -o pipefail && xcodebuild test -project Macaw.xcodeproj -scheme 'Macaw iOS' -sdk iphonesimulator13.5 ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,OS=13.5,name=iPhone X' | xcpretty; + - set -o pipefail && xcodebuild test -project Macaw.xcodeproj -scheme 'Macaw iOS' -sdk iphonesimulator13.5 ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,OS=13.5,name=iPhone 11 Pro' | xcpretty; - set -o pipefail && xcodebuild test -project Macaw.xcodeproj -scheme 'MacawOSX' ONLY_ACTIVE_ARCH=NO | xcpretty; From 8319462d752f398afa827f6b3cf34d62d226c1ea Mon Sep 17 00:00:00 2001 From: Yuri Strot Date: Tue, 9 Jun 2020 23:39:48 +0700 Subject: [PATCH 33/48] Update project signing settings --- Macaw.xcodeproj/project.pbxproj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Macaw.xcodeproj/project.pbxproj b/Macaw.xcodeproj/project.pbxproj index 84cd4c12..59ee40a5 100644 --- a/Macaw.xcodeproj/project.pbxproj +++ b/Macaw.xcodeproj/project.pbxproj @@ -2998,6 +2998,7 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = NO; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -3023,6 +3024,7 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = NO; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -3220,8 +3222,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = 7T95R85V93; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(SDKROOT)", @@ -3243,8 +3245,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = 7T95R85V93; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(SDKROOT)", From ae5627279ef55e49309b05648431e12bc9be018d Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Wed, 10 Jun 2020 15:11:36 +0700 Subject: [PATCH 34/48] Fix test references --- .../xcschemes/MacawTests.xcscheme | 29 ++++++++++++++++--- MacawTests/MacawSVGTests.swift | 8 +++-- MacawTests/svg/textBasicTransform.reference | 2 +- .../coords-trans-05-t-manual.reference | 2 +- .../coords-trans-07-t-manual.reference | 4 +-- .../coords-trans-09-t-manual.reference | 6 ++-- .../coords-trans-14-f-manual.reference | 2 +- ...coords-transformattr-01-f-manual.reference | 24 +++++++-------- ...coords-transformattr-02-f-manual.reference | 4 +-- ...coords-transformattr-05-f-manual.reference | 4 +-- .../masking-path-02-b-manual.reference | 4 +-- .../metadata-example-01-t-manual.reference | 2 +- .../pservers-grad-22-b-manual.reference | 4 +-- .../shapes-ellipse-03-f-manual.reference | 2 +- .../shapes-line-02-f-manual.reference | 2 +- .../shapes-rect-05-f-manual.reference | 2 +- .../struct-frag-06-t-manual.reference | 4 +-- .../struct-group-01-t-manual.reference | 2 +- .../struct-use-03-t-manual.reference | 4 +-- 19 files changed, 67 insertions(+), 44 deletions(-) diff --git a/Macaw.xcodeproj/xcshareddata/xcschemes/MacawTests.xcscheme b/Macaw.xcodeproj/xcshareddata/xcschemes/MacawTests.xcscheme index 94cec15d..998fd245 100644 --- a/Macaw.xcodeproj/xcshareddata/xcschemes/MacawTests.xcscheme +++ b/Macaw.xcodeproj/xcshareddata/xcschemes/MacawTests.xcscheme @@ -5,6 +5,22 @@ + + + + + + - - - - + + + + diff --git a/MacawTests/MacawSVGTests.swift b/MacawTests/MacawSVGTests.swift index fb22ef30..ba9a9b0f 100644 --- a/MacawTests/MacawSVGTests.swift +++ b/MacawTests/MacawSVGTests.swift @@ -43,7 +43,9 @@ class MacawSVGTests: XCTestCase { if let path = bundle.path(forResource: referenceFile, ofType: "reference") { let clipReferenceContent = try String.init(contentsOfFile: path).trimmingCharacters(in: .newlines) let result = SVGSerializer.serialize(node: node) - XCTAssertEqual(result, clipReferenceContent) + if result != clipReferenceContent { + XCTFail("result is not equal to referenceContent") + } } else { XCTFail("No file \(referenceFile)") } @@ -195,7 +197,7 @@ class MacawSVGTests: XCTestCase { let referenceContent = try String(contentsOfFile: path) let nodeContent = String(data: getJSONData(node: node), encoding: String.Encoding.utf8) - + if nodeContent != referenceContent { XCTFail("nodeContent is not equal to referenceContent") } @@ -314,7 +316,7 @@ class MacawSVGTests: XCTestCase { } func writeToFile(string: String, fileName: String) -> URL? { - guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else { + guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) as NSURL else { return .none } do { diff --git a/MacawTests/svg/textBasicTransform.reference b/MacawTests/svg/textBasicTransform.reference index f8c09e0a..1faa17c8 100644 --- a/MacawTests/svg/textBasicTransform.reference +++ b/MacawTests/svg/textBasicTransform.reference @@ -1 +1 @@ -Point +Point \ No newline at end of file diff --git a/MacawTests/w3cSVGTests/coords-trans-05-t-manual.reference b/MacawTests/w3cSVGTests/coords-trans-05-t-manual.reference index b257fd9d..477448c5 100644 --- a/MacawTests/w3cSVGTests/coords-trans-05-t-manual.reference +++ b/MacawTests/w3cSVGTests/coords-trans-05-t-manual.reference @@ -40,7 +40,7 @@ } ], "node" : "Group", - "place" : "7.5, 0, 0, 5, 125.000002, 525" + "place" : "7.5, 0, 0, 5, 125.000003, 525" } ], "node" : "Group", diff --git a/MacawTests/w3cSVGTests/coords-trans-07-t-manual.reference b/MacawTests/w3cSVGTests/coords-trans-07-t-manual.reference index 76e5d517..5b7afca7 100644 --- a/MacawTests/w3cSVGTests/coords-trans-07-t-manual.reference +++ b/MacawTests/w3cSVGTests/coords-trans-07-t-manual.reference @@ -36,7 +36,7 @@ } ], "node" : "Group", - "place" : ".866025, .5, -.5, .866025, 123.205081, 186.60254" + "place" : "0.866025, 0.5, -0.5, 0.866025, 123.205081, 186.60254" }, { "align" : "min", @@ -86,7 +86,7 @@ } ], "node" : "Group", - "place" : ".866025, .5, -.5, .866025, 200, 100" + "place" : "0.866025, 0.5, -0.5, 0.866025, 200, 100" }, { "align" : "min", diff --git a/MacawTests/w3cSVGTests/coords-trans-09-t-manual.reference b/MacawTests/w3cSVGTests/coords-trans-09-t-manual.reference index f452cef4..d094312d 100644 --- a/MacawTests/w3cSVGTests/coords-trans-09-t-manual.reference +++ b/MacawTests/w3cSVGTests/coords-trans-09-t-manual.reference @@ -186,7 +186,7 @@ } ], "node" : "Group", - "place" : "1, 0, .5, 1, 30, 170" + "place" : "1, 0, 0.5, 1, 30, 170" }, { "align" : "min", @@ -236,7 +236,7 @@ } ], "node" : "Group", - "place" : "1, .5, 0, 1, 100, 200" + "place" : "1, 0.5, 0, 1, 100, 200" }, { "align" : "min", @@ -336,7 +336,7 @@ } ], "node" : "Group", - "place" : "1, .8, .8, 1, 300, 220" + "place" : "1, 0.8, 0.8, 1, 300, 220" }, { "align" : "min", diff --git a/MacawTests/w3cSVGTests/coords-trans-14-f-manual.reference b/MacawTests/w3cSVGTests/coords-trans-14-f-manual.reference index db9708f8..916da24c 100644 --- a/MacawTests/w3cSVGTests/coords-trans-14-f-manual.reference +++ b/MacawTests/w3cSVGTests/coords-trans-14-f-manual.reference @@ -564,7 +564,7 @@ } ], "node" : "Group", - "place" : ".704769, -.256515, .256515, .704769, 0, 0" + "place" : "0.704769, -0.256515, 0.256515, 0.704769, 0, 0" } ], "node" : "Group" diff --git a/MacawTests/w3cSVGTests/coords-transformattr-01-f-manual.reference b/MacawTests/w3cSVGTests/coords-transformattr-01-f-manual.reference index 7cea70d3..4560daa7 100644 --- a/MacawTests/w3cSVGTests/coords-transformattr-01-f-manual.reference +++ b/MacawTests/w3cSVGTests/coords-transformattr-01-f-manual.reference @@ -20,7 +20,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -52,7 +52,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -72,7 +72,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -104,7 +104,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -124,7 +124,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -156,7 +156,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -176,7 +176,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -208,7 +208,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -228,7 +228,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -260,7 +260,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -280,7 +280,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" }, { "contents" : [ @@ -312,7 +312,7 @@ } ], "node" : "Group", - "place" : ".565685, .565685, -.41411, .71726, 50, 50" + "place" : "0.565685, 0.565685, -0.41411, 0.71726, 50, 50" } ], "node" : "Group" diff --git a/MacawTests/w3cSVGTests/coords-transformattr-02-f-manual.reference b/MacawTests/w3cSVGTests/coords-transformattr-02-f-manual.reference index 3081d23b..a94821e5 100644 --- a/MacawTests/w3cSVGTests/coords-transformattr-02-f-manual.reference +++ b/MacawTests/w3cSVGTests/coords-transformattr-02-f-manual.reference @@ -130,7 +130,7 @@ } ], "node" : "Group", - "place" : ".707107, .707107, -.707107, .707107, 0, 0" + "place" : "0.707107, 0.707107, -0.707107, 0.707107, 0, 0" } ], "node" : "Group", @@ -209,7 +209,7 @@ } ], "node" : "Group", - "place" : ".707107, .707107, -.707107, .707107, 0, 0" + "place" : "0.707107, 0.707107, -0.707107, 0.707107, 0, 0" } ], "node" : "Group", diff --git a/MacawTests/w3cSVGTests/coords-transformattr-05-f-manual.reference b/MacawTests/w3cSVGTests/coords-transformattr-05-f-manual.reference index e11a73d1..ef8dfa66 100644 --- a/MacawTests/w3cSVGTests/coords-transformattr-05-f-manual.reference +++ b/MacawTests/w3cSVGTests/coords-transformattr-05-f-manual.reference @@ -106,7 +106,7 @@ } ], "node" : "Group", - "place" : ".965926, .258819, -.258819, .965926, 0, 0" + "place" : "0.965926, 0.258819, -0.258819, 0.965926, 0, 0" }, { "contents" : [ @@ -214,7 +214,7 @@ } ], "node" : "Group", - "place" : ".965926, .258819, -.258819, .965926, 0, 0" + "place" : "0.965926, 0.258819, -0.258819, 0.965926, 0, 0" } ], "node" : "Group", diff --git a/MacawTests/w3cSVGTests/masking-path-02-b-manual.reference b/MacawTests/w3cSVGTests/masking-path-02-b-manual.reference index 5e21d1e7..ad5b4a32 100644 --- a/MacawTests/w3cSVGTests/masking-path-02-b-manual.reference +++ b/MacawTests/w3cSVGTests/masking-path-02-b-manual.reference @@ -87,7 +87,7 @@ "y" : 0 }, "node" : "Shape", - "place" : ".707107, -.707107, .707107, .707107, 100, 200" + "place" : "0.707107, -0.707107, 0.707107, 0.707107, 100, 200" }, { "form" : { @@ -98,7 +98,7 @@ "y" : 60 }, "node" : "Shape", - "place" : ".707107, -.707107, .707107, .707107, 100, 200", + "place" : "0.707107, -0.707107, 0.707107, 0.707107, 100, 200", "stroke" : { "cap" : "butt", "dashes" : [ diff --git a/MacawTests/w3cSVGTests/metadata-example-01-t-manual.reference b/MacawTests/w3cSVGTests/metadata-example-01-t-manual.reference index acf016fe..bc33eed6 100644 --- a/MacawTests/w3cSVGTests/metadata-example-01-t-manual.reference +++ b/MacawTests/w3cSVGTests/metadata-example-01-t-manual.reference @@ -6424,7 +6424,7 @@ } ], "node" : "Group", - "place" : ".04455, -.0227, .0227, .04455, 153, 58" + "place" : "0.04455, -0.0227, 0.0227, 0.04455, 153, 58" } ], "node" : "Group", diff --git a/MacawTests/w3cSVGTests/pservers-grad-22-b-manual.reference b/MacawTests/w3cSVGTests/pservers-grad-22-b-manual.reference index 045538db..0ad861b4 100644 --- a/MacawTests/w3cSVGTests/pservers-grad-22-b-manual.reference +++ b/MacawTests/w3cSVGTests/pservers-grad-22-b-manual.reference @@ -18,7 +18,7 @@ } ], "node" : "Group", - "place" : ".5, 0, 0, 1, 0, 0" + "place" : "0.5, 0, 0, 1, 0, 0" } ], "node" : "Group", @@ -51,7 +51,7 @@ "y" : 0 }, "node" : "Shape", - "place" : ".5, 0, 0, 1, 0, 0" + "place" : "0.5, 0, 0, 1, 0, 0" } ], "node" : "Group", diff --git a/MacawTests/w3cSVGTests/shapes-ellipse-03-f-manual.reference b/MacawTests/w3cSVGTests/shapes-ellipse-03-f-manual.reference index 593a0eb8..6933b717 100644 --- a/MacawTests/w3cSVGTests/shapes-ellipse-03-f-manual.reference +++ b/MacawTests/w3cSVGTests/shapes-ellipse-03-f-manual.reference @@ -170,7 +170,7 @@ } ], "node" : "Group", - "place" : ".866025, -.5, .5, .866025, 350, 150" + "place" : "0.866025, -0.5, 0.5, 0.866025, 350, 150" } ], "node" : "Group" diff --git a/MacawTests/w3cSVGTests/shapes-line-02-f-manual.reference b/MacawTests/w3cSVGTests/shapes-line-02-f-manual.reference index b31c8195..32ea892b 100644 --- a/MacawTests/w3cSVGTests/shapes-line-02-f-manual.reference +++ b/MacawTests/w3cSVGTests/shapes-line-02-f-manual.reference @@ -41,7 +41,7 @@ "y2" : 100 }, "node" : "Shape", - "place" : ".965926, .258819, -.258819, .965926, 0, 0", + "place" : "0.965926, 0.258819, -0.258819, 0.965926, 0, 0", "stroke" : { "cap" : "butt", "dashes" : [ diff --git a/MacawTests/w3cSVGTests/shapes-rect-05-f-manual.reference b/MacawTests/w3cSVGTests/shapes-rect-05-f-manual.reference index 5384b245..5d780160 100644 --- a/MacawTests/w3cSVGTests/shapes-rect-05-f-manual.reference +++ b/MacawTests/w3cSVGTests/shapes-rect-05-f-manual.reference @@ -160,7 +160,7 @@ } ], "node" : "Group", - "place" : ".866025, -.5, .815207, .68404, 100, 100" + "place" : "0.866025, -0.5, 0.815207, 0.68404, 100, 100" } ], "node" : "Group" diff --git a/MacawTests/w3cSVGTests/struct-frag-06-t-manual.reference b/MacawTests/w3cSVGTests/struct-frag-06-t-manual.reference index 55fce126..4c8893fb 100644 --- a/MacawTests/w3cSVGTests/struct-frag-06-t-manual.reference +++ b/MacawTests/w3cSVGTests/struct-frag-06-t-manual.reference @@ -192,7 +192,7 @@ } ], "node" : "Group", - "place" : ".2, 0, 0, .2, 90, 235" + "place" : "0.2, 0, 0, 0.2, 90, 235" }, { "align" : "mid", @@ -296,7 +296,7 @@ } ], "node" : "Group", - "place" : ".2, 0, 0, .2, 190, 235" + "place" : "0.2, 0, 0, 0.2, 190, 235" } ], "node" : "Group" diff --git a/MacawTests/w3cSVGTests/struct-group-01-t-manual.reference b/MacawTests/w3cSVGTests/struct-group-01-t-manual.reference index 4814ac3e..461c2681 100644 --- a/MacawTests/w3cSVGTests/struct-group-01-t-manual.reference +++ b/MacawTests/w3cSVGTests/struct-group-01-t-manual.reference @@ -102,7 +102,7 @@ } ], "node" : "Group", - "place" : ".939693, -.34202, .34202, .939693, 0, 0" + "place" : "0.939693, -0.34202, 0.34202, 0.939693, 0, 0" } ], "node" : "Group" diff --git a/MacawTests/w3cSVGTests/struct-use-03-t-manual.reference b/MacawTests/w3cSVGTests/struct-use-03-t-manual.reference index edb66756..b7df32a3 100644 --- a/MacawTests/w3cSVGTests/struct-use-03-t-manual.reference +++ b/MacawTests/w3cSVGTests/struct-use-03-t-manual.reference @@ -64,7 +64,7 @@ } ], "node" : "Group", - "place" : ".707107, .707107, -.707107, .707107, 120, 99.289322" + "place" : "0.707107, 0.707107, -0.707107, 0.707107, 120, 99.289322" }, { "contents" : [ @@ -81,7 +81,7 @@ "y" : 0 }, "node" : "Shape", - "place" : ".707107, .707107, -.707107, .707107, 120, 99.289322", + "place" : "0.707107, 0.707107, -0.707107, 0.707107, 120, 99.289322", "stroke" : { "cap" : "butt", "dashes" : [ From 4751d8b30100333292677b3526bf3e1e5301d72a Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Wed, 10 Jun 2020 15:34:13 +0700 Subject: [PATCH 35/48] Small improvement --- MacawTests/MacawSVGTests.swift | 8 ++++---- Source/svg/SVGSerializer.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MacawTests/MacawSVGTests.swift b/MacawTests/MacawSVGTests.swift index ba9a9b0f..9799315a 100644 --- a/MacawTests/MacawSVGTests.swift +++ b/MacawTests/MacawSVGTests.swift @@ -41,10 +41,10 @@ class MacawSVGTests: XCTestCase { do { if let path = bundle.path(forResource: referenceFile, ofType: "reference") { - let clipReferenceContent = try String.init(contentsOfFile: path).trimmingCharacters(in: .newlines) - let result = SVGSerializer.serialize(node: node) - if result != clipReferenceContent { - XCTFail("result is not equal to referenceContent") + let referenceContent = try String.init(contentsOfFile: path).trimmingCharacters(in: .newlines) + let nodeContent = SVGSerializer.serialize(node: node) + if nodeContent != referenceContent { + XCTFail("nodeContent is not equal to referenceContent") } } else { XCTFail("No file \(referenceFile)") diff --git a/Source/svg/SVGSerializer.swift b/Source/svg/SVGSerializer.swift index a5ef8822..64ad1607 100644 --- a/Source/svg/SVGSerializer.swift +++ b/Source/svg/SVGSerializer.swift @@ -407,7 +407,7 @@ extension Double { func serialize() -> String { let formatter = NumberFormatter() formatter.minimumIntegerDigits = 1 - formatter.maximumFractionDigits = 15 + formatter.maximumFractionDigits = 6 return abs(self.remainder(dividingBy: 1)) > 0.00001 ? formatter.string(from: NSNumber(value: self))! : String(Int(self.rounded())) } } From 75678de4ec7745dcc616305f70fa85ef53601a02 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Wed, 10 Jun 2020 15:40:50 +0700 Subject: [PATCH 36/48] Try to fix mac build --- Source/svg/SVGParser.swift | 6 +++--- Source/views/MacawView.swift | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 6c79ea89..facdd4d9 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -547,7 +547,7 @@ open class SVGParser { scanner.scanString("(", into: nil), let valuesString = scanner.scannedUpToString(")"), scanner.scanString(")", into: nil) else { - break stopParse + break stopParse } // Skip an optional comma after ")". @@ -1148,7 +1148,7 @@ open class SVGParser { baseline: .alphabetic, place: place, opacity: opacity) - } else if let tspanElement = element as? XMLElement, + } else if let tspanElement = element as? SWXMLHash.XMLElement, tspanElement.name == "tspan" { // parse as element // ultimately skip it if it cannot be parsed @@ -1179,7 +1179,7 @@ open class SVGParser { return collection } - fileprivate func parseTspan(_ element: XMLElement, + fileprivate func parseTspan(_ element: SWXMLHash.XMLElement, withWhitespace: Bool = false, textAnchor: String?, fill: Fill?, diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index be4becd0..aea5ee95 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -59,12 +59,10 @@ open class MacawView: MView, MGestureRecognizerDelegate { #if os(OSX) open override var layer: CALayer? { didSet { - guard self.layer != nil else { - return + if self.layer == nil { + initializeView() + renderer = RenderUtils.createNodeRenderer(node, view: drawingView) } - initializeView() - - renderer = RenderUtils.createNodeRenderer(node, view: drawingView) } } #endif From ee7fa2077bb2341d0d7243420e9b5f7f379953c0 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 11 Jun 2020 12:27:56 +0700 Subject: [PATCH 37/48] MacOS tests fix --- Source/svg/SVGSerializer.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/svg/SVGSerializer.swift b/Source/svg/SVGSerializer.swift index 64ad1607..096abbe1 100644 --- a/Source/svg/SVGSerializer.swift +++ b/Source/svg/SVGSerializer.swift @@ -408,6 +408,7 @@ extension Double { let formatter = NumberFormatter() formatter.minimumIntegerDigits = 1 formatter.maximumFractionDigits = 6 + formatter.decimalSeparator = "." return abs(self.remainder(dividingBy: 1)) > 0.00001 ? formatter.string(from: NSNumber(value: self))! : String(Int(self.rounded())) } } From 7e695e30c4f528cd298e3ba264d0ff40a21d22f1 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 11 Jun 2020 12:31:18 +0700 Subject: [PATCH 38/48] Fix some warnings --- Source/platform/iOS/Common_iOS.swift | 2 +- Source/platform/macOS/Common_macOS.swift | 2 +- Source/utils/CGMappings.swift | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/platform/iOS/Common_iOS.swift b/Source/platform/iOS/Common_iOS.swift index 9e2f21e6..3a25ac25 100644 --- a/Source/platform/iOS/Common_iOS.swift +++ b/Source/platform/iOS/Common_iOS.swift @@ -100,7 +100,7 @@ extension UIScreen { } extension UIBezierPath { - var usesEvenOddFillRule: Bool { + var mUsesEvenOddFillRule: Bool { return self.usesEvenOddFillRule } } diff --git a/Source/platform/macOS/Common_macOS.swift b/Source/platform/macOS/Common_macOS.swift index d870d1f2..f667c3e8 100644 --- a/Source/platform/macOS/Common_macOS.swift +++ b/Source/platform/macOS/Common_macOS.swift @@ -185,7 +185,7 @@ extension NSWindow { } extension NSBezierPath { - var usesEvenOddFillRule: Bool { + var mUsesEvenOddFillRule: Bool { return self.windingRule == .evenOdd } } diff --git a/Source/utils/CGMappings.swift b/Source/utils/CGMappings.swift index 45a96873..6fe5042e 100644 --- a/Source/utils/CGMappings.swift +++ b/Source/utils/CGMappings.swift @@ -157,7 +157,7 @@ public extension Node { extension MBezierPath { public func toMacaw() -> Path { - let fillRule: FillRule = self.usesEvenOddFillRule ? .evenodd : .nonzero + let fillRule: FillRule = self.mUsesEvenOddFillRule ? .evenodd : .nonzero return self.cgPath.toMacaw(fillRule: fillRule) } @@ -178,7 +178,7 @@ extension CGPath { } var segments = [PathSegment]() - self.forEach(body: { (element: CGPathElement) in + self.forEach { (element: CGPathElement) in let segment: PathSegment switch element.type { @@ -196,7 +196,7 @@ extension CGPath { fatalError("Unknown element type: \(element.type)") } segments.append(segment) - }) + } return Path(segments: segments, fillRule: .nonzero) } From 9c2c320c01a5d4902c7dc68b532eed25d522cc52 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 11 Jun 2020 13:15:50 +0700 Subject: [PATCH 39/48] Add test diff to log --- MacawTests/MacawSVGTests.swift | 32 +++++--- MacawTests/TestUtils.swift | 129 +++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 11 deletions(-) diff --git a/MacawTests/MacawSVGTests.swift b/MacawTests/MacawSVGTests.swift index 9799315a..86091cc9 100644 --- a/MacawTests/MacawSVGTests.swift +++ b/MacawTests/MacawSVGTests.swift @@ -36,16 +36,29 @@ class MacawSVGTests: XCTestCase { super.tearDown() } + func compareResults(nodeContent: String?, referenceContent: String?) { + guard let nodeContent = nodeContent else { + XCTFail("nodeContent is empty") + return + } + guard let referenceContent = referenceContent else { + XCTFail("referenceContent is empty") + return + } + + if nodeContent != referenceContent { + XCTFail("nodeContent is not equal to referenceContent" + TestUtils.prettyFirstDifferenceBetweenStrings(s1: nodeContent, s2: referenceContent)) + } + } + func validate(node: Node, referenceFile: String) { let bundle = Bundle(for: type(of: TestUtils())) do { if let path = bundle.path(forResource: referenceFile, ofType: "reference") { - let referenceContent = try String.init(contentsOfFile: path).trimmingCharacters(in: .newlines) - let nodeContent = SVGSerializer.serialize(node: node) - if nodeContent != referenceContent { - XCTFail("nodeContent is not equal to referenceContent") - } + let clipReferenceContent = try String.init(contentsOfFile: path).trimmingCharacters(in: .newlines) + let result = SVGSerializer.serialize(node: node) + compareResults(nodeContent: result, referenceContent: clipReferenceContent) } else { XCTFail("No file \(referenceFile)") } @@ -194,13 +207,10 @@ class MacawSVGTests: XCTestCase { let bundle = Bundle(for: type(of: TestUtils())) do { if let path = bundle.path(forResource: referenceFile, ofType: "reference") { - let referenceContent = try String(contentsOfFile: path) - - let nodeContent = String(data: getJSONData(node: node), encoding: String.Encoding.utf8) - if nodeContent != referenceContent { - XCTFail("nodeContent is not equal to referenceContent") - } + let referenceContent = try String(contentsOfFile: path) + let nodeContent = String(data: getJSONData(node: node), encoding: String.Encoding.utf8) + compareResults(nodeContent: nodeContent, referenceContent: referenceContent) let nativeImage = getImage(from: referenceFile) diff --git a/MacawTests/TestUtils.swift b/MacawTests/TestUtils.swift index 5fedc62f..71ca68b7 100644 --- a/MacawTests/TestUtils.swift +++ b/MacawTests/TestUtils.swift @@ -25,4 +25,133 @@ class TestUtils { return result } + class func prettyFirstDifferenceBetweenStrings(s1: String, s2: String) -> String { + return prettyFirstDifferenceBetweenNSStrings(s1: s1 as NSString, s2: s2 as NSString) as String + } + +} + +/// Find first differing character between two strings +/// +/// :param: s1 First String +/// :param: s2 Second String +/// +/// :returns: .DifferenceAtIndex(i) or .NoDifference +fileprivate func firstDifferenceBetweenStrings(s1: NSString, s2: NSString) -> FirstDifferenceResult { + let len1 = s1.length + let len2 = s2.length + + let lenMin = min(len1, len2) + + for i in 0.. NSString { + let firstDifferenceResult = firstDifferenceBetweenStrings(s1: s1, s2: s2) + return prettyDescriptionOfFirstDifferenceResult(firstDifferenceResult: firstDifferenceResult, s1: s1, s2: s2) +} + + +/// Create a formatted String representation of a FirstDifferenceResult for two strings +/// +/// :param: firstDifferenceResult FirstDifferenceResult +/// :param: s1 First string used in generation of firstDifferenceResult +/// :param: s2 Second string used in generation of firstDifferenceResult +/// +/// :returns: a printable string, possibly containing significant whitespace and newlines +fileprivate func prettyDescriptionOfFirstDifferenceResult(firstDifferenceResult: FirstDifferenceResult, s1: NSString, s2: NSString) -> NSString { + + func diffString(index: Int, s1: NSString, s2: NSString) -> NSString { + let markerArrow = "\u{2b06}" // "⬆" + let ellipsis = "\u{2026}" // "…" + /// Given a string and a range, return a string representing that substring. + /// + /// If the range starts at a position other than 0, an ellipsis + /// will be included at the beginning. + /// + /// If the range ends before the actual end of the string, + /// an ellipsis is added at the end. + func windowSubstring(s: NSString, range: NSRange) -> String { + let validRange = NSMakeRange(range.location, min(range.length, s.length - range.location)) + let substring = s.substring(with: validRange) + + let prefix = range.location > 0 ? ellipsis : "" + let suffix = (s.length - range.location > range.length) ? ellipsis : "" + + return "\(prefix)\(substring)\(suffix)" + } + + // Show this many characters before and after the first difference + let windowPrefixLength = 10 + let windowSuffixLength = 10 + let windowLength = windowPrefixLength + 1 + windowSuffixLength + + let windowIndex = max(index - windowPrefixLength, 0) + let windowRange = NSMakeRange(windowIndex, windowLength) + + let sub1 = windowSubstring(s: s1, range: windowRange) + let sub2 = windowSubstring(s: s2, range: windowRange) + + let markerPosition = min(windowSuffixLength, index) + (windowIndex > 0 ? 1 : 0) + + let markerPrefix = String(repeating: " ", count: markerPosition) + let markerLine = "\(markerPrefix)\(markerArrow)" + + return "Difference at index \(index):\n\(sub1)\n\(sub2)\n\(markerLine)" as NSString + } + + switch firstDifferenceResult { + case .NoDifference: return "No difference" + case .DifferenceAtIndex(let index): return diffString(index: index, s1: s1, s2: s2) + } +} + +/// Result type for firstDifferenceBetweenStrings() +public enum FirstDifferenceResult { + /// Strings are identical + case NoDifference + + /// Strings differ at the specified index. + /// + /// This could mean that characters at the specified index are different, + /// or that one string is longer than the other + case DifferenceAtIndex(Int) +} + +extension FirstDifferenceResult: CustomStringConvertible { + /// Textual representation of a FirstDifferenceResult + public var description: String { + switch self { + case .NoDifference: + return "NoDifference" + case .DifferenceAtIndex(let index): + return "DifferenceAtIndex(\(index))" + } + } + + /// Textual representation of a FirstDifferenceResult for debugging purposes + public var debugDescription: String { + return self.description + } } From 6ea8fb9335ff4a2cbedef929537217fae4e2de02 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Thu, 11 Jun 2020 14:20:02 +0700 Subject: [PATCH 40/48] Specify what kind of test failed --- MacawTests/MacawSVGTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MacawTests/MacawSVGTests.swift b/MacawTests/MacawSVGTests.swift index 86091cc9..78756407 100644 --- a/MacawTests/MacawSVGTests.swift +++ b/MacawTests/MacawSVGTests.swift @@ -20,13 +20,13 @@ class MacawSVGTests: XCTestCase { private let testFolderName = "MacawTestOutputData" private let shouldComparePNGImages = true private let multipleTestsWillRun = false - private let shouldSaveFaildedTestImage = false + private let shouldSaveFailedTestImage = false override func setUp() { // Put setup code here. This method is called before the invocation of each test method in the class. super.setUp() - if shouldSaveFaildedTestImage { + if shouldSaveFailedTestImage { setupTestFolderDirectory() } } @@ -273,9 +273,9 @@ class MacawSVGTests: XCTestCase { if referenceContentData != nodeContentData { - var failInfo = "referenceContentData is not equal to nodeContentData" + var failInfo = "referenceImageData is not equal to nodeImageData" - if shouldSaveFaildedTestImage { + if shouldSaveFailedTestImage { let _ = saveImage(image: referenceImage, fileName: referenceFile + "_reference") let _ = saveImage(image: nodeImage, fileName: referenceFile + "_incorrect") From 3755512b92700e29ff9992c72c50aa6cf9c3b393 Mon Sep 17 00:00:00 2001 From: Alisa Mylnikova Date: Tue, 16 Jun 2020 15:41:48 +0700 Subject: [PATCH 41/48] Regenerate PNG tests --- MacawTests/png/color-prop-01-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/color-prop-03-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/color-prop-04-t-manual-osx.png | Bin 691258 -> 691258 bytes MacawTests/png/color-prop-05-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-coord-01-t-manual.png | Bin 701350 -> 701350 bytes MacawTests/png/coords-coord-02-t-manual.png | Bin 701350 -> 701350 bytes MacawTests/png/coords-trans-01-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-02-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-03-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-04-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-05-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-06-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-07-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-08-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-09-t-manual.png | Bin 771734 -> 771734 bytes MacawTests/png/coords-trans-10-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-11-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-12-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-13-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/coords-trans-14-f-manual.png | Bin 890530 -> 890530 bytes .../png/coords-transformattr-01-f-manual.png | Bin 691258 -> 691258 bytes .../png/coords-transformattr-02-f-manual.png | Bin 709926 -> 709926 bytes .../png/coords-transformattr-03-f-manual.png | Bin 691258 -> 691258 bytes .../png/coords-transformattr-04-f-manual.png | Bin 691258 -> 691258 bytes .../png/coords-transformattr-05-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/masking-filter-01-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/masking-intro-01-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/masking-mask-02-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/masking-path-02-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/masking-path-13-f-manual.png | Bin 691258 -> 691258 bytes .../png/metadata-example-01-t-manual.png | Bin 691258 -> 691258 bytes .../png/painting-control-01-f-manual.png | Bin 691258 -> 691258 bytes .../png/painting-control-02-f-manual.png | Bin 691258 -> 691258 bytes .../png/painting-control-03-f-manual.png | Bin 691258 -> 691258 bytes .../png/painting-control-06-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/painting-fill-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/painting-fill-02-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/painting-fill-03-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/painting-fill-04-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/painting-fill-05-b-manual.png | Bin 691258 -> 691258 bytes .../png/painting-stroke-01-t-manual.png | Bin 697002 -> 697002 bytes .../png/painting-stroke-02-t-manual.png | Bin 697002 -> 697002 bytes .../png/painting-stroke-03-t-manual.png | Bin 699874 -> 699874 bytes .../png/painting-stroke-04-t-manual.png | Bin 697002 -> 697002 bytes .../png/painting-stroke-05-t-manual.png | Bin 691258 -> 691258 bytes .../png/painting-stroke-06-t-manual.png | Bin 691258 -> 691258 bytes .../png/painting-stroke-07-t-manual.png | Bin 691258 -> 691258 bytes .../png/painting-stroke-08-t-manual.png | Bin 691258 -> 691258 bytes .../png/painting-stroke-09-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-02-t-manual.png | Bin 724638 -> 724638 bytes MacawTests/png/paths-data-03-f-manual.png | Bin 994270 -> 994270 bytes MacawTests/png/paths-data-04-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-05-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-06-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-07-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-08-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-09-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-10-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-12-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-13-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-14-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-15-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-16-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-17-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-18-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-19-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/paths-data-20-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-01-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-02-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-03-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-07-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-09-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-12-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-13-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-15-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-22-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/pservers-grad-23-f-manual.png | Bin 716166 -> 698922 bytes MacawTests/png/pservers-grad-24-f-manual.png | Bin 716166 -> 698922 bytes .../png/pservers-grad-stops-01-f-manual.png | Bin 716166 -> 698922 bytes MacawTests/png/render-elems-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/render-elems-02-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/render-elems-03-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-circle-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-circle-02-t-manual.png | Bin 872622 -> 872622 bytes MacawTests/png/shapes-ellipse-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-ellipse-02-t-manual.png | Bin 954630 -> 954630 bytes MacawTests/png/shapes-ellipse-03-f-manual.png | Bin 701310 -> 701310 bytes MacawTests/png/shapes-grammar-01-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-intro-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-line-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-line-02-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-polygon-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-polygon-02-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-polygon-03-t-manual.png | Bin 691258 -> 691258 bytes .../png/shapes-polyline-01-t-manual.png | Bin 691258 -> 691258 bytes .../png/shapes-polyline-02-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-rect-02-t-manual.png | Bin 694614 -> 694614 bytes MacawTests/png/shapes-rect-03-t-manual.png | Bin 863706 -> 863706 bytes MacawTests/png/shapes-rect-04-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-rect-05-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-rect-06-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/shapes-rect-07-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/struct-defs-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/struct-frag-02-t-manual.png | Bin 224138 -> 223222 bytes MacawTests/png/struct-frag-03-t-manual.png | Bin 224138 -> 223222 bytes MacawTests/png/struct-frag-04-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/struct-frag-06-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/struct-group-01-t-manual.png | Bin 694614 -> 694614 bytes MacawTests/png/struct-use-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/struct-use-03-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/struct-use-12-f-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/text-align-01-b-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/text-fonts-01-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/text-fonts-02-t-manual.png | Bin 691258 -> 691258 bytes MacawTests/png/types-basic-01-f-manual.png | Bin 691258 -> 691258 bytes 116 files changed, 0 insertions(+), 0 deletions(-) diff --git a/MacawTests/png/color-prop-01-b-manual.png b/MacawTests/png/color-prop-01-b-manual.png index 18a3bea53f2d0166fa45b5fbc865d016dd88c662..ddc2e9c13d234f105cc021a26d30d8f56ed25d38 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/color-prop-03-t-manual.png b/MacawTests/png/color-prop-03-t-manual.png index 1a392e6755b491455b787998b6df8907baf0ce78..abd0daf5321f5dda50762c521b2b8ed9e9dcbab6 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/color-prop-04-t-manual-osx.png b/MacawTests/png/color-prop-04-t-manual-osx.png index 78172e79c8d18a440443f3abcae937901138ea5a..36820a9d92b62f77687bdbcff7e1ae2cf5b5eb12 100644 GIT binary patch delta 9299 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 diff --git a/MacawTests/png/color-prop-05-t-manual.png b/MacawTests/png/color-prop-05-t-manual.png index 9776596fc5379147fa4d281c7b972497711a11bc..01af37567377a4d4b6dad1ebd370ced4b41fedef 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/coords-coord-01-t-manual.png b/MacawTests/png/coords-coord-01-t-manual.png index 746438f13ca818057deb1b8e0f61136467522781..a8d7ea9dd477c5724c07ef0071e8d321fe4953c5 100644 GIT binary patch delta 49 zcmZ4XTx;2Lt%erH7N!>F7M2#)7Pc1l7LF~P<>KxC@~J delta 53 zcmZ4XTx;2Lt%erH7N!>F7M2#)7Pc1l7LF~P<>H$S8f@67?|#YR(_SvlxxHMRE1+V# J|8%Z!82}pG68-=H diff --git a/MacawTests/png/coords-coord-02-t-manual.png b/MacawTests/png/coords-coord-02-t-manual.png index 915d9eaad71626e85f6da0db6563937c598a9bbe..00f7cad7c378d75e0953bdf581687811552d5000 100644 GIT binary patch delta 49 zcmZ4XTx;2Lt%erH7N!>F7M2#)7Pc1l7LF~P<>KxC@~J delta 53 zcmZ4XTx;2Lt%erH7N!>F7M2#)7Pc1l7LF~P<>H$S8f@67?|#YR(_SvlxxHMRE1+V# J|8%Z!82}pG68-=H diff --git a/MacawTests/png/coords-trans-01-b-manual.png b/MacawTests/png/coords-trans-01-b-manual.png index 4b9a607a3f156bf4e01207930a92fff7da5df09a..a0ab8d979456b1272c45945e29246f390e995a1f 100644 GIT binary patch delta 11025 zcma)?37pN<`^Vku&ez2-BO`;6DNAU|h%7%#qmrp?QBi~tvPFn2T_szjk>#MVWGBne z=-Q1v3`xHvOOlc;Yql}N|NZ&i`A&B<{r~^-dVS`5_Vb+QdCqg5^W0fGrQ+Ht6^ACf z5;GQzE#b~%5!WSE2yceJ-$xpJNE zzyJO~$s7uCN5+*XQKDDgym?d0moHyg@OV6z^5n^r;dZ;PWa6^wAAb1ZDolOA<#JuO zEbDl#T)EN*4jg#a#9Hn$&&0>aU!o>0Xw((WtdS2s_+T?e-DH*1`SRsU4+#l5S-N!T zXVv!5Lk~4(#ZwSg(@e44H9}dZGV>p`w~*v{NJ2ZQ>H;=na$FgP1)@JBwri#~8 zLYp>i`c0ZNX^rxck B+H$k-x$-gx7U!KF%-x)8LP+mPEwZP`dl$m3ow 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 delta 10968 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 Ze;Ef$aCu-=9)th@ delta 88 zcmdlrLu=Ozt%erH7N#xC;*OgY_;pw}=c}%9Z5MZB-Y)LQA|T!Ve;Er9vjQ<25VLRp lzl?(=7^u>KeR964&vyU696rqLKoZE}G|1lmJDjO b?f(yQuvoO$GjeXPXXL8M+g_i+wObhgc-$iz delta 97 zcmdlrLu=Ozt%fa(hod(u@UyUN&R4zSHeEV~F`^wvY?qE<;*oCue~<}?nSq!Eh*^P{ q4T#yd|3ApVVgb}a*SdFNY6vJCFpjI1RG5|IX*KmjM8|&m}bg diff --git a/MacawTests/png/coords-trans-06-t-manual.png b/MacawTests/png/coords-trans-06-t-manual.png index d3eb1a7f46f1de20b63076e6ef5cf5d9f1b9174a..1970f14752c5880eb472acb52fea7633555235e4 100644 GIT binary patch delta 86 zcmdlrLu=Ozt%fa(_Vv?OpJTi;S%JT$z5F!e_VUwAHzYSJ@T;(I&R5;y-0uIB35c12 km<5PgftU@5*|+;Y<=7(9UeCz6y`GV)CU1Lv2G?$70A21R82|tP delta 92 zcmdlrLu=Ozt%fa(_Vt?;_`k4j&R2cn(r#bRxZS><$wIo_|0xp?GXpUT5VHa?8xXT^ m_kYT}3Gu>LqLd diff --git a/MacawTests/png/coords-trans-07-t-manual.png b/MacawTests/png/coords-trans-07-t-manual.png index e831309eba3d1ad1db01e015ea3fe70bbf873558..855b5a6b88d8bdf30872027ce680e77e983d9ea6 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/coords-trans-08-t-manual.png b/MacawTests/png/coords-trans-08-t-manual.png index a923c516338b65fd3530d98a3378f8f39c4ca2cc..dd205d85416b0bc7447c1e1ec5a01871348294bf 100644 GIT binary patch delta 575 zcmdlrLu=OztqtsIo4-3xWtzM|l4bG&jrPsr?hlzKH%#Q1ETCJwx!4;hqaegGSwRtq z4VpJk_InDEonR8Z`F7A+rpYtR^EZDFWn_Wqu?gNB9JLWy58GrV`^3$~v8)gk6P*1w zpHJKh(#JP>kz4HM_bHrEz24rNy)%I_0)E+>vvYW%>Vw=_IT#oiL^q!)*upeF_W5nw;N1nxx+kJezz~gQSC-BB?6PWl0mXpK(c{g*$I8S zHyiMqg52;NB;^B=3WaE#wjCn%;i%Z=NuXFZ;Aa8KMuKFUC(Lc1FqaXCnShuXh*^M` n6^Pk@n0@<%xg5nY?e&bD+v^#*zLai{KgV9fzP-4d>$nI2b6VO8 delta 665 zcmdlrLu=OztqtsIn+^DqFWwL@I z5F0eZR49T~OfZQCs#wc3d4_pDQ05LuuPQ{ZO)yYqBhzGrUbe|f_K7f6nh^cY{*w*( zB{s9G0o`E0&jB=tZ}K9ySg@eF=0j#6LjYo)w>Ma@Sr@8Oz%Lstu-M=!Le+#Icc384 z=GP`bH-XHX5SqT3-GY$?$bp&@<-9rB2IwxJ?4&Nf$x^YAo15)f5egKf2E^>!C(Pw2j@cd{$zi}gS$?#>~Fq)BFVv@{K`ehCJsVGN-5%mRuLC*AbZ$@!r@mNLL2QQaaaB% zo0p_LC=RmPgoC)@;NNL;kTNskHt)@R-g^^S@I@AUGcI#e+*CKs^|;*ix>i&?pJC}z z)NvUY$K#?YTb(`wMQ zNE+!apb(cT;0=?-6ytJdinhZ}y{Y$?(L9Hu3t8AngSSdaxzR}%zhNyvt#&$k$dU(x zA|Xe+s0V>&n*7Tt(N-Ud+O(yD5>Wb%LxnJg)?V5*coHuIgg@was2tXgVvfzw#&J3_ z<%e3z#th2BXOQ*{zX$&`eHs1{bs;)4;HG+6kLN`#RbiIQU8F?Vv9T7R6T_|Rc?0lo i=pE|ktN(h%3%O0_2DIhr7A5waD;^q&5=Nrw<=H=ifW+Pa delta 612 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 T$6m9K$M4;4_H7#f^R)j04a~;t delta 2785 zcmZ8jdr*|u6+dS=D{Kp8Q79lFYeWSEUDN~=aYe=#qJS-0>mv%j9|2J`Mp0s%2!SS8 zVvf@?0cV`(w387>n`<&Um_ItxPEE9Foa&gF(y28jC6T0>7$0nZcb6qLcjkQGeVlvl zx##@O@B7|6n)%+*%!aevP;kZ*UB&wBZ0|4j?l`9UY}0%Xu7^#!Y_>J zcv5c((brJ%Pb9kg+a`)VvvS;)eCqTj&Eja;I$mu5}nU7hnMrzU@*F` z@}ENPN3i;ib6wE=h92Hysy1>I>#9Z$*N2r{9)sOIu<9QVSxj_qny#qgBzGo;KZdv= z3y7|VTuz9?kt>psupwBPhj#_hHz|6xipTI$eN+XEK>r`=*(zCxVfYE(0cMMnyNqzU zSkM2SYxK&e++qG+!*R@R-L{E`^GDjUiFfhz&_S`9x0#bhzw#80Nh10dJ`S_DuOd?G z)JUTHn3xRz@9FcKIaNoklpHhd0I+I+zdn0hBy0W|-lEkO3(?bfhbeuY6Ipw=@s3zz z;-A*y9HP^B`4RMwaC(+@)^a>o=($?JI>lVC<=S9ifwDxWZrIH?6A%Eu-GjXS*4nVm zLzH3GyujTTr+o~ct>`Q_}qsW=Ll>K{S0$hv^XF@c2wKrxyqX)OS2T|ubcF}0AK)TF#T4Uy{iwix ztgW(hSokAe{!z&2Gemzg4M%y-V8lPM2PGq<$JynG98EN_2)e-km+D#LN&>{3djCy! z3(TC<$PV_zp&Sb^x`lT?g6K~}APcpygNN&|0%!K*^$>5vHG$E#M+l3;oPE~nUburnbOSVwm(+?^$M4n_;HdxU~`h>ICV`HC!c7|5#~|m z@I)D4T3^0+ons#DBkDc~9{8bb1%KGAsIWa>jYS7gq9Ra z5pOcBg)$*{KVPwoc3MhGa{?RnLp5fuOwIWY%6?Gk1Az0-=mQ?X2Bvx~244mb?iK}R zV5Aq^c_G9cNlP7|&~?~$3rqdjEisv1R^;nDRgf>P;Eu`Vy%e$X31txHXE+xhnBEc$ltODe<=c?pO}b z6Ws6%J-bpm6JAApZ3vLA$H;{raJ&SQY-*bLXCRN6(re@BW|O;FQXG11t?c4^rfi-3 zXAl~nFtH>9 zPigi+S(1%^0~?mXwi_fg7B(=^pG@>`uwD<4{>X-MzMeiPxe@llDMX)|zJoGAmWXDE zEP+aw!-;L#JGHP`#?7k5PF%jl9s%IB+wm;?o!If}f${7ozC&3=In6}ZpxeI?y5Nro zTlI9a42!T^Q9t|ar_#BW=+#<%+$>2WUjlst1O9Rd7`7L=?sN|P4AtW&saB`7NKzDb z+{B@gUZSTP%<2|dJecWgY@$_u6=pnb67R58cFpE~Bx~mlnGyVl>A4||huS*@;)D!= mgq!xxi1kasuHNvr>d_^p^l?N<*daX8b9pzLht)swCI1hO?dgdC diff --git a/MacawTests/png/coords-trans-11-f-manual.png b/MacawTests/png/coords-trans-11-f-manual.png index 6081e00fc756d0df6f3822b63d78ee00f6565ead..15b5370740aad404923a32d6aa683b20295507ba 100644 GIT binary patch delta 10014 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 Q_l6+7Gpw84?70j79}_DmlK=n! delta 9607 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 NHuDYs^dnie=|BDc11bOj delta 4513 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 Xxhc`u`YVtTew&TxB9B@NtJJ!m^}j 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 F`hOIt9&`Wz diff --git a/MacawTests/png/coords-transformattr-01-f-manual.png b/MacawTests/png/coords-transformattr-01-f-manual.png index 8585ff7be639228158d1b1f595f19eb3fa776a83..7abc321dafd618ae890c235f372f7906ac8022cb 100644 GIT binary patch delta 734 zcmW+!O-NKx6n^)<_vXzPQ*Q*RDb1Nc8JaPjg@FrABh7)C7{OX;SX7Hp&~)b_jfNl+ z-hhiUtH#~nrUZWum{E&_Kq*B@Q9+@|C?Y8rW=7pNyK}zto$ov6-ZPVp%w!`&X_1y` zJ#D1Tv?py%di4Vu6c}db!24RdVP+y^cgv{+Y6NY)LxydMS(#U%&hr+^1$Fr`N)E8e zJ4M(wIANnyP<06!DHlX7wFeL*y9jH%6@picglvO*PGL>;A4jpE_8`XD31Cz3&KYd$ zoG61&P%whHE8wSS7&q7sVMTD~S$t7_6-tLvRhT6wj0Ik;#;)=`2SbpPfsajui>x9 zR6f58LIc!U1@B0}_HnKQ-#w^~`WfoxSjmS7P>K==J4I0JV{}=&P zQocL1J1&}N=&KRtKN)n$M}h+VW_!=-;@g zq;DaWzkh~{mXPiqRW9P^)HLcX^N6Nx6t*j&*tC*(@e-|t@lTV#!4u`SIn=6r4o&LL z{F2pORhsN9pjGDWWeA0SlH9P4q+ojmNy$&v)i*16TTb*;v}^0e=ogxnl+=m+dld$Ppu$BOIvmox;+t0~`9%gLSBX!Fcr7p2Q}2{?A zF0_DeZbUNdDrd9!N`7O|ZjJ3IMlD-W7n#yRymy($Xu937ooSWewcC)ei342+Dyk{p zjCwm}H9c%XYd9*zgv&U=UEA?10@L5ym=Gh5+~=N=Tf64?^LS}1K1VzgKSptdBYbu@ zqFVOZ5%*x0t`5MnXGdoDqXQO_?QpxRwI z{|r0S-=DEFWLDJFGyWWs{Sh=NHZbFs&iOZJR6YJMWM9gEulf#D>@=cxSaIzS90eJq diff --git a/MacawTests/png/coords-transformattr-02-f-manual.png b/MacawTests/png/coords-transformattr-02-f-manual.png index f20af4b38525d9006719e5ca56964e9837af69ca..451d93078958c2a07878c35bcb035f5681ea5069 100644 GIT binary patch delta 49 zcmZ3sN_*KV?S>Y{7N!>F7M2#)7Pc1l7LF~P)fVmA4xHPy9k>cAwoCVOY+&C$dk0sy F3;?eZ5fuOc delta 53 zcmZ3sN_*KV?S>Y{7N!>F7M2#)7Pc1l7LF~P)fU?g`Z)~PC;t{}Xs@>5++JF7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 52 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hwkOE28L&@}f57U~&ispGJM%Bjn!N45 I^SSJ00Qry;IsgCw diff --git a/MacawTests/png/coords-transformattr-04-f-manual.png b/MacawTests/png/coords-transformattr-04-f-manual.png index 91f659919afa00acc159ea1d13ebfd01b940db7e..2c5ba78e098e56b7b5dd861f1b2ffe86ba8df218 100644 GIT binary patch delta 47 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7RareJR}@|DLsmeS2{^*KrX5 DrbZFB delta 58 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hwg-5!8L&_H|HtaH-TyC#4|6+^1hP0+ M6m2gq=Q=I|0D*oMjQ{`u diff --git a/MacawTests/png/coords-transformattr-05-f-manual.png b/MacawTests/png/coords-transformattr-05-f-manual.png index 516264cb943f7f54cfd93f55034dac99dc41a96a..7c952c807fdd9f3c72e121881b0063c0b5093537 100644 GIT binary patch delta 71 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hw%0RqPGsFadk8QZf3IJajDa7~EcUZ26WTNwb?M;hS( delta 90 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@N?WZgdHKgVmv?f!o`T$z9Z>`>-* o0d~&&jFT1kdA8Rxa!zCgO3i0%U(dn0eLV-)h4}5i^SSJ00J<+9e*gdg diff --git a/MacawTests/png/masking-filter-01-f-manual.png b/MacawTests/png/masking-filter-01-f-manual.png index 95d986a597016e11b7dfa3266beafe25fbe14ef1..859ee50bdc2583f047323ab28fc8721a8c912b22 100644 GIT binary patch delta 73 zcmdlrLu=OztquA0n_ssvz1Xb4@5eqlUsbRDZ5|^KGXXI(5VHU=D-g2*G5hwnc^qQh V?e&bD+v^#*YVx+%XK?LS1_0ZCANK$N delta 84 zcmdlrLu=OztquA0n-%zd*f-nP8@*_Mo5u*mOhC*G#4JF}3dC$c%)b3?9*0;rP^|&` cib8~{=CZ_F466~tX+oKP_c>V0 z+p^eoA#U6)1XMNuy2a%4&&sD)`kUeCz&rF8rLpBy#p+l$M& Hj*9>Q;hd^H delta 441 zcmdlrLu=Oztqt4_n-%!`8Jp#*+U2SkftU%1nSq#PyId9P1l@M6^{m^q*0ViK0jitN z$i^kez~IxlIYDp})Al3@c2(x>+#>8iac%_$hUn!G@q6+h@%uZNL>MP4@TW}n?^WNv zNQM0_>-Oja2r-4}_ZM=qGqLR4uAswyn{|7=7W)E5#tqv8T-dxAH|GiFFm4Yr0~z$& z5GcXg#lX<~ar**ecA&-QY}qxLw^v)T&tYT@W?*Rlv0cU*B<|%36rb*YkLk_i^*cGH zzjkC_&D8m4`wSnI7-SzIl6JAjnnk{ecE;XOCom!n!>_oP9du z_ND~(r>xsw#{t>X|E06@Y)%tg!?b-{7P~I<^!?1tPe3M2o_~G)^s0Pzp6$n2nddM~ z&Jz^aZdAl>$UNPi8>nLbb&JWt_sh3`D`S7jI(@egP-yzI^=#b23=F|Dwm0O1oTSyr z-pIUNzn0yZvHe{u`}TLO9HJ%L56E&Euuslc_1W(Km&1p-9Y_LMoGXg97ngG#7XbkK C>Y%>> diff --git a/MacawTests/png/masking-mask-02-f-manual.png b/MacawTests/png/masking-mask-02-f-manual.png index c32fb1f39ff9baabb6be0cb2f5a79c7b2791d13b..f280bfd62af979d5e2f33eb33f63fef3e2e089de 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/masking-path-02-b-manual.png b/MacawTests/png/masking-path-02-b-manual.png index 6d9fc8cc61ec62ec60d15d2a4cd052053b9ddd6d..28fd9cafe664921c311e29e798181dcd94c3a56c 100644 GIT binary patch delta 46 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7RareJR~sFIL08y||p~xCj7{ C;}G=# delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoGXg9 K7ngG#7XbiEbrn|t diff --git a/MacawTests/png/masking-path-13-f-manual.png b/MacawTests/png/masking-path-13-f-manual.png index e8b84a82ae89b6b09b46ccfcc5e4c68ad81b5993..cb284ea1657f31bd4a7195417899a488792300e5 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/metadata-example-01-t-manual.png b/MacawTests/png/metadata-example-01-t-manual.png index c883e4bbfc9b3304b375c1be1aa9601dc89c51dc..e00f35aa7828ec3da509f91fb5481af992d4a59b 100644 GIT binary patch delta 1380 zcmY*YTTEP46wSTGxd@4n~F0;9{LaNi~V@45Gc*DRA3SSBp6PuVdrKY*>w7NXgx0k9K0<2MM#{v1P5 zF815c)rzCjd_gIHebrsW(r_p_<+YoE_+tK%1AA*q}WGMqVMzFWyL446k z`WLx31yix8V|i1Y`-`h<=)KFB-x$U4xvSM{%VGkvSLAi!M^V$~6lg z77AtLayk+T4T@66!ILZHFdQ5<#%G_jWANc1)|2bd%es-Gsz7LU6ZuEoP(xnG@t5iz*a!XE<)PqLwczLyTJ~qYaYna zn^2?#l-vrGut@h^w3v$;*aa!-qn#f&Iu~i@&-#AhmrS29{h5qz%#N*@1FS5IWKu&o zEF&6KsQxFV*^2#-EC-;ke-8EUZpe`zq0-XqM=5d>aVjaf&;e!DO(5{K!prHjVl>B) z|Mi)jm`Fs!_O@)ispsN5HGFfmvf2fCYY1v_Q%u+K&HX*+D#&OJpD-O^r-tFeVU=PO zx>hM?ncS-5KM)$(4AN|qo~i7b@Sf+MCg-`Tf{PqITiGQL$7fX!4*vt*eEuc? delta 1395 zcmY*ZZA@EL7|y*^E{;Uhj4me1qRB>rLx;oYk0mZy;{I&$pYe}T;}4V3FFr~W27{PP z;?zk(=3T-9A#Bdf$Qr)RXkh|Tr-lqD-Ppzk1-iD-miD%E?fvTGIhRee$=h@8J{&MFi;ybjb1*1$F9B8Pgolfo;bc36k4 zsP)w1ivJ3t=_q1bF}N4pI5kN;KXM-mZ`vt{a-h~*i=Me21d~C8rLgfpy|>;tW9z`Z z0c7M%XYH|4(LKeR$VdI$0ioRCalkra1zRu9T*UI`GJ;YNmwcDtyx~O6Kn=@Jejesd z2<6UkC#-{3)Q#4md#2k!hf*P2_FYE(7)|y>(B5&1KGZVa0_zav9(AD4*N3&_8lqcK zT)ll24c-RWuiIfKc69vJ0e{2~g_hU*LnWF1joU5k@3`3k%b*1=j|=?^{l=5&Y#IZ1 z2hcdyXfWHalfr50!aHXs&R`-i!4yK(RZOi+;q=sLScWWc5C_iveGW6B8OXULH=gj42|2PZFfCV;U!};m+MrE3=K~Bs;efTBTXPk)o z8c6XXS0#L+s(GZ-IyN>GC`ukrf2nNBaD_SI7mVTp`4+kQ4R69hHO@czD;5 zD)*>?ZxEkzV#C(}nd+1yBhZvI?fk2v<0NMyM>e=%p%^gprm{prgCuFl<@EoRuW3La zB)%_kZ z7RcdiP;xOSsk=~CMM$^l*<1tkht0@^hpF^^hIElie}4E+-e36nHhtcxQI_+D1NkS} zB}qphAXCd#dVeQ1+c8XN!3%w<3G#iKI`j)vnwlMCL)Va)chT{1BD2s$An>Ka>&c{w zRBGFu|N6|F3kDUeud9eebmo}&UI|}SoV`b>gMCmlYhuF8S9P`>GZA+QA1mCm?JTQK zML4X`3KeW^>15E%e=Hneqrox-gDM_9QYb<-^s%~`wJbZsuwIFid%2g{$TVpYm<`X|F za~FYQtkHq{C;8PZ+NpA?K6$)rvdkrxm1bwUB1i}v=TJdFYx vCK}JsD5N2f_M=73BgB28=%64be5T{%1HI^YK|B4YyJvr@BlWIbH+TO7g#rQe diff --git a/MacawTests/png/painting-control-01-f-manual.png b/MacawTests/png/painting-control-01-f-manual.png index a6753fb6b97c8de95181600042dd14b9aa92e987..26089211eacbba3b3354f746f3e5d991e53a1110 100644 GIT binary patch delta 47 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7RareJS0Zf04C@eS2{^*KrX5 DrOFYh delta 57 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hw%uekV4v>)kJV?p|6dLt=5`F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-control-03-f-manual.png b/MacawTests/png/painting-control-03-f-manual.png index 40dcd0f6321bdce33f68af0afeb6fc3b541ac189..e8774fd196ba0d8beb33354b0bc015674d5d9fef 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-control-06-f-manual.png b/MacawTests/png/painting-control-06-f-manual.png index 219170cccb4560acdc2adfb42f143319a75eac95..547b1434db1d608cb8879352ab3bf7abb066783b 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-fill-01-t-manual.png b/MacawTests/png/painting-fill-01-t-manual.png index 6fdee5aadb95723008d26104da20fefc6e9f944c..37d49dd8672f6c21c54851910570eb03793f6ae1 100644 GIT binary patch delta 72 zcmdlrLu=OztqqfHn)zL}^SdxkIJjAX--oR|pM?pCnSq!Eh*^P{ZFxQmyI%WiOLidU U0AkMVuPwO*%(mBOaP3wG0Gyy0?EnA( delta 87 zcmdlrLu=OztqqfHHY@O-VVj(vk(! zc4?OFxnazS%)H9~85kI#*mJtPGqW?V5|U6DNa*$UcT1TsGi|T6VD4eu{?LhCm5Emn tNs-%hc~|!P)B9Z5dA9#^Wq+&Co}R}J#2i4(xjj9PYYOA``V6k!$^f}0JHr3~ delta 148 zcmdlrLu=Oztqs{Wn-%!KuuaZa?P$)nY0tJ{+@5X2B=)xbuK_an pKUem*3P5`-n76MFW_Mv}f1Sn-#2i4(x&3t-*A&L>zw^25WdMcKH?aT! diff --git a/MacawTests/png/painting-fill-03-t-manual.png b/MacawTests/png/painting-fill-03-t-manual.png index 7898d5ee5a52565b0bb078bf16cdb0147e8542fc..5436f66edab509bd2d09fb822cef807de00ff546 100644 GIT binary patch delta 74 zcmdlrLu=Oztqs{W&Ci{;KX+za_k6Pg{~^}R`Kr4Z+ox(X0WmWWvj8zG5VHX>`}V2Y Y9C_#3>lrz>*E4d}q diff --git a/MacawTests/png/painting-fill-04-t-manual.png b/MacawTests/png/painting-fill-04-t-manual.png index e62a0fae35939fae904477e488afcca92f08ade2..baa6f9c207c537f3ab8f2520fdf717dc72f2d24d 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-fill-05-b-manual.png b/MacawTests/png/painting-fill-05-b-manual.png index a42c7f9898caa87d12ac78922dbf989406c04fbe..9a464b02d54a28103f2c38e082e3422691e9296d 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-stroke-01-t-manual.png b/MacawTests/png/painting-stroke-01-t-manual.png index 031cdec6a7e514c7645274daa4dc12a327da4eee..2785ed535ee6659a61987bd452cc582af0159b3d 100644 GIT binary patch delta 78 zcmZ2=RBP2ytqt9}&Elrp#Z4LIuWokW7i8P4FIUajZpOs~#LPg<0>rF9%m&2lK+Lh- ZjEj?%x&62Z=l0_wTq2d*{oA;rWdOJn7Bm0= delta 86 zcmZ2=RBP2ytqt9}n;rN)*eC1Dxioj{ZtvD*l)u_;#>E80%s|Wn#H>Kf2E^<@%(2~! ei<6ZZsK*4PC!*b&mvg%{FPBK=_S?N&=S2XTq!~H@ diff --git a/MacawTests/png/painting-stroke-02-t-manual.png b/MacawTests/png/painting-stroke-02-t-manual.png index 83c239989086e4bcef6e8874b685db15915e9bfe..9428b4ac6816fc3c19756df08e8affbdb302fb12 100644 GIT binary patch delta 78 zcmZ2=RBP2ytqt9}&Elrp#Z4J&J2yMZ0G0+NH~;_u delta 88 zcmaEKTkFwntquEiw>v~~c(6~tuj diff --git a/MacawTests/png/painting-stroke-04-t-manual.png b/MacawTests/png/painting-stroke-04-t-manual.png index 94703bd76e282e1b3ed8e90d6b23a7ee9ce9c7f0..631b548feee65f228eba0767bb996cc09f6adca8 100644 GIT binary patch delta 75 zcmZ2=RBP2ytqs*jn%U2^v!7wy&VGg|JZAd_J*Ks6?e*%+K+FQftUwInvv03g=Q!}R a{j><@_R}I<9fh0q`Fq&62e)yB%K!kCxE%Qa delta 84 zcmZ2=RBP2ytqs*jwlBEN=)yjEzL-aI^^x}KBaGXtk1&PDw8v>M12GE_vjQ<25VLQO f)8Kgc1f)R+q#>c*o|kjGJug>B;r8G*u5cLu@{=IN diff --git a/MacawTests/png/painting-stroke-05-t-manual.png b/MacawTests/png/painting-stroke-05-t-manual.png index fd9f37d546b89191ca98273e7d765bfea44be7fd..4f233669b28e817330360445c9ce81ad2ec7aec3 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-stroke-06-t-manual.png b/MacawTests/png/painting-stroke-06-t-manual.png index 679fec11cd3d15fe302dd8aca30597941cd24432..99bab928fdf035ed8d9ac6d0b6e7c191fb6c5d05 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-stroke-07-t-manual.png b/MacawTests/png/painting-stroke-07-t-manual.png index 48b5faa69ffcb3f0050d511d48aa65d892f44588..fa9d194aec4b71322798877652a334426a217910 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/painting-stroke-08-t-manual.png b/MacawTests/png/painting-stroke-08-t-manual.png index 1fd52f57ae6975f99c024c8e9083ad7571c770e0..bc901e4d736d8e74155590ab0e386ae62a7ba6c2 100644 GIT binary patch delta 1156 zcmZWpZAep57=G_QynC0rnYfjvGrwCh%@lH!`ctA5`e9H~g8dMeq~Q;ZK+{3QepoC# z_RCSxW)@{yrzB9sAQJ_#qM}lWEc*b9C@{(1v%BS;U3fU}^SmGTxtw$EaF2bs$KLLi z+#0vmt#j+$QEqw2pk3x|5-#eyU&}hav})**SZOOp^WI5(qvv%po|U-EjNkNpK32r< ztP}@}t1Z|m@xwO!s_&kX4g6`c;Imn1lbGYN9L=LPV~vELR{;tMz-+fMk4+EA89Qc) zM+;2_2gRodO@u_1E^0d1$bh`F_z#6PRmwzhA7M14718P^Yz=YIDiNmn(Ijlp@bXOb zNW9WP4ik+=0H04m=Kw(MxPtaOtT6{GV%KRkcKmLv9|ef{DQvT2+wg$1m-;go1y^8L z259ALbH)6Net_%`Yc<*xfJ;}IIR_oiw!t-B%xHd~^tRqp^t4Y3KXWItZvPA?Kl?N+ zH3tp+MlN2F_+$~*HhFEP7&cOko8opwc0i{$&SZ@{xlXmSvU8YS+`LY96W0>dPYRJI zgwUr3*D}`V!-H|#Nj@J!7p)2Ou1Z!}h50c}s?-sZdBEZ>i5B`)f(YXC5-wo1G|fo2rac9haK=8gf#!b{i}t+}JY8DIQZwQ(fmIkT*Ddzbi+UP)4B1cnsj`_UHSp=mO=C?-1Cs`G+!CU zF2SRD{J6-skA?F8eh@FIXabqUO|zol^b8en&#Peb3u>mFk|mz9ATQ{6%RG+t{{8#bW9fY4{~91M$&AE0)qM(hSm1Sy`?)5GACv z)s^~?6|@^_FO3V*L!>@LP)PP7LqCKhCRS49V%oXxZaZrOKbZ4BuRHh5y}fOzy=|#Z zMN%}1R?#VXB}|cf3|eou#VAXB>8L!aId8vvN|k@JDoSg{~# zfmW7^zrtK@0LwVrzY@2u6}ie?0NESN6*Uqj0P+U_HVp!peunyrUow*w)3dHpZp>8B zhtmLu`T**xe!&19MuTKg8$Af5?L#Uq^GWv)>%rtX0VjOi#m~YOg`G#(b zu}AAVl9I^_ZCFvuY;H_8Z);X%QFbP0CJ)A%v%5m|T}>>_jmvYYs{yiZhx#&TOR5e7 z2w#9b^#HLWp>aj6?5i6~%sZO^Y>)js0@!zywco-5cHM(%yR$ok&W+cC1p|d04FFoz zJi{je_8t$7E3X4s{3+O6_j|rcnFnoITP_E6v0aI80S?px6rc0yal^h^+t1Q}qndyG zM^1~()36#34f9I}&6OK((qGC>MPsskmP)N7{!M`1?Zg@4Jkd(z5^Dcu_R4~*>|TIa1GdO`Z7Az8;~-p?n#7DvBs1p7Pw0y}w3-~a#s diff --git a/MacawTests/png/painting-stroke-09-t-manual.png b/MacawTests/png/painting-stroke-09-t-manual.png index 1d8b13c70c54307941f4d777009d3b3cd036f37a..c09a4fbbb34fe9e4abde97e032bd49a7e84af348 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-01-t-manual.png b/MacawTests/png/paths-data-01-t-manual.png index aa24bd623bfc6005bf6cd4be16361297c9f63f17..6e54bbd64bfd7502f7d8823897386de1f1c23787 100644 GIT binary patch delta 2186 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 diff --git a/MacawTests/png/paths-data-02-t-manual.png b/MacawTests/png/paths-data-02-t-manual.png index 8063f7c19a9f43f3f50b95c7b39c817be81e7de3..5962766bc784f8029ccd5918e1bf1409edf307c4 100644 GIT binary patch delta 2376 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 diff --git a/MacawTests/png/paths-data-03-f-manual.png b/MacawTests/png/paths-data-03-f-manual.png index df532e0060fbdedd3775d3c74214bcd19cb94179..8a4c81c47425bdfad986aff7c04a7c553c7e1422 100644 GIT binary patch delta 2227 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 X|3bIhw)M2@*wY>&FT}mnS&;ZYt1Ua& diff --git a/MacawTests/png/paths-data-04-t-manual.png b/MacawTests/png/paths-data-04-t-manual.png index c1b711ede1c816feb4e4d148ebb5415164339543..0a369f589271d09cee1049cb59673fa1dde88897 100644 GIT binary patch delta 456 zcmdlrLu=Ozt%fO#|J##I8Mh~!GF_UrS%Lo)>-PIbjI;Enn_ghLH2r)OBhU8mB*r+V z?S+?_gjgX0moq^EcYiR+v2OP-M6gd+AlU8=aQ1ZjenyV%r`s9BV48peFascxk1d%! znI-;S65C&xv2`#4x$igU zt8QSN&MnP+1<3Q+-XFls3s$z>LylRP6)5I0J$oBt0+`r-Jc8L5q)TA?0aa#ki0t#d zKv@v6-8lg!YoiO1^_f2Z2;-jZ`_q{{A=FiW#;R^X3i+OA{6EDvR_aE34=nYJr< zGv|Xud6~A)31ZHMFsFTGb(n5o$i})oGKyIXDm@{QxfsM`WoqZhWCmguAZ7((w(T65 m?5kV0iz{(t+y^Q!UKW548;s1MIExivMe$hgoGLji?APq4v{&O8CWy~Ek!>tXebD} z5OUx~sUgRLkcJ2c2|L<@XgG&2pYP>z-^*t_`HW{L>oS%_HsxOJJ*cu= zl^4#X|A7~XS2(Uead2_*{RF;LGQa^vdIQdl4=tsM_Gj9m;+kG zFT94>9LGC1ScSZgG7B!vurSAfmjDT39B&7}rj^?Q#yNHez^OsSP=l)x0*qUL?skPB zCFJoITc|I5x5FgEi)ly-soy27EsS@No_&d+^z|tMBT?`Obap{A$CDUr2)yhE!P9C2 rXmm7!MnN^9zuu=mY*OgMW_vJO(k~Zr>RX+FG;K-^z9d#sjl=dI<=K(z diff --git a/MacawTests/png/paths-data-05-t-manual.png b/MacawTests/png/paths-data-05-t-manual.png index aab64b3871bf8f8fa80bdf86d8385c7b0ef05255..90cdd1290b2a995799ad3a3a26e45f380258524d 100644 GIT binary patch delta 1007 zcmZXSTS!z<6o%RBoHLH&WyVq;%F93vmKP?;MnYuI+5AwiK$l9Vt7 z1GgF~Wb_cJ4AZp8OHmprB38bb$RyH*5*zG6FlcP;wwH+>HvH>b|GMmb&R~1|V0-+H zCv1T6lQ!nKYc{G#B6r+nHr`~sJQMV8D>xa2E#NA*q$vNhdB8WJ5C)r`SwMRvMEJ$r z>sIwR8VwvT9e`7@P^w|<@ojzYG=u`V1Gef^+KKuw$I8c+J9nK62&c1pb5E=Di+nDO;z5RB{jU>2R4(AWT# zLYXl61T9R)P~e3}m3_Eh)$&DQ3`6@_m=}*`0JL0yYT@!R>?#GHkkt$$Dqv0%AFm#{ zjm0qDaa~QvsVeB!aM}gUazbDoc>H4CRj#J<+-%U}L?d)=XM*u}3+=-5jFg(0fK^c* zj#XU1^%D9(dG91N;Yv4@ZNSM{_$ddKJcJ4j?F;Zr#-IBrmX)Jl;fI8UZhAur?cPoJ zVOT(K14~EmDm<0Wf-$-?T78?QiAlIcrZ)Ojql^bQU2vQ)>6Cuj(!~4rstI^?2F_4L zh_Wa=J1chY@ELsVIFt1(k#0bjS;fHW^>pPK+I(}erNnpvd+OH zdbKFLVpMDr;+;sPRmd+4wHRel$WDfJ7R9q6*AgXdV|*k?=RjwQ@<^mwS;KOgLPw>e d(b3Tfz~wZ3F*vl(^6$WUzx%ricrF$bP1c8fI36g_$i%37y_^CW99Da?ZK;zI!gu-;wI?NNx1l zV}1#r*AbtNM?|+L(YsB_)NYSC3s-=K2}{J)Lh+I?f0@J;nwuz}r~Ql_j)nIW8^&^N z$h4tsh4%g}2EOkW;n@gdw+Y)(PA1F*wUU>m)}X-3>dCPaTqh8SX2HnKSL@)4;{z<6 zAB7zW2So`f*M~^Cen2uf%zF?lVmskUg6cxX8Lkq@c5pPy(gGs|hfPtWClU#+bNrTp zG>&9A%DF)xCzH1c-Br9Tur@&rgxDIM*9WGAxa_=a=Hhuh_w0(bgoq|y4Do@}TuZXs zYv#&R`E0{f3wH^%t&6bLs+=gOOx4Q}jApF&@I|dE3zdCbqhGm%wK{Gie0mt>@`aDz zsDdA*LtMMl@=uz3`k(X|#S{9LiaYw2sTsZ|@bxbLB;*G)uIS|-gpql^F7RPMjC8BTdS8P4pU@X}Z%DBsdp~9lViq#xj+%xqZ>DG3yqY$1gTcOdcvO*E*)%fj=bjAMx D@9Qw9 diff --git a/MacawTests/png/paths-data-06-t-manual.png b/MacawTests/png/paths-data-06-t-manual.png index f4e064cadb8de6b9df52661ee2a81e66e65c481f..338005e03c798b300565e0ec508ef4af9e656137 100644 GIT binary patch delta 396 zcmdlrLu=Ozt%fO#|J##I8Mh~!GMUcWtiYenwmDxlhim)u=S-H2Kz=YYkpF>c`hHu+ zjOphSm^h}pXECZw|Nfq7DNObY({y`R#+2#q-oQIU@|Cvl`@p0M5j<}MGFgC~ zIfG^UgnLY{8K=Jw<>Z*YzJoDf`eG~Qi^x*jk2^vYdxJGkzi$I{guf@VEaUbbK4z$( z{Cr?g*t>J`Os}8EsL(DP$h=)Rkfl`^=mzfX^J|!|TDQ+gVF6-RAZ7z%_U$uLII^d< U*E4c%uV>_{$=hC^!L?f%0OLxCF8}}l delta 437 zcmdlrLu=Ozt%l7C{NGtO=d0%ExBh3``k%>k*7h0anI1C&#SIy^uerqJ%?4pAyku%; z**^ad(;1MQCgXO9fc7({J&e=uGcsmO&tJoI5hUu&HogA=6UX-M%}j=j(}4m?+xLB7 zQe~U&zk!ouJNIrTUB>MK?93S~+b7&(y3e@1{t%NXs>tt?P!Vso?bR27rfl!wWA?-0g->4*O@T|_9HzCW5VV!AyWGhED-ZMuCj zqssPfUS>wP2|z)94^DyU`SUqNfDYv3nO^VAq|k0G%DmlJl%-X;eMSlk5VHa?8xXT^ npOM0mJ$17JzXAK?d{v+A{(m`qnA?FQki}_`z5RDSm%R)Ce|?yP diff --git a/MacawTests/png/paths-data-07-t-manual.png b/MacawTests/png/paths-data-07-t-manual.png index 5fa935281c2589aae21722c6b502fba1c9e7d53d..07829cd6ee4e43dde9754ef760c1d7b17215fe36 100644 GIT binary patch delta 395 zcmZ9GJxIe)6ot7rNq<5$PIgNIp&->ss*{_eLb01T=p=P$GdfsEkwOL$hYYXK!O^K8 zByb^}oE)7ToI1Gc&_TP1Czbj)oR4?Tx$nK3_N!^X+D+(`)YT>>0-W8)u60LJf2Wc0 z_jp3eu{g=_hGtKT{YsbBU>Oz{nllk2q%i$W|(A<|v%nxc|ml`I4 ziP*$XhO)NcfmgNYu&vzZTq-Y1Jk-Ql;9ZJbtDr8nF$4UYnd-APzk?R|YUQ@fv4Ntb z!UlJw-e8XeZ$D;XLF)T_{o_mWaKwX|Z|c%s=T`RPiugkcNX|E~A-o%oeU`DWt$J`p jaW46w7MhU;>5u{CpuCt-VR{?}RFr_sPFsSeIr2UM=h=k8 delta 423 zcmY+9&q_j35XPCg_o!XS>!N5=dI=;jLJ2|_LN6c`=mWTlDD2QGgCJ?+16<;`bJ4d>oj45P|<2(;-Xg2x8M#xV!#{0>?g{r~yE zROx$87{i24r_dAh^$blQUUe&I{}md)wK{p@VMb49>n3e2VhIlCvBm1q!4jXMnn7%7 z_>mmGK;{ Te1vjFmC4muYLnijwLJX|Uk{nq diff --git a/MacawTests/png/paths-data-08-t-manual.png b/MacawTests/png/paths-data-08-t-manual.png index fa565060cff8271110dbf7f5e3f22a13fde911b2..e11717d065051c28122f6225039c50be6c89b21c 100644 GIT binary patch delta 55 zcmV-70LcHkm@2xMDu9FmgaU*Egam{Iga)(+v+cJRA_!>Tvmg&J2eWS_xFv^w0SUK% N0SbI>w|{5~yCX#)6g~g| delta 61 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc+yo8N9$;1^)qoUa<9w|(F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-10-t-manual.png b/MacawTests/png/paths-data-10-t-manual.png index 98f493e78c15ae204256858980e1c9e7b9c2e591..192bbbb115d1cca855a42a91b3817836a0452e36 100644 GIT binary patch delta 5865 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 delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-13-t-manual.png b/MacawTests/png/paths-data-13-t-manual.png index 26e12c20d82cc282407f3ac51617e238e498e55f..bc7f9844f18cc3b5fad6830286cc70c0bbe24a72 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-14-t-manual.png b/MacawTests/png/paths-data-14-t-manual.png index 8a09fd63b1e26f7dbae7b9c4a32bd5104a0f0d87..63c7c493a5f5e450ddf8f193da0b5c4e8e8f366c 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-15-t-manual.png b/MacawTests/png/paths-data-15-t-manual.png index fa320096514b4c8a019750cf8ebc4b453c0b86d4..d6b4a4d1e75201bf7aa7b61bb10359367d1868bb 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-16-t-manual.png b/MacawTests/png/paths-data-16-t-manual.png index 2be8c80596cb89ee253cdf41977dfc7c43c3c6a0..77fd85274166a85919a603e78467aaac273e08bb 100644 GIT binary patch delta 74 zcmdlrLu=Oztqq4SHgA64zWF`l_Ra5^<_2t5;Q!3JIbZd!czb{YGZ3=?F)I+W0Wtgb a00)lEJKO6SIk(p{a@FK*ug~DxtqcGbFdq>B delta 89 zcmdlrLu=Oztqq4SZdTy;Vc(puxF7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-18-f-manual.png b/MacawTests/png/paths-data-18-f-manual.png index 7866f214f4770e3df19fc277aefbe1e3050f56e0..1fd8a9ea2021ec6deb332cefc39bb17b29aaada5 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/paths-data-19-f-manual.png b/MacawTests/png/paths-data-19-f-manual.png index 3bfcb57f975d5daf2baae63600965aaf215a6953..a6e1e6e1d92cf7ac1752a127f6f2ff8c6acf5acc 100644 GIT binary patch delta 1671 zcmXw4e@s<%6hHUJAYLWM#@MH7E)n*0F?YAl)%NL2VMsc55N ziaz%aQ`6^K_QNr8lwV_-SQ@P~G+i<^!|7&DQ~X^rv~wThZfE!2-E%(Ye7@&%&h2VT z>uO7DY|@(aCZox0vNeS?*}riZ57k-?nFa^yOjG3yISNhw>o+8*fQA$U9-<$J=&_v- zS39z~qth^z(}qiiTIR+ZnlIiJ!7bsKDGxpTU^M!0%0+ZQ!uG0CCqhm3#3G+}#36@E zqH$9Ek~mPNo7@);r#p)1XA9o4Xrjw}Hyn?-W-M;0%MlnfR7o5%HRX;$iKeb3z^$oO zu{dvVTpDIrsBTVA#!*3a@u5;ct&2yorpi*0rLlXF!=d66(P^l;8Gu!6Bo8FRiAOCq}ZG||OpiH>t+3ht;gUaZ#CnKU#Q{A(8O z1;I}I7h-a$2dm`mV=nii%HbeN6E=H%t?Og?rWaGyo*b;PiG!QFC!vhDPQgWg4vM&c zCmc?TxJGna?iaW`2eVXiK9<|~?#faY>v89qZCX48UDMTj{ zi2fyBoTVr7v1xF)pCY;9t%~e@?G}q3QF6Yihltn@7|&QO?)J7rR0dd0-Cat3wNsYI{Ne z0%FdwL^+cMFk2Miw7lYi?Xi+Q5)0Nfis&`r8^z~J;8gQVQLpi=g{V}XMM$vuysh@| z<0HOyqI1IYd+wNSkCtvb-D8BY)N#~Hv|Q>6Q`?qezs7y9qFC*&K#YyAXTV{~!Ny1< zQyfy!px8hAmZ4ws-;i^ttUy7?VaYH^oLQ-lQ3cgV)jQ)|p}MbTd=b0ehId$?Mja;m zJ$BQUOLR8Oq7xgW6PYZ4GAIW`__YEg9VMT?3bBgoa8mQ!#{|2g`K6nFR;;HlmulSD-+xeIG-be>J?3(vxNZ`bl6$^zn=I zq0m|ykygYwp;+gd7EI;PX_&yiW{hWVpJAFZ`8!p2Gp6dnw7l*^ zB=HHK-NXp8#^Hu-l9J%>tkHE_$F90RLW!<;1McbUZb322W0CUsP!aUu@*@tr@_d3y z>xfJh#n232AYgJ}6ACzjW$rf2!Gz9u*Yg?&PKTJPZ8O&D%C{YEgIxz@O_-dy1s+pK zcJb!zC{#O6+g{Q3ZG%r&u3d! zu3FlWY7CD~?pGoS@5{RBj+B|UqGWjQ3E$n^(T+rk^lI!{=Wwe_U*pSR0-@g`%itp& zviUS?LOo8=Syku~Ht!{?N(rOSf{V2M*9}G$lW>u~Ghn<~V z)`M`BbOaL(*+lkH^|BRyx9Smj3}Iv^%B;UGdPwQUwP1?iZ$XMoH>{^HQ>68Rs_#LZ msa&T}ZS3oXoOsJwd~9%Ij{Hov{ehc?1hfy=rE)LeQTzY?R_h%A delta 1728 zcmY*ae@s<%6hHUg@8#VSm%U58LIiv74HN{FM*`9$-)o>mgT(oRwoEa{{6$j`OVjdM z&$5Zadd6W&igUBfT)L-zo%0V|w%TgR`Xx0>2!GBkaANt}&b>%mcl++0`+etp&gc6% z=X>sn_Usew+2M#1Q6pMJMD$2p#C62b?!wT5@?vu z0368zxSR@bi)sti0y-4agfw;Rx4lfcLBZ#2E1uXwQwNC81bC?g;A1(@#CXB~2{~Zk zQiVDfsDgSD(X=K4v`hf_JOFUA2;fT^O2mh@C#{Re9&#Z*AulB1T^m`*#THg@^hKAD zffSsgIF2_S$Lo~!s+ua-rQ*3^$MeaUugGQ});K%PR+>!x?0N|m$Mgd%+C0TgG;nDBMLL07Bp3TNJ8hNwv zd1`qU3us0rZl&(qaf0UM;kpDq9xQyco{lEE*Hdi(>n#LG;$8ch%j+tg3t)|u{3@>TQOED@#Jqdo{|FrJf%}8M-vH!02_kkQ5^hnGsBxwwikOj$Yu$Ll)R`O;WAJl&N{)n%&b;kF&JHD&P{7$%2f9#E(i(`mtM zH09aZ*kp4Q6l}r->Uo7r7n_TI=d3!H_RnbRwhZ!JjT31s)jbX$Ip`|3|9hlw9v)}a z>d|WZi$z;f$5qIik10C(0V??7{ASr_PNl$NRd@6z zdYmi|)ZsLh!tE}XT(ttLRVrVGZ&9cbN2M{h`?&xY`9%9zjY@{$Mj2l6)-oKjp)6X{ zgjo)KlR0$TDkuOxS@0U>s?@q!6|(nrT&~iY25fM2=vvFn-*3e0!&KH|K=28U(!Fu$ zWrt$vUX7;|s@=f@qxx;EQF+rt!L_)N0zH}#9xftl7L=|x@wS8Eq@m__F@vgq)dUkC z)=;OuVk{a756HYl>L$fjPj+p>mu*~wJ_+MTY`BzqS+)_&>`jxx{`vFP8%+nw-uMDGIcZFRHLn(9_suMONn1AV`=U>g*tzV6S93fZcydU7F3*hXLCA9 zW)(|Sy#p=27jT(6TCiN^MRAUDtOKK}OeVCnggYbb`7Simp*@&O;VT9YnPM~y?vQ_; z1PD^?L`;<}J8+@W#+@Tq?#10IZP|^M+`9`=vlC=d?tToBZ;C3c?BNUeFdm?vhtniF zz6;Z1K6gMNce7#1{?G7JRl4`#c$=<8rw{Q21X5yJ&TwL`>Af)kOQm&~$E$2Ph=s$R zy(?9rbDO+p(98zJezDBS3|M$Uv>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-01-b-manual.png b/MacawTests/png/pservers-grad-01-b-manual.png index 921d16f55b5aa0eace6620fc8161e0741935cd37..e6d78ed2671244bf88aa8cec9f1147fcb488c658 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-02-b-manual.png b/MacawTests/png/pservers-grad-02-b-manual.png index b544f0fe1b262df8060a35d82f4271a39fe52c72..174659ea44a4ee69e6cc81489906ab0d07b23e1f 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-03-b-manual.png b/MacawTests/png/pservers-grad-03-b-manual.png index 3acf5107b5d3eda05e9ada69797b2c48d2c3ec6e..c4bc31ac4086c2f863a3fed0e0abcb365d20981b 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-07-b-manual.png b/MacawTests/png/pservers-grad-07-b-manual.png index c508c90b93f11208fbc4fbf268c0b3111a93e6a8..262bbabeed2737efef78fd38f1a23d74f4534e23 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-09-b-manual.png b/MacawTests/png/pservers-grad-09-b-manual.png index 78f0b8b8e5edd88eccbd4b0a61a5bf7436e6244f..6baf64a4543689d07721887dffa4fd51cc0b5d0f 100644 GIT binary patch delta 66 zcmV-I0KNaZm@2xMDzF1uvv+X%kFy{Th6S^4CGHS~aRG&K0)=q{g>eLhaRr5O28D44 YwQ&fN`G+w*yHai_;)``|o@%dl>-ZavskB diff --git a/MacawTests/png/pservers-grad-12-b-manual.png b/MacawTests/png/pservers-grad-12-b-manual.png index 5d86eb6950b299e061812560b59a458da462f016..39fa6efc70fbe1705cbadd01821697dd3e412c91 100644 GIT binary patch delta 148 zcmdlrLu=Oztqsgko68G~`Zg=@`>}7%S5@P0UiokP%72W@W^cFO!Nl|eCb3AY-Ejf) zcE<%Q|7LI3k7BhyG+BXv<@ED>%q>916EFj)b_Wa4ki}w54Gr5b$g>47w;zya+kQZv pU4LTx{d4Tw@1NsXU<6cV0J6)4zrCK3b9+4_S54mb`V6k!$^gm|I`04g delta 164 zcmdlrLu=Oztqsgkn-%zd*f-~^YVdDnjxy?NUiokP%72W@X2Zmnh;8?u#l-ZY-Ejf) zcE<%Q|3ETd*nm3zh;6sGVYNRrJx_*hCFfQKhN%aEc*gbu8Mb&vRK5jc`vG~j?FZ!9 n^(P{F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-15-b-manual.png b/MacawTests/png/pservers-grad-15-b-manual.png index b22cf0b0ae3305aded55d288549ff4f24b2e177d..280a922cd711079cad5ccf0954d9f26ae896cf59 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-22-b-manual.png b/MacawTests/png/pservers-grad-22-b-manual.png index 6f645ab4384cc4d396e7aff9a7cd5757930583dc..e44dd6cfdd431386225a58cc21159f487aaaa5e2 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/pservers-grad-23-f-manual.png b/MacawTests/png/pservers-grad-23-f-manual.png index 7f08d5a14276d2bd0afbfa459371aeaaaa6ccba7..29273bd9bada287850541a6322722230e8f9e48b 100644 GIT binary patch literal 698922 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 literal 716166 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 diff --git a/MacawTests/png/pservers-grad-24-f-manual.png b/MacawTests/png/pservers-grad-24-f-manual.png index 4b3237c8340ccd2ba00ef8b074c18bd23b8a238b..6d35f59c6ccd904735ea0fb5831fbf767e46be33 100644 GIT binary patch delta 14643 zcmeHOdrVVj6u;0aP!x(zK^HeS9xgfzqHa+!I&hH)qC;XdQ>TLBgD{DSxP+SzP8XM&pntgOGW8E1pmRDz1+749X?tmTzs}D2=)K%qe3SGprZ+j<+wVK) zJMZ7=HTC<7F(+=14xT-m8_nqs-7opzVnBY7>1fw1O-yQP>e8&NtTl3)H*a2UdV2cO z#KgoITCFzRsQ)eGosVK;V?U8|$(~nbWMpJdn>Ou>$jHc8plDX-{1dgM&SQ||JX6yk4O;9aqT`P-q#Af z^zlCTSZM&}U%BAp&%BTG?d|MalK;QU%DTXFH8eB;O+_LaXf#pjqN%ziFRvp;mg0?k zPEJQxSGO$9Y`HK!)0%kww10o6Tv$vUtsOgXpj(TFnGA}SDH3_Q>>dOO_CtZa`Y(JWpeJ&P}7l_<20rV8)x){ zz>Q|QG#Z7O9q4Z5h7A(3TG1j}BNr?=IZ_B}b8{7PwBLqQbLNcXm1F$)UOblN%ZIbW z3E<2-cS1``3ln&H_%O5?3Q`7#%Y$G`PR78*N7xe+dpL`Wq1{w8pYLz@HbpBX^jY4J zRrMoAuz5jRoHuSj6Ry3AH^gVZc8wWF$n1!YR(R7)`xh@}Fb0>^3Qi2T!z0ij2p|j{ zst{mGWeI+ooD5xfh=MQz@Ak#pMm*3}UCoT55_fC03U6uS$-aGHn>7n;X=z}~$bjmF z3!(DhLG~)h_RpOII)@Av$)1ov6E}+;Y&9C89XD$W3RpqV7`Ei+GfzYTxwo`md*ldM zwr^KZ_13K*j2ua&-xr@L6T+Q4%rttpuJ^Q_Sg*7}CeVAd_=+ojF`)JO+qc0M7bp2j zrc=#ZusrcfU~OefNns35DklsqU4$jxfx_xFJW7Z(2nqdzsI}`RPwuOKq(ZoNkEx`b zB8)Iol_FT0&i_HPS}5&u}PUx&w8S>O%{QTRswxp1xCY$nz$aF&;YJ1~$nFa^_c z>J&SJLDOfSDTKxDBnB7CIkDp9c)-htIt%oZij=J;kMhsjflreN(RH#D_24T znoBVK^!$0oOF6FCvjx0HC_q9?sjO?Q z7!Qh(H+32!Rz&IF9H#6OCP)b9IC~aaMBUE;-I`&;z*SYnY-at3y+u7K+D!6w9xz5G zgEb)>YciPX@_lb~5_3vpr;ms$VBD?8k26@HUb8G)Cc%POuhe_q^TRr44*M2Q{xo9` z+mezbR~#`hQe8Jbc#s4ZoB)&~IXN$1?umsAi{8*TD*Z=zObrpjoMqcKuw1&t{31Qp zD_6iB94y)RBHvmu71l$CBv~2~4M9QBiR+dvTOC<3Yy_$Us6Dwc5(>F3v&b(xalqzwb=B_P0 z9c+kV#OJ^f@(;qLy`qABuEr|E)EU;N42!PG&Iad$2W-8P#sbPd#?+CVfVNntPDPjF zBkIPEmH5ni_j-RvLi#t6cE$XTpCq4e;k*)$oV<^RH(0htm94)c}9~RSiFfzjRa>(EWcJi$)GM zPO8!k4PVP~0fC$bL0Ze{ijNtWRO!YB7XNA7$L=SLzvDfu_}-57ToA_%NJXB<#n2x?ODHm{kt5;MF0hw!C*MT<{(dBUmqD57+_uWJU2JD#Mqjkcpz@=+O;{Tg|*7c z%I}S>dP)3iNl8ibkrx}C&Yy^z`%ujWfQncwH_R88>d6+N9hn%N06Y zT3UK&>eQ(ziHV7GXV0EJfA#9s*(Xk%_=yF??RJx_tgP3;TM6^HtgNgG6`_+$mMnR3 z`t<3##fulOJ9OyK4~)-dv$Z5I&#X zZtvf>Z{G+9=(HMV}In6_6tAy%W&JdCho1IYg{7M)jYL?>c} z*r5|t&@(EPYF1}wC(Subt`#d*Y-Z+-Wj2f}E?>Ufjye!!NRRlv%E^h+9Xob>igxbb zzyBMZPWLp6@P!K(t{5AaDejrwDn9b)(W7V40Bqn+*)5Egq^GAp#{yGTRdp8T%NY+l zB94#^?U#;2ny8_Nl8h`XQ&Dr#-8b5t7dR; zkZjqqrFhMnHLvg7x$|>}!$DK0rKP2F)~s3cA$6W#wrtr(oY&ac*b4kHC?h*N`~9Jz zAu88sG#-PlNbF}#=mE&GZ`-!*6QGY7ZQ8WyeJnxocbHxA83hFeWmGNvl$VzWJYt!J zV_vV95}sr7oH}*trx`P5fH$g)sDlR&p2SYQUSA64(n1m&8><1=X}Rm@=;&7}mD89j zc=WqtP;O91*45ShVk~-dY}1`m{#lUfo5*nk;t(&EV!vKqG|z_(;Kd1hAnLqGxHAL( z%lPYaay+;^v6U1SdMAvMOW0SoXOEXof}he|ySxl?qru?iaRAHH(IZEOzN)Bz)8SI} z!x*86M{)B@M7$ z=}JqBZ+l)I?|DzJ574U{H+n|%0+~}ZngO+S`&Ldi}@k7q;zZfzG_cail4<#D`kp2z`9WJq|Vi@DJ_?@iiY}yhl3wKf(WE zP7b|&#@)23u#lKXf$==_S$_Nxr_fvO3+vXACWilS1_K?yht!7DRB{d`|5jd3h2lN> zF9ijpafDD*Vt(r_x_u)v@(Kz0Si4?ZOBw|H=aQ1>LobgL+Ux77!rRS-pEVl#76W+m+#;_dmh^4!!<+6)^noT7Ur4yO9xI|9@lgFGG1Z;cpr*Q2K7o)kTXVlW!jT^)n7e@#%BiRJSqZ5h;_Bp3aA+BrJaEX#$cuizaPY=w; z@gAVgh6Y+-?B9M%9ET4FksYr4+S({T+_P)Q4V^9^pvzx9aI_1GM<19tN7*GP3r;8t zy)|Dg;AQ+x{(xp?`5>G-l9Ng2#fwxEKF(a-<7%A%qA4tg%#9o!$rB)MncQ>8h4Y0cu z3aaK#e3AQ+!gn{D@pYuTql4H04fhC}|H*C~bEKt_UU<`u4d2|^R;?n=Nt4K*c00}C z&Y3d}BvGA={@5!k={(eD+pyt|jZU^MT1485i*F(;&l5{t9&xN(NxGFv;>9>U(Uz4( zXIhGihznwd<}$*hvTWWQkaQhjypobauiIds^%!2+pq&Bhunk0`%4>dm|GfWxf&ytD ztX>aY4sKA&djEi|7_2GYo~EmLa`*Nr>}RK({~B(*NgT(y#35cR#eTiKXr2!nz>5?1 zK-77WaAyXD@fSA%!}zmI6z3RT|Kb)P(d&OS$H9FccNhq4;r0M&Lr8omm5ri{52?2| z8vrrCxif@)h`E>sgz*pX{YCw zJ|vR>Nr57OB7vfSWI%GD=#vl06qlqXtxjHA9j!Q79UW7b$HO17I0Cd!9OA`N?AHqk zEt2QMdT~LCX#g*#a?l;dKa9Va5eTn;aSKp*{c~J3K!E>lk1aRE#D`kp2z`9WJq|Vi z)_-tEh_7+~7t??+{-TCIH;liS*`JFK)S?;?#$VL%;{$b&0VZX2j7j^YfL@Z$jFk+3 z3n!6WfzLW5^t(8TWILQo>3Jl4Wal$VD`Fm&Uey>MrAL|ZRX9$(`grNv`0dX4XSkTF UnZ_?FVjh%s6`icDh}k;+F9=>JmH+?% diff --git a/MacawTests/png/pservers-grad-stops-01-f-manual.png b/MacawTests/png/pservers-grad-stops-01-f-manual.png index 398512e872a2fda946a3bf8b53e55913a9c59d3b..f3b698adcfaa0bddc16adddd7105e41e083f275e 100644 GIT binary patch literal 698922 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 delta 89 zcmdlrLu=Oztqq6QZC2oaz_K}Cbq-VW;dR>&uVXBl)xI%+35c12m<5PgftU@5*|%>D k;0Rg?)L_6qIbYRhyZ>JfALe!-31o2^WN-hS&t)$I00ovM;s5{u diff --git a/MacawTests/png/render-elems-02-t-manual.png b/MacawTests/png/render-elems-02-t-manual.png index 6b376051039e4c866d110886845e371ca3489ba4..7e4671a38bfe6707cf3c7f90ef01f1627eefb6e4 100644 GIT binary patch delta 74 zcmdlrLu=OztqsNNn%{5T{(dXti~7w9{C}7>=c_ibv|l%60%B$$2C-Rzm<@>8w_i8q X&^gjx&&avGo{_62Z+m?P*KTD1E~p@A delta 89 zcmdlrLu=OztqsNNHY@NOux`#*^ hht3g@2KLE74cqF7M2#)7Pc1lEga0hw%0Rq&SKd<_aDb2rtSC7ar!WW$S}t3 zifo+cn6_sNaBgGSzLb~q5Yu#hX-wHZmGgKi)p*R9_J^P?MJmayO_40Hsusz-JWR7nZ?xZZp*pd P-Ii;M(f0ZbuHDK2d%QPi delta 216 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@PYV%a|TAIBr6?f!o`{1|}({}>_6 z$qM`&+ZEY3&oKd6JlpFTITx{j#g_7N9s-H6Y=6(exeX$wDayGGB9<<|c?cxNvwfos z=Q4=ceR0l95HU*?&RI;`!{s?2f%pR3k7{#vF-@1(;`Er_Z_8=2-Cv!v3?!qlJ<*sm l3+O04P9fIK3jAhFlk-(g+E1HuZa;0xwZ&-r?|d$M82|)?M4JEr diff --git a/MacawTests/png/shapes-circle-02-t-manual.png b/MacawTests/png/shapes-circle-02-t-manual.png index e60fb4183486a4cdc002dab8197aec9b63326675..96eceb726bd45fe0e3a359883c8a8da101882681 100644 GIT binary patch delta 53 zcmZ3t&~)8G(}ott7N!>F7M2#)7Pc1l7LFFq7OpMa%MIJ#TXJuIZ^>hDeEa_etR5WO JF7M2#)7Pc1l7LFFq7OpMa%MG^&JZAmEKK=Ag){yPqrrb-I S+kqsI#nW?myF4Rrzdrx~uoo-< diff --git a/MacawTests/png/shapes-ellipse-01-t-manual.png b/MacawTests/png/shapes-ellipse-01-t-manual.png index c7f18ea94ed4848675be7bed29ceab5678fe2c5c..899e248bf7586368d103f83bf33ca67a1ca6181b 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-ellipse-02-t-manual.png b/MacawTests/png/shapes-ellipse-02-t-manual.png index bd9e13ec1a2dbcb0b226289c15e8d403ddc1b4af..b22c349d6d314773ab3f9a7d51a4e7860c655ae4 100644 GIT binary patch delta 61 zcmZo$WYxCFs-cCkg{g(Pg{6hHg{_6Xg`t%erH7N!>F7M2#)7Pc1l7LF~P%ca}xl{vTDD|1~b-t5gkgMGX3bgpa} E0Mu>~S^xk5 delta 53 zcmV-50LuUV<|_W?Du9FmgaU*Egam{Iga(8Mgb1_=ryIA>p9d@lle-pdho>6}x2GEl L)N{8JmkMhfPAL?g diff --git a/MacawTests/png/shapes-grammar-01-f-manual.png b/MacawTests/png/shapes-grammar-01-f-manual.png index 0e3e5ceb23a342bb10cb4eb756d3393cd6f6f4ee..fcb9abdeea21994c5101f5671c159062ad78d1e5 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-intro-01-t-manual.png b/MacawTests/png/shapes-intro-01-t-manual.png index efc010c010d133affa163a88eaa3423630f7c239..98bae9dd70fdc7733f2a5b2e8fa2dec20ce80a93 100644 GIT binary patch delta 112 zcmV-$0FVE=m@2xMDu9Fmv;x;%gqCvv$G%% zum-blCBzT6XMqGv_O~aF1=y^&e~kfj7Ki^j2Dkq@2hjhwLs1Aku(Kc!FbA`5CCCSd Se*p=%e*p@7ZnuAE3cDlb?Jtx7 delta 121 zcmdlrLu=Ozt%erHElk%_CoAx8*#5tj@rda5>#0luTc+P%$jPyNu>tdzw#^FsYuL8; zFK13Uygj{<#pfH6+Rf#xH`cWOw_@A=--`Xhf1uJc%$xI7R|suCZ^dD=VY~ld4j<-r RAPHn~8f0((ozG=20{~RKH#7hM diff --git a/MacawTests/png/shapes-line-01-t-manual.png b/MacawTests/png/shapes-line-01-t-manual.png index e992c6a39831ea9b7a299d6046a8856297fb728f..2b229cab636a57060d77cf88a6cfd8390f690565 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-line-02-f-manual.png b/MacawTests/png/shapes-line-02-f-manual.png index 44328a0c3b6e1eeb03e587082e6ad014cd7d0f28..d6361ca5812b8071760964c52e9242e01b2f3b4b 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-polygon-01-t-manual.png b/MacawTests/png/shapes-polygon-01-t-manual.png index 175066cf1fdf62b95b044663e180ab74c435cde8..c2e8ca73305ffbfdaa2949e8be36e7eb7af31dd6 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-polygon-02-t-manual.png b/MacawTests/png/shapes-polygon-02-t-manual.png index 4b6f9a96066b2bde5a7529d5e5c9411f423aa2d6..d5cdf0b419bc32d909cabf745c2dda3858b1b7f8 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-polygon-03-t-manual.png b/MacawTests/png/shapes-polygon-03-t-manual.png index 7a74aeec58be0969d01ee472f31468a4e3c97b10..208b4a1060e1897199d5707fb4f7a9c3e7dcbe38 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-polyline-01-t-manual.png b/MacawTests/png/shapes-polyline-01-t-manual.png index 86d077180603fe5a1722992989ebb18f49f7cfa3..b0266a7e7b52ebfe50d097dd1c428c80c5ec7b9a 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-polyline-02-t-manual.png b/MacawTests/png/shapes-polyline-02-t-manual.png index 3f53998106c1f1fdc44e934861d87362e4404623..9db8761db24d9a6b8eaf3e001b1f7bc5d76ac6af 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-rect-02-t-manual.png b/MacawTests/png/shapes-rect-02-t-manual.png index 4963f0ee82502d7754b60d6d2e518b608c3bd14f..d414a6d4e4505136b5844613380267c5fe7db58c 100644 GIT binary patch delta 49 zcmcb%N$c7st%erH7N!>F7M2#)7Pc1l7LF~P{%q~%c{#VA=jC$9-_C!4xq*EF7M2#)7Pc1l7LF~P{%qS74ltXrPi_|TY4>O2-0siD<&eLf Jzn05g1_1dp5aIv; diff --git a/MacawTests/png/shapes-rect-03-t-manual.png b/MacawTests/png/shapes-rect-03-t-manual.png index 9f82d28a20a303c85ee3977111a3d8c5b4d772d1..e6b32d81528e46bbdca94a1c32b35b68e1378555 100644 GIT binary patch delta 563 zcmW+zUuex?7|!qej-5TPIewon)e<)Rc(iXQ_~t<@6GJ!F)1O{UQ*>K#m=ir{S6p>eY~=T14IS$f!_;9M zP<-_iYQ)fo7=7&*w&4U0DPB5&TG95Sl6r>lgm0g*?C3c(igpO)^f7}+ylNOn6fd|a zo06YF8FgmyfOF?@TxDaLFL}0+$YLSA8!Oa%d9y7$TIsCaibGvLoYY(!Ark z^jO`p_~{M7cFgcy#5HfegDp~qx#&JNXr5v;2jzvwsMg%@5N%dW1G9+m;^$bTdGeWk z-{>og*S)}2#dB}bsCnYG4cR|yr>=M}Q~ARei*uhX-ZN)$#kU|$bN-jLct5N?Rj|p? zzd^F*Hz8B2IpdgJ!J4V4snHyQX$|(umzkBC2TM$QP|6{fX#Q7j;XEYRauT@8mg# z!yN9I&O9gI@j3G!Vmy+zvxdw>u}v|&1G~aw#r}w=44~o@KI_P z%2?92WX~8ZnhA*CO1lp=nziJw1Ojr&q&Y|vG3-};J{sALg$gL6CajZrF}$Yw<{ZZ< zbO1|pT&5M}@=TNBo*u(NMt6_oQj{nTTYhgUvs9FD`ng)Dm$@qx O?rIAOY71FMv;P1IF7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-rect-05-f-manual.png b/MacawTests/png/shapes-rect-05-f-manual.png index e02d9f0a564ea09d8d9fd0fd7d8b3d65f5d35a9b..27a16b2d121e69cacb7c080f2f9fa752ca9dfb56 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-rect-06-f-manual.png b/MacawTests/png/shapes-rect-06-f-manual.png index b37be75bf5935512389ca2bfc58dcc49528713a0..b1b932d16b1ebc54818f869ce12eb0e50d4f1f01 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/shapes-rect-07-f-manual.png b/MacawTests/png/shapes-rect-07-f-manual.png index 21bb1767441ad18e6b0f378dcae9bf3cc3bf803d..181cdadf5191ed9861542247e99d0a483d7ce27b 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/struct-defs-01-t-manual.png b/MacawTests/png/struct-defs-01-t-manual.png index 128f6c586a110022e35b3e666f748f0aa9d0dbee..0dfd2e7a39a795f46edb1ef13ada9e32da7da287 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/struct-frag-02-t-manual.png b/MacawTests/png/struct-frag-02-t-manual.png index 2f666a4351d4060e262918aabad8ebd0acc0ca3c..d7d13cc6d5d1fc7e247b677a1816948962ce50e7 100644 GIT binary patch delta 8643 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 Q$1T8eW6kbkY&nj<17$c88~^|S delta 8616 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 diff --git a/MacawTests/png/struct-frag-03-t-manual.png b/MacawTests/png/struct-frag-03-t-manual.png index 0016f6737928291e0aa66ad28b64780c143b08cc..1f77feaded7cd52d656a9e7cc0457f70fba406fd 100644 GIT binary patch literal 223222 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 E0JRhn6951J delta 52 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1l7LF~P^^BVp_)XX+zt{0-uV>`kUeCx?lefLR Ioa?X%0N=6^&Hw-a diff --git a/MacawTests/png/struct-frag-06-t-manual.png b/MacawTests/png/struct-frag-06-t-manual.png index b81294667a2f4c9e8444a751fa0368ebede1f10c..6518840ef901a8a94ea079e9742a82a16f868dee 100644 GIT binary patch delta 73 zcmdlrLu=OztqtK(&6g9~FDEhrF%u9o12GE_vu?kf$Yxr&S%E*8rTzYSHXvr-e*Zj& WidK6)Bj@&dMy{H??e!U4yOjaXvLA{7 delta 92 zcmdlrLu=OztqtK(n-%zfuuaZaZD|gVY7dWM1Y#y2W(HywAZFbj9>r!_*v@g84T#yd lb6n>5tp?O%zz)>ov)%tMhYxc*kOZF7M2#)7Pc1l7LF~P{%q~%c{#VA=jC$9-`?-b+Q7b@zn05g F1^~r{5W)Ze delta 55 zcmcb%N$c7st%erH7N!>F7M2#)7Pc1l7LF~P{%qS7+*qrbx1aK4Wo2piXXD)N&&K7D Lzn#C9%UuQl6s!>q diff --git a/MacawTests/png/struct-use-01-t-manual.png b/MacawTests/png/struct-use-01-t-manual.png index e9bfffe92158cceef9d61c55f3c0ea955f1f5857..e8136bcb5e099b9688d47a09d9488931551d00f4 100644 GIT binary patch delta 2636 zcma);UrZEN6vn?hv+Piy?hMu=|uz_f~JN=lP`G`nf|(t(s25t@xgjWYu>Y zZ)*A`Y%)KuQmgb%boQouhFu)@hB9josWh|tQ{#a-c3ydnag1Xa!)9BH=AV5Fx8wy6 z$UhEx(9@poX~?F3$myLNbtH z4!N}~<03AKhvrb8<~I+uX!G=K*ygZ5{GMJcsp~VVt{V>%`4DPDTa2yKI1NSSk-v!3 zj{`Um35*M_NCrH@qnLgfE9E3IoBT=SzhB5wGKZ`om&v4FpWVP~L^_EzcwDP+S=GN9 zyR-z(2LsEnEGBrOlzPmr-wpp}c@Ynoml2QTN1{DxIr<#MD1zi1k-i@ZBOU!l8j@G> z-%&3^UL{LjrmWOa^}`J}F5wbWFSzIiZXiJ7 zWg0&#mDM_C6ejdjKZ~=pv3Pzt%45{$BOmukypR0Pc;%sU_IJ}duz;<^ZK z9>draE8Xa3_C1;X*pFvPyllaiDSBiEdB|h^`4c*9l}=2PedbT&%BL5jn7S65jo%x- zB@!A*BPo!0nI<}KD=Wv9oN&VknqU5Gc+KV!BQr-zRLUt{)?>Yttt3hQKJ1I{eo`V^ zZ8NlHjVJAS#Z=y|tdCNY-6~*j@y_Mmi@ls|Ftigprv!EcBSx>cB>gjE2A#x7EF#&+wwjXk z3fVliYN>L5%W1>hk(J94U7rx=9l+MR9Lvq2KEvPMI^dl@feD7MRx(=lh8SDC2t@ub zM4?QMiv7lI+{Qs1G`D}J=IU%PaFH`>tEThb!XX9vWO1a*T}OQ4-AO+6$o)r-Sdj`; zMAQ|b2ph10?JUnVT(ez70~*XG?!m@YT*X?fwaxTs0l6ti;>vc<8yy2*iCpAzsWxWF suDgmz60an)FpFQVmLPr5zyv)eSHrxj_e1I#E2L8P?65+$DgAT)1z`^&!2kdN delta 2448 zcmbuBacGov7{|Zwy}P~dcCy>>4)4sRQ&w~`oH~+lxMIp>H=HyLo5-1@1~z6fhzPlX zCy5d|&Uz;jF=iJ17n{6nOBAsb_QxWP5oTFP1lzKxX1TWdKJV^5e(&8}LC**7{k`w+ zd7tO`J-_Goyf@e#8|;pCrnR&$t*499{&aD_(Y8Cj4cqYU|6~(3^;HCK7WK7PxBB#> zIGX9a@RUA;p}C)CDuRa=)}tP~$szJKS&rq@PR!bfZ@>n0%s~n%{GG=j2ItW1z;5qDnJ93V*~X|qaSg;fu5ojQ!ex!2s+FXJ-4B<{Uj ze(Gk6FBJu%I-{+1x&shZco@AF3$tg+hWI~7`=EnQ7W+%@W#hO|d(bNj8 zz#l~QAmu4iOgLk?|>_K0AxE?kl+~>u+{rT}8Kk?e-p{)t6Zo zjCg`g$Zop2a{ZC`i7R`s$1{Hp=iI5>RZb@>gS#2k$1!eQddLjVcM^7JR#Enir_imQAOozL(_EtC9YiPbn@+%H&|-j9CA@D*GU!%L-qNV?VZ%UBwK z3G?3;QLou&E*TGN1&-Dp!!eIhIUZ6}4?>47&aCQ?8OofvU|R40tVIfz$==_W?YVWD z9lT~dQs~-xY{m%b>^ihpqi?|$F1T`~9H+-gzT`K{PIS5+YFQ#3dYwHqVwB|_uPCDU zg{Wzpzyx*>jkr>day&;45>-M7dD1S~d5Ng@WGqd_AgT;wIOI_r!3Zi)!LZ}fI~n%P zH}pEGtdNx$HSVj2ty15Q{Y*%Bk+}SVd`H~Yc^7xBt$wpIA?px%kL1U61V_|x&`a`l zq@|1TCNwduh9bN9mXX)f1CpnA2>TiXs%oM0oZX$+>9MJv_#lE#4^(|lVG3)AE48T2 zc7w3>irc;h0k89*lqRz`$Hu+N`e*j#$ zj_WzSJGdizVU73ts#f-($JKGJ2_y<47o%-xbF#z$pVsUX6Glv`dZShIn$HYu#%8v* zdijJgx)*}{+u?kg<&!%5#cY(7ZPl!?(|X8$^mi63ms{$$8>X0ZXUO>%HG651nVsT2 pKO4OPvv^jO_orT`=7)ZcP9C(Ir_Cn)P5yFJQhUIjYAec diff --git a/MacawTests/png/struct-use-03-t-manual.png b/MacawTests/png/struct-use-03-t-manual.png index 58aaa97e6e2a7ec1bcf5e05d2fa7db37dcd76cb2..18a3bb36e17aef6ec92d818779f741410ff2749d 100644 GIT binary patch delta 77 zcmdlrLu=Ozt%fa(^R3&Zof)@FJ2O=qZ&u)+$+|gT^)X}nv@#|jW(HywAZ7((HXvr- aKCO%+x4FHZk#l=JBUeq{_WBI2-O2!ijvZ$J delta 92 zcmdlrLu=Ozt%fa(^Q|{4@LRBL&R2C|Y@ctF7M2#)7Pc1lEga0h+UprPx7RareJS1k{|QqK`}X2;uHzyA Dtep}` delta 58 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hwomxYWWYZC|5YZR?f!o`e3;vTB#_0q MqG)?@IoEL!0IVw){{R30 diff --git a/MacawTests/png/text-align-01-b-manual.png b/MacawTests/png/text-align-01-b-manual.png index e16f13f726b982b1d5b55fa6d7ccc21a4fc7d23c..a059137deefb9b5c802e18748423238ea033fd56 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ diff --git a/MacawTests/png/text-fonts-01-t-manual.png b/MacawTests/png/text-fonts-01-t-manual.png index d13ff5b9fcb4f265d233c08457a487b876078f28..6dcfa4d9a3a62d0557794c318ada2561075f2080 100644 GIT binary patch delta 9808 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 delta 13183 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 diff --git a/MacawTests/png/types-basic-01-f-manual.png b/MacawTests/png/types-basic-01-f-manual.png index 5682a2b38767a536119a94dacaca9eab0c6ff173..8c56631cb1cb328371be54cc1f83c55d970bbb47 100644 GIT binary patch delta 41 xcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0h+UprPx7Rar)#Poj&*0jv3;;Yg4yynF delta 56 zcmdlrLu=Ozt%erH7N!>F7M2#)7Pc1lEga0hHY@NOuuslc_1W(Km&1p-9Y_LMoCew3 Kf9G@A%K!jL1Qgf+ From 6c8849b0446cfb7583ebe83df9bcc0013c07b5f2 Mon Sep 17 00:00:00 2001 From: Artyom Date: Fri, 26 Jun 2020 15:01:19 +0300 Subject: [PATCH 42/48] Fix typecheck --- Source/model/draw/ColorMatrix.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/model/draw/ColorMatrix.swift b/Source/model/draw/ColorMatrix.swift index 0ee4ec31..9ac27a5a 100644 --- a/Source/model/draw/ColorMatrix.swift +++ b/Source/model/draw/ColorMatrix.swift @@ -57,7 +57,7 @@ open class ColorMatrix { let m3 = [-0.213, -0.715, 0.928, 0.143, 0.140, -0.283, -0.787, 0.715, 0.072] - let a = { i in + let a = { (i: Int) -> Double in m1[i] + c * m2[i] + s * m3[i] } self.init(values: [a(0), a(1), a(2), 0, 0, From 6ba007b75e39c068ddd3f79e1221780423ecd26b Mon Sep 17 00:00:00 2001 From: Anton Poltoratskyi Date: Mon, 13 Jul 2020 12:33:38 +0300 Subject: [PATCH 43/48] Fixed Node's `onTap` method. Previous implementation haven't considered copy on write behavior, so it was not possible to register second tap hander. --- Source/model/scene/Node.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/model/scene/Node.swift b/Source/model/scene/Node.swift index 0f321552..d580d7e9 100644 --- a/Source/model/scene/Node.swift +++ b/Source/model/scene/Node.swift @@ -111,11 +111,11 @@ open class Node: Drawable { @discardableResult public func onTap(tapCount: Int = 1, f: @escaping (TapEvent) -> Void) -> Disposable { let handler = ChangeHandler(f) - if var handlers = tapHandlers[tapCount] { - handlers.append(handler) - } else { - tapHandlers[tapCount] = [handler] - } + + var handlers = tapHandlers[tapCount] ?? [] + handlers.append(handler) + + tapHandlers[tapCount] = handlers return Disposable { [weak self, unowned handler] in guard let index = self?.tapHandlers[tapCount]?.firstIndex(of: handler) else { From 2181401dbb1e99256d1ab0264de7d1cd4d08a943 Mon Sep 17 00:00:00 2001 From: Anton Poltoratskyi Date: Mon, 13 Jul 2020 21:35:46 +0300 Subject: [PATCH 44/48] Fixed retain cycle between MacawView and MacawZoom --- Source/views/MacawView.swift | 6 +++--- Source/views/MacawZoom.swift | 15 +++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index aea5ee95..07a46057 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -15,7 +15,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { internal var drawingView = DrawingView() - public let zoom = MacawZoom() + public lazy var zoom = MacawZoom(view: self) open var node: Node { get { return drawingView.node } @@ -77,7 +77,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { self.node = node self.renderer = RenderUtils.createNodeRenderer(node, view: drawingView) - zoom.initialize(view: self, onChange: onZoomChange) + zoom.initialize(onChange: onZoomChange) initializeView() } @@ -91,7 +91,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { public override init(frame: CGRect) { super.init(frame: frame) - zoom.initialize(view: self, onChange: onZoomChange) + zoom.initialize(onChange: onZoomChange) initializeView() } diff --git a/Source/views/MacawZoom.swift b/Source/views/MacawZoom.swift index 677cb6ba..a8f1ca76 100644 --- a/Source/views/MacawZoom.swift +++ b/Source/views/MacawZoom.swift @@ -16,14 +16,18 @@ import AppKit open class MacawZoom { - private var view: MacawView! - private var onChange: ((Transform) -> Void)! + private unowned let view: MacawView + private var onChange: ((Transform) -> Void)? private var touches = [TouchData]() private var zoomData = ZoomData() private var trackMove = false private var trackScale = false private var trackRotate = false + + init(view: MacawView) { + self.view = view + } open func enable(move: Bool = true, scale: Bool = true, rotate: Bool = false) { trackMove = move @@ -47,11 +51,10 @@ open class MacawZoom { let s = scale ?? zoomData.scale let a = angle ?? zoomData.angle zoomData = ZoomData(offset: o, scale: s, angle: a) - onChange(zoomData.transform()) + onChange?(zoomData.transform()) } - func initialize(view: MacawView, onChange: @escaping ((Transform) -> Void)) { - self.view = view + func initialize(onChange: @escaping (Transform) -> Void) { self.onChange = onChange } @@ -63,7 +66,7 @@ open class MacawZoom { func touchesMoved(_ touches: Set) { let zoom = cleanTouches() ?? getNewZoom() - onChange(zoom.transform()) + onChange?(zoom.transform()) } func touchesEnded(_ touches: Set) { From e65dfc8d017322f4746d81b930025a1456f29535 Mon Sep 17 00:00:00 2001 From: Anton Poltoratskyi Date: Mon, 13 Jul 2020 21:37:38 +0300 Subject: [PATCH 45/48] Fixed retain cycle when passing `onZoomChange` method as a closure --- Source/views/MacawView.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 07a46057..7ede880b 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -77,7 +77,9 @@ open class MacawView: MView, MGestureRecognizerDelegate { self.node = node self.renderer = RenderUtils.createNodeRenderer(node, view: drawingView) - zoom.initialize(onChange: onZoomChange) + zoom.initialize(onChange: { [weak self] transform in + self?.onZoomChange(t: transform) + }) initializeView() } @@ -91,7 +93,9 @@ open class MacawView: MView, MGestureRecognizerDelegate { public override init(frame: CGRect) { super.init(frame: frame) - zoom.initialize(onChange: onZoomChange) + zoom.initialize(onChange: { [weak self] transform in + self?.onZoomChange(t: transform) + }) initializeView() } From f3634ab518c8d8046f353de2dd2ca42878337b98 Mon Sep 17 00:00:00 2001 From: Anton Poltoratskyi Date: Mon, 13 Jul 2020 22:00:15 +0300 Subject: [PATCH 46/48] Fixed retain cycle between renderers and GroupRenderer --- Source/render/NodeRenderer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/render/NodeRenderer.swift b/Source/render/NodeRenderer.swift index 11a8b8b8..418db4fb 100644 --- a/Source/render/NodeRenderer.swift +++ b/Source/render/NodeRenderer.swift @@ -29,8 +29,8 @@ class NodeRenderer { var layer: CachedLayer? var zPosition: Int = 0 - let parentRenderer: GroupRenderer? - + private(set) weak var parentRenderer: GroupRenderer? + fileprivate let onNodeChange: () -> Void fileprivate let disposables = GroupDisposable() fileprivate var active = false From 7d6a3b964b24474739d0116822a2c73888d60c87 Mon Sep 17 00:00:00 2001 From: Anton Poltoratskyi Date: Tue, 14 Jul 2020 12:54:45 +0300 Subject: [PATCH 47/48] Added search by predicate --- Source/model/scene/Group.swift | 16 ++++++++-------- Source/model/scene/Node.swift | 20 ++++++++++++++------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Source/model/scene/Group.swift b/Source/model/scene/Group.swift index 1358001d..171f3f28 100644 --- a/Source/model/scene/Group.swift +++ b/Source/model/scene/Group.swift @@ -26,28 +26,28 @@ open class Group: Node { } // Searching - - override public func nodeBy(tag: String) -> Node? { - if let node = super.nodeBy(tag: tag) { + + override public func nodeBy(predicate: (String) -> Bool) -> Node? { + if let node = super.nodeBy(predicate: predicate) { return node } for child in contents { - if let node = child.nodeBy(tag: tag) { + if let node = child.nodeBy(predicate: predicate) { return node } } return .none } - - override public func nodesBy(tag: String) -> [Node] { + + override public func nodesBy(predicate: (String) -> Bool) -> [Node] { var result = [Node]() contents.forEach { child in - result.append(contentsOf: child.nodesBy(tag: tag)) + result.append(contentsOf: child.nodesBy(predicate: predicate)) } - if let node = super.nodeBy(tag: tag) { + if let node = super.nodeBy(predicate: predicate) { result.append(node) } diff --git a/Source/model/scene/Node.swift b/Source/model/scene/Node.swift index 0f321552..74c0090b 100644 --- a/Source/model/scene/Node.swift +++ b/Source/model/scene/Node.swift @@ -42,16 +42,24 @@ open class Node: Drawable { // MARK: - Searching public func nodeBy(tag: String) -> Node? { - if self.tag.contains(tag) { - return self - } - - return .none + return nodeBy(predicate: { $0 == tag }) } public func nodesBy(tag: String) -> [Node] { - return [nodeBy(tag: tag)].compactMap { $0 } + return nodesBy(predicate: { $0 == tag }) } + + public func nodeBy(predicate: (String) -> Bool) -> Node? { + if self.tag.contains(where: predicate) { + return self + } + return .none + } + + public func nodesBy(predicate: (String) -> Bool) -> [Node] { + return [nodeBy(predicate: predicate)].compactMap { $0 } + } + // MARK: - Events internal var animationObservers = [AnimationObserver]() From d613d6c534c1f897e9db9043c63c7a63f5564bfd Mon Sep 17 00:00:00 2001 From: Anton Poltoratskyi Date: Sat, 18 Jul 2020 12:01:39 +0300 Subject: [PATCH 48/48] Update method signature for `nodeBy(predicate:)` --- Source/model/scene/Group.swift | 4 ++-- Source/model/scene/Node.swift | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/model/scene/Group.swift b/Source/model/scene/Group.swift index 171f3f28..a5e2f5ac 100644 --- a/Source/model/scene/Group.swift +++ b/Source/model/scene/Group.swift @@ -27,7 +27,7 @@ open class Group: Node { // Searching - override public func nodeBy(predicate: (String) -> Bool) -> Node? { + override public func nodeBy(predicate: (Node) -> Bool) -> Node? { if let node = super.nodeBy(predicate: predicate) { return node } @@ -41,7 +41,7 @@ open class Group: Node { return .none } - override public func nodesBy(predicate: (String) -> Bool) -> [Node] { + override public func nodesBy(predicate: (Node) -> Bool) -> [Node] { var result = [Node]() contents.forEach { child in result.append(contentsOf: child.nodesBy(predicate: predicate)) diff --git a/Source/model/scene/Node.swift b/Source/model/scene/Node.swift index 74c0090b..2ce2a604 100644 --- a/Source/model/scene/Node.swift +++ b/Source/model/scene/Node.swift @@ -42,21 +42,21 @@ open class Node: Drawable { // MARK: - Searching public func nodeBy(tag: String) -> Node? { - return nodeBy(predicate: { $0 == tag }) + return nodeBy(predicate: { $0.tag.contains(tag) }) } public func nodesBy(tag: String) -> [Node] { - return nodesBy(predicate: { $0 == tag }) + return nodesBy(predicate: { $0.tag.contains(tag) }) } - public func nodeBy(predicate: (String) -> Bool) -> Node? { - if self.tag.contains(where: predicate) { + public func nodeBy(predicate: (Node) -> Bool) -> Node? { + if predicate(self) { return self } return .none } - public func nodesBy(predicate: (String) -> Bool) -> [Node] { + public func nodesBy(predicate: (Node) -> Bool) -> [Node] { return [nodeBy(predicate: predicate)].compactMap { $0 } }