Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Find Color at Point Between Two Colors

I have a problem: I need to be able to take two colors and make a 'virtual gradient' out of them. I then need to be able to find the color at any point on this line. My current approach is this:

if (fahrenheit < kBottomThreshold)
{
    return [UIColor colorWithRed:kBottomR/255.0f green:kBottomG/255.0f blue:kBottomB/255.0f alpha:1];
}
if (fahrenheit > kTopThreshold)
{
    return [UIColor colorWithRed:kTopR/255.0f green:kTopG/255.0f blue:kTopB/255.0f alpha:1];
}

double rDiff = kTopR - kBottomR;
double gDiff = kTopG - kBottomG;
double bDiff = kTopB - kBottomB;

double tempDiff = kTopThreshold - kBottomThreshold;

double rValue;
double gValue;
double bValue;

rValue = kBottomR + ((rDiff/tempDiff) * fahrenheit);
gValue = kBottomG + ((gDiff/tempDiff) * fahrenheit);
bValue = kBottomB + ((bDiff/tempDiff) * fahrenheit);

return [UIColor colorWithRed:rValue/255.0f green:gValue/255.0f blue:bValue/255.0f alpha:1];

Variables:

  • fahrenheit is a variable passed into my function that is the number on this virtual line that I want to find the color for.
  • kTopR, kTopB, and kTopG are the RGB values for one end of the gradient. Same for their kBottom counterparts.
  • kBottomThreshold and kTopThreshold are the endpoints of my gradient.

Here's my problem: When fahrenheit goes over either end of the gradient, the gradient seems to 'jump' to a different value.

I've included an example project, hosted on my S3 server, here.

You really need to download the project and try it on the simulator/device to see what I mean (unless you are crazy smart and can tell just by looking at the code)

like image 619
Undo Avatar asked Feb 22 '13 20:02

Undo


2 Answers

Swift - 3.0 && 4.0

extension UIColor {
    func toColor(_ color: UIColor, percentage: CGFloat) -> UIColor {
        let percentage = max(min(percentage, 100), 0) / 100
        switch percentage {
        case 0: return self
        case 1: return color
        default:
            var (r1, g1, b1, a1): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
            var (r2, g2, b2, a2): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
            guard self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) else { return self }
            guard color.getRed(&r2, green: &g2, blue: &b2, alpha: &a2) else { return self }

            return UIColor(red: CGFloat(r1 + (r2 - r1) * percentage),
                           green: CGFloat(g1 + (g2 - g1) * percentage),
                           blue: CGFloat(b1 + (b2 - b1) * percentage),
                           alpha: CGFloat(a1 + (a2 - a1) * percentage))
        }
    }
}

Usage:-

let colorRed = UIColor.red
let colorBlue = UIColor.blue

let colorOutput = colorRed.toColor(colorBlue, percentage: 50)

Result

enter image description here

like image 118
ramchandra n Avatar answered Sep 21 '22 11:09

ramchandra n


The problem is that you're not subtracting kBottomThreshold from farenheit.

But let's simplify.

First, we want to map the input temperature to a parameter t in the range [0 ... 1]. Then, we want to map t to an output in the range [kBottomR ... kTopR], and also to an output in the range [kBottomG ... kTopG], and also to an output in the range [kBottomB ... kTopB].

UIColor *colorForDegreesFahrenheit(double fahrenheit) {
    double t = (fahrenheit - kBottomThreshold) / (kTopThreshold - kBottomThreshold);

    // Clamp t to the range [0 ... 1].
    t = MAX(0.0, MIN(t, 1.0));

    double r = kBottomR + t * (kTopR - kBottomR);
    double g = kBottomG + t * (kTopG - kBottomG);
    double b = kBottomB + t * (kTopB - kBottomB);

    return [UIColor colorWithRed:r/255 green:g/255 blue:b/255 alpha:1];
}
like image 21
rob mayoff Avatar answered Sep 21 '22 11:09

rob mayoff