Author Topic: BlockTesting2011  (Read 34972 times)

0 Members and 1 Guest are viewing this topic.

kaefer

  • Guest
Re: BlockTesting2011
« Reply #30 on: April 05, 2011, 09:43:36 PM »
F# translation

Multi-Attribute Block Insert

A couple of observations with this F# version, which I expect to concern the other languages too:

1. FindFile throws Autodesk.AutoCAD.Runtime.Exception: eFilerError if the block isn't found. May be intentional.

2. Multiline attributes do not get transformed.

I suggest something like this:
Code: [Select]
                let ar = new AttributeReference()
                ar.SetAttributeFromBlock(ad, br.BlockTransform)
                if ar.IsMTextAttribute then
                    ar.UpdateMTextAttribute()
                    ar.TransformBy br.BlockTransform

3. The InsertBlockReference method would benefit greatly from named and optional parameters. All of them - except the block name - have sensible defaults. Alas, to do this in C# seems to require 4.0.

A possible signature for F# might be thus:
Code: [Select]
type Document with
  member
    InsertBlockReference : blockName:string *
                           ?insertPoint:Autodesk.AutoCAD.Geometry.Point3d *
                           ?scale:Autodesk.AutoCAD.Geometry.Scale3d *
                           ?angle:float * ?layer:string *
                           ?attValues:seq<string * string> *
                           ?annotationScaleNames:seq<string> *
                           ?ownerId:Autodesk.AutoCAD.DatabaseServices.ObjectId ->
                             Autodesk.AutoCAD.DatabaseServices.ObjectId option

Note: Optional parameters are only permitted on type members, that's why there's now an extension method. The complete example is attached.

Other areas of unauthorized tampering with your collective wisdom include dumping the map/hashtable, replaced by a read-only dictionary internally, and provision of a sequence of annotation scales and of an owner BTR Id as additional parameters,

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: BlockTesting2011
« Reply #31 on: April 05, 2011, 11:59:35 PM »

kaefer,
Thanks for the comments, I'm pleased someone is playing with these.
As I stated at the outset these samples are not complete and have subtle issues. The idea was to start with basic concepts and build up from there.
I had expected a few more comments.


Quote
1. FindFile throws Autodesk.AutoCAD.Runtime.Exception: eFilerError if the block isn't found. May be intentional.
Yes it does. And yes, it was ignored intentionally ... untill 'someone' discovered the exception.
The idea was to see the error and find a resolution.

Quote
2. Multiline attributes do not get transformed
Yes.
And Dynamic blocks need addressing explicitly.
And Mirrors,
And Attribute modification to pre-inserted blocks.
And a few other things :)

Quote
3. < .. >  optional parameters. < .. > Alas, to do this in C# seems to require 4.0.
Yes, optional parameters are version and language specific. Most people trying these in c# will not be using .NET 4.0.
I didn't want to branch into language distinctions with this series.

Quote
Other areas of unauthorized tampering with your collective wisdom include dumping the map/hashtable, replaced by a read-only dictionary internally, and provision of a sequence of annotation scales and of an owner BTR Id as additional parameters

Doing this requres a structural change to the samples.
I had intended providing a Dictionary sample using the current structure.
My thought was to also use the HashTable and/or Dictionary for extracting attributes.

Thanks again kaefer.

Regards,
Kerry


kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: BlockTesting2011
« Reply #32 on: April 06, 2011, 03:14:57 AM »
About the FindFile exception, I'd suggest to use a new library method/function.

