Obtaining the Bounding Box of an AcDbSpline using ObjectARX

<?xml encoding=”UTF-8″>By Fenton Webb

Issue

I want to get the bounding box of spline, but the getGeomExtents() functions does not return the expected results. How do I get a tighter bounding box for a spline?

Solution

The AcDbSpline does not return a “tight” bounding box when getGeomExtents() is used.

To get a tighter bounding box you need to calculate the bounding box yourself. You can make use of the function getPointAtParam() to traverse along the spline and check for the maximum/minimum x and y coordinates. The accuracy of the bounding box depends upon how finely the curve is divided and sampled. The division value of 1e6 gives a good accuracy and takes acceptable time to calculate. The functions pasted below shows how this can be done. The attached VC++ project has the complete code (with minimal error checking). Type command “SplineBB” and select a spline object. It will draw two bounding boxes. The red rectangle is the extents returned by the getGeomExtents() while the yellow rectangle is the calculated one.

Something like this:

<p>///////////////////////////////////////////////////////////////////////////////////////////////<br>//Description: fKeepExtremes<br>//1) Function to check for the minimum/maximum X and Y. <br>//2) mPtMin and mPtMax will have minimum/maximum X and Y<br>///////////////////////////////////////////////////////////////////////////////////////////////</p>
<p>void fKeepExtremes(AcGePoint3d& mPtSample,AcGePoint3d& mPtMin,AcGePoint3d& mPtMax)<br>{<br> //test for max<br> if(mPtSample.x > mPtMax.x) mPtMax.x = mPtSample.x;<br> if(mPtSample.y > mPtMax.y) mPtMax.y = mPtSample.y;<br> if(mPtSample.z > mPtMax.z) mPtMax.z = mPtSample.z;<br> <br> //test for min<br> if(mPtSample.x < mPtMin.x) mPtMin.x = mPtSample.x;<br> if(mPtSample.y < mPtMin.y) mPtMin.y = mPtSample.y; <br> if(mPtSample.z > mPtMax.z) mPtMax.z = mPtSample.z;<br>}</p>
<p>///////////////////////////////////////////////////////////////////////////////////////////////<br>//Description: fGetBoundingBoxBySampling<br>//1) Function to divide the AcDbSpline 1e6 times and check for points at each division<br>///////////////////////////////////////////////////////////////////////////////////////////////<br>void fGetBoundingBoxBySampling(AcDbSpline *pSpline,AcGePoint3d& mPtMin, AcGePoint3d& mPtMax)<br>{<br> double mParam;<br> double mIncr;</p>
<p> double mStartParam;<br> double mEndParam;</p>
<p> AcGePoint3d mPtTemp;<br> pSpline->getStartPoint(mPtTemp);<br> pSpline->getParamAtPoint(mPtTemp,mStartParam);</p>
<p> pSpline->getEndPoint(mPtTemp);<br> pSpline->getParamAtPoint(mPtTemp,mEndParam);<br> <br> //calculate the division<br> mIncr = (mEndParam - mStartParam)/1e6; //1e6 is the sampling tolerance <br> <br> //set the seed point for max and min. It is set to the start point<br> mPtMax = mPtTemp;<br> mPtMin = mPtTemp;</p>
<p> for(mParam = mStartParam;mParam <= mEndParam;mParam +=mIncr)<br> {<br>  if(Acad::eOk == pSpline->getPointAtParam(mParam,mPtTemp))<br>  {<br>   fKeepExtremes(mPtTemp,mPtMin,mPtMax);<br>  }</p>
<p> }<br>}</p>
<p>///////////////////////////////////////////////////////////////////////////////////////////////<br>//Description: fDrawRect<br>//1) Draws a LwPolyline rectangle given lower left and upper right corners<br>///////////////////////////////////////////////////////////////////////////////////////////////<br>void fDrawRect(AcGePoint3d& mPtMin,AcGePoint3d& mPtMax,int mColor)<br>{<br> AcDbPolyline *pPline = new AcDbPolyline(4);<br> pPline->addVertexAt(0, AcGePoint2d(mPtMin.x,mPtMin.y));<br> pPline->addVertexAt(1,AcGePoint2d(mPtMax.x,mPtMin.y));<br> pPline->addVertexAt(2,AcGePoint2d(mPtMax.x,mPtMax.y));<br> pPline->addVertexAt(3,AcGePoint2d(mPtMin.x,mPtMax.y));<br> pPline->setClosed(Adesk::kTrue);<br> <br> fAddEntToDwg(acdbHostApplicationServices()->workingDatabase(),pPline);<br> pPline->setColorIndex(mColor);<br> pPline->close(); <br>}</p>
<p>///////////////////////////////////////////////////////////////////////////////////////////////<br>//Description: SplineBB<br>//1) command 'SplineBB' <br>///////////////////////////////////////////////////////////////////////////////////////////////<br>void SplineBB()<br>{<br> AcDbEntity *pEnt = NULL;<br> AcDbObjectId mId;<br> ads_point mPt;<br> ads_name mEname;<br> <br> //select the entity<br> if ( RTNORM == acedEntSel(L"Select a Spline", mEname, mPt))<br> {<br>  if ( Acad::eOk == acdbGetObjectId(mId, mEname ))<br>  {<br>   acdbOpenAcDbEntity(pEnt, mId, AcDb::kForRead);<br>  }<br> }<br> else<br> {<br>  return;<br> }</p>
<p> //see if it is a spline<br> AcDbSpline *pSpline = NULL;</p>
<p> if (NULL != pEnt)<br> {<br>  pSpline = AcDbSpline::cast(pEnt);</p>
<p>  if(NULL != pSpline)<br>  {<br>   AcGePoint3d mPtMin,mPtMax;</p>
<p>   //draw the bounding box returned by spline's getGeomExtents<br>   AcDbExtents mExts;<br>   pSpline->getGeomExtents(mExts);</p>
<p>   //draw bounding box returned by the spline in red<br>   fDrawRect(mExts.minPoint(),mExts.maxPoint(),1);<br>   <br>   //calculate the time taken<br>   struct _timeb t1,t2;<br>   _ftime(&t1);</p>
<p>   //calculate the bounding box<br>   fGetBoundingBoxBySampling(pSpline,mPtMin,mPtMax);<br>   <br>   _ftime(&t2);<br>   acutPrintf(L"nMethod Time %6.2f seconds.n", (t2.time + (double)(t2.millitm)/1000) - (t1.time + (double)(t1.millitm)/1000) );<br>   <br>   //draw calculated bounding box in yellow<br>   fDrawRect(mPtMin,mPtMax,2);</p>
<p>   pSpline->close();<br>  }<br>  else<br>  {<br>   acutPrintf(L"nEntity is not an Spline");<br>   pEnt->close();<br>  }<br> }<br> return;<br>}</p>

Comments

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading