ObjectARX
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Accessing point cloud entity loaded from a .pcg file

23 REPLIES 23
Reply
Message 1 of 24
xeri0n
1245 Views, 23 Replies

Accessing point cloud entity loaded from a .pcg file

Hello,

 

I had a look at the objectArx header files and I saw that there is a AcPointCloud.h file hinting at some Point cloud processing functions. 

What I want to do is obtain the point cloud points for specific extents. In essence the points that are located in a bounding box. The function to do that is acdbProcessPointCloudData which requires 4 parameters.

 

One of the input parameters is the point cloud of course. Is there a way to get the AcDbEntity of the .pcg loaded file, if we assume its only one for simplicity, so I can work with it ?

 

In AcDbPointcloud.h it is stated that it is the Api for AcDbPointCloud, however I see no class for AcDbPointCloud. Am I missing something ?

 

Thanks in advance! 

23 REPLIES 23
Message 2 of 24
owenwengerd
in reply to: xeri0n

It looks like Autodesk chose to not expose the AcDbPointCloud class, and instead have provided opaque functions to work with point cloud entities. You can still use the AcRxClass base class, even though the derived class is not documented.

--
Owen Wengerd
ManuSoft
Message 3 of 24
xeri0n
in reply to: owenwengerd

Thank you for your reply,

 

That is what I am trying out now. I am looping through the entities and checking if the AcRxClass->name() is "AcDbPointCloud". I guess then I can do whatever I want with it.

Message 4 of 24
owenwengerd
in reply to: xeri0n

Better to look up the point cloud AcRxClass object in the class dictionary and use it for your comparison:

AcRxClass* pRxPointCloud = (AcRxClass*)acrxClassDictionary->at( ACRX_T("AcDbPointCloud") );

 

--
Owen Wengerd
ManuSoft
Message 5 of 24
xeri0n
in reply to: owenwengerd

I guesss that way I can save iterating through the whole block table record. Much better ! 

 

I guess then the actual difficult part will be to implement the abstract base classes

IAcPcPointFilter

IAcPcDataBuffer

IAcPcPointProcessor

 

unless I am missing something?

 

Cheers ! 

Message 6 of 24
xeri0n
in reply to: xeri0n

I quickly hacked something in order to test the concept and see if it works.

 

Here is what I came up with and how I implmented the classes derived from the abstract implementations.

 

class ACDB_PORT AcPcPointFilter : public IAcPcPointFilter
{
public:
	AcPcPointFilter(){}

	void doFilter(const IAcPcDataBuffer& inBuffer, IAcPcDataBuffer& outBuffer){	}
};

class ACDB_PORT AcPcDataBuffer  : public IAcPcDataBuffer
{
public:
	AcPcDataBuffer(){	}

	///<summary>Indicate if the points are in single or double precision.</summary>
	bool nativeDbl(){return true;}
	
	///<summary>Resize the buffer. This may be a destructive operation.</summary>
	bool resize(DWORD size)
	{
		try
		{
			this->AcPcPointDoubleArray.resize(size);
		}
		catch (...)
		{
			return false;
		}

		return true;
	};

	///<summary>Shrink the buffer without destroying its contents.</summary>
	bool shrink(DWORD size)
	{
		try
		{
			// just discard excess capacity
			this->AcPcPointDoubleArray.shrink_to_fit();
		}
		catch (...)
		{
			return false;
		}

		return true;
	}
	
	/// <summary>Return the number of points in the buffer.</summary>
	DWORD size() const
	{
		return (DWORD)this->AcPcPointDoubleArray.size();
	}

	/// <summary>Return a array of single precision points.
	/// This may return NULL if the buffer is not single precision.</summary>
	AcPcPointFloat*  floatPoints (){return NULL;}

	/// <summary>Return a array of double precision points.
	/// This may return NULL if the buffer is not double precision.</summary>
	AcPcPointDouble* doublePoints()
	{
		return &this->AcPcPointDoubleArray[0];
	}

	/// <summary>Return a point in single precision.
	/// This will work regardless of the native precision of the buffer.</summary>
	bool floatPoint(DWORD ptIx, AcPcPointFloat& pt) const
	{
		if (ptIx < this->AcPcPointDoubleArray.size())
		{
			pt = (AcPcPointFloat&)this->AcPcPointDoubleArray[ptIx];
			return true;
		}

		return false;
	}

	/// <summary>Return a point in double precision.
	/// This will work regardless of the native precision of the buffer.</summary>
	bool doublePoint (DWORD ptIx, AcPcPointDouble& pt) const
	{
		if (ptIx < this->AcPcPointDoubleArray.size())
		{
			pt = this->AcPcPointDoubleArray[ptIx];
			return true;
		}

		return false;
	}

