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