Index

Welcome
Requirements
Class 1 - Overview
Class 2 - AutoCAD's Database
Class 3 - Application Overview
Class 3a - Minimum application
Class 3b - Minimum application using ARXWizard
Class 4 - Object Lifecycle
Class 5 - Object Management
Class 6 - Entities
Class 7 - Containers
Class 8 - Selection sets
Class 9 - Interacting with AutoCAD
Class 10 - Using MFC
Class 11 - Custom ObjectARX Class
Class 12 - Deriving from AcDbObject
Class 13 - Deriving from AcDbEntity
Class 14 - Creating a Custom Entity (step by step)
Lab 1 - Creating and Editing Entities
Lab 1 - Solved
Lab 2 - Using a MFC Dialog
Lab 2 - Solved
Lab 3 - Creating a custom object
Lab 3 - Solved
Appendix A - Debug versus Release

31 comments :

Unknown said...

Hello Malard,

I have questions (Doubts) for you.

1.
How to retrieve the Drawing Units in BricsCAD using ObjectARX.
like MM,inch etc...


2.
I have attached material to the solid body to different faces different materials,
m able to get the material id like Bricks02 as 196, Bricks05 as 205.(of integer value in ODA code).

Please let me know is there any solution to map the material id to material name.
so that i can achieve my bottle neck problem.

Here is the code.

OdBrFaceLoopTraverser faLoTrav;
OdBrFace face = FaceTr.getFace();

face.getMaterialID(nMaterialId) // Retrieve the material id (Integer)


3.
Why BREP APRI is not working in ObjectARX ?

Regards
Anithraj T
anith.raj@thedesignsense.com

Fernando Malard said...

Hello anith,

1. Probably the same way you do within AutoCAD: AcDbDatabase::lunits() and AcDbDatabase::aunits() (they correspond to the System Variables LUNITS and AUNITS respectively).

2. The ID returned by this method is an ObjectId of AcDbMaterial class. So you just need to open the object with this ID and call the method name() to retrieve its name.

3. Brep requires additional libraries and headers usually located at "C:\ObjectARX 2015\utils\brep" so they need to be added to your project to be able to compile and build your code.

Regards,

Kumari Pritam said...

Hello Malard,

I am not getting the API for the command Array3D.Is there any API for the same?
I would like to select an AcDb3dSolid and create a rectangular array of the same objects.

Regards,
Kumari Pritam

Fernando Malard said...

Hi Kumari,

There isn't any API for cloning entities.
You will need to manually calculate the transformation for each position e create clones of original entities thus transforming them from the start position to the new position relative to the row/column they would belong to.

So having your original solid, open it for read, call clone() method to get a new in-memory copy of this solid, transform it to the destination by using a translation matrix passing it into transformBy() call. After that, add this clone to ModelSpace and close it.

Regards,

DHANRAJ said...

Hello Sir,
I want to take intersection between cylinder and line, initially I was working with autocad 2014 and arx 2013 my code was working fine and then I switched to autocad 2015 and arx 2015
and then intersection API returns false. I am clueless what is wrong.
below is sample code
static void CheckIntersection()
{
//AcGeContext::gTol.setEqualPoint(1.0e-8);
//AcGeContext::gTol.setEqualVector(1.0e-6);
double m_DH = 2000.0;
AcGePoint3d org(0,0,0);
AcGeVector3d m_xAxis(1,0,0);
AcGeCylinder cylA(m_DH/2, org, m_xAxis);


AcGeLineSeg3d intLine;
AcGePoint3d pt1(0.52257073158706313,10.637171608585087,-1000.0000000000000);
AcGePoint3d pt2(0.52257073158700185,10.637171608585087,0.00000000000000000);
intLine.set(pt1, pt2);
int nrInt;
AcGePoint3d p3, p4;
//AcGeTol TOL;
//TOL.setEqualPoint(1.1);
//TOL.setEqualVector(1.1);
Adesk::Boolean stat = cylA.intersectWith(intLine, nrInt, p3, p4, AcGeContext::gTol);
if(stat == Adesk::kTrue)
{
ads_printf(L"cylA.intersectWith successfull");
}
else
{
ads_printf(L"cylA.intersectWith FAIL");
}

}

