Merge branch 'horizontals'
[swftools.git] / lib / gfxpoly / active.c
index aece368..c5f9c02 100644 (file)
@@ -1,4 +1,9 @@
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+#include "../../config.h"
 #include "../q.h"
+#include "../types.h"
 #include "active.h"
 
 actlist_t* actlist_new()
@@ -11,24 +16,25 @@ void actlist_destroy(actlist_t*a)
     free(a);
 }
 
-void actlist_dump(actlist_t*a, int32_t y)
+void actlist_dump(actlist_t*a, int32_t y, double gridsize)
 {
     segment_t*s = a->list;
     double lastx;
     char bad = 0;
+    if(!s) fprintf(stderr, "(empty)\n");
     while(s) {
         if(y) {
             double x = ((double)s->delta.x*(y-s->a.y)/s->delta.y)+s->a.x;
             if(s!=a->list) {
                 if(lastx>x) 
-                    fprintf(stderr, "?%f<->%f? ", lastx, x);
+                    fprintf(stderr, "?%.2f<->%.2f? ", lastx * gridsize, x * gridsize);
             }
             lastx = x;
         }
-        fprintf(stderr, "[%d]", s->nr);
+        fprintf(stderr, "[%d]", (int)s->nr);
         s = s->right;
         if(s) fprintf(stderr, " ");
-        else fprintf(stderr, "\n");
+        else fprintf(stderr, " y=%.2f\n", y * gridsize);
     }
 }
 void actlist_verify(actlist_t*a, int32_t y)
@@ -42,7 +48,7 @@ void actlist_verify(actlist_t*a, int32_t y)
                 /* we need to re-evaluate the x of the previous segment. if we
                    try to store it, it might end up being converted to a double,
                    which will make it non-equal to (and possibly larger than) the
-                   "long double" the FPU has in it's register. This only happens
+                   "long double" the FPU has in its register. This only happens
                    when compiler optimizations are turned on. */
                 assert((XPOS(s, y) - XPOS(l, y)) >= 0);
                 assert(XDIFF(s,l,y) >= 0);
@@ -55,27 +61,145 @@ void actlist_verify(actlist_t*a, int32_t y)
     }
 }
 
