/*
* Project: MoleCuilder
* Description: creates and alters molecular systems
* Copyright (C) 2010-2012 University of Bonn. All rights reserved.
*
*
* This file is part of MoleCuilder.
*
* MoleCuilder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* MoleCuilder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MoleCuilder. If not, see .
*/
/*
* Shape.cpp
*
* Created on: Jun 18, 2010
* Author: crueger
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Assert.hpp"
#include "LinearAlgebra/Vector.hpp"
#include "Shapes/Shape.hpp"
#include "Shapes/Shape_impl.hpp"
#include "Shapes/ShapeExceptions.hpp"
#include "Shapes/ShapeType.hpp"
#include "Tesselation/ApproximateShapeArea.hpp"
#include "Tesselation/ApproximateShapeVolume.hpp"
#include
#include
#include
#include
Shape::Shape(const Shape& src) :
impl(src.getImpl()), name(src.getName())
{}
Shape::~Shape(){}
bool Shape::isInside(const Vector &point) const{
return impl->isInside(point);
}
bool Shape::isOnSurface(const Vector &point) const{
return impl->isOnSurface(point);
}
Vector Shape::getNormal(const Vector &point) const throw (NotOnSurfaceException){
return impl->getNormal(point);
}
Vector Shape::getCenter() const{
return impl->getCenter();
}
double Shape::getRadius() const{
return impl->getRadius();
}
void Shape::setName(const std::string &_name){
name = _name;
}
std::string Shape::getName() const{
return name;
}
/** Returns the volume of the Shape.
*
* If the underlying implementation does not have a working implementation,
* i.e. returns -1., then we use an approximate method to calculate the
* volume via a mesh of grid points and checking for isInside (basically
* a Monte-Carlo integration of the volume).
*
* \return volume of the shape
*/
double Shape::getVolume() const
{
const double volume = impl->getVolume();
if (volume != -1.) {
return volume;
} else {
ApproximateShapeVolume Approximator(*this);
return Approximator();
}
}
/** Returns the surface area of the Shape.
*
* If the underlying implementation does not have a working implementation,
* i.e. returns -1., then we use the working filling of the shapes surface
* with points and subsequent tesselation and obtaining the approximate
* surface area therefrom.
*
* @return surface area of the Shape
*/
double Shape::getSurfaceArea() const
{
const double surfacearea = impl->getSurfaceArea();
if (surfacearea != -1.) {
return surfacearea;
} else {
ApproximateShapeArea Approximator(*this);
return Approximator();
}
}
LineSegmentSet Shape::getLineIntersections(const Line &line) const{
return impl->getLineIntersections(line);
}
std::vector Shape::getHomogeneousPointsOnSurface(const size_t N) const {
return impl->getHomogeneousPointsOnSurface(N);
}
std::vector Shape::getHomogeneousPointsInVolume(const size_t N) const {
return impl->getHomogeneousPointsInVolume(N);
}
Shape::Shape(Shape::impl_ptr _impl) :
impl(_impl)
{}
Shape &Shape::operator=(const Shape& rhs){
if(&rhs!=this){
impl=rhs.getImpl();
}
return *this;
}
bool Shape::operator==(const Shape &rhs) const{
return (this->getType() == rhs.getType());
}
std::string Shape::toString() const {
return impl->toString();
}
enum ShapeType Shape::getType() const{
return impl->getType();
}
Shape::impl_ptr Shape::getImpl() const{
return impl;
}
// allows arbitrary friendship, but only if implementation is known
Shape::impl_ptr getShapeImpl(const Shape &shape){
return shape.getImpl();
}
/***************************** Some simple Shapes ***************************/
Shape Everywhere(){
static Shape::impl_ptr impl = Shape::impl_ptr(new Everywhere_impl());
return Shape(impl);
}
Shape Nowhere(){
static Shape::impl_ptr impl = Shape::impl_ptr(new Nowhere_impl());
return Shape(impl);
}
/****************************** Operators ***********************************/
// AND
AndShape_impl::AndShape_impl(const Shape::impl_ptr &_lhs, const Shape::impl_ptr &_rhs) :
lhs(_lhs),
rhs(_rhs),
radius(NULL, boost::bind(&AndShape_impl::calculateRadius, this), "radius"),
center(NULL, boost::bind(&AndShape_impl::calculateCenter, this), "center")
{}
AndShape_impl::~AndShape_impl(){}
bool AndShape_impl::isInside(const Vector &point) const{
return lhs->isInside(point) && rhs->isInside(point);
}
bool AndShape_impl::isOnSurface(const Vector &point) const{
// check the number of surfaces that this point is on
int surfaces =0;
surfaces += lhs->isOnSurface(point);
surfaces += rhs->isOnSurface(point);
switch(surfaces){
case 0:
return false;
// no break necessary
case 1:
// if it is inside for the object where it does not lie on
// the surface the whole point lies inside
return (lhs->isOnSurface(point) && rhs->isInside(point)) ||
(rhs->isOnSurface(point) && lhs->isInside(point));
// no break necessary
case 2:
{
// it lies on both Shapes... could be an edge or an inner point
// test the direction of the normals
Vector direction=lhs->getNormal(point)+rhs->getNormal(point);
// if the directions are opposite we lie on the inside
return !direction.IsZero();
}
// no break necessary
default:
// if this happens there is something wrong
ASSERT(0,"Default case should have never been used");
}
return false; // never reached
}
Vector AndShape_impl::getNormal(const Vector &point) const throw (NotOnSurfaceException){
Vector res;
if(!isOnSurface(point)){
throw NotOnSurfaceException() << ShapeVector(&point);
}
res += lhs->isOnSurface(point)?lhs->getNormal(point):zeroVec;
res += rhs->isOnSurface(point)?rhs->getNormal(point):zeroVec;
res.Normalize();
return res;
}
Vector AndShape_impl::calculateCenter() const
{
// calculate closest position on sphere surface to other center ..
const Vector rhsDistance = rhs->getCenter() + rhs->getRadius()*((lhs->getCenter() - rhs->getCenter()).getNormalized());
const Vector lhsDistance = lhs->getCenter() + lhs->getRadius()*((rhs->getCenter() - lhs->getCenter()).getNormalized());
// .. and then it's right in between those two
return 0.5*(rhsDistance + lhsDistance);
}
double AndShape_impl::calculateRadius() const
{
const double distance = (lhs->getCenter() - rhs->getCenter()).Norm();
const double minradii = std::min(lhs->getRadius(), rhs->getRadius());
// if no intersection
if (distance > (lhs->getRadius() + rhs->getRadius()))
return 0.;
else // if intersection it can only be the smaller one
return minradii;
}
double AndShape_impl::getVolume() const
{
// TODO
return -1.;
}
double AndShape_impl::getSurfaceArea() const
{
// TODO
return -1.;
}
LineSegmentSet AndShape_impl::getLineIntersections(const Line &line) const{
return intersect(lhs->getLineIntersections(line),rhs->getLineIntersections(line));
}
std::string AndShape_impl::toString() const{
return std::string("(") + lhs->toString() + std::string("&&") + rhs->toString() + std::string(")");
}
enum ShapeType AndShape_impl::getType() const{
return CombinedType;
}
std::vector AndShape_impl::getHomogeneousPointsOnSurface(const size_t N) const {
std::vector PointsOnSurface_lhs = lhs->getHomogeneousPointsOnSurface(N);
std::vector PointsOnSurface_rhs = rhs->getHomogeneousPointsOnSurface(N);
std::vector PointsOnSurface;
for (std::vector::const_iterator iter = PointsOnSurface_lhs.begin(); iter != PointsOnSurface_lhs.end(); ++iter) {
if (rhs->isInside(*iter))
PointsOnSurface.push_back(*iter);
}
for (std::vector::const_iterator iter = PointsOnSurface_rhs.begin(); iter != PointsOnSurface_rhs.end(); ++iter) {
if (lhs->isInside(*iter))
PointsOnSurface.push_back(*iter);
}
return PointsOnSurface;
}
std::vector AndShape_impl::getHomogeneousPointsInVolume(const size_t N) const {
ASSERT(0,
"AndShape_impl::getHomogeneousPointsInVolume() - not implemented.");
return std::vector();
}
Shape operator&&(const Shape &lhs,const Shape &rhs){
Shape::impl_ptr newImpl = Shape::impl_ptr(new AndShape_impl(getShapeImpl(lhs),getShapeImpl(rhs)));
return Shape(newImpl);
}
// OR
OrShape_impl::OrShape_impl(const Shape::impl_ptr &_lhs, const Shape::impl_ptr &_rhs) :
lhs(_lhs),
rhs(_rhs),
radius(NULL, boost::bind(&OrShape_impl::calculateRadius, this), "radius"),
center(NULL, boost::bind(&OrShape_impl::calculateCenter, this), "center")
{}
OrShape_impl::~OrShape_impl(){}
bool OrShape_impl::isInside(const Vector &point) const{
return rhs->isInside(point) || lhs->isInside(point);
}
bool OrShape_impl::isOnSurface(const Vector &point) const{
// check the number of surfaces that this point is on
int surfaces =0;
surfaces += lhs->isOnSurface(point);
surfaces += rhs->isOnSurface(point);
switch(surfaces){
case 0:
return false;
// no break necessary
case 1:
// if it is inside for the object where it does not lie on
// the surface the whole point lies inside
return (lhs->isOnSurface(point) && !rhs->isInside(point)) ||
(rhs->isOnSurface(point) && !lhs->isInside(point));
// no break necessary
case 2:
{
// it lies on both Shapes... could be an edge or an inner point
// test the direction of the normals
Vector direction=lhs->getNormal(point)+rhs->getNormal(point);
// if the directions are opposite we lie on the inside
return !direction.IsZero();
}
// no break necessary
default:
// if this happens there is something wrong
ASSERT(0,"Default case should have never been used");
}
return false; // never reached
}
Vector OrShape_impl::getNormal(const Vector &point) const throw (NotOnSurfaceException){
Vector res;
if(!isOnSurface(point)){
throw NotOnSurfaceException() << ShapeVector(&point);
}
res += lhs->isOnSurface(point)?lhs->getNormal(point):zeroVec;
res += rhs->isOnSurface(point)?rhs->getNormal(point):zeroVec;
res.Normalize();
return res;
}
Vector OrShape_impl::calculateCenter() const
{
// calculate furthest position on sphere surface to other center ..
const Vector rhsDistance = rhs->getCenter() + rhs->getRadius()*((rhs->getCenter() - lhs->getCenter()).getNormalized());
const Vector lhsDistance = lhs->getCenter() + lhs->getRadius()*((lhs->getCenter() - rhs->getCenter()).getNormalized());
// .. and then it's right in between those two
return .5*(rhsDistance + lhsDistance);
}
double OrShape_impl::calculateRadius() const
{
const Vector rhsDistance = rhs->getCenter() + rhs->getRadius()*((rhs->getCenter() - lhs->getCenter()).getNormalized());
const Vector lhsDistance = lhs->getCenter() + lhs->getRadius()*((lhs->getCenter() - rhs->getCenter()).getNormalized());
return .5*(rhsDistance - lhsDistance).Norm();
}
double OrShape_impl::getVolume() const
{
// TODO
return -1.;
}
double OrShape_impl::getSurfaceArea() const
{
// TODO
return -1.;
}
LineSegmentSet OrShape_impl::getLineIntersections(const Line &line) const{
return merge(lhs->getLineIntersections(line),rhs->getLineIntersections(line));
}
std::string OrShape_impl::toString() const{
return std::string("(") + lhs->toString() + std::string("||") + rhs->toString() + std::string(")");
}
enum ShapeType OrShape_impl::getType() const{
return CombinedType;
}
std::vector OrShape_impl::getHomogeneousPointsOnSurface(const size_t N) const {
std::vector PointsOnSurface_lhs = lhs->getHomogeneousPointsOnSurface(N);
std::vector PointsOnSurface_rhs = rhs->getHomogeneousPointsOnSurface(N);
std::vector PointsOnSurface;
for (std::vector::const_iterator iter = PointsOnSurface_lhs.begin(); iter != PointsOnSurface_lhs.end(); ++iter) {
if (!rhs->isInside(*iter))
PointsOnSurface.push_back(*iter);
}
for (std::vector::const_iterator iter = PointsOnSurface_rhs.begin(); iter != PointsOnSurface_rhs.end(); ++iter) {
if (!lhs->isInside(*iter))
PointsOnSurface.push_back(*iter);
}
return PointsOnSurface;
}
std::vector OrShape_impl::getHomogeneousPointsInVolume(const size_t N) const {
ASSERT(0,
"OrShape_impl::getHomogeneousPointsInVolume() - not implemented.");
return std::vector();
}
Shape operator||(const Shape &lhs,const Shape &rhs){
Shape::impl_ptr newImpl = Shape::impl_ptr(new OrShape_impl(getShapeImpl(lhs),getShapeImpl(rhs)));
return Shape(newImpl);
}
// NOT
NotShape_impl::NotShape_impl(const Shape::impl_ptr &_arg) :
arg(_arg)
{}
NotShape_impl::~NotShape_impl(){}
bool NotShape_impl::isInside(const Vector &point) const{
return !arg->isInside(point);
}
bool NotShape_impl::isOnSurface(const Vector &point) const{
return arg->isOnSurface(point);
}
Vector NotShape_impl::getNormal(const Vector &point) const throw(NotOnSurfaceException){
return -1.*arg->getNormal(point);
}
Vector NotShape_impl::getCenter() const
{
return arg->getCenter();
}
double NotShape_impl::getRadius() const
{
return std::numeric_limits::infinity();
}
double NotShape_impl::getVolume() const
{
// TODO
return -1.; //-arg->getVolume();
}
double NotShape_impl::getSurfaceArea() const
{
// TODO
return -1.; // -arg->getSurfaceArea();
}
LineSegmentSet NotShape_impl::getLineIntersections(const Line &line) const{
return invert(arg->getLineIntersections(line));
}
std::string NotShape_impl::toString() const{
return std::string("!") + arg->toString();
}
enum ShapeType NotShape_impl::getType() const{
return CombinedType;
}
std::vector NotShape_impl::getHomogeneousPointsOnSurface(const size_t N) const {
// surfaces are the same, only normal direction is different
return arg->getHomogeneousPointsOnSurface(N);
}
std::vector NotShape_impl::getHomogeneousPointsInVolume(const size_t N) const {
ASSERT(0,
"NotShape_impl::getHomogeneousPointsInVolume() - not implemented.");
return std::vector();
}
Shape operator!(const Shape &arg){
Shape::impl_ptr newImpl = Shape::impl_ptr(new NotShape_impl(getShapeImpl(arg)));
return Shape(newImpl);
}
/**************** global operations *********************************/
std::ostream &operator<<(std::ostream &ost,const Shape &shape){
ost << shape.toString();
return ost;
}