4

要求如下:

我们需要将值映射到颜色。所以每个离散值都会有一种颜色。

我们允许用户指定 amaxColor minColor,但允许他们指定表示阴影数量的 bin 数量。因此,如果maxColor选择的是Color.GREENbins= 5,那么我们希望有 5 种绿色,选择为最大的颜色是最暗的,其余四种将按照增加亮度的顺序排列。

//Give me a list of 5 shades of Green with the first argument being the darkest.
List<Color> greenShades = calculateShades(Color.GREEN,5);

//Give me a list of 7 shades of RED with the first argument being the darkest.
List<Color> greenShades = calculateShades(Color.RED,7);

当我在 Java 中编码时,我将问题标记为 Java。但我知道这只是一种算法。所以在其他语言(如 JavaScript)中实现/想法也是可以接受的。

4

5 回答 5

13

基本概念围绕着基于一小部分源生成颜色的想法......

也就是说,如果你想要 5 个波段,每个波段的强度将是最后一个波段的 1/5...

public List<Color> getColorBands(Color color, int bands) {

    List<Color> colorBands = new ArrayList<>(bands);
    for (int index = 0; index < bands; index++) {
        colorBands.add(darken(color, (double) index / (double) bands));
    }
    return colorBands;

}

public static Color darken(Color color, double fraction) {

    int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
    int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
    int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));

    int alpha = color.getAlpha();

    return new Color(red, green, blue, alpha);

}

作为一个快速而讨厌的例子......

在此处输入图像描述

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class ColorBands {

    public static void main(String[] args) {
        new ColorBands();
    }

    public ColorBands() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JPanel bandsPane;
        private JSlider slider;
        private Timer changeTimer;

        public TestPane() {
            bandsPane = new JPanel(new GridBagLayout());
            slider = new JSlider(1, 100);
            setLayout(new BorderLayout());
            add(new JScrollPane(bandsPane));
            add(slider, BorderLayout.SOUTH);
            slider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    changeTimer.restart();
                }
            });

            changeTimer = new Timer(250, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int bands = slider.getValue();
                    List<Color> bandsList = getColorBands(Color.RED, bands);
                    bandsPane.removeAll();
                    GridBagConstraints gbc = new GridBagConstraints();
                    gbc.gridwidth = GridBagConstraints.REMAINDER;
                    gbc.insets = new Insets(1, 1, 1, 1);
                    for (Color color : bandsList) {
                        bandsPane.add(new ColorBand(color), gbc);
                    }
                    gbc.weighty = 1;
                    bandsPane.add(new JPanel(), gbc);
                    revalidate();
                    repaint();
                }
            });
            changeTimer.setRepeats(false);
            slider.setValue(1);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }
    }

    public List<Color> getColorBands(Color color, int bands) {

        List<Color> colorBands = new ArrayList<>(bands);
        for (int index = 0; index < bands; index++) {
            colorBands.add(darken(color, (double) index / (double) bands));
        }
        return colorBands;

    }

    public static Color darken(Color color, double fraction) {

        int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
        int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
        int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));

        int alpha = color.getAlpha();

        return new Color(red, green, blue, alpha);

    }

    public class ColorBand extends JPanel {

        public ColorBand(Color color) {
            setBackground(color);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 20);
        }

    }

}
于 2013-09-04T04:47:04.723 回答
0

RGB 颜色系统易于识别颜色比例,但缺乏操纵颜色的灵活性。使用平均值或比率会给你带来不想要的结果。简而言之,使用 RGB 颜色系统无法达到所需的效果。

解决方案是将颜色转换为 HSV 或 HSL(首选 HSL)并操纵值/亮度以获得结果。

看一下转换算法:

HSL 到 RGB 颜色转换

在数学上,假设你有一个颜色 R,G,B 然后:

所需的箱数 = 5

Hue = <some value h>
Saturation = <some value s>
Luminosity = (max(R,G,B) + min (R,G,B))/2

