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