Add initial implementation of external labels
authorEmden Gansner <erg@research.att.com>
Fri, 29 Jul 2011 18:34:30 +0000 (14:34 -0400)
committerEmden Gansner <erg@research.att.com>
Fri, 29 Jul 2011 18:34:30 +0000 (14:34 -0400)
23 files changed:
configure.ac
lib/Makefile.am
lib/common/Makefile.am
lib/common/emit.c
lib/common/globals.h
lib/common/input.c
lib/common/output.c
lib/common/postproc.c
lib/common/splines.c
lib/dotgen/dotsplines.c
lib/gvc/Makefile.am
lib/label/Makefile.am [new file with mode: 0644]
lib/label/index.c [new file with mode: 0644]
lib/label/index.h [new file with mode: 0644]
lib/label/node.c [new file with mode: 0644]
lib/label/node.h [new file with mode: 0644]
lib/label/nrtmain.c [new file with mode: 0644]
lib/label/rectangle.c [new file with mode: 0644]
lib/label/rectangle.h [new file with mode: 0644]
lib/label/split.q.c [new file with mode: 0644]
lib/label/split.q.h [new file with mode: 0644]
lib/label/xlabels.c [new file with mode: 0644]
lib/label/xlabels.h [new file with mode: 0644]

index 633f46bb18021082b9684302b31d4ad2af0659b2..86291bdffef281d0049459c92ed0ab853df3dfef 100644 (file)
@@ -3221,6 +3221,7 @@ AC_CONFIG_FILES(Makefile
        lib/neatogen/Makefile
        lib/fdpgen/Makefile
        lib/sparse/Makefile
+       lib/label/Makefile
        lib/sfdpgen/Makefile
        lib/osage/Makefile
        lib/gvpr/Makefile
index 9745352f03ae9342a6aad1eff06191cef0add598..9b39861b7ab94c6b050454119c9ac430419c8736 100644 (file)
@@ -4,7 +4,7 @@
 
 SUBDIRS = cdt graph cgraph gd pathplan sfio vmalloc ast \
        vpsc rbtree ortho sparse patchwork expr common \
-       pack xdot gvc ingraphs topfish glcomp \
+       pack xdot gvc ingraphs topfish glcomp label \
        circogen dotgen dotgen2 fdpgen neatogen twopigen sfdpgen osage gvpr
 
 EXTRA_DIST = Makefile.old gvc.vcproj gvc.def
index 654e225f6db9d3c36c44fbcb713a151c0eeba422..0ed8da8d67d9034dbb3f48794607a28ee1081e40 100644 (file)
@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
         -I$(top_srcdir) \
        -I$(top_srcdir)/lib/gvc \
        -I$(top_srcdir)/lib/pack \
+       -I$(top_srcdir)/lib/label \
        -I$(top_srcdir)/lib/xdot \
        -I$(top_srcdir)/lib/fdpgen \
        -I$(top_srcdir)/lib/pathplan \
index 33a473db67db57b800e77b1615d8b7abb0e9c55e..01b85ba6d8d18cc3634d314d5cd113e8e3b2be60 100644 (file)
@@ -1432,7 +1432,7 @@ static void emit_node(GVJ_t * job, node_t * n)
 
        emit_begin_node(job, n);
        ND_shape(n)->fns->codefn(job, n);
-       if (ND_xlabel(n))
+       if (ND_xlabel(n) && ND_xlabel(n)->set)
            emit_label(job, EMIT_NLABEL, ND_xlabel(n));
        emit_end_node(job);
     }
@@ -1991,7 +1991,7 @@ static boolean edge_in_box(edge_t *e, boxf b)
         return TRUE;
 
     lp = ED_xlabel(e);
-    if (lp && overlap_label(lp, b))
+    if (lp && lp->set && overlap_label(lp, b))
         return TRUE;
 
     return FALSE;
