Sunday, April 03, 2005

Class 9 - Interacting with AutoCAD

Hello,

On the last class I have presented how to perform selection sets. This class I will show how can you interact with AutoCAD using global functions and acquiring information such as numbers, coordinates, system variables and much more.

Invoking Commands

ObjectARX provide us two global functions that allows us to invoke registered commands. This functionality is very handy and will help users to perform quick operations that don't require complex procedures. Even this method is quite simple you should avoid using it in complex and huge operations. This method may also create problems when dealing with events handling.

The two provided functions are acedCmd() and acedCommand(). The first one invokes the command through a passed in resbuf list which will inform all command parameters. The second function will receive a variable number of parameters which will reproduce the way you fire the command from the prompt interface. Below are these functions signature:

int acedCmd(const struct resbuf * rbp);

int acedCommand(int rtype, ... unnamed);

To build the resbuf list when using acedCmd() there is a utility function called acutBuildList() which constructs this linked list easily. You just need to pass paired values with codes that describe the types and end the list with a 0 or RTNONE value. Another good practice is to clear the command prompt, calling acedCommand(RTNONE) , after issued the command. Don't forget to free memory used, when using resbuf pointers, through the acutRelRb() utility function to avoid memory leaks. There are several ways to use theses functions and I will show some of them below:

acedCmd():

a) Moving the last created entity based on (0,0,0):

ads_point pt;
pt[0] = pt[1] = pt[2] = 0.0;
struct resbuf *Mv;
Mv = acutBuildList(RTSTR,_T("_MOVE"),RTSTR,_T("_LAST"),RTSTR,_T(""),
RTPOINT,pt,RTSTR,PAUSE,0);
acedCmd(Mv);
acedCommand(RTNONE);
acutRelRb(Mv);


b) Calling a "redraw" native command:
struct resbuf *cmdlist;
cmdlist = acutBuildList(RTSTR, _T("_REDRAW"), 0);
acedCmd(cmdlist);
acedCommand(RTNONE);
acutRelRb(cmdlist);
acedCommand():

a) Calling a ZOOM command and pausing for user input:

acedCommand(RTSTR, _T("Zoom"), RTSTR, PAUSE, RTNONE);

b) Creating both a Circle and a Line entities:
acedCommand(RTSTR, _T("circle"), RTSTR, _T("10,10"),RTSTR, PAUSE,
RTSTR, _T("line"), RTSTR, _T("10,10"), RTSTR, _T("20,20"),
RTSTR, _T(""), 0);
System Variables

Your application will probably need to access AutoCAD system variables that can be read or write. ObjectARX provide two functions to deal with these variables using the resbuf structure to access and/or modify values. The function are called acedGetVar() and acedSetVar() and below are their signatures:

int acedGetVar(const ACHAR * sym,struct resbuf * result);

int acedSetVar(const ACHAR * sym,const struct resbuf * val);

The first parameter is the variable name the second the resbuf pointer to set / get information. The following example show how to change the FILLET radius which is stored through a system variable:
struct resbuf rb, rb1;
acedGetVar(_T("FILLETRAD"), &rb);
rb1.restype = RTREAL;
rb1.resval.rreal = 1.0;
acedSetVar(_T("FILLETRAD"), &rb1);
It is very important that you specify the correct type of resbuf item acquired.
In this case, the FILLET radius is a real number which is RTREAL type.

User Input Functions

There are additional global functions to allow interaction with users via command prompt interface. Each of these functions could be used alone or with other ones. The following table shows what each function does:

acedGetInt

Gets an integer value

acedGetReal

Gets a real value

acedGetDist

Gets a distance

acedGetAngle

Gets an angle (oriented to 0 degrees specified by the ANGBASE)

acedGetOrient

Gets an angle (oriented to 0 degrees at the right)

acedGetPoint

Gets a point

acedGetCorner

Gets the corner of a rectangle

acedGetKword

Gets a keyword

acedGetString

Gets a string


Each of these functions returns a int number as a result code that could be one of the following:

RTNORM

User entered a valid value

RTERROR

The function call failed

RTCAN

User entered ESC

RTNONE

User entered only ENTER

RTREJ

AutoCAD rejected the request as invalid

RTKWORD

User entered a keyword or arbitrary text


AutoCAD allows you to prevent invalid values when user respond to your input functions. This feature can be made through the acedInitGet() function which can receive one or a combination of the following values:

RSG_NONULL

Disallow null input

RSG_NOZERO

Disallow zero values

RSG_NONEG

Disallow negative values

RSG_NOLIM

