Package se.llbit.chunky.renderer.ui

Source Code of se.llbit.chunky.renderer.ui.ColorPicker

/* Copyright (c) 2014 Jesper Öqvist <jesper@llbit.se>
*
* This file is part of Chunky.
*
* Chunky is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chunky is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Chunky.  If not, see <http://www.gnu.org/licenses/>.
*/
package se.llbit.chunky.renderer.ui;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Group;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import se.llbit.math.Color;
import se.llbit.math.Constants;
import se.llbit.math.Vector3d;

@SuppressWarnings("serial")
public class ColorPicker extends JDialog {

  private static final int NUM_SWATCHES = 10;

  private static final Vector3d[] staticHistory = new Vector3d[NUM_SWATCHES];
  static {
    Random r = new Random(System.currentTimeMillis());
    for (int i = 0; i < NUM_SWATCHES; ++i) {
      staticHistory[i] = new Vector3d();
      getStochasticColor(r, staticHistory[i],
          (i+0.5)/10.0, 1, 0.5);
    }
  }

  private final JPanel currentSwatch = new JPanel();
  private final JPanel[] swatches = new JPanel[NUM_SWATCHES];
  private final JPanel[] history = new JPanel[NUM_SWATCHES];
  private final Vector3d[] swatchColors = new Vector3d[NUM_SWATCHES];
  private final Vector3d[] historyColors = new Vector3d[NUM_SWATCHES];
  private final JTextField colorHex = new JTextField(6);
  private double hue = 0;
  private double saturation = 1;
  private double lightness = 0.5;
  private final HuePicker huePicker;
  private final LightnessPicker lightnessPicker;
  private final SaturationPicker saturationPicker;
  private final Vector3d currentColor = new Vector3d();
  private final Random r = new Random(System.currentTimeMillis());
  private final DocumentListener hexColorListener = new DocumentListener() {
      @Override
      public void removeUpdate(DocumentEvent e) {
        update();
      }
      @Override
      public void insertUpdate(DocumentEvent e) {
        update();
      }
      @Override
      public void changedUpdate(DocumentEvent e) {
        update();
      }
      private void update() {
        String text = colorHex.getText();
        try {
          int rgb = Integer.parseInt(text, 16);
          Vector3d color = new Vector3d();
          Color.getRGBAComponents(rgb, color);
          setColorNoHexUpdate(color);
        } catch (NumberFormatException e) {
        }
      }
    };

  private final Collection<ColorListener> listeners = new ArrayList<ColorListener>();

