1 | /* $Id$Revision: */ |
2 | /* vim:set shiftwidth=4 ts=8: */ |
3 | |
4 | /************************************************************************* |
5 | * Copyright (c) 2011 AT&T Intellectual Property |
6 | * All rights reserved. This program and the accompanying materials |
7 | * are made available under the terms of the Eclipse Public License v1.0 |
8 | * which accompanies this distribution, and is available at |
9 | * http://www.eclipse.org/legal/epl-v10.html |
10 | * |
11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
12 | *************************************************************************/ |
13 | |
14 | |
15 | #include "config.h" |
16 | |
17 | #define DEBUG |
18 | |
19 | #include <stddef.h> |
20 | #include <maze.h> |
21 | #include <partition.h> |
22 | #include <memory.h> |
23 | #include <arith.h> |
24 | /* #include <values.h> */ |
25 | |
26 | #define MARGIN 36; |
27 | |
28 | #ifdef DEBUG |
29 | char* pre = "%!PS-Adobe-2.0\n\ |
30 | /node {\n\ |
31 | /Y exch def\n\ |
32 | /X exch def\n\ |
33 | /y exch def\n\ |
34 | /x exch def\n\ |
35 | newpath\n\ |
36 | x y moveto\n\ |
37 | x Y lineto\n\ |
38 | X Y lineto\n\ |
39 | X y lineto\n\ |
40 | closepath fill\n\ |
41 | } def\n\ |
42 | /cell {\n\ |
43 | /Y exch def\n\ |
44 | /X exch def\n\ |
45 | /y exch def\n\ |
46 | /x exch def\n\ |
47 | newpath\n\ |
48 | x y moveto\n\ |
49 | x Y lineto\n\ |
50 | X Y lineto\n\ |
51 | X y lineto\n\ |
52 | closepath stroke\n\ |
53 | } def\n" ; |
54 | |
55 | char* post = "showpage\n" ; |
56 | |
57 | static void |
58 | psdump (cell* gcells, int n_gcells, boxf BB, boxf* rects, int nrect) |
59 | { |
60 | int i; |
61 | boxf bb; |
62 | box absbb; |
63 | |
64 | absbb.LL.y = absbb.LL.x = 10; |
65 | absbb.UR.x = absbb.LL.x + BB.UR.x - BB.LL.x; |
66 | absbb.UR.y = absbb.LL.y + BB.UR.y - BB.LL.y; |
67 | fputs (pre, stderr); |
68 | fprintf (stderr, "%%%%Page: 1 1\n%%%%PageBoundingBox: %d %d %d %d\n" , |
69 | absbb.LL.x, absbb.LL.y, absbb.UR.x, absbb.UR.y); |
70 | |
71 | |
72 | fprintf (stderr, "%f %f translate\n" , 10-BB.LL.x, 10-BB.LL.y); |
73 | fputs ("0 0 1 setrgbcolor\n" , stderr); |
74 | for (i = 0; i < n_gcells; i++) { |
75 | bb = gcells[i].bb; |
76 | fprintf (stderr, "%f %f %f %f node\n" , bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y); |
77 | } |
78 | fputs ("0 0 0 setrgbcolor\n" , stderr); |
79 | for (i = 0; i < nrect; i++) { |
80 | bb = rects[i]; |
81 | fprintf (stderr, "%f %f %f %f cell\n" , bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y); |
82 | } |
83 | fputs ("1 0 0 setrgbcolor\n" , stderr); |
84 | fprintf (stderr, "%f %f %f %f cell\n" , BB.LL.x, BB.LL.y, BB.UR.x, BB.UR.y); |
85 | fputs (post, stderr); |
86 | } |
87 | #endif |
88 | |
89 | static int |
90 | vcmpid(Dt_t* d, pointf* key1, pointf* key2, Dtdisc_t* disc) |
91 | { |
92 | if (key1->x > key2->x) return 1; |
93 | else if (key1->x < key2->x) return -1; |
94 | else if (key1->y > key2->y) return 1; |
95 | else if (key1->y < key2->y) return -1; |
96 | else return 0; |
97 | } |
98 | |
99 | static int |
100 | hcmpid(Dt_t* d, pointf* key1, pointf* key2, Dtdisc_t* disc) |
101 | { |
102 | if (key1->y > key2->y) return 1; |
103 | else if (key1->y < key2->y) return -1; |
104 | else if (key1->x > key2->x) return 1; |
105 | else if (key1->x < key2->x) return -1; |
106 | else return 0; |
107 | } |
108 | |
109 | typedef struct { |
110 | snode* np; |
111 | pointf p; |
112 | Dtlink_t link; |
113 | } snodeitem; |
114 | |
115 | static Dtdisc_t vdictDisc = { |
116 | offsetof(snodeitem,p), |
117 | sizeof(pointf), |
118 | offsetof(snodeitem,link), |
119 | 0, |
120 | 0, |
121 | (Dtcompar_f)vcmpid, |
122 | 0, |
123 | 0, |
124 | 0 |
125 | }; |
126 | static Dtdisc_t hdictDisc = { |
127 | offsetof(snodeitem,p), |
128 | sizeof(pointf), |
129 | offsetof(snodeitem,link), |
130 | 0, |
131 | 0, |
132 | (Dtcompar_f)hcmpid, |
133 | 0, |
134 | 0, |
135 | 0 |
136 | }; |
137 | |
138 | #define delta 1 /* weight of length */ |
139 | #define mu 500 /* weight of bends */ |
140 | |
141 | #define BEND(g,e) ((g->nodes + e->v1)->isVert != (g->nodes + e->v2)->isVert) |
142 | #define HORZ(g,e) ((g->nodes + e->v1)->isVert) |
143 | #define BIG 16384 |
144 | #define CHANSZ(w) (((w)-3)/2) |
145 | #define IS_SMALL(v) (CHANSZ(v) < 2) |
146 | /* #define CHANSZ(w) (w) */ |
147 | |
148 | /* updateWt: |
149 | * At present, we use a step function. When a bound is reached, the weight |
150 | * becomes huge. We might consider bumping up the weight more gradually, the |
151 | * thinner the channel, the faster the weight rises. |
152 | */ |
153 | static void |
154 | updateWt (cell* cp, sedge* ep, int sz) |
155 | { |
156 | ep->cnt++; |
157 | if (ep->cnt > sz) { |
158 | ep->cnt = 0; |
159 | ep->weight += BIG; |
160 | } |
161 | } |
162 | |
163 | /* updateWts: |
164 | * Iterate over edges in a cell, adjust weights as necessary. |
165 | * It always updates the bent edges belonging to a cell. |
166 | * A horizontal/vertical edge is updated only if the edge traversed |
167 | * is bent, or if it is the traversed edge. |
168 | */ |
169 | void |
170 | updateWts (sgraph* g, cell* cp, sedge* ep) |
171 | { |
172 | int i; |
173 | sedge* e; |
174 | int isBend = BEND(g,ep); |
175 | int hsz = CHANSZ (cp->bb.UR.y - cp->bb.LL.y); |
176 | int vsz = CHANSZ (cp->bb.UR.x - cp->bb.LL.x); |
177 | int minsz = MIN(hsz, vsz); |
178 | |
179 | /* Bend edges are added first */ |
180 | for (i = 0; i < cp->nedges; i++) { |
181 | e = cp->edges[i]; |
182 | if (!BEND(g,e)) break; |
183 | updateWt (cp, e, minsz); |
184 | } |
185 | |
186 | for (; i < cp->nedges; i++) { |
187 | e = cp->edges[i]; |
188 | if (isBend || (e == ep)) updateWt (cp, e, (HORZ(g,e)?hsz:vsz)); |
189 | } |
190 | } |
191 | |
192 | /* markSmall: |
193 | * cp corresponds to a real node. If it is small, its associated cells should |
194 | * be marked as usable. |
195 | */ |
196 | static void |
197 | markSmall (cell* cp, sgraph* g) |
198 | { |
199 | int i; |
200 | snode* onp; |
201 | cell* ocp; |
202 | |
203 | if (IS_SMALL(cp->bb.UR.y-cp->bb.LL.y)) { |
204 | for (i = 0; i < cp->nsides; i++) { |
205 | onp = cp->sides[i]; |
206 | if (!onp->isVert) continue; |
207 | if (onp->cells[0] == cp) { /* onp on the right of cp */ |
208 | ocp = onp->cells[1]; |
209 | ocp->flags |= MZ_SMALLV; |
210 | while ((onp = ocp->sides[M_RIGHT]) && !IsNode(onp->cells[1])) { |
211 | ocp = onp->cells[1]; |
212 | ocp->flags |= MZ_SMALLV; |
213 | } |
214 | } |
215 | else { /* onp on the left of cp */ |
216 | ocp = onp->cells[0]; |
217 | ocp->flags |= MZ_SMALLV; |
218 | while ((onp = ocp->sides[M_LEFT]) && !IsNode(onp->cells[0])) { |
219 | ocp = onp->cells[0]; |
220 | ocp->flags |= MZ_SMALLV; |
221 | } |
222 | } |
223 | } |
224 | } |
225 | |
226 | if (IS_SMALL(cp->bb.UR.x-cp->bb.LL.x)) { |
227 | for (i = 0; i < cp->nsides; i++) { |
228 | onp = cp->sides[i]; |
229 | if (onp->isVert) continue; |
230 | if (onp->cells[0] == cp) { /* onp on the top of cp */ |
231 | ocp = onp->cells[1]; |
232 | ocp->flags |= MZ_SMALLH; |
233 | while ((onp = ocp->sides[M_TOP]) && !IsNode(onp->cells[1])) { |
234 | ocp = onp->cells[1]; |
235 | ocp->flags |= MZ_SMALLH; |
236 | } |
237 | } |
238 | else { /* onp on the bottom of cp */ |
239 | ocp = onp->cells[0]; |
240 | ocp->flags |= MZ_SMALLH; |
241 | while ((onp = ocp->sides[M_BOTTOM]) && !IsNode(onp->cells[0])) { |
242 | ocp = onp->cells[0]; |
243 | ocp->flags |= MZ_SMALLH; |
244 | } |
245 | } |
246 | } |
247 | } |
248 | } |
249 | |
250 | static void |
251 | createSEdges (cell* cp, sgraph* g) |
252 | { |
253 | boxf bb = cp->bb; |
254 | double hwt = delta*(bb.UR.x-bb.LL.x); |
255 | double vwt = delta*(bb.UR.y-bb.LL.y); |
256 | double wt = (hwt + vwt)/2.0 + mu; |
257 | |
258 | /* We automatically make small channels have high cost to guide routes to |
259 | * more spacious channels. |
260 | */ |
261 | if (IS_SMALL(bb.UR.y-bb.LL.y) && !IsSmallV(cp)) { |
262 | hwt = BIG; |
263 | wt = BIG; |
264 | } |
265 | if (IS_SMALL(bb.UR.x-bb.LL.x) && !IsSmallH(cp)) { |
266 | vwt = BIG; |
267 | wt = BIG; |
268 | } |
269 | |
270 | if (cp->sides[M_LEFT] && cp->sides[M_TOP]) |
271 | cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_LEFT], cp->sides[M_TOP], wt); |
272 | if (cp->sides[M_TOP] && cp->sides[M_RIGHT]) |
273 | cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_TOP], cp->sides[M_RIGHT], wt); |
274 | if (cp->sides[M_LEFT] && cp->sides[M_BOTTOM]) |
275 | cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_LEFT], cp->sides[M_BOTTOM], wt); |
276 | if (cp->sides[M_BOTTOM] && cp->sides[M_RIGHT]) |
277 | cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_BOTTOM], cp->sides[M_RIGHT], wt); |
278 | if (cp->sides[M_TOP] && cp->sides[M_BOTTOM]) |
279 | cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_TOP], cp->sides[M_BOTTOM], vwt); |
280 | if (cp->sides[M_LEFT] && cp->sides[M_RIGHT]) |
281 | cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_LEFT], cp->sides[M_RIGHT], hwt); |
282 | } |
283 | |
284 | static snode* |
285 | findSVert (sgraph* g, Dt_t* cdt, pointf p, snodeitem* ditems, boolean isVert) |
286 | { |
287 | snodeitem* n = dtmatch (cdt, &p); |
288 | |
289 | if (!n) { |
290 | snode* np = createSNode (g); |
291 | assert(ditems); |
292 | n = ditems + np->index; |
293 | n->p = p; |
294 | n->np = np; |
295 | np->isVert = isVert; |
296 | dtinsert (cdt, n); |
297 | } |
298 | |
299 | return n->np; |
300 | } |
301 | |
302 | static void |
303 | chkSgraph (sgraph* g) |
304 | { |
305 | int i; |
306 | snode* np; |
307 | |
308 | for (i = 0; i < g->nnodes; i++) { |
309 | np = g->nodes+i; |
310 | if (!np->cells[0]) fprintf (stderr, "failed at node %d[0]\n" , i); |
311 | assert (np->cells[0]); |
312 | if (!np->cells[1]) fprintf (stderr, "failed at node %d[1]\n" , i); |
313 | assert (np->cells[1]); |
314 | } |
315 | |
316 | } |
317 | |
318 | /* mkMazeGraph: |
319 | */ |
320 | static sgraph* |
321 | mkMazeGraph (maze* mp, boxf bb) |
322 | { |
323 | int nsides, i, ncnt, maxdeg; |
324 | int bound = 4*mp->ncells; |
325 | sgraph* g = createSGraph (bound + 2); |
326 | Dt_t* vdict = dtopen(&vdictDisc,Dtoset); |
327 | Dt_t* hdict = dtopen(&hdictDisc,Dtoset); |
328 | snodeitem* ditems = N_NEW(bound, snodeitem); |
329 | snode** sides; |
330 | |
331 | /* For each cell, create if necessary and attach a node in search |
332 | * corresponding to each internal face. The node also gets |
333 | * a pointer to the cell. |
334 | */ |
335 | sides = N_NEW(4*mp->ncells, snode*); |
336 | ncnt = 0; |
337 | for (i = 0; i < mp->ncells; i++) { |
338 | cell* cp = mp->cells+i; |
339 | snode* np; |
340 | pointf pt; |
341 | |
342 | cp->nsides = 4; |
343 | cp->sides = sides + 4*i; |
344 | if (cp->bb.UR.x < bb.UR.x) { |
345 | pt.x = cp->bb.UR.x; |
346 | pt.y = cp->bb.LL.y; |
347 | np = findSVert (g, vdict, pt, ditems, TRUE); |
348 | np->cells[0] = cp; |
349 | cp->sides[M_RIGHT] = np; |
350 | } |
351 | if (cp->bb.UR.y < bb.UR.y) { |
352 | pt.x = cp->bb.LL.x; |
353 | pt.y = cp->bb.UR.y; |
354 | np = findSVert (g, hdict, pt, ditems, FALSE); |
355 | np->cells[0] = cp; |
356 | cp->sides[M_TOP] = np; |
357 | } |
358 | if (cp->bb.LL.x > bb.LL.x) { |
359 | np = findSVert (g, vdict, cp->bb.LL, ditems, TRUE); |
360 | np->cells[1] = cp; |
361 | cp->sides[M_LEFT] = np; |
362 | } |
363 | if (cp->bb.LL.y > bb.LL.y) { |
364 | np = findSVert (g, hdict, cp->bb.LL, ditems, FALSE); |
365 | np->cells[1] = cp; |
366 | cp->sides[M_BOTTOM] = np; |
367 | } |
368 | } |
369 | |
370 | /* For each gcell, corresponding to a node in the input graph, |
371 | * connect it to its corresponding search nodes. |
372 | */ |
373 | maxdeg = 0; |
374 | sides = N_NEW(g->nnodes, snode*); |
375 | nsides = 0; |
376 | for (i = 0; i < mp->ngcells; i++) { |
377 | cell* cp = mp->gcells+i; |
378 | pointf pt; |
379 | snodeitem* np; |
380 | |
381 | cp->sides = sides+nsides; |
382 | pt = cp->bb.LL; |
383 | np = dtmatch (hdict, &pt); |
384 | for (; np && np->p.x < cp->bb.UR.x; np = dtnext (hdict, np)) { |
385 | cp->sides[cp->nsides++] = np->np; |
386 | np->np->cells[1] = cp; |
387 | } |
388 | np = dtmatch (vdict, &pt); |
389 | for (; np && np->p.y < cp->bb.UR.y; np = dtnext (vdict, np)) { |
390 | cp->sides[cp->nsides++] = np->np; |
391 | np->np->cells[1] = cp; |
392 | } |
393 | pt.y = cp->bb.UR.y; |
394 | np = dtmatch (hdict, &pt); |
395 | for (; np && np->p.x < cp->bb.UR.x; np = dtnext (hdict, np)) { |
396 | cp->sides[cp->nsides++] = np->np; |
397 | np->np->cells[0] = cp; |
398 | } |
399 | pt.x = cp->bb.UR.x; |
400 | pt.y = cp->bb.LL.y; |
401 | np = dtmatch (vdict, &pt); |
402 | for (; np && np->p.y < cp->bb.UR.y; np = dtnext (vdict, np)) { |
403 | cp->sides[cp->nsides++] = np->np; |
404 | np->np->cells[0] = cp; |
405 | } |
406 | nsides += cp->nsides; |
407 | if (cp->nsides > maxdeg) maxdeg = cp->nsides; |
408 | } |
409 | /* sides = RALLOC (nsides, sides, snode*); */ |
410 | |
411 | /* Mark cells that are small because of a small node, not because of the close |
412 | * alignment of two rectangles. |
413 | */ |
414 | for (i = 0; i < mp->ngcells; i++) { |
415 | cell* cp = mp->gcells+i; |
416 | markSmall (cp, g); |
417 | } |
418 | |
419 | /* Set index of two dummy nodes used for real nodes */ |
420 | g->nodes[g->nnodes].index = g->nnodes; |
421 | g->nodes[g->nnodes+1].index = g->nnodes+1; |
422 | |
423 | /* create edges |
424 | * For each ordinary cell, there can be at most 6 edges. |
425 | * At most 2 gcells will be used at a time, and each of these |
426 | * can have at most degree maxdeg. |
427 | */ |
428 | initSEdges (g, maxdeg); |
429 | for (i = 0; i < mp->ncells; i++) { |
430 | cell* cp = mp->cells+i; |
431 | createSEdges (cp, g); |
432 | } |
433 | |
434 | /* tidy up memory */ |
435 | /* g->nodes = RALLOC (g->nnodes+2, g->nodes, snode); */ |
436 | /* g->edges = RALLOC (g->nedges+2*maxdeg, g->edges, sedge); */ |
437 | dtclose (vdict); |
438 | dtclose (hdict); |
439 | free (ditems); |
440 | |
441 | chkSgraph (g); |
442 | /* save core graph state */ |
443 | gsave(g); |
444 | return g; |
445 | } |
446 | |
447 | /* mkMaze: |
448 | */ |
449 | maze* |
450 | mkMaze (graph_t* g, int doLbls) |
451 | { |
452 | node_t* n; |
453 | maze* mp = NEW(maze); |
454 | boxf* rects; |
455 | int i, nrect; |
456 | cell* cp; |
457 | double w2, h2; |
458 | boxf bb, BB; |
459 | |
460 | mp->ngcells = agnnodes(g); |
461 | cp = mp->gcells = N_NEW(mp->ngcells, cell); |
462 | |
463 | BB.LL.x = BB.LL.y = MAXDOUBLE; |
464 | BB.UR.x = BB.UR.y = -MAXDOUBLE; |
465 | for (n = agfstnode (g); n; n = agnxtnode(g,n)) { |
466 | w2 = ND_xsize(n)/2.0; |
467 | if (w2 < 1) w2 = 1; |
468 | h2 = ND_ysize(n)/2.0; |
469 | if (h2 < 1) h2 = 1; |
470 | bb.LL.x = ND_coord(n).x - w2; |
471 | bb.UR.x = ND_coord(n).x + w2; |
472 | bb.LL.y = ND_coord(n).y - h2; |
473 | bb.UR.y = ND_coord(n).y + h2; |
474 | BB.LL.x = MIN(BB.LL.x, bb.LL.x); |
475 | BB.LL.y = MIN(BB.LL.y, bb.LL.y); |
476 | BB.UR.x = MAX(BB.UR.x, bb.UR.x); |
477 | BB.UR.y = MAX(BB.UR.y, bb.UR.y); |
478 | cp->bb = bb; |
479 | cp->flags |= MZ_ISNODE; |
480 | ND_alg(n) = cp; |
481 | cp++; |
482 | } |
483 | |
484 | if (doLbls) { |
485 | } |
486 | |
487 | BB.LL.x -= MARGIN; |
488 | BB.LL.y -= MARGIN; |
489 | BB.UR.x += MARGIN; |
490 | BB.UR.y += MARGIN; |
491 | rects = partition (mp->gcells, mp->ngcells, &nrect, BB); |
492 | |
493 | #ifdef DEBUG |
494 | if (odb_flags & ODB_MAZE) psdump (mp->gcells, mp->ngcells, BB, rects, nrect); |
495 | #endif |
496 | mp->cells = N_NEW(nrect, cell); |
497 | mp->ncells = nrect; |
498 | for (i = 0; i < nrect; i++) { |
499 | mp->cells[i].bb = rects[i]; |
500 | } |
501 | free (rects); |
502 | |
503 | mp->sg = mkMazeGraph (mp, BB); |
504 | return mp; |
505 | } |
506 | |
507 | void freeMaze (maze* mp) |
508 | { |
509 | free (mp->cells[0].sides); |
510 | free (mp->gcells[0].sides); |
511 | free (mp->cells); |
512 | free (mp->gcells); |
513 | freeSGraph (mp->sg); |
514 | dtclose (mp->hchans); |
515 | dtclose (mp->vchans); |
516 | free (mp); |
517 | } |
518 | |
519 | |