Do not check drawing limits, even if LIMCHECK is on

RSG_DASH

Use dashed lines when drawing rubber-band line or box

RSG_2D

Ignore Z coordinate of 3D points (acedGetDist() only)

RSG_OTHER

Allow arbitrary input—whatever the user enters


The following example shows how to acquire a value greater than zero:
int age = -1;
acedInitGet(RSG_NONULL RSG_NOZERO RSG_NONEG, NULL);
acedGetInt(_T("How old are you? "), &age);



86 comments :

Anonymous said...

Hi,
Saying about interacting with AutoCAD I would indicate the Jig class. Sometimes it’s pretty handy.

Anonymous said...

I would like to set all the system variables in AutoCAD using arx, so that all users in the network of our company use the same settings. Can you please help me on that.
Thanks

Fernando Malard said...

Hello,

There are two utility functions to handle this: acedSetVar() and acedGetVar().

A simple example would be disable OSNAP during some operation and enable it back with old setup:

struct resbuf OldOsnap, NewOsnap;
// Get current OSNAP
acedGetVar("OSMODE", &OldOsnap);

// Chage OSNAP settings
NewOsnap.restype = RTSHORT;
NewOsnap.resval.rint = 0;
acedSetVar("OSMODE", &NewOsnap);

// Do something...

// Get back the old OSNAP
acedSetVar("OSMODE", &OldOsnap);

Regards,
Fernando.

Anonymous said...

Hi,

My application is accepting list of files from user. and according to
need it opens the files automatically to accept user input by selecting
entites.

it have a function

AcApDocument * asOpenFile(CString FileToOpen)

which checkes wheather the file is already open. if not then, the files
is opened.

it works well but after switching 3-4 drawings while executing
executeInApplicationContext()
normally execution control comes out..
but 3-4 times execution control stuck their, untill, user manually
switch to previous drawing.

when I used

acDocManager->setCurDocument()

it gives fatal error "Command can not be nested more than 4 times" and
crashes

this are the procedures

void asOpenSelFile(void* pData)
{
Acad::ErrorStatus es = Acad::eOk;
if(acDocManager->isApplicationContext())
es = acDocManager->appContextOpenDocument((const char *)pData);

}

AcApDocument * asOpenFile(CString FileToOpen)
{
char* pStr = NULL;
AcApDocument *pDoc = NULL;

if(!asGetDocFromFilename(FileToOpen, pDoc))
{
pStr = FileToOpen.GetBuffer(FileToOpen.GetLength());
//Problem sentence

acDocManager->executeInApplicationContext(asOpenSelFile, (void *)pStr);

//--------------------------------------------------------------------------
ShowMessage(gpAsdkAppDocGlobals->m_sDocIniCommand);
FileToOpen.ReleaseBuffer();
asGetDocFromFilename(FileToOpen, pDoc);
}
else
{
acDocManager->sendStringToExecute(acDocManager->mdiActiveDocument(),
"\003\003\003", false, true);
acDocManager->activateDocument(pDoc);
}
return pDoc;

}

Thanks in advance

Abhay

Fernando Malard said...

Hello Abhay,

Could you build a sample with this code, reproducing the problem, and send me at fpmalard@yahoo.com.br ?

This way I can help you faster.
Regards,
Fernando.

Anonymous said...

Hi,
When I use the acedGetPoint() function the AutoCAD hangs. This is the exact line.

acedGetPoint(NULL, "Enter Start Point", startPt);

acedGetPoint(startPt, "Enter Start Point", endPt);

I am using AutoCAD 2000 and Visual Studio 6. Why does the AutoCAD hang ?

Jatin.

Fernando Malard said...

Hi Jatin,

I don't think this is the culprit.But acedGetPoint() should be something like:

ads_point pt1;
int res = acedGetPoint(NULL,"Message",pt1);
if (res == RTNORM)
{
...
}

Maybe you need to check what is happening before your code reach this point.

Regards,
Fernando.

Anonymous said...

Hi,
Here is the code.


AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* appId)
{
AcDbObjectId lineId;
ads_point startPt, endPt;
AcGePoint3d geStartPt, geEndPt;
switch(msg) {
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(appId);
acrxDynamicLinker->registerAppMDIAware(appId);

acedGetPoint(NULL, "Enter Start Point", startPt);
acedGetPoint(startPt, "Enter Start Point", endPt);

geStartPt[0] = startPt[0];
geStartPt[1] = startPt[1];
geStartPt[2] = startPt[2];

geEndPt[0] = endPt[0];
geEndPt[1] = endPt[1];
geEndPt[2] = endPt[2];

lineId = createLine(geStartPt, geEndPt);
break ;

case AcRx::kUnloadAppMsg:
acutPrintf("\nYour simple ARX application has been unloaded\n");
break ;
}

return AcRx::kRetOK;
}

