EmojiFireworksAndExplosionsPackage(表情符号烟花和爆炸效果包)

EmojiFireworksAndExplosionsPackage 可用于提供独特的艺术性和高度可定制的动画效果,让您的用户乐在其中。这些效果可以用于游戏、社交媒体、广告或任何具有艺术内容的应用程序中。

请观看以下链接中的 4 分钟示例视频。请注意,在实际设备上运行时,动画会更流畅,质量更高。

https://drive.google.com/file/d/1sYh_W4oFAmnAb3FehbXk4OtA3nEexqxF/view

支持各种烟花和爆炸类效果,其中发射的单元可以是任何大小的表情符号、短文本或所需的图像。您可以控制发射单元的大小/比例。

我们在可见的 API 中公开了许多单元属性的控制,这使得这些烟花和爆炸效果高度可定制且具有独特的艺术性。

您可以控制加速度、旋转等等!许多单元属性参数都有一个合理的默认值,以防您不想指定它。X 和 Y 加速度默认为零,旋转也是如此。只需检查 detonate() 的 API。它主要用于自我记录。

在下面的术语中,我们通常说表情符号,请注意,如果您选择文本,或者为单元内容提供您自己的图像,它仍然适用。我们预计许多设计都会使用表情符号。

非常小的表情符号看起来像彩色火花。大型表情符号将显示所需的所有细节,使得烟花和爆炸效果成为一种表情符号艺术。

烟花和爆炸效果是通过将发射器放置在开发者指定的路径/形状上生成的。这些路径可以轻松地分配以覆盖任何 UI 对象的框架!或者您可以指定点源、线条、圆形、矩形、心形甚至椭圆形。

控制发射的单元速度、单元比例和单元出生率以模拟烟花或爆炸效果。开发者可以控制初始单元速度、速度范围、单元比例、比例范围、旋转、旋转范围以及爆炸开始时的单元出生率。开发者可以指定不同的结束比例,以便发射的单元可以随时间增长,或者如果需要,可以缩小到零尺寸,从而变得不可见。

同样,您可以控制 X 和 Y 加速度来模拟风、重力或反重力。

在下面,请找到 detonate() 的完整参数列表

请注意,您可能需要考虑您的应用是在较旧且速度较慢的设备上运行还是在较新且速度较快的设备上运行,以及同时绘制到屏幕上的其他动画的数量。发射器动画会占用大量的 CPU 和 GPU 处理能力。您指定的池越大,您选择的参数可能会影响性能并产生延迟。首先尝试在主要为静态的屏幕上使用这些效果。一种设计,显示一种效果,然后隐藏发射器足够长的时间,以允许所有先前发射的单元达到其生命周期的终点,这可能会提高性能。隐藏发射器有助于释放资源。当爆炸/烟花达到其持续时间的终点时,我们会自动隐藏这些发射器。

使用 Swift Package Manager 安装

要在 Xcode 项目中实现,请选择“File”(文件)、“Swift Packages”(Swift 包)、“Add Package Dependency”(添加包依赖项),并提供此 GitHub 链接:https://github.com/MichaelKucinski/EmitterFireworksAndExplosionsPackage。然后按照下面的说明进行操作。

用法

要创建效果,您必须创建一个发射器池,该池将处理您打算使用的最大数量的发射器。我们建议 50 到 1000 个发射器,具体取决于您所需的密度。

以下是代码片段,您可以使用这些代码片段快速让烟花和爆炸效果在您的项目中工作。

首先导入 EmojiFireworksAndExplosionsPackage

import EmojiFireworksAndExplosionsPackage

声明一个 EmojiFireworksAndExplosionsPackage 对象


let fireworksAndExplosionsEngine = EmojiFireworksAndExplosionsPackage()

定义这些全局对象很方便,注释中解释了这些对象


let countOfObjectsInExplosions = 125 // used when creating the pool of emitters
let useAllObjectsInExplosions = -1
//  A default that indicates the code will try to use every object in the emitter pool in the detonations of the explosions / fireworks.
//  If this default value is used when placing sparks on circles, lines, or rectangles it indicates how many emitters to place on those object paths.
//  Note that you can specify smaller detonations that use only a fraction of the available emitter pool.
//  When placing circles, lines, and rectangles for detonation, you can also specify that a smaller count that is less than the size of the emitter pool is to be used.

如果您的设计可以从具有多个表情符号烟花和爆炸对象中受益,您可以创建多个这样的对象,如下所示。但请注意,您需要为每个对象调用 viewDidLoad,为每个对象调用 createPoolOfEmitters 等。

let fireworksAndExplosionsEngine2 = EmojiFireworksAndExplosionsPackage()
let fireworksAndExplosionsEngine3 = EmojiFireworksAndExplosionsPackage()

确保从父视图的 viewDidLoad 中调用 fireworksAndExplosionsEngine.viewDidLoad()。

在调用 viewDidLoad 后立即创建您的发射器池。不要担心传递给 someEmojiCharacter 的默认表情符号字符。您可以随时指定和更改它。但是,如果您的设计始终使用相同的表情符号字符,则在调用中指定它。请注意,表情符号字符指定为字符串。我们会负责在后台将该字符串转换为图像。

如果您提供自己的图像,我们有一个 API 可以覆盖表情符号字符。我们将在下面记录。

在 viewDidLoad 中,您还需要为发射器池中的每个元素添加一个子层。

fireworksAndExplosionsEngine.viewDidLoad()

fireworksAndExplosionsEngine.createPoolOfEmitters(maxCountOfEmitters: countOfObjectsInExplosions, someEmojiCharacter: "🤯") // or use any count of objects desired.  We used 125 from above

for thisEmitter in fireworksAndExplosionsEngine.arrayOfEmitters { view.layer.addSublayer(thisEmitter) }


这样就完成了 viewDidLoad 中需要的初始化

其余代码可以在以后根据任何所需的事件调用。

要显示效果,您可能需要首先设置表情符号模式。

在这里,我们展示了如何为所有发射器使用单个表情符号


var thisArrayAsText = [String]()

thisArrayAsText.append("🤯")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

在这里,我们展示了如何为所有发射器使用各种表情符号


var thisArrayAsText = [String]()

thisArrayAsText.append("🦄")
thisArrayAsText.append("🐝")
thisArrayAsText.append("🐞")
thisArrayAsText.append("👀")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

接下来,指定您的发射器在屏幕上的放置位置。这是一个示例,它从我们的演示视频中已显示的对象的矩形框架开始爆炸。

readMeTextView.frame 是我们的示例中的 UITextView 对象,声明为 var readMeTextView = UITextView()

请注意,选择的比例因子 1.25 将爆炸的起始位置增加了 25%。如果您希望爆炸恰好在框架边界上开始,请指定比例因子:1.0。请注意,比例因子是一个可选参数,默认为 1.0

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(thisRectangle: readMeTextView.frame, scaleFactor: 1.25, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)


如果您想指定自己的矩形,只需声明一个 CGRect 对象并对其进行初始化即可。

var tempRect : CGRect = CGRect(
x: 100,
y: 300,
width: 300,
height: 300)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(thisRectangle: tempRect, scaleFactor: 1.25, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)


最后,调用 fireworksAndExplosionsEngine.detonate 以产生您的烟花或爆炸效果。

