From 86cd544b978b01fb736b31824683d09c743edae9 Mon Sep 17 00:00:00 2001 From: "sina.main" <> Date: Thu, 31 Dec 2020 15:07:15 +0100 Subject: [PATCH] Using TNT package --- C4.xcodeproj/project.pbxproj | 1060 +---------------- .../contents.xcworkspacedata | 2 +- .../xcshareddata/xcschemes/C4-iOS.xcscheme | 24 +- .../xcshareddata/xcschemes/C4-tvOS.xcscheme | 99 -- C4/C4.h | 28 - C4/Core/Color.swift | 351 ------ C4/Core/EventSource.swift | 113 -- C4/Core/Foundation.swift | 64 - C4/Core/Math.swift | 185 --- C4/Core/Path.swift | 231 ---- C4/Core/Point.swift | 193 --- C4/Core/Rect.swift | 307 ----- C4/Core/Size.swift | 175 --- C4/Core/Transform.swift | 300 ----- C4/Core/Vector.swift | 392 ------ C4/Info.plist | 24 - C4/UI/Animation.swift | 160 --- C4/UI/Arc.swift | 59 - C4/UI/AudioPlayer.swift | 250 ---- C4/UI/Camera.swift | 217 ---- C4/UI/CanvasController.swift | 51 - C4/UI/Circle.swift | 38 - C4/UI/Curve.swift | 112 -- C4/UI/Ellipse.swift | 45 - C4/UI/Filter.swift | 48 - C4/UI/Filters/Bloom.swift | 46 - C4/UI/Filters/Checkerboard.swift | 57 - C4/UI/Filters/ColorBurn.swift | 51 - C4/UI/Filters/DotScreen.swift | 58 - C4/UI/Filters/GaussianBlur.swift | 51 - C4/UI/Filters/HueAdjust.swift | 43 - C4/UI/Filters/LinearGradient.swift | 54 - C4/UI/Filters/Sepia.swift | 54 - C4/UI/Filters/Sharpen.swift | 43 - C4/UI/Filters/Twirl.swift | 57 - C4/UI/Font.swift | 215 ---- C4/UI/Generator.swift | 48 - C4/UI/Gradient.swift | 129 -- C4/UI/GradientLayer.swift | 62 - C4/UI/Image+ColorAt.swift | 74 -- C4/UI/Image+Crop.swift | 49 - C4/UI/Image+Filter.swift | 58 - C4/UI/Image+Generator.swift | 47 - C4/UI/Image.swift | 410 ------- C4/UI/ImageLayer.swift | 114 -- C4/UI/Layer.swift | 97 -- C4/UI/Line.swift | 168 --- C4/UI/Movie.swift | 251 ---- C4/UI/Pixel.swift | 57 - C4/UI/PlayerLayer.swift | 125 -- C4/UI/Polygon.swift | 110 -- C4/UI/QuadCurve.swift | 48 - C4/UI/Rectangle.swift | 70 -- C4/UI/RegularPolygon.swift | 85 -- C4/UI/ScreenRecorder.swift | 87 -- C4/UI/Shape+Creation.swift | 130 -- C4/UI/Shape.swift | 377 ------ C4/UI/ShapeLayer.swift | 153 --- C4/UI/Star.swift | 61 - C4/UI/StoredAnimation.swift | 74 -- C4/UI/TextShape.swift | 116 -- C4/UI/Timer.swift | 81 -- C4/UI/Triangle.swift | 43 - C4/UI/UIGestureRecognizer+Closure.swift | 365 ------ C4/UI/UIImage+Color.swift | 37 - C4/UI/UIView+AddRemove.swift | 95 -- C4/UI/UIViewController+C4View.swift | 54 - C4/UI/View+Animation.swift | 67 -- C4/UI/View+Border.swift | 82 -- C4/UI/View+Gestures.swift | 131 -- C4/UI/View+KeyValues.swift | 85 -- C4/UI/View+Render.swift | 61 - C4/UI/View+Shadow.swift | 108 -- C4/UI/View.swift | 460 ------- C4/UI/ViewAnimation.swift | 307 ----- C4/UI/Wedge.swift | 67 -- .../AppIcon.appiconset/Contents.json | 106 +- .../AppIcon.appiconset/logoLArge.png | Bin 0 -> 85071 bytes C4App/ViewController.swift | 2 +- Tests/ColorTests.swift | 64 - Tests/Info.plist | 24 - Tests/MathTests.swift | 130 -- Tests/PointTests.swift | 42 - Tests/RectTests.swift | 124 -- Tests/TransformTests.swift | 56 - Tests/VectorTests.swift | 93 -- 86 files changed, 125 insertions(+), 10816 deletions(-) delete mode 100644 C4.xcodeproj/xcshareddata/xcschemes/C4-tvOS.xcscheme delete mode 100644 C4/C4.h delete mode 100644 C4/Core/Color.swift delete mode 100644 C4/Core/EventSource.swift delete mode 100644 C4/Core/Foundation.swift delete mode 100644 C4/Core/Math.swift delete mode 100644 C4/Core/Path.swift delete mode 100644 C4/Core/Point.swift delete mode 100644 C4/Core/Rect.swift delete mode 100644 C4/Core/Size.swift delete mode 100644 C4/Core/Transform.swift delete mode 100644 C4/Core/Vector.swift delete mode 100644 C4/Info.plist delete mode 100644 C4/UI/Animation.swift delete mode 100644 C4/UI/Arc.swift delete mode 100644 C4/UI/AudioPlayer.swift delete mode 100644 C4/UI/Camera.swift delete mode 100644 C4/UI/CanvasController.swift delete mode 100644 C4/UI/Circle.swift delete mode 100644 C4/UI/Curve.swift delete mode 100644 C4/UI/Ellipse.swift delete mode 100644 C4/UI/Filter.swift delete mode 100644 C4/UI/Filters/Bloom.swift delete mode 100644 C4/UI/Filters/Checkerboard.swift delete mode 100644 C4/UI/Filters/ColorBurn.swift delete mode 100644 C4/UI/Filters/DotScreen.swift delete mode 100644 C4/UI/Filters/GaussianBlur.swift delete mode 100644 C4/UI/Filters/HueAdjust.swift delete mode 100644 C4/UI/Filters/LinearGradient.swift delete mode 100644 C4/UI/Filters/Sepia.swift delete mode 100644 C4/UI/Filters/Sharpen.swift delete mode 100644 C4/UI/Filters/Twirl.swift delete mode 100644 C4/UI/Font.swift delete mode 100644 C4/UI/Generator.swift delete mode 100644 C4/UI/Gradient.swift delete mode 100644 C4/UI/GradientLayer.swift delete mode 100644 C4/UI/Image+ColorAt.swift delete mode 100644 C4/UI/Image+Crop.swift delete mode 100644 C4/UI/Image+Filter.swift delete mode 100644 C4/UI/Image+Generator.swift delete mode 100644 C4/UI/Image.swift delete mode 100644 C4/UI/ImageLayer.swift delete mode 100644 C4/UI/Layer.swift delete mode 100644 C4/UI/Line.swift delete mode 100644 C4/UI/Movie.swift delete mode 100644 C4/UI/Pixel.swift delete mode 100644 C4/UI/PlayerLayer.swift delete mode 100644 C4/UI/Polygon.swift delete mode 100644 C4/UI/QuadCurve.swift delete mode 100644 C4/UI/Rectangle.swift delete mode 100644 C4/UI/RegularPolygon.swift delete mode 100644 C4/UI/ScreenRecorder.swift delete mode 100644 C4/UI/Shape+Creation.swift delete mode 100644 C4/UI/Shape.swift delete mode 100644 C4/UI/ShapeLayer.swift delete mode 100644 C4/UI/Star.swift delete mode 100644 C4/UI/StoredAnimation.swift delete mode 100644 C4/UI/TextShape.swift delete mode 100644 C4/UI/Timer.swift delete mode 100644 C4/UI/Triangle.swift delete mode 100644 C4/UI/UIGestureRecognizer+Closure.swift delete mode 100644 C4/UI/UIImage+Color.swift delete mode 100644 C4/UI/UIView+AddRemove.swift delete mode 100644 C4/UI/UIViewController+C4View.swift delete mode 100644 C4/UI/View+Animation.swift delete mode 100644 C4/UI/View+Border.swift delete mode 100644 C4/UI/View+Gestures.swift delete mode 100644 C4/UI/View+KeyValues.swift delete mode 100644 C4/UI/View+Render.swift delete mode 100644 C4/UI/View+Shadow.swift delete mode 100644 C4/UI/View.swift delete mode 100644 C4/UI/ViewAnimation.swift delete mode 100644 C4/UI/Wedge.swift create mode 100644 C4App/Images.xcassets/AppIcon.appiconset/logoLArge.png delete mode 100644 Tests/ColorTests.swift delete mode 100644 Tests/Info.plist delete mode 100644 Tests/MathTests.swift delete mode 100644 Tests/PointTests.swift delete mode 100644 Tests/RectTests.swift delete mode 100644 Tests/TransformTests.swift delete mode 100644 Tests/VectorTests.swift diff --git a/C4.xcodeproj/project.pbxproj b/C4.xcodeproj/project.pbxproj index 3feed7ad..46accb3a 100644 --- a/C4.xcodeproj/project.pbxproj +++ b/C4.xcodeproj/project.pbxproj @@ -3,206 +3,21 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ - 1F40C6FB1C82ADF0004AE1E7 /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F40C6FA1C82ADF0004AE1E7 /* Camera.swift */; }; - 1F754DCB1C48EE600036D39F /* ImageLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F754DCA1C48EE600036D39F /* ImageLayer.swift */; }; 1F75FDEB1C5B1FE700EB62C2 /* C4Loop.aif in Resources */ = {isa = PBXBuildFile; fileRef = 1F75FDE81C5B1FE700EB62C2 /* C4Loop.aif */; }; 1F75FDEC1C5B1FE700EB62C2 /* halo.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 1F75FDE91C5B1FE700EB62C2 /* halo.mp4 */; }; 1F75FDED1C5B1FE700EB62C2 /* haloGray.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 1F75FDEA1C5B1FE700EB62C2 /* haloGray.mp4 */; }; - 1F75FE951C5CADBF00EB62C2 /* ScreenRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F75FE941C5CADBF00EB62C2 /* ScreenRecorder.swift */; }; - 1FAB35E81C5372CE00620741 /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAB35E71C5372CE00620741 /* Pixel.swift */; }; - 1FAB35EA1C53745300620741 /* Layer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAB35E91C53745300620741 /* Layer.swift */; }; - 1FAB35EC1C53749500620741 /* UIView+AddRemove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAB35EB1C53749500620741 /* UIView+AddRemove.swift */; }; - 1FDB8B121DEF8E4B0082B461 /* Image+ColorAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDB8B111DEF8E4B0082B461 /* Image+ColorAt.swift */; }; - 610A6A571F928E1B003B841A /* C4.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 610A6A4E1F928E1B003B841A /* C4.framework */; }; - 610A6A651F928E3A003B841A /* C4.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D4F6E61B53516400F937AB /* C4.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 610A6A661F928E91003B841A /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6711B534F9F00F937AB /* Color.swift */; }; - 610A6A671F928E91003B841A /* EventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6721B534F9F00F937AB /* EventSource.swift */; }; - 610A6A681F928E91003B841A /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6731B534F9F00F937AB /* Foundation.swift */; }; - 610A6A691F928E91003B841A /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6741B534F9F00F937AB /* Math.swift */; }; - 610A6A6A1F928E91003B841A /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6761B534F9F00F937AB /* Path.swift */; }; - 610A6A6B1F928E91003B841A /* Point.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6771B534F9F00F937AB /* Point.swift */; }; - 610A6A6C1F928E91003B841A /* Rect.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6781B534F9F00F937AB /* Rect.swift */; }; - 610A6A6D1F928E91003B841A /* Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6791B534F9F00F937AB /* Size.swift */; }; - 610A6A6E1F928E91003B841A /* Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F67A1B534F9F00F937AB /* Transform.swift */; }; - 610A6A6F1F928E91003B841A /* Vector.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F67B1B534F9F00F937AB /* Vector.swift */; }; - 610A6A701F928E96003B841A /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F67E1B534F9F00F937AB /* Animation.swift */; }; - 610A6A711F928E96003B841A /* Arc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6801B534F9F00F937AB /* Arc.swift */; }; - 610A6A721F928E96003B841A /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6811B534F9F00F937AB /* AudioPlayer.swift */; }; - 610A6A741F928E96003B841A /* CanvasController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6821B534F9F00F937AB /* CanvasController.swift */; }; - 610A6A751F928E96003B841A /* Circle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6831B534F9F00F937AB /* Circle.swift */; }; - 610A6A761F928E96003B841A /* Curve.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6841B534F9F00F937AB /* Curve.swift */; }; - 610A6A771F928E96003B841A /* Ellipse.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6851B534F9F00F937AB /* Ellipse.swift */; }; - 610A6A781F928E96003B841A /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6861B534F9F00F937AB /* Filter.swift */; }; - 610A6A791F928E96003B841A /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6871B534F9F00F937AB /* Font.swift */; }; - 610A6A7A1F928E96003B841A /* Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6881B534F9F00F937AB /* Generator.swift */; }; - 610A6A7B1F928E96003B841A /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D917E1351B83E0920084BC82 /* Gradient.swift */; }; - 610A6A7C1F928E96003B841A /* GradientLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D917E1371B83E1DB0084BC82 /* GradientLayer.swift */; }; - 610A6A7D1F928E96003B841A /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68C1B534F9F00F937AB /* Image.swift */; }; - 610A6A7E1F928E96003B841A /* Image+ColorAt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FDB8B111DEF8E4B0082B461 /* Image+ColorAt.swift */; }; - 610A6A7F1F928E96003B841A /* Image+Crop.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6891B534F9F00F937AB /* Image+Crop.swift */; }; - 610A6A801F928E96003B841A /* Image+Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68A1B534F9F00F937AB /* Image+Filter.swift */; }; - 610A6A811F928E96003B841A /* Image+Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68B1B534F9F00F937AB /* Image+Generator.swift */; }; - 610A6A821F928E96003B841A /* ImageLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F754DCA1C48EE600036D39F /* ImageLayer.swift */; }; - 610A6A831F928E96003B841A /* Layer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAB35E91C53745300620741 /* Layer.swift */; }; - 610A6A841F928E96003B841A /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68D1B534F9F00F937AB /* Line.swift */; }; - 610A6A851F928E96003B841A /* Movie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68E1B534F9F00F937AB /* Movie.swift */; }; - 610A6A861F928E96003B841A /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAB35E71C5372CE00620741 /* Pixel.swift */; }; - 610A6A871F928E96003B841A /* PlayerLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B451C7B963B003BD61C /* PlayerLayer.swift */; }; - 610A6A881F928E96003B841A /* Polygon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68F1B534F9F00F937AB /* Polygon.swift */; }; - 610A6A891F928E96003B841A /* QuadCurve.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6901B534F9F00F937AB /* QuadCurve.swift */; }; - 610A6A8A1F928E96003B841A /* Rectangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6911B534F9F00F937AB /* Rectangle.swift */; }; - 610A6A8B1F928E96003B841A /* RegularPolygon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6921B534F9F00F937AB /* RegularPolygon.swift */; }; - 610A6A8C1F928E96003B841A /* ScreenRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F75FE941C5CADBF00EB62C2 /* ScreenRecorder.swift */; }; - 610A6A8D1F928E96003B841A /* Shape.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6941B534F9F00F937AB /* Shape.swift */; }; - 610A6A8E1F928E96003B841A /* Shape+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6931B534F9F00F937AB /* Shape+Creation.swift */; }; - 610A6A8F1F928E96003B841A /* ShapeLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6951B534F9F00F937AB /* ShapeLayer.swift */; }; - 610A6A901F928E96003B841A /* Star.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6961B534F9F00F937AB /* Star.swift */; }; - 610A6A911F928E96003B841A /* StoredAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61BBAF621BC372BD00A03FD0 /* StoredAnimation.swift */; }; - 610A6A921F928E96003B841A /* TextShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6971B534F9F00F937AB /* TextShape.swift */; }; - 610A6A931F928E96003B841A /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B461C7B963B003BD61C /* Timer.swift */; }; - 610A6A941F928E96003B841A /* Triangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6981B534F9F00F937AB /* Triangle.swift */; }; - 610A6A961F928E96003B841A /* UIImage+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BC57261C50ABA80068BBA3 /* UIImage+Color.swift */; }; - 610A6A971F928E96003B841A /* UIView+AddRemove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAB35EB1C53749500620741 /* UIView+AddRemove.swift */; }; - 610A6A981F928E96003B841A /* UIViewController+C4View.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6AB1B534F9F00F937AB /* UIViewController+C4View.swift */; }; - 610A6A991F928E96003B841A /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69C1B534F9F00F937AB /* View.swift */; }; - 610A6A9A1F928E96003B841A /* View+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6991B534F9F00F937AB /* View+Animation.swift */; }; - 610A6A9B1F928E96003B841A /* View+Border.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69A1B534F9F00F937AB /* View+Border.swift */; }; - 610A6A9D1F928E96003B841A /* View+KeyValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61BBAF641BC38C1200A03FD0 /* View+KeyValues.swift */; }; - 610A6A9E1F928E96003B841A /* View+Render.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B471C7B963B003BD61C /* View+Render.swift */; }; - 610A6A9F1F928E96003B841A /* View+Shadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69B1B534F9F00F937AB /* View+Shadow.swift */; }; - 610A6AA01F928E96003B841A /* ViewAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69D1B534F9F00F937AB /* ViewAnimation.swift */; }; - 610A6AA11F928E96003B841A /* Wedge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69E1B534F9F00F937AB /* Wedge.swift */; }; - 610A6AA21F928E9A003B841A /* Bloom.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A01B534F9F00F937AB /* Bloom.swift */; }; - 610A6AA31F928E9A003B841A /* Checkerboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A11B534F9F00F937AB /* Checkerboard.swift */; }; - 610A6AA41F928E9A003B841A /* ColorBurn.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A21B534F9F00F937AB /* ColorBurn.swift */; }; - 610A6AA51F928E9A003B841A /* DotScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A31B534F9F00F937AB /* DotScreen.swift */; }; - 610A6AA61F928E9A003B841A /* GaussianBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A41B534F9F00F937AB /* GaussianBlur.swift */; }; - 610A6AA71F928E9A003B841A /* HueAdjust.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A51B534F9F00F937AB /* HueAdjust.swift */; }; - 610A6AA81F928E9A003B841A /* LinearGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A61B534F9F00F937AB /* LinearGradient.swift */; }; - 610A6AA91F928E9A003B841A /* Sepia.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A71B534F9F00F937AB /* Sepia.swift */; }; - 610A6AAA1F928E9A003B841A /* Sharpen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A81B534F9F00F937AB /* Sharpen.swift */; }; - 610A6AAB1F928E9A003B841A /* Twirl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A91B534F9F00F937AB /* Twirl.swift */; }; - 610A6AAC1F928EAC003B841A /* ColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D401A054B4F0023323D /* ColorTests.swift */; }; - 610A6AAD1F928EAC003B841A /* MathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D411A054B4F0023323D /* MathTests.swift */; }; - 610A6AAE1F928EAC003B841A /* PointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D421A054B4F0023323D /* PointTests.swift */; }; - 610A6AAF1F928EAC003B841A /* RectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B4B1C7B96B8003BD61C /* RectTests.swift */; }; - 610A6AB01F928EAC003B841A /* VectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D431A054B4F0023323D /* VectorTests.swift */; }; - 610A6AB11F928EAC003B841A /* TransformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988A9161A4F57330044007C /* TransformTests.swift */; }; - 6159A4D61F928B79000A3A99 /* View+Gestures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6159A4D51F928B79000A3A99 /* View+Gestures.swift */; }; - 61B92B481C7B963B003BD61C /* PlayerLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B451C7B963B003BD61C /* PlayerLayer.swift */; }; - 61B92B491C7B963B003BD61C /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B461C7B963B003BD61C /* Timer.swift */; }; - 61B92B4A1C7B963B003BD61C /* View+Render.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B471C7B963B003BD61C /* View+Render.swift */; }; - 61B92B4C1C7B96B8003BD61C /* RectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B92B4B1C7B96B8003BD61C /* RectTests.swift */; }; - 61BBAF631BC372BD00A03FD0 /* StoredAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61BBAF621BC372BD00A03FD0 /* StoredAnimation.swift */; }; - 61BBAF651BC38C1200A03FD0 /* View+KeyValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61BBAF641BC38C1200A03FD0 /* View+KeyValues.swift */; }; - A94E3BD419E0E9390039A9C4 /* C4.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 614F824319DB5ED3001DF1D4 /* C4.framework */; }; - A96F4F451B538330002B3A46 /* ColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D401A054B4F0023323D /* ColorTests.swift */; }; - A96F4F461B538330002B3A46 /* MathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D411A054B4F0023323D /* MathTests.swift */; }; - A96F4F471B538330002B3A46 /* PointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D421A054B4F0023323D /* PointTests.swift */; }; - A96F4F491B538330002B3A46 /* VectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9596D431A054B4F0023323D /* VectorTests.swift */; }; - A96F4F4A1B538330002B3A46 /* TransformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988A9161A4F57330044007C /* TransformTests.swift */; }; + 4A943552259E12020064862F /* TNT in Frameworks */ = {isa = PBXBuildFile; productRef = 4A943551259E12020064862F /* TNT */; }; A96F505F1B5385A5002B3A46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96F505E1B5385A5002B3A46 /* AppDelegate.swift */; }; A96F50611B5385A5002B3A46 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96F50601B5385A5002B3A46 /* ViewController.swift */; }; A96F50641B5385A5002B3A46 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A96F50621B5385A5002B3A46 /* Main.storyboard */; }; A96F50661B5385A5002B3A46 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A96F50651B5385A5002B3A46 /* Images.xcassets */; }; A96F50691B5385A5002B3A46 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = A96F50671B5385A5002B3A46 /* LaunchScreen.xib */; }; - A96F507E1B538650002B3A46 /* C4.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 614F824319DB5ED3001DF1D4 /* C4.framework */; }; - A96F507F1B538650002B3A46 /* C4.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 614F824319DB5ED3001DF1D4 /* C4.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - A9BC57271C50ABA80068BBA3 /* UIImage+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BC57261C50ABA80068BBA3 /* UIImage+Color.swift */; }; - A9D4F6AD1B534F9F00F937AB /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6711B534F9F00F937AB /* Color.swift */; }; - A9D4F6AE1B534F9F00F937AB /* EventSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6721B534F9F00F937AB /* EventSource.swift */; }; - A9D4F6AF1B534F9F00F937AB /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6731B534F9F00F937AB /* Foundation.swift */; }; - A9D4F6B01B534F9F00F937AB /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6741B534F9F00F937AB /* Math.swift */; }; - A9D4F6B21B534F9F00F937AB /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6761B534F9F00F937AB /* Path.swift */; }; - A9D4F6B31B534F9F00F937AB /* Point.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6771B534F9F00F937AB /* Point.swift */; }; - A9D4F6B41B534F9F00F937AB /* Rect.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6781B534F9F00F937AB /* Rect.swift */; }; - A9D4F6B51B534F9F00F937AB /* Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6791B534F9F00F937AB /* Size.swift */; }; - A9D4F6B61B534F9F00F937AB /* Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F67A1B534F9F00F937AB /* Transform.swift */; }; - A9D4F6B71B534F9F00F937AB /* Vector.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F67B1B534F9F00F937AB /* Vector.swift */; }; - A9D4F6B91B534F9F00F937AB /* Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F67E1B534F9F00F937AB /* Animation.swift */; }; - A9D4F6BB1B534F9F00F937AB /* Arc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6801B534F9F00F937AB /* Arc.swift */; }; - A9D4F6BC1B534F9F00F937AB /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6811B534F9F00F937AB /* AudioPlayer.swift */; }; - A9D4F6BD1B534F9F00F937AB /* CanvasController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6821B534F9F00F937AB /* CanvasController.swift */; }; - A9D4F6BE1B534F9F00F937AB /* Circle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6831B534F9F00F937AB /* Circle.swift */; }; - A9D4F6BF1B534F9F00F937AB /* Curve.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6841B534F9F00F937AB /* Curve.swift */; }; - A9D4F6C01B534F9F00F937AB /* Ellipse.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6851B534F9F00F937AB /* Ellipse.swift */; }; - A9D4F6C11B534F9F00F937AB /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6861B534F9F00F937AB /* Filter.swift */; }; - A9D4F6C21B534F9F00F937AB /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6871B534F9F00F937AB /* Font.swift */; }; - A9D4F6C31B534F9F00F937AB /* Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6881B534F9F00F937AB /* Generator.swift */; }; - A9D4F6C41B534F9F00F937AB /* Image+Crop.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6891B534F9F00F937AB /* Image+Crop.swift */; }; - A9D4F6C51B534F9F00F937AB /* Image+Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68A1B534F9F00F937AB /* Image+Filter.swift */; }; - A9D4F6C61B534F9F00F937AB /* Image+Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68B1B534F9F00F937AB /* Image+Generator.swift */; }; - A9D4F6C71B534F9F00F937AB /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68C1B534F9F00F937AB /* Image.swift */; }; - A9D4F6C81B534F9F00F937AB /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68D1B534F9F00F937AB /* Line.swift */; }; - A9D4F6C91B534F9F00F937AB /* Movie.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68E1B534F9F00F937AB /* Movie.swift */; }; - A9D4F6CA1B534F9F00F937AB /* Polygon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F68F1B534F9F00F937AB /* Polygon.swift */; }; - A9D4F6CB1B534F9F00F937AB /* QuadCurve.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6901B534F9F00F937AB /* QuadCurve.swift */; }; - A9D4F6CC1B534F9F00F937AB /* Rectangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6911B534F9F00F937AB /* Rectangle.swift */; }; - A9D4F6CD1B534F9F00F937AB /* RegularPolygon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6921B534F9F00F937AB /* RegularPolygon.swift */; }; - A9D4F6CE1B534F9F00F937AB /* Shape+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6931B534F9F00F937AB /* Shape+Creation.swift */; }; - A9D4F6CF1B534F9F00F937AB /* Shape.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6941B534F9F00F937AB /* Shape.swift */; }; - A9D4F6D01B534F9F00F937AB /* ShapeLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6951B534F9F00F937AB /* ShapeLayer.swift */; }; - A9D4F6D11B534F9F00F937AB /* Star.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6961B534F9F00F937AB /* Star.swift */; }; - A9D4F6D21B534F9F00F937AB /* TextShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6971B534F9F00F937AB /* TextShape.swift */; }; - A9D4F6D31B534F9F00F937AB /* Triangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6981B534F9F00F937AB /* Triangle.swift */; }; - A9D4F6D41B534F9F00F937AB /* View+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6991B534F9F00F937AB /* View+Animation.swift */; }; - A9D4F6D51B534F9F00F937AB /* View+Border.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69A1B534F9F00F937AB /* View+Border.swift */; }; - A9D4F6D61B534F9F00F937AB /* View+Shadow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69B1B534F9F00F937AB /* View+Shadow.swift */; }; - A9D4F6D71B534F9F00F937AB /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69C1B534F9F00F937AB /* View.swift */; }; - A9D4F6D81B534F9F00F937AB /* ViewAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69D1B534F9F00F937AB /* ViewAnimation.swift */; }; - A9D4F6D91B534F9F00F937AB /* Wedge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F69E1B534F9F00F937AB /* Wedge.swift */; }; - A9D4F6DA1B534F9F00F937AB /* Bloom.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A01B534F9F00F937AB /* Bloom.swift */; }; - A9D4F6DB1B534F9F00F937AB /* Checkerboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A11B534F9F00F937AB /* Checkerboard.swift */; }; - A9D4F6DC1B534F9F00F937AB /* ColorBurn.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A21B534F9F00F937AB /* ColorBurn.swift */; }; - A9D4F6DD1B534F9F00F937AB /* DotScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A31B534F9F00F937AB /* DotScreen.swift */; }; - A9D4F6DE1B534F9F00F937AB /* GaussianBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A41B534F9F00F937AB /* GaussianBlur.swift */; }; - A9D4F6DF1B534F9F00F937AB /* HueAdjust.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A51B534F9F00F937AB /* HueAdjust.swift */; }; - A9D4F6E01B534F9F00F937AB /* LinearGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A61B534F9F00F937AB /* LinearGradient.swift */; }; - A9D4F6E11B534F9F00F937AB /* Sepia.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A71B534F9F00F937AB /* Sepia.swift */; }; - A9D4F6E21B534F9F00F937AB /* Sharpen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A81B534F9F00F937AB /* Sharpen.swift */; }; - A9D4F6E31B534F9F00F937AB /* Twirl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6A91B534F9F00F937AB /* Twirl.swift */; }; - A9D4F6E41B534F9F00F937AB /* UIGestureRecognizer+Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6AA1B534F9F00F937AB /* UIGestureRecognizer+Closure.swift */; }; - A9D4F6E51B534F9F00F937AB /* UIViewController+C4View.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D4F6AB1B534F9F00F937AB /* UIViewController+C4View.swift */; }; - A9D4F6E71B53517400F937AB /* C4.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D4F6E61B53516400F937AB /* C4.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D917E1361B83E0920084BC82 /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D917E1351B83E0920084BC82 /* Gradient.swift */; }; - D917E1381B83E1DB0084BC82 /* GradientLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D917E1371B83E1DB0084BC82 /* GradientLayer.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 610A6A581F928E1B003B841A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 614F823A19DB5ED3001DF1D4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 610A6A4D1F928E1B003B841A; - remoteInfo = "C4-tvOS"; - }; - A94E3BD219E0E9340039A9C4 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 614F823A19DB5ED3001DF1D4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 614F824219DB5ED3001DF1D4; - remoteInfo = C4iOS; - }; - A96F507C1B5385AF002B3A46 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 614F823A19DB5ED3001DF1D4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = A96F50591B5385A5002B3A46; - remoteInfo = C4App; - }; - A96F50801B538650002B3A46 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 614F823A19DB5ED3001DF1D4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 614F824219DB5ED3001DF1D4; - remoteInfo = C4; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ A96F50821B538650002B3A46 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -210,7 +25,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - A96F507F1B538650002B3A46 /* C4.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -218,32 +32,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 1F40C6FA1C82ADF0004AE1E7 /* Camera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Camera.swift; sourceTree = ""; }; - 1F754DCA1C48EE600036D39F /* ImageLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLayer.swift; sourceTree = ""; }; 1F75FDE81C5B1FE700EB62C2 /* C4Loop.aif */ = {isa = PBXFileReference; lastKnownFileType = file; path = C4Loop.aif; sourceTree = ""; }; 1F75FDE91C5B1FE700EB62C2 /* halo.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = halo.mp4; sourceTree = ""; }; 1F75FDEA1C5B1FE700EB62C2 /* haloGray.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = haloGray.mp4; sourceTree = ""; }; - 1F75FE941C5CADBF00EB62C2 /* ScreenRecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenRecorder.swift; sourceTree = ""; }; - 1FAB35E71C5372CE00620741 /* Pixel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pixel.swift; sourceTree = ""; }; - 1FAB35E91C53745300620741 /* Layer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layer.swift; sourceTree = ""; }; - 1FAB35EB1C53749500620741 /* UIView+AddRemove.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+AddRemove.swift"; sourceTree = ""; }; - 1FDB8B111DEF8E4B0082B461 /* Image+ColorAt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image+ColorAt.swift"; sourceTree = ""; }; - 610A6A4E1F928E1B003B841A /* C4.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = C4.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 610A6A561F928E1B003B841A /* C4Tests-tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "C4Tests-tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 614F824319DB5ED3001DF1D4 /* C4.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = C4.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 614F824E19DB5ED4001DF1D4 /* C4Tests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "C4Tests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 6159A4D51F928B79000A3A99 /* View+Gestures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Gestures.swift"; sourceTree = ""; }; - 61B92B451C7B963B003BD61C /* PlayerLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerLayer.swift; sourceTree = ""; }; - 61B92B461C7B963B003BD61C /* Timer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = ""; }; - 61B92B471C7B963B003BD61C /* View+Render.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Render.swift"; sourceTree = ""; }; - 61B92B4B1C7B96B8003BD61C /* RectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RectTests.swift; sourceTree = ""; }; - 61BBAF621BC372BD00A03FD0 /* StoredAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoredAnimation.swift; sourceTree = ""; }; - 61BBAF641BC38C1200A03FD0 /* View+KeyValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+KeyValues.swift"; sourceTree = ""; }; - A9596D291A054A960023323D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A9596D401A054B4F0023323D /* ColorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorTests.swift; sourceTree = ""; }; - A9596D411A054B4F0023323D /* MathTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MathTests.swift; sourceTree = ""; }; - A9596D421A054B4F0023323D /* PointTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointTests.swift; sourceTree = ""; }; - A9596D431A054B4F0023323D /* VectorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VectorTests.swift; sourceTree = ""; }; A96F505A1B5385A5002B3A46 /* C4App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = C4App.app; sourceTree = BUILT_PRODUCTS_DIR; }; A96F505D1B5385A5002B3A46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A96F505E1B5385A5002B3A46 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -251,104 +42,14 @@ A96F50631B5385A5002B3A46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; A96F50651B5385A5002B3A46 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; A96F50681B5385A5002B3A46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - A988A9161A4F57330044007C /* TransformTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransformTests.swift; sourceTree = ""; }; - A9BC57261C50ABA80068BBA3 /* UIImage+Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Color.swift"; sourceTree = ""; }; - A9D4F6711B534F9F00F937AB /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; - A9D4F6721B534F9F00F937AB /* EventSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventSource.swift; sourceTree = ""; }; - A9D4F6731B534F9F00F937AB /* Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Foundation.swift; sourceTree = ""; }; - A9D4F6741B534F9F00F937AB /* Math.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Math.swift; sourceTree = ""; }; - A9D4F6761B534F9F00F937AB /* Path.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Path.swift; sourceTree = ""; }; - A9D4F6771B534F9F00F937AB /* Point.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Point.swift; sourceTree = ""; }; - A9D4F6781B534F9F00F937AB /* Rect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rect.swift; sourceTree = ""; }; - A9D4F6791B534F9F00F937AB /* Size.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Size.swift; sourceTree = ""; }; - A9D4F67A1B534F9F00F937AB /* Transform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transform.swift; sourceTree = ""; }; - A9D4F67B1B534F9F00F937AB /* Vector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vector.swift; sourceTree = ""; }; - A9D4F67C1B534F9F00F937AB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A9D4F67E1B534F9F00F937AB /* Animation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animation.swift; sourceTree = ""; }; - A9D4F6801B534F9F00F937AB /* Arc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Arc.swift; sourceTree = ""; }; - A9D4F6811B534F9F00F937AB /* AudioPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; - A9D4F6821B534F9F00F937AB /* CanvasController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CanvasController.swift; sourceTree = ""; }; - A9D4F6831B534F9F00F937AB /* Circle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Circle.swift; sourceTree = ""; }; - A9D4F6841B534F9F00F937AB /* Curve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Curve.swift; sourceTree = ""; }; - A9D4F6851B534F9F00F937AB /* Ellipse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Ellipse.swift; sourceTree = ""; }; - A9D4F6861B534F9F00F937AB /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; - A9D4F6871B534F9F00F937AB /* Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; - A9D4F6881B534F9F00F937AB /* Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generator.swift; sourceTree = ""; }; - A9D4F6891B534F9F00F937AB /* Image+Crop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image+Crop.swift"; sourceTree = ""; }; - A9D4F68A1B534F9F00F937AB /* Image+Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image+Filter.swift"; sourceTree = ""; }; - A9D4F68B1B534F9F00F937AB /* Image+Generator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image+Generator.swift"; sourceTree = ""; }; - A9D4F68C1B534F9F00F937AB /* Image.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = ""; }; - A9D4F68D1B534F9F00F937AB /* Line.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; - A9D4F68E1B534F9F00F937AB /* Movie.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Movie.swift; sourceTree = ""; }; - A9D4F68F1B534F9F00F937AB /* Polygon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Polygon.swift; sourceTree = ""; }; - A9D4F6901B534F9F00F937AB /* QuadCurve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuadCurve.swift; sourceTree = ""; }; - A9D4F6911B534F9F00F937AB /* Rectangle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rectangle.swift; sourceTree = ""; }; - A9D4F6921B534F9F00F937AB /* RegularPolygon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegularPolygon.swift; sourceTree = ""; }; - A9D4F6931B534F9F00F937AB /* Shape+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Shape+Creation.swift"; sourceTree = ""; }; - A9D4F6941B534F9F00F937AB /* Shape.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shape.swift; sourceTree = ""; }; - A9D4F6951B534F9F00F937AB /* ShapeLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShapeLayer.swift; sourceTree = ""; }; - A9D4F6961B534F9F00F937AB /* Star.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Star.swift; sourceTree = ""; }; - A9D4F6971B534F9F00F937AB /* TextShape.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextShape.swift; sourceTree = ""; }; - A9D4F6981B534F9F00F937AB /* Triangle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Triangle.swift; sourceTree = ""; }; - A9D4F6991B534F9F00F937AB /* View+Animation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Animation.swift"; sourceTree = ""; }; - A9D4F69A1B534F9F00F937AB /* View+Border.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Border.swift"; sourceTree = ""; }; - A9D4F69B1B534F9F00F937AB /* View+Shadow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Shadow.swift"; sourceTree = ""; }; - A9D4F69C1B534F9F00F937AB /* View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; - A9D4F69D1B534F9F00F937AB /* ViewAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewAnimation.swift; sourceTree = ""; }; - A9D4F69E1B534F9F00F937AB /* Wedge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Wedge.swift; sourceTree = ""; }; - A9D4F6A01B534F9F00F937AB /* Bloom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bloom.swift; sourceTree = ""; }; - A9D4F6A11B534F9F00F937AB /* Checkerboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checkerboard.swift; sourceTree = ""; }; - A9D4F6A21B534F9F00F937AB /* ColorBurn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorBurn.swift; sourceTree = ""; }; - A9D4F6A31B534F9F00F937AB /* DotScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DotScreen.swift; sourceTree = ""; }; - A9D4F6A41B534F9F00F937AB /* GaussianBlur.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GaussianBlur.swift; sourceTree = ""; }; - A9D4F6A51B534F9F00F937AB /* HueAdjust.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HueAdjust.swift; sourceTree = ""; }; - A9D4F6A61B534F9F00F937AB /* LinearGradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinearGradient.swift; sourceTree = ""; }; - A9D4F6A71B534F9F00F937AB /* Sepia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sepia.swift; sourceTree = ""; }; - A9D4F6A81B534F9F00F937AB /* Sharpen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sharpen.swift; sourceTree = ""; }; - A9D4F6A91B534F9F00F937AB /* Twirl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Twirl.swift; sourceTree = ""; }; - A9D4F6AA1B534F9F00F937AB /* UIGestureRecognizer+Closure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Closure.swift"; sourceTree = ""; }; - A9D4F6AB1B534F9F00F937AB /* UIViewController+C4View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+C4View.swift"; sourceTree = ""; }; - A9D4F6E61B53516400F937AB /* C4.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = C4.h; sourceTree = ""; }; - D917E1351B83E0920084BC82 /* Gradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gradient.swift; sourceTree = ""; }; - D917E1371B83E1DB0084BC82 /* GradientLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientLayer.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 610A6A4A1F928E1B003B841A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 610A6A531F928E1B003B841A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 610A6A571F928E1B003B841A /* C4.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 614F823F19DB5ED3001DF1D4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 614F824B19DB5ED4001DF1D4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A94E3BD419E0E9390039A9C4 /* C4.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; A96F50571B5385A5002B3A46 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A96F507E1B538650002B3A46 /* C4.framework in Frameworks */, + 4A943552259E12020064862F /* TNT in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -358,8 +59,6 @@ 614F823919DB5ED3001DF1D4 = { isa = PBXGroup; children = ( - A9D4F66E1B534F9F00F937AB /* C4 */, - A9596D271A054A960023323D /* Tests */, A96F505B1B5385A5002B3A46 /* C4App */, 614F824419DB5ED3001DF1D4 /* Products */, ); @@ -368,37 +67,11 @@ 614F824419DB5ED3001DF1D4 /* Products */ = { isa = PBXGroup; children = ( - 614F824319DB5ED3001DF1D4 /* C4.framework */, - 614F824E19DB5ED4001DF1D4 /* C4Tests-iOS.xctest */, A96F505A1B5385A5002B3A46 /* C4App.app */, - 610A6A4E1F928E1B003B841A /* C4.framework */, - 610A6A561F928E1B003B841A /* C4Tests-tvOS.xctest */, ); name = Products; sourceTree = ""; }; - A9596D271A054A960023323D /* Tests */ = { - isa = PBXGroup; - children = ( - A9596D401A054B4F0023323D /* ColorTests.swift */, - A9596D411A054B4F0023323D /* MathTests.swift */, - A9596D421A054B4F0023323D /* PointTests.swift */, - 61B92B4B1C7B96B8003BD61C /* RectTests.swift */, - A9596D431A054B4F0023323D /* VectorTests.swift */, - A988A9161A4F57330044007C /* TransformTests.swift */, - A9596D281A054A960023323D /* Supporting Files */, - ); - path = Tests; - sourceTree = SOURCE_ROOT; - }; - A9596D281A054A960023323D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - A9596D291A054A960023323D /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; A96F505B1B5385A5002B3A46 /* C4App */ = { isa = PBXGroup; children = ( @@ -423,205 +96,9 @@ name = "Supporting Files"; sourceTree = ""; }; - A9D4F66E1B534F9F00F937AB /* C4 */ = { - isa = PBXGroup; - children = ( - A9D4F6E61B53516400F937AB /* C4.h */, - A9D4F67C1B534F9F00F937AB /* Info.plist */, - A9D4F6701B534F9F00F937AB /* Core */, - A9D4F67D1B534F9F00F937AB /* UI */, - ); - path = C4; - sourceTree = ""; - }; - A9D4F6701B534F9F00F937AB /* Core */ = { - isa = PBXGroup; - children = ( - A9D4F6711B534F9F00F937AB /* Color.swift */, - A9D4F6721B534F9F00F937AB /* EventSource.swift */, - A9D4F6731B534F9F00F937AB /* Foundation.swift */, - A9D4F6741B534F9F00F937AB /* Math.swift */, - A9D4F6761B534F9F00F937AB /* Path.swift */, - A9D4F6771B534F9F00F937AB /* Point.swift */, - A9D4F6781B534F9F00F937AB /* Rect.swift */, - A9D4F6791B534F9F00F937AB /* Size.swift */, - A9D4F67A1B534F9F00F937AB /* Transform.swift */, - A9D4F67B1B534F9F00F937AB /* Vector.swift */, - ); - path = Core; - sourceTree = ""; - }; - A9D4F67D1B534F9F00F937AB /* UI */ = { - isa = PBXGroup; - children = ( - A9D4F67E1B534F9F00F937AB /* Animation.swift */, - A9D4F6801B534F9F00F937AB /* Arc.swift */, - A9D4F6811B534F9F00F937AB /* AudioPlayer.swift */, - 1F40C6FA1C82ADF0004AE1E7 /* Camera.swift */, - A9D4F6821B534F9F00F937AB /* CanvasController.swift */, - A9D4F6831B534F9F00F937AB /* Circle.swift */, - A9D4F6841B534F9F00F937AB /* Curve.swift */, - A9D4F6851B534F9F00F937AB /* Ellipse.swift */, - A9D4F6861B534F9F00F937AB /* Filter.swift */, - A9D4F6871B534F9F00F937AB /* Font.swift */, - A9D4F6881B534F9F00F937AB /* Generator.swift */, - D917E1351B83E0920084BC82 /* Gradient.swift */, - D917E1371B83E1DB0084BC82 /* GradientLayer.swift */, - A9D4F68C1B534F9F00F937AB /* Image.swift */, - 1FDB8B111DEF8E4B0082B461 /* Image+ColorAt.swift */, - A9D4F6891B534F9F00F937AB /* Image+Crop.swift */, - A9D4F68A1B534F9F00F937AB /* Image+Filter.swift */, - A9D4F68B1B534F9F00F937AB /* Image+Generator.swift */, - 1F754DCA1C48EE600036D39F /* ImageLayer.swift */, - 1FAB35E91C53745300620741 /* Layer.swift */, - A9D4F68D1B534F9F00F937AB /* Line.swift */, - A9D4F68E1B534F9F00F937AB /* Movie.swift */, - 1FAB35E71C5372CE00620741 /* Pixel.swift */, - 61B92B451C7B963B003BD61C /* PlayerLayer.swift */, - A9D4F68F1B534F9F00F937AB /* Polygon.swift */, - A9D4F6901B534F9F00F937AB /* QuadCurve.swift */, - A9D4F6911B534F9F00F937AB /* Rectangle.swift */, - A9D4F6921B534F9F00F937AB /* RegularPolygon.swift */, - 1F75FE941C5CADBF00EB62C2 /* ScreenRecorder.swift */, - A9D4F6941B534F9F00F937AB /* Shape.swift */, - A9D4F6931B534F9F00F937AB /* Shape+Creation.swift */, - A9D4F6951B534F9F00F937AB /* ShapeLayer.swift */, - A9D4F6961B534F9F00F937AB /* Star.swift */, - 61BBAF621BC372BD00A03FD0 /* StoredAnimation.swift */, - A9D4F6971B534F9F00F937AB /* TextShape.swift */, - 61B92B461C7B963B003BD61C /* Timer.swift */, - A9D4F6981B534F9F00F937AB /* Triangle.swift */, - A9D4F6AA1B534F9F00F937AB /* UIGestureRecognizer+Closure.swift */, - A9BC57261C50ABA80068BBA3 /* UIImage+Color.swift */, - 1FAB35EB1C53749500620741 /* UIView+AddRemove.swift */, - A9D4F6AB1B534F9F00F937AB /* UIViewController+C4View.swift */, - A9D4F69C1B534F9F00F937AB /* View.swift */, - A9D4F6991B534F9F00F937AB /* View+Animation.swift */, - A9D4F69A1B534F9F00F937AB /* View+Border.swift */, - 6159A4D51F928B79000A3A99 /* View+Gestures.swift */, - 61BBAF641BC38C1200A03FD0 /* View+KeyValues.swift */, - 61B92B471C7B963B003BD61C /* View+Render.swift */, - A9D4F69B1B534F9F00F937AB /* View+Shadow.swift */, - A9D4F69D1B534F9F00F937AB /* ViewAnimation.swift */, - A9D4F69E1B534F9F00F937AB /* Wedge.swift */, - A9D4F69F1B534F9F00F937AB /* Filters */, - ); - path = UI; - sourceTree = ""; - }; - A9D4F69F1B534F9F00F937AB /* Filters */ = { - isa = PBXGroup; - children = ( - A9D4F6A01B534F9F00F937AB /* Bloom.swift */, - A9D4F6A11B534F9F00F937AB /* Checkerboard.swift */, - A9D4F6A21B534F9F00F937AB /* ColorBurn.swift */, - A9D4F6A31B534F9F00F937AB /* DotScreen.swift */, - A9D4F6A41B534F9F00F937AB /* GaussianBlur.swift */, - A9D4F6A51B534F9F00F937AB /* HueAdjust.swift */, - A9D4F6A61B534F9F00F937AB /* LinearGradient.swift */, - A9D4F6A71B534F9F00F937AB /* Sepia.swift */, - A9D4F6A81B534F9F00F937AB /* Sharpen.swift */, - A9D4F6A91B534F9F00F937AB /* Twirl.swift */, - ); - path = Filters; - sourceTree = ""; - }; /* End PBXGroup section */ -/* Begin PBXHeadersBuildPhase section */ - 610A6A4B1F928E1B003B841A /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 610A6A651F928E3A003B841A /* C4.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 614F824019DB5ED3001DF1D4 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A9D4F6E71B53517400F937AB /* C4.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - /* Begin PBXNativeTarget section */ - 610A6A4D1F928E1B003B841A /* C4-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 610A6A5F1F928E1B003B841A /* Build configuration list for PBXNativeTarget "C4-tvOS" */; - buildPhases = ( - 610A6A491F928E1B003B841A /* Sources */, - 610A6A4A1F928E1B003B841A /* Frameworks */, - 610A6A4B1F928E1B003B841A /* Headers */, - 610A6A4C1F928E1B003B841A /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "C4-tvOS"; - productName = "C4-tvOS"; - productReference = 610A6A4E1F928E1B003B841A /* C4.framework */; - productType = "com.apple.product-type.framework"; - }; - 610A6A551F928E1B003B841A /* C4Tests-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 610A6A621F928E1B003B841A /* Build configuration list for PBXNativeTarget "C4Tests-tvOS" */; - buildPhases = ( - 610A6A521F928E1B003B841A /* Sources */, - 610A6A531F928E1B003B841A /* Frameworks */, - 610A6A541F928E1B003B841A /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 610A6A591F928E1B003B841A /* PBXTargetDependency */, - ); - name = "C4Tests-tvOS"; - productName = "C4-tvOSTests"; - productReference = 610A6A561F928E1B003B841A /* C4Tests-tvOS.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 614F824219DB5ED3001DF1D4 /* C4-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 614F825619DB5ED4001DF1D4 /* Build configuration list for PBXNativeTarget "C4-iOS" */; - buildPhases = ( - 614F823E19DB5ED3001DF1D4 /* Sources */, - 614F823F19DB5ED3001DF1D4 /* Frameworks */, - 614F824019DB5ED3001DF1D4 /* Headers */, - 614F824119DB5ED3001DF1D4 /* Resources */, - 61A979591C533E8A00899F73 /* Swift Lint */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "C4-iOS"; - productName = C4iOS; - productReference = 614F824319DB5ED3001DF1D4 /* C4.framework */; - productType = "com.apple.product-type.framework"; - }; - 614F824D19DB5ED4001DF1D4 /* C4Tests-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 614F825919DB5ED4001DF1D4 /* Build configuration list for PBXNativeTarget "C4Tests-iOS" */; - buildPhases = ( - 614F824A19DB5ED4001DF1D4 /* Sources */, - 614F824B19DB5ED4001DF1D4 /* Frameworks */, - 614F824C19DB5ED4001DF1D4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - A94E3BD319E0E9340039A9C4 /* PBXTargetDependency */, - A96F507D1B5385AF002B3A46 /* PBXTargetDependency */, - ); - name = "C4Tests-iOS"; - productName = C4iOSTests; - productReference = 614F824E19DB5ED4001DF1D4 /* C4Tests-iOS.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; A96F50591B5385A5002B3A46 /* C4App */ = { isa = PBXNativeTarget; buildConfigurationList = A96F50761B5385A5002B3A46 /* Build configuration list for PBXNativeTarget "C4App" */; @@ -634,9 +111,11 @@ buildRules = ( ); dependencies = ( - A96F50811B538650002B3A46 /* PBXTargetDependency */, ); name = C4App; + packageProductDependencies = ( + 4A943551259E12020064862F /* TNT */, + ); productName = C4App; productReference = A96F505A1B5385A5002B3A46 /* C4App.app */; productType = "com.apple.product-type.application"; @@ -649,84 +128,38 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1230; ORGANIZATIONNAME = C4; TargetAttributes = { - 610A6A4D1F928E1B003B841A = { - CreatedOnToolsVersion = 9.0; - ProvisioningStyle = Automatic; - }; - 610A6A551F928E1B003B841A = { - CreatedOnToolsVersion = 9.0; - ProvisioningStyle = Automatic; - }; - 614F824219DB5ED3001DF1D4 = { - CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0900; - }; - 614F824D19DB5ED4001DF1D4 = { - CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0900; - TestTargetID = A96F50591B5385A5002B3A46; - }; A96F50591B5385A5002B3A46 = { CreatedOnToolsVersion = 6.4; DevelopmentTeam = 75C7KVJZ99; - LastSwiftMigration = 0900; + LastSwiftMigration = 1230; }; }; }; buildConfigurationList = 614F823D19DB5ED3001DF1D4 /* Build configuration list for PBXProject "C4" */; compatibilityVersion = "Xcode 8.0"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 614F823919DB5ED3001DF1D4; + packageReferences = ( + 4A943550259E12020064862F /* XCRemoteSwiftPackageReference "TNT-Package" */, + ); productRefGroup = 614F824419DB5ED3001DF1D4 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 614F824219DB5ED3001DF1D4 /* C4-iOS */, - 614F824D19DB5ED4001DF1D4 /* C4Tests-iOS */, - 610A6A4D1F928E1B003B841A /* C4-tvOS */, - 610A6A551F928E1B003B841A /* C4Tests-tvOS */, A96F50591B5385A5002B3A46 /* C4App */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 610A6A4C1F928E1B003B841A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 610A6A541F928E1B003B841A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 614F824119DB5ED3001DF1D4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 614F824C19DB5ED4001DF1D4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; A96F50581B5385A5002B3A46 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -742,202 +175,7 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 61A979591C533E8A00899F73 /* Swift Lint */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Swift Lint"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ - 610A6A491F928E1B003B841A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 610A6A991F928E96003B841A /* View.swift in Sources */, - 610A6A8A1F928E96003B841A /* Rectangle.swift in Sources */, - 610A6A9A1F928E96003B841A /* View+Animation.swift in Sources */, - 610A6A761F928E96003B841A /* Curve.swift in Sources */, - 610A6A7D1F928E96003B841A /* Image.swift in Sources */, - 610A6A8E1F928E96003B841A /* Shape+Creation.swift in Sources */, - 610A6A701F928E96003B841A /* Animation.swift in Sources */, - 610A6AA81F928E9A003B841A /* LinearGradient.swift in Sources */, - 610A6A9E1F928E96003B841A /* View+Render.swift in Sources */, - 610A6A6D1F928E91003B841A /* Size.swift in Sources */, - 610A6A711F928E96003B841A /* Arc.swift in Sources */, - 610A6A921F928E96003B841A /* TextShape.swift in Sources */, - 610A6A6C1F928E91003B841A /* Rect.swift in Sources */, - 610A6A881F928E96003B841A /* Polygon.swift in Sources */, - 610A6A941F928E96003B841A /* Triangle.swift in Sources */, - 610A6A9D1F928E96003B841A /* View+KeyValues.swift in Sources */, - 610A6AA61F928E9A003B841A /* GaussianBlur.swift in Sources */, - 610A6A971F928E96003B841A /* UIView+AddRemove.swift in Sources */, - 610A6A871F928E96003B841A /* PlayerLayer.swift in Sources */, - 610A6AA91F928E9A003B841A /* Sepia.swift in Sources */, - 610A6A851F928E96003B841A /* Movie.swift in Sources */, - 610A6A6F1F928E91003B841A /* Vector.swift in Sources */, - 610A6A821F928E96003B841A /* ImageLayer.swift in Sources */, - 610A6A7C1F928E96003B841A /* GradientLayer.swift in Sources */, - 610A6A8B1F928E96003B841A /* RegularPolygon.swift in Sources */, - 610A6A8C1F928E96003B841A /* ScreenRecorder.swift in Sources */, - 610A6A8D1F928E96003B841A /* Shape.swift in Sources */, - 610A6A771F928E96003B841A /* Ellipse.swift in Sources */, - 610A6A801F928E96003B841A /* Image+Filter.swift in Sources */, - 610A6A8F1F928E96003B841A /* ShapeLayer.swift in Sources */, - 610A6A7E1F928E96003B841A /* Image+ColorAt.swift in Sources */, - 610A6A681F928E91003B841A /* Foundation.swift in Sources */, - 610A6AA41F928E9A003B841A /* ColorBurn.swift in Sources */, - 610A6A9F1F928E96003B841A /* View+Shadow.swift in Sources */, - 610A6A661F928E91003B841A /* Color.swift in Sources */, - 610A6AAB1F928E9A003B841A /* Twirl.swift in Sources */, - 610A6A6B1F928E91003B841A /* Point.swift in Sources */, - 610A6A891F928E96003B841A /* QuadCurve.swift in Sources */, - 610A6A861F928E96003B841A /* Pixel.swift in Sources */, - 610A6A901F928E96003B841A /* Star.swift in Sources */, - 610A6A671F928E91003B841A /* EventSource.swift in Sources */, - 610A6AAA1F928E9A003B841A /* Sharpen.swift in Sources */, - 610A6AA01F928E96003B841A /* ViewAnimation.swift in Sources */, - 610A6A9B1F928E96003B841A /* View+Border.swift in Sources */, - 610A6AA21F928E9A003B841A /* Bloom.swift in Sources */, - 610A6A751F928E96003B841A /* Circle.swift in Sources */, - 610A6A911F928E96003B841A /* StoredAnimation.swift in Sources */, - 610A6A791F928E96003B841A /* Font.swift in Sources */, - 610A6AA11F928E96003B841A /* Wedge.swift in Sources */, - 610A6AA31F928E9A003B841A /* Checkerboard.swift in Sources */, - 610A6A6A1F928E91003B841A /* Path.swift in Sources */, - 610A6A781F928E96003B841A /* Filter.swift in Sources */, - 610A6A6E1F928E91003B841A /* Transform.swift in Sources */, - 610A6AA51F928E9A003B841A /* DotScreen.swift in Sources */, - 610A6A841F928E96003B841A /* Line.swift in Sources */, - 610A6AA71F928E9A003B841A /* HueAdjust.swift in Sources */, - 610A6A741F928E96003B841A /* CanvasController.swift in Sources */, - 610A6A811F928E96003B841A /* Image+Generator.swift in Sources */, - 610A6A721F928E96003B841A /* AudioPlayer.swift in Sources */, - 610A6A7F1F928E96003B841A /* Image+Crop.swift in Sources */, - 610A6A7A1F928E96003B841A /* Generator.swift in Sources */, - 610A6A981F928E96003B841A /* UIViewController+C4View.swift in Sources */, - 610A6A7B1F928E96003B841A /* Gradient.swift in Sources */, - 610A6A831F928E96003B841A /* Layer.swift in Sources */, - 610A6A691F928E91003B841A /* Math.swift in Sources */, - 610A6A961F928E96003B841A /* UIImage+Color.swift in Sources */, - 610A6A931F928E96003B841A /* Timer.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 610A6A521F928E1B003B841A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 610A6AB11F928EAC003B841A /* TransformTests.swift in Sources */, - 610A6AAE1F928EAC003B841A /* PointTests.swift in Sources */, - 610A6AAC1F928EAC003B841A /* ColorTests.swift in Sources */, - 610A6AAD1F928EAC003B841A /* MathTests.swift in Sources */, - 610A6AAF1F928EAC003B841A /* RectTests.swift in Sources */, - 610A6AB01F928EAC003B841A /* VectorTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 614F823E19DB5ED3001DF1D4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 1F754DCB1C48EE600036D39F /* ImageLayer.swift in Sources */, - A9D4F6C81B534F9F00F937AB /* Line.swift in Sources */, - A9D4F6B01B534F9F00F937AB /* Math.swift in Sources */, - A9D4F6CD1B534F9F00F937AB /* RegularPolygon.swift in Sources */, - 61B92B4A1C7B963B003BD61C /* View+Render.swift in Sources */, - A9D4F6B51B534F9F00F937AB /* Size.swift in Sources */, - A9D4F6C91B534F9F00F937AB /* Movie.swift in Sources */, - D917E1361B83E0920084BC82 /* Gradient.swift in Sources */, - 61BBAF631BC372BD00A03FD0 /* StoredAnimation.swift in Sources */, - A9D4F6C11B534F9F00F937AB /* Filter.swift in Sources */, - 61B92B491C7B963B003BD61C /* Timer.swift in Sources */, - A9D4F6DA1B534F9F00F937AB /* Bloom.swift in Sources */, - A9D4F6BE1B534F9F00F937AB /* Circle.swift in Sources */, - A9D4F6CA1B534F9F00F937AB /* Polygon.swift in Sources */, - 1FAB35EC1C53749500620741 /* UIView+AddRemove.swift in Sources */, - A9D4F6E21B534F9F00F937AB /* Sharpen.swift in Sources */, - 1FDB8B121DEF8E4B0082B461 /* Image+ColorAt.swift in Sources */, - A9D4F6E51B534F9F00F937AB /* UIViewController+C4View.swift in Sources */, - A9D4F6C21B534F9F00F937AB /* Font.swift in Sources */, - A9D4F6DF1B534F9F00F937AB /* HueAdjust.swift in Sources */, - A9D4F6DD1B534F9F00F937AB /* DotScreen.swift in Sources */, - A9D4F6E41B534F9F00F937AB /* UIGestureRecognizer+Closure.swift in Sources */, - A9D4F6D51B534F9F00F937AB /* View+Border.swift in Sources */, - A9D4F6E01B534F9F00F937AB /* LinearGradient.swift in Sources */, - A9D4F6B31B534F9F00F937AB /* Point.swift in Sources */, - A9D4F6D21B534F9F00F937AB /* TextShape.swift in Sources */, - A9D4F6D81B534F9F00F937AB /* ViewAnimation.swift in Sources */, - A9D4F6D01B534F9F00F937AB /* ShapeLayer.swift in Sources */, - A9D4F6B91B534F9F00F937AB /* Animation.swift in Sources */, - A9D4F6CE1B534F9F00F937AB /* Shape+Creation.swift in Sources */, - A9D4F6B61B534F9F00F937AB /* Transform.swift in Sources */, - D917E1381B83E1DB0084BC82 /* GradientLayer.swift in Sources */, - 1FAB35EA1C53745300620741 /* Layer.swift in Sources */, - A9D4F6B71B534F9F00F937AB /* Vector.swift in Sources */, - A9D4F6C61B534F9F00F937AB /* Image+Generator.swift in Sources */, - A9D4F6E31B534F9F00F937AB /* Twirl.swift in Sources */, - 6159A4D61F928B79000A3A99 /* View+Gestures.swift in Sources */, - A9BC57271C50ABA80068BBA3 /* UIImage+Color.swift in Sources */, - 1F40C6FB1C82ADF0004AE1E7 /* Camera.swift in Sources */, - 61BBAF651BC38C1200A03FD0 /* View+KeyValues.swift in Sources */, - A9D4F6AF1B534F9F00F937AB /* Foundation.swift in Sources */, - A9D4F6D41B534F9F00F937AB /* View+Animation.swift in Sources */, - A9D4F6CB1B534F9F00F937AB /* QuadCurve.swift in Sources */, - A9D4F6D11B534F9F00F937AB /* Star.swift in Sources */, - A9D4F6C31B534F9F00F937AB /* Generator.swift in Sources */, - A9D4F6CF1B534F9F00F937AB /* Shape.swift in Sources */, - A9D4F6CC1B534F9F00F937AB /* Rectangle.swift in Sources */, - A9D4F6B41B534F9F00F937AB /* Rect.swift in Sources */, - A9D4F6E11B534F9F00F937AB /* Sepia.swift in Sources */, - A9D4F6BD1B534F9F00F937AB /* CanvasController.swift in Sources */, - A9D4F6DE1B534F9F00F937AB /* GaussianBlur.swift in Sources */, - 61B92B481C7B963B003BD61C /* PlayerLayer.swift in Sources */, - A9D4F6DC1B534F9F00F937AB /* ColorBurn.swift in Sources */, - A9D4F6D61B534F9F00F937AB /* View+Shadow.swift in Sources */, - A9D4F6C71B534F9F00F937AB /* Image.swift in Sources */, - A9D4F6D31B534F9F00F937AB /* Triangle.swift in Sources */, - 1FAB35E81C5372CE00620741 /* Pixel.swift in Sources */, - A9D4F6AE1B534F9F00F937AB /* EventSource.swift in Sources */, - A9D4F6C41B534F9F00F937AB /* Image+Crop.swift in Sources */, - A9D4F6AD1B534F9F00F937AB /* Color.swift in Sources */, - A9D4F6C51B534F9F00F937AB /* Image+Filter.swift in Sources */, - A9D4F6BF1B534F9F00F937AB /* Curve.swift in Sources */, - A9D4F6BC1B534F9F00F937AB /* AudioPlayer.swift in Sources */, - A9D4F6B21B534F9F00F937AB /* Path.swift in Sources */, - A9D4F6D71B534F9F00F937AB /* View.swift in Sources */, - A9D4F6C01B534F9F00F937AB /* Ellipse.swift in Sources */, - A9D4F6DB1B534F9F00F937AB /* Checkerboard.swift in Sources */, - A9D4F6BB1B534F9F00F937AB /* Arc.swift in Sources */, - A9D4F6D91B534F9F00F937AB /* Wedge.swift in Sources */, - 1F75FE951C5CADBF00EB62C2 /* ScreenRecorder.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 614F824A19DB5ED4001DF1D4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A96F4F4A1B538330002B3A46 /* TransformTests.swift in Sources */, - A96F4F491B538330002B3A46 /* VectorTests.swift in Sources */, - A96F4F461B538330002B3A46 /* MathTests.swift in Sources */, - A96F4F451B538330002B3A46 /* ColorTests.swift in Sources */, - A96F4F471B538330002B3A46 /* PointTests.swift in Sources */, - 61B92B4C1C7B96B8003BD61C /* RectTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; A96F50561B5385A5002B3A46 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -949,29 +187,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 610A6A591F928E1B003B841A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 610A6A4D1F928E1B003B841A /* C4-tvOS */; - targetProxy = 610A6A581F928E1B003B841A /* PBXContainerItemProxy */; - }; - A94E3BD319E0E9340039A9C4 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 614F824219DB5ED3001DF1D4 /* C4-iOS */; - targetProxy = A94E3BD219E0E9340039A9C4 /* PBXContainerItemProxy */; - }; - A96F507D1B5385AF002B3A46 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = A96F50591B5385A5002B3A46 /* C4App */; - targetProxy = A96F507C1B5385AF002B3A46 /* PBXContainerItemProxy */; - }; - A96F50811B538650002B3A46 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 614F824219DB5ED3001DF1D4 /* C4-iOS */; - targetProxy = A96F50801B538650002B3A46 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ A96F50621B5385A5002B3A46 /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -992,116 +207,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 610A6A601F928E1B003B841A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = dwarf; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = C4/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.C4-tvOS"; - PRODUCT_NAME = C4; - SDKROOT = appletvos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Debug; - }; - 610A6A611F928E1B003B841A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = C4/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.C4-tvOS"; - PRODUCT_NAME = C4; - SDKROOT = appletvos; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Release; - }; - 610A6A631F928E1B003B841A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.C4-tvOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Debug; - }; - 610A6A641F928E1B003B841A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.C4-tvOSTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; - }; - name = Release; - }; 614F825419DB5ED4001DF1D4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1124,6 +229,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1149,7 +255,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -1184,6 +290,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1202,7 +309,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_SWIFT3_OBJC_INFERENCE = Off; @@ -1214,71 +321,6 @@ }; name = Release; }; - 614F825719DB5ED4001DF1D4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = C4/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = C4; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 614F825819DB5ED4001DF1D4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = C4/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = C4; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - }; - name = Release; - }; - 614F825A19DB5ED4001DF1D4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/C4App.app/C4App"; - }; - name = Debug; - }; - 614F825B19DB5ED4001DF1D4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/C4App.app/C4App"; - }; - name = Release; - }; A96F50771B5385A5002B3A46 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1290,9 +332,13 @@ "$(inherited)", ); INFOPLIST_FILE = C4App/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1304,34 +350,21 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 75C7KVJZ99; INFOPLIST_FILE = C4App/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.c4ios.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 610A6A5F1F928E1B003B841A /* Build configuration list for PBXNativeTarget "C4-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 610A6A601F928E1B003B841A /* Debug */, - 610A6A611F928E1B003B841A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 610A6A621F928E1B003B841A /* Build configuration list for PBXNativeTarget "C4Tests-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 610A6A631F928E1B003B841A /* Debug */, - 610A6A641F928E1B003B841A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 614F823D19DB5ED3001DF1D4 /* Build configuration list for PBXProject "C4" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1341,24 +374,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 614F825619DB5ED4001DF1D4 /* Build configuration list for PBXNativeTarget "C4-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 614F825719DB5ED4001DF1D4 /* Debug */, - 614F825819DB5ED4001DF1D4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 614F825919DB5ED4001DF1D4 /* Build configuration list for PBXNativeTarget "C4Tests-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 614F825A19DB5ED4001DF1D4 /* Debug */, - 614F825B19DB5ED4001DF1D4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; A96F50761B5385A5002B3A46 /* Build configuration list for PBXNativeTarget "C4App" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1369,6 +384,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 4A943550259E12020064862F /* XCRemoteSwiftPackageReference "TNT-Package" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/dotmain/TNT-Package.git"; + requirement = { + branch = main; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4A943551259E12020064862F /* TNT */ = { + isa = XCSwiftPackageProductDependency; + package = 4A943550259E12020064862F /* XCRemoteSwiftPackageReference "TNT-Package" */; + productName = TNT; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 614F823A19DB5ED3001DF1D4 /* Project object */; } diff --git a/C4.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/C4.xcodeproj/project.xcworkspace/contents.xcworkspacedata index e26710c0..919434a6 100644 --- a/C4.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/C4.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/C4.xcodeproj/xcshareddata/xcschemes/C4-iOS.xcscheme b/C4.xcodeproj/xcshareddata/xcschemes/C4-iOS.xcscheme index 83e9f074..5f60ad81 100644 --- a/C4.xcodeproj/xcshareddata/xcschemes/C4-iOS.xcscheme +++ b/C4.xcodeproj/xcshareddata/xcschemes/C4-iOS.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -39,17 +48,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/C4/C4.h b/C4/C4.h deleted file mode 100644 index c0573a46..00000000 --- a/C4/C4.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -#import -#import - -//! Project version number for C4. -FOUNDATION_EXPORT double C4VersionNumber; - -//! Project version string for C4. -FOUNDATION_EXPORT const unsigned char C4VersionString[]; - diff --git a/C4/Core/Color.swift b/C4/Core/Color.swift deleted file mode 100644 index 6439c1ea..00000000 --- a/C4/Core/Color.swift +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit -import CoreGraphics - -/// A Color object whose RGB value is 0, 0, 0 and whose alpha value is 1.0. -public let black = Color(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0) -/// A Color object whose RGB value is 0.33, 0.33, 0.33 and whose alpha value is 1.0. -public let darkGray = Color(red: 1.0/3.0, green: 1.0/3.0, blue: 1.0/3.0, alpha: 1.0) -/// A Color object whose RGB value is 0.66, 0.66, 0.66 and whose alpha value is 1.0. -public let lightGray = Color(red: 2.0/3.0, green: 2.0/3.0, blue: 2.0/3.0, alpha: 1.0) -/// A Color object whose RGB value is 1.0, 1.0, 1.0 and whose alpha value is 1.0. -public let white = Color(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) -/// A Color object whose RGB value is 0.5, 0.5, 0.5 and whose alpha value is 1.0. -public let gray = Color(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) -/// A Color object whose RGB value is 1.0, 0.0, 0.0 and whose alpha value is 1.0. -public let red = Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) -/// A Color object whose RGB value is 0.0, 1.0, 0.0 and whose alpha value is 1.0. -public let green = Color(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0) -/// A Color object whose RGB value is 0.0, 0.0, 1.0 and whose alpha value is 1.0. -public let blue = Color(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0) -/// A Color object whose RGB value is 0.0, 1.0, 1.0 and whose alpha value is 1.0. -public let cyan = Color(red: 0.0, green: 1.0, blue: 1.0, alpha: 1.0) -/// A Color object whose RGB value is 1.0, 1.0, 0.0 and whose alpha value is 1.0. -public let yellow = Color(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0) -/// A Color object whose RGB value is 1.0, 0.0, 1.0 and whose alpha value is 1.0. -public let magenta = Color(red: 1.0, green: 0.0, blue: 1.0, alpha: 1.0) -/// A Color object whose RGB value is 1.0, 0.5, 0.0 and whose alpha value is 1.0. -public let orange = Color(red: 1.0, green: 0.5, blue: 0.0, alpha: 1.0) -/// A Color object whose RGB value is 0.5, 0.0, 0.5 and whose alpha value is 1.0. -public let purple = Color(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0) -/// A Color object whose RGB value is 0.6, 0.4, 0.2 and whose alpha value is 1.0. -public let brown = Color(red: 0.6, green: 0.4, blue: 0.2, alpha: 1.0) -/// A Color object whose RGB value is 0.0, 0.0, 0.0 and whose alpha value is 0.0. -public let clear = Color(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0) - -///A Color object whose RGB value is 1.0, 0.0, 0.475 and whose alpha value is 1.0. -public let C4Pink = Color(red: 1.0, green: 0.0, blue: 0.475, alpha: 1.0) -///A Color object whose RGB value is 0.098, 0.271, 1.0 and whose alpha value is 1.0. -public let C4Blue = Color(red: 0.098, green: 0.271, blue: 1.0, alpha: 1.0) -///A Color object whose RGB value is 0.0, 0.0, 0.541 and whose alpha value is 1.0. -public let C4Purple = Color(red: 0.0, green: 0.0, blue: 0.541, alpha: 1.0) -///A Color object whose RGB value is 0.98, 0.98, 0.98 and whose alpha value is 1.0. -public let C4Grey = Color(red: 0.98, green: 0.98, blue: 0.98, alpha: 1.0) - -/// This document describes the Color object which represents color and sometimes opacity (alpha value). You can use Color -/// objects to store color data, and pass them between various C4 objects such as Shape, Image, etc. -/// -/// Color internally wraps a CGColorSpaceRef called colorSpace, as well as a CGColorRef. From these two objects Color is able to -/// properly maintain color data and convert it to / from other color objects such as UIColor, CIColor, Color, etc. -public class Color { - internal var colorSpace: CGColorSpace - internal var internalColor: CGColor - - /// Initializes and returns a new color object. Defaults to black with 0 opacity (i.e. clear). - /// ```` - /// let c = Color() - /// ```` - public init() { - colorSpace = CGColorSpaceCreateDeviceRGB() - internalColor = CGColor(colorSpace: colorSpace, components: [0, 0, 0, 0])! - } - - /// Initializes and returns a new Color object based on specified color values. - /// ```` - /// let c = Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) - /// ```` - /// - parameter red: The red value for the new color [0.0 ... 1.0] - /// - parameter green: The green value for the new color [0.0 ... 1.0] - /// - parameter blue: The blue value for the new color [0.0 ... 1.0] - /// - parameter alpha: The alpha value for the new color [0.0 ... 1.0] - public init(red: Double, green: Double, blue: Double, alpha: Double) { - colorSpace = CGColorSpaceCreateDeviceRGB() - internalColor = CGColor(colorSpace: colorSpace, components: [CGFloat(red), CGFloat(green), CGFloat(blue), CGFloat(alpha)])! - } - - /// Initializes and returns a new Color object based on specified color values. - /// ```` - /// let c = Color(hue: 1.0, saturation: 0.0, brightness: 0.0, alpha: 1.0) - /// ```` - /// - parameter red: The red value for the new color [0.0 ... 1.0] - /// - parameter green: The green value for the new color [0.0 ... 1.0] - /// - parameter blue: The blue value for the new color [0.0 ... 1.0] - /// - parameter alpha: The alpha value for the new color [0.0 ... 1.0] - public init(hue: Double, saturation: Double, brightness: Double, alpha: Double) { - let color = UIColor(hue: CGFloat(hue), saturation: CGFloat(saturation), brightness: CGFloat(brightness), alpha: CGFloat(alpha)) - let floatComponents = color.cgColor.components - colorSpace = CGColorSpaceCreateDeviceRGB() - internalColor = CGColor(colorSpace: colorSpace, components: floatComponents!)! - } - - /// Initializes and returns a new Color object based on a provided CGColor object. - /// ```` - /// let c = Color(UIColor.redColor().CGColor) - /// ```` - /// - parameter color: A CGColor object that will be used to create a new Color. - public init(_ color: CoreGraphics.CGColor) { - colorSpace = CGColorSpaceCreateDeviceRGB() - internalColor = color - } - - /// Initializes and returns a new Color object based on a provided UIColor object. - /// ```` - /// let c = Color(UIColor.redColor()) - /// ```` - /// - parameter color: A UIColor object whose components will be extrated to create a new Color. - public convenience init(_ color: UIColor) { - self.init(color.cgColor) - } - - /// Initializes and returns a new Color object made up of a repeating pattern based on a specified Image. - /// ```` - /// let p = Color("pattern") - /// ```` - /// - parameter pattern: a String, the name of an image to use as a pattern. - public convenience init(_ pattern: String) { - self.init(UIColor(patternImage: UIImage(named: pattern)!)) - } - - /// Initializes and returns a new Color object made from an Image. - /// ```` - /// let pattern = Image("pattern1") - /// let p = Color(pattern) - /// ```` - /// - parameter patternImage: an Image. - public convenience init(_ patternImage: Image) { - self.init(UIColor(patternImage: patternImage.uiimage)) - } - - /// Initializes and returns a new Color object based on specified color values. - /// ```` - /// let c = Color(red: 255, green: 0, blue: 0, alpha: 255) - /// ```` - /// - parameter red: The red value for the new color [0 ... 255] - /// - parameter green: The green value for the new color [0 ... 255] - /// - parameter blue: The blue value for the new color [0 ... 255] - /// - parameter alpha: The alpha value for the new color [0 ... 255] - public convenience init(red: Int, green: Int, blue: Int, alpha: Double) { - self.init(red: Double(red) / 255.0, green: Double(green) / 255.0, blue: Double(blue) / 255.0, alpha: alpha) - } - - /// Initializes and returns a new Color object based on a specified hex value. - /// Remember to precede with `0x` and include the alpha component at the end (i.e. 7th + 8th characters) - /// ```` - /// let c = Color(0xFF0000FF) - /// ```` - /// - parameter hexValue: A color value expressed in hexadecimal. - public convenience init(_ hexValue: UInt32) { - - let mask = 0x000000FF - let red = Int(hexValue >> 16) & mask - let green = Int(hexValue >> 8) & mask - let blue = Int(hexValue) & mask - - self.init(red: red, green: green, blue: blue, alpha: 1.0) - } - - /// The set of 3 color values + alpha that define the current color. - /// - returns: An array of 4 Double values in the range [0.0 ... 1.0] - public var components: [Double] { - get { - guard let floatComponents = internalColor.components else { - return [0, 0, 0, 0] - } - return [ - Double(floatComponents[0]), - Double(floatComponents[1]), - Double(floatComponents[2]), - Double(floatComponents[3]) - ] - } - set { - let floatComponents = [ - CGFloat(newValue[0]), - CGFloat(newValue[1]), - CGFloat(newValue[2]), - CGFloat(newValue[3]), - ] - internalColor = CoreGraphics.CGColor(colorSpace: colorSpace, components: floatComponents)! - } - } - - /// The value of the red component of the current color, [0.0 ... 1.0] - /// ```` - /// let c = Color() - /// let redVal = c.red - /// ```` - /// - returns: Double value in the range [0.0 ... 1.0] - public var red: Double { - get { - return components[0] - } - set { - components[0] = newValue - } - } - - /// The value of the green component of the current color - /// ```` - /// let c = Color() - /// let greenVal = c.green - /// ```` - /// - returns: Double value in the range [0.0 ... 1.0] - public var green: Double { - get { - return components[1] - } - set { - components[1] = newValue - } - } - - /// The value of the blue component of the current color - /// ```` - /// let c = Color() - /// let blueVal = c.blue - /// ```` - /// - returns: Double value in the range [0.0 ... 1.0] - public var blue: Double { - get { - return components[2] - } - set { - components[2] = newValue - } - } - - /// The value of the alpha component of the current color. - /// ```` - /// let c = Color() - /// let alphaVal = c.alpha - /// ```` - /// - returns: Double value in the range [0.0 ... 1.0] - public var alpha: Double { - get { - return components[3] - } - set { - components[3] = newValue - } - } - - /// The value of the hue component of the current color. - /// ```` - /// let c = Color() - /// let hue = c.hue - /// ```` - /// - returns: Double value in the range [0.0 ... 1.0] - public var hue: Double { - let r = components[0] - let g = components[1] - let b = components[2] - - let _min = min(r, min(g, b)) - let _max = max(r, max(g, b)) - - if _min == _max { - return 0.0 - } else { - let d = red == _min ? green-blue : ( blue == _min ? red-green : blue-red) - let h = red == _min ? 3.0 : (blue == _min ? 1.0 :5.0) - return (h - d / (_max - _min)) / 6.0 - } - } - - /// The value of the saturation component of the current color. - /// ```` - /// let c = Color() - /// let saturation = c.saturation - /// ```` - /// - returns: Double value in the range [0.0 ... 1.0] - public var saturation: Double { - let r = components[0] - let g = components[1] - let b = components[2] - - let _min = min(r, g, b) - let _max = max(r, g, b) - - return _max == 0 ? 0 : (_max - _min)/_max - } - - /// The value of the brightness component of the current color. - /// ```` - /// let c = Color() - /// let brightness = c.brightness - /// ```` - /// - returns: Double value in the range [0.0 ... 1.0] - public var brightness: Double { - let r = components[0] - let g = components[1] - let b = components[2] - - return max(r, max(g, b)) - } - - /// A CGColor representation of the current color. - /// ```` - /// let c = Color() - /// let cg = c.CGColor - /// ```` - /// - returns: CGColorRef object that matches the color's `internalColor` property - public var cgColor: CGColor { - return internalColor - } - - /// Creates and returns a color object that has the same color space and component values as the receiver, but has the specified alpha component. - /// ```` - /// let c = aColor.colorWithAlpha(0.2) - /// ```` - /// - parameter alpha: The opacity value of the new UIColor object. - /// - returns: A new color with a modified alpha component. - public func colorWithAlpha(_ alpha: Double) -> Color { - return Color(red: red, green: green, blue: blue, alpha: alpha) - } -} - -// MARK: - Casting to UIColor and CIColor - -public extension UIColor { - /// Initializes a UIColor object from a Color object. - /// - parameter color: The C4 color object. - public convenience init?(_ color: Color) { - self.init(cgColor: color.cgColor) - } -} - -public extension CIColor { - /// Initializes a CIColor object from a Color object. - /// - parameter color: The C4 color object. - public convenience init(_ color: Color) { - self.init(cgColor: color.cgColor) - } -} diff --git a/C4/Core/EventSource.swift b/C4/Core/EventSource.swift deleted file mode 100644 index 406e242a..00000000 --- a/C4/Core/EventSource.swift +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation - -///This protocol defines 3 required methods for objects to post and listen for notifications, as well as cancel. -public protocol EventSource { - - /// Posts a new notification originating from the receiver. - /// - /// - parameter event: The name of the event to post. - func post(_ event: NSNotification.Name) - - /// Register an action to run when an event is triggered. Returns an observer handle you can use to cancel the action. - /// - /// - parameter notificationName: The notification name to listen for - /// - parameter executionBlock: A block of code to run when the receiver "hears" the specified notification name - @discardableResult - func on(event notificationName: NSNotification.Name, run: @escaping () -> Void) -> Any - - /// Register an action to run when an event is triggered by the specified sender. Returns an observer handle you can use to cancel the action. - /// - /// - parameter notificationName: The notification name to listen for - /// - parameter sender: The object from which to listen for the notification - /// - parameter executionBlock: A block of code to run when the receiver "hears" the specified notification name - @discardableResult - func on(event notificationName: NSNotification.Name, from sender: Any?, run executionBlock: @escaping () -> Void) -> Any - - /// Cancel a previously registered action from an observer handle. - func cancel(_ observer: Any) -} - -/// This extension allows any NSObject to post and listen for events in the same way as C4 objects. -extension NSObject: EventSource { - - /// Posts a new notification originating from the receiver. - /// - /// ```` - /// canvas.addTapGestureRecognizer { location, state in - /// self.canvas.post("tapped") - /// } - /// ```` - /// - /// - parameter event: The notification name for the event - public func post(_ event: NSNotification.Name) { - NotificationCenter.default.post(name: event, object: self) - } - - /// An action to run on receipt of a given event. - /// - /// ```` - /// canvas.on(event: "tapped") { - /// println("received tap") - /// } - /// ```` - /// - /// - parameter event: The notification name to listen for - /// - parameter run: A block of code to run when the receiver "hears" the specified event name - /// - returns: A token to use for cancelling the action. - @discardableResult - public func on(event notificationName: NSNotification.Name, run executionBlock: @escaping () -> Void) -> Any { - return on(event: notificationName, from: nil, run: executionBlock) - } - - /// Register an action to run when an event is triggered by the specified sender. Returns an observer handle you can use to cancel the action. - /// - /// ```` - /// canvas.on(event: "tapped", from: anObject) { - /// print("obj was tapped") - /// } - /// ```` - /// - /// - parameter notificationName: The notification name to listen for - /// - parameter sender: The object from which to listen for the notification - /// - parameter executionBlock: A block of code to run when the receiver "hears" the specified notification name - /// - returns: A token to use for cancelling the action. - @discardableResult - public func on(event notificationName: NSNotification.Name, from sender: Any?, run executionBlock: @escaping () -> Void) -> Any { - let nc = NotificationCenter.default - let objectProtocol = nc.addObserver(forName: notificationName, object: sender, queue: OperationQueue.current, using: { _ in - executionBlock() - }) - return objectProtocol - } - - /// Cancels any actions registered to run for a specified object. - /// - /// ```` - /// canvas.cancel(self) - /// ```` - /// - /// - parameter token: A token returned from a call to `on(event:run:)` method - public func cancel(_ token: Any) { - let nc = NotificationCenter.default - nc.removeObserver(token) - } -} diff --git a/C4/Core/Foundation.swift b/C4/Core/Foundation.swift deleted file mode 100644 index 5ba75a92..00000000 --- a/C4/Core/Foundation.swift +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreGraphics - -/// Prints a string to the console. Replacement for the noisy NSlog. -/// -/// ```` -/// C4Log("A message") -/// C4Log(0) -/// ```` -/// -/// - parameter value: An object to print to the console -public func C4Log(_ value: T) { - print("[C4Log] \(value)") -} - -/// Returns a rectangle that contains all of the specified coordinates in an array. -/// -/// ```` -/// let points = [CGPointZero,CGPointMake(10,10)] -/// let cgrect = CGRectMakeFromPoints(points) -/// ```` -/// -/// - parameter points: An array of CGPoint coordinates -/// - returns: The smallest CGRect that contains all of the points in the specified array -public func CGRectMakeFromPoints(_ points: [CGPoint]) -> CGRect { - let path = CGMutablePath() - path.move(to: points[0]) - for i in 1.. Void) { - DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + seconds, execute: action) -} diff --git a/C4/Core/Math.swift b/C4/Core/Math.swift deleted file mode 100644 index e5bc7761..00000000 --- a/C4/Core/Math.swift +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation - -/// Clamp a value to the range [min, max]. -/// -/// If the value is less than min this function returns min, if the value is greater than max the function returns max, -/// otherwise it returns the value. -/// -/// ```` -/// clamp(10, 0, 5) = 5 -/// clamp(10, 0, 20) = 10 -/// clamp(10,20,30) = 20 -/// ```` -/// -/// - parameter val: The value -/// - parameter min: The lower bound -/// - parameter max: The upper bound -/// -/// - returns: The clamped value -public func clamp(_ val: T, min: T, max: T) -> T { - assert(min < max, "min has to be less than max") - if val < min { return min } - if val > max { return max } - return val -} - -/// Linear interpolation. For any two values a and b return a linear interpolation with parameter `param`. -/// -/// ```` -/// lerp(0, 100, 0.5) = 50 -/// lerp(100, 200, 0.5) = 150 -/// lerp(500, 1000, 0.33) = 665 -/// ```` -/// -/// - parameter a: first value -/// - parameter b: second value -/// - parameter param: parameter between 0 and 1 for interpolation -/// -/// - returns: The interpolated value -public func lerp(_ a: T, _ b: T, at: T) -> T { - return a + (b - a) * at -} - -/// Linear mapping. Maps a value in the source range [min, max) to a value in the target range [toMin, toMax) using linear interpolation. -/// -/// ```` -/// map(10, 0..<20, 0..<200) = 100 -/// map(10, 0..<100, 200..<300) = 210 -/// map(10, 0..<20, 200..<300) = 250 -/// ```` -/// -/// - parameter val: Source value -/// - parameter min: Source range lower bound -/// - parameter max: Source range upper bound -/// - parameter toMin: Target range lower bound -/// - parameter toMax: Target range upper bound -/// -/// - returns: The mapped value. -public func map(_ val: T, from: Range, to: Range) -> T { - let param = (val - from.lowerBound)/(from.upperBound - from.lowerBound) - return lerp(to.lowerBound, to.upperBound, at: param) -} - -/// Linear mapping. Maps a value in the source range [min, max] to a value in the target range [toMin, toMax] using linear interpolation. -/// -/// ```` -/// map(10, 0...20, 0...200) = 100 -/// map(10, 0...100, 200...300) = 210 -/// map(10, 0...20, 200...300) = 250 -/// ```` -/// -/// - parameter val: Source value -/// - parameter min: Source range lower bound -/// - parameter max: Source range upper bound -/// - parameter toMin: Target range lower bound -/// - parameter toMax: Target range upper bound -/// -/// - returns: The mapped value. -public func map(_ val: T, from: ClosedRange, to: ClosedRange) -> T { - let param = (val - from.lowerBound) / (from.upperBound - from.lowerBound) - return lerp(to.lowerBound, to.upperBound, at: param) -} - -/// Returns a random `Int`. -/// -/// ```` -/// let x = random() -/// ```` -/// -/// - returns: Random `Int`. -public func random() -> Int { - var r = 0 - withUnsafeMutableBytes(of: &r) { bufferPointer in - arc4random_buf(bufferPointer.baseAddress, MemoryLayout.size) - } - return r -} - -/// Return a random integer below `below` -/// -/// ```` -/// let x = random(below: 20) -/// ```` -/// -/// - parameter below: The upper bound -/// -/// - returns: A random value in the range `0 ..< below` -public func random(below: Int) -> Int { - return abs(random()) % below -} - -/// Return a random integer in the given range. -/// -/// ```` -/// let x = random(in: 10..<20) -/// ```` -/// -/// - parameter range: range of values -/// -/// - returns: A random value greater than or equal to min and less than max. -public func random(in range: Range) -> Int { - return range.lowerBound + random(below: range.upperBound - range.lowerBound) -} - -/// Return a random Double in the given range. -/// -/// ```` -/// let x = random(in: 0..<1) -/// ```` -/// -/// - parameter range: range of values -/// - returns: A random Double uniformly distributed between 0 and 1 -public func random(in range: Range) -> Double { - let intRange: Range = Double(-Int.max) ..< Double(Int.max) + 1 - let r = Double(random()) - return map(r, from: intRange, to: range) -} - -/// Converts radian values to degrees. -/// -/// Uses the following equation: value * 180.0 / PI -/// -/// ```` -/// radToDeg(Double.pi) = 180 -/// radToDeg(Double.pi / 2.0) = 90 -/// ```` -/// -/// - parameter val: The value in radians. -/// - returns: A double value representation of the radian value in degrees. -public func radToDeg(_ val: Double) -> Double { - return 180.0 * val / Double.pi -} - -/// Converts degree values to radians. -/// -/// Uses the following equation: value * PI / 180.0 -/// -/// ```` -/// degToRad(270) = 3*Double.pi / 2.0 (4.712...) -/// degToRad(360) = 2*PI (6.283...) -/// ```` -/// -/// - parameter val: The value in degrees. -/// - returns: A double value representation of the degree value in radians. -public func degToRad(_ val: Double) -> Double { - return Double.pi * val / 180.0 -} diff --git a/C4/Core/Path.swift b/C4/Core/Path.swift deleted file mode 100644 index d94c75be..00000000 --- a/C4/Core/Path.swift +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore - -/// Rules for determining the region of a path that gets filled with color. -public enum FillRule { - - /// Specifies the non-zero winding rule. Count each left-to-right path as +1 and each right-to-left path as -1. If the - /// sum of all crossings is 0, the point is outside the path. If the sum is nonzero, the point is inside the path and - /// the region containing it is filled. - case nonZero - - /// Specifies the even-odd winding rule. Count the total number of path crossings. If the number of crossings is even, - /// the point is outside the path. If the number of crossings is odd, the point is inside the path and the region - /// containing it should be filled. - case evenOdd -} - -/// A Path is a sequence of geometric segments which can be straight lines or curves. -@IBDesignable -public class Path: Equatable { - internal var internalPath: CGMutablePath = CGMutablePath() - - /// Initializes an empty Path. - public init() { - internalPath = CGMutablePath() - internalPath.move(to: CGPoint()) - } - - /// Initializes a new Path from an existing CGPathRef. - /// - /// - parameter path: a previously initialized CGPathRef - public init(path: CoreGraphics.CGPath) { - internalPath = path.mutableCopy()! - } - - /// Determine if the path is empty - /// - returns: A boolean, `false` if the path contains no points, otherwise `true` - public func isEmpty() -> Bool { - return internalPath.isEmpty - } - - /// Return the path bounding box. The path bounding box is the smallest rectangle completely enclosing all points - /// in the path, *not* including control points for Bézier cubic and quadratic curves. If the path is empty, then - /// return `CGRectNull`. - /// - returns: A rectangle that represents the path bounding box of the specified path - public func boundingBox() -> Rect { - return Rect(internalPath.boundingBoxOfPath) - } - - /// Return true if `point` is contained in `path`; false otherwise. A point is contained in a path if it is inside the - /// painted region when the path is filled; if `fillRule` is `EvenOdd`, then the even-odd fill rule is used to evaluate - /// the painted region of the path, otherwise, the winding-number fill rule is used. - /// - /// - parameter point: The point to test. - /// - parameter fillRule: The fill rule to use when testing for containment. - /// - returns: `true` if `point` is inside the path, `false` otherwise. - public func containsPoint(_ point: Point, fillRule: FillRule = .nonZero) -> Bool { - let rule = fillRule == .evenOdd ? CGPathFillRule.evenOdd : CGPathFillRule.winding - return internalPath.contains(CGPoint(point), using: rule, transform: CGAffineTransform.identity) - } - - /// Create a copy of the path - /// - returns: A new copy of the specified path. - public func copy() -> Path { - return Path(path: internalPath.mutableCopy()!) - } - - /// A CGPathRef representation of the receiver's path. - public var CGPath: CoreGraphics.CGPath { - return internalPath - } -} - -/// Determine if two paths are equal -/// - parameter left: the first path to compare -/// - parameter right: the second path to compare -/// - returns: a boolean, `true` if the patrhs are equal, otherwise `false` -public func == (left: Path, right: Path) -> Bool { - return left.internalPath == right.internalPath -} - -extension Path { - - /// Return the current point of the current subpath. - public var currentPoint: Point { - get { - return Point(internalPath.currentPoint) - } - set(point) { - moveToPoint(point) - } - } - - /// Move the current point of the current subpath. - /// - parameter point: A Point - public func moveToPoint(_ point: Point) { - internalPath.move(to: CGPoint(point)) - } - - /// Append a straight-line segment fron the current point to `point` and move the current point to `point`. - /// - parameter point: A Point - public func addLineToPoint(_ point: Point) { - internalPath.addLine(to: CGPoint(point)) - } - - /// Append a quadratic curve from the current point to `point` with control point `control` and move the current - /// point to `point`. - /// - parameter control: A Point used to shape the curve - /// - parameter point: A Point - public func addQuadCurveToPoint(_ point: Point, control: Point) { - internalPath.addQuadCurve(to: CGPoint(point), control: CGPoint(control)) - } - - /// Append a cubic Bézier curve from the current point to `point` with control points `control1` and `control2` - /// and move the current point to `point`. - /// - parameter control1: A Point used to shape the curve - /// - parameter control2: A Point used to shape the curve - /// - parameter point: A Point - public func addCurveToPoint(_ point: Point, control1: Point, control2: Point) { - internalPath.addCurve(to: CGPoint(point), control1: CGPoint(control1), control2: CGPoint(control2)) - } - - /// Append a line from the current point to the starting point of the current subpath and end the subpath. - public func closeSubpath() { - internalPath.closeSubpath() - } - - /// Add a rectangle to the path. - /// - parameter rect: a Rect to add to the path - public func addRect(_ rect: Rect) { - internalPath.addRect(CGRect(rect)) - } - - /// Add a rounded rectangle to the path. The rounded rectangle coincides with the edges of `rect`. Each corner consists - /// of one-quarter of an ellipse with axes equal to `cornerWidth` and `cornerHeight`. The rounded rectangle forms a - /// complete subpath of the path --- that is, it begins with a "move to" and ends with a "close subpath" --- oriented - /// in the clockwise direction. - /// - parameter rect: a Rect to add to the path - /// - parameter cornerWidth: the width of the shape's rounded corners - /// - parameter cornerHeight: the width of the shape's rounded corners - public func addRoundedRect(_ rect: Rect, cornerWidth: Double, cornerHeight: Double) { - internalPath.addRoundedRect(in: CGRect(rect), cornerWidth: CGFloat(cornerWidth), cornerHeight: CGFloat(cornerHeight)) - } - - /// Add an ellipse (an oval) inside `rect`. The ellipse is approximated by a sequence of Bézier curves. The center of - /// the ellipse is the midpoint of `rect`. If `rect` is square, then the ellipse will be circular with radius equal to - /// one-half the width (equivalently, one-half the height) of `rect`. If `rect` is rectangular, then the major- and - /// minor-axes will be the width and height of `rect`. The ellipse forms a complete subpath --- that is, it begins with - /// a "move to" and ends with a "close subpath" --- oriented in the clockwise direction. - /// - parameter rect: a Rect into which an ellipse will be created and added to the path - public func addEllipse(_ rect: Rect) { - internalPath.addEllipse(in: CGRect(rect)) - } - - /// Add an arc of a circle, possibly preceded by a straight line segment. The arc is approximated by a sequence of - /// Bézier curves. - /// - /// - parameter center: The center of the arc. - /// - parameter radius: The radius of the arc. - /// - parameter startAngle: The angle in radians to the first endpoint of the arc, measured counter-clockwise from the positive - /// x-axis. - /// - parameter delta: The angle between `startAngle` and the second endpoint of the arc, in radians. If `delta' is positive, - /// then the arc is drawn counter-clockwise; if negative, clockwise. - public func addRelativeArc(_ center: Point, radius: Double, startAngle: Double, delta: Double) { - internalPath.addRelativeArc(center: CGPoint(center), radius: CGFloat(radius), startAngle: CGFloat(startAngle), delta: CGFloat(delta)) - } - - /// Add an arc of a circle, possibly preceded by a straight line segment. The arc is approximated by a sequence of - /// Bézier curves. - /// - /// Note that using values very near 2π can be problematic. For example, setting `startAngle` to 0, `endAngle` to 2π, - /// and `clockwise` to true will draw nothing. (It's easy to see this by considering, instead of 0 and 2π, the values - /// ε and 2π - ε, where ε is very small.) Due to round-off error, however, it's possible that passing the value - /// `2 * Double.pi` to approximate 2π will numerically equal to 2π + δ, for some small δ; this will cause a full circle to - /// be drawn. - /// - /// If you want a full circle to be drawn clockwise, you should set `startAngle` to 2π, `endAngle` to 0, and - /// `clockwise` to true. This avoids the instability problems discussed above. - /// - /// - parameter center: The center of the arc. - /// - parameter radius: The radius of the arc. - /// - parameter startAngle: The angle to the first endpoint of the arc in radians. - /// - parameter endAngle: The angle to the second endpoint of the arc. - /// - parameter clockwise: If true the arc is drawn clockwise. - public func addArc(_ center: Point, radius: Double, startAngle: Double, endAngle: Double, clockwise: Bool) { - internalPath.addArc(center: CGPoint(center), radius: CGFloat(radius), startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: clockwise) - } - - /// Add an arc of a circle, possibly preceded by a straight line segment. The arc is approximated by a sequence of - /// Bézier curves. The resulting arc is tangent to the line from the current point to `point1`, and the line from - /// `point1` to `point2`. - /// - parameter point1: the begin point of the arc - /// - parameter point2: the end point of the arc - /// - parameter radius: the radius of the arc - public func addArcToPoint(_ point1: Point, point2: Point, radius: Double) { - internalPath.addArc(tangent1End: CGPoint(point1), tangent2End: CGPoint(point2), radius: CGFloat(radius)) - } - - /// Append a path. - /// - /// - parameter path: A new Path that is added to the end of the receiver. - public func addPath(_ path: Path) { - internalPath.addPath(path.internalPath) - } - - /// Transform a path. - /// - /// - parameter transform: A Transform to be applied to the receiver. - public func transform(_ transform: Transform) { - var t = transform.affineTransform - internalPath = internalPath.mutableCopy(using: &t)! - } -} diff --git a/C4/Core/Point.swift b/C4/Core/Point.swift deleted file mode 100644 index ff7c3095..00000000 --- a/C4/Core/Point.swift +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreGraphics - -///A structure that contains a point in a two-dimensional coordinate system. -public struct Point: Equatable, CustomStringConvertible { - - ///The x value of the coordinate. - public var x: Double = 0 - - /// The y value of the coordinate. - public var y: Double = 0 - - /// Initializes a new point with the coordinates {0,0} - /// - /// ```` - /// let p = Point() - /// ```` - public init() { - } - - /// Initializes a new point with the specified coordinates {x,y} - /// - /// ```` - /// let p = Point(10.5,10.5) - /// ```` - /// - /// - parameter x: a Double value - /// - parameter y: a Double value - public init(_ x: Double, _ y: Double) { - self.x = x - self.y = y - } - - /// Initializes a new point with the specified coordinates {x,y}, converting integer values to doubles - /// - /// ```` - /// let p = Point(10,10) - /// ```` - public init(_ x: Int, _ y: Int) { - self.x = Double(x) - self.y = Double(y) - } - - /// Initializes a Point initialized with a CGPoint. - /// - /// - parameter point: a previously initialized CGPoint - public init(_ point: CGPoint) { - x = Double(point.x) - y = Double(point.y) - } - - /// Returns true if the point's coordinates are {0,0}, otherwise returns false - public func isZero() -> Bool { - return x == 0 && y == 0 - } - - /// Transforms the point. - /// - /// ```` - /// var p = Point(10,10) - /// let v = Vector(x: 0, y: 0, z: 1) - /// let t = Transform.makeRotation(Double.pi, axis: v) - /// p.transform(t) // -> {-10.0, -10.0} - /// ```` - /// - /// - parameter t: A Transform to apply to the point - public mutating func transform(_ t: Transform) { - x = x * t[0, 0] + y * t[0, 1] + t[3, 0] - y = x * t[1, 0] + y * t[1, 1] + t[3, 1] - } - - /// A string representation of the point. - /// - /// ```` - /// let p = Point() - /// println(p) - /// ```` - /// - /// - returns: A string formatted to look like {x,y} - public var description: String { - return "{\(x), \(y)}" - } -} - -/// Translate a point by the given vector. -/// -/// - parameter lhs: a Point to translate -/// - parameter rhs: a Vector whose values will be applied to the point -public func += (lhs: inout Point, rhs: Vector) { - lhs.x += rhs.x - lhs.y += rhs.y -} - -/// Translate a point by the negative of the given vector -/// -/// - parameter lhs: a Point to translate -/// - parameter rhs: a Vector whose values will be applied to the point -public func -= (lhs: inout Point, rhs: Vector) { - lhs.x -= rhs.x - lhs.y -= rhs.y -} - -/// Calculate the vector between two points -/// -/// - parameter lhs: a Point -/// - parameter rhs: a Point -/// -/// - returns: a Vector whose value is the left-hand side _minus_ the right-hand side -public func - (lhs: Point, rhs: Point) -> Vector { - return Vector(x: lhs.x - rhs.x, y: lhs.y - rhs.y) -} - -/// Translate a point by the given vector. -/// -/// - parameter lhs: a Point to translate -/// - parameter rhs: a Vector whose values will be applied to the point -/// -/// - returns: A new point whose coordinates have been translated by the values from the vector (e.g. point.x = lhs.x + rhs.x) -public func + (lhs: Point, rhs: Vector) -> Point { - return Point(lhs.x + rhs.x, lhs.y + rhs.y) -} - -/// Translate a point by the negative of the vector. -/// -/// - parameter lhs: a Point to translate -/// - parameter rhs: a Vector whose values will be applied to the point -/// -/// - returns: A new point whose coordinates have been translated by the negative vector (e.g. point.x = lhs.x - rhs.x) -public func - (lhs: Point, rhs: Vector) -> Point { - return Point(lhs.x - rhs.x, lhs.y - rhs.y) -} - -/// Calculates the distance between two points. -/// -/// - parameter lhs: left-hand point -/// - parameter rhs: right-hand point -/// -/// - returns: The linear distance between two points -public func distance(_ lhs: Point, rhs: Point) -> Double { - let dx = rhs.x - lhs.x - let dy = rhs.y - lhs.y - return sqrt(dx*dx + dy*dy) -} - -/// Checks to see if two points are equal. -/// -/// - parameter lhs: a Point -/// - parameter rhs: a Point -/// -/// - returns: true if the two structs have identical coordinates -public func == (lhs: Point, rhs: Point) -> Bool { - return lhs.x == rhs.x && lhs.y == rhs.y -} - -/// Linear interpolation. -/// -/// For any two points `a` and `b` return a point that is the linear interpolation between a and b -/// for interpolation parameter `param`. For instance, a parameter of 0 will return `a`, a parameter of 1 will return `b` -/// and a parameter of 0.5 will return the midpoint between `a` and `b`. -/// -/// - parameter a: the first point -/// - parameter b: the second point -/// - parameter param: a Double value (between 0.0 and 1.0) used to calculate the point between a and b -/// -/// - returns: an interpolated point -public func lerp(_ a: Point, _ b: Point, at: Double) -> Point { - return a + (b - a) * at -} - -public extension CGPoint { - ///Initializes a CGPoint from a Point - public init(_ point: Point) { - self.init(x: CGFloat(point.x), y: CGFloat(point.y)) - } -} diff --git a/C4/Core/Rect.swift b/C4/Core/Rect.swift deleted file mode 100644 index ba0308fd..00000000 --- a/C4/Core/Rect.swift +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// A structure that contains the location and dimensions of a rectangle. -public struct Rect: Equatable, CustomStringConvertible { - /// The origin (top-left) of the rect. - public var origin: Point - - /// The size (width / height) of the rect. - public var size: Size - - /// The width of the rect. - public var width: Double { - get { - return size.width - } set { - size.width = newValue - } - } - - /// The height of the rect. - public var height: Double { - get { - return size.height - } set { - size.height = newValue - } - } - - /// Initializes a new Rect with the origin {0,0} and the size {0,0} - /// ```` - /// let r = Rect() - /// ```` - public init() { - self.init(0, 0, 0, 0) - } - - /// Initializes a new Rect with the origin {x,y} and the size {w,h} - /// ```` - /// let r = Rect(0.0,0.0,10.0,10.0) - /// ```` - public init(_ x: Double, _ y: Double, _ w: Double, _ h: Double) { - origin = Point(x, y) - size = Size(w, h) - } - - /// Initializes a new Rect with the origin {x,y} and the size {w,h}, converting values from Int to Double - /// ```` - /// let r = Rect(0,0,10,10) - /// ```` - public init(_ x: Int, _ y: Int, _ w: Int, _ h: Int) { - origin = Point(x, y) - size = Size(w, h) - } - - /// Initializes a new Rect with the origin {o.x,o.y} and the size {s.w,s.h} - /// ```` - /// let p = Point() - /// let s = Size() - /// let r = Rect(p,s) - /// ```` - public init(_ o: Point, _ s: Size) { - origin = o - size = s - } - - /// Initializes a Rect from a CGRect - public init(_ rect: CGRect) { - origin = Point(rect.origin) - size = Size(rect.size) - } - - /// Initializes a rectangle that contains all of the specified coordinates in an array. - /// ```` - /// let pts = [Point(), Point(0,5), Point(10,10), Point(9,8)] - /// let r = Rect(pts) //-> {{0.0, 0.0}, {10.0, 10.0}} - /// ```` - /// - parameter points: An array of Point coordinates - public init(_ points: [Point]) { - let count = points.count - assert(count >= 2, "To create a Polygon you need to specify an array of at least 2 points") - var cgPoints = [CGPoint]() - for i in 0.. true - /// r1.intersects(r3) //-> false - /// ```` - /// - parameter rect: The rectangle to examine. - /// - returns: true if the two specified rectangles intersect; otherwise, false. - public func intersects(_ rect: Rect) -> Bool { - return CGRect(self).intersects(CGRect(rect)) - } - - // MARK: - Center & Max - - /// The center point of the receiver. - /// ```` - /// let r = Rect(0,0,10,10) - /// r.center //-> {5,5} - /// ```` - public var center: Point { - get { - return Point(origin.x + size.width/2, origin.y + size.height/2) - } - set { - origin.x = newValue.x - size.width/2 - origin.y = newValue.y - size.height/2 - } - } - - /// The bottom-right point of the receiver. - /// ```` - /// let r = Rect(5,5,10,10) - /// r.max //-> {15,15} - /// ```` - public var max: Point { - return Point(origin.x + size.width, origin.y + size.height) - } - - /// Checks to see if the receiver has zero size and position - /// ```` - /// let r = Point() - /// r.isZero() //-> true - /// ```` - /// - returns: true if origin = {0,0} and size = {0,0} - public func isZero() -> Bool { - return origin.isZero() && size.isZero() - } - - // MARK: - Membership - - /// Returns whether a rectangle contains a specified point. - /// ```` - /// let r1 = Rect(0,0,10,10) - /// let r2 = Rect(5,5,10,10) - /// let p = Rect(2,2,2,2) - /// r1.contains(p) //-> true - /// r2.contains(p) //-> false - /// ```` - /// - parameter point: The point to examine. - /// - returns: true if the rectangle is not null or empty and the point is located within the rectangle; otherwise, false. - public func contains(_ point: Point) -> Bool { - return CGRect(self).contains(CGPoint(point)) - } - - /// Returns whether the first rectangle contains the second rectangle. - /// ```` - /// let r1 = Rect(0,0,10,10) - /// let r2 = Rect(5,5,10,10) - /// let r3 = Rect(2,2,2,2) - /// r1.contains(r2) //-> false - /// r1.contains(r3) //-> true - /// ```` - /// - parameter rect: The rectangle to examine for containment. - /// - returns: `true` if the rectangle is contained in this rectangle; otherwise, `false`. - public func contains(_ rect: Rect) -> Bool { - return CGRect(self).contains(CGRect(rect)) - } - - /// A string representation of the rect. - /// - returns: A string formatted to look like {{x,y},{w,h}} - public var description: String { - return "{\(origin),\(size)}" - } -} - -// MARK: - Comparing - -/// Checks to see if two Rects share identical origin and size -/// -/// ```` -/// let r1 = Rect(0,0,10,10) -/// let r2 = Rect(0,0,10,10.5) -/// println(r1 == r2) //-> false -/// ```` -/// - parameter lhs: The first rectangle to compare -/// - parameter rhs: The second rectangle to compare -/// - returns: A bool, `true` if the rects are identical, otherwise `false`. -public func == (lhs: Rect, rhs: Rect) -> Bool { - return lhs.origin == rhs.origin && lhs.size == rhs.size -} - -// MARK: - Manipulating - -/// Returns the intersection of two rectangles. -/// -/// ```` -/// let r1 = Rect(0,0,10,10) -/// let r2 = Rect(5,5,10,10) -/// intersection(r1,r2) //-> {5,5,5,5} -/// ```` -/// -/// - parameter rect1: The first source rectangle. -/// - parameter rect2: The second source rectangle. -/// -/// - returns: A rectangle that represents the intersection of the two specified rectangles. -public func intersection(_ rect1: Rect, rect2: Rect) -> Rect { - return Rect(CGRect(rect1).intersection(CGRect(rect2))) -} - -/// Returns the smallest rectangle that contains the two source rectangles. -/// -/// ```` -/// let r1 = Rect(0,0,10,10) -/// let r2 = Rect(5,5,10,10) -/// intersection(r1,r2) //-> {0,0,15,15} -/// ```` -/// -/// - parameter rect1: The first source rectangle. -/// - parameter rect2: The second source rectangle. -/// - returns: The smallest rectangle that completely contains both of the source rectangles. -public func union(_ rect1: Rect, rect2: Rect) -> Rect { - return Rect(CGRect(rect1).union(CGRect(rect2))) -} - -/// Returns the smallest rectangle that results from converting the source rectangle values to integers. -/// -/// ```` -/// let r = Rect(0.1, 0.9, 9.1, 9.9) -/// integral(r) //-> {0, 0, 10, 10} -/// ```` -/// -/// - parameter r: The source rectangle. -/// - returns: A rectangle with the smallest integer values for its origin and size that contains the source rectangle. -public func integral(_ r: Rect) -> Rect { - return Rect(CGRect(r).integral) -} - -/// Returns a rectangle with a positive width and height. -/// -/// ```` -/// let r = Rect(0, 0, -10, -10) -/// standardize(r) //-> {-10, -10, 10, 10} -/// ```` -/// -/// - parameter r: The source rectangle. -/// - returns: A rectangle that represents the source rectangle, but with positive width and height values. -public func standardize(_ r: Rect) -> Rect { - return Rect(CGRect(r).standardized) -} - -/// Returns a rectangle that is smaller or larger than the source rectangle, with the same center point. -/// -/// ```` -/// let r = Rect(0,0,10,10) -/// inset(r, 1, 1) //-> {1,1,8,8} -/// ```` -/// -/// - parameter r: The source Rect structure. -/// - parameter dx: The x-coordinate value to use for adjusting the source rectangle. -/// - parameter dy: The y-coordinate value to use for adjusting the source rectangle. -/// - returns: A rectangle. -public func inset(_ r: Rect, dx: Double, dy: Double) -> Rect { - return Rect(CGRect(r).insetBy(dx: CGFloat(dx), dy: CGFloat(dy))) -} - -// MARK: - Casting to CGRect -public extension CGRect { - /// Initializes a CGRect from a Rect - public init(_ rect: Rect) { - self.init(origin: CGPoint(rect.origin), size: CGSize(rect.size)) - } -} diff --git a/C4/Core/Size.swift b/C4/Core/Size.swift deleted file mode 100644 index 177fe286..00000000 --- a/C4/Core/Size.swift +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// A structure that contains width and height values. Values stored as Double, otherwise synonymous with CGSize. -public struct Size: Equatable, Comparable, CustomStringConvertible { - ///The width of the size. - public var width: Double - - ///The height of the size. - public var height: Double - - /// Initializes a new Size with the dimensions {0,0} - /// - /// ```` - /// let s = Size() - /// ```` - public init() { - width = 0 - height = 0 - } - - /// Initializes a new Size with the dimensions {width,height} - /// - /// ```` - /// let s = Size(5.2,5.2) - /// ```` - public init(_ width: Double, _ height: Double) { - self.width = width - self.height = height - } - - /// Initializes a new Size with the dimensions {width,height}, converting Int values to Double - /// - /// ```` - /// let s = Size(5,5) - /// ```` - public init(_ width: Int, _ height: Int) { - self.width = Double(width) - self.height = Double(height) - } - - /// Initializes a new Size from a CGSize. - /// - /// - public init(_ size: CGSize) { - width = Double(size.width) - height = Double(size.height) - } - - /// Returns true if the dimensions of the receiver are {0,0} - /// - /// ```` - /// let s = Size() - /// s.isZero() //-> true - /// ```` - public func isZero() -> Bool { - return width == 0 && height == 0 - } - - /// A string representation of the size. - /// - /// - returns: A string formatted to look like {w,h} - public var description: String { - return "{\(width),\(height)}" - } -} - -/// Returns true if the two source Size structs share identical dimensions -/// -/// ```` -/// let s1 = Size() -/// let s2 = Size(1,1) -/// s1 == s2 //-> false -/// ```` -/// - parameter lhs: The first size to compare -/// - parameter rhs: The second size to compare -/// - returns: A boolean, `true` if the sizes are equal, otherwise `false` -public func == (lhs: Size, rhs: Size) -> Bool { - return lhs.width == rhs.width && lhs.height == rhs.height -} - -/// Returns true if the left-hand size is bigger than the right-hand size -/// -/// ```` -/// let s1 = Size(3,4) -/// let s2 = Size(4,3) -/// let s3 = Size(2,2) -/// -/// s1 > s2 //-> false -/// s2 > s3 //-> true -/// ```` -/// - parameter lhs: The first size to compare -/// - parameter rhs: The second size to compare -/// - returns: A boolean, `true` if the area of lhs is greater than that of rhs -public func > (lhs: Size, rhs: Size) -> Bool { - return lhs.width * lhs.height > rhs.width * rhs.height -} - -/// Returns true if the left-hand size is smaller than the right-hand size -/// -/// ```` -/// let s1 = Size(3,4) -/// let s2 = Size(4,3) -/// let s3 = Size(2,2) -/// -/// s1 < s2 //-> false -/// s2 < s3 //-> false -/// ```` -/// - parameter lhs: The first size to compare -/// - parameter rhs: The second size to compare -/// - returns: A boolean, `true` if the area of lhs is less than that of rhs -public func < (lhs: Size, rhs: Size) -> Bool { - return lhs.width * lhs.height < rhs.width * rhs.height -} - -/// Returns true if the left-hand size is greater than or equal to the right-hand size -/// -/// ```` -/// let s1 = Size(3,4) -/// let s2 = Size(4,3) -/// let s3 = Size(2,2) -/// -/// s1 => s2 //-> true -/// s2 => s3 //-> true -/// ```` -/// - parameter lhs: The first size to compare -/// - parameter rhs: The second size to compare -/// - returns: A boolean, `true` if the area of lhs is greater than or equal to that of rhs -public func >= (lhs: Size, rhs: Size) -> Bool { - return lhs.width * lhs.height >= rhs.width * rhs.height -} - -/// Returns true if the left-hand size is smaller than or equal to the right-hand size -/// -/// ```` -/// let s1 = Size(3,4) -/// let s2 = Size(4,3) -/// let s3 = Size(2,2) -/// -/// s1 <= s2 //-> true -/// s2 <= s3 //-> false -/// ```` -/// - parameter lhs: The first size to compare -/// - parameter rhs: The second size to compare -/// - returns: A boolean, `true` if the area of lhs is less than or equal to that of rhs -public func <= (lhs: Size, rhs: Size) -> Bool { - return lhs.width * lhs.height <= rhs.width * rhs.height -} - -// MARK: - Casting to CGSize -public extension CGSize { - /// Initializes a new CGSize from a Size - public init(_ size: Size) { - self.init(width: CGFloat(size.width), height: CGFloat(size.height)) - } -} diff --git a/C4/Core/Transform.swift b/C4/Core/Transform.swift deleted file mode 100644 index 1035d874..00000000 --- a/C4/Core/Transform.swift +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreGraphics -import QuartzCore - -/// A structure for holding a transform matrix. -/// -/// Transform can translate, rotate, scale. -public struct Transform: Equatable { - var matrix = [Double](repeating: 0, count: 16) - - public subscript(row: Int, col: Int) -> Double { - get { - assert(row >= 0 && row < 4, "Row index out of bounds") - assert(col >= 0 && col < 4, "Column index out of bounds") - return matrix[row + col * 4] - } - set { - assert(row >= 0 && row < 4, "Row index out of bounds") - assert(col >= 0 && col < 4, "Column index out of bounds") - matrix[row + col * 4] = newValue - } - } - - /// Initializes a Transform. Defaults to an identity transform. - public init() { - self[0, 0] = 1 - self[1, 1] = 1 - self[2, 2] = 1 - self[3, 3] = 1 - } - - /// Creates a new transform from a `CGAffineTransform` structure. - /// - parameter t: A `CGAffineTransform` structure. - public init(_ t: CGAffineTransform) { - self.init() - self[0, 0] = Double(t.a) - self[0, 1] = Double(t.b) - self[1, 0] = Double(t.c) - self[1, 1] = Double(t.d) - self[0, 3] = Double(t.tx) - self[1, 3] = Double(t.ty) - } - - /// Creates a new transform from a `CATransform3D` structure. - /// - parameter t: A `CATransform3D` structure. - public init(_ t: CATransform3D) { - self[0, 0] = Double(t.m11) - self[0, 1] = Double(t.m12) - self[0, 2] = Double(t.m13) - self[0, 3] = Double(t.m14) - self[1, 0] = Double(t.m21) - self[1, 1] = Double(t.m22) - self[1, 2] = Double(t.m23) - self[1, 3] = Double(t.m24) - self[2, 0] = Double(t.m31) - self[2, 1] = Double(t.m32) - self[2, 2] = Double(t.m33) - self[2, 3] = Double(t.m34) - self[3, 0] = Double(t.m41) - self[3, 1] = Double(t.m42) - self[3, 2] = Double(t.m43) - self[3, 3] = Double(t.m44) - } - - /// Returns `true` if transform is affine, otherwise `false`. - public func isAffine() -> Bool { - return self[3, 0] == 0.0 && self[3, 1] == 0.0 && self[3, 2] == 0.0 && self[3, 3] == 1.0 - } - - /// The translation component of the tranform. - /// - returns: A `Vector` that represents the translation of the transform, where x = [0,3], y = [1,3] - public var translation: Vector { - get { - return Vector(x: self[3, 0], y: self[3, 1]) - } - set { - self[3, 0] = newValue.x - self[3, 1] = newValue.y - } - } - - /// Creates a transform that represents a translation in 2d (x,y) - /// ```` - /// let v = Vector(x: 1, y: 1) - /// let t = Transform.makeTranslation(v) - /// ```` - /// - parameter translation: A `Vector` that represents the translation to apply. - /// - returns: A `Transform` that can be used to apply a translation to a receiver. - public static func makeTranslation(_ translation: Vector) -> Transform { - var t = Transform() - t[3, 0] = translation.x - t[3, 1] = translation.y - return t - } - - /// Creates a transform that represents a scale in 3d (x, y, z). The `z` component is optional. - /// ```` - /// let t = Transform.makeScale(2.0, 2.0) - /// ```` - /// - parameter sx: The amount to scale in the `x` axis - /// - parameter sy: The amount to scale in the `y` axis - /// - parameter sz: The amount to scale in the `z` axis - /// - returns: A `Transform` that can be used to scale a receiver. - public static func makeScale(_ sx: Double, _ sy: Double, _ sz: Double = 1) -> Transform { - var t = Transform() - t[0, 0] = sx - t[1, 1] = sy - t[2, 2] = sz - return t - } - - /// Creates a transform that represents a rotation. The `axis` component is optional. - /// ```` - /// let t = Transform.makeRotation(Double.pi) - /// ```` - /// - parameter angle: The angle, in radians, to rotate - /// - parameter axis: The axis around which to rotate, defaults to the z axis {0,0,1} - /// - returns: A `Transform` that can be used to rotate a receiver. - public static func makeRotation(_ angle: Double, axis: Vector = Vector(x: 0, y: 0, z: 1)) -> Transform { - if axis.isZero() { - return Transform() - } - - let unitAxis = axis.unitVector()! - let ux = unitAxis.x - let uy = unitAxis.y - let uz = unitAxis.z - - let ca = cos(angle) - let sa = sin(angle) - - var t = Transform() - t[0, 0] = ux * ux * (1 - ca) + ca - t[0, 1] = ux * uy * (1 - ca) - uz * sa - t[0, 2] = ux * uz * (1 - ca) + uy * sa - t[1, 0] = uy * ux * (1 - ca) + uz * sa - t[1, 1] = uy * uy * (1 - ca) + ca - t[1, 2] = uy * uz * (1 - ca) - ux * sa - t[2, 0] = uz * ux * (1 - ca) - uy * sa - t[2, 1] = uz * uy * (1 - ca) + ux * sa - t[2, 2] = uz * uz * (1 - ca) + ca - return t - } - - /// Applies a translation to the receiver. - /// ```` - /// let v = Vector(x: 1, y: 1) - /// let t = Transform() - /// t.translate(v) - /// ```` - /// - parameter translation: A `Vector` that represents the translation to apply. - public mutating func translate(_ translation: Vector) { - let t = Transform.makeTranslation(translation) - self = concat(self, t2: t) - } - - /// Applies a scale to the receiver. The `z` variable is optional. - /// ```` - /// let t = Transform() - /// t.scale(2.0, 2.0) - /// ```` - /// - parameter sx: The amount to scale in the `x` axis - /// - parameter sy: The amount to scale in the `y` axis - /// - parameter sz: The amount to scale in the `z` axis - public mutating func scale(_ sx: Double, _ sy: Double, _ sz: Double = 1) { - let s = Transform.makeScale(sx, sy, sz) - self = concat(self, t2: s) - } - - /// Applies a rotation. The `axis` component is optional. - /// ```` - /// let t = Transform() - /// t.rotate(Double.pi) - /// ```` - /// - parameter angle: The angle, in radians, to rotate - /// - parameter axis: The axis around which to rotate, defaults to the z axis {0,0,1} - public mutating func rotate(_ angle: Double, axis: Vector = Vector(x: 0, y: 0, z: 1)) { - let r = Transform.makeRotation(angle, axis: axis) - self = concat(self, t2: r) - } - - /// The CGAffineTransform version of the receiver. - /// - returns: A `CGAffineTransform` that is equivalent to the receiver. - public var affineTransform: CGAffineTransform { - return CGAffineTransform( - a: CGFloat(self[0, 0]), - b: CGFloat(self[0, 1]), - c: CGFloat(self[1, 0]), - d: CGFloat(self[1, 1]), - tx: CGFloat(self[3, 0]), - ty: CGFloat(self[3, 1])) - } - - /// The CATransform3D version of the receiver. - /// - returns: A `CATransform3D` that is equivalent to the receiver. - public var transform3D: CATransform3D { - let t = CATransform3D( - m11: CGFloat(self[0, 0]), - m12: CGFloat(self[0, 1]), - m13: CGFloat(self[0, 2]), - m14: CGFloat(self[0, 3]), - m21: CGFloat(self[1, 0]), - m22: CGFloat(self[1, 1]), - m23: CGFloat(self[1, 2]), - m24: CGFloat(self[1, 3]), - m31: CGFloat(self[2, 0]), - m32: CGFloat(self[2, 1]), - m33: CGFloat(self[2, 2]), - m34: CGFloat(self[2, 3]), - m41: CGFloat(self[3, 0]), - m42: CGFloat(self[3, 1]), - m43: CGFloat(self[3, 2]), - m44: CGFloat(self[3, 3]) - ) - return t - } -} - -/// Returns true if the two source Transform structs share identical dimensions -/// - parameter lhs: The first transform to compare -/// - parameter rhs: The second transform to compare -/// - returns: A boolean, `true` if the both transforms are equal -public func == (lhs: Transform, rhs: Transform) -> Bool { - var equal = true - for col in 0...3 { - for row in 0...3 { - equal = equal && lhs[row, col] == rhs[row, col] - } - } - return equal -} - -/// Transform matrix multiplication -/// - parameter lhs: The first transform to multiply -/// - parameter rhs: The second transform to multiply -/// - returns: A new transform that is the result of multiplying `lhs` and `rhs` -public func * (lhs: Transform, rhs: Transform) -> Transform { - var t = Transform() - for col in 0...3 { - for row in 0...3 { - t[row, col] = lhs[row, 0] * rhs[0, col] + lhs[row, 1] * rhs[1, col] + lhs[row, 2] * rhs[2, col] + lhs[row, 3] * rhs[3, col] - } - } - return t -} - -/// Transform matrix scalar multiplication -/// - parameter t: The transform to scale -/// - parameter s: A scalar value to apply to the transform -/// - returns: A new trasform whose values are the scalar multiple of `t` -public func * (t: Transform, s: Double) -> Transform { - var r = Transform() - for col in 0...3 { - for row in 0...3 { - r[row, col] = t[row, col] * s - } - } - return r -} - -/// Transform matrix scalar multiplication -/// - parameter s: A scalar value to apply to the transform -/// - parameter t: The transform to scale -/// - returns: A new trasform whose values are the scalar multiple of `t` -public func * (s: Double, t: Transform) -> Transform { - return t * s -} - -/// Concatenate two transformations. This is the same as t2 * t1. -/// - parameter t1: The first transform to contatenate -/// - parameter t2: The second transform to contatenate -/// - returns: A new transform that is the contcatenation of `t1` and `t2` -public func concat(_ t1: Transform, t2: Transform) -> Transform { - return t2 * t1 -} - -/// Calculates the inverse of a transfomation. -/// - parameter t: The transform to invert -/// - returns: A new transform that is the inverse of `t` -public func inverse(_ t: Transform) -> Transform? { - return Transform(CATransform3DInvert(t.transform3D)) -} diff --git a/C4/Core/Vector.swift b/C4/Core/Vector.swift deleted file mode 100644 index da098326..00000000 --- a/C4/Core/Vector.swift +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreGraphics - -/// The Vector class is used for coordinate values and direction vectors. -public struct Vector: Equatable, CustomStringConvertible { - /// The x-value of the vector. - public var x: Double = 0 - /// The y-value of the vector. - public var y: Double = 0 - /// The z-value of the vector. - public var z: Double = 0 - - /// Creates a vector with default values {0,0,0,} - /// ```` - /// let v = Vector() - /// ```` - public init() { - } - - /// Create a vector with a cartesian representation: an x and a y coordinates. The `z` variable is optional. - /// ```` - /// let v = Vector(x: 1.0, y: 1.0, z: 1.0) - /// let v = Vector(x: 1.0, y: 1.0) - /// ```` - /// - parameter x: the x-value of the new vector - /// - parameter y: the y-value of the new vector - /// - parameter z: the z-value of the new vector (defaults to 0) - public init(x: Double, y: Double, z: Double = 0) { - self.x = x - self.y = y - self.z = z - } - - /// Create a vector with a cartesian representation: an x and a y coordinates. - /// ```` - /// let v = Vector(x: 1, y: 1, z: 1) - /// ```` - /// - parameter x: the x-value of the new vector - /// - parameter y: the y-value of the new vector - /// - parameter z: the z-value of the new vector (defaults to 0) - public init(x: Int, y: Int, z: Int = 0) { - self.x = Double(x) - self.y = Double(y) - self.z = Double(z) - } - - /// Create a vector with a polar representation: a magnitude and an angle in radians. The `z` variable is optional. - /// [Polar coordinate system - Wikipedia](http://en.wikipedia.org/wiki/Polar_coordinate_system) - /// ```` - /// let m = sqrt(2.0) - /// let h = Double.pi_4 - /// let v = Vector(magnitude: m, heading: h) - /// v //-> {1,1,0} - /// ```` - /// - parameter magnitude: the magnitude of the new vector - /// - parameter heading: the heading (angle) of the new vector - /// - parameter z: the z-value of the new vector (defaults to 0) - public init(magnitude: Double, heading: Double, z: Double = 0) { - x = magnitude * cos(heading) - y = magnitude * sin(heading) - self.z = z - } - - /// Initializes a Vector from a CGPoint - /// - parameter point: a previously initialized CGPoint - public init(_ point: CGPoint) { - x = Double(point.x) - y = Double(point.y) - z = 0 - } - - /// Initializes a Vector from a Point - /// - parameter point: a previously initialized Point - public init(_ point: Point) { - x = point.x - y = point.y - z = 0 - } - - /// Initializes a Vector from another Vector - /// - parameter copy: a previously initialized Vector - public init(copy original: Vector) { - x = original.x - y = original.y - z = original.z - } - - /// The polar representation magnitude of the vector. - /// ```` - /// let v = Vector(x: 2.0, y: 1.0, z: 0.0) - /// v.magnitude //-> √2 - /// ```` - public var magnitude: Double { - get { - return sqrt(x * x + y * y + z * z) - } - set { - x = newValue * cos(heading) - y = newValue * sin(heading) - } - } - - /// The polar representation heading angle of the vector, in radians. - /// ```` - /// let v = Vector(1,1,0) - /// v.heading //-> Double.pi_4 - /// ```` - public var heading: Double { - get { - return atan2(y, x) - } - set { - x = magnitude * cos(newValue) - y = magnitude * sin(newValue) - } - } - - /// The angle between two vectors, based on {0,0} - /// ```` - /// let v1 = Vector(x: 1, y: 1, z: 0) - /// let v2 = Vector(x: -1, y: 1, z: 0) - /// v1.angleTo(v2) //-> Double.pi / 2.0 - /// ```` - /// - parameter vec: The vector used to calcuate the angle to the receiver - /// - returns: The angle, measured in radians, between the receiver and `vec` - public func angleTo(_ vec: Vector) -> Double { - return acos(self ⋅ (vec / (self.magnitude * vec.magnitude))) - } - - /// The angle between two vectors, based on a provided point - /// ```` - /// let v1 = Vector(x: 1, y: 1, z: 0) - /// let v2 = Vector(x: -1, y: 1, z: 0) - /// let b = Vector(x: 0, y: 1, z: 0) - /// v1.angleTo(v2, basedOn: b) //-> PI - /// ```` - /// - parameter vec: The vector used to calcuate the angle to the receiver - /// - parameter basedOn: A second vector used to calcuate the angle to the receiver - /// - returns: The angle, measured in radians, between the receiver and `vec` - public func angleTo(_ vec: Vector, basedOn: Vector) -> Double { - var vecA = self - var vecB = vec - - vecA -= basedOn - vecB -= basedOn - - return acos(vecA ⋅ (vecB / (vecA.magnitude * vecB.magnitude))) - } - - /// Return the dot product. **You should use the ⋅ operator instead.** - /// ```` - /// let v1 = Vector(x: 1, y: 1, z: 0) - /// let v2 = Vector(x: -1, y: 1, z: 0) - /// v1.dot(v2) //-> 0.0 - /// ```` - /// - parameter vec: The vector used to calcuate the dot product - /// - returns: The dot product of the receiver and `vec` - public func dot(_ vec: Vector) -> Double { - return x * vec.x + y * vec.y + z * vec.z - } - - /// Return a vector with the same heading but a magnitude of 1. - /// ```` - /// let v1 = Vector(x: 1, y: 1, z: 0) - /// v1.unitVector() //-> {Double.pi_4,Double.pi_4,0} - /// ```` - /// - returns: A new vector that is the unit vector of the receiver - public func unitVector() -> Vector? { - guard self.magnitude != 0.0 else { - return nil - } - return Vector(x: x / self.magnitude, y: y / self.magnitude, z: z / self.magnitude) - } - - /// Return `true` if the vector is zero. - /// ```` - /// let v = Vector() - /// v.isZero() //-> true - /// ```` - /// - returns: A boolean, `true` if all values are 0, `false` otherwise - public func isZero() -> Bool { - return x == 0 && y == 0 && z == 0 - } - - /// Transform the vector. - /// ```` - /// var v = Vector(x: 1, y: 1, z:0) - /// let t = Transform.makeRotation(Double.pi, axis: Vector(x: 0, y:0, z:1)) - /// v.transform(t) //-> {-1, -1, 0} - /// ```` - /// - parameter t: A Transform to apply to the receiver - public mutating func transform(_ t: Transform) { - x = x * t[0, 0] + y * t[0, 1] + z * t[0, 2] - y = x * t[1, 0] + y * t[1, 1] + z * t[1, 2] - z = x * t[2, 0] + y * t[2, 1] + z * t[2, 2] - } - - /// A string representation of the vector. - /// - returns: A string formatted to look like {x,y,z} - public var description: String { - return "{\(x), \(y), \(z)}" - } -} - -/// Returns true if the coordinates of both vectors are identical -/// -/// ```` -/// let v1 = Vector(x: 1, y: 1) -/// let v2 = Vector(x: 1, y: 0) -/// v1 == v2 //-> false -/// ```` -/// - parameter lhs: A Vector -/// - parameter rhs: A Vector -/// - returns: A boolean, `true` if the vectors are equal, `false` otherwise -public func == (lhs: Vector, rhs: Vector) -> Bool { - return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z -} - -/// Transforms the left-hand vector by adding the values of the right-hand vector -/// -/// ```` -/// let v1 = Vector(x: 1, y: 1) -/// let v2 = Vector(x: 1, y: 0) -/// v1 += v2 //-> v1 = {2,1,0} -/// ```` -/// - parameter lhs: A Vector to which the values of `rhs` will be added -/// - parameter rhs: A Vector -public func += (lhs: inout Vector, rhs: Vector) { - lhs.x += rhs.x - lhs.y += rhs.y - lhs.z += rhs.z -} - -/// Transforms the left-hand vector by subtracting the values of the right-hand vector -/// -/// ```` -/// let v1 = Vector(x: 1, y: 1) -/// let v2 = Vector(x: 1, y: 0) -/// v1 += v2 //-> v1 = {0,1,0} -/// ```` -/// - parameter lhs: A Vector to which the values of `rhs` will be subtracted -/// - parameter rhs: A Vector -public func -= (lhs: inout Vector, rhs: Vector) { - lhs.x -= rhs.x - lhs.y -= rhs.y - lhs.z -= rhs.z -} - -/// Transforms the left-hand vector by multiplying each by the values of the right-hand vector -/// -/// ```` -/// let v1 = Vector(x: 1, y: 1) -/// v *= 2.0 //-> v1 = {2,2,0} -/// ```` -/// - parameter lhs: A Vector whose values will be multiplied by `rhs` -/// - parameter rhs: A scalar value -public func *= (lhs: inout Vector, rhs: Double) { - lhs.x *= rhs - lhs.y *= rhs - lhs.z *= rhs -} - -/// Transforms the left-hand vector by dividing each by the values of the right-hand vector -/// -/// ```` -/// let v1 = Vector(x: 1, y: 1) -/// v /= 2.0 //-> v = {0.5,0.5,0.0} -/// ```` -/// - parameter lhs: A Vector whose values will be divided by `rhs` -/// - parameter rhs: A scalar value -public func /= (lhs: inout Vector, rhs: Double) { - lhs.x /= rhs - lhs.y /= rhs - lhs.z /= rhs -} - -/// Returns a new vector whose coordinates are the sum of both input vectors -/// -/// ```` -/// let v1 = Vector(x: 1, y: 1) -/// let v2 = Vector(x: 1, y: 0) -/// v1+v2 //-> {2,1,0} -/// ```` -/// - parameter lhs: A Vector -/// - parameter rhs: A Vector -/// - returns: A new vector whose values are the sum of `lhs` and `rhs` -public func + (lhs: Vector, rhs: Vector) -> Vector { - return Vector(x: lhs.x + rhs.x, y: lhs.y + rhs.y, z: lhs.z + rhs.z) -} - -/// Returns a new vector whose coordinates are the subtraction of the right-hand vector from the left-hand vector -/// -/// ```` -/// var v1 = Vector(x: 1, y: 1) -/// var v2 = Vector(x: 1, y: 1) -/// v1-v2 //-> {0,0,0} -/// ```` -/// - parameter lhs: A Vector -/// - parameter rhs: A Vector -/// - returns: A new vector whose values are the difference of `lhs` and `rhs` -public func - (lhs: Vector, rhs: Vector) -> Vector { - return Vector(x: lhs.x - rhs.x, y: lhs.y - rhs.y, z: lhs.z - rhs.z) -} - -infix operator ⋅ - -/// Returns a new vector that is the dot product of the both input vectors. **Use this instead of v.dot(v)** -/// -/// ```` -/// let v1 = Vector(x: 1, y: 1) -/// let v2 = Vector(x: -1, y: 1) -/// v1 ⋅ v2 //-> 0.0 -/// ```` -/// - parameter lhs: A Vector -/// - parameter rhs: A Vector -/// - returns: The dot product of `lhs` and `rhs` -public func ⋅ (lhs: Vector, rhs: Vector) -> Double { - return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z -} - -/// Returns a new vector whose coordinates are the division of the left-hand vector coordinates by those of the right-hand vector -/// -/// ```` -/// var v1 = Vector(x: 1, y: 1) -/// var v2 = v1 / 2.0 -/// v2 //-> {0.5,0.5,0} -/// ```` -/// - parameter lhs: A Vector -/// - parameter rhs: A scalar -/// - returns: A new vector whose values are those of `lhs` divided by `rhs` -public func / (lhs: Vector, rhs: Double) -> Vector { - return Vector(x: lhs.x / rhs, y: lhs.y / rhs, z: lhs.z / rhs) -} - -/// Returns a new vector whose coordinates are the multiplication of the left-hand vector coordinates by those of the right-hand -/// vector -/// -/// ```` -/// var v1 = Vector(x: 1, y: 1) -/// var v2 = v2 * 2.0 -/// v2 //-> {2,2,0} -/// ```` -/// - parameter lhs: A Vector -/// - parameter rhs: A scalar -/// - returns: A new vector whose values are those of `lhs` multiplied by `rhs` -public func * (lhs: Vector, rhs: Double) -> Vector { - return Vector(x: lhs.x * rhs, y: lhs.y * rhs, z: lhs.z * rhs) -} - -/// Returns a new vector whose coordinates are the multiplication of the right-hand vector coordinates by the left-hand scalar -/// -/// ```` -/// var v1 = Vector(x: 1, y: 1) -/// var v2 = 2.0 * v2 -/// v2 //-> {2,2,0} -/// - parameter lhs: A scalar -/// - parameter rhs: A Vector -/// - returns: A new vector whose values are those of `lhs` divided by `rhs` -public func * (lhs: Double, rhs: Vector) -> Vector { - return Vector(x: rhs.x * lhs, y: rhs.y * lhs, z: rhs.z * lhs) -} - -/// Returns a new vector whose coordinates are the negative values of the receiver -/// -/// ```` -/// var v1 = Vector(x: 1, y: 1) -/// var v2 = -v1 -/// v2 //-> {-1,-1} -/// ```` -/// - parameter vector: A Vector -/// - returns: A new vector whose values are the negative of `vector` -public prefix func - (vector: Vector) -> Vector { - return Vector(x: -vector.x, y: -vector.y, z: -vector.z) -} diff --git a/C4/Info.plist b/C4/Info.plist deleted file mode 100644 index 1007fd9d..00000000 --- a/C4/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/C4/UI/Animation.swift b/C4/UI/Animation.swift deleted file mode 100644 index 92c321da..00000000 --- a/C4/UI/Animation.swift +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation - -private let AnimationCompletedEvent = "C4AnimationCompleted" -private let AnimationCancelledEvent = "C4AnimationCancelled" - -/// Defines an object that handles the creation of animations for specific actions and keys. -public class Animation { - /// Specifies the supported animation curves. - /// - /// ```` - /// animation.curve = .EaseIn - /// ```` - public enum Curve { - /// A linear animation curve causes an animation to occur evenly over its duration. - case linear - /// An ease-out curve causes the animation to begin quickly, and then slow down as it completes. - case easeOut - /// An ease-in curve causes the animation to begin slowly, and then speed up as it progresses. - case easeIn - /// An ease-in ease-out curve causes the animation to begin slowly, accelerate through the middle of its duration, and then slow again before completing. This is the default curve for most animations. - case easeInOut - } - - /// Determines if the animation plays in the reverse upon completion. - public var autoreverses = false - - /// Specifies the number of times the animation should repeat. - public var repeatCount = 0.0 - - /// If `true` the animation will repeat indefinitely. - public var repeats: Bool { - get { - return repeatCount > 0 - } - set { - if newValue { - repeatCount = Double.greatestFiniteMagnitude - } else { - repeatCount = 0 - } - } - } - - /// The duration of the animation, measured in seconds. - public var duration: TimeInterval = 1 - - /// The animation curve that the receiver will apply to the changes it is supposed to animate. - public var curve: Curve = .easeInOut - - private var completionObservers: [AnyObject] = [] - private var cancelObservers: [AnyObject] = [] - - static var stack = [Animation]() - static var currentAnimation: Animation? { - return stack.last - } - - ///Intializes an empty animation object. - public init() { - - } - - deinit { - let nc = NotificationCenter.default - for observer in completionObservers { - nc.removeObserver(observer) - } - for observer in cancelObservers { - nc.removeObserver(observer) - } - } - - /// Run the animation - func animate() {} - - /// Adds a completion observer to an animation. - /// - /// The completion observer listens for the end of the animation then executes a specified block of code. - /// - /// - parameter action: a block of code to be executed at the end of an animation. - /// - /// - returns: the observer object. - public func addCompletionObserver(_ action: @escaping () -> Void) -> AnyObject { - let nc = NotificationCenter.default - let observer = nc.addObserver(forName: NSNotification.Name(rawValue: AnimationCompletedEvent), object: self, queue: OperationQueue.current, using: { _ in - action() - }) - completionObservers.append(observer) - return observer - } - - /// Removes a specified observer from an animation. - /// - /// - parameter observer: the observer object to remove. - public func removeCompletionObserver(_ observer: AnyObject) { - let nc = NotificationCenter.default - nc.removeObserver(observer, name: NSNotification.Name(rawValue: AnimationCompletedEvent), object: self) - } - - /// Posts a completion event. - /// - /// This method is triggered when an animation completes. This can be used in place of `addCompletionObserver` for objects outside the scope of the context in which the animation is created. - public func postCompletedEvent() { - DispatchQueue.main.async { - NotificationCenter.default.post(name: Notification.Name(rawValue: AnimationCompletedEvent), object: self) - } - } - - /// Adds a cancel observer to an animation. - /// - /// The cancel observer listens for when an animation is canceled then executes a specified block of code. - /// - /// - parameter action: a block of code to be executed when an animation is canceled. - /// - /// - returns: the observer object. - public func addCancelObserver(_ action: @escaping () -> Void) -> AnyObject { - let nc = NotificationCenter.default - let observer = nc.addObserver(forName: NSNotification.Name(rawValue: AnimationCancelledEvent), object: self, queue: OperationQueue.current, using: { _ in - action() - }) - cancelObservers.append(observer) - return observer - } - - /// Removes a specified cancel observer from an animation. - /// - /// - parameter observer: the cancel observer object to remove. - public func removeCancelObserver(_ observer: AnyObject) { - let nc = NotificationCenter.default - nc.removeObserver(observer, name: NSNotification.Name(rawValue: AnimationCancelledEvent), object: self) - } - - /// Posts a cancellation event. - /// - /// This method is triggered when an animation is canceled. This can be used in place of `addCancelObserver` for objects outside the scope of the context in which the animation is created. - public func postCancelledEvent() { - DispatchQueue.main.async { - NotificationCenter.default.post(name: Notification.Name(rawValue: AnimationCancelledEvent), object: self) - } - } -} diff --git a/C4/UI/Arc.swift b/C4/UI/Arc.swift deleted file mode 100644 index 2a62ae3a..00000000 --- a/C4/UI/Arc.swift +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -/// Arc is a concrete subclass of Shape that has a special initialzer that creates an arc whose shape is defined by rotating around a specified point. -public class Arc: Shape { - - /// Creates an arc, whose edge is always drawn on the shorter circumference. - /// - /// ```` - /// let a = Arc(center: canvas.center,radius: 50, start: Double.pi, end: 2*Double.pi) - /// ```` - /// - /// - parameter center: The center-point of the arc - /// - parameter radius: The radius of the arc - /// - parameter start: The angle (in radians) that determines the starting point of the arc, measured from the x-axis in the current user space. - /// - parameter end: The angle (in radians) that determines the ending point of the arc, measured from the x-axis in the current user space. - public convenience init(center: Point, radius: Double, start: Double, end: Double) { - self.init(center: center, radius: radius, start: start, end: end, clockwise: end>start ? true : false) - } - - /// Creates an arc, whose edge is drawn based on the input for `clockwise`. - /// - /// ```` - /// let a = Arc(center: canvas.center,radius: 50, start: Double.pi, end: 2*Double.pi, clockwise: clockwise: false) - /// ```` - /// - /// - parameter center: The center-point of the arc - /// - parameter radius: The radius of the arc - /// - parameter start: The angle (in radians) that determines the starting point of the arc, measured from the x-axis in the current user space. - /// - parameter end: The angle (in radians) that determines the ending point of the arc, measured from the x-axis in the current user space. /// - /// - parameter clockwise: If true, the arc draws clockwise from start to end (counter clockwise if false) - public init(center: Point, radius: Double, start: Double, end: Double, clockwise: Bool) { - super.init() - - let arc = CGMutablePath() - arc.addArc(center: CGPoint(center), radius: CGFloat(radius), startAngle: CGFloat(start), endAngle: CGFloat(end), clockwise: !clockwise) - path = Path(path: arc) - adjustToFitPath() - } -} diff --git a/C4/UI/AudioPlayer.swift b/C4/UI/AudioPlayer.swift deleted file mode 100644 index aa48373f..00000000 --- a/C4/UI/AudioPlayer.swift +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit -import AVFoundation - -/// AudioPlayer provides playback of audio data from a file or memory. -/// -/// Using an audio player you can: -/// -/// Play sounds of any duration -/// -/// Play sounds from files or memory buffers -/// -/// Loop sounds -/// -/// Play multiple sounds simultaneously, one sound per audio player, with precise synchronization -/// -/// Control relative playback level, stereo positioning, and playback rate for each sound you are playing -/// -/// Seek to a particular point in a sound file, which supports such application features as fast forward and rewind -/// -/// Obtain data you can use for playback-level metering -public class AudioPlayer: NSObject, AVAudioPlayerDelegate { - internal var player: AVAudioPlayer! - - var filename: String! - - /// Initializes a new audio player from a given file name - /// ```` - /// let ap = AudioPlayer("audioTrackFileName") - /// ```` - public init?(_ name: String) { - do { - try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - print("Couldn't set up AVAudioSession") - } - - super.init() - - guard let url = Bundle.main.url(forResource: name, withExtension: nil) else { - print("Could not retrieve url for \(name)") - return nil - } - - guard let player = try? AVAudioPlayer(contentsOf: url) else { - print("Could not create player from contents of : \(url)") - return nil - } - - self.player = player - player.delegate = self - self.filename = name - } - - public convenience init?(copy original: AudioPlayer) { - self.init(original.filename) - } - - /// Plays a sound asynchronously. - public func play() { - player.play() - } - - /// Pauses playback; sound remains ready to resume playback from where it left off. - /// Calling pause leaves the audio player prepared to play; it does not release the audio hardware that was acquired upon - /// calling play or prepareToPlay. - public func pause() { - player.pause() - } - - /// Stops playback and undoes the setup needed for playback. - /// Calling this method, or allowing a sound to finish playing, undoes the setup performed upon calling the play or - /// prepareToPlay methods. - /// The stop method does not reset the value of the currentTime property to 0. In other words, if you call stop during - /// playback and then call play, playback resumes at the point where it left off. - public func stop() { - player.stop() - } - - /// Returns the total duration, in seconds, of the sound associated with the audio player. (read-only) - public var duration: Double { - return Double(player.duration) - } - - /// Returns true if the receiver's current playback rate > 0. Otherwise returns false. - public var playing: Bool { - return player.isPlaying - } - - /// The audio player’s stereo pan position. - /// By setting this property you can position a sound in the stereo field. A value of –1.0 is full left, 0.0 is center, and - /// 1.0 is full right. - public var pan: Double { - get { - return Double(player.pan) - } set(val) { - player.pan = clamp(Float(val), min: -1.0, max: 1.0) - } - } - - /// The playback volume for the audio player, ranging from 0.0 through 1.0 on a linear scale. - /// A value of 0.0 indicates silence; a value of 1.0 (the default) indicates full volume for the audio player instance. - /// Use this property to control an audio player’s volume relative to other audio output. - /// To provide UI in iOS for adjusting system audio playback volume, use the MPVolumeView class, which provides media - /// playback controls that users expect and whose appearance you can customize. - public var volume: Double { - get { - return Double(player.volume) - } set(val) { - player.volume = Float(val) - } - } - - /// The playback point, in seconds, within the timeline of the sound associated with the audio player. - /// If the sound is playing, currentTime is the offset of the current playback position, measured in seconds from the start - /// of the sound. If the sound is not playing, currentTime is the offset of where playing starts upon calling the play - /// method, measured in seconds from the start of the sound. - /// By setting this property you can seek to a specific point in a sound file or implement audio fast-forward and rewind - /// functions. - public var currentTime: Double { - get { - return player.currentTime - } set(val) { - player.currentTime = TimeInterval(val) - } - } - - /// The audio player’s playback rate. - /// This property’s default value of 1.0 provides normal playback rate. The available range is from 0.5 for half-speed - /// playback through 2.0 for double-speed playback. - /// To set an audio player’s playback rate, you must first enable rate adjustment as described in the enableRate property - /// description. - /// ```` - /// let ap = AudioPlayer("audioTrackFileName") - /// ap.enableRate = true - /// ap.rate = 0.5 - /// ap.play() - /// ```` - public var rate: Double { - get { - return Double(player.rate) - } set { - player.rate = Float(newValue) - } - } - - /// The number of times a sound will return to the beginning, upon reaching the end, to repeat playback. - /// A value of 0, which is the default, means to play the sound once. Set a positive integer value to specify the number of - /// times to return to the start and play again. For example, specifying a value of 1 results in a total of two plays of the - /// sound. Set any negative integer value to loop the sound indefinitely until you call the stop method. - /// Defaults to 1000000. - public var loops: Bool { - get { - return player.numberOfLoops > 0 ? true : false - } - set(val) { - if val { - player.numberOfLoops = 1000000 - } else { - player.numberOfLoops = 0 - } - } - } - - /// A Boolean value that specifies the audio-level metering on/off state for the audio player. - /// The default value for the meteringEnabled property is off (Boolean false). Before using metering for an audio player, you need to enable it by setting this - /// property to true. If player is an audio player instance variable of your controller class, you enable metering as shown here: - /// ```` - /// let ap = AudioPlayer("audioTrackFileName") - /// ap.meteringEnabled = true - /// ```` - public var meteringEnabled: Bool { - get { - return player.isMeteringEnabled - } set(v) { - player.isMeteringEnabled = v - } - } - - /// A Boolean value that specifies whether playback rate adjustment is enabled for an audio player. - /// To enable adjustable playback rate for an audio player, set this property to true after you initialize the player and before you call the prepareToPlay - /// instance method for the player. - public var enableRate: Bool { - get { - return player.enableRate - } set(v) { - player.enableRate = v - } - } - - /// Refreshes the average and peak power values for all channels of an audio player. - /// To obtain current audio power values, you must call this method before calling averagePowerForChannel: or peakPowerForChannel:. - /// ```` - /// let t = NSTimer.scheduledTimerWithTimeInterval(1.0/60.0, - /// target: self, - /// selector: "update", - /// userInfo: nil, - /// repeats: true) - /// let ap = AudioPlayer("audioTrackFileName") - /// ap.meteringEnabled = true - /// func update() { - /// ap.updateMeters() - /// } - /// ```` - public func updateMeters() { - player.updateMeters() - } - - /// Returns the average power for a given channel, in decibels, for the sound being played. - /// ```` - /// func update() { - /// let av = player.averagePower(channel: 0) - /// } - /// ```` - /// - parameter channel: The audio channel whose average power value you want to obtain. - /// - returns: A floating-point representation, in decibels, of a given audio channel’s current average power. - public func averagePower(_ channel: Int) -> Double { - return Double(player.averagePower(forChannel: channel)) - } - - /// Returns the peak power for a given channel, in decibels, for the sound being played. - /// ```` - /// func update() { - /// let pk = player.peakPower(channel: 0) - /// } - /// - parameter channel: The audio channel whose peak power value you want to obtain. - /// - returns: A floating-point representation, in decibels, of a given audio channel’s current peak power. - public func peakPower(_ channel: Int) -> Double { - return Double(player.peakPower(forChannel: channel)) - } -} diff --git a/C4/UI/Camera.swift b/C4/UI/Camera.swift deleted file mode 100644 index 3f62f6a1..00000000 --- a/C4/UI/Camera.swift +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright © 2016 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit -import AVFoundation - -public enum CameraPosition: Int { - case unspecified = 0 - case back - case front -} - -public class Camera: View { - public var capturedImage: Image? - public var quality = AVCaptureSession.Preset.photo - public var position = CameraPosition.front - - var imageOutput: AVCaptureStillImageOutput? - var captureDevice: AVCaptureDevice? - var previewView: CameraView? - var input: AVCaptureDeviceInput? - var stillImageOutput: AVCaptureStillImageOutput? - var captureSession: AVCaptureSession? - var didCaptureAction: (() -> Void)? - var orientationObserver: Any? - - class CameraView: UIView { - var previewLayer: PreviewLayer { - return self.layer as! PreviewLayer // swiftlint:disable:this force_cast - } - - override class var layerClass: AnyClass { - return PreviewLayer.self - } - } - - var previewLayer: PreviewLayer { - return self.cameraView.previewLayer - } - - var cameraView: CameraView { - return self.view as! CameraView // swiftlint:disable:this force_cast - } - - public override init(frame: Rect) { - super.init() - view = CameraView() - view.frame = CGRect(frame) - - previewLayer.backgroundColor = clear.cgColor - previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill - - orientationObserver = on(event: NSNotification.Name.UIDeviceOrientationDidChange) { [unowned self] in - self.updateOrientation() - } - } - - deinit { - if let observer = orientationObserver { - cancel(observer) - } - } - - public func startCapture(_ position: CameraPosition = .front) { - self.position = position - guard let cd = captureDevice(position) else { - print("Could not retrieve capture device for \(position)") - return - } - initializeInput(cd) - - guard input != nil else { - print("Could not initialize input for \(cd)") - return - } - initializeOutput(cd) - captureDevice = cd - - initializeCaptureSession() - - captureSession?.startRunning() - - updateOrientation() - } - - func captureDevice(_ position: CameraPosition) -> AVCaptureDevice? { - return AVCaptureDevice.devices(for: AVMediaType.video).first(where: { $0.position.rawValue == position.rawValue }) - } - - func updateOrientation() { - guard let connection = previewLayer.connection, connection.isVideoOrientationSupported else { - return - } - - switch UIApplication.shared.statusBarOrientation { - case .portraitUpsideDown: - connection.videoOrientation = .portraitUpsideDown - case .landscapeLeft: - connection.videoOrientation = .landscapeLeft - case .landscapeRight: - connection.videoOrientation = .landscapeRight - default: - connection.videoOrientation = .portrait - } - } - - func initializeInput(_ device: AVCaptureDevice) { - if input == nil { - do { - input = try AVCaptureDeviceInput(device: device) - } catch { - print("Could not create input for device: \(device)") - return - } - } - } - - func initializeOutput(_ device: AVCaptureDevice) { - if stillImageOutput == nil { - stillImageOutput = AVCaptureStillImageOutput() - stillImageOutput?.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG] - } - } - - func initializeCaptureSession() { - if captureSession == nil { - captureSession = AVCaptureSession() - previewLayer.session = captureSession - } - let session = captureSession! - - session.sessionPreset = quality - - for input in session.inputs { - session.removeInput(input) - } - session.addInput(input!) - - for output in session.outputs { - session.removeOutput(output) - } - session.addOutput(stillImageOutput!) - } - - public func captureImage() { - guard stillImageOutput?.isCapturingStillImage == false else { - print("Still capturing, please wait until I'm done until you put me to work again") - return - } - - guard let connection = stillImageOutput?.connection(with: AVMediaType.video) else { - return - } - - updateOrientation() - connection.videoOrientation = previewLayer.connection!.videoOrientation - - stillImageOutput?.captureStillImageAsynchronously(from: connection) { imageSampleBuffer, _ in - guard imageSampleBuffer != nil else { - print("Couldn't capture image from still image output") - return - } - - let data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageSampleBuffer! as CMSampleBuffer) - - if let img = UIImage(data: data!) { - self.capturedImage = Image(uiimage: self.orientRawImage(img)) - self.didCaptureAction?() - } - } - } - - func orientRawImage(_ image: UIImage) -> UIImage { - guard let cgimg = image.cgImage, let videoOrientation = previewLayer.connection?.videoOrientation else { - return image - } - - var orientation: UIImageOrientation - let shouldFlip = position == .front - - switch videoOrientation { - case .landscapeLeft: - orientation = shouldFlip ? .upMirrored : .down - case .landscapeRight: - orientation = shouldFlip ? .downMirrored : .up - case .portrait: - orientation = shouldFlip ? .leftMirrored : .right - case .portraitUpsideDown: - orientation = shouldFlip ? .rightMirrored : .left - } - return UIImage(cgImage: cgimg, scale: image.scale, orientation: orientation) - } - - public func didCaptureImage(_ action: (() -> Void)?) { - didCaptureAction = action - } -} - -class PreviewLayer: AVCaptureVideoPreviewLayer { - -} diff --git a/C4/UI/CanvasController.swift b/C4/UI/CanvasController.swift deleted file mode 100644 index a8d38a8a..00000000 --- a/C4/UI/CanvasController.swift +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -/// The CanvasController class provides the infrastructure for managing the views of your iOS apps. A canvas controller manages a set of views that make up a portion of your app’s user interface. It is responsible for loading and disposing of those views, for managing interactions with those views, and for coordinating responses with any appropriate data objects. Canvas controllers also coordinate their efforts with other controller objects—including other view controllers—and help manage your app’s overall interface. -open class CanvasController: UIViewController { - - /// Called after the controller'€™s view is loaded into memory. - /// - /// This override disables implicit CALayer animations, calls `setup()` and then re-enables animations. - /// - /// You should **not** override this method, instead use **setup()**. - open override func viewDidLoad() { - canvas.backgroundColor = C4Grey - ShapeLayer.disableActions = true - self.setup() - ShapeLayer.disableActions = false - } - - /// Called during the controller's `viewDidLoad()` method. - /// - /// This method should be used to set up any objects or behaviours necessary when the controller's view loads. - open func setup() { - } - - #if os(iOS) - /// Overrides default behaviour of showing the app's status bar. Defaults to `true` - /// - /// - returns: a boolean value representing whether or not the app should hide its status bar - open override var prefersStatusBarHidden: Bool { - return true - } - #endif -} diff --git a/C4/UI/Circle.swift b/C4/UI/Circle.swift deleted file mode 100644 index 04a436b7..00000000 --- a/C4/UI/Circle.swift +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -///Circle is a concrete subclass of Ellipse that has a special initialzer that creates a uniform ellipse. -open class Circle: Ellipse { - /// Creates a circle. - /// - /// ```` - /// let c = Circle(center: canvas.center, radius: 50) - /// canvas.add(c) - /// ```` - /// - /// - parameter center: The center-point of the circle - /// - parameter radius: The radius of the circle - convenience public init(center: Point, radius: Double) { - let frame = Rect(center.x-radius, center.y-radius, radius * 2, radius * 2) - self.init(frame: frame) - } -} diff --git a/C4/UI/Curve.swift b/C4/UI/Curve.swift deleted file mode 100644 index 132351d0..00000000 --- a/C4/UI/Curve.swift +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -/// Curve is a concrete subclass of Shape that has a special initialzer that creates an bezier whose shape is defined by its end points and two control points. -public class Curve: Shape { - - /// The beginning and end points of the receiver. Animatable. - public var endPoints = (Point(), Point()) { - didSet { - updatePath() - adjustToFitPath() - } - } - - /// The control points of the receiver. Animatable. - public var controlPoints = (Point(), Point()) { - didSet { - updatePath() - adjustToFitPath() - } - } - - /// The center of the curve's view. - public override var center: Point { - get { - return Point(view.center) - } - set { - let diff = newValue - center - batchUpdates { - self.endPoints.0 += diff - self.endPoints.1 += diff - self.controlPoints.0 += diff - self.controlPoints.1 += diff - } - } - } - - /// The origin of the curve's view. - public override var origin: Point { - get { - return Point(view.frame.origin) - } - set { - let diff = newValue - origin - batchUpdates { - self.endPoints.0 += diff - self.endPoints.1 += diff - self.controlPoints.0 += diff - self.controlPoints.1 += diff - } - } - } - - /// Creates a bezier curve. - /// - /// ```` - /// let crv = Curve(a: Point(), b: Point(0,50), c: Point(100,50), d: Point(100,0)) - /// ```` - /// - /// - parameter begin: The beginning point of the curve. - /// - parameter control0: The first control point used to define the shape of the curve. - /// - parameter control1: The second control point used to define the shape of the curve. - /// - parameter end: The end point of the curve. - convenience public init(begin: Point, control0: Point, control1: Point, end: Point) { - self.init() - endPoints = (begin, end) - controlPoints = (control0, control1) - updatePath() - adjustToFitPath() - } - - private var pauseUpdates = false - func batchUpdates(_ updates: () -> Void) { - pauseUpdates = true - updates() - pauseUpdates = false - updatePath() - adjustToFitPath() - } - - override func updatePath() { - if pauseUpdates { - return - } - - let curve = CGMutablePath() - curve.move(to: CGPoint(endPoints.0)) - curve.addCurve(to: CGPoint(endPoints.1), control1: CGPoint(controlPoints.0), control2: CGPoint(controlPoints.1), transform: CGAffineTransform.identity) - - path = Path(path: curve) - } -} diff --git a/C4/UI/Ellipse.swift b/C4/UI/Ellipse.swift deleted file mode 100644 index 66ed9a23..00000000 --- a/C4/UI/Ellipse.swift +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// Ellipse is a concrete subclass of Shape that has a special initialzer that creates an ellipse whose shape is defined by the object's frame. -open class Ellipse: Shape { - - /// Creates an ellipse. - /// - /// ```` - /// let r = Rect(0,0,100,200) - /// let e = Ellipse(frame: r) - /// ```` - /// - /// - parameter frame: The frame within which to draw an ellipse that touches each of the four sides of the frame. - public override init(frame: Rect) { - super.init() - view.frame = CGRect(frame) - updatePath() - } - - override func updatePath() { - let newPath = Path() - newPath.addEllipse(bounds) - path = newPath - } -} diff --git a/C4/UI/Filter.swift b/C4/UI/Filter.swift deleted file mode 100644 index 86a9b9aa..00000000 --- a/C4/UI/Filter.swift +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// The Filter protocol declares a variable and a method that are required by Filter object so it can properly interface with Core Image. -public protocol Filter { - /// The name of the Core Image filter (e.g. "CIBloom") - var filterName: String { get } - - /// Creates a Core Image filter using specified input image. - /// - /// This method is used internally by Image when applying filters to its layer's contents. - /// - /// ```` - /// public func createCoreImageFilter(inputImage: CIImage) -> CIFilter { - /// let filter = CIFilter(name: filterName)! - /// filter.setDefaults() - /// filter.setValue(radius, forKey:"inputRadius") - /// filter.setValue(intensity, forKey:"inputIntensity") - /// filter.setValue(inputImage, forKey:"inputImage") - /// return filter - /// } - /// ```` - /// - /// A specific filter will have internal properties whose values are applied to the CIFilter being generated. However, those properties are object / filter specific and so cannot be defined in the protocol. - /// - /// - parameter inputImage: A CIImage to be used in generating the filter. - /// - /// - returns: A CIFilter whose name is `filterName` and whose contents are based on `inputImage`. - func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter -} diff --git a/C4/UI/Filters/Bloom.swift b/C4/UI/Filters/Bloom.swift deleted file mode 100644 index 9205cbcd..00000000 --- a/C4/UI/Filters/Bloom.swift +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -///Softens edges and applies a pleasant glow to an image. -public struct Bloom: Filter { - ///The name of the corresponding CIFilter - public let filterName = "CIBloom" - ///A Double value whose attribute type is CIAttributeTypeDistance and whose display name is Radius. Default value: 10.0 - public var radius: Double = 10.0 - ///A Double value whose attribute type is CIAttributeTypeScalar and whose display name is Intensity. Default value: 1.0 - public var intensity: Double = 1.0 - - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(radius, forKey: "inputRadius") - filter.setValue(intensity, forKey: "inputIntensity") - filter.setValue(inputImage, forKey: "inputImage") - return filter - } -} diff --git a/C4/UI/Filters/Checkerboard.swift b/C4/UI/Filters/Checkerboard.swift deleted file mode 100644 index 36900240..00000000 --- a/C4/UI/Filters/Checkerboard.swift +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Generates a checkerboard pattern -/// -/// ```` -/// let image = Image(frame: canvas.frame) -/// image.generate(Checkerboard()) -/// canvas.add(image) -/// ```` -public struct Checkerboard: Generator { - ///The name of the Core Image filter. - public let filterName = "CICheckerboardGenerator" - ///The colors of the checkerboard. Defaults to: [C4Pink, C4Blue] - public var colors: [Color] = [C4Pink, C4Blue] - ///The center of the pattern. Defaults to {0,0} - public var center: Point = Point() - ///The sharpness of the pattern's edges. Defaults to 1.0 - public var sharpness: Double = 1 - ///The width of the pattern's segments. Defaults to 5.0 - public var width: Double = 5.0 - - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - returns: The new CIFilter object. - public func createCoreImageFilter() -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(CIColor(colors[0]), forKey: "inputColor0") - filter.setValue(CIColor(colors[1]), forKey: "inputColor1") - filter.setValue(width, forKey: "inputWidth") - filter.setValue(CIVector(cgPoint: CGPoint(center)), forKey: "inputCenter") - filter.setValue(sharpness, forKey: "inputSharpness") - return filter - } -} diff --git a/C4/UI/Filters/ColorBurn.swift b/C4/UI/Filters/ColorBurn.swift deleted file mode 100644 index 6e5b54fd..00000000 --- a/C4/UI/Filters/ColorBurn.swift +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Darkens the background image samples to reflect the source image samples. -/// -/// The following example uses an image to burn itself. -/// ```` -/// let logo = Image("logo") -/// var colorburn = ColorBurn() -/// colorburn.background = logo -/// logo.apply(colorburn) -/// canvas.add(logo) -/// ```` -public struct ColorBurn: Filter { - /// The name of the Core Image filter. - public let filterName = "CIColorBurnBlendMode" - /// The background image to use for the burn. - public var background: Image = Image() - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(background.ciImage, forKey: "inputImage") - filter.setValue(inputImage, forKey: "inputBackgroundImage") - return filter - } -} diff --git a/C4/UI/Filters/DotScreen.swift b/C4/UI/Filters/DotScreen.swift deleted file mode 100644 index 488ce952..00000000 --- a/C4/UI/Filters/DotScreen.swift +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Simulates the dot patterns of a halftone screen. -/// -/// ```` -/// let logo = Image("logo") -/// logo.apply(DotScreen()) -/// canvas.add(logo) -/// ```` -public struct DotScreen: Filter { - /// The name of the Core Image filter. - public let filterName = "CIDotScreen" - /// The center of the pattern. Defaults to {0,0} - public var center: Point = Point() - /// The width of the dots. Defaults to 2.0 - public var width: Double = 2.0 - /// The angle of the pattern. Defaults to 0.0 - public var angle: Double = 0 - /// The sharpness of the edges of the pattern. Defaults to 0.5 - public var sharpness: Double = 0.5 - - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(width, forKey: "inputWidth") - filter.setValue(angle, forKey: "inputAngle") - filter.setValue(sharpness, forKey: "inputSharpness") - filter.setValue(CIVector(cgPoint: CGPoint(center)), forKey: "inputCenter") - filter.setValue(inputImage, forKey: "inputImage") - return filter - } -} diff --git a/C4/UI/Filters/GaussianBlur.swift b/C4/UI/Filters/GaussianBlur.swift deleted file mode 100644 index 506c7d1a..00000000 --- a/C4/UI/Filters/GaussianBlur.swift +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Spreads source pixels by an amount specified by a Gaussian distribution. -/// -/// ```` -/// let logo = Image("logo") -/// logo.apply(GaussianBlur()) -/// canvas.add(logo) -/// ```` -public struct GaussianBlur: Filter { - /// The name of the Core Image filter. - public let filterName = "CIGaussianBlur" - - /// The radius of the blur. Defaults to 10.0 - public var radius: Double - - /// Initializes a new filter - /// - parameter radius: a Double value - public init(radius: Double = 5.0) { self.radius = radius } - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(radius, forKey: "inputRadius") - filter.setValue(inputImage, forKey: "inputImage") - return filter - } -} diff --git a/C4/UI/Filters/HueAdjust.swift b/C4/UI/Filters/HueAdjust.swift deleted file mode 100644 index 8e7514c3..00000000 --- a/C4/UI/Filters/HueAdjust.swift +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Changes the overall hue, or tint, of the source pixels. -public struct Hue: Filter { - ///The name of the Core Image filter - public let filterName = "CIHueAdjust" - ///The angle to apply to the hue filter. Default value: 1.0 - public var angle: Double = 1.0 - - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(angle, forKey: "inputAngle") - filter.setValue(inputImage, forKey: "inputImage") - return filter - } -} diff --git a/C4/UI/Filters/LinearGradient.swift b/C4/UI/Filters/LinearGradient.swift deleted file mode 100644 index 763cdd2b..00000000 --- a/C4/UI/Filters/LinearGradient.swift +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Generates a gradient that varies along a linear axis between two defined endpoints. -/// -/// ```` -/// let logo = Image(frame: canvas.frame) -/// var gradient = LinearGradient() -/// gradient.points = [logo.origin,logo.frame.max] -/// logo.generate(gradient) -/// canvas.add(logo) -/// ```` -public struct LinearGradient: Generator { - /// The name of the Core Image filter. - public let filterName = "CISmoothLinearGradient" - /// The colors of the filter. Defaults to `[C4Pink, C4Blue]` - public var colors: [Color] = [C4Pink, C4Blue] - /// The endpoints of the filter. Defaults to `[Point(), Point(100, 100)]` - public var points: [Point] = [Point(), Point(100, 100)] - - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - returns: The new CIFilter object. - public func createCoreImageFilter() -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(CIColor(colors[0]), forKey: "inputColor0") - filter.setValue(CIColor(colors[1]), forKey: "inputColor1") - filter.setValue(CIVector(cgPoint: CGPoint(points[0])), forKey: "inputPoint0") - filter.setValue(CIVector(cgPoint: CGPoint(points[1])), forKey: "inputPoint1") - return filter - } -} diff --git a/C4/UI/Filters/Sepia.swift b/C4/UI/Filters/Sepia.swift deleted file mode 100644 index 3de70d14..00000000 --- a/C4/UI/Filters/Sepia.swift +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Maps the colors of an image to various shades of brown. -/// -/// ```` -/// let logo = Image("logo") -/// logo.apply(Sepia()) -/// canvas.add(logo) -/// ```` -public struct Sepia: Filter { - /// The name of the Core Image filter. - public let filterName = "CISepiaTone" - /// The intensity of the filter. Defaults to 1.0 - public var intensity: Double = 1.0 - - ///Initializes a new filter - public init() {} - - /// Initializes a new filter, with a specified intensity. - /// - /// - parameter intensity: a Double value - public init(intensity: Double) { self.intensity = intensity } - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(intensity, forKey: "inputIntensity") - filter.setValue(inputImage, forKey: "inputImage") - return filter - } -} diff --git a/C4/UI/Filters/Sharpen.swift b/C4/UI/Filters/Sharpen.swift deleted file mode 100644 index 67eeb807..00000000 --- a/C4/UI/Filters/Sharpen.swift +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -///Increases image detail by sharpening. -public struct Sharpen: Filter { - /// The name of the Core Image filter (e.g. "CIBloom") - public let filterName = "CISharpenLuminance" - /// A Double value by which to sharpen the image. Default value: 0.4 - public var sharpness: Double = 0.4 - - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(sharpness, forKey: "inputSharpness") - filter.setValue(inputImage, forKey: "inputImage") - return filter - } -} diff --git a/C4/UI/Filters/Twirl.swift b/C4/UI/Filters/Twirl.swift deleted file mode 100644 index 5e824d88..00000000 --- a/C4/UI/Filters/Twirl.swift +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// Rotates pixels around a point to give a twirling effect. -/// -/// ```` -/// let logo = Image("logo") -/// logo.apply(Twirl()) -/// canvas.add(logo) -/// ```` -public struct Twirl: Filter { - /// The name of the Core Image filter. - public let filterName = "CITwirlDistortion" - /// The center of the twirl effet. Defaults to {0,0} - public var center: Point = Point() - /// The radius of the twirl effect. Defaults to 100.0 - public var radius: Double = 100.0 - /// The angle of the twirl effect. Defaults to 𝞹 - public var angle: Double = Double.pi - - ///Initializes a new filter - public init() {} - - /// Applies the properties of the receiver to create a new CIFilter object - /// - /// - parameter inputImage: The image to use as input to the filter. - /// - returns: The new CIFilter object. - public func createCoreImageFilter(_ inputImage: CIImage) -> CIFilter { - let filter = CIFilter(name: filterName)! - filter.setDefaults() - filter.setValue(radius, forKey: "inputRadius") - filter.setValue(angle, forKey: "inputAngle") - let filterSize = inputImage.extent.size - let vector = CIVector(x: CGFloat(center.x) * filterSize.width, y: CGFloat(1.0 - center.y) * filterSize.height) - filter.setValue(vector, forKey: "inputCenter") - filter.setValue(inputImage, forKey: "inputImage") - return filter - } -} diff --git a/C4/UI/Font.swift b/C4/UI/Font.swift deleted file mode 100644 index 902a8352..00000000 --- a/C4/UI/Font.swift +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -/// Font objects represent fonts to an application, providing access to characteristics of the font and assistance in laying out glyphs relative to one another. -public class Font { - internal var internalFont: UIFont! - - /// The UIFont representation of the receiver. - /// ```` - /// let uif = font.UIFont - /// ```` - public var uiifont: UIFont { - return internalFont - } - - /// Initializes a new Font using the specified font name and size. - /// ```` - /// let f = Font("Helvetica", 20) - /// ```` - /// - parameter name: The name of the font. - /// - parameter size: The point-size of the font. - public init?(name: String, size: Double) { - guard let font = UIFont(name: name, size: CGFloat(size)) else { - return nil - } - internalFont = font - } - - /// Initializes a new Font using the specified font name and default size of 12.0 pt. - /// ```` - /// let f = Font("Helvetica") - /// ```` - /// - parameter name: The name of the font - public convenience init?(name: String) { - self.init(name: name, size: 12.0) - } - - /// Initializes a new Font using a specified UIFont. - /// ```` - /// if let uif = UIFont(name: "Helvetica", size: 24) { - /// let f = Font(font: uif) - /// } - /// ```` - /// - parameter font: A UIFont - public init(font: UIFont) { - internalFont = font - } - - /// Returns an array of font family names available on the system. - /// - returns: An array of String objects, each of which contains the name of a font family. - public class func familyNames() -> [String] { - return UIFont.familyNames - } - - /// Returns an array of font names available in a particular font family. - /// ```` - /// for n in Font.fontNames("Avenir Next") { - /// println(n) - /// } - /// ```` - /// - parameter familyName: The name of the font family. - /// - returns: An array of String objects, each of which contains a font name associated with the specified family. - public class func fontNames(_ familyName: String) -> [String] { - return UIFont.fontNames(forFamilyName: familyName) - } - - /// Returns the font object used for standard interface items in the specified size. - /// ```` - /// let f = Font.systemFont(20) - /// ```` - /// - parameter size: The size (in points) to which the font is scaled. - /// - returns: A font object of the specified size. - public class func systemFont(_ size: Double) -> Font { - return Font(font: UIFont.systemFont(ofSize: CGFloat(size))) - } - - /// Returns the font object used for standard interface items that are rendered in boldface type in the specified size. - /// ```` - /// let f = Font.boldSystemFont(20) - /// ```` - /// - parameter size: The size (in points) to which the font is scaled. - /// - returns: A font object of the specified size. - public class func boldSystemFont(_ size: Double) -> Font { - return Font(font: UIFont.boldSystemFont(ofSize: CGFloat(size))) - } - - /// Returns the font object used for standard interface items that are rendered in italic type in the specified size. - /// ```` - /// let f = Font.italicSystemFont(20) - /// ```` - /// - parameter size: The size (in points) to which the font is scaled. - /// - returns: A font object of the specified size. - public class func italicSystemFont(_ size: Double) -> Font { - return Font(font: UIFont.italicSystemFont(ofSize: CGFloat(size))) - } - - /// Returns a font object that is the same as the receiver but which has the specified size instead. - /// ```` - /// let f = Font(name: "Avenir Next") - /// let f2 = f.font(20) - /// ```` - /// - parameter size: The desired size (in points) of the new font object. - /// - returns: A font object of the specified size. - public func font(_ size: Double) -> Font { - return Font(font: internalFont!.withSize(CGFloat(size))) - } - - /// The font family name. (read-only) - /// A family name is a name such as Times New Roman that identifies one or more specific fonts. The value in this property - /// is intended for an application’s internal usage only and should not be displayed. - public var familyName: String { - return internalFont!.familyName - } - - /// The font face name. (read-only) - /// The font name is a name such as HelveticaBold that incorporates the family name and any specific style information for - /// the font. The value in this property is intended for an application’s internal usage only and should not be displayed. - /// ```` - /// let f = Font(name: "Avenir Next") - /// let n = f.fontName - /// ```` - public var fontName: String { - return internalFont!.fontName - } - - /// The receiver’s point size, or the effective vertical point size for a font with a nonstandard matrix. (read-only) Defaults to 12.0 - public var pointSize: Double { - return Double(internalFont!.pointSize) - } - - /// The top y-coordinate, offset from the baseline, of the receiver’s longest ascender. (read-only) - /// The ascender value is measured in points. - public var ascender: Double { - return Double(internalFont!.ascender) - } - - /// The bottom y-coordinate, offset from the baseline, of the receiver’s longest descender. (read-only) - /// The descender value is measured in points. This value may be positive or negative. For example, if the longest descender - /// extends 2 points below the baseline, this method returns -2.0 . - public var descender: Double { - return Double(internalFont!.descender) - } - - /// The receiver’s cap height information. (read-only) - /// This value measures (in points) the height of a capital character. - public var capHeight: Double { - return Double(internalFont!.capHeight) - } - - /// The x-height of the receiver. (read-only) - /// This value measures (in points) the height of the lowercase character "x". - public var xHeight: Double { - return Double(internalFont!.xHeight) - } - - /// The height of text lines (measured in points). (read-only) - public var lineHeight: Double { - return Double(internalFont!.lineHeight) - } - - #if os(iOS) - /// Returns the standard font size used for labels. - /// - returns: The standard label font size in points. - public var labelFontSize: Double { - return Double(UIFont.labelFontSize) - } - - /// Returns the standard font size used for buttons. - /// - returns: The standard button font size in points. - public var buttonFontSize: Double { - return Double(UIFont.buttonFontSize) - } - - /// Returns the size of the standard system font. - /// - returns: The standard system font size in points. - public var systemFontSize: Double { - return Double(UIFont.systemFontSize) - } - - /// Returns the size of the standard small system font. - /// - returns: The standard small system font size in points. - public var smallSystemFontSize: Double { - return Double(UIFont.smallSystemFontSize) - } - #endif - - /// Returns a CGFontRef version of the receiver. - public var cgFont: CGFont? { - return CGFont(fontName as NSString) - } - - /// Returns a CTFontRef version of the receiver. - public var ctFont: CTFont { - return CTFontCreateWithNameAndOptions(fontName as CFString, CGFloat(pointSize), nil, []) - } -} diff --git a/C4/UI/Generator.swift b/C4/UI/Generator.swift deleted file mode 100644 index d2bcb11c..00000000 --- a/C4/UI/Generator.swift +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreImage - -/// The Generator protocol declares a variable and a method that are required by Generator object so it can properly interface with Core Image. -public protocol Generator { - /// The name of the Core Image filter (e.g. "CICheckerboardGenerator") - var filterName: String { get } - - /// Creates a Core Image filter using specified input image. - /// - /// This method is used internally by Image when applying filters to its layer's contents. - /// - /// ```` - /// public func createCoreImageFilter(inputImage: CIImage) -> CIFilter { - /// let filter = CIFilter(name: filterName)! - /// filter.setDefaults() - /// filter.setValue(CIColor(colors[0]), forKey:"inputColor0") - /// filter.setValue(CIColor(colors[1]), forKey:"inputColor1") - /// filter.setValue(width, forKey: "inputWidth") - /// filter.setValue(CIVector(CGPoint: CGPoint(center)), forKey:"inputCenter") - /// filter.setValue(sharpness, forKey: "inputSharpness") - /// return filter - /// } - /// ```` - /// - /// A specific filter will have internal properties whose values are applied to the CIFilter being generated. However, those properties are object / filter specific and so cannot be defined in the protocol. - /// - /// - returns: A CIFilter whose name is `filterName` and whose contents are generated by the filter itself. - func createCoreImageFilter() -> CIFilter -} diff --git a/C4/UI/Gradient.swift b/C4/UI/Gradient.swift deleted file mode 100644 index 90ca8fd6..00000000 --- a/C4/UI/Gradient.swift +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright © 2015 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -///The Gradient class draws a color gradient over its background color, filling the shape of the view (including rounded corners). -public class Gradient: View { - class GradientView: UIView { - var gradientLayer: GradientLayer { - return self.layer as! GradientLayer // swiftlint:disable:this force_cast - } - - override class var layerClass: AnyClass { - return GradientLayer.self - } - } - - ///The background layer of the receiver. - public var gradientLayer: GradientLayer { - return gradientView.gradientLayer - } - - internal var gradientView: GradientView { - return view as! GradientView // swiftlint:disable:this force_cast - } - - ///An array of Color objects defining the color of each gradient stop. Animatable. - public var colors: [Color] { - get { - if let cgcolors = gradientLayer.colors as? [CGColor] { - return cgcolors.map({ Color($0) }) - } - return [C4Blue, C4Pink] - } set { - assert(newValue.count >= 2, "colors must have at least 2 elements") - - let cgcolors = newValue.map({ $0.cgColor }) - self.gradientLayer.colors = cgcolors - } - } - - ///An optional array of Double values defining the location of each gradient stop. Animatable. - /// - ///Defaults to [0,1] - public var locations: [Double] { - get { - if let locations = gradientLayer.locations as? [Double] { - return locations - } - return [] - } set { - let numbers = newValue.map({ NSNumber(value: $0) }) - gradientLayer.locations = numbers - } - } - - ///The start point of the gradient when drawn in the layer’s coordinate space. Animatable. - /// - ///Defaults to the top-left corner of the frame {0.0,0.0} - public var startPoint: Point { - get { - return Point(gradientLayer.startPoint) - } set { - gradientLayer.startPoint = CGPoint(newValue) - } - } - - ///The end point of the gradient when drawn in the layer’s coordinate space. Animatable. - /// - ///Defaults to the top-right corner of the frame {1.0,0.0} - public var endPoint: Point { - get { - return Point(gradientLayer.endPoint) - } set { - gradientLayer.endPoint = CGPoint(newValue) - } - } - - /// The current rotation value of the view. Animatable. - /// - returns: A Double value representing the cumulative rotation of the view, measured in Radians. - public override var rotation: Double { - get { - if let number = gradientLayer.value(forKeyPath: Layer.rotationKey) as? NSNumber { - return number.doubleValue - } - return 0.0 - } - set { - gradientLayer.setValue(newValue, forKeyPath: Layer.rotationKey) - } - } - - /// Initializes a new Gradient. - /// - /// - parameter frame: A Rect that defines the frame for the gradient's view. - /// - parameter colors: An array of Color objects that define the gradient's colors. Defaults to [C4Blue, C4Purple]. - /// - parameter locations: An array of Double values that define the location of each gradient stop. Defaults to [0,1] - public convenience override init(frame: Rect) { - self.init() - self.view = GradientView(frame: CGRect(frame)) - self.colors = [C4Pink, C4Purple] - self.locations = [0, 1] - self.startPoint = Point() - self.endPoint = Point(0, 1) - } - - public convenience init(copy original: Gradient) { - self.init(frame: original.frame) - self.colors = original.colors - self.locations = original.locations - copyViewStyle(original) - } -} diff --git a/C4/UI/GradientLayer.swift b/C4/UI/GradientLayer.swift deleted file mode 100644 index 6b82544c..00000000 --- a/C4/UI/GradientLayer.swift +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright © 2015 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore - -/// The GradientLayer class draws a color gradient over its background color, filling the shape of the layer (including rounded corners) -public class GradientLayer: CAGradientLayer { - /// A boolean value that, when true, prevents the animation of a shape's properties. - /// - /// This value can be set globally, after which changes to any shape's properties will be immediate. - public static var disableActions = true - - private var _rotation = 0.0 - - /// The value of the receiver's current rotation state. - /// This value is cumulative, and can represent values beyong +/- π - @objc public dynamic var rotation: Double { - return _rotation - } - - /// This method searches for the given action object of the layer. Actions define dynamic behaviors for a layer. For example, the animatable properties of a layer typically have corresponding action objects to initiate the actual animations. When that property changes, the layer looks for the action object associated with the property name and executes it. You can also associate custom action objects with your layer to implement app-specific actions. - /// - /// - parameter key: The identifier of the action. - /// - returns: the action object assigned to the specified key. - public override func action(forKey key: String) -> CAAction? { - if ShapeLayer.disableActions == true { - return nil - } - - if key != "colors" { - return super.action(forKey: key) - } - - let animation: CABasicAnimation - if let viewAnimation = ViewAnimation.stack.last as? ViewAnimation, viewAnimation.spring != nil { - animation = CASpringAnimation(keyPath: key) - } else { - animation = CABasicAnimation(keyPath: key) - } - - animation.configureOptions() - animation.fromValue = self.colors - - return animation - } -} diff --git a/C4/UI/Image+ColorAt.swift b/C4/UI/Image+ColorAt.swift deleted file mode 100644 index b7d40e3f..00000000 --- a/C4/UI/Image+ColorAt.swift +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -public extension Image { - /// Initializes and returns a new cgimage from the color at a specified point in the receiver. - /// ```` - /// let image = cgimage(at: CGPoint()) - /// ```` - /// - parameter at: a CGPoint. - func cgimage(at point: CGPoint) -> CGImage? { - let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue) - - guard let offscreenContext = CGContext(data: nil, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo.rawValue) else { - print("Could not create offscreenContext") - return nil - } - - offscreenContext.translateBy(x: CGFloat(-point.x), y: CGFloat(-point.y)) - - layer?.render(in: offscreenContext) - - guard let image = offscreenContext.makeImage() else { - print("Could not create pixel image") - return nil - } - return image - } - - /// Initializes and returns a new Color from a Point within the receiver. - /// ```` - /// let color = img.color(at: Point()) - /// ```` - /// - parameter at: a Point. - public func color(at point: Point) -> Color { - - guard bounds.contains(point) else { - print("Point is outside the image bounds") - return clear - } - - guard let pixelImage = cgimage(at: CGPoint(point)) else { - print("Could not create pixel Image from CGImage") - return clear - } - - let imageProvider = pixelImage.dataProvider - let imageData = imageProvider?.data - let data: UnsafePointer = CFDataGetBytePtr(imageData) - - return Color(red: Double(data[1])/255.0, - green: Double(data[2])/255.0, - blue: Double(data[3])/255.0, - alpha: Double(data[0])/255.0) - } -} diff --git a/C4/UI/Image+Crop.swift b/C4/UI/Image+Crop.swift deleted file mode 100644 index 1714ee81..00000000 --- a/C4/UI/Image+Crop.swift +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -extension Image { - /// Crops the receiver's contents to the specified frame within the receiver's coordinate space. - /// - /// - parameter rect: a Rect - public func crop(_ rect: Rect) { - let intersection = CGRect(rect).intersection(CGRect(self.bounds)) - if intersection.isNull { return } - let inputRectangle = CGRect( - x: intersection.origin.x, - y: CGFloat(self.height) - intersection.origin.y - intersection.size.height, - width: intersection.size.width, - height: intersection.size.height) - - let crop = CIFilter(name: "CICrop")! - crop.setDefaults() - crop.setValue(CIVector(cgRect: inputRectangle), forKey: "inputRectangle") - crop.setValue(ciImage, forKey: "inputImage") - - if let outputImage = crop.outputImage { - self.output = outputImage - self.imageView.image = UIImage(ciImage: output) - self.frame = Rect(intersection) - } else { - print("Failed ot generate outputImage: \(#function)") - } - } -} diff --git a/C4/UI/Image+Filter.swift b/C4/UI/Image+Filter.swift deleted file mode 100644 index 96684bee..00000000 --- a/C4/UI/Image+Filter.swift +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -extension Image { - /// Applies a fiter to the receiver's contents. - /// - /// - parameter filter: a Filter - public func apply(_ filter: Filter) { - self.apply(filters: [filter]) - } - - /// Applies an array of fiters to the receiver's contents. - /// - /// - parameter filters: an array of Filter objects - public func apply(filters: [Filter]) { - for filter in filters { - let cifilter = filter.createCoreImageFilter(output) - if let outputImage = cifilter.outputImage { - self.output = outputImage - } else { - print("Failed ot generate outputImage: \(#function)") - } - } - self.renderFilteredImage() - } - - func renderFilteredImage() { - var extent = self.output.extent - if extent.isInfinite { - extent = self.ciImage.extent - } - let filterContext = CIContext(options: nil) - let filteredImage = filterContext.createCGImage(self.output, from: extent) - - DispatchQueue.main.async { - self.imageView.layer.contents = filteredImage - } - } -} diff --git a/C4/UI/Image+Generator.swift b/C4/UI/Image+Generator.swift deleted file mode 100644 index cba5be24..00000000 --- a/C4/UI/Image+Generator.swift +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -extension Image { - /// Applies a generator to the receiver's contents. - /// - /// - parameter generator: a Generator - public func generate(_ generator: Generator) { - let crop = CIFilter(name: "CICrop")! - crop.setDefaults() - crop.setValue(CIVector(cgRect: CGRect(self.bounds)), forKey: "inputRectangle") - let generatorFilter = generator.createCoreImageFilter() - crop.setValue(generatorFilter.outputImage, forKey: "inputImage") - - if var outputImage = crop.outputImage { - let scale = CGAffineTransform(scaleX: 1, y: -1) - outputImage = outputImage.transformed(by: scale) - output = outputImage - - //Need to output a CGImage that matches the current image's extent, {0, -h, w, h} - let cgimg = CIContext().createCGImage(self.output, from: outputImage.extent) - self.imageView.image = UIImage(cgImage: cgimg!) - _originalSize = Size(outputImage.extent.size) - } else { - print("Failed to generate outputImage: \(#function)") - } - } -} diff --git a/C4/UI/Image.swift b/C4/UI/Image.swift deleted file mode 100644 index 34fb00df..00000000 --- a/C4/UI/Image.swift +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -/// A Image provides a view-based container for displaying a single image. You can create images from files, from other image objects, or from raw image data you receive. -open class Image: View, NSCopying { - open class ImageView: UIImageView { - var imageLayer: ImageLayer { - return self.layer as! ImageLayer // swiftlint:disable:this force_cast - } - - override open class var layerClass: AnyClass { - return ImageLayer.self - } - } - - /// Shape's contents are drawn on a ShapeLayer. - open var imageLayer: ImageLayer { - return self.imageView.imageLayer - } - - // MARK: Initializers - - /// Initializes an empty Image - public override init() { - super.init() - let uiimage = UIImage() - self.view = ImageView(image: uiimage) - } - - public override init(frame: Rect) { - super.init(frame: frame) - let uiimage = UIImage() - let imageView = ImageView(image: uiimage) - imageView.frame = self.view.bounds - self.view = imageView - } - - /// Initializes a new Image using the specified filename from the bundle (i.e. your project), it will also grab images - /// from the web if the filename starts with http. - /// ```` - /// let img = Image("logo") - /// canvas.add(img) - /// ```` - /// - parameter name: The name of the image included in your project, or a web address. - convenience public init?(_ name: String) { - self.init(name, scale: 1.0) - } - - /// Initializes a new Image using the specified filename from the bundle (i.e. your project), it will also grab images - /// from the web if the filename starts with http. - /// ```` - /// let img = Image("http://www.c4ios.com/images/logo@2x.png", scale: 2.0) - /// canvas.add(img) - /// ```` - /// - parameter name: The name of the image included in your project, or a web address. - convenience public init?(_ name: String, scale: Double) { - guard let image = UIImage(named: name) else { - return nil - } - self.init(uiimage: image, scale: scale) - } - - /// Initializes a new Image using an existing Image (basically like copying). - /// ```` - /// let a = Image("logo") - /// canvas.add(a) - /// let b = Image(image: a) - /// b.center = canvas.center - /// canvas.add(b) - /// ```` - /// - parameter image: A Image. - convenience public init(copy image: Image) { - self.init() - let uiimage = image.uiimage - self.view = ImageView(image: uiimage) - copyViewStyle(image) - } - - /// Initializes a new Image using a UIImage. - /// ```` - /// if let uii = UIImage(named:"logo") { - /// let img = Image(uiimage: uii) - /// canvas.add(img) - /// } - /// ```` - /// - parameter uiimage: A UIImage object. - convenience public init(uiimage: UIImage) { - self.init(uiimage: uiimage, scale: 1.0) - } - - /// Initializes a new Image using a UIImage, with option for specifying the scale of the image. - /// ```` - /// if let uii = UIImage(named:"logo") { - /// let img = Image(uiimage: uii, scale: 2.0) - /// canvas.add(img) - /// } - /// ```` - /// - parameter uiimage: A UIImage object. - /// - parameter scale: A `Double` should be larger than 0.0 - convenience public init(uiimage: UIImage, scale: Double) { - self.init() - - if scale != 1.0 { - let scaledImage = UIImage(cgImage: uiimage.cgImage!, scale: CGFloat(scale), orientation: uiimage.imageOrientation) - self.view = ImageView(image: scaledImage) - } else { - self.view = ImageView(image: uiimage) - } - _originalSize = Size(view.frame.size) - } - - /// Initializes a new Image using a CGImageRef. - /// ```` - /// let cgi = CGImageCreate() - /// let img = Image(cgimage: cgi) - /// canvas.add(img) - /// ```` - /// [Example](https://gist.github.com/C4Framework/06319d420426cb0f1cb3) - /// - parameter cgimage: A CGImageRef object. - convenience public init(cgimage: CGImage) { - let image = UIImage(cgImage: cgimage) - self.init(uiimage: image, scale: 1.0) - } - - /// Initializes a new Image using a CGImageRef, with option for specifying the scale of the image. - /// ```` - /// let cgi = CGImageCreate() - /// let img = Image(cgimage: cgi, scale: 2.0) - /// canvas.add(img) - /// ```` - /// - parameter cgimage: A CGImageRef object. - /// - parameter scale: The scale of the image. - convenience public init(cgimage: CGImage, scale: Double) { - let image = UIImage(cgImage: cgimage) - self.init(uiimage: image, scale: scale) - } - - /// Initializes a new Image using a CIImage. - /// Use this method if you're working with the output of a CIFilter. - /// - parameter ciimage: A CIImage object. - convenience public init(ciimage: CIImage) { - self.init(ciimage: ciimage, scale: 1.0) - } - - /// Initializes a new Image using a CIImage, with option for specifying the scale of the image. - /// Use this method if you're working with the output of a CIFilter. - /// - parameter ciimage: A CIImage object. - /// - parameter scale: The scale of the image. - convenience public init(ciimage: CIImage, scale: Double) { - let image = UIImage(ciImage: ciimage) - self.init(uiimage: image, scale: scale) - } - - /// Initializes a new Image using raw data. - /// Use this if you download an image as data you can pass it here to create an image. - /// See the body of init(url:) to see how to download an image as data. - /// - parameter data: An NSData object. - convenience public init(data: Data) { - self.init(data: data, scale: 1.0) - } - - /// Initializes a new Image using raw data, with option for specifying the scale of the image. - /// Use this if you download an image as data you can pass it here to create an image. - /// See the body of init(url:) to see how to download an image as data. - /// - parameter data: An NSData object. - /// - parameter scale: The scale of the image. - convenience public init(data: Data, scale: Double) { - let image = UIImage(data: data) - self.init(uiimage: image!, scale: scale) - } - - /// Initializes a new Image from an URL. - /// ```` - /// if let url = NSURL(string: "http://www.c4ios.com/images/logo@2x.png") { - /// let img = Image(url: url) - /// canvas.add(img) - /// } - /// ```` - /// - parameter url: An NSURL object. - convenience public init(url: URL) { - self.init(url: url, scale: 1.0) - } - - /// Initializes a new Image from an URL, with option for specifying the scale of the image. - /// ```` - /// if let url = NSURL(string: "http://www.c4ios.com/images/logo@2x.png") { - /// let img = Image(url: url, scale: 2.0) - /// canvas.add(img) - /// } - /// ```` - /// - parameter url: An NSURL object. - /// - parameter scale: The scale of the image. - convenience public init(url: URL, scale: Double) { - var error: NSError? - var data: Data? - do { - data = try Data(contentsOf: url, options: NSData.ReadingOptions.mappedIfSafe) - } catch let error1 as NSError { - error = error1 - data = nil - } - if let d = data { - self.init(data: d, scale: scale) - return - } - if let e = error { - C4Log("There was an error loading image data from url:\n ERROR: \(e.localizedDescription)\n URL:\(url)") - } - self.init() - } - - /// Initializes a new Image using raw data. This method differs from `Image(data:...)` in that you can pass an array of - /// raw data to the initializer. This works if you're creating your own raw images by changing the values of individual - /// pixels. Pixel data should be RGBA. - /// - parameter pixels: An array of raw pixel data. - /// - parameter size: The size {w, h} of the image you're creating based on the pixel array. - convenience public init(pixels: [Pixel], size: Size) { - let rgbColorSpace = CGColorSpaceCreateDeviceRGB() - let bitmapInfo: CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) - let bitsPerComponent: Int = 8 - let bitsPerPixel: Int = 32 - let width: Int = Int(size.width) - let height: Int = Int(size.height) - - assert(pixels.count == Int(width * height)) - - var provider: CGDataProvider? - pixels.withUnsafeBufferPointer { p in - if let address = p.baseAddress { - let data = Data(bytes: UnsafePointer(address), count: pixels.count * MemoryLayout.size) - provider = CGDataProvider(data: data as CFData) - } - } - - let cgim = CGImage( - width: width, - height: height, - bitsPerComponent: bitsPerComponent, - bitsPerPixel: bitsPerPixel, - bytesPerRow: width * Int(MemoryLayout.size), - space: rgbColorSpace, - bitmapInfo: bitmapInfo, - provider: provider!, - decode: nil, - shouldInterpolate: true, - intent: CGColorRenderingIntent.defaultIntent - ) - - self.init(cgimage: cgim!) - } - - /// Initializes a new Image using another image. - /// - parameter c4image: An Image around which the new image is created. - convenience public init(c4image: Image) { - let cgim = c4image.cgImage - self.init(cgimage: cgim, scale: c4image.scale) - } - - /// Initializes a new copy of the receiver. - /// - parameter zone: This parameter is ignored. Memory zones are no longer used by Objective-C. - /// - returns: a new instance that’s a copy of the receiver. - public func copy(with zone: NSZone? = nil) -> Any { - let uiimage = UIImage(cgImage: self.contents) - let img = Image(uiimage: uiimage, scale: scale) - img.frame = self.frame - img.constrainsProportions = self.constrainsProportions - img._originalSize = _originalSize - return img - } - - // MARK: Properties - - /// Returns the UIImageView of the object. - /// - returns: A UIImageView object. - open var imageView: ImageView { - return self.view as! ImageView // swiftlint:disable:this force_cast - } - - /// Returns a UIImage representation of the receiver. - /// - returns: A UIImage object. - open var uiimage: UIImage { - let layer = imageView.layer as CALayer - let contents = layer.contents as! CGImage // swiftlint:disable:this force_cast - return UIImage(cgImage: contents, scale: CGFloat(scale), orientation: imageView.image!.imageOrientation) - } - - /// Returns a CGImageRef representation of the receiver. - /// - returns: A CGImageRef object. - open var cgImage: CGImage { - return uiimage.cgImage! - } - - /// Returns a CIImage representation of the receiver. Generally, this would be used to work with filters. - /// - returns: A CIImage object. - open var ciImage: CIImage { - return CIImage(cgImage: cgImage) - } - - /// An object that provides the contents of the layer. Animatable. - /// The default value of this property is nil. - /// If you are using the layer to display a static image, you can set this property to the CGImageRef containing the image - /// you want to display. Assigning a value to this property causes the layer to use your image rather than create a - /// separate backing store. - open var contents: CGImage { - get { - let layer = imageView.layer as CALayer - return layer.contents as! CGImage // swiftlint:disable:this force_cast - } set(val) { - imageView.layer.contents = val - } - } - - /// The current rotation value of the view. Animatable. - /// - returns: A Double value representing the cumulative rotation of the view, measured in Radians. - open override var rotation: Double { - get { - if let number = imageLayer.value(forKeyPath: Layer.rotationKey) as? NSNumber { - return number.doubleValue - } - return 0.0 - } - set { - imageLayer.setValue(newValue, forKeyPath: Layer.rotationKey) - } - } - - /// The scale factor of the image. (read-only) - var scale: Double { - return Double(imageView.image!.scale) - } - - /// A variable that provides access to the width of the receiver. Animatable. - /// The default value of this property is defined by the image being created. - /// Assigning a value to this property causes the receiver to change the width of its frame. If the receiver's - /// `contrainsProportions` variable is set to `true` the receiver's height will change to match the new width. - open override var width: Double { - get { - return Double(view.frame.size.width) - } set(val) { - var newSize = Size(val, Double(view.frame.size.height)) - if constrainsProportions { - let ratio = Double(self.size.height / self.size.width) - newSize.height = val * ratio - } - var rect = self.frame - rect.size = newSize - self.frame = rect - } - } - - /// A variable that provides access to the height of the receiver. Animatable. - /// The default value of this property is defined by the image being created. - /// Assigning a value to this property causes the receiver to change the height of its frame. If the receiver's - /// `contrainsProportions` variable is set to `true` the receiver's width will change to match the new width. - open override var height: Double { - get { - return Double(view.frame.size.height) - } set(val) { - var newSize = Size(Double(view.frame.size.width), val) - if constrainsProportions { - let ratio = Double(self.size.width / self.size.height) - newSize.width = val * ratio - } - var rect = self.frame - rect.size = newSize - self.frame = rect - } - } - - /// Assigning a value of true to this property will cause the receiver to scale its entire frame whenever its `width` or - /// `height` variables are set. - /// The default value of this property is `false`. - open var constrainsProportions: Bool = false - - internal var _originalSize: Size = Size() - /// The original size of the receiver when it was initialized. - public var originalSize: Size { - return _originalSize - } - - /// The original width/height ratio of the receiver when it was initialized. - public var originalRatio: Double { - return _originalSize.width / _originalSize.height - } - - // MARK: Filters - lazy internal var output: CIImage = self.ciImage - lazy internal var filterQueue: DispatchQueue = { - return DispatchQueue.global(qos: .background) - }() - lazy internal var renderImmediately = true -} diff --git a/C4/UI/ImageLayer.swift b/C4/UI/ImageLayer.swift deleted file mode 100644 index 2dd8a8e4..00000000 --- a/C4/UI/ImageLayer.swift +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore - -///Subclass of CALayer that handles animating its contents. -public class ImageLayer: CALayer { - /// Configures basic options for a CABasicAnimation. - /// - /// The options set in this method are favorable for the inner workings of C4's animation behaviours. - /// - parameter key: The identifier of the action. - /// - returns: The object that provides the action for key. - public override func action(forKey key: String) -> CAAction? { - if ShapeLayer.disableActions == true { - return nil - } - - let animatableProperties = ["contents", "rotation"] - if !animatableProperties.contains(key) { - return super.action(forKey: key) - } - - let animation: CABasicAnimation - if let viewAnimation = ViewAnimation.stack.last as? ViewAnimation, viewAnimation.spring != nil { - animation = CASpringAnimation(keyPath: key) - } else { - animation = CABasicAnimation(keyPath: key) - } - - animation.configureOptions() - animation.fromValue = value(forKey: key) - - if key == Layer.rotationKey { - if let layer = presentation() { - animation.fromValue = layer.value(forKey: key) - } - } - - return animation - } - - private var _rotation = 0.0 - - /// The value of the receiver's current rotation state. - /// This value is cumulative, and can represent values beyong +/- π - @objc public dynamic var rotation: Double { - return _rotation - } - - /// Initializes a new C4Layer - public override init() { - super.init() - } - - /// Initializes a new C4Layer from a specified layer of any other type. - /// - parameter layer: Another CALayer - public override init(layer: Any) { - super.init(layer: layer) - if let layer = layer as? ImageLayer { - _rotation = layer._rotation - } - } - - /// Initializes a new C4Layer from data in a given unarchiver. - /// - parameter coder: An unarchiver object. - public required init?(coder: NSCoder) { - super.init(coder: coder) - } - - /// Sets a value for a given key. - /// - parameter value: The value for the property identified by key. - /// - parameter key: The name of one of the receiver's properties - public override func setValue(_ value: Any?, forKey key: String) { - super.setValue(value, forKey: key) - if key == Layer.rotationKey { - _rotation = value as? Double ?? 0.0 - } - } - - /// Returns a Boolean indicating whether changes to the specified key require the layer to be redisplayed. - /// - parameter key: A string that specifies an attribute of the layer. - /// - returns: A Boolean indicating whether changes to the specified key require the layer to be redisplayed. - public override class func needsDisplay(forKey key: String) -> Bool { - if key == Layer.rotationKey { - return true - } - return super.needsDisplay(forKey: key) - } - - /// Reloads the content of this layer. - /// Do not call this method directly. - public override func display() { - guard let presentation = presentation() else { - return - } - setValue(presentation._rotation, forKeyPath: "transform.rotation.z") - } -} diff --git a/C4/UI/Layer.swift b/C4/UI/Layer.swift deleted file mode 100644 index 5d23fc65..00000000 --- a/C4/UI/Layer.swift +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore - -/// A subclass of CALayer that has a rotation property. -public class Layer: CALayer { - static let rotationKey = "transform.rotation.z" - - private var _rotation = 0.0 - - /// The value of the receiver's current rotation state. - /// This value is cumulative, and can represent values beyong +/- π - @objc public dynamic var rotation: Double { - return _rotation - } - - /// Initializes a new Layer - public override init() { - super.init() - } - - /// Initializes a new Layer from a specified layer of any other type. - /// - parameter layer: Another CALayer - public override init(layer: Any) { - super.init(layer: layer) - if let layer = layer as? Layer { - _rotation = layer._rotation - } - } - - /// Initializes a new Layer from data in a given unarchiver. - /// - parameter coder: An unarchiver object. - public required init?(coder: NSCoder) { - super.init(coder: coder) - } - - /// Sets a value for a given key. - /// - parameter value: The value for the property identified by key. - /// - parameter key: The name of one of the receiver's properties - public override func setValue(_ value: Any?, forKey key: String) { - super.setValue(value, forKey: key) - if key == Layer.rotationKey { - _rotation = value as? Double ?? 0.0 - } - } - - /// This method searches for the given action object of the layer. Actions define dynamic behaviors for a layer. For example, the animatable properties of a layer typically have corresponding action objects to initiate the actual animations. When that property changes, the layer looks for the action object associated with the property name and executes it. You can also associate custom action objects with your layer to implement app-specific actions. - /// - parameter key: The identifier of the action. - /// - returns: the action object assigned to the specified key. - public override func action(forKey key: String) -> CAAction? { - if key == Layer.rotationKey { - let animation = CABasicAnimation(keyPath: key) - animation.configureOptions() - if let layer = presentation() { - animation.fromValue = layer.value(forKey: key) - } - return animation - } - return super.action(forKey: key) - } - - /// Returns a Boolean indicating whether changes to the specified key require the layer to be redisplayed. - /// - parameter key: A string that specifies an attribute of the layer. - /// - returns: A Boolean indicating whether changes to the specified key require the layer to be redisplayed. - public override class func needsDisplay(forKey key: String) -> Bool { - if key == Layer.rotationKey { - return true - } - return super.needsDisplay(forKey: key) - } - - /// Reloads the content of this layer. - /// Do not call this method directly. - public override func display() { - guard let presentation = presentation() else { - return - } - setValue(presentation._rotation, forKeyPath: "transform.rotation.z") - } -} diff --git a/C4/UI/Line.swift b/C4/UI/Line.swift deleted file mode 100644 index 88896b6f..00000000 --- a/C4/UI/Line.swift +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -///Line is a is a concrete subclass of Polygon that contains only two points. -/// -///This subclass overrides the `points` variable so that it can only ever have 2 points, and also has an `endPoints` variable that allows the user to edit either end of the line (animatable). -public class Line: Polygon { - - /// The end points the receiver's line. Animatable. - /// - /// Assigning a tuple of Point values to this object will cause the receiver to update itself. - /// - /// - returns: A tuple (2) of Points that make up the the begin and end points of the line. - public var endPoints: (Point, Point) { - get { - return (points[0], points[1]) - } set { - self.points = [newValue.0, newValue.1] - } - } - - override func updatePath() { - if pauseUpdates { - return - } - - let p = Path() - p.moveToPoint(endPoints.0) - p.addLineToPoint(endPoints.1) - path = p - adjustToFitPath() - } - - /// The center point (top-left) of the receiver's frame. Animatable. - /// - /// - returns: A Point, the receiver's center. - public override var center: Point { - get { - return Point(view.center) - } - set { - let diff = newValue - center - batchUpdates { - self.endPoints.0 += diff - self.endPoints.1 += diff - } - } - } - - /// The origin point (top-left) of the receiver's frame. Animatable. - /// - /// - returns: A Point, the receiver's origin. - public override var origin: Point { - get { - return Point(view.frame.origin) - } - set { - let diff = newValue - origin - batchUpdates { - self.endPoints.0 += diff - self.endPoints.1 += diff - } - } - } - - /// The points that make up the line. Animatable. - /// - /// Assigning an array of Point values to this object will cause the receiver to update itself. - /// - /// - returns: A Point array of 2 points. - public override var points: [Point] { - didSet { - if points.count < 2 { - print("Invalid point array. There must be at least 2 points to update the line.") - } else { - if points.count > 2 { - print("Warning. The passed array has more than 2 points, only the first two will be used.") - points = [points[0], points[1]] - } else { - updatePath() - } - } - } - } - - /// Initializes a new Polygon using the specified array of points. - /// - /// Protects against trying to create a polygon with only 1 point (i.e. requires 2 points). - /// Trims point array if count > 2. - /// - /// ```` - /// let a = Point(100,100) - /// let b = Point(200,200) - /// - /// let l = Line([a,b]) - /// ```` - /// - /// - parameter points: An array of Point structs. - public override init(_ points: [Point]) { - let firstTwo = [Point](points[0...1]) - super.init(firstTwo) - - updatePath() - } - - /// Initializes a new Line using the specified tuple of points. - /// - /// ```` - /// let a = Point(100,100) - /// let b = Point(200,200) - /// - /// let l = Line((a,b)) - /// ```` - /// - /// - parameter points: A tuple of Point structs. - public init(_ points: (Point, Point)) { - super.init([points.0, points.1]) - } - - /// Initializes a new Line using two specified points. - /// - /// ```` - /// let a = Point(100,100) - /// let b = Point(200,200) - /// - /// let l = Line(begin: a, end: b) - /// ```` - /// - /// - parameter begin: The start point of the line. - /// - parameter end: The end point of the line. - public convenience init(begin: Point, end: Point) { - let points = (begin, end) - self.init(points) - } - - /// Initializes a new Polygon from data in a given unarchiver. - /// - parameter coder: An unarchiver object. - required public init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private var pauseUpdates = false - func batchUpdates(_ updates: () -> Void) { - pauseUpdates = true - updates() - pauseUpdates = false - updatePath() - } -} diff --git a/C4/UI/Movie.swift b/C4/UI/Movie.swift deleted file mode 100644 index 83a9bb09..00000000 --- a/C4/UI/Movie.swift +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit -import AVFoundation - -///This document describes the Movie class. You use a Movie object to implement the playback of video files, it encapulates an AVQueuePlayer object which handles the loading and control of assets. -/// -///The Movie class is meant to simplify the addition of videos to your application. It is also a subclass of View, and so has all the common animation, interaction and notification capabilities. -/// -///A C4Movie’s resizing behaviour is to map itself to the edges of its visible frame. This functionality implicitly uses AVLayerVideoGravityResize as its layer’s default gravity. You can change the frame of the movie from an arbitrary shape back to its original proportion by using its originalSize, originalRatio, or by independently setting either its width or height properties. -public class Movie: View { - var filename: String! - var player: AVQueuePlayer? - var currentItem: AVPlayerItem? - var reachedEndAction: (() -> Void)? - - /// Assigning a value of true to this property will cause the receiver to loop at the end of playback. - /// - /// The default value of this property is `true`. - public var loops: Bool = true - - /// Mute/Unmute the audio track. - /// - /// The default value of this property is `false`. - public var muted: Bool { - get { - guard let p = player else { - return false - } - return p.isMuted - } - set { - player?.isMuted = newValue - } - } - - /// A variable that provides access to the width of the receiver. Animatable. - /// The default value of this property is defined by the movie being created. - /// Assigning a value to this property causes the receiver to change the width of its frame. If the receiver's - /// `contrainsProportions` variable is set to `true` the receiver's height will change to match the new width. - public override var width: Double { - get { - return Double(view.frame.size.width) - } set(val) { - var newSize = Size(val, height) - if constrainsProportions { - newSize.height = val * height / width - } - var rect = self.frame - rect.size = newSize - self.frame = rect - } - } - - /// A variable that provides access to the height of the receiver. Animatable. - /// The default value of this property is defined by the movie being created. - /// Assigning a value to this property causes the receiver to change the height of its frame. If the receiver's - /// `contrainsProportions` variable is set to `true` the receiver's width will change to match the new height. - public override var height: Double { - get { - return Double(view.frame.size.height) - } set(val) { - var newSize = Size(Double(view.frame.size.width), val) - if constrainsProportions { - let ratio = Double(self.size.width / self.size.height) - newSize.width = val * ratio - } - var rect = self.frame - rect.size = newSize - self.frame = rect - } - } - - /// Assigning a value of true to this property will cause the receiver to scale its entire frame whenever its `width` or - /// `height` variables are set. - /// The default value of this property is `true`. - public var constrainsProportions: Bool = true - - /// The original size of the receiver when it was initialized. - public internal(set) var originalSize: Size = Size(1, 1) - - /// The original width/height ratio of the receiver when it was initialized. - public var originalRatio: Double { - return originalSize.width / originalSize.height - } - - var movieLayer: PlayerLayer { - return self.movieView.movieLayer - } - - var movieView: MovieView { - return self.view as! MovieView // swiftlint:disable:this force_cast - } - - public var playing: Bool { - return player?.rate != 0.0 - } - - class MovieView: UIView { - var movieLayer: PlayerLayer { - return self.layer as! PlayerLayer // swiftlint:disable:this force_cast - } - - override class var layerClass: AnyClass { - return PlayerLayer.self - } - } - - /// The current rotation value of the view. Animatable. - /// - returns: A Double value representing the cumulative rotation of the view, measured in Radians. - public override var rotation: Double { - get { - if let number = movieLayer.value(forKeyPath: Layer.rotationKey) as? NSNumber { - return number.doubleValue - } - return 0.0 - } - set { - movieLayer.setValue(newValue, forKeyPath: Layer.rotationKey) - } - } - - /// Initializes a new Movie using the specified filename from the bundle (i.e. your project). - /// - /// - parameter filename: The name of the movie file included in your project. - public convenience init?(_ filename: String) { - do { - try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback) - try AVAudioSession.sharedInstance().setActive(true) - } catch { - print("Couldn't set up AVAudioSession") - } - - guard let url = Bundle.main.url(forResource: filename, withExtension: nil) else { - print("Couldn't retrieve url for: \(filename)") - return nil - } - - let asset = AVAsset(url: url) - let tracks = asset.tracks(withMediaType: AVMediaType.video) - - let movieTrack = tracks[0] - self.init(frame: Rect(0, 0, Double(movieTrack.naturalSize.width), Double(movieTrack.naturalSize.height))) - self.filename = filename - - //initialize player with item - let newPlayer = AVQueuePlayer(playerItem: AVPlayerItem(asset: asset)) - newPlayer.actionAtItemEnd = .pause - currentItem = newPlayer.currentItem - player = newPlayer - - //runs an overrideable method for handling the end of the movie - on(event: NSNotification.Name.AVPlayerItemDidPlayToEndTime) { - self.handleReachedEnd() - } - - //movie view's player - movieLayer.player = player - movieLayer.videoGravity = AVLayerVideoGravity.resize - - originalSize = self.size - - // unmute - muted = false - } - - /// Initializes a new Movie using the specified frame. - /// - /// - parameter frame: The frame of the new movie object. - public override init(frame: Rect) { - super.init() - self.view = MovieView(frame: CGRect(frame)) - } - - public convenience init?(copy original: Movie) { - self.init(original.filename) - self.frame = original.frame - copyViewStyle(original) - } - - /// Begins playback of the current item. - /// - /// This is the same as setting rate to 1.0. - public func play() { - guard let p = player else { - print("The current movie's player is not properly initialized") - return - } - p.play() - } - - /// Pauses playback. - /// - /// This is the same as setting rate to 0.0. - public func pause() { - guard let p = player else { - print("The current movie's player is not properly initialized") - return - } - p.pause() - } - - /// Stops playback. - /// - /// This is the same as setting rate to 0.0 and resetting the current time to 0. - public func stop() { - guard let p = player else { - print("The current movie's player is not properly initialized") - return - } - p.seek(to: CMTimeMake(0, 1)) - p.pause() - } - - /// The action to perform at the end of playback. - /// - /// - parameter action: A block of code to execute at the end of playback. - public func reachedEnd(_ action: (() -> Void)?) { - reachedEndAction = action - } - - /// Called at the end of playback (i.e. when the movie reaches its end). - /// - /// You can override this function to add your own custom actions. - /// - /// By default, the movie should loop then the method calls `stop()` and `play()`. - func handleReachedEnd() { - if self.loops { - self.stop() - self.play() - } - reachedEndAction?() - } -} diff --git a/C4/UI/Pixel.swift b/C4/UI/Pixel.swift deleted file mode 100644 index 976a28a6..00000000 --- a/C4/UI/Pixel.swift +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -/// A structure of 4 8-bit values that represent r, g, b, a values of a single point (i.e. a pixel) -public struct Pixel { - var r: UInt8 = 0 - var g: UInt8 = 0 - var b: UInt8 = 0 - var a: UInt8 = 255 - - /// Initializes a pixel whose color is a specified gray. - /// - /// - parameter gray: the gray color of the pixel - public init(gray: Int) { - self.init(gray, gray, gray, 255) - } - - /// Initializes a Pixel structure - /// - /// ```` - /// let p = Pixel(255, 255, 255, 255) // -> white - /// ```` - /// - /// Values are calculated from 0...255 - /// - /// - parameter r: the red component - /// - parameter g: the green component - /// - parameter b: the blue component - /// - parameter a: the alpha component - public init(_ r: Int, _ g: Int, _ b: Int, _ a: Int) { - self.r = UInt8(r) - self.g = UInt8(g) - self.b = UInt8(b) - self.a = UInt8(a) - } - - public init(_ color: Color) { - let rgba: [Int] = color.components.map({ Int($0 * 255.0) }) - self.init(rgba[0], rgba[1], rgba[2], rgba[3]) - } -} diff --git a/C4/UI/PlayerLayer.swift b/C4/UI/PlayerLayer.swift deleted file mode 100644 index 6053ab26..00000000 --- a/C4/UI/PlayerLayer.swift +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright © 2015 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import AVFoundation - -/// Extension for CAShapeLayer that allows overriding the actions for specific properties. -public class PlayerLayer: AVPlayerLayer { - /// A boolean value that, when true, prevents the animation of a shape's properties. - /// - /// ```` - /// ShapeLayer.disableActions = true - /// circle.fillColor = red - /// ShapeLayer.disableActions = false - /// - /// This value can be set globally, after which changes to any shape's properties will be immediate. - public static var disableActions = true - - /// This method searches for the given action object of the layer. Actions define dynamic behaviors for a layer. For example, the animatable properties of a layer typically have corresponding action objects to initiate the actual animations. When that property changes, the layer looks for the action object associated with the property name and executes it. You can also associate custom action objects with your layer to implement app-specific actions. - /// - /// - parameter key: The identifier of the action. - /// - /// - returns: the action object assigned to the specified key. - public override func action(forKey key: String) -> CAAction? { - if ShapeLayer.disableActions == true { - return nil - } - - let animatableProperties = [Layer.rotationKey] - if !animatableProperties.contains(key) { - return super.action(forKey: key) - } - - let animation: CABasicAnimation - if let viewAnimation = ViewAnimation.stack.last as? ViewAnimation, viewAnimation.spring != nil { - animation = CASpringAnimation(keyPath: key) - } else { - animation = CABasicAnimation(keyPath: key) - } - - animation.configureOptions() - animation.fromValue = value(forKey: key) - - if key == Layer.rotationKey { - if let layer = presentation() { - animation.fromValue = layer.value(forKey: key) - } - } - - return animation - } - - private var _rotation = 0.0 - - /// The value of the receiver's current rotation state. - /// This value is cumulative, and can represent values beyong +/- π - @objc public dynamic var rotation: Double { - return _rotation - } - - /// Initializes a new C4Layer - public override init() { - super.init() - } - - /// Initializes a new C4Layer from a specified layer of any other type. - /// - parameter layer: Another CALayer - public override init(layer: Any) { - super.init(layer: layer) - if let layer = layer as? PlayerLayer { - _rotation = layer._rotation - } - } - - /// Initializes a new C4Layer from data in a given unarchiver. - /// - parameter coder: An unarchiver object. - public required init?(coder: NSCoder) { - super.init(coder: coder) - } - - /// Sets a value for a given key. - /// - parameter value: The value for the property identified by key. - /// - parameter key: The name of one of the receiver's properties - public override func setValue(_ value: Any?, forKey key: String) { - super.setValue(value, forKey: key) - if key == Layer.rotationKey { - _rotation = value as? Double ?? 0.0 - } - } - - /// Returns a Boolean indicating whether changes to the specified key require the layer to be redisplayed. - /// - parameter key: A string that specifies an attribute of the layer. - /// - returns: A Boolean indicating whether changes to the specified key require the layer to be redisplayed. - public override class func needsDisplay(forKey key: String) -> Bool { - if key == Layer.rotationKey { - return true - } - return super.needsDisplay(forKey: key) - } - - /// Reloads the content of this layer. - /// Do not call this method directly. - public override func display() { - guard let presentation = presentation() else { - return - } - setValue(presentation._rotation, forKeyPath: "transform.rotation.z") - } -} diff --git a/C4/UI/Polygon.swift b/C4/UI/Polygon.swift deleted file mode 100644 index 2badb2e0..00000000 --- a/C4/UI/Polygon.swift +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// Polygon is a concrete subclass of Shape that has a special initialzer that creates a non-uniform shape made up of 3 or more points. -open class Polygon: Shape { - - /// Returns the array of points that make up the polygon. - /// - /// Assigning an array of Point values to this object will cause the receiver to update itself. - /// - /// ```` - /// let p = Polygon() - /// let a = Point() - /// let b = Point(100,100) - /// let c = Point(200,0) - /// p.points = [a,b,c] - /// p.center = canvas.center - /// canvas.add(p) - /// ```` - public var points: [Point] { - didSet { - updatePath() - } - } - - /// Initializes a default Polygon. - public override init() { - self.points = [] - super.init() - fillColor = clear - } - - /// Initializes a new Polygon using the specified array of points. - /// - /// Protects against trying to create a polygon with only 1 point (i.e. requires 2 or more points). - /// - /// ```` - /// let a = Point() - /// let b = Point(100,100) - /// let c = Point(200,0) - /// let p = Polygon([a,b,c]) - /// p.center = canvas.center - /// canvas.add(p) - /// ```` - /// - parameter points: An array of Point structs. - public init(_ points: [Point]) { - assert(points.count >= 2, "To create a Polygon you need to specify an array of at least 2 points") - self.points = points - super.init() - - fillColor = clear - - let path = Path() - path.moveToPoint(points.first!) - for point in points.dropFirst() { - path.addLineToPoint(point) - } - self.path = path - - adjustToFitPath() - } - - /// Initializes a new Polygon from data in a given unarchiver. - /// - parameter coder: An unarchiver object. - required public init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func updatePath() { - if points.count > 1 { - let p = Path() - p.moveToPoint(points.first!) - for point in points.dropFirst() { - p.addLineToPoint(point) - } - - path = p - adjustToFitPath() - } - } - - /// Closes the shape. - /// - /// This method appends a line between the shape's last point and its first point. - public func close() { - let p = path - p?.closeSubpath() - self.path = p - adjustToFitPath() - } -} diff --git a/C4/UI/QuadCurve.swift b/C4/UI/QuadCurve.swift deleted file mode 100644 index 50449b0f..00000000 --- a/C4/UI/QuadCurve.swift +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -/// QuadCurve is a concrete subclass of Curve that modifies it shape based on a single point rather than 2 used by its parent class. -public class QuadCurve: Curve { - - /// A Point used to calculate the shape of the quadratic curve. - public var controlPoint: Point { - get { - return controlPoints.0 - } set { - self.controlPoints = (newValue, newValue) - } - } - - /// Initializes a new QuadCurve. - /// - /// ```` - /// let curve = QuadCurve(a: Point(), b: Point(50,50), c: Point(100,0)) - /// canvas.add(curve) - /// ```` - /// - /// - parameter begin: The beginning point of the curve. - /// - parameter control: A single Point used to calculate the shape of the curve. - /// - parameter end: The end point of the curve. - convenience public init(begin: Point, control: Point, end: Point) { - self.init(begin: begin, control0: control, control1: control, end: end) - } -} diff --git a/C4/UI/Rectangle.swift b/C4/UI/Rectangle.swift deleted file mode 100644 index 90e0f548..00000000 --- a/C4/UI/Rectangle.swift +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// Rectangle is a concrete subclass of Shape that has a special initialzer that creates a rectangle whose shape is determined by its frame. -public class Rectangle: Shape { - /// Returns the corner size for the receiver. - /// - /// The shape of a Rectangle's corners are specified with width and height. - /// - /// Automatically updates the shape of the receiver's corners when set. - /// - /// ```` - /// let f = Rect(100,100,100,100) - /// let r = Rectangle(frame: f) - /// r.corner = Size(10,10) - /// canvas.add(r) - /// ```` - public var corner: Size = Size(8, 8) { - didSet { - updatePath() - } - } - - /// Initializes a new Rectangle using the specified frame. - /// - /// ```` - /// let f = Rect(100,100,100,100) - /// let r = Rectangle(frame: f) - /// canvas.add(r) - /// ```` - /// - /// - parameter frame: A Rect whose dimensions are used to construct the Rectangle. - public override init(frame: Rect) { - super.init() - if frame.size.width <= corner.width * 2.0 || frame.size.height <= corner.width / 2.0 { - corner = Size() - } - view.frame = CGRect(frame) - updatePath() - } - - public override init() { - super.init() - } - - override func updatePath() { - let newPath = Path() - newPath.addRoundedRect(bounds, cornerWidth: corner.width, cornerHeight: corner.height) - path = newPath - } -} diff --git a/C4/UI/RegularPolygon.swift b/C4/UI/RegularPolygon.swift deleted file mode 100644 index d19a16f7..00000000 --- a/C4/UI/RegularPolygon.swift +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -///RegularPolygon is a is a concrete subclass of Polygon that defines a shape whose sides are uniform (e.g. pentagon, octagon, etc.). -/// -/// This class defines two variables called `sides` and `phase` that represent the number of sides and the initial rotation of the shape (respectively). The default shape is a hexagon. -public class RegularPolygon: Polygon { - /// Returns the number of sides in the polygon. - /// - /// Assigning a value to this property will change the number of sides and cause the receiver to automatically update its - /// path. - /// - /// ```` - /// let f = Rect(100,100,100,100) - /// var p = RegularPolygon(frame: f) - /// p.sides = 3 - /// canvas.add(p) - /// ```` - @IBInspectable - public var sides: Int = 6 { - didSet { - assert(sides > 0) - updatePath() - } - } - - /// Returns the phase (i.e. "rotated" beginning position) of the shape. This is not actual rotation, it simply changes - /// where the beginning of the shape is. - /// - /// Assigning a value to this property will change the starting position of the beginning of the shape. The shape will - /// still calculate its points based on the frame. - /// - /// ```` - /// let f = Rect(100,100,100,100) - /// var p = RegularPolygon(frame: f) - /// p.phase = Double.pi / 2.0 - /// canvas.add(p) - /// ```` - @IBInspectable - public var phase: Double = 0 { - didSet { - updatePath() - } - } - - /// Initializes a new RegularPolygon. - /// - /// Default values are are sides = 6 (i.e. a hexagon), phase = 0. - convenience public init(center: Point, radius: Double = 50.0, sides: Int = 6, phase: Double = 0.0) { - let dΘ = 2.0*Double.pi / Double(sides) - var pointArray = [Point]() - for i in 0..?) -> Void - public typealias RecorderStoppedAction = () -> Void - - let recorder = RPScreenRecorder.shared() - var preview: RPPreviewViewController? - var activities: Set? - - public var previewFinishedAction: PreviewControllerFinishedAction? - public var recordingEndedAction: RecorderStoppedAction? - public var enableMicrophone = false - - public var recording: Bool { - return recorder.isRecording - } - - public var available: Bool { - return recorder.isAvailable - } - - public func start() { - if !recording && available { - preview = nil - recorder.startRecording(withMicrophoneEnabled: enableMicrophone) { error in - if let error = error { - print("Start Recording Error: \(error.localizedDescription)") - } - } - } - } - - public func start(_ duration: Double) { - start() - wait(duration) { - self.stop() - } - } - - public func stop() { - recorder.stopRecording { previewViewController, _ in - self.preview = previewViewController - self.preview?.previewControllerDelegate = self - self.recordingEndedAction?() - } - } - - public func showPreviewInController(_ controller: UIViewController) { - guard let preview = preview else { - print("Recorder has no preview to show.") - return - } - - controller.present(preview, animated: true, completion: nil) - } - - #if os(iOS) - public func previewController(_ previewController: RPPreviewViewController, didFinishWithActivityTypes activityTypes: Set) { - activities = activityTypes - } - #endif - - public func previewControllerDidFinish(_ previewController: RPPreviewViewController) { - previewFinishedAction?(activities) - preview?.presentingViewController?.dismiss(animated: true, completion: nil) - } -} diff --git a/C4/UI/Shape+Creation.swift b/C4/UI/Shape+Creation.swift deleted file mode 100644 index 742c769b..00000000 --- a/C4/UI/Shape+Creation.swift +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// Extension for Shape that provides functionality for adding elements to a receiver's path. -extension Shape { - /// Appends a circle to the end of the receiver's path. - /// - /// ```` - /// var l = Line([Point(),Point(100,100)]) - /// l.addCircle(center: l.path!.currentPoint, radius: 20) - /// canvas.add(l) - /// ```` - /// - /// - parameter center: The center of the new circle - /// - parameter radius: The radius of the new circle - public func addCircle(center: Point, radius: Double) { - var newPath = path - if newPath == nil { - newPath = Path() - } - - let r = Rect(center.x - radius, center.y - radius, radius*2, radius*2) - newPath!.addEllipse(r) - path = newPath - adjustToFitPath() - } - - /// Appends a polygon to the end of the receiver's path. - /// - /// ```` - /// var l = Line([Point(),Point(100,100)]) - /// var points = [Point]() - /// for i in 0...10 { - /// let x = random01()*100.0 - /// let y = random01()*100.0 - /// points.append(Point(x,y)) - /// } - /// l.addPolygon(points: points, closed: true) - /// canvas.add(l) - /// ```` - /// - /// - parameter points: An array of Point structs that defines the new polygon - /// - parameter closed: If true then the polygon will have an additional line between its first and last points - public func addPolygon(points: [Point], closed: Bool = true) { - var newPath = path - if newPath == nil { - newPath = Path() - } - - if let firstPoint = points.first { - newPath!.moveToPoint(firstPoint) - } - for point in points { - newPath!.addLineToPoint(point) - } - if closed { - newPath!.closeSubpath() - } - path = newPath - adjustToFitPath() - } - - /// Appends a line segment to the end of the receiver's path. - /// - /// ```` - /// var l = Line([Point(),Point(100,100)]) - /// l.addLine([Point(100,100),Point(100,0)]) - /// canvas.add(l) - /// ```` - /// - /// - parameter points: An array of Point structs that defines the new line - public func addLine(_ points: [Point]) { - let newPath = path - if path == nil { - path = Path() - } - - if newPath!.currentPoint != points.first! { - newPath!.moveToPoint(points.first!) - } - newPath!.addLineToPoint(points[1]) - path = newPath - adjustToFitPath() - } - - /// Appends a bezier curve to the end of the receiver's path. - /// - /// ```` - /// var l = Line([Point(),Point(100,100)]) - /// let pts = [Point(100,100),Point(100,0)] - /// let ctrls = [Point(150,100),Point(150,0)] - /// l.addCurve(points: pts, controls: ctrls) - /// canvas.add(l) - /// ```` - /// - /// - parameter points: An array of Point structs that defines the beginning and end points of the curve - /// - parameter controls: An array of Point structs used to define the shape of the curve - public func addCurve(points: [Point], controls: [Point]) { - let newPath = path - if path == nil { - path = Path() - } - - if newPath!.currentPoint != points.first! { - newPath!.moveToPoint(points.first!) - } - newPath!.addCurveToPoint(points[1], control1: controls.first!, control2: controls[1]) - path = newPath - adjustToFitPath() - } -} diff --git a/C4/UI/Shape.swift b/C4/UI/Shape.swift deleted file mode 100644 index d30fc591..00000000 --- a/C4/UI/Shape.swift +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -/// Shape is a concrete subclass of View that draws a bezier path in its coordinate space. -open class Shape: View { - internal class ShapeView: UIView { - var shapeLayer: ShapeLayer { - return self.layer as! ShapeLayer // swiftlint:disable:this force_cast - } - - override class var layerClass: AnyClass { - return ShapeLayer.self - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - //check subviews first - if let subview = super.hitTest(point, with: event) { - return subview - } - - guard let path = shapeLayer.path else { - return nil - } - - let fillRule = shapeLayer.fillRule == kCAFillRuleNonZero ? CGPathFillRule.evenOdd : CGPathFillRule.winding - - if path.contains(point, using: fillRule, transform: CGAffineTransform.identity) { - return self - } - - return nil - } - } - - /// Shape's contents are drawn on a ShapeLayer. - open var shapeLayer: ShapeLayer { - return self.shapeView.shapeLayer - } - - internal var shapeView: ShapeView { - return self.view as! ShapeView // swiftlint:disable:this force_cast - } - - /// Initializes an empty Shape. - public override init() { - super.init() - self.view = ShapeView(frame: CGRect(frame)) - strokeColor = C4Purple - fillColor = C4Blue - lineWidth = 1 - lineCap = .round - lineJoin = .round - - let image = UIImage.createWithColor(UIColor.clear, size: CGSize(width: 1, height: 1)).cgImage - shapeLayer.contents = image - } - - /// Initializes a new Shape from a specified Path. - /// - parameter path: A Path around which the new shape is created with the frame of the new shape fitting the path on - /// screen. - public convenience init(_ path: Path) { - self.init() - - self.path = path - shapeLayer.path = path.CGPath - - updatePath() - adjustToFitPath() - } - - public override init(frame: Rect) { - super.init() - self.view = ShapeView(frame: CGRect(frame)) - strokeColor = C4Purple - fillColor = C4Blue - lineWidth = 1 - lineCap = .round - lineJoin = .round - - let image = UIImage.createWithColor(UIColor.clear, size: CGSize(width: 1, height: 1)).cgImage - shapeLayer.contents = image - } - - /// Initializes a new Shape from the properties of another Shape. Essentially, this copies the provided shape. - /// - parameter shape: A Shape around which the new shape is created. - public convenience init(copy original: Shape) { - //If there is a scale transform we need to undo that - let t = original.view.transform.inverted() - let x = sqrt(t.a * t.a + t.c * t.c) - let y = sqrt(t.b * t.b + t.d * t.d) - let s = CGAffineTransform(scaleX: x, y: y) - self.init(frame: Rect(original.view.frame.applying(s))) - - let disable = ShapeLayer.disableActions - ShapeLayer.disableActions = true - self.path = original.path - shapeLayer.path = path?.CGPath - self.lineWidth = original.lineWidth - self.lineDashPhase = original.lineDashPhase - self.lineCap = original.lineCap - self.lineJoin = original.lineJoin - self.lineDashPattern = original.lineDashPattern - self.fillColor = original.fillColor - self.strokeColor = original.strokeColor - self.strokeStart = original.strokeStart - self.strokeEnd = original.strokeEnd - self.miterLimit = original.miterLimit - updatePath() - adjustToFitPath() - copyViewStyle(original) - ShapeLayer.disableActions = disable - } - - ///An optional variable representing a gradient. If this is non-nil, then the shape will appear to be filled with a gradient. - public var gradientFill: Gradient? { - didSet { - guard let gradientFill = gradientFill else { - fillColor = clear - return - } - - let gim = gradientFill.render()?.cgImage - - //inverts coordinate for graphics context rendering - var b = bounds - b.origin.y = self.height - b.origin.y - - UIGraphicsBeginImageContextWithOptions(CGSize(b.size), false, UIScreen.main.scale) - let context = UIGraphicsGetCurrentContext() - - context?.draw(gim!, in: CGRect(b), byTiling: true) - let uiimage = UIGraphicsGetImageFromCurrentImageContext() - let uicolor = UIColor(patternImage: uiimage!) - fillColor = Color(uicolor) - UIGraphicsEndImageContext() - } - } - - /// The path defining the shape to be rendered. If the path extends outside the layer bounds it will not automatically - /// be clipped to the layer. Defaults to nil. Animatable. - public var path: Path? { - didSet { - shapeLayer.path = path?.CGPath - } - } - - internal func updatePath() {} - - /// Adjusts the shape's frame to the bounding bounding box of its specified path. - public func adjustToFitPath() { - if shapeLayer.path == nil { - return - } - view.bounds = (shapeLayer.path?.boundingBoxOfPath)! - view.frame = view.bounds - } - - /// Returns the receiver's frame. Animatable. - public override var frame: Rect { - get { - return Rect(view.frame) - } - set { - view.frame = CGRect(newValue) - updatePath() - } - } - - /// The line width used when stroking the path. Defaults to 1.0. Animatable. - @IBInspectable - public var lineWidth: Double { - get { return Double(shapeLayer.lineWidth) } - set(width) { shapeLayer.lineWidth = CGFloat(width) } - } - - /// The color to stroke the path, or nil for no fill. Defaults to opaque black. Animatable. - public var strokeColor: Color? { - get { - return shapeLayer.strokeColor.map({ Color($0) }) - } - set(color) { - shapeLayer.strokeColor = color?.cgColor - } - } - - /// The color to fill the path, or nil for no fill. Defaults to opaque black. Animatable. - public var fillColor: Color? { - get { - return shapeLayer.fillColor.map({ Color($0) }) - } - set(color) { - shapeLayer.fillColor = color?.cgColor - } - } - - /// The fill rule used when filling the path. Defaults to `nonZero`. - public var fillRule: FillRule { - get { - switch shapeLayer.fillRule { - case kCAFillRuleNonZero: - return .nonZero - case kCAFillRuleEvenOdd: - return .evenOdd - default: - return .nonZero - } - } - set(fillRule) { - switch fillRule { - case .nonZero: - shapeLayer.fillRule = kCAFillRuleNonZero - case .evenOdd: - shapeLayer.fillRule = kCAFillRuleEvenOdd - } - } - } - - /// The current rotation value of the view. Animatable. - /// - returns: A Double value representing the cumulative rotation of the view, measured in Radians. - open override var rotation: Double { - get { - if let number = shapeLayer.value(forKeyPath: Layer.rotationKey) as? NSNumber { - return number.doubleValue - } - return 0.0 - } - set { - shapeLayer.setValue(newValue, forKeyPath: Layer.rotationKey) - } - } - - /// This value defines the start of the path used to draw the stroked outline. The value must be in the range [0,1] - /// with zero representing the start of the path and one the end. Values in between zero and one are interpolated - /// linearly along the path length. Defaults to zero. Animatable. - public var strokeStart: Double { - get { return Double(shapeLayer.strokeStart) } - set(start) { shapeLayer.strokeStart = CGFloat(start) } - } - - /// This value defines the end of the path used to draw the stroked outline. The value must be in the range [0,1] - /// with zero representing the start of the path and one the end. Values in between zero and one are interpolated - /// linearly along the path length. Defaults to 1.0. Animatable. - public var strokeEnd: Double { - get { return Double(shapeLayer.strokeEnd) } - set(end) { shapeLayer.strokeEnd = CGFloat(end) } - } - - /// The miter limit used when stroking the path. Defaults to ten. Animatable. - @IBInspectable - public var miterLimit: Double { - get { return Double(shapeLayer.miterLimit) } - set(miterLimit) { shapeLayer.miterLimit = CGFloat(miterLimit) } - } - - /// The cap style used when stroking the path. Defaults to `Butt`. - public var lineCap: LineCap { - get { - switch shapeLayer.lineCap { - case kCALineCapRound: - return .round - case kCALineCapSquare: - return .square - default: - return .butt - } - } - set(lineCap) { - switch lineCap { - case .butt: - shapeLayer.lineCap = kCALineCapButt - case .round: - shapeLayer.lineCap = kCALineCapRound - case .square: - shapeLayer.lineCap = kCALineCapSquare - } - } - } - - /// The join style used when stroking the path. Defaults to `Miter`. - public var lineJoin: LineJoin { - get { - switch shapeLayer.lineJoin { - case kCALineJoinRound: - return .round - case kCALineJoinBevel: - return .bevel - default: - return .miter - } - } - set(lineJoin) { - switch lineJoin { - case .miter: - shapeLayer.lineJoin = kCALineJoinMiter - case .round: - shapeLayer.lineJoin = kCALineJoinRound - case .bevel: - shapeLayer.lineJoin = kCALineJoinBevel - } - } - } - - /// The phase of the dashing pattern applied when creating the stroke. Defaults to zero. Animatable. - public var lineDashPhase: Double { - get { return Double(shapeLayer.lineDashPhase) } - set(phase) { shapeLayer.lineDashPhase = CGFloat(phase) } - } - - /// The dash pattern applied when creating the stroked version of the path. Defaults to nil. - public var lineDashPattern: [NSNumber]? { - get { return shapeLayer.lineDashPattern as [NSNumber]? } - set(pattern) { shapeLayer.lineDashPattern = pattern } - } - - /// The size of the receiver including the width of its stroke. - /// - returns: The bounding box that surrounds the shape and its line. - public func intrinsicContentSize() -> CGSize { - if let path = path { - let boundingBox = path.boundingBox() - return CGSize(width: boundingBox.max.x + lineWidth/2, height: boundingBox.max.y + lineWidth/2) - } else { - return CGSize() - } - } - - /// Returns true if there is no path. - public func isEmpty() -> Bool { - return path?.isEmpty() ?? true - } - - /// The join style for joints on the shape's path. - public enum LineJoin { - /// Specifies a miter line shape of the joints between connected segments of a stroked path. - case miter - - /// Specifies a round line shape of the joints between connected segments of a stroked path. - case round - - /// Specifies a bevel line shape of the joints between connected segments of a stroked path. - case bevel - } - - /// The cap style for the ends of the shape's path. - public enum LineCap { - /// Specifies a butt line cap style for endpoints for an open path when stroked. - case butt - - /// Specifies a round line cap style for endpoints for an open path when stroked. - case round - - /// Specifies a square line cap style for endpoints for an open path when stroked. - case square - } - - public override func hitTest(_ point: Point) -> Bool { - return path?.containsPoint(point) ?? false - } -} diff --git a/C4/UI/ShapeLayer.swift b/C4/UI/ShapeLayer.swift deleted file mode 100644 index fada523a..00000000 --- a/C4/UI/ShapeLayer.swift +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright © 2015 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore - -/// Extension for CAShapeLayer that allows overriding the actions for specific properties. -public class ShapeLayer: CAShapeLayer { - /// A boolean value that, when true, prevents the animation of a shape's properties. - /// - /// ```` - /// ShapeLayer.disableActions = true - /// circle.fillColor = red - /// ShapeLayer.disableActions = false - /// - /// This value can be set globally, after which changes to any shape's properties will be immediate. - public static var disableActions = true - - /// This method searches for the given action object of the layer. Actions define dynamic behaviors for a layer. For example, the animatable properties of a layer typically have corresponding action objects to initiate the actual animations. When that property changes, the layer looks for the action object associated with the property name and executes it. You can also associate custom action objects with your layer to implement app-specific actions. - /// - /// - parameter key: The identifier of the action. - /// - /// - returns: the action object assigned to the specified key. - public override func action(forKey key: String) -> CAAction? { - if ShapeLayer.disableActions == true { - return nil - } - - let animatableProperties = ["lineWidth", "strokeEnd", "strokeStart", "strokeColor", "path", "fillColor", "lineDashPhase", "contents", Layer.rotationKey, "shadowColor", "shadowRadius", "shadowOffset", "shadowOpacity", "shadowPath"] - if !animatableProperties.contains(key) { - return super.action(forKey: key) - } - - let animation: CABasicAnimation - if let viewAnimation = ViewAnimation.stack.last as? ViewAnimation, viewAnimation.spring != nil { - animation = CASpringAnimation(keyPath: key) - } else { - animation = CABasicAnimation(keyPath: key) - } - - animation.configureOptions() - animation.fromValue = value(forKey: key) - - if key == Layer.rotationKey { - if let layer = presentation() { - animation.fromValue = layer.value(forKey: key) - } - } - - return animation - } - - private var _rotation = 0.0 - - /// The value of the receiver's current rotation state. - /// This value is cumulative, and can represent values beyong +/- π - @objc public dynamic var rotation: Double { - return _rotation - } - - /// Initializes a new C4Layer - public override init() { - super.init() - } - - /// Initializes a new C4Layer from a specified layer of any other type. - /// - parameter layer: Another CALayer - public override init(layer: Any) { - super.init(layer: layer) - if let layer = layer as? ShapeLayer { - _rotation = layer._rotation - } - } - - /// Initializes a new C4Layer from data in a given unarchiver. - /// - parameter coder: An unarchiver object. - public required init?(coder: NSCoder) { - super.init(coder: coder) - } - - /// Sets a value for a given key. - /// - parameter value: The value for the property identified by key. - /// - parameter key: The name of one of the receiver's properties - public override func setValue(_ value: Any?, forKey key: String) { - super.setValue(value, forKey: key) - if key == Layer.rotationKey { - _rotation = value as? Double ?? 0.0 - } - } - - /// Returns a Boolean indicating whether changes to the specified key require the layer to be redisplayed. - /// - parameter key: A string that specifies an attribute of the layer. - /// - returns: A Boolean indicating whether changes to the specified key require the layer to be redisplayed. - public override class func needsDisplay(forKey key: String) -> Bool { - if key == Layer.rotationKey { - return true - } - return super.needsDisplay(forKey: key) - } - - /// Reloads the content of this layer. - /// Do not call this method directly. - public override func display() { - guard let presentation = presentation() else { - return - } - setValue(presentation._rotation, forKeyPath: "transform.rotation.z") - } -} - -extension CABasicAnimation { - /// Configures basic options for a CABasicAnimation. - /// - /// The options set in this method are favorable for the inner workings of C4's action behaviours. - @objc public func configureOptions() { - if let animation = ViewAnimation.currentAnimation { - self.autoreverses = animation.autoreverses - self.repeatCount = Float(animation.repeatCount) - } - self.fillMode = kCAFillModeBoth - self.isRemovedOnCompletion = false - } -} - -extension CASpringAnimation { - /// Configures basic options for a CABasicAnimation. - /// - /// The options set in this method are favorable for the inner workings of C4's animation behaviours. - public override func configureOptions() { - super.configureOptions() - if let animation = ViewAnimation.currentAnimation as? ViewAnimation, let spring = animation.spring { - mass = CGFloat(spring.mass) - damping = CGFloat(spring.damping) - stiffness = CGFloat(spring.stiffness) - initialVelocity = CGFloat(spring.initialVelocity) - } - } -} diff --git a/C4/UI/Star.swift b/C4/UI/Star.swift deleted file mode 100644 index 9dc2d4df..00000000 --- a/C4/UI/Star.swift +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// Star is a concrete subclass of Polygon that defines a star shape. -public class Star: Polygon { - - /// Initializes a new Star shape. - /// - /// ```` - /// let star = Star( - /// center: canvas.center, - /// pointCount: 5, - /// innerRadius: 50, - /// outerRadius: 100) - /// canvas.add(star) - /// ```` - /// - /// - parameter center: The center of the star - /// - parameter pointCount: The number of points on the star - /// - parameter innerRadius: The radial distance from the center of the star to the inner points - /// - parameter outerRadius: The radial distance from the center of the start to the outer points - convenience public init(center: Point, pointCount: Int, innerRadius: Double, outerRadius: Double) { - let wedgeAngle = 2.0 * Double.pi / Double(pointCount) - var angle = Double.pi/Double(pointCount)-Double.pi - - var pointArray = [Point]() - - for i in 0.. 0 ? options.formUnion(.repeat) : options.subtract(.repeat) - - UIView.animate(withDuration: duration, delay: 0, options: options, animations: { - ViewAnimation.stack.append(self) - UIView.setAnimationRepeatCount(Float(self.repeatCount)) - CATransaction.begin() - CATransaction.setAnimationDuration(self.duration) - CATransaction.setAnimationTimingFunction(timing) - CATransaction.setCompletionBlock({ - self.postCompletedEvent() - }) - for (key, value) in self.values { - object.setValue(value, forKeyPath: key) - } - CATransaction.commit() - ViewAnimation.stack.removeLast() - }, completion: nil) - ShapeLayer.disableActions = disable - } -} diff --git a/C4/UI/TextShape.swift b/C4/UI/TextShape.swift deleted file mode 100644 index b91a19e9..00000000 --- a/C4/UI/TextShape.swift +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit -import Foundation - -/// TextShape defines a concrete subclass of Shape that draws a bezier curve whose shape looks like text. -public class TextShape: Shape { - /// The text used to define the shape's path. Defaults to "C4". - public var text: String = "C4" { - didSet { - updatePath() - } - } - /// The font used to define the shape's path. Defaults to AvenirNext-DemiBold, 80pt. - public var font = Font(name: "AvenirNext-DemiBold", size: 80)! { - didSet { - updatePath() - } - } - - /// Initializes an empty TextShape. - override init() { - super.init() - - lineWidth = 0.0 - fillColor = C4Pink - } - - /// Initializes a new TextShape from a specifed string and a font - /// - /// ```` - /// let f = Font(name:"Avenir Next", size: 120) - /// let t = TextShape(text:"C4", font: f) - /// t.center = canvas.center - /// canvas.add(t) - /// ```` - /// - /// - parameter text: The string to be rendered as a shape - /// - parameter font: The font used to define the shape of the text - public convenience init?(text: String, font: Font) { - self.init() - self.text = text - self.font = font - - updatePath() - origin = Point() - } - - /// Initializes a new TextShape from a specifed string, using C4's default font. - /// - /// ```` - /// let t = TextShape(text:"C4") - /// t.center = canvas.center - /// canvas.add(t) - /// ```` - /// - /// - parameter text: text The string to be rendered as a shape - public convenience init?(text: String) { - guard let font = Font(name: "AvenirNext-DemiBold", size: 80) else { - return nil - } - self.init(text: text, font: font) - } - - override func updatePath() { - path = TextShape.createTextPath(text: text, font: font) - adjustToFitPath() - } - - internal class func createTextPath(text: String, font: Font) -> Path? { - let ctfont = font.ctFont as CTFont? - if ctfont == nil { - return nil - } - - var unichars = [UniChar](text.utf16) - var glyphs = [CGGlyph](repeating: 0, count: unichars.count) - if !CTFontGetGlyphsForCharacters(ctfont!, &unichars, &glyphs, unichars.count) { - // Failed to encode characters into glyphs - return nil - } - - var advances = [CGSize](repeating: CGSize(), count: glyphs.count) - CTFontGetAdvancesForGlyphs(ctfont!, .default, &glyphs, &advances, glyphs.count) - let textPath = CGMutablePath() - var invert = CGAffineTransform(scaleX: 1, y: -1) - var origin = CGPoint() - for (advance, glyph) in zip(advances, glyphs) { - if let glyphPath = CTFontCreatePathForGlyph(ctfont!, glyph, &invert) { - let translation = CGAffineTransform(translationX: origin.x, y: origin.y) - textPath.addPath(glyphPath, transform: translation) - } - origin.x += CGFloat(advance.width) - origin.y += CGFloat(advance.height) - } - return Path(path: textPath) - } -} diff --git a/C4/UI/Timer.swift b/C4/UI/Timer.swift deleted file mode 100644 index d4147358..00000000 --- a/C4/UI/Timer.swift +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright © 2015 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation - -/// You use the Timer class to create timer objects or, more simply, timers. A timer waits until a certain time interval has elapsed and then fires, executing a specified block of code. -public final class Timer: NSObject { - /// The current number of times the timer has fired. - public internal(set) var step = 0 - /// The number of times the timer will fire. - public internal(set) var count: Int - /// The time interval between firing. - public internal(set) var interval: Double - var action: () -> Void - /// The timer that the receiver manages - weak var timer: Foundation.Timer? - - /// Initializes a new timer. - /// - /// ```` - /// let t = Timer(0.25) { - /// print("tick") - /// } - /// ```` - /// - parameter interval: the time between firing - /// - parameter count: the total number of times the timer should fire, defaults to Int.max - /// - parameter action: a block of code to execute - public init(interval: Double, count: Int = Int.max, action: @escaping () -> Void) { - self.action = action - self.count = count - self.interval = interval - super.init() - } - - /// Tells the timer to fire, i.e. execute its block of code. - @objc public func fire() { - action() - step += 1 - if step >= count { - stop() - } - } - - /// Tells the timer to attach itself to the main run loop of an application, after calling `start` the timer will continue firing until the timer reaches its `count` or is otherwise stopped. - public func start() { - guard timer == nil else { - return // Timer already running - } - - let t = Foundation.Timer(timeInterval: TimeInterval(interval), target: self, selector: #selector(Timer.fire), userInfo: nil, repeats: true) - RunLoop.main.add(t, forMode: RunLoopMode.defaultRunLoopMode) - timer = t - } - - /// Pauses the execution of the timer. - public func pause() { - timer?.invalidate() - } - - /// Stops the timer and resets its step to 0. - public func stop() { - pause() - step = 0 - } -} diff --git a/C4/UI/Triangle.swift b/C4/UI/Triangle.swift deleted file mode 100644 index 30658edf..00000000 --- a/C4/UI/Triangle.swift +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import CoreGraphics - -/// Triangle defines a concrete subclass of Polygon whose shape is a closed triangle. -public class Triangle: Polygon { - - /// Initializes a new Triangle using the specified array of points. - /// - /// Protects against trying to create a triangle with less than three points. - /// - /// - parameter points: An array of Point structs. - public override init(_ points: [Point]) { - assert(points.count >= 3, "To create a Triangle you need to specify an array of at least 3 points") - super.init(points) - self.fillColor = C4Blue - self.close() - } - - /// Initializes a new Polygon from data in a given unarchiver. - /// - parameter coder: An unarchiver object. - required public init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/C4/UI/UIGestureRecognizer+Closure.swift b/C4/UI/UIGestureRecognizer+Closure.swift deleted file mode 100644 index e62bea3f..00000000 --- a/C4/UI/UIGestureRecognizer+Closure.swift +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import UIKit - -private var handlerAssociationKey: UInt8 = 0 -private var viewAssociationKey: UInt8 = 0 - -extension UIGestureRecognizer { - /// The current location of the gesture in the reference view. - public var location: Point { - return Point(self.location(in: referenceView)) - } - internal var referenceView: UIView? { - get { - let weakViewWrapper: WeakViewWrapper? = objc_getAssociatedObject(self, &viewAssociationKey) as? WeakViewWrapper - return weakViewWrapper?.view - } - set { - var weakViewWrapper: WeakViewWrapper? = objc_getAssociatedObject(self, &viewAssociationKey) as? WeakViewWrapper - if weakViewWrapper == nil { - weakViewWrapper = WeakViewWrapper(newValue) - objc_setAssociatedObject(self, &viewAssociationKey, weakViewWrapper, .OBJC_ASSOCIATION_RETAIN) - } else { - weakViewWrapper!.view = newValue - } - } - } - internal var actionHandler: AnyObject? { - get { - return objc_getAssociatedObject(self, &handlerAssociationKey) as AnyObject? - } - set(newValue) { - objc_setAssociatedObject(self, &handlerAssociationKey, newValue, .OBJC_ASSOCIATION_RETAIN) - } - } - internal convenience init(view: UIView) { - self.init() - self.referenceView = view - } - /// Keeps a weak reference to a view. Used to work around the limitation that extensions cannot have stored - /// properties and objc_setAssociatedObject does not support zeroing weak references. See - /// [on Stack Overflow](http://stackoverflow.com/questions/27632867/how-do-i-create-a-weak-stored-property-in-a-swift-extension) - internal class WeakViewWrapper: NSObject { - weak var view: UIView? - init(_ view: UIView?) { - self.view = view - } - } -} - -public typealias TapAction = (_ locations: [Point], _ center: Point, _ state: UIGestureRecognizerState) -> Void - -extension UITapGestureRecognizer { - /// The closure to call when there is a gesture event. - public var tapAction: TapAction? { - get { - return (actionHandler as? TapGestureHandler)?.action - } - set { - if let handler: AnyObject = actionHandler { - removeTarget(handler, action: #selector(TapGestureHandler.handleGesture(_:))) - } - if let action = newValue { - actionHandler = TapGestureHandler(action) - addTarget(actionHandler!, action: #selector(TapGestureHandler.handleGesture(_:))) - } else { - actionHandler = nil - } - } - } - internal convenience init(view: UIView, action: @escaping TapAction) { - self.init() - self.referenceView = view - self.tapAction = action - } - /// This class is used as the target of the gesture recognizer action. It forwards the method call to the closure. - internal class TapGestureHandler: NSObject { - let action: TapAction - init(_ action: @escaping TapAction) { - self.action = action - } - @objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) { - var locations = [Point]() - for i in 0.. Void - -extension UIPanGestureRecognizer { - /// The closure to call when there is a gesture event. - public var panAction: PanAction? { - get { - return (actionHandler as? PanGestureHandler)?.action - } - set { - if let handler: AnyObject = actionHandler { - removeTarget(handler, action: #selector(PanGestureHandler.handleGesture(_:))) - } - if let action = newValue { - actionHandler = PanGestureHandler(action) - addTarget(actionHandler!, action: #selector(PanGestureHandler.handleGesture(_:))) - } else { - actionHandler = nil - } - } - } - - /// The translation of the pan gesture in the coordinate system of the specified view. - /// - /// The x and y values report the total translation over time. They are not delta values from the last time that the translation was reported. Apply the translation value to the state of the view when the gesture is first recognized—do not concatenate the value each time the handler is called. - public var translation: Vector { - if let view = referenceView { - return Vector(self.translation(in: view)) - } - return Vector() - } - - /// The velocity of the pan gesture in the coordinate system of the specified view. - /// - /// The velocity of the pan gesture, which is expressed in points per second. The velocity is broken into horizontal and vertical components. - public var velocity: Vector { - return Vector(self.velocity(in: view)) - } - - internal convenience init(view: UIView, action: @escaping PanAction) { - self.init() - self.referenceView = view - self.panAction = action - } - /// This class is used as the target of the gesture recognizer action. It forwards the method call to the closure. - internal class PanGestureHandler: NSObject { - let action: PanAction - init(_ action: @escaping PanAction) { - self.action = action - } - @objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) { - var locations = [Point]() - for i in 0.. Void - -extension UIPinchGestureRecognizer { - /// The closure to call when there is a gesture event. - public var pinchAction: PinchAction? { - get { - return (actionHandler as? PinchGestureHandler)?.action - } - set { - if let handler: AnyObject = actionHandler { - removeTarget(handler, action: #selector(PinchGestureHandler.handleGesture(_:))) - } - if let action = newValue { - actionHandler = PinchGestureHandler(action) - addTarget(actionHandler!, action: #selector(PinchGestureHandler.handleGesture(_:))) - } else { - actionHandler = nil - } - } - } - internal convenience init(view: UIView, action: @escaping PinchAction) { - self.init() - self.referenceView = view - self.pinchAction = action - } - /// This class is used as the target of the gesture recognizer action. It forwards the method call to the closure. - internal class PinchGestureHandler: NSObject { - let action: PinchAction - init(_ action: @escaping PinchAction) { - self.action = action - } - @objc func handleGesture(_ gestureRecognizer: UIPinchGestureRecognizer) { - var locations = [Point]() - for i in 0.. Void - -extension UIRotationGestureRecognizer { - /// The closure to call when there is a gesture event. - public var rotationAction: RotationAction? { - get { - return (actionHandler as? RotationGestureHandler)?.action - } - set { - if let handler: AnyObject = actionHandler { - removeTarget(handler, action: #selector(RotationGestureHandler.handleGesture(_:))) - } - if let action = newValue { - actionHandler = RotationGestureHandler(action) - addTarget(actionHandler!, action: #selector(RotationGestureHandler.handleGesture(_:))) - } else { - actionHandler = nil - } - } - } - internal convenience init(view: UIView, action: @escaping RotationAction) { - self.init() - self.referenceView = view - self.rotationAction = action - } - /// This class is used as the target of the gesture recognizer action. It forwards the method call to the closure. - internal class RotationGestureHandler: NSObject { - let action: RotationAction - init(_ action: @escaping RotationAction) { - self.action = action - } - @objc func handleGesture(_ gestureRecognizer: UIRotationGestureRecognizer) { - action(Double(gestureRecognizer.rotation), Double(gestureRecognizer.velocity), gestureRecognizer.state) - } - } -} - -public typealias LongPressAction = (_ locations: [Point], _ center: Point, _ state: UIGestureRecognizerState) -> Void - -extension UILongPressGestureRecognizer { - /// The closure to call when there is a gesture event. - public var longPressAction: LongPressAction? { - get { - return (actionHandler as? LongPressGestureHandler)?.action - } - set { - if let handler: AnyObject = actionHandler { - removeTarget(handler, action: #selector(LongPressGestureHandler.handleGesture(_:))) - } - if let action = newValue { - actionHandler = LongPressGestureHandler(action) - addTarget(actionHandler!, action: #selector(LongPressGestureHandler.handleGesture(_:))) - } else { - actionHandler = nil - } - } - } - internal convenience init(view: UIView, action: @escaping LongPressAction) { - self.init() - self.referenceView = view - self.longPressAction = action - } - /// This class is used as the target of the gesture recognizer action. It forwards the method call to the closure. - internal class LongPressGestureHandler: NSObject { - let action: LongPressAction - init(_ action: @escaping LongPressAction) { - self.action = action - } - @objc func handleGesture(_ gestureRecognizer: UILongPressGestureRecognizer) { - var locations = [Point]() - for i in 0.. Void - -extension UISwipeGestureRecognizer { - /// The closure to call when there is a gesture event. - public var swipeAction: SwipeAction? { - get { - return (actionHandler as? SwipeGestureHandler)?.action - } - set { - if let handler: AnyObject = actionHandler { - removeTarget(handler, action: #selector(SwipeGestureHandler.handleGesture(_:))) - } - if let action = newValue { - actionHandler = SwipeGestureHandler(action) - addTarget(actionHandler!, action: #selector(SwipeGestureHandler.handleGesture(_:))) - } else { - actionHandler = nil - } - } - } - internal convenience init(view: UIView, action: @escaping SwipeAction) { - self.init() - self.referenceView = view - self.swipeAction = action - } - /// This class is used as the target of the gesture recognizer action. It forwards the method call to the closure. - internal class SwipeGestureHandler: NSObject { - let action: SwipeAction - init(_ action: @escaping SwipeAction) { - self.action = action - } - @objc func handleGesture(_ gestureRecognizer: UISwipeGestureRecognizer) { - var locations = [Point]() - for i in 0.. Void - -extension UIScreenEdgePanGestureRecognizer { - /// The closure to call when there is a gesture event. - public var screenEdgePanAction: ScreenEdgePanAction? { - get { - return (actionHandler as? ScreenEdgePanGestureHandler)?.action - } - set { - if let handler: AnyObject = actionHandler { - removeTarget(handler, action: #selector(ScreenEdgePanGestureHandler.handleGesture(_:))) - } - if let action = newValue { - actionHandler = ScreenEdgePanGestureHandler(action) - addTarget(actionHandler!, action: #selector(ScreenEdgePanGestureHandler.handleGesture(_:))) - } else { - actionHandler = nil - } - } - } - internal convenience init(view: UIView, action: @escaping ScreenEdgePanAction) { - self.init() - self.referenceView = view - self.screenEdgePanAction = action - } - - /// This class is used as the target of the gesture recognizer action. It forwards the method call to the closure. - internal class ScreenEdgePanGestureHandler: NSObject { - let action: ScreenEdgePanAction - init(_ action: @escaping ScreenEdgePanAction) { - self.action = action - } - @objc func handleGesture(_ gestureRecognizer: UIScreenEdgePanGestureRecognizer) { - action(gestureRecognizer.location, gestureRecognizer.state) - } - } -} diff --git a/C4/UI/UIImage+Color.swift b/C4/UI/UIImage+Color.swift deleted file mode 100644 index b391f71d..00000000 --- a/C4/UI/UIImage+Color.swift +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -public extension UIImage { - /// Create an image with a solid color. - /// - /// - parameter color: The color to use when creating the image. - /// - parameter size: The size of image to create. - /// - returns: A UIImage filled with the specified color and whose dimensions equal the give size - public class func createWithColor(_ color: UIColor, size: CGSize) -> UIImage { - let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) - UIGraphicsBeginImageContextWithOptions(size, false, 0) - color.setFill() - UIRectFill(rect) - let image = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return image! - } -} diff --git a/C4/UI/UIView+AddRemove.swift b/C4/UI/UIView+AddRemove.swift deleted file mode 100644 index 64d2e46c..00000000 --- a/C4/UI/UIView+AddRemove.swift +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -/// Extension to UIView that adds handling addition and removal of View objects. -extension UIView { - - /// Adds a view to the end of the receiver’s list of subviews. - /// When working with C4, use this method to add views because it handles the addition of both UIView and View. - /// - parameter subview: The view to be added. - public func add(_ subview: T?) { - if let v = subview as? UIView { - self.addSubview(v) - } else if let v = subview as? View { - self.addSubview(v.view) - } else { - fatalError("Can't add subview of class `\(type(of: subview))`") - } - } - - // MARK: - AddRemoveSubview - - /// Adds an array of views to the end of the receiver’s list of subviews. - /// When working with C4, use this method to add views because it handles the addition of both UIView and View. - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// let subv1 = View(frame: Rect(25,25,50,50)) - /// let subv2 = View(frame: Rect(100,25,50,50)) - /// v.add([subv1,subv2]) - /// ```` - /// - parameter subviews: An array of UIView or View subclasses to be added to the receiver - public func add(_ subviews: [T?]) { - for subv in subviews { - self.add(subv) - } - } - - /// Unlinks the view from the receiver and its window, and removes it from the responder chain. - /// Calling this method removes any constraints that refer to the view you are removing, or that refer to any view in the - /// subtree of the view you are removing. - /// When working with C4, use this method to remove views because it handles the removal of both UIView and View. - /// - parameter subview: The view to be removed. - public func remove(_ subview: T?) { - if let v = subview as? UIView { - v.removeFromSuperview() - } else if let v = subview as? View { - v.view.removeFromSuperview() - } else { - fatalError("Can't remove subview of class `\(type(of: subview))`") - } - } - - /// Moves the specified subview so that it appears behind its siblings. - /// When working with C4, use this method because it handles both UIView and View. - /// - parameter subview: The subview to move to the back. - public func sendToBack(_ subview: T?) { - if let v = subview as? UIView { - self.sendSubview(toBack: v) - } else if let v = subview as? View { - self.sendSubview(toBack: v.view) - } else { - fatalError("Can't operate on subview of class `\(type(of: subview))`") - } - } - - /// Moves the specified subview so that it appears on top of its siblings. - /// When working with C4, use this method because it handles both UIView and View. - /// - parameter subview: The subview to move to the front. - public func bringToFront(_ subview: T?) { - if let v = subview as? UIView { - self.bringSubview(toFront: v) - } else if let v = subview as? View { - self.bringSubview(toFront: v.view) - } else { - fatalError("Can't operate on subview of class `\(type(of: subview))`") - } - } -} diff --git a/C4/UI/UIViewController+C4View.swift b/C4/UI/UIViewController+C4View.swift deleted file mode 100644 index d271f5e0..00000000 --- a/C4/UI/UIViewController+C4View.swift +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import ObjectiveC -import UIKit - -private var canvasAssociationKey: UInt8 = 0 - -/// Extension to UIViewController that adds a `canvas` object. -/// -/// The canvas is a View representation of the view controller's UIView property. -/// -/// This extension adds properties and functionality of View to the native contoller object. -/// -/// For example: -/// ```` -/// canvas.backgroundColor = Color(r,g,b,a) -/// canvas.addTapGestureRecognizer(0.5){...} -/// ```` -/// -/// Where, `canvas` is essentially equal to `self.viewController.view`, keeping the interaction with a controller's main view consistent with using other View objects. -public extension UIViewController { - /// Returns a View object representation of the controller's `view` property. - public var canvas: View { - if let canvas = objc_getAssociatedObject(self, &canvasAssociationKey) as? View { - return canvas - } - - let canvas = View(view: view) - objc_setAssociatedObject( - self, - &canvasAssociationKey, - canvas, - .OBJC_ASSOCIATION_RETAIN) - return canvas - } -} diff --git a/C4/UI/View+Animation.swift b/C4/UI/View+Animation.swift deleted file mode 100644 index 7bd961d0..00000000 --- a/C4/UI/View+Animation.swift +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import QuartzCore -import UIKit - -/// Extension to View that handles animating of basic properties. -public extension View { - /// Internal function for creating a basic animation and applying that to the receiver. - /// - parameter keyPath: The identifier to animate - /// - parameter toValue: The value to which the identifier will be animated - internal func animateKeyPath(_ keyPath: String, toValue: AnyObject) { - let anim = CABasicAnimation() - anim.duration = 0.25 - anim.beginTime = CACurrentMediaTime() - anim.keyPath = keyPath - anim.fromValue = view.layer.presentation()?.value(forKeyPath: keyPath) - anim.toValue = toValue - view.layer.add(anim, forKey: "C4AnimateKeyPath: \(keyPath)") - view.layer.setValue(toValue, forKeyPath: keyPath) - } - - /// A class-level function that executes an animation using a specified block of code. - /// - /// - parameter duration: The length of time in seconds for the animation to execute. - /// - parameter animations: A block of code with specified animations to execute. - public class func animate(duration: Double, animations: @escaping () -> Void) { - UIView.animate(withDuration: duration, animations: animations) - } - - /// A class-level function that executes an animation using a specified block of code, with parameters for delaying and completion. - /// - /// - parameter duration: The length of time in seconds for the animation to execute. - /// - parameter delay: The length of time in seconds to wait before executing the specified block of code. - /// - parameter completion: A block of code to execute when the animation completes. - /// - parameter animations: A block of code with specified animations to execute. - public class func animate(duration: Double, delay: Double, animations: @escaping () -> Void, completion: ((Bool) -> Void)?) { - UIView.animate(withDuration: duration, animations: animations, completion: completion) - } - - /// A class-level function that executes an animation using a specified block of code, with parameters for delaying, completion and animation options. - /// - /// - parameter duration: The length of time in seconds for the animation to execute. - /// - parameter delay: The length of time in seconds to wait before executing the specified block of code. - /// - parameter options: Options for animating views using block objects, see: UIViewAnimationOptions. - /// - parameter animations: A block of code with specified animations to execute. - /// - parameter completion: A block of code to execute when the animation completes. - public class func animate(duration: Double, delay: Double, options: UIViewAnimationOptions, animations: @escaping () -> Void, completion: ((Bool) -> Void)?) { - UIView.animate(withDuration: duration, delay: delay, options: options, animations: animations, completion: completion) - } -} diff --git a/C4/UI/View+Border.swift b/C4/UI/View+Border.swift deleted file mode 100644 index cb006b82..00000000 --- a/C4/UI/View+Border.swift +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreGraphics - -/// Defines a structure representing the border of a View. -public struct Border { - - /// Returns the color of the border. - public var color: Color? - - /// Returns the corner radius of the border. - public var radius: Double - - /// Returns the width of the border. - public var width: Double - - /// Initializes a new border struct with the following defaults: - /// - /// radius = 0.0 - /// color = black - /// width = 0.0 - public init() { - color = Color(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0) - radius = 0.0 - width = 0.0 - } -} - -/// Extension to View that adds a border property. -public extension View { - /// Returns a struct that represents the current visible state of the receiver's border. Animatable. - /// - /// ```` - /// let v = View(frame: Rect(25,25,100,100)) - /// var b = Border() - /// b.width = 10.0 - /// b.color = C4Purple - /// v.border = b - /// canvas.add(v) - /// ```` - /// - /// Assigning a new value to this will change the `borderWidth`, `borderColor` and `cornderRadius` of the receiver's layer. - public var border: Border { - get { - var border = Border() - if let layer = layer { - if let borderColor = layer.borderColor { - border.color = Color(borderColor) - } - border.radius = Double(layer.cornerRadius) - border.width = Double(layer.borderWidth) - } - return border - } - set { - if let layer = layer { - layer.borderWidth = CGFloat(newValue.width) - if let color = newValue.color { - layer.borderColor = color.cgColor - } - layer.cornerRadius = CGFloat(newValue.radius) - } - } - } -} diff --git a/C4/UI/View+Gestures.swift b/C4/UI/View+Gestures.swift deleted file mode 100644 index 3f87ea7c..00000000 --- a/C4/UI/View+Gestures.swift +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -extension View { - /// Adds a tap gesture recognizer to the receiver's view. - /// ```` - /// let f = Rect(0,0,100,100) - /// let v = View(frame: f) - /// v.addTapGestureRecognizer { location, state in - /// println("tapped") - /// } - /// ```` - /// - parameter action: A block of code to be executed when the receiver recognizes a tap gesture. - /// - returns: A UITapGestureRecognizer that can be customized. - public func addTapGestureRecognizer(_ action: @escaping TapAction) -> UITapGestureRecognizer { - let gestureRecognizer = UITapGestureRecognizer(view: self.view, action: action) - self.view.addGestureRecognizer(gestureRecognizer) - return gestureRecognizer - } - - /// Adds a pan gesture recognizer to the receiver's view. - /// ```` - /// let f = Rect(0,0,100,100) - /// let v = View(frame: f) - /// v.addPanGestureRecognizer { location, translation, velocity, state in - /// println("panned") - /// } - /// - parameter action: A block of code to be executed when the receiver recognizes a pan gesture. - /// - returns: A UIPanGestureRecognizer that can be customized. - public func addPanGestureRecognizer(_ action: @escaping PanAction) -> UIPanGestureRecognizer { - let gestureRecognizer = UIPanGestureRecognizer(view: self.view, action: action) - self.view.addGestureRecognizer(gestureRecognizer) - return gestureRecognizer - } - - /// Adds a pinch gesture recognizer to the receiver's view. - /// ```` - /// let f = Rect(0,0,100,100) - /// let v = View(frame: f) - /// v.addPinchGestureRecognizer { scale, velocity, state in - /// println("pinched") - /// } - /// - parameter action: A block of code to be executed when the receiver recognizes a pinch gesture. - /// - returns: A UIPinchGestureRecognizer that can be customized. - public func addPinchGestureRecognizer(_ action: @escaping PinchAction) -> UIPinchGestureRecognizer { - let gestureRecognizer = UIPinchGestureRecognizer(view: view, action: action) - self.view.addGestureRecognizer(gestureRecognizer) - return gestureRecognizer - } - - /// Adds a rotation gesture recognizer to the receiver's view. - /// ```` - /// let f = Rect(0,0,100,100) - /// let v = View(frame: f) - /// v.addRotationGestureRecognizer { rotation, velocity, state in - /// println("rotated") - /// } - /// ```` - /// - parameter action: A block of code to be executed when the receiver recognizes a rotation gesture. - /// - returns: A UIRotationGestureRecognizer that can be customized. - public func addRotationGestureRecognizer(_ action: @escaping RotationAction) -> UIRotationGestureRecognizer { - let gestureRecognizer = UIRotationGestureRecognizer(view: view, action: action) - self.view.addGestureRecognizer(gestureRecognizer) - return gestureRecognizer - } - - /// Adds a longpress gesture recognizer to the receiver's view. - /// ```` - /// let f = Rect(0,0,100,100) - /// let v = View(frame: f) - /// v.addLongPressGestureRecognizer { location, state in - /// println("longpress") - /// } - /// ```` - /// - parameter action: A block of code to be executed when the receiver recognizes a longpress gesture. - /// - returns: A UILongPressGestureRecognizer that can be customized. - public func addLongPressGestureRecognizer(_ action: @escaping LongPressAction) -> UILongPressGestureRecognizer { - let gestureRecognizer = UILongPressGestureRecognizer(view: view, action: action) - self.view.addGestureRecognizer(gestureRecognizer) - return gestureRecognizer - } - - /// Adds a swipe gesture recognizer to the receiver's view. - /// ```` - /// let f = Rect(0,0,100,100) - /// let v = View(frame: f) - /// v.addSwipeGestureRecognizer { location, state in - /// println("swiped") - /// } - /// ```` - /// - parameter action: A block of code to be executed when the receiver recognizes a swipe gesture. - /// - returns: A UISwipeGestureRecognizer that can be customized. - public func addSwipeGestureRecognizer(_ action: @escaping SwipeAction) -> UISwipeGestureRecognizer { - let gestureRecognizer = UISwipeGestureRecognizer(view: view, action: action) - self.view.addGestureRecognizer(gestureRecognizer) - return gestureRecognizer - } - - /// Adds a screen edge pan gesture recognizer to the receiver's view. - /// ```` - /// let v = View(frame: canvas.bounds) - /// v.addSwipeGestureRecognizer { location, state in - /// println("edge pan") - /// } - /// ```` - /// - parameter action: A block of code to be executed when the receiver recognizes a screen edge pan gesture. - /// - returns: A UIScreenEdgePanGestureRecognizer that can be customized. - public func addScreenEdgePanGestureRecognizer(_ action: @escaping ScreenEdgePanAction) -> UIScreenEdgePanGestureRecognizer { - let gestureRecognizer = UIScreenEdgePanGestureRecognizer(view: view, action: action) - self.view.addGestureRecognizer(gestureRecognizer) - return gestureRecognizer - } -} diff --git a/C4/UI/View+KeyValues.swift b/C4/UI/View+KeyValues.swift deleted file mode 100644 index 254d2d7e..00000000 --- a/C4/UI/View+KeyValues.swift +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -extension View { - /// Sets the arbitrary keyed-data for the specified key. - /// - /// - parameter value: The value for the key identified by _key_. - /// - parameter key: The name of one of the receiver's properties. - open override func setValue(_ value: Any?, forKey key: String) { - switch (key, value) { - case ("frame", let nsvalue as NSValue): - frame = Rect(nsvalue.cgRectValue) - - case ("bounds", let nsvalue as NSValue): - bounds = Rect(nsvalue.cgRectValue) - - case ("center", let nsvalue as NSValue): - center = Point(nsvalue.cgPointValue) - - case ("origin", let nsvalue as NSValue): - origin = Point(nsvalue.cgPointValue) - - case ("size", let nsvalue as NSValue): - size = Size(nsvalue.cgSizeValue) - - case ("backgroundColor", let color as UIColor): - backgroundColor = Color(color) - - default: - super.setValue(value, forKey: key) - } - } - - /// Returns the arbitrary keyed-data specified by the given key. - /// - /// - parameter key: The name of one of the receiver's properties. - /// - /// - returns: The value for the data specified by the key. - open override func value(forKey key: String) -> Any? { - switch key { - case "frame": - return NSValue(cgRect: CGRect(frame)) - - case "bounds": - return NSValue(cgRect: CGRect(bounds)) - - case "center": - return NSValue(cgPoint: CGPoint(center)) - - case "origin": - return NSValue(cgPoint: CGPoint(origin)) - - case "size": - return NSValue(cgSize: CGSize(size)) - - case "backgroundColor": - if let color = backgroundColor { - return UIColor(color) - } else { - return nil - } - - default: - return super.value(forKey: key) - } - } -} diff --git a/C4/UI/View+Render.swift b/C4/UI/View+Render.swift deleted file mode 100644 index c5e0b93c..00000000 --- a/C4/UI/View+Render.swift +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -public extension View { - /// Creates a flattened image of the receiver and its subviews / layers. - /// - returns: A new Image - @objc public func render() -> Image? { - guard let l = layer else { - print("Could not retrieve layer for current object: \(self)") - return nil - } - UIGraphicsBeginImageContextWithOptions(CGSize(size), false, UIScreen.main.scale) - let context = UIGraphicsGetCurrentContext()! - l.render(in: context) - let uiimage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return Image(uiimage: uiimage!) - } -} - -public extension Shape { - /// Creates a flattened image of the receiver and its subviews / layers. - /// This override takes into consideration the lineWidth of the receiver. - /// - returns: A new Image - public override func render() -> Image? { - var s = CGSize(size) - var inset: CGFloat = 0 - - if let alpha = strokeColor?.alpha, alpha > 0.0 && lineWidth > 0 { - inset = CGFloat(lineWidth/2.0) - s = CGRect(frame).insetBy(dx: -inset, dy: -inset).size - } - - let scale = CGFloat(UIScreen.main.scale) - UIGraphicsBeginImageContextWithOptions(s, false, scale) - let context = UIGraphicsGetCurrentContext()! - context.translateBy(x: CGFloat(-bounds.origin.x)+inset, y: CGFloat(-bounds.origin.y)+inset) - shapeLayer.render(in: context) - let uiimage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return Image(uiimage: uiimage!) - } -} diff --git a/C4/UI/View+Shadow.swift b/C4/UI/View+Shadow.swift deleted file mode 100644 index 9317b229..00000000 --- a/C4/UI/View+Shadow.swift +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import CoreGraphics - -/// Defines a structure representing the border of a View. -public struct Shadow { - - /// Returns the corner radius of the border. Animatable. - /// - /// Assigning an new value to this will change the corner radius of the shadow. - public var radius: Double - - /// Returns the color of the shadow. Animatable. - /// - /// Assigning an new value to this will change the color of the shadow. - public var color: Color? - - /// Returns the offset of the shadow. Animatable. - /// - /// Assigning an new value to this will change the offset of the shadow. - public var offset: Size - - /// Returns the opacity of the shadow. Animatable. - /// - /// Assigning an new value to this will change the opacity of the shadow. - public var opacity: Double - - /// Returns the outline of the shadow. Animatable. - /// - /// Assigning an new value to this will change the path of the shadow. - public var path: Path? - - /// Initializes a new C4Shadow struct with the following defaults: - /// - /// radius = 5.0 - /// color = black - /// offset = (5,5) - /// opacity = 0.0 - public init() { - radius = 5.0 - color = Color(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0) - offset = Size(5, 5) - opacity = 0.0 - } -} - -/// Extension to View that adds a shadow property. -public extension View { - - /// Returns a struct that represents the current visible state of the receiver's shadow. Animatable. - /// - /// Assigning a new value to this will change the `shadowRadius`, `shadowColor`, `shadowOpacity`, `shadowPath` and - /// `shadowOffset` of the receiver's layer. - /// - /// The path is optional, and only set if it has a value. - /// - /// ```` - /// let v = View(frame: Rect(25,25,100,100)) - /// v.backgroundColor = white - /// var s = Shadow() - /// s.opacity = 0.5 - /// v.shadow = s - /// canvas.add(v) - /// ```` - public var shadow: Shadow { - get { - var shadow = Shadow() - if let layer = layer { - shadow.radius = Double(layer.shadowRadius) - if let color = layer.shadowColor { - shadow.color = Color(color) - } - shadow.offset = Size(layer.shadowOffset) - shadow.opacity = Double(layer.shadowOpacity) - if let path = layer.shadowPath { - shadow.path = Path(path: path) - } - } - return shadow - } - set { - if let layer = layer { - layer.shadowColor = newValue.color?.cgColor - layer.shadowRadius = CGFloat(newValue.radius) - layer.shadowOffset = CGSize(newValue.offset) - layer.shadowOpacity = Float(newValue.opacity) - layer.shadowPath = newValue.path?.CGPath - } - } - } -} diff --git a/C4/UI/View.swift b/C4/UI/View.swift deleted file mode 100644 index 0faf73a1..00000000 --- a/C4/UI/View.swift +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import UIKit - -extension NSValue { - /// Initializes an NSValue with a Point - /// - parameter point: a Point - convenience init(Point point: Point) { - self.init(cgPoint: CGPoint(point)) - } -} - -/// The View class defines a rectangular area on the screen and the interfaces for managing visual content in that area. The View class itself provides basic behavior for filling its rectangular area with a background color. More sophisticated content can be presented by subclassing UIView and implementing the necessary drawing and event-handling code yourself. The C4 framework also includes a set of standard subclasses that range from simple shapes to movies and images that can be used as-is. -open class View: NSObject { - /// A UIView. Internally, View wraps and provides access to an internal UIView. - open var view: UIView = LayerView() - - /// The current rotation value of the view. Animatable. - /// - returns: A Double value representing the cumulative rotation of the view, measured in Radians. - open var rotation: Double { - get { - if let number = animatableLayer.value(forKeyPath: Layer.rotationKey) as? NSNumber { - return number.doubleValue - } - return 0.0 - } - set { - animatableLayer.setValue(newValue, forKeyPath: Layer.rotationKey) - } - } - - /// The view that contains the receiver's animatable layer. - open var layerView: LayerView { - return self.view as! LayerView // swiftlint:disable:this force_cast - } - - open class LayerView: UIView { - open var animatableLayer: Layer { - return self.layer as! Layer // swiftlint:disable:this force_cast - } - - override open class var layerClass: AnyClass { - return Layer.self - } - } - - /// The view's primary layer. - /// - returns: A Layer, whose properties are animatable (e.g. rotation) - open var animatableLayer: Layer { - return self.layerView.animatableLayer - } - - /// Initializes a View. - public override init() { - } - - /// Initializes a new View from a UIView. - /// - parameter view: A UIView. - public init(view: UIView) { - self.view = view - } - - public init(copyView: View) { - //If there is a scale transform we need to undo that - let t = copyView.view.transform.inverted() - let x = sqrt(t.a * t.a + t.c * t.c) - let y = sqrt(t.b * t.b + t.d * t.d) - let s = CGAffineTransform(scaleX: x, y: y) - let frame = Rect(copyView.view.frame.applying(s)) - super.init() - view.frame = CGRect(frame) - copyViewStyle(copyView) - } - - public func copyViewStyle(_ viewToCopy: View) { - ShapeLayer.disableActions = true - anchorPoint = viewToCopy.anchorPoint - shadow = viewToCopy.shadow - border = viewToCopy.border - rotation = viewToCopy.rotation - interactionEnabled = viewToCopy.interactionEnabled - backgroundColor = viewToCopy.backgroundColor - opacity = viewToCopy.opacity - - if let maskToCopy = viewToCopy.mask { - if viewToCopy.mask is Shape { - mask = Shape(copy: viewToCopy.mask as! Shape) // swiftlint:disable:this force_cast - } else if viewToCopy.mask is Image { - mask = Image(copy: viewToCopy.mask as! Image) // swiftlint:disable:this force_cast - } else { - mask = View(copyView: maskToCopy) - } - mask?.center = maskToCopy.center - } - - transform = viewToCopy.transform - ShapeLayer.disableActions = false - } - - /// Initializes a new View with the specifed frame. - /// ```` - /// let f = Rect(0,0,100,100) - /// let v = View(frame: f) - /// canvas.add(v) - /// ```` - /// - parameter frame: A Rect, which describes the view’s location and size in its superview’s coordinate system. - public init(frame: Rect) { - super.init() - self.view.frame = CGRect(frame) - } - - /// Returns the receiver's layer. - @objc public dynamic var layer: CALayer? { - return view.layer - } - - /// Returns the receiver's frame. Animatable. - public var frame: Rect { - get { - return Rect(view.frame) - } - set { - view.frame = CGRect(newValue) - } - } - - /// Returns the receiver's bounds. Animatable. - public var bounds: Rect { - get { - return Rect(view.bounds) - } - set { - view.bounds = CGRect(newValue) - } - } - - /// A Boolean indicating whether subviews, and layers are clipped to the object’s bounds. Animatable. - public var masksToBounds: Bool { - get { - return layer!.masksToBounds - } - set { - layer?.masksToBounds = newValue - } - } - - /// Returns the receiver's center point. Animatable. - public var center: Point { - get { - return Point(view.center) - } - set { - view.center = CGPoint(newValue) - } - } - - /// Returns the receiver's origin. Animatable. - public var origin: Point { - get { - return center - Vector(x: size.width/2, y: size.height/2) - } - set { - center = newValue + Vector(x: size.width/2, y: size.height/2) - } - } - - /// Returns the receiver's frame size. Animatable. - public var size: Size { - get { - return bounds.size - } - set { - bounds = Rect(origin, newValue) - } - } - - /// Returns the receiver's frame width. Animatable. - @objc public dynamic var width: Double { - return Double(bounds.size.width) - } - - /// Returns the receiver's frame height. Animatable. - @objc public dynamic var height: Double { - return Double(bounds.size.height) - } - - /// Returns the receiver's background color. Animatable. - public var backgroundColor: Color? { - get { - if let color = view.backgroundColor { - return Color(color) - } else { - return nil - } - } - set { - if let color = newValue { - view.backgroundColor = UIColor(color) - } else { - view.backgroundColor = nil - } - } - } - - /// Returns the receiver's opacity. Animatable. - @objc public dynamic var opacity: Double { - get { - return Double(view.alpha) - } - set { - view.alpha = CGFloat(newValue) - } - } - - /// Returns true if the receiver is hidden, false if visible. - public var hidden: Bool { - get { - return view.isHidden - } - set { - view.isHidden = newValue - } - } - - /// Returns the receiver's current transform. - public var transform: Transform { - get { - return Transform(view.layer.transform) - } - set { - view.layer.transform = newValue.transform3D - } - } - - /// Defines the anchor point of the layer's bounds rect, as a point in - /// normalized layer coordinates - '(0, 0)' is the bottom left corner of - /// the bounds rect, '(1, 1)' is the top right corner. Defaults to - /// '(0.5, 0.5)', i.e. the center of the bounds rect. Animatable. - public var anchorPoint: Point { - get { - return Point(view.layer.anchorPoint) - } - set(val) { - let oldFrame = view.frame - view.layer.anchorPoint = CGPoint(val) - view.frame = oldFrame - } - } - - /// The layer’s position on the z axis. Animatable. - /// The default value of this property is 0. Changing the value of this property changes the the front-to-back ordering of layers onscreen. This can affect the visibility of layers whose frame rectangles overlap. - /// The value of this property is measured in points. - @objc public dynamic var zPosition: Double { - get { - return Double(self.layer!.zPosition) - } set { - self.layer!.zPosition = CGFloat(newValue) - } - } - - /// An optional view whose alpha channel is used to mask the receiver's content. - /// The mask view's alpha channel determines how much of the receiver's content and background shows through. Fully or partially opaque pixels allow the underlying content to show through but fully transparent pixels block that content. - /// The default value of this property is nil. When configuring a mask, remember to set the size and position of the mask layer to ensure it is aligned properly with the layer it masks. - /// The layer you assign to this property must not have a superlayer. If it does, the behavior is undefined. - public var mask: View? { - didSet { - if let mask = mask, mask.view.superview != nil { - print("Invalid Mask. The view you are using as a mask has already been added to another view.") - self.mask = nil - } else { - self.layer?.mask = mask?.layer - } - } - } - - // MARK: - Touchable - - /// Returns true if the receiver accepts touch events. - public var interactionEnabled: Bool = true { - didSet { - self.view.isUserInteractionEnabled = interactionEnabled - } - } - - // MARK: - AddRemoveSubview - - /// Adds a view to the end of the receiver’s list of subviews. - /// When working with C4, use this method to add views because it handles the addition of both UIView and View. - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// let subv = View(frame: Rect(25,25,50,50)) - /// v.add(subv) - /// ```` - /// - parameter subview: The view to be added. - public func add(_ subview: T?) { - if let v = subview as? UIView { - view.addSubview(v) - } else if let v = subview as? View { - view.addSubview(v.view) - } else { - fatalError("Can't add subview of class `\(type(of: subview))`") - } - } - - // MARK: - AddRemoveSubview - - /// Adds an array of views to the end of the receiver’s list of subviews. - /// When working with C4, use this method to add views because it handles the addition of both UIView and View. - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// let subv1 = View(frame: Rect(25,25,50,50)) - /// let subv2 = View(frame: Rect(100,25,50,50)) - /// v.add([subv1,subv2]) - /// ```` - /// - parameter subviews: An array of UIView or View objects to be added to the receiver. - public func add(_ subviews: [T?]) { - for subv in subviews { - self.add(subv) - } - } - - /// Unlinks the view from the receiver and its window, and removes it from the responder chain. - /// Calling this method removes any constraints that refer to the view you are removing, or that refer to any view in the - /// subtree of the view you are removing. - /// When working with C4, use this method to add views because it handles the removal of both UIView and View. - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// let subv = View(frame: Rect(25,25,50,50)) - /// v.add(subv) - /// v.remove(subv) - /// ```` - /// - parameter subview: The view to be removed. - public func remove(_ subview: T?) { - if let v = subview as? UIView { - v.removeFromSuperview() - } else if let v = subview as? View { - v.view.removeFromSuperview() - } else { - fatalError("Can't remove subview of class `\(type(of: subview))`") - } - } - - /// Unlinks the view from its superview and its window, and removes it from the responder chain. - /// If the view’s superview is not nil, the superview releases the view. - /// Calling this method removes any constraints that refer to the view you are removing, or that refer to any view in the - /// subtree of the view you are removing. - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// let subv = View(frame: Rect(25,25,50,50)) - /// v.add(subv) - /// subv.removeFromSuperview() - /// ```` - public func removeFromSuperview() { - self.view.removeFromSuperview() - } - - /// Moves the specified subview so that it appears behind its siblings. - /// - parameter subview: The subview to move to the back. - public func sendToBack(_ subview: T?) { - if let v = subview as? UIView { - view.sendSubview(toBack: v) - } else if let v = subview as? View { - view.sendSubview(toBack: v.view) - } else { - fatalError("Can't operate on subview of class `\(type(of: subview))`") - } - } - - /// Moves the specified subview so that it appears on top of its siblings. - /// - parameter subview: The subview to move to the front. - public func bringToFront(_ subview: T?) { - if let v = subview as? UIView { - view.bringSubview(toFront: v) - } else if let v = subview as? View { - view.bringSubview(toFront: v.view) - } else { - fatalError("Can't operate on subview of class `\(type(of: subview))`") - } - } - - // MARK: - HitTest - - /// Checks if a specified point falls within the bounds of the current object. - /// - note: - /// Because each view has its own coordinates, if you want to check if a point from anywhere on screen falls within a - /// specific view you should use `hitTest(point, from: canvas)`. - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// v.hitTest(Point(50,50)) //-> true - /// v.hitTest(Point(50, 101)) //-> false - /// ```` - /// - parameter point: A `Point` to examine - /// - returns: `true` if the point is within the object's frame, otherwise `false`. - public func hitTest(_ point: Point) -> Bool { - return CGRect(bounds).contains(CGPoint(point)) - } - - /// Checks if a specified point, from another view, falls within the frame of the receiver. - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// canvas.add(v) - /// canvas.addTapGestureRecognizer() { location, state in - /// if v.hitTest(location, from: self.canvas) { - /// println("C4") - /// } - /// } - /// ```` - /// - parameter point: The point to check - /// - parameter from: The view whose coordinate system the point is to be converted from - /// - returns: `true` if the point is within the object's frame, otherwise `false`. - public func hitTest(_ point: Point, from: View) -> Bool { - let p = convert(point, from: from) - return hitTest(p) - } - - // MARK: – Convert - - /// Converts a specified point from a given view's coordinate system to that of the receiver. - /// ```` - /// let p = Point() - /// let cp = aView.convert(p, from:canvas) - /// ```` - /// - parameter point: The point to convert - /// - parameter from: The view whose coordinate system the point is to be converted from - /// - returns: A `Point` whose values have been translated into the receiver's coordinate system. - public func convert(_ point: Point, from: View) -> Point { - return Point(view.convert(CGPoint(point), from: from.view)) - } - - // MARK: - Positioning - - /// Moves the receiver so that it appears on top of the specified view. - /// - parameter view: The view above which the receive will be positioned - public func positionAbove(_ view: View) { - zPosition = view.zPosition + 1 - } - - /// Moves the receiver so that it appears on below of the specified view. - /// - parameter view: The view below which the receive will be positioned - public func positionBelow(_ view: View) { - zPosition = view.zPosition - 1 - } -} diff --git a/C4/UI/ViewAnimation.swift b/C4/UI/ViewAnimation.swift deleted file mode 100644 index 97e9586b..00000000 --- a/C4/UI/ViewAnimation.swift +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import Foundation -import UIKit - -///A structure representing the characteristics of spring motion. -public struct Spring { - /// The mass of the object attached to the end of the spring. Must be greater than 0. Defaults to one. - public var mass: Double - /// The spring stiffness coefficient. Must be greater than 0. Defaults to 100. - public var stiffness: Double - /// The damping coefficient. Must be greater than or equal to 0. Defaults to 10. - public var damping: Double - /// The initial velocity of the object attached to the spring. - /// Defaults to zero, which represents an unmoving object. - /// Negative values represent the object moving away from the spring attachment point, - /// positive values represent the object moving towards the spring attachment point. - public var initialVelocity: Double - - /// Initializes a new Spring structure - /// - parameter mass: The mass for the object - /// - parameter stiffness: The stiffness of the spring - /// - parameter damping: The damping coefficient used to calculate the motion of the object - /// - parameter initialVelocity: The initial velocity for the object - public init(mass: Double = 1.0, stiffness: Double = 100.0, damping: Double = 10.0, initialVelocity: Double = 1.0) { - self.mass = mass - self.stiffness = stiffness - self.damping = damping - self.initialVelocity = initialVelocity - } -} - -/// ViewAnimation is a concrete subclass of Animation whose execution blocks affect properties of view-based objects. -public class ViewAnimation: Animation { - /// An optional Spring structure. - /// If this value is non-nil, property animations will default to CASpringAnimation if they are able. - public var spring: Spring? - - /// The amount of time to way before executing the animation. - public var delay: TimeInterval = 0 - - /// A block animations to execute. - public var animations: () -> Void - - /// Initializes an animation object with a block of animtinos to execute. - /// - /// let anim = ViewAnimation() { - /// aView.backgroundColor = C4Blue - /// } - /// - /// - parameter animations: a block of animations to execute. - public init(_ animations: @escaping () -> Void) { - self.animations = animations - } - - /// Initializes a new ViewAnimation. - /// - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// canvas.add(v) - /// let bg = ViewAnimation(duration: 0.25) { - /// v.backgroundColor = C4Blue - /// } - /// wait(1.0) { - /// bg.animate() - /// } - /// ```` - /// - /// - parameter duration: The length of the animations, measured in seconds. - /// - parameter animations: A block containing a variety of animations to execute - public convenience init(duration: TimeInterval, animations: @escaping () -> Void) { - self.init(animations) - self.duration = duration - } - - /// This timingFunction represents one segment of a function that defines the pacing of an animation as a timing curve. The function maps an input time normalized to the range [0,1] to an output time also in the range [0,1]. - /// Options are `Linear`, `EaseOut`, `EaseIn`, `EaseInOut` - public var timingFunction: CAMediaTimingFunction { - switch curve { - case .linear: - return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) - case .easeOut: - return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) - case .easeIn: - return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) - case .easeInOut: - return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) - } - } - - ///Options for animating views using block objects. - public var options: UIViewAnimationOptions { - var options: UIViewAnimationOptions = [UIViewAnimationOptions.beginFromCurrentState] - switch curve { - case .linear: - options = [options, .curveLinear] - case .easeOut: - options = [options, .curveEaseOut] - case .easeIn: - options = [options, .curveEaseIn] - case .easeInOut: - options = [options, .curveEaseIn, .curveEaseOut] - } - - if autoreverses == true { - options.formUnion(.autoreverse) - } else { - options.subtract(.autoreverse) - } - - if repeatCount > 0 { - options.formUnion(.repeat) - } else { - options.subtract(.repeat) - } - return options - } - - /// Initiates the changes specified in the receivers `animations` block. - public override func animate() { - let disable = ShapeLayer.disableActions - ShapeLayer.disableActions = false - - wait(delay) { - if let spring = self.spring { - self.animateWithSpring(spring: spring) - } else { - self.animateNormal() - } - } - - ShapeLayer.disableActions = disable - } - - private func animateWithSpring(spring: Spring) { - UIView.animate(withDuration: self.duration, delay: 0, usingSpringWithDamping: CGFloat(spring.damping), initialSpringVelocity: CGFloat(spring.initialVelocity), options: self.options, animations: self.animationBlock, completion: nil) - } - - private func animateNormal() { - UIView.animate(withDuration: self.duration, delay: 0, options: self.options, animations: self.animationBlock, completion: nil) - } - - private func animationBlock() { - ViewAnimation.stack.append(self) - UIView.setAnimationRepeatCount(Float(self.repeatCount)) - self.doInTransaction(action: self.animations) - ViewAnimation.stack.removeLast() - } - - private func doInTransaction(action: () -> Void) { - CATransaction.begin() - CATransaction.setAnimationDuration(duration) - CATransaction.setAnimationTimingFunction(timingFunction) - CATransaction.setCompletionBlock({ - self.postCompletedEvent() - }) - action() - CATransaction.commit() - } -} - -/// A sequence of animations that run one after the other. This class ignores the duration property. -public class ViewAnimationSequence: Animation { - private var animations: [Animation] - private var currentAnimationIndex: Int = -1 - private var currentObserver: AnyObject? - - /// Initializes a set of animations to execute in sequence. - /// - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// canvas.add(v) - /// let bg = ViewAnimation(duration: 0.25) { - /// v.backgroundColor = C4Blue - /// } - /// let ctr = ViewAnimation(duration: 0.25) { - /// v.center = self.canvas.center - /// } - /// let seq = ViewAnimationSequence(animations: [bg,ctr]) - /// wait(1.0) { - /// seq.animate() - /// } - /// ```` - /// - parameter animations: An array of C4Animations - public init(animations: [Animation]) { - self.animations = animations - } - - /// Calling this method will tell the receiver to begin animating. - public override func animate() { - if currentAnimationIndex != -1 { - // Animation is already running - return - } - startNext() - } - - private func startNext() { - if let observer: AnyObject = currentObserver { - let currentAnimation = animations[currentAnimationIndex] - currentAnimation.removeCompletionObserver(observer) - currentObserver = nil - } - - currentAnimationIndex += 1 - if currentAnimationIndex >= animations.count && self.repeats { - // Start from the beginning if sequence repeats - currentAnimationIndex = 0 - } - - if currentAnimationIndex >= animations.count { - // Reached the end - currentAnimationIndex = -1 - postCompletedEvent() - return - } - - let animation = animations[currentAnimationIndex] - currentObserver = animation.addCompletionObserver({ - self.startNext() - }) - animation.animate() - } -} - -/// Groups animations so that they can all be run at the same time. The completion call is dispatched when all the -/// animations in the group have finished. This class ignores the duration property. -public class ViewAnimationGroup: Animation { - private var animations: [Animation] - private var observers: [AnyObject] = [] - private var completed: [Bool] - - /// Initializes a set of animations to be executed at the same time. - /// - /// ```` - /// let v = View(frame: Rect(0,0,100,100)) - /// canvas.add(v) - /// let bg = ViewAnimation(duration: 0.25) { - /// v.backgroundColor = C4Blue - /// } - /// let ctr = ViewAnimation(duration: 0.25) { - /// v.center = self.canvas.center - /// } - /// let grp = ViewAnimationGroup(animations: [bg,ctr]) - /// wait(1.0) { - /// grp.animate() - /// } - /// ```` - /// - parameter animations: An array of C4Animations - public init(animations: [Animation]) { - self.animations = animations - completed = [Bool](repeating: false, count: animations.count) - } - - /// Calling this method will tell the receiver to begin animating. - public override func animate() { - if !observers.isEmpty { - // Animation is already running - return - } - - for i in 0.. start ? true : false) - } - - /// Initializes a new Wedge, with the wedge always taking the shortest distance between start and end. - /// - /// This shape differs from Arc in that is adds a point at the "center" of the circle on which the wedge exists. - /// - /// ```` - /// let w = Wedge(center: canvas.center, radius: 50, start: Double.pi_4 * 3, end: Double.pi_4, clockwise: true) - /// canvas.add(w) - /// ```` - /// - /// - parameter center: The center of the wedge. - /// - parameter radius: The radius of the wedge. - /// - parameter start: The start angle of the wedge. - /// - parameter end: The end angle of the wedge. - /// - parameter clockwise: Whether or not to close the shape in a clockwise fashion. - public init(center: Point, radius: Double, start: Double, end: Double, clockwise: Bool) { - super.init() - - let wedge = CGMutablePath() - wedge.addArc(center: CGPoint(center), radius: CGFloat(radius), startAngle: CGFloat(start), endAngle: CGFloat(end), clockwise: !clockwise) - wedge.addLine(to: CGPoint(center)) - wedge.closeSubpath() - path = Path(path: wedge) - adjustToFitPath() - } -} diff --git a/C4App/Images.xcassets/AppIcon.appiconset/Contents.json b/C4App/Images.xcassets/AppIcon.appiconset/Contents.json index 24be6431..e49cc9dd 100644 --- a/C4App/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/C4App/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,86 +1,112 @@ { "images" : [ { - "size" : "29x29", "idiom" : "iphone", - "filename" : "iPhone29@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { - "size" : "29x29", "idiom" : "iphone", - "filename" : "iPhone29@3x.png", - "scale" : "3x" + "scale" : "3x", + "size" : "20x20" }, { - "size" : "40x40", + "filename" : "iPhone29@2x.png", "idiom" : "iphone", - "filename" : "iPhone40@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { - "size" : "40x40", + "filename" : "iPhone29@3x.png", "idiom" : "iphone", - "filename" : "iPhone40@3x.png", - "scale" : "3x" + "scale" : "3x", + "size" : "29x29" }, { - "size" : "60x60", + "filename" : "iPhone40@2x.png", "idiom" : "iphone", - "filename" : "iPhone60@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "iPhone40@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" }, { - "size" : "60x60", + "filename" : "iPhone60@2x.png", "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { "filename" : "iPhone60@3x.png", - "scale" : "3x" + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" }, { - "size" : "29x29", "idiom" : "ipad", - "filename" : "iPad29.png", - "scale" : "1x" + "scale" : "1x", + "size" : "20x20" }, { - "size" : "29x29", "idiom" : "ipad", - "filename" : "iPad29@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "20x20" }, { - "size" : "40x40", + "filename" : "iPad29.png", "idiom" : "ipad", - "filename" : "iPad40.png", - "scale" : "1x" + "scale" : "1x", + "size" : "29x29" }, { - "size" : "40x40", + "filename" : "iPad29@2x.png", "idiom" : "ipad", - "filename" : "iPad40@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "29x29" }, { - "size" : "76x76", + "filename" : "iPad40.png", "idiom" : "ipad", - "filename" : "iPad76.png", - "scale" : "1x" + "scale" : "1x", + "size" : "40x40" }, { - "size" : "76x76", + "filename" : "iPad40@2x.png", "idiom" : "ipad", - "filename" : "iPad76@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "iPad76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" }, { - "size" : "83.5x83.5", + "filename" : "iPad76@2x.png", "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { "filename" : "iPadPro83.5@2x.png", - "scale" : "2x" + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "logoLArge.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/C4App/Images.xcassets/AppIcon.appiconset/logoLArge.png b/C4App/Images.xcassets/AppIcon.appiconset/logoLArge.png new file mode 100644 index 0000000000000000000000000000000000000000..0e5b600869d83a3bbef1a78ca858edc6210b6bfa GIT binary patch literal 85071 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 literal 0 HcmV?d00001 diff --git a/C4App/ViewController.swift b/C4App/ViewController.swift index f4a20ee4..223bf671 100644 --- a/C4App/ViewController.swift +++ b/C4App/ViewController.swift @@ -17,7 +17,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. -import C4 +import TNT import UIKit class ViewController: CanvasController { diff --git a/Tests/ColorTests.swift b/Tests/ColorTests.swift deleted file mode 100644 index ebc6059e..00000000 --- a/Tests/ColorTests.swift +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import C4 -import XCTest - -class ColorTests: XCTestCase { - func testInitWithRedHexValue() { - let redColor = Color(0xFF0000) - XCTAssertEqual(redColor.red, 1.0, "Red value should be 1.0") - XCTAssertEqual(redColor.green, 0.0, "Green value should be 0.0") - XCTAssertEqual(redColor.blue, 0.0, "Blue value should be 0.0") - XCTAssertEqual(redColor.alpha, 1.0, "Alpha value should be 1.0") - } - - func testInitWithGreenHexValue() { - let greenColor = Color(0x00FF00) - XCTAssertEqual(greenColor.red, 0.0, "Red value should be 0.0") - XCTAssertEqual(greenColor.green, 1.0, "Green value should be 1.0") - XCTAssertEqual(greenColor.blue, 0.0, "Blue value should be 0.0") - XCTAssertEqual(greenColor.alpha, 1.0, "Alpha value should be 1.0") - } - - func testInitWithBlueHexValue() { - let blueColor = Color(0x0000FF) - XCTAssertEqual(blueColor.red, 0.0, "Red value should be 0.0") - XCTAssertEqual(blueColor.green, 0.0, "Green value should be 0.0") - XCTAssertEqual(blueColor.blue, 1.0, "Blue value should be 1.0") - XCTAssertEqual(blueColor.alpha, 1.0, "Alpha value should be 1.0") - } - - func testHueSaturationBrightnesWithRGBColors() { - XCTAssertEqual(red.hue, 0.0/360.0, "Red value should be 0.0") - XCTAssertEqual(red.saturation, 1.0, "Saturation value should be 0.0") - XCTAssertEqual(red.brightness, 1.0, "Brightness value should be 1.0") - XCTAssertEqual(red.alpha, 1.0, "Alpha value should be 1.0") - - XCTAssertEqual(green.hue, 120.0/360.0, "Red value should be 0.0") - XCTAssertEqual(green.saturation, 1.0, "Saturation value should be 1.0") - XCTAssertEqual(green.brightness, 1.0, "Brightness value should be 1.0") - XCTAssertEqual(green.alpha, 1.0, "Alpha value should be 1.0") - - XCTAssertEqual(blue.hue, 240.0/360.0, "Red value should be 0.0") - XCTAssertEqual(blue.saturation, 1.0, "Saturation value should be 1.0") - XCTAssertEqual(blue.brightness, 1.0, "Brightness value should be 1.0") - XCTAssertEqual(blue.alpha, 1.0, "Alpha value should be 1.0") - } -} diff --git a/Tests/Info.plist b/Tests/Info.plist deleted file mode 100644 index ba72822e..00000000 --- a/Tests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/Tests/MathTests.swift b/Tests/MathTests.swift deleted file mode 100644 index 4ed7c718..00000000 --- a/Tests/MathTests.swift +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import C4 -import XCTest - -class MathTests: XCTestCase { - - func testLerp() { - XCTAssert(lerp(0.0, 10.0, at: 0.2) == 2.0, "Value should be interpolated") - } - - func testClampLess() { - let testValue = clamp(-1, min: 10, max: 20) - let correctValue = 10 - XCTAssertEqual(testValue, correctValue, "Value should be clamped to lower bound") - } - - func testClampNoOp() { - let testValue = clamp(11, min: 10, max: 20) - let correctValue = 11 - XCTAssertEqual(testValue, correctValue, "Value should not be clamped") - } - - func testClampGreater() { - let testValue = clamp(21, min: 10, max: 20) - let correctValue = 20 - XCTAssertEqual(testValue, correctValue, "Value should be clamped to upper bound") - } - - func testMapOpen() { - let testValue = map(5, from: 0..<10, to: 0..<20) - let correctValue = 10.0 - XCTAssertEqual(testValue, correctValue, "Value should be mapped to the target range") - } - - func testMapClosed() { - let testValue = map(5, from: 0...10, to: 0...20) - let correctValue = 10.0 - XCTAssertEqual(testValue, correctValue, "Value should be mapped to the target range") - } - - func testLerpDouble() { - let testValue = map(5.0, from: 0.0..<10.0, to: 0.0..<20.0) - let correctValue = 10.0 - XCTAssertEqual(testValue, correctValue, "Double value should be mapped to the target range") - } - - func testLerpInt() { - let testValue = map(6, from: 0..<10, to: 0..<20) - let correctValue = 12.0 - XCTAssertEqual(testValue, correctValue, "Double value should be mapped to the target range") - } - - func testRandom() { - let testValue = random(below: 100) - XCTAssertLessThan(testValue, 100, "Returned value for random is not below provided value") - } - - func testRandomBelow() { - let upperBound = 10 - let samples = 1000 - var min = Int.max - var max = Int.min - for _ in 0.. max { max = value } - } - - XCTAssertGreaterThanOrEqual(min, 0, "Random values should be >= 0") - XCTAssertLessThan(max, upperBound, "Random values should be < \(upperBound)") - } - - func testRandomBetween() { - let lowerBound = 10 - let upperBound = 20 - let samples = 1000 - var min = Int.max - var max = Int.min - for _ in 0.. max { max = value } - } - - XCTAssertGreaterThanOrEqual(min, 0, "Random values should be >= \(lowerBound)") - XCTAssertLessThan(max, upperBound, "Random values should be < \(upperBound)") - } - - func testRandom01() { - let samples = 1000 - var min = 1.0 - var max = 0.0 - for _ in 0.. max { max = value } - } - - XCTAssertGreaterThanOrEqual(min, 0.0, "Random values should be >= 0") - XCTAssertLessThan(max, 1.0, "Random values should be < 1") - } - - func testRadToDeg() { - let testValue = radToDeg(Double.pi / 2.0) - XCTAssert(testValue == 90.0, "Retured value for radToDeg is invalid, should be 90.0") - } - - func testDegToRad() { - let testValue = degToRad(90.0) - XCTAssert(testValue == Double.pi / 2.0, "Retured value for degToRag is invalid, should be Double.pi / 2.0") - } -} diff --git a/Tests/PointTests.swift b/Tests/PointTests.swift deleted file mode 100644 index d374b3a4..00000000 --- a/Tests/PointTests.swift +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import C4 -import XCTest - -class PointTests: XCTestCase { - func testDistance() { - let pointA = Point() - let pointB = Point(1, 1) - XCTAssertEqual(distance(pointA, rhs: pointB), sqrt(2), accuracy: Double.leastNormalMagnitude, "Distance between origin and (1,1) should be √2") - } - - func testTranslate() { - let original = Point(2, 3) - let translated = original + Vector(x: 3, y: 2) - XCTAssertEqual(translated, Point(5, 5), "Point should be translated to (5, 5)") - } - - func testLerp() { - let target = Point(10, 10) - let lerped = lerp(Point(), target, at: 0.2) - XCTAssertEqual(lerped, Point(2, 2), "Point should be {2,2}") - } - -} diff --git a/Tests/RectTests.swift b/Tests/RectTests.swift deleted file mode 100644 index a52a02c2..00000000 --- a/Tests/RectTests.swift +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import C4 -import XCTest - -class RectTests: XCTestCase { - func testIntersects() { - let a = Rect(0, 0, 100, 100) - let b = Rect(50, 50, 100, 100) - let c = Rect(100, 100, 100, 100) - XCTAssertTrue(a.intersects(b), "a and b intersect") - XCTAssertFalse(a.intersects(c), "a and c do not intersect") - } - - func testCenter() { - for _ in 1...10 { - let val = Double(random(below: 100)) - let rect = Rect(0, 0, val, val) - XCTAssertEqual(rect.center, Point(val/2.0, val/2.0), "Center point should be half the width and height of the Rect") - } - } - - func testMax() { - for _ in 1...10 { - let x = Double(random(below: 100)) - let y = Double(random(below: 100)) - let rect = Rect(x, y, 100, 100) - XCTAssertEqual(rect.max, Point(x + 100, y + 100), "Max point should equal the origin plus the size of the Rect") - } - } - - func testIsZero() { - XCTAssertTrue(Point().isZero(), "A point created with no arguments should be {0,0}") - } - - func testContainsRect() { - let a = Rect(0, 0, 100, 100) - let b = Rect(50, 50, 50, 50) - let c = Rect(50, 50, 100, 100) - XCTAssertTrue(a.contains(b), "A should contain B") - XCTAssertTrue(c.contains(b), "C should contain B") - XCTAssertFalse(a.contains(c), "A should not contain C") - } - - func testContainsPoint() { - let a = Rect(0, 0, 100, 100) - let b = Rect(25, 25, 50, 50) - let c = Rect(50, 50, 100, 100) - XCTAssertTrue(a.contains(b.center), "A should contain the center of B") - XCTAssertTrue(b.contains(c.origin), "B should contain the origin of C") - XCTAssertFalse(c.contains(b.origin), "C should not contain the center of A") - } - - func testEquals() { - let a = Rect(10, 10, 10, 10) - var b = Rect(0, 0, 10, 10) - b.center = Point(15, 15) - XCTAssertEqual(a, b, "A should be equal to B") - } - - func testIntersection() { - func r() -> Double { - return Double(random(below: 90) + 10) - } - - let a = Rect(0, 0, r(), r()) - let b = Rect(10, 10, r(), r()) - let c = intersection(a, rect2: b) - let x = (b.max.x - a.max.x < 0) ? b.max.x : a.max.x - let y = (b.max.y - a.max.y < 0) ? b.max.y : a.max.y - let d = Rect(b.origin.x, b.origin.y, x - b.origin.x, y - b.origin.y) - XCTAssertEqual(c, d, "C should be equal to D") - } - - func testUnion() { - func r() -> Double { - return Double(random(below: 100)) - } - - let a = Rect(r(), r(), r() + 1, r() + 1) - let b = Rect(r(), r(), r() + 1, r() + 1) - let c = union(a, rect2: b) - let o = Point(min(a.origin.x, b.origin.x), min(a.origin.y, b.origin.y)) - let s = Size(max(a.max.x, b.max.x) - o.x, max(a.max.y, b.max.y) - o.y) - let d = Rect(o, s) - XCTAssertEqual(c, d, "C should be equal to D") - } - - func testIntegral() { - XCTAssertEqual(integral(Rect(0.1, 0.9, 9.9, 9.1)), Rect(0, 0, 10, 10)) - } - - func testStandardize() { - XCTAssertEqual(standardize(Rect(0, 0, -10, -10)), Rect(-10, -10, 10, 10)) - } - - func testInset() { - func r() -> Double { - return Double(random(below: 100)) - } - let a = Rect(r(), r(), r() + 1, r() + 1) - let x = r() - let y = r() - _ = inset(a, dx: x, dy: y) - _ = Rect(a.origin, Size(a.size.width - x, a.size.height - y)) - } -} diff --git a/Tests/TransformTests.swift b/Tests/TransformTests.swift deleted file mode 100644 index 9a42a18d..00000000 --- a/Tests/TransformTests.swift +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import C4 -import XCTest - -class TransformTests: XCTestCase { - func testMultiplyByIndentity() { - let identity = Transform() - var transform = Transform() - for col in 0...3 { - for row in 0...3 { - transform[row, col] = random(in: 0.0..<1.0) - } - } - XCTAssertEqual(identity * transform, transform, "Multiplying by the identity transform should not change the other transform") - XCTAssertEqual(transform * identity, transform, "Multiplying by the identity transform should not change the other transform") - } - - func testMultiplyByInverse() { - let identity = Transform() - var transform = Transform() - transform[0, 0] = 1 - transform[0, 1] = 2 - transform[1, 0] = 2 - transform[1, 1] = 5 - transform[0, 3] = 5 - transform[1, 3] = 6 - - XCTAssertEqual(transform * inverse(transform)!, identity, - "A transform multiplied by its inverse should result in the identity transform") - } - - func testTranslationProperty() { - let translation = Vector(x: 10, y: 20) - let transform = Transform.makeTranslation(translation) - XCTAssertEqual(transform.translation, translation, - "The transform's translation should match the translation used to create it") - } -} diff --git a/Tests/VectorTests.swift b/Tests/VectorTests.swift deleted file mode 100644 index 182a2f1a..00000000 --- a/Tests/VectorTests.swift +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright © 2014 C4 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -import C4 -import XCTest - -class VectorTests: XCTestCase { - func testUnitVector() { - let vector = Vector(x: 8, y: 8, z: 8) - let unitVector = vector.unitVector()! - XCTAssertEqual(unitVector.magnitude, 1, accuracy: 1e-15, "Magnitude of unit vector should be 1") - XCTAssertEqual(unitVector.heading, vector.heading, "Heading of unit vector should be the same as the original") - } - - func testNilUnitVector() { - let vector = Vector(x: 0, y: 0, z: 0) - let unitVector = vector.unitVector() - XCTAssert(unitVector == nil, "Callin unitVector on a zero vector should return nil") - } - - func testDotProduct() { - let vectorA = Vector(x: 1, y: 0) - let vectorB = Vector(x: 0, y: 1) - XCTAssertEqual(vectorA ⋅ vectorB, 0, accuracy: 1e-15, "Dot product of perpendicular vectors should be 0") - } - - func testAngleTo() { - let vectorA = Vector(x: 2, y: 0) - let vectorB = Vector(x: 1, y: 1) - let angle = vectorA.angleTo(vectorB) - XCTAssertEqual(angle, Double(Double.pi)/4.0, accuracy: 1e-15, "Product should be PI/4") - } - - func testAngleToBaseOn() { - let vectorA = Vector(x: 2, y: 0) - let vectorB = Vector(x: 1, y: 1) - let vectorC = Vector(x: 1, y: 0) - let angle = vectorA.angleTo(vectorB, basedOn: vectorC) - XCTAssertEqual(angle, Double(Double.pi)/2.0, accuracy: 1e-15, "Product should be PI/2") - } - - func testDivideScalar() { - for i in 10...1000 { - let v = Double(random(below: i))+1.0 - var vector = Vector(x: v, y: v) - vector /= v - XCTAssertEqual(vector.x, 1.0, "Vector should equal 1") - } - } - - func testMultiplyScalar() { - for i in 10...1000 { - let val = Double(i) - var vector = Vector(x: val, y: val) - vector *= 10 - XCTAssertEqual(vector.x, val*10, "Vector should equal 1") - } - } - - func testAddition() { - for i in 10...1000 { - let val = Double(i) - var vector = Vector(x: val, y: val) - vector += Vector(x: 1, y: 1) - XCTAssertEqual(vector.x, val+1, "Vectore should be original value + 1") - } - } - - func testSubtraction() { - for i in 10...1000 { - let val = Double(i) - var vector = Vector(x: val, y: val) - vector -= Vector(x: 1, y: 1) - XCTAssertEqual(vector.x, val-1, "Vectore should be original value - 1") - } - } -}