1
2 package hep.wired.heprep.util;
3
4 import java.awt.*;
5 import java.awt.geom.*;
6
7 /***
8 * Defines an Arrow shape, see figure:
9 * <IMG SRC="doc-files/arrow.gif"/>
10 *
11 * @author Mark Donszelmann
12 * @version $Id: Arrow.java 258 2004-06-08 06:27:49Z duns $
13 */
14 public class Arrow implements Shape {
15
16 private double points[];
17 private double length;
18 private double tip;
19 private double base;
20 private boolean open;
21
22 private ArrowPathIterator pathIterator;
23
24 public Arrow(Point2D start, Point2D end, double length, double tip, double base, boolean open) {
25 this(start.getX(), start.getY(), end.getX(), end.getY(), length, tip, base, open);
26 }
27
28 public Arrow(double xStart, double yStart, double xEnd, double yEnd, double length, double tip, double base, boolean open) {
29 points = new double[2*5];
30
31
32 points[0] = xStart;
33 points[1] = yStart;
34
35
36 points[2*3] = xEnd;
37 points[2*3+1] = yEnd;
38
39 this.length = Math.min(length, Point2D.distance(points[0], points[1], points[2*3], points[2*3+1]));
40 if (this.length <= 0)
41 throw new IllegalArgumentException(getClass()+": length less or equal to 0");
42
43 if ((tip <= 0) || (tip >= 90))
44 throw new IllegalArgumentException(getClass()+": tip angle not in range <0..90>");
45 this.tip = Math.toRadians(tip);
46
47 if ((base <= tip) || (base >= 180))
48 throw new IllegalArgumentException(getClass()+": base angle not in range <"+tip+"..180>");
49 this.base = Math.toRadians(base);
50
51 this.open = open;
52
53 pathIterator = new ArrowPathIterator(points);
54
55
56 double alpha = Math.atan2(points[2*3+1] - points[1], points[2*3] - points[0]);
57 points[1*2] = points[2*3] - length*Math.cos(alpha);
58 points[1*2+1] = points[2*3+1] - length*Math.sin(alpha);
59
60
61 double SL = length*Math.cos(tip) + length*Math.sin(tip) * Math.cos(base-tip) / Math.sin(base-tip);
62
63
64 points[2*2] = points[2*3] - SL*Math.cos(alpha+tip);
65 points[2*2+1] = points[2*3+1] - SL*Math.sin(alpha+tip);
66
67
68 points[2*4] = points[2*3] - SL*Math.cos(alpha-tip);
69 points[2*4+1] = points[2*3+1] - SL*Math.sin(alpha-tip);
70 }
71
72 public boolean contains(double x, double y) {
73 return new Area(this).contains(x, y);
74 }
75
76 public boolean contains(double x, double y, double w, double h) {
77 return contains(x, y) && contains(x+w,y) &&
78 contains(x,y+h) && contains(x+w,y+h);
79 }
80
81 public boolean contains(Point2D p) {
82 return contains(p.getX(), p.getY());
83 }
84
85 public boolean contains(Rectangle2D r) {
86 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
87 }
88
89 /*** Returns true, if at least one of the points is contained by the shape. */
90 public boolean intersects(double x, double y, double w, double h) {
91 return contains(x, y) || contains(x+w,y) ||
92 contains(x,y+h) || contains(x+w,y+h);
93 }
94
95 public boolean intersects(Rectangle2D r) {
96 return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
97 }
98
99 public PathIterator getPathIterator(AffineTransform at, double flatness) {
100 return getPathIterator(at);
101 }
102
103 public Rectangle2D getBounds2D() {
104 return new Area(this).getBounds2D();
105 }
106
107 public Rectangle getBounds() {
108 return getBounds2D().getBounds();
109 }
110
111 public PathIterator getPathIterator(AffineTransform t) {
112 if (t != null) {
113 t.transform(points, 0, pathIterator.points, 0, points.length/2);
114 }
115
116 pathIterator.reset();
117 return pathIterator;
118 }
119
120 public String toString() {
121 return getClass()+": ("+points[0]+","+points[1]+")-("+points[2*3]+","+points[2*3+1]+") length="+length+"; "
122 +"tip="+Math.toDegrees(tip)+"; base="+Math.toDegrees(base)+"; open="+open+";";
123 }
124
125 /***
126 * Returns a straight line shape, followed by the arrowhead.
127 */
128 private class ArrowPathIterator implements PathIterator {
129
130 private double[] points;
131 private int currentPoint = 0;
132
133 private ArrowPathIterator(double[] points) {
134 this.points = points;
135 }
136
137 public boolean isDone() {
138 int nPoints = open ? points.length/2 : points.length/2+2;
139 return currentPoint >= nPoints;
140 }
141
142 public void next() {
143 currentPoint++;
144 }
145
146 public int currentSegment(double[] coords) {
147 switch (currentPoint) {
148 default:
149 coords[0] = points[2*currentPoint];
150 coords[1] = points[2*currentPoint+1];
151 return ((currentPoint == 0) || (currentPoint == 2)) ? SEG_MOVETO : SEG_LINETO;
152 case 5:
153 coords[0] = points[2];
154 coords[1] = points[3];
155 return SEG_LINETO;
156 case 6:
157 return SEG_CLOSE;
158 }
159 }
160
161 public int currentSegment(float[] coords) {
162 switch (currentPoint) {
163 default:
164 coords[0] = (float)points[2*currentPoint];
165 coords[1] = (float)points[2*currentPoint+1];
166 return ((currentPoint == 0) || (currentPoint == 2)) ? SEG_MOVETO : SEG_LINETO;
167 case 5:
168 coords[0] = (float)points[2];
169 coords[1] = (float)points[3];
170 return SEG_LINETO;
171 case 6:
172 return SEG_CLOSE;
173 }
174 }
175
176 public int getWindingRule() {
177 return PathIterator.WIND_NON_ZERO;
178 }
179
180 private void reset() {
181 currentPoint = 0;
182 }
183
184 private void done() {
185 currentPoint = open ? points.length/2 : points.length/2+2;
186 }
187 }
188 }