C#
Code - C#: [Select]
  1.        //=======================================================================
  2.         /// <summary>
  3.         ///  GetBlock: search for the block table record ObjectId in the block table or the search pathes
  4.         /// </summary>
  5.         /// <param name="bName"></param>
  6.         /// <param name="db"></param>
  7.         /// <returns>ObjectId of BlockTableRecord (or ObjectId.Null) </returns>
  8.         public ObjectId GetBlock(string bName, Database db)
  9.         {
  10.             ObjectId result;
  11.             using (Transaction tr = db.TransactionManager.StartTransaction())
  12.             {
  13.                 BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  14.                 if (bt.Has(bName))
  15.                     result = bt[bName];
  16.                 else
  17.                 {
  18.                     try
  19.                     {
  20.                         string path = HostApplicationServices.Current.FindFile(bName + ".dwg", db, FindFileHint.Default);
  21.                         using (Database tmpDb = new Database())
  22.                         {
  23.                             tmpDb.ReadDwgFile(path, System.IO.FileShare.Read, true, "");
  24.                             result = db.Insert(bName, tmpDb, true);
  25.                         }
  26.                     }
  27.                     catch
  28.                     {
  29.                         result = ObjectId.Null;
  30.                     }
  31.                 }
  32.                 tr.Commit();
  33.             }
  34.             return result;
  35.         }
  36.         //=======================================================================

F#
Code - F#: [Select]
  1.  //=======================================================================
  2. /// <summary>
  3. ///  getBlock: search for the block table record ObjectId in the block table or the search pathes
  4. /// </summary>
  5. /// <param name="bName"></param>
  6. /// <param name="db"></param>
  7. /// <returns>ObjectId of BlockTableRecord (or ObjectId.Null) </returns>
  8. let getBlock (bName:string) (db:Database) =
  9.     use tr = db.TransactionManager.StartTransaction()
  10.     let bt = tr.openForRead(db.BlockTableId) :?> BlockTable
  11.     if bt.Has(bName) then bt.[bName]
  12.     else
  13.         try
  14.             let path = HostApplicationServices.Current.FindFile(bName + ".dwg", db, FindFileHint.Default)
  15.             use tmpDb = new Database()
  16.             tmpDb.ReadDwgFile(path, System.IO.FileShare.Read, true, "")
  17.             let id = db.Insert(bName, tmpDb, true)
  18.             tr.Commit()
  19.             id
  20.         with
  21.             |_ -> ObjectId.Null
  22. //=======================================================================
« Last Edit: January 25, 2012, 07:04:56 AM by Kerry »
Speaking English as a French Frog

dan.glassman

  • Guest
Re: BlockTesting2011
« Reply #33 on: April 08, 2011, 01:35:45 AM »
I'll bite -- sorry I've been silent.

Annotative police, plus one change with comment [and for comment] below. 

