Algorithm-BreakOverlappingRectangles

 view release on metacpan or  search on metacpan

BreakOverlappingRectangles.xs  view on Meta::CPAN

/* -*- Mode: C -*- */

#define PERL_NO_GET_CONTEXT 1

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#define X0 0
#define Y0 1
#define X1 2
#define Y1 3

#define IDOFFSET (sizeof(NV) * 4)
#define BRUTEFORCECUTOFF 40

#if 1
#define DP(f)
#define DUMP(msg, av, start)
#define my_assert(a) assert(a)
#else

#define my_assert(a)  if(!(a)) _failed_assertion(aTHX_ #a, __LINE__, __FILE__) 

#define DP(f) f
#define DUMP(msg, av, start) _dump(aTHX_ (msg), (av), (start))
static void
_dump(pTHX_ char *msg, AV *rects, I32 start) {
    I32 end = av_len(rects) + 1;
    SV **svs = AvARRAY(rects);
    I32 i;
    fprintf (stderr, "%s = start: %d, end: %d", msg, start, end);
    for (i = start; i < end; i++) {
        SV *sv = svs[i];
        if (SvOK(sv)) {
            STRLEN len, j;
            NV* nv = (NV*)SvPV(svs[i], len);
            IV* iv = (IV*)(SvPV_nolen(svs[i]) + IDOFFSET);
            len = (len - IDOFFSET) / sizeof(IV);
            fprintf (stderr, " [%.0f %.0f %.0f %.0f |", nv[0], nv[1], nv[2], nv[3]);
            for (j = 0; j < len; j++)
                fprintf(stderr, " %d", iv[j]);
            fprintf(stderr, "]");
        }
        else
            fprintf(stderr, " undef");
    }
    fprintf(stderr, "\n");
    fflush(stderr);
}
void _failed_assertion(pTHX_ char *str, int line, char *file) {
    fprintf(stderr, "assertion %s failed at %s line %d\n", str, line, file);
    fflush(stderr);
    exit(1);
}

#endif

static SV *
av_sure_fetch(pTHX_ AV *av, I32 i) {
    SV **sv = av_fetch(av, i, 0);
    my_assert(sv);
    return *sv;
}

static NV
sqr (NV a) {
    return a * a;
}
int

double_cmp(pTHX_ double *a, double *b) {
    double fa = *a;
    double fb = *b;
    DP(printf("cmp(%f, %f) => %d\n", fa, fb, (fa < fb) ? -1 : ((fa > fb) ? 1 : 0)));
    return (fa < fb) ? -1 : ((fa > fb) ? 1 : 0);
}

void
sort_inplace(pTHX_ double **v, int size) {
    sortsv((SV**)v, size, (SVCOMPARE_t)&double_cmp);
}

static NV
find_best_cut(pTHX_ AV *rects, I32 start, I32 end, int dir, NV *bestv) {
    NV **v0, **v1, **vc0, **vc1;
    NV v, med, best;
    int op, cl;
    int i;
    SV **svs;
    I32 size = end - start;

    my_assert(bestv);
    
    DUMP("fbc  in", rects, start);
    DP(fprintf(stderr, "end: %d\n", end));
    
    Newx(v0, size + 1, NV *);
    Newx(v1, size + 1, NV *);
    
    v0[size] = v1[size] = NULL;
    
    vc0 = v0; vc1 = v1;

    svs = AvARRAY(rects) + start;
    size = end - start;
    
    for (i = 0; i < size; i++) {
        NV *nv = (NV*)SvPV_nolen(svs[i]);
        if (dir == 'x') {
            v0[i] = nv + X0;
            v1[i] = nv + X1;
        }
        else {
            v0[i] = nv + Y0;
            v1[i] = nv + Y1;
        }
    }
    v0[size] = v1[size] = NULL;

    sort_inplace(aTHX_ v0, size);
    sort_inplace(aTHX_ v1, size);
    
    op = cl = 0;
    med = 0.5 * size;
    best =  .24 * sqr(size);

    my_assert(best >= 0);
             
    while (*v0 && *v1) {
        NV v, good;
        NV l, r;
        
        v =  (**v0 <= **v1) ? **v0 : **v1;

        while (*v0 && v == **v0) {
            op++;
            v0++;
        }
        while (*v1 && v == **v1) {
            cl++;
            v1++;
        }

        my_assert(op > 0 && op <= size);
        my_assert(cl >= 0 && cl <= size);
        
        l = op - med;
        r = size - cl - med;
        good = sqr(l) + sqr(r);

        my_assert(good >= 0);
        
        if (good < best) {
            DP(fprintf(stderr, "find_best_cut l: %.2f, r: %.2f, good: %.2f\n", l, r, good));
            best = good;
            *bestv = v;
        }
    }

    Safefree(vc0);
    Safefree(vc1);
    
    return best;
}

static void
_break(pTHX_ AV *rects, I32 start, AV *parts);

