source: ThirdParty/mpqc_open/src/lib/chemistry/molecule/molshape.cc@ 7516f6

Action_Thermostats Adding_MD_integration_tests Adding_StructOpt_integration_tests AutomationFragmentation_failures Candidate_v1.6.1 ChemicalSpaceEvaluator Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Exclude_Hydrogens_annealWithBondGraph Fix_Verbose_Codepatterns ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion Gui_displays_atomic_force_velocity JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool PythonUI_with_named_parameters Recreated_GuiChecks StoppableMakroAction TremoloParser_IncreasedPrecision
Last change on this file since 7516f6 was 860145, checked in by Frederik Heber <heber@…>, 8 years ago

Merge commit '0b990dfaa8c6007a996d030163a25f7f5fc8a7e7' as 'ThirdParty/mpqc_open'

  • Property mode set to 100644
File size: 40.3 KB
Line 
1//
2// molshape.cc
3//
4// Copyright (C) 1996 Limit Point Systems, Inc.
5//
6// Author: Curtis Janssen <cljanss@limitpt.com>
7// Maintainer: LPS
8//
9// This file is part of the SC Toolkit.
10//
11// The SC Toolkit is free software; you can redistribute it and/or modify
12// it under the terms of the GNU Library General Public License as published by
13// the Free Software Foundation; either version 2, or (at your option)
14// any later version.
15//
16// The SC Toolkit is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU Library General Public License for more details.
20//
21// You should have received a copy of the GNU Library General Public License
22// along with the SC Toolkit; see the file COPYING.LIB. If not, write to
23// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24//
25// The U.S. Government is granted a limited license as per AL 91-7.
26//
27
28#ifdef __GNUC__
29#pragma implementation
30#endif
31
32#include <stdio.h>
33#include <util/misc/math.h>
34#include <vector>
35
36#include <util/class/scexception.h>
37#include <chemistry/molecule/molshape.h>
38#include <chemistry/molecule/molecule.h>
39#include <math/scmat/matrix3.h>
40
41using namespace std;
42using namespace sc;
43
44////////////////////////////////////////////////////////////////////////
45// VDWShape
46
47static ClassDesc VDWShape_cd(
48 typeid(VDWShape),"VDWShape",1,"public UnionShape",
49 0, create<VDWShape>, 0);
50
51VDWShape::VDWShape(const Ref<Molecule>&mol)
52{
53 initialize(mol);
54}
55
56VDWShape::VDWShape(const Ref<KeyVal>&keyval)
57{
58 Ref<Molecule> mol; mol << keyval->describedclassvalue("molecule");
59 atominfo_ << keyval->describedclassvalue("atominfo");
60 initialize(mol);
61}
62
63void
64VDWShape::initialize(const Ref<Molecule>&mol)
65{
66 Ref<AtomInfo> a;
67 if (atominfo_.null()) a = mol->atominfo();
68 else a = atominfo_;
69
70 _shapes.clear();
71 for (int i=0; i<mol->natom(); i++) {
72 SCVector3 r;
73 for (int j=0; j<3; j++) r[j] = mol->r(i,j);
74 add_shape(
75 new SphereShape(r,a->vdw_radius(mol->Z(i)))
76 );
77 }
78}
79
80VDWShape::~VDWShape()
81{
82}
83
84////////////////////////////////////////////////////////////////////////
85// static functions for DiscreteConnollyShape and ConnollyShape
86
87static double
88find_atom_size(const Ref<AtomInfo>& a, int Z)
89{
90 return a->vdw_radius(Z);
91}
92
93////////////////////////////////////////////////////////////////////////
94// DiscreteConnollyShape
95
96static ClassDesc DiscreteConnollyShape_cd(
97 typeid(DiscreteConnollyShape),"DiscreteConnollyShape",1,"public UnionShape",
98 0, create<DiscreteConnollyShape>, 0);
99
100DiscreteConnollyShape::DiscreteConnollyShape(const Ref<KeyVal>&keyval)
101{
102 Ref<Molecule> mol; mol << keyval->describedclassvalue("molecule");
103 double probe_radius = keyval->doublevalue("probe_radius");
104 if (keyval->error() != KeyVal::OK) {
105 probe_radius = 2.6456173;
106 }
107 radius_scale_factor_ = keyval->doublevalue("radius_scale_factor");
108 if (keyval->error() != KeyVal::OK) {
109 radius_scale_factor_ = 1.2;
110 }
111 atominfo_ << keyval->describedclassvalue("atominfo");
112 initialize(mol,probe_radius);
113}
114
115void
116DiscreteConnollyShape::initialize(const Ref<Molecule>&mol,double probe_radius)
117{
118 _shapes.clear();
119 std::vector<Ref<SphereShape> > spheres(0);
120
121 Ref<AtomInfo> a;
122 if (atominfo_.null()) a = mol->atominfo();
123 else a = atominfo_;
124
125 int i;
126 for (i=0; i<mol->natom(); i++) {
127 SCVector3 r(mol->r(i));
128 Ref<SphereShape>
129 sphere(
130 new SphereShape(r,radius_scale_factor_*find_atom_size(a,
131 mol->Z(i)))
132 );
133 add_shape(sphere.pointer());
134 spheres.push_back(sphere);
135 }
136
137 ////////////////////// Leave out the other shapes
138 //return;
139
140 for (i=0; i<spheres.size(); i++) {
141 for (int j=0; j<i; j++) {
142 Ref<Shape> th =
143 UncappedTorusHoleShape::newUncappedTorusHoleShape(probe_radius,
144 *(spheres[i].pointer()),
145 *(spheres[j].pointer()));
146 if (th.null()) continue;
147 add_shape(th);
148
149 ////////////////////// Leave out the three sphere shapes
150 //continue;
151
152 // now check for excluding volume for groups of three spheres
153 for (int k=0; k<j; k++) {
154 Ref<Shape> e =
155 Uncapped5SphereExclusionShape::
156 newUncapped5SphereExclusionShape(probe_radius,
157 *(spheres[i].pointer()),
158 *(spheres[j].pointer()),
159 *(spheres[k].pointer()));
160 if (e.nonnull()) add_shape(e);
161 }
162 }
163 }
164}
165
166DiscreteConnollyShape::~DiscreteConnollyShape()
167{
168}
169
170////////////////////////////////////////////////////////////////////////
171// ConnollyShape
172
173static ClassDesc ConnollyShape_cd(
174 typeid(ConnollyShape),"ConnollyShape",1,"public Shape",
175 0, create<ConnollyShape>, 0);
176
177ConnollyShape::ConnollyShape(const Ref<KeyVal>&keyval)
178{
179 box_ = 0;
180 sphere = 0;
181 Ref<Molecule> mol; mol << keyval->describedclassvalue("molecule");
182 probe_r = keyval->doublevalue("probe_radius");
183 if (keyval->error() != KeyVal::OK) {
184 probe_r = 2.6456173;
185 }
186 atominfo_ << keyval->describedclassvalue("atominfo");
187 radius_scale_factor_ = keyval->doublevalue("radius_scale_factor");
188 if (keyval->error() != KeyVal::OK) {
189 radius_scale_factor_ = 1.2;
190 }
191 initialize(mol,probe_r);
192}
193
194#if COUNT_CONNOLLY
195int ConnollyShape::n_total_ = 0;
196int ConnollyShape::n_inside_vdw_ = 0;
197int ConnollyShape::n_with_nsphere_[CONNOLLYSHAPE_N_WITH_NSPHERE_DIM];
198#endif
199
200void
201ConnollyShape::print_counts(ostream& os)
202{
203 os << indent << "ConnollyShape::print_counts():\n" << incindent;
204#if COUNT_CONNOLLY
205 os
206 << indent << "n_total = " << n_total_ << endl
207 << indent << "n_inside_vdw = " << n_inside_vdw_ << endl;
208 for (int i=0; i<CONNOLLYSHAPE_N_WITH_NSPHERE_DIM-1; i++) {
209 os << indent
210 << scprintf("n with nsphere = %2d: %d\n", i, n_with_nsphere_[i]);
211 }
212 os << indent
213 << scprintf("n with nsphere >= %d: %d\n",
214 CONNOLLYSHAPE_N_WITH_NSPHERE_DIM-1,
215 n_with_nsphere_[CONNOLLYSHAPE_N_WITH_NSPHERE_DIM-1])
216 << decindent;
217#else
218 os << indent << "No count information is available.\n" << decindent;
219#endif
220}
221
222void
223ConnollyShape::initialize(const Ref<Molecule>&mol,double probe_radius)
224{
225 clear();
226
227 n_spheres = mol->natom();
228 sphere = new CS2Sphere[n_spheres];
229
230 Ref<AtomInfo> a;
231 if (atominfo_.null()) a = mol->atominfo();
232 else a = atominfo_;
233
234 int i;
235 for (i=0; i<n_spheres; i++) {
236 SCVector3 r(mol->r(i));
237 sphere[i].initialize(r,radius_scale_factor_*find_atom_size(a,
238 mol->Z(i))
239 + probe_r);
240 }
241
242 // initialize a grid of lists of local spheres
243 if (n_spheres) {
244 // find the bounding box
245 SCVector3 lower(sphere[0].center()), upper(sphere[0].center());
246 for (i=0; i<n_spheres; i++) {
247 SCVector3 l(sphere[i].center()), u(sphere[i].center());
248 for (int j=0; j<3; j++) {
249 l[j] -= probe_r + sphere[i].radius();
250 u[j] += probe_r + sphere[i].radius();
251 if (l[j]<lower[j]) lower[j] = l[j];
252 if (u[j]>upper[j]) upper[j] = u[j];
253 }
254 }
255 // compute the parameters for converting x, y, z into a box number
256 lower_ = lower;
257 l_ = 10.0;
258 xmax_ = (int)((upper[0]-lower[0])/l_);
259 ymax_ = (int)((upper[1]-lower[1])/l_);
260 zmax_ = (int)((upper[2]-lower[2])/l_);
261 // allocate the boxes
262 box_ = new std::vector<int>**[xmax_+1];
263 for (i=0; i<=xmax_; i++) {
264 box_[i] = new std::vector<int>*[ymax_+1];
265 for (int j=0; j<=ymax_; j++) {
266 box_[i][j] = new std::vector<int>[zmax_+1];
267 }
268 }
269 // put the spheres in the boxes
270 for (i=0; i<n_spheres; i++) {
271 int ixmin, iymin, izmin, ixmax, iymax, izmax;
272 SCVector3 l(sphere[i].center()), u(sphere[i].center());
273 for (int j=0; j<3; j++) {
274 l[j] -= probe_r + sphere[i].radius();
275 u[j] += probe_r + sphere[i].radius();
276 }
277 get_box(l,ixmin,iymin,izmin);
278 get_box(u,ixmax,iymax,izmax);
279 for (int ii=ixmin; ii<=ixmax; ii++) {
280 for (int jj=iymin; jj<=iymax; jj++) {
281 for (int kk=izmin; kk<=izmax; kk++) {
282 box_[ii][jj][kk].push_back(i);
283 }
284 }
285 }
286 }
287 }
288}
289
290int
291ConnollyShape::get_box(const SCVector3 &v, int &x, int &y, int &z) const
292{
293 if (!box_) return 0;
294 SCVector3 pos = v-lower_;
295 x = (int)(pos[0]/l_);
296 y = (int)(pos[1]/l_);
297 z = (int)(pos[2]/l_);
298 if (x<0) x=0;
299 if (y<0) y=0;
300 if (z<0) z=0;
301 if (x>xmax_) x=xmax_;
302 if (y>ymax_) y=ymax_;
303 if (z>zmax_) z=zmax_;
304 return 1;
305}
306
307ConnollyShape::~ConnollyShape()
308{
309 clear();
310}
311
312void
313ConnollyShape::clear()
314{
315 delete[] sphere;
316 sphere = 0;
317 if (box_) {
318 for (int i=0; i<=xmax_; i++) {
319 for (int j=0; j<=ymax_; j++) {
320 delete[] box_[i][j];
321 }
322 delete[] box_[i];
323 }
324 delete[] box_;
325 box_ = 0;
326 }
327}
328
329double
330ConnollyShape::distance_to_surface(const SCVector3&r, SCVector3*grad) const
331{
332#if COUNT_CONNOLLY
333 n_total_++;
334#endif
335
336 // can't compute grad so zero it if it is requested
337 if (grad) {
338 *grad = 0.0;
339 }
340
341 CS2Sphere probe_centers(r,probe_r);
342
343 const int max_local_spheres = 60;
344 CS2Sphere local_sphere[max_local_spheres];
345
346 const double outside = 1.0;
347 const double inside = -1.0;
348
349 // find out which spheres are near the probe_centers sphere
350 int n_local_spheres = 0;
351 int boxi, boxj, boxk;
352 if (get_box(r,boxi,boxj,boxk)) {
353 std::vector<int> & box = box_[boxi][boxj][boxk];
354 for (int ibox=0; ibox<box.size(); ibox++) {
355 int i = box[ibox];
356 double distance = sphere[i].distance(probe_centers);
357 double r_i = sphere[i].radius();
358 if (distance < r_i + probe_r) {
359 if (distance < r_i - probe_r) {
360#if COUNT_CONNOLLY
361 n_inside_vdw_++;
362#endif
363 return inside;
364 }
365 if (n_local_spheres == max_local_spheres) {
366 throw LimitExceeded<int>("distance_to_surface: "
367 "max_local_spheres exceeded",
368 __FILE__, __LINE__,
369 max_local_spheres,
370 n_local_spheres+1,
371 class_desc());
372 }
373 local_sphere[n_local_spheres] = sphere[i];
374 n_local_spheres++;
375 }
376 }
377 }
378
379#if COUNT_CONNOLLY
380 if (n_local_spheres >= CONNOLLYSHAPE_N_WITH_NSPHERE_DIM) {
381 n_with_nsphere_[CONNOLLYSHAPE_N_WITH_NSPHERE_DIM-1]++;
382 }
383 else {
384 n_with_nsphere_[n_local_spheres]++;
385 }
386#endif
387
388 if (probe_centers.intersect(local_sphere,n_local_spheres)
389 == 1) return inside;
390 return outside;
391}
392
393void
394ConnollyShape::boundingbox(double valuemin,
395 double valuemax,
396 SCVector3& p1, SCVector3& p2)
397{
398 int i,j;
399 if (valuemin < -1.0 || valuemax > 1.0) {
400 throw LimitExceeded<double>("boundingbox: value out of range",
401 __FILE__, __LINE__,
402 ((valuemin<0.0)?-1.0:1.0),
403 valuemin, class_desc());
404 }
405
406 if (n_spheres == 0) {
407 for (i=0; i<3; i++) {
408 p1[i] = 0.0;
409 p2[i] = 0.0;
410 }
411 return;
412 }
413
414 double r = sphere[0].radius() - probe_r;
415 SCVector3 v1(sphere[0].x() - r, sphere[0].y() - r, sphere[0].z() - r);
416 SCVector3 v2(sphere[0].x() + r, sphere[0].y() + r, sphere[0].z() + r);
417
418 for (i=1; i<n_spheres; i++) {
419 double r = sphere[i].radius() - probe_r;
420 for (j=0; j<3; j++) {
421 if (v1[j] > sphere[i].center()[j] - r) {
422 v1[j] = sphere[i].center()[j] - r;
423 }
424 if (v2[j] < sphere[i].center()[j] + r) {
425 v2[j] = sphere[i].center()[j] + r;
426 }
427 }
428 }
429
430 for (i=0; i<3; i++) {
431 p1[i] = v1[i] - 0.01;
432 p2[i] = v2[i] + 0.01;
433 }
434}
435
436////////////////////////////////////////////////////////////////////////
437// interval class needed by CS2Sphere
438
439// Simple class to keep track of regions along an interval
440class interval
441{
442 int _nsegs; // # disjoint segments in interval
443 int _max_segs; // # segments currently allocated
444
445 double *_min, *_max; // arrays of ranges for segments
446
447 private:
448 // internal member function to compact interval list--this
449 // assumes that new segment is located in last element of
450 // _min and _max
451 void compact(void)
452 {
453
454 if (_nsegs==1) return;
455
456 // case 0 new segment is disjoint and below all other segments
457 if (_max[_nsegs-1] < _min[0])
458 {
459 double mintmp=_min[_nsegs-1];
460 double maxtmp=_max[_nsegs-1];
461 for (int i=_nsegs-2; i>=0 ; i--)
462 {
463 _min[i+1]=_min[i];
464 _max[i+1]=_max[i];
465 }
466 _min[0]=mintmp;
467 _max[0]=maxtmp;
468 return;
469 }
470
471 // case 1: new segment is disjoint and above all other segments
472 if (_min[_nsegs-1] > _max[_nsegs-2]) return;
473
474 // Fast forward to where this interval belongs
475 int icount=0;
476 while (_min[_nsegs-1] > _max[icount]) icount++;
477
478 // case 2: new segment is disjoint and between two segments
479 if (_max[_nsegs-1] < _min[icount])
480 {
481 double mintmp=_min[_nsegs-1];
482 double maxtmp=_max[_nsegs-1];
483 for (int i=_nsegs-2; i >= icount; i--)
484 {
485 _min[i+1]=_min[i];
486 _max[i+1]=_max[i];
487 }
488 _min[icount]=mintmp;
489 _max[icount]=maxtmp;
490 return;
491 }
492
493 // new segment must overlap lower part of segment icount,
494 // so redefine icount's lower boundary
495 _min[icount] = (_min[_nsegs-1] < _min[icount])?
496 _min[_nsegs-1]:_min[icount];
497
498 // Now figure how far up this new segment extends
499 // case 3: if it doesn't extend beyond this segment, just exit
500 if (_max[_nsegs-1] < _max[icount]) { _nsegs--; return;}
501
502 // Search forward till we find its end
503 int jcount=icount;
504 while (_max[_nsegs-1] > _max[jcount]) jcount++;
505
506 // Case 4
507 // The new segment goes to the end of all the other segments
508 if (jcount == _nsegs-1)
509 {
510 _max[icount]=_max[_nsegs-1];
511 _nsegs=icount+1;
512 return;
513 }
514
515 // Case 5
516 // The new segment ends between segments
517 if (_max[_nsegs-1] < _min[jcount])
518 {
519 _max[icount]=_max[_nsegs-1];
520 // Now clobber all the segments covered by the new one
521 int kcount=icount+1;
522 for (int i=jcount; i<_nsegs; i++)
523 {
524 _min[kcount]=_min[i];
525 _max[kcount]=_max[i];
526 kcount++;
527 }
528 _nsegs=kcount-1;
529 return;
530 }
531
532 // Case 6
533 // The new segment ends inside a segment
534 if (_max[_nsegs-1] >= _min[jcount])
535 {
536 _max[icount]=_max[jcount];
537 // Now clobber all the segments covered by the new one
538 int kcount=icount+1;
539 for (int i=jcount+1; i<_nsegs; i++)
540 {
541 _min[kcount]=_min[i];
542 _max[kcount]=_max[i];
543 kcount++;
544 }
545 _nsegs=kcount-1;
546 return;
547 }
548
549 // Shouldn't get here!
550 ExEnv::err0() << indent
551 << "Found no matching cases in interval::compact()\n";
552 print();
553 exit(1);
554 }
555
556 public:
557 interval(void):_nsegs(0),_max_segs(10)
558 { _min = (double*) malloc(_max_segs*sizeof(double)); // Use malloc so
559 _max = (double*) malloc(_max_segs*sizeof(double));} //we can use realloc
560
561 ~interval() { free(_min); free(_max); }
562
563 // add a new segment to interval
564 void add(double min, double max)
565 {
566 if (min > max) {double tmp=min; min=max; max=tmp;}
567 if (_nsegs == _max_segs)
568 {
569 _max_segs *= 2;
570 _min=(double *)realloc(_min, _max_segs*sizeof(double));
571 _max=(double *)realloc(_max, _max_segs*sizeof(double));
572 }
573
574 _min[_nsegs]=min;
575 _max[_nsegs]=max;
576 _nsegs++;
577 compact();
578 }
579
580 // Test to see if the interval is complete over {min, max}
581 int test_interval(double min, double max)
582 {
583 if (_nsegs == 0) return 0;
584
585 if (min > max) {double tmp=min; min=max; max=tmp;}
586
587 if (min < _min[0] || max > _max[_nsegs-1]) return 0;
588 for (int i=0; i < _nsegs; i++)
589 {
590 if (min > _min[i] && max < _max[i]) return 1;
591 if (max < _min[i]) return 0;
592 }
593 return 0;
594 }
595
596 // Print out the currect state of the interval
597 void print()
598 {
599 ExEnv::out0() << indent
600 << scprintf(" _nsegs=%d; _max_segs=%d\n",_nsegs, _max_segs);
601 for (int i=0; i<_nsegs; i++)
602 ExEnv::out0() << indent
603 << scprintf("min[%d]=%7.4lf, max[%d]=%7.4lf\n",
604 i,_min[i],i,_max[i]);
605 }
606
607 void clear() { _nsegs = 0; }
608};
609
610////////////////////////////////////////////////////////////////////////
611// CS2Sphere
612
613#if COUNT_CONNOLLY
614int CS2Sphere::n_no_spheres_ = 0;
615int CS2Sphere::n_probe_enclosed_by_a_sphere_ = 0;
616int CS2Sphere::n_probe_center_not_enclosed_ = 0;
617int CS2Sphere::n_surface_of_s0_not_covered_ = 0;
618int CS2Sphere::n_plane_totally_covered_ = 0;
619int CS2Sphere::n_internal_edge_not_covered_ = 0;
620int CS2Sphere::n_totally_covered_ = 0;
621#endif
622
623void
624CS2Sphere::print_counts(ostream& os)
625{
626 os << indent << "CS2Sphere::print_counts():\n" << incindent;
627#if COUNT_CONNOLLY
628 os
629 << indent << "n_no_spheres = " << n_no_spheres_ << endl
630 << indent << "n_probe_enclosed_by_a_sphere = "
631 << n_probe_enclosed_by_a_sphere_ << endl
632 << indent << "n_probe_center_not_enclosed = "
633 << n_probe_center_not_enclosed_ << endl
634 << indent << "n_surface_of_s0_not_covered = "
635 << n_surface_of_s0_not_covered_ << endl
636 << indent << "n_plane_totally_covered_ = "
637 << n_plane_totally_covered_ << endl
638 << indent << "n_internal_edge_not_covered = "
639 << n_internal_edge_not_covered_ << endl
640 << indent << "n_totally_covered = " << n_totally_covered_ << endl
641 << decindent;
642#else
643 os << indent << "No count information is available.\n"
644 << decindent;
645#endif
646}
647
648// Function to determine if the centers of a bunch of spheres are separated
649// by a plane from the center of another plane
650
651// s0 is assumed to be at the origin.
652
653// Return 1 if all of the points can be placed on the same side of a
654// plane passing through s0's center.
655static int
656same_side(const CS2Sphere& s0, CS2Sphere *s, int n_spheres)
657{
658 if (n_spheres <= 3) return 1;
659
660 SCVector3 perp;
661 int sign;
662
663 for (int i=0; i<n_spheres; i++)
664 {
665 for (int j=0; j<i; j++)
666 {
667 perp = s[i].center().perp_unit(s[j].center());
668 int old_sign=0;
669 for (int k=0; k < n_spheres; k++)
670 {
671 if (i != k && j != k)
672 {
673 sign=(perp.dot(s[k].center()) < 0)? -1:1;
674 if (old_sign && old_sign != sign)
675 goto next_plane;
676 old_sign=sign;
677 }
678 }
679 // We found a plane with all centers on one side
680 return 1;
681 next_plane:
682 continue;
683 }
684 }
685 // All of the planes had points on both sides.
686 return 0;
687}
688
689double
690CS2Sphere::common_radius(CS2Sphere &asphere)
691{
692 double d=distance(asphere);
693 double s=0.5*(d+_radius+asphere._radius);
694 double p = s*(s-d)*(s-_radius)*(s-asphere._radius);
695 //printf("common_radius: p = %5.3f\n", p);
696 if (p <= 0.0) return 0.0;
697 return 2.*sqrt(p)/d;
698}
699
700#define PRINT_SPECIAL_CASES 0
701#if PRINT_SPECIAL_CASES
702static void
703print_spheres(const CS2Sphere& s0, CS2Sphere* s, int n_spheres)
704{
705 static int output_number;
706 char filename[80];
707 sprintf(filename,"spherelist_%d.oogl",output_number);
708 FILE* fp = fopen(filename,"w");
709 fprintf(fp,"LIST\n");
710 fprintf(fp,"{\n");
711 fprintf(fp," appearance {\n");
712 fprintf(fp," material {\n");
713 fprintf(fp," ambient 0.5 0.1 0.1\n");
714 fprintf(fp," diffuse 1.0 0.2 0.2\n");
715 fprintf(fp," }\n");
716 fprintf(fp," }\n");
717 fprintf(fp," = SPHERE\n");
718 fprintf(fp," %15.8f %15.8f %15.8f %15.8f\n",
719 s0.radius(), s0.x(), s0.y(), s0.z());
720 fprintf(fp,"}\n");
721 for (int i=0; i<n_spheres; i++) {
722 fprintf(fp,"{ = SPHERE\n");
723 fprintf(fp," %15.8f %15.8f %15.8f %15.8f\n",
724 s[i].radius(), s[i].x(), s[i].y(), s[i].z());
725 fprintf(fp,"}\n");
726 }
727 fclose(fp);
728 output_number++;
729}
730#endif
731
732// Function to determine if there is any portion of s0 that
733// is not inside one or more of the spheres in s[]
734int
735CS2Sphere::intersect(CS2Sphere *s, int n_spheres) const
736{
737 if (n_spheres == 0) {
738 n_no_spheres_++;
739 return 0;
740 }
741 CS2Sphere s0;
742 s0 = *this;
743 // Declare an interval object to manage overlap information
744 // it is static so it will only call malloc twice
745 static interval intvl;
746 // First make sure that at least one sphere in s[] contains
747 // the center of s0 and that s0 is not contained inside
748 // one of the spheres
749 int center_is_contained = 0;
750 int i;
751 for (i=0; i<n_spheres; i++)
752 {
753 double d=s0.distance(s[i]);
754 if (d+s0.radius() < s[i].radius()) {
755 n_probe_enclosed_by_a_sphere_++;
756 return 1;
757 }
758 if (d < s[i].radius()) center_is_contained = 1;
759 }
760 if (!center_is_contained) {
761 n_probe_center_not_enclosed_++;
762 return 0;
763 }
764
765 // Let's first put s0 at the origin
766 for (i=0; i<n_spheres; i++)
767 s[i].recenter(s0.center());
768 s0.recenter(s0.center());
769
770 // Now check to make sure that the surface of s0 is completely
771 // included in spheres in s[], by making sure that all the
772 // circles describing the intersections of every sphere with
773 // s0 are included in at least one other sphere.
774 double epsilon=1.e-8;
775 for (i=0; i<n_spheres; i++)
776 {
777 // calculate radius of the intersection of s0 and s[i]
778 double cr = s0.common_radius(s[i]);
779 if (cr == 0.0) {
780 continue;
781 }
782
783 // We're chosing that the intersection of s[i] and s0
784 // occurs parallel to the x-y plane, so we'll need to rotate the
785 // center of s[j] appropriately.
786 // Create a rotation matrix that take the vector from
787 // the centers of s0 to s[i] and puts it on the z axis
788 static const SCVector3 Zaxis(0.0, 0.0, 1.0);
789 SCMatrix3 rot = rotation_mat(s0.center_vec(s[i]),Zaxis);
790
791 // Now calculate the Z position of the intersection of
792 // s0 and s[i]
793 double d=s0.distance(s[i]);
794 double z_plane;
795 if (s[i].radius()*s[i].radius() < d*d+s0.radius()*s0.radius())
796 z_plane=sqrt(s0.radius()*s0.radius()-cr*cr);
797 else
798 z_plane=-sqrt(s0.radius()*s0.radius()-cr*cr);
799
800 // Initialize the interval object
801 intvl.clear();
802
803 // Loop over the other spheres
804 for (int j=0; j<n_spheres; j++)
805 if (i != j)
806 {
807 // Rotate the center of s[j] to appropriate refence frame
808 SCVector3 rcent = rot*s0.center_vec(s[j]);
809
810 double x0=rcent.x();
811 double y0=rcent.y();
812 double z0=rcent.z();
813
814 // Does this sphere even reach the plane where
815 // the intersection of s0 and s[i] occurs?
816 // If not, let's go to the next sphere
817 double z_dist=s[j].radius()*s[j].radius()-
818 (z0-z_plane)*(z0-z_plane);
819 if (z_dist < 0.0)
820 continue;
821
822 // Calculate radius of circular projection of s[j]
823 // onto s0-s[i] intersection plane
824 double r_2=z_dist;
825
826 // Precalculate a bunch of factors
827 double cr_2=cr*cr;
828 double x0_2=x0*x0; double y0_2=y0*y0;
829 double dist=sqrt(x0_2+y0_2);
830
831 // If the projection of s[j] on x-y doesn't reach the
832 // intersection of s[i] and s0, continue.
833 if (r_2 < (dist-cr)*(dist-cr))
834 continue;
835
836 // If the projection of s[j] on x-y engulfs the intersection
837 // of s[i] and s0, cover interval and continue
838 if (r_2 > (dist+cr)*(dist+cr))
839 {
840 intvl.add(0, 2.*M_PI);
841 continue;
842 }
843
844 // Calculation the radical in the quadratic equation
845 // determining the overlap of the two circles
846 double radical=x0_2*(-cr_2*cr_2 + 2*cr_2*r_2 -
847 r_2*r_2 + 2*cr_2*x0_2 +
848 2*r_2*x0_2 - x0_2*x0_2 +
849 2*cr_2*y0_2 + 2*r_2*y0_2 -
850 2*x0_2*y0_2 - y0_2*y0_2);
851
852 // Check to see if there's any intersection at all
853 // I.e. if one circle is inside the other (Note that
854 // we've already checked to see if s[j] engulfs
855 // the intersection of s0 and s[i])
856 if (radical <= 0.0) continue;
857
858 // Okay, go ahead and calculate the intersection points
859 double x_numer = cr_2*x0_2 - r_2*x0_2 + x0_2*x0_2 + x0_2*y0_2;
860 double x_denom = 2*x0*x0_2 + 2*x0*y0_2;
861 double y_numer = cr_2*y0 - r_2*y0 + x0_2*y0 + y0*y0_2;
862 double y_denom = 2*(x0_2 + y0_2);
863
864 double sqrt_radical = sqrt(radical);
865
866 double x_0=(x_numer - y0*sqrt_radical)/x_denom;
867 double y_0=(y_numer + sqrt_radical)/y_denom;
868 double x_1=(x_numer + y0*sqrt_radical)/x_denom;
869 double y_1=(y_numer - sqrt_radical)/y_denom;
870
871 // Now calculate the angular range of these ordered
872 // points and place them on the first Riemann sheet.
873 // and sort their order
874 double theta1=atan2(y_0, x_0);
875 double theta2=atan2(y_1, x_1);
876 if (theta1 < 0.0) theta1+=2.*M_PI;
877 if (theta2 < 0.0) theta2+=2.*M_PI;
878 if (theta1 > theta2)
879 {
880 double tmptheta=theta1;
881 theta1=theta2;
882 theta2=tmptheta;
883 }
884
885 // Determine which of the two possible chords
886 // is inside s[j]
887 double dor=(x0-cr)*(x0-cr)+y0*y0;
888 if (dor < r_2)
889 {
890 intvl.add(0, theta1);
891 intvl.add(theta2, 2.*M_PI);
892 }
893 else
894 {
895 intvl.add(theta1, theta2);
896 }
897
898 // Now test to see if the range is covered
899 if (intvl.test_interval(epsilon, 2.*M_PI-epsilon))
900 {
901 // No need to keep testing, move on to next i
902 break;
903 }
904
905 }
906 // If the intersection wasn't totally covered, the sphere
907 // intersection is incomplete
908 if (!intvl.test_interval(epsilon, 2.*M_PI-epsilon)) {
909 n_surface_of_s0_not_covered_++;
910 // goto next_test;
911 return 0;
912 }
913 }
914
915 // for the special case of all sphere's centers on one side of
916 // a plane passing through s0's center we are done; the probe
917 // must be completely intersected.
918 if (same_side(s0,s,n_spheres)) {
919 n_plane_totally_covered_++;
920 return 1;
921 }
922
923 // As a final test of the surface coverage, make sure that all
924 // of the intersection surfaces between s0 and s[] are included
925 // inside more than one sphere.
926 int angle_segs;
927 double max_angle[2], min_angle[2];
928 for (i=0; i<n_spheres; i++)
929 {
930 // For my own sanity, let's put s[i] at the origin
931 int k;
932 for (k=0; k<n_spheres; k++)
933 if (k != i)
934 s[k].recenter(s[i].center());
935 s0.recenter(s[i].center());
936 s[i].recenter(s[i].center());
937
938 for (int j=0; j<i; j++)
939 {
940
941 // calculate radius of the intersection of s[i] and s[j]
942 double cr = s[i].common_radius(s[j]);
943 if (cr == 0.0) {
944 continue; // s[i] and s[j] don't intersect
945 }
946
947 // We're chosing that the intersection of s[i] and s[j]
948 // occurs parallel to the x-y plane, so we'll need to rotate the
949 // center of all s[]'s and s0 appropriately.
950 // Create a rotation matrix that take the vector from
951 // the centers of s0 to s[i] and puts it on the z axis
952 static const SCVector3 Zaxis(0.0, 0.0, 1.0);
953 SCMatrix3 rot = rotation_mat(s[i].center_vec(s[j]),Zaxis);
954
955 // Now calculate the Z position of the intersection of
956 // s[i] and s[j]
957 double d=s[i].distance(s[j]);
958 double z_plane;
959 if (s[j].radius()*s[j].radius() < s[i].radius()*s[i].radius()+d*d)
960 z_plane=sqrt(s[i].radius()*s[i].radius()-cr*cr);
961 else
962 z_plane=-sqrt(s[i].radius()*s[i].radius()-cr*cr);
963
964 // Determine which part of the this intersection
965 // occurs within s0
966 // Rotate the center of s0 to appropriate refence frame
967 SCVector3 rcent = rot*s[i].center_vec(s0);
968
969 double x0=rcent.x();
970 double y0=rcent.y();
971 double z0=rcent.z();
972
973 // Does this s0 even reach the plane where
974 // the intersection of s[i] and s[j] occurs?
975 // If not, let's go to the next sphere j
976 double z_dist=s0.radius()*s0.radius()-
977 (z0-z_plane)*(z0-z_plane);
978 if (z_dist < 0.0)
979 continue;
980
981 // Calculate radius of circular projection of s0
982 // onto s[i]-s[j] intersection plane
983 double r_2=z_dist;
984
985 // Precalculate a bunch of factors
986 double cr_2=cr*cr;
987 double x0_2=x0*x0; double y0_2=y0*y0;
988 double dist=sqrt(x0_2+y0_2);
989
990 // If the projection of s[j] on x-y doesn't reach the
991 // intersection of s[i] and s0, continue.
992 if (r_2 < (dist-cr)*(dist-cr))
993 continue;
994
995 // If the projection of s0 on x-y engulfs the intersection
996 // of s[i] and s[j], the intersection interval is 0 to 2pi
997 if (r_2 > (dist+cr)*(dist+cr))
998 {
999 angle_segs=1;
1000 min_angle[0]=0.0;
1001 max_angle[0]=2.*M_PI;
1002 }
1003
1004 // Calculation the radical in the quadratic equation
1005 // determining the overlap of the two circles
1006 double radical=x0_2*(-cr_2*cr_2 + 2*cr_2*r_2 -
1007 r_2*r_2 + 2*cr_2*x0_2 +
1008 2*r_2*x0_2 - x0_2*x0_2 +
1009 2*cr_2*y0_2 + 2*r_2*y0_2 -
1010 2*x0_2*y0_2 - y0_2*y0_2);
1011
1012 // Check to see if there's any intersection at all
1013 // I.e. if one circle is inside the other (Note that
1014 // we've already checked to see if s0 engulfs
1015 // the intersection of s[i] and s[j]), so this
1016 // must mean that the intersection of s[i] and s[j]
1017 // occurs outside s0
1018 if (radical <= 0.0) continue;
1019
1020 // Okay, go ahead and calculate the intersection points
1021 double x_numer = cr_2*x0_2 - r_2*x0_2 + x0_2*x0_2 + x0_2*y0_2;
1022 double x_denom = 2*x0*x0_2 + 2*x0*y0_2;
1023 double y_numer = cr_2*y0 - r_2*y0 + x0_2*y0 + y0*y0_2;
1024 double y_denom = 2*(x0_2 + y0_2);
1025
1026 double sqrt_radical = sqrt(radical);
1027
1028 double x_0=(x_numer - y0*sqrt_radical)/x_denom;
1029 double y_0=(y_numer + sqrt_radical)/y_denom;
1030 double x_1=(x_numer + y0*sqrt_radical)/x_denom;
1031 double y_1=(y_numer - sqrt_radical)/y_denom;
1032
1033 // Now calculate the angular range of these ordered
1034 // points and place them on the first Riemann sheet.
1035 // and sort their order
1036 double theta1=atan2(y_0, x_0);
1037 double theta2=atan2(y_1, x_1);
1038 if (theta1 < 0.0) theta1+=2.*M_PI;
1039 if (theta2 < 0.0) theta2+=2.*M_PI;
1040 if (theta1 > theta2)
1041 {
1042 double tmptheta=theta1;
1043 theta1=theta2;
1044 theta2=tmptheta;
1045 }
1046 //printf("theta1=%lf, theta2=%lf\n",theta1,theta2);
1047
1048 // Determine which of the two possible chords
1049 // is inside s0
1050
1051 // But first see if s0 is inside this intersection:
1052 double origin_dist=((x0-cr)*(x0-cr)+(y0*y0));
1053 if (origin_dist < r_2) // it's the angle containing
1054 // the origin
1055 {
1056 angle_segs=2;
1057 min_angle[0]=0.0;
1058 max_angle[0]=theta1;
1059 min_angle[1]=theta2;
1060 max_angle[1]=2.*M_PI;
1061 }
1062 else // it's the angle not including the origin
1063 {
1064 angle_segs=1;
1065 min_angle[0]=theta1;
1066 max_angle[0]=theta2;
1067 }
1068
1069 // Initialize the interval object
1070 intvl.clear();
1071
1072 // Loop over the other spheres
1073 for (k=0; k<n_spheres; k++)
1074 {
1075 if (k != i && k != j)
1076 {
1077 // Rotate the center of s[k] to appropriate reference frame
1078 rcent = rot*s[i].center_vec(s[k]);
1079
1080 double x0=rcent.x();
1081 double y0=rcent.y();
1082 double z0=rcent.z();
1083
1084 // Does this sphere even reach the plane where
1085 // the intersection of s[i] and s[j] occurs?
1086 // If not, let's go to the next sphere
1087 double z_dist=s[k].radius()*s[k].radius()-
1088 (z0-z_plane)*(z0-z_plane);
1089 if (z_dist < 0.0)
1090 continue;
1091
1092 // Calculate radius of circular projection of s[k]
1093 // onto s[i]-s[j] intersection plane
1094 double r_2=z_dist;
1095
1096 // Precalculate a bunch of factors
1097 double cr_2=cr*cr;
1098 double x0_2=x0*x0; double y0_2=y0*y0;
1099 double dist=sqrt(x0_2+y0_2);
1100
1101 // If the projection of s[k] on x-y doesn't reach the
1102 // intersection of s[i] and s[j], continue.
1103 if (r_2 < (dist-cr)*(dist-cr))
1104 continue;
1105
1106 // If the projection of s[k] on x-y engulfs the intersection
1107 // of s[i] and s0, cover interval and continue
1108 if (r_2 > (dist+cr)*(dist+cr))
1109 {
1110 intvl.add(0, 2.*M_PI);
1111 continue;
1112 }
1113
1114 // Calculation the radical in the quadratic equation
1115 // determining the overlap of the two circles
1116 radical=x0_2*(-cr_2*cr_2 + 2*cr_2*r_2 -
1117 r_2*r_2 + 2*cr_2*x0_2 +
1118 2*r_2*x0_2 - x0_2*x0_2 +
1119 2*cr_2*y0_2 + 2*r_2*y0_2 -
1120 2*x0_2*y0_2 - y0_2*y0_2);
1121
1122 // Check to see if there's any intersection at all
1123 // I.e. if one circle is inside the other (Note that
1124 // we've already checked to see if s[k] engulfs
1125 // the intersection of s[i] and s[j])
1126 if (radical <= 0.0) continue;
1127
1128 // Okay, go ahead and calculate the intersection points
1129 x_numer = cr_2*x0_2 - r_2*x0_2 + x0_2*x0_2 + x0_2*y0_2;
1130 x_denom = 2*x0*x0_2 + 2*x0*y0_2;
1131 y_numer = cr_2*y0 - r_2*y0 + x0_2*y0 + y0*y0_2;
1132 y_denom = 2*(x0_2 + y0_2);
1133
1134 sqrt_radical = sqrt(radical);
1135
1136 double x_0=(x_numer - y0*sqrt_radical)/x_denom;
1137 double y_0=(y_numer + sqrt_radical)/y_denom;
1138 double x_1=(x_numer + y0*sqrt_radical)/x_denom;
1139 double y_1=(y_numer - sqrt_radical)/y_denom;
1140
1141 // Now calculate the angular range of these ordered
1142 // points and place them on the first Riemann sheet.
1143 // and sort their order
1144 theta1=atan2(y_0, x_0);
1145 theta2=atan2(y_1, x_1);
1146 if (theta1 < 0.0) theta1+=2.*M_PI;
1147 if (theta2 < 0.0) theta2+=2.*M_PI;
1148 if (theta1 > theta2)
1149 {
1150 double tmptheta=theta1;
1151 theta1=theta2;
1152 theta2=tmptheta;
1153 }
1154 //printf("In k loop, k=%d, theta1=%lf, theta2=%lf\n",
1155 // k,theta1, theta2);
1156 // Determine which of the two possible chords
1157 // is inside s[k]
1158 double origin_dist=((x0-cr)*(x0-cr)+(y0*y0));
1159 if (origin_dist < r_2) // it's got the origin
1160 {
1161 intvl.add(0, theta1);
1162 intvl.add(theta2, 2.*M_PI);
1163 }
1164 else // it doesn't have the origin
1165 {
1166 intvl.add(theta1, theta2);
1167 }
1168
1169 // Now test to see if the range is covered
1170 if (intvl.test_interval(min_angle[0]+epsilon,
1171 max_angle[0]-epsilon) &&
1172 (angle_segs!=2 ||
1173 intvl.test_interval(min_angle[1]+epsilon,
1174 max_angle[1]-epsilon)))
1175 {
1176 goto next_j;
1177 }
1178 }
1179 }
1180 if (!intvl.test_interval(min_angle[0]+epsilon,
1181 max_angle[0]-epsilon))
1182 {
1183 // No need to keep testing, return 0
1184 n_internal_edge_not_covered_++;
1185 return 0;
1186 //printf(" Non-internal coverage(1)\n");
1187 //goto next_test;
1188 }
1189 if (angle_segs==2)
1190 {
1191 if (!intvl.test_interval(min_angle[1]+epsilon,
1192 max_angle[1]-epsilon))
1193 {
1194 n_internal_edge_not_covered_++;
1195 return 0;
1196 //printf(" Non-internal coverage(2)\n");
1197 //goto next_test;
1198 }
1199 else
1200 {
1201 goto next_j;
1202 }
1203 }
1204 next_j:
1205 continue;
1206 }
1207 }
1208
1209 // Since we made it past all of the sphere intersections, the
1210 // surface is totally covered
1211 n_totally_covered_++;
1212 return 1;
1213}
1214
1215/////////////////////////////////////////////////////////////////////////////
1216
1217// Local Variables:
1218// mode: c++
1219// c-file-style: "CLJ"
1220// End:
Note: See TracBrowser for help on using the repository browser.