Planned contributions, unless somebody beats me to it [preferably Autodesk, in some cases -- though I hope I'm not that slow]:

BlockReference annotative portion when time allows -- intending current scale only.  IEnumerable<ObjectContext> per kaefer...meh -- that's up to the user's ANNOAUTOSCALE, in my opinion; don't see a reason to include it in a lib, but may not be seeing past the end of my nose.

Attributes maybe shouldn't be an IDictionary<string, string> in the "use this collective goodness" version, since AutoCAD allows duplicate tags.  Unless there's a corresponding "we refuse to accept your block until you fix your duplicate tags" warning/exception.

IsMTextAttribute/IsDefaultAlignment issues.

DrawOrderTable preservation issues.

Sync Attribute properties to destination drawing TextStyle properties. 

Code - C#: [Select]
  1. try
  2. {
  3.     string path = HostApplicationServices.Current.FindFile(bName + ".dwg", db, FindFileHint.Default);
  4.     bool isAnno = False;
  5.     using (Database tmpDb = new Database())
  6.     {
  7.       tmpDb.ReadDwgFile(path, System.IO.FileShare.Read, true, "");
  8.       isAnno = tmpDb.AnnotativeDwg;
  9.       result = db.Insert(bName, tmpDb, [color=red]false[/color]);  // tmpDb is immediately disposed because of using -- no need to preserve it
  10.     }
  11.  
  12.     if (isAnno)
  13.     {
  14.       BlockTableRecord btr = (BlockTableRecord)(tr.Open(result, OpenMode.ForWrite));
  15.       btr.Annotative = AnnotativeStates.True;
  16.     }
  17. }
  18. catch
  19. {
  20.     result = ObjectId.Null;
  21. }
  22.  
  23.  
« Last Edit: December 18, 2011, 12:50:29 AM by Kerry »

Jeff H

  • Needs a day job
  • Posts: 6144
Re: BlockTesting2011
« Reply #34 on: April 08, 2011, 09:06:23 PM »
Just a couple of things noticed real quick for InsertBlockReference function.

Do you think 'br' or when the new block reference is created that it should be in a using block in case of an error?
The transaction will know nothing about it and will not dispose of it until 'tr.AddNewlyCreatedDBObject(br, true)' is reached.
Same for 'aRef' AttributeReference

Would it save enough time(That probably will never be noticeable) not using bt[blockName] twice by not searching the BlockTable a second time use btr.ObjectID

Changes in red

Code - C#: [Select]
  1. public ObjectId InsertBlockReference(
  2.     Database db,
  3.     string blockName,
  4.     Point3d insertPoint,
  5.     Scale3d scale,
  6.     double angle,
  7.     string layer,
  8.     System.Collections.Hashtable attValues)
  9.         {
  10.             using (Transaction tr = db.TransactionManager.StartTransaction())
  11.             {
  12.                 BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
  13.                 if (!bt.Has(blockName))
  14.                 {
  15.                     string blockPath = HostApplicationServices.Current.FindFile(blockName + ".dwg", db, FindFileHint.Default);
  16.                     if (string.IsNullOrEmpty(blockPath))
  17.                     {
  18.                         return ObjectId.Null;
  19.                     }
  20.                     bt.UpgradeOpen();
  21.                     using (Database tmpDb = new Database(false, true))
  22.                     {
  23.                         tmpDb.ReadDwgFile(blockPath, FileShare.Read, true, null);
  24.                         db.Insert(blockName, tmpDb, true);
  25.                     }
  26.                 }
  27.                 BlockTableRecord btr = tr.GetObject(bt[blockName], OpenMode.ForRead) as BlockTableRecord;
  28.  
  29.                 using (BlockReference br = new BlockReference(insertPoint, btr.ObjectId)
  30.                     { ScaleFactors = scale, Rotation = angle, Layer = layer })
  31.                 {
  32.                     br.TransformBy(ed.CurrentUserCoordinateSystem);
  33.  
  34.                     br.RecordGraphicsModified(true);
  35.                     if (btr.Annotative == AnnotativeStates.True)
  36.                     {
  37.                         ObjectContextCollection contextCollection =
  38.                                                  db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES");
  39.                         Autodesk.AutoCAD.Internal.ObjectContexts.AddContext(br, contextCollection.GetContext("1:1"));
  40.                     }
  41.                     (tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord).AppendEntity(br);
  42.                     foreach (ObjectId id in btr)
  43.                     {
  44.                         AttributeDefinition aDef = tr.GetObject(id, OpenMode.ForRead) as AttributeDefinition;
  45.                         if (aDef != null)
  46.                         {
  47.                              using (AttributeReference aRef = new AttributeReference())
  48.                             {                              
  49.                                 aRef.SetAttributeFromBlock(aDef, br.BlockTransform);
  50.                                 aRef.Position = aDef.Position + br.Position.GetAsVector();
  51.                                 if (attValues.ContainsKey(aDef.Tag.ToUpper()))
  52.                                 {
  53.                                     aRef.TextString = attValues[aDef.Tag.ToUpper()].ToString();
  54.                                 }
  55.                                 br.AttributeCollection.AppendAttribute(aRef);
  56.                                 tr.AddNewlyCreatedDBObject(aRef, true);
  57.                             }
  58.                         }
  59.                     }
  60.                     tr.AddNewlyCreatedDBObject(br, true);
  61.                     tr.Commit();
  62.                     return br.ObjectId;
  63.                 }
  64.             }
  65.         }
  66.         //=======================================================================
  67.  

« Last Edit: October 23, 2023, 04:08:47 PM by kdub_nz »

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: BlockTesting2011
« Reply #35 on: April 09, 2011, 01:15:45 AM »

Quote
Do you think 'br' or when the new block reference is created that it should be in a using block in case of an error?

Yes, probably Jeff.

I had done that with the some of the other samples ... missed it for this one.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Jeff H

  • Needs a day job
  • Posts: 6144
Re: BlockTesting2011
« Reply #36 on: April 13, 2011, 04:09:27 AM »
Just wanted to add I really like the way you intialize your Document, Database,  & Editor------clever idea


I messed around to see when the destructor was called if for some reason someone needed to keep up with open documents you have run a command in, but the destructor was not consistent when called.

No big deal was mainly wondering if it was disposed when a document was closed but I am defintely going to borrow that little setup if it is okay with you and of course will note who I stole it from.

Glenn R

  • Guest
Re: BlockTesting2011
« Reply #37 on: April 13, 2011, 05:32:02 AM »
Well, seeing as Kerry has been quite clever and only used public non-static methods for his command definitions, the class is spun up once and once only for each document the command is called in.

This is the opposite to how a public static command method definition, which is only spun up once per-session.

Not too shabby Kerry, not too shabby at all.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: BlockTesting2011
« Reply #38 on: April 13, 2011, 05:51:21 AM »

< .. >
I am defintely going to borrow that little setup if it is okay with you and of course will note who I stole it from.


You're welcome to perloin the procedure Jeff. don't be fussed about the attribution though cause I stole the idea several years ago and have since lost the reference. :-)

