768dcc508d
Correction following bug posted on the forum "Crash when a grain is only 2 points"
1286 lines
38 KiB
C++
1286 lines
38 KiB
C++
#include "GrainsAsEllipsoids.h"
|
|
|
|
/// qCC_db
|
|
#include <ccPointCloud.h>
|
|
#include <ccGLMatrix.h>
|
|
#include <ccSerializableObject.h>
|
|
|
|
#include <QCoreApplication>
|
|
#include <QDir>
|
|
#include <QOpenGLShaderProgram>
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
#include <G3PointAction.h>
|
|
|
|
// from GeometricTools/GTE
|
|
// #include <Mathematics/DistPointHyperellipsoid.h>
|
|
// #include <Mathematics/Vector2.h>
|
|
// #include <Mathematics/Vector3.h>
|
|
|
|
GrainsAsEllipsoids::GrainsAsEllipsoids(ccMainAppInterface *app)
|
|
: m_app(app)
|
|
{
|
|
assert(m_app);
|
|
|
|
this->setMetaData("class_name", "GrainsAsEllipsoids");
|
|
this->setMetaData("plugin_name", "G3Point");
|
|
|
|
setShaderPath();
|
|
}
|
|
|
|
GrainsAsEllipsoids::~GrainsAsEllipsoids(){}
|
|
|
|
GrainsAsEllipsoids::GrainsAsEllipsoids(ccPointCloud *cloud, ccMainAppInterface *app, const std::vector<std::vector<int> >& stacks, const RGBAColorsTableType& colors)
|
|
: m_cloud(cloud)
|
|
, m_app(app)
|
|
, m_stacks(stacks)
|
|
{
|
|
this->setMetaData("class_name", "GrainsAsEllipsoids");
|
|
this->setMetaData("plugin_name", "G3Point");
|
|
|
|
setShaderPath();
|
|
setGrainColorsTable(colors);
|
|
|
|
m_center.resize(m_stacks.size());
|
|
m_radii.resize(m_stacks.size());
|
|
m_rotationMatrix.resize(m_stacks.size());
|
|
|
|
// fit all ellipsoids
|
|
std::cout << "[GrainsAsEllipsoids::GrainsAsEllipsoids] fit " << stacks.size() << " ellipsoids" << std::endl;
|
|
|
|
lockVisibility(false);
|
|
setVisible(true);
|
|
|
|
m_ccBBoxAll.setValidity(false);
|
|
m_ccBBoxAll.clear();
|
|
|
|
for (int idx = 0; idx < m_stacks.size(); idx++)
|
|
{
|
|
if (!fitEllipsoidToGrain(idx, m_center[idx], m_radii[idx], m_rotationMatrix[idx]))
|
|
{
|
|
m_fitNotOK.insert(idx);
|
|
ccLog::Warning("[GrainsAsEllipsoids::GrainsAsEllipsoids] fit not possible for grain " + QString::number(idx)
|
|
+ " of size " + QString::number(m_stacks[idx].size()));
|
|
}
|
|
else
|
|
{ // update the bounding box
|
|
float maxRadius = m_radii[idx].maxCoeff();
|
|
CCVector3 center(m_center[idx](0), m_center[idx](1), m_center[idx](2));
|
|
m_ccBBoxAll.add(CCVector3(center.x + maxRadius, center.y + maxRadius, center.z + maxRadius));
|
|
m_ccBBoxAll.add(CCVector3(center.x - maxRadius, center.y - maxRadius, center.z - maxRadius));
|
|
}
|
|
}
|
|
|
|
// remove data corresponding to stacks were the fit was not successful
|
|
for (auto el : m_fitNotOK)
|
|
{
|
|
// if the fit is not OK, we use the centroid as a center
|
|
int nPoints = static_cast<int>(m_stacks[el].size());
|
|
Eigen::MatrixX3d points(nPoints, 3);
|
|
for (int index = 0; index < nPoints; index++)
|
|
{
|
|
const CCVector3* point = m_cloud->getPoint(m_stacks[el][index]);
|
|
points(index, 0) = point->x;
|
|
points(index, 1) = point->y;
|
|
points(index, 2) = point->z;
|
|
}
|
|
// compute the centroid of the label
|
|
Eigen::RowVector3d centroid = points.colwise().mean();
|
|
m_center[el] << centroid.x(), centroid.y(), centroid.z();
|
|
|
|
m_radii[el].fill(0);
|
|
|
|
m_rotationMatrix[el].fill(NAN);
|
|
}
|
|
|
|
m_ccBBoxAll.setValidity(true);
|
|
}
|
|
|
|
void GrainsAsEllipsoids::setShaderPath()
|
|
{
|
|
QDir appDir = QCoreApplication::applicationDirPath();
|
|
m_shaderPath = (appDir.absolutePath() + "/shaders/G3Point");
|
|
|
|
QCoreApplication::instance();
|
|
}
|
|
|
|
void GrainsAsEllipsoids::setGrainColorsTable(const RGBAColorsTableType& colorTable)
|
|
{
|
|
m_grainColors.resize(colorTable.size());
|
|
|
|
for (int k = 0; k < colorTable.size(); k++)
|
|
{
|
|
ccColor::Rgba color = colorTable[k];
|
|
m_grainColors[k] = CCVector3f(static_cast<float>(color.r) / ccColor::MAX,
|
|
static_cast<float>(color.g) / ccColor::MAX,
|
|
static_cast<float>(color.b) / ccColor::MAX);
|
|
}
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::exportResultsAsCloud()
|
|
{
|
|
// create cloud
|
|
QString cloudName = "g3point_results";
|
|
ccPointCloud *cloud = new ccPointCloud(cloudName);
|
|
|
|
for (int idx = 0; idx < m_center.size(); idx++)
|
|
{
|
|
// if (m_fitNotOK.count(idx))
|
|
// {
|
|
// continue;
|
|
// }
|
|
Eigen::Vector3f center {m_center[idx].x(), m_center[idx].y(), m_center[idx].z()};
|
|
Eigen::Vector3f point = center;
|
|
CCVector3 ccPoint(point(0), point(1), point(2));
|
|
cloud->addPoint(ccPoint);
|
|
}
|
|
|
|
//allocate colors if necessary
|
|
if (cloud->resizeTheRGBTable())
|
|
{
|
|
for (unsigned int index = 0; index < cloud->size(); index++)
|
|
{
|
|
ccColor::Rgb color(m_grainColors[index].x * ccColor::MAX * 0.8,
|
|
m_grainColors[index].y * ccColor::MAX * 0.8,
|
|
m_grainColors[index].z * ccColor::MAX * 0.8);
|
|
cloud->setPointColor(index, color);
|
|
}
|
|
}
|
|
|
|
int sfIdx;
|
|
CCCoreLib::ScalarField* sf;
|
|
|
|
// EXPORT g3point_index
|
|
sfIdx = cloud->addScalarField("g3point_index");
|
|
if (sfIdx == -1)
|
|
{
|
|
ccLog::Error("[GrainsAsEllipsoids::exportResultsAsCloud] impossible to allocate g3point_index scalar field");
|
|
return false;
|
|
}
|
|
sf = cloud->getScalarField(sfIdx);
|
|
int indexInResults = 0;
|
|
for (int index = 0; index < m_center.size(); index++)
|
|
{
|
|
// if (m_fitNotOK.count(index)) // when the fit was not successful, the point is not exported
|
|
// {
|
|
// continue;
|
|
// }
|
|
sf->setValue(indexInResults, index);
|
|
indexInResults++;
|
|
}
|
|
sf->computeMinAndMax();
|
|
|
|
// <EXPORT RADII>
|
|
int sfIdxRadiusX = cloud->addScalarField("g3point_radius_x");
|
|
int sfIdxRadiusY = cloud->addScalarField("g3point_radius_y");
|
|
int sfIdxRadiusZ = cloud->addScalarField("g3point_radius_z");
|
|
if (sfIdxRadiusX == -1 || sfIdxRadiusY == -1 || sfIdxRadiusZ == -1)
|
|
{
|
|
ccLog::Error("[GrainsAsEllipsoids::exportResultsAsCloud] impossible to allocate scalar fields to export the radii");
|
|
return false;
|
|
}
|
|
CCCoreLib::ScalarField* sfRadiusX = cloud->getScalarField(sfIdxRadiusX);
|
|
CCCoreLib::ScalarField* sfRadiusY = cloud->getScalarField(sfIdxRadiusY);
|
|
CCCoreLib::ScalarField* sfRadiusZ = cloud->getScalarField(sfIdxRadiusZ);
|
|
for (unsigned int index = 0; index < cloud->size(); index++)
|
|
{
|
|
// if (m_fitNotOK.count(index))
|
|
// {
|
|
// continue;
|
|
// }
|
|
sfRadiusX->setValue(index, m_radii[index].x());
|
|
sfRadiusY->setValue(index, m_radii[index].y());
|
|
sfRadiusZ->setValue(index, m_radii[index].z());
|
|
}
|
|
sfRadiusX->computeMinAndMax();
|
|
sfRadiusY->computeMinAndMax();
|
|
sfRadiusZ->computeMinAndMax();
|
|
// </EXPORT RADII>
|
|
|
|
// <EXPORT ROTATION>
|
|
int sfIdxR00 = cloud->addScalarField("g3point_r00");
|
|
int sfIdxR01 = cloud->addScalarField("g3point_r01");
|
|
int sfIdxR02 = cloud->addScalarField("g3point_r02");
|
|
int sfIdxR10 = cloud->addScalarField("g3point_r10");
|
|
int sfIdxR11 = cloud->addScalarField("g3point_r11");
|
|
int sfIdxR21 = cloud->addScalarField("g3point_r12");
|
|
int sfIdxR20 = cloud->addScalarField("g3point_r20");
|
|
int sfIdxR12 = cloud->addScalarField("g3point_r21");
|
|
int sfIdxR22 = cloud->addScalarField("g3point_r22");
|
|
if (sfIdxR00 == -1 || sfIdxR01 == -1 || sfIdxR02 == -1
|
|
|| sfIdxR10 == -1 || sfIdxR11 == -1 || sfIdxR12 == -1
|
|
|| sfIdxR20 == -1 || sfIdxR21 == -1 || sfIdxR22 == -1)
|
|
{
|
|
ccLog::Error("[GrainsAsEllipsoids::exportResultsAsCloud] impossible to allocate scalar fields to export the rotation");
|
|
return false;
|
|
}
|
|
CCCoreLib::ScalarField* sfR00 = cloud->getScalarField(sfIdxR00);
|
|
CCCoreLib::ScalarField* sfR01 = cloud->getScalarField(sfIdxR01);
|
|
CCCoreLib::ScalarField* sfR02 = cloud->getScalarField(sfIdxR02);
|
|
CCCoreLib::ScalarField* sfR10 = cloud->getScalarField(sfIdxR10);
|
|
CCCoreLib::ScalarField* sfR11 = cloud->getScalarField(sfIdxR11);
|
|
CCCoreLib::ScalarField* sfR12 = cloud->getScalarField(sfIdxR12);
|
|
CCCoreLib::ScalarField* sfR20 = cloud->getScalarField(sfIdxR20);
|
|
CCCoreLib::ScalarField* sfR21 = cloud->getScalarField(sfIdxR21);
|
|
CCCoreLib::ScalarField* sfR22 = cloud->getScalarField(sfIdxR22);
|
|
for (unsigned int index = 0; index < cloud->size(); index++)
|
|
{
|
|
// if (m_fitNotOK.count(index))
|
|
// {
|
|
// continue;
|
|
// }
|
|
sfR00->setValue(index, m_rotationMatrix[index](0, 0));
|
|
sfR01->setValue(index, m_rotationMatrix[index](0, 1));
|
|
sfR02->setValue(index, m_rotationMatrix[index](0, 2));
|
|
sfR10->setValue(index, m_rotationMatrix[index](1, 0));
|
|
sfR11->setValue(index, m_rotationMatrix[index](1, 1));
|
|
sfR12->setValue(index, m_rotationMatrix[index](1, 2));
|
|
sfR20->setValue(index, m_rotationMatrix[index](2, 0));
|
|
sfR21->setValue(index, m_rotationMatrix[index](2, 1));
|
|
sfR22->setValue(index, m_rotationMatrix[index](2, 2));
|
|
}
|
|
sfR00->computeMinAndMax();
|
|
sfR01->computeMinAndMax();
|
|
sfR02->computeMinAndMax();
|
|
sfR10->computeMinAndMax();
|
|
sfR11->computeMinAndMax();
|
|
sfR12->computeMinAndMax();
|
|
sfR20->computeMinAndMax();
|
|
sfR21->computeMinAndMax();
|
|
sfR22->computeMinAndMax();
|
|
// </EXPORT ROTATION>
|
|
|
|
cloud->showColors(true);
|
|
cloud->setPointSize(9);
|
|
|
|
m_cloud->addChild(cloud);
|
|
m_app->addToDB(cloud);
|
|
|
|
return true;
|
|
}
|
|
|
|
// INIT ORIGINAL SPHERE
|
|
|
|
void GrainsAsEllipsoids::initSphereVertices()
|
|
{
|
|
// clear memory of prev arrays
|
|
std::vector<float>().swap(vertices);
|
|
std::vector<float>().swap(normals);
|
|
std::vector<float>().swap(texCoords);
|
|
|
|
float x, y, z, xy; // vertex position
|
|
float nx, ny, nz; // vertex normal
|
|
float s, t; // vertex texCoord
|
|
|
|
float sectorStep = 2 * M_PI / sectorCount;
|
|
float stackStep = M_PI / stackCount;
|
|
float sectorAngle, stackAngle;
|
|
|
|
for(int i = 0; i <= stackCount; ++i)
|
|
{
|
|
stackAngle = M_PI / 2 - i * stackStep; // starting from pi/2 to -pi/2
|
|
xy = cosf(stackAngle); // r * cos(u)
|
|
z = sinf(stackAngle); // r * sin(u)
|
|
|
|
// add (sectorCount+1) vertices per stack
|
|
// first and last vertices have same position and normal, but different tex coords
|
|
for(int j = 0; j <= sectorCount; ++j)
|
|
{
|
|
sectorAngle = j * sectorStep; // starting from 0 to 2pi
|
|
|
|
// vertex position (x, y, z)
|
|
x = xy * cosf(sectorAngle); // r * cos(u) * cos(v)
|
|
y = xy * sinf(sectorAngle); // r * cos(u) * sin(v)
|
|
vertices.push_back(x);
|
|
vertices.push_back(y);
|
|
vertices.push_back(z);
|
|
|
|
// normalized vertex normal (nx, ny, nz)
|
|
nx = x;
|
|
ny = y;
|
|
nz = z;
|
|
normals.push_back(nx);
|
|
normals.push_back(ny);
|
|
normals.push_back(nz);
|
|
|
|
// vertex tex coord (s, t) range between [0, 1]
|
|
s = (float)j / sectorCount;
|
|
t = (float)i / stackCount;
|
|
texCoords.push_back(s);
|
|
texCoords.push_back(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GrainsAsEllipsoids::initSphereIndexes()
|
|
{
|
|
// generate CCW index list of sphere triangles
|
|
// k1--k1+1
|
|
// | / |
|
|
// | / |
|
|
// k2--k2+1
|
|
|
|
int k1, k2;
|
|
for(int i = 0; i < stackCount; ++i)
|
|
{
|
|
k1 = i * (sectorCount + 1); // beginning of current stack
|
|
k2 = k1 + sectorCount + 1; // beginning of next stack
|
|
|
|
for(int j = 0; j < sectorCount; ++j, ++k1, ++k2)
|
|
{
|
|
// 2 triangles per sector excluding first and last stacks
|
|
// k1 => k2 => k1+1
|
|
if(i != 0)
|
|
{
|
|
indices.push_back(k1);
|
|
indices.push_back(k2);
|
|
indices.push_back(k1 + 1);
|
|
}
|
|
|
|
// k1+1 => k2 => k2+1
|
|
if(i != (stackCount-1))
|
|
{
|
|
indices.push_back(k1 + 1);
|
|
indices.push_back(k2);
|
|
indices.push_back(k2 + 1);
|
|
}
|
|
|
|
// store indices for lines
|
|
// vertical lines for all stacks, k1 => k2
|
|
lineIndices.push_back(k1);
|
|
lineIndices.push_back(k2);
|
|
if(i != 0) // horizontal lines except 1st stack, k1 => k+1
|
|
{
|
|
lineIndices.push_back(k1);
|
|
lineIndices.push_back(k1 + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ELLIPSOID FITTING
|
|
|
|
double GrainsAsEllipsoids::ellipsoidDistance(const Eigen::ArrayXd& p, int idx)
|
|
{
|
|
// // Compute the mean distance between the points of the grain and the ellipsoid
|
|
|
|
// // GTE Geometric Tools Engine
|
|
|
|
// // center
|
|
// gte::Vector3<double> center = {m_center[idx](0), m_center[idx](1), m_center[idx](2)};
|
|
|
|
// // axis
|
|
// std::array<gte::Vector3<double>, 3> axis;
|
|
// axis[0] = {m_rotationMatrix[idx](0, 0), m_rotationMatrix[idx](1, 0), m_rotationMatrix[idx](2, 0)};
|
|
// axis[1] = {m_rotationMatrix[idx](0, 1), m_rotationMatrix[idx](1, 1), m_rotationMatrix[idx](2, 1)};
|
|
// axis[2] = {m_rotationMatrix[idx](0, 2), m_rotationMatrix[idx](1, 2), m_rotationMatrix[idx](2, 2)};
|
|
|
|
// // extent
|
|
// gte::Vector3<double> extent = {m_radii[idx](0), m_radii[idx](1), m_radii[idx](2)};
|
|
|
|
// // create the ellipsoid
|
|
// gte::Ellipsoid3<double> ellipsoid(center, axis, extent);
|
|
|
|
// gte::DCPQuery<double, gte::Vector3<double>, gte::Ellipsoid3<double>> query;
|
|
|
|
// std::vector<int> stack = m_stacks[idx];
|
|
// double sum_a = 0; // distances with respect to the ellipsoid
|
|
// double sum_b = 0; // distances with respect to the mean
|
|
|
|
// //compute gravity center
|
|
// size_t count = m_cloud->size();
|
|
// CCVector3 mean(0, 0, 0);
|
|
// for (int index : m_stacks[idx])
|
|
// {
|
|
// const CCVector3* P = m_cloud->getPoint(index);
|
|
// mean.x += P->x;
|
|
// mean.y += P->y;
|
|
// mean.z += P->z;
|
|
// }
|
|
// mean.x = mean.x / count;
|
|
// mean.y = mean.y / count;
|
|
// mean.z = mean.z / count;
|
|
|
|
// for (int index : stack)
|
|
// {
|
|
// const CCVector3 *P = m_cloud->getPoint(index);
|
|
// gte::Vector3<double> P_gte = {P->x, P->y, P->z};
|
|
// auto result = query(P_gte, ellipsoid);
|
|
// sum_a = sum_a + pow(result.distance, 2);
|
|
|
|
// CCVector3 P_minus_min = *P - CCVector3(mean.x, mean.y, mean.z);
|
|
// sum_b = sum_b + P_minus_min.norm2d();
|
|
// }
|
|
|
|
// double r2 = 1 - sum_a / sum_b;
|
|
|
|
// // in Matlab
|
|
// // d = (x-xp).^2 + (y-yp).^2 + (z-zp).^2;
|
|
// // r2 = 1 - sum((x-xp).^2 + (y-yp).^2 + (z-zp).^2)./sum((x-mean(x)).^2 + (y-mean(y)).^2 + (z-mean(z)).^2);
|
|
|
|
// return r2;
|
|
return 0.;
|
|
}
|
|
|
|
void GrainsAsEllipsoids::updateBBoxOnlyOne(int index)
|
|
{
|
|
m_ccBBoxOnlyOne.setValidity(false);
|
|
m_ccBBoxOnlyOne.clear();
|
|
if (index < m_stacks.size())
|
|
{
|
|
if (m_fitNotOK.count(index) == 0)
|
|
{
|
|
float maxRadius = m_radii[index].maxCoeff();
|
|
CCVector3 center(m_center[index](0), m_center[index](1), m_center[index](2));
|
|
m_ccBBoxOnlyOne.add(CCVector3(center.x + maxRadius, center.y + maxRadius, center.z + maxRadius));
|
|
m_ccBBoxOnlyOne.add(CCVector3(center.x - maxRadius, center.y - maxRadius, center.z - maxRadius));
|
|
m_ccBBoxOnlyOne.setValidity(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ccLog::Error("[GrainsAsEllipsoids::updateBBox] asking for the bounding of index " + QString::number(index) + " out of range");
|
|
}
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::explicitToImplicit(const Eigen::Array3f& center,
|
|
const Eigen::Array3f& radii,
|
|
const Eigen::Matrix3f& rotationMatrix,
|
|
Eigen::ArrayXd& parameters)
|
|
{
|
|
// INSPIRED BY MATLAB CODE
|
|
|
|
// Cast ellipsoid defined with explicit parameters to implicit vector form.
|
|
//
|
|
// Examples:
|
|
// p = ellipse_ex2im([xc,yc,zc],[xr,yr,zr],eye(3,3));
|
|
|
|
// Matlab code => Copyright 2011 Levente Hunyadi
|
|
|
|
float xrr = 1 / radii(0);
|
|
float yrr = 1 / radii(1);
|
|
float zrr = 1 / radii(2);
|
|
|
|
float r11 = rotationMatrix.data()[0];
|
|
float r21 = rotationMatrix.data()[1];
|
|
float r31 = rotationMatrix.data()[2];
|
|
float r12 = rotationMatrix.data()[3];
|
|
float r22 = rotationMatrix.data()[4];
|
|
float r32 = rotationMatrix.data()[5];
|
|
float r13 = rotationMatrix.data()[6];
|
|
float r23 = rotationMatrix.data()[7];
|
|
float r33 = rotationMatrix.data()[8];
|
|
|
|
float xc = center(0);
|
|
float yc = center(1);
|
|
float zc = center(2);
|
|
|
|
// terms collected from symbolic expression
|
|
|
|
parameters << pow(r11, 2) * pow(xrr, 2) + pow(r21, 2) * pow(yrr, 2) + pow(r31, 2) * pow(zrr, 2),
|
|
pow(r12, 2) * pow(xrr, 2) + pow(r22, 2) * pow(yrr, 2) + pow(r32, 2) * pow(zrr, 2),
|
|
pow(r13, 2) * pow(xrr, 2) + pow(r23, 2) * pow(yrr, 2) + pow(r33, 2) * pow(zrr, 2),
|
|
2 * r11 * r12 * pow(xrr, 2) + 2 * r21 * r22 * pow(yrr, 2) + 2 * r31 * r32 * pow(zrr, 2),
|
|
2 * r11 * r13 * pow(xrr, 2) + 2 * r21 * r23 * pow(yrr, 2) + 2 * r31 * r33 * pow(zrr, 2),
|
|
2 * r12 * r13 * pow(xrr, 2) + 2 * r22 * r23 * pow(yrr, 2) + 2 * r32 * r33 * pow(zrr, 2),
|
|
(-2) * (pow(r11, 2) * xc * pow(xrr, 2) + pow(r21, 2) * xc * pow(yrr, 2) + pow(r31, 2) * xc * pow(zrr, 2)
|
|
+ r11 * r12 * pow(xrr, 2) * yc
|
|
+ r11 * r13 * pow(xrr, 2) * zc
|
|
+ r21 * r22 * yc * pow(yrr, 2)
|
|
+ r21 * r23 * pow(yrr, 2) * zc
|
|
+ r31 * r32 * yc * pow(zrr, 2)
|
|
+ r31 * r33 * zc * pow(zrr, 2)),
|
|
(-2) * (pow(r12, 2) * pow(xrr, 2) * yc + pow(r22, 2) * yc * pow(yrr, 2) + pow(r32, 2) * yc * pow(zrr, 2)
|
|
+ r11 * r12 * xc * pow(xrr, 2)
|
|
+ r21 * r22 * xc * pow(yrr, 2)
|
|
+ r12 * r13 * pow(xrr, 2) * zc
|
|
+ r31 * r32 * xc * pow(zrr, 2)
|
|
+ r22 * r23 * pow(yrr, 2) * zc
|
|
+ r32 * r33 * zc * pow(zrr, 2)),
|
|
(-2) * (pow(r13, 2)*pow(xrr, 2) * zc + pow(r23, 2) * pow(yrr, 2) * zc + pow(r33, 2) * zc * pow(zrr, 2)
|
|
+ r11 * r13 * xc * pow(xrr, 2)
|
|
+ r12 * r13 * pow(xrr, 2) * yc
|
|
+ r21 * r23 * xc * pow(yrr, 2)
|
|
+ r22 * r23 * yc * pow(yrr, 2)
|
|
+ r31 * r33 * xc * pow(zrr, 2)
|
|
+ r32 * r33 * yc * pow(zrr, 2)),
|
|
pow(r11, 2) * pow(xc, 2) * pow(xrr, 2)
|
|
+ 2 * r11 * r12 * xc * pow(xrr, 2) * yc
|
|
+ 2 * r11 * r13 * xc * pow(xrr, 2) * zc
|
|
+ pow(r12, 2) * pow(xrr, 2) * pow(yc, 2)
|
|
+ 2 * r12 * r13 * pow(xrr, 2) * yc * zc
|
|
+ pow(r13, 2) * pow(xrr, 2) * pow(zc, 2)
|
|
+ pow(r21, 2) *pow(xc, 2) * pow(yrr, 2)
|
|
+ 2 * r21 * r22 * xc * yc * pow(yrr, 2)
|
|
+ 2 * r21 * r23 * xc * pow(yrr, 2) * zc
|
|
+ pow(r22, 2) * pow(yc, 2) * pow(yrr, 2)
|
|
+ 2 * r22 * r23 * yc * pow(yrr, 2) * zc
|
|
+ pow(r23, 2) * pow(yrr, 2) * pow(zc, 2)
|
|
+ pow(r31, 2) * pow(xc, 2) * pow(zrr, 2)
|
|
+ 2 * r31 * r32 * xc * yc * pow(zrr, 2)
|
|
+ 2 * r31 * r33 * xc * zc * pow(zrr, 2)
|
|
+ pow(r32, 2) * pow(yc, 2) * pow(zrr, 2)
|
|
+ 2 * r32 * r33 * yc * zc * pow(zrr, 2)
|
|
+ pow(r33, 2) * pow(zc, 2) * pow(zrr, 2) - 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::implicitToExplicit(const Eigen::ArrayXd& parameters,
|
|
Eigen::Array3f& center,
|
|
Eigen::Array3f& radii,
|
|
Eigen::Matrix3f& rotationMatrix)
|
|
{
|
|
// INSPIRED BY MATLAB CODE
|
|
|
|
// Cast ellipsoid defined with implicit parameter vector to explicit form.
|
|
// The implicit equation of a general ellipse is
|
|
// F(x,y,z) = Ax^2 + By^2 + Cz^2 + 2Dxy + 2Exz + 2Fyz + 2Gx + 2Hy + 2Iz - 1 = 0
|
|
//
|
|
// Input arguments:
|
|
// v:
|
|
// the 10 parameters describing the ellipsoid algebraically
|
|
// Output arguments:
|
|
// center:
|
|
// ellispoid center coordinates [cx; cy; cz]
|
|
// ax:
|
|
// ellipsoid semi-axes (radii) [a; b; c]
|
|
// quat: NOT IN THIS CPP VERSION, ONLY MATLAB VERSION
|
|
// ellipsoid rotation in quaternion representation
|
|
// R:
|
|
// ellipsoid rotation (radii directions as rows of the 3x3 matrix)
|
|
//
|
|
// See also: ellipse_im2ex
|
|
|
|
// Matlab code => Copyright 2011 Levente Hunyadi
|
|
|
|
Eigen::ArrayXd p = parameters;
|
|
|
|
p(3) = 0.5 * p(3);
|
|
p(4) = 0.5 * p(4);
|
|
p(5) = 0.5 * p(5);
|
|
p(6) = 0.5 * p(6);
|
|
p(7) = 0.5 * p(7);
|
|
p(8) = 0.5 * p(8);
|
|
|
|
Eigen::MatrixXd q(4, 4);
|
|
|
|
q << p(0), p(3), p(4), p(6)
|
|
, p(3), p(1), p(5), p(7)
|
|
, p(4), p(5), p(2), p(8)
|
|
, p(6), p(7), p(8), p(9);
|
|
|
|
center = q.block(0, 0, 3, 3).colPivHouseholderQr().solve(-p(Eigen::seq(6, 8)).matrix()).cast<float>();
|
|
|
|
Eigen::MatrixXd t(4, 4);
|
|
t = Eigen::MatrixXd::Identity(4, 4);
|
|
t(3, 0) = center(0);
|
|
t(3, 1) = center(1);
|
|
t(3, 2) = center(2);
|
|
|
|
Eigen::MatrixXd s(4, 4);
|
|
s = t * q * t.transpose();
|
|
|
|
// check for positive definiteness
|
|
Eigen::LLT<Eigen::MatrixXd> lltOfA((-s(3, 3) * s.block(0, 0, 3, 3).array()));
|
|
if (lltOfA.info() != Eigen::Success)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Eigen::EigenSolver<Eigen::MatrixXd> eigensolver(s.block(0, 0, 3, 3));
|
|
if (eigensolver.info() != Eigen::Success)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
radii = (-s(3, 3) / eigensolver.eigenvalues().array().real()).sqrt().cast<float>();
|
|
rotationMatrix = eigensolver.eigenvectors().transpose().real().cast<float>();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::directFit(const Eigen::ArrayX3d& xyz, Eigen::ArrayXd& parameters)
|
|
{
|
|
// INSPIRED BY MATLAB CODE
|
|
|
|
// Direct least squares fitting of ellipsoids under the constraint 4J - I^2 > 0.
|
|
// The constraint confines the class of ellipsoids to fit to those whose smallest radius
|
|
// is at least half of the largest radius.
|
|
//
|
|
// Input arguments:
|
|
// x,y,z;
|
|
// x, y and z coodinates of 3D points
|
|
//
|
|
// Output arguments:
|
|
// p:
|
|
// a 10-parameter vector of the algebraic ellipsoid fit
|
|
//
|
|
// References:
|
|
// Qingde Li and John G. Griffiths, "Least Squares Ellipsoid Specific Fitting",
|
|
// Proceedings of the Geometric Modeling and Processing, 2004.
|
|
|
|
// Matlab code reference => Copyright 2011 Levente Hunyadi
|
|
|
|
Eigen::MatrixXd d(xyz.rows(), 10);
|
|
|
|
d << xyz(Eigen::placeholders::all, 0).pow(2).matrix()
|
|
, xyz(Eigen::placeholders::all, 1).pow(2).matrix()
|
|
, xyz(Eigen::placeholders::all, 2).pow(2).matrix()
|
|
, (2 * xyz(Eigen::placeholders::all, 1) * xyz(Eigen::placeholders::all, 2)).matrix()
|
|
, (2 * xyz(Eigen::placeholders::all, 0) * xyz(Eigen::placeholders::all, 2)).matrix()
|
|
, (2 * xyz(Eigen::placeholders::all, 0) * xyz(Eigen::placeholders::all, 1)).matrix()
|
|
, (2 * xyz(Eigen::placeholders::all, 0)).matrix()
|
|
, (2 * xyz(Eigen::placeholders::all, 1)).matrix()
|
|
, (2 * xyz(Eigen::placeholders::all, 2)).matrix()
|
|
, Eigen::MatrixXd::Ones(xyz.rows(), 1);
|
|
|
|
Eigen::MatrixXd s = d.transpose() * d;
|
|
|
|
int k = 4;
|
|
Eigen::Matrix3d c1;
|
|
Eigen::Matrix3d c2;
|
|
Eigen::MatrixXd c;
|
|
c = Eigen::MatrixXd::Zero(10, 10);
|
|
c1 << 0 , k , k
|
|
, k, 0, k
|
|
, k , k , 0;
|
|
c1 = c1.array() / 2 - 1;
|
|
c2 = - k * Eigen::Matrix3d::Identity();
|
|
c.block(0, 0, 3, 3) = c1;
|
|
c.block(3, 3, 3, 3) = c2;
|
|
|
|
Eigen::GeneralizedEigenSolver<Eigen::MatrixXd> eigensolver(s, c);
|
|
if (eigensolver.info() != Eigen::Success)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Eigen::ArrayXd eigenValues(10);
|
|
Eigen::VectorXd eigenValuesAsAMatrix(10);
|
|
eigenValues = eigensolver.eigenvalues().real();
|
|
eigenValuesAsAMatrix = eigensolver.eigenvalues().real().matrix();
|
|
Xb condition = (eigenValues > 0) && (!eigenValues.isInf());
|
|
|
|
int flt = condition.count();
|
|
// std::cout << "flt " << flt << std::endl;
|
|
Eigen::ArrayXd finiteValues(flt);
|
|
finiteValues = Eigen::ArrayXd::Zero(flt);
|
|
int finiteValuesCounter = 0;
|
|
for (int k = 0; k < eigenValues.size(); k++)
|
|
{
|
|
if (condition(k))
|
|
{
|
|
finiteValues(finiteValuesCounter++) = eigenValues(k);
|
|
}
|
|
}
|
|
|
|
double eigenValue;
|
|
Eigen::MatrixXd v;
|
|
switch (flt) {
|
|
case 1: // regular case
|
|
eigenValue = finiteValues(0); // there is only one positive finite value
|
|
for (k = 0; k < 10; k++)
|
|
{
|
|
if (eigenValues(k) == eigenValue)
|
|
{
|
|
v = eigensolver.eigenvectors()(Eigen::placeholders::all, k).real();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 0: // degenerate case
|
|
// # single positive eigenvalue becomes near-zero negative eigenvalue due to round-off error
|
|
eigenValue = eigenValues.abs().minCoeff();
|
|
for (k = 0; k < 10; k++)
|
|
{
|
|
if (abs(eigenValues(k)) == eigenValue)
|
|
{
|
|
v = eigensolver.eigenvectors()(Eigen::placeholders::all, k).real();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default: // degenerate case
|
|
// several positive eigenvalues appear
|
|
eigenValue = finiteValues.abs().minCoeff();
|
|
for (k = 0; k < 10; k++)
|
|
{
|
|
if (eigenValues(k) == eigenValue)
|
|
{
|
|
v = eigensolver.eigenvectors()(Eigen::placeholders::all, k).real();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
parameters.resize(10);
|
|
|
|
if (v.rows() != 10 || v.cols() != 1)
|
|
{
|
|
return false; // Invalid eigenvector size
|
|
}
|
|
|
|
parameters << v(0), v(1), v(2)
|
|
, 2 * v(5), 2 * v(4), 2* v(3)
|
|
, 2 * v(6), 2 * v(7), 2 * v(8)
|
|
, v(9);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::fitEllipsoidToGrain(const int grainIndex,
|
|
Eigen::Array3f& center,
|
|
Eigen::Array3f& radii,
|
|
Eigen::Matrix3f& rotationMatrix,
|
|
const Method& method)
|
|
{
|
|
// Shift point cloud to have only positive coordinates
|
|
// (problem with quadfit if the point cloud is far from the coordinates of the origin (0,0,0))
|
|
|
|
bool ret = true;
|
|
|
|
// extract the point cloud related to the current index
|
|
CCCoreLib::ReferenceCloud referenceCloud(m_cloud);
|
|
for (int index : m_stacks[grainIndex])
|
|
{
|
|
referenceCloud.addPointIndex(index);
|
|
}
|
|
|
|
ccPointCloud* grainCloud = m_cloud->partialClone(&referenceCloud);
|
|
Eigen::Map<const Eigen::MatrixX3f, Eigen::Unaligned, Eigen::Stride<1, 3>>
|
|
grainPoints(static_cast<const float*>(grainCloud->getPoint(0)->u), grainCloud->size(), 3);
|
|
|
|
CCVector3 bbMin;
|
|
CCVector3 bbMax;
|
|
grainCloud->getBoundingBox(bbMin, bbMax);
|
|
CCVector3 bb(bbMax - bbMin);
|
|
Eigen::Vector3d scales(bb.x, bb.y, bb.z);
|
|
double scale = 1 / scales.maxCoeff();
|
|
Eigen::RowVector3d means = grainPoints.cast<double>().colwise().mean();
|
|
|
|
Eigen::ArrayXd p(10);
|
|
|
|
switch (method) {
|
|
case DIRECT:
|
|
// Direct least squares fitting of ellipsoids under the constraint 4J - I**2 > 0.
|
|
// The constraint confines the class of ellipsoids to fit to those whose smallest radius is at least half of the
|
|
// largest radius.
|
|
|
|
if(!directFit(scale * (grainPoints.cast<double>().rowwise() - means), p)) // Ellipsoid fit
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!implicitToExplicit(p, center, radii, rotationMatrix)) // Get the explicit parameters
|
|
{
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Rescale the explicit parameters (the rotation matrix is unchanged by the scaling)
|
|
center = center / scale + Eigen::Array3f(means.cast<float>());
|
|
radii = radii / scale;
|
|
|
|
// re-order the radii
|
|
std::vector<float> sortedRadii{radii(0), radii(1), radii(2)};
|
|
std::sort(sortedRadii.begin(), sortedRadii.end());
|
|
Eigen::Array3f updatedRadii = {sortedRadii[0], sortedRadii[1], sortedRadii[2]}; // from the smallest to the largest
|
|
Eigen::Matrix3f updatedRotationMatrix;
|
|
for (int k = 0; k < 3; k++)
|
|
{
|
|
float radius = updatedRadii(k);
|
|
int col = 0;
|
|
for (int idx = 0; idx < 3; idx++)
|
|
{
|
|
if (radii[idx] == radius)
|
|
{
|
|
break;
|
|
}
|
|
col++;
|
|
}
|
|
updatedRotationMatrix(k, 0) = rotationMatrix(col, 0);
|
|
updatedRotationMatrix(k, 1) = rotationMatrix(col, 1);
|
|
updatedRotationMatrix(k, 2) = rotationMatrix(col, 2);
|
|
}
|
|
|
|
radii = updatedRadii;
|
|
rotationMatrix = updatedRotationMatrix;
|
|
|
|
ret = explicitToImplicit(center, radii, rotationMatrix, p);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// DRAW
|
|
|
|
void GrainsAsEllipsoids::releaseShaders()
|
|
{
|
|
m_program.clear();
|
|
}
|
|
|
|
void GrainsAsEllipsoids::setUniformValueColor(const ccColor::Rgba &color)
|
|
{
|
|
m_program->setUniformValue("color", color.r, color.g, color.b, color.a);
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::initProgram(QOpenGLContext* context)
|
|
{
|
|
if (m_program.isNull())
|
|
{
|
|
QString error;
|
|
|
|
if (!context)
|
|
{
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
m_program.reset(new QOpenGLShaderProgram(context));
|
|
|
|
// create vertex shader
|
|
QString vertexShaderFile(m_shaderPath + "/DrawGrains.vs");
|
|
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexShaderFile))
|
|
{
|
|
error = m_program->log();
|
|
ccLog::Error(error);
|
|
return false;
|
|
}
|
|
|
|
// create fragment shader
|
|
QString fragmentShaderFile(m_shaderPath + "/DrawGrains.fs");
|
|
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentShaderFile))
|
|
{
|
|
error = m_program->log();
|
|
ccLog::Error(error);
|
|
return false;
|
|
}
|
|
|
|
if (!m_program->link())
|
|
{
|
|
error = m_program->log();
|
|
ccLog::Error(error);
|
|
return false;
|
|
}
|
|
|
|
initSphereVertices();
|
|
initSphereIndexes();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GrainsAsEllipsoids::drawEllipsoid(CC_DRAW_CONTEXT& context, int idx)
|
|
{
|
|
QOpenGLFunctions_2_1* glFunc = context.glFunctions<QOpenGLFunctions_2_1>();
|
|
assert(glFunc != nullptr);
|
|
|
|
if (!m_fitNotOK.count(idx))
|
|
{
|
|
QMatrix4x4 projection;
|
|
QMatrix4x4 modelView;
|
|
QMatrix4x4 model;
|
|
QMatrix4x4 matrixNormal;
|
|
|
|
Eigen::Matrix3f rotation(m_rotationMatrix[idx].transpose());
|
|
QMatrix4x4 matrixFromFit(rotation(0, 0), rotation(0, 1), rotation(0, 2), m_center[idx](0),
|
|
rotation(1, 0), rotation(1, 1), rotation(1, 2), m_center[idx](1),
|
|
rotation(2, 0), rotation(2, 1), rotation(2, 2), m_center[idx](2),
|
|
0, 0, 0, 1);
|
|
|
|
CCVector3f color;
|
|
|
|
color = m_grainColors[idx];
|
|
glFunc->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glFunc->glEnable(GL_BLEND);
|
|
m_program->setUniformValue("materialAmbient", color.x, color.y, color.z, 1.);
|
|
m_program->setUniformValue("materialDiffuse", color.x, color.y, color.z, 1.);
|
|
m_program->setUniformValue("objectColor", color.x, color.y, color.z);
|
|
|
|
// prepare translation, rotation and scaling
|
|
glFunc->glPushMatrix(); // save the current matrix
|
|
|
|
// rotation and translation from the ellipsoid fitting
|
|
glFunc->glMultMatrixf(matrixFromFit.data());
|
|
// scale from the ellipsoid fitting
|
|
glFunc->glScalef(m_radii[idx](0), m_radii[idx](1), m_radii[idx](2));
|
|
|
|
// get matrices
|
|
glFunc->glGetFloatv(GL_PROJECTION_MATRIX, projection.data());
|
|
glFunc->glGetFloatv(GL_MODELVIEW_MATRIX, modelView.data());
|
|
matrixNormal = modelView;
|
|
matrixNormal.setColumn(3, QVector4D(0,0,0,1));
|
|
m_program->setUniformValue("modelViewMatrix", modelView);
|
|
m_program->setUniformValue("normalMatrix", matrixNormal);
|
|
m_program->setUniformValue("modelViewProjectionMatrix", projection * modelView);
|
|
|
|
// draw triangles
|
|
if (m_drawSurfaces)
|
|
{
|
|
m_program->setUniformValue("drawLines", 0);
|
|
m_program->setUniformValue("drawPoints", 0);
|
|
|
|
glFunc->glEnable(GL_POLYGON_OFFSET_FILL);
|
|
|
|
glFunc->glPolygonOffset(1.0, 1.0f); // move polygon backward
|
|
|
|
glFunc->glDrawElements(GL_TRIANGLES, (unsigned int) indices.size(), GL_UNSIGNED_INT, indices.data());
|
|
|
|
glFunc->glDisable(GL_POLYGON_OFFSET_FILL);
|
|
}
|
|
|
|
// draw lines
|
|
if (m_drawLines)
|
|
{
|
|
m_program->setUniformValue("drawLines", 1);
|
|
m_program->setUniformValue("drawPoints", 0);
|
|
|
|
glFunc->glDrawElements(GL_LINES, (unsigned int)lineIndices.size(), GL_UNSIGNED_INT, lineIndices.data());
|
|
}
|
|
|
|
glFunc->glPopMatrix();
|
|
}
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::drawEllipsoids(CC_DRAW_CONTEXT& context)
|
|
{
|
|
QOpenGLFunctions_2_1* glFunc = context.glFunctions<QOpenGLFunctions_2_1>();
|
|
assert(glFunc != nullptr);
|
|
|
|
CCVector3f color;
|
|
|
|
// set uniforms
|
|
QVector4D lightPosition(2 * m_ccBBoxAll.maxCorner().x, 2 * m_ccBBoxAll.maxCorner().y, 10 * m_ccBBoxAll.maxCorner().z, 0);
|
|
QVector4D lightAmbient(0.8f, 0.8f, 0.8f, 1); // grey
|
|
QVector4D lightDiffuse(0.8f, 0.8f, 0.8f, 1); // light grey
|
|
QVector4D lightSpecular(1.0f, 1.0f, 1.0f, 1); // white
|
|
QVector4D materialDiffuse(0.7f, 0.7f, 0.7f, m_transparency);
|
|
QVector4D materialSpecular(0.4f, 0.4f, 0.4f, 1);
|
|
float materialShininess = 16;
|
|
|
|
m_program->setUniformValue("lightPosition", lightPosition);
|
|
m_program->setUniformValue("lightAmbient", lightAmbient);
|
|
m_program->setUniformValue("lightDiffuse", lightDiffuse);
|
|
m_program->setUniformValue("lightSpecular", lightSpecular);
|
|
m_program->setUniformValue("materialSpecular", materialSpecular);
|
|
m_program->setUniformValue("materialShininess", materialShininess);
|
|
|
|
m_program->setAttributeArray("vertexPosition", static_cast<GLfloat*>(vertices.data()), 3);
|
|
m_program->setAttributeArray("vertexNormal", static_cast<GLfloat*>(normals.data()), 3);
|
|
m_program->setAttributeArray("vertexTexCoord", static_cast<GLfloat*>(texCoords.data()), 2);
|
|
|
|
m_program->enableAttributeArray("vertexPosition");
|
|
m_program->enableAttributeArray("vertexNormal");
|
|
m_program->enableAttributeArray("vertexTexCoord");
|
|
|
|
QMatrix4x4 projection;
|
|
QMatrix4x4 modelView;
|
|
|
|
if (m_showAll)
|
|
{
|
|
for (int idx = 0; idx < m_center.size(); idx++)
|
|
{
|
|
drawEllipsoid(context, idx);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
drawEllipsoid(context, m_onlyOne);
|
|
|
|
// draw points
|
|
if (m_drawPoints)
|
|
{
|
|
// get matrices
|
|
glFunc->glGetFloatv(GL_PROJECTION_MATRIX, projection.data());
|
|
glFunc->glGetFloatv(GL_MODELVIEW_MATRIX, modelView.data());
|
|
|
|
m_program->setUniformValue("modelViewProjectionMatrix", projection * modelView);
|
|
m_program->setUniformValue("drawLines", 0);
|
|
m_program->setUniformValue("drawPoints", 1);
|
|
m_program->setUniformValue("materialAmbient", 1, 1, 1, 1.);
|
|
|
|
std::vector<int> stack = m_stacks[m_onlyOne];
|
|
std::vector<std::array<GLfloat, 3>> points(stack.size());
|
|
std::vector<int> indices;
|
|
for (int k = 0; k < stack.size(); k++)
|
|
{
|
|
const CCVector3* P = m_cloud->getPoint(stack[k]);
|
|
points[k] = {P->x, P->y, P->z};
|
|
}
|
|
|
|
// change the vertex positions to the points of the current grain
|
|
m_program->setAttributeArray("vertexPosition", static_cast<GLfloat*>(points[0].data()), 3);
|
|
|
|
m_program->setUniformValue("pointSize", m_glPointSize);
|
|
|
|
glFunc->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
glFunc->glDisable(GL_LIGHTING);
|
|
glFunc->glDisable(GL_TEXTURE_2D);
|
|
|
|
glFunc->glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(stack.size()));
|
|
|
|
// reset the vertex positions to the template sphere
|
|
m_program->setAttributeArray("vertexPosition", static_cast<GLfloat*>(vertices.data()), 3);
|
|
}
|
|
}
|
|
|
|
m_program->disableAttributeArray("vertexPosition");
|
|
m_program->disableAttributeArray("vertexNormal");
|
|
m_program->disableAttributeArray("vertexTexCoord");
|
|
|
|
return true;
|
|
}
|
|
|
|
void GrainsAsEllipsoids::drawGrains(CC_DRAW_CONTEXT& context)
|
|
{
|
|
if (!initProgram(context.qGLContext))
|
|
{
|
|
ccLog::Warning("[GrainsAsEllipsoids::drawGrains] impossible to init shader program");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
m_programInitialized = true;
|
|
}
|
|
|
|
if (m_programInitialized)
|
|
{
|
|
m_program->bind();
|
|
|
|
drawEllipsoids(context);
|
|
|
|
m_program->release();
|
|
|
|
m_app->redrawAll();
|
|
}
|
|
}
|
|
|
|
void GrainsAsEllipsoids::setOnlyOne(int i)
|
|
{
|
|
m_onlyOne = i;
|
|
updateBBoxOnlyOne(i);
|
|
redrawDisplay();
|
|
}
|
|
|
|
void GrainsAsEllipsoids::showOnlyOne(bool state)
|
|
{
|
|
m_showAll =!state;
|
|
m_ccBBox = m_ccBBoxOnlyOne;
|
|
redrawDisplay();
|
|
}
|
|
|
|
void GrainsAsEllipsoids::showAll(bool state)
|
|
{
|
|
m_showAll = state;
|
|
m_ccBBox = m_ccBBoxAll;
|
|
redrawDisplay();
|
|
}
|
|
|
|
void GrainsAsEllipsoids::draw(CC_DRAW_CONTEXT& context)
|
|
{
|
|
if (m_radii.empty()) // nothing to draw, probably due to a bad initialization
|
|
return;
|
|
|
|
if (isVisible() && isEnabled())
|
|
{
|
|
if (MACRO_Draw3D(context))
|
|
{
|
|
drawGrains(context);
|
|
//if the entity is currently selected, we draw its bounding-box
|
|
if (m_selected && !MACRO_EntityPicking(context) && context.currentLODLevel == 0)
|
|
{
|
|
drawBB(context, context.bbDefaultCol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ccBBox GrainsAsEllipsoids::getOwnBB(bool withGLFeatures)
|
|
{
|
|
return m_ccBBox;
|
|
}
|
|
|
|
/// template <class Type, int N, class ComponentType> static
|
|
/// <Eigen::Array3f, 1, float>
|
|
bool genericArrayToFile(const std::vector<Eigen::Array3f>& data, QFile& out)
|
|
{
|
|
assert(out.isOpen() && (out.openMode() & QIODevice::WriteOnly));
|
|
|
|
//removed to allow saving empty clouds
|
|
//if (data.empty())
|
|
//{
|
|
// return ccSerializableObject::MemoryError();
|
|
//}
|
|
|
|
int N = 1;
|
|
|
|
//component count (dataVersion>=20)
|
|
::uint8_t componentCount = static_cast<::uint8_t>(N);
|
|
if (out.write((const char*)&componentCount, 1) < 0)
|
|
return ccSerializableObject::WriteError();
|
|
|
|
//element count = array size (dataVersion>=20)
|
|
::uint32_t elementCount = static_cast<::uint32_t>(data.size());
|
|
if (out.write((const char*)&elementCount, 4) < 0)
|
|
return ccSerializableObject::WriteError();
|
|
|
|
//array data (dataVersion>=20)
|
|
{
|
|
//DGM: do it by chunks, in case it's too big to be processed by the system
|
|
const char* _data = (const char*)data.data();
|
|
qint64 byteCount = static_cast<qint64>(elementCount);
|
|
byteCount *= sizeof(Eigen::Array3f);
|
|
while (byteCount != 0)
|
|
{
|
|
static const qint64 s_maxByteSaveCount = (1 << 26); //64 Mb each time
|
|
qint64 saveCount = std::min(byteCount, s_maxByteSaveCount);
|
|
if (out.write(_data, saveCount) < 0)
|
|
return ccSerializableObject::WriteError();
|
|
_data += saveCount;
|
|
byteCount -= saveCount;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool readArrayHeader(QFile& in,
|
|
short dataVersion,
|
|
::uint8_t &componentCount,
|
|
::uint32_t &elementCount)
|
|
{
|
|
assert(in.isOpen() && (in.openMode() & QIODevice::ReadOnly));
|
|
|
|
if (dataVersion < 20)
|
|
return ccSerializableObject::CorruptError();
|
|
|
|
//component count (dataVersion>=20)
|
|
if (in.read((char*)&componentCount, 1) < 0)
|
|
return ccSerializableObject::ReadError();
|
|
|
|
//element count = array size (dataVersion>=20)
|
|
if (in.read((char*)&elementCount, 4) < 0)
|
|
return ccSerializableObject::ReadError();
|
|
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
bool stdVectorToFile(QString name, std::vector<T> vector)
|
|
{
|
|
std::ofstream file(name.toLatin1());
|
|
int elementSize = vector[0].size();
|
|
for (int i = 0; i < vector.size(); i++)
|
|
{
|
|
for (int j = 0; j < elementSize; j++)
|
|
{
|
|
file << vector[i][j] << ", ";
|
|
}
|
|
file << std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool rotationMatrixToFile(QString name, std::vector<Eigen::Matrix3f> rotationMatrix)
|
|
{
|
|
std::ofstream file(name.toLatin1());
|
|
int elementSize = rotationMatrix[0].size();
|
|
for (int i = 0; i < rotationMatrix.size(); i++)
|
|
{
|
|
for (int j = 0; j < elementSize; j++)
|
|
{
|
|
file << rotationMatrix[i](j) << ", ";
|
|
}
|
|
file << std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::toFile_MeOnly(QFile& out, short dataVersion) const
|
|
{
|
|
ccLog::Print("[G3Point] write GrainsAsEllipsoids object in .bin");
|
|
|
|
if (!ccHObject::toFile_MeOnly(out, dataVersion))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ccSerializationHelper::GenericArrayToFile<Eigen::Array3f, 1, Eigen::Array3f>(m_center, out))
|
|
return WriteError();
|
|
|
|
if (!ccSerializationHelper::GenericArrayToFile<Eigen::Array3f, 1, Eigen::Array3f>(m_radii, out))
|
|
return WriteError();
|
|
|
|
if (!ccSerializationHelper::GenericArrayToFile<Eigen::Matrix3f, 1, Eigen::Matrix3f>(m_rotationMatrix, out))
|
|
return WriteError();
|
|
|
|
if (!ccSerializationHelper::GenericArrayToFile<CCVector3f, 1, CCVector3f>(m_grainColors, out))
|
|
return WriteError();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GrainsAsEllipsoids::fromFile_MeOnly(QFile& in, short dataVersion, int flags, LoadedIDMap& oldToNewIDMap)
|
|
{
|
|
ccLog::Print("[G3Point] read GrainsAsEllipsoids object from .bin");
|
|
|
|
if (!ccHObject::fromFile_MeOnly(in, dataVersion, flags, oldToNewIDMap))
|
|
return false;
|
|
|
|
if (!ccSerializationHelper::GenericArrayFromFile<Eigen::Array3f, 1, Eigen::Array3f>(m_center, in, dataVersion, "G3Point m_center"))
|
|
{
|
|
ccLog::Warning("[G3Point] error reading m_center");
|
|
return ReadError();
|
|
}
|
|
|
|
if (!ccSerializationHelper::GenericArrayFromFile<Eigen::Array3f, 1, Eigen::Array3f>(m_radii, in, dataVersion, "G3Point m_radii"))
|
|
{
|
|
ccLog::Warning("[G3Point] error reading m_radii");
|
|
return ReadError();
|
|
}
|
|
|
|
if (!ccSerializationHelper::GenericArrayFromFile<Eigen::Matrix3f, 1, Eigen::Matrix3f>(m_rotationMatrix, in, dataVersion, "G3Point m_rotationMatrix"))
|
|
{
|
|
ccLog::Warning("[G3Point] error reading m_rorationMatrix");
|
|
return ReadError();
|
|
}
|
|
|
|
if (!ccSerializationHelper::GenericArrayFromFile<CCVector3f, 1, CCVector3f>(m_grainColors, in, dataVersion, "G3Point m_grainColors"))
|
|
{
|
|
ccLog::Warning("[G3Point] error reading m_rorationMatrix");
|
|
return ReadError();
|
|
}
|
|
|
|
lockVisibility(false);
|
|
setVisible(true);
|
|
|
|
m_ccBBoxAll.setValidity(false);
|
|
m_ccBBoxAll.clear();
|
|
|
|
for (int idx = 0; idx < m_center.size(); idx++)
|
|
{
|
|
float maxRadius = m_radii[idx].maxCoeff();
|
|
CCVector3 center(m_center[idx](0), m_center[idx](1), m_center[idx](2));
|
|
if (m_radii[idx].x() != -1) // all radii are equal to zero when the fit was not successful
|
|
{
|
|
m_fitNotOK.insert(idx);
|
|
continue;
|
|
}
|
|
m_ccBBoxAll.add(CCVector3(center.x + maxRadius, center.y + maxRadius, center.z + maxRadius));
|
|
m_ccBBoxAll.add(CCVector3(center.x - maxRadius, center.y - maxRadius, center.z - maxRadius));
|
|
}
|
|
|
|
m_ccBBoxAll.setValidity(true);
|
|
|
|
m_ccBBox = m_ccBBoxAll;
|
|
redrawDisplay();
|
|
|
|
return true;
|
|
}
|