Swift Generate A Random Color On A Colorwheel - swift

I'm using the SwiftHSVColorPicker framework and needed to generate a random color on the color wheel.My current way of doing works but something that brightness is off.Here is my code
func generateRandomColor() -> UIColor {
let lowerx : UInt32 = UInt32(0.0)
let upperx : UInt32 = 707
let randomNumberx = arc4random_uniform(upperx - lowerx) + lowerx
let lowery : UInt32 = UInt32(0.0)
let uppery : UInt32 = 707
let randomNumbery = arc4random_uniform(upperx - lowerx) + lowerx
let c = Colorwheel.colorWheel.hueSaturationAtPoint(CGPoint(x: Double(randomNumberx), y: Double(randomNumbery)))
let brightness = 1.0
return UIColor(hue: c.hue, saturation: c.saturation, brightness: CGFloat(brightness), alpha: 1.0)
}

Why don't you use something like
func getRandomColor() -> UIColor{
let randomRed:CGFloat = CGFloat(arc4random()) / CGFloat(UInt32.max)
let randomGreen:CGFloat = CGFloat(arc4random()) / CGFloat(UInt32.max)
let randomBlue:CGFloat = CGFloat(arc4random()) / CGFloat(UInt32.max)
return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0)
}
EDIT:
Try this, In this hue,brightness is also there
func generateRandomColor() -> UIColor {
let hue : CGFloat = CGFloat(arc4random() % 256) / 256 // use 256 to get full range from 0.0 to 1.0
let saturation : CGFloat = CGFloat(arc4random() % 128) / 256 + 0.5 // from 0.5 to 1.0 to stay away from white
let brightness : CGFloat = CGFloat(arc4random() % 128) / 256 + 0.5 // from 0.5 to 1.0 to stay away from black
return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1)
}
SwiftHSVColorPicker results

Related

How to not cause dark gray color to be transparent removing background from image

I'm having an issue where when I try to remove the green from an image (in this case the image background) but all the dark grays (within the part of the image I want to keep) become semi-transparent. I am unsure why, would like some advice on how to:
func chromaKeyFilter(fromHue: CGFloat, toHue: CGFloat) -> CIFilter? {
let size = 64
var cubeRGB = [Float]()
for z in 0 ..< size {
let blue = CGFloat(z) / CGFloat(size-1)
for y in 0 ..< size {
let green = CGFloat(y) / CGFloat(size-1)
for x in 0 ..< size {
let red = CGFloat(x) / CGFloat(size-1)
let color = UIColor(red: red, green: green, blue: blue, alpha: 1)
let hueColor = color.hsbColor
let alpha: CGFloat = (hueColor.hue >= fromHue && hueColor.hue <= toHue) ? 0 : 1
cubeRGB.append(Float(red * alpha))
cubeRGB.append(Float(green * alpha))
cubeRGB.append(Float(blue * alpha))
cubeRGB.append(Float(alpha))
}
}
}
let data = Data(bytes: cubeRGB, count: cubeRGB.count * MemoryLayout<Float>.size)
let params: [String: Any] = ["inputCubeDimension": size, "inputCubeData": data]
return CIFilter(name: "CIColorCube", parameters: params)
}
func filterPixels(foregroundCIImage: CIImage) -> CIImage {
let chromaCIFilter = self.chromaKeyFilter(fromHue: 0.33, toHue: 0.34)
chromaCIFilter?.setValue(foregroundCIImage, forKey: kCIInputImageKey)
let sourceCIImageWithoutBackground = chromaCIFilter?.outputImage
var image = CIImage()
if let filteredImage = sourceCIImageWithoutBackground {
image = filteredImage
}
return image
}
}
extension UIColor {
/// Decomposes UIColor to its HSBA components
var hsbColor: HSBColor {
var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
self.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return HSBColor(hue: h, saturation: s, brightness: b, alpha: a)
}
/// Holds the CGFloat values of HSBA components of a color
public struct HSBColor {
var hue: CGFloat
var saturation: CGFloat
var brightness: CGFloat
var alpha: CGFloat
}
}
Sample image:
Your code is correct, but remember that a dark gray could really be a very dark green.
On this line:
let alpha: CGFloat = (hueColor.hue >= fromHue && hueColor.hue <= toHue) ? 0 : 1
I would take brightness/saturation into account. For example
let alpha: CGFloat = (hueColor.saturation > 0.1 && hueColor.hue >= fromHue && hueColor.hue <= toHue) ? 0 : 1

SceneKit - How to Add Animations to Change SCNNode's Color?

I would like to know how I can animate an SCNNode's color using Swift.
For example: I would like the node to constantly be changing color or I would like the node to fade from black to blue.
Do I use the SCNAction fadeIn or fadeOut?
You can create a custom action.
If you have a red sphere in your scene
let sphereNode = scene.rootNode.childNode(withName: "sphere", recursively: false)!
sphereNode.geometry!.firstMaterial!.diffuse.contents = UIColor.red
This is how you build the custom action
let changeColor = SCNAction.customAction(duration: 10) { (node, elapsedTime) -> () in
let percentage = elapsedTime / 5
let color = UIColor(red: 1 - percentage, green: percentage, blue: 0, alpha: 1)
node.geometry!.firstMaterial!.diffuse.contents = color
}
Finally you just need to run the action on the sphere
sphereNode.runAction(changeColor)
Result
Got idea from #Luca Angeletti, I write the code so we can animate between any colors, include their alphas:
func aniColor(from: UIColor, to: UIColor, percentage: CGFloat) -> UIColor {
let fromComponents = from.cgColor.components!
let toComponents = to.cgColor.components!
let color = UIColor(red: fromComponents[0] + (toComponents[0] - fromComponents[0]) * percentage,
green: fromComponents[1] + (toComponents[1] - fromComponents[1]) * percentage,
blue: fromComponents[2] + (toComponents[2] - fromComponents[2]) * percentage,
alpha: fromComponents[3] + (toComponents[3] - fromComponents[3]) * percentage)
return color
}
Use:
let oldColor = UIColor.red
let newColor = UIColor(colorLiteralRed: 0.0, green: 0.0, blue: 1.0, alpha: 0.5)
let duration: TimeInterval = 1
let act0 = SCNAction.customAction(duration: duration, action: { (node, elapsedTime) in
let percentage = elapsedTime / CGFloat(duration)
node.geometry?.firstMaterial?.diffuse.contents = self.aniColor(from: newColor, to: oldColor, percentage: percentage)
})
let act1 = SCNAction.customAction(duration: duration, action: { (node, elapsedTime) in
let percentage = elapsedTime / CGFloat(duration)
node.geometry?.firstMaterial?.diffuse.contents = self.aniColor(from: oldColor, to: newColor, percentage: percentage)
})
let act = SCNAction.repeatForever(SCNAction.sequence([act0, act1]))
node.runAction(act)
I am using this function;
You just give name of material, how long animation will take and current color and
the which color will be reached.
You give color as arrays (red, green, blue and alpha);
let startColor: [CGFloat] = [0.5, 0.5, 1, 1]
let targetColor: [CGFloat] = [1, 1, 1, 1]
This is how you call function;
changeColorWithAnimation(duration : 5,
materialName: myCubeMaterial,
start : startColor,
end : targetColor)
func chageColorWithAnimation(duration: CGFloat,
materialName: SCNMaterial,
start: [CGFloat],
end: [CGFloat]){
let rs = (end[0] - start[0]) / duration
let gs = (end[1] - start[1]) / duration
let bs = (end[2] - start[2]) / duration
let alphas = (end[3] - start[3]) / duration
let changeColor = SCNAction.customAction(duration: TimeInterval(duration)) { (node, elapsedTime) -> () in
let red = start[0] + rs * elapsedTime
let green = start[1] + gs * elapsedTime
let blue = start[2] + bs * elapsedTime
let alpha = start[3] + alphas * elapsedTime
materialName.diffuse.contents = UIColor(displayP3Red:red,
green: green,
blue: blue,
alpha: alpha)
}
rootNode.runAction(changeColor)
}

Computing complementary, triadic, tetradic, and analagous colors

