New method to get colour given distance and variable parameter
danhalliday opened this issue · comments
I'm working on a project that needs a fair bit of colour work, and thought I'd take a stab at contributing here as your project already has some of what I'll need (LAB support).
I'm looking to add some methods which produce a derivative colour with a set perceptual distance, based on altering some parameter of the original colour. For example, say you want a highlight colour that's just noticeable on top of a given background colour, regardless what the background is. Maybe it'd look something like this:
let highlight = base.colourWithDistance(5, variable: .CIE_L, direction: .Up, type: .CIE76)
I thought I knew how to do it (rearrange the various formulas in the distanceFromColor:colortype:distanceType
methods and solve for variable
), but I'm stuck on the basic LAB manipulation. Does anyone have an idea why in the following code, when I change L, A and B also change? Am I misunderstanding how the LAB model works? Are L, A and B not independent?
let firstColour = ...
let dictionary1 = firstColour.CIE_LabDictionary() as [NSString:NSNumber]
println(dictionary1)
dictionary1[kColoursCIE_L] = 60
let secondColour = UIColor(fromCIE_LabDictionary: dictionary1)
dictionary2 = secondColour.CIE_LabDictionary() as [NSString:NSNumber]
println(dictionary2)
// Gives:
// [LABa-A: -57.8215, LABa-L: 91.2403, LABa-B: 87.4349, LABa-a: 1]
// [LABa-A: -54.8815, LABa-L: 60.2418, LABa-B: 61.5021, LABa-a: 1]
Thanks for any input!
My initial mathematical inclination is that _a and b should be unaffected as L changes, but I'm not extremely familiar with how it all works. I basically just transcribed the formulas from Wikipedia (going from RGB to L_a_b, and then the distances between L_ab values).
My guess is that there is some loss of precision with the RGB -> L_a_b and vice versa calculations that make it inexact. I'll explore this in depth when I get some free time.
Just tried this with an Objective-C only app and seems to confirm it's a precision thing, as the following works fine (to several decimal places):
NSColor *first = [NSColor seafoamColor];
NSArray *firstLab = [first CIE_LabArray];
float l = [[firstLab objectAtIndex:0] floatValue];
float a = [[firstLab objectAtIndex:1] floatValue];
float b = [[firstLab objectAtIndex:2] floatValue];
NSLog(@"%@", firstLab);
NSColor *second = [NSColor colorFromCIE_LabArray:@[@(l-50), @(a), @(b), @1]];
NSArray *secondLab = [second CIE_LabArray];
NSLog(@"%@", secondLab);
// Gives:
// ("80.66308836072838", "-58.30120582025216", "30.79395128553721", 1)
// ("30.66368317685438", "-58.30043357569728", "30.79344302647758", 1)
I'm going to close this for now, as it seems to be a bug with Swift's precision and not necessarily my library.