├── Tests
└── SwiftyCreativesTests
│ ├── Resources
│ ├── SampleObject.mtl
│ ├── Media.xcassets
│ │ ├── Contents.json
│ │ └── sampleImage.imageset
│ │ │ ├── sampleImage.png
│ │ │ └── Contents.json
│ └── SampleObjectWithTexture.mtl
│ └── SnapshotTests
│ ├── Functions
│ ├── __Snapshots__
│ │ ├── SVGTests
│ │ │ └── testSVGIsDrawed.1.png
│ │ └── SketchForTest
│ │ │ ├── testBoxIsDrawed.1.png
│ │ │ ├── testImgIsDrawed.1.png
│ │ │ ├── testBoxVariations.1.png
│ │ │ ├── testFogIsWorking.1.png
│ │ │ ├── testLineIsDrawed.1.png
│ │ │ ├── testMeshIsDrawed.1.png
│ │ │ ├── testModelIsDrawed.1.png
│ │ │ ├── testRectIsDrawed.1.png
│ │ │ ├── testBoldLineIsDrawed.1.png
│ │ │ ├── testBoxColorWorking.1.png
│ │ │ ├── testCircleIsDrawed.1.png
│ │ │ ├── testColorIsWorking.1.png
│ │ │ ├── testLineColorWorking.1.png
│ │ │ ├── testPushPopWorking.1.png
│ │ │ ├── testRectColorWorking.1.png
│ │ │ ├── testRectPosWorking.1.png
│ │ │ ├── testText2DGradient.1.png
│ │ │ ├── testText2DIsDrawed.1.png
│ │ │ ├── testText3DIsDrawed.1.png
│ │ │ ├── testTriangleIsDrawed.1.png
│ │ │ ├── testBoldLineVariations.1.png
│ │ │ ├── testBoxRotationWorking.1.png
│ │ │ ├── testCircleColorWorking.1.png
│ │ │ ├── testText3DRawIsDrawed.1.png
│ │ │ ├── testTrianglePosWorking.1.png
│ │ │ ├── testBoldLineColorWorking.1.png
│ │ │ ├── testMeshWithVertexColor.1.png
│ │ │ ├── testNestedPushPopWorking.1.png
│ │ │ ├── testTextFactoryIsWorking.1.png
│ │ │ ├── testTexturedMeshIsDrawed.1.png
│ │ │ ├── testTriangleColorWorking.1.png
│ │ │ ├── testBoldLineWithVertexColor.1.png
│ │ │ ├── testCircleParameterWorking.1.png
│ │ │ ├── testHitTestableBoxIsDrawed.1.png
│ │ │ ├── testHitTestableImgIsDrawed.1.png
│ │ │ ├── testModelIsDrawedWithTexture.1.png
│ │ │ └── testModelIsDrawedWithCustomTexture.1.png
│ ├── ColorTests.swift
│ ├── HitTestableBoxTests.swift
│ ├── ImgTests.swift
│ ├── HitTestableImgTests.swift
│ ├── LineTests.swift
│ ├── SketchForTest.swift
│ ├── FogTests.swift
│ ├── PushPopTests.swift
│ ├── RectTests.swift
│ ├── CircleTests.swift
│ ├── TriangleTests.swift
│ ├── BoxTests.swift
│ ├── BoldLineTests.swift
│ └── SVGTests.swift
│ ├── Renderers
│ └── __Snapshots__
│ │ ├── AddRendererTests
│ │ └── testAddRendering.1.png
│ │ ├── NormalRendererTests
│ │ └── testNormalRendering.1.png
│ │ └── TransparentRendererTests
│ │ └── testTransparentRendering.1.png
│ └── SnapshotTestUtil.swift
├── Examples
├── ExampleVisionOS
│ ├── Packages
│ │ └── RealityKitContent
│ │ │ ├── README.md
│ │ │ ├── Sources
│ │ │ └── RealityKitContent
│ │ │ │ ├── RealityKitContent.swift
│ │ │ │ └── RealityKitContent.rkassets
│ │ │ │ ├── Immersive.usda
│ │ │ │ └── Scene.usda
│ │ │ ├── Package.realitycomposerpro
│ │ │ ├── WorkspaceData
│ │ │ │ └── Settings.rcprojectdata
│ │ │ └── ProjectData
│ │ │ │ └── main.json
│ │ │ └── Package.swift
│ ├── ExampleVisionOS
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── AppIcon.solidimagestack
│ │ │ │ ├── Back.solidimagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ ├── Front.solidimagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ ├── Middle.solidimagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ └── ColorMap.textureset
│ │ │ │ ├── Universal.mipmapset
│ │ │ │ ├── ColorMap.png
│ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ ├── SampleSketch.swift
│ │ ├── ExampleVisionOSApp.swift
│ │ └── ContentView.swift
│ └── ExampleVisionOS.xcodeproj
│ │ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
├── ExampleiOSApp
│ ├── ExampleiOSApp
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ ├── ExampleiOSApp.entitlements
│ │ ├── ExampleiOSApp.swift
│ │ ├── SketchSample1.swift
│ │ ├── SketchSample2.swift
│ │ └── TestView.xib
│ └── ExampleiOSApp.xcodeproj
│ │ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
└── ExampleMacOSApp
│ ├── ExampleMacOSApp
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── apple.imageset
│ │ │ ├── Image.png
│ │ │ └── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── sphere.mtl
│ ├── ExampleMacOSApp.entitlements
│ ├── Samples
│ │ ├── Sample3.swift
│ │ ├── Sample2.swift
│ │ ├── Sample6.swift
│ │ ├── Sample1.swift
│ │ ├── Sample8.swift
│ │ ├── Sample4.swift
│ │ ├── Sample9.swift
│ │ ├── Sample7.swift
│ │ ├── Sample10.swift
│ │ └── Sample5.swift
│ ├── ExampleMacOSApp.swift
│ └── FeatureExamples
│ │ ├── Feature1.swift
│ │ └── Feature2.swift
│ └── ExampleMacOSApp.xcodeproj
│ └── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── swiftpm
│ └── Package.resolved
├── .gitignore
├── Sources
├── SwiftyCreativesMacro
│ ├── Utils
│ │ └── String+Error.swift
│ ├── SwiftyCreativesMacroPlugin.swift
│ └── SketchObject
│ │ └── SketchObject.swift
├── SwiftyCreatives
│ ├── Extensions
│ │ ├── Bool+.swift
│ │ ├── Numeric+.swift
│ │ ├── Float+.swift
│ │ ├── MTLCommandBuffer+.swift
│ │ ├── MTLTexture+.swift
│ │ ├── UIView+.swift
│ │ ├── Comparable+.swift
│ │ ├── CGPoint+.swift
│ │ └── CGSize+.swift
│ ├── Primitives
│ │ ├── Texts
│ │ │ ├── Text
│ │ │ │ ├── TextBufferCreationError.swift
│ │ │ │ └── Text2D.swift
│ │ │ ├── LetterCache.swift
│ │ │ └── Factory
│ │ │ │ └── TextFactory.swift
│ │ ├── PrimitiveUtils
│ │ │ ├── Abstract
│ │ │ │ ├── ImageAdjustOption.swift
│ │ │ │ ├── ScaleSettable.swift
│ │ │ │ ├── ImageAdjuster.swift
│ │ │ │ ├── HitTestablePrimitive.swift
│ │ │ │ ├── RectanglePlanePrimitive.swift
│ │ │ │ └── ImageLoadable.swift
│ │ │ ├── Info
│ │ │ │ ├── PrimitiveInfo.swift
│ │ │ │ ├── ModelObjectInfo.swift
│ │ │ │ ├── TriangleInfo.swift
│ │ │ │ └── RectShapeInfo.swift
│ │ │ └── Primitive.swift
│ │ ├── HitTestables
│ │ │ ├── HitTestableRect.swift
│ │ │ ├── HitTestableImg.swift
│ │ │ └── UIViewObject.swift
│ │ └── Basics
│ │ │ └── Img.swift
│ ├── Sketch
│ │ ├── FunctionBase
│ │ │ ├── FunctionBase+Fog.swift
│ │ │ ├── FunctionBase+PushPop.swift
│ │ │ ├── FunctionBase+HitTestableImg.swift
│ │ │ ├── FunctionBase+Translate.swift
│ │ │ ├── FunctionBase+Line.swift
│ │ │ ├── FunctionBase+Color.swift
│ │ │ ├── FunctionBase+Scale.swift
│ │ │ ├── FunctionBase+Box.swift
│ │ │ ├── FunctionBase+ModelObject.swift
│ │ │ ├── FunctionBase+Circle.swift
│ │ │ ├── FunctionBase+SVG.swift
│ │ │ ├── FunctionBase+Img.swift
│ │ │ ├── FunctionBase+Rotate.swift
│ │ │ ├── FunctionBase+HitTestableBox.swift
│ │ │ ├── FunctionBase+Triangle.swift
│ │ │ └── FunctionBase.swift
│ │ ├── SCPacket.swift
│ │ └── SketchFunctions
│ │ │ └── Sketch+UtilFunctions.swift
│ ├── Utils
│ │ ├── EXPORTER.swift
│ │ ├── AssetUtil.swift
│ │ ├── DefaultBuffers.swift
│ │ ├── SharedIndices.swift
│ │ ├── Alias.swift
│ │ └── BlendMode.swift
│ ├── DrawUtils
│ │ └── Config
│ │ │ ├── DrawConfig.swift
│ │ │ └── DefaultDrawConfig.swift
│ ├── Macros
│ │ ├── SketchObjectHasDraw.swift
│ │ └── SketchObject.swift
│ ├── Renderers
│ │ ├── Base
│ │ │ ├── RendererBase+Functions.swift
│ │ │ └── RendererBase.swift
│ │ └── visionOS
│ │ │ └── visionOSSettings.swift
│ ├── GeometryPresets
│ │ └── Box.swift
│ ├── Views
│ │ ├── TouchableMTKView
│ │ │ ├── TouchableMTKView+Deinitializer.swift
│ │ │ ├── TouchableMTKView+Utils.swift
│ │ │ ├── TouchableMTKView+Initializer.swift
│ │ │ └── TouchableMTKView+Configure.swift
│ │ ├── KitSketchView.swift
│ │ └── SketchView.swift
│ ├── Camera
│ │ ├── EasyCameraType.swift
│ │ └── Config
│ │ │ ├── DefaultOrthographicConfig.swift
│ │ │ ├── DefaultPerspectiveConfig.swift
│ │ │ └── CameraConfig.swift
│ ├── ShaderUtils
│ │ └── ShaderCore.swift
│ ├── Resources
│ │ └── Shaders
│ │ │ ├── SharedIndices.h
│ │ │ └── Types.metal
│ ├── PropertyWrappers
│ │ └── SCAnimatable.swift
│ └── PostProcess
│ │ └── Presets
│ │ └── CornerRadiusPostProcessor.swift
└── SwiftyCreativesSound
│ ├── FFT
│ ├── FFTNoiseExtractionMethod.swift
│ ├── FFTBandCalculationMethod.swift
│ ├── TempiFFTWindowType.swift
│ └── FFTResultComponent.swift
│ ├── AudioCapturer.swift
│ ├── FFTVisualizer.swift
│ └── Utils
│ └── NoiseExtractor.swift
├── SwiftyCreatives.xcworkspace
├── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── swiftpm
│ │ └── Package.resolved
└── contents.xcworkspacedata
├── .swiftpm
└── xcode
│ └── xcshareddata
│ └── xcschemes
│ ├── SwiftyCreativesTests.xcscheme
│ └── SwiftyCreatives.xcscheme
└── Package.resolved
/Tests/SwiftyCreativesTests/Resources/SampleObject.mtl:
--------------------------------------------------------------------------------
1 | # Blender 3.4.1 MTL File: 'None'
2 | # www.blender.org
3 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/Packages/RealityKitContent/README.md:
--------------------------------------------------------------------------------
1 | # RealityKitContent
2 |
3 | A description of this package.
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/Resources/Media.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Assets.xcassets/apple.imageset/Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Examples/ExampleMacOSApp/ExampleMacOSApp/Assets.xcassets/apple.imageset/Image.png
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesMacro/Utils/String+Error.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import Foundation
9 |
10 | extension String: Error {}
11 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Bundle for the RealityKitContent project
4 | public let realityKitContentBundle = Bundle.module
5 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/sphere.mtl:
--------------------------------------------------------------------------------
1 | # Blender 3.4.1 MTL File: 'None'
2 | # www.blender.org
3 |
4 | newmtl _texture
5 | Kd 1.00 1.00 1.00
6 | Ka 0.00 0.00 0.00
7 | Tf 1.00 1.00 1.00
8 | Ni 1.00
9 | map_Kd apple
10 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/Resources/Media.xcassets/sampleImage.imageset/sampleImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/Resources/Media.xcassets/sampleImage.imageset/sampleImage.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SVGTests/testSVGIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SVGTests/testSVGIsDrawed.1.png
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testImgIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testImgIsDrawed.1.png
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxVariations.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxVariations.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testFogIsWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testFogIsWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testLineIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testLineIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testMeshIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testMeshIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testModelIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testModelIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testRectIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testRectIsDrawed.1.png
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxColorWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxColorWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testCircleIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testCircleIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testColorIsWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testColorIsWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testLineColorWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testLineColorWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testPushPopWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testPushPopWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testRectColorWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testRectColorWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testRectPosWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testRectPosWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText2DGradient.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText2DGradient.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText2DIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText2DIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText3DIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText3DIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTriangleIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTriangleIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Renderers/__Snapshots__/AddRendererTests/testAddRendering.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Renderers/__Snapshots__/AddRendererTests/testAddRendering.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineVariations.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineVariations.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxRotationWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoxRotationWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testCircleColorWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testCircleColorWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText3DRawIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testText3DRawIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTrianglePosWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTrianglePosWorking.1.png
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/ColorMap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/ColorMap.png
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/Bool+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bool+.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/10.
6 | //
7 |
8 | extension Bool {
9 | static var memorySize: Int {
10 | return MemoryLayout.stride
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineColorWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineColorWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testMeshWithVertexColor.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testMeshWithVertexColor.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testNestedPushPopWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testNestedPushPopWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTextFactoryIsWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTextFactoryIsWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTexturedMeshIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTexturedMeshIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTriangleColorWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testTriangleColorWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Renderers/__Snapshots__/NormalRendererTests/testNormalRendering.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Renderers/__Snapshots__/NormalRendererTests/testNormalRendering.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineWithVertexColor.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testBoldLineWithVertexColor.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testCircleParameterWorking.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testCircleParameterWorking.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testHitTestableBoxIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testHitTestableBoxIsDrawed.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testHitTestableImgIsDrawed.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testHitTestableImgIsDrawed.1.png
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/Numeric+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Numeric+.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/05/16.
6 | //
7 |
8 | extension Numeric {
9 | static var memorySize: Int {
10 | return MemoryLayout.stride
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testModelIsDrawedWithTexture.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testModelIsDrawedWithTexture.1.png
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/Texts/Text/TextBufferCreationError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import Foundation
9 |
10 | enum TextBufferCreationError: LocalizedError {
11 | case noVertices
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testModelIsDrawedWithCustomTexture.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Functions/__Snapshots__/SketchForTest/testModelIsDrawedWithCustomTexture.1.png
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Renderers/__Snapshots__/TransparentRendererTests/testTransparentRendering.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukiny0811/swifty-creatives/HEAD/Tests/SwiftyCreativesTests/SnapshotTests/Renderers/__Snapshots__/TransparentRendererTests/testTransparentRendering.1.png
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesSound/FFT/FFTNoiseExtractionMethod.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/07/04.
6 | //
7 |
8 | public enum FFTNoiseExtractionMethod {
9 | case none
10 | case freqDomain(Double)
11 | case timeDomain(Double)
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesSound/FFT/FFTBandCalculationMethod.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/07/05.
6 | //
7 |
8 | public enum FFTBandCalculationMethod {
9 | case linear(Int) //bandCount
10 | case logarithmic(Int) //bandCountPerOctave
11 | }
12 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "levels" : [
7 | {
8 | "filename" : "ColorMap.png",
9 | "mipmap-level" : "base"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Abstract/ImageAdjustOption.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageAdjustOption.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/03.
6 | //
7 |
8 | public enum ImageAdjustOption {
9 | case basedOnWidth
10 | case basedOnHeight
11 | case basedOnLonger
12 | }
13 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/ExampleiOSApp.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftyCreatives.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "vision",
5 | "scale" : "2x"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "vision",
5 | "scale" : "2x"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "vision",
5 | "scale" : "2x"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Abstract/ScaleSettable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScaleSettable.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/03.
6 | //
7 |
8 | public protocol ScaleSettable: AnyObject {
9 | var scale: f3 { get }
10 | @discardableResult func setScale(_ value: f3) -> Self
11 | }
12 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/HitTestables/HitTestableRect.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HitTestableRect.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/25.
6 | //
7 |
8 | import Metal
9 |
10 | open class HitTestableRect: RectanglePlanePrimitive {
11 | public override init() {
12 | super.init()
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesSound/FFT/TempiFFTWindowType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/07/04.
6 | //
7 | // Original code from https://github.com/jscalo/tempi-fft (CC0 1.0 Universal)
8 |
9 | public enum TempiFFTWindowType: Int {
10 | case none
11 | case hanning
12 | case hamming
13 | }
14 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/Resources/SampleObjectWithTexture.mtl:
--------------------------------------------------------------------------------
1 | # Blender 3.4.1 MTL File: 'None'
2 | # www.blender.org
3 |
4 | newmtl Material
5 | Ns 250.000000
6 | Ka 1.000000 1.000000 1.000000
7 | Kd 0.800000 0.087901 0.294495
8 | Ks 0.500000 0.500000 0.500000
9 | Ke 0.000000 0.000000 0.000000
10 | Ni 1.450000
11 | d 1.000000
12 | illum 2
13 | map_Kd sampleImage
14 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Fog.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Fog.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/08.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 | func setFog(color: f4, density: Float) {
13 | setFogDensity(density)
14 | setFogColor(color)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/Float+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Float+.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/01/31.
6 | //
7 |
8 | extension Float {
9 | public static func degreesToRadians(_ deg: Float) -> Self {
10 | return Self(deg / 360 * Float.pi * 2)
11 | }
12 | public func degreesToRadians() -> Self {
13 | return Self(self / 360 * Float.pi * 2)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/AppIcon.solidimagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "layers" : [
7 | {
8 | "filename" : "Front.solidimagestacklayer"
9 | },
10 | {
11 | "filename" : "Middle.solidimagestacklayer"
12 | },
13 | {
14 | "filename" : "Back.solidimagestacklayer"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Assets.xcassets/ColorMap.textureset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "properties" : {
7 | "origin" : "bottom-left",
8 | "interpretation" : "non-premultiplied-colors"
9 | },
10 | "textures" : [
11 | {
12 | "idiom" : "universal",
13 | "filename" : "Universal.mipmapset"
14 | }
15 | ]
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata:
--------------------------------------------------------------------------------
1 | {
2 | "cameraPresets" : {
3 |
4 | },
5 | "secondaryToolbarData" : {
6 | "isGridVisible" : true,
7 | "sceneReverbPreset" : -1
8 | },
9 | "unitDefaults" : {
10 | "°" : "°",
11 | "kg" : "g",
12 | "m" : "cm",
13 | "m\/s" : "m\/s",
14 | "m\/s²" : "m\/s²",
15 | "s" : "s"
16 | }
17 | }
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/MTLCommandBuffer+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/06.
6 | //
7 |
8 | import Metal
9 |
10 | public extension MTLCommandBuffer {
11 | func compute(_ f: @escaping (MTLComputeCommandEncoder) -> ()) {
12 | let encoder = self.makeComputeCommandEncoder()!
13 | f(encoder)
14 | encoder.endEncoding()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Info/PrimitiveInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrimitiveInfo.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/17.
6 | //
7 |
8 | import Metal
9 |
10 | public protocol PrimitiveInfo {
11 | static var primitiveType: MTLPrimitiveType { get }
12 | static var vertices: [f3] { get }
13 | static var uvs: [f2] { get }
14 | static var normals: [f3] { get }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesMacro/SwiftyCreativesMacroPlugin.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import SwiftCompilerPlugin
9 | import SwiftSyntax
10 | import SwiftSyntaxBuilder
11 | import SwiftSyntaxMacros
12 |
13 | @main
14 | struct SwiftyCreativesMacroPlugin: CompilerPlugin {
15 | let providingMacros: [Macro.Type] = [
16 | SketchObject.self
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Utils/EXPORTER.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | @_exported import SimpleSimdSwift
9 | @_exported import MetalKit
10 | @_exported import SwiftyCreativesSound
11 | @_exported import EasyMetalShader
12 | @_exported import FontVertexBuilder
13 | @_exported import SVGVertexBuilder
14 | @_exported import VectorTriangulator
15 | @_exported import SwiftyCoreText
16 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesSound/FFT/FFTResultComponent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/07/04.
6 | //
7 |
8 | import Foundation
9 |
10 | public struct FFTResultComponent {
11 |
12 | public var frequency: Float
13 | public var magnitude: Float
14 |
15 | public init(frequency: Float, magnitude: Float) {
16 | self.frequency = frequency
17 | self.magnitude = magnitude
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/SCPacket.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SCPacket.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/28.
6 | //
7 |
8 | public class SCPacket: FunctionBase {
9 | public var privateEncoder: SCEncoder?
10 | public var customMatrix: [f4x4] = []
11 | public init(privateEncoder: SCEncoder, customMatrix: f4x4) {
12 | self.privateEncoder = privateEncoder
13 | self.customMatrix.append(customMatrix)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/Basics/Img.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Img.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/19.
6 | //
7 |
8 | import MetalKit
9 |
10 | open class Img: Primitive, ImageLoadable {
11 | public override init() { super.init() }
12 | public var texture: MTLTexture?
13 |
14 | @available(*, unavailable, message: "Use img() in Sketch instead.")
15 | override public func draw(_ encoder: SCEncoder) {}
16 | }
17 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Assets.xcassets/apple.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Image.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/Resources/Media.xcassets/sampleImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "sampleImage.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/DrawUtils/Config/DrawConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | /// Config for draw config.
9 | public class DrawConfig {
10 |
11 | public var contentScaleFactor: Int
12 | public var frameRate: Int
13 |
14 | init(contentScaleFactor: Int, frameRate: Int) {
15 | self.contentScaleFactor = contentScaleFactor
16 | self.frameRate = frameRate
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Macros/SketchObjectHasDraw.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import Foundation
9 |
10 | public protocol SketchObjectHasDraw: FunctionBase {
11 | func draw()
12 | }
13 |
14 | public extension SketchObjectHasDraw {
15 | func draw(encoder: SCEncoder, customMatrix: f4x4) {
16 | self.privateEncoder = encoder
17 | self.customMatrix = [customMatrix]
18 | draw()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Renderers/Base/RendererBase+Functions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RendererBase+Functions.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/26.
6 | //
7 |
8 | import Foundation
9 |
10 | #if !os(visionOS)
11 |
12 | extension RendererBase {
13 | func calculateDeltaTime() {
14 | let date = Date()
15 | let elapsed: Float = Float(date.timeIntervalSince(savedDate))
16 | drawProcess.deltaTime = elapsed
17 | savedDate = date
18 | }
19 | }
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/ExampleMacOSApp.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.device.audio-input
8 |
9 | com.apple.security.device.camera
10 |
11 | com.apple.security.files.user-selected.read-only
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/ExampleiOSApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExampleiOSApp.swift
3 | // ExampleiOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/15.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftyCreatives
10 |
11 | @main
12 | struct ExampleiOSApp: App {
13 | var body: some Scene {
14 | WindowGroup {
15 | VStack {
16 | SketchView(SketchSample1())
17 | SketchView(SketchSample2())
18 | }
19 | .background(.black)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SwiftyCreatives.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/DrawUtils/Config/DefaultDrawConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainDrawConfig.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/09.
6 | //
7 |
8 | #if !os(visionOS)
9 |
10 | // Default draw config.
11 | public class DefaultDrawConfig: DrawConfig {
12 | public override init(
13 | contentScaleFactor: Int = 3,
14 | frameRate: Int = 120
15 | ) {
16 | super.init(
17 | contentScaleFactor: contentScaleFactor,
18 | frameRate: frameRate
19 | )
20 | }
21 | }
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationPreferredDefaultSceneSessionRole
8 | UIWindowSceneSessionRoleApplication
9 | UIApplicationSupportsMultipleScenes
10 |
11 | UISceneConfigurations
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Info/ModelObjectInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ModelObjectInfo.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/25.
6 | //
7 |
8 | import Metal
9 |
10 | public struct ModelObjectInfo: PrimitiveInfo {
11 | public static var vertices: [f3] = []
12 | public static var uvs: [f2] = []
13 | public static var normals: [f3] = []
14 | public static let vertexCount: Int = 0
15 | public static let primitiveType: MTLPrimitiveType = .triangleStrip
16 | public static let hasTexture: [Bool] = [true]
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/GeometryPresets/Box.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import Foundation
9 |
10 | @SketchObject
11 | public class Box {
12 |
13 | public var pos: f3
14 | public var col: f4
15 | public var scale: f3
16 |
17 | public init(pos: f3, col: f4, scale: f3) {
18 | self.pos = pos
19 | self.col = col
20 | self.scale = scale
21 | }
22 |
23 | public func draw() {
24 | color(col)
25 | box(pos, scale)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/Texts/LetterCache.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | import Foundation
9 | import Metal
10 | import SimpleSimdSwift
11 |
12 | public struct LetterCache {
13 | public var buffer: MTLBuffer
14 | public var verticeCount: Int
15 | public var offset: simd_double2
16 | public var size: simd_double2
17 | }
18 |
19 | public struct LetterCacheRaw {
20 | public var vertices: [simd_double3]
21 | public var offset: simd_double2
22 | public var size: simd_double2
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Views/TouchableMTKView/TouchableMTKView+Deinitializer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TouchableMTKView+Deinitializer.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/05/16.
6 | //
7 |
8 | import MetalKit
9 |
10 | #if !os(visionOS)
11 |
12 | extension TouchableMTKView {
13 | func deinitView() {
14 | #if os(iOS)
15 | if let recognizers = self.gestureRecognizers {
16 | for recognizer in recognizers {
17 | self.removeGestureRecognizer(recognizer)
18 | }
19 | }
20 | #endif
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesSound/AudioCapturer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/07/06.
6 | //
7 |
8 | import Foundation
9 |
10 | public protocol AudioCapturer {
11 | var fftResult: [FFTResultComponent] { get set }
12 | var fftNoiseExtractionMethod: FFTNoiseExtractionMethod { get set }
13 | var fftWindowType: TempiFFTWindowType { get set }
14 | var fftMinFreq: Float { get set }
15 | var fftMaxFreq: Float { get set }
16 | var bandCalculationMethod: FFTBandCalculationMethod { get set }
17 | func start()
18 | func stop()
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Utils/AssetUtil.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssetUtil.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/19.
6 | //
7 |
8 | import MetalKit
9 |
10 | class AssetUtil {
11 | private static let defaultMTLTextureDescriptor: MTLTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
12 | pixelFormat: MTLPixelFormat.bgra8Unorm,
13 | width: 1,
14 | height: 1,
15 | mipmapped: false
16 | )
17 | static let defaultMTLTexture: MTLTexture = ShaderCore.device.makeTexture(
18 | descriptor: defaultMTLTextureDescriptor
19 | )!
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Camera/EasyCameraType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyCameraType.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/12.
6 | //
7 |
8 | /// Easy Camera T
9 | public enum EasyCameraType {
10 |
11 | /// Manual 3D Camera
12 | case manual
13 |
14 | /// Standard 3D Camera
15 | /// - Parameters:
16 | /// - polarSpacing: defines how much camera can move along y axis in the screen. set this number small to allow the camera to move more freely. Should be 0.0...1.0
17 | case easy(polarSpacing: Float)
18 |
19 | /// Flexible 3D Camera
20 | case flexible
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Macros/SketchObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import Foundation
9 |
10 | @attached(
11 | extension,
12 | conformances: FunctionBase, SketchObjectHasDraw,
13 | names:
14 | named(privateEncoder),
15 | named(customMatrix)
16 | )
17 | @attached(
18 | member,
19 | conformances: FunctionBase, SketchObjectHasDraw,
20 | names:
21 | named(privateEncoder),
22 | named(customMatrix)
23 | )
24 | public macro SketchObject() = #externalMacro(module: "SwiftyCreativesMacro", type: "SketchObject")
25 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+PushPop.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+PushPop.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/08.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 | func pushMatrix() {
13 | self.customMatrix.append(f4x4.createIdentity())
14 | setCustomMatrix()
15 | }
16 | func popMatrix() {
17 | let _ = self.customMatrix.popLast()
18 | setCustomMatrix()
19 | }
20 | func push(_ process: () -> Void) {
21 | pushMatrix()
22 | process()
23 | popMatrix()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Views/TouchableMTKView/TouchableMTKView+Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TouchableMTKView+Utils.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/05/16.
6 | //
7 |
8 | import MetalKit
9 |
10 | #if !os(visionOS)
11 |
12 | extension TouchableMTKView {
13 | func checkIfExceedsPolarSpacing(rad: Float, polarSpacing: Float) -> Bool {
14 | let mockedMainMatrix = renderer.camera.mock_rotateAroundVisibleX(rad)
15 | let detectionValue = mockedMainMatrix.columns.1.y
16 | if detectionValue < polarSpacing {
17 | return true
18 | }
19 | return false
20 | }
21 | }
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/MTLTexture+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTLTexture+.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/26.
6 | //
7 |
8 | import Metal
9 | import CoreGraphics
10 | import CoreImage
11 |
12 | public extension MTLTexture {
13 | var cgImage: CGImage? {
14 | guard let ciimage = CIImage(mtlTexture: self) else {
15 | return nil
16 | }
17 | let flipped = ciimage.transformed(by: CGAffineTransform(scaleX: 1, y: -1))
18 | let cgimage = ShaderCore.context.createCGImage(
19 | flipped,
20 | from: flipped.extent
21 | )
22 | return cgimage
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample3.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample3.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/02.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | final class Sample3: Sketch {
12 |
13 | var c = f4(1, 0.5, 1, 1)
14 |
15 | override func update(camera: MainCamera) {
16 | camera.rotateAroundY(0.01)
17 | }
18 |
19 | override func draw(encoder: SCEncoder, camera: MainCamera) {
20 | color(c)
21 | box(f3.one * 5)
22 | }
23 | }
24 |
25 | struct Sample3View: View {
26 | var body: some View {
27 | SketchView(Sample3())
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/UIView+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/27.
6 | //
7 |
8 | #if os(iOS)
9 |
10 | import UIKit
11 |
12 | public extension UIView {
13 | class func fromNib(type: T.Type) -> T {
14 | return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
15 | }
16 | func convertToImage() -> UIImage {
17 | let imageRenderer = UIGraphicsImageRenderer.init(size: bounds.size)
18 | return imageRenderer.image { context in
19 | layer.render(in: context.cgContext)
20 | }
21 | }
22 | }
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample2.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/02.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | final class Sample2: Sketch {
12 |
13 | var c: f4 = .init(1, 0.5, 1, 1)
14 |
15 | override func update(camera: MainCamera) {
16 | camera.rotateAroundY(0.01)
17 | }
18 |
19 | override func draw(encoder: SCEncoder, camera: MainCamera) {
20 | color(c)
21 | rect(f3.one * 5)
22 | }
23 | }
24 |
25 | struct Sample2View: View {
26 | var body: some View {
27 | SketchView(Sample2())
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Abstract/ImageAdjuster.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageAdjuster.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/03.
6 | //
7 |
8 | public struct ImageAdjuster {
9 | public static func adjustedScale(width: Float, height: Float, with option: ImageAdjustOption) -> f3 {
10 | switch option {
11 | case .basedOnWidth:
12 | return f3(1, height / width, 1)
13 | case .basedOnHeight:
14 | return f3(width / height, 1, 1)
15 | case .basedOnLonger:
16 | let longer = max(width, height)
17 | return f3(width / longer, height / longer, 1)
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/Comparable+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Comparable+.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/28.
6 | //
7 |
8 | extension Comparable {
9 | func clamp(_ minValue: Self, _ maxValue: Self) -> Self {
10 | return min(max(minValue, self), maxValue)
11 | }
12 | func clamp(_ range: ClosedRange) -> Self {
13 | return self.clamp(range.lowerBound, range.upperBound)
14 | }
15 | static func clamp(value: Self, _ minValue: Self, _ maxValue: Self) -> Self {
16 | return min(max(minValue, value), maxValue)
17 | }
18 | static func clamp(value: Self, _ range: ClosedRange) -> Self {
19 | return value.clamp(range.lowerBound, range.upperBound)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Camera/Config/DefaultOrthographicConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultOrthoConfig.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/01/30.
6 | //
7 |
8 | /// Default camera config for orthographic projection.
9 | public class DefaultOrthographicConfig: CameraConfig {
10 | public override init(
11 | fov: Float = 85,
12 | near: Float = -1000,
13 | far: Float = 1000,
14 | easyCameraType: EasyCameraType = .manual,
15 | isPerspective: Bool = false
16 | ) {
17 | super.init(
18 | fov: fov,
19 | near: near,
20 | far: far,
21 | easyCameraType: easyCameraType,
22 | isPerspective: isPerspective
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Abstract/HitTestablePrimitive.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HitTestablePrimitive.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/01/31.
6 | //
7 |
8 | import simd
9 |
10 | open class HitTestablePrimitive: Primitive {
11 | override init() { super.init() }
12 | public var cachedCustomMatrix: f4x4 = f4x4.createIdentity()
13 |
14 | public func drawWithCache(encoder: SCEncoder, customMatrix: f4x4) {
15 | draw(encoder)
16 | cachedCustomMatrix = customMatrix
17 | }
18 | public func drawWithCache(packet: SCPacket) {
19 | draw(packet.privateEncoder!)
20 | cachedCustomMatrix = packet.customMatrix.reduce(f4x4.createIdentity(), *)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Views/KitSketchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KitSketchView.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/17.
6 | //
7 |
8 | import MetalKit
9 |
10 | #if !os(visionOS)
11 |
12 | #if os(macOS)
13 | import AppKit
14 | #elseif os(iOS)
15 | import UIKit
16 | #endif
17 |
18 | public class KitSketchView: TouchableMTKView {
19 | var thisRenderer: RendererBase
20 | public init(_ sketch: Sketch, blendMode: RendererBase.BlendMode, cameraConfig: CameraConfig, drawConfig: DrawConfig) {
21 | thisRenderer = blendMode.getRenderer(
22 | sketch: sketch,
23 | cameraConfig: cameraConfig,
24 | drawConfig: drawConfig
25 | )
26 | super.init(renderer: thisRenderer)
27 | }
28 | }
29 |
30 | #endif
31 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Camera/Config/DefaultPerspectiveConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Default camera config for perspective projection.
11 | public class DefaultPerspectiveConfig: CameraConfig {
12 | public override init(
13 | fov: Float = 85,
14 | near: Float = 0.01,
15 | far: Float = 1000,
16 | easyCameraType: EasyCameraType = .easy(polarSpacing: 0.03),
17 | isPerspective: Bool = true
18 | ) {
19 | super.init(
20 | fov: fov,
21 | near: near,
22 | far: far,
23 | easyCameraType: easyCameraType,
24 | isPerspective: isPerspective
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "swift-snapshot-testing",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/pointfreeco/swift-snapshot-testing",
7 | "state" : {
8 | "revision" : "e7b77228b34057041374ebef00c0fd7739d71a2b",
9 | "version" : "1.15.3"
10 | }
11 | },
12 | {
13 | "identity" : "swift-syntax",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/apple/swift-syntax.git",
16 | "state" : {
17 | "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
18 | "version" : "509.1.1"
19 | }
20 | }
21 | ],
22 | "version" : 2
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Extensions/CGPoint+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint+.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/10.
6 | //
7 |
8 | import CoreGraphics
9 |
10 | extension CGPoint {
11 | public static func * (lhs: CGPoint, rhs: CGFloat) -> CGPoint {
12 | return CGPoint(x: lhs.x * rhs, y: lhs.y * rhs)
13 | }
14 | public static func * (lhs: CGPoint, rhs: Float) -> CGPoint {
15 | return CGPoint(x: lhs.x * CGFloat(rhs), y: lhs.y * CGFloat(rhs))
16 | }
17 | public static func / (lhs: CGPoint, rhs: CGFloat) -> CGPoint {
18 | return CGPoint(x: lhs.x / rhs, y: lhs.y / rhs)
19 | }
20 | public static func / (lhs: CGPoint, rhs: Float) -> CGPoint {
21 | return CGPoint(x: lhs.x / CGFloat(rhs), y: lhs.y / CGFloat(rhs))
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/SnapshotTestUtil.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SnapshotTestUtil.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/27.
6 | //
7 |
8 | import XCTest
9 | import MetalKit
10 | @testable import SwiftyCreatives
11 |
12 | #if os(macOS)
13 |
14 | enum SnapshotTestUtil {
15 |
16 | static let isRecording = false
17 |
18 | static func testGPU() throws {
19 | try XCTSkipIf(MTLCreateSystemDefaultDevice() == nil)
20 | }
21 |
22 | static func render(sketch: SketchForTest) {
23 | let swiftuiView = SketchView(sketch, blendMode: .normalBlend)
24 | let mtkView = MTKView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), device: ShaderCore.device)
25 | swiftuiView.renderer.draw(in: mtkView)
26 | }
27 | }
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Utils/DefaultBuffers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultBuffers.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/26.
6 | //
7 |
8 | import Metal
9 |
10 | enum DefaultBuffers {
11 | static let default_false = ShaderCore.device.makeBuffer(bytes: [false], length: Bool.memorySize)
12 | static let default_true = ShaderCore.device.makeBuffer(bytes: [true], length: Bool.memorySize)
13 | static let default_f2 = ShaderCore.device.makeBuffer(bytes: [f2.zero], length: f2.memorySize)
14 | static let default_f3 = ShaderCore.device.makeBuffer(bytes: [f3.zero], length: f3.memorySize)
15 | static let default_f4 = ShaderCore.device.makeBuffer(bytes: [f4.zero], length: f4.memorySize)
16 | static let default_f4x4 = ShaderCore.device.makeBuffer(bytes: [f4x4.createIdentity()], length: f4x4.memorySize)
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/HitTestables/HitTestableImg.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HitTestableImg.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/25.
6 | //
7 |
8 | import MetalKit
9 |
10 | open class HitTestableImg: RectanglePlanePrimitive, ImageLoadable {
11 | public override init() { super.init() }
12 | public var texture: MTLTexture?
13 |
14 | @available(*, unavailable, message: "Use img() in Sketch instead.")
15 | override public func draw(_ encoder: SCEncoder) {}
16 |
17 | @available(*, unavailable, message: "Use img() in Sketch instead.")
18 | public override func drawWithCache(packet: SCPacket) {}
19 |
20 | @available(*, unavailable, message: "Use img() in Sketch instead.")
21 | public override func drawWithCache(encoder: SCEncoder, customMatrix: f4x4) {}
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+HitTestableImg.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+HitTestableImg.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/27.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 | func img(_ hitTestableImg: HitTestableImg) {
13 | setUniforms(modelPos: .zero, modelScale: hitTestableImg.scale, hasTexture: true)
14 | setVertices(RectShapeInfo.vertices)
15 | setUVs(RectShapeInfo.uvs)
16 | setNormals(RectShapeInfo.normals)
17 | setTexture(hitTestableImg.texture)
18 | privateEncoder?.drawPrimitives(type: RectShapeInfo.primitiveType, vertexStart: 0, vertexCount: RectShapeInfo.vertices.count)
19 | hitTestableImg.cachedCustomMatrix = customMatrix.reduce(f4x4.createIdentity(), *)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Translate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Translate.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/07.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 |
13 | func translate(_ x: Float, _ y: Float, _ z: Float) {
14 | let translateMatrix = f4x4.createTransform(x, y, z)
15 | self.customMatrix[self.customMatrix.count - 1] = self.customMatrix[self.customMatrix.count - 1] * translateMatrix
16 | setCustomMatrix()
17 | }
18 |
19 | func translate(_ value: f3) {
20 | let translateMatrix = f4x4.createTransform(value.x, value.y, value.z)
21 | self.customMatrix[self.customMatrix.count - 1] = self.customMatrix[self.customMatrix.count - 1] * translateMatrix
22 | setCustomMatrix()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample6.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample6.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/12.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | final class Sample6: Sketch {
12 | override init() {
13 | super.init()
14 | }
15 | let count = 10
16 | override func draw(encoder: SCEncoder, camera: MainCamera) {
17 | setFog(color: f4.one, density: 0.02)
18 | color(0.8, 0.1, 0.1, 0.8)
19 | for z in -count...count {
20 | for y in -count...count {
21 | for x in -count...count {
22 | box(Float(x) * 5, Float(y) * 5, Float(z) * 5, 1, 1, 1)
23 | }
24 | }
25 | }
26 | }
27 | }
28 |
29 | struct Sample6View: View {
30 | var body: some View {
31 | SketchView(Sample6())
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/ColorTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/05.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class ColorTests: XCTestCase {
16 |
17 | @MainActor
18 | func testColorIsWorking() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | color(0.3, 0.6, 1.0, 1.0)
23 | box(7)
24 | }
25 | }
26 | let expectation = XCTestExpectation()
27 | let sketch = TestSketch(expectation, testName: "testColorIsWorking")
28 | SnapshotTestUtil.render(sketch: sketch)
29 | await fulfillment(of: [expectation], timeout: 5.0)
30 | }
31 | }
32 | #endif
33 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Line.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Line.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/07.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 |
13 | func line(_ x1: Float, _ y1: Float, _ z1: Float, _ x2: Float, _ y2: Float, _ z2: Float) {
14 | setUniforms(modelPos: .zero, modelScale: .one, hasTexture: false)
15 | setVertices([f3(x1, y1, z1), f3(x2, y2, z2)])
16 | setUVs([f2.zero, f2.zero])
17 | setNormals([f3.zero, f3.zero])
18 | privateEncoder?.drawPrimitives(type: .line, vertexStart: 0, vertexCount: 2)
19 | }
20 |
21 | func line(_ pos1: f3, _ pos2: f3) {
22 | setUniforms(modelPos: .zero, modelScale: .one, hasTexture: false)
23 | setVertices([pos1, pos2])
24 | setUVs([f2.zero, f2.zero])
25 | setNormals([f3.zero, f3.zero])
26 | privateEncoder?.drawPrimitives(type: .line, vertexStart: 0, vertexCount: 2)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Color.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Color.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/07.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 |
13 | func color(_ r: Float, _ g: Float, _ b: Float, _ a: Float) {
14 | setColor(.init(r, g, b, a))
15 | }
16 |
17 | func color(_ r: Float, _ g: Float, _ b: Float) {
18 | setColor(.init(r, g, b, 1))
19 | }
20 |
21 | func color(_ color: f4) {
22 | setColor(color)
23 | }
24 |
25 | func color(_ rgb: f3, alpha: Float) {
26 | setColor(f4(rgb.x, rgb.y, rgb.z, alpha))
27 | }
28 |
29 | func color(_ rgb: f3) {
30 | setColor(f4(rgb.x, rgb.y, rgb.z, 1))
31 | }
32 |
33 | func color(_ gray: Float) {
34 | setColor(f4(gray, gray, gray, 1))
35 | }
36 |
37 | func color(_ gray: Float, _ alpha: Float) {
38 | setColor(f4(gray, gray, gray, alpha))
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Info/TriangleInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TriangleInfo.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/25.
6 | //
7 |
8 | import Metal
9 |
10 | public struct TriangleInfo: PrimitiveInfo {
11 | public final class VertexPoint {
12 | static let A: f3 = f3(x: 0, y: 1.0, z: 0.0)
13 | static let B: f3 = f3(x: cos(Float.pi * 7.0 / 6.0), y: sin(Float.pi * 7.0 / 6.0), z: 0.0)
14 | static let C: f3 = f3(x: cos(Float.pi * 11.0 / 6.0), y: sin(Float.pi * 11.0 / 6.0), z: 0.0)
15 | }
16 | public static let primitiveType: MTLPrimitiveType = .triangle
17 |
18 | public static var vertices: [f3] = [
19 | Self.VertexPoint.A,
20 | Self.VertexPoint.B,
21 | Self.VertexPoint.C
22 | ]
23 |
24 | public static var uvs: [f2] = [
25 | f2(0, 0),
26 | f2(0, 0),
27 | f2(0, 0)
28 | ]
29 |
30 | public static var normals: [f3] = [
31 | f3(0, 0, 1),
32 | f3(0, 0, 1),
33 | f3(0, 0, 1)
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Camera/Config/CameraConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Config for camera
11 | public class CameraConfig {
12 |
13 | /// field of view in radians
14 | public var fov: Float
15 |
16 | /// near clipping distance
17 | public var near: Float
18 |
19 | /// far clipping distance
20 | public var far: Float
21 |
22 | /// easy camera type
23 | public var easyCameraType: EasyCameraType
24 |
25 | /// perspective or orthographic camera.
26 | /// orthographic if false
27 | public var isPerspective: Bool
28 |
29 | public init(
30 | fov: Float,
31 | near: Float,
32 | far: Float,
33 | easyCameraType: EasyCameraType,
34 | isPerspective: Bool
35 | ) {
36 | self.fov = fov
37 | self.near = near
38 | self.far = far
39 | self.easyCameraType = easyCameraType
40 | self.isPerspective = isPerspective
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample1.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample1.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/02.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | final class Sample1: Sketch {
12 |
13 | let bloomPostProcessor = BloomPostProcessor()
14 |
15 | override func draw(encoder: SCEncoder, camera: MainCamera) {
16 | let count = 20
17 | for i in 0.. Self {
12 | return Self.init(width: lhs.width + rhs.width, height: lhs.height + rhs.height)
13 | }
14 | public static func - (lhs: Self, rhs: Self) -> Self {
15 | return Self.init(width: lhs.width - rhs.width, height: lhs.height - rhs.height)
16 | }
17 | public static func * (lhs: Self, rhs: CGFloat) -> Self {
18 | return Self.init(width: lhs.width * rhs, height: lhs.height * rhs)
19 | }
20 | public static func * (lhs: Self, rhs: Float) -> Self {
21 | return Self.init(width: lhs.width * CGFloat(rhs), height: lhs.height * CGFloat(rhs))
22 | }
23 | public static func / (lhs: Self, rhs: CGFloat) -> Self {
24 | return Self.init(width: lhs.width / rhs, height: lhs.height / rhs)
25 | }
26 | public static func / (lhs: Self, rhs: Float) -> Self {
27 | return Self.init(width: lhs.width / CGFloat(rhs), height: lhs.height / CGFloat(rhs))
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/HitTestableImgTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/05.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class HitTestableImgTests: XCTestCase {
16 |
17 | @MainActor
18 | func testHitTestableImgIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | let testImage = HitTestableImg().load(name: "sampleImage", bundle: .module).adjustScale(with: .basedOnWidth).multiplyScale(3)
22 | override func draw(encoder: SCEncoder) {
23 | color(1, 0, 1, 1)
24 | rotateY(0.5)
25 | rotateZ(0.5)
26 | img(testImage)
27 | }
28 | }
29 | let expectation = XCTestExpectation()
30 | let sketch = TestSketch(expectation, testName: "testHitTestableImgIsDrawed")
31 | SnapshotTestUtil.render(sketch: sketch)
32 | await fulfillment(of: [expectation], timeout: 5.0)
33 | }
34 | }
35 | #endif
36 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+ModelObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/05.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 | import Metal
11 | import CoreImage
12 |
13 | public extension FunctionBase {
14 |
15 | func model(_ modelObject: ModelObject) {
16 | setUniforms(modelPos: .zero, modelScale: .one, hasTexture: false)
17 | modelObject.draw(privateEncoder!)
18 | }
19 |
20 | func model(_ modelObject: ModelObject, primitiveType: MTLPrimitiveType) {
21 | setUniforms(modelPos: .zero, modelScale: .one, hasTexture: false)
22 | modelObject.draw(privateEncoder!, primitiveType: primitiveType)
23 | }
24 |
25 | func model(_ modelObject: ModelObject, with image: CGImage) {
26 | setUniforms(modelPos: .zero, modelScale: .one, hasTexture: false)
27 | modelObject.draw(privateEncoder!, with: image)
28 | }
29 |
30 | func model(_ modelObject: ModelObject, with texture: MTLTexture) {
31 | setUniforms(modelPos: .zero, modelScale: .one, hasTexture: false)
32 | modelObject.draw(privateEncoder!, with: texture)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Utils/Alias.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Alias.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/10.
6 | //
7 |
8 | // MARK: - SwiftUI
9 | import SwiftUI
10 |
11 | #if os(macOS)
12 | typealias ViewRepresentable = NSViewRepresentable
13 | #elseif os(iOS) || os(visionOS) || os(tvOS)
14 | typealias ViewRepresentable = UIViewRepresentable
15 | #endif
16 |
17 | // MARK: - simd
18 | import simd
19 |
20 | public typealias f2 = simd_float2
21 | public typealias f3 = simd_float3
22 | public typealias f4 = simd_float4
23 | public typealias f4x4 = simd_float4x4
24 |
25 | // MARK: - View
26 | #if os(macOS)
27 | public typealias NSSketchView = KitSketchView
28 | #elseif os(iOS) || os(tvOS)
29 | public typealias UISketchView = KitSketchView
30 | #endif
31 |
32 | // MARK: - Metal
33 | import Metal
34 | public typealias SCEncoder = MTLRenderCommandEncoder
35 | public typealias SCCommandBuffer = MTLCommandBuffer
36 |
37 | // MARK: - Font
38 | #if os(macOS)
39 | public typealias FontAlias = NSFont
40 | #elseif os(iOS) || os(visionOS) || os(tvOS)
41 | public typealias FontAlias = UIFont
42 | #endif
43 |
44 | // MARK: - Color
45 | #if os(macOS)
46 | public typealias ColorAlias = NSColor
47 | #elseif os(iOS) || os(visionOS) || os(tvOS)
48 | public typealias ColorAlias = UIColor
49 | #endif
50 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Circle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Circle.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/07.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 |
13 | func circle(_ x: Float, _ y: Float, _ z: Float, _ radX: Float, _ radY: Float) {
14 | setUniforms(modelPos: f3(x, y, z), modelScale: f3(radX, radY, 1), hasTexture: false)
15 | setVertices(CircleInfo.vertices)
16 | setUVs(CircleInfo.uvs)
17 | setNormals(CircleInfo.normals)
18 | setVertexColors(CircleInfo.vertices.map { _ in f4.zero })
19 | privateEncoder?.drawIndexedPrimitives(type: CircleInfo.primitiveType, indexCount: 28 * 3, indexType: .uint16, indexBuffer:CircleInfo.indexBuffer, indexBufferOffset: 0)
20 | }
21 |
22 | func circle(_ pos: f3, _ radX: Float, _ radY: Float) {
23 | circle(pos.x, pos.y, pos.z, radX, radY)
24 | }
25 |
26 | func circle(_ pos: f3, _ rad: Float) {
27 | circle(pos.x, pos.y, pos.z, rad, rad)
28 | }
29 |
30 | func circle(_ rad: Float) {
31 | circle(0, 0, 0, rad, rad)
32 | }
33 |
34 | func circle(_ radX: Float, _ radY: Float) {
35 | circle(0, 0, 0, radX, radY)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample8.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample8.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/19.
6 | //
7 |
8 | import SwiftyCreatives
9 | import CoreGraphics
10 | import AppKit
11 | import SwiftUI
12 |
13 | final class Sample8: Sketch {
14 |
15 | let box = HitTestableBox().setScale(f3(2, 3, 4))
16 | var boxColor: f4 = .one
17 | var testBoxPos: f3 = .zero
18 |
19 | override func setupCamera(camera: MainCamera) {
20 | camera.setTranslate(0, 0, -20)
21 | }
22 |
23 | override func draw(encoder: SCEncoder, camera: MainCamera) {
24 | color(1, 0, 0, 1)
25 | box(testBoxPos, f3.one * 0.1)
26 | translate(0, 3, 0)
27 | color(boxColor)
28 | box(box)
29 | }
30 |
31 | override func mouseMoved(camera: MainCamera, location: f2) {
32 | let ray = camera.screenToWorldDirection(x: location.x, y: location.y)
33 | if let hitPos = box.hitTest(origin: ray.origin, direction: ray.direction) {
34 | boxColor = f4(0, 1, 0, 1)
35 | testBoxPos = hitPos
36 | } else {
37 | boxColor = .one
38 | }
39 | }
40 | }
41 |
42 | struct Sample8View: View {
43 | var body: some View {
44 | SketchView(Sample8())
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Resources/Shaders/SharedIndices.h:
--------------------------------------------------------------------------------
1 | //
2 | // SharedIndices.h
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/19.
6 | //
7 |
8 | #ifndef SharedIndices_h
9 | #define SharedIndices_h
10 |
11 | typedef enum {
12 | VertexAttribute_Position = 0,
13 | VertexAttribute_UV = 1,
14 | VertexAttribute_Normal = 2,
15 | VertexAttribute_Color = 3,
16 | } VertexAttributeIndex;
17 |
18 | typedef enum {
19 | VertexBuffer_Position = 0,
20 | VertexBuffer_ModelPos = 1,
21 | VertexBuffer_ModelRot = 2,
22 | VertexBuffer_ModelScale = 3,
23 | VertexBuffer_ProjectionMatrix = 4,
24 | VertexBuffer_ViewMatrix = 5,
25 | VertexBuffer_CameraPos = 6,
26 | VertexBuffer_Color = 10,
27 | VertexBuffer_UV = 11,
28 | VertexBuffer_Normal = 12,
29 | VertexBuffer_CustomMatrix = 15,
30 | VertexBuffer_UseVertexColor = 18,
31 | VertexBuffer_VertexColor = 19,
32 | } VertexBufferIndex;
33 |
34 | typedef enum {
35 | FragmentBuffer_Material = 1,
36 | FragmentBuffer_LightCount = 2,
37 | FragmentBuffer_Lights = 3,
38 | FragmentBuffer_HasTexture = 6,
39 | FragmentBuffer_IsActiveToLight = 7,
40 | FragmentBuffer_FogDensity = 16,
41 | FragmentBuffer_FogColor = 17,
42 | } FragmentBufferIndex;
43 |
44 | typedef enum {
45 | FragmentTexture_MainTexture = 0
46 | } FragmentTextureIndex;
47 |
48 | #endif /* SharedIndices_h */
49 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample4.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample4.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/02.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | @SketchObject
12 | class MyBox {
13 | var pos: f3
14 | var col: f4
15 | var scale: f3
16 | init(pos: f3, col: f4, scale: f3) {
17 | self.pos = pos
18 | self.col = col
19 | self.scale = scale
20 | }
21 | func draw() {
22 | color(col)
23 | box(pos, scale)
24 | }
25 | }
26 |
27 | final class Sample4: Sketch {
28 | var objects: [MyBox] = []
29 | override init() {
30 | super.init()
31 | for _ in 0..<100 {
32 | let box = MyBox(
33 | pos: f3.randomPoint(-7...7),
34 | col: f4.randomPoint(0...1),
35 | scale: f3.randomPoint(1...2)
36 | )
37 | objects.append(box)
38 | }
39 | }
40 | override func update(camera: MainCamera) {
41 | camera.rotateAroundY(0.01)
42 | }
43 | override func draw(encoder: SCEncoder, camera: MainCamera) {
44 | for o in objects {
45 | o.draw(encoder: encoder, customMatrix: getCustomMatrix())
46 | }
47 | }
48 | }
49 |
50 | struct Sample4View: View {
51 | var body: some View {
52 | SketchView(Sample4())
53 | }
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+SVG.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/06.
6 | //
7 |
8 | import SimpleSimdSwift
9 | import SVGVertexBuilder
10 | import Metal
11 |
12 | public extension FunctionBase {
13 |
14 | func svg(_ object: SVGObj, colors: [f4], primitiveType: MTLPrimitiveType = .triangle) {
15 | for (i, path) in object.triangulated.enumerated() {
16 | let index = min(i, colors.count-1)
17 | color(colors[index])
18 | if path.count * f3.memorySize < 4096 {
19 | mesh(path, primitiveType: primitiveType)
20 | } else {
21 | if let buffer = ShaderCore.device.makeBuffer(bytes: path, length: path.count * f3.memorySize) {
22 | mesh(buffer)
23 | }
24 | }
25 | }
26 | }
27 |
28 | func svg(_ object: SVGObj, primitiveType: MTLPrimitiveType = .triangle) {
29 | for (i, path) in object.triangulated.enumerated() {
30 | color(object.colors[i])
31 | if path.count * f3.memorySize < 4096 {
32 | mesh(path, primitiveType: primitiveType)
33 | } else {
34 | if let buffer = ShaderCore.device.makeBuffer(bytes: path, length: path.count * f3.memorySize) {
35 | mesh(buffer)
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Img.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Img.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/07.
6 | //
7 |
8 | import Metal
9 | import simd
10 | import SimpleSimdSwift
11 |
12 | public extension FunctionBase {
13 | func img(texture: MTLTexture, with option: ImageAdjustOption) {
14 | let adjustedScale = ImageAdjuster.adjustedScale(width: Float(texture.width), height: Float(texture.height), with: option)
15 | setUniforms(modelPos: .zero, modelScale: adjustedScale, hasTexture: true)
16 | setVertices(RectShapeInfo.vertices)
17 | setUVs(RectShapeInfo.uvs)
18 | setNormals(RectShapeInfo.normals)
19 | setVertexColors(RectShapeInfo.vertices.map { _ in f4.zero })
20 | setTexture(texture)
21 | privateEncoder?.drawPrimitives(type: RectShapeInfo.primitiveType, vertexStart: 0, vertexCount: RectShapeInfo.vertices.count)
22 | }
23 |
24 | func img(_ imgObj: Img) {
25 | setUniforms(modelPos: .zero, modelScale: imgObj.scale, hasTexture: true)
26 | setVertices(RectShapeInfo.vertices)
27 | setUVs(RectShapeInfo.uvs)
28 | setNormals(RectShapeInfo.normals)
29 | setVertexColors(RectShapeInfo.vertices.map { _ in f4.zero })
30 | setTexture(imgObj.texture)
31 | privateEncoder?.drawPrimitives(type: RectShapeInfo.primitiveType, vertexStart: 0, vertexCount: RectShapeInfo.vertices.count)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample9.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample9.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 | import Observation
11 |
12 | @Observable
13 | final class Sample9: Sketch {
14 |
15 | var fov: Float = 85
16 |
17 | override func update(camera: MainCamera) {
18 | camera.rotateAroundY(0.01)
19 | camera.setFov(to: fov)
20 | }
21 |
22 | override func draw(encoder: SCEncoder, camera: MainCamera) {
23 | color(1, 0.5, 1, 0.3)
24 | let count = 10
25 | let spacing: Float = 10
26 | let size: Float = 1
27 | for z in -count...count {
28 | for y in -count...count {
29 | for x in -count...count {
30 | box(Float(x) * spacing, Float(y) * spacing, Float(z) * spacing, size, size, size)
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | struct Sample9View: View {
38 | @State var sketch = Sample9()
39 | var body: some View {
40 | SketchView(sketch)
41 | .toolbar {
42 | ToolbarItem {
43 | HStack {
44 | Text("fov")
45 | Slider(value: $sketch.fov, in: 10...180)
46 | .frame(width: 400)
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Views/SketchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SketchView.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/28.
6 | //
7 |
8 | import MetalKit
9 | import SwiftUI
10 |
11 | #if !os(visionOS)
12 |
13 | #if os(macOS)
14 | import AppKit
15 | #elseif os(iOS)
16 | import UIKit
17 | #endif
18 |
19 | public struct SketchView: ViewRepresentable {
20 | let renderer: RendererBase
21 | let drawProcess: Sketch
22 | public init(
23 | _ sketch: Sketch,
24 | blendMode: RendererBase.BlendMode = .alphaBlend,
25 | cameraConfig: CameraConfig = DefaultPerspectiveConfig(),
26 | drawConfig: DrawConfig = DefaultDrawConfig()
27 | ) {
28 | self.drawProcess = sketch
29 | self.renderer = blendMode.getRenderer(
30 | sketch: self.drawProcess,
31 | cameraConfig: cameraConfig,
32 | drawConfig: drawConfig
33 | )
34 | }
35 |
36 | #if os(macOS)
37 | public func makeNSView(context: Context) -> MTKView {
38 | let mtkView = TouchableMTKView(renderer: renderer)
39 | return mtkView
40 | }
41 | public func updateNSView(_ nsView: MTKView, context: Context) {}
42 | #elseif os(iOS) || os(tvOS)
43 | public func makeUIView(context: Context) -> MTKView {
44 | let mtkView = TouchableMTKView(renderer: renderer)
45 | return mtkView
46 | }
47 | public func updateUIView(_ uiView: MTKView, context: Context) {}
48 | #endif
49 | }
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/PropertyWrappers/SCAnimatable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SCAnimatable.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/27.
6 | //
7 |
8 | @propertyWrapper
9 | public class SCAnimatable {
10 | public enum SCAnimationType {
11 | case linear
12 | case easeOut
13 | }
14 | private var value: Float
15 | private var targetValue: Float
16 | public var projectedValue: SCAnimatable { self }
17 | public init (wrappedValue: Float) {
18 | value = 0
19 | targetValue = wrappedValue
20 | }
21 | public var wrappedValue: Float {
22 | get { targetValue }
23 | set { targetValue = newValue }
24 | }
25 | private(set) public var animationValue: Float {
26 | get { value }
27 | set {
28 | value = newValue
29 | targetValue = newValue
30 | }
31 | }
32 | public func directSet(_ value: Float) {
33 | animationValue = value
34 | }
35 | public func update(multiplier: Float, with type: SCAnimationType = .easeOut) {
36 | switch type {
37 | case .easeOut:
38 | let clampedMultiplier = multiplier.clamp(0...1)
39 | let diff = targetValue - value
40 | let changeValue = diff * clampedMultiplier
41 | value += changeValue
42 | case .linear:
43 | let diff = targetValue - value
44 | let clamped = diff.clamp(-multiplier...multiplier)
45 | value += clamped
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample7.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample7.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/19.
6 | //
7 |
8 | import SwiftyCreatives
9 | import CoreGraphics
10 | import AppKit
11 | import SwiftUI
12 |
13 | class MyHitTestableRect: HitTestableRect {
14 | var color: f4 = .zero
15 | }
16 |
17 | final class Sample7: Sketch {
18 | override func setupCamera(camera: MainCamera) {
19 | camera.setTranslate(0, 0, -10)
20 | }
21 | var rect: [MyHitTestableRect] = [
22 | MyHitTestableRect(),
23 | MyHitTestableRect(),
24 | MyHitTestableRect()
25 | ]
26 | override func draw(encoder: SCEncoder, camera: MainCamera) {
27 | for r in rect {
28 | translate(0, 3, 0)
29 | rotateY(0.3)
30 | color(r.color)
31 | r.drawWithCache(encoder: encoder, customMatrix: getCustomMatrix())
32 | }
33 | }
34 |
35 | override func mouseMoved(camera: MainCamera, location: f2) {
36 |
37 | let ray = camera.screenToWorldDirection(x: location.x, y: location.y)
38 |
39 | for r in rect {
40 | if let _ = r.hitTestGetPos(origin: ray.origin, direction: ray.direction) {
41 | r.color = f4(1, 0.5, 1, 1)
42 | } else {
43 | r.color = .one
44 | }
45 | }
46 | }
47 | }
48 |
49 | struct Sample7View: View {
50 | var body: some View {
51 | SketchView(Sample7())
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/LineTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LineTests.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/28.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class LineTests: XCTestCase {
16 |
17 | @MainActor
18 | func testLineIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | color(1)
23 | line(0, 0, 0, 10, 10, 10)
24 | }
25 | }
26 | let expectation = XCTestExpectation()
27 | let sketch = TestSketch(expectation, testName: "testLineIsDrawed")
28 | SnapshotTestUtil.render(sketch: sketch)
29 | await fulfillment(of: [expectation], timeout: 5.0)
30 | }
31 |
32 | @MainActor
33 | func testLineColorWorking() async throws {
34 | try SnapshotTestUtil.testGPU()
35 | class TestSketch: SketchForTest {
36 | override func draw(encoder: SCEncoder) {
37 | color(1, 0.5, 0.2, 0.8)
38 | line(0, 0, 0, 10, 10, 10)
39 | }
40 | }
41 | let expectation = XCTestExpectation()
42 | let sketch = TestSketch(expectation, testName: "testLineColorWorking")
43 | SnapshotTestUtil.render(sketch: sketch)
44 | await fulfillment(of: [expectation], timeout: 5.0)
45 | }
46 | }
47 | #endif
48 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesSound/FFTVisualizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/07/04.
6 | //
7 |
8 | import Foundation
9 |
10 | public class FFTVisualizer {
11 |
12 | public var baseUpOffset: Float = 20
13 | public var historyCount: Int = 5
14 | public var averageMags: [Float] = []
15 | public var freqArray: [Float] = []
16 |
17 | var magsHistory: [[Float]] = []
18 |
19 | public init() {}
20 |
21 | public func updateData(_ capturer: AudioCapturer) {
22 |
23 | let mags = capturer.fftResult.map {
24 | var db = TempiFFT.toDB($0.magnitude)
25 | if db.isNaN {
26 | db = 0
27 | }
28 | return db + baseUpOffset
29 | }
30 | freqArray = capturer.fftResult.map { $0.frequency }
31 |
32 | if averageMags.count != mags.count {
33 | averageMags = mags
34 | magsHistory = []
35 | }
36 |
37 | magsHistory.append(mags)
38 | while magsHistory.count > historyCount {
39 | magsHistory.removeFirst()
40 | }
41 |
42 | for i in 0.. f2 {
14 | var location = loc
15 | if isPerspective {
16 | location.y = event.window!.contentRect(
17 | forFrameRect: event.window!.frame
18 | ).height - location.y
19 | location.x -= viewFrame.minX
20 | location.y -= viewFrame.minY
21 | return f2(Float(location.x), Float(location.y))
22 | } else {
23 | let contentRect = event.window!.contentRect(forFrameRect: event.window!.frame)
24 | var adjustedLocation = f2(
25 | Float(location.x),
26 | Float(contentRect.height - location.y)
27 | )
28 | adjustedLocation.x -= Float(viewFrame.minX)
29 | adjustedLocation.y -= Float(viewFrame.minY)
30 | let normalizedLocation: f2 = f2(
31 | adjustedLocation.x / Float(viewFrame.width),
32 | 1 - (adjustedLocation.y / Float(viewFrame.height))
33 | )
34 | let signedNormalizedLocation = normalizedLocation * 2 - f2.one
35 | return f2(signedNormalizedLocation.x * metalDrawableSize.x / 2, signedNormalizedLocation.y * metalDrawableSize.y / 2)
36 | }
37 | }
38 | }
39 | #endif
40 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/SketchForTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | #if os(macOS)
9 |
10 | @testable import SwiftyCreatives
11 | import XCTest
12 | import SnapshotTesting
13 |
14 | class SketchForTest: Sketch {
15 |
16 | let expectation: XCTestExpectation
17 | let testName: String
18 |
19 | init(_ expectation: XCTestExpectation, testName: String) {
20 | self.expectation = expectation
21 | self.testName = testName
22 | super.init()
23 | }
24 |
25 | override func draw(encoder: SCEncoder) {}
26 |
27 | override func afterCommit(texture: MTLTexture?) {
28 | self.expectation.fulfill()
29 | let desc = MTLTextureDescriptor()
30 | desc.width = texture!.width
31 | desc.height = texture!.height
32 | desc.textureType = .type2D
33 | desc.pixelFormat = .bgra8Unorm
34 | let tex = ShaderCore.device.makeTexture(descriptor: desc)!
35 |
36 | let cb = ShaderCore.commandQueue.makeCommandBuffer()!
37 | let blitEncoder = cb.makeBlitCommandEncoder()!
38 | blitEncoder.copy(from: texture!, to: tex)
39 | blitEncoder.endEncoding()
40 | cb.commit()
41 | cb.waitUntilCompleted()
42 |
43 | let cgimage = tex.cgImage!
44 | let finalimage = NSImage(cgImage: cgimage, size: NSSize(width: 100, height: 100))
45 | assertSnapshot(matching: finalimage, as: .image, record: SnapshotTestUtil.isRecording, testName: testName)
46 | }
47 | }
48 |
49 | #endif
50 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample10.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample10.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | class Sample10: Sketch {
12 |
13 | var swiftTextObj: Text2D = Text2D(
14 | text: "Swifty\nCreatives",
15 | fontName: "Avenir-BlackOblique",
16 | fontSize: 10
17 | )
18 |
19 | override func draw(encoder: SCEncoder, camera: MainCamera) {
20 | color(1)
21 | text(swiftTextObj)
22 | }
23 | }
24 |
25 | struct Sample10View: View {
26 |
27 | let sketch = Sample10()
28 |
29 | var body: some View {
30 | SketchView(sketch)
31 | .toolbar {
32 | ToolbarItem {
33 | Menu("Font") {
34 | ForEach(getAvailableFonts(), id: \.self) { font in
35 | Button(font) {
36 | sketch.swiftTextObj = Text2D(
37 | text: "Swifty\nCreatives",
38 | fontName: font,
39 | fontSize: 10
40 | )
41 | }
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
48 | func getAvailableFontFamilies() -> [String] {
49 | return NSFontManager.shared.availableFontFamilies
50 | }
51 |
52 | func getAvailableFonts() -> [String] {
53 | return NSFontManager.shared.availableFonts
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/FogTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/05.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class FogTests: XCTestCase {
16 |
17 | @MainActor
18 | func testFogIsWorking() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | setFog(color: f4(1, 0, 0, 1), density: 0.03)
23 | color(1)
24 | let count = 5
25 | rotateY(0.5)
26 | rotateZ(0.5)
27 | for x in -count...count {
28 | for y in -count...count {
29 | for z in -count...count {
30 | box(
31 | Float(x) * 10,
32 | Float(y) * 10,
33 | Float(z) * 10,
34 | 1,
35 | 1,
36 | 1
37 | )
38 | }
39 | }
40 | }
41 | }
42 | }
43 | let expectation = XCTestExpectation()
44 | let sketch = TestSketch(expectation, testName: "testFogIsWorking")
45 | SnapshotTestUtil.render(sketch: sketch)
46 | await fulfillment(of: [expectation], timeout: 5.0)
47 | }
48 | }
49 | #endif
50 |
51 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/ExampleVisionOSApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // App.swift
3 | //
4 |
5 | import SwiftUI
6 | import CompositorServices
7 | import SwiftyCreatives
8 |
9 | struct ContentStageConfiguration: CompositorLayerConfiguration {
10 | func makeConfiguration(capabilities: LayerRenderer.Capabilities, configuration: inout LayerRenderer.Configuration) {
11 | configuration.depthFormat = .depth32Float_stencil8
12 | configuration.colorFormat = .bgra8Unorm_srgb
13 |
14 | let foveationEnabled = capabilities.supportsFoveation
15 | configuration.isFoveationEnabled = foveationEnabled
16 |
17 | let options: LayerRenderer.Capabilities.SupportedLayoutsOptions = foveationEnabled ? [.foveationEnabled] : []
18 | let supportedLayouts = capabilities.supportedLayouts(options: options)
19 |
20 | configuration.layout = supportedLayouts.contains(.layered) ? .layered : .dedicated
21 | }
22 | }
23 |
24 | @main
25 | struct TestingApp: App {
26 | init() {
27 | print("wow", ShaderCore.device.supportsFamily(.apple4))
28 | }
29 | var body: some Scene {
30 | WindowGroup {
31 | ContentView()
32 | }
33 |
34 | ImmersiveSpace(id: "ImmersiveSpace") {
35 | CompositorLayer(configuration: ContentStageConfiguration()) { layerRenderer in
36 | let renderer = RendererBase.BlendMode.normalBlend.getRenderer(sketch: SampleSketch(), layerRenderer: layerRenderer)
37 | renderer.startRenderLoop()
38 | }
39 | }.immersionStyle(selection: .constant(.full), in: .full)
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Resources/Shaders/Types.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Types.metal
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/16.
6 | //
7 |
8 | #include
9 | #include "SharedIndices.h"
10 | using namespace metal;
11 |
12 | struct FrameUniforms_ProjectionMatrix {
13 | float4x4 value;
14 | };
15 |
16 | struct FrameUniforms_ViewMatrix {
17 | float4x4 value;
18 | };
19 |
20 | struct FrameUniforms_ModelPos {
21 | float3 value;
22 | };
23 |
24 | struct FrameUniforms_ModelRot {
25 | float3 value;
26 | };
27 |
28 | struct FrameUniforms_ModelScale {
29 | float3 value;
30 | };
31 |
32 | struct FrameUniforms_HasTexture {
33 | bool value;
34 | };
35 |
36 | struct FrameUniforms_CameraPos {
37 | float3 value;
38 | };
39 |
40 | struct FrameUniforms_IsActiveToLight {
41 | bool value;
42 | };
43 |
44 | struct FrameUniforms_CustomMatrix {
45 | float4x4 value;
46 | };
47 |
48 | struct FrameUniforms_FogDensity {
49 | float value;
50 | };
51 |
52 | struct FrameUniforms_FogColor {
53 | float4 value;
54 | };
55 |
56 | struct FrameUniforms_UseVertexColor {
57 | bool value;
58 | };
59 |
60 | struct RasterizerData {
61 | float4 position [[ position ]];
62 | float4 color;
63 | float2 uv;
64 | float3 worldPosition;
65 | float3 surfaceNormal;
66 | float3 toCameraVector;
67 | float size [[ point_size ]];
68 | };
69 |
70 | struct Vertex {
71 | float3 position [[ attribute(VertexAttribute_Position) ]];
72 | float2 uv [[ attribute(VertexAttribute_UV) ]];
73 | float3 normal [[ attribute(VertexAttribute_Normal) ]];
74 | float4 color [[ attribute(VertexAttribute_Color) ]];
75 | };
76 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/SketchSample1.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SketchSample1.swift
3 | // ExampleiOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/27.
6 | //
7 |
8 | import SwiftyCreatives
9 | import UIKit
10 |
11 | struct MyBox {
12 | var pos: f3 = f3.random(in: -7...7)
13 | var color: f4 = f4.randomPoint(0...1)
14 | var scale: f3 = f3.one * Float.random(in: 0.3...0.5)
15 | }
16 |
17 | final class SketchSample1: Sketch {
18 |
19 | var boxes: [MyBox] = []
20 | var elapsed: Float = 0.0
21 |
22 | override init() {
23 | super.init()
24 | for _ in 0...100 {
25 | boxes.append(MyBox())
26 | }
27 | }
28 |
29 | override func update(camera: MainCamera) {
30 | camera.rotateAroundY(deltaTime)
31 | elapsed += deltaTime
32 | }
33 |
34 | override func draw(encoder: SCEncoder, camera: MainCamera) {
35 | let elapsedSin = sin(elapsed)
36 | for b in boxes {
37 | color(elapsedSin, b.color.y, b.color.z, b.color.w)
38 | pushMatrix()
39 | box(b.pos, b.scale)
40 | popMatrix()
41 | }
42 | }
43 |
44 | override func touchesBegan(camera: MainCamera, touchLocations: [f2]) {
45 | let location = touchLocations.first!
46 | let processed = camera.screenToWorldDirection(x: location.x, y: location.y)
47 | let origin = processed.origin
48 | let direction = processed.direction
49 | let finalPos = origin + direction * 20
50 |
51 | var box = MyBox()
52 | box.color = f4.one
53 | box.pos = finalPos
54 | box.scale = f3.one * 0.2
55 | boxes.append(box)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "easymetalshader",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/yukiny0811/EasyMetalShader.git",
7 | "state" : {
8 | "revision" : "d482c6defd91980b78583a4f567f6be9b0533400",
9 | "version" : "3.1.1"
10 | }
11 | },
12 | {
13 | "identity" : "fontvertexbuilder",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/yukiny0811/FontVertexBuilder",
16 | "state" : {
17 | "revision" : "b34c94e079cd95b03e44ad0bec962f7526e26530",
18 | "version" : "1.0.0"
19 | }
20 | },
21 | {
22 | "identity" : "simplesimdswift",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/yukiny0811/SimpleSimdSwift",
25 | "state" : {
26 | "revision" : "4a0b195450e42f8a46b0a77f183b1ff116696697",
27 | "version" : "1.0.1"
28 | }
29 | },
30 | {
31 | "identity" : "swift-syntax",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/apple/swift-syntax.git",
34 | "state" : {
35 | "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
36 | "version" : "509.1.1"
37 | }
38 | },
39 | {
40 | "identity" : "swiftycoretext",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/yukiny0811/SwiftyCoreText",
43 | "state" : {
44 | "revision" : "ed0583f3ecc5c4e4d3ec56c8a9752d6e9a1e9b86",
45 | "version" : "1.0.0"
46 | }
47 | }
48 | ],
49 | "version" : 2
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/PostProcess/Presets/CornerRadiusPostProcessor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | import EasyMetalShader
9 |
10 | @EMComputeShader
11 | public class CornerRadiusPostProcessor {
12 |
13 | public var tex: MTLTexture?
14 | public var radius: Float = 10
15 |
16 | public var impl: String {
17 | "float width = tex.get_width();"
18 | "float height = tex.get_height();"
19 |
20 | "float4 color = tex.read(gid);"
21 |
22 | "if (gid.x < radius) {"
23 | "if (gid.y < radius) {"
24 | "if (distance(float2(gid.x, gid.y), float2(radius, radius)) > radius) {"
25 | "color = float4(0);"
26 | "}"
27 | "} else if (gid.y > height - radius) {"
28 | "if (distance(float2(gid.x, gid.y), float2(radius, height - radius)) > radius) {"
29 | "color = float4(0);"
30 | "}"
31 | "}"
32 | "} else if (gid.x > width - radius) {"
33 | "if (gid.y < radius) {"
34 | "if (distance(float2(gid.x, gid.y), float2(width - radius, radius)) > radius) {"
35 | "color = float4(0);"
36 | "}"
37 | "} else if (gid.y > height - radius) {"
38 | "if (distance(float2(gid.x, gid.y), float2(width - radius, height - radius)) > radius) {"
39 | "color = float4(0);"
40 | "}"
41 | "}"
42 | "}"
43 |
44 | "tex.write(color, gid);"
45 | }
46 |
47 | public var customMetalCode: String {
48 | ""
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Rotate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Rotate.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/07.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 |
13 | func rotate(_ rad: Float, axis: f3) {
14 | let rotateMatrix = f4x4.createRotation(angle: rad, axis: axis)
15 | self.customMatrix[self.customMatrix.count - 1] = self.customMatrix[self.customMatrix.count - 1] * rotateMatrix
16 | setCustomMatrix()
17 | }
18 |
19 | func rotate(_ rad: Float, _ axisX: Float, _ axisY: Float, _ axisZ: Float) {
20 | let rotateMatrix = f4x4.createRotation(angle: rad, axis: f3(axisX, axisY, axisZ))
21 | self.customMatrix[self.customMatrix.count - 1] = self.customMatrix[self.customMatrix.count - 1] * rotateMatrix
22 | setCustomMatrix()
23 | }
24 |
25 | func rotateX(_ rad: Float) {
26 | let rotateMatrix = f4x4.createRotation(angle: rad, axis: f3(1, 0, 0))
27 | self.customMatrix[self.customMatrix.count - 1] = self.customMatrix[self.customMatrix.count - 1] * rotateMatrix
28 | setCustomMatrix()
29 | }
30 |
31 | func rotateY(_ rad: Float) {
32 | let rotateMatrix = f4x4.createRotation(angle: rad, axis: f3(0, 1, 0))
33 | self.customMatrix[self.customMatrix.count - 1] = self.customMatrix[self.customMatrix.count - 1] * rotateMatrix
34 | setCustomMatrix()
35 | }
36 |
37 | func rotateZ(_ rad: Float) {
38 | let rotateMatrix = f4x4.createRotation(angle: rad, axis: f3(0, 0, 1))
39 | self.customMatrix[self.customMatrix.count - 1] = self.customMatrix[self.customMatrix.count - 1] * rotateMatrix
40 | setCustomMatrix()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda:
--------------------------------------------------------------------------------
1 | #usda 1.0
2 | (
3 | defaultPrim = "Root"
4 | metersPerUnit = 1
5 | upAxis = "Y"
6 | )
7 |
8 | def Xform "Root"
9 | {
10 | reorder nameChildren = ["Sphere_Left", "Sphere_Right", "GridMaterial"]
11 | def Sphere "Sphere_Right" (
12 | active = true
13 | prepend apiSchemas = ["MaterialBindingAPI"]
14 | )
15 | {
16 | rel material:binding = (
17 | bindMaterialAs = "weakerThanDescendants"
18 | )
19 | double radius = 0.1
20 | quatf xformOp:orient = (1, 0, 0, 0)
21 | float3 xformOp:scale = (1, 1, 1)
22 | float3 xformOp:translate = (0.5, 1.5, -1.5)
23 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"]
24 | }
25 |
26 | def Sphere "Sphere_Left" (
27 | active = true
28 | prepend apiSchemas = ["MaterialBindingAPI"]
29 | )
30 | {
31 | rel material:binding = (
32 | bindMaterialAs = "weakerThanDescendants"
33 | )
34 | double radius = 0.1
35 | quatf xformOp:orient = (1, 0, 0, 0)
36 | float3 xformOp:scale = (1, 1, 1)
37 | float3 xformOp:translate = (-0.5, 1.5, -1.5)
38 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"]
39 | }
40 |
41 | def "GridMaterial" (
42 | active = true
43 | prepend references = @Materials/GridMaterial.usda@
44 | )
45 | {
46 | float3 xformOp:scale = (1, 1, 1)
47 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"]
48 | }
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/ExampleVisionOS/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // ExampleVisionOS
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/02.
6 | //
7 |
8 | import SwiftUI
9 | import RealityKit
10 | import RealityKitContent
11 |
12 | struct ContentView: View {
13 |
14 | @State private var showImmersiveSpace = false
15 | @State private var immersiveSpaceIsShown = false
16 |
17 | @Environment(\.openImmersiveSpace) var openImmersiveSpace
18 | @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
19 |
20 | var body: some View {
21 | VStack {
22 | Model3D(named: "Scene", bundle: realityKitContentBundle)
23 | .padding(.bottom, 50)
24 |
25 | Text("Hello, world!")
26 |
27 | Toggle("Show Immersive Space", isOn: $showImmersiveSpace)
28 | .toggleStyle(.button)
29 | .padding(.top, 50)
30 | }
31 | .padding()
32 | .onChange(of: showImmersiveSpace) { _, newValue in
33 | Task {
34 | if newValue {
35 | switch await openImmersiveSpace(id: "ImmersiveSpace") {
36 | case .opened:
37 | immersiveSpaceIsShown = true
38 | case .error, .userCancelled:
39 | fallthrough
40 | @unknown default:
41 | immersiveSpaceIsShown = false
42 | showImmersiveSpace = false
43 | }
44 | } else if immersiveSpaceIsShown {
45 | await dismissImmersiveSpace()
46 | immersiveSpaceIsShown = false
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
53 | #Preview(windowStyle: .automatic) {
54 | ContentView()
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Renderers/visionOS/visionOSSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | #if os(visionOS)
9 |
10 | import Foundation
11 | import Spatial
12 | import CompositorServices
13 |
14 | extension LayerRenderer.Clock.Instant.Duration {
15 | var timeInterval: TimeInterval {
16 | let nanoseconds = TimeInterval(components.attoseconds / 1_000_000_000)
17 | return TimeInterval(components.seconds) + (nanoseconds / TimeInterval(NSEC_PER_SEC))
18 | }
19 | }
20 |
21 | func matrix4x4_rotation(radians: Float, axis: SIMD3) -> matrix_float4x4 {
22 | let unitAxis = normalize(axis)
23 | let ct = cosf(radians)
24 | let st = sinf(radians)
25 | let ci = 1 - ct
26 | let x = unitAxis.x, y = unitAxis.y, z = unitAxis.z
27 | return matrix_float4x4.init(columns:(vector_float4( ct + x * x * ci, y * x * ci + z * st, z * x * ci - y * st, 0),
28 | vector_float4(x * y * ci - z * st, ct + y * y * ci, z * y * ci + x * st, 0),
29 | vector_float4(x * z * ci + y * st, y * z * ci - x * st, ct + z * z * ci, 0),
30 | vector_float4( 0, 0, 0, 1)))
31 | }
32 |
33 | func matrix4x4_translation(_ translationX: Float, _ translationY: Float, _ translationZ: Float) -> matrix_float4x4 {
34 | return matrix_float4x4.init(columns:(vector_float4(1, 0, 0, 0),
35 | vector_float4(0, 1, 0, 0),
36 | vector_float4(0, 0, 1, 0),
37 | vector_float4(translationX, translationY, translationZ, 1)))
38 | }
39 |
40 | func radians_from_degrees(_ degrees: Float) -> Float {
41 | return (degrees / 180) * .pi
42 | }
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/Examples/ExampleVisionOS/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda:
--------------------------------------------------------------------------------
1 | #usda 1.0
2 | (
3 | defaultPrim = "Root"
4 | metersPerUnit = 1
5 | upAxis = "Y"
6 | )
7 |
8 | def Xform "Root"
9 | {
10 | reorder nameChildren = ["GridMaterial", "Sphere"]
11 | rel material:binding = None (
12 | bindMaterialAs = "weakerThanDescendants"
13 | )
14 |
15 | def Sphere "Sphere" (
16 | active = true
17 | prepend apiSchemas = ["MaterialBindingAPI"]
18 | )
19 | {
20 | rel material:binding = (
21 | bindMaterialAs = "weakerThanDescendants"
22 | )
23 | double radius = 0.05
24 | quatf xformOp:orient = (1, 0, 0, 0)
25 | float3 xformOp:scale = (1, 1, 1)
26 | float3 xformOp:translate = (0, 0, 0.0004)
27 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"]
28 |
29 | def RealityKitComponent "Collider"
30 | {
31 | uint group = 1
32 | uniform token info:id = "RealityKit.Collider"
33 | uint mask = 4294967295
34 | token type = "Default"
35 |
36 | def RealityKitStruct "Shape"
37 | {
38 | float3 extent = (0.2, 0.2, 0.2)
39 | float radius = 0.05
40 | token shapeType = "Sphere"
41 | }
42 | }
43 |
44 | def RealityKitComponent "InputTarget"
45 | {
46 | uniform token info:id = "RealityKit.InputTarget"
47 | }
48 | }
49 |
50 | def "GridMaterial" (
51 | active = true
52 | prepend references = @Materials/GridMaterial.usda@
53 | )
54 | {
55 | float3 xformOp:scale = (1, 1, 1)
56 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"]
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Primitive.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Primitive.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/17.
6 | //
7 |
8 | import simd
9 |
10 | open class Primitive: ScaleSettable {
11 |
12 | open var hasTexture: [Bool] = [false]
13 | open var isActiveToLight: [Bool] = [false]
14 |
15 | internal var _mScale: [f3] = [f3.one]
16 |
17 | public var scale: f3 { _mScale[0] }
18 |
19 | init() {}
20 |
21 | @discardableResult
22 | public func setScale(_ value: f3) -> Self {
23 | _mScale[0] = value
24 | return self
25 | }
26 |
27 | public func draw(_ encoder: SCEncoder) {
28 | encoder.setVertexBytes(Info.vertices, length: Info.vertices.count * f3.memorySize, index: VertexBufferIndex.Position.rawValue)
29 | encoder.setVertexBytes(_mScale, length: f3.memorySize, index: VertexBufferIndex.ModelScale.rawValue)
30 | encoder.setVertexBytes(Info.uvs, length: Info.uvs.count * f2.memorySize, index: VertexBufferIndex.UV.rawValue)
31 | encoder.setVertexBytes(Info.normals, length: Info.normals.count * f3.memorySize, index: VertexBufferIndex.Normal.rawValue)
32 | encoder.setVertexBytes([f4](repeating: .zero, count: Info.vertices.count), length: Info.vertices.count * f4.memorySize, index: VertexBufferIndex.VertexColor.rawValue)
33 | encoder.setFragmentBytes(self.hasTexture, length: Bool.memorySize, index: FragmentBufferIndex.HasTexture.rawValue)
34 | encoder.setFragmentBytes(self.isActiveToLight, length: Bool.memorySize, index: FragmentBufferIndex.IsActiveToLight.rawValue)
35 | encoder.drawPrimitives(type: Info.primitiveType, vertexStart: 0, vertexCount: Info.vertices.count)
36 | }
37 |
38 | // util functions
39 | @discardableResult
40 | public func multiplyScale(_ value: Float) -> Self {
41 | _mScale[0] *= value
42 | return self
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/PushPopTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PushPopTests.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/28.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class PushPopTests: XCTestCase {
16 |
17 | @MainActor
18 | func testPushPopWorking() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | pushMatrix()
23 | translate(5, 0, 0)
24 | color(1)
25 | box(3)
26 | popMatrix()
27 | color(1, 0, 1)
28 | box(3)
29 | }
30 | }
31 | let expectation = XCTestExpectation()
32 | let sketch = TestSketch(expectation, testName: "testPushPopWorking")
33 | SnapshotTestUtil.render(sketch: sketch)
34 | await fulfillment(of: [expectation], timeout: 5.0)
35 | }
36 |
37 | @MainActor
38 | func testNestedPushPopWorking() async throws {
39 | try SnapshotTestUtil.testGPU()
40 | class TestSketch: SketchForTest {
41 | override func draw(encoder: SCEncoder) {
42 | pushMatrix()
43 | translate(5, 0, 0)
44 | color(1)
45 | box(3)
46 | pushMatrix()
47 | translate(0, 5, 0)
48 | color(1, 1, 0)
49 | box(3)
50 | popMatrix()
51 | popMatrix()
52 | color(1, 0, 1)
53 | box(3)
54 | }
55 | }
56 | let expectation = XCTestExpectation()
57 | let sketch = TestSketch(expectation, testName: "testNestedPushPopWorking")
58 | SnapshotTestUtil.render(sketch: sketch)
59 | await fulfillment(of: [expectation], timeout: 5.0)
60 | }
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "easymetalshader",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/yukiny0811/EasyMetalShader.git",
7 | "state" : {
8 | "revision" : "d482c6defd91980b78583a4f567f6be9b0533400",
9 | "version" : "3.1.1"
10 | }
11 | },
12 | {
13 | "identity" : "fontvertexbuilder",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/yukiny0811/FontVertexBuilder",
16 | "state" : {
17 | "revision" : "b34c94e079cd95b03e44ad0bec962f7526e26530",
18 | "version" : "1.0.0"
19 | }
20 | },
21 | {
22 | "identity" : "simplesimdswift",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/yukiny0811/SimpleSimdSwift",
25 | "state" : {
26 | "revision" : "4a0b195450e42f8a46b0a77f183b1ff116696697",
27 | "version" : "1.0.1"
28 | }
29 | },
30 | {
31 | "identity" : "swift-snapshot-testing",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/pointfreeco/swift-snapshot-testing",
34 | "state" : {
35 | "revision" : "e7b77228b34057041374ebef00c0fd7739d71a2b",
36 | "version" : "1.15.3"
37 | }
38 | },
39 | {
40 | "identity" : "swift-syntax",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/apple/swift-syntax.git",
43 | "state" : {
44 | "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
45 | "version" : "509.1.1"
46 | }
47 | },
48 | {
49 | "identity" : "swiftycoretext",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/yukiny0811/SwiftyCoreText",
52 | "state" : {
53 | "revision" : "ed0583f3ecc5c4e4d3ec56c8a9752d6e9a1e9b86",
54 | "version" : "1.0.0"
55 | }
56 | }
57 | ],
58 | "version" : 2
59 | }
60 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyCreativesTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
37 |
38 |
44 |
45 |
47 |
48 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Views/TouchableMTKView/TouchableMTKView+Configure.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TouchableMTKView+Configure.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/05/16.
6 | //
7 |
8 | import MetalKit
9 |
10 | #if !os(visionOS)
11 |
12 | extension TouchableMTKView {
13 | func configure() {
14 | #if os(macOS)
15 | configureMacOS()
16 | #elseif os(iOS)
17 | configureiOS()
18 | #endif
19 | }
20 | }
21 |
22 | extension TouchableMTKView {
23 | #if os(macOS)
24 | func configureMacOS() {
25 | let options: NSTrackingArea.Options = [
26 | .mouseMoved,
27 | .activeAlways,
28 | .inVisibleRect
29 | ]
30 | let trackingArea = NSTrackingArea(rect: bounds, options: options, owner: self, userInfo: nil)
31 | self.addTrackingArea(trackingArea)
32 | }
33 | #endif
34 |
35 | #if os(iOS)
36 | func configureiOS() {
37 | let scrollGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(onScroll))
38 | scrollGestureRecognizer.allowedScrollTypesMask = .continuous
39 | scrollGestureRecognizer.minimumNumberOfTouches = 2
40 | scrollGestureRecognizer.maximumNumberOfTouches = 2
41 | self.addGestureRecognizer(scrollGestureRecognizer)
42 |
43 | let pinch = UIPinchGestureRecognizer(target: self, action: #selector(onPinch))
44 | pinch.delegate = self
45 | addGestureRecognizer(pinch)
46 |
47 | let rotationRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(onRotate))
48 | rotationRecognizer.delegate = self
49 | addGestureRecognizer(rotationRecognizer)
50 | }
51 | #endif
52 | }
53 |
54 | #if os(iOS)
55 | extension TouchableMTKView: UIGestureRecognizerDelegate {
56 | public func gestureRecognizer(
57 | _ gestureRecognizer: UIGestureRecognizer,
58 | shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
59 | ) -> Bool {
60 | return true
61 | }
62 | }
63 | #endif
64 |
65 | #endif
66 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+HitTestableBox.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+HitTestableBox.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/08.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 | func box(_ hitTestableBox: HitTestableBox) {
13 | setUniforms(modelPos: .zero, modelScale: .one, hasTexture: false)
14 |
15 | push {
16 | push {
17 | translate(0, 0, hitTestableBox.scale.z)
18 | hitTestableBox.front.drawWithCache(encoder: privateEncoder!, customMatrix: self.customMatrix.reduce(f4x4.createIdentity(), *))
19 | }
20 | push {
21 | translate(0, 0, -hitTestableBox.scale.z)
22 | hitTestableBox.back.drawWithCache(encoder: privateEncoder!, customMatrix: self.customMatrix.reduce(f4x4.createIdentity(), *))
23 | }
24 | push {
25 | rotateY(Float.pi/2)
26 | translate(0, 0, hitTestableBox.scale.x)
27 | hitTestableBox.l.drawWithCache(encoder: privateEncoder!, customMatrix: self.customMatrix.reduce(f4x4.createIdentity(), *))
28 | }
29 | push {
30 | rotateY(Float.pi/2)
31 | translate(0, 0, -hitTestableBox.scale.x)
32 | hitTestableBox.r.drawWithCache(encoder: privateEncoder!, customMatrix: self.customMatrix.reduce(f4x4.createIdentity(), *))
33 | }
34 | push {
35 | rotateX(Float.pi/2)
36 | translate(0, 0, hitTestableBox.scale.y)
37 | hitTestableBox.top.drawWithCache(encoder: privateEncoder!, customMatrix: self.customMatrix.reduce(f4x4.createIdentity(), *))
38 | }
39 | push {
40 | rotateX(Float.pi/2)
41 | translate(0, 0, -hitTestableBox.scale.y)
42 | hitTestableBox.bottom.drawWithCache(encoder: privateEncoder!, customMatrix: self.customMatrix.reduce(f4x4.createIdentity(), *))
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/RectTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RectTests.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/27.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class RectTests: XCTestCase {
16 |
17 | @MainActor
18 | func testRectIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | color(1)
23 | rect(3)
24 | }
25 | }
26 | let expectation = XCTestExpectation()
27 | let sketch = TestSketch(expectation, testName: "testRectIsDrawed")
28 | SnapshotTestUtil.render(sketch: sketch)
29 | await fulfillment(of: [expectation], timeout: 5.0)
30 | }
31 |
32 | @MainActor
33 | func testRectPosWorking() async throws {
34 | try SnapshotTestUtil.testGPU()
35 | class TestSketch: SketchForTest {
36 | override func draw(encoder: SCEncoder) {
37 | color(1)
38 | rect(1, 1, 1, 2, 3)
39 | }
40 | }
41 | let expectation = XCTestExpectation()
42 | let sketch = TestSketch(expectation, testName: "testRectPosWorking")
43 | SnapshotTestUtil.render(sketch: sketch)
44 | await fulfillment(of: [expectation], timeout: 5.0)
45 | }
46 |
47 | @MainActor
48 | func testRectColorWorking() async throws {
49 | try SnapshotTestUtil.testGPU()
50 | class TestSketch: SketchForTest {
51 | override func draw(encoder: SCEncoder) {
52 | color(1, 0.5, 0.2, 0.8)
53 | rect(3)
54 | }
55 | }
56 | let expectation = XCTestExpectation()
57 | let sketch = TestSketch(expectation, testName: "testRectColorWorking")
58 | SnapshotTestUtil.render(sketch: sketch)
59 | await fulfillment(of: [expectation], timeout: 5.0)
60 | }
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/CircleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CircleTests.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/27.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class CircleTests: XCTestCase {
16 |
17 | @MainActor
18 | func testCircleIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | color(1)
23 | circle(3)
24 | }
25 | }
26 | let expectation = XCTestExpectation()
27 | let sketch = TestSketch(expectation, testName: "testCircleIsDrawed")
28 | SnapshotTestUtil.render(sketch: sketch)
29 | await fulfillment(of: [expectation], timeout: 5.0)
30 | }
31 |
32 | @MainActor
33 | func testCircleParameterWorking() async throws {
34 | try SnapshotTestUtil.testGPU()
35 | class TestSketch: SketchForTest {
36 | override func draw(encoder: SCEncoder) {
37 | color(1)
38 | circle(1, 1, 1, 2, 3)
39 | }
40 | }
41 | let expectation = XCTestExpectation()
42 | let sketch = TestSketch(expectation, testName: "testCircleParameterWorking")
43 | SnapshotTestUtil.render(sketch: sketch)
44 | await fulfillment(of: [expectation], timeout: 5.0)
45 | }
46 |
47 | @MainActor
48 | func testCircleColorWorking() async throws {
49 | try SnapshotTestUtil.testGPU()
50 | class TestSketch: SketchForTest {
51 | override func draw(encoder: SCEncoder) {
52 | color(1, 0.5, 0.2, 0.8)
53 | circle(3)
54 | }
55 | }
56 | let expectation = XCTestExpectation()
57 | let sketch = TestSketch(expectation, testName: "testCircleColorWorking")
58 | SnapshotTestUtil.render(sketch: sketch)
59 | await fulfillment(of: [expectation], timeout: 5.0)
60 | }
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/SketchSample2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SketchSample2.swift
3 | // ExampleiOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/27.
6 | //
7 |
8 | import SwiftyCreatives
9 | import UIKit
10 |
11 | class RotatingViewObject: UIViewObject {
12 | @SCAnimatable var rotation: Float = 0
13 | }
14 |
15 | final class SketchSample2: Sketch {
16 |
17 | let postProcessor = CornerRadiusPostProcessor()
18 | var viewObj = RotatingViewObject()
19 |
20 | override init() {
21 | super.init()
22 | let view: TestView = TestView.fromNib(type: TestView.self)
23 | view.onHit = {
24 | self.viewObj.rotation += Float.pi / 2
25 | }
26 | viewObj.load(view: view)
27 | viewObj.multiplyScale(6)
28 |
29 | let dispatch = EMMetalDispatch()
30 | dispatch.compute { [weak self] encoder in
31 | guard let self else { return }
32 | postProcessor.tex = viewObj.texture
33 | postProcessor.radius = 50
34 | postProcessor.dispatch(encoder, textureSizeReference: viewObj.texture!)
35 | }
36 | dispatch.commit()
37 | }
38 |
39 | override func update(camera: MainCamera) {
40 | viewObj.$rotation.update(multiplier: deltaTime * 5)
41 | }
42 |
43 | override func draw(encoder: SCEncoder, camera: MainCamera) {
44 | rotateZ(viewObj.$rotation.animationValue)
45 | viewObj.drawWithCache(encoder: encoder, customMatrix: getCustomMatrix())
46 | }
47 |
48 | override func touchesBegan(camera: MainCamera, touchLocations: [f2]) {
49 | let location = touchLocations.first!
50 | let processed = camera.screenToWorldDirection(x: location.x, y: location.y)
51 | let origin = processed.origin
52 | let direction = processed.direction
53 | viewObj.buttonTest(origin: origin, direction: direction)
54 | }
55 | }
56 |
57 | class TestView: UIView {
58 |
59 | var onHit: (() -> Void)?
60 |
61 | @IBAction func onButtonTap() {
62 | if let onHit = onHit {
63 | onHit()
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/TriangleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TriangleTests.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/27.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class TriangleTests: XCTestCase {
16 |
17 | @MainActor
18 | func testTriangleIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | color(1)
23 | triangle(3)
24 | }
25 | }
26 | let expectation = XCTestExpectation()
27 | let sketch = TestSketch(expectation, testName: "testTriangleIsDrawed")
28 | SnapshotTestUtil.render(sketch: sketch)
29 | await fulfillment(of: [expectation], timeout: 5.0)
30 | }
31 |
32 | @MainActor
33 | func testTrianglePosWorking() async throws {
34 | try SnapshotTestUtil.testGPU()
35 | class TestSketch: SketchForTest {
36 | override func draw(encoder: SCEncoder) {
37 | color(1)
38 | triangle(1, 1, 1, 2, 3)
39 | }
40 | }
41 | let expectation = XCTestExpectation()
42 | let sketch = TestSketch(expectation, testName: "testTrianglePosWorking")
43 | SnapshotTestUtil.render(sketch: sketch)
44 | await fulfillment(of: [expectation], timeout: 5.0)
45 | }
46 |
47 | @MainActor
48 | func testTriangleColorWorking() async throws {
49 | try SnapshotTestUtil.testGPU()
50 | class TestSketch: SketchForTest {
51 | override func draw(encoder: SCEncoder) {
52 | color(1, 0.5, 0.2, 0.8)
53 | triangle(3)
54 | }
55 | }
56 | let expectation = XCTestExpectation()
57 | let sketch = TestSketch(expectation, testName: "testTriangleColorWorking")
58 | SnapshotTestUtil.render(sketch: sketch)
59 | await fulfillment(of: [expectation], timeout: 5.0)
60 | }
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/Samples/Sample5.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Sample5.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/10.
6 | //
7 |
8 | import SwiftyCreatives
9 | import CoreGraphics
10 | import AppKit
11 | import SwiftUI
12 |
13 | final class Sample5: Sketch {
14 |
15 | var tree = "F"
16 |
17 | override init() {
18 | super.init()
19 | for _ in 0..<4 {
20 | var currentTree = ""
21 | for t in tree {
22 | if t == "F" {
23 | currentTree += "FyF+[+FyF-yB]-y[-yF+YP]"
24 | } else {
25 | currentTree += String(t)
26 | }
27 | }
28 | tree = currentTree
29 | }
30 | }
31 |
32 | override func setupCamera(camera: MainCamera) {
33 | camera.setTranslate(0, -20, -40)
34 | }
35 |
36 | override func update(camera: MainCamera) {
37 | camera.rotateAroundY(0.01)
38 | }
39 |
40 | override func draw(encoder: SCEncoder, camera: MainCamera) {
41 | for t in tree {
42 | compile(char: t)
43 | }
44 | }
45 |
46 | func compile(char: Character) {
47 | switch char {
48 | case "B":
49 | color(0.5, 0.5, 1.0, 1.0)
50 | boldline(0, 0, 0, 0, 1, 0, width: 0.1)
51 | translate(0, 1, 0)
52 | case "P":
53 | color(1.0, 0.3, 1.0, 1.0)
54 | boldline(0, 0, 0, 0, 1, 0, width: 0.1)
55 | translate(0, 1, 0)
56 | case "F":
57 | color(0.9, 1.0, 1.0, 0.8)
58 | boldline(0, 0, 0, 0, 1, 0, width: 0.1)
59 | translate(0, 1, 0)
60 | case "+":
61 | rotateZ(0.3)
62 | case "-":
63 | rotateZ(-0.3)
64 | case "[":
65 | pushMatrix()
66 | case "]":
67 | popMatrix()
68 | case "y":
69 | rotateY(0.15)
70 | case "Y":
71 | rotateY(-0.15)
72 | default:
73 | break
74 | }
75 | }
76 | }
77 |
78 | struct Sample5View: View {
79 | var body: some View {
80 | SketchView(Sample5())
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesMacro/SketchObject/SketchObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/04.
6 | //
7 |
8 | import SwiftCompilerPlugin
9 | import SwiftSyntax
10 | import SwiftSyntaxBuilder
11 | import SwiftSyntaxMacros
12 |
13 | public struct SketchObject {}
14 |
15 | extension SketchObject: ExtensionMacro {
16 | public static func expansion(
17 | of node: SwiftSyntax.AttributeSyntax,
18 | attachedTo declaration: some SwiftSyntax.DeclGroupSyntax,
19 | providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol,
20 | conformingTo protocols: [SwiftSyntax.TypeSyntax],
21 | in context: some SwiftSyntaxMacros.MacroExpansionContext
22 | ) throws -> [SwiftSyntax.ExtensionDeclSyntax] {
23 |
24 | if protocols.isEmpty { return [] }
25 |
26 | let thisExtension1: DeclSyntax =
27 | """
28 | extension \(type.trimmed): FunctionBase {}
29 | """
30 |
31 | let thisExtension2: DeclSyntax =
32 | """
33 | extension \(type.trimmed): SketchObjectHasDraw {}
34 | """
35 |
36 | guard let extensionDecl1 = thisExtension1.as(ExtensionDeclSyntax.self) else {
37 | return []
38 | }
39 | guard let extensionDecl2 = thisExtension2.as(ExtensionDeclSyntax.self) else {
40 | return []
41 | }
42 |
43 | return [extensionDecl1, extensionDecl2]
44 | }
45 | }
46 |
47 | extension SketchObject: MemberMacro {
48 |
49 | public static func expansion(
50 | of node: AttributeSyntax,
51 | providingMembersOf declaration: some DeclGroupSyntax,
52 | conformingTo protocols: [TypeSyntax],
53 | in context: some MacroExpansionContext
54 | ) throws -> [DeclSyntax] {
55 | if protocols.isEmpty { return [] }
56 |
57 | let thisDecl1: DeclSyntax =
58 | """
59 | public var privateEncoder: SCEncoder?
60 | """
61 |
62 | let thisDecl2: DeclSyntax =
63 | """
64 | public var customMatrix: [f4x4] = []
65 | """
66 |
67 | return [thisDecl1, thisDecl2]
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Sketch/FunctionBase/FunctionBase+Triangle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunctionBase+Triangle.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/07.
6 | //
7 |
8 | import simd
9 | import SimpleSimdSwift
10 |
11 | public extension FunctionBase {
12 |
13 | func triangle(_ x: Float, _ y: Float, _ z: Float, _ scaleX: Float, _ scaleY: Float) {
14 | setUniforms(modelPos: f3(x, y, z), modelScale: f3(scaleX, scaleY, 1), hasTexture: false)
15 | setVertices(TriangleInfo.vertices)
16 | setUVs(TriangleInfo.uvs)
17 | setNormals(TriangleInfo.normals)
18 | privateEncoder?.drawPrimitives(type: TriangleInfo.primitiveType, vertexStart: 0, vertexCount: TriangleInfo.vertices.count)
19 | }
20 |
21 | func triangle(_ pos: f3, _ scaleX: Float, _ scaleY: Float) {
22 | setUniforms(modelPos: pos, modelScale: f3(scaleX, scaleY, 1), hasTexture: false)
23 | setVertices(TriangleInfo.vertices)
24 | setUVs(TriangleInfo.uvs)
25 | setNormals(TriangleInfo.normals)
26 | privateEncoder?.drawPrimitives(type: TriangleInfo.primitiveType, vertexStart: 0, vertexCount: TriangleInfo.vertices.count)
27 | }
28 |
29 | func triangle(_ x: Float, _ y: Float, _ z: Float, _ scale: Float) {
30 | setUniforms(modelPos: f3(x, y, z), modelScale: f3(scale, scale, 1), hasTexture: false)
31 | setVertices(TriangleInfo.vertices)
32 | setUVs(TriangleInfo.uvs)
33 | setNormals(TriangleInfo.normals)
34 | privateEncoder?.drawPrimitives(type: TriangleInfo.primitiveType, vertexStart: 0, vertexCount: TriangleInfo.vertices.count)
35 | }
36 |
37 | func triangle(_ scale: Float) {
38 | setUniforms(modelPos: .zero, modelScale: f3(scale, scale, 1), hasTexture: false)
39 | setVertices(TriangleInfo.vertices)
40 | setUVs(TriangleInfo.uvs)
41 | setNormals(TriangleInfo.normals)
42 | privateEncoder?.drawPrimitives(type: TriangleInfo.primitiveType, vertexStart: 0, vertexCount: TriangleInfo.vertices.count)
43 | }
44 |
45 | func triangle(_ scaleX: Float, _ scaleY: Float) {
46 | setUniforms(modelPos: .zero, modelScale: f3(scaleX, scaleY, 1), hasTexture: false)
47 | setVertices(TriangleInfo.vertices)
48 | setUVs(TriangleInfo.uvs)
49 | setNormals(TriangleInfo.normals)
50 | privateEncoder?.drawPrimitives(type: TriangleInfo.primitiveType, vertexStart: 0, vertexCount: TriangleInfo.vertices.count)
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/Texts/Text/Text2D.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | import Metal
9 | import CoreText
10 | import SimpleSimdSwift
11 | import FontVertexBuilder
12 |
13 | open class Text2D: PathText {
14 | public var posBuffer: MTLBuffer?
15 | public var finalVertices: [simd_double3] = []
16 | public var finalVerticesNormalized: [simd_double3] = []
17 | func createAndSetBuffer(from triangulatedPaths: [TriangulatedLetterPath]) throws {
18 | finalVertices = []
19 | for letter in triangulatedPaths {
20 | for portion in letter.glyphLines {
21 | finalVertices += portion
22 | }
23 | }
24 | if finalVertices.count == 0 {
25 | throw TextBufferCreationError.noVertices
26 | }
27 | var largestX: Double = 0
28 | var largestY: Double = 0
29 | for vert in finalVertices {
30 | if vert.x > largestX {
31 | largestX = vert.x
32 | }
33 | if vert.y < largestY {
34 | largestY = vert.y
35 | }
36 | }
37 | finalVerticesNormalized = finalVertices.map { simd_double3($0.x / largestX, $0.y / largestY, 0)}
38 | posBuffer = ShaderCore.device.makeBuffer(
39 | bytes: finalVertices.map { f3($0) },
40 | length: finalVertices.count * f3.memorySize
41 | )
42 | }
43 | public override init(
44 | text: String,
45 | fontName: String = "Avenir-BlackOblique",
46 | fontSize: Double = 10.0,
47 | bounds: CGSize = .zero,
48 | pivot: simd_double2 = .zero,
49 | textAlignment: CTTextAlignment = .natural,
50 | verticalAlignment: PathText.VerticalAlignment = .center,
51 | kern: Double = 0.0,
52 | lineSpacing: Double = 0.0,
53 | maxDepth: Int = 1
54 | ) {
55 | super.init(
56 | text: text,
57 | fontName: fontName,
58 | fontSize: fontSize,
59 | bounds: bounds,
60 | pivot: pivot,
61 | textAlignment: textAlignment,
62 | verticalAlignment: verticalAlignment,
63 | kern: kern,
64 | lineSpacing: lineSpacing,
65 | maxDepth: maxDepth
66 | )
67 | let triangulatedPaths = GlyphUtil.MainFunctions.triangulate(self.calculatedPaths)
68 | try? createAndSetBuffer(from: triangulatedPaths)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/ExampleMacOSApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExampleMacOSApp.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/15.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftyCreatives
10 |
11 | @main
12 | struct ExampleMacOSApp: App {
13 | var body: some Scene {
14 | WindowGroup {
15 | NavigationSplitView {
16 | List {
17 | Section("Examples") {
18 | NavigationLink("1 Blooming Boxes") {
19 | Sample1View()
20 | }
21 | NavigationLink("2 Rectangle") {
22 | Sample2View()
23 | }
24 | NavigationLink("3 Box") {
25 | Sample3View()
26 | }
27 | NavigationLink("4 Colorful Boxes") {
28 | Sample4View()
29 | }
30 | NavigationLink("5 Tree (L-system)") {
31 | Sample5View()
32 | }
33 | NavigationLink("6 Fog") {
34 | Sample6View()
35 | }
36 | NavigationLink("7 Rect with hit test") {
37 | Sample7View()
38 | }
39 | NavigationLink("8 Box with hit test") {
40 | Sample8View()
41 | }
42 | NavigationLink("9 fov") {
43 | Sample9View()
44 | }
45 | NavigationLink("10 Text2D") {
46 | Sample10View()
47 | }
48 | }
49 | Section("Features") {
50 | NavigationLink("Feature1") {
51 | Feature1.VIEW()
52 | }
53 | NavigationLink("Feature2") {
54 | Feature2.VIEW()
55 | }
56 | NavigationLink("Feature3") {
57 | Feature3.VIEW()
58 | }
59 | }
60 | }
61 | } detail: {
62 | Text("Choose sample sketch from the sidebar")
63 | .foregroundStyle(.white)
64 | }
65 | .background(.black)
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Utils/BlendMode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlendMode.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/09.
6 | //
7 |
8 | #if os(visionOS)
9 |
10 | import CompositorServices
11 |
12 | public extension RendererBase {
13 | enum BlendMode {
14 |
15 | case normalBlend
16 | case add
17 | case alphaBlend
18 |
19 | public func getRenderer(sketch: Sketch, layerRenderer: LayerRenderer) -> RendererBase {
20 | switch self {
21 | case .normalBlend:
22 | return NormalBlendRendererVision(sketch: sketch, layerRenderer: layerRenderer)
23 | case .add:
24 | if ShaderCore.device.supportsFamily(.apple3) {
25 | return AddBlendRendererVision(sketch: sketch, layerRenderer: layerRenderer)
26 | } else {
27 | return NormalBlendRendererVision(sketch: sketch, layerRenderer: layerRenderer)
28 | }
29 | case .alphaBlend:
30 | if ShaderCore.device.supportsFamily(.apple4) {
31 | return TransparentRendererVision(sketch: sketch, layerRenderer: layerRenderer)
32 | } else {
33 | return NormalBlendRendererVision(sketch: sketch, layerRenderer: layerRenderer)
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
40 | #else
41 |
42 | public extension RendererBase {
43 |
44 | @MainActor
45 | enum BlendMode {
46 |
47 | case normalBlend
48 | case add
49 | case alphaBlend
50 |
51 | public func getRenderer(sketch: Sketch, cameraConfig: CameraConfig, drawConfig: DrawConfig) -> RendererBase {
52 | switch self {
53 | case .normalBlend:
54 | return NormalBlendRenderer(sketch: sketch, cameraConfig: cameraConfig, drawConfig: drawConfig)
55 | case .add:
56 | if ShaderCore.device.supportsFamily(.apple3) {
57 | return AddRenderer(sketch: sketch, cameraConfig: cameraConfig, drawConfig: drawConfig)
58 | } else {
59 | return NormalBlendRenderer(sketch: sketch, cameraConfig: cameraConfig, drawConfig: drawConfig)
60 | }
61 | case .alphaBlend:
62 | if ShaderCore.device.supportsFamily(.apple4) {
63 | return TransparentRenderer(sketch: sketch, cameraConfig: cameraConfig, drawConfig: drawConfig)
64 | } else {
65 | return NormalBlendRenderer(sketch: sketch, cameraConfig: cameraConfig, drawConfig: drawConfig)
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/FeatureExamples/Feature1.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Feature1.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2024/08/03.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | final class Feature1: Sketch {
12 |
13 | enum StandardGeometry: Int {
14 | case rect
15 | case box
16 | case circle
17 | case triangle
18 | case line
19 | case boldline
20 | case image
21 | case svg
22 | case object3d
23 | mutating func toggleNext() {
24 | self = StandardGeometry(rawValue: self.rawValue + 1) ?? StandardGeometry(rawValue: 0)!
25 | }
26 | }
27 |
28 | var frameCount = 0
29 | var currentGeometry: StandardGeometry = StandardGeometry(rawValue: 0)!
30 |
31 | let imgObj = Img().load(name: "apple", bundle: nil).adjustScale(with: .basedOnWidth).multiplyScale(1.2)
32 | var svgObj: SVGObj?
33 | let modelObj = ModelObject().loadModel(name: "sphere", extensionName: "obj")
34 |
35 | override init() {
36 | super.init()
37 | DispatchQueue.main.async {
38 | Task {
39 | self.svgObj = await SVGObj(url: Bundle.main.url(forResource: "apple", withExtension: "svg")!)?
40 | .normalizeScale(imageAdjustOption: .basedOnWidth)
41 | .makeAnchorCenter()
42 | .changeColors { currentColors in
43 | return currentColors.map { _ in f4.randomPoint(0...1) }
44 | }
45 | }
46 | }
47 | }
48 |
49 | override func update(camera: MainCamera) {
50 | frameCount += 1
51 | if frameCount.isMultiple(of: 300) {
52 | currentGeometry.toggleNext()
53 | }
54 | }
55 |
56 | override func draw(encoder: SCEncoder, camera: MainCamera) {
57 | color(1, 1, 1, 1)
58 |
59 | switch currentGeometry {
60 | case .rect:
61 | rect(1)
62 | case .box:
63 | box(1)
64 | case .circle:
65 | circle(1)
66 | case .triangle:
67 | triangle(1)
68 | case .line:
69 | line(.zero, .one)
70 | case .boldline:
71 | boldline(.zero, .one, width: 0.1)
72 | case .image:
73 | img(imgObj)
74 | case .svg:
75 | if let svgObj {
76 | svg(svgObj)
77 | }
78 | case .object3d:
79 | model(modelObj)
80 | }
81 | }
82 |
83 | struct VIEW: View {
84 | var body: some View {
85 | VStack {
86 | Text("standard geometries")
87 | SketchView(Feature1())
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreativesSound/Utils/NoiseExtractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/07/04.
6 | //
7 | // original code from Apple, modified by Yuki Kuwashima
8 | //
9 | // Copyright © 2023 Apple Inc.
10 | // 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:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13 | //
14 | // 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.
15 |
16 | import Accelerate
17 |
18 | public enum NoiseExtractor {
19 | public static func extractSignalFromNoise(
20 | sampleCount: Int,
21 | noisySignal: [Float],
22 | threshold: Double,
23 | timeDomainDestination: inout [Float],
24 | frequencyDomainDestination: inout [Float]
25 | ) {
26 | guard let forwardDCTSetup = vDSP.DCT(
27 | count: sampleCount,
28 | transformType: vDSP.DCTTransformType.II
29 | ) else {
30 | return
31 | }
32 | guard let inverseDCTSetup = vDSP.DCT(
33 | count: sampleCount,
34 | transformType: vDSP.DCTTransformType.III
35 | ) else {
36 | return
37 | }
38 |
39 | forwardDCTSetup.transform(
40 | noisySignal,
41 | result: &frequencyDomainDestination
42 | )
43 |
44 | vDSP.threshold(
45 | frequencyDomainDestination,
46 | to: Float(threshold),
47 | with: .zeroFill,
48 | result: &frequencyDomainDestination
49 | )
50 |
51 | inverseDCTSetup.transform(
52 | frequencyDomainDestination,
53 | result: &timeDomainDestination
54 | )
55 |
56 | let divisor = Float(sampleCount / 2)
57 |
58 | vDSP.divide(
59 | timeDomainDestination,
60 | divisor,
61 | result: &timeDomainDestination
62 | )
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/BoxTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BoxTests.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/28.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class BoxTests: XCTestCase {
16 |
17 | @MainActor
18 | func testBoxIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | color(1)
23 | box(3)
24 | }
25 | }
26 | let expectation = XCTestExpectation()
27 | let sketch = TestSketch(expectation, testName: "testBoxIsDrawed")
28 | SnapshotTestUtil.render(sketch: sketch)
29 | await fulfillment(of: [expectation], timeout: 5.0)
30 | }
31 |
32 | @MainActor
33 | func testBoxColorWorking() async throws {
34 | try SnapshotTestUtil.testGPU()
35 | class TestSketch: SketchForTest {
36 | override func draw(encoder: SCEncoder) {
37 | color(1, 0.5, 0.2, 0.8)
38 | box(3)
39 | }
40 | }
41 | let expectation = XCTestExpectation()
42 | let sketch = TestSketch(expectation, testName: "testBoxColorWorking")
43 | SnapshotTestUtil.render(sketch: sketch)
44 | await fulfillment(of: [expectation], timeout: 5.0)
45 | }
46 |
47 | @MainActor
48 | func testBoxRotationWorking() async throws {
49 | try SnapshotTestUtil.testGPU()
50 | class TestSketch: SketchForTest {
51 | override func draw(encoder: SCEncoder) {
52 | color(1, 0.5, 0.2, 0.8)
53 | rotateX(0.5)
54 | rotateY(0.5)
55 | box(3)
56 | }
57 | }
58 | let expectation = XCTestExpectation()
59 | let sketch = TestSketch(expectation, testName: "testBoxRotationWorking")
60 | SnapshotTestUtil.render(sketch: sketch)
61 | await fulfillment(of: [expectation], timeout: 5.0)
62 | }
63 |
64 | @MainActor
65 | func testBoxVariations() async throws {
66 | try SnapshotTestUtil.testGPU()
67 | class TestSketch: SketchForTest {
68 | override func draw(encoder: SCEncoder) {
69 | color(1, 0.5, 0.2, 0.8)
70 | rotateX(0.5)
71 | rotateY(0.5)
72 | box(3, 5, 3, 1, 2, 3)
73 | }
74 | }
75 | let expectation = XCTestExpectation()
76 | let sketch = TestSketch(expectation, testName: "testBoxVariations")
77 | SnapshotTestUtil.render(sketch: sketch)
78 | await fulfillment(of: [expectation], timeout: 5.0)
79 | }
80 | }
81 | #endif
82 |
--------------------------------------------------------------------------------
/Examples/ExampleiOSApp/ExampleiOSApp/TestView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Abstract/RectanglePlanePrimitive.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RectanglePlanePrimitive.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/02/19.
6 | //
7 |
8 | import simd
9 |
10 | open class RectanglePlanePrimitive: HitTestablePrimitive {
11 | override init() { super.init() }
12 | private func calculateHitTest(origin: f3, direction: f3, testDistance: Float) -> (globalPos: f3, localPos: f3)? {
13 |
14 | let model = f4x4.createIdentity()
15 | let customModel = cachedCustomMatrix.transpose
16 |
17 |
18 | var selfPos_f4 = f4(0, 0, 0, 1) * model
19 | selfPos_f4.w = 1
20 | selfPos_f4 = selfPos_f4 * customModel
21 |
22 | let selfPos = f3(selfPos_f4.x, selfPos_f4.y, selfPos_f4.z)
23 |
24 | var a = f4(0, 0, 1000, 1) * model
25 | a.w = 1
26 | a = a * customModel
27 |
28 | let A = origin
29 | let B = origin + direction * testDistance
30 |
31 | let n = simd_normalize(f3(a.x, a.y, a.z) - selfPos)
32 |
33 | let P = selfPos
34 |
35 | let PAdotN = simd_dot(A-P, n)
36 | let PBdotN = simd_dot(B-P, n)
37 |
38 | guard (PAdotN >= 0 && PBdotN <= 0) || (PAdotN <= 0 && PBdotN >= 0) else {
39 | return nil
40 | }
41 |
42 | let ttt = abs(PAdotN) / (abs(PAdotN)+abs(PBdotN))
43 | let x = A + (B-A) * ( ttt )
44 |
45 | let inverseModel = model.inverse
46 | let inverseCustomModel = customModel.inverse
47 | var processedLocalPos = f4(x.x, x.y, x.z, 1) * inverseCustomModel
48 | processedLocalPos.w = 1
49 | processedLocalPos = processedLocalPos * inverseModel
50 |
51 | guard abs(processedLocalPos.x) <= scale.x && abs(processedLocalPos.y) <= scale.y else {
52 | return nil
53 | }
54 |
55 | let processedLocalPos_f3 = f3(processedLocalPos.x, processedLocalPos.y, processedLocalPos.z)
56 |
57 | return (x, processedLocalPos_f3)
58 | }
59 |
60 | public func hitTestGetPos(origin: f3, direction: f3, testDistance: Float = 3000) -> f3? {
61 | if let result = calculateHitTest(origin: origin, direction: direction, testDistance: testDistance) {
62 | return result.globalPos
63 | } else {
64 | return nil
65 | }
66 | }
67 |
68 | public func hitTestGetNormalizedCoord(origin: f3, direction: f3, testDistance: Float = 3000) -> f2? {
69 | if let result = calculateHitTest(origin: origin, direction: direction, testDistance: testDistance) {
70 | let localPos = result.localPos
71 | return f2(localPos.x / scale.x, localPos.y / scale.y)
72 | } else {
73 | return nil
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "easymetalshader",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/yukiny0811/EasyMetalShader.git",
7 | "state" : {
8 | "revision" : "7f496fcd2b000262151b90a863228215bf101eda",
9 | "version" : "3.3.2"
10 | }
11 | },
12 | {
13 | "identity" : "fontvertexbuilder",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/yukiny0811/FontVertexBuilder",
16 | "state" : {
17 | "revision" : "2dc3a96a7abfbb6678bd39a70fc0c0f2a80a7d35",
18 | "version" : "2.1.1"
19 | }
20 | },
21 | {
22 | "identity" : "simplesimdswift",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/yukiny0811/SimpleSimdSwift",
25 | "state" : {
26 | "revision" : "4a0b195450e42f8a46b0a77f183b1ff116696697",
27 | "version" : "1.0.1"
28 | }
29 | },
30 | {
31 | "identity" : "svgpath",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/yukiny0811/SVGPath",
34 | "state" : {
35 | "revision" : "e738fb6400d4e4d14e82c41c9d30c3908285eae9",
36 | "version" : "1.0.0"
37 | }
38 | },
39 | {
40 | "identity" : "swift-custom-dump",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/pointfreeco/swift-custom-dump",
43 | "state" : {
44 | "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1",
45 | "version" : "1.3.3"
46 | }
47 | },
48 | {
49 | "identity" : "swift-snapshot-testing",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/pointfreeco/swift-snapshot-testing",
52 | "state" : {
53 | "revision" : "a8b7c5e0ed33d8ab8887d1654d9b59f2cbad529b",
54 | "version" : "1.18.7"
55 | }
56 | },
57 | {
58 | "identity" : "swift-syntax",
59 | "kind" : "remoteSourceControl",
60 | "location" : "https://github.com/apple/swift-syntax.git",
61 | "state" : {
62 | "revision" : "4799286537280063c85a32f09884cfbca301b1a1",
63 | "version" : "602.0.0"
64 | }
65 | },
66 | {
67 | "identity" : "swiftycoretext",
68 | "kind" : "remoteSourceControl",
69 | "location" : "https://github.com/yukiny0811/SwiftyCoreText",
70 | "state" : {
71 | "revision" : "ed0583f3ecc5c4e4d3ec56c8a9752d6e9a1e9b86",
72 | "version" : "1.0.0"
73 | }
74 | },
75 | {
76 | "identity" : "xctest-dynamic-overlay",
77 | "kind" : "remoteSourceControl",
78 | "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
79 | "state" : {
80 | "revision" : "4c27acf5394b645b70d8ba19dc249c0472d5f618",
81 | "version" : "1.7.0"
82 | }
83 | }
84 | ],
85 | "version" : 2
86 | }
87 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/Texts/Factory/TextFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/03.
6 | //
7 |
8 | import Foundation
9 | import CoreText
10 | import CoreGraphics
11 | import Metal
12 | import SimpleSimdSwift
13 | import FontVertexBuilder
14 |
15 | public class TextFactory {
16 |
17 | private var fontName: String
18 | private var fontSize: Double
19 | private var bounds: CGSize
20 | private var pivot: simd_double2
21 | private var textAlignment: CTTextAlignment
22 | private var verticalAlignment: PathText.VerticalAlignment
23 | private var kern: Double
24 | private var lineSpacing: Double
25 | private var isClockwiseFont: Bool
26 |
27 | public var cached: [Character: LetterCache] = [:]
28 |
29 | public func cacheCharacter(char: Character) {
30 | if char == " " { return }
31 | let vectorText = PathText(text: String(char), fontName: fontName, fontSize: fontSize, bounds: bounds, pivot: pivot, textAlignment: textAlignment, verticalAlignment: verticalAlignment, kern: kern, lineSpacing: lineSpacing)
32 | let resultTuple = GlyphUtil.MainFunctions.triangulateWithoutLetterOffset(vectorText.calculatedPaths)
33 | let path = resultTuple.paths.first!
34 | guard let offset = resultTuple.letterOffsets.first else {
35 | print("failed to cache \(char)")
36 | return
37 | }
38 |
39 | let characterPath = path.glyphLines.flatMap { $0.map { $0 + path.offset } }
40 | let pathBuffer = ShaderCore.device.makeBuffer(bytes: characterPath, length: characterPath.count * f3.memorySize)!
41 | cached[char] = LetterCache(
42 | buffer: pathBuffer,
43 | verticeCount: characterPath.count,
44 | offset: simd_double2(offset.x, offset.y),
45 | size: simd_double2(
46 | characterPath.max(by: {$0.x > $1.x})!.x,
47 | characterPath.max(by: {$0.y > $1.y})!.y
48 | )
49 | )
50 | }
51 |
52 | public init(fontName: String = "Avenir-BlackOblique",
53 | fontSize: Double = 10.0,
54 | bounds: CGSize = .zero,
55 | pivot: simd_double2 = .zero,
56 | textAlignment: CTTextAlignment = .natural,
57 | verticalAlignment: PathText.VerticalAlignment = .center,
58 | kern: Double = 0.0,
59 | lineSpacing: Double = 0.0,
60 | isClockwiseFont: Bool = true
61 | ) {
62 | self.fontName = fontName
63 | self.fontSize = fontSize
64 | self.bounds = bounds
65 | self.pivot = pivot
66 | self.textAlignment = textAlignment
67 | self.verticalAlignment = verticalAlignment
68 | self.kern = kern
69 | self.lineSpacing = lineSpacing
70 | self.isClockwiseFont = isClockwiseFont
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/SwiftyCreatives.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "easymetalshader",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/yukiny0811/EasyMetalShader.git",
7 | "state" : {
8 | "revision" : "7f496fcd2b000262151b90a863228215bf101eda",
9 | "version" : "3.3.2"
10 | }
11 | },
12 | {
13 | "identity" : "fontvertexbuilder",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/yukiny0811/FontVertexBuilder",
16 | "state" : {
17 | "revision" : "2dc3a96a7abfbb6678bd39a70fc0c0f2a80a7d35",
18 | "version" : "2.1.1"
19 | }
20 | },
21 | {
22 | "identity" : "simplesimdswift",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/yukiny0811/SimpleSimdSwift",
25 | "state" : {
26 | "revision" : "4a0b195450e42f8a46b0a77f183b1ff116696697",
27 | "version" : "1.0.1"
28 | }
29 | },
30 | {
31 | "identity" : "svgpath",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/yukiny0811/SVGPath",
34 | "state" : {
35 | "revision" : "e738fb6400d4e4d14e82c41c9d30c3908285eae9",
36 | "version" : "1.0.0"
37 | }
38 | },
39 | {
40 | "identity" : "swift-custom-dump",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/pointfreeco/swift-custom-dump",
43 | "state" : {
44 | "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1",
45 | "version" : "1.3.3"
46 | }
47 | },
48 | {
49 | "identity" : "swift-snapshot-testing",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/pointfreeco/swift-snapshot-testing",
52 | "state" : {
53 | "revision" : "a8b7c5e0ed33d8ab8887d1654d9b59f2cbad529b",
54 | "version" : "1.18.7"
55 | }
56 | },
57 | {
58 | "identity" : "swift-syntax",
59 | "kind" : "remoteSourceControl",
60 | "location" : "https://github.com/apple/swift-syntax.git",
61 | "state" : {
62 | "revision" : "4799286537280063c85a32f09884cfbca301b1a1",
63 | "version" : "602.0.0"
64 | }
65 | },
66 | {
67 | "identity" : "swiftycoretext",
68 | "kind" : "remoteSourceControl",
69 | "location" : "https://github.com/yukiny0811/SwiftyCoreText",
70 | "state" : {
71 | "revision" : "ed0583f3ecc5c4e4d3ec56c8a9752d6e9a1e9b86",
72 | "version" : "1.0.0"
73 | }
74 | },
75 | {
76 | "identity" : "xctest-dynamic-overlay",
77 | "kind" : "remoteSourceControl",
78 | "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
79 | "state" : {
80 | "revision" : "4c27acf5394b645b70d8ba19dc249c0472d5f618",
81 | "version" : "1.7.0"
82 | }
83 | }
84 | ],
85 | "version" : 2
86 | }
87 |
--------------------------------------------------------------------------------
/Examples/ExampleMacOSApp/ExampleMacOSApp/FeatureExamples/Feature2.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Feature2.swift
3 | // ExampleMacOSApp
4 | //
5 | // Created by Yuki Kuwashima on 2024/08/03.
6 | //
7 |
8 | import SwiftyCreatives
9 | import SwiftUI
10 |
11 | final class Feature2: Sketch {
12 |
13 | enum TextSamples: Int {
14 | case textFactory
15 | case text2d
16 | case text3d
17 | case text3dRaw
18 | case textFactoryRaw
19 | case text2dPath
20 | mutating func toggleNext() {
21 | self = Self(rawValue: self.rawValue + 1) ?? Self(rawValue: 0)!
22 | }
23 | }
24 |
25 | var frameCount = 0
26 | var currentGeometry: TextSamples = TextSamples(rawValue: 0)!
27 |
28 | let text2d = Text2D(text: "Test")
29 | let text3d = Text3D(text: "Test3D", extrudingValue: 1)
30 | let factory: TextFactory = {
31 | let f = TextFactory()
32 | for c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!?.," {
33 | f.cacheCharacter(char: c)
34 | }
35 | return f
36 | }()
37 |
38 | let text3dRaw = Text3DRaw(text: "Test3D", extrudingValue: 1)
39 | let factoryRaw: RawTextFactory = {
40 | let f = RawTextFactory()
41 | for c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!?.," {
42 | f.cacheCharacter(char: c)
43 | }
44 | return f
45 | }()
46 |
47 | override func update(camera: MainCamera) {
48 | frameCount += 1
49 | if frameCount.isMultiple(of: 300) {
50 | currentGeometry.toggleNext()
51 | }
52 | }
53 |
54 | override func draw(encoder: SCEncoder, camera: MainCamera) {
55 | color(1, 1, 1, 1)
56 |
57 | switch currentGeometry {
58 | case .textFactory:
59 | text("Hello, World!", factory: factory)
60 | case .text2d:
61 | text(text2d)
62 | case .text3d:
63 | text(text3d)
64 | case .text3dRaw:
65 | for blob in text3dRaw.chunkedBlobs {
66 | mesh(blob.map { $0 + f3.randomPoint(0...0.3) }, primitiveType: .lineStrip)
67 | }
68 | case .textFactoryRaw:
69 | if let vertices = factoryRaw.cached["H"]?.vertices.map({ f3($0) }) {
70 | mesh(vertices: vertices, colors: vertices.map { _ in f4.randomPoint(0.5...1) }, primitiveType: .triangle)
71 | }
72 | break
73 | case .text2dPath:
74 | let str = text2d.calculatedPaths.map { $0.glyphs }
75 | if let letter = str.first {
76 | for line in letter {
77 | mesh(line.map { f3(Float($0.x), Float($0.y), 0) }, primitiveType: .lineStrip)
78 | }
79 | }
80 | }
81 | }
82 |
83 | struct VIEW: View {
84 | var body: some View {
85 | VStack {
86 | Text("text geometries")
87 | SketchView(Feature2())
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/HitTestables/UIViewObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewObject.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/27.
6 | //
7 |
8 | #if os(iOS)
9 |
10 | import MetalKit
11 | import UIKit
12 |
13 | open class UIViewObject: RectanglePlanePrimitive {
14 | private(set) public var texture: MTLTexture?
15 |
16 | public var viewObj: UIView?
17 |
18 | public override init() {
19 | super.init()
20 | hasTexture = [true]
21 | }
22 |
23 | @discardableResult
24 | public func load(view: UIView) -> Self {
25 |
26 | self.viewObj = view
27 |
28 | let image = view.convertToImage().cgImage!
29 |
30 | let tex = try! ShaderCore.textureLoader.newTexture(
31 | cgImage: image,
32 | options: ShaderCore.defaultTextureLoaderOptions
33 | )
34 | self.texture = tex
35 | let longer: Float = Float(max(image.width, image.height))
36 | self.setScale(
37 | f3(
38 | Float(image.width) / longer,
39 | Float(image.height) / longer,
40 | 1
41 | )
42 | )
43 | return self
44 | }
45 | override public func draw(_ encoder: SCEncoder) {
46 | encoder.setVertexBytes(RectShapeInfo.vertices, length: RectShapeInfo.vertices.count * f3.memorySize, index: VertexBufferIndex.Position.rawValue)
47 | encoder.setVertexBytes(_mScale, length: f3.memorySize, index: VertexBufferIndex.ModelScale.rawValue)
48 | encoder.setVertexBytes(RectShapeInfo.uvs, length: RectShapeInfo.uvs.count * f2.memorySize, index: VertexBufferIndex.UV.rawValue)
49 | encoder.setVertexBytes(RectShapeInfo.normals, length: RectShapeInfo.normals.count * f3.memorySize, index: VertexBufferIndex.Normal.rawValue)
50 | encoder.setFragmentBytes(self.hasTexture, length: Bool.memorySize, index: FragmentBufferIndex.HasTexture.rawValue)
51 | encoder.setFragmentTexture(self.texture, index: FragmentTextureIndex.MainTexture.rawValue)
52 | encoder.drawPrimitives(type: RectShapeInfo.primitiveType, vertexStart: 0, vertexCount: RectShapeInfo.vertices.count)
53 | }
54 |
55 | public func buttonTest(origin: f3, direction: f3, testDistance: Float = 3000) {
56 | guard let coord = hitTestGetNormalizedCoord(origin: origin, direction: direction, testDistance: testDistance) else {
57 | return
58 | }
59 | let viewCoord = CGPoint(
60 | x: (CGFloat(coord.x)+1.0) / 2,
61 | y: 1.0 - (CGFloat(coord.y)+1.0) / 2
62 | )
63 | let result = self.viewObj!.hitTest(CGPoint(
64 | x: viewCoord.x * self.viewObj!.bounds.width,
65 | y: viewCoord.y * self.viewObj!.bounds.height
66 | ), with: nil)
67 | guard let button = result as? UIButton else {
68 | return
69 | }
70 | button.sendActions(for: .touchUpInside)
71 | }
72 | }
73 |
74 | #endif
75 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Renderers/Base/RendererBase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RendererBase.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2022/12/14.
6 | //
7 |
8 | import MetalKit
9 |
10 | #if os(visionOS)
11 |
12 | import CompositorServices
13 | import Spatial
14 |
15 | @MainActor
16 | public class RendererBase {
17 |
18 | public var drawProcess: Sketch
19 |
20 | let arSession: ARKitSession
21 | let worldTracking: WorldTrackingProvider
22 | let layerRenderer: LayerRenderer
23 |
24 | public init(sketch: Sketch, layerRenderer: LayerRenderer) {
25 | self.drawProcess = sketch
26 | self.layerRenderer = layerRenderer
27 | worldTracking = WorldTrackingProvider()
28 | arSession = ARKitSession()
29 |
30 |
31 | }
32 |
33 | public func startRenderLoop() {
34 | Task {
35 | do {
36 | try await arSession.run([worldTracking])
37 | } catch {
38 | fatalError("Failed to initialize ARSession")
39 | }
40 |
41 | let renderThread = Thread {
42 | self.renderLoop()
43 | }
44 | renderThread.name = "Render Thread"
45 | renderThread.start()
46 | }
47 | }
48 |
49 | func renderLoop() {
50 | while true {
51 | if layerRenderer.state == .invalidated {
52 | print("Layer is invalidated")
53 | return
54 | } else if layerRenderer.state == .paused {
55 | layerRenderer.waitUntilRunning()
56 | continue
57 | } else {
58 | autoreleasepool {
59 | self.renderFrame()
60 | }
61 | }
62 | }
63 | }
64 | func renderFrame() {
65 |
66 | }
67 | }
68 | #else
69 | @MainActor
70 | public class RendererBase: NSObject, MTKViewDelegate {
71 | var camera: MainCamera
72 | public var drawProcess: Sketch
73 | var savedDate: Date
74 | var drawConfig: DrawConfig
75 | public var cachedTexture: MTLTexture?
76 | public init(drawProcess: Sketch, cameraConfig: CameraConfig, drawConfig: DrawConfig) {
77 | self.camera = MainCamera(config: cameraConfig)
78 | self.drawConfig = drawConfig
79 | self.drawProcess = drawProcess
80 | self.savedDate = Date()
81 | }
82 | public func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
83 | public func draw(in view: MTKView) {
84 | calculateDeltaTime()
85 | view.drawableSize = CGSize(
86 | width: view.frame.size.width * CGFloat(drawConfig.contentScaleFactor),
87 | height: view.frame.size.height * CGFloat(drawConfig.contentScaleFactor)
88 | )
89 | camera.setFrame(
90 | width: Float(view.frame.size.width) * Float(drawConfig.contentScaleFactor),
91 | height: Float(view.frame.size.height) * Float(drawConfig.contentScaleFactor)
92 | )
93 | }
94 | }
95 | #endif
96 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/BoldLineTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BoldLineTests.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/28.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class BoldLineTests: XCTestCase {
16 |
17 | @MainActor
18 | func testBoldLineIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | class TestSketch: SketchForTest {
21 | override func draw(encoder: SCEncoder) {
22 | color(1)
23 | boldline(0, 0, 0, 10, 10, 10, width: 5)
24 | }
25 | }
26 | let expectation = XCTestExpectation()
27 | let sketch = TestSketch(expectation, testName: "testBoldLineIsDrawed")
28 | SnapshotTestUtil.render(sketch: sketch)
29 | await fulfillment(of: [expectation], timeout: 5.0)
30 | }
31 |
32 | @MainActor
33 | func testBoldLineColorWorking() async throws {
34 | try SnapshotTestUtil.testGPU()
35 | class TestSketch: SketchForTest {
36 | override func draw(encoder: SCEncoder) {
37 | color(1, 0.5, 0.2, 0.8)
38 | boldline(0, 0, 0, 10, 10, 10, width: 5)
39 | }
40 | }
41 | let expectation = XCTestExpectation()
42 | let sketch = TestSketch(expectation, testName: "testBoldLineColorWorking")
43 | SnapshotTestUtil.render(sketch: sketch)
44 | await fulfillment(of: [expectation], timeout: 5.0)
45 | }
46 |
47 | @MainActor
48 | func testBoldLineVariations() async throws {
49 | try SnapshotTestUtil.testGPU()
50 | class TestSketch: SketchForTest {
51 | override func draw(encoder: SCEncoder) {
52 | color(1, 0.5, 0.2, 0.8)
53 | boldline(0, 0, 0, 10, 10, 0, width: 2)
54 | boldline(0, 0, 0, 10, 0, 0, width: 2)
55 | boldline(-3, -4, 0, -3, 1, 0, width: 2)
56 | boldline(-5, -3, 0, 2, -5, 0, width: 2)
57 | }
58 | }
59 | let expectation = XCTestExpectation()
60 | let sketch = TestSketch(expectation, testName: "testBoldLineVariations")
61 | SnapshotTestUtil.render(sketch: sketch)
62 | await fulfillment(of: [expectation], timeout: 5.0)
63 | }
64 |
65 | @MainActor
66 | func testBoldLineWithVertexColor() async throws {
67 | try SnapshotTestUtil.testGPU()
68 | class TestSketch: SketchForTest {
69 | override func draw(encoder: SCEncoder) {
70 | color(1, 0.5, 0.2, 0.8)
71 | boldline(0, 0, 0, 10, 10, 0, width: 2, color1: f4(1, 0, 0, 1), color2: f4(0, 0, 1, 1))
72 | }
73 | }
74 | let expectation = XCTestExpectation()
75 | let sketch = TestSketch(expectation, testName: "testBoldLineWithVertexColor")
76 | SnapshotTestUtil.render(sketch: sketch)
77 | await fulfillment(of: [expectation], timeout: 5.0)
78 | }
79 | }
80 | #endif
81 |
--------------------------------------------------------------------------------
/Sources/SwiftyCreatives/Primitives/PrimitiveUtils/Abstract/ImageLoadable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageLoadable.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2023/03/03.
6 | //
7 |
8 | import Metal
9 | import CoreGraphics
10 |
11 | public protocol ImageLoadable: AnyObject, ScaleSettable {
12 | var texture: MTLTexture? { get set }
13 | }
14 |
15 | // MARK: Util Functions
16 | public extension ImageLoadable {
17 | @discardableResult
18 | func adjustScale(with option: ImageAdjustOption) -> Self {
19 | let width = Float(texture!.width)
20 | let height = Float(texture!.height)
21 | switch option {
22 | case .basedOnWidth:
23 | self.setScale(f3(1, height / width, 1))
24 | case .basedOnHeight:
25 | self.setScale(f3(width / height, 1, 1))
26 | case .basedOnLonger:
27 | let longer = max(width, height)
28 | self.setScale(f3(width / longer, height / longer, 1))
29 | }
30 | return self
31 | }
32 | }
33 |
34 | // MARK: Load Functions
35 | public extension ImageLoadable {
36 |
37 | @discardableResult
38 | func load(name: String, bundle: Bundle?) -> Self {
39 | self.texture = try! ShaderCore.textureLoader.newTexture(
40 | name: name,
41 | scaleFactor: 3,
42 | bundle: bundle,
43 | options: ShaderCore.defaultTextureLoaderOptions
44 | )
45 | return self
46 | }
47 |
48 | @discardableResult
49 | func load(image: CGImage) -> Self {
50 | self.texture = try! ShaderCore.textureLoader.newTexture(
51 | cgImage: image,
52 | options: ShaderCore.defaultTextureLoaderOptions
53 | )
54 | return self
55 | }
56 |
57 | @discardableResult
58 | func load(data: Data) -> Self {
59 | self.texture = try! ShaderCore.textureLoader.newTexture(
60 | data: data,
61 | options: ShaderCore.defaultTextureLoaderOptions
62 | )
63 | return self
64 | }
65 | }
66 |
67 | // MARK: Async Load Functions
68 | public extension ImageLoadable {
69 |
70 | @discardableResult
71 | func load(url: URL) async -> Self {
72 | self.texture = try! await ShaderCore.textureLoader.newTexture(
73 | URL: url,
74 | options: ShaderCore.defaultTextureLoaderOptions
75 | )
76 | return self
77 | }
78 |
79 | @discardableResult
80 | func load(name: String, bundle: Bundle?) async -> Self {
81 | self.texture = try! await ShaderCore.textureLoader.newTexture(
82 | name: name,
83 | scaleFactor: 3,
84 | bundle: bundle,
85 | options: ShaderCore.defaultTextureLoaderOptions
86 | )
87 | return self
88 | }
89 |
90 | @discardableResult
91 | func load(data: Data) async -> Self {
92 | self.texture = try! await ShaderCore.textureLoader.newTexture(
93 | data: data,
94 | options: ShaderCore.defaultTextureLoaderOptions
95 | )
96 | return self
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyCreatives.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Tests/SwiftyCreativesTests/SnapshotTests/Functions/SVGTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Yuki Kuwashima on 2024/02/06.
6 | //
7 |
8 | @testable import SwiftyCreatives
9 | import XCTest
10 | import SwiftUI
11 | import SnapshotTesting
12 | import MetalKit
13 |
14 | #if os(macOS)
15 | final class SVGTests: XCTestCase {
16 |
17 | @MainActor
18 | func testSVGIsDrawed() async throws {
19 | try SnapshotTestUtil.testGPU()
20 | let svgObject = await SVGObj(url: Bundle.module.url(forResource: "sampleSvg", withExtension: "svg")!)!
21 | class TestSketch: SketchForTest {
22 | let svgObject: SVGObj
23 | init(svgObject: SVGObj, _ expectation: XCTestExpectation, testName: String) {
24 | self.svgObject = svgObject
25 | super.init(expectation, testName: testName)
26 | }
27 | override func draw(encoder: SCEncoder) {
28 | color(0.3, 0.6, 1.0, 1.0)
29 | scale(0.05)
30 | let count = svgObject.triangulated.reduce([], +).count
31 | var colors: [f4] = []
32 | for i in 0..