/**********************************************************************
Cube - Primitive class to encapsulate volumetric data
Copyright (C) 2008 Marcus D. Hanwell
This file is part of the Avogadro molecular editor project.
For more information, see
Avogadro 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.
Avogadro 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
**********************************************************************/
#include "cube.h"
#include
#include
#include
#include
namespace Avogadro {
using Eigen::Vector3i;
using Eigen::Vector3f;
using Eigen::Vector3d;
Cube::Cube(QObject *parent) : Primitive(CubeType, parent), m_data(0),
m_min(0.0, 0.0, 0.0), m_max(0.0, 0.0, 0.0), m_spacing(0.0, 0.0, 0.0),
m_points(0, 0, 0), m_minValue(0.0), m_maxValue(0.0)
{
}
Cube::~Cube()
{
}
bool Cube::setLimits(const Vector3d &min, const Vector3d &max,
const Vector3i &points)
{
// We can calculate all necessary properties and initialise our data
Vector3d delta = max - min;
m_spacing = Vector3d(delta.x() / (points.x()-1),
delta.y() / (points.y()-1),
delta.z() / (points.z()-1));
m_min = min;
m_max = max;
m_points = points;
m_data.resize(m_points.x() * m_points.y() * m_points.z());
return true;
}
bool Cube::setLimits(const Vector3d &min, const Vector3d &max,
double spacing)
{
Vector3i points;
Vector3d delta = max - min;
delta = delta / spacing;
points = Vector3i(delta.x(), delta.y(), delta.z());
return setLimits(min, max, points);
}
bool Cube::setLimits(const Vector3d &min, const Vector3i &dim,
double spacing)
{
Vector3d max = Vector3d(min.x() + (dim.x()-1) * spacing,
min.y() + (dim.y()-1) * spacing,
min.z() + (dim.z()-1) * spacing);
m_min = min;
m_max = max;
m_points = dim;
m_spacing = Vector3d(spacing, spacing, spacing);
m_data.resize(m_points.x() * m_points.y() * m_points.z());
return true;
}
bool Cube::setLimits(const Molecule *mol, double spacing, double padding)
{
QList atoms = mol->atoms();
Vector3d min, max;
if (atoms.size()) {
min = max = *(atoms.at(0)->pos());
foreach (Atom *atom, atoms) {
if (atom->pos()->x() < min.x())
min[0] = atom->pos()->x();
else if (atom->pos()->x() > max.x())
max(0) = atom->pos()->x();
if (atom->pos()->y() < min.y())
min(1) = atom->pos()->y();
else if (atom->pos()->y() > max.y())
max(1) = atom->pos()->y();
if (atom->pos()->z() < min.z())
min(2) = atom->pos()->z();
else if (atom->pos()->z() > max.z())
max(2) = atom->pos()->z();
}
} else {
min = max = Eigen::Vector3d::Zero();
}
// Now to take care of the padding term
min += Vector3d(-padding, -padding, -padding);
max += Vector3d(padding, padding, padding);
return setLimits(min, max, spacing);
}
bool Cube::setLimits(const Cube &cube)
{
m_min = cube.m_min;
m_max = cube.m_max;
m_points = cube.m_points;
m_spacing = cube.m_spacing;
m_data.resize(m_points.x() * m_points.y() * m_points.z());
return true;
}
std::vector * Cube::data()
{
return &m_data;
}
bool Cube::setData(const std::vector &values)
{
if (!values.size()) {
qDebug() << "Zero sized vector passed to Cube::setData. Nothing to do.";
return false;
}
if (static_cast(values.size()) == m_points.x() * m_points.y() * m_points.z()) {
m_data = values;
qDebug() << "Loaded in cube data" << m_data.size();
// Now to update the minimum and maximum values
m_minValue = m_maxValue = m_data[0];
foreach(double val, m_data) {
if (val < m_minValue)
m_minValue = val;
else if (val > m_maxValue)
m_maxValue = val;
}
return true;
}
else {
qDebug() << "The vector passed to Cube::setData does not have the correct"
<< "size. Expected" << m_points.x() * m_points.y() * m_points.z()
<< "got" << values.size();
return false;
}
}
bool Cube::addData(const std::vector &values)
{
// Initialise the cube to zero if necessary
if (!m_data.size()) {
m_data.resize(m_points.x() * m_points.y() * m_points.z());
}
if (values.size() != m_data.size() || !values.size()) {
qDebug() << "Attempted to add values to cube - sizes do not match...";
return false;
}
for (unsigned int i = 0; i < m_data.size(); i++) {
m_data[i] += values[i];
if (m_data[i] < m_minValue)
m_minValue = m_data[i];
else if (m_data[i] > m_maxValue)
m_maxValue = m_data[i];
}
return true;
}
unsigned int Cube::closestIndex(const Vector3d &pos) const
{
int i, j, k;
// Calculate how many steps each coordinate is along its axis
i = (pos.x() - m_min.x()) / m_spacing.x();
j = (pos.y() - m_min.y()) / m_spacing.y();
k = (pos.z() - m_min.z()) / m_spacing.z();
return i*m_points.y()*m_points.z() + j*m_points.z() + k;
}
Vector3i Cube::indexVector(const Vector3d &pos) const
{
// Calculate how many steps each coordinate is along its axis
int i, j, k;
i = (pos.x() - m_min.x()) / m_spacing.x();
j = (pos.y() - m_min.y()) / m_spacing.y();
k = (pos.z() - m_min.z()) / m_spacing.z();
return Vector3i(i, j, k);
}
Vector3d Cube::position(unsigned int index) const
{
int x, y, z;
x = static_cast(index / (m_points.y()*m_points.z()));
y = static_cast((index - (x*m_points.y()*m_points.z())) / m_points.z());
z = index % m_points.z();
return Vector3d(x * m_spacing.x() + m_min.x(),
y * m_spacing.y() + m_min.y(),
z * m_spacing.z() + m_min.z());
}
double Cube::value(int i, int j, int k) const
{
unsigned int index = i*m_points.y()*m_points.z() + j*m_points.z() + k;
if (index < m_data.size())
return m_data.at(index);
else {
// qDebug() << "Attempt to identify out of range index" << index << m_data.size();
return 0.0;
}
}
double Cube::value(const Vector3i &pos) const
{
unsigned int index = pos.x()*m_points.y()*m_points.z() +
pos.y()*m_points.z() +
pos.z();
if (index < m_data.size())
return m_data.at(index);
else {
qDebug() << "Attempted to access an index out of range.";
return 6969.0;
}
}
float Cube::valuef(const Vector3f &pos) const
{
// This is a really expensive operation and so should be avoided
// Interpolate the value at the supplied vector - trilinear interpolation...
Vector3f delta = pos - m_min.cast();
// Find the integer low and high corners
Vector3i lC(delta.x() / m_spacing.x(),
delta.y() / m_spacing.y(),
delta.z() / m_spacing.z());
Vector3i hC(lC.x() + 1,
lC.y() + 1,
lC.z() + 1);
// So there are six corners in total - work out the delta of the position
// and the low corner
Vector3f P((delta.x() - lC.x()*m_spacing.x()) / m_spacing.x(),
(delta.y() - lC.y()*m_spacing.y()) / m_spacing.y(),
(delta.z() - lC.z()*m_spacing.z()) / m_spacing.z());
Vector3f dP = Vector3f(1.0, 1.0, 1.0) - P;
// Now calculate and return the interpolated value
return value(lC.x(), lC.y(), lC.z()) * dP.x() * dP.y() * dP.z() +
value(hC.x(), lC.y(), lC.z()) * P.x() * dP.y() * dP.z() +
value(lC.x(), hC.y(), lC.z()) * dP.x() * P.y() * dP.z() +
value(lC.x(), lC.y(), hC.z()) * dP.x() * dP.y() * P.z() +
value(hC.x(), lC.y(), hC.z()) * P.x() * dP.y() * P.z() +
value(lC.x(), hC.y(), hC.z()) * dP.x() * P.y() * P.z() +
value(hC.x(), hC.y(), lC.z()) * P.x() * P.y() * dP.z() +
value(hC.x(), hC.y(), hC.z()) * P.x() * P.y() * P.z();
}
double Cube::value(const Vector3d &pos) const
{
// This is a really expensive operation and so should be avoided
// Interpolate the value at the supplied vector - trilinear interpolation...
Vector3d delta = pos - m_min;
// Find the integer low and high corners
Vector3i lC(delta.x() / m_spacing.x(),
delta.y() / m_spacing.y(),
delta.z() / m_spacing.z());
Vector3i hC(lC.x() + 1,
lC.y() + 1,
lC.z() + 1);
// So there are six corners in total - work out the delta of the position
// and the low corner
Vector3d P((delta.x() - lC.x()*m_spacing.x()) / m_spacing.x(),
(delta.y() - lC.y()*m_spacing.y()) / m_spacing.y(),
(delta.z() - lC.z()*m_spacing.z()) / m_spacing.z());
Vector3d dP = Vector3d(1.0, 1.0, 1.0) - P;
// Now calculate and return the interpolated value
return value(lC.x(), lC.y(), lC.z()) * dP.x() * dP.y() * dP.z() +
value(hC.x(), lC.y(), lC.z()) * P.x() * dP.y() * dP.z() +
value(lC.x(), hC.y(), lC.z()) * dP.x() * P.y() * dP.z() +
value(lC.x(), lC.y(), hC.z()) * dP.x() * dP.y() * P.z() +
value(hC.x(), lC.y(), hC.z()) * P.x() * dP.y() * P.z() +
value(lC.x(), hC.y(), hC.z()) * dP.x() * P.y() * P.z() +
value(hC.x(), hC.y(), lC.z()) * P.x() * P.y() * dP.z() +
value(hC.x(), hC.y(), hC.z()) * P.x() * P.y() * P.z();
}
bool Cube::setValue(int i, int j, int k, double value)
{
unsigned int index = i*m_points.y()*m_points.z() + j*m_points.z() + k;
if (index < m_data.size()) {
m_data[index] = value;
return true;
}
else
return false;
}
} // End namespace Avogadro
#include "cube.moc"