Invoke a function after object rotation

Invoke a function after object rotation

Anonymous
Not applicable
11,768 Views
46 Replies
Message 1 of 47

Invoke a function after object rotation

Anonymous
Not applicable

Hi to all,

I'd like to have a block that when The user rotate the block I can call automatically a custom function and print of how much degrees is rotated. Could you give me some examples or idea ? 

 

Best Regards,

Marco

0 Likes
Accepted solutions (2)
11,769 Views
46 Replies
Replies (46)
Message 2 of 47

ronjonp
Mentor
Mentor

Something like this?

(defun c:foo (/ s o)
  ;; RJP » 2020-09-10
  (if (setq s (ssget))
    (foreach e (vl-remove-if 'listp (mapcar 'cadr (ssnamex s)))
      (if (vlax-property-available-p (setq o (vlax-ename->vla-object e)) 'rotation)
	(princ (strcat "\nRotation: "
		       (vl-princ-to-string (* (/ (vla-get-rotation o) pi) 180.0))
		       " degrees."
	       )
	)
      )
    )
  )
  (princ)
)
0 Likes
Message 3 of 47

Anonymous
Not applicable

Sorry but I'm not very confident with ObjectArx, this is an function that is invoked automatically ? 

 

0 Likes
Message 4 of 47

ronjonp
Mentor
Mentor

Oops .. just realized this was the ARX forum. My solution is in lisp. 

0 Likes
Message 5 of 47

devitg
Advisor
Advisor

@Anonymous , do you mean how much it was rotated from the before rotation 

 

Say,  a block is insert with a say 30 degree , and user ROTATE it 15 degree . Then the lisp must tell you it was rotated 15 , but if you read the ROTATION property   it will be 45 degree. 

 

For better understanding, and, maybe, get further help, please upload such sample.dwg , with a before and an after  situation 

 

 

 

 

 

0 Likes
Message 6 of 47

tbrammer
Advisor
Advisor

First: I assume that you mean "block reference" (AcDbBlockReference or short "BREF" ) when you write "block".

In ObjectARX you can use various reactor classes to monitor changes on entities.

 

For example an AcDbEntityReactor. You can derive your own class from it and implement these virtual methods.

 

  • openedForModify(const AcDbObject* dbObj)       
    Here you have to store the old block transform matrix of your BREF
    AcDbBlockReference::cast()->blockTransform()
  • modified(const AcDbObject* dbObj)
    Here you read the new block transform matrix and calculate the rotation

You must explicitely add your AcDbEntityReactor to each BREF that you want to watch by calling

bref->addReactor(myReactor).

 

You can also use an AcDbDatabaseReactor. For each modified entity in a drawing this one will  call

  • objectOpenedForModify(const AcDbDatabase* dwg, const AcDbObject* dbObj)
  • objectModified (const AcDbDatabase* dwg, const AcDbObject* dbObj);

I would recomment to use the AcDbEntityReactor for performance reasons.

To collect new BREFs and attach your AcDbEntityReactor to them you can use

 AcDbDatabaseReactor::objectAppended(const AcDbDatabase* dwg, const AcDbObject* dbObj)

 

I recomment to check out the ARXDBG sample project in <ARX>\samples\database\ARXDBG. Build the project and load ArxDbg.arx. It implements the command "Reactors" that lets you play with the various reactor types. With the command "SnoopEnts" you can see which reactors are attached to an entity. You can also check out the source code to learn how to use the reactor classes.


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

Message 7 of 47

Alexander.Rivilis
Mentor
Mentor
Accepted solution

@Anonymous 

AcDbTransformOverrule can help you. You have to override transformBy method. In this method you have calculate rotation from matrix.
 

Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"


Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Expert Elite Member

Message 8 of 47

Anonymous
Not applicable

Hi @tbrammer , 

I'm thinking that reactor is a good way but I'm not confident with them. I'll detail more specifically my needs. 

 

Do you remeber the Wheel block ? 🙂 

 

I'd like to insert the Wheel block on my draw. After insert the total rotation from original insertion is 0 degrees. After some times I'll decide to rotate the whell to the right side of 30°. Finished the rotation I'd like to store on my xData resbuf the value : "totalRotation" = "30°". This process should be automatic after rotation command.

 

Can you give me any other suggestion or example ? 

 

Thank you for your Reply,

 

Kind Regards,

