mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			151 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Swift
		
	
		
		
			
		
	
	
			151 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Swift
		
	
| 
								 
											6 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								import CryptoSwift
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								extension String {
							 | 
						||
| 
								 | 
							
								    func matches(_ regex: String) -> Bool {
							 | 
						||
| 
								 | 
							
								        return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								private class RNG {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    private let int32Max = Int(Int32.max) // 2147483647
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    private var seed: Int
							 | 
						||
| 
								 | 
							
								    private var initial: Int
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    init(seed: Int) {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        self.seed = seed % int32Max
							 | 
						||
| 
								 | 
							
								        if (self.seed <= 0) { self.seed += int32Max - 1 }
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        self.initial = self.seed
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    func next() -> Int {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        // Casting to Int64 incase number goes above Int32
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        let seed = (Int64(self.seed) * 16807) % Int64(int32Max)
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        self.seed = Int(seed)
							 | 
						||
| 
								 | 
							
								        return self.seed
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    func nextFloat() -> Float {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        return Float(next() - 1) / Float(int32Max - 1)
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    func nextCGFloat() -> CGFloat {
							 | 
						||
| 
								 | 
							
								        return CGFloat(nextFloat())
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    func reset() {
							 | 
						||
| 
								 | 
							
								        seed = initial
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								public class JazzIcon {
							 | 
						||
| 
								 | 
							
								    private let generator: RNG
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Colour palette
							 | 
						||
| 
								 | 
							
								    private var colours: [UIColor] = [
							 | 
						||
| 
								 | 
							
								        0x01888c, // Teal
							 | 
						||
| 
								 | 
							
								        0xfc7500, // bright orange
							 | 
						||
| 
								 | 
							
								        0x034f5d, // dark teal
							 | 
						||
| 
								 | 
							
								        0xE784BA, // light pink
							 | 
						||
| 
								 | 
							
								        0x81C8B6, // bright green
							 | 
						||
| 
								 | 
							
								        0xc7144c, // raspberry
							 | 
						||
| 
								 | 
							
								        0xf3c100, // goldenrod
							 | 
						||
| 
								 | 
							
								        0x1598f2, // lightning blue
							 | 
						||
| 
								 | 
							
								        0x2465e1, // sail blue
							 | 
						||
| 
								 | 
							
								        0xf19e02, // gold
							 | 
						||
| 
								 | 
							
								    ].map { UIColor(rgb: $0) }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    // Defaults
							 | 
						||
| 
								 | 
							
								    private let shapeCount = 4
							 | 
						||
| 
								 | 
							
								    private let wobble = 30
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    init(seed: Int, colours: [UIColor]? = nil) {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        self.generator = RNG(seed: seed)
							 | 
						||
| 
								 | 
							
								        if let colours = colours {
							 | 
						||
| 
								 | 
							
								            self.colours = colours
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    convenience init(seed: String, colours: [UIColor]? = nil) {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        // Ensure we have a correct hash
							 | 
						||
| 
								 | 
							
								        var hash = seed
							 | 
						||
| 
								 | 
							
								        if !hash.matches("^[0-9A-Fa-f]+$") || hash.count < 12 { hash = seed.sha512() }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        guard let number = Int(hash.substring(to: 12), radix: 16) else {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								            owsFailDebug("[JazzIcon] Failed to generate number from seed string: \(seed)")
							 | 
						||
| 
								 | 
							
								            self.init(seed: 1234, colours: colours)
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        self.init(seed: number, colours: colours)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    public func generateLayer(ofSize diameter: CGFloat) -> CALayer {
							 | 
						||
| 
								 | 
							
								        generator.reset()
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        let newColours = hueShift(colours: colours)
							 | 
						||
| 
								 | 
							
								        let shuffled = shuffle(newColours)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        let base = getSquareLayer(with: diameter, colour: shuffled[0].cgColor)
							 | 
						||
| 
								 | 
							
								        base.masksToBounds = true
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        for index in 0..<shapeCount {
							 | 
						||
| 
								 | 
							
								            let layer = generateShapeLayer(diameter: diameter, colour: shuffled[index + 1].cgColor, index: index, total: shapeCount - 1)
							 | 
						||
| 
								 | 
							
								            base.addSublayer(layer)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        return base
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    private func getSquareLayer(with diameter: CGFloat, colour: CGColor? = nil) -> CAShapeLayer {
							 | 
						||
| 
								 | 
							
								        let frame = CGRect(x: 0, y: 0, width: diameter, height: diameter)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        let layer = CAShapeLayer()
							 | 
						||
| 
								 | 
							
								        layer.frame = frame
							 | 
						||
| 
								 | 
							
								        layer.path = UIBezierPath(roundedRect: frame, cornerRadius: 0).cgPath
							 | 
						||
| 
								 | 
							
								        layer.fillColor = colour
							 | 
						||
| 
								 | 
							
								        return layer
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    private func generateShapeLayer(diameter: CGFloat, colour: CGColor, index: Int, total: Int) -> CALayer {
							 | 
						||
| 
								 | 
							
								        let center = diameter / 2
							 | 
						||
| 
								 | 
							
								        let firstRotation = generator.nextCGFloat()
							 | 
						||
| 
								 | 
							
								        let angle = CGFloat.pi * 2 * firstRotation
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        let a = diameter / CGFloat(total)
							 | 
						||
| 
								 | 
							
								        let b: CGFloat = generator.nextCGFloat()
							 | 
						||
| 
								 | 
							
								        let c = CGFloat(index) * a
							 | 
						||
| 
								 | 
							
								        let velocity = a * b + c
							 | 
						||
| 
								 | 
							
								        let translation = CGPoint(x: cos(angle) * velocity, y: sin(angle) * velocity)
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								        // Third random is a shape rotation ontop of all that
							 | 
						||
| 
								 | 
							
								        let secondRotation = generator.nextCGFloat()
							 | 
						||
| 
								 | 
							
								        let rotation = (firstRotation * 360.0) + (secondRotation * 180)
							 | 
						||
| 
								 | 
							
								        let radians = rotation.rounded(toPlaces: 1) * CGFloat.pi / 180.0
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        let layer = getSquareLayer(with: diameter, colour: colour)
							 | 
						||
| 
								 | 
							
								        layer.position = CGPoint(x: center + translation.x, y: center + translation.y)
							 | 
						||
| 
								 | 
							
								        layer.transform = CATransform3DMakeRotation(radians, 0, 0, center)
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        return layer
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    private func shuffle<T>(_ array: [T]) -> [T] {
							 | 
						||
| 
								 | 
							
								        var currentIndex = array.count
							 | 
						||
| 
								 | 
							
								        var mutated = array
							 | 
						||
| 
								 | 
							
								        while (currentIndex > 0) {
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								            let randomIndex = generator.next() % currentIndex
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								            currentIndex -= 1
							 | 
						||
| 
								 | 
							
								            mutated.swapAt(currentIndex, randomIndex)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return mutated
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    private func hueShift(colours: [UIColor]) -> [UIColor] {
							 | 
						||
| 
								 | 
							
								        let amount = generator.nextCGFloat() * 30 - CGFloat(wobble / 2);
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								        return colours.map { $0.adjustHue(by: amount) }
							 | 
						||
| 
								 
											6 years ago
										 
									 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |