1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
|
/***************************************************************
*
* MODULE: v.delaunay
*
* AUTHOR(S): Martin Pavlovsky (Google SoC 2008, Paul Kelly mentor)
* Based on "dct" by Geoff Leach, Department of Computer
* Science, RMIT.
*
* PURPOSE: Creates a Delaunay triangulation vector map
*
* COPYRIGHT: (C) RMIT 1993
* (C) 2008-2009 by the GRASS Development Team
*
* This program is free software under the GNU General
* Public License (>=v2). Read the file COPYING that
* comes with GRASS for details.
*
* The following notices apply to portions of the code originally
* derived from work by Geoff Leach of RMIT:
*
* Author: Geoff Leach, Department of Computer Science, RMIT.
* email: gl@cs.rmit.edu.au
*
* Date: 6/10/93
*
* Version 1.0
*
* Copyright (c) RMIT 1993. All rights reserved.
*
* License to copy and use this software purposes is granted provided
* that appropriate credit is given to both RMIT and the author.
*
* License is also granted to make and use derivative works provided
* that appropriate credit is given to both RMIT and the author.
*
* RMIT makes no representations concerning either the merchantability
* of this software or the suitability of this software for any particular
* purpose. It is provided "as is" without express or implied warranty
* of any kind.
*
* These notices must be retained in any copies of any part of this software.
*
**************************************************************/
#include <stddef.h>
#include "defs.h"
#include "data_types.h"
#include "memory.h"
#include "geometry.h"
#include "geom_primitives.h"
#include "edge.h"
static void find_lowest_cross_edge(struct edge *r_cw_l, struct vertex *s,
struct edge *l_ccw_r, struct vertex *u,
struct edge **l_lower,
struct vertex **org_l_lower,
struct edge **r_lower,
struct vertex **org_r_lower);
static void merge(struct edge *r_cw_l, struct vertex *s, struct edge *l_ccw_r,
struct vertex *u, struct edge **l_tangent);
void divide(unsigned int l, unsigned int r, struct edge **l_ccw,
struct edge **r_cw)
{
unsigned int n;
unsigned int split;
struct edge *l_ccw_l, *r_cw_l, *l_ccw_r, *r_cw_r, *l_tangent;
struct edge *a, *b, *c;
double c_p;
n = r - l + 1;
if (n == 2) {
/* Base case #1 - 2 sites in region. Construct an edge from
two sites in the region */
*l_ccw = *r_cw = create_edge(&(sites[l]), &(sites[r]));
}
else if (n == 3) {
/* Base case #2 - 3 sites. Construct a triangle or two edges */
a = create_edge(&(sites[l]), &(sites[l + 1]));
b = create_edge(&(sites[l + 1]), &(sites[r]));
splice(a, b, &(sites[l + 1]));
c_p = CROSS_PRODUCT_3P(&(sites[l]), &(sites[l + 1]), &(sites[r]));
if (c_p > 0.0) {
/* Create a triangle */
c = join(a, &(sites[l]), b, &(sites[r]), RIGHT);
*l_ccw = a;
*r_cw = b;
}
else if (c_p < 0.0) {
/* Create a triangle */
c = join(a, &(sites[l]), b, &(sites[r]), LEFT);
*l_ccw = c;
*r_cw = c;
}
else {
/* Cross product is zero. Sites are located on a line.
Triangle cannot be created */
*l_ccw = a;
*r_cw = b;
}
}
else if (n > 3) {
/* Recursive case. Continue splitting */
/* Splitting point */
split = (l + r) / 2;
/* Divide into two halves */
divide(l, split, &l_ccw_l, &r_cw_l);
divide(split + 1, r, &l_ccw_r, &r_cw_r);
/* Merge the two triangulations */
merge(r_cw_l, &(sites[split]), l_ccw_r, &(sites[split + 1]),
&l_tangent);
/* The lower tangent added by merge may have invalidated
l_ccw_l or r_cw_r. Update them if necessary. */
if (ORG(l_tangent) == &(sites[l]))
l_ccw_l = l_tangent;
if (DEST(l_tangent) == &(sites[r]))
r_cw_r = l_tangent;
/* Update leftmost ccw edge and rightmost cw edge */
*l_ccw = l_ccw_l;
*r_cw = r_cw_r;
}
}
/*
* Find the lowest cross edge of the two triangulations
*/
static void find_lowest_cross_edge(struct edge *r_cw_l, struct vertex *s,
struct edge *l_ccw_r, struct vertex *u,
struct edge **l_lower,
struct vertex **org_l_lower,
struct edge **r_lower,
struct vertex **org_r_lower)
{
struct edge *l, *r;
struct vertex *o_l, *o_r, *d_l, *d_r;
unsigned char ready;
l = r_cw_l;
r = l_ccw_r;
o_l = s;
d_l = OTHER_VERTEX(l, s);
o_r = u;
d_r = OTHER_VERTEX(r, u);
ready = FALSE;
while (ready == FALSE)
/* left_of */
if (LEFT_OF(o_l, d_l, o_r)) {
l = PREV(l, d_l);
o_l = d_l;
d_l = OTHER_VERTEX(l, o_l);
/* right_of */
}
else if (RIGHT_OF(o_r, d_r, o_l)) {
r = NEXT(r, d_r);
o_r = d_r;
d_r = OTHER_VERTEX(r, o_r);
}
else
ready = TRUE;
*l_lower = l;
*r_lower = r;
*org_l_lower = o_l;
*org_r_lower = o_r;
}
/*
* The most time-expensive function, most of the work gets done here.
*/
static void merge(struct edge *r_cw_l, struct vertex *s, struct edge *l_ccw_r,
struct vertex *u, struct edge **l_tangent)
{
struct edge *base, *l_cand, *r_cand;
struct vertex *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;
/* cross product */
double c_p_l_cand, c_p_r_cand;
/* dot product */
double d_p_l_cand, d_p_r_cand;
unsigned char above_l_cand, above_r_cand, above_next, above_prev;
struct vertex *dest_l_cand, *dest_r_cand;
double cot_l_cand, cot_r_cand;
struct edge *l_lower, *r_lower;
struct vertex *org_r_lower, *org_l_lower;
/* Create first cross edge by joining lower common tangent */
find_lowest_cross_edge(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, RIGHT);
org_base = org_l_lower;
dest_base = org_r_lower;
/* Need to return lower tangent. */
*l_tangent = base;
/* The merge loop */
while (TRUE) {
/* Initialise edges l_cand and r_cand */
l_cand = NEXT(base, org_base);
r_cand = PREV(base, dest_base);
dest_l_cand = OTHER_VERTEX(l_cand, org_base);
dest_r_cand = OTHER_VERTEX(r_cand, dest_base);
/* Vectors used for above and modified IN_CIRCLE tests
u/v left/right candidate origin/destination */
CREATE_VECTOR(dest_l_cand, org_base, u_l_c_o_b, v_l_c_o_b);
CREATE_VECTOR(dest_l_cand, dest_base, u_l_c_d_b, v_l_c_d_b);
CREATE_VECTOR(dest_r_cand, org_base, u_r_c_o_b, v_r_c_o_b);
CREATE_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;
/* Terminate merge loop. No valid sites left in L or R.
The top-most cross-edge have already been added. */
if (!above_l_cand && !above_r_cand)
break;
/* Move to next l_cand ccw, delete the old l_cand edge, until the
in_circle test gets invalid. */
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;
struct edge *next;
struct vertex *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;
while (TRUE) {
next = NEXT(l_cand, org_base);
dest_next = OTHER_VERTEX(next, org_base);
CREATE_VECTOR(dest_next, org_base, u_n_o_b, v_n_o_b);
CREATE_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; /* Terminate loop. */
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; /* Terminate loop. */
delete_edge(l_cand);
l_cand = next;
cot_l_cand = cot_next;
}
}
/* Essentially the same done for r_cand symmetrically. */
/* Move to prev r_cand cw, delete the old r_cand edge, until the
in_circle test gets invalid. */
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;
struct edge *prev;
struct vertex *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;
while (TRUE) {
prev = PREV(r_cand, dest_base);
dest_prev = OTHER_VERTEX(prev, dest_base);
CREATE_VECTOR(dest_prev, org_base, u_p_o_b, v_p_o_b);
CREATE_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; /* Terminate. */
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; /* Terminate. */
delete_edge(r_cand);
r_cand = prev;
cot_r_cand = cot_prev;
}
}
/*
Add a successive L-R cross edge from base
If both candidates are valid, choose the one with smallest
circumcircle. Set the base to the new L-R cross edge.
*/
dest_l_cand = OTHER_VERTEX(l_cand, org_base);
dest_r_cand = OTHER_VERTEX(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, RIGHT);
dest_base = dest_r_cand;
}
else {
/* Connect to the left */
base = join(l_cand, dest_l_cand, base, dest_base, RIGHT);
org_base = dest_l_cand;
}
}
}
|