Author Topic: SetExtensionDictionaryEntryFilter Method  (Read 1330 times)

0 Members and 1 Guest are viewing this topic.

Augusto

  • Newt
  • Posts: 75
SetExtensionDictionaryEntryFilter Method
« on: March 05, 2024, 12:48:29 PM »
Hello everyone. I've been experimenting with Overrule in AutoCAD's .NET API but encountered recursive method calls when creating objects in WorldDraw using 'myObject.WorldDraw(wd);'. Also, within the polylines I aim to apply Overrule on, there are some data inserted using a specific method.

Code - C#: [Select]
  1.  
  2. // Set XRecord
  3. public void StoreBlockData(ObjectId blockId, double diameter, string type)
  4. {
  5.     using (Transaction tr = blockId.Database.TransactionManager.StartTransaction())
  6.     {
  7.         DBObject obj = tr.GetObject(blockId, OpenMode.ForWrite);
  8.  
  9.         DBDictionary extDict;
  10.         if (obj.ExtensionDictionary.IsNull)
  11.         {
  12.             obj.CreateExtensionDictionary();
  13.             extDict = (DBDictionary)tr.GetObject(obj.ExtensionDictionary, OpenMode.ForWrite);
  14.         }
  15.         else
  16.         {
  17.             extDict = (DBDictionary)tr.GetObject(obj.ExtensionDictionary, OpenMode.ForWrite);
  18.         }
  19.  
  20.         // Store Diameter
  21.         Xrecord diameterXRec = new Xrecord { Data = new ResultBuffer(new TypedValue((int)DxfCode.Real, diameter)) };
  22.         extDict.SetAt("Diameter", diameterXRec);
  23.         tr.AddNewlyCreatedDBObject(diameterXRec, true);
  24.  
  25.         // Store Type
  26.         Xrecord typeXRec = new Xrecord { Data = new ResultBuffer(new TypedValue((int)DxfCode.Text, type)) };
  27.         extDict.SetAt("Type", typeXRec);
  28.         tr.AddNewlyCreatedDBObject(typeXRec, true);
  29.  
  30.         tr.Commit();
  31.     }
  32. }
  33.  
  34.  

Could anyone provide insights or examples on how the 'SetExtensionDictionaryEntryFilter' method operates to help manage these scenarios?

I didn't find usage examples in the documentation. https://help.autodesk.com/view/OARX/2022/ENU/?guid=OARX-ManagedRefGuide-Autodesk_AutoCAD_GraphicsInterface_DrawableOverrule_SetExtensionDictionaryEntryFilter_string

Forgive me if the answer is in front of me, but I wasn't able to find it.

Thank you in advance, Luís Augusto

edit Kerry : add code tags for formatting [ code = csharp ]  with no spaces
« Last Edit: March 08, 2024, 03:21:55 PM by kdub_nz »

Augusto

  • Newt
  • Posts: 75
Re: SetExtensionDictionaryEntryFilter Method
« Reply #1 on: March 06, 2024, 01:01:20 PM »
I found this example that uses a filter for XDATA that utilizes the application name. The filter is set during the initialization of the PipeOverrule class with the command SetXDataFilter(regAppName);

This actually works, i.e., it eliminates the unnecessary call of the WorldDraw method, but as Kean himself said, it might be that in the future XDATA won't be viable for the application, so we can use entries from the extension dictionary.

This is where I'm not understanding. The SetExtensionDictionaryEntryFilter() filtering method expects a string entryName.

https://help.autodesk.com/view/OARX/2024/ENU/?guid=OARX-ManagedRefGuide-Autodesk_AutoCAD_GraphicsInterface_DrawableOverrule_SetExtensionDictionaryEntryFilter_string

Does anyone have any idea on how to use this method?

I will attach the classes that are on the website.

https://www.keanw.com/2009/04/optimized-overruling-in-autocad-2010-using-net.html

Sincerely, Luís Augusto.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: SetExtensionDictionaryEntryFilter Method
« Reply #2 on: March 07, 2024, 03:08:46 AM »
Hi,

