1
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
18
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
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
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
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
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
327
328
329
330 angle += dangle;
331 scale *= dscale;
332
333
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
347
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