I have created swift functions, where I send color value to and want to return triadic and tetrads values. It sort of works, but I am not happy about the color results. Can anyone help me to fine-tune the formula please?
I was following few sources, but the returned colours were too bright or saturated in comparison to several online web based color schemes. I know it's a matter of preference as well and I kinda like the results of the code below, but in some instances of colors the result of one color returned is way too close to the original one, so it's barely visible. It applies only to a few colors...
I was using the formula from here:
my code:
func getTriadColor(color: UIColor) -> (UIColor, UIColor){
var hue : CGFloat = 0
var saturation : CGFloat = 0
var brightness : CGFloat = 0
var alpha : CGFloat = 0
let triadHue = CGFloat(color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha))
let triadColor1 = UIColor(hue: (triadHue + 0.33) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
let triadColor2 = UIColor(hue: (triadHue + 0.66) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
return (triadColor1, triadColor2)
}
func getTetradColor(color: UIColor) -> (UIColor, UIColor, UIColor){
var hue : CGFloat = 0
var saturation : CGFloat = 0
var brightness : CGFloat = 0
var alpha : CGFloat = 0
let tetradHue = CGFloat(color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha))
let tetradColor1 = UIColor(hue: (tetradHue + 0.25) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
let tetradColor2 = UIColor(hue: (tetradHue + 0.5) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
let tetradColor3 = UIColor(hue: (tetradHue + 0.75) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
return (tetradColor1, tetradColor2, tetradColor3)
}
And I also found nice clean code for finding complementary color, which I am very happy about the results
func getComplementColor(color: UIColor) -> UIColor{
let ciColor = CIColor(color: color)
let compRed: CGFloat = 1.0 - ciColor.red
let compGreen: CGFloat = 1.0 - ciColor.green
let compBlue: CGFloat = 1.0 - ciColor.blue
return UIColor(red: compRed, green: compGreen, blue: compBlue, alpha: 1.0)
}
Your screen shot is of this web page. (Wayback Machine link because, six years later, the page has been deleted.) The formulas on that page are incorrect, because they specify the use of the absolute value function instead of the modulo function. That is, for example, your screen shot defines
H1 = |(H0 + 180°) - 360°|
but consider what this gives for the input H0 = 90°:
H1 = |(90° + 180°) - 360°| = |270° - 360°| = |-90°| = 90°
Do you think that the complementary hue of H0 = 90° is H1 = 90°, the same hue?
The correct formula is
H1 = (H0 + 180°) mod 360°
where “mod” is short for “modulo” and means “the remainder after dividing by”. In other words, if the answer would be above 360°, subtract 360°. For H0 = 90°, this gives the correct answer of H1 = 270°.
But you don't even have this problem in your code, because you didn't use the absolute value function (or the modulo function) in your code. Since you're not doing anything to keep your hue values in the range 0…1, your hue values that are less than zero are clipped to zero, and your hue values above one are clipped to one (and both zero and one mean red).
Your getComplementColor is also not at all the standard definition of the “complementary color”.
Here are the correct definitions:
extension UIColor {
var complement: UIColor {
return self.withHueOffset(0.5)
}
var splitComplement0: UIColor {
return self.withHueOffset(150 / 360)
}
var splitComplement1: UIColor {
return self.withHueOffset(210 / 360)
}
var triadic0: UIColor {
return self.withHueOffset(120 / 360)
}
var triadic1: UIColor {
return self.withHueOffset(240 / 360)
}
var tetradic0: UIColor {
return self.withHueOffset(0.25)
}
var tetradic1: UIColor {
return self.complement
}
var tetradic2: UIColor {
return self.withHueOffset(0.75)
}
var analagous0: UIColor {
return self.withHueOffset(-1 / 12)
}
var analagous1: UIColor {
return self.withHueOffset(1 / 12)
}
func withHueOffset(offset: CGFloat) -> UIColor {
var h: CGFloat = 0
var s: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
self.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: fmod(h + offset, 1), saturation: s, brightness: b, alpha: a)
}
}
Here are some examples of complementary colors (original on top, complementary beneath):
Here are split complementary colors (original on top):
Here are triadic colors (original on top):
Here are tetradic colors (original on top):
Here are analagous colors (original in the middle):
Here is the playground I used to generate those images:
import XCPlayground
import UIKit
let view = UIView(frame: CGRectMake(0, 0, 320, 480))
view.backgroundColor = [#Color(colorLiteralRed: 0.9607843137254902, green: 0.9607843137254902, blue: 0.9607843137254902, alpha: 1)#]
let vStack = UIStackView(frame: view.bounds)
vStack.autoresizingMask = [ .FlexibleWidth, .FlexibleHeight ]
view.addSubview(vStack)
vStack.axis = .Vertical
vStack.distribution = .FillEqually
vStack.alignment = .Fill
vStack.spacing = 10
typealias ColorTransform = (UIColor) -> UIColor
func tile(color color: UIColor) -> UIView {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = color
return view
}
func strip(transforms: [ColorTransform]) -> UIStackView {
let strip = UIStackView()
strip.translatesAutoresizingMaskIntoConstraints = false
strip.axis = .Vertical
strip.distribution = .FillEqually
strip.alignment = .Fill
strip.spacing = 0
let hStacks = (0 ..< transforms.count).map { (i: Int) -> UIStackView in
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .Horizontal
stack.distribution = .FillEqually
stack.alignment = .Fill
stack.spacing = 4
strip.addArrangedSubview(stack)
return stack
}
for h in 0 ..< 10 {
let hue = CGFloat(h) / 10
let color = UIColor(hue: hue, saturation: 1, brightness: 1, alpha: 1)
for (i, transform) in transforms.enumerate() {
hStacks[i].addArrangedSubview(tile(color: transform(color)))
}
}
return strip
}
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.complement }]))
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.splitComplement0 },
{ $0.splitComplement1 }]))
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.triadic0 },
{ $0.triadic1 }]))
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.tetradic0 },
{ $0.tetradic1 },
{ $0.tetradic2 }]))
vStack.addArrangedSubview(strip([
{ $0.analagous0 },
{ $0 },
{ $0.analagous1 }]))
XCPlaygroundPage.currentPage.liveView = view