Here's an example which uses an extension dictionary entry instead of extension data.
Only the PipeOverrule is affected by this modification.
Accessing to an xrecord in the extension dictionary of a DBObject is not as trivial as accessing to its xdata. To do it, I used some extension methods extracted from this library.
« Last Edit: March 07, 2024, 04:46:42 AM by gile »
Speaking English as a French Frog

Augusto

  • Newt
  • Posts: 75
Re: SetExtensionDictionaryEntryFilter Method
« Reply #3 on: March 07, 2024, 08:29:12 AM »
Wow! It worked perfectly, Gile!
Thank you so much for your generosity in sharing the example and also your library. I'm sure I'll use some of your solutions. This is very valuable for the learning phase I'm in. I can't thank you enough.

Leveraging your knowledge, I'd like to ask you another question.
When you say that 'Accessing to an xrecord in the extension dictionary of a DBObject is not as trivial as accessing to its xdata', is there any downside in your view regarding performance, or is it just a matter of ease of use of xdata?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: SetExtensionDictionaryEntryFilter Method
« Reply #4 on: March 07, 2024, 04:33:32 PM »
When you say that 'Accessing to an xrecord in the extension dictionary of a DBObject is not as trivial as accessing to its xdata', is there any downside in your view regarding performance, or is it just a matter of ease of use of xdata?
IMHO, it is not a question of performance. To read or write xrecord data, you need more code (i.e. the extension methods).
Speaking English as a French Frog

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8721
  • AKA Daniel
Re: SetExtensionDictionaryEntryFilter Method
« Reply #5 on: March 07, 2024, 05:08:33 PM »
From my understanding, xdata’s size is limited, but has a feature that xrecords don’t, in that some items are transformed with the entity (if it’s an entity)

Xrecord’s size isn’t limited .

Performance you will have to measure, there is a cost for opening objects, but in an overrule context, you already have the DbObject opened.
so xdata may win

ResultBuffer is a huge class.
If you don’t need the overrule filter to be persisted and performance is an issue it would be much much faster to have a dictionary {objectid : filterdata}

MickD

  • King Gator
  • Posts: 3637
  • (x-in)->[process]->(y-out) ... simples!
Re: SetExtensionDictionaryEntryFilter Method
« Reply #6 on: March 07, 2024, 05:21:04 PM »
From my understanding, xdata’s size is limited, but has a feature that xrecords don’t, in that some items are transformed with the entity (if it’s an entity)

Xrecord’s size isn’t limited .

Performance you will have to measure, there is a cost for opening objects, but in an overrule context, you already have the DbObject opened.
so xdata may win

ResultBuffer is a huge class.
If you don’t need the overrule filter to be persisted and performance is an issue it would be much much faster to have a dictionary {objectid : filterdata}


Yeah, and handling ResultBuffers is hard work!
With XRecords I usually use a test string in a csv style key:value pair or xml/json for more complex data that I need to put into an object anyway for processing.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

Augusto

  • Newt
  • Posts: 75
Re: SetExtensionDictionaryEntryFilter Method
« Reply #7 on: March 08, 2024, 10:29:13 AM »
IMHO, it is not a question of performance. To read or write xrecord data, you need more code (i.e. the extension methods).

Thank you very much for your contribution, Gile. All answers, including those from other forum members, are very important to me.

From my understanding, xdata’s size is limited, but has a feature that xrecords don’t, in that some items are transformed with the entity (if it’s an entity)

Thanks for the answer, Daniel.

I was curious about what you said above. Could you give me some examples so I can test?

I know that when exploding an object, we lose the information contained in the Xrecord, so I'm testing Overrule to "Undo" this action.

