Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
maze.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11
12#include "config.h"
13
14#define DEBUG
15
16#include <cgraph/alloc.h>
17#include <float.h>
18#include <math.h>
19#include <stdbool.h>
20#include <stddef.h>
21#include <ortho/maze.h>
22#include <ortho/partition.h>
23#include <ortho/trap.h>
24#include <common/arith.h>
25
26#define MARGIN 36;
27
28#ifdef DEBUG
29char* 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
55char* post = "showpage\n";
56
58
59static void
60psdump(cell *gcells, int n_gcells, boxf BB, boxf *rects, size_t nrect) {
61 boxf bb;
62 boxf absbb = {.LL = {.y = 10.0, .x = 10.0}};
63
64 absbb.UR.x = absbb.LL.x + BB.UR.x - BB.LL.x;
65 absbb.UR.y = absbb.LL.y + BB.UR.y - BB.LL.y;
66 fputs (pre, stderr);
67 fprintf(stderr, "%%%%Page: 1 1\n%%%%PageBoundingBox: %.0f %.0f %.0f %.0f\n",
68 absbb.LL.x, absbb.LL.y, absbb.UR.x, absbb.UR.y);
69
70
71 fprintf (stderr, "%f %f translate\n", 10-BB.LL.x, 10-BB.LL.y);
72 fputs ("0 0 1 setrgbcolor\n", stderr);
73 for (int i = 0; i < n_gcells; i++) {
74 bb = gcells[i].bb;
75 fprintf (stderr, "%f %f %f %f node\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
76 }
77 fputs ("0 0 0 setrgbcolor\n", stderr);
78 for (size_t i = 0; i < nrect; i++) {
79 bb = rects[i];
80 fprintf (stderr, "%f %f %f %f cell\n", bb.LL.x, bb.LL.y, bb.UR.x, bb.UR.y);
81 }
82 fputs ("1 0 0 setrgbcolor\n", stderr);
83 fprintf (stderr, "%f %f %f %f cell\n", BB.LL.x, BB.LL.y, BB.UR.x, BB.UR.y);
84 fputs (post, stderr);
85}
86#endif
87
89
90static int vcmpid(void *k1, void *k2) {
91 const pointf *key1 = k1;
92 const pointf *key2 = k2;
93 int dx = dfp_cmp(key1->x, key2->x);
94 if (dx != 0)
95 return dx;
96 return dfp_cmp(key1->y, key2->y);
97}
98
100
101static int hcmpid(void *k1, void *k2) {
102 const pointf *key1 = k1;
103 const pointf *key2 = k2;
104 int dy = dfp_cmp(key1->y, key2->y);
105 if (dy != 0)
106 return dy;
107 return dfp_cmp(key1->x, key2->x);
108}
109
110typedef struct {
114} snodeitem;
115
117 offsetof(snodeitem,p),
118 sizeof(pointf),
119 offsetof(snodeitem,link),
120 0,
121 0,
122 vcmpid,
123};
125 offsetof(snodeitem,p),
126 sizeof(pointf),
127 offsetof(snodeitem,link),
128 0,
129 0,
130 hcmpid,
131};
132
133#define delta 1 /* weight of length */
134#define mu 500 /* weight of bends */
135
136#define BEND(g,e) ((g->nodes + e->v1)->isVert != (g->nodes + e->v2)->isVert)
137#define HORZ(g,e) ((g->nodes + e->v1)->isVert)
138#define BIG 16384
139#define CHANSZ(w) (((w)-3)/2)
140#define IS_SMALL(v) (CHANSZ(v) < 2)
141
150static void updateWt(sedge *ep, double sz) {
151 ep->cnt++;
152 if (ep->cnt > sz) {
153 ep->cnt = 0;
154 ep->weight += BIG;
155 }
156}
157
167void
169{
170 int i;
171 sedge* e;
172 int isBend = BEND(g,ep);
173 const double hsz = CHANSZ(cp->bb.UR.y - cp->bb.LL.y);
174 const double vsz = CHANSZ(cp->bb.UR.x - cp->bb.LL.x);
175 const double minsz = fmin(hsz, vsz);
176
177 /* Bend edges are added first */
178 for (i = 0; i < cp->nedges; i++) {
179 e = cp->edges[i];
180 if (!BEND(g,e)) break;
181 updateWt (e, minsz);
182}
183
184 for (; i < cp->nedges; i++) {
185 e = cp->edges[i];
186 if (isBend || e == ep) updateWt (e, HORZ(g,e)?hsz:vsz);
187 }
188}
189
195static void
197{
198 int i;
199 snode* onp;
200 cell* ocp;
201
202 if (IS_SMALL(cp->bb.UR.y-cp->bb.LL.y)) {
203 for (i = 0; i < cp->nsides; i++) {
204 onp = cp->sides[i];
205 if (!onp->isVert) continue;
206 if (onp->cells[0] == cp) { /* onp on the right of cp */
207 ocp = onp->cells[1];
208 ocp->flags |= MZ_SMALLV;
209 while ((onp = ocp->sides[M_RIGHT]) && !IsNode(onp->cells[1])) {
210 ocp = onp->cells[1];
211 ocp->flags |= MZ_SMALLV;
212 }
213 }
214 else { /* onp on the left of cp */
215 ocp = onp->cells[0];
216 ocp->flags |= MZ_SMALLV;
217 while ((onp = ocp->sides[M_LEFT]) && !IsNode(onp->cells[0])) {
218 ocp = onp->cells[0];
219 ocp->flags |= MZ_SMALLV;
220 }
221 }
222 }
223 }
224
225 if (IS_SMALL(cp->bb.UR.x-cp->bb.LL.x)) {
226 for (i = 0; i < cp->nsides; i++) {
227 onp = cp->sides[i];
228 if (onp->isVert) continue;
229 if (onp->cells[0] == cp) { /* onp on the top of cp */
230 ocp = onp->cells[1];
231 ocp->flags |= MZ_SMALLH;
232 while ((onp = ocp->sides[M_TOP]) && !IsNode(onp->cells[1])) {
233 ocp = onp->cells[1];
234 ocp->flags |= MZ_SMALLH;
235 }
236 }
237 else { /* onp on the bottom of cp */
238 ocp = onp->cells[0];
239 ocp->flags |= MZ_SMALLH;
240 while ((onp = ocp->sides[M_BOTTOM]) && !IsNode(onp->cells[0])) {
241 ocp = onp->cells[0];
242 ocp->flags |= MZ_SMALLH;
243 }
244 }
245 }
246 }
247}
248
250
251static void
253{
254 boxf bb = cp->bb;
255 double hwt = delta*(bb.UR.x-bb.LL.x);
256 double vwt = delta*(bb.UR.y-bb.LL.y);
257 double wt = (hwt + vwt)/2.0 + mu;
258
259 /* We automatically make small channels have high cost to guide routes to
260 * more spacious channels.
261 */
262 if (IS_SMALL(bb.UR.y-bb.LL.y) && !IsSmallV(cp)) {
263 hwt = BIG;
264 wt = BIG;
265 }
266 if (IS_SMALL(bb.UR.x-bb.LL.x) && !IsSmallH(cp)) {
267 vwt = BIG;
268 wt = BIG;
269 }
270
271 if (cp->sides[M_LEFT] && cp->sides[M_TOP])
272 cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_LEFT], cp->sides[M_TOP], wt);
273 if (cp->sides[M_TOP] && cp->sides[M_RIGHT])
274 cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_TOP], cp->sides[M_RIGHT], wt);
275 if (cp->sides[M_LEFT] && cp->sides[M_BOTTOM])
276 cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_LEFT], cp->sides[M_BOTTOM], wt);
277 if (cp->sides[M_BOTTOM] && cp->sides[M_RIGHT])
278 cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_BOTTOM], cp->sides[M_RIGHT], wt);
279 if (cp->sides[M_TOP] && cp->sides[M_BOTTOM])
280 cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_TOP], cp->sides[M_BOTTOM], vwt);
281 if (cp->sides[M_LEFT] && cp->sides[M_RIGHT])
282 cp->edges[cp->nedges++] = createSEdge (g, cp->sides[M_LEFT], cp->sides[M_RIGHT], hwt);
283}
284
286
287static snode*
288findSVert (sgraph* g, Dt_t* cdt, pointf p, snodeitem* ditems, bool isVert)
289{
290 snodeitem* n = dtmatch (cdt, &p);
291
292 if (!n) {
293 snode* np = createSNode (g);
294 assert(ditems);
295 n = ditems + np->index;
296 n->p = p;
297 n->np = np;
298 np->isVert = isVert;
299 dtinsert (cdt, n);
300 }
301
302 return n->np;
303}
304
305static void
307{
308 int i;
309 snode* np;
310
311 for (i = 0; i < g->nnodes; i++) {
312 np = g->nodes+i;
313 if (!np->cells[0]) fprintf (stderr, "failed at node %d[0]\n", i);
314 assert (np->cells[0]);
315 if (!np->cells[1]) fprintf (stderr, "failed at node %d[1]\n", i);
316 assert (np->cells[1]);
317 }
318
319}
320
328static sgraph*
330{
331 int nsides, i, maxdeg;
332 int bound = 4*mp->ncells;
333 sgraph* g = createSGraph (bound + 2);
334 Dt_t* vdict = dtopen(&vdictDisc,Dtoset);
335 Dt_t* hdict = dtopen(&hdictDisc,Dtoset);
336 snodeitem* ditems = gv_calloc(bound, sizeof(snodeitem));
337 snode** sides;
338
339 /* For each cell, create if necessary and attach a node in search
340 * corresponding to each internal face. The node also gets
341 * a pointer to the cell.
342 */
343 sides = gv_calloc(4 * mp->ncells, sizeof(snode*));
344 for (i = 0; i < mp->ncells; i++) {
345 cell* cp = mp->cells+i;
346 snode* np;
347 pointf pt;
348
349 cp->nsides = 4;
350 cp->sides = sides + 4*i;
351 if (cp->bb.UR.x < bb.UR.x) {
352 pt.x = cp->bb.UR.x;
353 pt.y = cp->bb.LL.y;
354 np = findSVert(g, vdict, pt, ditems, true);
355 np->cells[0] = cp;
356 cp->sides[M_RIGHT] = np;
357 }
358 if (cp->bb.UR.y < bb.UR.y) {
359 pt.x = cp->bb.LL.x;
360 pt.y = cp->bb.UR.y;
361 np = findSVert(g, hdict, pt, ditems, false);
362 np->cells[0] = cp;
363 cp->sides[M_TOP] = np;
364 }
365 if (cp->bb.LL.x > bb.LL.x) {
366 np = findSVert(g, vdict, cp->bb.LL, ditems, true);
367 np->cells[1] = cp;
368 cp->sides[M_LEFT] = np;
369 }
370 if (cp->bb.LL.y > bb.LL.y) {
371 np = findSVert(g, hdict, cp->bb.LL, ditems, false);
372 np->cells[1] = cp;
373 cp->sides[M_BOTTOM] = np;
374 }
375 }
376
377 /* For each gcell, corresponding to a node in the input graph,
378 * connect it to its corresponding search nodes.
379 */
380 maxdeg = 0;
381 sides = gv_calloc(g->nnodes, sizeof(snode*));
382 nsides = 0;
383 for (i = 0; i < mp->ngcells; i++) {
384 cell* cp = mp->gcells+i;
385 pointf pt;
386 snodeitem* np;
387
388 cp->sides = sides+nsides;
389 pt = cp->bb.LL;
390 np = dtmatch (hdict, &pt);
391 for (; np && np->p.x < cp->bb.UR.x; np = dtnext (hdict, np)) {
392 cp->sides[cp->nsides++] = np->np;
393 np->np->cells[1] = cp;
394 }
395 np = dtmatch (vdict, &pt);
396 for (; np && np->p.y < cp->bb.UR.y; np = dtnext (vdict, np)) {
397 cp->sides[cp->nsides++] = np->np;
398 np->np->cells[1] = cp;
399 }
400 pt.y = cp->bb.UR.y;
401 np = dtmatch (hdict, &pt);
402 for (; np && np->p.x < cp->bb.UR.x; np = dtnext (hdict, np)) {
403 cp->sides[cp->nsides++] = np->np;
404 np->np->cells[0] = cp;
405 }
406 pt.x = cp->bb.UR.x;
407 pt.y = cp->bb.LL.y;
408 np = dtmatch (vdict, &pt);
409 for (; np && np->p.y < cp->bb.UR.y; np = dtnext (vdict, np)) {
410 cp->sides[cp->nsides++] = np->np;
411 np->np->cells[0] = cp;
412 }
413 nsides += cp->nsides;
414 if (cp->nsides > maxdeg) maxdeg = cp->nsides;
415 }
416
417 /* Mark cells that are small because of a small node, not because of the close
418 * alignment of two rectangles.
419 */
420 for (i = 0; i < mp->ngcells; i++) {
421 cell* cp = mp->gcells+i;
422 markSmall (cp);
423 }
424
425 /* Set index of two dummy nodes used for real nodes */
426 g->nodes[g->nnodes].index = g->nnodes;
427 g->nodes[g->nnodes+1].index = g->nnodes+1;
428
429 /* create edges
430 * For each ordinary cell, there can be at most 6 edges.
431 * At most 2 gcells will be used at a time, and each of these
432 * can have at most degree maxdeg.
433 */
434 initSEdges (g, maxdeg);
435 for (i = 0; i < mp->ncells; i++) {
436 cell* cp = mp->cells+i;
437 createSEdges (cp, g);
438 }
439
440 /* tidy up memory */
441 dtclose (vdict);
442 dtclose (hdict);
443 free (ditems);
444
445chkSgraph (g);
446 /* save core graph state */
447 gsave(g);
448 return g;
449}
450
452
454 node_t* n;
455 maze* mp = gv_alloc(sizeof(maze));
456 boxf* rects;
457 cell* cp;
458 double w2, h2;
459 boxf bb;
460
461 mp->ngcells = agnnodes(g);
462 cp = mp->gcells = gv_calloc(mp->ngcells, sizeof(cell));
463
464 boxf BB = {.LL = {.x = DBL_MAX, .y = DBL_MAX},
465 .UR = {.x = -DBL_MAX, .y = -DBL_MAX}};
466 for (n = agfstnode (g); n; n = agnxtnode(g,n)) {
467 w2 = fmax(1, ND_xsize(n) / 2.0);
468 h2 = fmax(1, ND_ysize(n) / 2.0);
469 bb.LL.x = ND_coord(n).x - w2;
470 bb.UR.x = ND_coord(n).x + w2;
471 bb.LL.y = ND_coord(n).y - h2;
472 bb.UR.y = ND_coord(n).y + h2;
473 BB.LL.x = fmin(BB.LL.x, bb.LL.x);
474 BB.LL.y = fmin(BB.LL.y, bb.LL.y);
475 BB.UR.x = fmax(BB.UR.x, bb.UR.x);
476 BB.UR.y = fmax(BB.UR.y, bb.UR.y);
477 cp->bb = bb;
478 cp->flags |= MZ_ISNODE;
479 ND_alg(n) = cp;
480 cp++;
481 }
482
483 BB.LL.x -= MARGIN;
484 BB.LL.y -= MARGIN;
485 BB.UR.x += MARGIN;
486 BB.UR.y += MARGIN;
487 size_t nrect;
488 rects = partition (mp->gcells, mp->ngcells, &nrect, BB);
489
490#ifdef DEBUG
491 if (odb_flags & ODB_MAZE) psdump (mp->gcells, mp->ngcells, BB, rects, nrect);
492#endif
493 mp->cells = gv_calloc(nrect, sizeof(cell));
494 mp->ncells = nrect;
495 for (size_t i = 0; i < nrect; i++) {
496 mp->cells[i].bb = rects[i];
497 }
498 free (rects);
499
500 mp->sg = mkMazeGraph (mp, BB);
501 return mp;
502}
503
504void freeMaze (maze* mp)
505{
506 free (mp->cells[0].sides);
507 free (mp->gcells[0].sides);
508 free (mp->cells);
509 free (mp->gcells);
510 freeSGraph (mp->sg);
511 dtclose (mp->hchans);
512 dtclose (mp->vchans);
513 free (mp);
514}
515
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define dtmatch(d, o)
Definition cdt.h:192
#define dtinsert(d, o)
Definition cdt.h:193
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:304
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
#define dtnext(d, o)
Definition cdt.h:188
static float dy
Definition draw.c:38
static float dx
Definition draw.c:37
struct pointf_s pointf
void free(void *)
int agnnodes(Agraph_t *g)
Definition graph.c:158
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define ND_ysize(n)
Definition types.h:538
#define ND_alg(n)
Definition types.h:484
#define ND_coord(n)
Definition types.h:490
#define ND_xsize(n)
Definition types.h:537
static snode * findSVert(sgraph *g, Dt_t *cdt, pointf p, snodeitem *ditems, bool isVert)
finds a snode by point or creates it
Definition maze.c:288
static void createSEdges(cell *cp, sgraph *g)
fills cell::sides and sgraph::edges
Definition maze.c:252
#define mu
Definition maze.c:134
#define BIG
Definition maze.c:138
static int vcmpid(void *k1, void *k2)
compares points by X and then by Y
Definition maze.c:90
char * post
Definition maze.c:55
#define IS_SMALL(v)
Definition maze.c:140
#define HORZ(g, e)
Definition maze.c:137
static sgraph * mkMazeGraph(maze *mp, boxf bb)
creates and fills sgraph for maze
Definition maze.c:329
maze * mkMaze(graph_t *g)
creates maze and fills maze::gcells and maze::cells. A subroutine of orthoEdges.
Definition maze.c:453
#define delta
Definition maze.c:133
#define CHANSZ(w)
Definition maze.c:139
void updateWts(sgraph *g, cell *cp, sedge *ep)
updates sedge::weight of cell edges
Definition maze.c:168
#define MARGIN
Definition maze.c:26
static void psdump(cell *gcells, int n_gcells, boxf BB, boxf *rects, size_t nrect)
dumps maze::gcells and maze::cells via rects to PostScript
Definition maze.c:60
void freeMaze(maze *mp)
Definition maze.c:504
char * pre
Definition maze.c:29
static Dtdisc_t vdictDisc
Definition maze.c:116
static int hcmpid(void *k1, void *k2)
compares points by Y and then by X
Definition maze.c:101
static void chkSgraph(sgraph *g)
Definition maze.c:306
static void markSmall(cell *cp)
Definition maze.c:196
#define BEND(g, e)
Definition maze.c:136
static void updateWt(sedge *ep, double sz)
updates single sedge::weight
Definition maze.c:150
static Dtdisc_t hdictDisc
Definition maze.c:124
makes maze with mkMaze for routing orthogonal edges
#define IsSmallH(cp)
cell has small width corresponding to a small width node
Definition maze.h:37
#define IsSmallV(cp)
cell has small height corresponding to a small height node
Definition maze.h:35
#define MZ_SMALLH
Definition maze.h:26
@ M_TOP
Definition maze.h:20
@ M_RIGHT
Definition maze.h:20
@ M_BOTTOM
Definition maze.h:20
@ M_LEFT
Definition maze.h:20
#define MZ_ISNODE
Definition maze.h:22
#define IsNode(cp)
cell corresponds to node
Definition maze.h:29
#define MZ_SMALLV
Definition maze.h:25
int odb_flags
Definition ortho.c:55
boxf * partition(cell *cells, int ncells, size_t *nrects, boxf bb)
partitions space around cells (nodes) into rectangular tiles
Definition partition.c:687
function partition, subroutine of mkMaze
void freeSGraph(sgraph *g)
Definition sgraph.c:102
void initSEdges(sgraph *g, int maxdeg)
Definition sgraph.c:41
sedge * createSEdge(sgraph *g, snode *v1, snode *v2, double wt)
Definition sgraph.c:84
void gsave(sgraph *G)
Definition sgraph.c:19
snode * createSNode(sgraph *g)
Definition sgraph.c:68
sgraph * createSGraph(int nnodes)
Definition sgraph.c:57
graph or subgraph
Definition cgraph.h:425
Definition cdt.h:104
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
result of partitioning available space, part of maze
Definition grid.h:33
int nsides
Definition maze.h:53
int flags
Definition maze.h:42
int nedges
Definition maze.h:43
boxf bb
Definition maze.h:55
sedge * edges[6]
up to six links (sedge) between four sides (snode) of the cell
Definition maze.h:44
snode ** sides
up to four sides: M_RIGHT, M_TOP, M_LEFT, M_BOTTOM
Definition maze.h:54
available channels for orthogonal edges around nodes of graph_t
Definition maze.h:65
cell * cells
cells not corresponding to graph nodes
Definition maze.h:67
cell * gcells
cells corresponding to graph nodes
Definition maze.h:68
int ncells
Definition maze.h:66
Dt_t * vchans
set of vertical channels, created by extractVChans
Definition maze.h:71
int ngcells
Definition maze.h:66
Dt_t * hchans
set of horizontal channels, created by extractHChans.
Definition maze.h:70
sgraph * sg
search graph
Definition maze.h:69
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition sgraph.h:42
double weight
Definition sgraph.h:43
int cnt
Definition sgraph.h:44
int nnodes
Definition sgraph.h:52
snode * nodes
Definition sgraph.h:54
a node of search graph sgraph, is created as a border segment between two adjusted cells of type cell...
Definition sgraph.h:26
bool isVert
Definition sgraph.h:39
int index
Definition sgraph.h:38
struct cell * cells[2]
[0] - left or botom, [1] - top or right adjusted cell
Definition sgraph.h:32
Dtlink_t link
Definition maze.c:113
snode * np
Definition maze.c:111
pointf p
Definition maze.c:112
trapezoid elements and utilities for partition.c
static int dfp_cmp(double f1, double f2)
double floating point three-way comparison
Definition trap.h:71