When the code executes, I get the prompt at command line "Enter start point". But the AutoCAD hangs and I cannot enter the point or do anything else. I feel sure that the function acedGetPoint() is causing the problem. The createLine() function is never called because the AutoCAD hangs before it.

regards,
Jatin.

Fernando Malard said...

Hello Jatin,

The problem is because you are running an user interaction function from inside kInitAppMsg. According to SDK manual, during this message callback:

"Don't expect device drivers to be initialized, any user interface resources to be active, applications to be loaded in a particular order, AutoLISP to be present, or any databases to be open. Calls involving any of these assumptions will result in an error condition, sometimes fatal. AcDb and AcGi libraries are generally not yet active, although related AcRx and other structures are in place.
"

The right thing to do is to register a command and map this command to a function that will execute your procedures.

Regards,
Fernando.

Anonymous said...

Hi,
Thanks very much. But I dont know how to register a command and map it to a function. Can you please tell me the details for the same ?

Fernando Malard said...

Hi,

You just need to add a new command using the "a>" icon at ARXWizard's toolbar.

Check the Lab1 sample at:
http://arxdummies.blogspot.com/2005/03/lab-1-creating-and-editing-entities.html

Regards,
Fernando.

Anonymous said...

Hi Fernando,
Thanks once again. The ARX Wizard installation does'nt have ArxWizards.msi file in the ObjARXWiz folder. There is a file named WizardSetup.exe but running this file does'nt include any ToolBar to the Visual Studio. I am using ObjectARX for AutoCAD 2000. How do I get the ObjectARX Wizard working ?

Jatin.

Fernando Malard said...

Hi Jatin,

Yes, on 2000 the ARXWizard installer is an EXE file.

It should create a new toolbar (but if I remember correctly it is initally hidden so you have to enable it through a right click at VStudio toolbars).

It should also create a new project type option at VStudio new project dialog "ObjectARX...".

Could you check this?

Regards,
Fernando.

Anonymous said...

Hi Fernando,
I have registerd the command through the wizard and its working fine. Thanks for all the help.

regards,
Jatin.

Anonymous said...

I registered my command in aoutocad and from command handler function i am making two consecutive calls to acedcommands.

It fails (with error message out of memory.)on second calls if i follow a specific flow in my application.But for other work flow it forks fine.

I tried out by replacing second call to acedCommands with sendStringToExecute and it works.

I want to know the reason behind this behaviour.

Thanks.

Fernando Malard said...

Without looking at your code is hard to say what is going on.

It is possible to post your code here or send me through my e-mail?

Try to place the following call to acedCommand, between your two existing command calls, like this:

acedCommand(RTNONE);

Check if this will solve the problem. If not, please give me more details.

Fernando.

Anonymous said...

Hello:
You mentioned that "
I tried out by replacing second call to acedCommands with sendStringToExecute and it works."
Is it possible to paste a couple of code to illustrate your method please?
Cheers

Anonymous said...

Hello

How can I make a global variable similar to Autocad system variables, that can be seen from every object in Autocad?

Fernando Malard said...

Hello,

You may use a custom Object inside NOD (Named Object Dictionary). This object, derived from AcDbObject, can be stored there and may contain everything you want.

Next, create some commands to read/write these object´s properties.

Another approach is to use AcDbXRecord object and attach it to a specific extension dictionary.

In both above solutions your data will persist inside each DWG file.

In fact the best solution will depend on the use this variable will require.

All these options are well explained on ObjectARX samples.

Regards,
Fernando.

Anonymous said...

Hi Fernando,

In my application I have to DWG file opened.
I need to read some information from two DWG without change current document.


pdocMgr = acDocManager ;
pAcDoc = acDocManager->document(pDataBase);

acDocManager->activateDocument(pAcDoc);


I use activateDocument to be able to use global function just like

acedSSSetFirst(NULL, NULL);

I can’t use these function to second DWG without using activeDocument

-ActiveDocument take long time when interchangeable between two DWG


Best Regards
M.S

Fernando Malard said...

Hi M.S,

Every acedXXXXX() like function interact with AutoCAD interface and thus within the current document context.

Every document has its own command heap, memory buffer, AcDbDatabase, etc.

I'm afraid activateDocument() is the only way to go.

Regards,
Fernando.

Anonymous said...

Hi Fernando

Thanks a lot