Code - C#: [Select]
  1. public class ExplodeOverrule : TransformOverrule
  2. {
  3.     const string entryName = "TTIF_PIPE";
  4.  
  5.     public ExplodeOverrule()
  6.     {
  7.         // Tell AutoCAD to filter on our application name
  8.         // (this means our overrule will only be called
  9.         // on objects possessing XRecords with this name)
  10.         SetExtensionDictionaryEntryFilter(entryName);
  11.     }
  12.  
  13.     public override void Explode(Entity e, DBObjectCollection objs)
  14.     {
  15.         // Check if the entity is a Polyline
  16.         if (e is Polyline polyline)
  17.         {
  18.             // If so, prevent the explode operation and display a message
  19.             Document doc = Application.DocumentManager.MdiActiveDocument;
  20.             Editor ed = doc.Editor;
  21.             ed.WriteMessage("\nA polyline cannot be exploded.");
  22.  
  23.             // Add the original entity back to the database
  24.             objs.Add(e);
  25.         }
  26.         else
  27.         {
  28.             // Otherwise, proceed with the normal explode operation
  29.             base.Explode(e, objs);
  30.         }
  31.     }
  32.  
  33.     // Keep a reference to the overrule
  34.     private static ExplodeOverrule _explodeOverrule;
  35.  
  36.     public static void StartOverrule()
  37.     {
  38.         _explodeOverrule = new ExplodeOverrule();
  39.         Overrule.AddOverrule(RXObject.GetClass(typeof(Polyline)), _explodeOverrule, false);
  40.         Overrule.Overruling = true;
  41.     }
  42.  
  43.     public static void EndOverrule()
  44.     {
  45.         Overrule.RemoveOverrule(RXObject.GetClass(typeof(Polyline)), _explodeOverrule);
  46.         Overrule.Overruling = false;
  47.     }
  48. }
  49.  

Performance you will have to measure, there is a cost for opening objects, but in an overrule context, you already have the DbObject opened.
so xdata may win

ResultBuffer is a huge class.
If you don’t need the overrule filter to be persisted and performance is an issue it would be much much faster to have a dictionary {objectid : filterdata}

I believe this reinforces your thesis, but as you said, we would have to test and understand the impacts on the application. Costs vs benefits.

https://help.autodesk.com/view/OARX/2023/ENU/?guid=GUID-752D0BE7-CF03-4AC1-8AA6-2DE5BE6A054A

Yeah, and handling ResultBuffers is hard work!
With XRecords I usually use a test string in a csv style key:value pair or xml/json for more complex data that I need to put into an object anyway for processing.

I can consider today to be my lucky day. I received many contributions that expanded my knowledge.

Thank you very much for the answer, MickD.

Did I understand wrong or do you use a single entry in the extension dictionary to insert several text-type data in json format?


I am currently preparing an example to try to stress the Overrule methods in an attempt to discover their limits and also acquire knowledge.
As soon as I finish the code, I will post it here.

Could someone show me how I put the C# code in the correct format here on the forum?

edit Kerry : add code tags for formatting [ code = csharp ]  with no spaces
« Last Edit: March 08, 2024, 03:19:32 PM by kdub_nz »

MickD

  • King Gator
  • Posts: 3637
  • (x-in)->[process]->(y-out) ... simples!
Re: SetExtensionDictionaryEntryFilter Method
« Reply #8 on: March 08, 2024, 04:05:34 PM »
Quote
Did I understand wrong or do you use a single entry in the extension dictionary to insert several text-type data in json format?

Yes, I usually just store a single string entry in the result buffer. JSON and XML are just long strings and C# has powerful parsers and serialisation tools which makes it easy to work with them.

On the occasion where your string data is very large (it happens easily with xml) you can 'minify' the xml/json data and also use Daniel's StringToResultBuffer code linked below to compress the data even further.

https://www.theswamp.org/index.php?topic=27010.msg325377#msg325377
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

Augusto

  • Newt
  • Posts: 75
Re: SetExtensionDictionaryEntryFilter Method
« Reply #9 on: March 08, 2024, 11:29:55 PM »
Yes, I usually just store a single string entry in the result buffer. JSON and XML are just long strings and C# has powerful parsers and serialisation tools which makes it easy to work with them.

On the occasion where your string data is very large (it happens easily with xml) you can 'minify' the xml/json data and also use Daniel's StringToResultBuffer code linked below to compress the data even further.

Thanks, MickD! Your explanation was awesome, man. It really opened my mind to these storage and compression techniques. A few days ago, I was actually studying how some compression methods work behind the scenes. It's a pretty complex subject. Daniel is an exceptional guy. Thanks a lot for sharing this trick with us!

