chiark / gitweb /
new arrangements for trivia etc., wip for movfeat stuff
authorian <ian>
Sun, 20 Mar 2005 17:53:51 +0000 (17:53 +0000)
committerian <ian>
Sun, 20 Mar 2005 17:53:51 +0000 (17:53 +0000)
layout/graph-data.h
layout/redactgraph.c

index 37b860ec707a778fab3dc6a5ff2901cb724fe8dc..6ecbcadca7abdf5c7c08ced52c87535e8d984d43 100644 (file)
@@ -64,6 +64,13 @@ extern int next_nodenum, next_edgenum;
 #define FOR_ALL_NODES(node) for (node=all_nodes.head; node; node=node->next)
 #define FOR_BOTH(sideend) for (sideend=0; sideend<2; sideend++)
 
+#define FOR_NODE_EDGEENDS(side,edgeend, node)  \
+  FOR_BOTH(side)                               \
+    FOR_SIDE_EDGEENDS(edgeend, (node),side)
+
+#define FOR_SIDE_EDGEENDS(edgeend, node,side) \
+  for (edgeend=(node)->sides[(side)].head; edgeend; edgeend=edgeend->next)
+
 #define FOR_SEGMENT_MOVFEATS(i,f, segment) \
   for (i=0, f=segment->movfeats; i < segment->n_movfeats; i++, f++)
 
@@ -76,11 +83,10 @@ extern int next_nodenum, next_edgenum;
  * appropriately.  edgeend must be of type EdgeEnd*, and side of type
  * int; these are used as working variables by FOR_EDGES.
  */
-#define FOR_EDGES(edge, node,side,edgeend)                              \
-  FOR_BOTH(side)                                                        \
-    for (edgeend=node->sides[side].head; edgeend; edgeend=edgeend->next) \
-      if ((edge= edgeend->edge), edgeend->end) {                        \
-        /* do each edge once, from end 0, only */                       \
+#define FOR_EDGES(edge, node,side,edgeend)             \
+    FOR_NODE_EDGEENDS(side,edgeend, node)                      \
+      if ((edge= edgeend->edge), edgeend->end) {       \
+        /* do each edge once, from end 0, only */      \
       } else
      
 
index 4ec2e6f6f15aac9e59a26cf204e92691099698ed..29fa3f6998372fbd64365b4852c533bffdfd5237 100644 (file)
@@ -166,8 +166,8 @@ static int count_edges(const NodeSide *node) {
   return count;
 }
 
-static Edge *makeedge(const char *pname, double distance,
-                     MovFeat *subseg, int movpos) {
+static Edge *edge_create(const char *pname, double distance,
+                        MovFeat *subseg, int movpos) {
   Edge *e;
   int end;
   
@@ -184,6 +184,29 @@ static Edge *makeedge(const char *pname, double distance,
   return e;
 }
 
+static Node *node_create(const char *pname) {
+  Node *n;
+  int side;
+
+  n= MMALLOC(sizeof(*n));
+  n->pname= pname;
+  FOR_BOTH(side) {
+    n->sides[side].node= n;
+    n->sides[side].side= side;
+    LIST_INIT(n->sides[side]);
+  }
+  LIST_LINK_TAIL(all_nodes,n);
+  return n;
+}
+
+/* Perhaps surprisingly, it's OK to remove nodes and edges in loops
+ * made with the macros in graph-data.h, provided that if the
+ * _current_ node or edge is removed, that happens last.  This is
+ * because removing a node or edge it doesn't actually free it or
+ * change its contents, so the thing->next is still valid afterwards.
+ * This depends on the fact that we never free anything we're done
+ * with !
+ */
 
 static void edgeend_detach(EdgeEnd *toremove) {
   trace("[detach:"); trace_edgeend(toremove);
@@ -201,9 +224,11 @@ static void edgeend_replumb(EdgeEnd *tomove, NodeSide *newtarget) {
   edgeend_attach(tomove,newtarget);
 }
 
-static void edge_delete(Edge *edge) {
-  edgeend_detach(&edge->ends[0]);
-  edgeend_detach(&edge->ends[1]);
+static void edge_delete(EdgeEnd *detachlast) {
+  Edge *edge;
+  edge= detachlast->edge;
+  edgeend_detach(&edge->ends[!detachlast->end]);
+  edgeend_detach(detachlast);
   trace("[delete:"); trace_edge(edge); trace("]");
 }
 
@@ -230,7 +255,7 @@ static void node_surely_orphaned(Node *node) {
    *
    * The conversion has two stages:
    *
-   * FIRST STAGE (movfeatmultedges):
+   * FIRST STAGE (movfeatsplitedges):
    *
    * We multiply up each edge of any segment with moveable features to
    * be an edge which is a position of the *-feature.
@@ -248,7 +273,7 @@ static void node_surely_orphaned(Node *node) {
    *     *----A1/P0----*   becomes   *----A1/P0J0----*
    *                                  `---A1/P0J1---'
    *
-   * SECOND STAGE:
+   * SECOND STAGE (movfeatrmstubs):
    *
    * We remove stub moveable edges: whenever, at a node which has only
    * edges which are part of the same segment, we find an edge on one
@@ -290,7 +315,7 @@ static void node_surely_orphaned(Node *node) {
    *            `---A1/P1J0----*---A1/P1J1--'
    */
 
-static void movfeatmultedges(void) {
+static void movfeatsplitedges(void) {
   Node *node;
   int side, l, i, weight, combpos, end;
   EdgeEnd *edgeend;
@@ -299,13 +324,13 @@ static void movfeatmultedges(void) {
   MovFeat *subseg, *starfeature, *loopfeat;
   char *featstr;
   
-  trace("movfeatmultedges ...\n");
+  trace("movfeatsplitedges ...\n");
   FOR_ALL_NODES(node) {
     FOR_EDGES(edge, node,side,edgeend) {
       subseg= edge->subseg;
       segment= subseg->segment;
 
-      trace("movfeatmultedges "); trace_edge(edge);
+      trace("movfeatsplitedges "); trace_edge(edge);
       trace(" segment %s ", segment->segname);
       if (segment->n_movfeats<=1) { trace("no moveables\n"); continue; }
       if (subseg == segment->starfeature) { trace("*-feature\n"); continue; }
@@ -354,41 +379,130 @@ static void movfeatmultedges(void) {
       trace(" ...\n");
        
       for (combpos=0; combpos<starfeature->n_positions; combpos++) {
-       trace("movfeatmultedges  "); trace_combpos(combpos,segment);
+       trace("movfeatsplitedges  "); trace_combpos(combpos,segment);
        if ((combpos / weight) % subseg->n_positions != edge->movpos) {
          trace(" excluded\n");
          continue;
        }
-       newedge= makeedge(masprintf("%s/*%d",edge->pname,combpos),
-                         edge->distance,
-                         starfeature,
-                         combpos);
+       newedge= edge_create(masprintf("%s/*%d",edge->pname,combpos),
+                            edge->distance,
+                            starfeature,
+                            combpos);
        FOR_BOTH(end)
          edgeend_attach(&newedge->ends[end], edge->ends[end].node);
       }
-      edge_delete(edge);
-      /* edge_delete doesn't destroy or modify this edge node,
-       * so we can safely carry on with this loop, which will
-       * now carry on with the next edge - either one we've just
-       * added, or the one which was next before we started messing
-       * about.
-       */
+      edge_delete(edgeend);
     }
   }
-  trace("movfeatmultedges complete.\n");
+  trace("movfeatsplitedges complete.\n");
+}
+
+static int movfeat_isinnernode(Node *node) {
+  int side;
+  EdgeEnd *edgeend;
+  Segment *all_segment, *segment;
+
+  trace_node(node);
+  all_segment= 0;
+  FOR_NODE_EDGEENDS(side,edgeend, node) {
+    segment= edgeend->edge->subseg->segment;
+    if (!all_segment) {
+      all_segment= segment;
+    } else if (all_segment != segment) {
+      trace(" not inner: %s and %s\n",
+           all_segment->segname, segment->segname);
+      return 0;
+    }
+  }
+  if (!all_segment) {
+    trace(" not inner: no edges?!\n");
+    return 0;
+  }
+  trace(" inner %s ",segment->segname);
+  return 1;
 }
 
-static void elimtrivial(void) {
-  /* Eliminate trivial nodes: ones which have only two edges, which
-   * come in on opposite sides, have the same subsegspec, and with the
-   * two ends identically aligned.
+static EdgeEnd *movfeat_findmate(EdgeEnd *key) {
+  Node *node;
+  EdgeEnd *mate;
+  int mateside;
+
+  node= key->node->node;
+  mateside= !key->node->side;
+  
+  FOR_SIDE_EDGEENDS(mate, node,mateside) {
+    if (key->edge->subseg == mate->edge->subseg &&
+       key->edge->movpos == mate->edge->movpos &&
+       key->end != mate->end)
+      return mate;
+  }
+  return 0; /* no mate - must be a stub */
+}
+
+static void movfeatrmstubs(void) {
+  int pass, anychange, side;
+  Node *node;
+  Edge *edge;
+  EdgeEnd *edgeend, *mate;
+
+  pass= 0;
+  do {
+    anychange= 0;
+    pass++;
+    trace("movfeatrmstubs pass %d ...\n", pass);
+    FOR_ALL_NODES(node) {
+      trace("movfeatrmstubs ");
+      if (!movfeat_isinnernode(node)) continue;
+      FOR_NODE_EDGEENDS(side,edgeend, node) {
+       edge= edgeend->edge;
+       mate= movfeat_findmate(edgeend);
+       if (mate) {
+         trace("("); trace_edgeend(edgeend); trace(")");
+         continue;
+       }
+       edge_delete(edgeend);
+       anychange++;
+      }
+      trace("\n");
+    }
+    trace("movfeatrmstubs pass %d %s\n", pass, anychange?"changed":"alldone");
+  } while (anychange);
+}
+
+static void movfeatsplitnodes(void) {
+  Node *node, *newnode;
+  EdgeEnd *edgeend, *mate;
+  Edge *edge;
+  int side;
+  
+  FOR_ALL_NODES(node) {
+    trace("movfeatsplitnodes ");
+    if (!movfeat_isinnernode(node)) continue;
+    if (!node->sides[0].head->next) {
+      trace("pair-trivial\n");
+      continue;
+    }
+    FOR_NODE_EDGEENDS(side,edgeend, node) {
+      edge= edgeend->edge;
+      mate= movfeat_findmate(edgeend);
+      assert(mate);
+      newnode= node_create(masprintf("%s/*%d",node->pname,edge->movpos));
+      edgeend_replumb(mate, &newnode->sides[mate->node->side]);
+      edgeend_replumb(edgeend, &newnode->sides[edgeend->node->side]);
+    }
+  }
+}
+
+static void trivpairnodes(void) {
+  /* Eliminate near-trivial nodes - in this case, ones which have only
+   * two edges, which come in on opposite sides, have the same
+   * subsegspec, and with the two ends identically aligned.
    */
   EdgeEnd *leftedge, *rightedge;
-  Node *node, *next_node;
+  Node *node;
 
   FOR_ALL_NODES(node) {
-    next_node=node->next;
-    trace("elimtrivial node "); trace_node(node); trace(" ");
+    trace("trivpairnodes node "); trace_node(node); trace(" ");
     if (!(count_edges(&node->sides[0])==1 &&
          count_edges(&node->sides[1])==1)) {
       trace("no, a non-unitary side\n");
@@ -413,11 +527,27 @@ static void elimtrivial(void) {
       masprintf("%s+%s", leftedge->edge->pname, rightedge->edge->pname);
     rightedge->edge->distance += leftedge->edge->distance;
     edgeend_replumb(rightedge, edgeend_otherend(leftedge)->node);
-    edge_delete(leftedge->edge);
+    edge_delete(leftedge);
+    trace(" trivpairnodes operation complete\n");
+  }
+}
+
+static void trivnullnodes(void) {
+  /* Eliminate trivial nodes - ones which have no edges. */
+  Node *node;
+  int side;
+  
+  trace("trivnullnodes ");
+  FOR_ALL_NODES(node) {
+    FOR_BOTH(side) {
+      if (node->sides[side].head)
+       goto non_null;
+    }
     node_surely_orphaned(node);
-    trace(" elimtrivial operation complete\n");
+  non_null:;
   }
-} 
+  trace(" trivnullnodes done.\n");
+}
 
 static void check(const char *why) {
   Node *node;
@@ -439,7 +569,7 @@ static void check(const char *why) {
       trace(" consistency "); trace_nodeside(&node->sides[side]); trace("\n");
       assert(node->sides[side].node == node);
       assert(node->sides[side].side == side);
-      for (edgeend=node->sides[side].head; edgeend; edgeend=edgeend->next) {
+      FOR_SIDE_EDGEENDS(edgeend, node,side) {
        trace(" consistency "); trace_nodeside(&node->sides[side]);
        trace(" "); trace_edgeend(edgeend); trace("\n");
        
@@ -522,8 +652,11 @@ static const OpInfo opinfos[]= {
 #define OI(x) { #x, x },
   OI(printforneato)
   OI(consistency)
-  OI(movfeatmultedges)
-  OI(elimtrivial)
+  OI(movfeatsplitedges)
+  OI(movfeatrmstubs)
+  OI(movfeatsplitnodes)
+  OI(trivpairnodes)
+  OI(trivnullnodes)
   { 0,0 }
 };