static void
_brute_force_merge(pTHX_ AV *rects, I32 start, AV *parts) {
    I32 i, len;
    for (i = start; i <= av_len(rects); i++) {
        SV *svr = (AvARRAY(rects))[i];
        I32 j;
        for (j=0; j <= av_len(parts);) {
            NV *r = (NV*) SvPV_nolen(svr);
            SV *svp = (AvARRAY(parts))[j];
            NV *p = (NV*) SvPV_nolen(svp);
            
            if ((r[X1] > p[X0]) &&
                (r[X0] < p[X1]) &&
                (r[Y1] > p[Y0]) &&
                (r[Y0] < p[Y1])) {
                if ((r[X0] == p[X0]) &&
                    (r[Y0] == p[Y0]) &&
                    (r[X1] == p[X1]) &&
                    (r[Y1] == p[Y1])) {
                    SV *last;
                    STRLEN len;
                    char *pv = SvPV(svp, len);
                    sv_catpvn(svr, pv + IDOFFSET, len - IDOFFSET);
                    last = av_pop(parts);
                    if (last == svp)
                        SvREFCNT_dec(last);
                    else
                        av_store(parts, j, last);
                }
                else {
                    NV x[4], y[4];
                    /* sort xs and ys */
                    if (r[X0] < p[X0]) {
                        x[0] = r[X0]; x[1] = p[X0];
                    }
                    else {
                        x[0] = p[X0]; x[1] = r[X0];
                    }
                    if (r[X1] < p[X1]) {
                        x[2] = r[X1]; x[3] = p[X1];
                    }
                    else {
                        x[2] = p[X1]; x[3] = r[X1];
                    }
                    if (r[Y0] < p[Y0]) {
                        y[0] = r[Y0]; y[1] = p[Y0];
                    }
                    else {
                        y[0] = p[Y0]; y[1] = r[Y0];
                    }
                    if (r[Y1] < p[Y1]) {
                        y[2] = r[Y1]; y[3] = p[Y1];
                    }
                    else {
                        y[2] = p[Y1]; y[3] = r[Y1];
                    }
                    
                    if ( ( ((x[3] - x[0]) > (y[3] - y[0])) &&
                           ((x[0] != x[1]) || (x[2] != x[3])) ) ||
                         ((y[0] == y[1]) && (y[2] == y[3])) ) {
                        NV b = ((sqr(x[1] - x[0]) + sqr(x[1] - x[3]))
                                < (sqr(x[2] - x[0]) + sqr(x[2] - x[3])) ? x[1] : x[2]);
                        if ((r[X0] < b) && (r[X1] > b)) {
                            SV *svcp = newSVsv(svr);

BreakOverlappingRectangles.xs  view on Meta::CPAN

                               b, r[1], r[2], r[3]); fflush(stderr); */
                            
                            r[X1] = b;
                        }
                        if ((p[X0] < b) && (p[X1] > b)) {
                            SV *svcp = newSVsv(svp);
                            NV *cp = (NV*)SvPV_nolen(svcp);
                            cp[X0] = b;
                            av_push(parts, svcp);
                            
                            /* fprintf(stderr, "[%f %f %f %f] -> [%f %f %f %f], [%f %f %f %f]\n",
                               p[0], p[1], p[2], p[3],
                               cp[0], cp[1], cp[2], cp[3],
                               p[0], p[1], b, p[3]); fflush(stderr); */
                            
                            p[X1] = b;
                        }
                    }
                    else {
                        NV b = ((sqr(y[1] - y[0]) + sqr(y[1] - y[3]))
                                < (sqr(y[2] - y[0]) + sqr(y[2] - y[3])) ? y[1] : y[2]);
                        if ((r[Y0] < b) && (r[Y1] > b)) {
                            SV *svcp = newSVsv(svr);
                            NV *cp = (NV*)SvPV_nolen(svcp);
                            cp[Y0] = b;
                            av_push(rects, svcp);
                            
                            /* fprintf(stderr, "[%f %f %f %f] -> [%f %f %f %f], [%f %f %f %f]\n",
                               r[0], r[1], r[2], r[3],
                               cp[0], cp[1], cp[2], cp[3],
                               r[0], b, r[2], r[3]); fflush(stderr); */
                            
                            r[Y1] = b;
                        }
                        if ((p[Y0] < b) && (p[Y1] > b)) {
                            SV *svcp = newSVsv(svp);
                            NV *cp = (NV*)SvPV_nolen(svcp);
                            cp[Y0] = b;
                            av_push(parts, svcp);
                            
                            /* fprintf(stderr, "[%f %f %f %f] -> [%f %f %f %f], [%f %f %f %f]\n",
                               p[0], p[1], p[2], p[3],
                               cp[0], cp[1], cp[2], cp[3],
                               p[0], p[1], p[2], b); fflush(stderr); */
                            
                            p[Y1] = b;
                        }
                    }
                    continue;
                }
            }
            j++;
        }
    }
    /*
    for (i = av_len(parts); i >= 0; i--)
        av_push(rects, av_pop(parts));
    */
    if ((len = av_len(parts)) >= 0) {
        SV **svrs, **svps;
        I32 start = av_len(rects) + 1;
        av_extend(rects, start + len);
        svrs = AvARRAY(rects) + start;
        svps = AvARRAY(parts);
        AvFILLp(parts) = -1;
        AvFILLp(rects) = start + len;

        do {
            svrs[len] = svps[len];
            svps[len] = &PL_sv_undef;
        } while (--len >= 0);
    }
}