+static inline double single_cmp(segment_t*s, point_t p1)
+{
+    return LINE_EQ(p1, s);
+}
+
+static inline double cmp(segment_t*s, point_t p1, point_t p2)
+{
+    double d = LINE_EQ(p1, s);
+    if(d==0) {
+       d = LINE_EQ(p2, s);
+       if(d==0) {
+           /* We default to always inserting the new segment to the right of the old segment.
+              We do this so that we don't place new segments into the middle of already
+              overlapping lines which may have intersections scheduled.
+            */
+           //fprintf(stderr, "Notice: actlist_find: both points (%d,%d) and (%d,%d) exactly on segment [%d]\n", p1.x, p1.y, p2.x, p2.y, s->nr);
+       }
+    }
+    return d;
+}
+
+#ifdef SPLAY
+static void actlist_splay_dump(actlist_t*a);
+segment_t* actlist_find(actlist_t*a, point_t p1, point_t p2)
+{
+#ifdef CHECKS
+    segment_t*t = a->list;
+    char to_the_left = 0;
+    char fail = 0;
+    while(t) {
+       /* this check doesn't work out with cmp() because during horizontal
+          processing, both segments ending as well as segments starting will
+          be active in this scanline */
+       //double d = cmp(t, p1, p2);
+       double d = single_cmp(t, p1);
+       if(d>=0 && to_the_left) {
+           actlist_dump(a, p1.y, 1);
+           segment_t*s = a->list;
+           while(s) {
+               fprintf(stderr, "[%d] %f/%f (%d,%d) -> (%d,%d)\n", SEGNR(s),
+                       single_cmp(s,p1), cmp(s,p1,p2),
+                       s->a.x, s->a.y, s->b.x, s->b.y);
+               s = s->right;
+           }
+       }
+       assert(!(d>=0 && to_the_left));
+       if(d<0) to_the_left=1;
+       t = t->right;
+    }
+#if 0
+    if(a->size > 100) {
+       static actlist_t*last = 0;
+       if(last != a) {
+           last = a;
+           actlist_splay_dump(a);
+       }
+    }
+#endif
+#endif
+    segment_t*last=0, *s = a->root;
+    if(!s) return 0;
+    double d=0;
+    int depth = 0;
+    while(s) {
+        last = s;
+       depth++;
+       d = single_cmp(s, p1);
+        if(d<=0) {
+           s = s->leftchild;
+       } else {
+           s = s->rightchild;
+       }
+    }
+//#ifdef DEBUG
+#if 0
+    if(a->size > 1) {
+       /* 80% hit, average cost per miss ~ 4 nodes */
+       int expected_depth = (int)((double)log2((double)a->size+1))+1;
+       static int hits = 0;
+       static int misses = 0;
+       static int miss_cost = 0;
+       if(depth <= expected_depth) hits++;
+       else {misses++;
+           miss_cost += depth - expected_depth;
+       }
+       if(hits && misses)
+           fprintf(stderr, "%02.2f%% %f\n", hits / (double)(hits+misses), miss_cost / (double)misses);
+    }
+#endif
+
+    /* this can be optimized for (is not needed in) normal mode (as opposed to horizontal postprocess mode) */
+    segment_t*out = last;
+    if(d<0 || (d==0 && LINE_EQ(p2,last)<0)) {
+       last = last->left;
+       if(!last) {
+           assert(cmp(a->list, p1, p2)<0);
+           return 0;
+       }
+    } else {
+       while(last->right && cmp(last->right, p1, p2)>=0) {
+           last = last->right;
+       }
+    }
+
+#ifdef CHECKS
+    segment_t*l=0;
+    s = a->list;
+    while(s) {
+        if(cmp(s, p1, p2)<0)
+            break;
+        l = s;s = s->right;
+    }
+    if(l!=last) {
+       printf("[%d]!=[%d]\n", SEGNR(l), SEGNR(last));
+       printf("after tree: [%d]\n", SEGNR(out));
+       actlist_splay_dump(a);
+       s = a->list;
+       while(s) {
+           double d1 = single_cmp(s,p1);
+           double d2 = cmp(s,p1,p2);
+           int x1 = d1<0?-1:(d1>0?1:0);
+           int x2 = d2<0?-1:(d2>0?1:0);
+           printf("[%d](%d,%d) ", SEGNR(s), x1, x2);
+           s = s->right;
+       }
+       printf("\n");
+    }
+    assert(l == last);
+#endif
+
+    return last;
+}
+#else
 segment_t* actlist_find(actlist_t*a, point_t p1, point_t p2)
 {
-    /* this runs in O(n) right now, and should be optimized using a splay
-       tree to run in ammortized O(log(n)) 
-       (update: currently only 2.5% of the algorithm's running time is spent here,
-        so maybe we don't need to bother)
-     */
     segment_t*last=0, *s = a->list;
     if(!s) return last;
     while(s) {
-        double d = LINE_EQ(p1, s);
-        if(d==0) {
-            d = LINE_EQ(p2, s);
-            if(d==0) {
-                /* We default to always inserting the new segment to the right of the old segment.
-                   We do this so that we don't place new segments into the middle of already
-                   overlapping lines which may have intersections scheduled.
-                 */
-                //fprintf(stderr, "Notice: actlist_find: both points (%d,%d) and (%d,%d) exactly on segment [%d]\n", p1.x, p1.y, p2.x, p2.y, s->nr);
-            }
-        }
+       double d = cmp(s, p1, p2);
         if(d<0)
             break;
         last = s;
@@ -83,9 +207,242 @@ segment_t* actlist_find(actlist_t*a, point_t p1, point_t p2)
     }
     return last;
 }
