Friday, January 12, 2007

Exercise2 - Step3

Creating a Custom Entity – Exercise 2 – Step 3

Now you will learn how to add GRIP points to your entity. In fact, as the default implementation of AuPolyline forward the call to its base class you may already noted that its GRIP points are visible and working. The GRIP point behavior is handled by two methods. The first method, called getGripPoints() is responsible for acquiring all GRIP points from your entity. The second, called moveGripPointsAt(), is responsible for the action fired by each grip.
Each default polyline GRIP action is to move the related vertex. In this example we want to add one extra grip positioned at our polygon’s center. To do that we first need to create a helper method to calculate this point. We will walk through all polygon points and will sum the coordinates dividing the result by numVerts(). This method will not change our entity data so it is recommended to be CONST and will require only the assertReadEnabled() call:

AcGePoint3d AuPolyline::GetPolylineCenter() const
{
assertReadEnabled();
AcGePoint3d ptC,pti;
double cx = 0.0, cy = 0.0, cz = 0.0;
for (int i=0; i<numVerts(); i++)
{
this->getPointAt(i,pti);
cx += pti[X];
cy += pti[Y];
cz += pti[Z];
}
cx = cx / numVerts();
cy = cy / numVerts();
cz = cz / numVerts();
ptC.set(cx, cy, cz);
return ptC;
}


Lines 04-15 apply the center formula and at line 16 we build the point using cx, cy and cz as the point’s X, Y and Z coordinates respectively.
Next we need to change the default implementation of the both GRIP point methods. First we will redefine the getGripPoints() method. This method has 2 signatures and we will use the new AcDbGripData method instead of the “old style” method.
This method receives an array of AcDbGripData pointers. These objects represent the GRIP point information. We need to inform at least the point and an arbitrary data (void*). This arbitrary data can be used later to get back information from each GRIP point allowing the moveGripPointsAt() method to perform the custom actions:

Acad::ErrorStatus AuPolyline::getGripPoints (
AcDbGripDataPtrArray &grips,
const double curViewUnitSize,
const int gripSize,
const AcGeVector3d &curViewDir,
const int bitflags) const
{
assertReadEnabled () ;
AcDbGripData* gpd = new AcDbGripData();
gpd->setAppData((void*)9999); // Center Grip code
gpd->setGripPoint(GetPolylineCenter());
grips.append(gpd);
AcDbPolyline::getGripPoints (grips, curViewUnitSize, gripSize, curViewDir, bitflags);
return (Acad::eOk);
}


This method will not change our entity’s data so it is also a CONST method and calls assertReadEnabled() method. On lines 09-12 we instantiate an AcDbGripData pointer and set its application data (9999 in this case) and the grip point which is calculated by the GetPolylineCenter() method. Next, on line 13 we forward the call to AcDbPolyline’s grip point method so it is able to add its own GRIP points at last. These GRIP points are those vertexes points we have mentioned before. The next step is to change the moveGripPointsAt() method so when the user clicks on this center GRIP it will reflect a custom action. In this example, our custom action will move the entire polyline. The following code shows how to do that:

Acad::ErrorStatus AuPolyline::moveGripPointsAt (
const AcDbVoidPtrArray &gripAppData,
const AcGeVector3d &offset,
const int bitflags)
{
assertWriteEnabled () ;
for (int g=0; g<gripAppData.length(); g++)
{
// Get grip data back and see if it is our 0 Grip
int i = (int)gripAppData.at(g);
// If it is our grip, move the entire entity. If not, forward the call
if (i == 9999)
this->transformBy(offset);
else
AcDbCurve::moveGripPointsAt (gripAppData, offset, bitflags);
}
return (Acad::eOk);
}


This time, our method cannot be CONST once we will change our entity’s data. Due that, we need to call assertWriteEnabled() at it’s beginning. On line 07 we start to inspect the AcDbVoidPtrArray (an array of void*) looking for our application data (9999). This method also receives a 3D vector which represents the transformation being applied to the GRIP. If the GRIP being modified is our 9999 we will apply this vector transformation to the entire polyline. This can be done through the transformBy() method passing the same vector (Figure 16).


Figure 16 – Center GRIP in action.


If the fired GRIP is not our 9999 code we will forward the call to AcDbCurve class (AcDbPolylin’s base class) which will handle it for us. The resulting behavior is when you select a GRIP over the polyline boundary the entity stretches and when you select the center GRIP the entity moves.

8 comments :

Anonymous said...

Hi Fernando,

I know my question is not completely related but i know that it deals with grips in some way. I am using Land Desktop 2007, (which im fairly new at) and had a question about labeling. When you create a contour line you can have that label basically be a part of the line. It will move with it, etc. So i want to know how to add this type of label to other lines. Is this possible? Thanks

Chris

Fernando Malard said...

Hi Chris,

I'm not familiar with Land. You need to find out if this "Label" is a specific Land custom entity and if so, if there is a Land SDK.

Both Autodesk MAP and ADT provide specific ObjectARX API to be used with native AutoCAD ObjectARX.

Sorry but this is what I can help for now.

Regards.

Anonymous said...

Hi Fernando,

I want to say that its not just specific to Land but can not guarantee that. Do know what I would be looking for if I looked at the ObjectARX API and also where exactly could I find it. Thank you for you help.

Chris

Fernando Malard said...

Hi Chris,

I have found that Labels use AeccLabelStyle class. I'm not sure where this class was implemented once the Autodesk Civil 3D is the new name for old Land Desktop.

Take a look at VBA documentation of Land to see if you can find some information about AeccLabelStyle.

Regards,
Fernando.

antonis said...

I got the code to work, however the central grip does not move with the entity; instead it stays stuck at (0,0,0). I can move the entity through it, but when the translation is done it keeps at (0,0,0).

Fernando Malard said...

Hi antonis,

Sorry, just saw your post today.
Glad you got it working.

About the grip points, I think these GRIP methods have changed and probably you will need to refactor their function signatures.
Something like:



Acad::ErrorStatus
YOURENTITY::subGetGripPoints(
AcGePoint3dArray& gripPoints,
AcDbIntArray& osnapModes,
AcDbIntArray& geomIds) const
{
assertReadEnabled();
return Acad::eOk;
}

Acad::ErrorStatus
YOURENTITY::subMoveGripPointsAt(
const AcDbIntArray& indices,
const AcGeVector3d& offset)
{
assertWriteEnabled();
if (indices.length()== 0 || offset.isZeroLength())
return Acad::eOk;

return Acad::eOk;
}



Hope it helps.
Regards,

antonis said...

Thank you very much Fernando,

No, I had the new signatures including "sub" already figured out (actually the YOURENTITY::subGetGripPoints(AcGePoint3dArray& gripPoints,AcDbIntArray& osnapModes, AcDbIntArray& geomIds) is the "older" implementation of the method if I'm not mistaken).

My mistake was much more basic; I'd left out the "this->getPointAt(i, pti);" line from the
GetPolylineCenter() function, so my central grip was always at (0,0,0).

Fernando Malard said...

Hi antonis,

Yes, that will make the point to stay always at 0,0,0.
Glad you figured it out.

Cheers!