Example #1
0
/*
 * Determines the lower tangent of two triangulations. 
 */
static void lower_tangent(edge * r_cw_l, point * s, edge * l_ccw_r,
                          point * u, edge ** l_lower, point ** org_l_lower,
                          edge ** r_lower, point ** org_r_lower)
{
    edge *l, *r;
    point *o_l, *o_r, *d_l, *d_r;
    boolean finished;

    l = r_cw_l;
    r = l_ccw_r;
    o_l = s;
    d_l = Other_point(l, s);
    o_r = u;
    d_r = Other_point(r, u);
    finished = FALSE;

    while (!finished)
        if (Cross_product_3p(o_l, d_l, o_r) > 0.0) {
            l = Prev(l, d_l);
            o_l = d_l;
            d_l = Other_point(l, o_l);
        } else if (Cross_product_3p(o_r, d_r, o_l) < 0.0) {
            r = Next(r, d_r);
            o_r = d_r;
            d_r = Other_point(r, o_r);
        } else
            finished = TRUE;

    *l_lower = l;
    *r_lower = r;
    *org_l_lower = o_l;
    *org_r_lower = o_r;
}
static int DelaunayClustering(int MaxClusterSize)
{
    int Count = 0, Count1, Count2 = 0, i, j = 0;
    Edge *EdgeSet, Key;
    Node *From, *To, *N, *F, *T;
    point *u, *v;
    edge *e_start, *e;

    delaunay(Dimension);
    for (i = 0; i < Dimension; i++) {
        u = &p_array[i];
        e_start = e = u->entry_pt;
        do {
            v = Other_point(e, u);
            if (u->id < v->id)
                Count++;
        } while ((e = Next(e, u)) != e_start);
    }
    assert(EdgeSet = (Edge *) malloc(Count * sizeof(Edge)));
    for (i = 0; i < Dimension; i++) {
        u = &p_array[i];
        e_start = e = u->entry_pt;
        do {
            v = Other_point(e, u);
            if (u->id < v->id) {
                EdgeSet[j].From = From = &NodeSet[u->id];
                EdgeSet[j].To = To = &NodeSet[v->id];
                EdgeSet[j++].Cost = FixedOrCommon(From, To) ? INT_MIN :
                    Distance(From, To) * Precision + From->Pi + To->Pi;
            }
        } while ((e = Next(e, u)) != e_start);
    }
    free_memory();
    if (WeightType == GEO || WeightType == GEOM ||
        WeightType == GEO_MEEUS || WeightType == GEOM_MEEUS) {
        N = FirstNode;
        while ((N = N->Suc) != FirstNode)
            if ((N->Y > 0) != (FirstNode->Y > 0))
                break;
        if (N != FirstNode) {
            N = FirstNode;
            do
                N->Zc = N->Y;
            while ((N = N->Suc) != FirstNode);
            /* Transform longitude (180 and -180 map to 0) */
            From = FirstNode;
            do {
                From->Zc = From->Y;
                if (WeightType == GEO || WeightType == GEO_MEEUS)
                    From->Y =
                        (int) From->Y + 5.0 * (From->Y -
                                               (int) From->Y) / 3.0;
                From->Y += From->Y > 0 ? -180 : 180;
                if (WeightType == GEO || WeightType == GEO_MEEUS)
                    From->Y =
                        (int) From->Y + 3.0 * (From->Y -
                                               (int) From->Y) / 5.0;
            } while ((From = From->Suc) != FirstNode);
            delaunay(Dimension);
            do
                From->Y = From->Zc;
            while ((From = From->Suc) != FirstNode);

            qsort(EdgeSet, Count, sizeof(Edge), compareFromTo);
            for (i = 0; i < Dimension; i++) {
                u = &p_array[i];
                e_start = e = u->entry_pt;
                do {
                    v = Other_point(e, u);
                    if (u->id < v->id)
                        Count2++;
                } while ((e = Next(e, u)) != e_start);
            }
            Count1 = Count;
            assert(EdgeSet =
                   (Edge *) realloc(EdgeSet,
                                    (Count1 + Count2) * sizeof(Edge)));
            for (i = 0; i < Dimension; i++) {
                u = &p_array[i];
                e_start = e = u->entry_pt;
                do {
                    v = Other_point(e, u);
                    if (u->id > v->id)
                        continue;
                    Key.From = From = &NodeSet[u->id];
                    Key.To = To = &NodeSet[v->id];
                    if (!bsearch
                        (&Key, EdgeSet, Count1, sizeof(Edge),
                         compareFromTo)) {
                        EdgeSet[Count].From = From;
                        EdgeSet[Count].To = To;
                        EdgeSet[Count].Cost =
                            FixedOrCommon(From, To) ? INT_MIN :
                            Distance(From,
                                     To) * Precision + From->Pi + To->Pi;
                        Count++;
                    }
                } while ((e = Next(e, u)) != e_start);
            }
            free_memory();
        }
    }
    qsort(EdgeSet, Count, sizeof(Edge), compareCost);

    /* Union-Find with path compression */
    N = FirstNode;
    do {
        N->Next = N;
        N->Size = 1;
    } while ((N = N->Suc) != FirstNode);
    for (i = 0; i < Count; i++) {
        for (F = EdgeSet[i].From; F != F->Next;)
            F = F->Next = F->Next->Next;
        for (T = EdgeSet[i].To; T != T->Next;)
            T = T->Next = T->Next->Next;
        if (F != T && F->Size + T->Size <= MaxClusterSize) {
            if (F->Size < T->Size) {
                F->Next = T;
                T->Size += F->Size;
            } else {
                T->Next = F;
                F->Size += T->Size;
            }
        }
    }
    free(EdgeSet);

    Count = 0;
    N = FirstNode;
    do {
        if (N->Next == N && N->Size > 3)
            N->Subproblem = ++Count;
    } while ((N = N->Suc) != FirstNode);
    do {
        for (T = N; T != T->Next;)
            T = T->Next = T->Next->Next;
        N->Subproblem = T->Subproblem;
    } while ((N = N->Suc) != FirstNode);
    return Count;
}
void CreateDelaunayCandidateSet()
{
    Node *From, *To;
    point *u, *v;
    edge *e_start, *e;
    int d, i, Count;

    if (TraceLevel >= 2)
        printff("Creating Delaunay candidate set ... ");
    if (Level == 0 && MaxCandidates == 0) {
        AddTourCandidates();
        From = FirstNode;
        do {
            if (!From->CandidateSet)
                eprintf("MAX_CANDIDATES = 0: No candidates");
        } while ((From = From->Suc) != FirstNode);
        if (TraceLevel >= 2)
            printff("done\n");
        return;
    }

    /* Find the Delaunay edges */
    delaunay(Dimension);

    /* Add the Delaunay edges to the candidate set */
    for (i = 0; i < Dimension; i++) {
        u = &p_array[i];
        From = &NodeSet[u->id];
        e_start = e = u->entry_pt;
        Count = 0;
        do {
            v = Other_point(e, u);
            if (u < v) {
                To = &NodeSet[v->id];
                d = D(From, To);
                AddCandidate(From, To, d, 1);
                AddCandidate(To, From, d, 1);
            }
        } while ((e = Next(e, u)) != e_start && ++Count < Dimension);
    }
    free_memory();
    if (Level == 0 &&
        (WeightType == GEO || WeightType == GEOM ||
         WeightType == GEO_MEEUS || WeightType == GEOM_MEEUS)) {
        if (TraceLevel >= 2)
            printff("done\n");
        From = FirstNode;
        while ((From = From->Suc) != FirstNode)
            if ((From->Y > 0) != (FirstNode->Y > 0))
                break;
        if (From != FirstNode) {
            /* Transform longitude (180 and -180 map to 0) */
            From = FirstNode;
            do {
                From->Zc = From->Y;
                if (WeightType == GEO || WeightType == GEO_MEEUS)
                    From->Y =
                        (int) From->Y + 5.0 * (From->Y -
                                               (int) From->Y) / 3.0;
                From->Y += From->Y > 0 ? -180 : 180;
                if (WeightType == GEO || WeightType == GEO_MEEUS)
                    From->Y =
                        (int) From->Y + 3.0 * (From->Y -
                                               (int) From->Y) / 5.0;
            } while ((From = From->Suc) != FirstNode);
            Level++;
            CreateDelaunayCandidateSet();
            Level--;
            From = FirstNode;
            do
                From->Y = From->Zc;
            while ((From = From->Suc) != FirstNode);
        }
    }
    if (Level == 0) {
        AddTourCandidates();
        /* Add quadrant neighbors if any node has less than two candidates. 
           That is, if it should happen that delaunay_edges fails. */
        From = FirstNode;
        do {
            if (From->CandidateSet == 0 ||
                From->CandidateSet[0].To == 0 ||
                From->CandidateSet[1].To == 0) {
                if (TraceLevel >= 2)
                    printff("*** Not complete ***\n");
                AddExtraCandidates(CoordType == THREED_COORDS ? 8 : 4,
                                   QUADRANT, 1);
                break;
            }
        } while ((From = From->Suc) != FirstNode);
        if (TraceLevel >= 2)
            printff("done\n");
    }
}
Example #4
0
/* 
 * The merge function is where most of the work actually gets done. It is
 * written as one (longish) function for speed.
 */
