CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

For any question about plugins!
Charlesw
Posts: 45
Joined: Mon Apr 10, 2017 3:54 pm

CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by Charlesw »

Good afternoon,

I am working on converting a plugin I designed into a command line COMMAND.

In doing so, I have come upon a few issues I was hoping I could acquire advice for.

1. What is the best way to troubleshoot/ debug code that is being tested by running the command line version. To test, I have a bat file setup to run the commands needed. Is there a best practice from Visual Studio to simulate terminal commands that would allow the executing code to be debugged?

2. I have a code snippet that has executed well to a certain task. This task is to import multiple clouds. Merging all the children of each cloud (E57s) upon import, and return the in a vector of ccHObject*s called "selectedEntities":
std::vector<ccHObject*> importGeneralFiles(ccCommandLineInterface &cmd, QStringList allSelectedFiles)
I have confirmed that this vector ends up with the correct number of children in the end. AKA: 3 files selected, this vector ends up being of 3 length.

I now want to create a new ccHObject, say "finalGroup", and make each element of the vector mentioned above as a Child of "finalGroup":

for (int i = 0; i < selectedEntities.size(); ++i) {
//convert each item into a cloud and store as a child of "CCHObject* finalGroup"
ccPointCloud* pc = static_cast<ccPointCloud*>(selectedEntities);
finalGroup->addChild(pc);
cmd.error(QString::number(i) + QString("\n") + QString::number(finalGroup->getChildrenNumber()));
}
cmd.error(QString::number(finalGroup->getChildrenNumber()));

The above code, however, does not work. No matter what, every time the "->addChild()" method is used, "finalGroup" still remains with no more than 1 child. I have confimed that "i" is incrementing and that "selectedEntities" has the correct number of elements. I am using "cmd.error()" to try and debug the program as I am not sure hot to link this with the Visual Studio Debugger....

Any insight into either of the issues would be amazing.

Respectfully,
daniel
Site Admin
Posts: 7382
Joined: Wed Oct 13, 2010 7:34 am
Location: Grenoble, France
Contact:

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by daniel »

1) You can input the command line (parameters) in the "command arguments" option of the "Debug" section of the "CloudCompare" project properties (make sure the Configuration is Debug as well when changing this option ;).

You can then launch CloudCompare in debug mode, and it will trigger the command line mechanism with the input parameters.

2) ccPointCloud* pc = static_cast<ccPointCloud*>(selectedEntities); --> I hope it's selectedEntities instead?!
Daniel, CloudCompare admin
Charlesw
Posts: 45
Joined: Mon Apr 10, 2017 3:54 pm

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by Charlesw »

So I made a typo when putting the code into the forum. I was using:

Code: Select all

ccPointCloud* pc = static_cast<ccPointCloud*>(selectedEntities[i]);
I also am trying to use:

Code: Select all

ccPointCloud* pc = ccHObjectCaster::ToPointCloud(selectedEntities[i]);
Otherwise, I am not sure what you were inferring with your comment:
"I hope it's selectedEntities instead?!"

I am giving the debug a shot now. This will help me tremendously being able to see what errors are being thrown. ;P
daniel
Site Admin
Posts: 7382
Joined: Wed Oct 13, 2010 7:34 am
Location: Grenoble, France
Contact:

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by daniel »

Ah ah I also wanted to write selectedEntities!
Daniel, CloudCompare admin
Charlesw
Posts: 45
Joined: Mon Apr 10, 2017 3:54 pm

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by Charlesw »

Is there a limitation in the command line where only one cloud can be loaded at a time?

If not, I have no idea why I am getting an error. Basically, I can import a single cloud, but any additional clouds give me NULL objects returned.

I am using recycled code from a custom plugin for import and saving of clouds rather than the built in command line methods.

Here is some snippets of the code, maybe you'd have a better idea as to why I am unable to use multiple clouds?

Background:
I have a directory that has multiple E57 or PTS cloud files. I want to loop through the directory and pull each cloud in, merge its children, and then save the cloud back to the directory it came from, except in a subfolder titled "autosave...etc".

Code: Select all

