Class 14 - Creating a Custom Entity (step by step)
Hello,
On this class I will complete the Class 13 with a tutorial to show you how to create a simple custom entity step by step. This entity has some features that will help you to see what is possible to do using ObjectARX. Please read carefully the following instructions.
On this class I will complete the Class 13 with a tutorial to show you how to create a simple custom entity step by step. This entity has some features that will help you to see what is possible to do using ObjectARX. Please read carefully the following instructions.
-Create a Blank Solution called CustomEntitySample;
-Add a new DBX project called CustEntityDBX;
-Add a new ARX project called CustEntityARX;
-Remember to enable MFC Extension on both projects;
-Create a dependency from ARX project to DBX:

-Add a new DBX project called CustEntityDBX;
-Add a new ARX project called CustEntityARX;
-Remember to enable MFC Extension on both projects;
-Create a dependency from ARX project to DBX:

-Go to DBX project and open Autodesk Class Explorer;
-Right click on CustEntityDBX node and select "Add ObjectDBX Custom Object...":
-Choose MyCustomEntity as class name and derive it from AcDbEntity;
-Other fields will be filled automatically (you may change these values);
-Go to the Protocols TAB and enable both Osnap and Grip-points protocols:
-Rebuild your project to see if everything is ok;
-Now, open again the Autodesk Class Explorer, go to CustEntityDBX project;
-Select the MyCustomEntity node (if it is not there, click on Refresh icon);
-Right click on it and select "Add Variable...":
-Name it as "m_PtA", type will be an AcGePoint3d and its access will be protected;
-Enable "Participate to DWG filing protocol", disable "Increase Version number";
-Enable "Implement Get/Put methods" and fill desired comments for it;
-Repeat the process and create : "m_PtB", "m_PtAB" and "m_PtBA":

