1    /*
2    Copyright 2003 by Ralph Hartley
3    This software is licenced under the terms of the
4    Gnu Public Licence
5    */
6    
7    import java.awt.*;
8    import javax.swing.*;
9    import java.awt.geom.*;
10   import java.io.*;
11   import java.util.*;
12   
13   /**
14    * A style of arrow head.
15    */
16   public class Arrow extends FileDefaultable implements Serializable {
17   
18     private static final long serialVersionUID = Version.getSUID();
19   
20     /** Draw left half of arrow
21      */
22     public boolean left = true;
23   
24     /** Draw Right half of arrow
25      */
26     public boolean right = true;
27   
28     /** Draw Back of arrow
29      */
30     public boolean back = true;
31   
32     /** Draw solid arrow
33      */
34     public boolean solid = false;
35   
36     /** length/width of arrow */
37     public float aspect = (float)2.0;
38   
39     /** Curvature of side of arrow */
40     public float concavity = (float)0.3;
41   
42     /** Curvature of back of arrow */
43     public float backconcavity = (float)0.3;
44   
45     /** Where back of arrow arrow meets axis */
46     public float backjunction = (float)0.3;
47     
48     /**
49      * The size of the arrow to draw.<br>
50      * If none, the size will be based on the
51      * thickness of the line.
52      */
53     public Size size = Size.none;
54   
55     public String name;
56     public String getName() {return(name);}
57   
58     public Arrow(String name) {
59       this.name = name;
60     }
61   
62     public static TreeSet defaults;
63     public static TreeSet mindefaults = new TreeSet();
64     public static TreeSet file = new TreeSet();
65     public static TreeSet fixed = new TreeSet();
66   
67     public static Arrow none;
68   
69     static {
70       fixed.add(none = new Arrow("None"));
71   
72       mindefaults.add(new Arrow("plain",true,true,false,false,Size.arrow,1.4, 0.5,1,1));
73       mindefaults.add(new Arrow("closed",true,true,false,true,Size.arrow,1.4, 0.5,1,1));
74       mindefaults.add(new Arrow("solid",true,true,true,true,Size.arrow,1.4, 0.5,1,1));
75       mindefaults.add(new Arrow("thin",true,true,false,true,Size.arrow,0.8, 0.3,0.9,0.85));
76       mindefaults.add(new Arrow("thinsolid",true,true,true,true,Size.arrow,0.8, 0.3,0.9,0.85));
77       mindefaults.add(new Arrow("left",true,false,true,true,Size.arrow,0.8, 0.3,0.9,0.85));
78       mindefaults.add(new Arrow("right",false,true,true,true,Size.arrow,0.8, 0.3,0.9,0.85));
79   
80       defaults = (TreeSet)mindefaults.clone();
81   
82       fixedmap.put(Arrow.class,fixed);
83       defmap.put(Arrow.class,defaults);
84       minmap.put(Arrow.class,mindefaults);
85       filemap.put(Arrow.class,file);
86     }
87   
88     public Arrow(String name,
89                  boolean left, boolean right, boolean solid, boolean back,
90                  Size size,double aspect, double concavity,
91                  double backjunction, double backconcavity) {
92       this.name = name;
93       this.left=left;
94       this.right=right;
95       this.back = back;
96       this.solid = solid;
97       this.size= size;
98       this.aspect = (float)aspect;
99       this.concavity = (float)concavity;
100      this.backjunction = (float)backjunction;
101      this.backconcavity = (float)backconcavity;
102    }
103  
104    public boolean edit(JFrame owner) {
105      JPanel panel = new JPanel();
106      panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
107  
108      javax.swing.Box row = new javax.swing.Box(BoxLayout.X_AXIS);
109      JComboBox sizefield = new JComboBox(Size.getAll(Size.class));
110      sizefield.setSelectedItem(size);
111      row.add(sizefield);
112      JTextField aspectfield = PrefEditor.addDouble(row,aspect,"Aspect Ratio");
113      JCheckBox solidbox = new JCheckBox("Solid",solid);
114      row.add(solidbox);
115      panel.add(row);
116  
117      row = new javax.swing.Box(BoxLayout.X_AXIS);
118      JCheckBox leftbox = new JCheckBox("Left",left);
119      row.add(leftbox);
120      JCheckBox rightbox = new JCheckBox("Right",right);
121      row.add(rightbox);
122      JTextField concavityfield = PrefEditor.addDouble(row,concavity,"Concavity");
123      panel.add(row);
124  
125      row = new javax.swing.Box(BoxLayout.X_AXIS);
126  
127      JCheckBox backbox = new JCheckBox("Back",back);
128      row.add(backbox);
129  
130      JTextField backfield = PrefEditor.addDouble(row,backjunction,"Back Junction point");
131      JTextField backconcavityfield = PrefEditor.addDouble(row,backconcavity,"Back Concavity");
132      panel.add(row);
133  
134      if (JOptionPane.showConfirmDialog(owner,panel,"Edit size"+name,JOptionPane.OK_CANCEL_OPTION) ==
135          JOptionPane.OK_OPTION) {
136        left = leftbox.isSelected();
137        right = rightbox.isSelected();
138        back = backbox.isSelected();
139        solid = solidbox.isSelected();
140        aspect = (float)PrefEditor.readDouble(aspectfield);
141        concavity = (float)PrefEditor.readDouble(concavityfield);
142        backjunction = (float)PrefEditor.readDouble(backfield);
143        backconcavity = (float)PrefEditor.readDouble(backconcavityfield);
144        size = (Size)sizefield.getSelectedItem();
145        if (size==Size.none) size = null;
146  
147  //      clearEdit();
148        return(true);
149      }
150  
151  //    clearEdit();
152      return(false);
153  
154    }
155    
156    public Object clone() {
157      Arrow res = new Arrow(name);
158      res.copy(this);
159      return((Object)res);
160    }
161  
162    public void copy(FileDefaultable thatitem) {
163      Arrow that = (Arrow)thatitem;
164      name = that.name;
165      left = that.left;
166      right = that.right;
167      back = that.back;
168      solid = that.solid;
169      size = that.size;
170      aspect = that.aspect;
171      concavity = that.concavity;
172      backjunction = that.backjunction;
173      backconcavity = that.backconcavity;
174    }
175    
176    public Object readResolve() throws ObjectStreamException {
177      return((Object)getGlobal(Arrow.class));
178    }
179  
180    public void draw(View view, Anchor point, int direction, CurveAtt props) {
181      point.guideUpdate();
182  
183      Color color = view.draw.getColor();
184      view.draw.setColor(props.color);
185  
186      double thickness = props.thickness.getSize(view.mapscale);
187      double scale = (size==null?thickness*10:size.getSize(view.mapscale))/view.scale; 
188  
189      float scalethick = (float)(thickness/(view.scale*scale*aspect));
190      float len = (float)(point.position.distance(point.guide[direction])/scale);
191  
192  //    System.out.println("Arrow thick = "+thickness+" scale = "+scale+" len = "+len+" scalethick = "+scalethick+
193  //        	       " View.scale = "+view.scale+"\n");
194  
195      AffineTransform trans = (AffineTransform)view.trans.clone();
196      trans.concatenate(point.position);
197      trans.rotate(-Math.atan2(point.guidelocal[direction].getX(),point.guidelocal[direction].getY()));
198      trans.scale(scale*aspect/2,scale);
199  
200      GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO);
201  
202      if (!solid) {
203        Stroke st = view.draw.getStroke();
204        view.draw.setStroke(new BasicStroke((float)thickness,
205          				  BasicStroke.CAP_BUTT,
206          				  BasicStroke.JOIN_MITER,(float)30.0));
207        path.moveTo(0,0);
208        path.lineTo(0,len-backjunction);
209  
210        if (left) {
211          if (back) path.quadTo((float)-0.5,len-backconcavity,-1,len-1);
212          else path.moveTo(-1,len-1);
213          path.quadTo(-concavity,(float)(len-0.5),0,len);
214        }
215        else
216          path.lineTo(0,len);
217  
218        if (right) {
219          path.quadTo(concavity,(float)(len-0.5),1,len-1);
220          if (back) path.quadTo((float)0.5,len-backconcavity,0,len-backjunction);
221          else path.moveTo(0,len-backjunction);
222        }
223        else
224          path.lineTo(0,len-backjunction);
225  
226        if (!back && left && right) path.lineTo(0,len);
227  
228        Shape shape = trans.createTransformedShape(path);
229        view.draw.draw(shape);
230  
231        view.draw.setStroke(st);
232      }
233      else { //Solid
234        float ppos = 0;
235        if (left) ppos += scalethick;
236        if (right) ppos -= scalethick;
237  
238        path.moveTo(0,0);
239        path.lineTo(-scalethick,0);
240        path.lineTo(-scalethick,len-backjunction);
241  
242        if (left) {
243          path.quadTo((float)-0.5,len-backconcavity,-1,len-1);
244          path.quadTo(-concavity,(float)(len-0.5),ppos,len);
245        }
246        else
247          path.lineTo(ppos,len);
248  
249        if (right) {
250          path.quadTo(concavity,(float)(len-0.5),1,len-1);
251          path.quadTo((float)0.5,len-backconcavity,scalethick,len-backjunction);
252        }
253        else
254          path.lineTo(scalethick,len-backjunction);
255  
256        path.lineTo(scalethick,0);
257        path.closePath();
258  
259        Shape shape = trans.createTransformedShape(path);
260        view.draw.fill(shape);
261      }
262  
263      view.draw.setColor(color);
264    }
265  
266  }
267