Making a custom entity Associative Dimension enabled

<?xml encoding=”UTF-8″>By Balaji Ramamoorthy

You can create associative dimensions after setting the system variable DIMASSOC value to 2 and calling any of the dimensioning commands. This works fine on all the native AutoCAD entities, but it does not with your own custom entity. To enable associative dimensioning for your custom entity, there are two ways. In this blog post and in its attached sample, these are demonstrated.

Before we look at sample code, here is a short video of its working :

Method 1 : 

You need to take help of few exported APIs in AcDimX20.dll. Unfortunately these are not published APIs and hence they are not supported and you have to use them at your own risk.

The three APIs exported in the AcDimX20.dll are:

Acad::ErrorStatus acdbEnableDimAssocForEntity(AcRxClass* pClass)
Acad::ErrorStatus acdbDisableDimAssocForEntity(AcRxClass* pClass)
bool acdbDimAssocIsEnabledForEntity(AcRxClass* pClass)

Their function is self explanatory. The AcDimX.dll file is available in the AutoCAD install folder.

Using acdbEnableDimAssocForEntity() API you can enable associative dimensioning for your custom entity. In the attached sample project, “MyEnt2” uses this approach.

You should look at the On_kInitAppMsg() function in the acrxEntryPoint.cpp that registers the custom entity class for associative dimensioning.

Also, you must implement subSubentPtr() override for your custom entity. In the attached sample project, “MyEnt2” is a custom entity that enables associative dimension using this method. It has subSubentPtr method implemented which allows the user to use associative dimensioning at its end points.

Here is the relevant portion of the code. To try it, please download the attached sample project.

 <span>typedef</span><span>  Acad::ErrorStatus </span>
 	(*exp_acdbDimAssocForEntity)(AcRxClass* pClass);
 
 MyEnt2::rxInit();
 acrxBuildClassHierarchy();
 HMODULE hModule = LoadLibrary( _T(<span>"AcDimX20.dll"</span><span> ));</span>
 
 exp_acdbDimAssocForEntity fPtrEnableDimAssoc = NULL;
 <span>if</span><span>  (NULL != hModule)</span>
 <span>{</span>
 	fPtrEnableDimAssoc = (exp_acdbDimAssocForEntity)
 		GetProcAddress(hModule, 
 		<span>"acdbEnableDimAssocForEntity"</span><span> );</span>
 <span>}</span>
 
 Acad::ErrorStatus es;
 <span>if</span><span> (fPtrEnableDimAssoc)</span>
 <span>{</span>
 	<span>// Enable associative dimension for MyEnt2 using </span><span> </span>
 	<span>// acdbEnableDimAssocForEntity</span><span> </span>
 	es = fPtrEnableDimAssoc(MyEnt2::desc());
 <span>}</span>
 
 <span>//free the library</span><span> </span>
 <span>if</span><span>  (NULL != hModule)</span>
 <span>{</span>
 	FreeLibrary(hModule);
 <span>}</span>
 

Method 2 :

For custom entity derived from AcDb3dSolid and AcDbSurface, Method 1 does not work. Here is the explanation provided by Jiri Kripac from our engineering team on why the first approach does not work :

AcDb3dSolid and AcDbSurface use the new associativity mechanism based on the Associative Framework. The associative dimension is controlled by an action derived from AcDbAssocAnnotationActionBody, not by the legacy AcDbDimAssoc. This allows to maintain associativity even when the topology of the solid/surface changes because the new mechanism uses persistent subentity ids (complex internal objects) to persistently identify subentities (faces/edges/vertices) of the entity, whereas AcDbDimAssoc uses just simple indices that become invalid when the topology of the entity changes.

Entities reveal their subentities by implementing AcDbAssocPersSubentIdPE. The Associative Framework queries this protocol extension when creating new associative dimensions based on AcDbAssocAnnotationActionBody. AcDb3dSolid exposes this protocol extension. Your custom entity derived from AcDb3dSolid does not expose this protocol extension to reveal your subentities, therefore the base class protocol extension for AcDb3dSolid is used that reveals subentities of the solid. 

You need to implement AcDbAssocPersSubentIdPE to reveal your subentities of your custom entity.

In the attached sample project, “MyEnt1” is a custom entity derived from AcDb3dSolid and implements AcDbAssocPersSubentIdPE to reveal its subentities.