+#endif
+
+#ifdef SPLAY
+
+#define LINK(node,side,child) (node)->side = (child);if(child) {(child)->parent = (node);}
+ //;fprintf(stderr, "[%d]->%s now [%d]\n", SEGNR(node), __STRING(side), SEGNR(child));
+
+// rotates segment's left node to the top
+static inline segment_t*rotate_right(actlist_t*a, segment_t*s)
+{
+    /*      s             n
+          /               \
+         n      ->         s
+          \               /
+           l             l
+    */
+    assert(s->leftchild);
+    segment_t*p = s->parent;
+    segment_t*n = s->leftchild;
+    segment_t*l = n->rightchild;
+    LINK(n,rightchild,s);
+    LINK(s,leftchild,l);
+    n->parent = p;
+    if(p) {
+       if(p->leftchild == s)
+           p->leftchild = n;
+       else if(p->rightchild == s)
+           p->rightchild = n;
+    } else {
+       a->root = n;
+    }
+    return n;
+}
+// rotates segment's right node to the top
+static inline segment_t*rotate_left(actlist_t*a, segment_t*s)
+{
+    /*      s             n
+            \           /
+             n  ->     s
+            /           \
+           r             r
+    */
+    assert(s->rightchild);
+    segment_t*p = s->parent;
+    segment_t*n = s->rightchild;
+    segment_t*r = n->leftchild;
+    LINK(n,leftchild,s);
+    LINK(s,rightchild,r);
+    n->parent = p;
+    if(p) {
+       if(p->leftchild == s)
+           p->leftchild = n;
+       else if(p->rightchild == s)
+           p->rightchild = n;
+    } else {
+       a->root = n;
+    }
+    return n;
+}
+
+static int actlist_splay_walk(actlist_t*a, segment_t*s, segment_t**ss, segment_t*parent)
+{
+    if(!s) return 1;
+    if(parent != s->parent) {
+       fprintf(stderr, "Parent mismatch in [%d]: [%d] != [%d]\n", SEGNR(s), SEGNR(parent), SEGNR(s->parent));
+       return 0;
+    }
+    if(!actlist_splay_walk(a, s->leftchild, ss, s)) return 0;
+    if(s != *ss) {
+       fprintf(stderr, "[%d] != [%d]\n", SEGNR(s), SEGNR(*ss));
+       return 0;
+    }
+    (*ss) = (*ss)->right; 
+    if(!actlist_splay_walk(a, s->rightchild, ss, s)) return 0;
+    return 1;
+}
+
+static int actlist_splay_verify(actlist_t*a)
+{
+    segment_t*c = a->list;
+    if(!actlist_splay_walk(a, a->root, &c, 0)) return 0;
+    if(c) return 0;
+    return 1;
+}
+static void actlist_splay_dump2(actlist_t*a, segment_t*s, char*mid, char*up, char*down)
+{
+    if(!s) return;
+    
+    if(s->leftchild || s->rightchild) {
+        int t;
+
+       if(s->leftchild) {
+           char*o3 = malloc(strlen(up)+3);
+           strcpy(o3, up);strcat(o3, "+-");
+           char*newup = malloc(strlen(up)+3);
+           strcpy(newup, up);strcat(newup, "| ");
+           char*newup2 = malloc(strlen(up)+3);
+           strcpy(newup2, up);strcat(newup2, "  ");
+           actlist_splay_dump2(a, s->leftchild, o3, newup2, newup);
+           fprintf(stderr, "%s| \n", up);
+           free(newup);
+           free(newup2);
+           free(o3);
+       }
+        fprintf(stderr, "%s[%d]\n", mid, SEGNR(s));
+       if(s->rightchild) {
+           char*o3 = malloc(strlen(down)+3);
+           strcpy(o3, down);strcat(o3, "+-");
+           char*newdown = malloc(strlen(down)+3);
+           strcpy(newdown, down);strcat(newdown, "| ");
+           char*newdown2 = malloc(strlen(down)+3);
+           strcpy(newdown2, down);strcat(newdown2, "  ");
+           fprintf(stderr, "%s| \n", down);
+           actlist_splay_dump2(a, s->rightchild, o3, newdown, newdown2);
+           free(newdown);
+           free(newdown2);
+           free(o3);
+       }
+    } else {
+        fprintf(stderr, "%s[%d]\n", mid, SEGNR(s));
+    }
+}
+static void actlist_splay_dump(actlist_t*a)
+{
+    actlist_splay_dump2(a, a->root, "", "", "");
+}
+
+
+static void move_to_root(actlist_t*a, segment_t*s)
+{
+    if(!s) return;
+    /* this is a textbook implementation of the three splay operations
+       zig, zig-zig and zig-zag */
+    int nr=0;
+    while(a->root != s) {
+       assert(s->parent);
+       segment_t*p = s->parent;
+       if(p == a->root) {
+           // zig
+           if(a->root->leftchild == s) {
+               rotate_right(a, a->root);
+           } else {
+               rotate_left(a, a->root);
+           }
+           assert(a->root == s);
+        } else {
+           segment_t*pp = p->parent;
+           if(p->leftchild == s && pp->leftchild == p) {
+               // zig-zig (left)
+               rotate_right(a, pp);
+               rotate_right(a, s->parent);
+           } else if(p->rightchild == s && pp->rightchild == p) {
+               // zig-zig (right)
+               rotate_left(a, pp);
+               rotate_left(a, s->parent);
+           } else if(p->leftchild == s && pp->rightchild == p) {
+               // zig-zag (left)
+               rotate_right(a, p);
+               rotate_left(a, s->parent);
+           } else if(p->rightchild == s && pp->leftchild == p) {
+               // zig-zag (right)
+               rotate_left(a, p);
+               rotate_right(a, s->parent);
+           } else {
+               assert(0);
+           }
+       }
+    }
+}
+
+static void actlist_splay(actlist_t*a, point_t p1, point_t p2)
+{
+    if(!a->list) return;
+
+    segment_t tmp;
+    memset(&tmp, 0, sizeof(tmp));
+    segment_t*left=&tmp,*right=&tmp;
+   
+    int c = 0;
+    while(1) {
+       if(cmp(a->root,p1,p2)<0) {
+           /* we're to the left of the root */
+           if(!a->root->leftchild) {
+               c = -1;break;
+           }
+           if(cmp(a->root->leftchild,p1,p2)<0) {
+               /* we're also to the left of the root's left child
+                  -> rotate right, so that the left child is root */
+               segment_t*s = a->root->leftchild;
+               LINK(a->root, leftchild, s->rightchild);
+               LINK(s, rightchild, a->root);
+               a->root = s;
+               if(!a->root->leftchild) {
+                   c = -1;break;
+               }
+           }
+           LINK(right, leftchild, a->root);
+           right = a->root;
+           a->root = a->root->leftchild;
+       } else /* cmp(a->root,p1,p2)>=0 */ {
+           /* we're to the right of the root */
+           if(!a->root->rightchild) {
+               c = 1;break;
+           }
+           if(cmp(a->root->rightchild,p1,p2)>=0) {
+               /* we're also to the right of the root's right child
+                  -> rotate left, so that the right child is root */
+               segment_t*s = a->root->rightchild;
+               LINK(a->root, rightchild, s->leftchild);
+               LINK(s, leftchild, a->root);
+               a->root = s;
+               if(!a->root->rightchild)
+                   c = 1;break;
+           }
+           LINK(left, rightchild, a->root);
+           left = a->root;
+           a->root = a->root->rightchild;
+       }
+    }
+    LINK(left, rightchild, a->root->leftchild);
+    LINK(right, leftchild, a->root->rightchild);
+    LINK(a->root, leftchild, tmp.rightchild);
+    LINK(a->root, rightchild, tmp.leftchild);
+    a->root->parent = 0;
+}
+
+#endif
 
 static void actlist_insert_after(actlist_t*a, segment_t*left, segment_t*s)
 {
+#ifdef SPLAY
+    //fprintf(stderr, "insert [%d] after [%d]\n", SEGNR(s), SEGNR(left));
+    //actlist_splay_dump(a);
+    //actlist_dump(a, s->a.y);
+#endif
+
     s->left = left;
     if(left) {
         s->right = left->right;
@@ -97,17 +454,39 @@ static void actlist_insert_after(actlist_t*a, segment_t*left, segment_t*s)
         s->left->right = s;
     if(s->right) 
         s->right->left = s;
-    a->size++;
-}
 
-void actlist_insert(actlist_t*a, point_t p, segment_t*s)
-{
-    segment_t*left = actlist_find(a, p, s->b);
-    actlist_insert_after(a, left, s);
+#ifdef SPLAY
+    // we insert nodes not trees 
+    assert(!s->leftchild);
+    assert(!s->rightchild);
+
+    if(a->root) {
+       move_to_root(a, left);
+       if(left) {
+           LINK(s,leftchild,a->root);
+           // steal right child from (previous) root
+           LINK(s,rightchild,a->root->rightchild);
+           a->root->rightchild = 0;
+       } else {
+           LINK(s,rightchild,a->root);
+       }
+    }
+    a->root = s;
+    a->root->parent = 0;
+  
+    assert(actlist_splay_verify(a));
+#endif
+
+    a->size++;
 }
 
 void actlist_delete(actlist_t*a, segment_t*s)
 {
+#ifdef SPLAY
+    assert(actlist_splay_verify(a));
+    move_to_root(a, s);
+    assert(actlist_splay_verify(a));
+#endif
     if(s->left) {
         s->left->right = s->right;
     } else {
@@ -118,6 +497,54 @@ void actlist_delete(actlist_t*a, segment_t*s)
     }
     s->left = s->right = 0;
     a->size--;
+#ifdef SPLAY
+    assert(a->root == s);
+    // delete root node
+    if(!a->root->leftchild) {
+       a->root = a->root->rightchild;
+    } else if(!a->root->rightchild) {
+       a->root = a->root->leftchild;
+    } else {
+#ifdef HAVE_LRAND48
+       if(lrand48()&1) {
+#else
+       if(((ptroff_t)s)&16) {
+#endif
+           // free up root->left->right
+           segment_t*t = a->root->leftchild;
+           while(t->rightchild) {
+               segment_t*r = t->rightchild;
+               segment_t*l = r->leftchild;
+               LINK(r, leftchild, t);
+               LINK(t, rightchild, l);
+               t = r;
+           }
+           LINK(a->root,leftchild,t);
+           assert(!a->root->leftchild->rightchild);
+           LINK(a->root->leftchild,rightchild,a->root->rightchild);
+           a->root = a->root->leftchild;
+       } else {
+           // free up root->right->left
+           segment_t*t = a->root->rightchild;
+           while(t->leftchild) {
+               segment_t*l = t->leftchild;
+               segment_t*r = l->rightchild;
+               LINK(l, rightchild, t);
+               LINK(t, leftchild, r);
+               t = l;
+           }
+           LINK(a->root,rightchild,t);
+           assert(!a->root->rightchild->leftchild);
+           LINK(a->root->rightchild,leftchild,a->root->leftchild);
+           a->root = a->root->rightchild;
+       }
+    }
+    if(a->root) 
+       a->root->parent = 0;
+    s->leftchild = s->rightchild = s->parent = 0;
+    
+    assert(actlist_splay_verify(a));
+#endif
 }
 int actlist_size(actlist_t*a)
 {
@@ -132,6 +559,9 @@ segment_t* actlist_leftmost(actlist_t*a)
 segment_t* actlist_rightmost(actlist_t*a)
 {
     /* this is only used in checks, so it doesn't matter that it's slow */
+#ifndef CHECKS
+    fprintf(stderr, "Warning: actlist_rightmost should not be used\n");
+#endif
     segment_t*s = a->list;
     segment_t*last = 0;
     while(s) {
@@ -141,19 +571,123 @@ segment_t* actlist_rightmost(actlist_t*a)
     return last;
 }
 
-segment_t* actlist_left(actlist_t*a, segment_t*s)
-{
-    return s->left;
-}
-
-segment_t* actlist_right(actlist_t*a, segment_t*s)
+void actlist_insert(actlist_t*a, point_t p1, point_t p2, segment_t*s)
 {
-    if(s) return s->right;
-    else  return a->list;
+    segment_t*left = actlist_find(a, p1, p2);
+    actlist_insert_after(a, left, s);
 }
 
 void actlist_swap(actlist_t*a, segment_t*s1, segment_t*s2)
 {
+#ifdef SPLAY
+    assert(actlist_splay_verify(a));
+#endif
+#ifdef CHECKS
+    /* test that s1 is to the left of s2- our swap
+       code depends on that */
+    segment_t*s = s1;
+    while(s && s!=s2) s = s->right;
+    assert(s==s2);
+#endif
+/*#ifndef SPLAY
     actlist_delete(a, s1);
     actlist_insert_after(a, s2, s1);
+#else*/
+    segment_t*s1l = s1->left;
+    segment_t*s1r = s1->right;
+    segment_t*s2l = s2->left;
+    segment_t*s2r = s2->right;
+    if(s1l) s1l->right = s2;
+    else    a->list = s2;
+    s2->left = s1l;
+    if(s2r) s2r->left = s1;
+    s1->right = s2r;
+    if(s2l!=s1) s1->left = s2l; 
+    else        s1->left = s2;
+    if(s1r!=s2) s2->right = s1r; 
+    else        s2->right = s1;
+   
+#ifdef SPLAY
+    if(s2->parent==s1) {
+       /* 
+            s1            s2
+           /      ->     /
+         s2            s1
+       */
+       segment_t*l = s2->leftchild;
+       segment_t*r = s2->rightchild;
+       assert(s1->rightchild == s2); // because s1 < s2
+       segment_t*l1 = s1->leftchild;
+       segment_t*p = s1->parent;
+       s1->parent = s2;
+       s2->parent = p;
+       if(p) {
+           if(p->leftchild == s1) p->leftchild=s2;
+           else {assert(p->rightchild == s1);p->rightchild=s2;}
+       } else {
+           a->root = s2;
+       }
+       s2->leftchild = l1;
+       s2->rightchild = s1;
+       s1->leftchild = l;
+       s1->rightchild = r;
+    } else if(s1->parent==s2) {
+       /* 
+            s2            s1
+           /      ->     /
+         s1            s2
+       */
+       segment_t*l = s1->leftchild;
+       segment_t*r = s1->rightchild;
+       segment_t*r2 = s2->rightchild;
+       assert(s2->leftchild == s1); // because s1 < s2
+       segment_t*p = s2->parent;
+       s2->parent = s1;
+       s1->parent = p;
+       if(p) {
+           if(p->leftchild == s2) p->leftchild=s1;
+           else {assert(p->rightchild == s2);p->rightchild=s1;}
+       } else {
+           a->root = s1;
+       }
+       s1->leftchild = s2;
+       s1->rightchild = r2;
+       s2->leftchild = l;
+       s2->rightchild = r;
+    } else {
+       segment_t*s1p = s1->parent;
+       segment_t*s1l = s1->leftchild;
+       segment_t*s1r = s1->rightchild;
+       segment_t*s2p = s2->parent;
+       segment_t*s2l = s2->leftchild;
+       segment_t*s2r = s2->rightchild;
+       s2->parent = s1p;
+       s2->leftchild = s1l;
+       s2->rightchild = s1r;
+       s1->parent = s2p;
+       s1->leftchild = s2l;
+       s1->rightchild = s2r;
+       assert(s1p || s2p);
+       if(s1p) {
+           if(s1p->leftchild == s1) s1p->leftchild=s2;
+           else {assert(s1p->rightchild == s1);s1p->rightchild=s2;}
+       } else {
+           a->root = s2;
+       }
+       if(s2p) {
+           if(s2p->leftchild == s2) s2p->leftchild=s1;
+           else {assert(s2p->rightchild == s2);s2p->rightchild=s1;}
+       } else {
+           a->root = s1;
+       }
+    }
+    if(s1->leftchild) s1->leftchild->parent = s1;
+    if(s2->leftchild) s2->leftchild->parent = s2;
+    if(s1->rightchild) s1->rightchild->parent = s1;
+    if(s2->rightchild) s2->rightchild->parent = s2;
+
+    assert(actlist_splay_verify(a));
+#endif
+
+//#endif
 }