请注意,此示例未使用一些可选参数,因此您可以根据需要获得更大的灵活性。


fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 120,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.3,
thisEndingScale: 0,
thisCellBirthrate: 15,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.5)

请注意,爆炸效果应该在持续时间到期后有效地隐藏自己。

在某些时候,您可能希望在效果仍在爆炸过程中隐藏它。只需调用


fireworksAndExplosionsEngine.hideAllEmitters()

以上应该能让您开始!

您可以随时更改烟花或爆炸的放置位置,但在发射器隐藏时这样做可能看起来最好。

您可以随时更改表情符号,但在发射器隐藏时这样做可能看起来最好。

您可以随时调用 detonate,但在发射器隐藏时这样做可能看起来最好。

这是 API 的完整参数列表,该 API 执行实际的爆炸引爆,包括加速度、旋转、色调等


public func detonate(
thisCountOfEmittersGeneratingSparks : Int,
thisVelocity : CGFloat,
thisVelocityRange : CGFloat,
thisSpin : CGFloat,
thisSpinRange : CGFloat,
thisStartingScale : CGFloat,
thisStartingScaleRange : CGFloat = 0,
thisEndingScale : CGFloat,
thisAccelerationIn_X : CGFloat = 0,
thisAccelerationIn_Y : CGFloat = 0,
thisCellBirthrate: CGFloat,
thisCellLifetime: CGFloat,
thisCellLifetimeRange: CGFloat,
thisExplosionDuration : CGFloat,
thisTint              : UIColor = .white )

如果您希望为您的烟花和爆炸效果指定更复杂的表情符号模式,您可以使用 for 循环、决策等构建任何所需的表情符号数组。

您可以指定一小段文本(例如“Love”)而不是使用表情符号。


var thisArrayAsText = [String]()

thisArrayAsText.append("Love")

您可以指定单个文本单词/短语,或者您可以指定单词和短语数组以获得更有趣的效果。


var thisArrayAsText = [String]()

thisArrayAsText.append("Love")
thisArrayAsText.append("You")
thisArrayAsText.append("Baby")

可以通过在 detonate 中指定色调参数来调整文本颜色。要获得黄色文本或黄色着色的表情符号,请参见下面参数的最后一行


fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 8,
thisStartingScale: 0,
thisEndingScale: 3,
thisAccelerationIn_X : -180,
thisCellBirthrate: 10,
thisCellLifetime: 7.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4,
thisTint : .yellow)

或者,您可以指定要使用的多个颜色数组。您需要在调用 detonate() 之后立即调用 alternateImageTintWithGivenArray(),并传递您的颜色数组,如下所示。


fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 8,
thisStartingScale: 0,
thisEndingScale: 3,
thisAccelerationIn_X : -180,
thisCellBirthrate: 10,
thisCellLifetime: 7.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4)

var thisArrayOfColors = [UIColor]()

thisArrayOfColors.append(.red)
thisArrayOfColors.append(.blue)
thisArrayOfColors.append(.green)
thisArrayOfColors.append(.yellow)

fireworksAndExplosionsEngine.alternateImageTintWithGivenArray(desiredArray: thisArrayOfColors)

如果您想提供自己的图像,您可能需要在将其传递到我们的 API 之前缩放该图像。大约 100 像素(或更少)是一个不错的起始大小。这是一个调用我们的 API 的示例


fireworksAndExplosionsEngine.useSpecifiedImageAsContentsForAllEmitters(specifiedImage: tempImageToUseWhenChangingCellImages)

其中 tempImageToUseWhenChangingCellImages 是一个 UIImage。

如果您想将爆炸放置在一条线上,这里有一些示例代码


let pointOne = CGPoint(x: 0, y: 0)
let pointTwo = CGPoint(x: 432, y: 234)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedLine(startingPoint: pointOne, endingPoint: pointTwo, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

如果您想将爆炸放置在一个圆上,这里有一些示例代码


fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 300), thisCircleRadius: 400, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

请注意,thisCircleArcFactor 为 1 绘制一个完整的圆。值为 0.5 将绘制半圆的弧。然后 offsetAngleInDegrees 将控制半圆在屏幕上显示的哪个角度。

使用值 3 作为 thisCircleArcFactor 会将爆炸位置环绕圆 3 次。

如果您想要发射器的点源,只需使用 placeEmittersOnSpecifiedCircleOrArc 并为 thisCircleRadius 提供值 0.0。

如果您想将爆炸放置在椭圆上,这里有一些示例代码


fireworksAndExplosionsEngine.placeEmittersOnSpecifiedEllipse(thisCenter: CGPoint(x: 500, y: 700), thisMajorRadius: 300, thisMinorRadius: 200, alphaAngleInDegrees: 0, omegaAngleInDegreesUsedForRotation: -40, thisScaleFactor: 1, thisCountOfEmittersGeneratingSparks: -1)

如果您想将爆炸放置在心形上,这里有一些示例代码


fireworksAndExplosionsEngine.placeEmittersOnSpecifiedHeart(thisHeartCenter: CGPoint(x: 500, y: 500), thisHeartScaleFactor: 20, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

您可以在任何指定的圆内随机放置发射器。这是示例代码


fireworksAndExplosionsEngine.placeEmittersRandomlyInsideSpecifiedCircle(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 300, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

您可以在指定矩形的任何一侧精确放置发射器。此示例代码显示它们放置在顶部


fireworksAndExplosionsEngine.placeEmittersOnSpecifiedSideOfRectangle(thisSide: EmojiFireworksAndExplosionsPackage.Sides.Top, thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

为了梳理发射器,使其路径从圆中辐射出来,这里有一些示例代码。请注意,您应该已经调用了 placeEmittersOnSpecifiedCircleOrArc,然后您需要调用 detonate 在以下示例代码之后。以下的一些参数用于高级用例,但显示的这些值对于基本情况应该可以正常工作。


fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 0,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

您可以梳理所有发射器以指向任何一般方向,例如向上、向左、向右等。

要实现类似喷泉的效果,需要将发射器梳理到向上方向,并指定一个随机性锥,以及 Y 中的加速度带来的少量重力。这是示例代码

// a radius of zero will effectively make this a point source for our fountain

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 800), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combAllEmittersToPointInDesiredDirectionWithDesiredCone(directionAngleForCellFlow: 270, coneWideningFactorNormallyZero: 0.10)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 900,
thisVelocityRange: 420,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.85,
thisStartingScaleRange: 0,
thisEndingScale: 0,
thisAccelerationIn_Y : 900,
thisCellBirthrate: 11,
thisCellLifetime: 2,
thisCellLifetimeRange: 0,
thisExplosionDuration: 3.5)

您可以梳理矩形情况下的发射器,其中每一侧都可以分配一个唯一的方向和一个唯一的随机性锥。这是示例代码


fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(
thisRectangle: readMeTextView.frame,
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
combEmittersDesired: true,
topDesiredCombingRelativeAngle: 0,
topConeWideningFactorNormallyZero: 0.05,
bottomDesiredCombingRelativeAngle: 75,
bottomConeWideningFactorNormallyZero: 0.1,
leftDesiredCombingRelativeAngle: 30,
leftConeWideningFactorNormallyZero: 0.15,
rightDesiredCombingRelativeAngle: -30,
rightConeWideningFactorNormallyZero: 0.2)

要梳理椭圆情况下的发射器,这里是示例代码


fireworksAndExplosionsEngine.combEllipticalEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow: 45, coneWideningFactorNormallyZero: 0.0)