-Now, add a CString variable called "m_Text";
-Remember to correct the CString manipulation and filing as we have done in Lab3;
-Now you should compile your code ok;
Before continue, we will need to remove some functions that we will not use. First remove the following from .H file:
virtual Acad::ErrorStatus getGripPoints (
AcDbGripDataPtrArray &grips, const double curViewUnitSize, const int gripSize,
const AcGeVector3d &curViewDir, const int bitflags) const;
virtual Acad::ErrorStatus moveGripPointsAt (
const AcDbVoidPtrArray &gripAppData,
const AcGeVector3d &offset, const int bitflags);
There are 4 methods getOsnapPoints(). We will only use the following method:
virtual Acad::ErrorStatus getOsnapPoints (
AcDb::OsnapMode osnapMode, int gsSelectionMark, const AcGePoint3d &pickPoint,
const AcGePoint3d &lastPoint, const AcGeMatrix3d &viewXform,
AcGePoint3dArray &snapPoints, AcDbIntArray &geomIds) const ;
Now, you will need to delete the body of these functions from cpp file.
WORLDDRAW
-To draw our custom entity, we will need to add the following code to worldDraw() method:
assertReadEnabled();
// Bounding Polyline
AcGePoint3d pts[4];
pts[0] = m_PtA;
pts[1] = m_PtAB;
pts[2] = m_PtB;
pts[3] = m_PtBA;
mode->subEntityTraits().setSelectionMarker(1); // Mark 1
mode->subEntityTraits().setColor(1); // Red
mode->geometry().polygon(4,pts);
// Entity's Text
mode->subEntityTraits().setSelectionMarker(2); // Mark 2
mode->subEntityTraits().setColor(256); // ByLayer
AcGiTextStyle style;
style.setFileName(_T("txt.shx"));
style.setBigFontFileName(_T(""));
style.setTextSize(25);
style.loadStyleRec();
AcGePoint3d txtPt((m_PtB.x+m_PtA.x)/2.0, (m_PtB.y+m_PtA.y)/2.0, m_PtA.z);
mode->geometry().text(txtPt, AcGeVector3d::kZAxis,
(m_PtAB-m_PtA),m_Text, m_Text.GetLength(),Adesk::kFalse, style);
return Adesk::kTrue;
GRIP Points
-Open remaining getGripPoints() method inside MyCustomEntity implementation (cpp);
-We would like to enable 5 grips for this entity. One at each point (A,B,AB and BA) plus one at the center;
-Inside the getGripPoints(), add the following code:
gripPoints.append(m_PtA);
gripPoints.append(m_PtAB);
gripPoints.append(m_PtB);
gripPoints.append(m_PtBA);
gripPoints.append(AcGePoint3d((m_PtB.x+m_PtA.x)/2.0,
(m_PtB.y+m_PtA.y)/2.0,m_PtA.z));
-Now, when user clicks on each Grip we would like to perform an action. This is done using the moveGripPointsAt() method;
-We need to check the index of each fired grip accordingly to the getGripPoints() method and then apply the transformation;
-Inside remaining moveGripPointsAt() method, add the following code:
assertWriteEnabled();
for(int i=0; i<indices.length(); i++) {
int idx = indices.at(i);
// For A and center point
if (idx==0 idx==4) m_PtA += offset;
// For AB and center point
if (idx==1 idx==4) m_PtAB += offset;
// For B and center point
if (idx==2 idx==4) m_PtB += offset;
// For BA and center point
if (idx==3 idx==4) m_PtBA += offset;
}
return (Acad::eOk);
OSNAP Points
-Open the getOsnapPoints() method. We will add 3 Osnap modes: EndPoint, MidPoint and Center:
assertReadEnabled();
switch (osnapMode) {
case AcDb::kOsModeEnd:
snapPoints.append(m_PtA);
snapPoints.append(m_PtAB);
snapPoints.append(m_PtB);
snapPoints.append(m_PtBA);
break;
case AcDb::kOsModeMid:
snapPoints.append(m_PtA+((m_PtAB-m_PtA).length()/2.0)*((m_PtAB-m_PtA).normalize()));
snapPoints.append(m_PtAB+((m_PtB-m_PtAB).length()/2.0)*((m_PtB-m_PtAB).normalize()));
snapPoints.append(m_PtB+((m_PtBA-m_PtB).length()/2.0)*((m_PtBA-m_PtB).normalize()));
snapPoints.append(m_PtBA+((m_PtA-m_PtBA).length()/2.0)*((m_PtA-m_PtBA).normalize()));
break;
case AcDb::kOsModeCen:
snapPoints.append(AcGePoint3d((m_PtB.x+m_PtA.x)/2.0,
(m_PtB.y+m_PtA.y)/2.0, m_PtA.z));
break;
}
return (Acad::eOk);
switch (osnapMode) {
case AcDb::kOsModeEnd:
snapPoints.append(m_PtA);
snapPoints.append(m_PtAB);
snapPoints.append(m_PtB);
snapPoints.append(m_PtBA);
break;
case AcDb::kOsModeMid:
snapPoints.append(m_PtA+((m_PtAB-m_PtA).length()/2.0)*((m_PtAB-m_PtA).normalize()));
snapPoints.append(m_PtAB+((m_PtB-m_PtAB).length()/2.0)*((m_PtB-m_PtAB).normalize()));
snapPoints.append(m_PtB+((m_PtBA-m_PtB).length()/2.0)*((m_PtBA-m_PtB).normalize()));
snapPoints.append(m_PtBA+((m_PtA-m_PtBA).length()/2.0)*((m_PtA-m_PtBA).normalize()));
break;
case AcDb::kOsModeCen:
snapPoints.append(AcGePoint3d((m_PtB.x+m_PtA.x)/2.0,
(m_PtB.y+m_PtA.y)/2.0, m_PtA.z));
break;
}
return (Acad::eOk);
TRANSFORMATION
-The custom entity transformation is done through transformBy() method;
-Open the Autodesk Class Explorer, expand the MyCustomEntity node, expand Base Classes node and then expand AcDbEntity node;
-Scroll down the list and select:
Acad::ErrorStatus transformBy(const AcGeMatrix3d &);
-Right click on this method and select "Implement Base Class Method";
-This function logic is very simple, just add the following code:
assertWriteEnabled();
m_PtA.transformBy(xform);
m_PtAB.transformBy(xform);
m_PtB.transformBy(xform);
m_PtBA.transformBy(xform);
return (Acad::eOk);
-This function will allow the custom entity to be transformed;
ARX PROJECT
-First we will need to add a new command called "MyCustEnt";
-This will create a method inside acxrEntryPoint.cpp file;
-Now, add the necessary include instruction after the #include "StdAfx.h":
#include "..\CustEntityDBX\MyCustomEntity.h"
-Inside the method we will create our entity:
// Input information
ads_point pt1,pt2;
if (acedGetPoint(NULL,_T("Set the first point:\n"),pt1) != RTNORM)
return;
if (acedGetCorner(pt1,_T("Set the second point:\n"),pt2) != RTNORM)
return;
TCHAR buffer[512];
if (acedGetString(0,_T("Enter the text:\n"),buffer) != RTNORM)
return;
// Setup entity
MyCustomEntity *pEnt = new MyCustomEntity();
pEnt->put_m_PtA(asPnt3d(pt1));
pEnt->put_m_PtAB(AcGePoint3d(pt2[X],pt1[Y],pt1[Z]));
pEnt->put_m_PtB(asPnt3d(pt2));
pEnt->put_m_PtBA(AcGePoint3d(pt1[X],pt2[Y],pt2[Z]));
pEnt->put_m_Text(buffer);
// Post to Database
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()->getSymbolTable(pBlockTable,
AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId retId = AcDbObjectId::kNull;
pBlockTableRecord->appendAcDbEntity(retId, pEnt);
pBlockTableRecord->close();
pEnt->close();
TEST
-Rebuild your project;
-Open AutoCAD and load DBX module first and then load the ARX;
-Fire command MYCUSTENT and create as many entities as you want;
-Test it against GRIP edit, MOVE, ROTATE, SCALE;
-Fire a LINE command and try to get precision points (EndPoint, MidPoint and Center) over your entities;











8 Comments:
Hello,thanks a lot for such a good web page about ObjectARX. Very nice tutorials even better than Tutorial of AutoDesk:)))
I stuck in the first step.I am creating the DBX,ARX modules adding dependencies and compiling. I gives such an error:
'CustEntityDBXDLL':undeclared identifier
'InitAdUiDLLL':undeclared identifier
The problem is at the Macro AC_IMPLEMENT_EXTENSION_MODULE(CustEntityDBXDLL) and the in the CustEntityDBX.cpp.
Obviously something wrong with the MFC support???
What do you think the problem is???
By
Mazhar Basa, at 5:31 AM
Hey thanks to your finished project!! it helped me solve his problem.
There is something wrong with the ARXWizard. It has to add "#include "AcExtensionModule.h" at the end of the "Stdafx.h" file of DBX module. You have to add this at the end after creating your DBX module wşth MFC extension.
Thanks anyways
By
Mazhar Basa, at 6:26 AM
Hello Mazhar,
Yes, exactly that. ARXWizard is missing this include.
Hope you are doing great and thank you for attending this course.
Regards,
Fernando.
By
Fernando Malard, at 8:08 AM
Ok here is my new question how can I add Arrays of points like those above?
When I use a single AcDbPoint3d and use Get/Put functions options while adding variables from wizard, there is no problem. But if I use an array of AcDbPoint3d it gives errors.
So what if we want to use arrays in our custom entity?
By
Mazhar Basa, at 11:08 AM
Hi Mazhar,
You need to declare a member variable as an AcGePoint3dArray.
It is a dynamic array of AcGePoint3d.
To save, first save the length of this array using length() method then, traverse the array and save each item as AcGePoint3d.
To load, first load the saved lenght then create a FOR statement, read each point and add it to the this array. It is important to zero the array's lengh before adding elements through the FOR statement.
Regards,
Fernando.
By
Fernando Malard, at 11:21 AM
I am sorry I meant "dwgIn" and "dwgOut" functions not "get/put" functions.
By
Mazhar Basa, at 2:37 AM
Hello Firnds.
Can somebody help me? I have problem with "add member variable wizard". It returns mi a javascript error in line 472 col 3. I do not now how to fix this. :( I'm using VS 2005 and objectarx 2007.
By
Anonymous, at 6:39 AM
Hello,
Please, try to uninstall the ARX Wizard and then install again. Make sure this will be done with all Visual Studio 2005 instances closed.
Cheers,
Fernando.
By
Fernando Malard, at 9:08 AM
Post a Comment
<< Home