Package se.llbit.chunky.renderer.ui

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

/* 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.BasicStroke;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.swing.JPanel;

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

@SuppressWarnings("serial")
public class GradientUI extends JPanel {

  private final List<Vector4d> gradient = new ArrayList<Vector4d>();
  private BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
  private int width = 1;
  private int height = 1;
  private int selected = 0;

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

  public GradientUI(Collection<Vector4d> gradient) {
    if (gradient.size() < 2) {
      throw new IllegalArgumentException("Too few gradient stops!");
    }
    this.gradient.addAll(gradient);

    setPreferredSize(new Dimension(400, 60));
    addComponentListener(new ComponentListener() {
      @Override
      public void componentShown(ComponentEvent e) {
      }
      @Override
      public void componentResized(ComponentEvent e) {
        updateGradient();
      }
      @Override
      public void componentMoved(ComponentEvent e) {
      }
      @Override
      public void componentHidden(ComponentEvent e) {
      }
    });
    MouseAdapter mouseAdapter = new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent e) {
        double x = e.getX() / (double) width;
        closestStop(x);
      }

      @Override
      public void mouseDragged(MouseEvent e) {
        moveStop(e.getX() / (double)width);
      }
    };
    addMouseListener(mouseAdapter);
    addMouseMotionListener(mouseAdapter);
  }

  /**
   * Helper method to create a gradient image.
   * @param gradient
   * @param width
   * @param height
   * @return gradiant image
   */
  public static final BufferedImage gradientImage(List<Vector4d> gradient, int width, int height) {
    if (width <= 0 || height <= 0 || gradient.size() < 2) {
      throw new IllegalArgumentException();
    }
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    int x = 0;
    for (int i = 0; i < width; ++i) {
      double weight = i/(double)width;
      Vector4d c0 = gradient.get(x);
      Vector4d c1 = gradient.get(x+1);
      double xx = (weight - c0.w) / (c1.w-c0.w);
      while (x+2 < gradient.size() && xx > 1) {
        x += 1;
        c0 = gradient.get(x);
        c1 = gradient.get(x+1);
        xx = (weight - c0.w) / (c1.w-c0.w);
      }
      xx = 0.5*(Math.sin(Math.PI*xx-Constants.HALF_PI)+1);
      double a = 1-xx;
      double b = xx;
      int argb = se.llbit.math.Color.getRGBA(a*c0.x+b*c1.x, a*c0.y+b*c1.y, a*c0.z+b*c1.z, 1);
      for (int j = 0; j < height; ++j) {
        image.setRGB(i, j, argb);
      }
    }
    return image;
  }

  public static final BufferedImage gradientImageLinear(List<Vector4d> gradient, int width, int height) {
    if (width <= 0 || height <= 0 || gradient.size() < 2) {
      throw new IllegalArgumentException();
    }
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    int x = 0;
    for (int i = 0; i < width; ++i) {
      double weight = i/(double)width;
      Vector4d c0 = gradient.get(x);
      Vector4d c1 = gradient.get(x+1);
      double xx = (weight - c0.w) / (c1.w-c0.w);
      while (x+2 < gradient.size() && xx > 1) {
        x += 1;
        c0 = gradient.get(x);
        c1 = gradient.get(x+1);
        xx = (weight - c0.w) / (c1.w-c0.w);
      }
      double a = 1-xx;
      double b = xx;
      int argb = se.llbit.math.Color.getRGBA(a*c0.x+b*c1.x, a*c0.y+b*c1.y, a*c0.z+b*c1.z, 1);
      for (int j = 0; j < height; ++j) {
        image.setRGB(i, j, argb);
      }
    }
    return image;
  }

  public void updateGradient() {
    int newWidth = getWidth();
    int newHeight = getHeight();
    if (newWidth > 0 && newHeight > 0) {
      width = newWidth;
      height = newHeight;
      image = gradientImage(gradient, width, height);
      int x = 0;
      if (gradient.size() < 2) {
        return;
      }
      for (int i = 0; i < newWidth; ++i) {
        double weight = i/(double)newWidth;
        Vector4d c0 = gradient.get(x);
        Vector4d c1 = gradient.get(x+1);
        double xx = (weight - c0.w) / (c1.w-c0.w);
        while (x+2 < gradient.size() && xx > 1) {
          x += 1;
          c0 = gradient.get(x);
          c1 = gradient.get(x+1);
          xx = (weight - c0.w) / (c1.w-c0.w);
        }
        xx = 0.5*(Math.sin(Math.PI*xx-Constants.HALF_PI)+1);
        double a = 1-xx;
        double b = xx;
        int argb = se.llbit.math.Color.getRGBA(a*c0.x+b*c1.x, a*c0.y+b*c1.y, a*c0.z+b*c1.z, 1);
        for (int j = 0; j < newHeight; ++j) {
          image.setRGB(i, j, argb);
        }
      }
    }
  }

  private static Stroke thickLine = new BasicStroke(2);
  private static java.awt.Color lightGray = new java.awt.Color(220, 220, 220);

  @Override
  protected void paintComponent(Graphics graphics) {
    Graphics2D g = (Graphics2D) graphics;
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    //Rectangle bounds = g.getClipBounds();
    g.drawImage(image, 0, 0, null);
    g.setColor(java.awt.Color.WHITE);
    g.fillRect(0, 0, width, 15);
    int min = 0, max = width;
    if (selected > 0) {
      min = (int) (gradient.get(selected-1).w * width);
    }
    if (selected < gradient.size()-1) {
      max = (int) (gradient.get(selected+1).w * width);
    }
    g.setColor(lightGray);
    g.fillRect(min, 0, max-min, 15);
    int index = 0;
    for (Vector4d stop: gradient) {
      if (index == selected) {
        index += 1;
        continue;
      }

      // fill color
      g.setColor(java.awt.Color.WHITE);
      int x = Math.min(width-1, (int) (stop.w*width));
      int[] xPoints = { x-5, x, x+5 };
      int[] yPoints = { 0, 15, 0 };
      g.fillPolygon(xPoints, yPoints, 3);

      // border color
      boolean isEndpoint = index == 0 || index == gradient.size()-1;
      if (isEndpoint) {
        g.setColor(java.awt.Color.GRAY);
      } else {
        g.setColor(java.awt.Color.BLACK);
      }
      g.drawLine(x-5, 0, x, 15);
      g.drawLine(x, 15, x+5, 0);
      g.drawLine(x-5, 0, x+5, 0);
      index += 1;
    }
    g.setStroke(thickLine);
    Vector4d stop = gradient.get(selected);
    // fill color
    boolean isEndpoint = selected == 0 || selected == gradient.size()-1;
    if (isEndpoint) {
      g.setColor(java.awt.Color.GRAY);
    } else {
      g.setColor(java.awt.Color.BLACK);
    }
    int x = Math.min(width-1, (int) (stop.w*width));
    int[] xPoints = { x-5, x, x+5 };
    int[] yPoints = { 0, 15, 0 };
    g.fillPolygon(xPoints, yPoints, 3);

    // border color
    g.setColor(java.awt.Color.WHITE);
    g.drawLine(x-5, 0, x, 15);
    g.drawLine(x, 15, x+5, 0);
    g.drawLine(x-5, 0, x+5, 0);
    index += 1;
  }

  protected void moveStop(double d) {
    if (selected > 0 && selected < gradient.size()-1) {
      double min = gradient.get(selected-1).w;
      double max = gradient.get(selected+1).w;
      gradient.get(selected).w = Math.max(min, Math.min(max, d));
      gradientChanged();
      repaint();
      fireStopModifiedNotification();
    }
  }

  private synchronized void fireStopModifiedNotification() {
    for (GradientListener listener: listeners) {
      listener.stopModified(selected, gradient.get(selected));
    }
  }

  private void gradientChanged() {
    updateGradient();
    repaint();
    fireGradientChangedNotification();
  }

  private synchronized void fireGradientChangedNotification() {
    for (GradientListener listener: listeners) {
      listener.gradientChanged(gradient);
    }

  }

  protected int closestStop(double x) {
    double closest = Double.MAX_VALUE;
    int stop = 0;
    int index = 0;
    for (Vector4d m: gradient) {
      double distance = Math.abs(m.w - x);
      if (distance < closest) {
        stop = index;
        closest = distance;
      }
      index += 1;
    }
    setSelectedIndex(stop);
    return stop;
  }

  public synchronized void addGradientListener(GradientListener listener) {
    this.listeners.add(listener);
  }

  public synchronized void removeGradientListener(GradientListener listener) {
    this.listeners.remove(listener);
  }

  /**
   * Set selected gradient stop.
   * @param index
   */
  public void setSelectedIndex(int index) {
    if (index >= 0 && index < gradient.size()) {
      selected = index;
      repaint();
      fireStopSelectedNotification();
    }
  }

  private synchronized void fireStopSelectedNotification() {
    for (GradientListener listener: listeners) {
      listener.stopSelected(selected);
    }
  }

  /**
   * @return currently selected stop index
   */
  public int getSelctedIndex() {
    return selected;
  }

  /**
   * @return number of gradient stops
   */
  public int getNumStop() {
    return gradient.size();
  }

  /**
   * @param index
   * @return stop with index 'index'
   */
  public Vector4d getStop(int index) {
    return gradient.get(index);
  }

  public void setColor(Vector3d newColor) {
    Vector4d stop = gradient.get(selected);
    stop.x = newColor.x;
    stop.y = newColor.y;
    stop.z = newColor.z;
    gradientChanged();
  }

  public void setPosition(double newPosition) {
    Vector4d stop = gradient.get(selected);
    stop.w = newPosition;
    gradientChanged();
  }

  public void addStop() {
    int i0;
    int i1;
    if (selected == gradient.size()-1) {
      i0 = selected-1;
      i1 = selected;
    } else {
      i0 = selected;
      i1 = selected+1;
    }
    gradient.add(i1, blend(gradient.get(i0), gradient.get(i1), 0.5));
    selected = i1;
    fireStopSelectedNotification();
    gradientChanged();
  }

  private Vector4d blend(Vector4d s0, Vector4d s1, double d) {
    double xx = 0.5*(Math.sin(Math.PI*d-Constants.HALF_PI)+1);
    double a = 1-xx;
    double b = xx;
    return new Vector4d(a*s0.x+b*s1.x, a*s0.y+b*s1.y, a*s0.z+b*s1.z, a*s0.w+b*s1.w);
  }

  public void removeStop() {
    if (gradient.size() > 2) {
      gradient.remove(selected);
      if (selected == 0) {
        gradient.get(0).w = 0;
      } else if (selected == gradient.size()) {
        gradient.get(selected-1).w = 1;
      }
      if (selected > 0) {
        selected -= 1;
      }
      fireStopSelectedNotification();
      gradientChanged();
    }
  }

  public void setGradient(List<Vector4d> newGradient) {
    gradient.clear();
    gradient.addAll(newGradient);
    gradientChanged();
  }

  public Collection<Vector4d> getGradient() {
    return gradient;
  }

}
TOP

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

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.