/* * 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"); } }
/* * 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); }