Created
April 27, 2023 17:56
-
-
Save wizard1066/df47cec2902a6aeb9ffafcc6bae2591a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// Emitter | |
// | |
// Created by localuser on 18.04.23. | |
// | |
import SwiftUI | |
import Combine | |
import Algorithms | |
let screenSize: CGRect = UIScreen.main.bounds | |
var screenWidth: CGFloat = UIScreen.main.bounds.width | |
let screenHeight: CGFloat = UIScreen.main.bounds.height | |
let noshow = PassthroughSubject<Int,Never>() | |
let colorIndex = PassthroughSubject<Int,Never>() | |
let colorChange = PassthroughSubject<(Int,Int),Never>() | |
let numberOf = 1024 | |
class Angles { | |
static var shared = Angles() | |
var newSet:[Double] = [] | |
// init() { | |
// var sample:[Double] = [] | |
// for i in 0...359 { | |
// sample.append(Double(i)) | |
// } | |
// newSet = sample.randomSample(count: 180) | |
// //print("newSet \(newSet)") | |
// } | |
var degree: Double = 0 { | |
// need not exceed the number of degrees in a circle | |
didSet { | |
if degree > 360 { | |
degree = 0 | |
} | |
} | |
} | |
var created = [Int: Double]() | |
func returnDegree() -> Double { | |
degree += 1 | |
return(degree) | |
} | |
func returnDegree(kee:Int, type:Bool) -> Double { | |
if created[kee] == nil { | |
if type { | |
degree = newSet.popLast()! | |
} else { | |
degree += 1 | |
} | |
created[kee] = degree | |
return(degree) | |
} else { | |
return(Double(created[kee]!)) | |
} | |
} | |
} | |
class Colors { | |
static var shared = Colors() | |
var color2D:[Color] = [] | |
init() { | |
for i in 0..<13 { | |
let color = Color(hue: Double(i*13/255), saturation: 1.0, brightness: 1.0) | |
color2D.append(color) | |
} | |
color2D[0] = .red | |
color2D[1] = .blue | |
color2D[2] = .green | |
color2D[3] = .yellow | |
color2D[4] = .mint | |
} | |
} | |
struct ContentView: View { | |
@State var isReady = false | |
var body: some View { | |
if !isReady { | |
Text("Go") | |
.font(.largeTitle) | |
.foregroundColor(Color.white) | |
.onTapGesture { | |
isReady.toggle() | |
} | |
} else { | |
EmitterView() | |
} | |
} | |
} | |
struct EmitterView: View { | |
let timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect() | |
@State var update = 0 | |
@State var particlesLaunched = 0 | |
var body: some View { | |
ZStack { | |
//Color.black | |
//Spacer() | |
ZStack { | |
ForEach((0..<update), id: \.self) { num in | |
ParticleBuild(indexOf: update, particlesLaunched: $particlesLaunched) | |
} | |
}.padding(.bottom, 24) | |
// Circle() | |
// .fill(RadialGradient(colors: [.white,.yellow,.orange,.red], center: .center, startRadius: 0, endRadius: 64)) | |
// .mask(ZStack { | |
// ForEach((0..<update), id: \.self) { num in | |
// ParticleBuild(indexOf: update, particlesLaunched: $particlesLaunched) | |
// }}) | |
// } | |
}.onReceive(timer) { _ in | |
if update < numberOf { | |
update += 1 | |
particlesLaunched += 1 | |
//print("updated \(particlesLaunched)") | |
} | |
} | |
//.offset(y: 128) | |
} | |
} | |
struct ParticleBuild: View { | |
let shared = Angles.shared | |
var indexOf:Int | |
//var colorGroup:ColoursAvailable | |
@State var alpha = 1.0 | |
@Binding var particlesLaunched:Int | |
// fuke | |
@ViewBuilder | |
var body: some View { | |
let angle = shared.returnDegree() | |
//let angle = Double.random(in: 0...359) | |
let newAttr = Attributes(radius: CGSize(width: 512, height: 512), angle: angle, xSize:16, ySize:16) | |
//let newAttr = Attributes(rect: CGSize(width: 0, height: -512), xSize: 16, ySize: 16) | |
Particle(opacity: alpha, indexOf: indexOf, attributes: newAttr, rotate: angle, particlesLaunched: $particlesLaunched) | |
.onAppear(perform: { | |
withAnimation(.easeIn(duration: newAttr.sourceRange).delay(newAttr.delay)) { | |
alpha = 0.0 | |
} | |
}) | |
.task { | |
log[indexOf] = true | |
} | |
} | |
} | |
var log:[Int:Bool] = [:] | |
var colours:[Int:Color] = [:] | |
var cycles:[Int:Int] = [:] | |
struct Particle: View { | |
var opacity: Double | |
var colors = Colors.shared | |
@State var indexOf:Int | |
@State var attributes:Attributes! | |
@State var xOffset = 0.0 | |
@State var yOffset = 0.0 | |
@State var xSize = 0.0 | |
@State var ySize = 0.0 | |
@State var scale:Double = 0 | |
@State var show = true | |
@State var index2U = 0 | |
@State var rotate:Double | |
@State var colour = Color.clear | |
@Binding var particlesLaunched:Int | |
var body: some View { | |
if show { | |
ZStack { | |
Star(sides: 5, startAngle: 0, radiusWidth: 8, radiusHeight: 8, percent: 90) | |
//Polygon(sides: 5, angle: 20) | |
//Rectangle() | |
//Triangle() | |
//Circle() | |
.fill(colour) | |
// .onReceive(colorChange, perform: { value in | |
// let (index2D,index2K) = value | |
// // print("index2U \(index2U)") | |
// if index2D == indexOf { | |
// index2U = index2K | |
// } | |
// }) | |
.frame(width: xSize, height: ySize) | |
.scaleEffect(scale) | |
.rotation3DEffect(.degrees(rotate), axis: (x: 0, y: 0, z: 1)) | |
.task { | |
scale = attributes.scaleRange | |
//colour = returnColor(indexOf: indexOf) | |
//colour = returnColor(particlesLaunched: particlesLaunched) | |
colour = returnColor2(particlesLaunched: particlesLaunched) | |
//colour = returnColor(angle: rotate) | |
} | |
.modifier(OffsetView(xOffset: xOffset, yOffset: yOffset, indexOf:indexOf)) | |
.modifier(AlphaView(particlesLaunched: $particlesLaunched, indexOf:indexOf, alpha: opacity)) | |
.onAppear(perform: { | |
let sourceTimes = attributes.sourceRange / 2 | |
let sourceXspeed = attributes.xAcceleration / 2 | |
let sourceYspeed = attributes.yAcceleration / 2 | |
scale = attributes.scaleRange | |
if attributes.shapes == .square { xOffset += sourceXspeed } | |
withAnimation(.easeIn(duration: attributes.delay)) { | |
// cut-down the sun burst effect | |
xSize = attributes.xSize | |
ySize = attributes.ySize | |
//if attributes.shapes == .circle { xOffset += sourceXspeed / 8} | |
yOffset += Double.random(in: -xSize...xSize) | |
xOffset += Double.random(in: -ySize...ySize) | |
} | |
withAnimation(.easeIn(duration: sourceTimes * 8).delay(attributes.delay)) { | |
if attributes.shapes == .circle { xOffset += sourceXspeed * 8 } | |
yOffset += sourceYspeed * 8 | |
scale = 0 | |
xSize = 0 | |
ySize = 0 | |
} | |
}) | |
.blendMode(.screen) | |
}.onReceive(noshow.removeDuplicates()) { value in | |
if value == indexOf || value == -1 { | |
show = false | |
} | |
let particlesfinished = log.filter{$0.value == false}.count | |
if particlesfinished == particlesLaunched { | |
print("complete \(particlesLaunched)") | |
} | |
} | |
} | |
} | |
// Something for graduating colors | |
// func returnColor() -> Color { | |
// let color = Color.red | |
// return color | |
// } | |
func returnColor2(particlesLaunched: Int) -> Color { | |
// produces sequences of colours | |
let bp:[UInt] = [0x0000b3, 0x0010d9, 0x0020ff, 0x0040ff, 0x0060ff, 0x0080ff, 0x009fff, 0x00bfff, 0x00ffff] | |
let mu:[UInt] = [0x0000ff,0x00ff00,0x00ffff,0xff0000,0xff00ff,0xffff00,0x1f1f1f] | |
//let colors2R = [Color(hex: bp[0]),Color(hex: bp[2]),Color(hex: bp[4]),Color(hex: bp[6])] | |
let dc:[UInt] = [0x3a86ff,0x8338ec,0xff006e,0xfb5607,0xffbe0b,0xffffff] | |
var colors2R:[Color] = [] | |
for pallate in dc { | |
colors2R.append(Color(hex: pallate)) | |
} | |
//let colors2R = [Color.white, Color.yellow, Color.orange, Color.red] | |
var startingTarget = 64 | |
for i in 0..<colors2R.count { | |
if particlesLaunched <= (startingTarget * i) { | |
return colors2R[i] | |
} | |
} | |
return Color.clear | |
} | |
func returnColor(particlesLaunched: Int) -> Color { | |
// produces random result | |
let colors2R = [Color.white, Color.yellow, Color.orange, Color.red] | |
let foo = particlesLaunched % colors2R.count | |
return colors2R[foo] | |
} | |
func returnColor(indexOf:Int, Cords:CGPoint) -> Color { | |
// does work | |
let colors2R = [Color.white, Color.yellow, Color.orange, Color.red] | |
if abs(Cords.x) < 10 && abs(Cords.y) < 10 { | |
return colors2R[0] | |
} | |
if ((abs(Cords.x) > 10 && abs(Cords.x) < 99) && (abs(Cords.y) > 10 && abs(Cords.y) < 99)) { | |
return colors2R[1] | |
} | |
return Color.black | |
} | |
func returnColor(angle: Double) -> Color { | |
// works with angle | |
let colors2R = [Color.white, Color.yellow, Color.orange, Color.red] | |
var beginSlice = 0.0 | |
var endSlice = 90.0 | |
for i in 0..<4 { | |
if angle > beginSlice && angle < endSlice { | |
return colors2R[i] | |
} | |
beginSlice += 90.0 | |
endSlice += 90.0 | |
} | |
return Color.blue | |
} | |
func returnColor(indexOf:Int) -> Color { | |
print("index \(indexOf)") | |
// works with cycles | |
let bp:[UInt] = [0x0000b3, 0x0010d9, 0x0020ff, 0x0040ff, 0x0060ff, 0x0080ff, 0x009fff, 0x00bfff, 0x00ffff] | |
let nc:[UInt] = [0x5BC0EB, 0xFDE74C, 0x9BC53D, 0xE55934, 0xFA7921] | |
let rs:[UInt] = [0xe63946,0xf1faee,0xa8dadc,0x457b9d,0x1d3557] | |
//let colors2R = [Color.white, Color.yellow, Color.orange, Color.red] | |
//let colors2R = [Color.white, Color.blue, Color.mint, Color.green] | |
//let colors2R = [Color(hex: bp[0]),Color(hex: bp[2]),Color(hex: bp[4]),Color(hex: bp[6])] | |
var colors2R:[Color] = [] | |
for pallate in rs { | |
colors2R.append(Color(hex: pallate)) | |
} | |
var newColor = Color.clear | |
switch indexOf { | |
case 0...100: | |
newColor = colors2R[0] | |
case 101...200: | |
newColor = colors2R[1] | |
case 201...300: | |
newColor = colors2R[2] | |
case 301...400: | |
newColor = colors2R[3] | |
default: | |
newColor = Color.clear | |
} | |
return(newColor) | |
} | |
} | |
extension Float { | |
func floatToHex()->String { | |
return String(self.bitPattern, radix: 16, uppercase: true) | |
} | |
} | |
extension Double { | |
func doubleToHex()-> String { | |
return String(self.bitPattern, radix: 16, uppercase: true) | |
} | |
} | |
struct OffsetView: AnimatableModifier { | |
let colors = Colors.shared | |
var xOffset: Double | |
var yOffset: Double | |
var indexOf: Int | |
var animatableData: AnimatablePair<Double,Double> { | |
get { AnimatablePair(Double(xOffset),Double(yOffset)) } | |
set { xOffset = Double(newValue.first) | |
yOffset = Double(newValue.second) | |
//let calcX = (abs(xOffset) / 100 * 10).rounded() | |
//let calcY = (abs(yOffset) / 100 * 10).rounded() | |
//let ranger = calcY.truncatingRemainder(dividingBy: 5) | |
//colorChange.send((indexOf,Int(ranger))) | |
} | |
} | |
func body(content: Content) -> some View { | |
content | |
.offset(x: xOffset, y:yOffset) | |
} | |
} | |
struct AlphaView: AnimatableModifier { | |
@Binding var particlesLaunched:Int | |
@State var indexOf: Int | |
var alpha: Double | |
var animatableData: CGFloat { | |
get { Double(alpha) } | |
set { alpha = Double(newValue); | |
if alpha == 0 { | |
//print("ended \(indexOf) \(particlesLaunched) ") | |
log[indexOf] = false | |
noshow.send(indexOf) | |
} | |
} | |
} | |
func body(content: Content) -> some View { | |
content | |
.opacity(alpha) | |
} | |
} | |
struct Triangle: Shape { | |
func path(in rect: CGRect) -> Path { | |
var path = Path() | |
path.move(to: CGPoint(x: rect.midX, y: rect.minY)) | |
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) | |
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) | |
path.addLine(to: CGPoint(x: rect.midX, y: rect.minY)) | |
return path | |
} | |
} | |
struct Polygon: Shape { | |
let sides:Int | |
let angle:Int | |
func path(in rect: CGRect) -> Path { | |
var path = Path() | |
let center:CGPoint = CGPoint(x: rect.width / 2, y: rect.height / 2) | |
let radius:Double = Double(rect.width / 2) | |
for i in stride(from: angle, to: (360 + angle), by: 360/sides) { | |
let radians = Double(i) * Double.pi / 180.0 | |
let x = Double(center.x) + radius * cos(radians) | |
let y = Double(center.y) + radius * sin(radians) | |
if i == angle { | |
path.move(to: CGPoint(x: x, y: y)) | |
} else { | |
path.addLine(to: CGPoint(x: x, y: y)) | |
} | |
} | |
path.closeSubpath() | |
return path | |
} | |
} | |
struct Star: Shape { | |
let sides:Int | |
let startAngle:Int | |
let radiusWidth:Double | |
let radiusHeight:Double | |
let percent:Double | |
func path(in rect: CGRect) -> Path { | |
var path = Path() | |
let edge = Double(rect.width * CGFloat(percent/100)) | |
let center:CGPoint = CGPoint(x: rect.width / 2, y: rect.height / 2) | |
let skew = 360 / (sides * 2) | |
let step = 360 / sides | |
var radius: CGSize | |
for i in stride(from: startAngle, to: (360 + startAngle), by: 360/sides) { | |
radius = CGSize(width: radiusWidth, height: radiusHeight) | |
let outside = calcAngle(start: startAngle, angle: i + skew, center: center, radius: radius) | |
radius = CGSize(width: radiusWidth - edge, height: radiusHeight - edge) | |
let inside = calcAngle(start: startAngle, angle: i, center: center, radius: radius) | |
let inside2 = calcAngle(start: startAngle, angle: i + step, center: center, radius: radius) | |
if i == 0 { | |
path.move(to: inside) | |
} | |
path.addLine(to: outside) | |
path.addLine(to: inside2) | |
} | |
return path | |
} | |
func calcAngle(start: Int, angle: Int, center: CGPoint, radius:CGSize) -> CGPoint { | |
let radians = Double(angle) * Double.pi / 180.0 | |
let x = Double(center.x) + Double(radius.width) * cos(radians) | |
let y = Double(center.y) + Double(radius.height) * sin(radians) | |
let angle2 = CGPoint(x: x, y: y) | |
return angle2 | |
} | |
} | |
enum Shapes { | |
case square | |
case circle | |
} | |
enum ColoursAvailable:Int { | |
case colorA = 0 | |
case colorB = 1 | |
case colorC = 2 | |
case colorD = 3 | |
case colorE = 4 | |
case colorF = 5 | |
case colorG = 6 | |
} | |
let blueA = Color(hex: 0x00E7FF) | |
let blueB = Color(hex: 0x009EFF) | |
let blueC = Color(hex: 0x0014FF) | |
let redA = Color(hex: 0xFFB4B4) | |
let redB = Color(hex: 0xFFDEB4) | |
let redC = Color(hex: 0xFDF7C3) | |
let greenA = Color(hex: 0xFF4949) | |
let greenB = Color(hex: 0xFF8D29) | |
let greenC = Color(hex: 0xFFCD38) | |
class ColorBuilds { | |
let blueP:[UInt] = [0x0000b3, 0x0010d9, 0x0020ff, 0x0040ff, 0x0060ff, 0x0080ff, 0x009fff, 0x00bfff, 0x00ffff,0x000000,0x000000,0x000000,0x000000] | |
var colorsG:[Color] = [] | |
var colorsA = [Color.red, Color.yellow, Color.blue] | |
init() { | |
for color in 0..<blueP.count { | |
colorsG.append(Color(hex: blueP[color])) | |
} | |
} | |
} | |
extension Color { | |
init(hex: UInt, alpha: Double = 1) { | |
self.init( | |
.sRGB, | |
red: Double((hex >> 16) & 0xff) / 255, | |
green: Double((hex >> 08) & 0xff) / 255, | |
blue: Double((hex >> 00) & 0xff) / 255, | |
opacity: alpha | |
) | |
} | |
} | |
struct Attributes { | |
//@State var colorIndex:ColoursAvailable | |
static var coloursB = [Color.blue, Color.purple, Color.indigo] | |
static var coloursC = [Color.teal, Color.green, Color.mint] | |
static var coloursD = [blueA, blueB, blueC] | |
static var coloursE = [redA, redB, redC] | |
static var coloursF = [greenA,greenB,greenC] | |
//@State var birthRate = 10 | |
//@State var targetRange = Double.random(in: 2...4) | |
var sourceRange = Double.random(in: 4...8) | |
var xAcceleration:Double | |
var yAcceleration:Double | |
var xSize:Double | |
var ySize:Double | |
//@State var colorCount:Int = Int.random(in: 0...colorsG.count) | |
var scaleRange = Double.random(in: 0.1...0.9) | |
var angle = Double.random(in: 0...359) | |
//@State var delay = Double.random(in: 0...4) | |
var delay = 0.1 | |
var shapes:Shapes | |
//@State var speed = 0.01 | |
//init(radius:CGSize, colorGroup:ColoursAvailable,xSize:Double = 16,ySize:Double = 16) { | |
init(radius:CGSize, angle:Double, xSize:Double = 16,ySize:Double = 16) { | |
let radians = Double(angle) * Double.pi / 180.0 | |
let xValue = Double(radius.width) * Double(cos(radians)) | |
let yValue = Double(radius.height) * Double(sin(radians)) | |
xAcceleration = xValue | |
yAcceleration = yValue | |
shapes = .circle | |
// colorIndex = colorGroup | |
self.xSize = xSize | |
self.ySize = ySize | |
} | |
init(rect:CGSize, xSize:Double = 16,ySize:Double = 16) { | |
var rndH:Double = 0 | |
var rndW:Double = 0 | |
if rect.height == 0 { | |
rndH = Double.random(in: -abs(rect.width)...abs(rect.width)) | |
xAcceleration = rect.width | |
yAcceleration = rndH | |
} else { | |
rndW = Double.random(in: -abs(rect.height)...abs(rect.height)) | |
xAcceleration = rndW | |
yAcceleration = rect.height | |
} | |
shapes = .square | |
//colorIndex = colorGroup | |
self.xSize = xSize | |
self.ySize = ySize | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment