/* VisualReferenceEditor.java created 2007-11-30
*
*/
package org.signalml.app.view.montage.visualreference;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import org.signalml.app.util.GeometryUtils;
/** VisualReferenceEditor
*
*
* @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o.
*/
public class VisualReferenceEditor extends VisualReferenceDisplay {
private static final long serialVersionUID = 1L;
private static final BasicStroke WHITE_SELECTION_STROKE = new BasicStroke(1F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10F, new float[] {3,3}, 0F);
private static final BasicStroke BLACK_SELECTION_STROKE = new BasicStroke(1F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10F, new float[] {3,3}, 3F);
private static final Color[] ARROW_COLORS = {
Color.RED,
Color.GREEN.darker(),
Color.BLUE,
Color.CYAN,
Color.MAGENTA,
Color.YELLOW,
Color.ORANGE,
Color.BLACK
};
private static final Color INACTIVE_CHANNEL_COLOR = Color.LIGHT_GRAY;
private static final Color ACTIVE_CHANNEL_COLOR = new Color(0, 198, 255);
private LinkedList<VisualReferenceArrow> tempArrowsToDraw = new LinkedList<VisualReferenceArrow>();
private VisualReferenceArrow prospectiveArrow;
public VisualReferenceEditor(VisualReferenceModel model) {
super(model);
VisualReferenceEditorMouseHandler mouseHandler = new VisualReferenceEditorMouseHandler(this);
addMouseListener(mouseHandler);
addMouseMotionListener(mouseHandler);
}
public VisualReferenceArrow getProspectiveArrow() {
return prospectiveArrow;
}
public void setProspectiveArrow(VisualReferenceArrow prospectiveArrow) {
if (this.prospectiveArrow != prospectiveArrow) {
this.prospectiveArrow = prospectiveArrow;
if (prospectiveArrow != null) {
positionArrow(prospectiveArrow, true);
}
repaint();
}
}
@Override
public boolean isOpaque() {
return true;
}
public void paintChannel(String label, int perPrimarySize, Shape shape, Shape outlineShape, boolean source, boolean selected, Graphics2D g) {
Color fill;
Color outline;
boolean boldBorder;
if (source) fill = INACTIVE_CHANNEL_COLOR;
else fill = ACTIVE_CHANNEL_COLOR;
if (selected) {
outline = Color.RED;
boldBorder = true;
} else {
outline = Color.BLACK;
boldBorder = false;
}
paintGivenChannel(label, perPrimarySize, shape, outlineShape, fill, outline, boldBorder, g);
}
@Override
protected void paintBinContents(VisualReferenceBin bin, Graphics2D g) {
Iterator<VisualReferenceSourceChannel> it = bin.iterator();
VisualReferenceSourceChannel channel;
int channelIdx;
Iterator<VisualReferenceChannel> montageIt;
VisualReferenceChannel selChannel = model.getActiveChannel();
VisualReferenceChannel montageChannel;
boolean selectedPainted;
int perPrimarySize;
while (it.hasNext()) {
channel = it.next();
channelIdx = channel.getChannel();
perPrimarySize = model.channelsPerPrimarySize(channelIdx);
if (perPrimarySize == 0) {
paintChannel(channel.getLabel(), 0, channel.getShape(), channel.getOutlineShape(), true, false, g);
} else {
selectedPainted = false;
if (selChannel != null) {
montageIt = model.channelsPerPrimaryIterator(channelIdx);
while (montageIt.hasNext()) {
montageChannel = montageIt.next();
if (montageChannel == selChannel) {
paintChannel(montageChannel.getLabel(), perPrimarySize, channel.getShape(), channel.getOutlineShape(), false, true, g);
selectedPainted = true;
break;
}
}
if (!selectedPainted) {
montageChannel = model.getChannelPerPrimary(channelIdx, 0);
paintChannel(channel.getLabel(), 0, channel.getShape(), channel.getOutlineShape(), (selChannel != null), false, g);
}
} else {
if (!selectedPainted) {
montageChannel = model.getChannelPerPrimary(channelIdx, 0);
paintChannel(montageChannel.getLabel(), perPrimarySize, channel.getShape(), channel.getOutlineShape(), (selChannel != null), false, g);
}
}
}
}
}
protected void paintArrow(VisualReferenceArrow arrow, Graphics2D g, boolean active, boolean selected) {
if (active) {
g.setColor(arrow.getColor());
} else {
g.setColor(Color.LIGHT_GRAY);
}
Shape arrowShape = arrow.getShape();
g.fill(arrowShape);
if (selected) {
g.setColor(Color.WHITE);
g.setStroke(WHITE_SELECTION_STROKE);
g.draw(arrowShape);
g.setColor(Color.BLACK);
g.setStroke(BLACK_SELECTION_STROKE);
g.draw(arrowShape);
}
}
@Override
protected void paintComponent(Graphics gOrig) {
super.paintComponent(gOrig);
Graphics2D g = Get2DGraphics(gOrig);
VisualReferenceChannel selChannel = model.getActiveChannel();
int selChannelIndex = -1;
if (selChannel != null) {
selChannelIndex = model.indexOfChannel(selChannel);
}
VisualReferenceArrow selArrow = model.getActiveArrow();
VisualReferenceArrow arrow;
tempArrowsToDraw.clear();
int colorIndex;
VisualReferenceChannel toChannel;
Iterator<VisualReferenceArrow> arrowIt = model.arrowsIterator();
while (arrowIt.hasNext()) {
arrow = arrowIt.next();
if (selChannel == null) {
toChannel = model.getChannel(arrow.getTargetChannel());
colorIndex = model.indexOfChannelPerPrimary(toChannel.getPrimaryChannel(), toChannel);
colorIndex = colorIndex % ARROW_COLORS.length;
paintArrow(arrow, g, true, (arrow == selArrow));
} else {
if (arrow.getTargetChannel() != selChannelIndex) {
paintArrow(arrow, g, false, false);
} else {
tempArrowsToDraw.add(arrow);
}
}
}
if (selChannel != null) {
arrowIt = tempArrowsToDraw.iterator();
while (arrowIt.hasNext()) {
arrow = arrowIt.next();
paintArrow(arrow, g, true, (arrow == selArrow));
}
}
if (prospectiveArrow != null) {
paintArrow(prospectiveArrow, g, true, true);
}
}
@Override
public boolean isDoubleBuffered() {
return true;
}
@Override
public void invalidate() {
super.invalidate();
requiredSize = null;
}
@Override
public Dimension getPreferredSize() {
if (requiredSize == null) {
requiredSize = calculateRequiredSize();
}
return requiredSize;
}
@Override
public Dimension getMinimumSize() {
return super.getPreferredSize();
}
public VisualReferenceSourceChannel findChannelInBinAt(VisualReferenceBin bin, Point point) {
if (bin.isEmpty()) {
return null;
}
Iterator<VisualReferenceSourceChannel> it = bin.iterator();
VisualReferenceSourceChannel channel;
while (it.hasNext()) {
channel = it.next();
if (channel.getShape().contains(point)) {
return channel;
}
}
return null;
}
public VisualReferenceSourceChannel findChannelAt(Point point) {
VisualReferencePositionedBin positionedBin = model.getPositionedBin();
VisualReferenceBin othersBin = model.getOthersBin();
if (positionedBin.getBounds().contains(point)) {
return findChannelInBinAt(positionedBin, point);
}
else if (othersBin.getBounds().contains(point)) {
return findChannelInBinAt(othersBin, point);
}
return null;
}
public ArrayList<VisualReferenceArrow> findArrowsAtPoint(Point point, ArrayList<VisualReferenceArrow> listToFill) {
ArrayList<VisualReferenceArrow> list;
if (listToFill != null) {
list = listToFill;
list.clear();
} else {
list = new ArrayList<VisualReferenceArrow>();
}
Iterator<VisualReferenceArrow> arrowIt = model.arrowsIterator();
VisualReferenceArrow arrow;
Shape arrowShape;
while (arrowIt.hasNext()) {
arrow = arrowIt.next();
arrowShape = arrow.getShape();
if (arrowShape.getBounds().contains(point)) {
if (arrowShape.contains(point)) {
list.add(arrow);
}
}
}
return list;
}
@Override
protected Dimension calculateRequiredSize() {
Dimension retval = super.calculateRequiredSize();
positionArrows(true);
return retval;
}
private void positionArrows(boolean all) {
VisualReferenceArrow arrow;
Iterator<VisualReferenceArrow> it = model.arrowsIterator();
while (it.hasNext()) {
arrow = it.next();
if (all || !arrow.isPositioned()) {
positionArrow(arrow, false);
}
}
}
private void positionArrow(VisualReferenceArrow arrow, boolean targetIsPrimary) {
int source, target;
VisualReferenceSourceChannel arrowSource;
VisualReferenceSourceChannel arrowTarget;
VisualReferenceChannel toChannel;
Point point;
Point fromPoint;
Point toPoint;
Rectangle bounds;
source = arrow.getSourceChannel();
target = arrow.getTargetChannel();
arrowSource = model.getSourceChannel(source);
int arrowOrder;
int colorIndex;
if (targetIsPrimary) {
arrowTarget = model.getSourceChannel(target);
arrow.setColor(Color.WHITE);
arrowOrder = 0;
} else {
toChannel = model.getChannel(target);
arrowTarget = model.getSourceChannel(toChannel.getPrimaryChannel());
arrowOrder = model.getArrowOrder(target, source);
colorIndex = model.indexOfChannelPerPrimary(toChannel.getPrimaryChannel(), toChannel);
arrow.setColor(ARROW_COLORS[ colorIndex % ARROW_COLORS.length ]);
}
point = arrowSource.getLocation();
bounds = arrowSource.getShape().getBounds();
fromPoint = new Point(point.x + bounds.width / 2, point.y + bounds.height / 2);
point = arrowTarget.getLocation();
bounds = arrowTarget.getShape().getBounds();
toPoint = new Point(point.x + bounds.width / 2, point.y + bounds.height / 2);
GeometryUtils.translatePointToCircleBorder(fromPoint, toPoint, VisualReferenceSourceChannel.CIRCLE_DIAMETER/2);
GeometryUtils.translatePointToCircleBorder(toPoint, fromPoint, VisualReferenceSourceChannel.CIRCLE_DIAMETER/2);
if (arrowOrder > 0) {
// this rotates the arrows slightly so that they superimpose less
arrowOrder = Math.min(10, arrowOrder); // limit to 10 to prevent reahing anything close 90 deg
double angle = Math.toRadians(10*((arrowOrder+1)/2) * ((arrowOrder % 2) == 0 ? -1 : 1));
Point centerPoint = (Point) arrowTarget.getLocation().clone();
centerPoint.translate(VisualReferenceSourceChannel.CIRCLE_DIAMETER / 2, VisualReferenceSourceChannel.CIRCLE_DIAMETER / 2);
GeometryUtils.rotatePoint(toPoint, centerPoint, angle);
}
arrow.setFromPoint(fromPoint);
arrow.setToPoint(toPoint);
arrow.setPositioned(true);
}
@Override
public void montageChannelsChanged(VisualReferenceEvent ev) {
prospectiveArrow = null;
// TODO maybe optimize this
revalidate();
repaint();
}
@Override
public void montageStructureChanged(VisualReferenceEvent ev) {
prospectiveArrow = null;
revalidate();
repaint();
}
@Override
public void referenceChanged(VisualReferenceEvent ev) {
prospectiveArrow = null;
// TODO maybe optimize this
positionArrows(true);
repaint();
}
@Override
public void sourceChannelsChanged(VisualReferenceEvent ev) {
prospectiveArrow = null;
revalidate();
repaint();
}
}