Best Regards,
M.S

Anonymous said...

Dear fernando,

when I use activateDocument fucnction the next line of code does not execute until activate last document.
why? what is the solution???

and may case "Command may be nested more than 4 deep" error??

Do you have any idea???

Regards,
M.S

Anonymous said...

Dear Fernanod,

I have this code

AcDbVoidPtrArray *m_pDBArray = acdbActiveDatabaseArray();
if(m_pDBArray->length() >= 2)
{
AcDbDatabase *pDb1 = (AcDbDatabase *)(m_pDBArray->at(0));
AcDbDatabase *pDb2 = (AcDbDatabase *)(m_pDBArray->at(1));

AcApDocument *pDoc0;
AcApDocManager *doc_manager = acDocManagerPtr();
pDoc0 = doc_manager->curDocument();


AcDbDatabase *pDbTemp0 = pDoc0->database();
if(pDbTemp0 == pDb1)
{
AcApDocument *pDoc;
pDoc = doc_manager->document(pDb2);
Acad::ErrorStatus error11= doc_manager->activateDocument(pDoc);
acutPrintf("\n1");
acutPrintf("\n2");
acutPrintf("\n3");
}
else if(pDbTemp0 == pDb2)
{
AcApDocument *pDoc;
pDoc = doc_manager->document(pDb1);
Acad::ErrorStatus error11= doc_manager->activateDocument(pDoc);
acutPrintf("\n11");
acutPrintf("\n12");
acutPrintf("\n13");
}
}
else
{
acutPrintf("\n Number of DWG less than 2");
}

I have a problem when use this code no acutPrintf execute until reactivate document (manually by click on DWG)

Do you have any idea

Regards,
M.S

Fernando Malard said...

Hi M.S,

Try to use sendStringToExecute() method. Be aware that there is a stack of commands associated with each document and these commands may not execute at the prompt in time and order you expect.

Take a look at MDI documentation for further information.

Regards,
Fernando.

Anonymous said...

Thanks fernando

M.S

Anonymous said...

Hi fernando,

I have one question.

How to disable printing from AutoCAD?


Regards,
M.S

Fernando Malard said...

Hi M.S,

Could you explain better?
Do you mean disable all printing commands or just some specific entities printing?

Fernando.

Anonymous said...

Hi fernando,

I need to disable printing commands

(Disable print command in File Menu)

Fernando Malard said...

M.S,

During your kLoadDwgMsg you can undefine native AutoCAD commands and then redefine, if you want, with the same name or leave them undefined.

To do that, use the following function to disable print command:

ads_queueexpr( "(command \"_.undefine\" \"print\")" );

You can do the same for any other AutoCAD native command.

Hope this help.
Fernando.

Anonymous said...

Hi Fernando,

ads_queueexpr( "(command \"_.undefine\" \"print\")" );

Generat Compile Error :undeclared identifier ads_queueexpr


I use ARX wizard is any Header File to include.

Fernando Malard said...

Hi M.S,

It is undocumented.
Put this:

extern "C" int ads_queueexpr(const char*);

on the CPP where are your trying to use it.

Regards,
Fernando.

Anonymous said...

Hi Fernando,

Yes It Work Thank You very Much

I Have Other Question on print(plot)

I try to add AcplReactotPlot but when I load it to AutoCad I Got

FATAL ERROR: Class 'cstmPlotR' parent named 'AcPlotReactor' not Found

Do you have any idea.
Tanks again

Best Regards,
M.S

Fernando Malard said...

Hello,

Take a look at this sample:

ObjectARX 2007\samples\editor\AsdkPlotAPI

It shows everything you need.

Regards.

Anonymous said...

Hi Fernando,

I have 3 Question.

1) Is any way to rename comand.
Such as rename line command to DrawLine.

2) Is any way to cancel Running Event.
Such as Print Command.

3) Is any way to get event for command

Regards,
M.S

Fernando Malard said...

Hi M.S,

1)You need to undefine the existing command and add yours with the same name. There is no way to redefine a command.

2)Use AcApDocManagerReactor to handle the docLockModeChanged() event. Inside this method, check you want and call the veto() method if you want to cancel the command.

3)Take a look at AcEdEditorReactor class. It will implement command events such as commandoWillStart(), commandEnded(), etc.

Regards.

Anonymous said...

Thank You Frenando

M.S

Albu Emil said...

Hy.

Is it possible to get a list of the system variables (or at least those that get saved in the DWG) ?

I'd like to be able to get a list with all variables and then use acedGetVar and acedSetVar using the list, is it possible somehow ?

Fernando Malard said...