Dont know what is wrong with 2015

Fernando Malard said...

Hi,

The AcGeCylinder constructor you are using created a infinite cylinder.
Have you tried with the other constructor with the cylinder's height defined?

Further, I think intersectWith() will fail once this line is placed almost at the cylinder's cap. I know you are moving 0.52257073 towards X direction but you will need to play with the AcGeContext::gTol to see how you need to tweak it so the intersection will be found.

In Z direction you are also using 1000 as the line's length which will make its endpoint touch exactly the cylinder's side which also could be a problem in terms of tolerance.

Hope this helps.

Regards,

Unknown said...

Hi Malard,

I want to edit standard style of AcDbTable. Actually, I want to use standard style table only but all rows should have same height & should be Data type row only.Can you suggest me functions for the same.

Thankyou

Fernando Malard said...

Hi Prashansa,

The Table's properties are driven both by the AcDbTableStyle attached to it and also by the AcDbTable itself.
Things like Cell formatting are inherited by the style attached to it but other properties like setRowHeight() are defined directly into the inserted table (AcDbTable entity).

The data type is defined on AcDbTableStyle and at the AcDbTable level you define only if a cell is a text or a block.

I don't recommend you to use the default table style and change it. The recommended approach is to create your own application table style with a unique name thus avoiding conflicts with other styles created either by the user or by other applications running at the same time.

Take a look at those classes to better understand how to use them.

Regards,

Unknown said...

Hi Malard,

Thank-you for the suggestion & I will try to create my own style but I was trying to fix the row height of header and title row same as data row. I tried setRowHeight() to set same row height for all types of rows but it was not working so, I fixed the text height of all rows using setTextHeight() & after that row height of all rows are same.

Thanks & regards

Unknown said...

Hi Malard,

I want to know how to convert a 3Dsolid figure into component? Is there is any API to select a 3Dsolid figure & then save it as a component with its layer name?

Thanks & regards

Fernando Malard said...

Hi Prashansa,

One option is to call explode() method through the solid's pointer. This method will return a AcDbVoidPtrArray of the corresponding exploded entities. Those pointers represent "in-memory" entities so you need to either delete the pointers after doing your stuff or add them to database so they become database-resident entities.

The original solid entity will still be there and if you want to delete it call erase() method after collecting the entities from explode() call and finally close() it.

The layer can be changed through the returned entities pointers because the default is going to be layer "0" as usual.

Hope this helps.

Unknown said...

Hello Malard,

I want to know Is there is an API for "BMFORM" command? After selecting 3dSolid figure using AcDbEntity, how I can save the entity as a component in some particular folder with the same name as its layer name?.

Thanks & regards

Fernando Malard said...

I believe BMFORM is a BricsCAD command, not AutoCAD.
In AutoCAD you would need to first create the block with BMAKE or BLOCK command then export the block you have just created with WBLOCK command. If you don't need the block (or component) sitting into the current drawing you can directly use WBLOCK and select the entities you want to export.

This process can be achieved programmatically in AutoCAD by the overall following steps:

- Create a new AcDbBlockTableRecord and name it as you wish
- Add the desired entities to this container (can be existing entities in ModelSpace or entities you will create programmatically)
- Create a new "in-memory" AcDbDatabase and copy the block table record to it through AcDbDatabase::wblock() function
- Save this AcDbDatabase to an external file using the AcDbDatabase::saveAs() method

Hope this helps.

Unknown said...

Hello Malard,