要梳理心形情况下的发射器,这里是示例代码。请注意,值为 180 度会使单元格向外流出,远离心脏。值为零将使单元格向内流向心脏中心。如果您想指定任何其他偏移角度,您可以。


// 180 below will offset them to flow outwards and away from the center.
fireworksAndExplosionsEngine.combHeartEmittersTowardsHeartCenter(desiredOffsetAngleForCellFlowInDegrees : 180,
coneWideningFactorNormallyZero: 0.05)

您可以梳理任何爆炸,使其流向(或远离任何指定点),并具有任何偏移角度。这是示例代码


// Using a 0 for the angle will offset them to flow towards the point.
// Using a 180 for the angle will offset them to flow away from the point.

fireworksAndExplosionsEngine.combEmittersTowardsOrAwayFromSpecifiedPoint(thisPoint: CGPoint(x: 0, y: 500), desiredOffsetAngleForCellFlowInDegrees : 0,  coneWideningFactorNormallyZero: 0, thisCountOfEmittersGeneratingSparks : pointsOnShape)

如果您选择梳理任何引爆,则需要调用 stopCombingTheEmitters,否则所有未来的引爆都将应用梳理。

如果您想停止梳理效果并返回到随机定向的单元格,则需要调用此 API


fireworksAndExplosionsEngine.stopCombingTheEmitters()

嘿,总的来说,不要尝试对同一次爆炸使用多个梳理效果。应用于发射器的最后一个将是您得到的。

许可证

这个项目尚未指定任何许可。 它有点实验性质。 我正在与一些团队分享它,并允许他们进行实验以获取反馈。 这个项目将来可能会以 MIT 许可证分享。 如果您发现了这个存储库,您可以随意进行实验。 如果您想考虑使用它来发布代码,请尽早通过 engineermichigan@gmail.com 发送电子邮件给我以获得批准。 如果您是一个小型企业,应该可以顺利获得批准。 但任何大型组织都应该与我达成协议,聘请我作为直接雇员或顾问等。 可能考虑申请专利,组建团队等。 欢迎随时与我联系,提出改进建议或请求,以及任何反馈。 我善于接受批评,并欢迎好的建议。

电子邮件: engineermichigan@gmail.com

如果有任何学生或开发者想要进行实验和改进,或分支(fork)设计,我会考虑合作。

请注意,如果为单元指定非常高的出生率(数千个),或者指定大量的发射器池(同样,数千个),或者指定较长的单元生命周期,或者同时显示多个爆炸效果,则可能会使 GPU 过载并出现延迟。 即使是单元格的大小也可能起作用,因为 GPU 试图处理与更大的单元格图像相关的增加的工作负载。 因此,您可能需要一开始就保守一些。

话虽如此,我认为您很容易就能找到合理的属性值,从而实现一些有趣且具有艺术效果的效果。 存在用于游戏、广告、社交媒体以及任何向用户展示艺术作品的应用程序的潜在用例。

尝试将这些效果与用户在屏幕上的触摸操作结合起来可能会很有趣。

— Mike

附言:如果您想运行我的驱动代码,以下是我整个 ViewController.swift 的复制粘贴。

请注意,所有内容都是为 iPad Pro 缩放的,因此如果您只有 iPhone 可以进行实验,则需要更改一些值。 您可以将屏幕左下角显示的计数器与每个爆炸的驱动代码相关联。 只需检查 touchesBegan 并将其与变量 countOfTouches 相关联即可。 如果你想复制视频中的效果,只需使用与显示的计数器相关的相同参数即可。

//
//  ViewController.swift
//  EmojiFireworksAndExplosionsDriver
//
//  Created by Michael Kucinski on 8/1/20.
//  Copyright © 2020 Michael Kucinski. All rights reserved.
//

import UIKit
import EmojiFireworksAndExplosionsPackage

class ViewController: UIViewController {

let fireworksAndExplosionsEngine = EmojiFireworksAndExplosionsPackage()
let fireworksAndExplosionsEngine2 = EmojiFireworksAndExplosionsPackage()
let fireworksAndExplosionsEngine3 = EmojiFireworksAndExplosionsPackage()

var countView = UITextView()
var readMeTextView = UITextView()

let countOfObjectsInExplosions = 125
let useAllObjectsInExplosions = -1
//  A default that indicates the code will try to use every object in the emitter pool in the detonations of the explosions / fireworks.
//  If this default value is used when placing sparks on circles, lines, or rectangles it indicates how many emitters to place on those object paths.
//  Note that you can specify smaller detonations that use only a fraction of the available emitter pool.
//  When placing circles, lines, and rectangles for detonation, you can also specify that a smaller count that is less than the size of the emitter pool is to be used.

override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.

readMeTextView.frame = CGRect(x: 100, y:  50, width: 845, height: 800)
readMeTextView.textAlignment = NSTextAlignment.justified
readMeTextView.backgroundColor = UIColor.white
readMeTextView.layer.borderColor = UIColor.blue.cgColor
readMeTextView.layer.borderWidth = 10.0
readMeTextView.font = UIFont.systemFont(ofSize: 32.0)
readMeTextView.textContainerInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
readMeTextView.textColor = .black
readMeTextView.isEditable = false
readMeTextView.allowsEditingTextAttributes = true // allows memoji
readMeTextView.isUserInteractionEnabled = false
readMeTextView.alpha = 0

countView.frame = CGRect(x: 20, y:  1300, width: 550, height: 50)
countView.textAlignment = NSTextAlignment.center
countView.backgroundColor = UIColor.blue
countView.layer.borderColor = UIColor.orange.cgColor
countView.layer.borderWidth = 5.0
countView.font = UIFont.systemFont(ofSize: 24.0)
countView.textContainerInset = UIEdgeInsets(top: 10, left: 4, bottom: 4, right: 4)
countView.textColor = .white
countView.text = "Tap screen to walk through examples."
countView.isEditable = false
countView.allowsEditingTextAttributes = true // allows memoji
countView.isUserInteractionEnabled = false

fireworksAndExplosionsEngine.viewDidLoad()

fireworksAndExplosionsEngine.createPoolOfEmitters(maxCountOfEmitters: countOfObjectsInExplosions, someEmojiCharacter: "🤯")

for thisEmitter in fireworksAndExplosionsEngine.arrayOfEmitters { view.layer.addSublayer(thisEmitter) }

fireworksAndExplosionsEngine2.viewDidLoad()

fireworksAndExplosionsEngine2.createPoolOfEmitters(maxCountOfEmitters: countOfObjectsInExplosions, someEmojiCharacter: "🤯")

for thisEmitter in fireworksAndExplosionsEngine2.arrayOfEmitters { view.layer.addSublayer(thisEmitter) }

fireworksAndExplosionsEngine3.viewDidLoad()

fireworksAndExplosionsEngine3.createPoolOfEmitters(maxCountOfEmitters: countOfObjectsInExplosions, someEmojiCharacter: "🤯")

for thisEmitter in fireworksAndExplosionsEngine3.arrayOfEmitters { view.layer.addSublayer(thisEmitter) }

self.view.addSubview(readMeTextView)
self.view.addSubview(countView)

var localTimer = Timer()

if localTimer.isValid
{
// This blank if statement gets rid of a nuisance warning about never reading the timer.
}

// start the timer
localTimer = Timer.scheduledTimer(timeInterval: 1.0/60.0, target: self, selector: #selector(handleTimerEvent), userInfo: nil, repeats: true)

} // ends viewDidLoad

var totalFramesSinceStarting = 0
var timerRunning : Bool = false

// starts handleTimer...
@objc func handleTimerEvent()
{
totalFramesSinceStarting += 1

if timerRunning
{
if totalFramesSinceStarting % 40 == 0
{
fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 300), thisCircleRadius: 10, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 2450,
thisVelocityRange: 500,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.25,
thisEndingScale: 0,
thisCellBirthrate: 31,
thisCellLifetime: 0.3,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.25)
}
if totalFramesSinceStarting % 60 == 0
{
fireworksAndExplosionsEngine2.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 600), thisCircleRadius: 400, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine2.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 500,
thisVelocityRange: 200,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 0.2,
thisEndingScale: 0.1,
thisCellBirthrate: 31,
thisCellLifetime: 0.4,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.45)
}
if totalFramesSinceStarting % 80 == 0
{
fireworksAndExplosionsEngine3.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 900), thisCircleRadius: 10, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine3.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 450,
thisVelocityRange: 500,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 3,
thisEndingScale: 0,
thisCellBirthrate: 31,
thisCellLifetime: 0.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.5)
}
}

} // ends handleTimerEvent

var countOfTouches = 0
// stub
//var countOfTouches = 36

var thisTouchPoint : CGPoint = CGPoint(x:-4100,y:-4100)

// Detect touch
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
let touch = touches.first!
thisTouchPoint = touch.location(in: self.view)

if thisTouchPoint.y > 1200 && thisTouchPoint.x > 700
{
// provides a way to repeat the last explosion for my testing purposes
countOfTouches = previousCountOfTouches
}

if countOfTouches == 0
{
// Stop the combing from the end of the last pass through the count.
fireworksAndExplosionsEngine.stopCombingTheEmitters()
fireworksAndExplosionsEngine2.hideAllEmitters()

readMeTextView.alpha = 1
readMeTextView.frame = CGRect(x: 100, y:  1100, width: 845, height: 160)
readMeTextView.text = "This Swift Package lets you produce an explosion or fireworks like effect where emoji, text, or a specified image is used as the sparks."

var thisArrayAsText = [String]()

thisArrayAsText.append("🦄")
thisArrayAsText.append("🐝")
thisArrayAsText.append("🐞")
thisArrayAsText.append("👀")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let pointOne = CGPoint(x: 0, y: 0)
let pointTwo = CGPoint(x: 0, y: 1240)

// below lines helps to test the call to useSpecifiedImageAsContentsForAllEmitters which is commented out

//let textOrEmojiToUIImage = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

//textOrEmojiToUIImage.text = "🇲🇾"
//textOrEmojiToUIImage.sizeToFit()

//let tempImageToUseWhenChangingCellImages  =  UIImage.imageWithLabel(label: textOrEmojiToUIImage)

//fireworksAndExplosionsEngine.useSpecifiedImageAsContentsForAllEmitters(specifiedImage: tempImageToUseWhenChangingCellImages)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedLine(startingPoint: pointOne, endingPoint: pointTwo, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 1400,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 3,
thisEndingScale: 0,
thisCellBirthrate: 55,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.4)
}
if countOfTouches == 1
{
readMeTextView.text = "The sparks are emitters where developers can control the starting size, ending size, spin, tint, accelerations in X and Y, birthrate, lifetime, etc."

var thisArrayAsText = [String]()

thisArrayAsText.append("🤯")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 700,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 0.1,
thisEndingScale: 1.3,
thisCellBirthrate: 125,
thisCellLifetime: 3.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.4,
thisTint : .green)
}
if countOfTouches == 2
{
readMeTextView.text = "Your explosions/fireworks can consist of as many different emoji as desired (22 shown now)!\nJust append your emoji like this : \nthisArrayAsText.append(⚡️)\nthisArrayAsText.append(🧨) \nthisArrayAsText.append(🍀) \nthisArrayAsText.append(🦄) \nthisArrayAsText.append(🍟)"
readMeTextView.frame = CGRect(x: 100, y:  900, width: 845, height: 340)

var thisArrayAsText = [String]()

thisArrayAsText.append("⚡️")
thisArrayAsText.append("🧨")
thisArrayAsText.append("🐞")
thisArrayAsText.append("🦄")
thisArrayAsText.append("💘")
thisArrayAsText.append("🐥")
thisArrayAsText.append("🌞")
thisArrayAsText.append("🍀")
thisArrayAsText.append("🍟")
thisArrayAsText.append("🍔")
thisArrayAsText.append("🍒")
thisArrayAsText.append("🍕")
thisArrayAsText.append("🍋")
thisArrayAsText.append("🍫")
thisArrayAsText.append("🍰")
thisArrayAsText.append("🍧")
thisArrayAsText.append("🎯")
thisArrayAsText.append("🧲")
thisArrayAsText.append("💰")
thisArrayAsText.append("💎")
thisArrayAsText.append("📆")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 50, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 400,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 1.15,
thisEndingScale: 1.25,
thisCellBirthrate: 25,
thisCellLifetime: 4.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4)
}

if countOfTouches == 3
{
readMeTextView.text = "Your explosions / fireworks can come from a point source."
readMeTextView.frame = CGRect(x: 100, y:  1100, width: 845, height: 160)

var thisArrayAsText = [String]()

thisArrayAsText.append("✅")
thisArrayAsText.append("🅰️")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 120,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.3,
thisEndingScale: 0,
thisCellBirthrate: 15,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.5)
}
if countOfTouches == 4
{
readMeTextView.text = "Your explosions / fireworks can come from sources evenly spaced out around a circle."

var thisArrayAsText = [String]()

thisArrayAsText.append("✅")
thisArrayAsText.append("🅰️")
thisArrayAsText.append("☮️")
thisArrayAsText.append("🆚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 400, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 20,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.1,
thisEndingScale: 0.2,
thisCellBirthrate: 15,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 1.4)
}
if countOfTouches == 5
{
readMeTextView.text = "Your explosions / fireworks can cover any fraction of a circles arc and any offset angle."

var thisArrayAsText = [String]()

thisArrayAsText.append("✅")
thisArrayAsText.append("🅰️")
thisArrayAsText.append("☮️")
thisArrayAsText.append("🆚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 400, thisCircleArcFactor: 0.5, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 20,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.1,
thisEndingScale: 0.2,
thisCellBirthrate: 15,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 1.4)
}
if countOfTouches == 6
{
readMeTextView.text = "Your explosions / fireworks can cover any fraction of a circles arc and any offset angle."

var thisArrayAsText = [String]()

thisArrayAsText.append("✅")
thisArrayAsText.append("🅰️")
thisArrayAsText.append("☮️")
thisArrayAsText.append("🆚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 400, thisCircleArcFactor: 0.25, offsetAngleInDegrees: 160, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 20,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.1,
thisEndingScale: 0.2,
thisCellBirthrate: 15,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 1.4)
}
if countOfTouches == 7
{
readMeTextView.text = "Your explosions / fireworks can cover any fraction of a circles arc and any offset angle."

var thisArrayAsText = [String]()

thisArrayAsText.append("✅")
thisArrayAsText.append("🅰️")
thisArrayAsText.append("☮️")
thisArrayAsText.append("🆚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 400, thisCircleArcFactor: 0.25, offsetAngleInDegrees: -25, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 20,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.1,
thisEndingScale: 0.2,
thisCellBirthrate: 15,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 1.4)
}
if countOfTouches == 8
{
readMeTextView.text = "Your explosions / fireworks can pick any side of a rectangle as the effects source.\n\n\n                               Top : "

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)

