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