现在对于相同的 h,s,您将有 5 个 L 值:

L1 = 0
L2 = ((1 * 100) / 4)
L3 = ((2 * 100) / 4)
L4 = ((3 * 100) / 4)
L5 = 100

在这里,因为第一个和最后一个 bin 将是黑白的,所以我们使用 4 而不是 5。

现在将 HSL 转换回 RGB 以获得所需的 RGB 颜色。

于 2013-09-04T20:29:25.290 回答
0

基本思想是您希望以 bin 大小为增量从您提供给 Color.WHITE (R=255, G=255, B=255) 的颜色中遍历每个颜色分量 (RGB)。这是一些可以做到这一点的代码。

public List<Color> calculateShades(Color baseColor, int numberShades)
{
  //decompose color into RGB
  int redMax  = baseColor.getRed();
  int greenMax  = baseColor.getGreen();
  int blueMax  = baseColor.getBlue();


  //Max color component in RGB
  final int MAX_COMPONENT = 255;

  //bin sizes for each color component
  int redDelta = (MAX_COMPONENT - redMax) / numberShades;
  int greenDelta = (MAX_COMPONENT - greenMax) / numberShades;
  int blueDelta = (MAX_COMPONENT - blueMax) / numberShades;

  List<Color> colors = new ArrayList<Color>();

  int redCurrent = redMax;
  int greenCurrent = greenMax;
  int blueCurrent = blueMax;

  //now step through each shade, and decrease darkness by adding color to it
  for(int i = 0; i < numberShades; i++)
  {

     //step up by the bin size, but stop at the max color component (255)
     redCurrent = (redCurrent+redDelta) < MAX_COMPONENT ? (redCurrent + redDelta ) : MAX_COMPONENT;
     greenCurrent = (greenCurrent+greenDelta) < MAX_COMPONENT ? (greenCurrent + greenDelta ) : MAX_COMPONENT;
     blueCurrent = (blueCurrent+blueDelta) < MAX_COMPONENT ? (blueCurrent + blueDelta ) : MAX_COMPONENT;

     Color nextShade = new Color(redCurrent, greenCurrent, blueCurrent);
     colors.add(nextShade);
  }

  return colors;
}
于 2013-09-04T05:00:58.353 回答
0

看一下 Java 源代码Color#darker(),用不同的逻辑应用相同的逻辑FACTOR

public Color darker() {
    return new Color(Math.max((int)(getRed()  *FACTOR), 0), 
         Math.max((int)(getGreen()*FACTOR), 0),
         Math.max((int)(getBlue() *FACTOR), 0));
}
于 2013-09-04T05:08:05.133 回答
0

除了上面的 MadProgrammer 回答之外,您还可以减轻颜色,所以整个事情可能看起来像这样:

public static List<Color> getColorBands(
        Color color, 
        int bands, 
        SortDirection direction) {

    List<Color> colorBands = new ArrayList<>(bands);
    if(direction.equals(SortDirection.ASC)) {
        
        for (int index = 0; index < bands; index++)
            colorBands.add(lighten(color, (double) index / (double) bands));
    }
    if(direction.equals(SortDirection.DESC)) {
        
        for (int index = 0; index < bands; index++)
            colorBands.add(darken(color, (double) index / (double) bands));
    }       
    return colorBands;
}

public static Color darken(Color color, double fraction) {

    int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
    int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
    int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));

    int alpha = color.getAlpha();

    return new Color(red, green, blue, alpha);
}

public static Color lighten(Color color, double fraction) {

    int red = (int) Math.round(Math.min(255, color.getRed() + 255 * fraction));
    int green = (int) Math.round(Math.min(255, color.getGreen() + 255 * fraction));
    int blue = (int) Math.round(Math.min(255, color.getBlue() + 255 * fraction));

    int alpha = color.getAlpha();

    return new Color(red, green, blue, alpha);
}
于 2021-12-01T21:28:59.533 回答