source: src/Shapes/BaseShapes.cpp@ ff4fff9

CombiningParticlePotentialParsing
Last change on this file since ff4fff9 was 9ec4b8, checked in by Frederik Heber <heber@…>, 9 years ago

FIX: Cuboid_impl::getNormal() did not take numerical imprecision into account.

  • Property mode set to 100644
File size: 14.1 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. All rights reserved.
5 * Copyright (C) 2013 Frederik Heber. All rights reserved.
6 *
7 *
8 * This file is part of MoleCuilder.
9 *
10 * MoleCuilder is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * MoleCuilder is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24/*
25 * BaseShapes_impl.cpp
26 *
27 * Created on: Jun 18, 2010
28 * Author: crueger
29 */
30
31// include config.h
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36#include "CodePatterns/MemDebug.hpp"
37
38#include "Shapes/BaseShapes.hpp"
39#include "Shapes/BaseShapes_impl.hpp"
40#include "Shapes/ShapeExceptions.hpp"
41#include "Shapes/ShapeOps.hpp"
42
43#include "Helpers/defs.hpp"
44
45#include "CodePatterns/Assert.hpp"
46#include "LinearAlgebra/Vector.hpp"
47#include "LinearAlgebra/RealSpaceMatrix.hpp"
48#include "LinearAlgebra/Line.hpp"
49#include "LinearAlgebra/Plane.hpp"
50#include "LinearAlgebra/LineSegment.hpp"
51#include "LinearAlgebra/LineSegmentSet.hpp"
52
53#include <cmath>
54#include <algorithm>
55
56// CYLINDER CODE
57// ----------------------------------------------------------------------------
58bool Cylinder_impl::isInside(const Vector &point) const {
59 return (Vector(point[0], point[1], 0.0).NormSquared() < 1.0+MYEPSILON) &&
60 (point[2] > -1.0-MYEPSILON) && (point[2] < 1.0+MYEPSILON);
61}
62
63bool Cylinder_impl::isOnSurface(const Vector &point) const {
64 // on the side?
65 if (fabs(Vector(point[0], point[1], 0.0).NormSquared()-1.0)<MYEPSILON &&
66 (point[2] > -1.0-MYEPSILON) && (point[2] < 1.0+MYEPSILON))
67 return true;
68 // on top/bottom?
69 if ((Vector(point[0], point[1], 0.0).NormSquared()< 1.0 + MYEPSILON) &&
70 ((fabs(point[2]-1)<MYEPSILON) || (fabs(point[2]+1)<MYEPSILON)))
71 return true;
72 return false;
73
74}
75
76Vector Cylinder_impl::getNormal(const Vector &point) const throw(NotOnSurfaceException) {
77 if(!isOnSurface(point)){
78 throw NotOnSurfaceException() << ShapeVector(&point);
79 }
80
81 Vector n = Vector(0, 0, 0);
82 if ((fabs(point[2]-1)<MYEPSILON) || (fabs(point[2]+1)<MYEPSILON))
83 n += Vector(0.0, 0.0, point[2]);
84 else
85 n += Vector(point[0], point[1], 0.0);
86 n.Normalize();
87 return n;
88}
89
90Vector Cylinder_impl::getCenter() const
91{
92 return Vector(0.0, 0.0, 0.0);
93}
94
95double Cylinder_impl::getRadius() const
96{
97 return 1.0;
98}
99
100double Cylinder_impl::getVolume() const
101{
102 return M_PI*2.0; // pi r^2 h
103}
104
105double Cylinder_impl::getSurfaceArea() const
106{
107 return 2.0*M_PI*2.0; // 2 pi r h
108}
109
110LineSegmentSet Cylinder_impl::getLineIntersections(const Line &line) const {
111 const Vector origin = line.getOrigin();
112 const Vector direction = line.getDirection();
113
114 const Vector e(direction[0], direction[1], 0.0);
115 const Vector f(origin[0], origin[1], 0.0);
116 const double A = e.ScalarProduct(e);
117 const double B = 2.0*e.ScalarProduct(f);
118 const double C = f.ScalarProduct(f) - 1.0;
119
120 std::vector<double> solutions;
121
122 // Common routine to solve quadratic equations, anywhere?
123 const double neg_p_half = -B/(2.0*A);
124 const double q = C/A;
125 const double radicant = neg_p_half*neg_p_half-q;
126
127 if (radicant > 0.0) {
128 const double root = sqrt(radicant);
129 solutions.push_back(neg_p_half+root);
130 const double sln2 = neg_p_half-root;
131 if (sln2 != solutions.back())
132 solutions.push_back(sln2);
133 }
134
135 // Now get parameter for intersection with z-Planes.
136 const double origin_z = origin[2];
137 const double dir_z = direction[2];
138
139 if (dir_z != 0.0) {
140 solutions.push_back((-1.0-origin_z)/dir_z);
141 solutions.push_back((1.0-origin_z)/dir_z);
142 }
143
144 // Calculate actual vectors from obtained parameters and check,
145 // if they are actual intersections.
146 std::vector<Vector> intersections;
147
148 for(unsigned int i=0; i<solutions.size(); i++) {
149 const Vector check_me(origin + direction*solutions[i]);
150 if (isOnSurface(check_me))
151 intersections.push_back(check_me);
152 }
153
154 LineSegmentSet result(line);
155 if (intersections.size()==2)
156 result.insert(LineSegment(intersections[0], intersections[1]));
157 return result;
158}
159
160std::string Cylinder_impl::toString() const
161{
162 return "Cylinder()";
163}
164
165enum ShapeType Cylinder_impl::getType() const
166{
167 return CylinderType;
168}
169
170std::vector<Vector> Cylinder_impl::getHomogeneousPointsOnSurface(const size_t N) const {
171 const double nz_float = sqrt(N/M_PI);
172 const int nu = round(N/nz_float);
173 const int nz = round(nz_float);
174
175 const double dphi = 2.0*M_PI/nu;
176 const double dz = 2.0/nz;
177
178 std::vector<Vector> result;
179
180 for(int useg=0; useg<nu; useg++)
181 for(int zseg=0; zseg<=nz; zseg++)
182 result.push_back(Vector(cos(useg*dphi), sin(useg*dphi), zseg*dz-1.0));
183
184 return result;
185}
186
187std::vector<Vector> Cylinder_impl::getHomogeneousPointsInVolume(const size_t N) const {
188 const double nz_float = pow(N/(2.0*M_PI), 1.0/3.0);
189 const int nu = round(nz_float*M_PI);
190 const int nr = round(nz_float*0.5);
191 const int nz = round(nz_float);
192
193 const double dphi = 2.0*M_PI/nu;
194 const double dz = 2.0/nz;
195 const double dr = 1.0/nr;
196
197 std::vector<Vector> result;
198
199 for(int useg=0; useg<nu; useg++)
200 for(int zseg=0; zseg<nz; zseg++)
201 for(int rseg=0; rseg<nr; rseg++)
202 {
203 const double r = dr+rseg*dr;
204 result.push_back(Vector(r*cos(useg*dphi), r*sin(useg*dphi), zseg*dz-1.0));
205 }
206
207 return result;
208}
209
210Shape Cylinder() {
211 Shape::impl_ptr impl = Shape::impl_ptr(new Cylinder_impl());
212 return Shape(impl);
213}
214
215Shape Cylinder(const Vector &center, const double xrot, const double yrot,
216 const double height, const double radius)
217{
218 RealSpaceMatrix rot;
219 rot.setRotation(xrot, yrot, 0.0);
220
221 return translate(
222 transform(
223 stretch(
224 Cylinder(),
225 Vector(radius, radius, height*0.5)),
226 rot),
227 center);
228}
229// ----------------------------------------------------------------------------
230
231bool Sphere_impl::isInside(const Vector &point) const{
232 return point.NormSquared() <= 1. + MYEPSILON;
233}
234
235bool Sphere_impl::isOnSurface(const Vector &point) const{
236 return fabs(point.NormSquared()-1.)<MYEPSILON;
237}
238
239Vector Sphere_impl::getNormal(const Vector &point) const throw(NotOnSurfaceException){
240 if(!isOnSurface(point)){
241 throw NotOnSurfaceException() << ShapeVector(&point);
242 }
243 return point;
244}
245
246Vector Sphere_impl::getCenter() const
247{
248 return Vector(0.,0.,0.);
249}
250
251double Sphere_impl::getRadius() const
252{
253 return 1.;
254}
255
256double Sphere_impl::getVolume() const
257{
258 return (4./3.)*M_PI; // 4/3 pi r^3
259}
260
261double Sphere_impl::getSurfaceArea() const
262{
263 return 2.*M_PI; // 2 pi r^2
264}
265
266
267LineSegmentSet Sphere_impl::getLineIntersections(const Line &line) const{
268 LineSegmentSet res(line);
269 std::vector<Vector> intersections = line.getSphereIntersections();
270 if(intersections.size()==2){
271 res.insert(LineSegment(intersections[0],intersections[1]));
272 }
273 return res;
274}
275
276std::string Sphere_impl::toString() const{
277 return "Sphere()";
278}
279
280enum ShapeType Sphere_impl::getType() const
281{
282 return SphereType;
283}
284
285/**
286 * algorithm taken from http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere
287 * \param N number of points on surface
288 */
289std::vector<Vector> Sphere_impl::getHomogeneousPointsOnSurface(const size_t N) const
290{
291 std::vector<Vector> PointsOnSurface;
292 if (true) {
293 // Exactly N points but not symmetric.
294
295 // This formula is derived by finding a curve on the sphere that spirals down from
296 // the north pole to the south pole keeping a constant distance between consecutive turns.
297 // The curve is then parametrized by arch length and evaluated in constant intervals.
298 double a = sqrt(N) * 2;
299 for (size_t i=0; i<N; ++i){
300 double t0 = ((double)i + 0.5) / (double)N;
301 double t = (sqrt(t0) - sqrt(1.0 - t0) + 1.0) / 2.0 * M_PI;
302 Vector point;
303 point.Zero();
304 point[0] = sin(t) * sin(t * a);
305 point[1] = sin(t) * cos(t * a);
306 point[2] = cos(t);
307 PointsOnSurface.push_back(point);
308 }
309 ASSERT(PointsOnSurface.size() == N,
310 "Sphere_impl::getHomogeneousPointsOnSurface() did not create "
311 +::toString(N)+" but "+::toString(PointsOnSurface.size())+" points.");
312 } else {
313 // Symmetric but only approximately N points.
314 double a=4*M_PI/N;
315 double d= sqrt(a);
316 size_t Mtheta=round(M_PI/d);
317 double dtheta=M_PI/Mtheta;
318 double dphi=a/dtheta;
319 for (size_t m=0; m<Mtheta; ++m)
320 {
321 double theta=M_PI*(m+0.5)/Mtheta;
322 size_t Mphi=round(2*M_PI*sin(theta)/dphi);
323 for (size_t n=0; n<Mphi;++n)
324 {
325 double phi= 2*M_PI*n/Mphi;
326 Vector point;
327 point.Zero();
328 point[0]=sin(theta)*cos(phi);
329 point[1]=sin(theta)*sin(phi);
330 point[2]=cos(theta);
331 PointsOnSurface.push_back(point);
332 }
333 }
334 }
335 return PointsOnSurface;
336}
337
338std::vector<Vector> Sphere_impl::getHomogeneousPointsInVolume(const size_t N) const {
339 ASSERT(0,
340 "Sphere_impl::getHomogeneousPointsInVolume() - not implemented.");
341 return std::vector<Vector>();
342}
343
344Shape Sphere(){
345 Shape::impl_ptr impl = Shape::impl_ptr(new Sphere_impl());
346 return Shape(impl);
347}
348
349Shape Sphere(const Vector &center,double radius){
350 return translate(resize(Sphere(),radius),center);
351}
352
353Shape Ellipsoid(const Vector &center, const Vector &radius){
354 return translate(stretch(Sphere(),radius),center);
355}
356
357bool Cuboid_impl::isInside(const Vector &point) const{
358 return (point[0]>=-MYEPSILON && point[0]<=1+MYEPSILON) && (point[1]>=-MYEPSILON && point[1]<=1+MYEPSILON) && (point[2]>=-MYEPSILON && point[2]<=1+MYEPSILON);
359}
360
361bool Cuboid_impl::isOnSurface(const Vector &point) const{
362 bool retVal = isInside(point);
363 // test all borders of the cuboid
364 // double fabs
365 retVal = retVal &&
366 (((fabs(point[0]-1.) < MYEPSILON) || (fabs(point[0]) < MYEPSILON)) ||
367 ((fabs(point[1]-1.) < MYEPSILON) || (fabs(point[1]) < MYEPSILON)) ||
368 ((fabs(point[2]-1.) < MYEPSILON) || (fabs(point[2]) < MYEPSILON)));
369 return retVal;
370}
371
372Vector Cuboid_impl::getNormal(const Vector &point) const throw(NotOnSurfaceException){
373 if(!isOnSurface(point)){
374 throw NotOnSurfaceException() << ShapeVector(&point);
375 }
376 Vector res;
377 // figure out on which sides the Vector lies (maximum 3, when it is in a corner)
378 for(int i=NDIM;i--;){
379 if((fabs(point[i])<MYEPSILON) || (fabs(point[i]-1.)<MYEPSILON)){
380 // add the scaled (-1/+1) Vector to the set of surface vectors
381 res[i] = point[i] * 2.0 - 1.0;
382 }
383 }
384 ASSERT((fabs(res.NormSquared() - 1.) >= -MYEPSILON)
385 && (fabs(res.NormSquared() - 3.) >= -MYEPSILON),
386 "To many or to few sides found for this Vector");
387
388 res.Normalize();
389 return res;
390}
391
392
393Vector Cuboid_impl::getCenter() const
394{
395 return Vector(0.5,0.5,0.5);
396}
397
398double Cuboid_impl::getRadius() const
399{
400 return .5;
401}
402
403double Cuboid_impl::getVolume() const
404{
405 return 1.; // l^3
406}
407
408double Cuboid_impl::getSurfaceArea() const
409{
410 return 6.; // 6 * l^2
411}
412
413LineSegmentSet Cuboid_impl::getLineIntersections(const Line &line) const{
414 LineSegmentSet res(line);
415 // get the intersection on each of the six faces
416 std::vector<Vector> intersections;
417 intersections.resize(2);
418 int c=0;
419 int x[2]={-1,+1};
420 for(int i=NDIM;i--;){
421 for(int j=0;j<2;++j){
422 if(c==2) goto end; // I know this sucks, but breaking two loops is stupid
423 Vector base;
424 base[i]=x[j];
425 // base now points to the surface and is normal to it at the same time
426 Plane p(base,base);
427 Vector intersection = p.GetIntersection(line);
428 if(isInside(intersection)){
429 // if we have a point on the edge it might already be contained
430 if(c==1 && intersections[0]==intersection)
431 continue;
432 intersections[c++]=intersection;
433 }
434 }
435 }
436 end:
437 if(c==2){
438 res.insert(LineSegment(intersections[0],intersections[1]));
439 }
440 return res;
441}
442
443std::string Cuboid_impl::toString() const{
444 return "Cuboid()";
445}
446
447enum ShapeType Cuboid_impl::getType() const
448{
449 return CuboidType;
450}
451
452/**
453 * \param N number of points on surface
454 */
455std::vector<Vector> Cuboid_impl::getHomogeneousPointsOnSurface(const size_t N) const {
456 std::vector<Vector> PointsOnSurface;
457 // sides
458 int n = sqrt((N - 1) / 6) + 1;
459 for (int i=0; i<=n; i++){
460 double ii = (double)i / (double)n;
461 for (int k=0; k<n; k++){
462 double kk = (double)k / (double)n;
463 PointsOnSurface.push_back(Vector(ii, kk, 1));
464 PointsOnSurface.push_back(Vector(ii, 1, 1-kk));
465 PointsOnSurface.push_back(Vector(ii, 1-kk, 0));
466 PointsOnSurface.push_back(Vector(ii, 0, kk));
467 }
468 }
469 // top and bottom
470 for (int i=1; i<n; i++){
471 double ii = (double)i / (double)n;
472 for (int k=1; k<n; k++){
473 double kk = (double)k / (double)n;
474 PointsOnSurface.push_back(Vector(0, ii, kk));
475 PointsOnSurface.push_back(Vector(1, ii, kk));
476 }
477 }
478 return PointsOnSurface;
479}
480
481std::vector<Vector> Cuboid_impl::getHomogeneousPointsInVolume(const size_t N) const {
482 ASSERT(0,
483 "Cuboid_impl::getHomogeneousPointsInVolume() - not implemented.");
484 return std::vector<Vector>();
485}
486
487Shape Cuboid(){
488 Shape::impl_ptr impl = Shape::impl_ptr(new Cuboid_impl());
489 return Shape(impl);
490}
491
492Shape Cuboid(const Vector &corner1, const Vector &corner2){
493 // make sure the two edges are upper left front and lower right back
494 Vector sortedC1;
495 Vector sortedC2;
496 for(int i=NDIM;i--;){
497 sortedC1[i] = std::min(corner1[i],corner2[i]);
498 sortedC2[i] = std::max(corner1[i],corner2[i]);
499 ASSERT(corner1[i]!=corner2[i],"Given points for cuboid edges did not define a valid space");
500 }
501 // get the middle point
502 Vector middle = (1./2.)*(sortedC1+sortedC2);
503 Vector factors = sortedC2-middle;
504 return translate(stretch(Cuboid(),factors),middle);
505}
Note: See TracBrowser for help on using the repository browser.