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