Repository: rnkyr/metaballs
Branch: master
Commit: 7cb846388d14
Files: 22
Total size: 166.6 KB
Directory structure:
gitextract_r062yy9l/
├── .gitignore
├── .swiftlint.yml
├── LICENSE.md
├── README.md
├── metaballs/
│ ├── AppDelegate.swift
│ ├── Base.lproj/
│ │ └── Main.storyboard
│ ├── ConfigViewController.swift
│ ├── Info.plist
│ ├── LayeredButton.swift
│ ├── MetaballsView/
│ │ ├── Blob.swift
│ │ ├── CGPoint+Trigonometry.swift
│ │ ├── Metaball.swift
│ │ └── MetaballsView.swift
│ ├── Resources/
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ └── Base.lproj/
│ │ └── LaunchScreen.storyboard
│ └── ViewController.swift
├── metaballs.xcodeproj/
│ ├── project.pbxproj
│ └── project.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
└── resources/
├── blob.sketch
└── rorschach.ai
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
# Build generated
build/
DerivedData/
# Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
# Other
*.moved-aside
*.xcuserstate
# Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# Playgrounds
timeline.xctimeline
playground.xcworkspace
# CocoaPods
Pods/
# Carthage
Carthage/Build
Carthage/Checkouts
# development
logs/*
tmp/*
# AppCode
.idea/
# fastlane
fastlane/report.xml
fastlane/README.md
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
## GitLab CI
.bundle/
vendor/
================================================
FILE: .swiftlint.yml
================================================
disabled_rules: # rule identifiers to exclude from running
- notification_center_detachment
- identifier_name
- unused_setter_value # complains about overriden setter value
- type_name # introduced with "CS_Assignment" prefix
opt_in_rules: # some rules are only opt-in
- attributes
- closure_end_indentation
- closure_spacing
- contains_over_first_not_nil
- empty_count
- empty_string
- explicit_init
- first_where
- joined_default_parameter
- literal_expression_end_indentation
- unneeded_parentheses_in_closure_argument
- operator_usage_whitespace
- private_action
excluded: # paths to ignore during linting. Takes precedence over `included`.
- Carthage
- Pods
- vendor/bundle
- XCTemplates
custom_rules:
rx_swift_debug:
included: ".*\\.swift"
name: "RxSwift debug() operator"
regex: "(\\.debug\\(\\))"
message: "Debug operation"
comma: error
force_cast: warning
force_try: warning
return_arrow_whitespace: error
trailing_whitespace:
ignores_empty_lines: true
line_length:
warning: 250
error: 350
type_body_length:
warning: 300
error: 400
cyclomatic_complexity:
ignores_case_statements: true
file_length:
warning: 500
error: 1000
function_body_length:
warning: 80
error: 100
warning_threshold: 30
reporter: "xcode"
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2020 Roman Kyrylenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center"><img src="resources/header.png" width="70%" height="auto"></p>
# Blob effect in iOS
  
The project demonstrates implementation of a 2D blob effect (or [metaballs](https://en.wikipedia.org/wiki/Metaballs)) written using UIKit.
Project contains example setup and separated `MetaballsView` with the implementation of an effect.
<img src="resources/example.gif" width="100%" height="auto">
## Overview
Mostly inspired by [these](http://paperjs.org/examples/meta-balls/) [two](https://varun.ca/metaballs/) articles I implemented the effect utilizing Bezier curves with the help of UIKit. During the R&D process, I tried a couple of approaches (like building pixel-by-pixel, [marching squares](http://jamie-wong.com/2014/08/19/metaballs-and-marching-squares/) and some more), but due to the nature of the rendering process on a mobile platform selected one is the most efficient. Each time a user move a ball (changes its position), the host view recalculates curves for each pair of balls.
## Implementation details
There's a host view class `MetaballsView` which is responsible for building, configuring and interaction.
It holds a list of interactable `UIView`s (yep, it holds it twice due to the hierarchy, but in that case, it doesn't create any retain issues).
`blobLayers` is a two-dimensional array of `CAShapeLayer`s that used to display blob connection between balls. To be able to directly access layer between i-th and j-th balls, the array is made two-dimensional so it could be accessed like `blobLayers[i][j]`. Due to mirroring, `blobLayers[j][i]` should be drawn the same as `blobLayers[i][j]`, but I haven't optimized it yet, just ignoring the path.
The `Blob` entity calculates and holds information regarding tangent points and handle curves points.
The `Metaball` entity holds actual ball information and utilizes `Blob` to build a path with another `Metaball` and translate it into `UIBezierPath`. Basically, these two classes could be used to implement the same effect independently from UIKit.
The following image demonstrates the principle behind math.
<p align="center"><img src="resources/curves.png" width="70%" height="auto"></p>
There're two bezier curves: from point `p1` to `p3` with control points in `h1` and `h3`, and inversed one from points `p2` to `p4` through `h2`, `h4`. Refer to [debugging](https://github.com/rnkyr/metaballs/tree/debugging-bezier) branch to view details.
For pixel-by-pixel implementation refer to [this](https://github.com/rnkyr/metaballs/tree/point-by-point) branch.
================================================
FILE: metaballs/AppDelegate.swift
================================================
//
// AppDelegate.swift
// metaballs
//
// Created by Roman Kyrylenko on 03.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}
================================================
FILE: metaballs/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="metaballs" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC" customClass="MetaballsView" customModule="metaballs" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zQD-on-b51" customClass="LayeredButton" customModule="metaballs" customModuleProvider="target">
<rect key="frame" x="126" y="787" width="162" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="6Ie-hd-XSI"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="162" id="wNx-J9-God"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
<state key="normal" title="Configuration">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<segue destination="TF5-xg-DRc" kind="presentation" modalPresentationStyle="overFullScreen" id="raB-Xk-xfL"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="zQD-on-b51" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="vPC-UF-RRu"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="zQD-on-b51" secondAttribute="bottom" constant="25" id="xEH-uF-qX6"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="139" y="127"/>
</scene>
<!--Config View Controller-->
<scene sceneID="9EF-uP-GEj">
<objects>
<viewController id="TF5-xg-DRc" customClass="ConfigViewController" customModule="metaballs" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="hoE-Zs-dlb">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JZm-1F-Qw9">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="dPE-4s-BIb">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bN3-AP-hre">
<rect key="frame" x="0.0" y="0.0" width="174" height="768"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" id="SIO-QJ-C1R">
<rect key="frame" x="0.0" y="0.0" width="174" height="768"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<vibrancyEffect>
<blurEffect style="light"/>
</vibrancyEffect>
</visualEffectView>
</subviews>
</view>
<blurEffect style="systemUltraThinMaterial"/>
</visualEffectView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EG4-hv-3kU">
<rect key="frame" x="50" y="247.5" width="314" height="401.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GaW-dR-ITr">
<rect key="frame" x="25" y="25" width="264" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="2.5" minValue="0.0" maxValue="4" translatesAutoresizingMaskIntoConstraints="NO" id="F3F-1Q-VUD">
<rect key="frame" x="22" y="65" width="270" height="31"/>
<color key="minimumTrackTintColor" red="0.16078431372549018" green="0.094117647058823528" blue="0.38431372549019605" alpha="1" colorSpace="calibratedRGB"/>
<color key="thumbTintColor" red="0.16078431369999999" green="0.094117647060000004" blue="0.38431372549999998" alpha="1" colorSpace="calibratedRGB"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="value">
<real key="value" value="1.5"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="minimumValue">
<real key="value" value="0.0"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="number" keyPath="maximumValue">
<real key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="handleSizeChanged" destination="TF5-xg-DRc" eventType="valueChanged" id="FE7-pN-1nh"/>
</connections>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Wi-Cp-W8u">
<rect key="frame" x="25" y="119" width="264" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="Hqi-au-lkN">
<rect key="frame" x="22" y="158.5" width="270" height="31"/>
<color key="minimumTrackTintColor" red="0.16078431372549018" green="0.094117647058823528" blue="0.38431372549019605" alpha="1" colorSpace="calibratedRGB"/>
<color key="thumbTintColor" red="0.16078431369999999" green="0.094117647060000004" blue="0.38431372549999998" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="curvatureChanged" destination="TF5-xg-DRc" eventType="valueChanged" id="d9a-Tf-bcf"/>
</connections>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cfT-zW-VKu">
<rect key="frame" x="25" y="212.5" width="264" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="4" minValue="2" maxValue="10" translatesAutoresizingMaskIntoConstraints="NO" id="c0e-nv-U6w">
<rect key="frame" x="22" y="252" width="270" height="31"/>
<color key="minimumTrackTintColor" red="0.16078431369999999" green="0.094117647060000004" blue="0.38431372549999998" alpha="1" colorSpace="calibratedRGB"/>
<color key="thumbTintColor" red="0.16078431369999999" green="0.094117647060000004" blue="0.38431372549999998" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="numberOfBallsChanged" destination="TF5-xg-DRc" eventType="valueChanged" id="dnZ-4M-i0p"/>
</connections>
</slider>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="37B-Oi-SVK" customClass="LayeredButton" customModule="metaballs" customModuleProvider="target">
<rect key="frame" x="167" y="324.5" width="127" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="pqH-uh-atM"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
<state key="normal" title="Apply">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="saveButtonAction" destination="TF5-xg-DRc" eventType="touchUpInside" id="oj5-PG-Gui"/>
</connections>
</button>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="o0v-xr-tJ8">
<rect key="frame" x="20" y="324.5" width="127" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="Def-ih-JrD"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
<state key="normal" title="Cancel">
<color key="titleColor" red="0.97647058823529409" green="0.11372549019607843" blue="0.29803921568627451" alpha="1" colorSpace="calibratedRGB"/>
</state>
<connections>
<action selector="cancelButtonAction" destination="TF5-xg-DRc" eventType="touchUpInside" id="avs-DS-2LM"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.94117647058823528" green="0.93725490196078431" blue="0.94509803921568625" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="Hqi-au-lkN" secondAttribute="trailing" constant="24" id="0jQ-ah-m0A"/>
<constraint firstItem="o0v-xr-tJ8" firstAttribute="leading" secondItem="EG4-hv-3kU" secondAttribute="leading" constant="20" id="1Sz-Td-pp0"/>
<constraint firstItem="c0e-nv-U6w" firstAttribute="top" secondItem="cfT-zW-VKu" secondAttribute="bottom" constant="19" id="4xU-Fn-o5V"/>
<constraint firstItem="GaW-dR-ITr" firstAttribute="leading" secondItem="EG4-hv-3kU" secondAttribute="leading" constant="25" id="6YM-GQ-fZe"/>
<constraint firstItem="cfT-zW-VKu" firstAttribute="trailing" secondItem="0Wi-Cp-W8u" secondAttribute="trailing" id="7vX-Ya-5uw"/>
<constraint firstItem="GaW-dR-ITr" firstAttribute="top" secondItem="EG4-hv-3kU" secondAttribute="top" constant="25" id="8uM-Uw-jcU"/>
<constraint firstItem="cfT-zW-VKu" firstAttribute="leading" secondItem="0Wi-Cp-W8u" secondAttribute="leading" id="FTP-1y-qPP"/>
<constraint firstAttribute="bottom" secondItem="37B-Oi-SVK" secondAttribute="bottom" constant="27" id="GiD-MR-ZgY"/>
<constraint firstItem="37B-Oi-SVK" firstAttribute="leading" secondItem="o0v-xr-tJ8" secondAttribute="trailing" constant="20" id="J4C-f2-gsu"/>
<constraint firstItem="cfT-zW-VKu" firstAttribute="top" secondItem="Hqi-au-lkN" secondAttribute="bottom" constant="24" id="LqA-je-D0j"/>
<constraint firstItem="37B-Oi-SVK" firstAttribute="width" secondItem="o0v-xr-tJ8" secondAttribute="width" id="Mb6-kh-ZTE"/>
<constraint firstAttribute="trailing" secondItem="GaW-dR-ITr" secondAttribute="trailing" constant="25" id="NI1-Dx-cto"/>
<constraint firstItem="F3F-1Q-VUD" firstAttribute="top" secondItem="GaW-dR-ITr" secondAttribute="bottom" constant="19" id="O30-1n-gpK"/>
<constraint firstItem="o0v-xr-tJ8" firstAttribute="centerY" secondItem="37B-Oi-SVK" secondAttribute="centerY" id="Og3-L4-fxr"/>
<constraint firstItem="Hqi-au-lkN" firstAttribute="top" secondItem="0Wi-Cp-W8u" secondAttribute="bottom" constant="19" id="Rcc-Nx-xEo"/>
<constraint firstItem="c0e-nv-U6w" firstAttribute="trailing" secondItem="Hqi-au-lkN" secondAttribute="trailing" id="RrE-gw-xah"/>
<constraint firstItem="0Wi-Cp-W8u" firstAttribute="leading" secondItem="EG4-hv-3kU" secondAttribute="leading" constant="25" id="Rxp-6J-IX6"/>
<constraint firstAttribute="trailing" secondItem="0Wi-Cp-W8u" secondAttribute="trailing" constant="25" id="TJU-KY-hfL"/>
<constraint firstItem="37B-Oi-SVK" firstAttribute="top" secondItem="c0e-nv-U6w" secondAttribute="bottom" constant="42.5" id="kBa-NQ-12R"/>
<constraint firstItem="0Wi-Cp-W8u" firstAttribute="top" secondItem="F3F-1Q-VUD" secondAttribute="bottom" constant="24" id="n8Q-gg-VUE"/>
<constraint firstAttribute="trailing" secondItem="F3F-1Q-VUD" secondAttribute="trailing" constant="24" id="owP-ln-XUj"/>
<constraint firstAttribute="trailing" secondItem="37B-Oi-SVK" secondAttribute="trailing" constant="20" id="pRY-3d-OdR"/>
<constraint firstItem="c0e-nv-U6w" firstAttribute="leading" secondItem="Hqi-au-lkN" secondAttribute="leading" id="sYd-4A-VMi"/>
<constraint firstItem="Hqi-au-lkN" firstAttribute="leading" secondItem="EG4-hv-3kU" secondAttribute="leading" constant="24" id="xfF-Ya-iDs"/>
<constraint firstItem="F3F-1Q-VUD" firstAttribute="leading" secondItem="EG4-hv-3kU" secondAttribute="leading" constant="24" id="yv6-O9-zyQ"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="20"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="JZm-1F-Qw9" secondAttribute="trailing" id="6DD-kf-5I2"/>
<constraint firstItem="JZm-1F-Qw9" firstAttribute="top" secondItem="hoE-Zs-dlb" secondAttribute="top" id="BfV-Jn-9kw"/>
<constraint firstItem="EG4-hv-3kU" firstAttribute="leading" secondItem="BmZ-Ko-uKF" secondAttribute="leading" constant="50" id="CI2-YK-83z"/>
<constraint firstItem="JZm-1F-Qw9" firstAttribute="leading" secondItem="hoE-Zs-dlb" secondAttribute="leading" id="CWO-Uq-tnH"/>
<constraint firstAttribute="bottom" secondItem="JZm-1F-Qw9" secondAttribute="bottom" id="apL-TW-8wJ"/>
<constraint firstItem="EG4-hv-3kU" firstAttribute="centerY" secondItem="hoE-Zs-dlb" secondAttribute="centerY" id="cLw-yw-Q16"/>
<constraint firstItem="BmZ-Ko-uKF" firstAttribute="trailing" secondItem="EG4-hv-3kU" secondAttribute="trailing" constant="50" id="jgo-bO-gB7"/>
</constraints>
<viewLayoutGuide key="safeArea" id="BmZ-Ko-uKF"/>
</view>
<navigationItem key="navigationItem" id="Dv1-Ht-JYw"/>
<connections>
<outlet property="curvatureLabel" destination="0Wi-Cp-W8u" id="R73-ZR-xZq"/>
<outlet property="curvatureSlider" destination="Hqi-au-lkN" id="vIQ-zv-b7F"/>
<outlet property="handleSizeLabel" destination="GaW-dR-ITr" id="SBy-2P-3I1"/>
<outlet property="handleSizeSlider" destination="F3F-1Q-VUD" id="Dyb-lz-WVo"/>
<outlet property="numberOfBallsLabel" destination="cfT-zW-VKu" id="n7O-P8-RRH"/>
<outlet property="numberOfBallsSlider" destination="c0e-nv-U6w" id="Tfc-pY-9fe"/>
</connections>
</viewController>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JM6-27-JVw">
<rect key="frame" x="0.0" y="0.0" width="264" height="20.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" id="1Nz-FV-TQm">
<rect key="frame" x="0.0" y="0.0" width="270" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="minimumTrackTintColor" red="0.16078431369999999" green="0.094117647060000004" blue="0.38431372549999998" alpha="1" colorSpace="calibratedRGB"/>
<color key="thumbTintColor" red="0.16078431369999999" green="0.094117647060000004" blue="0.38431372549999998" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="curvatureChanged" destination="TF5-xg-DRc" eventType="valueChanged" id="gRY-FY-oOC"/>
</connections>
</slider>
<placeholder placeholderIdentifier="IBFirstResponder" id="Wd9-1z-s78" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="957.97101449275374" y="126.5625"/>
</scene>
</scenes>
</document>
================================================
FILE: metaballs/ConfigViewController.swift
================================================
//
// ConfigViewController.swift
// metaballs
//
// Created by Roman Kyrylenko on 10.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import UIKit
final class ConfigViewController: UIViewController {
@IBOutlet private var handleSizeSlider: UISlider!
@IBOutlet private var handleSizeLabel: UILabel!
@IBOutlet private var curvatureSlider: UISlider!
@IBOutlet private var curvatureLabel: UILabel!
@IBOutlet private var numberOfBallsLabel: UILabel!
@IBOutlet private var numberOfBallsSlider: UISlider!
private var handleSize: Float = 0 {
didSet { handleSizeLabel.text = "Handle size: \((handleSize * 10).rounded() / 10)" }
}
private var curvature: Float = 0 {
didSet { curvatureLabel.text = "Curvature: \((curvature * 10).rounded() / 10)" }
}
private var numberOfBalls: Int = 0 {
didSet { numberOfBallsLabel.text = "Balls: \(numberOfBalls)" }
}
private var config: MetaballsView.Config!
private var callback: ((MetaballsView.Config) -> Void)!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
curvatureSlider.value = Float(config.curvature)
curvature = Float(config.curvature)
handleSizeSlider.value = Float(config.handleSize)
handleSize = Float(config.handleSize)
numberOfBalls = config.numberOfBalls
numberOfBallsSlider.value = Float(config.numberOfBalls)
}
func setup(with config: MetaballsView.Config, callback: @escaping (MetaballsView.Config) -> Void) {
self.config = config
self.callback = callback
}
@IBAction private func curvatureChanged() {
curvature = curvatureSlider.value
}
@IBAction private func handleSizeChanged() {
handleSize = handleSizeSlider.value
}
@IBAction private func numberOfBallsChanged() {
numberOfBalls = Int(numberOfBallsSlider.value)
}
@IBAction private func cancelButtonAction() {
dismiss(animated: true, completion: nil)
}
@IBAction private func saveButtonAction() {
callback(MetaballsView.Config(
handleSize: CGFloat(handleSizeSlider.value),
curvature: CGFloat(curvatureSlider.value),
ballColor: config.ballColor,
numberOfBalls: Int(numberOfBallsSlider.value)
))
dismiss(animated: true, completion: nil)
}
}
================================================
FILE: metaballs/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
================================================
FILE: metaballs/LayeredButton.swift
================================================
//
// LayeredButton.swift
// metaballs
//
// Created by Roman Kyrylenko on 23.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import UIKit
final class LayeredButton: UIButton {
private let foregroundLayer = CALayer()
private let backgroundLayer1 = CALayer()
private let backgroundLayer2 = CALayer()
override var bounds: CGRect {
didSet { updateFrames() }
}
override var frame: CGRect {
didSet { updateFrames() }
}
override init(frame: CGRect) {
super.init(frame: frame)
layout()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
layout()
}
private func layout() {
backgroundColor = UIColor.clear
[backgroundLayer1, backgroundLayer2, foregroundLayer].forEach { layer in
self.layer.addSublayer(layer)
layer.cornerRadius = 21.5
layer.masksToBounds = true
}
foregroundLayer.backgroundColor = UIColor(red: 0.16, green: 0.09, blue: 0.38, alpha: 1.00).cgColor
backgroundLayer1.backgroundColor = UIColor(red: 0.98, green: 0.11, blue: 0.30, alpha: 1.00).cgColor
backgroundLayer2.backgroundColor = UIColor(red: 0.01, green: 0.80, blue: 0.97, alpha: 1.00).cgColor
}
private func updateFrames() {
let insets = CGSize(width: 5.5, height: 3)
foregroundLayer.frame = bounds.inset(
by: UIEdgeInsets(top: insets.height, left: insets.width, bottom: insets.height, right: insets.width)
)
backgroundLayer1.frame = bounds.inset(
by: UIEdgeInsets(top: 0, left: 2 * insets.width, bottom: 2 * insets.height, right: 0)
)
backgroundLayer2.frame = bounds.inset(
by: UIEdgeInsets(top: 2 * insets.height, left: 0, bottom: 0, right: 2 * insets.width)
)
}
}
================================================
FILE: metaballs/MetaballsView/Blob.swift
================================================
//
// Blob.swift
// metaballs
//
// Created by Roman Kyrylenko on 10.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import CoreGraphics
struct Blob {
// handle curve control points
let h1: CGPoint, h2: CGPoint, h3: CGPoint, h4: CGPoint
// tangent points
let p1: CGPoint, p2: CGPoint, p3: CGPoint, p4: CGPoint
init(
handleSize: CGFloat, curvature: CGFloat,
metaball1: Metaball, metaball2: Metaball,
distance: CGFloat,
u1: CGFloat, u2: CGFloat
) {
let angleBetweenCenters = metaball2.position.angle(with: metaball1.position)
let maxSpread = acos((metaball1.radius - metaball2.radius) / distance)
let pi = CGFloat.pi
// calculating tangent points
let angle1 = angleBetweenCenters + u1 + (maxSpread - u1) * curvature
let angle2 = angleBetweenCenters - u1 - (maxSpread - u1) * curvature
let angle3 = angleBetweenCenters + pi - u2 - (pi - u2 - maxSpread) * curvature
let angle4 = angleBetweenCenters - pi + u2 + (pi - u2 - maxSpread) * curvature
p1 = metaball1.position.vector(with: angle1, and: metaball1.radius)
p2 = metaball1.position.vector(with: angle2, and: metaball1.radius)
p3 = metaball2.position.vector(with: angle3, and: metaball2.radius)
p4 = metaball2.position.vector(with: angle4, and: metaball2.radius)
let totalRadius = metaball1.radius + metaball2.radius
let d2Base = min(curvature * handleSize, p1.distance(to: p3) / totalRadius)
let d2 = d2Base * min(1, distance * 2 / totalRadius)
// find length of the handles
let r1 = metaball1.radius * d2
let r2 = metaball2.radius * d2
let halfPi: CGFloat = CGFloat.pi / 2
// calculate control points
h1 = p1.vector(with: angle1 - halfPi, and: r1)
h2 = p2.vector(with: angle2 + halfPi, and: r1)
h3 = p3.vector(with: angle3 + halfPi, and: r2)
h4 = p4.vector(with: angle4 - halfPi, and: r2)
}
}
================================================
FILE: metaballs/MetaballsView/CGPoint+Trigonometry.swift
================================================
//
// CGPoint+Trigonometry.swift
// metaballs
//
// Created by Roman Kyrylenko on 10.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import CoreGraphics
extension CGPoint {
func distance(to p2: CGPoint) -> CGFloat {
return pow(pow(x - p2.x, 2) + pow(y - p2.y, 2), 0.5)
}
func angle(with p2: CGPoint) -> CGFloat {
return atan2(y - p2.y, x - p2.x)
}
func vector(with angle: CGFloat, and radius: CGFloat) -> CGPoint {
return CGPoint(
x: x + radius * cos(angle),
y: y + radius * sin(angle)
)
}
}
================================================
FILE: metaballs/MetaballsView/Metaball.swift
================================================
//
// Metaball.swift
// metaballs
//
// Created by Roman Kyrylenko on 10.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import Foundation
import UIKit.UIBezierPath
struct Metaball: Equatable {
let handleSize: CGFloat
let curvature: CGFloat
let radius: CGFloat
let position: CGPoint
func blobPath(with metaball: Metaball) -> UIBezierPath? {
let distance = position.distance(to: metaball.position)
// this coefficient should depend on the size of balls
let maxDistance = radius + metaball.radius * 2.4
if distance > maxDistance || distance <= abs(radius - metaball.radius) {
return nil
}
// angles between center and circles' overlapping point
let u1: CGFloat, u2: CGFloat
if distance < radius + metaball.radius {
u1 = acos(
(pow(radius, 2) + pow(distance, 2) - pow(metaball.radius, 2)) / (2 * radius * distance)
)
u2 = acos(
(pow(metaball.radius, 2) + pow(distance, 2) - pow(radius, 2)) / (2 * metaball.radius * distance)
)
} else {
u1 = 0
u2 = 0
}
return path(
for: Blob(
handleSize: handleSize, curvature: curvature,
metaball1: self, metaball2: metaball, distance: distance, u1: u1, u2: u2
),
c1: position,
c2: metaball.position
)
}
private func path(for blob: Blob, c1: CGPoint, c2: CGPoint) -> UIBezierPath {
let path = UIBezierPath()
path.move(to: c1)
path.addLine(to: blob.p1)
path.addCurve(to: blob.p3, controlPoint1: blob.h1, controlPoint2: blob.h3)
path.addLine(to: c2)
path.addLine(to: blob.p4)
path.addCurve(to: blob.p2, controlPoint1: blob.h4, controlPoint2: blob.h2)
path.addLine(to: c1)
return path
}
}
================================================
FILE: metaballs/MetaballsView/MetaballsView.swift
================================================
//
// MetaballsView.swift
// metaballs
//
// Created by Roman Kyrylenko on 10.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import UIKit
public final class MetaballsView: UIView {
public struct Config {
public let handleSize: CGFloat
public let curvature: CGFloat
public let ballColor: UIColor
public let numberOfBalls: Int
public let frameForBall: (Int) -> CGRect
public init(
handleSize: CGFloat = 2.4,
curvature: CGFloat = 0.5,
ballColor: UIColor = UIColor(red: 0.16, green: 0.09, blue: 0.38, alpha: 1.00),
numberOfBalls: Int = 4,
frameForBall: @escaping (Int) -> CGRect = { _ in
let side = CGFloat.random(in: 75...175)
return CGRect(x: .random(in: 43...201), y: .random(in: 173...548), width: side, height: side)
}
) {
self.handleSize = handleSize
self.curvature = curvature
self.ballColor = ballColor
self.numberOfBalls = numberOfBalls
self.frameForBall = frameForBall
}
}
public var config = Config() {
didSet { didChangeConfig(from: oldValue, to: config) }
}
private var metaballs: [UIView] = []
private var blobLayers: [[CAShapeLayer]] = []
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initialize()
}
private func initialize() {
layoutBalls()
backgroundColor = .white
rebuildPaths()
}
private func layoutBalls() {
metaballs.forEach {
$0.removeFromSuperview()
}
metaballs = (0..<config.numberOfBalls).map { index in UIView(frame: config.frameForBall(index)) }
metaballs.forEach { ball in
ball.backgroundColor = config.ballColor
ball.layer.cornerRadius = ball.frame.width / 2
addSubview(ball)
ball.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan)))
}
blobLayers.forEach { layers in
layers.forEach {
$0.removeFromSuperlayer()
}
}
blobLayers = (0..<config.numberOfBalls).map { _ in
let innerList: [CAShapeLayer] = (0..<config.numberOfBalls).map { _ in
let layer = CAShapeLayer()
layer.fillColor = config.ballColor.cgColor
self.layer.addSublayer(layer)
return layer
}
return innerList
}
}
private func didChangeConfig(from oldValue: Config, to newValue: Config) {
var shouldRebuild = false
if oldValue.numberOfBalls != newValue.numberOfBalls {
layoutBalls()
shouldRebuild = true
}
if oldValue.handleSize != newValue.handleSize || oldValue.curvature != newValue.curvature {
shouldRebuild = true
}
if oldValue.ballColor != newValue.ballColor {
metaballs.forEach {
$0.backgroundColor = newValue.ballColor
}
blobLayers.forEach {
$0.forEach {
$0.fillColor = newValue.ballColor.cgColor
}
}
}
if shouldRebuild {
rebuildPaths()
}
}
private func rebuildPaths() {
// not optimized, but could be ignored due to possible N
for i in 0..<config.numberOfBalls {
for j in 0..<config.numberOfBalls {
let lhs = metaball(from: metaballs[i])
let rhs = metaball(from: metaballs[j])
blobLayers[i][j].path = lhs.blobPath(with: rhs)?.cgPath
}
}
}
private func rebuildPaths(with metaballView: UIView) {
guard let j = metaballs.firstIndex(of: metaballView) else {
return
}
let rhs = metaball(from: metaballs[j])
for i in 0..<config.numberOfBalls {
let lhs = metaball(from: metaballs[i])
// clean up mirrored paths
blobLayers[j][i].path = nil
blobLayers[i][j].path = lhs.blobPath(with: rhs)?.cgPath
}
}
@objc
private func pan(_ recognizer: UIPanGestureRecognizer) {
guard let metaballView = recognizer.view else {
return
}
bringSubviewToFront(metaballView)
let translation = recognizer.translation(in: self)
metaballView.center.x += translation.x
metaballView.center.y += translation.y
recognizer.setTranslation(.zero, in: self)
rebuildPaths(with: metaballView)
}
private func metaball(from view: UIView) -> Metaball {
return Metaball(
handleSize: config.handleSize,
curvature: config.curvature,
radius: view.frame.width / 2,
position: view.center
)
}
}
================================================
FILE: metaballs/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "ItunesArtwork@2x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: metaballs/Resources/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: metaballs/Resources/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: metaballs/ViewController.swift
================================================
//
// ViewController.swift
// metaballs
//
// Created by Roman Kyrylenko on 03.03.2020.
// Copyright © 2020 Roman Kyrylenko. All rights reserved.
//
import UIKit
final class ViewController: UIViewController {
@IBAction private func configureAction() {
let controller = ConfigViewController()
present(controller, animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
defer {
super.prepare(for: segue, sender: sender)
}
guard let controller = segue.destination as? ConfigViewController, let view = view as? MetaballsView else {
return
}
controller.setup(with: view.config) { config in
view.config = config
}
}
}
================================================
FILE: metaballs.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
B81704AA240EC29800F0C5DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81704A9240EC29800F0C5DD /* AppDelegate.swift */; };
B81704AE240EC29800F0C5DD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81704AD240EC29800F0C5DD /* ViewController.swift */; };
B81704B1240EC29800F0C5DD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B81704AF240EC29800F0C5DD /* Main.storyboard */; };
B81704B3240EC29900F0C5DD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B81704B2240EC29900F0C5DD /* Assets.xcassets */; };
B81704B6240EC29900F0C5DD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B81704B4240EC29900F0C5DD /* LaunchScreen.storyboard */; };
B8730EF624177F9200796676 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8730EF524177F9200796676 /* Blob.swift */; };
B8730EF824177FB600796676 /* Metaball.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8730EF724177FB600796676 /* Metaball.swift */; };
B8730EFA24177FEC00796676 /* CGPoint+Trigonometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8730EF924177FEB00796676 /* CGPoint+Trigonometry.swift */; };
B8730EFC2417801000796676 /* MetaballsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8730EFB2417801000796676 /* MetaballsView.swift */; };
B8A337C924291CD200F77B9F /* LayeredButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A337C824291CD200F77B9F /* LayeredButton.swift */; };
B8C4201E2417A34E00435172 /* ConfigViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C4201D2417A34E00435172 /* ConfigViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B81704A6240EC29800F0C5DD /* metaballs.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = metaballs.app; sourceTree = BUILT_PRODUCTS_DIR; };
B81704A9240EC29800F0C5DD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B81704AD240EC29800F0C5DD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
B81704B0240EC29800F0C5DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
B81704B2240EC29900F0C5DD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B81704B5240EC29900F0C5DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
B81704B7240EC29900F0C5DD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B8730EF524177F9200796676 /* Blob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Blob.swift; sourceTree = "<group>"; };
B8730EF724177FB600796676 /* Metaball.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metaball.swift; sourceTree = "<group>"; };
B8730EF924177FEB00796676 /* CGPoint+Trigonometry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGPoint+Trigonometry.swift"; sourceTree = "<group>"; };
B8730EFB2417801000796676 /* MetaballsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaballsView.swift; sourceTree = "<group>"; };
B8A337C824291CD200F77B9F /* LayeredButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayeredButton.swift; sourceTree = "<group>"; };
B8C4201D2417A34E00435172 /* ConfigViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B81704A3240EC29800F0C5DD /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B817049D240EC29800F0C5DD = {
isa = PBXGroup;
children = (
B81704A8240EC29800F0C5DD /* metaballs */,
B81704A7240EC29800F0C5DD /* Products */,
);
sourceTree = "<group>";
};
B81704A7240EC29800F0C5DD /* Products */ = {
isa = PBXGroup;
children = (
B81704A6240EC29800F0C5DD /* metaballs.app */,
);
name = Products;
sourceTree = "<group>";
};
B81704A8240EC29800F0C5DD /* metaballs */ = {
isa = PBXGroup;
children = (
B81704A9240EC29800F0C5DD /* AppDelegate.swift */,
B8C4201D2417A34E00435172 /* ConfigViewController.swift */,
B81704B7240EC29900F0C5DD /* Info.plist */,
B8A337C824291CD200F77B9F /* LayeredButton.swift */,
B81704AF240EC29800F0C5DD /* Main.storyboard */,
B8730EF424177F8800796676 /* MetaballsView */,
B8A337CB24291D3600F77B9F /* Resources */,
B81704AD240EC29800F0C5DD /* ViewController.swift */,
);
path = metaballs;
sourceTree = "<group>";
};
B8730EF424177F8800796676 /* MetaballsView */ = {
isa = PBXGroup;
children = (
B8730EF524177F9200796676 /* Blob.swift */,
B8730EF924177FEB00796676 /* CGPoint+Trigonometry.swift */,
B8730EF724177FB600796676 /* Metaball.swift */,
B8730EFB2417801000796676 /* MetaballsView.swift */,
);
path = MetaballsView;
sourceTree = "<group>";
};
B8A337CB24291D3600F77B9F /* Resources */ = {
isa = PBXGroup;
children = (
B81704B4240EC29900F0C5DD /* LaunchScreen.storyboard */,
B81704B2240EC29900F0C5DD /* Assets.xcassets */,
);
path = Resources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B81704A5240EC29800F0C5DD /* metaballs */ = {
isa = PBXNativeTarget;
buildConfigurationList = B81704BA240EC29900F0C5DD /* Build configuration list for PBXNativeTarget "metaballs" */;
buildPhases = (
B81704A2240EC29800F0C5DD /* Sources */,
B81704A3240EC29800F0C5DD /* Frameworks */,
B81704A4240EC29800F0C5DD /* Resources */,
B8191014242B9B230065FA3D /* swiftlint */,
);
buildRules = (
);
dependencies = (
);
name = metaballs;
productName = metaballs;
productReference = B81704A6240EC29800F0C5DD /* metaballs.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B817049E240EC29800F0C5DD /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1130;
LastUpgradeCheck = 1130;
ORGANIZATIONNAME = "Roman Kyrylenko";
TargetAttributes = {
B81704A5240EC29800F0C5DD = {
CreatedOnToolsVersion = 11.3.1;
};
};
};
buildConfigurationList = B81704A1240EC29800F0C5DD /* Build configuration list for PBXProject "metaballs" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = B817049D240EC29800F0C5DD;
productRefGroup = B81704A7240EC29800F0C5DD /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B81704A5240EC29800F0C5DD /* metaballs */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B81704A4240EC29800F0C5DD /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B81704B6240EC29900F0C5DD /* LaunchScreen.storyboard in Resources */,
B81704B3240EC29900F0C5DD /* Assets.xcassets in Resources */,
B81704B1240EC29800F0C5DD /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
B8191014242B9B230065FA3D /* swiftlint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = swiftlint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"${PODS_ROOT+x}\" ] && [ -x \"${PODS_ROOT}/SwiftLint/swiftlint\" ]; then\n ${PODS_ROOT}/SwiftLint/swiftlint\nelif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
B81704A2240EC29800F0C5DD /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B8730EFC2417801000796676 /* MetaballsView.swift in Sources */,
B8730EF624177F9200796676 /* Blob.swift in Sources */,
B81704AE240EC29800F0C5DD /* ViewController.swift in Sources */,
B81704AA240EC29800F0C5DD /* AppDelegate.swift in Sources */,
B8C4201E2417A34E00435172 /* ConfigViewController.swift in Sources */,
B8730EF824177FB600796676 /* Metaball.swift in Sources */,
B8730EFA24177FEC00796676 /* CGPoint+Trigonometry.swift in Sources */,
B8A337C924291CD200F77B9F /* LayeredButton.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
B81704AF240EC29800F0C5DD /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
B81704B0240EC29800F0C5DD /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
B81704B4240EC29900F0C5DD /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
B81704B5240EC29900F0C5DD /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
B81704B8240EC29900F0C5DD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
B81704B9240EC29900F0C5DD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
B81704BB240EC29900F0C5DD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = YDKT2YXVNA;
INFOPLIST_FILE = metaballs/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.pr0ctopus.metaballs;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
B81704BC240EC29900F0C5DD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = YDKT2YXVNA;
INFOPLIST_FILE = metaballs/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.pr0ctopus.metaballs;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B81704A1240EC29800F0C5DD /* Build configuration list for PBXProject "metaballs" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B81704B8240EC29900F0C5DD /* Debug */,
B81704B9240EC29900F0C5DD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B81704BA240EC29900F0C5DD /* Build configuration list for PBXNativeTarget "metaballs" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B81704BB240EC29900F0C5DD /* Debug */,
B81704BC240EC29900F0C5DD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B817049E240EC29800F0C5DD /* Project object */;
}
================================================
FILE: metaballs.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:metaballs.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: metaballs.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: resources/rorschach.ai
================================================
%PDF-1.5
%
1 0 obj
<</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 124 0 R 125 0 R 126 0 R 127 0 R 128 0 R 129 0 R 130 0 R 131 0 R 164 0 R 165 0 R 187 0 R 188 0 R 210 0 R 211 0 R 233 0 R 234 0 R]/Order 235 0 R/RBGroups[]>>/OCGs[5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 124 0 R 125 0 R 126 0 R 127 0 R 128 0 R 129 0 R 130 0 R 131 0 R 164 0 R 165 0 R 187 0 R 188 0 R 210 0 R 211 0 R 233 0 R 234 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<</Length 28912/Subtype/XML/Type/Metadata>>stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164050, 2019/10/01-18:03:16 ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/"
xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
xmlns:stFnt="http://ns.adobe.com/xap/1.0/sType/Font#"
xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/">
<xmp:CreatorTool>Adobe Illustrator 24.0 (Macintosh)</xmp:CreatorTool>
<xmp:CreateDate>2020-03-25T11:18:13+02:00</xmp:CreateDate>
<xmp:ModifyDate>2020-03-25T12:28:08+02:00</xmp:ModifyDate>
<xmp:MetadataDate>2020-03-25T12:28:08+02:00</xmp:MetadataDate>
<xmp:Thumbnails>
<rdf:Alt>
<rdf:li rdf:parseType="Resource">
<xmpGImg:width>256</xmpGImg:width>
<xmpGImg:height>128</xmpGImg:height>
<xmpGImg:format>JPEG</xmpGImg:format>
<xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAgAEAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4qwb82/wAwpvJPluTU
LaBZ7kq3phzsp5Kikgf5UgzO0WlGWzLlF12s1Uo5MeKFcWS9+4AWdvufHV5598wXvmM67cXDidmW
scbMqhVbkAu/ZiWB/m3zZ6nCMsOHl3eTuOw5js+QMfUN+K/4uL6r9717zD/zlRqDeUrWw0OFo/ML
JwvdVmVCicdg8UZ5BncdeQop6V7a/T6A88jfr44fGJwm8Z3Hl5fBg/lL88PzFstcglu9cubyKSQc
orlzJHUnoVO3E9NunamT1mkHBxQ2IczsiWGWQYc0QYZNr/iiehB9/fs+ydA1m21rRbPVbb+5vIlk
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
/9k=</xmpGImg:image>
</rdf:li>
</rdf:Alt>
</xmp:Thumbnails>
<illustrator:CreatorSubTool>Adobe Illustrator</illustrator:CreatorSubTool>
<illustrator:Type>Document</illustrator:Type>
<dc:format>application/pdf</dc:format>
<dc:title>
<rdf:Alt>
<rdf:li xml:lang="x-default">rorschach</rdf:li>
</rdf:Alt>
</dc:title>
<xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
<xmpMM:DocumentID>xmp.did:6ec362cb-eb24-4c54-a887-39076c817006</xmpMM:DocumentID>
<xmpMM:InstanceID>uuid:4106acb0-f72e-9146-a9bb-8a8f5a30a00d</xmpMM:InstanceID>
<xmpMM:OriginalDocumentID>uuid:9c363efd-302c-d341-b185-8438ee5f5256</xmpMM:OriginalDocumentID>
<xmpMM:DerivedFrom rdf:parseType="Resource">
<stRef:instanceID>uuid:09b36535-a7f2-864b-b32c-4e56264f51c1</stRef:instanceID>
<stRef:documentID>xmp.did:6e8d4305-7fbb-4dba-b3e9-0f354a2482e4</stRef:documentID>
<stRef:originalDocumentID>uuid:9c363efd-302c-d341-b185-8438ee5f5256</stRef:originalDocumentID>
<stRef:renditionClass>proof:pdf</stRef:renditionClass>
</xmpMM:DerivedFrom>
<xmpMM:History>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<stEvt:action>saved</stEvt:action>
<stEvt:instanceID>xmp.iid:612617a1-62e7-4cd7-aa16-8ca21cfd761d</stEvt:instanceID>
<stEvt:when>2020-03-21T00:22:17+02:00</stEvt:when>
<stEvt:softwareAgent>Adobe Illustrator 24.0 (Macintosh)</stEvt:softwareAgent>
<stEvt:changed>/</stEvt:changed>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<stEvt:action>saved</stEvt:action>
<stEvt:instanceID>xmp.iid:6ec362cb-eb24-4c54-a887-39076c817006</stEvt:instanceID>
<stEvt:when>2020-03-25T11:18:13+02:00</stEvt:when>
<stEvt:softwareAgent>Adobe Illustrator 24.0 (Macintosh)</stEvt:softwareAgent>
<stEvt:changed>/</stEvt:changed>
</rdf:li>
</rdf:Seq>
</xmpMM:History>
<pdf:Producer>Adobe PDF library 15.00</pdf:Producer>
<xmpTPg:NPages>1</xmpTPg:NPages>
<xmpTPg:HasVisibleTransparency>False</xmpTPg:HasVisibleTransparency>
<xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
<xmpTPg:MaxPageSize rdf:parseType="Resource">
<stDim:w>454.145036</stDim:w>
<stDim:h>258.294989</stDim:h>
<stDim:unit>Points</stDim:unit>
</xmpTPg:MaxPageSize>
<xmpTPg:Fonts>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<stFnt:fontName>SFProDisplay-Light</stFnt:fontName>
<stFnt:fontFamily>SF Pro Display</stFnt:fontFamily>
<stFnt:fontFace>Light</stFnt:fontFace>
<stFnt:fontType>Open Type</stFnt:fontType>
<stFnt:versionString>Version 14.0d2e0</stFnt:versionString>
<stFnt:composite>False</stFnt:composite>
<stFnt:fontFileName>SF-Pro-Display-Light.otf</stFnt:fontFileName>
</rdf:li>
</rdf:Bag>
</xmpTPg:Fonts>
<xmpTPg:PlateNames>
<rdf:Seq>
<rdf:li>Cyan</rdf:li>
<rdf:li>Magenta</rdf:li>
<rdf:li>Yellow</rdf:li>
<rdf:li>Black</rdf:li>
</rdf:Seq>
</xmpTPg:PlateNames>
<xmpTPg:SwatchGroups>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<xmpG:groupName>Default Swatch Group</xmpG:groupName>
<xmpG:groupType>0</xmpG:groupType>
</rdf:li>
</rdf:Seq>
</xmpTPg:SwatchGroups>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream
endobj
3 0 obj
<</Count 1/Kids[13 0 R]/Type/Pages>>
endobj
13 0 obj
<</ArtBox[35.4194 34.4726 419.855 224.564]/BleedBox[0.0 0.0 454.145 258.295]/Contents 236 0 R/CropBox[0.0 0.0 454.145 258.295]/LastModified(D:20200325122808+02'00')/MediaBox[0.0 0.0 454.145 258.295]/Parent 3 0 R/PieceInfo<</Illustrator 237 0 R>>/Resources<</ColorSpace<</CS0 238 0 R>>/ExtGState<</GS0 239 0 R>>/Font<</T1_0 232 0 R>>/ProcSet[/PDF/Text]/Properties<</MC0 233 0 R/MC1 234 0 R>>>>/Thumb 240 0 R/TrimBox[0.0 0.0 454.145 258.295]/Type/Page>>
endobj
236 0 obj
<</Filter/FlateDecode/Length 24648>>stream
HtK}@~l-^0o"٣euXd23"2/O?푮]vZownz߯?>}[^soSoz'o/_yݮ=Wۗ<=k7MFn^zx;=V/cW?k2Z>N5mVm[|8g\-pVi^Jg_WN?6Hy̓#8[O#_zԯwRow'U=*nբm]Z:W)5ꝋШo_xMl:!akAI3ı0z<iw_:x5ʂL?*La
&[! UYk^W}zF |NzKbw^Ha8#-|82g@IL[R;t(qv$
V4I@{LV?3`
cIq@ۇ\ڣyRx6ꠚS8цrM»Wxv^"fw냘QE2I]
/eڢ?uuŅkv.mm ~m~G?$[6K^RmhfgBCc(۰Z$'Z9ܡF>2[B:ʰ-!Ԝ r\ʐA̻2eLؔ%ΑDH*_4WH/bߓ,)5k
=]HVቨM2h>t2|"Q Pv+?bRQ3p2Xr;qE[iPZOG&l0iıǚʤ)0Sq-gҩYU|+Ty*+h{LQL5t r[<+!3ҕJp )o\6p[L*_간ߠD)$*d度TEKW:6ʁ:OiY!IYR J$Y{,>V,etj=#Ygэ>w}`hxAU_h g V`UR*(E0/qmDC"֜㍵@ޛҡ6 zBЯJu jƑ
&+Ks|]twOICxyusl5HS0S
9AuIHGVt&q]p̜NE((쐫]Tuht][t}vd{:-d
!EmM5!-v!AoؖC J8WQNOHo'5ٯMᄅ}qJxUכڪ'-);Y:+u$^֨&eD*asWEZ(Tb Murrs[%Dw;YFIv% Hn\k=ԜzfNYX&X AX=%Ⱦ=vPL-m-25DX/b#YVor3TXkoMK9UTvt%}Vy KE.Y6u 4$wvmW({TT9/:J.f$AUM[9-A{iCfUcs[3P\:WeP17UiTK]WfHO|?n6L$jbZ
i~A9h\zUiښ订 ERh-7]%D6!]V7/ݗmmY\nkg0FWӎzJF,VR
ЈP̆Bٺ#$l^Ņ>Gn^#x**Ph~l c-@+:?EqO䝦Ⱥ³B.7=!.
3H_-S~z i-šo]j{.M`3Gkrj@5DZŽf3WǭpkǶYiײGH\L
yD듲
>0f!qARzm 0#k$ȡ3>1u,/9c-کF?Y7c
uC-NTF[sU<xuz$mwTә5?N{Rz%uCՄ\Os;- /A*&IESCj;-4P1'in7"cSz}[sftr:=FOPr
!<)#I>|7%|#*B?H6G!t
YPΣXgjO(HA6wER\KYYDmu
MF7j
Hsř#N)<Hg2p/v?"
#>8h}9Φm7MI /Fb奩Ʒ7lDZᔜ>GG)Fh 먡*:b> ;n]6_j^w?+T[+д(iNvÈkk\?is=nP)%SGX$\LitYgփuE;DP`1E2mQl]y%UѕL)3%6.]6Ӆ0Q5&\}|]n)v,W? R6ޱ"-!Nkgxā^kP%{9qQx|E
*K?e<ek^]h^TPqS%@Djib*ܶ܆A6UЌ~.g'xQCe^r.DNƿIHӜXZnLJAӓLrGYU$8xxg#E!fz'IrWUf\; 3Zmhph=@3UIo>\EFU3Mce۟6렀W4Wckh}7ՍZKfl`ۑe|Gީ$OUMc
*S3-kkia* KY=rqWq|w+a#|sQ䎅EP5z\JzjZr='_$`E0/fYaAOlAw>2^l+ݿ)^/{P`\l~Uxv@ĵEj-Xav/1hBQLD_*$NɠpBe,0\ky+Z>qˀ9 H2Mxղ) Эlmsկw? f x\]sL/7:AFIG_=}6d#ܣ3\S_,zʦ2=&,7l;Pnj~RNʼnrFlHo]_ԋiJ3m[gZF
ʸA\Hz9h&o?&`f
"]bQ
1ܘvQg9^9K\_y ˿aHkf~7+rhIv]a۾,/$7?lCx) h3i'&!lQv{K_ENAT%MݼoP\x4in 'Ǻz=Hk!wVLv>n贯$v~;fKc*3l9JY9\RNg&1") 6Bq_1xf$'Q
F/&))XAG(&Zh'6>״s_3g@
KQY׆N
^/12^! xOw*"W[~[I}i;cjܢy59.<~ŕcٱ`(ͨBk,EYqMkz}A*ߎ]ZY>QtSs~=Rx9Jsz)N!![ϻǣĊu)þkd\+*zTޖ^<֮:%ovょCw ;LlJFpsXv떥
mx.9&U \L@&Z;ϫHD3-YI8|?~ʭ}V DpGZgܖZλUKxiEI/W|pm_*Q>!2 ]R/@/\rO+ UeɡQU/:0i6zö@9K /7eQI»͵֦`
F%iT ̱bx~Iɶe4-ce>ԅӀ-i\mnU٣oDOe.1"lQ@S\!aN~'&qow{qS4'{ܨrUWS3!%A!ouR C,jd=](ft7Nx܁2.tڼ[mPXyݼ>gP77`m]ӝ8I#0P?RGCN*LQoڪYtC?^*_u_mEa=sKFto ~p..ToudGXiPƢ7A$ꝕWC23#8T}Pb6XTեi3!RLkPh'TYf.=F3KW &yO:wr&k7JxRNJ%.u;n/7NP$T>1W)H'FmײoJ\m&^և|Ge齝d*G>FPL+ݦ5,j&]A"듋 q@C}ކzfCYT/1<:s}z
ٺI>[ t
h1tt&f'nqTÓ]eʷ"
B%i7J^8$}[w)=CF~xfWuL
,S4B7HEDj`Zl$&u!@鐢K|Ml6֛@?jPa7mg&y'a%4XR77ޥ0S/5e īߧ8.~~]hx.#J>U_ ,.C/"VbrLzi391ͻj>~TͰ5#6i'DsO@=
E)Դꮣ/VY|sQxEŤ[u dxT8ѹbGF9|bcDKPb;eHt!ouZY\,Lz<0 ۲X(<1!
1[J ƞ%bQ2m IYhS?Fb{JJOj@A_{~tYl{u%a-u":p}q&z`KJϥ97NzUGO~3_=ySƹ9j%;0
\0@Aawy-lJs-7,JnXTmԷ#BVVa=hVOBG6f^b3c;nב~t4(wsCQ@_&>ͨݷD/ ֒Izul hi?$7:E]N}7tK6ÀfeYөAQ}fk9ew'*~kyGeF`5\E>Q@{hSc p0"6nT3>wPH(
h!Ʒd*r0('9bP;@VTM`^`4@+}B|²!HX^] 0z&%d؍Ov2HjoPk3*~ݑ;\UT1ɪj5sN.bCOV"!ثKYiҹi/۪=wT.V d3&ܩUtԓY2+e)syɒ+afOG$u%,GOWޕFZ#i>JM#j:{HʞddCb]LUVxjs`b}G=ol Mu-kWv3lo*LZz="*#bŬG
/b2 !2eHY IDTj܊M0⦱p"d:m5 40.+#\e&t4
`$ipEH*o+
/rjC 5_/nxrI)YoXzN'Piυ8JW-[ 4]+DSYP[\^pƼgT:M⫕0
jZ4'bN!,jgn<xi<jaau/:QTNpanD aO9~]7DtjH6\_PZ7Ɩ"Kԛ>0"E(cW,@OԬV] O4keɎa.?}S|JTh϶ȑrqD@5U20<