Marco

 

 

P.S. thank to @Alexander.Rivilis for your response 🙂

0 Likes
Message 9 of 47

Alexander.Rivilis
Mentor
Mentor

Get the Rotation Angle and Axis from AcGeMatrix3d

Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"


Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Expert Elite Member

0 Likes
Message 10 of 47

Anonymous
Not applicable

Hi @tbrammer , I'm trying to write reactor, this is the skeleton 

 

#pragma once
#include <dbmain.h>
class Reattore :
	public AcDbEntityReactor
{

public:
	void openedForModify(const AcDbObject* dbObj) {
		acutPrintf(L"Inizio");
	}

	void modified(const AcDbObject* dbObj) {
		acutPrintf(L"Fine");
	}


};

 

I don't know if it is correct ... 🙂 

 

Now I'm not understanding how to add reactor to this snippet of code also provided from you.

 

void cmdDrawCar()
{
	Acad::ErrorStatus es;
	LPCWSTR blockname = L"*U"; // Blockname. This is for an unnamed block.
	blockname = L"car"; // And this for a named block

	AcDbDatabase *db = acdbHostApplicationServices()->workingDatabase();
	AcDbObjectId BtrID;
	if (blockname[0] != L'*') // '*' as first char means: Unnamed block. Don't search for it.
		BtrID = FindBlock(db, blockname); // Search for an existing named block 

	if (BtrID.isNull())
		BtrID = insertDwgAsBlock(db,L"C:\\Users\\Marco\\source\\dwg\\AutoSoloScocca.dwg",blockname); // For "*U" AutoCAD will create unique names like "*U1", "*U2", ...

	if (BtrID.isNull())
	{
		acutPrintf(L"\nFailed.");
		return;
	}

	// Now create a block reference
	AcDbBlockReference *bref = new AcDbBlockReference;
	bref->setDatabaseDefaults();
	bref->setBlockTableRecord(BtrID);

	AcGeVector3d insertionPoint;
	acedGetPoint(nullptr, L"\nInsertion point: ", asDblArray(insertionPoint));
	AcGeMatrix3d trans; // the transformation matrix
	trans.setToTranslation(insertionPoint);
	bref->setBlockTransform(trans);

//HERE I'd like to add reactor
	bref->addReactor(MY REACTOR);

	// Append it to modelspace
	AcDbObjectId BrefID;
	es = PostToDb(db, BrefID, bref);
	if (!es)
		bref->close();
	else
		delete bref;
}

 

Thank you,

Marco

0 Likes
Message 11 of 47

Alexander.Rivilis
Mentor
Mentor

@Anonymous 

Once again, I recommend to use AcDbTransformOverrule

 

Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"


Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Expert Elite Member

Message 12 of 47

tbrammer
Advisor
Advisor

You can find a sample that demonstrats the usage of various reactors in <ARX>\samples\reactors\dbreact_dg.

 

The common way is to have a global or static pointer to a singleton reactor object.

You have to initialize your reactor class during initialization of your app and to remove the class when the app is unloaded:

static Reattore *scMyEntityReactor=nullptr;

void initReattore() {
	Reattore::rxInit();
}

void cleanupReattore() {
	if (scMyEntityReactor) {
		delete scMyEntityReactor;
		scMyEntityReactor = nullptr;
	}
	deleteAcRxClass(Reattore::desc());
}

extern "C" AcRx::AppRetCode 
acrxEntryPoint( AcRx::AppMsgCode msg, void* pPkt ) {
	switch (msg) 	{
		case AcRx::kInitAppMsg:
			initReattore();
			break;
			
		case AcRx::kUnloadAppMsg:
			cleanupReattore();
			break;
	}
}

 

Create your scMyEntityReactor instance when you first need it.
You can implement a function like this, that adds your reactor to an object:

 

 

bool AddReattore(AcDbEntity *ent) {
	if (!scMyEntityReactor)
		scMyEntityReactor = new Reattore();
	return scMyEntityReactor->addToObject(ent);
}

 

 

 


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

0 Likes
Message 13 of 47

tbrammer
Advisor
Advisor

@Alexander.Rivilis 's suggestion to use AcDbTransformOverrule makes sense. You should give it a try.

The biggest advantage is, that the its methods transformBy() and getTransformedCopy() provide the applied transformation matrix directly. If you use a reactor, you have to analyse the modified entity and you don't know whether it was transformed or changed otherwise.

 

However it is up to you to decide what to use. Maybe using a reactor has other advantages in your case.


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

Message 14 of 47

Anonymous
Not applicable

Hi @tbrammer  and @Alexander.Rivilis ,  thank you both for your answers. 

@Alexander.Rivilis Between the two solutions I preferred to choose the reactor because I'm doing experiments for a university thesis. Your solution apparently seems faster but I would like to go into detail on the reactors. But anyway, thank you so much for the details you provided me with

 

@tbrammer  Thank you so much for the code you provided me, now I am having trouble understanding how to write the two reactor functions in order to calculate the rotation of the block. Could you help me with an example if you can? I'm not confident with the Matrixs you mentioned.

 

Thank you,

Marco

0 Likes
Message 15 of 47

Alexander.Rivilis
Mentor
Mentor

@Anonymous

Only with AcDbTransformOverrule you can get transform matrix of entity and calculate rotation angle.

With reactor you can only check the fact of modifying entity and new state of entity, but has not previous state of entity.

Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"


Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Expert Elite Member

0 Likes
Message 16 of 47

tbrammer
Advisor
Advisor

An AcGeMatrix3d is a 4 x 4 matrix.

class AcGeMatrix3d {
  double         entry[4][4];    // [row][column]
};

It can represent a translation, rotation and scaling and more.

You can retrieve the coordinate system of a AcGeMatrix3d using

AcGeMatrix3d::getCoordSystem(
    AcGePoint3d& origin,
    AcGeVector3d& xAxis,
    AcGeVector3d& yAxis,
    AcGeVector3d& zAxis
);

For a matrix that represents translation and rotation the entry[4][4] values are like this:

xAxis.x, yAxis.x, zAxis.x, origin.x      <- entry[0][0..3]
xAxis.y, yAxis.y, zAxis.y, origin.y      <- entry[1][0..3]
xAxis.z, yAxis.z, zAxis.z, origin.z      <- entry[2][0..3]
0,       0,             0,        1

origin can be seen as the translation vector of the matrix.

You want to calculate the rotation axis and angle of the matrix. This article describes a general solution for an arbitrary rotation axis.

Things are much easier, if you only have to deal with rotations around the Z axis. In this case your matrix will look like this:

xAxis.x, yAxis.x, 0, origin.x       cos(a), -sin(a), 0, origin.x
xAxis.y, yAxis.y, 0, origin.y   =   sin(a),  cos(a), 0, origin.y
0,       0,       1, origin.z       0,       0,      1, origin.z
0,       0,       0,        1       0,       0,      0,        1

 With a being the Z rotation angle in radians. So you can simply calculate it like this:

a = atan2(xAxis.y, xAxis.x);

 

a * 180/pi gives the angle in degrees.


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

0 Likes
Message 17 of 47

tbrammer
Advisor
Advisor

As @Alexander.Rivilis  mentioned you will only get the current transformation matrix of your BREF.

So you can only calculate the absolute rotation angle.

If a BREF is rotated first by 20° and than by 30° around Z, you won't be able to find out that angle of the 2nd rotation.


Thomas Brammer ● Software Developer ● imos AGLinkedIn
If an answer solves your problem please [ACCEPT SOLUTION]. Otherwise explain why not.

0 Likes
Message 18 of 47

Anonymous
Not applicable

Hi @tbrammer ,

 

I'm confused 🙂 . As you know I'm not very confident with AutoCAD and ObjectARX.

 

I'd like to do a simple rotation(just 1 axis).

 

The solution proposed by Alexander at this link can be used in my reactors ? I hope you can help me.

 

Best Regards,

Marco

0 Likes
Message 19 of 47

Anonymous
Not applicable

Yes, I only need the total(absoule) rotation from the beginnig. Assuming I'm starting from 0°, if I rotate 20 ° and the 35° I need the absolute 55° rotation.

 

Thank you,

Marco

0 Likes
Message 20 of 47

Alexander.Rivilis
Mentor
Mentor

@Anonymous wrote:

Yes, I only need the total(absoule) rotation from the beginnig. Assuming I'm starting from 0°, if I rotate 20 ° and the 35° I need the absolute 55° rotation.

 

Thank you,

Marco


So you need to call method AcDbBlockReference::rotation() in order to get rotation angle in radians

Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"


Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Expert Elite Member