static void
_brute_force_break(pTHX_ AV *rects, I32 start, AV *parts) {
    I32 i, j, end1;

    DUMP("bfb  in", rects, start);

    end1 = av_len(rects);
    if (end1 < start + 1)
        return;

    if (end1 - start > 2 * BRUTEFORCECUTOFF ) {
        SV **svs;
        I32 end;
        I32 middle = (start + end1 + 1) / 2;
        _break(aTHX_ rects, middle, parts);

        svs = AvARRAY(rects);
    
        end = av_len(rects) + 1;

        i = start;
        j = end;
        while (i < middle && j > middle) {
            j--;
            SV *tmp = svs[i];
            svs[i] = svs[j];
            svs[j] = tmp;
            i++;
        }

        middle = start + end - middle;
        _break(aTHX_ rects, middle, parts);
        
        end = av_len(rects);

        if ((end - start) > (end1 - start) * 1.2)
            return _break(aTHX_ rects, start, parts);

        while (end-- >= middle)
            av_push(parts, av_pop(rects));

        return _brute_force_merge(aTHX_ rects, start, parts);
    }

    while (--end1 >= start) {
        SV *last, *next;
        SV **svs;

        DP(fprintf(stderr, "bfb: start: %d, end1: %d, end: %d\n", start, end1, av_len(rects) + 1));

        svs = AvARRAY(rects);
        last = svs[AvFILLp(rects)];
        svs[AvFILLp(rects)--] = &PL_sv_undef;
        next = svs[end1];
        svs[end1] = last;
        av_push(parts, next);

        _brute_force_merge(aTHX_ rects, end1, parts);
    }
    return;
}

static void
_break(pTHX_ AV *rects, I32 start, AV *parts) {
    NV bestx, bestxx, besty, bestyy, div;
    int off;
    I32 i, j, middle, end;
    SV **svs;
    
    DUMP("break", rects, start);

    while (1) {

        end = av_len(rects) + 1;

        if ((end - start) <= BRUTEFORCECUTOFF)
            return _brute_force_break(aTHX_ rects, start, parts);

        bestx = find_best_cut(aTHX_ rects, start, end, 'x', &bestxx);
        besty = ((bestx == 0) ? 1 : find_best_cut(aTHX_ rects, start, end, 'y', &bestyy));

        if (bestx < besty) {
            off = X0;
            div = bestxx;
            DP(fprintf(stderr, "cutting at x=%.0f, best=%.2f\n", bestxx, bestx));
        }
        else {
            off = Y0;
            div = bestyy;
            DP(fprintf(stderr, "cutting at y=%.0f, best=%.2f\n", bestyy, besty));
        }
    
        svs = AvARRAY(rects);
        i = start;
        middle = end;
        while (i < middle) {
            SV *sv = svs[i];
            NV n0 = ((NV*)SvPV_nolen(sv))[off];
            if (n0 < div) {
                middle--;
                svs[i] = svs[middle];
                svs[middle] = sv;
            }
            else
                i++;
        }

        DUMP("b0", rects, start);
    
        if (middle == start || middle == end)
            return _brute_force_break(aTHX_ rects, start, parts);


        _break(aTHX_ rects, middle, parts);

        DUMP("b1", rects, start);

        svs = AvARRAY(rects);
    
        end = av_len(rects) + 1;

        i = start;
        j = end;
        while (i < middle && j > middle) {
            j--;
            SV *tmp = svs[i];
            svs[i] = svs[j];
            svs[j] = tmp;
            i++;
        }

        DUMP("b2", rects, start);

        end += start - middle;

        off += 2;
        i = start;
        middle = end;
        DP(fprintf(stderr, "i: %d, middle: %d\n", i, middle));
        while (i < middle) {
            SV *sv = svs[i];
            NV n0 = ((NV*)SvPV_nolen(sv))[off];
            if (n0 > div) {
                middle--;
                svs[i] = svs[middle];
                svs[middle] = sv;
            }
            else
                i++;
        }

        DUMP("b3", rects, start);

        if (middle == start)
            return _brute_force_break(aTHX_ rects, start, parts);

        start = middle;
    }
    /* _break(aTHX_ rects, middle); */
}

MODULE = Algorithm::BreakOverlappingRectangles		PACKAGE = Algorithm::BreakOverlappingRectangles		
PROTOTYPES: DISABLE

void
_break_rectangles(rects)
    AV *rects;
CODE:
    if (SvMAGICAL((SV*)rects))
        Perl_croak(aTHX_ "internal error: unacceptable magic AV found");
    _break(aTHX_ rects, 0, (AV*)sv_2mortal((SV*)newAV()));



( run in 0.333 second using v1.01-cache-2.11-cpan-119454b85a5 )