Author Topic: How do I create a simple BlockInsert jig?  (Read 2552 times)

0 Members and 1 Guest are viewing this topic.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
How do I create a simple BlockInsert jig?
« on: January 21, 2016, 11:17:46 PM »
I'm starting this thread in the interest of not resurrecting any number of dormant threads. I've read many but I still need the push to get it right, especially when the user cancels the insertion process.

If anyone has a concise example of passing a block (either object or file name, its irrelevant at this point) to an insertion function that jigs the block, I'd appreciate it.

Language is not relevant, just so long as it is a .NET version. C#, C++, or VB is fine. I'll convert it if needed.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How do I create a simple BlockInsert jig?
« Reply #1 on: January 22, 2016, 01:54:30 AM »
Hi,

Here's a minimalist insertion block jig. The testing command requires a block named 'BlockTest' already in the block table.

Code - C#: [Select]
  1. using Autodesk.AutoCAD.DatabaseServices;
  2. using Autodesk.AutoCAD.EditorInput;
  3. using Autodesk.AutoCAD.Geometry;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System;
  6. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. [assembly: CommandClass(typeof(InsertBlockJigSample.Commands))]
  9.  
  10. namespace InsertBlockJigSample
  11. {
  12.     public class Commands
  13.     {
  14.         [CommandMethod("Test")]
  15.         public void Test()
  16.         {
  17.             var doc = AcAp.DocumentManager.MdiActiveDocument;
  18.             var db = doc.Database;
  19.             var ed = doc.Editor;
  20.  
  21.             using (Transaction tr = db.TransactionManager.StartTransaction())
  22.             {
  23.                 var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  24.                 if (!bt.Has("BlockTest"))
  25.                 {
  26.                     AcAp.ShowAlertDialog("Block 'BlockTest' not found");
  27.                     return;
  28.                 }
  29.                 using (var br = new BlockReference(Point3d.Origin, bt["BlockTest"]))
  30.                 {
  31.                     br.TransformBy(ed.CurrentUserCoordinateSystem);
  32.                     var jig = new BlockJig(br, "\nInsertion point: ");
  33.                     var result = ed.Drag(jig);
  34.                     if (result.Status == PromptStatus.OK)
  35.                     {
  36.                         var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  37.                         curSpace.AppendEntity(br);
  38.                         tr.AddNewlyCreatedDBObject(br, true);
  39.                     }
  40.                 }
  41.                 tr.Commit();
  42.             }
  43.         }
  44.     }
  45.  
  46.     public class BlockJig : EntityJig
  47.     {
  48.         protected Point3d position;
  49.         protected BlockReference blockReference;
  50.         protected string message;
  51.  
  52.         public BlockJig(BlockReference br, string message)
  53.             : base(br)
  54.         {
  55.             if (br == null)
  56.                 throw new ArgumentNullException("br");
  57.             blockReference = br;
  58.             position = br.Position;
  59.             this.message = message;
  60.         }
  61.  
  62.         protected override SamplerStatus Sampler(JigPrompts prompts)
  63.         {
  64.             var jppo = new JigPromptPointOptions(message);
  65.             jppo.UserInputControls = UserInputControls.Accept3dCoordinates;
  66.             var ppr = prompts.AcquirePoint(jppo);
  67.             if (position.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint)
  68.                 return SamplerStatus.NoChange;
  69.             position = ppr.Value;
  70.             return SamplerStatus.OK;
  71.         }
  72.  
  73.         protected override bool Update()
  74.         {
  75.             blockReference.Position = position;
  76.             return true;
  77.         }
  78.     }
  79. }
  80.  
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How do I create a simple BlockInsert jig?
« Reply #2 on: January 22, 2016, 04:46:41 AM »
.NET do have a PromptStatus.Cancel but most of the time checking for PromptStatus.OK is enough:
result != PromptStatus.OK means result == PromptStatus.Cancel

In the upper code  the jigged block reference is not added to the database before the Drag ends. If the Drag status is PromptStatus.OK, the block is added to the database, else (i.e. the user cancelled the drag operation) the block is disposed (using statement).