var thisArrayAsText = [String]()

thisArrayAsText.append("😱")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedSideOfRectangle(thisSide: EmojiFireworksAndExplosionsPackage.Sides.Top, thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 250,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 3,
thisEndingScale: 0,
thisAccelerationIn_Y : -100,
thisCellBirthrate: 85,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 9
{
readMeTextView.text = "Your explosions / fireworks can pick any side of a rectangle as the effects source.\n\n\n                               Bottom : "

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)

var thisArrayAsText = [String]()

thisArrayAsText.append("😤")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedSideOfRectangle(thisSide: EmojiFireworksAndExplosionsPackage.Sides.Bottom, thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 250,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 3,
thisEndingScale: 0,
thisAccelerationIn_Y : 100,
thisCellBirthrate: 85,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 10
{
readMeTextView.text = "Your explosions / fireworks can pick any side of a rectangle as the effects source.\n\n\n                               Left : "

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)

var thisArrayAsText = [String]()

thisArrayAsText.append("🧐")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedSideOfRectangle(thisSide: EmojiFireworksAndExplosionsPackage.Sides.Left, thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 250,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 3,
thisEndingScale: 0,
thisAccelerationIn_X : -1000,
thisCellBirthrate: 85,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 11
{
readMeTextView.text = "Your explosions / fireworks can pick any side of a rectangle as the effects source.\n\n\n                               Right : "

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)

var thisArrayAsText = [String]()

thisArrayAsText.append("😎")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedSideOfRectangle(thisSide: EmojiFireworksAndExplosionsPackage.Sides.Right, thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 250,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 3,
thisEndingScale: 0,
thisAccelerationIn_X : 1000,
thisCellBirthrate: 85,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 12
{
readMeTextView.text = "Setting an X acceleration can simulate a wind."

readMeTextView.frame = CGRect(x: 100, y:  1100, width: 845, height: 160)

var thisArrayAsText = [String]()

thisArrayAsText.append("Love")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let desiredSubsetOfPool : Int = 10

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 200, y: 600), thisCircleRadius: 200, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 8,
thisStartingScale: 0,
thisEndingScale: 3,
thisAccelerationIn_X : 400,
thisCellBirthrate: 10,
thisCellLifetime: 4,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4,
thisTint : .magenta)

}
if countOfTouches == 13
{
readMeTextView.text = "Note that text / sparks can be tinted with any colors as defined by a simple array.\nJust append your colors like this : \nthisArrayOfColors.append(.red)\nthisArrayOfColors.append(.blue)\nthisArrayOfColors.append(.green)\nthisArrayOfColors.append(.yellow)"


readMeTextView.frame = CGRect(x: 100, y:  900, width: 845, height: 340)

var thisArrayAsText = [String]()

thisArrayAsText.append("Love")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let desiredSubsetOfPool : Int = 10

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 200, y: 600), thisCircleRadius: 200, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 8,
thisStartingScale: 0,
thisEndingScale: 3,
thisAccelerationIn_X : 400,
thisCellBirthrate: 10,
thisCellLifetime: 4,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4,
thisTint : .magenta)

var thisArrayOfColors = [UIColor]()

thisArrayOfColors.append(.red)
thisArrayOfColors.append(.blue)
thisArrayOfColors.append(.green)
thisArrayOfColors.append(.yellow)

fireworksAndExplosionsEngine.alternateImageTintWithGivenArray(desiredArray: thisArrayOfColors)
}

if countOfTouches == 14
{
readMeTextView.alpha = 0

var thisArrayAsText = [String]()

thisArrayAsText.append("Love")
thisArrayAsText.append("You")
thisArrayAsText.append("Baby")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let desiredSubsetOfPool : Int = 60

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 800, y: 600), thisCircleRadius: 200, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: desiredSubsetOfPool,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 8,
thisStartingScale: 0,
thisEndingScale: 3,
thisAccelerationIn_X : -180,
thisCellBirthrate: 10,
thisCellLifetime: 7.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4,
thisTint : .yellow)

var thisArrayOfColors = [UIColor]()

thisArrayOfColors.append(.red)
thisArrayOfColors.append(.blue)
thisArrayOfColors.append(.green)
thisArrayOfColors.append(.yellow)

fireworksAndExplosionsEngine.alternateImageTintWithGivenArray(desiredArray: thisArrayOfColors)
}

if countOfTouches == 15
{
readMeTextView.text = "\nSetting a Y acceleration can simulate gravity."

readMeTextView.frame = CGRect(x: 100, y:  900, width: 845, height: 160)

readMeTextView.alpha = 1

var thisArrayAsText = [String]()

thisArrayAsText.append("❤️")
thisArrayAsText.append("💙")
thisArrayAsText.append("💛")
thisArrayAsText.append("💜")
thisArrayAsText.append("🧡")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 400), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 240,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 1,
thisEndingScale: 1,
thisAccelerationIn_Y : 200,
thisCellBirthrate: 5,
thisCellLifetime: 5.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 1.4)
}

if countOfTouches == 16
{
readMeTextView.text = "\nYou can control the explosion / fireworks duration down to the millisecond.\n\n\n                               0.3 seconds"

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)


var thisArrayAsText = [String]()

thisArrayAsText.append("👀")
thisArrayAsText.append("👁")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(

thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 0.3,
thisEndingScale: 0.0,
thisCellBirthrate: 500,
thisCellLifetime: 0.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.3)
}

if countOfTouches == 17
{
readMeTextView.text = "\nYou can control the explosion / fireworks duration down to the millisecond.\n\n\n                               3.14 seconds"

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)

var thisArrayAsText = [String]()

thisArrayAsText.append("🌹")
thisArrayAsText.append("💐")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(

thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 0.3,
thisEndingScale: 0.2,
thisCellBirthrate: 500,
thisCellLifetime: 0.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 3.14)
}

