AutoCAD .NET API provides two concrete Jig classes for us to jig different entities in different circumstances, EntityJig and DrawJig. EntityJig is to jig a specific entity as its name indicates and the DrawJig is to jig anything that has graphics to draw, which can be a single entity, a group of entities, or even something that is not available natively in AutoCAD.
We have demonstrated various ways to jig a block (INSERT as used in AutoCAD users and BlockReference in programmers) such as by its position, rotation or both of these factors, from the block name input on, with the original graphics highlighted or not, with user cancellation handling or not, honoring UCS or not, inputting True Color or not, and so on.
But those block jiggers did not take into account associated block attributes. They would ignore all block attributes either during jigging or at creation. In this article, we are going to address the issue. Here is the test command:
[CommandMethod("TestEntityJigger15")]
public static void TestEntityJigger15_Method()
{
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
Database db = HostApplicationServices.WorkingDatabase;
PromptResult pr = ed.GetString("\nBlock name:");
if (pr.Status == PromptStatus.OK)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = tr.GetObject(bt[pr.StringResult], OpenMode.ForRead) as BlockTableRecord;
if (btr != null)
{
BlockReference ent = new BlockReference(new Point3d(0, 0, 0), btr.ObjectId);
BlockTableRecord modelspace = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
modelspace.AppendEntity(ent);
tr.AddNewlyCreatedDBObject(ent, true);
Dictionary<AttributeReference, AttributeDefinition> dict = new Dictionary<AttributeReference, AttributeDefinition>();
if (btr.HasAttributeDefinitions)
{
foreach (ObjectId id in btr)
{
DBObject obj = tr.GetObject(id, OpenMode.ForRead);
if (obj is AttributeDefinition)
{
AttributeDefinition ad = obj as AttributeDefinition;
AttributeReference ar = new AttributeReference();
ar.SetAttributeFromBlock(ad, ent.BlockTransform);
ent.AttributeCollection.AppendAttribute(ar);
tr.AddNewlyCreatedDBObject(ar, true);
dict.Add(ar, ad);
}
}
}
if (BlockAttributesMovingRotatingScaling.Jig(ent, dict))
{
tr.Commit();
}
else
{
ent.Dispose();
tr.Abort();
}
}
}
}
}
Here is what the block (INSERT/BlockReference) and its attributes look like after the insertion point, rotation angle, and scale factors have been jigged.
As indicated by the attribute strings, the justification/alighnment of each attribute is different, from the default Left alignment to Right, Center and even Backward. As also can be seen, the block attributes have good relative positions including alignment ones to the block insertion point, proper rotation angles even for the backward attribute, and correct scaled height and width. Furthermore, our jigger works perfectly not only in WCS but also in any UCS.
By the way, the block attribute (represented by the API classes AttributeReference and AttributeDefinition) does not support non-uniform scaling, so if you’d like to scale the other graphics of the block reference non-uniformly a good logic has to be carefully worked out if the block reference/definition has some attributes. Another reminder is that the AutoCAD INSERT does not hornor UCS at all. The behavior would be very weird if a block were going to be inserted through dragging and jigging especially for the rotation angle. It is still true in the latest version of AutoCAD, 2012. Please give it a try if readers are wondering what is being talked about here.
Here is the core source:
#region Namespaces
using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Windows;
using MgdAcApplication = Autodesk.AutoCAD.ApplicationServices.Application;
using MgdAcDocument = Autodesk.AutoCAD.ApplicationServices.Document;
using AcWindowsNS = Autodesk.AutoCAD.Windows;
#endregion
namespace AcadNetAddinWizard_Namespace
{
public class BlockAttributesMovingRotatingScaling : EntityJig
{
#region Fields
private const double DblTol = 0.0001;
public int mCurJigFactorNumber = 1;
private Point3d mPosition = new Point3d(0,0,0); // Factor #1
private double mRotation = 0.0; // Factor #2
private double mScaleFactor = 1.0; // Factor #3
private double mAngleOffset;
private Dictionary<AttributeReference, AttributeDefinition> mRef2DefMap;
#endregion
#region Constructors
public BlockAttributesMovingRotatingScaling(BlockReference ent, Dictionary<AttributeReference, AttributeDefinition> dict)
: base(ent)
{
ent.TransformBy(MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.CurrentUserCoordinateSystem);
mAngleOffset = (ent as BlockReference).Rotation;
mRef2DefMap = dict;
}
#endregion
#region Overrides
protected override bool Update()
{
switch (mCurJigFactorNumber)
{
case 1:
(Entity as BlockReference).Position = mPosition;
break;
case 2:
(Entity as BlockReference).Rotation = mAngleOffset + mRotation;
break;
case 3:
(Entity as BlockReference).ScaleFactors = new Scale3d(mScaleFactor, mScaleFactor, mScaleFactor);
break;
default:
break;
}
foreach (KeyValuePair<AttributeReference, AttributeDefinition> ar2ad in mRef2DefMap)
ar2ad.Key.SetAttributeFromBlock(ar2ad.Value, (Entity as BlockReference).BlockTransform);
return true;
}
protected override SamplerStatus Sampler(JigPrompts prompts)
{
switch (mCurJigFactorNumber)
{
case 1:
JigPromptPointOptions prOptions1 = new JigPromptPointOptions("\nBlock insertion point:");
prOptions1.UserInputControls = UserInputControls.GovernedByUCSDetect | UserInputControls.UseBasePointElevation | UserInputControls.Accept3dCoordinates;
PromptPointResult prResult1 = prompts.AcquirePoint(prOptions1);
if (prResult1.Status == PromptStatus.Cancel) return SamplerStatus.Cancel;
if (prResult1.Value.IsEqualTo(mPosition, new Tolerance(DblTol, DblTol)))
{
return SamplerStatus.NoChange;
}
else
{
mPosition = prResult1.Value;
return SamplerStatus.OK;
}
case 2:
JigPromptAngleOptions prOptions2 = new JigPromptAngleOptions("\nBlock rotation angle:");
prOptions2.BasePoint = mPosition;
prOptions2.UseBasePoint = true;
PromptDoubleResult prResult2 = prompts.AcquireAngle(prOptions2);
if (prResult2.Status == PromptStatus.Cancel) return SamplerStatus.Cancel;
if (Math.Abs(prResult2.Value - mRotation) < DblTol)
{
return SamplerStatus.NoChange;
}
else
{
mRotation = prResult2.Value;
return SamplerStatus.OK;
}
case 3:
JigPromptDistanceOptions prOptions3 = new JigPromptDistanceOptions("\nBlock scale factor:");
prOptions3.BasePoint = mPosition;
prOptions3.UseBasePoint = true;
PromptDoubleResult prResult3 = prompts.AcquireDistance(prOptions3);
if (prResult3.Status == PromptStatus.Cancel) return SamplerStatus.Cancel;
if (Math.Abs(prResult3.Value - mScaleFactor) < DblTol)
{
return SamplerStatus.NoChange;
}
else
{
mScaleFactor = prResult3.Value;
return SamplerStatus.OK;
}
default:
break;
}
return SamplerStatus.OK;
}
#endregion
#region Method to Call
public static bool Jig(BlockReference ent, Dictionary<AttributeReference, AttributeDefinition> dict)
{
try
{
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
BlockAttributesMovingRotatingScaling jigger = new BlockAttributesMovingRotatingScaling(ent, dict);
PromptResult pr;
do
{
pr = ed.Drag(jigger);
} while (pr.Status != PromptStatus.Cancel &&
pr.Status != PromptStatus.Error &&
jigger.mCurJigFactorNumber++ <= 3);
return pr.Status == PromptStatus.OK;
}
catch
{
return false;
}
}
#endregion
}
}
Some highlights about the code may be helpful:
• The UserInputControls enumerator values GovernedByUCSDetect and Accept3dCoordinates for the JigPromptPointOptions indicate if the point input honors the UCS and if the picked point is in 3D.
• The UCS transformation can be obtained from the static property Editor.CurrentUserCoordinateSystem.
• An entity type needs to be specified in the EntityJig derivative, as BlockReference here.
• The Sampler() override is to acquire input for the block position and rotation angle in a certain order.
• If the input is the same as the stored variable, we’d better return SamplerStatus.NoChange to avoid unnecessary flashing; if not, return SamplerStatus.OK.
• Please do not forget to handle the cancel/escape circumstance as demonstrated.
• The Update() override is to update the position and the rotation properties of the block in the same order as set in the Sampler().
• The Editor.Draw() is the power to fire the jig. If two properties need to be set, the jig needs to be fired off twice. That is why a while loop is used.
• The while loop needs to think about the PromptStatus.Keyword case of the PromptResult after each Jig Drag.
• Only after the jig succeeds should the BlockReference and its attributes be really added to the database to avoid database messy.
• To avoid unnecessary flashing, point and double value comparisions had better be thought about carefully. A good tolerance is important.
The leading edge AutoCAD .NET Addin Wizard (AcadNetAddinWizard) provides a coder, Entity Jigger, to help us create entity jig code automatically, quickly and reliably.
Posted by: |