< .. >

Not too shabby Kerry, not too shabby at all.

Thanks Glenn .. I'm pretty satisfied with it.

I'm trying to find the time to finish the rest of the samples I had planned.

Be well guys.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

kaefer

  • Guest
Re: BlockTesting2011
« Reply #39 on: April 13, 2011, 08:33:15 AM »
Well, seeing as Kerry has been quite clever and only used public non-static methods for his command definitions, the class is spun up once and once only for each document the command is called in.

Question: What does Kerry's technique buy that can't be bought by standard assignment inside each command? That is, except cutting those assignments themselves?

Not to appear a spoil-sport, I appreciate this feature nonetheless, see F# example below. But IMHO instance member commands display their full power if there's per-document data to store, which doesn't (yet) seem to be the case here. Besides, what about the failure mode, when doc is null: Under which circumstances can that possibly happen?

Code: [Select]
// Class with implicit constructor, let bindings, do bindings and members must
// appear in the order indicated
type ActiveDocumentTest() =
    // 1. Let bindings in implicit constructor
    let (doc, db, ed) =
        match AcadApp.DocumentManager.MdiActiveDocument with
        | null -> failwith "No Active Document"  // Can't happen?
        | doc -> doc, doc.Database, doc.Editor
   
    // 2. Do bindings in implicit constructor
    do  ed.WriteMessage("\nDocument {0} initialized. ", doc.Name)
   
    // 3. Member definitions
    [<CommandMethod "ActiveDocumentTestCmd">]
    member __.ActiveDocumentTestCmd() =
        ed.WriteMessage("\nCommand in document {0} executed. ", doc.Name)

Glenn R

  • Guest
Re: BlockTesting2011
« Reply #40 on: April 13, 2011, 10:15:35 AM »
Question: What does Kerry's technique buy that can't be bought by standard assignment inside each command? That is, except cutting those assignments themselves?

Nothing, but he's using the class constructor in a different way to what most use. I do use constructors and their chaining quite a bit, I just never thought of doing it for acad command methods. In most instances, like others, I omit the constructor when defining an acad command as it's normally not necessary and the compiler synthesized one is adequate.

What language is that you posted kaefer - French?  :evil:

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: BlockTesting2011
« Reply #41 on: April 13, 2011, 10:57:55 AM »
What language is that you posted kaefer - French?  :evil:

No, it's not French (French is much more verbose), but the Frenchy I am understands it a bit and likes it more and more. :wink:

One more time, many thanks to all, I learn every day new things here (both Programming languages and English).
Speaking English as a French Frog

LE3

  • Guest
Re: BlockTesting2011
« Reply #42 on: April 13, 2011, 11:13:25 AM »
Quote
The idea was to start with basic concepts and build up from there.

Great stuff Kerry - thank you for sharing! - and of course the same goes to the ones doing the porting/translations. nice.