// Main Method:
		QDir directory(inputDirectoryStr);
		files = directory.entryList(QStringList() << "*.E57" << "*.PTS" << "*.TXT" << "*.LAS" << "*.LAZ", QDir::Files);
		//autosave the pts file to the project folder
		FileIOFilter::SaveParameters saveParameters;
		{
			saveParameters.alwaysDisplaySaveDialog = false;
			//saveParameters.parentWidget = m_app->getMainWindow();
		}

		for (int i = 0; i < files.size(); ++i){
			files[i] = inputDirectoryStr + "\\" + files[i];
			tempList << files[i];
		}
		//tempStr = tempStr + "\n" + files[i];
			
		selectedEntities = importGeneralFiles(cmd, tempList);
		
		for (int i = 0; i < selectedEntities.size(); ++i) {
			//convert each item into a cloud and store as a child of "CCHObject* projectGroup"
			//ccPointCloud* pc = static_cast<ccPointCloud*>(selectedEntities[i]);
			ccGenericPointCloud* pc = ccHObjectCaster::ToGenericPointCloud(selectedEntities[i]);
			ccHObject* tempObj = pc->clone(); //ISSUE: returns a null object
			projectGroup->addChild(tempObj);
		}
		save(cmd, files, projectGroup, QString("Export"), QString("PTS"), saveParameters);