I used WBLOCK command and created a block reference, But the problem is that WBLOCK Doesn’t treat the converted Block as component part of an assembly.
The individual components converted into Wblock are not part of the BOM (Bill of Materials).
I have sent you an email regarding this to your yahoo id & I have attached 2 images describing my requirements.
Please see to it & give a suggestion to automate BOM Command using objectarx API & my requirement is like second image(ComponentsUsingBmform.JPG).

Thanks & regards
Prashansa

Fernando Malard said...

Hi Prashansa,

I don't know what command BOM is nor how it works...it seems something specific from Bricscad.

Unfortunately I can't help with this specific API.

PrsTechnicals said...

I fernando ,
I have a question to ask you .How to invoke the suberase function .Does it get a call when explode function is successfully run .

thanks
Pradeep Sharma

Fernando Malard said...

Hi pradeep,

In fact the explode process will eventually delete the entity. Here is what the SDK help says:

This function must not add the new entities to the database, nor should it erase the entity it is a part of. Both of these steps will be taken care of by AutoCAD (or whatever other application calls this function). This also means that if an ObjectARX application calls the explode method of an object, the calling application is responsible for the resulting entities (that is, either appending them to a database or deleting them when they are no longer needed) and for erasing the original entity (if appropriate).

That said, if your subErase() method is not being called it is probably because the exploded entity is failing to be erased OR the explode method is being called by a non-native AutoCAD process. For example, if you put a routine to manually explode the entities the very call to explode method doesn't erase the entity. As stated at the documentation text above, the caller is responsible for erasing the entity after the call to explode is made.

Double check if the explode process is indeed called by AutoCAD and also if your entity can't be exploded. For example, a AcDbLine entity cannot be exploded so subErase() won't never be called for it. If your entity doesn't support proper exploding through any particular workflow it won't be erased thus subErase() won't be called.

Hope this helps.
Regards,

Anonymous said...

hi Fernand

do you know some function to calculate z coordinate intersection of a line and a plane (3dface)?
the cal tool of autocad does wrong

thanks

Fernando Malard said...

Hi,

You should be able to obtain the intersection point between the line and face entities using their AcDbEntity::intersectWith() methods.

If you are doing the calculation in memory, you can also achieve this by using AcGePlane or AcGeBoundedPlane to represent the face and AcGeLinearEnt3D to represent the 3D line. All these AcGe classes do have intersectWith() methods available to allow pure (in-memory) calculations which are much faster.

Note that you can tweak these calls by determining if one entity should be extended to intersect another, if you want to calculate a projected intersection, etc. Take a look at ObjectARX documentation for further information.

Hope it helps.

Anonymous said...

HI Fernand!

Do you kmow if i can access these functions from Visual Lisp?

Thanks

Fernando Malard said...

Hi,

I believe you can use vlax-invoke to call it.
Here is one example:

https://www.cadtutor.net/forum/topic/64755-vlax-invoke-intersectwith-catches-the-neighboring-segments/

You should find more online.

Anonymous said...

Hi Fernand!

I compiled an arx aplication from autocad 2014 to 2018, some functions hangs now...in 2014 all was ok what happened?

Mike

Anonymous said...

I Fernand!

How can i get a safearray value?
the vlax-invoke-method give me a variant
i get the variant value and is a safearray
but i can not get the value of this safearray
thanks

Fernando Malard said...

Hi Mike,

Sorry about the late reply.
What SDK did you use?

AutoCAD 2018 requires ObjectARX 2018 and VC.NET 2015 (14.0 - Update 3)

These samples are too old so you would need to review the "What's new" section of SDKs after 2014.
I would suggest small steps, try to create a blank sample and start copying the code and test it as you go.

Regards,

Fernando Malard said...

About the safearray, did you try to use functions to list the return data?

More info here: https://documentation.help/AutoLISP-Functions/WS1a9193826455f5ff1a32d8d10ebc6b7ccc-67f2.htm

Regards,

Anonymous said...

Hi, Fernand...

Yes i tried all and nothing, the problem maybe is the function vlax-invoque-method always return nil value. I have a line intersecting a 3dface and i want to get the Z coordinate intersect point.

