package pl.edu.fuw.fid.signalanalysis.dtf; 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.Iterator; import java.util.LinkedList; import org.apache.commons.math.linear.RealMatrix; import org.signalml.app.util.GeometryUtils; import org.signalml.app.view.montage.visualreference.VisualReferenceArrow; import org.signalml.app.view.montage.visualreference.VisualReferenceBin; import org.signalml.app.view.montage.visualreference.VisualReferenceChannel; import org.signalml.app.view.montage.visualreference.VisualReferenceDisplay; import org.signalml.app.view.montage.visualreference.VisualReferenceModel; import org.signalml.app.view.montage.visualreference.VisualReferenceSourceChannel; /** * Component displaying EEG channels positioned on the scalp, with arrows * between them. Intensity and colour of arrows correspond to connection * strength (DTF) between EEG channels. * * @author ptr@mimuw.edu.pl */ public class DtfArrowsDisplay extends VisualReferenceDisplay { private static final Color INACTIVE_CHANNEL_COLOR = Color.LIGHT_GRAY; private static final Color ACTIVE_CHANNEL_COLOR = new Color(0, 198, 255); private final LinkedList<VisualReferenceArrow> arrows = new LinkedList<VisualReferenceArrow>(); private RealMatrix transfers; // between output channels of montage public DtfArrowsDisplay(VisualReferenceModel model) { super(model); } public void setTransferData(RealMatrix transfers) { this.transfers = transfers; positionArrows(); repaint(); } public void paintChannel(String label, int perPrimarySize, Shape shape, Shape outlineShape, boolean source, boolean selected, Graphics2D g) { Color fill = source ? INACTIVE_CHANNEL_COLOR : ACTIVE_CHANNEL_COLOR; paintGivenChannel(label, perPrimarySize, shape, outlineShape, fill, Color.BLACK, false, g); } @Override protected void paintBinContents(VisualReferenceBin bin, Graphics2D g) { Iterator<VisualReferenceSourceChannel> it = bin.iterator(); while (it.hasNext()) { VisualReferenceSourceChannel channel = it.next(); int channelIdx = channel.getChannel(); int perPrimarySize = model.channelsPerPrimarySize(channelIdx); if (perPrimarySize == 0) { paintChannel(channel.getLabel(), 0, channel.getShape(), channel.getOutlineShape(), true, false, g); } else { VisualReferenceChannel montageChannel = model.getChannelPerPrimary(channelIdx, 0); paintChannel(montageChannel.getLabel(), perPrimarySize, channel.getShape(), channel.getOutlineShape(), false, false, g); } } } @Override protected void paintComponent(Graphics gOrig) { super.paintComponent(gOrig); // let's paint some arrows on top Graphics2D g = Get2DGraphics(gOrig); for (VisualReferenceArrow arrow : arrows) { g.setColor(arrow.getColor()); Shape arrowShape = arrow.getShape(); g.fill(arrowShape); } } @Override protected Dimension calculateRequiredSize() { Dimension retval = super.calculateRequiredSize(); positionArrows(); return retval; } @Override public Dimension getMinimumSize() { return null; } private void positionArrow(VisualReferenceArrow arrow) { Point point, fromPoint, toPoint; Rectangle bounds; int source = arrow.getSourceChannel(); int target = arrow.getTargetChannel(); VisualReferenceSourceChannel arrowSource = model.getSourceChannel(source); VisualReferenceChannel toChannel = model.getChannel(target); VisualReferenceSourceChannel arrowTarget = model.getSourceChannel(toChannel.getPrimaryChannel()); 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); arrow.setFromPoint(fromPoint); arrow.setToPoint(toPoint); arrow.setPositioned(true); } private void positionArrows() { arrows.clear(); if (transfers != null) { double max = 0.0; for (int src=0; src<transfers.getRowDimension(); ++src) { double[] srcCoeffs = transfers.getRow(src); for (int dst=0; dst<srcCoeffs.length; ++dst) if (dst != src) { max = Math.max(max, srcCoeffs[dst]); } } for (int src=0; src<transfers.getRowDimension(); ++src) { double[] srcCoeffs = transfers.getRow(src); for (int dst=0; dst<srcCoeffs.length; ++dst) if (dst != src) { if (srcCoeffs[dst] > 0) { double value = srcCoeffs[dst] / max; float[] rgb = Color.getHSBColor((2+(float)value)/3, 1, 1).getRGBColorComponents(new float[3]); Color color = new Color(rgb[0], rgb[1], rgb[2], (float) value); VisualReferenceArrow arrow = new VisualReferenceArrow(model.getMontage().getMontagePrimaryChannelAt(src), dst); arrow.setColor(color); positionArrow(arrow); arrows.add(arrow); } } } } } }