Code: Select all

				// importGeneralFiles
	std::vector<ccHObject*> importGeneralFiles(ccCommandLineInterface &cmd, QStringList allSelectedFiles)
	{
		std::vector<ccHObject*> resultingObjects(allSelectedFiles.size());
		...
		...
				int numberChildren = newGroup->getChildrenNumber();
				std::vector<ccHObject*> newGroupChildren(numberChildren);

				if (numberChildren > 1)
				{
					for (int count = 0; count < numberChildren; ++count)
					{
						newGroupChildren[count] = newGroup->getChild(count);
					}
					//m_ccRoot->selectEntities(newGroupChildren, false); //if the newly created group has multiple children, end the import having all of the children Auto-Selected for Merge
					mergeFiles(cmd, newGroupChildren);

					//make sure for e57, we add the cloud not the file structure
					QString fileName = allSelectedFiles.takeAt(i);
					int lastPoint = fileName.lastIndexOf(".");
					QString fileNameExt = fileName.right(fileName.size() - lastPoint);
					if (fileNameExt == QString(".e57"))
					{
						resultingObjects[i] = newGroupChildren[1]; //trace changes; //current child is the "file structure" of the e57, move to next child for the merge cloud of the e57 file
					}
					else
					{
						resultingObjects[i] = newGroupChildren[0]; //trace changes
					}


				}
				return resultingObjects;

Code: Select all


void mergeFiles(ccCommandLineInterface &cmd, std::vector<ccHObject*> newGroupChildren)
	{
	    //let's look for clouds or meshes (warning: we don't mix them)
		newGroupChildren.size() <= 0 ? cmd.error(QString("No Children")) : cmd.error(QString("Has Children"));
		std::vector<ccPointCloud*> clouds;
		std::vector<ccMesh*> meshes;
		try
		{
			size_t selNum = newGroupChildren.size();
			for (size_t i = 0; i < selNum; ++i)
			{
				ccHObject* ent = newGroupChildren[i];
				if (!ent)
					continue;

				if (ent->isA(CC_TYPES::POINT_CLOUD))
				{
					ccPointCloud* cloud = ccHObjectCaster::ToPointCloud(ent);
					clouds.push_back(cloud);
				}
				else if (ent->isKindOf(CC_TYPES::MESH))
				{
					ccMesh* mesh = ccHObjectCaster::ToMesh(ent);
					//this is a purely theoretical test for now!
					if (mesh && mesh->getAssociatedCloud() && mesh->getAssociatedCloud()->isA(CC_TYPES::POINT_CLOUD))
					{
						meshes.push_back(mesh);
					}
					else
					{
						cmd.warning(QString("Only meshes with standard vertices are handled for now! Can't merge entity '%1'...").arg(ent->getName()));
					}
				}
				else
				{
					cmd.warning(QString("Entity '%1' is neither a cloud nor a mesh, can't merge it!").arg(ent->getName()));
				}
			}
		}
		catch (const std::bad_alloc&)
		{
			cmd.error(QString("Not enough memory!"));
			return;
		}

		if (clouds.empty() && meshes.empty())
		{
			cmd.error(QString("Select only clouds or meshes!"));
			return;
		}
		if (!clouds.empty() && !meshes.empty())
		{
			cmd.error(QString("Can't mix point clouds and meshes!"));
		}

		//merge clouds?
		if (!clouds.empty())
		{
			//we will remove the useless clouds/meshes later
			ccHObject::Container toBeRemoved;

			ccPointCloud* firstCloud = 0;
			ccHObjectContext firstCloudContext;

			for (size_t i = 0; i < clouds.size(); ++i)
			{
				ccPointCloud* pc = clouds[i];
				if (!firstCloud)
				{
					//we don't delete the first cloud (we'll merge the other one 'inside' it)
					firstCloud = pc;
					if (clouds.size() == 1)
					{
						mergedGroup->addChild(firstCloud);
					}
					//we still have to temporarily detach the first cloud, as it may undergo
					//"severe" modifications (octree deletion, etc.) --> see ccPointCloud::operator +=
					//firstCloudContext = removeObjectTemporarilyFromDBTree(firstCloud); eco-verify
				}
				else
				{
					unsigned countBefore = firstCloud->size();
					unsigned countAdded = pc->size();
					*firstCloud += pc;

					//success?
					if (firstCloud->size() == countBefore + countAdded)
					{
						firstCloud->prepareDisplayForRefresh_recursive();
						//mergedGroup->addChild(firstCloud);
						if (i==0)
						{
							mergedGroup->addChild(firstCloud);
						}
					}
					else
					{
						cmd.error(QString("Fusion failed! (not enough memory?)"));
						break;
					}
					pc = 0;
				}
			}
		}
	}

Here are some of the results I am getting:
Within the Main Method, the lines

Code: Select all

projectGroup->addChild(selectedEntities[0]);
save(cmd, files, projectGroup, QString("Export"), QString("PTS"), saveParameters);"
returns the following error:
Capture.PNG
Capture.PNG (8.85 KiB) Viewed 10527 times

Using:

Code: Select all

save(cmd, files, selectedEntities[0], QString("Export"), QString("PTS"), saveParameters);
Works great. If I only want to save the first cloud.

But Using:

Code: Select all

save(cmd, files, selectedEntities[1], QString("Export"), QString("PTS"), saveParameters);
returns the following error:
I dont get an error, but no cloud is saved in the output directory.
daniel
Site Admin
Posts: 7382
Joined: Wed Oct 13, 2010 7:34 am
Location: Grenoble, France
Contact:

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by daniel »

Have you tried in 'debug' mode? It would be easier to spot where the program crash exactly.
Daniel, CloudCompare admin
Charlesw
Posts: 45
Joined: Mon Apr 10, 2017 3:54 pm

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by Charlesw »

Yes, it crashes on the following in the main method:

Code: Select all

1.		for (int i = 0; i < selectedEntities.size(); ++i) {
2.			//convert each item into a cloud and store as a child of "CCHObject* projectGroup"
3.			//ccPointCloud* pc = static_cast<ccPointCloud*>(selectedEntities[i]);
4.			ccGenericPointCloud* pc = ccHObjectCaster::ToGenericPointCloud(selectedEntities[i]);
5.			ccHObject* tempObj = pc->clone(); //ISSUE Crashes on this execution due to trying to clone null object
6.			projectGroup->addChild(tempObj);
7.		}
8.		save(cmd, files, projectGroup, QString("Export"), QString("PTS"), saveParameters); //This calls upon saveToFile method mentioned below
The error shown while in debug is as follows:
Capture2.PNG
Capture2.PNG (8.09 KiB) Viewed 10503 times
Code Broke at: AsciiFilter.cpp, saveToFile Line 179: unsigned numberOfPoints = cloud->size();

I believe the merge function must be the issue somewhere along the lines. I am unsure where. And the function works in my other plugins.
daniel
Site Admin
Posts: 7382
Joined: Wed Oct 13, 2010 7:34 am
Location: Grenoble, France
Contact:

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by daniel »

Oh, indeed the ccHObjectCaster::ToGenericPointCloud method returns nullptr if the entity is not a (generic) point cloud.

Which means one of the selected entity is not a point cloud (it may be a group, a sensor, etc. - I don't know). You could display the type of the entities, or add a break point and look at it with the debugger).

Anyway, you just need to test 'pc' against nullptr, and skip it if it's nullptr...
Daniel, CloudCompare admin
Charlesw
Posts: 45
Joined: Mon Apr 10, 2017 3:54 pm

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by Charlesw »

Thank you! I tested against nullptr to confirm there was a null object.

How would one properly return the type of the entities? I see the use of getClassID(), but not sure how to get this Enum in a way that is readable for me or translatable (This is a novice question and I apologize. Not super familiar with Enums and returning their values or definitions). I tried converting the returned enum to qstring::number and then just looking up the section of code that defines the enums, however that does not work for this case.
daniel
Site Admin
Posts: 7382
Joined: Wed Oct 13, 2010 7:34 am
Location: Grenoble, France
Contact:

Re: CLI Help - Adding Child to ccHObject* - CommandLine Visual Studio Debugging

Post by daniel »

If you use Visual Studio, you can add a breakpoint anywhere (in debug mode) and use the "spy" mode to look at a variable and see its type.

Otherwise you would have to create your own method where you test the entity against each know type (well, the most probable) and display the type name...
Daniel, CloudCompare admin
Post Reply