analogous method
A set of colors with differing hues, equidistant in temperature.
In art, this is usually described as a set of 5 colors on a color wheel divided into 12 sections. This method allows provision of either of those values.
Behavior is undefined when count
or divisions
is 0.
When divisions < count, colors repeat.
count
The number of colors to return, includes the input color.
divisions
The number of divisions on the color wheel.
Implementation
List<Hct> analogous({int count = 5, int divisions = 12}) {
final startHue = input.hue.round();
final startHct = hctsByHue[startHue];
var lastTemp = relativeTemperature(startHct);
List<Hct> allColors = [startHct];
var absoluteTotalTempDelta = 0.0;
for (int i = 0; i < 360; i++) {
final hue = MathUtils.sanitizeDegreesInt(startHue + i);
final hct = hctsByHue[hue];
final temp = relativeTemperature(hct);
final tempDelta = (temp - lastTemp).abs();
lastTemp = temp;
absoluteTotalTempDelta += tempDelta;
}
var hueAddend = 1;
final tempStep = absoluteTotalTempDelta / divisions;
var totalTempDelta = 0.0;
lastTemp = relativeTemperature(startHct);
while (allColors.length < divisions) {
final hue = MathUtils.sanitizeDegreesInt(startHue + hueAddend);
final hct = hctsByHue[hue];
final temp = relativeTemperature(hct);
final tempDelta = (temp - lastTemp).abs();
totalTempDelta += tempDelta;
final desiredTotalTempDeltaForIndex = allColors.length * tempStep;
var indexSatisfied = totalTempDelta >= desiredTotalTempDeltaForIndex;
var indexAddend = 1;
// Keep adding this hue to the answers until its temperature is
// insufficient. This ensures consistent behavior when there aren't
// [divisions] discrete steps between 0 and 360 in hue with [tempStep]
// delta in temperature between them.
//
// For example, white and black have no analogues: there are no other
// colors at T100/T0. Therefore, they should just be added to the array
// as answers.
while (indexSatisfied && allColors.length < divisions) {
allColors.add(hct);
final desiredTotalTempDeltaForIndex =
((allColors.length + indexAddend) * tempStep);
indexSatisfied = totalTempDelta >= desiredTotalTempDeltaForIndex;
indexAddend++;
}
lastTemp = temp;
hueAddend++;
if (hueAddend > 360) {
while (allColors.length < divisions) {
allColors.add(hct);
}
break;
}
}
final answers = <Hct>[input];
// First, generate analogues from rotating counter-clockwise.
final increaseHueCount = ((count - 1) / 2.0).floor();
for (int i = 1; i < (increaseHueCount + 1); i++) {
var index = 0 - i;
while (index < 0) {
index = allColors.length + index;
}
if (index >= allColors.length) {
index = index % allColors.length;
}
answers.insert(0, allColors[index]);
}
// Second, generate analogues from rotating clockwise.
final decreaseHueCount = count - increaseHueCount - 1;
for (int i = 1; i < (decreaseHueCount + 1); i++) {
var index = i;
while (index < 0) {
index = allColors.length + index;
}
if (index >= allColors.length) {
index = index % allColors.length;
}
answers.add(allColors[index]);
}
return answers;
}