| 1 | #ifndef lint | 
| 2 | static const char       RCSid[] = "$Id: srcsupp.c,v 2.24 2022/08/04 22:43:46 greg Exp $"; | 
| 3 | #endif | 
| 4 | /* | 
| 5 | *  Support routines for source objects and materials | 
| 6 | * | 
| 7 | *  External symbols declared in source.h | 
| 8 | */ | 
| 9 |  | 
| 10 | #include "copyright.h" | 
| 11 |  | 
| 12 | #include  "ray.h" | 
| 13 |  | 
| 14 | #include  "otypes.h" | 
| 15 |  | 
| 16 | #include  "source.h" | 
| 17 |  | 
| 18 | #include  "cone.h" | 
| 19 |  | 
| 20 | #include  "face.h" | 
| 21 |  | 
| 22 | #define SRCINC          32              /* realloc increment for array */ | 
| 23 |  | 
| 24 | SRCREC  *source = NULL;                 /* our list of sources */ | 
| 25 | int  nsources = 0;                      /* the number of sources */ | 
| 26 |  | 
| 27 | SRCFUNC  sfun[NUMOTYPE];                /* source dispatch table */ | 
| 28 |  | 
| 29 |  | 
| 30 | void | 
| 31 | initstypes(void)                        /* initialize source dispatch table */ | 
| 32 | { | 
| 33 | extern VSMATERIAL  mirror_vs, direct1_vs, direct2_vs; | 
| 34 | static SOBJECT  fsobj = {fsetsrc, flatpart, fgetplaneq, fgetmaxdisk}; | 
| 35 | static SOBJECT  ssobj = {ssetsrc, nopart}; | 
| 36 | static SOBJECT  sphsobj = {sphsetsrc, nopart}; | 
| 37 | static SOBJECT  cylsobj = {cylsetsrc, cylpart}; | 
| 38 | static SOBJECT  rsobj = {rsetsrc, flatpart, rgetplaneq, rgetmaxdisk}; | 
| 39 |  | 
| 40 | sfun[MAT_MIRROR].mf = &mirror_vs; | 
| 41 | sfun[MAT_DIRECT1].mf = &direct1_vs; | 
| 42 | sfun[MAT_DIRECT2].mf = &direct2_vs; | 
| 43 | sfun[OBJ_FACE].of = &fsobj; | 
| 44 | sfun[OBJ_SOURCE].of = &ssobj; | 
| 45 | sfun[OBJ_SPHERE].of = &sphsobj; | 
| 46 | sfun[OBJ_CYLINDER].of = &cylsobj; | 
| 47 | sfun[OBJ_RING].of = &rsobj; | 
| 48 | } | 
| 49 |  | 
| 50 |  | 
| 51 | int | 
| 52 | newsource(void)                 /* allocate new source in our array */ | 
| 53 | { | 
| 54 | if (nsources == 0) | 
| 55 | source = (SRCREC *)malloc(SRCINC*sizeof(SRCREC)); | 
| 56 | else if (nsources%SRCINC == 0) | 
| 57 | source = (SRCREC *)realloc((void *)source, | 
| 58 | (unsigned)(nsources+SRCINC)*sizeof(SRCREC)); | 
| 59 | if (source == NULL) | 
| 60 | return(-1); | 
| 61 | source[nsources].sflags = 0; | 
| 62 | source[nsources].nhits = 1; | 
| 63 | source[nsources].ntests = 2;    /* initial hit probability = 50% */ | 
| 64 | #if SHADCACHE | 
| 65 | source[nsources].obscache = NULL; | 
| 66 | #endif | 
| 67 | return(nsources++); | 
| 68 | } | 
| 69 |  | 
| 70 |  | 
| 71 | void | 
| 72 | setflatss(                              /* set sampling for a flat source */ | 
| 73 | SRCREC  *src | 
| 74 | ) | 
| 75 | { | 
| 76 | double  mult; | 
| 77 | int  i; | 
| 78 |  | 
| 79 | getperpendicular(src->ss[SU], src->snorm, rand_samp); | 
| 80 | mult = .5 * sqrt( src->ss2 ); | 
| 81 | for (i = 0; i < 3; i++) | 
| 82 | src->ss[SU][i] *= mult; | 
| 83 | fcross(src->ss[SV], src->snorm, src->ss[SU]); | 
| 84 | } | 
| 85 |  | 
| 86 |  | 
| 87 | void | 
| 88 | fsetsrc(                        /* set a face as a source */ | 
| 89 | SRCREC  *src, | 
| 90 | OBJREC  *so | 
| 91 | ) | 
| 92 | { | 
| 93 | FACE  *f; | 
| 94 | int  i, j; | 
| 95 | double  d; | 
| 96 |  | 
| 97 | src->sa.success = 2*AIMREQT-1;          /* bitch on second failure */ | 
| 98 | src->so = so; | 
| 99 | /* get the face */ | 
| 100 | f = getface(so); | 
| 101 | if (f->area == 0.) | 
| 102 | objerror(so, USER, "zero source area"); | 
| 103 | /* find the center */ | 
| 104 | for (j = 0; j < 3; j++) { | 
| 105 | src->sloc[j] = 0.0; | 
| 106 | for (i = 0; i < f->nv; i++) | 
| 107 | src->sloc[j] += VERTEX(f,i)[j]; | 
| 108 | src->sloc[j] /= (double)f->nv; | 
| 109 | } | 
| 110 | if (!inface(src->sloc, f)) | 
| 111 | objerror(so, USER, "cannot hit source center"); | 
| 112 | src->sflags |= SFLAT; | 
| 113 | VCOPY(src->snorm, f->norm); | 
| 114 | src->ss2 = f->area; | 
| 115 | /* find maximum radius */ | 
| 116 | src->srad = 0.; | 
| 117 | for (i = 0; i < f->nv; i++) { | 
| 118 | d = dist2(VERTEX(f,i), src->sloc); | 
| 119 | if (d > src->srad) | 
| 120 | src->srad = d; | 
| 121 | } | 
| 122 | src->srad = sqrt(src->srad); | 
| 123 | /* compute size vectors */ | 
| 124 | if (f->nv == 4) {                               /* parallelogram case */ | 
| 125 | for (j = 0; j < 3; j++) { | 
| 126 | src->ss[SU][j] = .5*(VERTEX(f,1)[j]-VERTEX(f,0)[j]); | 
| 127 | src->ss[SV][j] = .5*(VERTEX(f,3)[j]-VERTEX(f,0)[j]); | 
| 128 | } | 
| 129 | } else if (f->nv == 3) {                        /* triangle case */ | 
| 130 | int     near0 = 2; | 
| 131 | double  dmin = dist2line(src->sloc, VERTEX(f,2), VERTEX(f,0)); | 
| 132 | for (i = 0; i < 2; i++) { | 
| 133 | double  d2 = dist2line(src->sloc, VERTEX(f,i), VERTEX(f,i+1)); | 
| 134 | if (d2 >= dmin) | 
| 135 | continue; | 
| 136 | near0 = i; | 
| 137 | dmin = d2;                      /* radius = min distance */ | 
| 138 | } | 
| 139 | if (dmin < .08*f->area) | 
| 140 | objerror(so, WARNING, "triangular source with poor aspect"); | 
| 141 | i = (near0 + 1) % 3; | 
| 142 | for (j = 0; j < 3; j++) | 
| 143 | src->ss[SU][j] = VERTEX(f,i)[j] - VERTEX(f,near0)[j]; | 
| 144 | normalize(src->ss[SU]); | 
| 145 | dmin = sqrt(dmin); | 
| 146 | for (j = 0; j < 3; j++) | 
| 147 | src->ss[SU][j] *= dmin; | 
| 148 | fcross(src->ss[SV], f->norm, src->ss[SU]); | 
| 149 | } else | 
| 150 | setflatss(src);                         /* hope for convex! */ | 
| 151 | } | 
| 152 |  | 
| 153 |  | 
| 154 | void | 
| 155 | ssetsrc(                        /* set a source as a source */ | 
| 156 | SRCREC  *src, | 
| 157 | OBJREC  *so | 
| 158 | ) | 
| 159 | { | 
| 160 | double  theta; | 
| 161 |  | 
| 162 | src->sa.success = 2*AIMREQT-1;          /* bitch on second failure */ | 
| 163 | src->so = so; | 
| 164 | if (so->oargs.nfargs != 4) | 
| 165 | objerror(so, USER, "bad arguments"); | 
| 166 | src->sflags |= (SDISTANT|SCIR); | 
| 167 | VCOPY(src->sloc, so->oargs.farg); | 
| 168 | if (normalize(src->sloc) == 0.0) | 
| 169 | objerror(so, USER, "zero direction"); | 
| 170 | theta = PI/180.0/2.0 * so->oargs.farg[3]; | 
| 171 | if (theta <= FTINY) | 
| 172 | objerror(so, USER, "zero size"); | 
| 173 | src->ss2 = 2.0*PI * (1.0 - cos(theta)); | 
| 174 | /* the following is approximate */ | 
| 175 | src->srad = sqrt(src->ss2/PI); | 
| 176 | VCOPY(src->snorm, src->sloc); | 
| 177 | setflatss(src);                 /* hey, whatever works */ | 
| 178 | src->ss[SW][0] = src->ss[SW][1] = src->ss[SW][2] = 0.0; | 
| 179 | } | 
| 180 |  | 
| 181 |  | 
| 182 | void | 
| 183 | sphsetsrc(                      /* set a sphere as a source */ | 
| 184 | SRCREC  *src, | 
| 185 | OBJREC  *so | 
| 186 | ) | 
| 187 | { | 
| 188 | int  i; | 
| 189 |  | 
| 190 | src->sa.success = 2*AIMREQT-1;          /* bitch on second failure */ | 
| 191 | src->so = so; | 
| 192 | if (so->oargs.nfargs != 4) | 
| 193 | objerror(so, USER, "bad # arguments"); | 
| 194 | if (so->oargs.farg[3] <= FTINY) | 
| 195 | objerror(so, USER, "illegal source radius"); | 
| 196 | src->sflags |= SCIR; | 
| 197 | VCOPY(src->sloc, so->oargs.farg); | 
| 198 | src->srad = so->oargs.farg[3]; | 
| 199 | src->ss2 = PI * src->srad * src->srad; | 
| 200 | memset(src->ss, 0, sizeof(src->ss)); | 
| 201 | for (i = 0; i < 3; i++) | 
| 202 | src->ss[i][i] = 0.7236 * so->oargs.farg[3]; | 
| 203 | } | 
| 204 |  | 
| 205 |  | 
| 206 | void | 
| 207 | rsetsrc(                        /* set a ring (disk) as a source */ | 
| 208 | SRCREC  *src, | 
| 209 | OBJREC  *so | 
| 210 | ) | 
| 211 | { | 
| 212 | CONE  *co; | 
| 213 |  | 
| 214 | src->sa.success = 2*AIMREQT-1;          /* bitch on second failure */ | 
| 215 | src->so = so; | 
| 216 | /* get the ring */ | 
| 217 | co = getcone(so, 0); | 
| 218 | if (co == NULL) | 
| 219 | objerror(so, USER, "illegal source"); | 
| 220 | if (CO_R1(co) <= FTINY) | 
| 221 | objerror(so, USER, "illegal source radius"); | 
| 222 | VCOPY(src->sloc, CO_P0(co)); | 
| 223 | if (CO_R0(co) > 0.0) | 
| 224 | objerror(so, USER, "cannot hit source center"); | 
| 225 | src->sflags |= (SFLAT|SCIR); | 
| 226 | VCOPY(src->snorm, co->ad); | 
| 227 | src->srad = CO_R1(co); | 
| 228 | src->ss2 = PI * src->srad * src->srad; | 
| 229 | setflatss(src); | 
| 230 | } | 
| 231 |  | 
| 232 |  | 
| 233 | void | 
| 234 | cylsetsrc(                      /* set a cylinder as a source */ | 
| 235 | SRCREC  *src, | 
| 236 | OBJREC  *so | 
| 237 | ) | 
| 238 | { | 
| 239 | CONE  *co; | 
| 240 | int  i; | 
| 241 |  | 
| 242 | src->sa.success = 4*AIMREQT-1;          /* bitch on fourth failure */ | 
| 243 | src->so = so; | 
| 244 | /* get the cylinder */ | 
| 245 | co = getcone(so, 0); | 
| 246 | if (co == NULL) | 
| 247 | objerror(so, USER, "illegal source"); | 
| 248 | if (CO_R0(co) <= FTINY) | 
| 249 | objerror(so, USER, "illegal source radius"); | 
| 250 | if (CO_R0(co) > .2*co->al)              /* heuristic constraint */ | 
| 251 | objerror(so, WARNING, "source aspect too small"); | 
| 252 | src->sflags |= SCYL; | 
| 253 | for (i = 0; i < 3; i++) | 
| 254 | src->sloc[i] = .5 * (CO_P1(co)[i] + CO_P0(co)[i]); | 
| 255 | src->srad = .5*co->al; | 
| 256 | src->ss2 = 2.*CO_R0(co)*co->al; | 
| 257 | /* set sampling vectors */ | 
| 258 | for (i = 0; i < 3; i++) | 
| 259 | src->ss[SU][i] = .5 * co->al * co->ad[i]; | 
| 260 | getperpendicular(src->ss[SW], co->ad, rand_samp); | 
| 261 | for (i = 0; i < 3; i++) | 
| 262 | src->ss[SW][i] *= .8559 * CO_R0(co); | 
| 263 | fcross(src->ss[SV], src->ss[SW], co->ad); | 
| 264 | } | 
| 265 |  | 
| 266 |  | 
| 267 | SPOT * | 
| 268 | makespot(                       /* make a spotlight */ | 
| 269 | OBJREC  *m | 
| 270 | ) | 
| 271 | { | 
| 272 | SPOT  *ns; | 
| 273 |  | 
| 274 | if ((ns = (SPOT *)m->os) != NULL) | 
| 275 | return(ns); | 
| 276 | if ((ns = (SPOT *)malloc(sizeof(SPOT))) == NULL) | 
| 277 | return(NULL); | 
| 278 | if (m->oargs.farg[3] <= FTINY) | 
| 279 | objerror(m, USER, "zero angle"); | 
| 280 | ns->siz = 2.0*PI * (1.0 - cos(PI/180.0/2.0 * m->oargs.farg[3])); | 
| 281 | VCOPY(ns->aim, m->oargs.farg+4); | 
| 282 | if ((ns->flen = normalize(ns->aim)) == 0.0) | 
| 283 | objerror(m, USER, "zero focus vector"); | 
| 284 | m->os = (char *)ns; | 
| 285 | return(ns); | 
| 286 | } | 
| 287 |  | 
| 288 |  | 
| 289 | int | 
| 290 | spotout(                        /* check if we're outside spot region */ | 
| 291 | RAY  *r, | 
| 292 | SPOT  *s | 
| 293 | ) | 
| 294 | { | 
| 295 | double  d; | 
| 296 | FVECT  vd; | 
| 297 |  | 
| 298 | if (s == NULL) | 
| 299 | return(0); | 
| 300 | if (s->flen < -FTINY) {         /* distant source */ | 
| 301 | vd[0] = s->aim[0] - r->rorg[0]; | 
| 302 | vd[1] = s->aim[1] - r->rorg[1]; | 
| 303 | vd[2] = s->aim[2] - r->rorg[2]; | 
| 304 | d = DOT(r->rdir,vd); | 
| 305 | /*                      wrong side? | 
| 306 | if (d <= FTINY) | 
| 307 | return(1);      */ | 
| 308 | d = DOT(vd,vd) - d*d; | 
| 309 | if (PI*d > s->siz) | 
| 310 | return(1);      /* out */ | 
| 311 | return(0);      /* OK */ | 
| 312 | } | 
| 313 | /* local source */ | 
| 314 | if (s->siz < 2.0*PI * (1.0 + DOT(s->aim,r->rdir))) | 
| 315 | return(1);      /* out */ | 
| 316 | return(0);      /* OK */ | 
| 317 | } | 
| 318 |  | 
| 319 |  | 
| 320 | double | 
| 321 | fgetmaxdisk(            /* get center and squared radius of face */ | 
| 322 | FVECT  ocent, | 
| 323 | OBJREC  *op | 
| 324 | ) | 
| 325 | { | 
| 326 | double  maxrad2; | 
| 327 | double  d; | 
| 328 | int  i, j; | 
| 329 | FACE  *f; | 
| 330 |  | 
| 331 | f = getface(op); | 
| 332 | if (f->area == 0.) | 
| 333 | return(0.); | 
| 334 | for (i = 0; i < 3; i++) { | 
| 335 | ocent[i] = 0.; | 
| 336 | for (j = 0; j < f->nv; j++) | 
| 337 | ocent[i] += VERTEX(f,j)[i]; | 
| 338 | ocent[i] /= (double)f->nv; | 
| 339 | } | 
| 340 | d = DOT(ocent,f->norm); | 
| 341 | for (i = 0; i < 3; i++) | 
| 342 | ocent[i] += (f->offset - d)*f->norm[i]; | 
| 343 | maxrad2 = 0.; | 
| 344 | for (j = 0; j < f->nv; j++) { | 
| 345 | d = dist2(VERTEX(f,j), ocent); | 
| 346 | if (d > maxrad2) | 
| 347 | maxrad2 = d; | 
| 348 | } | 
| 349 | return(maxrad2); | 
| 350 | } | 
| 351 |  | 
| 352 |  | 
| 353 | double | 
| 354 | rgetmaxdisk(            /* get center and squared radius of ring */ | 
| 355 | FVECT  ocent, | 
| 356 | OBJREC  *op | 
| 357 | ) | 
| 358 | { | 
| 359 | CONE  *co; | 
| 360 |  | 
| 361 | co = getcone(op, 0); | 
| 362 | if (co == NULL) | 
| 363 | return(0.); | 
| 364 | VCOPY(ocent, CO_P0(co)); | 
| 365 | return(CO_R1(co)*CO_R1(co)); | 
| 366 | } | 
| 367 |  | 
| 368 |  | 
| 369 | double | 
| 370 | fgetplaneq(                     /* get plane equation for face */ | 
| 371 | FVECT  nvec, | 
| 372 | OBJREC  *op | 
| 373 | ) | 
| 374 | { | 
| 375 | FACE  *fo; | 
| 376 |  | 
| 377 | fo = getface(op); | 
| 378 | VCOPY(nvec, fo->norm); | 
| 379 | return(fo->offset); | 
| 380 | } | 
| 381 |  | 
| 382 |  | 
| 383 | double | 
| 384 | rgetplaneq(                     /* get plane equation for ring */ | 
| 385 | FVECT  nvec, | 
| 386 | OBJREC  *op | 
| 387 | ) | 
| 388 | { | 
| 389 | CONE  *co; | 
| 390 |  | 
| 391 | co = getcone(op, 0); | 
| 392 | if (co == NULL) { | 
| 393 | memset(nvec, 0, sizeof(FVECT)); | 
| 394 | return(0.); | 
| 395 | } | 
| 396 | VCOPY(nvec, co->ad); | 
| 397 | return(DOT(nvec, CO_P0(co))); | 
| 398 | } | 
| 399 |  | 
| 400 |  | 
| 401 | int | 
| 402 | commonspot(             /* set sp1 to intersection of sp1 and sp2 */ | 
| 403 | SPOT  *sp1, | 
| 404 | SPOT  *sp2, | 
| 405 | FVECT  org | 
| 406 | ) | 
| 407 | { | 
| 408 | FVECT  cent; | 
| 409 | double  rad2, cos1, cos2; | 
| 410 |  | 
| 411 | cos1 = 1. - sp1->siz/(2.*PI); | 
| 412 | cos2 = 1. - sp2->siz/(2.*PI); | 
| 413 | if (sp2->siz >= 2.*PI-FTINY)            /* BIG, just check overlap */ | 
| 414 | return(DOT(sp1->aim,sp2->aim) >= cos1*cos2 - | 
| 415 | sqrt((1.-cos1*cos1)*(1.-cos2*cos2))); | 
| 416 | /* compute and check disks */ | 
| 417 | rad2 = intercircle(cent, sp1->aim, sp2->aim, | 
| 418 | 1./(cos1*cos1) - 1.,  1./(cos2*cos2) - 1.); | 
| 419 | if (rad2 <= FTINY || normalize(cent) == 0.) | 
| 420 | return(0); | 
| 421 | VCOPY(sp1->aim, cent); | 
| 422 | sp1->siz = 2.*PI*(1. - 1./sqrt(1.+rad2)); | 
| 423 | return(1); | 
| 424 | } | 
| 425 |  | 
| 426 |  | 
| 427 | int | 
| 428 | commonbeam(             /* set sp1 to intersection of sp1 and sp2 */ | 
| 429 | SPOT  *sp1, | 
| 430 | SPOT  *sp2, | 
| 431 | FVECT  dir | 
| 432 | ) | 
| 433 | { | 
| 434 | FVECT  cent, c1, c2; | 
| 435 | double  rad2, d; | 
| 436 | /* move centers to common plane */ | 
| 437 | d = DOT(sp1->aim, dir); | 
| 438 | VSUM(c1, sp1->aim, dir, -d); | 
| 439 | d = DOT(sp2->aim, dir); | 
| 440 | VSUM(c2, sp2->aim, dir, -d); | 
| 441 | /* compute overlap */ | 
| 442 | rad2 = intercircle(cent, c1, c2, sp1->siz/PI, sp2->siz/PI); | 
| 443 | if (rad2 <= FTINY) | 
| 444 | return(0); | 
| 445 | VCOPY(sp1->aim, cent); | 
| 446 | sp1->siz = PI*rad2; | 
| 447 | return(1); | 
| 448 | } | 
| 449 |  | 
| 450 |  | 
| 451 | int | 
| 452 | checkspot(                      /* check spotlight for behind source */ | 
| 453 | SPOT  *sp,      /* spotlight */ | 
| 454 | FVECT  nrm      /* source surface normal */ | 
| 455 | ) | 
| 456 | { | 
| 457 | double  d, d1; | 
| 458 |  | 
| 459 | d = DOT(sp->aim, nrm); | 
| 460 | if (d > FTINY)                  /* center in front? */ | 
| 461 | return(1); | 
| 462 | /* else check horizon */ | 
| 463 | d1 = 1. - sp->siz/(2.*PI); | 
| 464 | return(1.-FTINY-d*d < d1*d1); | 
| 465 | } | 
| 466 |  | 
| 467 |  | 
| 468 | double | 
| 469 | spotdisk(               /* intersect spot with object op */ | 
| 470 | FVECT  oc, | 
| 471 | OBJREC  *op, | 
| 472 | SPOT  *sp, | 
| 473 | FVECT  pos | 
| 474 | ) | 
| 475 | { | 
| 476 | FVECT  onorm; | 
| 477 | double  offs, d, dist; | 
| 478 |  | 
| 479 | offs = getplaneq(onorm, op); | 
| 480 | d = -DOT(onorm, sp->aim); | 
| 481 | if (d >= -FTINY && d <= FTINY) | 
| 482 | return(0.); | 
| 483 | dist = (DOT(pos, onorm) - offs)/d; | 
| 484 | if (dist < 0.) | 
| 485 | return(0.); | 
| 486 | VSUM(oc, pos, sp->aim, dist); | 
| 487 | return(sp->siz*dist*dist/PI/(d*d)); | 
| 488 | } | 
| 489 |  | 
| 490 |  | 
| 491 | double | 
| 492 | beamdisk(               /* intersect beam with object op */ | 
| 493 | FVECT  oc, | 
| 494 | OBJREC  *op, | 
| 495 | SPOT  *sp, | 
| 496 | FVECT  dir | 
| 497 | ) | 
| 498 | { | 
| 499 | FVECT  onorm; | 
| 500 | double  offs, d, dist; | 
| 501 |  | 
| 502 | offs = getplaneq(onorm, op); | 
| 503 | d = -DOT(onorm, dir); | 
| 504 | if (d >= -FTINY && d <= FTINY) | 
| 505 | return(0.); | 
| 506 | dist = (DOT(sp->aim, onorm) - offs)/d; | 
| 507 | VSUM(oc, sp->aim, dir, dist); | 
| 508 | return(sp->siz/PI/(d*d)); | 
| 509 | } | 
| 510 |  | 
| 511 |  | 
| 512 | double | 
| 513 | intercircle(                            /* intersect two circles */ | 
| 514 | FVECT  cc,              /* midpoint (return value) */ | 
| 515 | FVECT  c1,              /* circle centers */ | 
| 516 | FVECT  c2, | 
| 517 | double  r1s,            /* radii squared */ | 
| 518 | double  r2s | 
| 519 | ) | 
| 520 | { | 
| 521 | double  a2, d2, l; | 
| 522 | FVECT  disp; | 
| 523 |  | 
| 524 | VSUB(disp, c2, c1); | 
| 525 | d2 = DOT(disp,disp); | 
| 526 | /* circle within overlap? */ | 
| 527 | if (r1s < r2s) { | 
| 528 | if (r2s >= r1s + d2) { | 
| 529 | VCOPY(cc, c1); | 
| 530 | return(r1s); | 
| 531 | } | 
| 532 | } else { | 
| 533 | if (r1s >= r2s + d2) { | 
| 534 | VCOPY(cc, c2); | 
| 535 | return(r2s); | 
| 536 | } | 
| 537 | } | 
| 538 | a2 = .25*(2.*(r1s+r2s) - d2 - (r2s-r1s)*(r2s-r1s)/d2); | 
| 539 | /* no overlap? */ | 
| 540 | if (a2 <= 0.) | 
| 541 | return(0.); | 
| 542 | /* overlap, compute center */ | 
| 543 | l = sqrt((r1s - a2)/d2); | 
| 544 | VSUM(cc, c1, disp, l); | 
| 545 | return(a2); | 
| 546 | } |