Hi AlbuEmil,

I'm afraid you can't.
System variables were implemented through different classes because some are per DWG and some are per AutoCAD instance.

For instance, part of them are inside AcDbDatabase and part inside AcDbAppSystemVariables.

Regards,

Ioan said...

Hello Fernando!
I try to insert a block in the current document (an empty dwg file) If the UCS is World, using acedGetPoint() I have no problem with block positioning. But if I change the UCS position, the insertion point of the block is still related to the WUCS .I do not understand what is wrong, as I've read in documentation about acedGetPoint(..) that "the coordinates of the point stored in result are expressed in terms of the current UCS"... But on my screen is still based on the WUCS!!
Do I have to make a transformation from the current UCS into the World UCS and after the insertion to restore the ucs back to the last user? Is this really necessary?

Any idea?
Kind regards!
Ioan

Fernando Malard said...

Hello Ioan,

acedGetPoint() returns the point on the current UCS:

"The coordinates of the point stored in result are expressed in terms of the current UCS."

Internally ObjectARX deal on almost its methods within WCS. You will need to convert using these functions to go from/to:

bool acdbWcs2Ucs(ads_point p, ads_point q, bool vec);

bool acdbUcs2Wcs(ads_point p, ads_point q, bool vec);

Try to get your point and convert before create the entity.

Anonymous said...

Hello Fernando!
I would like to know which is the fast way to scale an entire drawing (database).

Best regards!
Ioan

Fernando Malard said...

Hello Ioan,

Basically I would do the following:

- Create a 3D Matrix to scale:

double dFac = 1.5; // Your scale
AcGePoint3d ptCenter(0,0,0); // Scale point reference

AcGeMatrix3d scaleMat;
scaleMat.setToScaling(dFac,ptCenter);

- After this, step through an AcDbBlockTableRecord entries and open each one;

- For each AcDbBlockTableRecord, step through its entities;

- For each AcDbEntity, call the transformBy() method passing in your transformation matrix;

* If you want only to transform Model Space you only need to traverse its entities.

That's it.

Anonymous said...

As usual.. the fastest response in the world to ARX questions! :-)
So there is no other way to avoid individual scaling? In fact this is the real question: there is no global function to automatically scaling the entire ModelSpace entities?
Thank you Fernando!
Ioan

Fernando Malard said...

Ioan,

I really don't know a single call function that can do the magic.

If there is one, it would probably do the same way you will need to do.

Note that the overall result of the scaling process can affect entities depending on the center point specified.

Regards,

Anonymous said...

Hello Fernando!
Thank you for the valuable informations posted here!
But I'm a little bit confused:
Which are the differences between the

bool acdbWcs2Ucs(ads_point p, ads_point q, bool vec);
bool acdbUcs2Wcs(ads_point p, ads_point q, bool vec);

and

int acedTrans(const ads_point pt, const struct resbuf * from, const struct resbuf * to, int disp, ads_point result);

It seems that both set of functions do the same thing but in practice I got different results combined with the scale problem:

Which is the correct transformation function, to make a correct scale operations on all drawing elements, when the UCS is not set to the default WCS to get the right scale point supposed that the scale point is the drawing origin (0,0,0)?

Fernando Malard said...

Hello,

The default AutoCAD blank drawing has both WCS and UCS with the same initial configuration.

In this case, both functions will transform from one system to another with the same configurations and thus result to nothing.

To be able to see the difference between these two methods you need to change the current UCS and choose a different origin and axis.

Major ObjectARX API methods work based only on WCS which is the global coordinate system that is always static. In this case the result of using these functions need to be adjusted with the proper tranformation to allow you to reach the desired results.

Hope this explain why are you getting the same results.

Regarding to the scale I recommend you to build the scale matrix from the 0,0,0 point. In fact the result over objects placed far from this point will result a movement away from the 0,0,0.

If you think better there is no way to scale a drawing without moving the entities but if you want to scale each single entity from its center we will need to calculate each entity center. This can be done by getting the entity extents and by calculation this bouding box center.

Hope this help.
Regards,

Anonymous said...

Hi Guys,

I would like to use the "-Boundary" command and pass in a coordinate, but I want the routine to return the AcDbObjectId of the created boundary. Is this posible?

Fernando Malard said...

You will need to use acedEntLast() method which will return the last created entity after the Boundary command.

Something like this:

ads_name ent;
AcDbObjectId id = AcDbObjectId::kNull;
if (acdbEntLast(ent) == RTNORM)
{
if (acdbGetObjectId (id,ent) == Acad::eOk)
{
// Here you can open the object
}
}