https://www.theswamp.org/index.php?topic=27010.msg325377#msg325377

This is gold

Augusto

  • Newt
  • Posts: 75
Re: SetExtensionDictionaryEntryFilter Method
« Reply #10 on: March 12, 2024, 09:26:27 PM »
I am currently preparing an example to try to stress the Overrule methods in an attempt to discover their limits and also acquire knowledge.
As soon as I finish the code, I will post it here.

I've recently embarked on a journey to delve deeper into the capabilities of AutoCAD's .NET API, particularly focusing on the concept of overrules to customize object behaviors and graphical representations. The fruit of this exploration is a GitHub repository that I've put together, which you can find here: Flex_Tube on GitHub.

While working with overrules, I stumbled upon a challenge that I think is worth discussing. When a document is opened by a different department, the custom graphical representations and behaviors might not be applied. This means that a polyline could potentially be exploded, and the extension dictionary might get lost. From my perspective, there are two possible solutions to this:

1 - Distributing a .dll that contains the overrule methods (excluding editing methods). This, however, requires the end-user to have a CAD version that supports .dll file loading.
2 - Creating an anonymous block containing the modified object's graphical representation and freezing the involved layers. The question then becomes: when should this conversion method be executed? What's the most suitable event for this action?

It's also worth noting that the overrule method for grips is commented out in the Commands class, as it occasionally exhibits unpredictable behavior, but is fun to test.

I'm sharing this repository not only as a resource, but also as an invitation for feedback and contributions. If you have the opportunity, I would appreciate it if you could test the code and maybe even contribute. Additionally, I'm eager to hear from more experienced developers who might have ideas on how to further refine and improve the code and/or my way of programming.

I look forward to your thoughts and contributions on this project.

Best regards, Luís Augusto.



Edit:

I forgot some important things:
1 - The FLEX command must be applied to polylines.
2 - Polylines must have rounded corners.
3 - The tube diameter must respect the polyline curves, otherwise the graphic representation may not be adequate or expected.

The size of the text on the tube can be changed with the TEXTSIZE variable.

As this is a code example, the dimensions are in millimeters for me. I don't know if this is linked to Cultura Corrente, but I inserted an mm at the end of the text.
It may be that in different locations this behavior is different.
« Last Edit: March 12, 2024, 09:37:26 PM by Augusto »

VVeli

  • Newt
  • Posts: 27
Re: SetExtensionDictionaryEntryFilter Method
« Reply #11 on: March 22, 2024, 03:59:26 AM »
From my understanding, xdata’s size is limited, but has a feature that xrecords don’t, in that some items are transformed with the entity (if it’s an entity)

Xrecord’s size isn’t limited .

Performance you will have to measure, there is a cost for opening objects, but in an overrule context, you already have the DbObject opened.
so xdata may win

ResultBuffer is a huge class.
If you don’t need the overrule filter to be persisted and performance is an issue it would be much much faster to have a dictionary {objectid : filterdata}

Sorry to say but XRecord size is limited.
https://www.theswamp.org/index.php?topic=27010.0
https://help.autodesk.com/view/OARX/2023/ENU/?guid=GUID-0B9BDDE0-CDB7-4235-A2B7-8BD1FA82503F
Br. Veli V.

huiz

  • Swamp Rat
  • Posts: 919
  • Certified Prof C3D
Re: SetExtensionDictionaryEntryFilter Method
« Reply #12 on: March 23, 2024, 06:25:21 AM »
...
Sorry to say but XRecord size is limited.
...


On 32-bit computers the limitation is 2 GB. This is not the case on 64-bit computers, and I think there is no 32-bit AutoCAD anymore.
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8721
  • AKA Daniel
Re: SetExtensionDictionaryEntryFilter Method
« Reply #13 on: March 23, 2024, 08:15:14 AM »

Augusto

  • Newt
  • Posts: 75
Re: SetExtensionDictionaryEntryFilter Method
« Reply #14 on: March 27, 2024, 05:49:57 PM »
Daniel, what did you mean in the sentence below?