Going a little further with an attributed block we want to drag the attributes too (inspired by this Kean's thread).

The BlockAttribJig class inherits from the upper BlockJig class it uses a little helper to store some text geometry infos.

Code - C#: [Select]
  1.     public class BlockAttribJig : BlockJig
  2.     {
  3.         private Dictionary<AttributeReference, TextInfo> attRefInfos;
  4.         private Transaction tr;
  5.  
  6.         public BlockAttribJig(BlockReference br, string message, Dictionary<AttributeReference, TextInfo> attInfos)
  7.             : base(br, message)
  8.         {
  9.             attRefInfos = attInfos;
  10.             tr = br.Database.TransactionManager.TopTransaction;
  11.         }
  12.  
  13.         protected override bool Update()
  14.         {
  15.             base.Update();
  16.             foreach (var entry in attRefInfos)
  17.             {
  18.                 var att = entry.Key;
  19.                 var info = entry.Value;
  20.                 att.Position = info.Position.TransformBy(blockReference.BlockTransform);
  21.                 if (info.IsAligned)
  22.                 {
  23.                     att.AlignmentPoint = info.Alignment.TransformBy(blockReference.BlockTransform);
  24.                     att.AdjustAlignment(blockReference.Database);
  25.                 }
  26.                 if (att.IsMTextAttribute)
  27.                 {
  28.                     att.UpdateMTextAttribute();
  29.                 }
  30.             }
  31.             return true;
  32.         }
  33.     }
  34.  
  35.     public class TextInfo
  36.     {
  37.         public Point3d Position { get; private set; }
  38.  
  39.         public Point3d Alignment { get; private set; }
  40.  
  41.         public bool IsAligned { get; private set; }
  42.  
  43.         public double Rotation { get; private set; }
  44.  
  45.         public TextInfo(DBText text)
  46.         {
  47.             Position = text.Position;
  48.             IsAligned = text.Justify != AttachmentPoint.BaseLeft;
  49.             Alignment = text.AlignmentPoint;
  50.             Rotation = text.Rotation;
  51.         }
  52.     }

In this case, the block and its attributes have to be added to the database before the drag operation, so, if the drag result is PromptStatus.Cancel, we need to erase the block.

Code - C#: [Select]
  1. [CommandMethod("Cmd2")]
  2.         public void Cmd2()
  3.         {
  4.             var doc = AcAp.DocumentManager.MdiActiveDocument;
  5.             var db = doc.Database;
  6.             var ed = doc.Editor;
  7.  
  8.             using (Transaction tr = db.TransactionManager.StartTransaction())
  9.             {
  10.                 var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  11.                 if (!bt.Has("bloc-att"))
  12.                 {
  13.                     AcAp.ShowAlertDialog("Block 'bloc-att' not found");
  14.                     return;
  15.                 }
  16.                 var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  17.  
  18.                 // create a block reference and add it to the current space
  19.                 var br = new BlockReference(Point3d.Origin, bt["bloc-att"]);
  20.                 br.TransformBy(ed.CurrentUserCoordinateSystem);
  21.                 curSpace.AppendEntity(br);
  22.                 tr.AddNewlyCreatedDBObject(br, true);
  23.  
  24.                 // add the attribute references to the block reference and fill a dictionary with attributes text infos
  25.                 var btr = (BlockTableRecord)tr.GetObject(bt["bloc-att"], OpenMode.ForRead);
  26.                 var attInfos = new Dictionary<AttributeReference, TextInfo>();
  27.                 foreach (ObjectId id in btr)
  28.                 {
  29.                     if (id.ObjectClass.Name == "AcDbAttributeDefinition")
  30.                     {
  31.                         var attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  32.                         AttributeReference attRef = new AttributeReference();
  33.                         attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  34.                         br.AttributeCollection.AppendAttribute(attRef);
  35.                         tr.AddNewlyCreatedDBObject(attRef, true);
  36.                         attInfos.Add(attRef, new TextInfo(attDef));
  37.                     }
  38.                 }
  39.                 var jig = new BlockAttribJig(br, "\nInsertion point: ", attInfos);
  40.                 var result = ed.Drag(jig);
  41.  
  42.                 // erase the block reference if the user cancelled
  43.                 if (result.Status == PromptStatus.Cancel)
  44.                 {
  45.                     br.Erase();
  46.                 }
  47.                 tr.Commit();
  48.             }
  49.         }
Speaking English as a French Frog

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: How do I create a simple BlockInsert jig?
« Reply #3 on: January 22, 2016, 04:01:45 PM »
I've read Kean's blog. Very useful info there.

This looks simple enough. I do need the attributes because the blocks will have a random number of attributes from 1 to a theoretical 60. In practice it will probably be closer to 15 for most typical blocks.

So I will need to put the block into the block table before I can use the jig .. that adds a little more complexity to the issue, especially considering that the jig may be cancelled before actually dropping the block. I'll need to clean up the block table as well if there are no references.

This gives me a starting point  ... the next question is whether I can make the jig scale to a specific value. For example, there may be a sequence of blocks that have to be inserted at a predetermined scale other than 1:1 and at other times, the user may elect to scale each block independently upon insertion. If the scale is preset, ideally, the value is passed to the jig so it can scale accordingly. If the scale is not preset, the jig will be 1:1. Could that be handled the same way that the attributes are handled? Add the block to the drawing at the specified scale prior to jigging then jig the specific insert?
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How do I create a simple BlockInsert jig?
« Reply #4 on: January 22, 2016, 06:52:19 PM »
I would use Editor.Command("Insert".........)

And let that handle all the attributes, alignment parameters, annotative, etc...... and can feed the options programmatically for different scale or whatever.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: How do I create a simple BlockInsert jig?
« Reply #5 on: January 23, 2016, 02:29:37 AM »
Keith,

You can scale the block reference before dragging it (you can see a TransformBy() call line 20 of the last testing command I posted).
But Jeff advice is intersting if you're targeting AutoCAD 2015 or later.
Speaking English as a French Frog