ArrowLineEndStyle.java
/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jtstest.testbuilder.ui.style;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import org.locationtech.jtstest.testbuilder.ui.ColorUtil;
import org.locationtech.jtstest.testbuilder.ui.Viewport;
public class ArrowLineEndStyle
extends LineEndStyle {
private static final int FILL_ALPHA = 150;
private final static double ANGLE = 18;
private final static double LENGTH = 15;
private static final double OFFSET_SIZE = 5;
private boolean filled = true;
// default in case colour is not set
private Color color = Color.RED;
public ArrowLineEndStyle(Color color, boolean start, boolean filled) {
super(start);
setColor(color);
this.filled = filled;
}
public void setColor(Color color) {
this.color = ColorUtil.setAlpha(color, FILL_ALPHA);
}
protected void paint(Point2D terminal, Point2D next, Viewport viewport,
Graphics2D g) throws NoninvertibleTransformException
{
// can't compute valid arrow for zero-length segments
if (terminal.equals(next)) {
return;
}
g.setPaint(color);
GeneralPath arrowhead = arrowheadPath(next, terminal, LENGTH,
ANGLE, OFFSET_SIZE);
if (filled) {
arrowhead.closePath();
g.fill(arrowhead);
}
// draw to get effect of stroke
g.draw(arrowhead);
}
/**
* @param finLength
* required distance from the tip to each fin's tip
*/
public static GeneralPath arrowheadPath(Point2D p0, Point2D p1,
Point2D tipPt,
double finLength, double finAngle) {
return arrowheadPath(p0, tipPt, finLength, finLength, 0);
}
/**
* @param finLength
* required distance from the tip to each fin's tip
*/
public static GeneralPath arrowheadPath(Point2D p0, Point2D tipPt,
double finLength, double finAngle,
double offsetSize) {
GeneralPath arrowhead = new GeneralPath();
Point2D offset = AWTUtil.vector(p0, tipPt, offsetSize);
Point2D finTip1 = fin(tipPt, p0, finLength, finAngle);
Point2D finTip2 = fin(tipPt, p0, finLength, -finAngle);
arrowhead.moveTo((float) finTip1.getX() - offset.getX(),
(float) finTip1.getY() - offset.getY() );
arrowhead.lineTo((float) tipPt.getX() - offset.getX(),
(float) tipPt.getY() - offset.getY());
arrowhead.lineTo((float) finTip2.getX() - offset.getX(),
(float) finTip2.getY() - offset.getY());
return arrowhead;
}
public static Point2D fin(Point2D shaftTip, Point2D shaftTail, double length,
double angle) {
double shaftLength = shaftTip.distance(shaftTail);
Point2D finTail = shaftTip;
Point2D finTip = AWTUtil.add(AWTUtil.multiply(AWTUtil.subtract(shaftTail,
shaftTip), length / shaftLength), finTail);
AffineTransform affineTransform = new AffineTransform();
affineTransform.rotate((angle * Math.PI) / 180, finTail.getX(), finTail
.getY());
return affineTransform.transform(finTip, null);
}
}