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