From my understanding, xdata’s size is limited, but has a feature that xrecords don’t, in that some items are transformed with the entity (if it’s an entity)

Could you give me an example so I can explore the case?
I was intrigued because I imagine it could be a point of failure or loss of data contained in the object.

Thanks

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8721
  • AKA Daniel
Re: SetExtensionDictionaryEntryFilter Method
« Reply #15 on: March 27, 2024, 07:33:53 PM »
If you mean Xdata transform, sure

Some Xdata entries are modified in AcDbObject::xDataTransformBy. See this link for the codes
https://help.autodesk.com/view/OARX/2023/ENU/?guid=OARX-RefGuide-AcDbObject__xDataTransformBy_AcGeMatrix3d_

So, if I create an object with these transformable codes, I can read them back later and see how the state has changed.
Why is this useful? Some objects may not store a normal, or a direction and it can be impossible to determine the width, height, or depth once the object is moved or rotated.

Armed with Xdata, you can build a matrix to transform the entity back to its original state to get it’s bounding box

I’m using Python here, but it should be readable

Code - Python: [Select]
  1. import traceback
  2. from pyrx_imp import Rx
  3. from pyrx_imp import Ge
  4. from pyrx_imp import Gi
  5. from pyrx_imp import Db
  6. from pyrx_imp import Ap
  7. from pyrx_imp import Ed
  8. from pyrx_imp import Gs
  9.  
  10. def PyRxCmd_doit() -> None:
  11.     try:
  12.         Db.Core.regApp("MYXD")
  13.        
  14.         solid = Db.Solid3d()
  15.         solid.createBox(2.0,4.0,96.0)
  16.        
  17.         xd = [(Db.DxfCode.kDxfRegAppName, "MYXD"),
  18.               (Db.DxfCode.kDxfXdWorldXCoord, Ge.Point3d(0, 0, 0)),
  19.               (Db.DxfCode.kDxfXdWorldXDir, Ge.Vector3d.kXAxis),
  20.               (Db.DxfCode.kDxfXdWorldXDir, Ge.Vector3d.kYAxis),
  21.               (Db.DxfCode.kDxfXdWorldXDir, Ge.Vector3d.kZAxis)]
  22.        
  23.         solid.setXData(xd)
  24.         print("\nBefore",solid.xData("MYXD"))
  25.        
  26.         mat = Ge.Matrix3d()
  27.        
  28.         mat.setCoordSystem(
  29.             Ge.Point3d(100,100,100),
  30.             Ge.Vector3d.kZAxis,
  31.             Ge.Vector3d.kXAxis,
  32.             Ge.Vector3d.kYAxis)
  33.        
  34.         solid.transformBy(mat)
  35.         print("\nAfter",solid.xData("MYXD"))
  36.        
  37.         db = Db.curDb()
  38.         db.addToModelspace(solid)
  39.  
  40.     except Exception as err:
  41.         traceback.print_exception(err)
  42.  

Quote
Before [(1001, 'MYXD'),
(1011, <PyGe.Point3d(0.00000000000000,0.00000000000000,0.00000000000000)>),
(1013, <PyGe.Point3d(1.00000000000000,0.00000000000000,0.00000000000000)>),
(1013, <PyGe.Point3d(0.00000000000000,1.00000000000000,0.00000000000000)>),
(1013, <PyGe.Point3d(0.00000000000000,0.00000000000000,1.00000000000000)>)]

After [(1001, 'MYXD'),
(1011, <PyGe.Point3d(100.00000000000000,100.00000000000000,100.00000000000000)>),
(1013, <PyGe.Point3d(0.00000000000000,0.00000000000000,1.00000000000000)>),
(1013, <PyGe.Point3d(1.00000000000000,0.00000000000000,0.00000000000000)>),
(1013, <PyGe.Point3d(0.00000000000000,1.00000000000000,0.00000000000000)>)]


It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8721
  • AKA Daniel