	/// <summary>Set a point in single precision.
	/// This will work regardless of the native precision of the buffer.</summary>
	bool setFloatPoint(DWORD ptIx, AcPcPointFloat&  pt)
	{
		if (ptIx < this->AcPcPointDoubleArray.size())
		{
			this->AcPcPointDoubleArray[ptIx] = (AcPcPointDouble&)pt;
			return true;
		}

		return false;
	}

	/// <summary>Set a point in double precision.
	/// This will work regardless of the native precision of the buffer.</summary>
	bool setDoublePoint (DWORD ptIx, AcPcPointDouble& pt)
	{
		if (ptIx < this->AcPcPointDoubleArray.size())
		{
			this->AcPcPointDoubleArray[ptIx] = pt;
			return true;
		}

		return true;
	}

	/// <summary>Get the translation offset of the points.
	/// This offset needs to be added to each point in order to get the 
	/// original location of the point in the point cloud</summary>
	bool offset(double& x, double& y, double& z) const
	{
		return true;
	}
	/// <summary>This transform needs to be applied to the points to
	/// get the points in world space.</summary>
	bool entityTransform(AcGeMatrix3d& matrix) const
	{
		return true;
	}

	/// <summary>Copy the contents of the given buffer.</summary>
	void copyFrom(IAcPcDataBuffer const & from)
	{
		this->AcPcPointDoubleArray.clear();
		AcPcPointDouble temp;

		for (DWORD i = 0 ; i < from.size(); i++)
		{
			from.doublePoint(i,temp);
			this->AcPcPointDoubleArray.push_back( temp );
		}
	}

public:
	std::vector<AcPcPointDouble> AcPcPointDoubleArray;
};

class ACDB_PORT AcPcPointProcessor : public IAcPcPointProcessor
{
public:
	AcPcPointProcessor()
	{
		AcPcDataBuffer *buf = new AcPcDataBuffer();
		this->setBuffer(buf);

		this->bCancel = false;
		this->ptFilter = NULL;
	};

	~AcPcPointProcessor()
	{
		if (this->buffer())
			delete this->buffer();
	}

	/// <summary>Return true to cancel processing.</summary>
	bool cancel(){return false;}

	bool setCancel(bool b) {this->bCancel = b;}

	/// <summary>Called to abort the process</summary>
	void abort(){}

	/// <summary>Called after the last call to ProcessPoints</summary>
	void finished(){}

	/// <summary>Process point in buffer. This will be called repeatedly with 
	/// new buffer contents until all points have been processed.</summary>
	bool processPoints(){return true;}

	/// <summary>Filter, if available, to filter points.
	/// Called before processPoints() on the next batch of points.</summary>
	IAcPcPointFilter*  filter()
	{
		if (ptFilter)
			return this->ptFilter;
		else
			return NULL;
	}

	/// <summary> Set Filter, to filter points.
	void setFilter(IAcPcPointFilter* newfilter){this->ptFilter = newfilter;}

private:
	bool bCancel;
	IAcPcPointFilter* ptFilter;
};

  And here is how I call the acdbProcessPointCloudData function

 

// set bounding box - just testing so nothing useful
AcGePoint3d ptmin(0,0,0);
AcGePoint3d ptmax(1000000000,1000000000,2000000);
const AcDbExtents boundingBox( ptmin, ptmax);
		
// init AcPcPointProcessor
AcPcPointProcessor* ptProc = new AcPcPointProcessor();

Acad::ErrorStatus err = acdbProcessPointCloudData(pEntity, boundingBox, 100, ptProc);

 

While the function returns eOK and the size of the buffer seems fine the actual data in the vector is garbage.

Do you see anything suspicious ?

 

Any idea is appreciated ! 

Message 7 of 24
owenwengerd
in reply to: xeri0n

I think you're supposed to let the point cloud engine allocate and manage the data buffer.

--
Owen Wengerd
ManuSoft
Message 8 of 24
xeri0n
in reply to: owenwengerd

This is actually something that I initially tried and it does not work. The class is abstract so I cannot really pass anything else apart from a null pointer and hope that autocad has derived classes (not exposed) to handle it.

 

 

IAcPcPointProcessor* iptProc = 0;
Acad::ErrorStatus err = acdbProcessPointCloudData(pEntity, boundingBox, 100, iptProc);

However the above call results in an error of "eNullPtr"...


Message 9 of 24
owenwengerd
in reply to: xeri0n

Of course you need to provide a IAcPcPointProcessor, but let AutoCAD allocate and manage its data buffer.

--
Owen Wengerd
ManuSoft
Message 10 of 24
xeri0n
in reply to: owenwengerd

