Source code: com/port80/graph/dot/impl/RouteFactory.java
1 //
2 // Copyright(c) 2002, Chris Leung
3 //
4
5 package com.port80.graph.dot.impl;
6
7 import java.awt.geom.GeneralPath;
8 import java.awt.geom.PathIterator;
9
10 import com.port80.util.msg;
11 import com.port80.util.sprint;
12 import com.port80.util.attr.IAttrFactory;
13 import com.port80.util.struct.FloatList;
14
15 /** Route (DotRoute) attribute factory.
16 *
17 */
18 public class RouteFactory implements IAttrFactory {
19
20 ////////////////////////////////////////////////////////////////////////
21
22 private static final String NAME = "RouteFactory";
23 private static final boolean DEBUG = false;
24
25 private static RouteFactory instance = null;
26
27 ////////////////////////////////////////////////////////////////////////
28
29 private static boolean isDotMode = false;
30 private static double maxY = 0.0;
31
32 ////////////////////////////////////////////////////////////////////////
33
34 public static void dotModeOn(double maxy) {
35 maxY = maxy; isDotMode = true;
36 }
37
38 public static void dotModeOff() {
39 isDotMode = false;
40 }
41
42 // Instance fields /////////////////////////////////////////////////////
43 //
44
45 ////////////////////////////////////////////////////////////////////////
46
47 /** @return The singleton instance of the factory. */
48 public static RouteFactory getInstance() {
49 if (instance == null) instance = new RouteFactory();
50 return instance;
51 }
52
53 /** @return An instance of the specified shape. */
54 public static DotRoute create(String stringvalue) {
55 if (instance == null) instance = new RouteFactory();
56 return (DotRoute)instance.createObject(stringvalue);
57 }
58
59 private RouteFactory() {}
60
61 // IAttrFactory interface //////////////////////////////////////////////
62 //
63
64 /** Create an instance of route from the String spec.
65 *
66 * Current accepted .dot file string format:
67 *
68 * For beizer curves:
69 * [x0,y0 (x11,y11 x12,y12 x13,y13)+]
70 * p0-p1-...
71 * [e|E,xe,ye x0,y0 (x11,y11 x12,y12 x13,y13)+]
72 * p0-p1-...->pe
73 * [s|S,xs,ys [e,xe,ye] x0,y0 (x11,y11 x12,y12 x13,y13)+]
74 * ps<-p0-p1-...->pe
75 * For polyline:
76 * [p x0,y0 x1,y1 ...]
77 * [p s|S,xs,ys x0,y0 x1,y1 ...]
78 * [p e|E,xs,ys x0,y0 x1,y1 ...]
79 *
80 * s=location of tail arrow head from start of spline.
81 * S=location of head arrow head from start of spline.
82 * e=location of head arrow head from end of spline.
83 * E=location of tail arrow head from end of spline.
84 *
85 * where xs,ys and xe,ye are the arrow end points away from the curve.
86 */
87 public Object createObject(String str) {
88
89 if (str == null || str.length() == 0 || str.equals("null")) return null;
90 if (DEBUG) msg.println(NAME + ".createObject(): str=" + str);
91
92 FloatList pts = new FloatList();
93
94 int end = str.length();
95 int start = skipWhiteSpace(str, 0, end);
96 char type = str.charAt(start);
97 FloatList startpt = null;
98 FloatList endpt = null;
99 boolean iscurve = true;
100 boolean isReversed = false;
101 if (type == 'p') {
102 start += 2;
103 iscurve = false;
104 }
105 if (type == 's' || type == 'S') {
106 if (type == 'S') isReversed = true;
107 startpt = new FloatList();
108 start = parsePoint(str, start + 2, end, startpt);
109 if (start >= 0) type = str.charAt(start);
110 }
111 if (type == 'e' || type == 'E') {
112 if (type == 'E') isReversed = true;
113 endpt = new FloatList();
114 start = parsePoint(str, start + 2, end, endpt);
115 }
116 while (start >= 0 && start < end) start = parsePoint(str, start, end, pts);
117 if (start < 0) {
118 msg.err("RouteFactory.createObject(): invalid input string"
119 + ": str=" + str
120 );
121 return null;
122 }
123 if (iscurve) return createBezier(pts, startpt, endpt, isReversed);
124 else return createPolyline(pts, startpt, endpt, isReversed);
125 }
126
127 public boolean isValid(Object a) {
128 return (a instanceof DotRoute);
129 }
130
131 /** The String representation of an attribute value. */
132 public String toString(Object attrObject) {
133 DotRoute attr = (DotRoute)attrObject;
134 PathIterator it = attr.getPath().getPathIterator(null);
135 int type;
136 boolean iscurve = false;
137 StringBuffer ret = new StringBuffer();
138 for (float[] pts = new float[6]; !it.isDone(); it.next()) {
139 type = it.currentSegment(pts);
140 if (ret.length() > 0) ret.append(" ");
141 if (type == PathIterator.SEG_CUBICTO) {
142 iscurve = true;
143 for (int i = -1; i < 5;) {
144 //if(i>=0) ret.append(" ");
145 ret.append(sprint.f(" %.1f,%.1f")
146 .a(pts[++i])
147 .a(isDotMode ? (maxY - pts[++i]) : pts[++i])
148 .end()
149 );
150 }
151 } else ret.append(sprint.f("%.1f,%.1f")
152 .a(pts[0])
153 .a(isDotMode ? (maxY - pts[1]) : pts[1])
154 .end()
155 );
156 }
157 DotPoint pt = attr.getEndPt();
158 if (pt != null) ret.insert(0, sprint.f((attr.isReversed() ? "E" : "e") + ",%.1f,%.1f ")
159 .a(pt.getX())
160 .a(isDotMode ? (maxY - pt.getY()) : pt.getY())
161 .end()
162 );
163 pt = attr.getStartPt();
164 if (pt != null) ret.insert(0, sprint.f((attr.isReversed() ? "S" : "s") + ",%.1f,%.1f ")
165 .a(pt.getX())
166 .a(isDotMode ? (maxY - pt.getY()) : pt.getY())
167 .end()
168 );
169 if (!iscurve) ret.insert(0, "p ");
170 //if(iscurve) ret.replace(0,2,"s,");
171 //else ret.replace(0,2,"e,");
172 return ret.toString();
173 }
174
175 /** The String representation of attribute type itself. */
176 public String toString() {
177 return NAME;
178 }
179 /** Prompt user and present a user interface to obtain an
180 * attribute value from user. */
181 public Object promptUser(String prompt) {
182 return null;
183 }
184
185 ////////////////////////////////////////////////////////////////////////
186
187 /** @return false if parse failed.*/
188 private int parsePoint(String str, int start, int max, FloatList pts) {
189 String xstr, ystr;
190 char c;
191 int end = str.indexOf(',', start);
192 if (end <= 0) return -1;
193 xstr = str.substring(start, end);
194 start = end + 1;
195 while (start < max
196 && !((c = str.charAt(start)) == ' '
197 || c == '\t'
198 || c == '\n'
199 || c == '\r'
200 || c == '\f'
201 )
202 ) ++start;
203 ystr = str.substring(end + 1, start);
204 float x = -1;
205 float y = -1;
206 try {
207 x = Float.parseFloat(xstr);
208 y = Float.parseFloat(ystr);
209 if (isDotMode) y = (float)maxY - y;
210 } catch (Exception e) {
211 msg.err("RouteFactory.createObject(): invalid float numbers"
212 + ": x=" + xstr + ", y=" + ystr
213 );
214 return -1;
215 }
216 if (DEBUG) msg.println(NAME + ".parserPoint(): x=" + x + ", y=" + y);
217 pts.add(x);
218 pts.add(y);
219 start = skipWhiteSpace(str, start, max);
220 return start;
221 }
222
223 private int skipWhiteSpace(String str, int start, int max) {
224 char c;
225 while (start < max &&
226 ( (c = str.charAt(start)) == ' '
227 || c == '\t'
228 || c == '\n'
229 || c == '\r'
230 || c == '\f'
231 )
232 ) ++start;
233 return start;
234 }
235
236 private DotRoute createPolyline(FloatList pts, FloatList startpt, FloatList endpt, boolean isReversed) {
237 GeneralPath ret = new GeneralPath();
238 int n = -1;
239 int max = pts.size();
240 ret.moveTo(pts.get(++n), pts.get(++n));
241 for (; n < max - 1;) {
242 ret.lineTo(pts.get(++n), pts.get(++n));
243 }
244 DotPoint s0 = new DotPoint(pts.get(0), pts.get(1));
245 DotPoint e0 = new DotPoint(pts.get(max - 2), pts.get(max - 1));
246 DotPoint s = (startpt == null ? null : new DotPoint(startpt.get(0), startpt.get(1)));
247 DotPoint e = (endpt == null) ? null : new DotPoint(endpt.get(0), endpt.get(1));
248 return new DotRoute(ret, s0, s, e0, e, isReversed);
249 }
250
251 /** The Bezier points from the .dot file looks like a mess. It
252 * appears to be arranged like this:
253 *
254 * 'e' x0,y0 (xn3,yn3 xn2,yn2 xn1,yn1) ... (x13,y13 x12,y12 x11,y11) x_ y_
255 * p_<-p0->p1->...->pn
256 * 's' x_,y_ (xn3,yn3 xn2,yn2 xn1,yn1) ... (x13,y13 x12,y12 x11,y11) x0 y0
257 * p0->p1->...->pn->p_
258 * where
259 * p_ is the arrow head.
260 */
261 private DotRoute createBezier(FloatList pts, FloatList startpt, FloatList endpt, boolean isReversed) {
262 if (DEBUG) msg.println(NAME + ".createBezier()"
263 + ": size=" + pts.size()
264 + ", start=" + (startpt != null)
265 + ", end=" + (endpt != null)
266 );
267 int max = pts.size();
268 if (max % 6 != 2) msg.err(NAME + ".createBezier(): expected 3n+1 points: size=" + max);
269 if (DEBUG) {
270 StringBuffer buf = new StringBuffer(NAME + ".createBezier(): route=\n");
271 if (startpt != null) buf.append("start=" + startpt.get(0) + "," + startpt.get(1) + "\n");
272 for (int i = 0; i <= max - 2; i += 2) buf.append(" " + pts.get(i) + "," + pts.get(i + 1));
273 if (endpt != null) buf.append("end=" + endpt.get(0) + "," + endpt.get(1));
274 msg.println(buf.toString());
275 }
276 GeneralPath ret = new GeneralPath();
277 float x0, y0;
278 int n = -1;
279 x0 = pts.get(++n);
280 y0 = pts.get(++n);
281 ret.moveTo(x0, y0);
282 while (n < max - 6) {
283 ret.curveTo(pts.get(++n),
284 pts.get(++n),
285 pts.get(++n),
286 pts.get(++n),
287 pts.get(++n),
288 pts.get(++n)
289 );
290 }
291 // Start,end arrows.
292 //
293 //FIXME:
294 /*
295 if(endpt!=null) ret.lineTo(endpt.get(0),endpt.get(1));
296 if(startpt!=null) {
297 ret.moveTo(x0,y0);
298 ret.lineTo(startpt.get(0),startpt.get(1));
299 }
300 */
301 DotPoint s0 = new DotPoint(pts.get(0), pts.get(1));
302 DotPoint e0 = new DotPoint(pts.get(max - 2), pts.get(max - 1));
303 DotPoint s = (startpt == null ? null : new DotPoint(startpt.get(0), startpt.get(1)));
304 DotPoint e = (endpt == null) ? null : new DotPoint(endpt.get(0), endpt.get(1));
305 return new DotRoute(ret, s0, s, e0, e, isReversed);
306 }
307
308 ////////////////////////////////////////////////////////////////////////
309
310 }
311