Re: SetExtensionDictionaryEntryFilter Method
« Reply #16 on: March 27, 2024, 08:44:19 PM »
Here’s a better sample
https://help.autodesk.com/view/ACDLT/2024/ENU/?guid=GUID-3481BF7B-73CB-4FD5-B421-C25BE92C6D56

Code - Python: [Select]
  1. def PyRxCmd_doit() -> None:
  2.     try:
  3.         Db.Core.regApp("MYXD")
  4.        
  5.         solid = Db.Solid3d()
  6.         solid.createBox(2.0,4.0,96.0)
  7.        
  8.         xd = [(Db.DxfCode.kDxfRegAppName, "MYXD"),
  9.               (Db.DxfCode.kDxfXdWorldXCoord, Ge.Point3d(0, 0, 0)),
  10.              
  11.               (Db.DxfCode.kDxfXdWorldXDir, Ge.Vector3d.kXAxis),
  12.               (Db.DxfCode.kDxfXdWorldXDir, Ge.Vector3d.kYAxis),
  13.               (Db.DxfCode.kDxfXdWorldXDir, Ge.Vector3d.kZAxis),
  14.              
  15.               (Db.DxfCode.kDxfXdWorldXDisp,Ge.Vector3d.kXAxis),
  16.               (Db.DxfCode.kDxfXdWorldXDisp,Ge.Vector3d.kYAxis),
  17.               (Db.DxfCode.kDxfXdWorldXDisp,Ge.Vector3d.kZAxis)]
  18.        
  19.         solid.setXData(xd)
  20.         print("\nBefore",solid.xData("MYXD"))
  21.        
  22.         mat = Ge.Matrix3d()
  23.         mat.setCoordSystem(
  24.             Ge.Point3d(100,100,100), #pos
  25.             Ge.Vector3d.kZAxis * 2,  #dir + scale
  26.             Ge.Vector3d.kXAxis * 2,
  27.             Ge.Vector3d.kYAxis * 2)
  28.        
  29.         solid.transformBy(mat)
  30.         print("\nAfter",solid.xData("MYXD"))
  31.        
  32.         db = Db.curDb()
  33.         db.addToModelspace(solid)
  34.  
  35.     except Exception as err:
  36.         traceback.print_exception(err)
  37.  

Quote
[(1001, 'MYXD'),
(1011, <PyGe.Point3d(0.00000000000000,0.00000000000000,0.00000000000000)>),

(1013, <PyGe.Vector3d(1.00000000000000,0.00000000000000,0.00000000000000)>),
(1013, <PyGe.Vector3d(0.00000000000000,1.00000000000000,0.00000000000000)>),
(1013, <PyGe.Vector3d(0.00000000000000,0.00000000000000,1.00000000000000)>),
 
(1012, <PyGe.Vector3d(1.00000000000000,0.00000000000000,0.00000000000000)>),
(1012, <PyGe.Vector3d(0.00000000000000,1.00000000000000,0.00000000000000)>),
(1012, <PyGe.Vector3d(0.00000000000000,0.00000000000000,1.00000000000000)>)]

After [(1001, 'MYXD'),
(1011, <PyGe.Point3d(100.00000000000000,100.00000000000000,100.00000000000000)>),

(1013, <PyGe.Vector3d(0.00000000000000,0.00000000000000,1.00000000000000)>),
(1013, <PyGe.Vector3d(1.00000000000000,0.00000000000000,0.00000000000000)>),
(1013, <PyGe.Vector3d(0.00000000000000,1.00000000000000,0.00000000000000)>),

(1012, <PyGe.Vector3d(0.00000000000000,0.00000000000000,2.00000000000000)>),
(1012, <PyGe.Vector3d(2.00000000000000,0.00000000000000,0.00000000000000)>),
(1012, <PyGe.Vector3d(0.00000000000000,2.00000000000000,0.00000000000000)>)]

I just changed the python wrappers to return the correct type, pretty sure .NET returns points instead of vectors

Augusto

  • Newt
  • Posts: 75
Re: SetExtensionDictionaryEntryFilter Method
« Reply #17 on: March 28, 2024, 02:09:27 PM »
Thanks for the answer with examples. It was very clear to me.