0

I'm working on a Delphi FMX project where I need to generate a set of 10 random colors for use in a UI. The problem is that when I use TAlphaColorRec with random RGB values, many of the colors end up looking too similar.

For example, multiple shades of blue or green that are hard to tell apart.

This is my current code that generates the numbers:

function GenerateRandomColors(Count: UInt64): TArray<TAlphaColor>;
begin
  SetLength(Result, Count);
  for var i := 1 to Count do
  begin
    var C: TAlphaColorRec;
    C.R := Random(256);
    C.G := Random(256);
    C.B := Random(256);
    C.A := 255;
    Result[i-1] := C.Color;
  end;
end;

And I'm using it within this procedure:

procedure TForm1.GenerateColors;
begin
  Randomize;

  Memo1.Lines.Clear;
  GridLayout1.DeleteChildren;

  var Colors := GenerateRandomColors(10);
  for var Col in Colors do
  begin
    Memo1.Lines.Add(AlphaColorToString(Col));

    var Rect := TRectangle.Create(GridLayout1);
    Rect.Parent := GridLayout1;
    Rect.Fill.Color := Col;
    Rect.Stroke.Kind := TBrushKind.None;
  end;
end;

And this is how it looks:

Delphi Programming Colors

I need to modify GenerateRandomColors so that it generates colors that are more visually distinct from each other.


I have looked at the following questions already:

But the answers are not super useful because I need it to work with the Delphi Programming Language and most of those answers are using other programming languages.

Can someone please help me to modify GenerateRandomColors so that it generates distinct colors using Delphi within the FireMonkey (FMX) framework.

5

2 Answers 2

3

Rather than using RGB with random digits for all colors, it might be better to use HSL where L and S are fixed values and your 10 colors are offsets from a single random starting point with a minimum distance that you feel is pleasing. You could randomize L and S as well, just watch out for very low or high values which approach black/white.

Use the function System.UIConsts.HSLtoRGB to convert to RGB space. I've used this approach for coloring the slices of a pie chart so that each slice is visually different from its neighbor and it works well.

10 circles with different colors

The sample below is from the fixed function which I had to adjust slightly to reduce dependencies. It adjusts the S & L if it's odd to avoid near colors from being too close as the number of colors increase. The following is a VCL example, remove the AlphaColorToColor for FMX. I wouldn't random every color or you may end up with two being too close to each other. Just random the first one, then continue with an even offset for H wrapping back to 0 if it goes over 1 and adjust S and L appropriately.

uses
  System.UIConsts;

procedure TForm1.GenerateColors;
var
  H : single;
  i : integer;
begin
  SetLength(fColors,10);
  H := 0.0;
  for I := 0 to High(fColors) do
    begin
      if Odd(I) then
        fColors[i] := AlphaColorToColor( HSLtoRGB(H,0.7,0.7) )
      else
        fColors[i] := AlphaColorToColor( HSLtoRGB(H,0.5,0.5) );
      H := H + (1 / Length(fColors));
    end;
end;
Sign up to request clarification or add additional context in comments.

1 Comment

@Thanks. This is almost exactly what I need. I'm busy adapting it now. The key was System.UIConsts.HSLtoRGB.
2

Thanks skamradt for pointing me in the right direction. This is the function I ended up making and using:

uses
  System.UIConsts;

function GenerateRandomDistinctColors(Count: UInt64): TArray<TAlphaColor>;
begin
  SetLength(Result, Count);
  if Count = 0 then Exit;

  var reserved := 0;
  if Count > 0 then
  begin
    Result[reserved] := TAlphaColors.Black;
    Inc(reserved);
  end;
  if Count > 1 then
  begin
    Result[reserved] := TAlphaColors.White;
    Inc(reserved);
  end;
  if Count > 2 then
  begin
    Result[reserved] := TAlphaColors.Gray;
    Inc(reserved);
  end;

  var remain := Count - reserved;
  if remain <= 0 then Exit;

  var baseHue := Random * 360.0;
  var stepHue := 360.0 / remain;

  var j := 0;
  while j < remain do
  begin
    var hue := baseHue + j * stepHue + (Random * 2.0 - 1.0) * (stepHue * 0.08);
    hue := Frac(hue / 360.0) * 360.0;

    var sat := 0.6 + Random * 0.4; // 0.6..1.0
    var light := 0.45 + Random * 0.2; // 0.45..0.65

    Result[reserved + j] := HSLtoRGB(hue / 360.0, sat, light);
    Inc(j);
  end;
end;

It works similar to this answer, but modified to provide a slightly wider ranger of colors.

Here's an example of the colors it generated:

10 squares displaying different colors

They are now much more distinct from each other. 😁

1 Comment

This still needs to support different kinds of colorblindness ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.