Regards.

Fernando Malard said...

Actually is acdbEntLast() not aced.

Anonymous said...

Hi,

I have a similar problem to what Abhay had. I try to open a new drawing and I want it to become the current document. I use

acDocManager->executeInApplicationContext(newSyncDocHelper, (void *)pData);

void newSyncDocHelper( void *pData)
{
if (acDocManager->isApplicationContext())
{
acDocManager->appContextNewDocument((const TCHAR *)pData)
}

executeInApplicationContext does not return before the user clicks on the original drawing first.

Any thoughts?

Dawie

Fernando Malard said...

Dawie,

You need to explicity lock the document you want to make current after executing the appContextNewDocument() method.

Take a look at Document Locking notes at SDK Documentation.

Regards.

Anonymous said...

Fernando,

My document opens and it is current now, but the problem is that executeInApplicationContext() does not return the control back to its caller function before the user clicks on the original drawing. The caller function needs to continue, because I have some things that has to be loaded into the new drawing. Clear as mud?

Dawie

Fernando Malard said...

Try to lock the document and make it current.

Anonymous said...

Hello Fernando,

Trying to break an entity (LWpolyline) with acedCommand i get a message "No object found".
When i feed the same ads_name to acedCommand "Move" it is accepted but nothing moves.

Here's what i tried;

if (pEnt->upgradeOpen() != Acad::eOk)
acutPrintf (_T("\nNew Entity not Write"));

acedCommand(RTSTR, _T("break"), RTENAME, en, RTSTR, _T("f"), RTPOINT, pt1, RTPOINT, pt1, 0);
//error: no object foud. Also "RTLB entity & point RTLE" doesn't work.

acedCommand(RTSTR, _T("move"), RTENAME, en, RTSTR, _T(""), RTPOINT, _T("10,10"), RTPOINT, _T("30,30"), 0);
//a test with fake points. No error but nothing moves.

acedCmd doesn't help me out either.

Any idea?

Regards,

Frank Nauwelaerts

Fernando Malard said...

Hello Frank,

You cannot run a native command with the entity on AcDb::kForWrite opened state. It need to be closed because the command will need to open it for write.

Try to call the commands with the entity closed and probably they will work just fine.

Regards.

Anonymous said...

Hi Fernando,


Right!
Makes perfect sense.
At first it used to be kForRead to check if entity isKindOf curve. Now i've closed the pointer before acedCommand.
Seems to work all right.

Thanks,

Frank Nauwelaerts

Anonymous said...

Hi Fernando Malard

I've read about the using of acedCommand function in creating specific entities , but i would like to create "text" on the table,

this is my source code , i don't know how to continue with the entry ->Justify/Style 、height、rotation degree and "text content"



acedCommand(RTSTR, ("text"), RTSTR, PAUSE,RTNONE);

I also want to create a text ex:"test" in the right location if AutoCAD user click on that location , could i do that ?


plz give some suggestions , many thanks

Fernando Malard said...

Hello,

Don't do that, acedCommand/sendStringToExecute are just the last resource solutions for things you can't do directly to the AutoCAD's database.

To create a text entity directly to database use the AcDbText entity class which will allow you to set all desired parameters plus the text contents.

Something like this:

// CODE BEGIN


AcDbText* pText = new AcDbText();
pText->setTextString(_T("MyText"));
pText->setHeight(10.0);
pText->setRotation(0.0);
pText->setNormal(AcGeVector(0,0,1));
pText->setPosition(AcGePoint3d(0,0,0));
pText->setAlignmentPoint(AcGePoint3d(0,0,0));
pText->setColorIndex(3);

AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()->getBlockTable(pBlockTable, AcDb::kForRead);

AcDbBlockTableRecord *pBlockTableRecord = NULL;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite);

pBlockTable->close();

AcDbObjectId textId;
pBlockTableRecord->appendAcDbEntity(textId, pText);

pBlockTableRecord->close();
pText->close();


// CODE END

Hope this help.

Anonymous said...

hi again Fernando

im forgot to tell you im using VC 6 to build ARX 2000 for AutoCAD 2000, there is something wrong when compiling this code you gave me, is there some head files that i must include ?

the error messages are like this
Acdbtext 'ptext ... and some syntax errors

sry about my noob in VC 6 , but this is very important for me.

Thanks for your reply ~!

Anonymous said...

Brilliant Fernando this is works , the acdbtext works fine after include some headers,



thank you a lot

Jimmy32 said...

Hello Fernando

