Page 1 of 1
Cloud2CloudDistance and Cloud2MeshDistance: empty scalar field
Posted: Sun Jul 19, 2015 10:28 pm
by the_grateful_dawg
Good day,
I am trying to learn how to create Cloud Compare plugins. Currently, I have written simple plug-ins that make use of the computeCloud2CloudDistance(...) and computeCloud2MeshDistance(...) functions from the DistanceComputationTools class. When they execute, the functions return 0, and a new scalar field is generated in the "compared cloud" (in the cloud to cloud case) and the "point cloud" (in the cloud to mesh case). The issue is that the scalar field that is generated is set to 0 for all points in the cloud. The goal of these plugins is to compute the distance between 2 datasets, identical to the pre-built functions in Cloud Compare (without prompting the user with a GUI). Currently, using the cloud-to-cloud and the cloud-to-mesh distance functions in Cloud Compare produces the expected distance scalar field for one of the point clouds. I want to replicate this behaviour in my own plugins. Any help would be appreciated.
Re: Cloud2CloudDistance and Cloud2MeshDistance: empty scalar field
Posted: Mon Jul 20, 2015 7:04 am
by daniel
I'll need to see some code to help you ;)
And I guess you have already looked at the original code? Do you know also that you can perform the same tasks in command line mode?
Re: Cloud2CloudDistance and Cloud2MeshDistance: empty scalar field
Posted: Tue Jul 21, 2015 3:11 pm
by the_grateful_dawg
Hi Daniel,
thanks for the reply. I have played with the command line conversion tool a bit, but I am looking to develop more extensive plugins down the road, and this is my means of learning the CC API and C++. I am going to attach the code for the cloud2cloud distance computation doAction() method.
Code: Select all
void qCloudToCloud::doAction()
{
//m_app should have already been initialized by CC when plugin is loaded!
//(--> pure internal check)
assert(m_app);
if (!m_app)
return;
//Retrieve selected data sets from the DB Tree
const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
size_t selNum = selectedEntities.size();
//Create an instance of the DistanceComputationParams
CCLib::DistanceComputationTools::Cloud2CloudDistanceComputationParams params;
int placeholder = 0;
//Create a pointer to store the input cloud
CCLib::GenericIndexedCloudPersist* refCloud = 0;
//Find the first selected point cloud in the DBTree
for (int i = 0; i<selNum; ++i)
{
ccHObject* ent = selectedEntities[i];
if (ent->isKindOf(CC_TYPES::POINT_CLOUD))
{
m_app->dispToConsole("[qCloudToCloud] POINT CLOUD",ccMainAppInterface::STD_CONSOLE_MESSAGE);
refCloud = ccHObjectCaster::ToGenericPointCloud(ent);
placeholder = i;
break;
}
}
//Find the next instance of a point cloud and compute the cloud to cloud distance
for (int i = 0; i < selNum; ++i)
{
ccHObject* ent = selectedEntities[i];
if (ent->isKindOf(CC_TYPES::POINT_CLOUD) && i!=placeholder)
{
CCLib::GenericIndexedCloudPersist* inputCloud = 0;
inputCloud = ccHObjectCaster::ToGenericPointCloud(ent);
int success = CCLib::DistanceComputationTools::computeCloud2CloudDistance(inputCloud, refCloud, params, 0, 0, 0);
//Print out the size of the two selected clouds (sanity check) and the output of the distance computation
m_app->dispToConsole(QString::number(BIMcloud->size()),ccMainAppInterface::STD_CONSOLE_MESSAGE);
m_app->dispToConsole(QString::number(inputCloud->size()),ccMainAppInterface::STD_CONSOLE_MESSAGE);
m_app->dispToConsole(QString::number(success),ccMainAppInterface::STD_CONSOLE_MESSAGE);
}
}
}
Re: Cloud2CloudDistance and Cloud2MeshDistance: empty scalar field
Posted: Tue Jul 21, 2015 8:01 pm
by daniel
Hard to tell... Using the default parameters should be ok. Maybe it's an issue with the default scalar field. Can you try to explicitely init the SF before calling the computeCloud2CloudDistance method maybe? (with enableScalarField). And afterwards you should call 'updateMinAndMax' on the output scalar field.
Otherwise can you share the full code maybe? (I mean the full plugin - it will be easier for me to debug).
Re: Cloud2CloudDistance and Cloud2MeshDistance: empty scalar field
Posted: Wed Jul 22, 2015 4:15 am
by the_grateful_dawg
Hi,
here is the full code (which is just the dummy plugin with a modified doAction()):
Code: Select all
//Default constructor: should mainly be used to initialize
//actions (pointers) and other members
qCloudToCloud::qCloudToCloud(QObject* parent/*=0*/)
: QObject(parent)
, m_action(0)
{
}
//This method should enable or disable each plugin action
//depending on the currently selected entities ('selectedEntities').
//For example: if none of the selected entities is a cloud, and your
//plugin deals only with clouds, call 'm_action->setEnabled(false)'
void qCloudToCloud::onNewSelection(const ccHObject::Container& selectedEntities)
{
//if (m_action)
// m_action->setEnabled(!selectedEntities.empty());
}
//This method returns all 'actions' of your plugin.
//It will be called only once, when plugin is loaded.
void qCloudToCloud::getActions(QActionGroup& group)
{
//default action (if it has not been already created, it's the moment to do it)
if (!m_action)
{
//here we use the default plugin name, description and icon,
//but each action can have its own!
m_action = new QAction(getName(),this);
m_action->setToolTip(getDescription());
m_action->setIcon(getIcon());
//connect appropriate signal
connect(m_action, SIGNAL(triggered()), this, SLOT(doAction()));
}
group.addAction(m_action);
}
//This is an example of an action's slot called when the corresponding action
//is triggered (i.e. the corresponding icon or menu entry is clicked in CC
//main's interface). You can access to most of CC components (database,
//3D views, console, etc.) via the 'm_app' attribute (ccMainAppInterface
//object).
void qCloudToCloud::doAction()
{
//m_app should have already been initialized by CC when plugin is loaded!
//(--> pure internal check)
assert(m_app);
if (!m_app)
return;
//Retrieve selected data sets from the DB Tree
const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
size_t selNum = selectedEntities.size();
//Create an instance of the DistanceComputationParams
CCLib::DistanceComputationTools::Cloud2CloudDistanceComputationParams params;
//Create a placeholder for the index of the reference cloud in the DBTree
int placeholder = 0;
//Create a pointer to store the input cloud
CCLib::GenericIndexedCloudPersist* refCloud = 0;
//Find the first selected point cloud in the DBTree
for (int i = 0; i<selNum; ++i)
{
ccHObject* ent = selectedEntities[i];
if (ent->isKindOf(CC_TYPES::POINT_CLOUD))
{
m_app->dispToConsole("[qCloudToCloud] POINT CLOUD",ccMainAppInterface::STD_CONSOLE_MESSAGE);
refCloud = ccHObjectCaster::ToGenericPointCloud(ent);
placeholder = i;
break;
}
}
//Find the next instance of a point cloud and compute the cloud to cloud distance
for (int i = 0; i < selNum; ++i)
{
ccHObject* ent = selectedEntities[i];
if (ent->isKindOf(CC_TYPES::POINT_CLOUD) && i!=placeholder)
{
CCLib::GenericIndexedCloudPersist* inputCloud = 0;
inputCloud = ccHObjectCaster::ToGenericPointCloud(ent);
inputCloud->enableScalarField();
int success = CCLib::DistanceComputationTools::computeCloud2CloudDistance(inputCloud, refCloud, params, 0, 0, 0);
//Print out the size of the two selected clouds (sanity check) and the output of the distance computation
m_app->dispToConsole(QString::number(refCloud->size()),ccMainAppInterface::STD_CONSOLE_MESSAGE);
m_app->dispToConsole(QString::number(inputCloud->size()),ccMainAppInterface::STD_CONSOLE_MESSAGE);
m_app->dispToConsole(QString::number(success),ccMainAppInterface::STD_CONSOLE_MESSAGE);
}
}
}
//This method should return the plugin icon (it will be used mainly
//if your plugin as several actions in which case CC will create a
//dedicated sub-menu entry with this icon.
QIcon qCloudToCloud::getIcon() const
{
//open qCloudToCloud.qrc (text file), update the "prefix" and the
//icon(s) filename(s). Then save it with the right name (yourPlugin.qrc).
//(eventually, remove the original qCloudToCloud.qrc file!)
return QIcon(":/CC/plugin/qCloudToCloud/icon.png");
}
#ifndef CC_QT5
//Don't forget to replace 'qCloudToCloud' by your own plugin class name here also!
Q_EXPORT_PLUGIN2(qCloudToCloud,qCloudToCloud);
#endif
As you can see I have called enableScalarField on the point cloud, but I was unable to figure out where to call the updateMinAndMax from/on.
Re: Cloud2CloudDistance and Cloud2MeshDistance: empty scalar field
Posted: Wed Jul 22, 2015 8:15 am
by daniel
Actually I was expecting the plugin files (I had to create the new plugin structure, update the header file, include the right headers in the cpp file, etc.).
What your plugin is missing here is proper SF management. CCLib performs the distances computation but don't care about the display and other cloud structure specifics. Notably the 'ccPointCloud' structure can handle multiple scalar fields (so you have to explicitly define which SF is 'active' and will be used to store the distances (ccPointCloud::setCurrentScalarField). You also have to call 'computeMinAndMax' on the scalar field (so that all the SF display parameters can be properly setup).
If you rely on the default 'enableScalarField' method you can't easily update and set the displayed scalar field (you have to look afterwards at the cloud SFs and guess which one has been automatically enabled and used for distances computation).
Therefore, to properly handle scalar fields it's better to create them yourself:
Code: Select all
//Find the next instance of a point cloud and compute the cloud to cloud distance
for (int i = 1; i < selNum; ++i)
{
ccHObject* ent = selectedEntities[i];
if (ent->isA(CC_TYPES::POINT_CLOUD) && i != placeholder)
{
ccPointCloud* inputCloud = ccHObjectCaster::ToPointCloud(ent);
//create a new SF to store the distances
//(warning: the name should be unique - normally you should look for existing SFs with
// the same name before creating a new one - search for 'addScalarField' in CC's code
// to see an example)
ccScalarField* sf = new ccScalarField("distances");
if (!sf->resize(inputCloud->size()))
{
m_app->dispToConsole("Not enough memory", ccMainAppInterface::ERR_CONSOLE_MESSAGE);
sf->release();
return;
}
int sfIdx = inputCloud->addScalarField(sf);
//make this SF 'active'
inputCloud->setCurrentScalarField(sfIdx);
int errorCode = CCLib::DistanceComputationTools::computeCloud2CloudDistance(inputCloud, refCloud, params, 0, 0, 0);
if (errorCode == 0)
{
sf->computeMinAndMax();
//make the SF the visible
inputCloud->setCurrentDisplayedScalarField(sfIdx);
inputCloud->showSF(true);
}
else
{
//if an error occurred, this SF is useless
inputCloud->deleteScalarField(sfIdx);
}
//redraw the GL window!
inputCloud->redrawDisplay();
//Print out the size of the two selected clouds (sanity check) and the output of the distance computation
m_app->dispToConsole(QString::number(refCloud->size()), ccMainAppInterface::STD_CONSOLE_MESSAGE);
m_app->dispToConsole(QString::number(inputCloud->size()), ccMainAppInterface::STD_CONSOLE_MESSAGE);
m_app->dispToConsole(QString::number(errorCode), ccMainAppInterface::STD_CONSOLE_MESSAGE);
}
}