/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.ui.components.SplineEditor; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.geom.Area; import java.awt.geom.CubicCurve2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.io.OutputStream; import javax.imageio.ImageIO; import org.gephi.ui.components.SplineEditor.equation.EquationDisplay; public class SplineDisplay extends EquationDisplay { private static final double CONTROL_POINT_SIZE = 12.0; private Point2D control1 = new Point2D.Double(0, 0); private Point2D control2 = new Point2D.Double(1, 1); private Point2D selected = null; private Point dragStart = null; private boolean isSaving = false; private PropertyChangeSupport support; SplineDisplay() { super(0.0, 0.0, -0.1, 1.1, -0.1, 1.1, 0.2, 6, 0.2, 6); setEnabled(false); addMouseMotionListener(new ControlPointsHandler()); addMouseListener(new SelectionHandler()); support = new PropertyChangeSupport(this); } @Override public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { support.addPropertyChangeListener(propertyName, listener); } @Override public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { support.removePropertyChangeListener(propertyName, listener); } public Point2D getControl1() { return (Point2D) control1.clone(); } public Point2D getControl2() { return (Point2D) control2.clone(); } public void setControl1(Point2D control1) { support.firePropertyChange("control1", this.control1.clone(), control1.clone()); this.control1 = (Point2D) control1.clone(); repaint(); } public void setControl2(Point2D control2) { support.firePropertyChange("control2", this.control2.clone(), control2.clone()); this.control2 = (Point2D) control2.clone(); repaint(); } synchronized void saveAsTemplate(OutputStream out) { BufferedImage image = Java2dHelper.createCompatibleImage(getWidth(), getHeight()); Graphics g = image.getGraphics(); isSaving = true; setDrawText(false); paint(g); setDrawText(true); isSaving = false; g.dispose(); BufferedImage subImage = image.getSubimage((int) xPositionToPixel(0.0), (int) yPositionToPixel(1.0), (int) (xPositionToPixel(1.0) - xPositionToPixel(0.0)) + 1, (int) (yPositionToPixel(0.0) - yPositionToPixel(1.0)) + 1); try { ImageIO.write(subImage, "PNG", out); } catch (IOException e) { } image.flush(); subImage = null; image = null; } @Override protected void paintInformation(Graphics2D g2) { if (!isSaving) { paintControlPoints(g2); } paintSpline(g2); } private void paintControlPoints(Graphics2D g2) { paintControlPoint(g2, control1); paintControlPoint(g2, control2); } private void paintControlPoint(Graphics2D g2, Point2D control) { double origin_x = xPositionToPixel(control.getX()); double origin_y = yPositionToPixel(control.getY()); double pos = control == control1 ? 0.0 : 1.0; Ellipse2D outer = getDraggableArea(control); Ellipse2D inner = new Ellipse2D.Double(origin_x + 2.0 - CONTROL_POINT_SIZE / 2.0, origin_y + 2.0 - CONTROL_POINT_SIZE / 2.0, 8.0, 8.0); Area circle = new Area(outer); circle.subtract(new Area(inner)); Stroke stroke = g2.getStroke(); g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 5, new float[] { 5, 5 }, 0)); g2.setColor(new Color(1.0f, 0.0f, 0.0f, 0.4f)); g2.drawLine(0, (int) origin_y, (int) origin_x, (int) origin_y); g2.drawLine((int) origin_x, (int) origin_y, (int) origin_x, getHeight()); g2.setStroke(stroke); if (selected == control) { g2.setColor(new Color(1.0f, 1.0f, 1.0f, 1.0f)); } else { g2.setColor(new Color(0.8f, 0.8f, 0.8f, 0.6f)); } g2.fill(inner); g2.setColor(new Color(0.0f, 0.0f, 0.5f, 0.5f)); g2.fill(circle); g2.drawLine((int) origin_x, (int) origin_y, (int) xPositionToPixel(pos), (int) yPositionToPixel(pos)); } private Ellipse2D getDraggableArea(Point2D control) { Ellipse2D outer = new Ellipse2D.Double(xPositionToPixel(control.getX()) - CONTROL_POINT_SIZE / 2.0, yPositionToPixel(control.getY()) - CONTROL_POINT_SIZE / 2.0, CONTROL_POINT_SIZE, CONTROL_POINT_SIZE); return outer; } private void paintSpline(Graphics2D g2) { CubicCurve2D spline = new CubicCurve2D.Double(xPositionToPixel(0.0), yPositionToPixel(0.0), xPositionToPixel(control1.getX()), yPositionToPixel(control1.getY()), xPositionToPixel(control2.getX()), yPositionToPixel(control2.getY()), xPositionToPixel(1.0), yPositionToPixel(1.0)); g2.setColor(new Color(0.0f, 0.3f, 0.0f, 1.0f)); g2.draw(spline); } private void resetSelection() { Point2D oldSelected = selected; selected = null; if (oldSelected != null) { Rectangle bounds = getDraggableArea(oldSelected).getBounds(); repaint(bounds.x, bounds.y, bounds.width, bounds.height); } } private class ControlPointsHandler extends MouseMotionAdapter { @Override public void mouseMoved(MouseEvent e) { Ellipse2D area1 = getDraggableArea(control1); Ellipse2D area2 = getDraggableArea(control2); if (area1.contains(e.getPoint()) || area2.contains(e.getPoint())) { setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { setCursor(Cursor.getDefaultCursor()); } } @Override public void mouseDragged(MouseEvent e) { if (selected == null) { return; } Point dragEnd = e.getPoint(); double distance = xPixelToPosition(dragEnd.getX()) - xPixelToPosition(dragStart.getX()); double x = selected.getX() + distance; if (x < 0.0) { x = 0.0; } else if (x > 1.0) { x = 1.0; } distance = yPixelToPosition(dragEnd.getY()) - yPixelToPosition(dragStart.getY()); double y = selected.getY() + distance; if (y < 0.0) { y = 0.0; } else if (y > 1.0) { y = 1.0; } Point2D selectedCopy = (Point2D) selected.clone(); selected.setLocation(x, y); support.firePropertyChange("control" + (selected == control1 ? "1" : "2"), selectedCopy, selected.clone()); repaint(); double xPos = xPixelToPosition(dragEnd.getX()); double yPos = -yPixelToPosition(dragEnd.getY()); if (xPos >= 0.0 && xPos <= 1.0) { dragStart.setLocation(dragEnd.getX(), dragStart.getY()); } if (yPos >= 0.0 && yPos <= 1.0) { dragStart.setLocation(dragStart.getX(), dragEnd.getY()); } } } private class SelectionHandler extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { Ellipse2D area1 = getDraggableArea(control1); Ellipse2D area2 = getDraggableArea(control2); if (area1.contains(e.getPoint())) { selected = control1; dragStart = e.getPoint(); Rectangle bounds = area1.getBounds(); repaint(bounds.x, bounds.y, bounds.width, bounds.height); } else if (area2.contains(e.getPoint())) { selected = control2; dragStart = e.getPoint(); Rectangle bounds = area2.getBounds(); repaint(bounds.x, bounds.y, bounds.width, bounds.height); } else { resetSelection(); } } @Override public void mouseReleased(MouseEvent e) { resetSelection(); } } }