if countOfTouches == 18
{
readMeTextView.text = "You can place the sparks randomly inside any specified circle."

readMeTextView.frame = CGRect(x: 100, y:  1100, width: 845, height: 160)

var thisArrayAsText = [String]()

thisArrayAsText.append("🍀")
thisArrayAsText.append("🍄")
thisArrayAsText.append("💦")
thisArrayAsText.append("🍁")
thisArrayAsText.append("🌷")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersRandomlyInsideSpecifiedCircle(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 300, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 12,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 0.2,
thisEndingScale: 0.2,
thisCellBirthrate: 5,
thisCellLifetime: 2.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 1.14)
}
if countOfTouches == 19
{
readMeTextView.text = "You can easily program implosions!\nAnd the direction of the particles can be combed."

var thisArrayAsText = [String]()

thisArrayAsText.append("🤯")
thisArrayAsText.append("🍎")
thisArrayAsText.append("🥶")
thisArrayAsText.append("👻")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 400, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 180,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 450,
thisVelocityRange: 250,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.25,
thisEndingScale: 0.0,
thisCellBirthrate: 31,
thisCellLifetime: 0.7,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 20
{
readMeTextView.text = "Explosions can sling cells from the edge of circles."

var thisArrayAsText = [String]()

thisArrayAsText.append("🤯")
thisArrayAsText.append("🍎")
thisArrayAsText.append("🥶")
thisArrayAsText.append("👻")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)


fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 200, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 90,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 650,
thisVelocityRange: 250,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.25,
thisEndingScale: 0.0,
thisCellBirthrate: 31,
thisCellLifetime: 0.7,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 21
{
readMeTextView.text = "Explosions can comb the flow streams in any desired direction relative to the circle."

var thisArrayAsText = [String]()

thisArrayAsText.append("🍎")
thisArrayAsText.append("🥶")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 100, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 0,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 950,
thisVelocityRange: 250,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 0.5,
thisEndingScale: 0.5,
thisCellBirthrate: 31,
thisCellLifetime: 0.7,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 22
{
readMeTextView.text = "Combing the emitters is naturally designed for the circular explosion cases.  However, if you use the combing algorithm for other shapes like lines or rectangles, you can get some unique artistic displays."
readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 300)

var thisArrayAsText = [String]()

thisArrayAsText.append("🔴")
thisArrayAsText.append("🟡")
thisArrayAsText.append("🔵")
thisArrayAsText.append("🟢")
thisArrayAsText.append("🟣")
thisArrayAsText.append("⚪️")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(thisRectangle: readMeTextView.frame, scaleFactor: 1.4, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 0,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 150,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 1,
thisEndingScale: 0.3,
thisCellBirthrate: 30,
thisCellLifetime: 0.4,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.2)
}
if countOfTouches == 23
{
readMeTextView.text = "Combing the emitters is naturally designed for the circular explosion cases.  However, if you use the combing algorithm for other shapes like lines or rectangles, you can get some unique artistic displays."
readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 300)

var thisArrayAsText = [String]()

thisArrayAsText.append("🔴")
thisArrayAsText.append("🟡")
thisArrayAsText.append("🔵")
thisArrayAsText.append("🟢")
thisArrayAsText.append("🟣")
thisArrayAsText.append("⚪️")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedSideOfRectangle(thisSide: EmojiFireworksAndExplosionsPackage.Sides.Top, thisRectangle: readMeTextView.frame, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 0,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1,overrideAngleIncrementWithThisManyDegrees: 12)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 250,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 1,
thisEndingScale: 0.3,
thisCellBirthrate: 30,
thisCellLifetime: 0.4,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.2)
}

if countOfTouches == 24
{
readMeTextView.frame = CGRect(x: 500, y:  150, width: 445, height: 150)
readMeTextView.text = "You get an amazing amount of animation and flexibility for about 10 lines of code 🧐"

var thisArrayAsText = [String]()

thisArrayAsText.append("🔴")
thisArrayAsText.append("🟡")
thisArrayAsText.append("🔵")
thisArrayAsText.append("🟢")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let pointOne = CGPoint(x: 0, y: 0)
let pointTwo = CGPoint(x: 1024, y: 1360)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedLine(startingPoint: pointOne, endingPoint: pointTwo, thisCountOfEmittersGeneratingSparks : useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 0,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0.01,
combArcFactor: 1,overrideAngleIncrementWithThisManyDegrees: 12)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 150,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 0,
thisStartingScale: 1,
thisEndingScale: 0.3,
thisCellBirthrate: 30,
thisCellLifetime: 0.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2)
}
if countOfTouches == 25
{
readMeTextView.text = "Your can program two or more explosions at the same time and same place if you create two instances.\n\nThe possibilities do seem to approach endlessness.\nHere we show one implosion and one explosion together."
readMeTextView.frame = CGRect(x: 100, y:  1000, width: 845, height: 260)

var thisArrayAsText = [String]()

thisArrayAsText.append("⚡️")
thisArrayAsText.append("🧨")

fireworksAndExplosionsEngine.stopCombingTheEmitters()

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 120,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.8,
thisEndingScale: 0.3,
thisCellBirthrate: 15,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 1.5)

thisArrayAsText = [String]()

thisArrayAsText.append("🤯")
thisArrayAsText.append("🍎")
thisArrayAsText.append("🥶")
thisArrayAsText.append("👻")

fireworksAndExplosionsEngine2.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine2.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 500, y: 500), thisCircleRadius: 400, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine2.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 180,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine2.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 750,
thisVelocityRange: 250,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.25,
thisEndingScale: 0.0,
thisCellBirthrate: 31,
thisCellLifetime: 0.7,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.2)
}
if countOfTouches == 26
{
readMeTextView.text = "Here are two explosions side by side."
readMeTextView.frame = CGRect(x: 100, y:  1000, width: 845, height: 160)

var thisArrayAsText = [String]()

thisArrayAsText.append("🤯")
thisArrayAsText.append("🍎")
thisArrayAsText.append("🥶")
thisArrayAsText.append("👻")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 250, y: 500), thisCircleRadius: 100, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 90,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 950,
thisVelocityRange: 250,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.25,
thisEndingScale: 0.0,
thisCellBirthrate: 31,
thisCellLifetime: 1,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.3)

thisArrayAsText.append("🦊")
thisArrayAsText.append("🌺")
thisArrayAsText.append("🍓")
thisArrayAsText.append("🪀")

fireworksAndExplosionsEngine2.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine2.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 750, y: 500), thisCircleRadius: 100, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine2.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 90,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine2.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 950,
thisVelocityRange: 250,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.25,
thisEndingScale: 0.0,
thisCellBirthrate: 31,
thisCellLifetime: 1,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.3)
}

if countOfTouches == 27
{
// see handleTimerEvent

// Stop the combing from the end of the last pass through the count.
fireworksAndExplosionsEngine.stopCombingTheEmitters()
fireworksAndExplosionsEngine2.stopCombingTheEmitters()
fireworksAndExplosionsEngine3.stopCombingTheEmitters()

var thisArrayAsText = [String]()

readMeTextView.text = "Here are 3 explosions that repeat at different time intervals."
readMeTextView.frame = CGRect(x: 100, y:  1200, width: 845, height: 120)

timerRunning = true

thisArrayAsText.append("🤯")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

thisArrayAsText.removeAll()
thisArrayAsText.append("❎")
thisArrayAsText.append("🅾️")

fireworksAndExplosionsEngine2.combCircularEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow : 180,
desiredOffsetAngleForShape : 0,
coneWideningFactorNormallyZero: 0,
combArcFactor: 1)

fireworksAndExplosionsEngine2.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

thisArrayAsText.removeAll()
thisArrayAsText.append("❤️")
thisArrayAsText.append("🤍")
thisArrayAsText.append("💙")

fireworksAndExplosionsEngine3.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)
}
if countOfTouches == 28
{
timerRunning = false

// Stop the combing from above
fireworksAndExplosionsEngine.stopCombingTheEmitters()

readMeTextView.frame = CGRect(x: 100, y:  900, width: 845, height: 340)

readMeTextView.text = "You can specify a direction angle for the cells to flow towards and specify a spreading cone factor too.\n\n               direction angle = 90 degrees\n               cone factor     = 25 percent"

var thisArrayAsText = [String]()

thisArrayAsText.append("🤯")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 300), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combAllEmittersToPointInDesiredDirectionWithDesiredCone(directionAngleForCellFlow: 90, coneWideningFactorNormallyZero: 0.25)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 900,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 0.1,
thisEndingScale: 4,
thisCellBirthrate: 30,
thisCellLifetime: 1.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.75)
}

if countOfTouches == 29
{
timerRunning = false

// Stop the combing from above
fireworksAndExplosionsEngine.stopCombingTheEmitters()

readMeTextView.frame = CGRect(x: 100, y:  900, width: 845, height: 340)

readMeTextView.text = "You can specify a direction angle for the cells to flow towards and specify a spreading cone factor too.\n\n               direction angle = 180 degrees\n               cone factor     = 10 percent"

var thisArrayAsText = [String]()

thisArrayAsText.append("🧚🏽‍♀️")
thisArrayAsText.append("🧞‍♂️")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 300), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combAllEmittersToPointInDesiredDirectionWithDesiredCone(directionAngleForCellFlow: 180, coneWideningFactorNormallyZero: 0.1)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 500,
thisVelocityRange: 220,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 0.1,
thisEndingScale: 0.4,
thisCellBirthrate: 30,
thisCellLifetime: 0.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 0.75)
}
if countOfTouches == 30
{
timerRunning = false

// Stop the combing from above
fireworksAndExplosionsEngine.stopCombingTheEmitters()

readMeTextView.frame = CGRect(x: 100, y:  900, width: 845, height: 340)

readMeTextView.text = "You can specify a direction angle for the cells to flow towards and specify a spreading cone factor too.\n\n               direction angle = 270 degrees\n               cone factor     = 10 percent\n\nAdd gravity to produce a fountain effect."

var thisArrayAsText = [String]()

thisArrayAsText.append("💙")
thisArrayAsText.append("💛")
thisArrayAsText.append("💜")
thisArrayAsText.append("🧡")
thisArrayAsText.append("❤️")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 800), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combAllEmittersToPointInDesiredDirectionWithDesiredCone(directionAngleForCellFlow: 270, coneWideningFactorNormallyZero: 0.10)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 900,
thisVelocityRange: 420,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 1.85,
thisStartingScaleRange: 0,
thisEndingScale: 0,
thisAccelerationIn_Y : 900,
thisCellBirthrate: 11,
thisCellLifetime: 2,
thisCellLifetimeRange: 0,
thisExplosionDuration: 3.5)
}
if countOfTouches == 31
{
timerRunning = false

// Stop the combing from above
fireworksAndExplosionsEngine.stopCombingTheEmitters()

readMeTextView.frame = CGRect(x: 100, y:  1000, width: 845, height: 240)

readMeTextView.text = "We call this stream and splat.\n\nSeveral streams with more gravity could simulate a waterfall or cascade effect."

var thisArrayAsText = [String]()

thisArrayAsText.append("💙")
thisArrayAsText.append("💛")
thisArrayAsText.append("💜")
thisArrayAsText.append("🧡")
thisArrayAsText.append("❤️")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 20, y: 300), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

fireworksAndExplosionsEngine.combAllEmittersToPointInDesiredDirectionWithDesiredCone(directionAngleForCellFlow: 0, coneWideningFactorNormallyZero: 0.01)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 400,
thisVelocityRange: 20,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 0.1,
thisStartingScaleRange: 0,
thisEndingScale: 0.2,
thisAccelerationIn_Y : 100,
thisCellBirthrate: 23,
thisCellLifetime: 2,
thisCellLifetimeRange: 0,
thisExplosionDuration: 13.5)
}
if countOfTouches == 32
{
timerRunning = false

// Stop the combing from above
fireworksAndExplosionsEngine2.stopCombingTheEmitters()

var thisArrayAsText = [String]()

thisArrayAsText.append("💙")
thisArrayAsText.append("💛")
thisArrayAsText.append("💜")
thisArrayAsText.append("🧡")
thisArrayAsText.append("❤️")

fireworksAndExplosionsEngine2.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine2.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 800, y: 480), thisCircleRadius: 0, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions)

//fireworksAndExplosionsEngine2.combAllEmittersToPointInDesiredDirectionWithDesiredCone(directionAngleForCellFlow: 0, coneWideningFactorNormallyZero: 0.01)

fireworksAndExplosionsEngine2.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 120,
thisVelocityRange: 20,
thisSpin: 0,
thisSpinRange: 4,
thisStartingScale: 0.1,
thisStartingScaleRange: 0,
thisEndingScale: 0.2,
thisCellBirthrate: 23,
thisCellLifetime: 1,
thisCellLifetimeRange: 0,
thisExplosionDuration: 13.5)
}
if countOfTouches == 33
{
// Stop the combing from the end of the last pass through the count.
fireworksAndExplosionsEngine.stopCombingTheEmitters()
fireworksAndExplosionsEngine2.hideAllEmitters()

readMeTextView.text = "\nYou can specify a relative direction for the emitters to flow away from any rectangle, and a cone for that direction.\n\n Each side can get it's own unique angle and cone values."

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)

var thisArrayAsText = [String]()

thisArrayAsText.append("🔴")
thisArrayAsText.append("🟡")
thisArrayAsText.append("🔵")
thisArrayAsText.append("🟢")
thisArrayAsText.append("🟣")
fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(
thisRectangle: readMeTextView.frame,
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
combEmittersDesired: true,
topDesiredCombingRelativeAngle: 0,
topConeWideningFactorNormallyZero: 0.05,
bottomDesiredCombingRelativeAngle: 75,
bottomConeWideningFactorNormallyZero: 0.1,
leftDesiredCombingRelativeAngle: 30,
leftConeWideningFactorNormallyZero: 0.15,
rightDesiredCombingRelativeAngle: -30,
rightConeWideningFactorNormallyZero: 0.2)