Regards

Fernando Malard said...

Hi,

I'm sorry to hear that.
I think you should try to post this in Autodesk groups with ObjectARX or AutoLISP forums.

I don't use VLX too often to better support you.

Thank you,

Anonymous said...

HI Fernand! i insert a block in my drawing but do not gets the correct rotation i gave :
0 degrees its ok, but 90 degrees not it gets 117 degrees, whats up?


for (; !pBlockIterator->done(); pBlockIterator->step()) {
pBlockIterator->getRecord(pBlockTableRcd, AcDb::kForRead);
pBlockTableRcd->getName(pBlockName);
pBlockTableRcd->close();
if (wcscmp(pBlockName, blockName) == 0)
break;
free(pBlockName);
}
if (!pBlockIterator->done())
{
//pBlockIterator->getRecord(pBlockTableRcd, AcDb::kForRead);
//AcDbBlockReference newBlockRef = AcDbBlockReference(basePoint, pBlockTableRcd->objectId());
//newBlockRef.close();

AcDbBlockReference *pBlkRef = new AcDbBlockReference;

// Step 2: Set up the block reference to the newly
// created block definition.
//
AcDbObjectId blockId = pBlockTableRcd->objectId();
pBlkRef->setBlockTableRecord(pBlockTableRcd->objectId());

// Give it the current UCS normal.
//
struct resbuf to, from;
from.restype = RTSHORT;
from.resval.rint = 1; // UCS
to.restype = RTSHORT;
to.resval.rint = 0; // WCS
AcGeVector3d normal(0.0, 0.0, 1.0);
acedTrans(&(normal.x), &from, &to, Adesk::kTrue,
&(normal.x));

// Set the insertion point for the block reference.
//
pBlkRef->setPosition(basePoint);
pBlkRef->setLayer(layer);

// Indicate the LCS 0.0 angle, not necessarily the UCS 0.0 angle.
//
pBlkRef->setRotation(rotation);
pBlkRef->setNormal(normal);


// Step 3: Open the current database's model space
// block Table Record.
//
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
pBlockTable->close();

// Append the block reference to the model space
// block Table Record.
//
AcDbObjectId newEntId;
pBlockTableRecord->appendAcDbEntity(newEntId, pBlkRef);
pBlockTableRecord->close();

// Step 4: Open the block definition for read.
//
AcDbBlockTableRecord *pBlockDef;
acdbOpenObject(pBlockDef, blockId, AcDb::kForRead);


delete pIterator;
pBlockDef->close();
pBlkRef->close();
delete pBlockIterator;
pBlockTbl->close();
return true;
}
...
delete pBlockIterator;
pBlockTbl->close();
return false;
}

Thanks in advance

Fernando Malard said...

Hello,

Are you passing the angle in radians?

From docs:


This function sets newVal to be the rotation value (in radians) of the block reference. The rotation value is relative to the X axis of a coordinate system that is parallel to the OCS of the block reference, but has its origin at the position point of the block reference. The rotation axis is the Z axis of this coordinate system with positive rotations going counterclockwise when looking down the Z axis towards the origin.

The rotation value is used for DXF group code 50.

Returns Acad::eOk if successful, or Acad::eInvalidInput if the data passed in is not acceptable.


If you are, please check if there are any SNAP or OSNAP flags turned on which may influence transformations.

Hope it helps.

Regards,

Chatko said...

Hi, Fernando, whether it is possible to add CStringArray to the struct resbuf list. The CStringArray string is populated.

Thank in advance.

Fernando Malard said...

Hi Chatko,

resbuf is a linked list so you will need to store your array elements individually.

There are several ways to do it but I would say the safest would be having a mark where you put this array size and then you can save each string individually. Another approach would be using Begin/End list marks to delimit your array.

I think it worth checking this thread here:
https://forums.autodesk.com/t5/net/get-list-of-strings-from-resultbuffer/td-p/10170487

Regards,