The header file of “AcDbAssocPersSubentIdPE” is very well documented by our engineering team. Please read it and that should help in customizing it for your custom entity. Here is the relevant portion of the custom AcDbAssocPersSubentIdPE implementation for “MyEnt1” custom entity from the attached sample project.

 <span>// MyEntPersSubentIdPE.cpp</span><span> </span>
 
 AcDbAssocPersSubentId* MyEntPersSubentIdPE::
 	createNewPersSubent(
 					AcDbEntity*                 pEntity,
 					AcDbDatabase*               pDatabase,
                     <span>const</span><span>  AcDbCompoundObjectId& compId,</span>
                     <span>const</span><span>  AcDbSubentId&         subentId)</span>
 <span>{</span>
 	<span>switch</span><span> (subentId.index())</span>
 	<span>{</span>
 		<span>case</span><span>  100: </span>
 			<span>return</span><span>  <span>new</span><span>  AcDbAssocEdgePersSubentId(101, 102);</span></span>
 
 		<span>case</span><span>  101: </span>
 			<span>return</span><span>  <span>new</span><span>  AcDbAssocEdgePersSubentId(101, 0);</span></span>
 
 		<span>case</span><span>  102: </span>
 			<span>return</span><span>  <span>new</span><span>  AcDbAssocEdgePersSubentId(102, 0);</span></span>
 
 		<span>default</span><span> :</span>
 		<span>{</span>
 			acutPrintf(ACRX_T(<span>"\n createNewPersSubent </span><span> </span>
 				not implemented !!<span>"));</span><span> </span>
 			<span>break</span><span> ;</span>
 		<span>}</span>
 	<span>}</span>
     <span>return</span><span>  <span>new</span><span>  AcDbAssocSimplePersSubentId(subentId);</span></span>
 <span>}</span>
 
 Acad::ErrorStatus MyEntPersSubentIdPE::
 				getTransientSubentIds(
 				<span>const</span><span>  AcDbEntity* pEntity, </span>
                 AcDbDatabase*                pDatabase,
                 <span>const</span><span>  AcDbAssocPersSubentId* pPersSubentId,</span>
                 AcArray<AcDbSubentId>&       subents) <span>const</span><span> </span>
 <span>{</span>
 	Acad::ErrorStatus es = Acad::eNotImplemented;
 
 	MyEnt1 *pMyEnt1 = MyEnt1::cast(pEntity);
 	<span>if</span><span> (pMyEnt1 != NULL)</span>
 	<span>{</span>
 		AcDbAssocEdgePersSubentId *pEdgePerSubEntId 
 			= AcDbAssocEdgePersSubentId::cast(pPersSubentId);
 		<span>if</span><span> (pEdgePerSubEntId != NULL)</span>
 		<span>{</span>
 			<span>int</span><span>  index1 = pEdgePerSubEntId->index1();</span>
 			<span>int</span><span>  index2 = pEdgePerSubEntId->index2();</span>
 			<span>if</span><span> (index2 == 0)</span>
 			<span>{</span>
 				subents.append(
 					AcDbSubentId(
 					AcDb::kVertexSubentType, 
 					index1));
 			<span>}</span>
 			<span>else</span><span> </span>
 			<span>{</span>
 				subents.append(
 					AcDbSubentId(
 					AcDb::kEdgeSubentType, 
 					100));
 			<span>}</span>
 
 			<span>return</span><span>  Acad::eOk;</span>
 		<span>}</span>
 	<span>}</span>
 	<span>return</span><span>  es;</span>
 <span>}</span>
 
 Acad::ErrorStatus MyEntPersSubentIdPE::
 			getAllSubentities(
 					<span>const</span><span>  AcDbEntity* pEntity,</span>
 					AcDb::SubentType subentType,
                     AcArray<AcDbSubentId>& allSubentIds)
 <span>{</span>
 	Acad::ErrorStatus es = Acad::eNotImplemented;
 
 	MyEnt1 *pMyEnt1 = MyEnt1::cast(pEntity);
 	<span>if</span><span> (pMyEnt1 != NULL)</span>
 	<span>{</span>
 		<span>if</span><span> (subentType == AcDb::kEdgeSubentType)</span>
 		<span>{</span>
 			allSubentIds.append(
 				AcDbSubentId(AcDb::kEdgeSubentType, 100));
 			<span>return</span><span>  Acad::eOk;</span>
 		<span>}</span>
 		<span>else</span><span>  <span>if</span><span> (subentType == AcDb::kVertexSubentType)</span></span>
 		<span>{</span>
 			allSubentIds.append(
 				AcDbSubentId(
 				AcDb::kVertexSubentType, 101));
 
 			allSubentIds.append(
 				AcDbSubentId(
 				AcDb::kVertexSubentType, 102));
 			<span>return</span><span>  Acad::eOk;</span>
 		<span>}</span>
 	<span>}</span>
     <span>return</span><span>  es;</span>
 <span>}</span>
 
 Acad::ErrorStatus MyEntPersSubentIdPE::
 				getEdgeVertexSubentities(
 				<span>const</span><span>  AcDbEntity*      pEntity,</span>
                 <span>const</span><span>  AcDbSubentId&    edgeSubentId,</span>
 				AcDbSubentId&          startVertexSubentId,
                 AcDbSubentId&          endVertexSubentId,
 				AcArray<AcDbSubentId>& otherVertexSubentIds)
 <span>{</span>
 
 	Acad::ErrorStatus es = Acad::eNotImplemented;
 
 	MyEnt1 *pMyEnt1 = MyEnt1::cast(pEntity);
 	Adesk::GsMarker gsMarker = edgeSubentId.index();
 	<span>switch</span><span> (gsMarker)</span>
 	<span>{</span>
 		<span>case</span><span>  100:</span>
 			startVertexSubentId = AcDbSubentId(
 				AcDb::kVertexSubentType, 101);
 			endVertexSubentId = AcDbSubentId(
 				AcDb::kVertexSubentType, 102);
 			<span>return</span><span>  Acad::eOk;</span>
 
 		<span>default</span><span> :</span>
 		<span>{</span>
 			acutPrintf(ACRX_T(
 				<span>"\n getEdgeVertexSubentities </span><span> </span>
 				not implemented !!<span>"));</span><span> </span>
 			<span>break</span><span> ;</span>
 		<span>}</span>
 	<span>}</span>
     <span>return</span><span>  es;</span>
 <span>}</span>
 
 Acad::ErrorStatus MyEntPersSubentIdPE::
 				getVertexSubentityGeometry(
 				<span>const</span><span>  AcDbEntity*   pEntity,</span>
                 <span>const</span><span>  AcDbSubentId& vertexSubentId,</span>
                 AcGePoint3d&        vertexPosition)
 <span>{</span>
 	Acad::ErrorStatus es =  Acad::eNotImplemented;
 
 	MyEnt1 *pMyEnt1 = MyEnt1::cast(pEntity);
 
 	Adesk::GsMarker gsMarker = vertexSubentId.index();
 	<span>switch</span><span> (gsMarker)</span>
 	<span>{</span>
 		<span>case</span><span>  100:</span>
 		<span>{</span>
 			acutPrintf(ACRX_T(<span>"\n </span><span> </span>
 				getVertexSubentityGeometry 
 				not implemented <span>for</span><span>  %d !!<span>"), gsMarker);</span><span> </span></span>
 			vertexPosition = pMyEnt1->m_ptEP;
 			<span>return</span><span>  Acad::eOk;</span>
 		<span>}</span>
 
 		<span>case</span><span>  101:</span>
 			vertexPosition = pMyEnt1->m_ptSP;
 			<span>return</span><span>  Acad::eOk;</span>
 
 		<span>case</span><span>  102:</span>
 			vertexPosition = pMyEnt1->m_ptEP;
 			<span>return</span><span>  Acad::eOk;</span>
 
 		<span>case</span><span>  0: </span>
 			<span>return</span><span>  Acad::eOk;</span>
 
 		<span>default</span><span>  :</span>
 		<span>{</span>
 			acutPrintf(ACRX_T(<span>"\n </span><span> </span>
 				getVertexSubentityGeometry 
 				not implemented !!<span>"));</span><span> </span>
 			<span>break</span><span> ;</span>
 		<span>}</span>
 	<span>}</span>
     <span>return</span><span>  es;</span>
 <span>}</span>
 
 Acad::ErrorStatus MyEntPersSubentIdPE::
 			getEdgeSubentityGeometry(
 				<span>const</span><span>  AcDbEntity*   pEntity,</span>
                 <span>const</span><span>  AcDbSubentId& edgeSubentId,</span>
                 AcGeCurve3d*&       pEdgeCurve)
 <span>{</span>
 	Acad::ErrorStatus es =  Acad::eNotImplemented;
 
 	MyEnt1 *pMyEnt1 = MyEnt1::cast(pEntity);
 	<span>switch</span><span> (edgeSubentId.index())</span>
 	<span>{</span>
 		<span>case</span><span>  100:</span>
 			pEdgeCurve = <span>new</span><span>  AcGeLine3d(</span>
 				pMyEnt1->m_ptSP, pMyEnt1->m_ptEP);
 			<span>return</span><span>  Acad::eOk;</span>
 
 		<span>default</span><span> :</span>
 		<span>{</span>
 			acutPrintf(ACRX_T(<span>"\n </span><span> </span>
 				getEdgeSubentityGeometry 
 				not implemented <span>for</span><span>  %d!!<span>"), </span><span> </span></span>
 				edgeSubentId.index());
 			<span>break</span><span> ;</span>
 		<span>}</span>
 	<span>}</span>
 
     <span>return</span><span>  es;</span>
 <span>}</span>
 

Here is the project for download :

Download Customentity_assocdimEnabled


Comments

Leave a Reply

Discover more from Autodesk Developer Blog

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

Continue reading