Skip to content

Instantly share code, notes, and snippets.

@wizard1066
Created April 27, 2023 17:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wizard1066/df47cec2902a6aeb9ffafcc6bae2591a to your computer and use it in GitHub Desktop.
Save wizard1066/df47cec2902a6aeb9ffafcc6bae2591a to your computer and use it in GitHub Desktop.
//
// 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