View Javadoc

1   // Copyright 2004, FreeHEP.
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          // P0
32          points[0] = xStart;        
33          points[1] = yStart;        
34          
35          // P3
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          // P1 = P3 - length*alpha
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          // SL (sideLength) = length*cos(tip) + length * sin(tip) * cos(base-tip) / sin(base-tip) 
61          double SL = length*Math.cos(tip) + length*Math.sin(tip) * Math.cos(base-tip) / Math.sin(base-tip);
62          
63          // P2 = P3 - SL*alpha+tip
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          // P4 = P3 - SL*alpha-tip
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 }