git.iko.soy/metal-metaballs
on master help sign in
Merged feature-colors into dev
commit21443eabd49a66aafc5990ff556105f4fbbfee26 authorvinivendra <vinivendra@gmail.com> dateSat 20 Feb 2016, 18:42 (11 years ago) parent28a13e227c0833
25 files changed+386−143
.gitignore+5−1 PixelImage.xcodeproj/project.pbxproj+96 PixelImage.xcworkspace/contents.xcworkspacedata+10 PixelImage.xcworkspace/xcuserdata/vini.xcuserdatad/UserInterfaceState.xcuserstate PixelImage.xcworkspace/xcuserdata/vini.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist+5 PixelImage/AppDelegate.swift−4 PixelImage/Interpolation.swift−2 PixelImage/Matrix.swift+1−1 PixelImage/MetaballDataSource.swift+2−2 PixelImage/Metal.swift+1−1 PixelImage/MetalMetaballRenderer.swift+34−28 PixelImage/SwiftSugar/Dictionary.swift+1−1 PixelImage/SwiftSugar/Foundation/NSNumber.swift+4−1 PixelImage/SwiftSugar/Numeric.swift+15−4 PixelImage/SwiftSugar/PowerOperator.swift+1−1 PixelImage/SwiftSugar/Sequences/Memoize.swift+51−46 PixelImage/SwiftSugar/SignOperator.swift+8−9 PixelImage/SwiftSugar/String.swift+4−1 PixelImage/SwiftSugar/TestCommons.swift+2−1 PixelImage/UIColor.swift+9 PixelImage/UIKit.swift+52−18 PixelImage/ViewController.swift+9−8 PixelImage/shaders.metal+58−14 Podfile+6 Podfile.lock+12
.gitignore+5−1
@@ -1,3 +1,7 @@
11.DS*
22PixelImage.xcodeproj/xcuserdata/*
3−PixelImage.xcodeproj/project.xcworkspace/xcuserdata/*
3+PixelImage.xcodeproj/project.xcworkspace/xcuserdata/*
4+
5+Pods/*
6+
7+
PixelImage.xcodeproj/project.pbxproj+96
@@ -13,6 +13,7 @@
1313 2426868F1C63B72A00D62456 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2426868E1C63B72A00D62456 /* Assets.xcassets */; };
1414 242686921C63B72A00D62456 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 242686901C63B72A00D62456 /* LaunchScreen.storyboard */; };
1515 2461651B1C6DA4F400A913C8 /* Interpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2461651A1C6DA4F400A913C8 /* Interpolation.swift */; };
16+ 246B0F831C70635D00A71D05 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246B0F821C70635D00A71D05 /* UIColor.swift */; };
1617 249CF84C1C6D113C008B0C78 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249CF84B1C6D113C008B0C78 /* Matrix.swift */; };
1718 24E1E7D71C67C8B200ECF1C4 /* UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */; };
1819 24E1E7E21C67D37F00ECF1C4 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */; };
@@ -49,6 +50,7 @@
4950 24FFA8841C6BA038005ACD60 /* TakeDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FFA8681C6BA038005ACD60 /* TakeDrop.swift */; };
5051 24FFA8851C6BA038005ACD60 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FFA8691C6BA038005ACD60 /* Zip.swift */; };
5152 24FFA8861C6BA038005ACD60 /* TestCommons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FFA86A1C6BA038005ACD60 /* TestCommons.swift */; };
53+ 2BF7957B6518CA68241BB2CE /* Pods_PixelImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C55F9558B3B267413C3A07A6 /* Pods_PixelImage.framework */; };
5254/* End PBXBuildFile section */
5355
5456/* Begin PBXFileReference section */
@@ -60,6 +62,7 @@
6062 242686911C63B72A00D62456 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
6163 242686931C63B72A00D62456 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6264 2461651A1C6DA4F400A913C8 /* Interpolation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Interpolation.swift; path = PixelImage/Interpolation.swift; sourceTree = SOURCE_ROOT; };
65+ 246B0F821C70635D00A71D05 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = "<group>"; };
6366 249CF84B1C6D113C008B0C78 /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = "<group>"; };
6467 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKit.swift; sourceTree = "<group>"; };
6568 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
@@ -96,6 +99,9 @@
9699 24FFA8681C6BA038005ACD60 /* TakeDrop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TakeDrop.swift; sourceTree = "<group>"; };
97100 24FFA8691C6BA038005ACD60 /* Zip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zip.swift; sourceTree = "<group>"; };
98101 24FFA86A1C6BA038005ACD60 /* TestCommons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestCommons.swift; sourceTree = "<group>"; };
102+ B1A755EFC03DA2A172C2B82F /* Pods-PixelImage.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PixelImage.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PixelImage/Pods-PixelImage.debug.xcconfig"; sourceTree = "<group>"; };
103+ C419D37C90A6BE8B5139806A /* Pods-PixelImage.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PixelImage.release.xcconfig"; path = "Pods/Target Support Files/Pods-PixelImage/Pods-PixelImage.release.xcconfig"; sourceTree = "<group>"; };
104+ C55F9558B3B267413C3A07A6 /* Pods_PixelImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PixelImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
99105/* End PBXFileReference section */
100106
101107/* Begin PBXFrameworksBuildPhase section */
@@ -105,6 +111,7 @@
105111 files = (
106112 24E1E7E41C67D39300ECF1C4 /* MetalKit.framework in Frameworks */,
107113 24E1E7E21C67D37F00ECF1C4 /* Metal.framework in Frameworks */,
114+ 2BF7957B6518CA68241BB2CE /* Pods_PixelImage.framework in Frameworks */,
108115 );
109116 runOnlyForDeploymentPostprocessing = 0;
110117 };
@@ -118,6 +125,8 @@
118125 24E1E7E11C67D37F00ECF1C4 /* Metal.framework */,
119126 242686861C63B72A00D62456 /* PixelImage */,
120127 242686851C63B72A00D62456 /* Products */,
128+ F1933B48ED44BA9FCD7A4A1F /* Pods */,
129+ 588576572D7A188031904848 /* Frameworks */,
121130 );
122131 sourceTree = "<group>";
123132 };
@@ -136,6 +145,7 @@
136145 242686871C63B72A00D62456 /* AppDelegate.swift */,
137146 242686891C63B72A00D62456 /* ViewController.swift */,
138147 24E1E7D61C67C8B200ECF1C4 /* UIKit.swift */,
148+ 246B0F821C70635D00A71D05 /* UIColor.swift */,
139149 24ED21CC1C6C2397009C40FC /* MetaballDataSource.swift */,
140150 24E1E7EA1C67D70300ECF1C4 /* MetalMetaballRenderer.swift */,
141151 24E1E7EE1C67D74300ECF1C4 /* Metal.swift */,
@@ -209,6 +219,23 @@
209219 path = SwiftSequences;
210220 sourceTree = "<group>";
211221 };
222+ 588576572D7A188031904848 /* Frameworks */ = {
223+ isa = PBXGroup;
224+ children = (
225+ C55F9558B3B267413C3A07A6 /* Pods_PixelImage.framework */,
226+ );
227+ name = Frameworks;
228+ sourceTree = "<group>";
229+ };
230+ F1933B48ED44BA9FCD7A4A1F /* Pods */ = {
231+ isa = PBXGroup;
232+ children = (
233+ B1A755EFC03DA2A172C2B82F /* Pods-PixelImage.debug.xcconfig */,
234+ C419D37C90A6BE8B5139806A /* Pods-PixelImage.release.xcconfig */,
235+ );
236+ name = Pods;
237+ sourceTree = "<group>";
238+ };
212239/* End PBXGroup section */
213240
214241/* Begin PBXNativeTarget section */
@@ -216,9 +243,13 @@
216243 isa = PBXNativeTarget;
217244 buildConfigurationList = 242686961C63B72A00D62456 /* Build configuration list for PBXNativeTarget "PixelImage" */;
218245 buildPhases = (
246+ EF55C7246917A184756D93D8 /* Check Pods Manifest.lock */,
219247 242686801C63B72A00D62456 /* Sources */,
220248 242686811C63B72A00D62456 /* Frameworks */,
221249 242686821C63B72A00D62456 /* Resources */,
250+ C7A2DF67265A9D17DEFBD193 /* Embed Pods Frameworks */,
251+ FADC01F75BFAC01F0260D1C9 /* Copy Pods Resources */,
252+ DD0396CE594E3EAC8D18CC6C /* Tailor */,
222253 );
223254 buildRules = (
224255 );
@@ -276,6 +307,68 @@
276307 };
277308/* End PBXResourcesBuildPhase section */
278309
310+/* Begin PBXShellScriptBuildPhase section */
311+ C7A2DF67265A9D17DEFBD193 /* Embed Pods Frameworks */ = {
312+ isa = PBXShellScriptBuildPhase;
313+ buildActionMask = 2147483647;
314+ files = (
315+ );
316+ inputPaths = (
317+ );
318+ name = "Embed Pods Frameworks";
319+ outputPaths = (
320+ );
321+ runOnlyForDeploymentPostprocessing = 0;
322+ shellPath = /bin/sh;
323+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PixelImage/Pods-PixelImage-frameworks.sh\"\n";
324+ showEnvVarsInLog = 0;
325+ };
326+ DD0396CE594E3EAC8D18CC6C /* Tailor */ = {
327+ isa = PBXShellScriptBuildPhase;
328+ buildActionMask = 2147483647;
329+ files = (
330+ );
331+ inputPaths = (
332+ );
333+ name = Tailor;
334+ outputPaths = (
335+ );
336+ runOnlyForDeploymentPostprocessing = 0;
337+ shellPath = /bin/sh;
338+ shellScript = "if hash tailor 2>/dev/null; then\n tailor --except=function-whitespace,leading-whitespace,constant-k-prefix --max-severity error\nelse\n echo \"warning: Please install Tailor from https://tailor.sh\"\nfi";
339+ };
340+ EF55C7246917A184756D93D8 /* Check Pods Manifest.lock */ = {
341+ isa = PBXShellScriptBuildPhase;
342+ buildActionMask = 2147483647;
343+ files = (
344+ );
345+ inputPaths = (
346+ );
347+ name = "Check Pods Manifest.lock";
348+ outputPaths = (
349+ );
350+ runOnlyForDeploymentPostprocessing = 0;
351+ shellPath = /bin/sh;
352+ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
353+ showEnvVarsInLog = 0;
354+ };
355+ FADC01F75BFAC01F0260D1C9 /* Copy Pods Resources */ = {
356+ isa = PBXShellScriptBuildPhase;
357+ buildActionMask = 2147483647;
358+ files = (
359+ );
360+ inputPaths = (
361+ );
362+ name = "Copy Pods Resources";
363+ outputPaths = (
364+ );
365+ runOnlyForDeploymentPostprocessing = 0;
366+ shellPath = /bin/sh;
367+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PixelImage/Pods-PixelImage-resources.sh\"\n";
368+ showEnvVarsInLog = 0;
369+ };
370+/* End PBXShellScriptBuildPhase section */
371+
279372/* Begin PBXSourcesBuildPhase section */
280373 242686801C63B72A00D62456 /* Sources */ = {
281374 isa = PBXSourcesBuildPhase;
@@ -308,6 +401,7 @@
308401 24FFA86E1C6BA038005ACD60 /* Dictionary.swift in Sources */,
309402 24FFA87D1C6BA038005ACD60 /* Hopping.swift in Sources */,
310403 24E1E7EB1C67D70300ECF1C4 /* MetalMetaballRenderer.swift in Sources */,
404+ 246B0F831C70635D00A71D05 /* UIColor.swift in Sources */,
311405 24FFA87A1C6BA038005ACD60 /* Enumerate.swift in Sources */,
312406 242686881C63B72A00D62456 /* AppDelegate.swift in Sources */,
313407 24E1E7F01C67D74300ECF1C4 /* Metal.swift in Sources */,
@@ -428,6 +522,7 @@
428522 };
429523 242686971C63B72A00D62456 /* Debug */ = {
430524 isa = XCBuildConfiguration;
525+ baseConfigurationReference = B1A755EFC03DA2A172C2B82F /* Pods-PixelImage.debug.xcconfig */;
431526 buildSettings = {
432527 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
433528 INFOPLIST_FILE = PixelImage/Info.plist;
@@ -439,6 +534,7 @@
439534 };
440535 242686981C63B72A00D62456 /* Release */ = {
441536 isa = XCBuildConfiguration;
537+ baseConfigurationReference = C419D37C90A6BE8B5139806A /* Pods-PixelImage.release.xcconfig */;
442538 buildSettings = {
443539 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
444540 INFOPLIST_FILE = PixelImage/Info.plist;
PixelImage.xcworkspace/contents.xcworkspacedatanew file+10
@@ -0,0 +1,10 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "group:PixelImage.xcodeproj">
6+ </FileRef>
7+ <FileRef
8+ location = "group:Pods/Pods.xcodeproj">
9+ </FileRef>
10+</Workspace>
PixelImage.xcworkspace/xcuserdata/vini.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

PixelImage.xcworkspace/xcuserdata/vini.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlistnew file+5
@@ -0,0 +1,5 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Bucket
3+ type = "0"
4+ version = "2.0">
5+</Bucket>
PixelImage/AppDelegate.swift−4
@@ -13,7 +13,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
1313
1414 var window: UIWindow?
1515
16−
1716 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
1817 // Override point for customization after application launch.
1918 return true
@@ -40,7 +39,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
4039 func applicationWillTerminate(application: UIApplication) {
4140 // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
4241 }
43−
44−
4542}
46−
PixelImage/Interpolation.swift−2
@@ -1,4 +1,3 @@
1−
21func interpolateCubeEaseIn<T: Numeric>(linear: T) -> T {
32 return linear ^ 3
43}
@@ -14,4 +13,3 @@ func interpolateSquareEaseOut<T: Numeric>(linear: T) -> T {
1413func interpolateSmooth<T: Numeric>(linear: T) -> T {
1514 return ((linear) * (linear) * (linear) * ((linear) * ((linear) * 6 - 15) + 10))
1615}
17−
PixelImage/Matrix.swift+1−1
@@ -1,4 +1,3 @@
1−
21import UIKit
32
43class Graph<T> {
@@ -27,6 +26,7 @@ class Graph<T> {
2726 adjacencyMatrix.reset(i, j)
2827 adjacencyMatrix.reset(j, i)
2928 }
29+
3030}
3131
3232struct Matrix: CustomStringConvertible {
PixelImage/MetaballDataSource.swift+2−2
@@ -1,4 +1,3 @@
1−
21import Metal
32
43protocol MetaballDataSource {
@@ -11,4 +10,5 @@ extension MetaballDataSource {
1110 return metaballGraph.vertices
1211 }
1312 }
14−}
13+
14+}
PixelImage/Metal.swift+1−1
@@ -40,4 +40,4 @@ class MTLComputeContext {
4040 deinit {
4141 free(imageBuffer)
4242 }
43−}
43+}
PixelImage/MetalMetaballRenderer.swift+34−28
@@ -1,5 +1,6 @@
11import Metal
22import UIKit
3+import ChameleonFramework
34
45class MetalMetaballRenderer {
56
@@ -38,8 +39,7 @@ class MetalMetaballRenderer {
3839
3940 let width = Int(frame.width)
4041 let height = Int(frame.height)
41− let textureDescriptor =
42− MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(.BGRA8Unorm, width: width, height: height, mipmapped: false)
42+ let textureDescriptor = MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(.BGRA8Unorm, width: width, height: height, mipmapped: false)
4343
4444 let texture1 = context.device.newTextureWithDescriptor(textureDescriptor)
4545 activeComputeContext = MTLComputeContext(size: targetView.size, texture: texture1)
@@ -54,7 +54,8 @@ class MetalMetaballRenderer {
5454
5555 state = .Ending
5656
57− dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)) { () -> Void in
57+ let userInteractiveQueue = dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.rawValue), 0)
58+ dispatch_async(userInteractiveQueue) { () -> Void in
5859
5960 let computeContext = self.activeComputeContext
6061 swap(&self.activeComputeContext, &self.idleComputeContext)
@@ -73,10 +74,10 @@ class MetalMetaballRenderer {
7374
7475 dispatch_semaphore_signal(self.semaphore)
7576
76− dispatch_async(dispatch_get_main_queue(), { () -> Void in
77+ dispatch_async(dispatch_get_main_queue()) { () -> Void in
7778 // Send image to view
7879 self.targetView.image = uiimage
79− })
80+ }
8081 }
8182 }
8283
@@ -93,8 +94,7 @@ class MetalMetaballRenderer {
9394
9495 // Configure info for computing
9596 let threadGroupCounts = MTLSizeMake(8, 8, 1)
96− let threadGroups = MTLSizeMake(width / threadGroupCounts.width,
97− height / threadGroupCounts.height, 1)
97+ let threadGroups = MTLSizeMake(width / threadGroupCounts.width, height / threadGroupCounts.height, 1)
9898
9999 // Send commands to metal and render
100100 let commandBuffer = context.commandQueue.commandBuffer()
@@ -112,7 +112,6 @@ class MetalMetaballRenderer {
112112
113113 commandBuffer.commit()
114114 commandBuffer.waitUntilCompleted()
115−
116115 } catch _ {
117116 print("Error!")
118117 }
@@ -122,8 +121,7 @@ class MetalMetaballRenderer {
122121 let texture = computeContext.texture
123122
124123 // Get image info
125− let imageSize = CGSizeMake(CGFloat(texture.width),
126− CGFloat(texture.height))
124+ let imageSize = CGSizeMake(CGFloat(texture.width), CGFloat(texture.height))
127125 let width = Int(imageSize.width)
128126 let height = Int(imageSize.height)
129127 let imageByteCount = width * height * 4
@@ -136,27 +134,20 @@ class MetalMetaballRenderer {
136134 }
137135
138136 // Transfer texture info into image buffer
139− texture.getBytes(computeContext.imageBuffer, bytesPerRow: bytesPerRow,
140− fromRegion: region, mipmapLevel: 0)
137+ texture.getBytes(computeContext.imageBuffer, bytesPerRow: bytesPerRow, fromRegion: region, mipmapLevel: 0)
141138
142139 // Get info for UIImage
143− let provider = CGDataProviderCreateWithData(nil, computeContext.imageBuffer,
144− imageByteCount, nil)
140+ let provider = CGDataProviderCreateWithData(nil, computeContext.imageBuffer, imageByteCount, nil)
145141 let bitsPerComponent = 8
146142 let bitsperPixel = 32
147143 let colorSpaceRef = CGColorSpaceCreateDeviceRGB()
148− let bitmapInfo = CGBitmapInfo(rawValue:
149− CGImageAlphaInfo.PremultipliedLast.rawValue |
150− CGBitmapInfo.ByteOrder32Big.rawValue)
144+ let bitmapInfoRaw = CGImageAlphaInfo.PremultipliedLast.rawValue | CGBitmapInfo.ByteOrder32Big.rawValue
145+ let bitmapInfo = CGBitmapInfo(rawValue: bitmapInfoRaw)
151146 let renderingIntent = CGColorRenderingIntent.RenderingIntentDefault
152147
153148 // Create UIImage from image buffer
154− let imageRef = CGImageCreate(width, height,
155− bitsPerComponent, bitsperPixel, bytesPerRow,
156− colorSpaceRef, bitmapInfo, provider,
157− nil, false, renderingIntent)
158− let image = UIImage(CGImage: imageRef!, scale: 0.0,
159− orientation: UIImageOrientation.Up)
149+ let imageRef = CGImageCreate(width, height, bitsPerComponent, bitsperPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, nil, false, renderingIntent)
150+ let image = UIImage(CGImage: imageRef!, scale: 0.0, orientation: .Up)
160151
161152 return image
162153 }
@@ -164,7 +155,9 @@ class MetalMetaballRenderer {
164155 func metaballEdgesBuffer() -> MTLBuffer {
165156 let floats = dataSource.metaballGraph.adjacencyMatrix.buffer
166157
167− let buffer = context.device.newBufferWithBytes(floats, length: floats.count * sizeof(Float), options: .StorageModeShared)
158+ let bufferLength = floats.count * sizeof(Float)
159+ let device = context.device
160+ let buffer = device.newBufferWithBytes(floats, length: bufferLength, options: .StorageModeShared)
168161
169162 return buffer
170163 }
@@ -174,22 +167,35 @@ class MetalMetaballRenderer {
174167 // Exclude metaballs far from the view's bounds
175168 let border: CGFloat = 100
176169 let metaballs = self.dataSource.metaballs
177− var floats: [Float] = metaballs.reduce([Float(0)]) { (var array, metaball) -> [Float] in
170+
171+ var floats = [Float](count: 5, repeatedValue: 0)
172+ floats = metaballs.reduce(floats) {
173+ (var array, metaball) -> [Float] in
174+
178175 let x = CGFloat(metaball.midX)
179176 let y = CGFloat(metaball.midY)
180177 if -border < x ≤ targetView.width + border &&
181178 -border < y ≤ targetView.height + border {
182179 array.append(Float(x))
183180 array.append(Float(y))
181+
182+ let color = metaball.backgroundColor!
183+ let components = color.components
184+ array.append(Float(components.red))
185+ array.append(Float(components.green))
186+ array.append(Float(components.blue))
184187 }
185188 return array
186189 }
190+
187191 // Add array size to start of array so metal knows how far to go
188− floats[0] = Float(floats.count) / 2
192+ floats[0] = Float(floats.count) / 5
189193
190194 // Create buffer
191− let buffer = context.device.newBufferWithBytes(floats, length: floats.count * sizeof(Float), options: .StorageModeShared)
192−
195+ let bufferLength = floats.count * sizeof(Float)
196+ let device = context.device
197+ let buffer = device.newBufferWithBytes(floats, length: bufferLength, options: .StorageModeShared)
198+
193199 return buffer
194200 }
195201}
PixelImage/SwiftSugar/Dictionary.swift+1−1
@@ -1,7 +1,7 @@
11extension Dictionary {
22 subscript(keys: [Key]) -> [Value] {
33 get {
4− return keys.unwrappedMap({ self[$0] })
4+ return keys.unwrappedMap { self[$0] }
55 }
66 set (values) {
77 for (key, value) in zip(keys, values) {
PixelImage/SwiftSugar/Foundation/NSNumber.swift+4−1
@@ -2,10 +2,13 @@ import Foundation
22
33extension NSNumber: DoubleValuable {
44 var toDouble: Double {
5− get { return self.doubleValue }
5+ get {
6+ return self.doubleValue
7+ }
68 }
79
810 static func fromDouble(double: Double) -> Self {
911 return self.init(double: double)
1012 }
13+
1114}
PixelImage/SwiftSugar/Numeric.swift+15−4
@@ -20,43 +20,54 @@ protocol Numeric: DoubleValuable, Incrementable, Comparable,
2020 postfix func ++ (inout _: Self) -> Self
2121}
2222
23−protocol FastNumeric: Numeric { }
23+protocol FastNumeric: Numeric {}
2424
2525extension Double: FastNumeric {
2626 var toDouble: Double {
27− get { return self }
27+ get {
28+ return self
29+ }
2830 }
2931
3032 static func fromDouble(double: Double) -> Double {
3133 return double
3234 }
35+
3336}
3437
3538extension Float: FastNumeric {
3639 var toDouble: Double {
37− get { return Double(self) }
40+ get {
41+ return Double(self)
42+ }
3843 }
3944
4045 static func fromDouble(double: Double) -> Float {
4146 return Float(double)
4247 }
48+
4349}
4450
4551extension Int: FastNumeric {
4652 var toDouble: Double {
47− get { return Double(self) }
53+ get {
54+ return Double(self)
55+ }
4856 }
4957
5058 static func fromDouble(double: Double) -> Int {
5159 return Int(double)
5260 }
61+
5362}
5463
5564//
5665extension Int {
66+
5767 func times(@noescape block: () throws -> ()) rethrows {
5868 for _ in 1...self {
5969 try block()
6070 }
6171 }
72+
6273}
PixelImage/SwiftSugar/PowerOperator.swift+1−1
@@ -2,7 +2,7 @@ import Darwin
22
33infix operator ^ { associativity right precedence 131 }
44
5−func ^<T: DoubleValuable>(left: T, right: T) -> T {
5+func ^ <T: DoubleValuable>(left: T, right: T) -> T {
66 return T.fromDouble(pow(left.toDouble, right.toDouble))
77}
88
PixelImage/SwiftSugar/Sequences/Memoize.swift+51−46
@@ -7,62 +7,66 @@ protocol MemoizedSequence: LazySequenceType {
77 memoizedFunction: (IndexType -> ReturnType))
88}
99
10+struct HashMemoizedSequence<IndexType: Hashable, ReturnType>:
11+ MemoizedSequence {
1012
11−struct HashMemoizedSequence <IndexType: Hashable, ReturnType>:
12−MemoizedSequence {
13+ let memoizedFunction: (IndexType -> ReturnType)
14+ let increment: ((inout IndexType) -> Void)
15+ let firstIndex: IndexType
1316
14− let memoizedFunction: (IndexType -> ReturnType)
15− let increment: ((inout IndexType) -> Void)
16− let firstIndex: IndexType
17+ var index: IndexType
1718
18− var index: IndexType
19+ init(first: IndexType,
20+ incrementer: ((inout IndexType) -> Void),
21+ memoizedFunction: (IndexType -> ReturnType)) {
1922
20− init(first: IndexType,
21− incrementer: ((inout IndexType) -> Void),
22− memoizedFunction: (IndexType -> ReturnType)) {
23+ self.firstIndex = first
2324
24− self.firstIndex = first
25+ self.index = first
26+ self.increment = incrementer
27+ self.memoizedFunction = memoizedFunction
28+ }
2529
26− self.index = first
27− self.increment = incrementer
28− self.memoizedFunction = memoizedFunction
29− }
30+ init(first: IndexType,
31+ incrementer: ((inout IndexType) -> Void),
32+ closure: ((IndexType -> ReturnType, IndexType) -> ReturnType)) {
33+ let memoizedFunction = memoize(closure)
3034
31− init(first: IndexType,
32− incrementer: ((inout IndexType) -> Void),
33− closure: ((IndexType -> ReturnType, IndexType) -> ReturnType)) {
34− let memoizedFunction = memoize(closure)
35−
36− self.init(first: first, incrementer: incrementer,
37− memoizedFunction: memoizedFunction)
38− }
35+ self.init(first: first, incrementer: incrementer,
36+ memoizedFunction: memoizedFunction)
37+ }
3938
40− init(first: IndexType,
41− incrementer: ((inout IndexType) -> Void),
42− function: (IndexType -> ReturnType)) {
39+ init(first: IndexType,
40+ incrementer: ((inout IndexType) -> Void),
41+ function: (IndexType -> ReturnType)) {
4342
44− let memoizedFunction = memoize(function)
43+ let memoizedFunction = memoize(function)
4544
46− self.init(first: first, incrementer: incrementer,
47− memoizedFunction: memoizedFunction)
48− }
45+ self.init(first: first, incrementer: incrementer,
46+ memoizedFunction: memoizedFunction)
47+ }
4948}
5049
5150extension HashMemoizedSequence: GeneratorType {
51+
5252 mutating func next() -> ReturnType? {
5353 let result = memoizedFunction(index)
5454 increment(&index)
5555 return result
5656 }
57+
5758}
5859
5960extension HashMemoizedSequence: LazySequenceType {
61+
6062 func generate() -> HashMemoizedSequence {
6163 return self
6264 }
65+
6366}
6467
6568extension HashMemoizedSequence where ReturnType: Comparable {
69+
6670 @warn_unused_result
6771 func contains(element: ReturnType) -> Bool {
6872 var index = firstIndex
@@ -79,6 +83,7 @@ extension HashMemoizedSequence where ReturnType: Comparable {
7983 increment(&index)
8084 }
8185 }
86+
8287}
8388
8489extension HashMemoizedSequence where IndexType: Incrementable {
@@ -104,29 +109,29 @@ extension HashMemoizedSequence where IndexType: Incrementable {
104109}
105110
106111extension HashMemoizedSequence where IndexType: Incrementable,
107− IndexType: IntegerLiteralConvertible {
112+IndexType: IntegerLiteralConvertible {
108113 init(_ closure: ((IndexType -> ReturnType, IndexType) -> ReturnType)) {
109− self.firstIndex = 1
110− self.index = 1
111− self.increment = { (inout index: IndexType) in
112− index++
113− }
114− self.memoizedFunction = memoize(closure)
114+ self.firstIndex = 1
115+ self.index = 1
116+ self.increment = { (inout index: IndexType) in
117+ index++
118+ }
119+ self.memoizedFunction = memoize(closure)
115120 }
116121
117122 init(_ function: (IndexType -> ReturnType)) {
118− self.firstIndex = 1
119− self.index = 1
120− self.increment = { (inout index: IndexType) in
121− index++
122− }
123− self.memoizedFunction = memoize(function)
123+ self.firstIndex = 1
124+ self.index = 1
125+ self.increment = { (inout index: IndexType) in
126+ index++
127+ }
128+ self.memoizedFunction = memoize(function)
124129 }
125130}
126131
127−func memoize <H: Hashable, R> (closure: ((H -> R, H) -> R) ) -> (H -> R) {
132+func memoize<H: Hashable, R>(closure: ((H -> R, H) -> R)) -> (H -> R) {
128133
129− var table = [H : R]()
134+ var table = [H: R]()
130135
131136 var auxiliaryFunction: (H -> R)!
132137 auxiliaryFunction = { index in
@@ -143,9 +148,9 @@ func memoize <H: Hashable, R> (closure: ((H -> R, H) -> R) ) -> (H -> R) {
143148 return auxiliaryFunction
144149}
145150
146−func memoize <H: Hashable, R> (function: (H -> R) ) -> (H -> R) {
151+func memoize<H: Hashable, R>(function: (H -> R)) -> (H -> R) {
147152
148− var table = [H : R]()
153+ var table = [H: R]()
149154
150155 let result: (H -> R) = { index in
151156 if let result = table[index] {
PixelImage/SwiftSugar/SignOperator.swift+8−9
@@ -1,13 +1,12 @@
1−
21prefix operator ± { }
32
4−prefix func ± <T: Numeric> (number: T) -> T {
3+prefix func ± <T: Numeric>(number: T) -> T {
54
6− if number > 0 {
7− return 1
8− } else if number == 0 {
9− return 0
10− } else {
11− return -1
12− }
5+ if number > 0 {
6+ return 1
7+ } else if number == 0 {
8+ return 0
9+ } else {
10+ return -1
11+ }
1312}
PixelImage/SwiftSugar/String.swift+4−1
@@ -1,9 +1,12 @@
11extension String: DoubleValuable {
22 var toDouble: Double {
3− get { return Double(self) ?? Double(0) }
3+ get {
4+ return Double(self) ?? Double(0)
5+ }
46 }
57
68 static func fromDouble(double: Double) -> String {
79 return "\(double)"
810 }
11+
912}
PixelImage/SwiftSugar/TestCommons.swift+2−1
@@ -8,7 +8,7 @@
88
99import Darwin
1010
11−class Random <T: Numeric> {
11+class Random<T: Numeric> {
1212
1313 let max: Double = Double(INT32_MAX)
1414 let exponent: Double = 7
@@ -92,4 +92,5 @@ class Random <T: Numeric> {
9292 let result = Double((2 * rawRandom) - 1)
9393 return T.fromDouble(result)
9494 }
95+
9596}
PixelImage/UIColor.swiftnew file+9
@@ -0,0 +1,9 @@
1+import UIKit
2+
3+extension UIColor {
4+ var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
5+ var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
6+ getRed(&r, green: &g, blue: &b, alpha: &a)
7+ return (r, g, b, a)
8+ }
9+}
PixelImage/UIKit.swift+52−18
@@ -31,6 +31,7 @@ extension CGVector {
3131 dx = dx / norm
3232 dy = dy / norm
3333 }
34+
3435}
3536
3637extension CGFloat: DoubleValuable {
@@ -42,19 +43,28 @@ extension CGFloat: DoubleValuable {
4243 static func fromDouble(double: Double) -> CGFloat {
4344 return CGFloat(double)
4445 }
46+
4547}
4648
4749extension CGFloat: Numeric {}
4850
4951extension CGRect {
5052 var x: CGFloat {
51− get { return origin.x }
52− set { origin.x = newValue }
53+ get {
54+ return origin.x
55+ }
56+ set {
57+ origin.x = newValue
58+ }
5359 }
5460
5561 var y: CGFloat {
56− get { return origin.y }
57− set { origin.y = newValue }
62+ get {
63+ return origin.y
64+ }
65+ set {
66+ origin.y = newValue
67+ }
5868 }
5969}
6070
@@ -66,41 +76,65 @@ extension CGSize {
6676
6777extension UIView {
6878 var origin: CGPoint {
69− get { return frame.origin }
70− set { frame.origin = newValue }
79+ get {
80+ return frame.origin
81+ }
82+ set {
83+ frame.origin = newValue
84+ }
7185 }
7286
7387 var size: CGSize {
74− get { return frame.size }
75− set { frame.size = newValue }
88+ get {
89+ return frame.size
90+ }
91+ set {
92+ frame.size = newValue
93+ }
7694 }
7795
7896 var width: CGFloat {
79− get { return frame.width }
80− set { frame.size = CGSize(width: newValue, height: height) }
97+ get {
98+ return frame.width
99+ }
100+ set {
101+ frame.size = CGSize(width: newValue, height: height)
102+ }
81103 }
82104
83105 var height: CGFloat {
84− get { return frame.height }
85− set { frame.size = CGSize(width: width, height: newValue) }
106+ get {
107+ return frame.height
108+ }
109+ set {
110+ frame.size = CGSize(width: width, height: newValue)
111+ }
86112 }
87113
88114 var middle: CGPoint {
89115 get {
90− return CGPoint(x: origin.x + width/2, y: origin.y + height/2)
116+ return CGPoint(x: origin.x + width / 2, y: origin.y + height / 2)
91117 }
92118 set {
93− origin = CGPoint(x: newValue.x - width/2, y: newValue.y - height/2)
119+ origin = CGPoint(x: newValue.x - width / 2, y: newValue.y - height / 2)
94120 }
95121 }
96122
97123 var midX: CGFloat {
98− get { return middle.x }
99− set { middle = CGPoint(x: newValue, y: midY) }
124+ get {
125+ return middle.x
126+ }
127+ set {
128+ middle = CGPoint(x: newValue, y: midY)
129+ }
100130 }
101131
102132 var midY: CGFloat {
103− get { return middle.y }
104− set { middle = CGPoint(x: midX, y: newValue) }
133+ get {
134+ return middle.y
135+ }
136+ set {
137+ middle = CGPoint(x: midX, y: newValue)
138+ }
105139 }
106140}
PixelImage/ViewController.swift+9−8
@@ -1,4 +1,3 @@
1−
21import UIKit
32
43class ViewController: UIViewController, MetaballDataSource {
@@ -33,7 +32,7 @@ class ViewController: UIViewController, MetaballDataSource {
3332 metaballGraph.addEdge(2, 3)
3433
3534 let border = 20
36− let metaballViewFrame = CGRect(x: border/2, y: border/2, width: width, height: height)
35+ let metaballViewFrame = CGRect(x: border / 2, y: border / 2, width: width, height: height)
3736 renderer = MetalMetaballRenderer(dataSource: self, frame: metaballViewFrame)
3837 metaballView = renderer.targetView
3938
@@ -61,11 +60,12 @@ class ViewController: UIViewController, MetaballDataSource {
6160
6261 func animateEdge(i: Int, _ j: Int, fadeIn: Bool) {
6362 let parameters = EdgeAnimationParameters(startDate: NSDate(), duration: edgeAnimationDuration, fadeIn: fadeIn, i: i, j: j)
64− NSTimer.scheduledTimerWithTimeInterval(1.0/60.0, target: self, selector: "animateEdgeWithTimer:", userInfo: parameters, repeats: true)
63+ NSTimer.scheduledTimerWithTimeInterval(1.0 / 60.0, target: self, selector: "animateEdgeWithTimer:", userInfo: parameters, repeats: true)
6564 }
6665
6766 func animateEdge(withTimer timer: NSTimer) {
68− let (animationStart, duration, fadeIn, i, j) = (timer.userInfo as! EdgeAnimationParameters).unpack()
67+ guard let userInfo = timer.userInfo as? EdgeAnimationParameters else { preconditionFailure() }
68+ let (animationStart, duration, fadeIn, i, j) = (userInfo).unpack()
6969
7070 let now = NSDate()
7171 var timeElapsed = Float(now.timeIntervalSinceDate(animationStart))
@@ -89,9 +89,9 @@ class ViewController: UIViewController, MetaballDataSource {
8989
9090 if selectedMetaball == nil {
9191 for metaball in metaballs where
92− abs(metaball.midX - location.x) < 50 && abs(metaball.midY - location.y) < 50 {
93− selectedMetaball = metaball
94− break
92+ abs(metaball.midX - location.x) < 50 && abs(metaball.midY - location.y) < 50 {
93+ selectedMetaball = metaball
94+ break
9595 }
9696 }
9797
@@ -105,6 +105,7 @@ class ViewController: UIViewController, MetaballDataSource {
105105 selectedMetaball = nil
106106 }
107107 }
108+
108109}
109110
110111class Metaball: UIView {
@@ -114,7 +115,7 @@ class Metaball: UIView {
114115
115116 size = CGSize(50)
116117 middle = position
117− backgroundColor = UIColor(red: 0.1306, green: 0.7522, blue: 0.0307, alpha: 0.3)
118+ backgroundColor = UIColor(randomFlatColorOfShadeStyle: .Light)
118119 }
119120
120121 required init?(coder aDecoder: NSCoder) {
PixelImage/shaders.metal+58−14
@@ -3,36 +3,61 @@
33
44using namespace metal;
55
6−kernel void drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]], constant float *edgesBuffer[[buffer(1)]], constant float *metaballBuffer[[buffer(0)]], uint2 gid[[thread_position_in_grid]]) {
7−
8− char numberOfMetaballs = metaballBuffer[0];
9− char metaballArraySize = numberOfMetaballs * 2 + 1;
6+struct metaballData {
7+ float x;
8+ float y;
9+ float r;
10+ float g;
11+ float b;
12+};
13+
14+kernel void
15+ drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)]],
16+ constant float *edgesBuffer[[buffer(1)]],
17+ constant metaballData *metaballBuffer[[buffer(0)]],
18+ uint2 gid[[thread_position_in_grid]]) {
19+
20+ char numberOfMetaballs = metaballBuffer[0].x - 1;
1021
1122 float sum = 0;
23+ float3 colorSum = float3(0, 0, 0);
24+ float colorAccumulation = 0;
25+ float3 colorSumLink = float3(0, 0, 0);
1226
1327 char x, y;
1428
1529 float metaballDistances[10];
1630 float2 metaballDirections[10];
31+ float3 metaballColors[10];
1732
18− for (y = 1, x = 0; y < metaballArraySize; y += 2, x += 1) {
19− float2 metaball = float2(metaballBuffer[y], metaballBuffer[y + 1]);
20− float2 vector = float2(metaball.x - gid.x, metaball.y - gid.y);
33+ for (x = 1; x <= numberOfMetaballs; x += 1) {
34+ metaballData metaball = metaballBuffer[x];
35+ float2 metaballPosition = float2(metaball.x, metaball.y);
36+ float2 vector
37+ = float2(metaballPosition.x - gid.x, metaballPosition.y - gid.y);
2138 float squaredDistance = dot(vector, vector);
2239 float realDistance = sqrt(squaredDistance);
23− metaballDistances[x] = squaredDistance;
24− metaballDirections[x] = vector/realDistance;
40+ metaballDistances[x - 1] = squaredDistance;
41+ metaballDirections[x - 1] = vector / realDistance;
42+ metaballColors[x - 1] = float3(metaball.r, metaball.g, metaball.b);
2543 }
2644
2745 float bendClose = 0.0;
2846 float bendCloseCount = 0.0;
47+ float ball = 0.0;
2948
3049 for (x = 0; x < numberOfMetaballs; x += 1) {
31− float value1 = 2048 / (metaballDistances[x] + 1);
50+ float distance1 = metaballDistances[x];
51+ float value1 = 2048 / (distance1 + 1);
3252 float2 direction1 = metaballDirections[x];
3353
54+ float colorContribution = 1 / distance1;
55+ colorAccumulation += colorContribution;
56+ colorSum += metaballColors[x] * colorContribution;
57+
3458 for (y = x + 1; y < numberOfMetaballs; y += 1) {
35− float value2 = 2048 / (metaballDistances[y] + 1);
59+ float distance2 = metaballDistances[y];
60+ float value2 = 2048 / (distance2 + 1);
3661 float2 direction2 = metaballDirections[y];
3762
3863 float v = value1 + value2;
@@ -51,14 +76,25 @@ kernel void drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)
5176 bendCloseCount += 1.0;
5277 }
5378
54− sum += step(0.5, weightedValue + weightedLink);
79+ ball += step(0.4, weightedValue);
80+
81+ float metaballValue = step(0.5, weightedValue + weightedLink);
82+
83+ if (metaballValue > 0.0) {
84+ colorSumLink = float3(0.0, 0.0, 0.0);
85+ colorSumLink += metaballColors[x] / distance1;
86+ colorSumLink += metaballColors[y] / distance2;
87+ colorSumLink /= (1 / distance1) + (1 / distance2);
88+ }
89+
90+ sum += metaballValue;
5591 }
5692 }
5793
5894 bendClose = bendClose / bendCloseCount;
5995
6096 if (bendClose != 0.0) {
61− bendClose = (bendClose - 0.4) * (1/(0.6-0.4));
97+ bendClose = (bendClose - 0.4) * (1 / (0.6 - 0.4));
6298 } else {
6399 bendClose = 0.0;
64100 }
@@ -67,5 +103,13 @@ kernel void drawMetaballs(texture2d<float, access::write> outTexture[[texture(0)
67103 result += bendClose * 10;
68104 result = clamp(result, 0.0, 1.0);
69105
70− outTexture.write(float4(result, result / 2, 0.0, 1), gid);
106+ colorSum = colorSum / colorAccumulation;
107+
108+ if (result > 0.0 && ball < 1.0) {
109+ colorSum = colorSumLink;
110+ }
111+
112+ colorSum *= result;
113+
114+ outTexture.write(float4(colorSum.bgr, 1), gid);
71115}
Podfilenew file+6
@@ -0,0 +1,6 @@
1+platform :ios, '9.2'
2+use_frameworks!
3+
4+target 'PixelImage' do
5+ pod 'ChameleonFramework/Swift'
6+end
Podfile.locknew file+12
@@ -0,0 +1,12 @@
1+PODS:
2+ - ChameleonFramework/Default (2.1.0)
3+ - ChameleonFramework/Swift (2.1.0):
4+ - ChameleonFramework/Default
5+
6+DEPENDENCIES:
7+ - ChameleonFramework/Swift
8+
9+SPEC CHECKSUMS:
10+ ChameleonFramework: d21a3cc247abfe5e37609a283a8238b03575cf64
11+
12+COCOAPODS: 0.39.0