Thanks again for your reply.

 

That sounds interesting and for some reason the buffer is initialised as I can see in the debugger. I dont know if its just a random memory position or actually initialised as I have not way to check that since its an abstract class. I cannot get access to any of the size(), or parse the array at all to check the contents.

 

Your help is greatly appreciated ! 

 

Message 11 of 24
owenwengerd
in reply to: xeri0n

I think you are confused by the abstract class declaration. The fact that its members are pure virtual does not mean you can't call them. You should be able to call IAcPcDataBuffer::size() to get the number of points in the buffer each time your IAcPcPointProcessor::filter() and IAcPcPointProcessor:: processPoints() functions are called.

--
Owen Wengerd
ManuSoft
Message 12 of 24
xeri0n
in reply to: owenwengerd

What I tried to do was

 

Acad::ErrorStatus err = acdbProcessPointCloudData(pEntity, boundingBox, 100, ptProc);
IAcPcDataBuffer* buf = ptProc->buffer();
DWORD size = buf->size();

This resulted in a runtime error. 

I tried what you mentioned and put in the finished() as well as the processPoints() 

DWORD size = this->buffer()->size();

and was OK. 

 

In the finished() function it was 0 size array but in every processPoints() function call there was 1 million Pts which makes sense. 

This makes things a bit more clear now. The buffer is allocated and deallocated after iteration so accessing the buffer as I did (when the call has ended) is not possible as its deallocated already. What helped me understand is that in the finished() function the array was 0 size. Pretty much if I want to do anything with the points I need to keep/store them somewhere else and do that in the processPoints().

 

Thank you for you help ! 

 

Message 13 of 24
owenwengerd
in reply to: xeri0n

I'm not sure why you are accessing the data in the finished() function. That function is for you to signal to the processor that you are finished processing. Also, you should never dereference the data pointer without first checking that it isn't NULL.

--
Owen Wengerd
ManuSoft
Message 14 of 24
xeri0n
in reply to: owenwengerd

No reason really ! 

I was just trying to understand what was going on with the buffer.

Message 15 of 24
xeri0n
in reply to: xeri0n

Hm one more question if I may.

 

I actually set a proper query and I see something weird I think.

Just to confirm some things before going on here is some info for my point cloud entity from Acad

 

AcDbExtents boundBox;
pEntity->getGeomExtents(boundBox);

// min Value {x=704669.92000000004 y=5751465.0200000005 z=115.55000000000000 }
// max value {x=704680.00000000000 y=5755356.0000000000 z=160.00000000000000 }

 So I set as my search

 

// set bounding box for query
AcGePoint3d ptmin(704669.0, 5755340.0, 0);
AcGePoint3d ptmax(704680.0, 5755356.0, 160);
const AcDbExtents boundingBox( ptmin, ptmax);

 

The returned buffer size was 175108 which makes sense for a converted LAS file.

 

When I access the points by

AcPcPointFloat* pt = this->buffer()->floatPoints();

the all are like this

0x0000000040940040 {m_x=-11470.385 m_y=1847.1350 m_z=-41.020000 ...}

 

This is definitely not in the extents that I am queryin for. Any idea ? Am i doing something wrong ?

Message 16 of 24
owenwengerd
in reply to: xeri0n

It looks like you are assuming the points are in float format. Call nativeDbl() first to check the format, or call floatPoint() instead to get individual points if you want them in float format.

--
Owen Wengerd
ManuSoft
Message 17 of 24
xeri0n
in reply to: owenwengerd

This time I actually had tested that 🙂

 

The this->buffer()->doublePoints(); was returning NULL and this is what the documentation says while also the nativeDbl is false.

Both calls to floatPoint and doublePoint return the exact same coordinates ! 

Message 18 of 24
owenwengerd
in reply to: xeri0n

Did you notice the comments for the offset() and entityTransform() functions?

--
Owen Wengerd
ManuSoft
Message 19 of 24
xeri0n
in reply to: owenwengerd

Mhm I did not apply that transformation ...

I just tried it though and it seems that the matrix is just identity though so no difference.

 

AcPcPointFloat* pt = this->buffer()->floatPoints();
AcGeMatrix3d trans;
bool b = this->buffer()->entityTransform(trans);
AcGePoint3d tmpDBPoint(pt->m_x,pt->m_y,pt->m_z);
tmpDBPoint.transformBy(trans);

 I am sure that there is no offset by the way just to rule out that one as well.

Message 20 of 24
xeri0n
in reply to: xeri0n

Mhm lets just forget my last statement.

Seems like the point cloud engine is doing something and there is an offset even if my insertion point is (0,0,0).

 

Cheers ! 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost