1    /*
2    Copyright 2000 by Ralph Hartley
3    This software is licenced under the terms of the
4    Gnu Public Licence
5    */
6    import java.awt.geom.*;
7    import java.util.*;
8    
9    public class ViewTransform extends AffineTransform implements ViewOperator,Aligner {
10   
11     private static final long serialVersionUID = Version.getSUID();
12   
13     static int PARENT = 0;
14     static int ABSOLUTE = 0x1;
15     static int PREMULTIPLY = 0x2;
16     static int POSTMULTIPLY = 0x4;
17   //  static int TRANSLATE = 0x8;
18   //  static int SCALEROTATE = 0x16;
19   
20     int mode = ABSOLUTE;
21   
22     double scale = 1.0;
23     double angle = 0.0;
24   
25     SavableWeakHashMap listeners = null;
26     public ViewTransform aligner = null;
27     int alignmentmask = 0;
28   
29     double oldscale = 1;
30     double oldangle = 0;
31     double oldx = 0;
32     double oldy = 0;
33   
34     AffineTransform oldalignment = null;
35   //  boolean onetime = false;
36     public String name = null;
37     int num = 0;
38   
39     public String toString() {
40       if (name==null) return(super.toString());
41       return(name+" "+super.toString());
42     }
43   
44     public ViewTransform(int mode) {
45       if (mode==ABSOLUTE) setTransform(1,0,0,-1,0,0);
46       this.mode = mode;
47     }
48   
49     public ViewTransform() {
50       this(0,0);
51     }
52   
53     public ViewTransform(double scale,double rotation,double x,double y) {
54       mode = PREMULTIPLY;
55       setTransform(scale,rotation,x,y);
56     }
57   
58     public ViewTransform(double scale,double rotation,Point2D translation) {
59       mode = PREMULTIPLY;
60       setTransform(scale,rotation,translation.getX(),translation.getY());
61     }
62   
63     public ViewTransform(Point2D translation) {
64       mode = PREMULTIPLY;
65       scale = 1;
66       angle = 0;
67       setTransform(1,0,0,1,translation.getX(),translation.getY());
68     }
69   
70     public ViewTransform(double x,double y) {
71       mode = PREMULTIPLY;
72       scale = 1;
73       angle = 0;
74       setTransform(1,0,0,1,x,y);
75     }
76   
77     public void setTransform(double scale,double rotation,double x, double y) {
78       this.scale = scale;
79       this.angle = rotation;
80       double c = Math.cos(rotation);
81       double s = Math.sin(rotation);
82       setTransform(scale*c,scale*s,-scale*s,scale*c,x,y);
83     }
84   
85     public boolean setTrans(AffineTransform trans,Point2D pos) {
86       Matrix2 mat = new Matrix2(trans.getScaleX(),trans.getShearX(),trans.getShearY(),trans.getScaleY());
87       boolean res = mat.orthogonalize();
88       if (res) setTransform(mat.getScale(),mat.getAngle(),pos.getX(),pos.getY());
89       return(res);
90     }
91   
92     public Point2D transformOrigin(AffineTransform trans,Point2D res) {
93       res.setLocation(getTranslateX(),getTranslateY());
94       return(trans.transform(res,res));
95     }
96   
97     public double distance(Point2D to) {
98       double dx = to.getX() - getTranslateX();
99       double dy = to.getY() - getTranslateY();
100      return(Math.sqrt(dx*dx+dy*dy));
101    }
102  
103    public double distance(double x,double y) {
104      double dx = x - getTranslateX();
105      double dy = y - getTranslateY();
106      return(Math.sqrt(dx*dx+dy*dy));
107    }
108  
109    public Point2D.Double getTranslation() {
110      return(new Point2D.Double(getTranslateX(),getTranslateY()));
111    }
112  
113    public void setLocation(Point2D newpos) {
114      translate(newpos.getX()-getTranslateX(),
115                newpos.getY()-getTranslateY());
116    }
117  
118    public void setLocation(double x,double y) {
119      translate(x-getTranslateX(),
120                y-getTranslateY());
121    }
122  
123    public Aligner defaultAligner() {
124      return(Aligner.absolute);
125    }
126  
127    public void apply(View v) {
128      v.stack.push(v.trans);
129      v.stack.push(new Double(v.scale));
130      v.stack.push(new Double(v.angle));
131      v.stack.push(v.mapscale);
132      if (mode==ABSOLUTE) {
133        v.scale=scale;
134        v.mapscale = (MapScale)v.mapscale.clone();
135        v.mapscale.display.size=scale;
136        v.angle=angle;
137        v.trans = new AffineTransform(this);
138      }
139      else {
140        v.scale *= scale;
141        v.mapscale = (MapScale)v.mapscale.clone();
142        v.mapscale.display.size=scale;
143        v.angle += angle;
144        v.trans = new AffineTransform(v.trans);
145        if (mode==PREMULTIPLY) v.trans.concatenate(this);
146        else if (mode==POSTMULTIPLY) v.trans.preConcatenate(this);
147      }
148      v.stack.push(this);
149    }
150  
151    public void strip(View v) {
152      v.mapscale = (MapScale)v.stack.pop();
153      v.angle = ((Double)v.stack.pop()).doubleValue();
154      v.scale = ((Double)v.stack.pop()).doubleValue();
155      v.trans = (AffineTransform)v.stack.pop();
156    }
157  
158    public void setScale(double scale,double x,double y) {
159      translate((1.0-scale/this.scale)*x,(1.0-scale/this.scale)*y);
160      scale(scale/this.scale,scale/this.scale);
161      this.scale = scale;
162    }
163  
164    public void setScale(double scale) {
165      setScale(scale,getTranslateX(),getTranslateY());
166    }
167  
168    public void setRotation(double angle,double x, double y) {
169      if (mode==ABSOLUTE) rotate(-angle+this.angle,x,y);
170      else rotate(angle-this.angle,x,y);
171      this.angle = angle;
172    }
173  
174    public void setRotation(double angle) {
175      setRotation(angle,getTranslateX(),getTranslateY());
176    }
177  
178    public boolean hasAlignment() {
179      return(alignmentmask!=0);
180    }
181  
182    public ViewTransform getAlignTransform() {
183      return(this);
184    }
185  
186    public void clearAlignment() {
187      if (alignmentmask != 0) aligner.removeAlignmentListener(this);
188      aligner = null;
189      alignmentmask = 0;
190    }
191  
192    public boolean checkDepend(ViewTransform target) {
193      if (target == this) return(false);
194      if (alignmentmask==0) return(true);
195      return(aligner.checkDepend(target));
196    }
197  
198    public void alignAll(Vector targets, Alignment params) {
199  
200      if (name==null) {
201      name = "aligner "+num;
202      num++;
203      }
204  
205      if (targets.size()==0) return;
206      if (targets.size()==1) {
207        params.distributex = params.distributey = false;
208      }
209  
210      for (int i = 0; i<targets.size();i++) {
211        ViewTransform target = (ViewTransform)targets.elementAt(i);
212        if (target.name == null) {
213        target.name = "alignee "+num;
214        num++;
215        }
216  
217        if (!checkDepend(target))
218          ErrorLog.log("Tried to align a symbol to itself\n" +
219          	     "(directly or indirectly)");
220        else {
221          if (params.zerorot || params.zeroscale || params.zerox || params.zeroy ||
222              params.distributex || params.distributey) {
223            Point2D.Double myorigin = getTranslation();
224  
225            if (params.zerorot) target.setRotation(this.angle);
226            if (params.zeroscale) target.setRotation(this.scale);
227  
228            if (params.distributex)
229              myorigin.x += scale*(2.0*i/(targets.size()-1) - 1.0);
230            else if (!params.zerox)
231              myorigin.x = target.getTranslateX();
232  
233            if (params.distributey)
234              myorigin.y = scale*(2.0*i/(targets.size()-1) - 1.0);
235            else if (!params.zeroy)
236              myorigin.y = target.getTranslateY();
237  
238            target.setLocation(myorigin);
239  
240            target.notifyChange();
241          }
242          if (params.mask!=0) {
243            target.track(this,params.mask);
244          }
245        }
246      }
247    }
248  
249    public void track(ViewTransform master, int mask) {
250      clearAlignment();
251      master.addAlignmentListener(this);
252      try {
253        oldalignment = master.createInverse();
254      } catch (NoninvertibleTransformException ex){}
255      oldx = master.getTranslateX();
256      oldy = master.getTranslateY();
257      oldscale = master.scale;
258      oldangle = master.angle;
259      aligner = master;
260      alignmentmask = mask;
261      System.out.println("setting "+this);
262    }
263  
264    /**
265     *Print list of AlignmentListeners<br>
266     *to standard output.
267     *For debuggung purposes.
268     */
269    public void listListeners() {
270      System.out.print("Viewtrans "+this);
271      if (listeners==null) System.out.println(" has no listeners");
272      else {
273        System.out.println("");
274        for (java.util.Iterator it = listeners.keySet().iterator();it.hasNext();)
275          System.out.println(it.next());
276      }
277    }
278  
279    public void addAlignmentListener(AlignmentListener target) {
280      if (target!=null) {
281  //      System.out.println("View "+this+"\n   adding listener "+target);
282        if (listeners==null) listeners = new SavableWeakHashMap();
283        listeners.put(target,null);
284      }
285    }
286  
287    public void removeAlignmentListener(Aligner target) {
288      if (target!=null && listeners!=null) {
289        listeners.remove(target);
290        if (listeners.isEmpty()) listeners = null;
291      }
292    }
293  
294    public ViewTransform getAlignView() {
295      return(this);
296    }
297  
298    public void alignmentChanged() {
299  
300      if (aligner==null || alignmentmask==0)
301        ErrorLog.log("Alignment changed on a component with no alignment set\n"+
302          	   "(may be harmless but should never happen)\n"+
303          	   "mask = "+alignmentmask+" aligner = "+aligner);
304      else {
305        double dscale = aligner.scale/oldscale;
306        double dangle = aligner.angle - oldangle;
307  
308  //      System.out.println("start ="+this);
309  
310        oldalignment.preConcatenate(aligner);
311        if ((alignmentmask&8)==0) {
312          oldalignment.scale(1.0/dscale,1.0/dscale);
313          dscale = 1;
314        }
315        if ((alignmentmask&4)==0) {
316          oldalignment.rotate(dangle);
317          dangle = 0;
318        }
319        if ((alignmentmask&1)==0) {
320          oldalignment.setToScale(dscale,dscale);
321          oldalignment.rotate(-dangle);
322          concatenate(oldalignment);
323        }
324        else 
325          preConcatenate(oldalignment);
326  //oldalignment.translate(-oldalignment.getTranslateX(),
327  //        					       -oldalignment.getTranslateY());
328        
329  //      System.out.println("tranformed by = "+oldalignment);
330        angle += dangle;
331        scale *= dscale;
332  
333  //      System.out.println("end ="+this);
334        try {
335          oldalignment = aligner.createInverse();
336        } catch (NoninvertibleTransformException ex){}
337        oldscale = aligner.scale;
338        oldangle = aligner.angle;
339        oldx = aligner.getTranslateX();
340        oldy = aligner.getTranslateY();
341        notifyChange();
342      }
343    }
344  
345    public void notifyChange() {
346  //    System.out.println("notify by "+this);
347  //    if (listeners!=null) System.out.println("\n     "+listeners.keySet());
348      if (listeners!=null)
349        for (Iterator it = listeners.keySet().iterator();it.hasNext();)
350          ((AlignmentListener)it.next()).alignmentChanged();
351    }
352  
353    public void showAlignee(View view) {
354      if (aligner!=null) {
355        Point2D apos = aligner.showAligner(view);
356        if (apos!=null) {
357          Point2D tpos = getTranslation();
358          view.trans.transform(tpos,tpos);
359          view.draw.drawLine((int)apos.getX(),(int)apos.getY(),(int)tpos.getX(),(int)tpos.getY());
360        }
361      }
362    }
363  
364    public Point2D showAligner(View view) {
365      int BOXSIZE = (int)Size.handle.getSize(view.mapscale);
366      Point2D tpos = getTranslation();
367      view.trans.transform(tpos,tpos);
368  
369      java.awt.Color color = view.draw.getColor();
370      view.draw.setColor(Comp.alignercolor);
371  
372      view.draw.fillRect((int)tpos.getX()-BOXSIZE,(int)tpos.getY()-BOXSIZE,2*BOXSIZE,2*BOXSIZE);  
373  
374      view.draw.setColor(color);
375      return(tpos);
376    }
377  }
378  
379