#include <openbabel/mol.h>
#include <openbabel/atom.h>
#include <openbabel/obconversion.h>
#include <openbabel/obmolecformat.h>
#include <openbabel/elements.h>
#include <openbabel/obiter.h>
#include <openbabel/data.h>
#include <openbabel/bitvec.h>
#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <cstdlib>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
using namespace std;
namespace OpenBabel
{
class PointCloudFormat : public OpenBabel::OBMoleculeFormat
{
public:
PointCloudFormat()
{
OpenBabel::OBConversion::RegisterFormat( "pointcloud", this );
}
virtual const char* Description() {
return
"Point cloud on VDW surface\n"
"Generates a point cloud on the VDW surface around the molecule\n\n"
"The surface location is calculated by adding the probe atom radius\n"
"(if specified) to the Van der Waal radius of the particular atom multipled\n"
"by the specified multiple (1.0 if unspecified)."
"Output is a list of {x,y,z} tuples in Angstrom. Alternatively, if the ``x``\n"
"option is specified, the :ref:`XYZ_cartesian_coordinates_format` is used\n"
"instead.\n\n"
"Write Options, e.g. -xx\n"
" r <radii> create a surface for each VDS radius (default 1.0)\n"
" A comma-separated list of VDW radius multiples\n"
" d <densities> for each surface, specify the point density (default 1.0 Angstrom^2)\n"
" A comma-separated list of densities\n"
" p <radius> radius of the probe atom in Angstrom (default 0.0)\n"
" x output in xyz format\n\n";
}
virtual const char* SpecificationURL()
{
return "N/A";
}
virtual const char* GetMIMEType() { return "chemical/x-pointcloud"; };
virtual unsigned int Flags()
{
return WRITEONEONLY | NOTREADABLE;
};
virtual int SkipObjects( int n, OpenBabel::OBConversion* pConv ) { return 0; }
virtual bool ReadMolecule( OpenBabel::OBBase*, OpenBabel::OBConversion* )
{
return false;
}
virtual bool WriteMolecule( OpenBabel::OBBase* , OpenBabel::OBConversion* );
};
PointCloudFormat thePointCloudFormat;
double rv( void ){
return ((double)std::rand()) / RAND_MAX; }
vector3 surface_point( double cx, double cy, double cz, double radius ) {
double theta = rv() * 2. * M_PI;
double phi = acos( 2. * rv() - 1. );
double x = radius * cos(theta) * sin(phi);
double y = radius * sin(theta) * sin(phi);
double z = radius * cos(phi);
return vector3( cx+x, cy+y, cz+z );
}
bool conditional_add( vector<vector3> &list, vector3 point, double density_r ) {
double density_r2 = density_r * density_r;
for( std::vector<vector3>::iterator it = list.begin(); it != list.end(); ++it ) {
vector3 r = *it - point;
double r2 = dot(r,r);
if( r2 < density_r2 ) {
return false;
}
}
list.push_back( point );
return true;
}
bool PointCloudFormat::WriteMolecule( OBBase* pOb, OBConversion* pConv )
{
OBMol* pmol = dynamic_cast< OBMol* >(pOb);
if (pmol == nullptr) return false;
ostream& os = *pConv->GetOutStream();
const char *radius_list_str = nullptr;
const char *density_list_str = nullptr;
double probe_radius = 0.;
bool format_xyz = false;
if( pConv->IsOption( "r" ) ) {
radius_list_str = pConv->IsOption("r", OBConversion::OUTOPTIONS);
}
if( pConv->IsOption( "d" ) ) {
density_list_str = pConv->IsOption("d", OBConversion::OUTOPTIONS);
}
if( pConv->IsOption( "p" ) ) {
probe_radius = atof( pConv->IsOption("p", OBConversion::OUTOPTIONS) );
if( !isfinite(probe_radius) || probe_radius < 0. ) { probe_radius = 0.; }
}
if( pConv->IsOption( "x" ) ) {
format_xyz = true;
}
int total_points = 0;
std::srand(0);
vector< vector3 > filtered_points;
vector<double> radius_mult_list;
vector<double> density_list;
if( radius_list_str ) {
char*a = strdup( radius_list_str );
const char * x = strtok( a, "," );
while( x ) {
double d = atof(x);
if( isfinite(d) && d>0. ) { radius_mult_list.push_back( d ); }
x = strtok(nullptr, ",");
}
free(a);
}
if( density_list_str ) {
char*a = strdup( density_list_str );
const char * x = strtok( a, "," );
while( x ) {
double d = atof(x);
if( isfinite(d) && d>0. ) { density_list.push_back( d ); }
x = strtok(nullptr, ",");
}
free(a);
}
if( radius_mult_list.size() == 0 ) { radius_mult_list.push_back( 1.0 ); }
while( density_list.size() < radius_mult_list.size()) {
density_list.push_back(1.0);
}
for( int j = 0; j < radius_mult_list.size(); j++ ) {
double radius_mult = radius_mult_list[j];
double density = density_list[j];
double density_r = sqrt ( density / M_PI );
FOR_ATOMS_OF_MOL( a, *pmol )
{
vector<vector3> pt;
const double* c = a->GetCoordinate();
double vdwrad = probe_radius + ( OBElements::GetVdwRad( a->GetAtomicNum() ) * radius_mult );
int estimate_number_of_points = (int) ( 0.6 * (( 4. * M_PI * M_PI * vdwrad * vdwrad ) / ( density )) );
int count = 0;
while ( count < estimate_number_of_points ) {
vector3 p = surface_point( c[0], c[1], c[2], vdwrad );
if( conditional_add( pt, p, density_r ) ) {
count++;
total_points++;
}
}
for( std::vector< vector3 >::iterator it = pt.begin(); it != pt.end(); ++it ) {
bool exclude = false;
FOR_ATOMS_OF_MOL( a, *pmol )
{
const double* c = a->GetCoordinate();
double vdwrad = probe_radius + ( OBElements::GetVdwRad( a->GetAtomicNum() ) * radius_mult );
vdwrad *= vdwrad;
vector3 r = *it - vector3( c[0], c[1], c[2] );
double r2 = r[0]*r[0] + r[1]*r[1] + r[2] * r[2];
if( r2 < vdwrad ) { exclude = true; break; }
}
if( !exclude ) {
filtered_points.push_back( *it );
}
}
}
}
if( format_xyz ) {
os << filtered_points.size() << "\n\n";
}
for( std::vector<vector3>::iterator it2 = filtered_points.begin(); it2 != filtered_points.end(); ++it2 ) {
if( format_xyz ) {
os << "Xx\t";
}
os << (*it2)[0] << "\t" << (*it2)[1] << "\t" << (*it2)[2] << "\n";
}
os.flush();
return true;
}
}