Is the method of using the | bitwise operator to mix two colours correct?

I am building an app that allows users to select two colors and see the result of mixing them. For example, the user selects red (#ff0000) and blue (#0000ff) and the result is purple (#ff00ff).
I started trying by writing 3 methods in a UIColor extension:
hexColor converts an Int to a color
intValue returns the color's integer representation. i.e. the reverse of hexColor
hexDescription returns the string representation of the color, such as "#ff00ff"
Here are the implementations, just in case this is needed:
public static func hexColor(hex: Int32) -> UIColor {
return UIColor.init(red: CGFloat((hex>>16)&0xFF) / 255.0, green: CGFloat((hex>>8)&0xFF) / 255.0, blue: CGFloat(hex&0xFF) / 255.0, alpha: 1.0)
}
public func intValue() -> Int {
var hexString = self.hexDescription()
hexString = hexString.substringFromIndex(hexString.startIndex.successor())
return Int(hexString, radix: 16)!
}
public func hexDescription() -> String {
var rF: CGFloat = 0,
gF: CGFloat = 0,
bF: CGFloat = 0,
aF: CGFloat = 0
self.getRed(&rF, green: &gF, blue: &bF, alpha: &aF)
let r = Int(rF * 255.0)
let g = Int(gF * 255.0)
let b = Int(bF * 255.0)
return "#" + String(format: "%02x%02x%02x", r, g, b)
}
Then I thought about how can I actually mix the colors. My first try is to get the average of the HSV values:
public func mixWith(color: UIColor) -> UIColor {
var myHue: CGFloat = 0
var mySat: CGFloat = 0
var myVal: CGFloat = 0
var otherHue: CGFloat = 0
var otherSat: CGFloat = 0
var otherVal: CGFloat = 0
self.getHue(&myHue, saturation: &mySat, brightness: &myVal, alpha: nil)
color.getHue(&otherHue, saturation: &otherSat, brightness: &otherVal, alpha: nil)
let averageHue = (myHue + otherHue) / 2.0
let averageSat = (mySat + otherSat) / 2.0
let averageVal = (myVal + otherVal) / 2.0
return UIColor(hue: averageHue, saturation: averageSat, brightness: averageVal, alpha: 1.0)
}
But this failed. When I mix blue and yellow, I get #00ff7f but it should be white.
Then I try to get the average of the int values:
public func mixWith2(color: UIColor) -> UIColor {
let average = (self.intValue() + color.intValue()) / 2
return UIColor.hexColor(Int32(average))
}
But again, blue mix with yellow is not white using the above method.
At the end, I decided to use bitwise operators. I tested |, & and ^. Surprisingly, this returns white!
UIColor.hexColor(Int32(UIColor.blueColor().intValue() |
UIColor.yellowColor().intValue()))
and so does this:
UIColor.hexColor(Int32(UIColor.blueColor().intValue() ^
UIColor.yellowColor().intValue()))
I did some other tests and this method passed all of them!
Is this a correct (gives correct result all the time) method of mixing two colors? If yes, how does it work?
The short answer is "no you can't" as in the comments, perhaps the easiest way of blending two colors is using a weighted average:
extension UIColor {
func blend(rhs:UIColor, midpoint left:CGFloat = 0.50) -> NSColor {
let right = 1.0 - left
var lr : CGFloat = 0
var lg : CGFloat = 0
var lb : CGFloat = 0
var la : CGFloat = 0
getRed(&lr, green: &lg, blue: &lb, alpha: &la)
var rr : CGFloat = 0
var rg : CGFloat = 0
var rb : CGFloat = 0
var ra : CGFloat = 0
rhs.getRed(&rr, green: &rg, blue: &rb, alpha: &ra)
return UIColor(
red: lr * left + rr * right,
green: lg * left + rg * right,
blue: lb * left + rb * right,
alpha: la * left + ra * right
)
}
}

Swift Random Color Generator

I am making a game where there's a grid of squares, and one is a different color (all colors are shades of turquoise). I'm using a function to generate two different random colors
func generateColors() -> (main: UIColor, diff: UIColor) {
let redMain: CGFloat = CGFloat(arc4random(104.0))
let greenMain: CGFloat = redMain + 105
let blueMain: CGFloat = redMain + 83
let redColorMain: CGFloat = redMain / 255.0
let greenColorMain: CGFloat = greenMain / 255.0
let blueColorMain: CGFLoat = blueMain / 255.0
let main = UIColor(red: redColorMain, green: greenColorMain, blue: blueColorMain, alpha: 0.8)
let redDiff: CGFloat = CGFloat(arc4random(104.0))
let greenDiff: CGFloat = redDiff + 105
let blueDiff: CGFloat = redDiff + 83
let redColorDiff: CGFloat = redDiff / 255.0
let greenColorDiff: CGFloat = greenDiff / 255.0
let blueColorDiff: CGFloat = blueDiff / 255
let diff = UIColor(red: redColorDiff, green: greenColorDiff, blue: blueColorDiff, alpha: 0.8)
return (main, diff)
}
However, I keep getting an error that says "Cannot invoke arc4random with an argument type 'Double'". Why does it say this, and do you know how to solve it? Thanks!
Try like this:
func generateColors() -> (main: UIColor, diff: UIColor) {
let redMain: CGFloat = CGFloat(arc4random_uniform(104))
let greenMain: CGFloat = redMain + 105.0
let blueMain: CGFloat = redMain + 83.0
let redColorMain: CGFloat = redMain / 255.0
let greenColorMain: CGFloat = greenMain / 255.0
let blueColorMain: CGFloat = blueMain / 255.0
let main = UIColor(red: redColorMain, green: greenColorMain, blue: blueColorMain, alpha: 0.8)
let redDiff: CGFloat = CGFloat(arc4random_uniform(104))
let greenDiff: CGFloat = redDiff + 105.0
let blueDiff: CGFloat = redDiff + 83.0
let redColorDiff: CGFloat = redDiff / 255.0
let greenColorDiff: CGFloat = greenDiff / 255.0
let blueColorDiff: CGFloat = blueDiff / 255.0
let diff = UIColor(red: redColorDiff, green: greenColorDiff, blue: blueColorDiff, alpha: 0.8)
return (main, diff)
}