5

Let's say I am given two colors.

public final static Color FAR = new Color(237, 237, 30);
public final static Color CLOSE = new Color(58, 237, 221);

How would I transition from one color to the next without dipping into dark colors?

I have come up with ideas such as

    double ratio = diff / range; // goes from 1 to 0
    int red = (int)Math.abs((ratio * FAR.getRed()) - ((1 - ratio) * CLOSE.getRed()));
    int green = (int)Math.abs((ratio * FAR.getGreen()) - ((1 - ratio) * CLOSE.getGreen()));
    int blue = (int)Math.abs((ratio * FAR.getBlue()) - ((1 - ratio) * CLOSE.getBlue()));

OR

    double ratio = diff / range; // goes from 1 to 0
    int red = (int) ((1 - (diff / range)) * FAR.getRed() + CLOSE.getRed() - FAR.getRed());
    int green = (int) ((1 - (diff / range)) * FAR.getGreen() + CLOSE.getGreen() - FAR.getGreen());
    int blue = (int) ((1 - (diff / range)) * FAR.getBlue() + CLOSE.getBlue() - FAR.getBlue());

But unfortunately none of them smoothly transition from one color to the next. Would anyone know how to do so while keeping the color bright and not dipping into darker colors, or how to ensure that gradient transition is smooth rather than slow at first then fast and then slow again?

I really ca not come up with any formula.

Quillion
  • 6,346
  • 11
  • 60
  • 97
  • 2
    Does `diff` and `range` both integers ? If yes, take care about integer division. – Alexis C. Nov 07 '13 at 16:38
  • I am using them inside a loop, where I start diff from 30 and go down to 0. My range is 30 double. So I use it as means for smoothly going from 1 to 0 in 30 steps. However I think number of steps should not matter as much. – Quillion Nov 07 '13 at 16:41
  • possible duplicate of [Algorithm to generate RGB graduated colors in PHP](http://stackoverflow.com/questions/8214979/algorithm-to-generate-rgb-graduated-colors-in-php) – mbeckish Nov 07 '13 at 16:44
  • 2
    Think of the RGB colorspace as a [cube](http://en.wikipedia.org/wiki/RGB_color_space). Find your two colors on the cube, and then decide how you want to travel from one point to the other to get the desired transitional colors. For example, you could bore a tunnel through the cube to go directly from one color to another. Or, you could restrict travel to stay along the surface of the cube, to avoid unsaturated, grayish colors. – mbeckish Nov 07 '13 at 16:48

3 Answers3

7

You're using the wrong sign in the calcuations. Should be plus, not minus, to apply the ratio properly.

int red = (int)Math.abs((ratio * FAR.getRed()) + ((1 - ratio) * CLOSE.getRed()));
int green = (int)Math.abs((ratio * FAR.getGreen()) + ((1 - ratio) * CLOSE.getGreen()));
int blue = (int)Math.abs((ratio * FAR.getBlue()) + ((1 - ratio) * CLOSE.getBlue()));

The reason you are getting dark colours with your existing implementation is that with (-), they would often fall close to zero (less than 50? or negative but greater than -50?) and in the negative case, well, you are taking the absolute value so it becomes a small positive number, i.e. a dark colour.

Alex Walker
  • 2,337
  • 1
  • 17
  • 32
  • Thanks worked like a charm :) this was a tough problem to solve, and you saved me many hours of reading about colors – Quillion Nov 07 '13 at 17:11
  • 1
    Glad to help. If you're performing this task several times, it's well worth writing a helper function. Say, `int interpolate(double ratio, int value1, int value2)` that returns `(int)Math.abs((ratio * value1) + ((1 - ratio) * value2))`. That way it'll always be clear what you're trying to do when transitioning colours. – Alex Walker Nov 07 '13 at 17:25
  • This is something I was messing around for, for fun, so I do not want to bother writing helper functions. However I am doing more color tricks that I want to learn, so I think I will post more questions later on. – Quillion Nov 07 '13 at 17:29
1
(ratio * FAR.getGreen()) + ((1 - ratio) * CLOSE.getGreen())

if ratio goest from 0 to 1, then this is weighted average, lets say ratio = 1/2, then it would be aritmetical average, if ratio = 1/3, then it is weighted average where FAR has weight 1 and CLOSE has weight 2

kajacx
  • 12,361
  • 5
  • 43
  • 70
  • Yes I do use weighted average, because if ratio is 0 I want color to be CLOSE if ratio is 1 then I want color to be FAR, however I want inbetween to be smooth and bright. – Quillion Nov 07 '13 at 16:49
1

This works nicely for me:

// Steps between fading from one colour to another.
private static final int FadeSteps = 25;

private void fade(Label panel, Color colour) throws InterruptedException {
  final Color oldColour = panel.getBackground();
  final int dRed = colour.getRed() - oldColour.getRed();
  final int dGreen = colour.getGreen() - oldColour.getGreen();
  final int dBlue = colour.getBlue() - oldColour.getBlue();
  // No point if no difference.
  if (dRed != 0 || dGreen != 0 || dBlue != 0) {
    // Do it in n steps.
    for (int i = 0; i <= FadeSteps; i++) {
      final Color c = new Color(
              oldColour.getRed() + ((dRed * i) / FadeSteps),
              oldColour.getGreen() + ((dGreen * i) / FadeSteps),
              oldColour.getBlue() + ((dBlue * i) / FadeSteps));
      panel.setBackground(c);
      Thread.sleep(10);
    }
  }
}

Not the neatest bit of code but it works.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213