fireworksAndExplosionsEngine.detonate(

thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 540,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 0.1,
thisEndingScale: 0.0,
thisCellBirthrate: 500,
thisCellLifetime: 0.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 3.14)
}
if countOfTouches == 34
{
// Stop the combing from the end of the last pass through the count.
fireworksAndExplosionsEngine.stopCombingTheEmitters()

readMeTextView.text = "\nYou can specify a relative direction for the emitters to flow away from a rectangle, and a cone for that direction.\n\n Each side can get it's own unique angle and cone values."

readMeTextView.frame = CGRect(x: 200, y:  500, width: 600, height: 400)

var thisArrayAsText = [String]()

thisArrayAsText.append("💙")
thisArrayAsText.append("💛")
thisArrayAsText.append("💜")
thisArrayAsText.append("🧡")
thisArrayAsText.append("❤️")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedRectangle(
thisRectangle: readMeTextView.frame,
scaleFactor : 1.25,
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
combEmittersDesired: true,
topDesiredCombingRelativeAngle: 180,
topConeWideningFactorNormallyZero: 0.05,
bottomDesiredCombingRelativeAngle: 180,
bottomConeWideningFactorNormallyZero: 0.05,
leftDesiredCombingRelativeAngle: 180,
leftConeWideningFactorNormallyZero: 0.05,
rightDesiredCombingRelativeAngle: 180,
rightConeWideningFactorNormallyZero: 0.05)

fireworksAndExplosionsEngine.detonate(

thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 80,
thisVelocityRange: 30,
thisSpin: 2,
thisSpinRange: 0,
thisStartingScale: 0.65,
thisEndingScale: 0.2,
thisCellBirthrate: 100,
thisCellLifetime: 1,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.14)
}
if countOfTouches == 35
{
// Stop the combing from the end of the last pass through the count.
fireworksAndExplosionsEngine.stopCombingTheEmitters()

readMeTextView.text = "Your can specify ellipse based shapes for the fireworks / explosions and choose to comb, or not to comb."
readMeTextView.frame = CGRect(x: 200, y:  1100, width: 600, height: 150)

var thisArrayAsText = [String]()

thisArrayAsText.append("✅")
thisArrayAsText.append("🅰️")
thisArrayAsText.append("☮️")
thisArrayAsText.append("🆚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedEllipse(thisCenter: CGPoint(x: 500, y: 700), thisMajorRadius: 300, thisMinorRadius: 200, alphaAngleInDegrees: 0, omegaAngleInDegreesUsedForRotation: -40, thisScaleFactor: 1, thisCountOfEmittersGeneratingSparks: -1)

fireworksAndExplosionsEngine.combEllipticalEmittersToPointInDesiredDirections(desiredOffsetAngleForCellFlow: 45, coneWideningFactorNormallyZero: 0.0)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: useAllObjectsInExplosions,
thisVelocity: 150,
thisVelocityRange: 0,
thisSpin: 1,
thisSpinRange: 2,
thisStartingScale: 0.6,
thisEndingScale: 0.1,
thisCellBirthrate: 29,
thisCellLifetime: 0.6,
thisCellLifetimeRange: 0,
thisExplosionDuration: 4.1)
}
if countOfTouches == 36
{
readMeTextView.text = "Your explosions / fireworks can come from a heart shape.  You can comb the flow towards the center, or comb it outwards, or comb it with any offsetting angle."
readMeTextView.alpha = 1
readMeTextView.frame = CGRect(x: 175, y:  1100, width: 740, height: 230)

var thisArrayAsText = [String]()

thisArrayAsText.append("❤️")
thisArrayAsText.append("💛")
thisArrayAsText.append("💙")
thisArrayAsText.append("💚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let pointsOnHeart : Int = 120

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedHeart(thisHeartCenter: CGPoint(x: 500, y: 500), thisHeartScaleFactor: 20, thisCountOfEmittersGeneratingSparks: pointsOnHeart)

// 180 below will offset them to flow outwards and away from the center.
fireworksAndExplosionsEngine.combHeartEmittersTowardsHeartCenter(desiredOffsetAngleForCellFlowInDegrees : 180,
coneWideningFactorNormallyZero: 0.05)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: pointsOnHeart,
thisVelocity: 200,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 0.6,
thisEndingScale: 0,
thisCellBirthrate: 70,
thisCellLifetime: 0.5,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4)
}
if countOfTouches == 37
{
readMeTextView.text = "You can comb any explosion to flow towards (or away from any specified point), with any offsetting angle.\n\nTowards shown here."
readMeTextView.alpha = 1
readMeTextView.frame = CGRect(x: 175, y:  1100, width: 740, height: 230)

var thisArrayAsText = [String]()

thisArrayAsText.append("❤️")
thisArrayAsText.append("💛")
thisArrayAsText.append("💙")
thisArrayAsText.append("💚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let pointsOnShape : Int = 120

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedCircleOrArc(thisCircleCenter: CGPoint(x: 512, y: 500), thisCircleRadius: 300, thisCircleArcFactor: 1, offsetAngleInDegrees: 0, scaleFactor: 1, thisCountOfEmittersGeneratingSparks: pointsOnShape)

fireworksAndExplosionsEngine.combEmittersTowardsOrAwayFromSpecifiedPoint(thisPoint: CGPoint(x: 0, y: 500), desiredOffsetAngleForCellFlowInDegrees : 0,  coneWideningFactorNormallyZero: 0, thisCountOfEmittersGeneratingSparks : pointsOnShape)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: pointsOnShape,
thisVelocity: 200,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 2,
thisStartingScale: 0.6,
thisEndingScale: 0,
thisCellBirthrate: 70,
thisCellLifetime: 1,
thisCellLifetimeRange: 0,
thisExplosionDuration: 2.4)
}
if countOfTouches == 38
{
readMeTextView.text = "You can comb any explosion to flow towards (or away from any specified point), with any offsetting angle.\n\nAway shown here."
readMeTextView.alpha = 1
readMeTextView.frame = CGRect(x: 175, y:  1100, width: 740, height: 230)

var thisArrayAsText = [String]()

thisArrayAsText.append("❤️")
thisArrayAsText.append("💛")
thisArrayAsText.append("💙")
thisArrayAsText.append("💚")

fireworksAndExplosionsEngine.alternateImageContentsWithGivenArray(
desiredArrayAsText: thisArrayAsText)

let pointsOnShape : Int = 120

let pointOne = CGPoint(x: 200, y: 600)
let pointTwo = CGPoint(x: 500, y: 900)

fireworksAndExplosionsEngine.placeEmittersOnSpecifiedLine(startingPoint: pointOne, endingPoint: pointTwo, thisCountOfEmittersGeneratingSparks : pointsOnShape)

// 180 below will offset them to flow away from the point.
fireworksAndExplosionsEngine.combEmittersTowardsOrAwayFromSpecifiedPoint(thisPoint: CGPoint(x: 300, y: 750), desiredOffsetAngleForCellFlowInDegrees : 180,  coneWideningFactorNormallyZero: 0.1, thisCountOfEmittersGeneratingSparks : pointsOnShape)

fireworksAndExplosionsEngine.detonate(
thisCountOfEmittersGeneratingSparks: pointsOnShape,
thisVelocity: 555,
thisVelocityRange: 0,
thisSpin: 0,
thisSpinRange: 18,
thisStartingScale: 0,
thisEndingScale: 1.5,
thisCellBirthrate: 150,
thisCellLifetime: 2,
thisCellLifetimeRange: 0.1,
thisExplosionDuration: 0.1)
}


previousCountOfTouches = countOfTouches

countOfTouches += 1
if countOfTouches == touchesEndValue
{
countOfTouches = 0
}

countView.text = ("Counter : \(previousCountOfTouches)")
countView.frame = CGRect(x: 5, y:  1300, width: 160, height: 50)

} // ends touchesBegan

var previousCountOfTouches = 0
var touchesEndValue = 39
}