@@ -2184,7 +2184,7 @@ emit_edge_label(GVJ_t* job, textlabel_t* lbl, emit_state_t lkind, int explicit,
     char* newid;
     char* type;
 
-    if (lbl == NULL) return;
+    if ((lbl == NULL) || !(lbl->set)) return;
     if (id) { /* non-NULL if needed */
        newid = N_NEW(strlen(id) + sizeof("-headlabel"),char);
        switch (lkind) {
index 679ad25d62fd6d66d6d1dd94482e00e2dc6cba74..a0d17cd329dc40659a6da8eab64d2b50c3bd7452 100644 (file)
@@ -85,6 +85,7 @@ extern "C" {
     EXTERN int MaxIter;
     EXTERN int Ndim;
     EXTERN int State;          /* last finished phase */
+    EXTERN int EdgeLabelsDone; /* true if edge labels have been positioned */
     EXTERN double Initial_dist;
     EXTERN double Damping;
     EXTERN int Y_invert;       /* invert y in dot & plain output */
index b9ab99c9e21e3e18c1b03eaf19408cea75a2b5f2..2f708d71141e4ec9efb540019330575cbd268365 100644 (file)
@@ -760,6 +760,7 @@ void graph_init(graph_t * g, boolean use_rankdir)
     p = agget(g, "concentrate");
     Concentrate = mapbool(p);
     State = GVBEGIN;
+    EdgeLabelsDone = 0;
 
     GD_drawing(g)->dpi = 0.0;
     if (((p = agget(g, "dpi")) && p[0])
index 59ca78b68a971bcda9abdac65bb2d7279912462f..88508a2f16532f16b28d8b676f386949f85842f1 100644 (file)
@@ -362,7 +362,7 @@ void attach_attrs_and_arrows(graph_t* g, int* sp, int* ep)
        sprintf(buf, "%.5g", PS2INCH(ND_lw(n) + ND_rw(n)));
        agxset(n, N_width, buf);
 #endif
-       if (ND_xlabel(n)) {
+       if (ND_xlabel(n) && ND_xlabel(n)->set) {
            ptf = ND_xlabel(n)->pos;
            sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
            agset(n, "xlp", buf);
@@ -443,7 +443,7 @@ void attach_attrs_and_arrows(graph_t* g, int* sp, int* ep)
                    sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
                    agset(e, "lp", buf);
                }
-               if (ED_xlabel(e)) {
+               if (ED_xlabel(e) && ED_xlabel(e)->set) {
                    ptf = ED_xlabel(e)->pos;
                    sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
                    agset(e, "xlp", buf);
index 2945198a147fdb46aed6a15381ba775187441397..b7022b3d6d9311a99bf951d45ecb524e66960865 100644 (file)
@@ -13,6 +13,7 @@
 
 
 #include "render.h"
+#include "xlabels.h"
 
 static int Rankdir;
 static boolean Flip;
@@ -80,7 +81,7 @@ static void place_flip_graph_label(graph_t * g);
 
 static pointf map_point(pointf p)
 {
-    p = ccwrotatepf(p, Rankdir*90);
+    p = ccwrotatepf(p, Rankdir * 90);
     p.x -= Offset.x;
     p.y -= Offset.y;
     return p;
@@ -93,7 +94,7 @@ static void map_edge(edge_t * e)
 
     if (ED_spl(e) == NULL) {
        if ((Concentrate == FALSE) || (ED_edge_type(e) != IGNORED))
-           agerr(AGERR, "lost %s %s edge\n",agnameof(agtail(e)),
+           agerr(AGERR, "lost %s %s edge\n", agnameof(agtail(e)),
                  agnameof(aghead(e)));
        return;
     }
@@ -149,9 +150,11 @@ static void translate_drawing(graph_t * g)
     edge_t *e;
     int shift = (Offset.x || Offset.y);
 
-    if (!shift && !Rankdir) return;
+    if (!shift && !Rankdir)
+       return;
     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
-       if (Rankdir) gv_nodesize(v, FALSE);
+       if (Rankdir)
+           gv_nodesize(v, FALSE);
        ND_coord(v) = map_point(ND_coord(v));
        if (ND_xlabel(v))
            ND_xlabel(v)->pos = map_point(ND_xlabel(v)->pos);
@@ -190,23 +193,187 @@ static void place_root_label(graph_t * g, pointf d)
     GD_label(g)->set = TRUE;
 }
 
-static void 
-addXLabels (Agraph_t* g)
+static pointf
+centerPt (xlabel_t* xlp) {
+  pointf p;
+
+  p = xlp->pos;
+  p.x += (xlp->sz.x)/2.0;
+  p.y += (xlp->sz.y)/2.0;
+
+  return p;
+}
+
+static int
+printData (object_t* objs, int n_objs, xlabel_t* lbls, int n_lbls,
+          label_params_t* params) {
+  int i;
+  fprintf (stderr, "%d objs %d xlabels force=%d bb=(%.02f,%.02f) (%.02f,%.02f)\n",
+          n_objs, n_lbls, params->force, params->bb.LL.x, params->bb.LL.y,
+          params->bb.UR.x, params->bb.UR.y);
+  if (Verbose < 2) return 0;
+  for (i = 0; i < n_objs; i++) {
+    fprintf (stderr, " [%d] (%.02f,%.02f) (%.02f,%.02f) %p\n",
+            i, objs->pos.x,objs->pos.y,objs->sz.x,objs->sz.y, objs->lbl);
+    objs++;
+  }
+  for (i = 0; i < n_lbls; i++) {
+    fprintf (stderr, " [%d] %p (%.02f,%.02f) %s\n",
+            i, lbls, lbls->sz.x,lbls->sz.y, ((textlabel_t*)lbls->lbl)->text);  
+    lbls++;
+  }
+  return 0;
+}
+
+/* addXLabels:
+ * Position xlabels and any unpositioned edge labels using
+ * a map placement algorithm to avoid overlap.
+ *
+ * TODO: interaction with spline=ortho
+ *       interaction with rankdir=LR
+ */
+static void addXLabels(Agraph_t * gp)
 {
+    Agnode_t *np;
+    Agedge_t *ep;
+    int cnt, i, n_objs, n_lbls;
+    int n_nlbls = 0;           /* # of node xlabels */
+    int n_elbls = 0;           /* # of edge xlabels */
+    int n_set_elbls = 0;       /* # of edge labels set */
+    boxf bb;
+    pointf ur;
     textlabel_t* lp;
-    pointf p, pp;
-    Agnode_t* n;
-
-    for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
-       lp = ND_xlabel(n);
-       if (!lp) continue;
-       p = ND_coord(n);
-       
-       pp.y = p.y;
-       pp.x += p.x + ND_rw(n) + lp->dimen.x/2.0; 
-       lp->pos = pp;
-       lp->set = 1;
+    label_params_t params;
+    object_t* objs;
+    xlabel_t* lbls;
+    object_t* objp;
+    xlabel_t* xlp;
+    Agsym_t* force;
+
+    if (!(GD_has_labels(gp) & NODE_XLABEL) &&
+       !(GD_has_labels(gp) & EDGE_XLABEL) &&
+       ((!GD_has_labels(gp) & EDGE_LABEL) || EdgeLabelsDone))
+       return;
+
+    for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
+       if (ND_xlabel(np))
+           n_nlbls++;
+       for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
+           if (ED_xlabel(ep))
+               n_elbls++;
+           if (ED_label(ep)) {
+               if (ED_label(ep)->set)
+                   n_set_elbls++;
+               else
+                   n_elbls++;
+           }
+       }
+    }
+
+    /* An object for each node, each positioned edge label, and all unset edge
+     * labels and xlabels.
+     */
+    n_objs = agnnodes(gp) + n_set_elbls + n_elbls;
+    /* A label for each unpositioned label */
+    n_lbls = n_nlbls + n_elbls;
+    objp = objs = N_NEW(n_objs, object_t);
+    xlp = lbls = N_NEW(n_lbls, xlabel_t);
+    bb.LL = pointfof(INT_MAX, INT_MAX);
+    bb.UR = pointfof(-INT_MAX, -INT_MAX);
+
+    for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
+       /* Add an obstacle per node */
+       objp->sz.x = INCH2PS(ND_width(np));
+       objp->sz.y = INCH2PS(ND_height(np));
+       objp->pos = ND_coord(np);
+       objp->pos.x -= (objp->sz.x) / 2.0;
+       objp->pos.y -= (objp->sz.y) / 2.0;
+
+       /* Adjust bounding box */
+       bb.LL.x = MIN(bb.LL.x, objp->pos.x);
+       bb.LL.y = MIN(bb.LL.y, objp->pos.y);
+       ur.x = objp->pos.x + objp->sz.x;
+       ur.y = objp->pos.y + objp->sz.y;
+       bb.UR.x = MAX(bb.UR.x, ur.x);
+       bb.UR.y = MAX(bb.UR.y, ur.y);
+
+       if (ND_xlabel(np)) {
+           xlp->sz = ND_xlabel(np)->dimen;
+           xlp->lbl = ND_xlabel(np);
+           xlp->set = 0;
+           objp->lbl = xlp;
+           xlp++;
+       }
+       objp++;
+       for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
+           if ((lp = ED_label(ep))) {
+               if (lp->set) {
+                   objp->sz.x = lp->dimen.x; 
+                   objp->sz.y = lp->dimen.y;
+                   objp->pos = lp->pos;
+                   objp->pos.x -= (objp->sz.x) / 2.0;
+                   objp->pos.y -= (objp->sz.y) / 2.0;
+
+                       /* Adjust bounding box */
+                   bb.LL.x = MIN(bb.LL.x, objp->pos.x);
+                   bb.LL.y = MIN(bb.LL.y, objp->pos.y);
+                   ur.x = objp->pos.x + objp->sz.x;
+                   ur.y = objp->pos.y + objp->sz.y;
+                   bb.UR.x = MAX(bb.UR.x, ur.x);
+                   bb.UR.y = MAX(bb.UR.y, ur.y);
+               }
+               else {
+                   objp->sz.x = 0;
+                   objp->sz.y = 0;
+                   objp->pos = edgeMidpoint(gp, ep);
+
+                   xlp->sz = ED_label(ep)->dimen;
+                   xlp->lbl = ED_label(ep);
+                   xlp->set = 0;
+                   objp->lbl = xlp;
+                   xlp++;
+               }
+               objp++;
+           }
+           if (ED_xlabel(ep)) {
+               objp->sz.x = 0;
+               objp->sz.y = 0;
+               objp->pos = edgeMidpoint(gp, ep);
+
+               xlp->sz = ED_xlabel(ep)->dimen;
+               xlp->lbl = ED_xlabel(ep);
+               xlp->set = 0;
+               objp->lbl = xlp;
+               xlp++;
+               objp++;
+           }
+       }
+    }
+
+    force = agfindgraphattr(gp, "forcelabels");
+
+    params.force = late_bool(gp, force, FALSE);
+    params.bb = bb;
+    if (Verbose)
+       printData(objs, n_objs, lbls, n_lbls, &params);
+    placeLabels(objs, n_objs, lbls, n_lbls, &params);
+
+    xlp = lbls;
+    cnt = 0;
+    for (i = 0; i < n_lbls; i++) {
+       if (xlp->set) {
+           cnt++;
+           lp = (textlabel_t *) (xlp->lbl);
+           lp->set = 1;
+           lp->pos = centerPt(xlp);
+           updateBB (gp, lp);
+       }
+       xlp++;
     }
+    if (Verbose)
+       fprintf (stderr, "%d out of %d labels positioned.\n", cnt, n_lbls);
+    free(objs);
+    free(lbls);
 }
 
 /* dotneato_postprocess:
@@ -216,12 +383,12 @@ addXLabels (Agraph_t* g)
  * Assumes the boxes of all clusters have been computed.
  * When done, the bounding box of g has LL at origin.
  */
-void gv_postprocess(Agraph_t *g, int allowTranslation)
+void gv_postprocess(Agraph_t * g, int allowTranslation)
 {
     double diff;
-    pointf dimen = {0., 0.};
+    pointf dimen = { 0., 0. };
 
-    addXLabels (g);
+    addXLabels(g);
 
     Rankdir = GD_rankdir(g);
     Flip = GD_flip(g);
@@ -230,7 +397,7 @@ void gv_postprocess(Agraph_t *g, int allowTranslation)
     else
        place_graph_label(g);
 
-       /* Add space for graph label if necessary */
+    /* Add space for graph label if necessary */
     if (GD_label(g) && !GD_label(g)->set) {
        dimen = GD_label(g)->dimen;
        PAD(dimen);
@@ -293,15 +460,15 @@ void gv_postprocess(Agraph_t *g, int allowTranslation)
        if (Flip)
            sprintf(buf, M2, Offset.x, Offset.y, Offset.x, Offset.y);
        else
-           sprintf(buf, M1, Offset.y, Offset.x, Offset.y, Offset.x, 
-                 -Offset.x, -Offset.y);
+           sprintf(buf, M1, Offset.y, Offset.x, Offset.y, Offset.x,
+                   -Offset.x, -Offset.y);
        Show_boxes[0] = strdup(buf);
     }
 }
 
 void dotneato_postprocess(Agraph_t * g)
 {
-    gv_postprocess (g, 1);
+    gv_postprocess(g, 1);
 }
 
 /* place_flip_graph_label:
@@ -314,9 +481,9 @@ static void place_flip_graph_label(graph_t * g)
 
 #ifndef WITH_CGRAPH
     if ((g != g->root) && (GD_label(g)) && !GD_label(g)->set) {
-#else /* WITH_CGRAPH */
+#else                          /* WITH_CGRAPH */
     if ((g != agroot(g)) && (GD_label(g)) && !GD_label(g)->set) {
-#endif /* WITH_CGRAPH */
+#endif                         /* WITH_CGRAPH */
 
        if (GD_label_pos(g) & LABEL_AT_TOP) {
            d = GD_border(g)[RIGHT_IX];
@@ -354,9 +521,9 @@ void place_graph_label(graph_t * g)
 
 #ifndef WITH_CGRAPH
     if ((g != g->root) && (GD_label(g)) && !GD_label(g)->set) {
-#else /* WITH_CGRAPH */
+#else                          /* WITH_CGRAPH */
     if ((g != agroot(g)) && (GD_label(g)) && !GD_label(g)->set) {
-#endif /* WITH_CGRAPH */
+#endif                         /* WITH_CGRAPH */
        if (GD_label_pos(g) & LABEL_AT_TOP) {
            d = GD_border(g)[TOP_IX];
            p.y = GD_bb(g).UR.y - d.y / 2;
index ada1efeab972c50153de4755c711334f4f5c736c..c0e8fffa8b8391492fb005d0be639e397edeeb5c 100644 (file)
@@ -1232,6 +1232,7 @@ edgeMidpoint (graph_t* g, edge_t * e)
  */
 void addEdgeLabels(graph_t* g, edge_t * e, pointf rp, pointf rq)
 {
+#if 0
     int et = EDGE_TYPE (g);
     pointf p, q;
     pointf d;                  /* midpoint of segment p-q */
@@ -1286,6 +1287,7 @@ void addEdgeLabels(graph_t* g, edge_t * e, pointf rp, pointf rq)
        ED_label(e)->set = TRUE;
        updateBB(agraphof(agtail(e)), ED_label(e));
     }
+#endif
     makePortLabels(e);
 }
 
index 1520a4c577b22e1b7bdf17c379a8b2c7b13eb2bf..5fc6a4a2460ed46b0b5d485de7d29e98f2ad1a18 100644 (file)
@@ -494,6 +494,7 @@ finish :
     } 
 #endif
     State = GVSPLINES;
+    EdgeLabelsDone = 1;
 }
 
 /* dot_splines:
index acffaf6f9127f64a5420e357646dc520d76dd731..d5e3404e674b2b5a4d3ed9c05834222d20d0abdb 100644 (file)
@@ -44,10 +44,12 @@ libgvc_C_la_SOURCES = gvrender.c gvlayout.c gvdevice.c gvloadimage.c \
 libgvc_C_la_LIBADD = \
        $(top_builddir)/lib/pack/libpack_C.la \
        $(top_builddir)/lib/xdot/libxdot_C.la \
+       $(top_builddir)/lib/label/liblabel_C.la \
        $(top_builddir)/lib/common/libcommon_C.la
 libgvc_C_la_DEPENDENCIES = \
        $(top_builddir)/lib/pack/libpack_C.la \
        $(top_builddir)/lib/xdot/libxdot_C.la \
+       $(top_builddir)/lib/label/liblabel_C.la \
        $(top_builddir)/lib/common/libcommon_C.la
 if WITH_ORTHO
 libgvc_C_la_LIBADD += $(top_builddir)/lib/ortho/libortho_C.la
diff --git a/lib/label/Makefile.am b/lib/label/Makefile.am
new file mode 100644 (file)
index 0000000..a6c8809
--- /dev/null
@@ -0,0 +1,18 @@
+# $Id$ $Revision$
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+    -I$(top_srcdir) \
+       -I$(top_srcdir)/lib/gvc \
+       -I$(top_srcdir)/lib/pathplan \
+       -I$(top_srcdir)/lib/common \
+       -I$(top_srcdir)/lib/graph \
+    -I$(top_srcdir)/lib/cdt
+
+noinst_HEADERS = xlabels.h index.h node.h rectangle.h split.q.h
+noinst_LTLIBRARIES = liblabel_C.la
+
+liblabel_C_la_SOURCES = xlabels.c index.c node.c rectangle.c split.q.c
+
+EXTRA_DIST = rtmain.c
+
diff --git a/lib/label/index.c b/lib/label/index.c
new file mode 100644 (file)
index 0000000..0f67a80
--- /dev/null
@@ -0,0 +1,473 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#include <stdlib.h>
+
+#include "index.h"
+#include <stdio.h>
+#include <assert.h>
+#include "logic.h"
+#include "memory.h"
+
+LeafList_t *RTreeNewLeafList(Leaf_t * lp)
+{
+    LeafList_t *llp;
+
+    if ((llp = NEW(LeafList_t)))
+       llp->leaf = lp;
+    return llp;
+}
+
+LeafList_t *RTreeLeafListAdd(LeafList_t * llp, Leaf_t * lp)
+{
+    LeafList_t *nlp;
+    if (!lp)
+       return llp;
+
+    nlp = RTreeNewLeafList(lp);
+    nlp->next = llp;
+    return nlp;
+}
+
+void RTreeLeafListFree(LeafList_t * llp)
+{
+    LeafList_t *tlp;
+    while (llp->next) {
+       tlp = llp->next;
+       free(llp);
+       llp = tlp;
+    }
+    return;
+}
+
+/* Allocate space for a node in the list used in DeletRect to
+ * store Nodes that are too empty.
+ */
+static struct ListNode *RTreeNewListNode()
+{
+    return NEW(struct ListNode);
+}
+
+#if UNUSED
+static void RTreeFreeListNode(struct ListNode *p)
+{
+    free(p);
+}
+#endif
+
+/* Add a node to the reinsertion list.  All its branches will later
+ * be reinserted into the index structure.
+ */
+static int RTreeReInsert(RTree_t * rtp, Node_t * n, struct ListNode **ee)
+{
+    register struct ListNode *l;
+
+    if (!(l = RTreeNewListNode()))
+       return -1;
+    l->node = n;
+    l->next = *ee;
+    *ee = l;
+    return 0;
+}
+
+RTree_t *RTreeOpen()
+{
+    RTree_t *rtp;
+
+    if ((rtp = NEW(RTree_t)))
+       rtp->root = RTreeNewIndex(rtp);
+    return rtp;
+}
+
+/* Make a new index, empty.  Consists of a single node. */
+Node_t *RTreeNewIndex(RTree_t * rtp)
+{
+    Node_t *x;
+    x = RTreeNewNode(rtp);
+    x->level = 0;              /* leaf */
+    rtp->LeafCount++;
+    return x;
+}
+
+static int RTreeClose2(RTree_t * rtp, Node_t * n)
+{
+    int i;
+
+    if (n->level > 0) {
+       for (i = 0; i < NODECARD; i++) {
+           if (!n->branch[i].child)
+               continue;
+           if (!RTreeClose2(rtp, n->branch[i].child)) {
+               free(n->branch[i].child);
+               DisconBranch(n, i);
+               rtp->EntryCount--;
+               if (rtp->StatFlag)
+                   rtp->ElimCount++;
+           }
+       }
+    } else {
+       for (i = 0; i < NODECARD; i++) {
+           if (!n->branch[i].child)
+               continue;
+           DisconBranch(n, i);
+           rtp->EntryCount--;
+       }
+    }
+    return 0;
+}
+
+
+int RTreeClose(RTree_t * rtp)
+{
+    RTreeClose2(rtp, rtp->root);
+    free(rtp);
+    return 0;
+}
+
+#ifdef RTDEBUG
+/* Print out all the nodes in an index.
+** Prints from root downward.
+*/
+void PrintIndex(Node_t * n)
+{
+    int i;
+    Node_t *nn;
+    assert(n);
+    assert(n->level >= 0);
+
+    if (n->level > 0) {
+       for (i = 0; i < NODECARD; i++) {
+           if ((nn = n->branch[i].child) != NULL)
+               PrintIndex(nn);
+       }
+    }
+
+    PrintNode(n);
+}
+
+/* Print out all the data rectangles in an index.
+*/
+void PrintData(Node_t * n)
+{
+    int i;
+    Node_t *nn;
+    assert(n);
+    assert(n->level >= 0);
+
+    if (n->level == 0)
+       PrintNode(n);
+    else {
+       for (i = 0; i < NODECARD; i++) {
+           if ((nn = n->branch[i].child) != NULL)
+               PrintData(nn);
+       }
+    }
+}
+#endif
+
+/* RTreeSearch in an index tree or subtree for all data retangles that
+** overlap the argument rectangle.
+** Returns the number of qualifying data rects.
+*/
+LeafList_t *RTreeSearch(RTree_t * rtp, Node_t * n, Rect_t * r)
+{
+    register int i;
+    LeafList_t *llp = 0;
+
+    assert(n);
+    assert(n->level >= 0);
+    assert(r);
+
+    rtp->SeTouchCount++;
+
+    if (n->level > 0) {                /* this is an internal node in the tree */
+       for (i = 0; i < NODECARD; i++)
+           if (n->branch[i].child && Overlap(r, &n->branch[i].rect)) {
+               LeafList_t *tlp = RTreeSearch(rtp, n->branch[i].child, r);
+               if (llp) {
+                   LeafList_t *xlp = llp;
+                   while (xlp->next)
+                       xlp = xlp->next;
+                   xlp->next = tlp;
+               } else
+                   llp = tlp;
+           }
+    } else {                   /* this is a leaf node */
+       for (i = 0; i < NODECARD; i++) {
+           if (n->branch[i].child && Overlap(r, &n->branch[i].rect)) {
+               llp = RTreeLeafListAdd(llp, (Leaf_t *) & n->branch[i]);
+#                              ifdef RTDEBUG
+               PrintRect(&n->branch[i].rect);
+#                              endif
+           }
+       }
+    }
+    return llp;
+}
+
+/* Insert a data rectangle into an index structure.
+** RTreeInsert provides for splitting the root;
+** returns 1 if root was split, 0 if it was not.
+** The level argument specifies the number of steps up from the leaf
+** level to insert; e.g. a data rectangle goes in at level = 0.
+** RTreeInsert2 does the recursion.
+*/
+static int RTreeInsert2(RTree_t *, Rect_t *, void *, Node_t *, Node_t **,
+                       int);
+/*static int RTreeInsert2(RTree_t*, Rect_t*, int, Node_t*, Node_t**, int); */
+
+int
+RTreeInsert(RTree_t * rtp, Rect_t * r, void *data, Node_t ** n, int level)
+{
+    /* RTreeInsert(RTree_t*rtp, Rect_t*r, int data, Node_t**n, int level) { */
+    register int i;
+    register Node_t *newroot;
+    Node_t *newnode;
+    Branch_t b;
+    int result = 0;
+
+
+    assert(r && n);
+    assert(level >= 0 && level <= (*n)->level);
+    for (i = 0; i < NUMDIMS; i++)
+       assert(r->boundary[i] <= r->boundary[NUMDIMS + i]);
+
+#      ifdef RTDEBUG
+    fprintf(stderr, "RTreeInsert  level=%d\n", level);
+#      endif
+
+    if (rtp->StatFlag) {
+       if (rtp->Deleting)
+           rtp->ReInsertCount++;
+       else
+           rtp->InsertCount++;
+    }
+    if (!rtp->Deleting)
+       rtp->RectCount++;
+
+    if (RTreeInsert2(rtp, r, data, *n, &newnode, level)) {     /* root was split */
+       if (rtp->StatFlag) {
+           if (rtp->Deleting)
+               rtp->DeTouchCount++;
+           else
+               rtp->InTouchCount++;
+       }
+
+       newroot = RTreeNewNode(rtp);    /* grow a new root, make tree taller */
+       rtp->NonLeafCount++;
+       newroot->level = (*n)->level + 1;
+       b.rect = NodeCover(*n);
+       b.child = *n;
+       AddBranch(rtp, &b, newroot, NULL);
+       b.rect = NodeCover(newnode);
+       b.child = newnode;
+       AddBranch(rtp, &b, newroot, NULL);
+       *n = newroot;
+       rtp->EntryCount += 2;
+       result = 1;
+    }
+
+    return result;
+}
+
+/* Inserts a new data rectangle into the index structure.
+** Recursively descends tree, propagates splits back up.
+** Returns 0 if node was not split.  Old node updated.
+** If node was split, returns 1 and sets the pointer pointed to by
+** new to point to the new node.  Old node updated to become one of two.
+** The level argument specifies the number of steps up from the leaf
+** level to insert; e.g. a data rectangle goes in at level = 0.
+*/
+static int
+RTreeInsert2(RTree_t * rtp, Rect_t * r, void *data,
+            Node_t * n, Node_t ** new, int level)
+{
+    /*static int */
+    /* RTreeInsert2(RTree_t*rtp, Rect_t*r,
+       int data, Node_t*n, Node_t**new, int level) {
+     */
+    register int i;
+    Branch_t b;
+    Node_t *n2;
+
+    assert(r && n && new);
+    assert(level >= 0 && level <= n->level);
+
+    if (rtp->StatFlag) {
+       if (rtp->Deleting)
+           rtp->DeTouchCount++;
+       else
+           rtp->InTouchCount++;
+    }
+
+    /* Still above level for insertion, go down tree recursively */
+    if (n->level > level) {
+       i = PickBranch(r, n);
+       if (!RTreeInsert2(rtp, r, data, n->branch[i].child, &n2, level)) {      /* recurse: child was not split */
+           n->branch[i].rect = CombineRect(r, &(n->branch[i].rect));
+           return 0;
+       } else {                /* child was split */
+           n->branch[i].rect = NodeCover(n->branch[i].child);
+           b.child = n2;
+           b.rect = NodeCover(n2);
+           rtp->EntryCount++;
+           return AddBranch(rtp, &b, n, new);
+       }
+    } else if (n->level == level) {    /* at level for insertion. */
+       /*Add rect, split if necessary */
+       b.rect = *r;
+       b.child = (Node_t *) data;
+       rtp->EntryCount++;
+       return AddBranch(rtp, &b, n, new);
+    } else {                   /* Not supposed to happen */
+       assert(FALSE);
+       return 0;
+    }
+}
+
+static void FreeListNode(register struct ListNode *p)
+{
+    free(p);
+}
+
+/* Delete a data rectangle from an index structure.
+** Pass in a pointer to a Rect, the data of the record, ptr to ptr to root node.
+** Returns 1 if record not found, 0 if success.
+** RTreeDelete provides for eliminating the root.
+*/
+static int RTreeDelete2(RTree_t *, Rect_t *, void *, Node_t *,
+                       ListNode_t **);
+/* static int RTreeDelete2(RTree_t*, Rect_t*, int, Node_t*, ListNode_t**); */
+
+int RTreeDelete(RTree_t * rtp, Rect_t * r, void *data, Node_t ** nn)
+{
+    /* int */
+    /* RTreeDelete(RTree_t*rtp, Rect_t*r, int data, Node_t**nn) { */
+    register int i;
+    register Node_t *t;
+    struct ListNode *reInsertList = NULL;
+    register struct ListNode *e;
+
+    assert(r && nn);
+    assert(*nn);
+    assert(data >= 0);
+
+    rtp->Deleting = TRUE;
+
+#      ifdef RTDEBUG
+    fprintf(stderr, "RTreeDelete\n");
+#      endif
+
+    if (!RTreeDelete2(rtp, r, data, *nn, &reInsertList)) {
+       /* found and deleted a data item */
+       if (rtp->StatFlag)
+           rtp->DeleteCount++;
+       rtp->RectCount--;
+
+       /* reinsert any branches from eliminated nodes */
+       while (reInsertList) {
+           t = reInsertList->node;
+           for (i = 0; i < NODECARD; i++) {
+               if (t->branch[i].child) {
+                   RTreeInsert(rtp, &(t->branch[i].rect),
+                               /* (int)t->branch[i].child, nn, t->level); */
+                               t->branch[i].child, nn, t->level);
+                   rtp->EntryCount--;
+               }
+           }
+           e = reInsertList;
+           reInsertList = reInsertList->next;
+           RTreeFreeNode(rtp, e->node);
+           FreeListNode(e);
+       }
+
+       /* check for redundant root (not leaf, 1 child) and eliminate */
+       if ((*nn)->count == 1 && (*nn)->level > 0) {
+           if (rtp->StatFlag)
+               rtp->ElimCount++;
+           rtp->EntryCount--;
+           for (i = 0; i < NODECARD; i++) {
+               if ((t = (*nn)->branch[i].child))
+                   break;
+           }
+           RTreeFreeNode(rtp, *nn);
+           *nn = t;
+       }
+       rtp->Deleting = FALSE;
+       return 0;
+    } else {
+       rtp->Deleting = FALSE;
+       return 1;
+    }
+}
+
+/* Delete a rectangle from non-root part of an index structure.
+** Called by RTreeDelete.  Descends tree recursively,
+** merges branches on the way back up.
+*/
+static int
+RTreeDelete2(RTree_t * rtp, Rect_t * r, void *data, Node_t * n,
+            ListNode_t ** ee)
+/* static int */
+/* RTreeDelete2(RTree_t*rtp, Rect_t*r, int data, Node_t*n, ListNode_t**ee) */
+{
+    register int i;
+    Rect_t NodeCover();
+
+    assert(r && n && ee);
+    assert(data >= 0);
+    assert(n->level >= 0);
+
+    if (rtp->StatFlag)
+       rtp->DeTouchCount++;
+
+    if (n->level > 0) {                /* not a leaf node */
+       for (i = 0; i < NODECARD; i++) {
+           if (n->branch[i].child && Overlap(r, &(n->branch[i].rect))) {
+               if (!RTreeDelete2(rtp, r, data, n->branch[i].child, ee)) {      /*recurse */
+                   if (n->branch[i].child->count >= rtp->MinFill)
+                       n->branch[i].rect = NodeCover(n->branch[i].child);
+                   else {      /* not enough entries in child, eliminate child node */
+                       RTreeReInsert(rtp, n->branch[i].child, ee);
+                       DisconBranch(n, i);
+                       rtp->EntryCount--;
+                       if (rtp->StatFlag)
+                           rtp->ElimCount++;
+                   }
+                   return 0;
+               }
+           }
+       }
+       return 1;
+    } else {                   /* a leaf node */
+       for (i = 0; i < NODECARD; i++) {
+           if (n->branch[i].child
+               && n->branch[i].child == (Node_t *) data) {
+               DisconBranch(n, i);
+               rtp->EntryCount--;
+               return 0;
+           }
+       }
+       return 1;
+    }
+}
+
+#ifdef UNUSED
+/* Allocate space for a node in the list used in DeletRect to
+** store Nodes that are too empty.
+*/
+struct ListNode *NewListNode()
+{
+    return (struct ListNode *) malloc(sizeof(struct ListNode));
+}
+
+#endif
diff --git a/lib/label/index.h b/lib/label/index.h
new file mode 100644 (file)
index 0000000..676a0e1
--- /dev/null
@@ -0,0 +1,136 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifndef INDEX_H
+#define INDEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * this library is derived from an archived home directory of Antonin Guttman 
+ * that implemented the ideas described in
+ * "R-trees: a dynamic index structure for spatial searching"
+ * Antonin Guttman, University of California, Berkeley
+ * SIGMOD '84 Proceedings of the 1984 ACM SIGMOD international conference on Management of data
+ * ISBN:0-89791-128-8
+ * http://dx.doi.org/10.1145/602259.602266
+ * this copy of the code was retrieved from
+ * http://web.archive.org/web/20030210112132/http://www.es.ucsc.edu/~tonig/rtrees/
+ * we are using the quadratic node splitter only
+ * we made a few cosmetic changes to fit our needs
+ * per Antonin there is no copyright
+ */
+
+/* %W% %G% */
+/*-----------------------------------------------------------------------------
+| Global definitions.
+-----------------------------------------------------------------------------*/
+
+#ifndef NUMDIMS
+#define NUMDIMS 2
+#endif /*NUMDIMS*/
+/* #define NDEBUG */
+#define NUMSIDES 2*NUMDIMS
+/* branching factor of a node */
+/* #define NODECARD (int)((PGSIZE-(2*sizeof(int)))/sizeof(struct Branch))*/
+#define NODECARD 64
+typedef struct RTree RTree_t;
+
+#include <rectangle.h>
+#include <node.h>
+#include <split.q.h>
+
+#define CX(i)  (i)
+#define NX(i)  (i+NUMDIMS)
+#define CY(i)  (i+1)
+#define NY(i)  (i+1+NUMDIMS)
+
+typedef struct Leaf {
+    Rect_t rect;
+    void *data;
+} Leaf_t;
+
+typedef struct LeafList {
+    struct LeafList *next;
+    Leaf_t *leaf;
+} LeafList_t;
+
+#ifndef METHODS
+#define METHODS 1
+#endif /*METHODS*/
+    struct RTree {
+    Node_t *root;
+
+    SplitQ_t split;
+
+    /* balance criterion for node splitting */
+    int MinFill;
+
+    /* times */
+    long ElapsedTime;
+    float UserTime, SystemTime;
+
+    int Deleting;
+
+    /* variables for statistics */
+    int StatFlag;              /* tells if we are counting or not */
+    /* counters affected only when StatFlag set */
+    int InsertCount;
+    int DeleteCount;
+    int ReInsertCount;
+    int InSplitCount;
+    int DeSplitCount;
+    int ElimCount;
+    int EvalCount;
+    int InTouchCount;
+    int DeTouchCount;
+    int SeTouchCount;
+    int CallCount;
+    float SplitMeritSum;
+
+    /* counters used even when StatFlag not set */
+    int RectCount;
+    int NodeCount;
+    int LeafCount, NonLeafCount;
+    int EntryCount;
+    int SearchCount;
+    int HitCount;
+
+};
+
+typedef struct ListNode {
+    struct ListNode *next;
+    struct Node *node;
+} ListNode_t;
+
+RTree_t *RTreeOpen();
+int RTreeClose(RTree_t * rtp);
+Node_t *RTreeNewIndex(RTree_t * rtp);
+LeafList_t *RTreeSearch(RTree_t *, Node_t *, Rect_t *);
+int RTreeInsert(RTree_t *, Rect_t *, void *, Node_t **, int);
+int RTreeDelete(RTree_t *, Rect_t *, void *, Node_t **);
+
+LeafList_t *RTreeNewLeafList(Leaf_t * lp);
+LeafList_t *RTreeLeafListAdd(LeafList_t * llp, Leaf_t * lp);
+void RTreeLeafListFree(LeafList_t * llp);
+
+#ifdef RTDEBUG
+void PrintNode(Node_t *);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif                         /*INDEX_H */
diff --git a/lib/label/node.c b/lib/label/node.c
new file mode 100644 (file)
index 0000000..cdbe4f7
--- /dev/null
@@ -0,0 +1,208 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#include <stdlib.h>
+
+#include "index.h"
+#include <stdio.h>
+#include <assert.h>
+#include "node.h"
+
+/* Make a new node and initialize to have all branch cells empty.
+*/
+Node_t *RTreeNewNode(RTree_t * rtp)
+{
+    register Node_t *n;
+
+    rtp->NodeCount++;
+    n = (Node_t *) malloc(sizeof(Node_t));
+    InitNode(n);
+    return n;
+}
+
+void RTreeFreeNode(RTree_t * rtp, Node_t * p)
+{
+    rtp->NodeCount--;
+    if (p->level == 0)
+       rtp->LeafCount--;
+    else
+       rtp->NonLeafCount--;
+    free(p);
+}
+
+/* Initialize a Node structure.
+*/
+void InitNode(Node_t * n)
+{
+    register int i;
+    n->count = 0;
+    n->level = -1;
+    for (i = 0; i < NODECARD; i++)
+       InitBranch(&(n->branch[i]));
+}
+
+/* Initialize one branch cell in a node.
+*/
+void InitBranch(Branch_t * b)
+{
+    InitRect(&(b->rect));
+    b->child = NULL;
+}
+
+#ifdef RTDEBUG
+/* Print out the data in a node.
+*/
+void PrintNode(Node_t * n)
+{
+    int i;
+    assert(n);
+
+    fprintf(stderr, "node");
+    if (n->level == 0)
+       fprintf(stderr, " LEAF");
+    else if (n->level > 0)
+       fprintf(stderr, " NONLEAF");
+    else
+       fprintf(stderr, " TYPE=?");
+    fprintf(stderr, "  level=%d  count=%d  child address=%X\n",
+           n->level, n->count, (unsigned int) n);
+
+    for (i = 0; i < NODECARD; i++) {
+       if (n->branch[i].child != NULL)
+           PrintBranch(i, &n->branch[i]);
+    }
+}
+
+void PrintBranch(int i, Branch_t * b)
+{
+    fprintf(stderr, "  child[%d] X%X\n", i, (unsigned int) b->child);
+    PrintRect(&(b->rect));
+}
+#endif
+
+/* Find the smallest rectangle that includes all rectangles in
+** branches of a node.
+*/
+Rect_t NodeCover(Node_t * n)
+{
+    register int i, flag;
+    Rect_t r, CombineRect();
+    assert(n);
+
+    InitRect(&r);
+    flag = 1;
+    for (i = 0; i < NODECARD; i++)
+       if (n->branch[i].child) {
+           if (flag) {
+               r = n->branch[i].rect;
+               flag = 0;
+           } else
+               r = CombineRect(&r, &(n->branch[i].rect));
+       }
+    return r;
+}
+
+/* Pick a branch.  Pick the one that will need the smallest increase
+** in area to accomodate the new rectangle.  This will result in the
+** least total area for the covering rectangles in the current node.
+** In case of a tie, pick the one which was smaller before, to get
+** the best resolution when searching.
+*/
+int PickBranch(Rect_t * r, Node_t * n)
+{
+    register Rect_t *rr;
+    register int i, flag, increase, bestIncr, area, bestArea;
+    int best;
+    Rect_t CombineRect();
+    assert(r && n);
+
+    flag = 1;
+    for (i = 0; i < NODECARD; i++) {
+       if (n->branch[i].child) {
+           Rect_t rect;
+           rr = &n->branch[i].rect;
+           area = RectArea(rr);
+           /* increase = RectArea(&CombineRect(r, rr)) - area; */
+           rect = CombineRect(r, rr);
+           increase = RectArea(&rect) - area;
+           if (increase < bestIncr || flag) {
+               best = i;
+               bestArea = area;
+               bestIncr = increase;
+               flag = 0;
+           } else if (increase == bestIncr && area < bestArea) {
+               best = i;
+               bestArea = area;
+               bestIncr = increase;
+           }
+#                      ifdef RTDEBUG
+           fprintf(stderr,
+                   "i=%d  area before=%d  area after=%d  increase=%d\n",
+                   i, area, area + increase, increase);
+#                      endif
+       }
+    }
+#      ifdef RTDEBUG
+    fprintf(stderr, "\tpicked %d\n", best);
+#      endif
+    return best;
+}
+
+/* Add a branch to a node.  Split the node if necessary.
+** Returns 0 if node not split.  Old node updated.
+** Returns 1 if node split, sets *new to address of new node.
+** Old node updated, becomes one of two.
+*/
+int AddBranch(RTree_t * rtp, Branch_t * b, Node_t * n, Node_t ** new)
+{
+    register int i;
+
+    assert(b);
+    assert(n);
+
+    if (n->count < NODECARD) { /* split won't be necessary */
+       for (i = 0; i < NODECARD; i++) {        /* find empty branch */
+           if (n->branch[i].child == NULL) {
+               n->branch[i] = *b;
+               n->count++;
+               break;
+           }
+       }
+       assert(i < NODECARD);
+       return 0;
+    } else {
+       if (rtp->StatFlag) {
+           if (rtp->Deleting)
+               rtp->DeTouchCount++;
+           else
+               rtp->InTouchCount++;
+       }
+       assert(new);
+       SplitNode(rtp, n, b, new);
+       if (n->level == 0)
+           rtp->LeafCount++;
+       else
+           rtp->NonLeafCount++;
+       return 1;
+    }
+}
+
+/* Disconnect a dependent node.
+*/
+void DisconBranch(Node_t * n, int i)
+{
+    assert(n && i >= 0 && i < NODECARD);
+    assert(n->branch[i].child);
+
+    InitBranch(&(n->branch[i]));
+    n->count--;
+}
diff --git a/lib/label/node.h b/lib/label/node.h
new file mode 100644 (file)
index 0000000..e6c76f4
--- /dev/null
@@ -0,0 +1,51 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifndef NODE_H
+#define NODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <index.h>
+
+typedef struct Branch {
+    Rect_t rect;
+    struct Node *child;
+} Branch_t;
+
+typedef struct Node {
+    int count;
+    int level;                 /* 0 is leaf, others positive */
+    struct Branch branch[NODECARD];
+} Node_t;
+
+void RTreeFreeNode(RTree_t *, Node_t *);
+void InitNode(Node_t *);
+void InitBranch(Branch_t *);
+Rect_t NodeCover(Node_t *);
+int PickBranch(Rect_t *, Node_t *);
+int AddBranch(RTree_t *, Branch_t *, Node_t *, Node_t **);
+void DisconBranch(Node_t *, int);
+void PrintBranch(int, Branch_t *);
+Node_t *RTreeNewNode(RTree_t *);
+#ifdef RTDEBUG
+void PrintNode(Node_t * n);
+void PrintBranch(int i, Branch_t * b);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif                         /*NODE_H */
diff --git a/lib/label/nrtmain.c b/lib/label/nrtmain.c
new file mode 100644 (file)
index 0000000..21c325e
--- /dev/null
@@ -0,0 +1,323 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory.h>
+#include <gvc.h>
+#include "xlabels.h"
+
+#if 0
+#define POINTS_PER_INCH 72
+#define N_NEW(n,t)       (t*)calloc((n),sizeof(t))
+#define MIN(a,b)        ((a)<(b)?(a):(b))
+#define MAX(a,b)        ((a)>(b)?(a):(b))
+#define INT_MAX         ((int)(~(unsigned)0 >> 1))
+#define INCH2PS(a_inches)       ((a_inches)*(double)POINTS_PER_INCH)
+#endif
+
+static char *progname;
+static int Verbose;
+extern pointf edgeMidpoint(graph_t * g, edge_t * e);
+
+static inline pointf pointfof(double x, double y)
+{
+    pointf r;
+
+    r.x = x;
+    r.y = y;
+    return r;
+}
+
+typedef struct {
+    GVC_t *gvc;
+    char *infname;
+    char *outfname;
+    FILE *inf;
+    FILE *outf;
+    char *lay;
+    char *fmt;
+    int force;
+} opts_t;
+
+static pointf centerPt(xlabel_t * xlp)
+{
+    pointf p;
+
+    p = xlp->pos;
+    p.x += (xlp->sz.x) / 2.0;
+    p.y += (xlp->sz.y) / 2.0;
+
+    return p;
+}
+
+static int
+printData(object_t * objs, int n_objs, xlabel_t * lbls, int n_lbls,
+         label_params_t * params)
+{
+    int i;
+    fprintf(stderr,
+           "%d objs %d xlabels force=%d bb=(%.02f,%.02f) (%.02f,%.02f)\n",
+           n_objs, n_lbls, params->force, params->bb.LL.x,
+           params->bb.LL.y, params->bb.UR.x, params->bb.UR.y);
+    if (Verbose < 2)
+       return 0;
+    for (i = 0; i < n_objs; i++) {
+       fprintf(stderr, " [%d] (%.02f,%.02f) (%.02f,%.02f) %p\n",
+               i, objs->pos.x, objs->pos.y, objs->sz.x, objs->sz.y,
+               objs->lbl);
+       objs++;
+    }
+    for (i = 0; i < n_lbls; i++) {
+       fprintf(stderr, " [%d] %p (%.02f,%.02f) %s\n",
+               i, lbls, lbls->sz.x, lbls->sz.y,
+               ((textlabel_t *) lbls->lbl)->text);
+       lbls++;
+    }
+    return 0;
+}
+
+int doxlabel(opts_t * opts)
+{
+    Agraph_t *gp;
+    object_t *objs;
+    xlabel_t *lbls;
+    int i, n_objs, n_lbls;
+    label_params_t params;
+    Agnode_t *np;
+    Agedge_t *ep;
+    int n_nlbls = 0, n_elbls = 0;
+    boxf bb;
+    textlabel_t *lp;
+    object_t *objp;
+    xlabel_t *xlp;
+    pointf ur;
+
+    fprintf(stderr, "reading %s\n", opts->infname);
+    if (!(gp = agread(opts->inf))) {
+       fprintf(stderr, "%s: %s not a dot file\n", progname,
+               opts->infname);
+       exit(1);
+    }
+    fclose(opts->inf);
+
+    fprintf(stderr, "laying out %s\n", opts->lay);
+    if (gvLayout(opts->gvc, gp, opts->lay)) {
+       fprintf(stderr, "%s: layout %s failed\n", progname, opts->lay);
+       exit(1);
+    }
+
+    fprintf(stderr, "attach labels\n");
+    /* In the real code, this should be optimized using GD_has_labels() */
+    /* We could probably provide the number of node and edge xlabels */
+    for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
+       if (ND_xlabel(np))
+           n_nlbls++;
+       for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
+           if (ED_xlabel(ep))
+               n_elbls++;
+       }
+    }
+    n_objs = agnnodes(gp) + n_elbls;
+    n_lbls = n_nlbls + n_elbls;
+    objp = objs = N_NEW(n_objs, object_t);
+    xlp = lbls = N_NEW(n_lbls, xlabel_t);
+    bb.LL = pointfof(INT_MAX, INT_MAX);
+    bb.UR = pointfof(-INT_MAX, -INT_MAX);
+
+    for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
+       /* Add an obstacle per node */
+       objp->sz.x = INCH2PS(ND_width(np));
+       objp->sz.y = INCH2PS(ND_height(np));
+       objp->pos = ND_coord(np);
+       objp->pos.x -= (objp->sz.x) / 2.0;
+       objp->pos.y -= (objp->sz.y) / 2.0;
+
+       /* Adjust bounding box */
+       bb.LL.x = MIN(bb.LL.x, objp->pos.x);
+       bb.LL.y = MIN(bb.LL.y, objp->pos.y);
+       ur.x = objp->pos.x + objp->sz.x;
+       ur.y = objp->pos.y + objp->sz.y;
+       bb.UR.x = MAX(bb.UR.x, ur.x);
+       bb.UR.y = MAX(bb.UR.y, ur.y);
+
+       if (ND_xlabel(np)) {
+           xlp->sz = ND_xlabel(np)->dimen;
+           xlp->lbl = ND_xlabel(np);
+           xlp->set = 0;
+           objp->lbl = xlp;
+           xlp++;
+       }
+       objp++;
+       for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
+           if (ED_label(ep)) {
+               textlabel_t *lp = ED_label(ep);
+               lp->pos.x = lp->pos.y = 0.0;
+           }
+           if (!ED_xlabel(ep))
+               continue;
+           objp->sz.x = 0;
+           objp->sz.y = 0;
+           objp->pos = edgeMidpoint(gp, ep);
+
+           xlp->sz = ED_xlabel(ep)->dimen;
+           xlp->lbl = ED_xlabel(ep);
+           xlp->set = 0;
+           objp->lbl = xlp;
+           xlp++;
+           objp++;
+       }
+    }
+
+    params.force = opts->force;
+    params.bb = bb;
+    if (Verbose)
+       printData(objs, n_objs, lbls, n_lbls, &params);
+    placeLabels(objs, n_objs, lbls, n_lbls, &params);
+
+    fprintf(stderr, "read label positions\n");
+    xlp = lbls;
+    for (i = 0; i < n_lbls; i++) {
+       if (xlp->set) {
+           lp = (textlabel_t *) (xlp->lbl);
+           lp->set = 1;
+           lp->pos = centerPt(xlp);
+       }
+       xlp++;
+    }
+    free(objs);
+    free(lbls);
+
+    fprintf(stderr, "writing %s\n", opts->outfname);
+    gvRender(opts->gvc, gp, opts->fmt, opts->outf);
+
+    /* clean up */
+    fprintf(stderr, "cleaning up\n");
+    gvFreeLayout(opts->gvc, gp);
+    agclose(gp);
+    return 0;
+}
+
+int checkOpt(char *l, char *legal[], int n)
+{
+    int i;
+    for (i = 0; i < n; i++) {
+       if (strcmp(l, legal[i]) == 0)
+           return 1;
+    }
+    return 0;
+}
+
+void usage(char *pp)
+{
+    fprintf(stderr,
+           "Usage: %s [-fv] [-V$level] [-T$fmt] [-l$layout] [-o$outfile] dotfile\n",
+           pp);
+    return;
+}
+
+static FILE *openFile(char *name, char *mode)
+{
+    FILE *fp;
+    char *modestr;
+
+    fp = fopen(name, mode);
+    if (!fp) {
+       if (*mode == 'r')
+           modestr = "reading";
+       else
+           modestr = "writing";
+       fprintf(stderr, "%s: could not open file %s for %s -- %s\n",
+               progname, name, modestr, strerror(errno));
+       exit(1);
+    }
+    return (fp);
+}
+
+static void init(int argc, char *argv[], opts_t * opts)
+{
+    int c, cnt;
+    char **optList;
+    opts->outf = stdout;
+    opts->outfname = "stdout";
+
+    progname = argv[0];
+    opts->gvc = gvContext();
+    opts->lay = "dot";
+    opts->fmt = "ps";
+    opts->force = 0;
+
+    while ((c = getopt(argc, argv, "o:vFl:T:V:")) != EOF) {
+       switch (c) {
+       case 'F':
+           opts->force = 1;
+           break;
+       case 'l':
+           optList = gvPluginList(opts->gvc, "layout", &cnt, NULL);
+           if (checkOpt(optarg, optList, cnt))
+               opts->lay = optarg;
+           else {
+               fprintf(stderr, "%s: unknown layout %s\n", progname,
+                       optarg);
+               exit(1);
+           }
+           break;
+       case 'T':
+           optList = gvPluginList(opts->gvc, "device", &cnt, NULL);
+           if (checkOpt(optarg, optList, cnt))
+               opts->fmt = optarg;
+           else {
+               fprintf(stderr, "%s: unknown format %s\n", progname,
+                       optarg);
+               exit(1);
+           }
+           break;
+       case 'v':
+           Verbose = 1;
+           break;
+       case 'V':
+           Verbose = atoi(optarg);
+           break;
+       case 'o':
+           opts->outf = openFile(optarg, "w");
+           opts->outfname = optarg;
+           break;
+       default:
+           usage(progname);
+           exit(1);
+       }
+    }
+
+    if (optind < argc) {
+       opts->inf = openFile(argv[optind], "r");
+       opts->infname = argv[optind];
+    } else {
+       opts->inf = stdin;
+       opts->infname = "<stdin>";
+    }
+
+    if (!opts->outf) {
+       opts->outf = stdout;
+       opts->outfname = "<stdout>";
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    opts_t opts;
+
+    init(argc, argv, &opts);
+    doxlabel(&opts);
+    return 0;
+}
diff --git a/lib/label/rectangle.c b/lib/label/rectangle.c
new file mode 100644 (file)
index 0000000..8413f04
--- /dev/null
@@ -0,0 +1,194 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#include "index.h"
+#include <stdio.h>
+#include <assert.h>
+#include "logic.h"
+#include "arith.h"
+#include "rectangle.h"
+
+#define Undefined(x) ((x)->boundary[0] > (x)->boundary[NUMDIMS])
+
+extern Rect_t CoverAll;
+
+/*-----------------------------------------------------------------------------
+| Initialize a rectangle to have all 0 coordinates.
+-----------------------------------------------------------------------------*/
+void InitRect(Rect_t * r)
+{
+    register int i;
+    for (i = 0; i < NUMSIDES; i++)
+       r->boundary[i] = 0;
+}
+
+/*-----------------------------------------------------------------------------
+| Return a rect whose first low side is higher than its opposite side -
+| interpreted as an undefined rect.
+-----------------------------------------------------------------------------*/
+Rect_t NullRect()
+{
+    Rect_t r;
+    register int i;
+
+    r.boundary[0] = 1;
+    r.boundary[NUMDIMS] = -1;
+    for (i = 1; i < NUMDIMS; i++)
+       r.boundary[i] = r.boundary[i + NUMDIMS] = 0;
+    return r;
+}
+
+#ifdef UNUSED
+/*-----------------------------------------------------------------------------
+| Fills in random coordinates in a rectangle.
+| The low side is guaranteed to be less than the high side.
+-----------------------------------------------------------------------------*/
+RandomRect(Rect_t * r)
+{
+    register int i, width;
+    for (i = 0; i < NUMDIMS; i++) {
+       /* width from 1 to 1000 / 4, more small ones */
+       width = rand() % (1000 / 4) + 1;
+
+       /* sprinkle a given size evenly but so they stay in [0,100] */
+       r->boundary[i] = rand() % (1000 - width);       /* low side */
+       r->boundary[i + NUMDIMS] = r->boundary[i] + width;      /* high side */
+    }
+}
+
+/*-----------------------------------------------------------------------------
+| Fill in the boundaries for a random search rectangle.
+| Pass in a pointer to a rect that contains all the data,
+| and a pointer to the rect to be filled in.
+| Generated rect is centered randomly anywhere in the data area,
+| and has size from 0 to the size of the data area in each dimension,
+| i.e. search rect can stick out beyond data area.
+-----------------------------------------------------------------------------*/
+SearchRect(Rect_t * search, Rect_t * data)
+{
+    register int i, j, size, center;
+
+    assert(search);
+    assert(data);
+
+    for (i = 0; i < NUMDIMS; i++) {
+       j = i + NUMDIMS;        /* index for high side boundary */
+       if (data->boundary[i] > INT_MIN && data->boundary[j] < INT_MAX) {
+           size =
+               (rand() % (data->boundary[j] - data->boundary[i] + 1)) / 2;
+           center = data->boundary[i]
+               + rand() % (data->boundary[j] - data->boundary[i] + 1);
+           search->boundary[i] = center - size / 2;
+           search->boundary[j] = center + size / 2;
+       } else {                /* some open boundary, search entire dimension */
+           search->boundary[i] = INT_MIN;
+           search->boundary[j] = INT_MAX;
+       }
+    }
+}
+#endif
+
+#ifdef RTDEBUG
+/*-----------------------------------------------------------------------------
+| Print rectangle lower upper bounds by dimension
+-----------------------------------------------------------------------------*/
+void PrintRect(Rect_t * r)
+{
+    register int i;
+    assert(r);
+    fprintf(stderr, "rect:");
+    for (i = 0; i < NUMDIMS; i++)
+       fprintf(stderr, "\t%d\t%d\n", r->boundary[i],
+               r->boundary[i + NUMDIMS]);
+}
+#endif
+
+/*-----------------------------------------------------------------------------
+| Calculate the n-dimensional area of a rectangle
+-----------------------------------------------------------------------------*/
+int RectArea(Rect_t * r)
+{
+    register int i, area;
+    assert(r);
+
+    if (Undefined(r))
+       return 0;
+
+    area = 1;
+    for (i = 0; i < NUMDIMS; i++) {
+       area *= r->boundary[i + NUMDIMS] - r->boundary[i];
+    }
+    return area;
+}
+
+/*-----------------------------------------------------------------------------
+| Combine two rectangles, make one that includes both.
+-----------------------------------------------------------------------------*/
+Rect_t CombineRect(Rect_t * r, Rect_t * rr)
+{
+    register int i, j;
+    Rect_t new;
+    assert(r && rr);
+
+    if (Undefined(r))
+       return *rr;
+    if (Undefined(rr))
+       return *r;
+
+    for (i = 0; i < NUMDIMS; i++) {
+       new.boundary[i] = MIN(r->boundary[i], rr->boundary[i]);
+       j = i + NUMDIMS;
+       new.boundary[j] = MAX(r->boundary[j], rr->boundary[j]);
+    }
+    return new;
+}
+
+/*-----------------------------------------------------------------------------
+| Decide whether two rectangles overlap.
+-----------------------------------------------------------------------------*/
+int Overlap(Rect_t * r, Rect_t * s)
+{
+    register int i, j;
+    assert(r && s);
+
+    for (i = 0; i < NUMDIMS; i++) {
+       j = i + NUMDIMS;        /* index for high sides */
+       if (r->boundary[i] > s->boundary[j]
+           || s->boundary[i] > r->boundary[j])
+           return FALSE;
+    }
+    return TRUE;
+}
+
+/*-----------------------------------------------------------------------------
+| Decide whether rectangle r is contained in rectangle s.
+-----------------------------------------------------------------------------*/
+int Contained(Rect_t * r, Rect_t * s)
+{
+    register int i, j, result;
+    assert((int) r && (int) s);
+
+    /* undefined rect is contained in any other */
+    if (Undefined(r))
+       return TRUE;
+    /* no rect (except an undefined one) is contained in an undef rect */
+    if (Undefined(s))
+       return FALSE;
+
+    result = TRUE;
+    for (i = 0; i < NUMDIMS; i++) {
+       j = i + NUMDIMS;        /* index for high sides */
+       result = result && r->boundary[i] >= s->boundary[i]
+           && r->boundary[j] <= s->boundary[j];
+    }
+    return result;
+}
diff --git a/lib/label/rectangle.h b/lib/label/rectangle.h
new file mode 100644 (file)
index 0000000..75f7529
--- /dev/null
@@ -0,0 +1,38 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifndef RECTANGLE_H
+#define RECTANGLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Rect {
+    int boundary[NUMSIDES];
+} Rect_t;
+
+void InitRect(Rect_t * r);
+#ifdef RTDEBUG
+void PrintRect(Rect_t *);
+#endif
+int RectArea(Rect_t *);
+int Overlap(Rect_t *, Rect_t *);
+int Contained(Rect_t *, Rect_t *);
+Rect_t CombineRect(Rect_t *, Rect_t *);
+Rect_t NullRect();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif                         /*RECTANGLE_H */
diff --git a/lib/label/split.q.c b/lib/label/split.q.c
new file mode 100644 (file)
index 0000000..c382f88
--- /dev/null
@@ -0,0 +1,374 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#include "index.h"
+#include <stdio.h>
+#include <assert.h>
+#include "split.q.h"
+#include "logic.h"
+
+/* Forward declarations */
+static void MethodZero(RTree_t * rtp);
+static void InitPVars(RTree_t * rtp);
+static void LoadNodes(RTree_t * rtp, Node_t * n, Node_t * q,
+                     struct PartitionVars *p);
+static void Classify(RTree_t * rtp, int i, int group);
+static void PickSeeds(RTree_t * rtp);
+static void GetBranches(RTree_t * rtp, Node_t * n, Branch_t * b);
+
+/*-----------------------------------------------------------------------------
+| Split a node.
+| Divides the nodes branches and the extra one between two nodes.
+| Old node is one of the new ones, and one really new one is created.
+| Tries more than one method for choosing a partition, uses best result.
+-----------------------------------------------------------------------------*/
+void SplitNode(RTree_t * rtp, Node_t * n, Branch_t * b, Node_t ** nn)
+{
+    register struct PartitionVars *p;
+    register int level;
+    int area;
+
+    assert(n);
+    assert(b);
+
+#ifdef RTDEBUG
+    fprintf(stderr, "Splitting:\n");
+    PrintNode(n);
+    fprintf(stderr, "new branch:\n");
+    PrintBranch(-1, b);
+#endif
+
+    if (rtp->StatFlag) {
+       if (rtp->Deleting)
+           rtp->DeSplitCount++;
+       else
+           rtp->InSplitCount++;
+    }
+
+    /* load all the branches into a buffer, initialize old node */
+    level = n->level;
+    GetBranches(rtp, n, b);
+
+#ifdef RTDEBUG
+    {
+       int i;
+       /* Indicate that a split is about to take place */
+       for (i = 0; i < NODECARD + 1; i++) {
+           PrintRect(&rtp->split.BranchBuf[i].rect);
+       }
+       PrintRect(&rtp->split.CoverSplit);
+    }
+#endif
+
+    /* find partition */
+    p = &rtp->split.Partitions[0];
+    MethodZero(rtp);
+
+    area = RectArea(&p->cover[0]) + RectArea(&p->cover[1]);
+
+    /* record how good the split was for statistics */
+    if (rtp->StatFlag && !rtp->Deleting && area)
+       rtp->SplitMeritSum += (float) rtp->split.CoverSplitArea / area;
+
+    /* put branches from buffer into 2 nodes according to chosen partition */
+    *nn = RTreeNewNode(rtp);
+    (*nn)->level = n->level = level;
+    LoadNodes(rtp, n, *nn, p);
+    assert(n->count + (*nn)->count == NODECARD + 1);
+
+#ifdef RTDEBUG
+    PrintPVars(p);
+    fprintf(stderr, "group 0:\n");
+    PrintNode(n);
+    fprintf(stderr, "group 1:\n");
+    PrintNode(*nn);
+    fprintf(stderr, "\n");
+#endif
+
+}
+
+/*-----------------------------------------------------------------------------
+| Load branch buffer with branches from full node plus the extra branch.
+-----------------------------------------------------------------------------*/
+static void GetBranches(RTree_t * rtp, Node_t * n, Branch_t * b)
+{
+    register int i;
+
+    assert(n);
+    assert(b);
+
+    /* load the branch buffer */
+    for (i = 0; i < NODECARD; i++) {
+       assert(n->branch[i].child);     /* node should have every entry full */
+       rtp->split.BranchBuf[i] = n->branch[i];
+    }
+    rtp->split.BranchBuf[NODECARD] = *b;
+
+    /* calculate rect containing all in the set */
+    rtp->split.CoverSplit = rtp->split.BranchBuf[0].rect;
+    for (i = 1; i < NODECARD + 1; i++) {
+       rtp->split.CoverSplit = CombineRect(&rtp->split.CoverSplit,
+                                           &rtp->split.BranchBuf[i].rect);
+    }
+    rtp->split.CoverSplitArea = RectArea(&rtp->split.CoverSplit);
+
+    InitNode(n);
+}
+
+/*-----------------------------------------------------------------------------
+| Method #0 for choosing a partition:
+| As the seeds for the two groups, pick the two rects that would waste the
+| most area if covered by a single rectangle, i.e. evidently the worst pair
+| to have in the same group.
+| Of the remaining, one at a time is chosen to be put in one of the two groups.
+| The one chosen is the one with the greatest difference in area expansion
+| depending on which group - the rect most strongly attracted to one group
+| and repelled from the other.
+| If one group gets too full (more would force other group to violate min
+| fill requirement) then other group gets the rest.
+| These last are the ones that can go in either group most easily.
+-----------------------------------------------------------------------------*/
+static void MethodZero(RTree_t * rtp)
+{
+    register Rect_t *r;
+    register int i, growth0, growth1, diff, biggestDiff;
+    register int group, chosen, betterGroup;
+
+    InitPVars(rtp);
+    PickSeeds(rtp);
+
+    while (rtp->split.Partitions[0].count[0] +
+          rtp->split.Partitions[0].count[1] < NODECARD + 1 &&
+          rtp->split.Partitions[0].count[0] < NODECARD + 1 - rtp->MinFill
+          && rtp->split.Partitions[0].count[1] <
+          NODECARD + 1 - rtp->MinFill) {
+       biggestDiff = -1;
+       for (i = 0; i < NODECARD + 1; i++) {
+           if (!rtp->split.Partitions[0].taken[i]) {
+               Rect_t rect;
+               r = &rtp->split.BranchBuf[i].rect;
+               /* growth0 = RectArea(&CombineRect(r,
+                  &rtp->split.Partitions[0].cover[0])) -
+                  rtp->split.Partitions[0].area[0];
+                */
+               /* growth1 = RectArea(&CombineRect(r,
+                  &rtp->split.Partitions[0].cover[1])) -
+                  rtp->split.Partitions[0].area[1];
+                */
+               rect = CombineRect(r, &rtp->split.Partitions[0].cover[0]);
+               growth0 =
+                   RectArea(&rect) - rtp->split.Partitions[0].area[0];
+               rect = CombineRect(r, &rtp->split.Partitions[0].cover[1]);
+               growth1 =
+                   RectArea(&rect) - rtp->split.Partitions[0].area[1];
+               diff = growth1 - growth0;
+               if (diff >= 0)
+                   group = 0;
+               else {
+                   group = 1;
+                   diff = -diff;
+               }
+
+               if (diff > biggestDiff) {
+                   biggestDiff = diff;
+                   chosen = i;
+                   betterGroup = group;
+               } else if (diff == biggestDiff &&
+                          rtp->split.Partitions[0].count[group] <
+                          rtp->split.Partitions[0].count[betterGroup]) {
+                   chosen = i;
+                   betterGroup = group;
+               }
+           }
+       }
+       Classify(rtp, chosen, betterGroup);
+    }
+
+    /* if one group too full, put remaining rects in the other */
+    if (rtp->split.Partitions[0].count[0] +
+       rtp->split.Partitions[0].count[1] < NODECARD + 1) {
+       group = 0;
+       if (rtp->split.Partitions[0].count[0] >=
+           NODECARD + 1 - rtp->MinFill)
+           group = 1;
+       for (i = 0; i < NODECARD + 1; i++) {
+           if (!rtp->split.Partitions[0].taken[i])
+               Classify(rtp, i, group);
+       }
+    }
+
+    assert(rtp->split.Partitions[0].count[0] +
+          rtp->split.Partitions[0].count[1] == NODECARD + 1);
+    assert(rtp->split.Partitions[0].count[0] >= rtp->MinFill
+          && rtp->split.Partitions[0].count[1] >= rtp->MinFill);
+}
+
+/*-----------------------------------------------------------------------------
+| Pick two rects from set to be the first elements of the two groups.
+| Pick the two that waste the most area if covered by a single rectangle.
+-----------------------------------------------------------------------------*/
+static void PickSeeds(RTree_t * rtp)
+{
+    register int i, j, waste, worst, seed0, seed1;
+    int area[NODECARD + 1];
+
+    for (i = 0; i < NODECARD + 1; i++)
+       area[i] = RectArea(&rtp->split.BranchBuf[i].rect);
+
+    worst = -rtp->split.CoverSplitArea - 1;
+    for (i = 0; i < NODECARD; i++) {
+       for (j = i + 1; j < NODECARD + 1; j++) {
+           Rect_t rect;
+           /* waste = RectArea(&CombineRect(&rtp->split.BranchBuf[i].rect,
+              //                  &rtp->split.BranchBuf[j].rect)) - area[i] - area[j];
+            */
+           rect = CombineRect(&rtp->split.BranchBuf[i].rect,
+                              &rtp->split.BranchBuf[j].rect);
+           waste = RectArea(&rect) - area[i] - area[j];
+           if (waste > worst) {
+               worst = waste;
+               seed0 = i;
+               seed1 = j;
+           }
+       }
+    }
+    Classify(rtp, seed0, 0);
+    Classify(rtp, seed1, 1);
+}
+
+/*-----------------------------------------------------------------------------
+| Put a branch in one of the groups.
+-----------------------------------------------------------------------------*/
+static void Classify(RTree_t * rtp, int i, int group)
+{
+
+    assert(!rtp->split.Partitions[0].taken[i]);
+
+    rtp->split.Partitions[0].partition[i] = group;
+    rtp->split.Partitions[0].taken[i] = TRUE;
+
+    if (rtp->split.Partitions[0].count[group] == 0)
+       rtp->split.Partitions[0].cover[group] =
+           rtp->split.BranchBuf[i].rect;
+    else
+       rtp->split.Partitions[0].cover[group] =
+           CombineRect(&rtp->split.BranchBuf[i].rect,
+                       &rtp->split.Partitions[0].cover[group]);
+    rtp->split.Partitions[0].area[group] =
+       RectArea(&rtp->split.Partitions[0].cover[group]);
+    rtp->split.Partitions[0].count[group]++;
+
+#      ifdef RTDEBUG
+    {
+       /* redraw entire group and its cover */
+       int j;
+       MFBSetColor(WHITE);     /* cover is white */
+       PrintRect(&rtp->split.Partitions[0].cover[group]);
+       MFBSetColor(group + 3); /* group 0 green, group 1 blue */
+       for (j = 0; j < NODECARD + 1; j++) {
+           if (rtp->split.Partitions[0].taken[j] &&
+               rtp->split.Partitions[0].partition[j] == group)
+               PrintRect(&rtrtp->split.Partitions[0].BranchBuf[j].rect);
+       }
+       GraphChar();
+    }
+#      endif
+}
+
+/*-----------------------------------------------------------------------------
+| Copy branches from the buffer into two nodes according to the partition.
+-----------------------------------------------------------------------------*/
+static void LoadNodes(RTree_t * rtp, Node_t * n, Node_t * q,
+                     struct PartitionVars *p)
+{
+    register int i;
+    assert(n);
+    assert(q);
+    assert(p);
+
+    for (i = 0; i < NODECARD + 1; i++) {
+       assert(rtp->split.Partitions[0].partition[i] == 0 ||
+              rtp->split.Partitions[0].partition[i] == 1);
+       if (rtp->split.Partitions[0].partition[i] == 0)
+           AddBranch(rtp, &rtp->split.BranchBuf[i], n, NULL);
+       else if (rtp->split.Partitions[0].partition[i] == 1)
+           AddBranch(rtp, &rtp->split.BranchBuf[i], q, NULL);
+    }
+}
+
+/*-----------------------------------------------------------------------------
+| Initialize a PartitionVars structure.
+-----------------------------------------------------------------------------*/
+static void InitPVars(RTree_t * rtp)
+{
+    register int i;
+
+    rtp->split.Partitions[0].count[0] = rtp->split.Partitions[0].count[1] =
+       0;
+    rtp->split.Partitions[0].cover[0] = rtp->split.Partitions[0].cover[1] =
+       NullRect();
+    rtp->split.Partitions[0].area[0] = rtp->split.Partitions[0].area[1] =
+       0;
+    for (i = 0; i < NODECARD + 1; i++) {
+       rtp->split.Partitions[0].taken[i] = FALSE;
+       rtp->split.Partitions[0].partition[i] = -1;
+    }
+}
+
+#ifdef RTDEBUG
+
+/*-----------------------------------------------------------------------------
+| Print out data for a partition from PartitionVars struct.
+-----------------------------------------------------------------------------*/
+PrintPVars(RTree_t * rtp)
+{
+    register int i;
+
+    fprintf(stderr, "\npartition:\n");
+    for (i = 0; i < NODECARD + 1; i++) {
+       fprintf(stderr, "%3d\t", i);
+    }
+    fprintf(stderr, "\n");
+    for (i = 0; i < NODECARD + 1; i++) {
+       if (rtp->split.Partitions[0].taken[i])
+           fprintf(stderr, "  t\t");
+       else
+           fprintf(stderr, "\t");
+    }
+    fprintf(stderr, "\n");
+    for (i = 0; i < NODECARD + 1; i++) {
+       fprintf(stderr, "%3d\t", rtp->split.Partitions[0].partition[i]);
+    }
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "count[0] = %d  area = %d\n",
+           rtp->split.Partitions[0].count[0],
+           rtp->split.Partitions[0].area[0]);
+    fprintf(stderr, "count[1] = %d  area = %d\n",
+           rtp->split.Partitions[0].count[1],
+           rtp->split.Partitions[0].area[1]);
+    if (rtp->split.Partitions[0].area[0] +
+       rtp->split.Partitions[0].area[1] > 0) {
+       fprintf(stderr, "total area = %d  effectiveness = %3.2f\n",
+               rtp->split.Partitions[0].area[0] +
+               rtp->split.Partitions[0].area[1],
+               (float) rtp->split.CoverSplitArea /
+               (rtp->split.Partitions[0].area[0] +
+                rtp->split.Partitions[0].area[1]));
+    }
+    fprintf(stderr, "cover[0]:\n");
+    PrintRect(&rtp->split.Partitions[0].cover[0]);
+
+    fprintf(stderr, "cover[1]:\n");
+    PrintRect(&rtp->split.Partitions[0].cover[1]);
+}
+#endif
diff --git a/lib/label/split.q.h b/lib/label/split.q.h
new file mode 100644 (file)
index 0000000..ee341f3
--- /dev/null
@@ -0,0 +1,51 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifndef SPLIT_Q_H
+#define SPLIT_Q_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-----------------------------------------------------------------------------
+| Definitions and global variables.
+-----------------------------------------------------------------------------*/
+#include <rectangle.h>
+#include <index.h>
+
+#ifndef METHODS
+#define METHODS 1
+#endif /*METHODS*/
+/* variables for finding a partition */
+    struct PartitionVars {
+    int partition[NODECARD + 1];
+    int taken[NODECARD + 1];
+    int count[2];
+    struct Rect cover[2];
+    int area[2];
+};
+
+typedef struct split_q_s {
+    struct Branch BranchBuf[NODECARD + 1];
+    struct Rect CoverSplit;
+    int CoverSplitArea;
+    struct PartitionVars Partitions[METHODS];
+} SplitQ_t;
+
+void SplitNode(RTree_t *, Node_t *, Branch_t *, Node_t **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif                         /*SPLIT_Q_H */
diff --git a/lib/label/xlabels.c b/lib/label/xlabels.c
new file mode 100644 (file)
index 0000000..8212965
--- /dev/null
@@ -0,0 +1,643 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#define XLABEL_INT
+#include <xlabels.h>
+#include <memory.h>
+
+extern int Verbose;
+
+static int icompare(Dt_t *, Void_t *, Void_t *, Dtdisc_t *);
+
+Dtdisc_t Hdisc = { offsetof(HDict_t, key), sizeof(int), -1, 0, 0,
+    icompare, 0, 0, 0
+};
+
+static int icompare(Dt_t * dt, Void_t * v1, Void_t * v2, Dtdisc_t * disc)
+{
+    int k1 = *((int *) v1), k2 = *((int *) v2);
+    return k1 - k2;
+}
+
+static XLabels_t *xlnew(object_t * objs, int n_objs,
+                       xlabel_t * lbls, int n_lbls,
+                       label_params_t * params)
+{
+    XLabels_t *xlp;
+
+    xlp = NEW(XLabels_t);
+
+    /* used to load the rtree in hilbert space filling curve order */
+    if (!(xlp->hdx = dtopen(&Hdisc, Dtobag))) {
+       fprintf(stderr, "out of memory\n");
+       goto bad;
+    }
+
+    /* for querying intersection candidates */
+    if (!(xlp->spdx = RTreeOpen())) {
+       fprintf(stderr, "out of memory\n");
+       goto bad;
+    }
+    /* save arg pointers in the handle */
+    xlp->objs = objs;
+    xlp->n_objs = n_objs;
+    xlp->lbls = lbls;
+    xlp->n_lbls = n_lbls;
+    xlp->params = params;
+
+    return xlp;
+
+  bad:
+    if (xlp->hdx)
+       dtclose(xlp->hdx);
+    if (xlp->spdx)
+       RTreeClose(xlp->spdx);
+    free(xlp);
+    return 0;
+}
+
+static void xlfree(XLabels_t * xlp)
+{
+    RTreeClose(xlp->spdx);
+    free(xlp);
+    return;
+}
+
+/***************************************************************************/
+
+/*
+ * floorlog2 - largest base 2 integer logarithm less than n
+ * http://en.wikipedia.org/wiki/Binary_logarithm
+ * ultimately from http://www.hackersdelight.org/
+ */
+static int floorLog2(unsigned int n)
+{
+    int pos = 0;
+
+    if (n == 0)
+       return -1;
+
+    if (n >= 1 << 16) {
+       n >>= 16;
+       pos += 16;
+    }
+    if (n >= 1 << 8) {
+       n >>= 8;
+       pos += 8;
+    }
+    if (n >= 1 << 4) {
+       n >>= 4;
+       pos += 4;
+    }
+    if (n >= 1 << 2) {
+       n >>= 2;
+       pos += 2;
+    }
+    if (n >= 1 << 1) {
+       pos += 1;
+    }
+    return pos;
+}
+
+/*
+ * determine the order(depth) of the hilbert sfc so that we satisfy the
+ * precondition of hd_hil_s_from_xy()
+ */
+unsigned int xlhorder(XLabels_t * xlp)
+{
+    double maxx = xlp->params->bb.UR.x, maxy = xlp->params->bb.UR.y;
+    return floorLog2(maxx > maxy ? maxx : maxy) + 1;
+}
+
+/* from http://www.hackersdelight.org/ site for the book by Henry S Warren */
+/*
+ * precondition
+ * pow(2, n) >= max(p.x, p.y)
+ */
+/* adapted from lams1.c
+Given the "order" n of a Hilbert curve and coordinates x and y, this
+program computes the length s of the curve from the origin to (x, y).
+The square that the Hilbert curve traverses is of size 2**n by 2**n.
+   The method is that given in [Lam&Shap], described by the following
+table.  Here i = n-1 for the most significant bit of x and y, and i = 0
+for the least significant bits.
+
+                    x[i]  y[i] | s[2i+1:2i]   x   y
+                    -----------|-------------------
+                     0     0   |     00       y   x
+                     0     1   |     01       x   y
+                     1     0   |     11      ~y  ~x
+                     1     1   |     10       x   y
+
+To use this table, start at the most significant bits of x and y
+(i = n - 1).  If they are both 0 (first row), set the most significant
+two bits of s to 00 and interchange x and y.  (Actually, it is only
+necessary to interchange the remaining bits of x and y.)  If the most
+significant bits of x and y are 10 (third row), output 11, interchange x
+and y, and complement x and y.
+   Then, consider the next most significant bits of x and y (which may
+have been changed by this process), and select the appropriate row of
+the table to determine the next two bits of s, and how to change x and
+y.  Continue until the least significant bits of x and y have been
+processed. */
+
+static unsigned int hd_hil_s_from_xy(point p, int n)
+{
+    int i, x = p.x, y = p.y, xi, yi;
+    unsigned s;
+
+    s = 0;                     /* Initialize. */
+    for (i = n - 1; i >= 0; i--) {
+       xi = (x >> i) & 1;      /* Get bit i of x. */
+       yi = (y >> i) & 1;      /* Get bit i of y. */
+       s = 4 * s + 2 * xi + (xi ^ yi); /* Append two bits to s. */
+
+       x = x ^ y;              /* These 3 lines swap */
+       y = y ^ (x & (yi - 1)); /* x and y if yi = 0. */
+       x = x ^ y;
+       x = x ^ (-xi & (yi - 1));       /* Complement x and y if */
+       y = y ^ (-xi & (yi - 1));       /* xi = 1 and yi = 0. */
+    }
+    return s;
+}
+
+/* intersection test from
+ * from Real-Time Collision Detection 4.2.1 by Christer Ericson
+ * intersection area from
+ * http://stackoverflow.com/questions/4549544/total-area-of-intersecting-rectangles
+*/
+static double aabbaabb(Rect_t * r, Rect_t * s)
+{
+    /* per dimension if( max < omin || min > omax) */
+    double iminx, iminy, imaxx, imaxy;
+    if (r->boundary[2] < s->boundary[0] || r->boundary[0] > s->boundary[2])
+       return 0;
+    if (r->boundary[3] < s->boundary[1] || r->boundary[1] > s->boundary[3])
+       return 0;
+
+    /* if we get here we have an intersection */
+
+    /* rightmost left edge of the 2 rectangles */
+    iminx =
+       r->boundary[0] > s->boundary[0] ? r->boundary[0] : s->boundary[0];
+    /* upmost bottom edge */
+    iminy =
+       r->boundary[1] > s->boundary[1] ? r->boundary[1] : s->boundary[1];
+    /* leftmost right edge */
+    imaxx =
+       r->boundary[2] < s->boundary[2] ? r->boundary[2] : s->boundary[2];
+    /* downmost top edge */
+    imaxy =
+       r->boundary[3] < s->boundary[3] ? r->boundary[3] : s->boundary[3];
+    return (imaxx - iminx) * (imaxy - iminy);
+}
+
+/*fill in rectangle from the object */
+static void objp2rect(object_t * op, Rect_t * r)
+{
+    r->boundary[0] = op->pos.x;
+    r->boundary[1] = op->pos.y;
+    r->boundary[2] = op->pos.x + op->sz.x;
+    r->boundary[3] = op->pos.y + op->sz.y;
+    return;
+}
+
+static void objplp2rect(object_t * objp, Rect_t * r)
+{
+    xlabel_t *lp = objp->lbl;
+    r->boundary[0] = lp->pos.x;
+    r->boundary[1] = lp->pos.y;
+    r->boundary[2] = lp->pos.x + lp->sz.x;
+    r->boundary[3] = lp->pos.y + lp->sz.y;
+    return;
+}
+
+/* compute boundary that encloses all possible label boundaries */
+static Rect_t objplpmks(XLabels_t * xlp, object_t * objp)
+{
+    Rect_t rect;
+    pointf p;
+
+    p.x = p.y = 0.0;
+    if (objp->lbl)
+       p = objp->lbl->sz;
+
+    rect.boundary[0] = (int) floor(objp->pos.x - p.x);
+    rect.boundary[1] = (int) floor(objp->pos.y - p.y);
+
+    rect.boundary[2] = (int) ceil(objp->pos.x + objp->sz.x + p.x);
+    assert(rect.boundary[2] < INT_MAX);
+    rect.boundary[3] = (int) ceil(objp->pos.y + objp->sz.y + p.y);
+    assert(rect.boundary[3] < INT_MAX);
+
+    return rect;
+}
+
+/* determine the position clp will occupy in intrsx[] */
+static int getintrsxi(XLabels_t * xlp, object_t * op, object_t * cp)
+{
+    int i = -1;
+    xlabel_t *lp = op->lbl, *clp = cp->lbl;
+    assert(lp != clp);
+
+    if (lp->set == 0 || clp->set == 0)
+       return i;
+    if ((op->pos.x == 0.0 && op->pos.y == 0.0) ||
+       (cp->pos.x == 0.0 && cp->pos.y == 0.0))
+       return i;
+
+    if (cp->pos.y < op->pos.y)
+       if (cp->pos.x < op->pos.x)
+           i = XLPXPY;
+       else if (cp->pos.x > op->pos.x)
+           i = XLNXPY;
+       else
+           i = XLCXPY;
+    else if (cp->pos.y > op->pos.y)
+       if (cp->pos.x < op->pos.x)
+           i = XLPXNY;
+       else if (cp->pos.x > op->pos.x)
+           i = XLNXNY;
+       else
+           i = XLCXNY;
+    else if (cp->pos.x < op->pos.x)
+       i = XLPXCY;
+    else if (cp->pos.x > op->pos.x)
+       i = XLNXCY;
+
+    return i;
+
+}
+
+/* record the intersecting objects label */
+static double
+recordointrsx(XLabels_t * xlp, object_t * op, object_t * cp, Rect_t * rp,
+             double a, object_t * intrsx[XLNBR])
+{
+    int i = getintrsxi(xlp, op, cp);
+    if (i < 0)
+       i = 5;
+    if (intrsx[i] != NULL) {
+       double sa, maxa = 0.0;
+       Rect_t srect;
+       /* keep maximally overlapping object */
+       objp2rect(intrsx[i], &srect);
+       sa = aabbaabb(rp, &srect);
+       if (sa > a)
+           maxa = sa;
+       /*keep maximally overlapping label */
+       if (intrsx[i]->lbl) {
+           objplp2rect(intrsx[i], &srect);
+           sa = aabbaabb(rp, &srect);
+           if (sa > a)
+               maxa = sa > maxa ? sa : maxa;
+       }
+       if (maxa > 0.0)
+           return maxa;
+       /*replace overlapping label/object pair */
+       intrsx[i] = cp;
+       return a;
+    }
+    intrsx[i] = cp;
+    return a;
+}
+
+/* record the intersecting label */
+static double
+recordlintrsx(XLabels_t * xlp, object_t * op, object_t * cp, Rect_t * rp,
+             double a, object_t * intrsx[XLNBR])
+{
+    int i = getintrsxi(xlp, op, cp);
+    if (i < 0)
+       i = 5;
+    if (intrsx[i] != NULL) {
+       double sa, maxa = 0.0;
+       Rect_t srect;
+       /* keep maximally overlapping object */
+       objp2rect(intrsx[i], &srect);
+       sa = aabbaabb(rp, &srect);
+       if (sa > a)
+           maxa = sa;
+       /*keep maximally overlapping label */
+       if (intrsx[i]->lbl) {
+           objplp2rect(intrsx[i], &srect);
+           sa = aabbaabb(rp, &srect);
+           if (sa > a)
+               maxa = sa > maxa ? sa : maxa;
+       }
+       if (maxa > 0.0)
+           return maxa;
+       /*replace overlapping label/object pair */
+       intrsx[i] = cp;
+       return a;
+    }
+    intrsx[i] = cp;
+    return a;
+}
+
+/* find the objects and labels intersecting lp */
+static BestPos_t
+xlintersections(XLabels_t * xlp, object_t * objp, object_t * intrsx[XLNBR])
+{
+    LeafList_t *ilp, *llp;
+    Rect_t rect, srect;
+    BestPos_t bp;
+
+    assert(objp->lbl);
+
+    bp.n = 0;
+    bp.area = 0.0;
+    bp.pos = objp->lbl->pos;
+
+    objplp2rect(objp, &rect);
+
+    llp = RTreeSearch(xlp->spdx, xlp->spdx->root, &rect);
+    if (!llp)
+       return bp;
+
+    for (ilp = llp; ilp; ilp = ilp->next) {
+       double a, ra;
+       object_t *cp = ilp->leaf->data;
+
+       if (cp == objp)
+           continue;
+
+       /*label-object intersect */
+       objp2rect(cp, &srect);
+       a = aabbaabb(&rect, &srect);
+       if (a > 0.0) {
+           ra = recordointrsx(xlp, objp, cp, &rect, a, intrsx);
+           bp.n++;
+           bp.area += ra;
+       }
+       /*label-label intersect */
+       if (!cp->lbl || !cp->lbl->set)
+           continue;
+       objplp2rect(cp, &srect);
+       a = aabbaabb(&rect, &srect);
+       if (a > 0.0) {
+           recordlintrsx(xlp, objp, cp, &rect, a, intrsx);
+           bp.n++;
+           bp.area += a;
+       }
+    }
+    RTreeLeafListFree(llp);
+    return bp;
+}
+
+/*
+ * xladjust - find a label position
+ * the individual tests at the top are intended to place a preference order
+ * on the position
+ */
+static BestPos_t xladjust(XLabels_t * xlp, object_t * objp)
+{
+    xlabel_t *lp = objp->lbl;
+    double xincr = ((2 * lp->sz.x) + objp->sz.x) / XLXDENOM;
+    double yincr = ((2 * lp->sz.y) + objp->sz.y) / XLYDENOM;
+    object_t *intrsx[XLNBR];
+    BestPos_t bp, nbp;
+
+    assert(objp->lbl);
+
+    memset(intrsx, 0, sizeof(intrsx));
+
+    /*x left */
+    lp->pos.x = objp->pos.x - lp->sz.x;
+    /*top */
+    lp->pos.y = objp->pos.y + objp->sz.y;
+    bp = xlintersections(xlp, objp, intrsx);
+    if (bp.n == 0)
+       return bp;
+    /*mid */
+    lp->pos.y = objp->pos.y;
+    nbp = xlintersections(xlp, objp, intrsx);
+    if (nbp.n == 0)
+       return nbp;
+    if (nbp.area < bp.area)
+       bp = nbp;
+    /*bottom */
+    lp->pos.y = objp->pos.y - lp->sz.y;
+    nbp = xlintersections(xlp, objp, intrsx);
+    if (nbp.n == 0)
+       return nbp;
+    if (nbp.area < bp.area)
+       bp = nbp;
+
+    /*x mid */
+    lp->pos.x = objp->pos.x;
+    /*tobjp */
+    lp->pos.y = objp->pos.y + objp->sz.y;
+    nbp = xlintersections(xlp, objp, intrsx);
+    if (nbp.n == 0)
+       return nbp;
+    if (nbp.area < bp.area)
+       bp = nbp;
+    /*bottom */
+    lp->pos.y = objp->pos.y - lp->sz.y;
+    nbp = xlintersections(xlp, objp, intrsx);
+    if (nbp.n == 0)
+       return nbp;
+    if (nbp.area < bp.area)
+       bp = nbp;
+
+    /*x right */
+    lp->pos.x = objp->pos.x + objp->sz.x;
+    /*top */
+    lp->pos.y = objp->pos.y + objp->sz.y;
+    nbp = xlintersections(xlp, objp, intrsx);
+    if (nbp.n == 0)
+       return nbp;
+    if (nbp.area < bp.area)
+       bp = nbp;
+    /*mid */
+    lp->pos.y = objp->pos.y;
+    nbp = xlintersections(xlp, objp, intrsx);
+    if (nbp.n == 0)
+       return nbp;
+    if (nbp.area < bp.area)
+       bp = nbp;
+    /*bottom */
+    lp->pos.y = objp->pos.y - lp->sz.y;
+    nbp = xlintersections(xlp, objp, intrsx);
+    if (nbp.n == 0)
+       return nbp;
+    if (nbp.area < bp.area)
+       bp = nbp;
+
+    /*sliding from top left */
+    if (intrsx[XLPXNY] || intrsx[XLCXNY] || intrsx[XLNXNY] || intrsx[XLPXCY] || intrsx[XLPXPY]) {      /* have to move */
+       if (!intrsx[XLCXNY] && !intrsx[XLNXNY]) {       /* some room right? */
+           /* slide along upper edge */
+           for (lp->pos.x = objp->pos.x - lp->sz.x,
+                lp->pos.y = objp->pos.y + objp->sz.y;
+                lp->pos.x <= (objp->pos.x + objp->sz.x);
+                lp->pos.x += xincr) {
+               nbp = xlintersections(xlp, objp, intrsx);
+               if (nbp.n == 0)
+                   return nbp;
+               if (nbp.area < bp.area)
+                   bp = nbp;
+           }
+       }
+       if (!intrsx[XLPXCY] && !intrsx[XLPXPY]) {       /* some room down? */
+           /* slide down left edge */
+           for (lp->pos.x = objp->pos.x - lp->sz.x,
+                lp->pos.y = objp->pos.y + objp->sz.y;
+                lp->pos.y >= (objp->pos.y - lp->sz.y);
+                lp->pos.y -= yincr) {
+               nbp = xlintersections(xlp, objp, intrsx);
+               if (nbp.n == 0)
+                   return nbp;
+               if (nbp.area < bp.area)
+                   bp = nbp;
+
+           }
+       }
+    }
+
+    /*sliding from bottom right */
+    lp->pos.x = objp->pos.x + objp->sz.x;
+    lp->pos.y = objp->pos.y - lp->sz.y;
+    if (intrsx[XLNXPY] || intrsx[XLCXPY] || intrsx[XLPXPY] || intrsx[XLNXCY] || intrsx[XLNXNY]) {      /* have to move */
+       if (!intrsx[XLCXPY] && !intrsx[XLPXPY]) {       /* some room left? */
+           /* slide along lower edge */
+           for (lp->pos.x = objp->pos.x + objp->sz.x,
+                lp->pos.y = objp->pos.y - lp->sz.y;
+                lp->pos.x >= (objp->pos.x - lp->sz.x);
+                lp->pos.x -= xincr) {
+               nbp = xlintersections(xlp, objp, intrsx);
+               if (nbp.n == 0)
+                   return nbp;
+               if (nbp.area < bp.area)
+                   bp = nbp;
+           }
+       }
+       if (!intrsx[XLNXCY] && !intrsx[XLNXNY]) {       /* some room up? */
+           /* slide up right edge */
+           for (lp->pos.x = objp->pos.x + objp->sz.x,
+                lp->pos.y = objp->pos.y - lp->sz.y;
+                lp->pos.y <= (objp->pos.y + objp->sz.y);
+                lp->pos.y += yincr) {
+               nbp = xlintersections(xlp, objp, intrsx);
+               if (nbp.n == 0)
+                   return nbp;
+               if (nbp.area < bp.area)
+                   bp = nbp;
+           }
+       }
+    }
+    return bp;
+}
+
+/* load the hilbert sfc keyed tree */
+static int xlhdxload(XLabels_t * xlp)
+{
+    int i;
+    int order = xlhorder(xlp);
+
+    for (i = 0; i < xlp->n_objs; i++) {
+       HDict_t *hp;
+       point pi;
+
+       hp = NEW(HDict_t);
+
+       hp->d.data = &xlp->objs[i];
+       hp->d.rect = objplpmks(xlp, &xlp->objs[i]);
+       /* center of the labeling area */
+       pi.x = hp->d.rect.boundary[0] +
+           (hp->d.rect.boundary[2] - hp->d.rect.boundary[0]) / 2;
+       pi.y = hp->d.rect.boundary[1] +
+           (hp->d.rect.boundary[3] - hp->d.rect.boundary[1]) / 2;
+
+       hp->key = hd_hil_s_from_xy(pi, order);
+
+       if (dtsearch(xlp->hdx, hp) != 0)
+           continue;
+       if (!(dtinsert(xlp->hdx, hp)))
+           return -1;
+    }
+    return 0;
+}
+
+static int xlspdxload(XLabels_t * xlp)
+{
+    HDict_t *op;
+
+    for (op = dtfirst(xlp->hdx); op; op = dtnext(xlp->hdx, op)) {
+       /*          tree       rectangle    data        node             lvl */
+       RTreeInsert(xlp->spdx, &op->d.rect, op->d.data, &xlp->spdx->root,
+                   0);
+    }
+    return 0;
+}
+
+static int xlinitialize(XLabels_t * xlp)
+{
+    int r;
+    if ((r = xlhdxload(xlp)) < 0)
+       return r;
+    if ((r = xlspdxload(xlp)) < 0)
+       return r;
+    return dtclose(xlp->hdx);
+}
+
+int
+placeLabels(object_t * objs, int n_objs,
+           xlabel_t * lbls, int n_lbls, label_params_t * params)
+{
+    int r, i;
+    BestPos_t bp;
+    XLabels_t *xlp = xlnew(objs, n_objs, lbls, n_lbls, params);
+    if ((r = xlinitialize(xlp)) < 0)
+       return r;
+
+    /* Place xlabel_t* lp near lp->obj so that the rectangle whose lower-left
+     * corner is lp->pos, and size is lp->sz does not intersect any object
+     * in objs (by convention, an object consisting of a single point
+     * intersects nothing) nor any other label, if possible. On input,
+     * lp->set is 0.
+     *
+     * On output, any label with a position should have this stored in
+     * lp->pos and have lp->set non-zero.
+     *
+     * If params->force is true, all labels must be positioned, even if
+     * overlaps are necessary.
+     *
+     * Return 0 if all labels could be placed without overlap;
+     * non-zero otherwise.
+     */
+    r = 0;
+    for (i = 0; i < n_objs; i++) {
+       if (objs[i].lbl == 0)
+           continue;
+       bp = xladjust(xlp, &objs[i]);
+       if (bp.n == 0) {
+           objs[i].lbl->set = 1;
+       } else if (params->force == 1) {
+           objs[i].lbl->pos.x = bp.pos.x;
+           objs[i].lbl->pos.y = bp.pos.y;
+           objs[i].lbl->set = 1;
+       } else {
+           r = 1;
+       }
+    }
+    xlfree(xlp);
+    return r;
+}
diff --git a/lib/label/xlabels.h b/lib/label/xlabels.h
new file mode 100644 (file)
index 0000000..63286bd
--- /dev/null
@@ -0,0 +1,108 @@
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifndef XLABELS_H
+#define XLABELS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <geom.h>
+
+typedef struct {
+    pointf sz;                 /* Size of label (input) */
+    pointf pos;                        /* Position of lower-left corner of label (output) */
+    void *lbl;                 /* Pointer to label in the graph */
+    unsigned char set;         /* True if the position has been set (input/output) */
+} xlabel_t;
+
+typedef struct {
+    pointf pos;                        /* Position of lower-left corner of object */
+    pointf sz;                 /* Size of object; may be zero for a point */
+    xlabel_t *lbl;             /* Label attached to object, or NULL */
+} object_t;
+
+typedef struct {
+    boxf bb;                   /* Bounding box of all objects */
+    unsigned char force;       /* If true, all labels must be placed */
+} label_params_t;
+
+int placeLabels(object_t * objs, int n_objs,
+               xlabel_t * lbls, int n_lbls, label_params_t * params);
+
+#ifdef XLABEL_INT
+#include <index.h>
+#include <logic.h>
+#include <cdt.h>
+
+#ifndef XLNDSCALE
+#define XLNDSCALE 72.0
+#endif /*XLNDSCALE*/
+#ifndef XLNBR
+#define XLNBR    9
+#endif /*XLNBR*/
+#ifndef XLXDENOM
+#define XLXDENOM 8
+#endif /*XLXDENOM*/
+#ifndef XLYDENOM
+#define XLYDENOM 2
+#endif /*XLYDENOM*/
+#define XLNBR    9
+#define XLCNR    4
+#define XLNDSCALE 72.0
+#define XLODCR   -1
+// indexes of neighbors in certain arrays
+// the node of interest is usually in node 4
+// 6 7 8
+// 3 4 5
+// 0 1 2
+#define XLPXPY   0
+#define XLCXPY   1
+#define XLNXPY   2
+#define XLPXCY   3
+#define XLCXCY   4
+#define XLNXCY   5
+#define XLPXNY   6
+#define XLCXNY   7
+#define XLNXNY   8
+    typedef struct best_p_s {
+    int n;
+    double area;
+    pointf pos;
+} BestPos_t;
+
+typedef struct obyh {
+    Dtlink_t link;
+    int key;
+    Leaf_t d;
+} HDict_t;
+
+typedef struct XLabels_s {
+    object_t *objs;
+    int n_objs;
+    xlabel_t *lbls;
+    int n_lbls;
+    label_params_t *params;
+
+    Dt_t *hdx;                 // splay tree keyed with hilbert spatial codes
+    RTree_t *spdx;             // rtree
+
+} XLabels_t;
+
+#endif                         /* XLABEL_INT */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif                         /*XLABELS_H */