  public ColorPicker(JComponent parent, Vector3d color) {
    huePicker = new HuePicker(this);
    lightnessPicker = new LightnessPicker(this);
    saturationPicker = new SaturationPicker(this);

    for (int i = 0; i < NUM_SWATCHES; ++i) {
      swatchColors[i] = new Vector3d();
      swatches[i] = new JPanel();
      swatches[i].setPreferredSize(new Dimension(25, 25));
      final int index = i;
      swatches[i].addMouseListener(new MouseListener() {
        @Override
        public void mouseClicked(MouseEvent e) {
        }
        @Override
        public void mousePressed(MouseEvent e) {
          setColor(swatchColors[index]);
        }
        @Override
        public void mouseReleased(MouseEvent e) {
        }
        @Override
        public void mouseEntered(MouseEvent e) {
        }
        @Override
        public void mouseExited(MouseEvent e) {
        }

      });
    }

    for (int i = 0; i < NUM_SWATCHES; ++i) {
      historyColors[i] = new Vector3d();
      history[i] = new JPanel();
      history[i].setPreferredSize(new Dimension(25, 25));
      final int index = i;
      history[i].addMouseListener(new MouseListener() {
        @Override
        public void mouseClicked(MouseEvent e) {
        }
        @Override
        public void mousePressed(MouseEvent e) {
          setColor(historyColors[index]);
          onColorEditFinished();
        }
        @Override
        public void mouseReleased(MouseEvent e) {
        }
        @Override
        public void mouseEntered(MouseEvent e) {
        }
        @Override
        public void mouseExited(MouseEvent e) {
        }

      });
    }

    setColor(color);
    onColorEditFinished();
    initHistory();
    onColorChanged();

    currentSwatch.setPreferredSize(new Dimension(300, 300));
    currentSwatch.addMouseListener(new MouseListener() {
      @Override
      public void mouseReleased(MouseEvent e) {
      }
      @Override
      public void mousePressed(MouseEvent e) {
        onColorPicked();
        closeDialog();
      }
      @Override
      public void mouseExited(MouseEvent e) {
      }
      @Override
      public void mouseEntered(MouseEvent e) {
      }
      @Override
      public void mouseClicked(MouseEvent e) {
      }
    });

    JButton doneBtn = new JButton("Done");
    doneBtn.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        onColorPicked();
        closeDialog();
      }
    });
    JButton cancelBtn = new JButton("Cancel");
    cancelBtn.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        closeDialog();
      }
    });

    addWindowFocusListener(new WindowFocusListener() {
      @Override
      public void windowLostFocus(WindowEvent e) {
        closeDialog();
      }
      @Override
      public void windowGainedFocus(WindowEvent e) {
      }
    });

    colorHex.getDocument().addDocumentListener(hexColorListener);

    setUndecorated(true);
    setAlwaysOnTop(true);
    JPanel panel = new JPanel();
    panel.setBorder(BorderFactory.createTitledBorder("Color Picker"));
    GroupLayout layout = new GroupLayout(panel);
    panel.setLayout(layout);
    Group swatchesParallel = layout.createParallelGroup();
    for (JPanel swatch: swatches) {
      swatchesParallel.addComponent(swatch);
    }
    Group swatchesSequential = layout.createSequentialGroup();
    boolean first = true;
    for (JPanel swatch: swatches) {
      if (!first) swatchesSequential.addGap(5);
      first = false;
      swatchesSequential.addComponent(swatch);
    }
    Group historyParallel = layout.createParallelGroup();
    for (JPanel swatch: history) {
      historyParallel.addComponent(swatch);
    }
    Group historySequential = layout.createSequentialGroup();
    first = true;
    for (JPanel swatch: history) {
      if (!first) historySequential.addGap(5);
      first = false;
      historySequential.addComponent(swatch);
    }
    layout.setHorizontalGroup(layout.createParallelGroup()
      .addGroup(layout.createSequentialGroup()
        .addGroup(layout.createParallelGroup()
          .addComponent(huePicker)
          .addComponent(lightnessPicker)
          .addComponent(saturationPicker)
          .addGroup(swatchesSequential)
          .addGroup(historySequential)
        )
        .addPreferredGap(ComponentPlacement.RELATED)
        .addComponent(currentSwatch)
      )
      .addGroup(layout.createSequentialGroup()
        .addComponent(colorHex, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(ComponentPlacement.UNRELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addComponent(cancelBtn)
        .addPreferredGap(ComponentPlacement.UNRELATED)
        .addComponent(doneBtn)
      )
    );
    layout.setVerticalGroup(layout.createSequentialGroup()
      .addGroup(layout.createParallelGroup()
        .addGroup(layout.createSequentialGroup()
          .addComponent(huePicker)
          .addPreferredGap(ComponentPlacement.RELATED)
          .addComponent(lightnessPicker)
          .addPreferredGap(ComponentPlacement.RELATED)
          .addComponent(saturationPicker)
          .addPreferredGap(ComponentPlacement.RELATED)
          .addGroup(swatchesParallel)
          .addPreferredGap(ComponentPlacement.RELATED)
          .addGroup(historyParallel)
        )
        .addComponent(currentSwatch)
      )
      .addPreferredGap(ComponentPlacement.RELATED)
      .addGroup(layout.createParallelGroup()
        .addComponent(colorHex)
        .addComponent(cancelBtn)
        .addComponent(doneBtn)
      )
    );
    setContentPane(panel);
    pack();

    getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
        KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Close Dialog");
    getRootPane().getActionMap().put("Close Dialog", new AbstractAction() {
      @Override
      public void actionPerformed(ActionEvent e) {
        closeDialog();
      }
    });

    getRootPane().setDefaultButton(doneBtn);

    Dimension parentSize = parent.getSize();
    Dimension size = getPreferredSize();
    Point loc = SwingUtilities.convertPoint(parent,
        getLocation(), this);
    int x = loc.x;
    int y = loc.y+parentSize.height;
    setBounds(x, y, size.width, size.height);
    setVisible(true);
  }

  protected void onColorPicked() {
    updateHistory();
    Vector3d color = new Vector3d();
    se.llbit.math.Color.RGBfromHSL(color, hue, saturation, lightness);
    for (ColorListener listener: listeners) {
      listener.onColorPicked(color);
    }
  }

  protected void closeDialog() {
    setVisible(false);
    dispose();
  }

  protected void setHue(double value) {
    this.hue = value;
    saturationPicker.setHueLight(hue, lightness);
    lightnessPicker.setHueSat(hue, saturation);
    Color.RGBfromHSL(currentColor, hue, saturation, lightness);
    updateHexColor();
    onColorChanged();
  }

  protected void setSaturation(double value) {
    this.saturation = value;
    lightnessPicker.setHueSat(hue, saturation);
    Color.RGBfromHSL(currentColor, hue, saturation, lightness);
    updateHexColor();
    onColorChanged();
  }

  protected void setLightness(double value) {
    this.lightness = value;
    saturationPicker.setHueLight(hue, lightness);
    Color.RGBfromHSL(currentColor, hue, saturation, lightness);
    updateHexColor();
    onColorChanged();
  }

  private void updateHexColor() {
    colorHex.getDocument().removeDocumentListener(hexColorListener);
    colorHex.setText(Color.toString(currentColor));
    colorHex.getDocument().addDocumentListener(hexColorListener);
  }

  private void onColorChanged() {
    currentSwatch.setBackground(new java.awt.Color((float)currentColor.x, (float)currentColor.y, (float)currentColor.z));
    currentSwatch.repaint();
  }

  private void setColor(Vector3d color) {
    colorHex.setText(Color.toString(color));
    setColorNoHexUpdate(color);
  }
  private void setColorNoHexUpdate(Vector3d color) {
    currentColor.set(color);
    double r = color.x;
    double g = color.y;
    double b = color.z;
    double alpha = 0.5 * (2*r - g - b);
    double beta = Math.sqrt(3) * 0.5 * (g - b);
    double angle = Math.atan2(beta, alpha);
    if (angle < 0) {
      angle = Constants.TAU + angle;
    }
    hue = angle / Constants.TAU;
    double c = Math.sqrt(alpha*alpha + beta*beta);
    lightness = 0.5*Math.max(r, Math.max(g, b)) + 0.5*Math.min(r, Math.min(g, b));
    double den = 1 - Math.abs(2*lightness - 1);
    if (den > 0) {
      saturation = Math.max(0, Math.min(1, c / den));
    } else {
      saturation = 0;
    }
    saturationPicker.setHueLight(hue, lightness);
    lightnessPicker.setHueSat(hue, saturation);
    lightnessPicker.setLightness(lightness);
    saturationPicker.setSaturation(saturation);
    huePicker.setHue(hue);
    onColorChanged();
  }

  private void initHistory() {
    synchronized (ColorPicker.class) {
      for (int i = 0; i < NUM_SWATCHES; ++i) {
        historyColors[i].set(staticHistory[i]);
      }
    }
    for (int i = 0; i < NUM_SWATCHES; ++i) {
      history[i].setBackground(Color.toAWT(historyColors[i]));
      history[i].repaint();
    }
  }

  private void updateHistory() {
    int rgb = Color.getRGB(currentColor);
    int end = NUM_SWATCHES-1;
    for (int i = 0; i < NUM_SWATCHES; ++i) {
      if (Color.getRGB(historyColors[i]) == rgb) {
        end = i;
        break;
      }
    }
    for (int i = end; i >= 1; --i) {
      historyColors[i].set(historyColors[i-1]);
    }
    historyColors[0].set(currentColor);
    synchronized (ColorPicker.class) {
      for (int i = 0; i < NUM_SWATCHES; ++i) {
        staticHistory[i].set(historyColors[i]);
      }
    }
  }

  protected void onColorEditFinished() {
    for (int i = 0; i < NUM_SWATCHES; ++i) {
      getStochasticColor(r, swatchColors[i], hue, saturation, lightness);
      swatches[i].setBackground(Color.toAWT(swatchColors[i]));
      swatches[i].repaint();
    }
  }

  private static void getStochasticColor(Random r, Vector3d color,
      double hue, double saturation, double lightness) {
    double x = r.nextDouble()*.25;
    double y = r.nextDouble()*.75;
    double z = r.nextDouble()*.25;
    int sx = r.nextInt(3)-1;
    int sy = r.nextInt(3)-1;
    int sz = r.nextInt(3)-1;
    double h = hue + sx*x*x;
    double s = Math.max(0, Math.min(1, saturation + sy*y*y));
    double l = Math.max(0, Math.min(1, lightness + sz*z*z));
    if (h > 1) h -= 1;
    else if (h < 0) h += 1;
    se.llbit.math.Color.RGBfromHSL(color, h, s, l);
  }

  public void addColorListener(ColorListener listener) {
    listeners.add(listener);
  }
}
TOP

Related Classes of se.llbit.chunky.renderer.ui.ColorPicker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.