/*
* This file is part of the OSMembrane project.
* More informations under www.osmembrane.de
*
* The project is licensed under the GNU GENERAL PUBLIC LICENSE 3.0.
* for more details about the license see http://www.osmembrane.de/license/
*
* Source: $HeadURL$ ($Revision$)
* Last changed: $Date$
*/
package de.osmembrane.view.panels;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import de.osmembrane.resources.Constants;
/**
* The visual (and selectable) component which displays the links between
* {@link PipelineConnector}s.
*
* @author tobias_kuhn
*/
public class PipelineLink extends JPanel {
private static final long serialVersionUID = 8878462047772169198L;
/**
* actual source and destination of the connection
*/
protected PipelineConnector linkSource;
protected PipelineConnector linkDestination;
/**
* {@link Color} this connection will be drawn in
*/
protected Color color;
/**
* The coordinates of the line to draw
*/
protected Line2D line;
/**
* The specific widths of the actual line in object coordinates
*/
protected static final double LINE_DRAWING_WIDTH = 5.0;
protected static final double LINE_SELECTION_WIDTH = 15.0;
protected static final double CONNECTOR_WIDTH = PipelineConnector.displayTemplate
.getIconWidth();
/**
* Pipeline this connector is drawn on.
*/
protected PipelinePanel pipeline;
/**
* Creates a new pipeline link.
*
* @param pipeline
* the pipeline to drawn on (and forward events to)
* @param linkSource
* the *true* source for the connection, i.e. the actual starting
* point
* @param linkDestination
* the *true* destination for the connection, i.e. the actual
* ending point
*/
public PipelineLink(final PipelinePanel pipeline,
PipelineConnector linkSource, PipelineConnector linkDestination) {
this.pipeline = pipeline;
this.linkSource = linkSource;
this.linkDestination = linkDestination;
this.color = linkSource.getModelConnector().getType().getColor();
this.line = new Line2D.Double();
this.setOpaque(false);
this.addMouseListener(new MouseListener() {
@Override
public void mouseReleased(MouseEvent e) {
MouseEvent pipelineEvent = SwingUtilities.convertMouseEvent(
PipelineLink.this, e, pipeline);
pipeline.dispatchEvent(pipelineEvent);
}
@Override
public void mousePressed(MouseEvent e) {
MouseEvent pipelineEvent = SwingUtilities.convertMouseEvent(
PipelineLink.this, e, pipeline);
switch (pipeline.getActiveTool()) {
case DEFAULT_MAGIC_TOOL:
case SELECTION_TOOL:
pipeline.selected(PipelineLink.this);
break;
default:
pipeline.dispatchEvent(pipelineEvent);
}
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
});
addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseMoved(MouseEvent e) {
MouseEvent pipelineEvent = SwingUtilities.convertMouseEvent(
PipelineLink.this, e, pipeline);
pipeline.dispatchEvent(pipelineEvent);
}
@Override
public void mouseDragged(MouseEvent e) {
MouseEvent pipelineEvent = SwingUtilities.convertMouseEvent(
PipelineLink.this, e, pipeline);
switch (pipeline.getActiveTool()) {
case VIEW_TOOL:
pipeline.dispatchEvent(pipelineEvent);
break;
}
}
});
}
/**
* Constructor only applying the {@link PipelinePanel} for descendant
* classes.
*
* @param pipeline
* the pipeline to draw on
*/
protected PipelineLink(PipelinePanel pipeline) {
this.pipeline = pipeline;
}
/**
* Regenerates the line to be drawn, if one or more connectors have moved.
* It is assumed the link's size and location is already correctly set.
*/
public void regenerateLine() {
Point2D left;
Point2D right;
Point offset = new Point(linkSource.getWidth() / 2,
linkSource.getHeight() / 2);
if (linkSource.getX() < linkDestination.getX()) {
// arrow goes like left -----> right
if (linkSource.getY() < linkDestination.getY()) {
// left top to right bottom
left = new Point2D.Double(offset.x, offset.y);
right = new Point2D.Double(getWidth() - offset.x, getHeight()
- offset.y);
} else {
// left bottom to right top
left = new Point2D.Double(offset.x, getHeight() - offset.y);
right = new Point2D.Double(getWidth() - offset.x, offset.y);
}
} else {
// arrow goes like left -----> right, always
if (linkSource.getY() < linkDestination.getY()) {
// right top to left bottom
right = new Point2D.Double(offset.x, getHeight() - offset.y);
left = new Point2D.Double(getWidth() - offset.x, offset.y);
} else {
// right bottom to left top
right = new Point2D.Double(offset.x, offset.y);
left = new Point2D.Double(getWidth() - offset.x, getHeight()
- offset.y);
}
}
line.setLine(left, right);
repaint();
}
@Override
protected void paintComponent(Graphics g) {
if (this.equals(pipeline.getSelected())) {
float[] colorRGB = color.getComponents(null);
Color highlightColor = new Color(
Math.min(1.0f, colorRGB[0] + 0.25f), Math.min(1.0f,
colorRGB[1] + 0.25f), Math.min(1.0f,
colorRGB[2] + 0.25f));
g.setColor(highlightColor);
} else {
g.setColor(color);
}
Polygon p = new Polygon();
int drawWidth = pipeline.objToWindowDelta(new Point2D.Double(0.0,
LINE_DRAWING_WIDTH * Constants.DEFAULT_ZOOM_SIZE)).y;
// use the dot product, Luke
double deltaX = line.getX2() - line.getX1();
double deltaY = line.getY2() - line.getY1();
double length = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
double alpha = Math.acos(-1.0 * (line.getX2() - line.getX1()) / length);
// if alpha too large to get returned by acos
if (line.getY2() > line.getY1()) {
alpha = 2.0 * Math.PI - alpha;
}
// alpha = the clockwise counted angle from the x-axis to the left
// y = sin(alpha), x = cos(alpha)
p.addPoint(
(int) (line.getX1() + (Math.cos(alpha - 0.25 * Math.PI) * drawWidth)),
(int) (line.getY1() + (Math.sin(alpha - 0.25 * Math.PI) * drawWidth)));
p.addPoint(
(int) (line.getX1() + (Math.cos(alpha + 0.25 * Math.PI) * drawWidth)),
(int) (line.getY1() + (Math.sin(alpha + 0.25 * Math.PI) * drawWidth)));
p.addPoint(
(int) (line.getX2() + (Math.cos(alpha + 0.25 * Math.PI) * drawWidth)),
(int) (line.getY2() + (Math.sin(alpha + 0.25 * Math.PI) * drawWidth)));
p.addPoint(
(int) (line.getX2() + (Math.cos(alpha - 0.25 * Math.PI) * drawWidth)),
(int) (line.getY2() + (Math.sin(alpha - 0.25 * Math.PI) * drawWidth)));
g.fillPolygon(p);
// some radius, some tip
double arrowRadius = pipeline.objToWindowDelta(
new Point2D.Double(CONNECTOR_WIDTH
* Constants.DEFAULT_ZOOM_SIZE / 2.0, 0.0)).getX();
Polygon arrowHead = new Polygon();
// arrow goes like left -----> right
double tipLength = (length - arrowRadius) / length;
Point2D arrowTip = new Point2D.Double(line.getX1()
+ (line.getX2() - line.getX1()) * tipLength, line.getY1()
+ (line.getY2() - line.getY1()) * tipLength);
arrowHead.addPoint(
(int) (arrowTip.getX() + arrowRadius
* Math.cos(alpha - 0.25 * Math.PI)),
(int) (arrowTip.getY() + arrowRadius
* Math.sin(alpha - 0.25 * Math.PI)));
arrowHead.addPoint((int) arrowTip.getX(), (int) arrowTip.getY());
arrowHead.addPoint(
(int) (arrowTip.getX() + arrowRadius
* Math.cos(alpha + 0.25 * Math.PI)),
(int) (arrowTip.getY() + arrowRadius
* Math.sin(alpha + 0.25 * Math.PI)));
g.fillPolygon(arrowHead);
}
@Override
public boolean contains(int x, int y) {
return line.ptSegDist(x, y) <= LINE_SELECTION_WIDTH;
}
/**
* Checks whether this link is established between a specific pair of
* connectors.
*
* @param questFrom
* start point of connection asked
* @param questTo
* end point of connection asked
* @return whether this link is between questFrom and questTo, regardless of
* the orientation of the actual connection
*/
public boolean doesLink(PipelineConnector questFrom,
PipelineConnector questTo) {
return (linkSource.equals(questFrom) && linkDestination.equals(questTo))
|| (linkSource.equals(questTo) && linkDestination
.equals(questFrom));
}
/**
* @return the link source connector (i.e. an out connector)
*/
public PipelineConnector getLinkSource() {
return this.linkSource;
}
/**
* @return the link destination connector (i.e. an in connector)
*/
public PipelineConnector getLinkDestination() {
return this.linkDestination;
}
}