//this is my code
void test()
{
double r,d;
d = 150;
r = d/3;
acedCommand(RTSTR,"CIRCLE",RTSTR,"100,100",RTREAL,r,0);
acutPrintf("\ng01");

struct resbuf *rb , *rb2 ;
int rc;
int rc2;
rc = acedGetSym("var", &rb);
rc2 = acedGetSym("var2", &rb2);
rb = acutBuildList(RTREAL, 3.12,0);
rb2 = acutBuildList(RTREAL,6.66,0);
rc = acedPutSym("var", rb);
rc2 = acedPutSym("var2", rb2);
} //end code

I want to put the r 、 d value directly into the var 、var2 variables , do i have any alternative method to make it work ? for example , if i determined some int or char value in the specific definition , how do i make acedgetsym and command "!" to show the value r & d

Here is the code that you show to me,yes i've also tried and it works fine ,but how do i make the "my text" result to connect the acedgetsym and acedputsym function so im able to type "!sym" to see the string "my text"

plz help Mr Fernando

Fernando Malard said...

Jimmy,

The only way to send/receive AutoLISP symbols is through acedPutSym()/acedGetSym().

Take a look at your ObjectARX documentation and search for "AutoLISP Symbols".

There are examples of Put/Get methods.

Regards,
Fernando.

Anonymous said...

Hello Fernando!
Could you indicate (at least the pseudo-code) how to start aa ARX routine which moves a point on an arc. The animation (the point moving on the arc) must be visible on the screen.

Best wishes!
Krom

Fernando Malard said...

Hello Krom,

You can access the AcDbArc curve parameters and calculate the point position by an increment you set.

To do that use the getPointAtParam() method which will return the point over the arc. The AcDbArc class is derived from AcDbCurve which implements all the parametric curve stuff.

The full arc length in terms of parameters can be calculated by acquiring the start and end parameters.

For each step get your existing AcDbPoint entity and set its new position.

It should work...

Anonymous said...

Hello Fernando!
Thank you for your fast response!
I can calculate with no problems all the intermediary positions on the arc, but my problem is how to "move" the point to create the animated illusion. All I'm asking is the pseudo-code from Arx point of view: for example I do not know if I can move the point or the point must be deleted and recreate after. Which is the fastest and safe solution?

Best wishes!
Krom

Fernando Malard said...

Krom,

AutoCAD is not a multithread environment so any parallel processing it risky.

One thing you could do is to set a Windows Timer and from time to time draw a temporary X mark at the current point position by using the acedGrDraw() method.

Sorry, I can't think an easier solution for that.

Regards.

Unknown said...

First of all, thank you for this excellent resource site! It's much appreciated.

I have a question that I'm hoping you can help me with.


I'm developing a plugin using ObjectARX that produces a PDF file containing hyperlinks.

Rather than write an entire PDF exporter from scratch, I'm exporting the file from Autocad and then adding the link in as a separate step. It's working great, but the interface is a bit awkward -- the user exports the PDF file, then runs a command (provided by my plugin) that prompts them to enter the name of the file they just exported. They end up having to specify the PDF filename twice, once for the exporter and once for my plugin. I'd like to streamline the process.

I can think of two ways of doing it. The first is to have my plugin prompt the user for a filename to save the PDF file to, then invoke the PDF exporter from my plugin and do my post-processing. The second is to have my plugin run the PDF exporter, and somehow retrieve the name of the exported file.

For the first approach, I would have to pass a filename (and possibly other settings) to the exportpdf command. For the second approach, I would have to retrieve the filename that the user gave to the PDF exporter.

Is there a way to do either of those using ObjectARX? I've checked the documentation, and I haven't found anything useful so far.

Thanks in advance for any help.

Fernando Malard said...

Bernie,

I think the best and most reliable way to do that is by monitoring the plot reactor.

Take a look at the AcPublishReactor and AcPublishUIReactor classes. They both deal with some information packages that may contain the information you want:

AcPublishReactorInfo
AcPublishUIReactorInfo

There should be something else available at the AcPl classes which is the Plotting branch into ARX class tree.

Regards.

Unknown said...

Thanks very much, Fernando. I'll give that a try.

Anonymous said...

hi Fernando,
I like the result buffer very convenient.
Is there any idea of storing objectID in result buffer?

Fernando Malard said...

Hello,

You can always store it as an "old ID" which can be obtained with the method asOldId() inside AcDbObjectId class.

The only problem is that it is not valid across AutoCAD sessions. It is safe to use AcDbHandle which stays the same during the DWG existence.

Take a look at the ARX docs about entity relationship and handles.

Regards.

Anonymous said...

