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