static void merge(edge * r_cw_l, point * s, edge * l_ccw_r, point * u,
                  edge ** l_tangent)
{
    edge *base, *l_cand, *r_cand;
    point *org_base, *dest_base;
    double u_l_c_o_b, v_l_c_o_b, u_l_c_d_b, v_l_c_d_b;
    double u_r_c_o_b, v_r_c_o_b, u_r_c_d_b, v_r_c_d_b;
    double c_p_l_cand, c_p_r_cand;
    double d_p_l_cand, d_p_r_cand;
    boolean above_l_cand, above_r_cand, above_next, above_prev;
    point *dest_l_cand, *dest_r_cand;
    double cot_l_cand = 0, cot_r_cand = 0;
    edge *l_lower, *r_lower;
    point *org_r_lower, *org_l_lower;

    /* Create first cross edge by joining lower common tangent */
    lower_tangent(r_cw_l, s, l_ccw_r, u, &l_lower, &org_l_lower, &r_lower,
                  &org_r_lower);
    base = join(l_lower, org_l_lower, r_lower, org_r_lower, side::right);
    org_base = org_l_lower;
    dest_base = org_r_lower;

    /* Need to return lower tangent. */
    *l_tangent = base;

    /* Main merge loop */
    do {
        /* Initialise l_cand and r_cand */
        l_cand = Next(base, org_base);
        r_cand = Prev(base, dest_base);
        dest_l_cand = Other_point(l_cand, org_base);
        dest_r_cand = Other_point(r_cand, dest_base);

        /* Vectors for above and "in_circle" tests. */
        Vector(dest_l_cand, org_base, u_l_c_o_b, v_l_c_o_b);
        Vector(dest_l_cand, dest_base, u_l_c_d_b, v_l_c_d_b);
        Vector(dest_r_cand, org_base, u_r_c_o_b, v_r_c_o_b);
        Vector(dest_r_cand, dest_base, u_r_c_d_b, v_r_c_d_b);

        /* Above tests. */
        c_p_l_cand =
            Cross_product_2v(u_l_c_o_b, v_l_c_o_b, u_l_c_d_b, v_l_c_d_b);
        c_p_r_cand =
            Cross_product_2v(u_r_c_o_b, v_r_c_o_b, u_r_c_d_b, v_r_c_d_b);
        above_l_cand = c_p_l_cand > 0.0;
        above_r_cand = c_p_r_cand > 0.0;
        if (!above_l_cand && !above_r_cand)
            break;              /* Finished. */

        /* Advance l_cand ccw, deleting the old l_cand edge, until the 
           "in_circle" test fails. */
        if (above_l_cand) {
            double u_n_o_b, v_n_o_b, u_n_d_b, v_n_d_b;
            double c_p_next, d_p_next, cot_next;
            edge *next;
            point *dest_next;

            d_p_l_cand =
                Dot_product_2v(u_l_c_o_b, v_l_c_o_b, u_l_c_d_b, v_l_c_d_b);
            cot_l_cand = d_p_l_cand / c_p_l_cand;

            do {
                next = Next(l_cand, org_base);
                dest_next = Other_point(next, org_base);
                Vector(dest_next, org_base, u_n_o_b, v_n_o_b);
                Vector(dest_next, dest_base, u_n_d_b, v_n_d_b);
                c_p_next =
                    Cross_product_2v(u_n_o_b, v_n_o_b, u_n_d_b, v_n_d_b);
                above_next = c_p_next > 0.0;

                if (!above_next)
                    break;      /* Finished. */

                d_p_next =
                    Dot_product_2v(u_n_o_b, v_n_o_b, u_n_d_b, v_n_d_b);
                cot_next = d_p_next / c_p_next;

                if (cot_next > cot_l_cand)
                    break;      /* Finished. */

                delete_edge(l_cand);
                l_cand = next;
                cot_l_cand = cot_next;

            } while (TRUE);
        }

        /* Now do the symmetrical for r_cand */
        if (above_r_cand) {
            double u_p_o_b, v_p_o_b, u_p_d_b, v_p_d_b;
            double c_p_prev, d_p_prev, cot_prev;
            edge *prev;
            point *dest_prev;

            d_p_r_cand =
                Dot_product_2v(u_r_c_o_b, v_r_c_o_b, u_r_c_d_b, v_r_c_d_b);
            cot_r_cand = d_p_r_cand / c_p_r_cand;

            do {
                prev = Prev(r_cand, dest_base);
                dest_prev = Other_point(prev, dest_base);
                Vector(dest_prev, org_base, u_p_o_b, v_p_o_b);
                Vector(dest_prev, dest_base, u_p_d_b, v_p_d_b);
                c_p_prev =
                    Cross_product_2v(u_p_o_b, v_p_o_b, u_p_d_b, v_p_d_b);
                above_prev = c_p_prev > 0.0;

                if (!above_prev)
                    break;      /* Finished. */

                d_p_prev =
                    Dot_product_2v(u_p_o_b, v_p_o_b, u_p_d_b, v_p_d_b);
                cot_prev = d_p_prev / c_p_prev;

                if (cot_prev > cot_r_cand)
                    break;      /* Finished. */

                delete_edge(r_cand);
                r_cand = prev;
                cot_r_cand = cot_prev;

            } while (TRUE);
        }

        /*
         * Now add a cross edge from base to either l_cand or r_cand. 
         * If both are valid choose on the basis of the in_circle test. 
         * Advance base and whichever candidate was chosen.
         */
        dest_l_cand = Other_point(l_cand, org_base);
        dest_r_cand = Other_point(r_cand, dest_base);
        if (!above_l_cand
            || (above_l_cand && above_r_cand && cot_r_cand < cot_l_cand)) {
            /* Connect to the right */
            base = join(base, org_base, r_cand, dest_r_cand, side::right);
            dest_base = dest_r_cand;
        } else {
            /* Connect to the left */
            base = join(l_cand, dest_l_cand, base, dest_base, side::right);
            org_base = dest_l_cand;
        }

    } while (TRUE);
}