Hello Fernando!
Your blog is very helpful for me!


I have a function in LISP. It's name for example: "ReadFile".
I can invoke it in AutoCad's command line by writing:

(ReadFile "c:\file.txt").

Now, I have a ObjectArx application.
I can invoke simply commands like _circle from ObjectArx application:


acedCommand(RTSTR, _T("_circle"), RTSTR, _T("10,10"),RTSTR , _T("10") ,RTSTR, _T(""), 0);

but I don't know how to invoke: (ReadFile "c:\file.txt") ?

Best Regards!
Peter

Ps. Fernando rules! :)

Fernando Malard said...

Hi Peter,

Use sendStringToExecute() method and pass your LISP expression.

Cheers.

Anonymous said...

Hello Fernando,
I have build my arx application in 32 bit machine/compiler. It works fine. Now I want to use my 32 bit arx on 64 bit machine/AutoCAD. I tried to do appload but it says "Unable to load application". What is the easiest way to use the application in 64 bit AutoCAD? Do it need to completely port it using 64 bit Visual studio with 64 bit settings or is there some other way?

Thanks

Fernando Malard said...

Hello,

AutoCAD 32-bit is locked to work only into 32 machines even being theoretically possible to do it. There are some tricks to force it to work but I woudn't recommend you to go that way.

To properly make your application compatible with x64 machines you need to port your code making it suitable for x64 compilation. The ObjectARX SDK comes with appropriate files for both platforms and your compilation platforms need to target the appropriate files as well.

You will end up with multiple configurations at your project like Debug32, Debug64, Release32, Release64.

Note that x64 compiled code won't work into 32bit AutoCAD as well.

Take a look at the ObjectARX documentation for further information.

Regards,

Unknown said...

Hi Fernando!
- I want export PDF file silent in my code.
( path of PDF file i already have)
.
Before I do save automatic drawing by:

resbuf _new_result;
_new_result.restype=RTSHORT;
_new_result.resval.rint=0;
ads_setvar(L"CMDECHO",&_new_result);
ads_setvar(L"FILEDIA",&_new_result); acedCommand(RTSTR,L"_qsave",RTSTR,filePath,RTNONE);


but I can't do same with _EPDF.
thanks !

Fernando Malard said...

Hi minhnv,

You need to use the "-EXPORT" command...here is a sample LISP expression to export the current Display to a my.pdf file into your C:\ drive:

(command "_-EXPORT" "_PDF" "_D" "_NO" "C:\\my.pdf")

Hope it helps you.
Regards,

Unknown said...
This comment has been removed by the author.
Unknown said...
This comment has been removed by the author.
Anonymous said...

Hi,

How can I use the methods acedcmd() and acedCommand() for insert command. I want to bring a block to my model space so I need to use insert command. How I use acedCommand for it?

Thanks

Fernando Malard said...

Hello,

These methods are not valid after AutoCAD 2015 when fibers were removed.

Take a look at this article (there is a handy tutorial video at this page too):

http://adndevblog.typepad.com/autocad/2014/04/migration-after-fiber-is-removed-in-autocad-2015.html

Regards,

Anonymous said...

Hello,

I'm newbie with c++ and I've a question for pro..

can we get the value of a variable create in LISP ?
Lisp example: (setq item1 "test")

then capture the item1 value in c++ ?

any samples wil;l be appreciated. Thank you.
Mike

Fernando Malard said...

Hello,

You can use the acedGetSym() and acedPutSym() methods from C++.
Take a look at this blog post:

http://adndevblog.typepad.com/autocad/2012/08/setget-autolisp-symbol.html

Regards,

Akash Das said...

Thank you for knowledge sharing
i need some more information
I have created mfc application(Add-in) for BricsCAD Application. The main starting dll is loading by a registry rest project dlls are dynamically loading. Now I want to load that same mfc application(Add-in) for AutoCAD. So can you tell how to do?

Fernando Malard said...

Hi Akash,

BricsCAD does have BRX programming environment which mimics AutoCAD ObjectARX API.
These APIs comprehend C++ and .NET programming languages and .NET seems to be quite different on both platforms.

From the extent I know about BRX C++, it should match most of ObjectARX C++ features so it would be just a matter of rename the classes.

I never created ObjectARX+BRX applications into a single solution in Visual Studio but it supposed to be viable once you can use conditional definitions in C++ to tweak class names.

If you create all your MFC as a pure DLL without using any of BRX or ObjectARX specific classes, it could be shared by both when running into Windows systems.

Anyway, I think it is feasible but unfortunately I don't have any sample code to share.

Best Regards,