Graphviz 16.1.1~dev.20260906.1627
Loading...
Searching...
No Matches
xlabels.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#include "config.h"
12
13#include <assert.h>
14#include <errno.h>
15#include <limits.h>
16#include <math.h>
17#include <stdbool.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#define XLABEL_INT
22#include <label/xlabels.h>
23#include <util/alloc.h>
24#include <util/exit.h>
25
26static int icompare(void *, void *);
27
28Dtdisc_t Hdisc = {.key = offsetof(HDict_t, key),
29 .size = sizeof(int),
30 .link = -1,
31 .freef = free,
32 .comparf = icompare};
33
34static int icompare(void *v1, void *v2) {
35 const int k1 = *(int *)v1;
36 const int k2 = *(int *)v2;
37 if (k1 < k2) {
38 return -1;
39 }
40 if (k1 > k2) {
41 return 1;
42 }
43 return 0;
44}
45
46static XLabels_t *xlnew(object_t *objs, size_t n_objs) {
47 XLabels_t *xlp = gv_alloc(sizeof(XLabels_t));
48
49 /* used to load the rtree in hilbert space filling curve order */
50 if (!(xlp->hdx = dtopen(&Hdisc, Dtobag))) {
51 fprintf(stderr, "out of memory\n");
52 graphviz_exit(EXIT_FAILURE);
53 }
54
55 /* for querying intersection candidates */
56 xlp->spdx = RTreeOpen();
57 /* save arg pointers in the handle */
58 xlp->objs = objs;
59 xlp->n_objs = n_objs;
60
61 return xlp;
62}
63
64static void xlfree(XLabels_t *xlp) {
65 RTreeClose(xlp->spdx);
66 free(xlp);
67}
68
69/***************************************************************************/
70
71/*
72 * determine the order(depth) of the hilbert sfc so that we satisfy the
73 * precondition of hd_hil_s_from_xy()
74 *
75 * @param obj_bb Bounding box of all objects
76 */
77static unsigned int xlhorder(boxf obj_bb) {
78 double maxx = obj_bb.UR.x, maxy = obj_bb.UR.y;
79 return (unsigned)floor(log2(round(fmax(maxx, maxy)))) + 1;
80}
81
82/* from http://www.hackersdelight.org/ site for the book by Henry S Warren */
83/*
84 * precondition
85 * pow(2, n) >= max(p.x, p.y)
86 */
87/* adapted from lams1.c
88Given the "order" n of a Hilbert curve and coordinates x and y, this
89program computes the length s of the curve from the origin to (x, y).
90The square that the Hilbert curve traverses is of size 2**n by 2**n.
91 The method is that given in [Lam&Shap], described by the following
92table. Here i = n-1 for the most significant bit of x and y, and i = 0
93for the least significant bits.
94
95 x[i] y[i] | s[2i+1:2i] x y
96 -----------|-------------------
97 0 0 | 00 y x
98 0 1 | 01 x y
99 1 0 | 11 ~y ~x
100 1 1 | 10 x y
101
102To use this table, start at the most significant bits of x and y
103(i = n - 1). If they are both 0 (first row), set the most significant
104two bits of s to 00 and interchange x and y. (Actually, it is only
105necessary to interchange the remaining bits of x and y.) If the most
106significant bits of x and y are 10 (third row), output 11, interchange x
107and y, and complement x and y.
108 Then, consider the next most significant bits of x and y (which may
109have been changed by this process), and select the appropriate row of
110the table to determine the next two bits of s, and how to change x and
111y. Continue until the least significant bits of x and y have been
112processed. */
113
114static unsigned hd_hil_s_from_xy(point p, unsigned n) {
115 int x = p.x, y = p.y;
116
117 unsigned s = 0; /* Initialize. */
118 for (unsigned i = n - 1; n > 0; i--) {
119 int xi = (x >> i) & 1; /* Get bit i of x. */
120 int yi = (y >> i) & 1; /* Get bit i of y. */
121 s = 4 * s + 2 * (unsigned)xi +
122 ((unsigned)xi ^ (unsigned)yi); // Append two bits to s.
123
124 x = x ^ y; /* These 3 lines swap */
125 y = y ^ (x & (yi - 1)); /* x and y if yi = 0. */
126 x = x ^ y;
127 x = x ^ (-xi & (yi - 1)); /* Complement x and y if */
128 y = y ^ (-xi & (yi - 1)); /* xi = 1 and yi = 0. */
129 if (i == 0) {
130 break;
131 }
132 }
133 return s;
134}
135
136/* intersection test from
137 * from Real-Time Collision Detection 4.2.1 by Christer Ericson
138 * intersection area from
139 * http://stackoverflow.com/questions/4549544/total-area-of-intersecting-rectangles
140 */
141static double aabbaabb(Rect_t r, Rect_t s) {
142 if (!Overlap(r, s))
143 return 0;
144
145 /* if we get here we have an intersection */
146
147 /* rightmost left edge of the 2 rectangles */
148 double iminx = fmax(r.boundary[0], s.boundary[0]);
149 /* upmost bottom edge */
150 double iminy = fmax(r.boundary[1], s.boundary[1]);
151 /* leftmost right edge */
152 double imaxx = fmin(r.boundary[2], s.boundary[2]);
153 /* downmost top edge */
154 double imaxy = fmin(r.boundary[3], s.boundary[3]);
155 return (imaxx - iminx) * (imaxy - iminy);
156}
157
158/*
159 * test if objp1, a size 0 object is enclosed in the xlabel
160 * associated with objp
161 */
162static bool lblenclosing(object_t *objp, object_t *objp1) {
163 xlabel_t *xlp = objp->lbl;
164
165 assert(objp1->sz.x == 0 && objp1->sz.y == 0);
166
167 if (!xlp)
168 return false;
169
170 return objp1->pos.x > xlp->pos.x && objp1->pos.x < xlp->pos.x + xlp->sz.x &&
171 objp1->pos.y > xlp->pos.y && objp1->pos.y < xlp->pos.y + xlp->sz.y;
172}
173
174/*fill in rectangle from the object */
175static Rect_t objp2rect(const object_t *op) {
176 Rect_t r = {0};
177 r.boundary[0] = round(op->pos.x);
178 r.boundary[1] = round(op->pos.y);
179 r.boundary[2] = round(op->pos.x + op->sz.x);
180 r.boundary[3] = round(op->pos.y + op->sz.y);
181 return r;
182}
183
184/*fill in rectangle from the objects xlabel */
185static Rect_t objplp2rect(const object_t *objp) {
186 Rect_t r = {0};
187 const xlabel_t *lp = objp->lbl;
188 r.boundary[0] = round(lp->pos.x);
189 r.boundary[1] = round(lp->pos.y);
190 r.boundary[2] = round(lp->pos.x + lp->sz.x);
191 r.boundary[3] = round(lp->pos.y + lp->sz.y);
192 return r;
193}
194
195/* compute boundary that encloses all possible label boundaries */
196static Rect_t objplpmks(object_t *objp) {
197 Rect_t rect;
198 pointf p = {0};
199
200 if (objp->lbl)
201 p = objp->lbl->sz;
202
203 rect.boundary[0] = floor(objp->pos.x - p.x);
204 rect.boundary[1] = floor(objp->pos.y - p.y);
205
206 rect.boundary[2] = ceil(objp->pos.x + objp->sz.x + p.x);
207 rect.boundary[3] = ceil(objp->pos.y + objp->sz.y + p.y);
208
209 return rect;
210}
211
212/* determine the position clp will occupy in intrsx[] */
213static int getintrsxi(object_t *op, object_t *cp) {
214 xlabel_t *lp = op->lbl, *clp = cp->lbl;
215 assert(lp != clp);
216
217 if (!lp->set || !clp->set)
218 return -1;
219 if ((op->pos.x == 0.0 && op->pos.y == 0.0) ||
220 (cp->pos.x == 0.0 && cp->pos.y == 0.0))
221 return -1;
222
223 if (cp->pos.y < op->pos.y) {
224 if (cp->pos.x < op->pos.x)
225 return XLPXPY;
226 if (cp->pos.x > op->pos.x)
227 return XLNXPY;
228 return XLCXPY;
229 }
230 if (cp->pos.y > op->pos.y) {
231 if (cp->pos.x < op->pos.x)
232 return XLPXNY;
233 if (cp->pos.x > op->pos.x)
234 return XLNXNY;
235 return XLCXNY;
236 }
237 if (cp->pos.x < op->pos.x)
238 return XLPXCY;
239 if (cp->pos.x > op->pos.x)
240 return XLNXCY;
241
242 return -1;
243}
244
245/* record the intersecting objects label */
246static double recordointrsx(object_t *op, object_t *cp, Rect_t rp, double a,
247 object_t *intrsx[XLNBR]) {
248 int i = getintrsxi(op, cp);
249 if (i < 0)
250 i = 5;
251 if (intrsx[i] != NULL) {
252 double sa, maxa = 0.0;
253 /* keep maximally overlapping object */
254 Rect_t srect = objp2rect(intrsx[i]);
255 sa = aabbaabb(rp, srect);
256 if (sa > a)
257 maxa = sa;
258 /*keep maximally overlapping label */
259 if (intrsx[i]->lbl) {
260 srect = objplp2rect(intrsx[i]);
261 sa = aabbaabb(rp, srect);
262 if (sa > a)
263 maxa = fmax(sa, maxa);
264 }
265 if (maxa > 0.0)
266 return maxa;
267 /*replace overlapping label/object pair */
268 intrsx[i] = cp;
269 return a;
270 }
271 intrsx[i] = cp;
272 return a;
273}
274
275/* record the intersecting label */
276static double recordlintrsx(object_t *op, object_t *cp, Rect_t *rp, double a,
277 object_t *intrsx[XLNBR]) {
278 int i = getintrsxi(op, cp);
279 if (i < 0)
280 i = 5;
281 if (intrsx[i] != NULL) {
282 double sa, maxa = 0.0;
283 /* keep maximally overlapping object */
284 Rect_t srect = objp2rect(intrsx[i]);
285 sa = aabbaabb(*rp, srect);
286 if (sa > a)
287 maxa = sa;
288 /*keep maximally overlapping label */
289 if (intrsx[i]->lbl) {
290 srect = objplp2rect(intrsx[i]);
291 sa = aabbaabb(*rp, srect);
292 if (sa > a)
293 maxa = fmax(sa, maxa);
294 }
295 if (maxa > 0.0)
296 return maxa;
297 /*replace overlapping label/object pair */
298 intrsx[i] = cp;
299 return a;
300 }
301 intrsx[i] = cp;
302 return a;
303}
304
305/* find the objects and labels intersecting lp */
306static BestPos_t xlintersections(XLabels_t *xlp, object_t *objp,
307 object_t *intrsx[XLNBR]) {
308 assert(objp->lbl);
309
310 BestPos_t bp = {.pos = objp->lbl->pos};
311
312 for (size_t i = 0; i < xlp->n_objs; i++) {
313 if (objp == &xlp->objs[i])
314 continue;
315 if (xlp->objs[i].sz.x > 0 && xlp->objs[i].sz.y > 0)
316 continue;
317 if (lblenclosing(objp, &xlp->objs[i])) {
318 bp.n++;
319 }
320 }
321
322 Rect_t rect = objplp2rect(objp);
323
324 LeafList_t *llp = RTreeSearch(xlp->spdx, xlp->spdx->root, rect);
325 if (!llp)
326 return bp;
327
328 for (LeafList_t *ilp = llp; ilp; ilp = ilp->next) {
329 double a;
330 object_t *cp = ilp->leaf->data;
331
332 if (cp == objp)
333 continue;
334
335 /*label-object intersect */
336 Rect_t srect = objp2rect(cp);
337 a = aabbaabb(rect, srect);
338 if (a > 0.0) {
339 const double ra = recordointrsx(objp, cp, rect, a, intrsx);
340 bp.n++;
341 bp.area += ra;
342 }
343 /*label-label intersect */
344 if (!cp->lbl || !cp->lbl->set)
345 continue;
346 srect = objplp2rect(cp);
347 a = aabbaabb(rect, srect);
348 if (a > 0.0) {
349 const double ra = recordlintrsx(objp, cp, &rect, a, intrsx);
350 bp.n++;
351 bp.area += ra;
352 }
353 }
355 return bp;
356}
357
358/*
359 * xladjust - find a label position
360 * the individual tests at the top are intended to place a preference order
361 * on the position
362 */
363static BestPos_t xladjust(XLabels_t *xlp, object_t *objp) {
364 xlabel_t *lp = objp->lbl;
365 double xincr = (2 * lp->sz.x + objp->sz.x) / XLXDENOM;
366 double yincr = (2 * lp->sz.y + objp->sz.y) / XLYDENOM;
367 object_t *intrsx[XLNBR] = {0};
368
369 assert(objp->lbl);
370
371 /*x left */
372 lp->pos.x = objp->pos.x - lp->sz.x;
373 /*top */
374 lp->pos.y = objp->pos.y + objp->sz.y;
375 BestPos_t bp = xlintersections(xlp, objp, intrsx);
376 if (bp.n == 0)
377 return bp;
378 /*mid */
379 lp->pos.y = objp->pos.y;
380 BestPos_t nbp = xlintersections(xlp, objp, intrsx);
381 if (nbp.n == 0)
382 return nbp;
383 if (nbp.area < bp.area)
384 bp = nbp;
385 /*bottom */
386 lp->pos.y = objp->pos.y - lp->sz.y;
387 nbp = xlintersections(xlp, objp, intrsx);
388 if (nbp.n == 0)
389 return nbp;
390 if (nbp.area < bp.area)
391 bp = nbp;
392
393 /*x mid */
394 lp->pos.x = objp->pos.x;
395 /*top */
396 lp->pos.y = objp->pos.y + objp->sz.y;
397 nbp = xlintersections(xlp, objp, intrsx);
398 if (nbp.n == 0)
399 return nbp;
400 if (nbp.area < bp.area)
401 bp = nbp;
402 /*bottom */
403 lp->pos.y = objp->pos.y - lp->sz.y;
404 nbp = xlintersections(xlp, objp, intrsx);
405 if (nbp.n == 0)
406 return nbp;
407 if (nbp.area < bp.area)
408 bp = nbp;
409
410 /*x right */
411 lp->pos.x = objp->pos.x + objp->sz.x;
412 /*top */
413 lp->pos.y = objp->pos.y + objp->sz.y;
414 nbp = xlintersections(xlp, objp, intrsx);
415 if (nbp.n == 0)
416 return nbp;
417 if (nbp.area < bp.area)
418 bp = nbp;
419 /*mid */
420 lp->pos.y = objp->pos.y;
421 nbp = xlintersections(xlp, objp, intrsx);
422 if (nbp.n == 0)
423 return nbp;
424 if (nbp.area < bp.area)
425 bp = nbp;
426 /*bottom */
427 lp->pos.y = objp->pos.y - lp->sz.y;
428 nbp = xlintersections(xlp, objp, intrsx);
429 if (nbp.n == 0)
430 return nbp;
431 if (nbp.area < bp.area)
432 bp = nbp;
433
434 /*sliding from top left */
435 if (intrsx[XLPXNY] || intrsx[XLCXNY] || intrsx[XLNXNY] || intrsx[XLPXCY] ||
436 intrsx[XLPXPY]) { /* have to move */
437 if (!intrsx[XLCXNY] && !intrsx[XLNXNY]) { /* some room right? */
438 /* slide along upper edge */
439 for (lp->pos.x = objp->pos.x - lp->sz.x,
440 lp->pos.y = objp->pos.y + objp->sz.y;
441 lp->pos.x <= (objp->pos.x + objp->sz.x); lp->pos.x += xincr) {
442 nbp = xlintersections(xlp, objp, intrsx);
443 if (nbp.n == 0)
444 return nbp;
445 if (nbp.area < bp.area)
446 bp = nbp;
447 }
448 }
449 if (!intrsx[XLPXCY] && !intrsx[XLPXPY]) { /* some room down? */
450 /* slide down left edge */
451 for (lp->pos.x = objp->pos.x - lp->sz.x,
452 lp->pos.y = objp->pos.y + objp->sz.y;
453 lp->pos.y >= (objp->pos.y - lp->sz.y); lp->pos.y -= yincr) {
454 nbp = xlintersections(xlp, objp, intrsx);
455 if (nbp.n == 0)
456 return nbp;
457 if (nbp.area < bp.area)
458 bp = nbp;
459 }
460 }
461 }
462
463 /*sliding from bottom right */
464 lp->pos.x = objp->pos.x + objp->sz.x;
465 lp->pos.y = objp->pos.y - lp->sz.y;
466 if (intrsx[XLNXPY] || intrsx[XLCXPY] || intrsx[XLPXPY] || intrsx[XLNXCY] ||
467 intrsx[XLNXNY]) { /* have to move */
468 if (!intrsx[XLCXPY] && !intrsx[XLPXPY]) { /* some room left? */
469 /* slide along lower edge */
470 for (lp->pos.x = objp->pos.x + objp->sz.x,
471 lp->pos.y = objp->pos.y - lp->sz.y;
472 lp->pos.x >= (objp->pos.x - lp->sz.x); lp->pos.x -= xincr) {
473 nbp = xlintersections(xlp, objp, intrsx);
474 if (nbp.n == 0)
475 return nbp;
476 if (nbp.area < bp.area)
477 bp = nbp;
478 }
479 }
480 if (!intrsx[XLNXCY] && !intrsx[XLNXNY]) { /* some room up? */
481 /* slide up right edge */
482 for (lp->pos.x = objp->pos.x + objp->sz.x,
483 lp->pos.y = objp->pos.y - lp->sz.y;
484 lp->pos.y <= (objp->pos.y + objp->sz.y); lp->pos.y += yincr) {
485 nbp = xlintersections(xlp, objp, intrsx);
486 if (nbp.n == 0)
487 return nbp;
488 if (nbp.area < bp.area)
489 bp = nbp;
490 }
491 }
492 }
493 return bp;
494}
495
496/* load the hilbert sfc keyed tree
497 *
498 * @param obj_bb Bounding box of all objects
499 */
500static int xlhdxload(XLabels_t *xlp, boxf obj_bb) {
501 const unsigned order = xlhorder(obj_bb);
502
503 for (size_t i = 0; i < xlp->n_objs; i++) {
504 HDict_t *hp = gv_alloc(sizeof(HDict_t));
505
506 hp->d.data = &xlp->objs[i];
507 hp->d.rect = objplpmks(&xlp->objs[i]);
508 /* center of the labeling area */
509 const double x = hp->d.rect.boundary[0] +
510 (hp->d.rect.boundary[2] - hp->d.rect.boundary[0]) / 2;
511 const double y = hp->d.rect.boundary[1] +
512 (hp->d.rect.boundary[3] - hp->d.rect.boundary[1]) / 2;
513 assert(x >= INT_MIN && x <= INT_MAX);
514 assert(y >= INT_MIN && y <= INT_MAX);
515 const point pi = {.x = (int)x, .y = (int)y};
516
517 hp->key = hd_hil_s_from_xy(pi, order);
518
519 if (!dtinsert(xlp->hdx, hp))
520 return -1;
521 }
522 return 0;
523}
524
525static void xlspdxload(XLabels_t *xlp) {
526 for (HDict_t *op = dtfirst(xlp->hdx); op; op = dtnext(xlp->hdx, op)) {
527 // tree rectangle data node
528 RTreeInsert(xlp->spdx, op->d.rect, op->d.data, &xlp->spdx->root);
529 }
530}
531
533static int xlinitialize(XLabels_t *xlp, boxf obj_bb) {
534 int r = 0;
535 if ((r = xlhdxload(xlp, obj_bb)) < 0)
536 return r;
537 xlspdxload(xlp);
538 return dtclose(xlp->hdx);
539}
540
541int placeLabels(object_t *objs, size_t n_objs, const label_params_t *params) {
542 int r;
543 XLabels_t *xlp = xlnew(objs, n_objs);
544 if ((r = xlinitialize(xlp, params->bb)) < 0) {
545 xlfree(xlp);
546 return r;
547 }
548
549 /* Place xlabel_t* lp near lp->obj so that the rectangle whose lower-left
550 * corner is lp->pos, and size is lp->sz does not intersect any object
551 * in objs (by convention, an object consisting of a single point
552 * intersects nothing) nor any other label, if possible. On input,
553 * lp->set is false.
554 *
555 * On output, any label with a position should have this stored in
556 * lp->pos and have lp->set true.
557 *
558 * If params->force is true, all labels must be positioned, even if
559 * overlaps are necessary.
560 *
561 * Return 0 if all labels could be placed without overlap;
562 * non-zero otherwise.
563 */
564 r = 0;
565 for (size_t i = 0; i < n_objs; i++) {
566 if (objs[i].lbl == 0)
567 continue;
568 const BestPos_t bp = xladjust(xlp, &objs[i]);
569 if (bp.n == 0) {
570 objs[i].lbl->set = true;
571 } else if (bp.area == 0) {
572 objs[i].lbl->pos = bp.pos;
573 objs[i].lbl->set = true;
574 } else if (params->force) {
575 objs[i].lbl->pos = bp.pos;
576 objs[i].lbl->set = true;
577 } else {
578 r = 1;
579 }
580 }
581 xlfree(xlp);
582 return r;
583}
Memory allocation wrappers that exit on failure.
static void * gv_alloc(size_t size)
Definition alloc.h:47
CDT_API Dtmethod_t * Dtobag
ordered multiset
Definition dttree.c:307
#define dtinsert(d, o)
Definition cdt.h:186
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:10
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:11
#define dtnext(d, o)
Definition cdt.h:181
#define dtfirst(d)
Definition cdt.h:180
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
void free(void *)
node NULL
Definition grammar.y:181
static void freef(void *ident)
void RTreeLeafListFree(LeafList_t *llp)
Definition index.c:37
RTree_t * RTreeOpen(void)
Definition index.c:48
void RTreeClose(RTree_t *rtp)
Definition index.c:81
int RTreeInsert(RTree_t *rtp, Rect_t r, void *data, Node_t **n)
Definition index.c:170
LeafList_t * RTreeSearch(RTree_t *rtp, Node_t *n, Rect_t r)
Definition index.c:130
bool Overlap(const Rect_t r, const Rect_t s)
Definition rectangle.c:103
struct LeafList * next
Definition index.h:61
double boundary[NUMSIDES]
Definition rectangle.h:21
Definition geom.h:41
pointf UR
Definition geom.h:41
int key
Definition cdt.h:85
bool force
if true, all labels must be placed
Definition xlabels.h:38
xlabel_t * lbl
Definition xlabels.h:33
pointf pos
Definition xlabels.h:31
pointf sz
Definition xlabels.h:32
Definition geom.h:27
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
bool set
true if the position has been set (input/output)
Definition xlabels.h:27
pointf sz
Definition xlabels.h:24
pointf pos
Definition xlabels.h:25
Definition grammar.c:90
static BestPos_t xlintersections(XLabels_t *xlp, object_t *objp, object_t *intrsx[XLNBR])
Definition xlabels.c:306
static double aabbaabb(Rect_t r, Rect_t s)
Definition xlabels.c:141
Dtdisc_t Hdisc
Definition xlabels.c:28
static double recordlintrsx(object_t *op, object_t *cp, Rect_t *rp, double a, object_t *intrsx[XLNBR])
Definition xlabels.c:276
static unsigned int xlhorder(boxf obj_bb)
Definition xlabels.c:77
static int xlhdxload(XLabels_t *xlp, boxf obj_bb)
Definition xlabels.c:500
static void xlfree(XLabels_t *xlp)
Definition xlabels.c:64
static void xlspdxload(XLabels_t *xlp)
Definition xlabels.c:525
static Rect_t objplp2rect(const object_t *objp)
Definition xlabels.c:185
static Rect_t objp2rect(const object_t *op)
Definition xlabels.c:175
static double recordointrsx(object_t *op, object_t *cp, Rect_t rp, double a, object_t *intrsx[XLNBR])
Definition xlabels.c:246
static XLabels_t * xlnew(object_t *objs, size_t n_objs)
Definition xlabels.c:46
static int getintrsxi(object_t *op, object_t *cp)
Definition xlabels.c:213
static bool lblenclosing(object_t *objp, object_t *objp1)
Definition xlabels.c:162
static BestPos_t xladjust(XLabels_t *xlp, object_t *objp)
Definition xlabels.c:363
int placeLabels(object_t *objs, size_t n_objs, const label_params_t *params)
Definition xlabels.c:541
static Rect_t objplpmks(object_t *objp)
Definition xlabels.c:196
static unsigned hd_hil_s_from_xy(point p, unsigned n)
Definition xlabels.c:114
static int xlinitialize(XLabels_t *xlp, boxf obj_bb)
Definition xlabels.c:533
static int icompare(void *, void *)
Definition xlabels.c:34