September 21, 2007
Creating a multileader in AutoCAD using a jig from .NET
I'm now back from a fantastic break in Italy and am trying hard to catch back up. Next week I'm off again to San Diego (work, this time), which may cause further interruptions in blog postings.
This question came through from Genésio from Brazil:
I wish jig a leader with an bubble in the and of the leader, at the same time. Can you help me. Perhaps post the solution in your blog (through the interface).
It took me a while - frustratingly long, in fact, and probably this is not exactly what Genésio is after - but here's what I managed to come up with. The "bubble" is framed MText, but it should be modifiable to use a classic bubble block, instead. I drew heavily on this previous post for the jig code.
The positioning of the text took some work, but I'm reasonably happy with the results. If anyone has tweaks to suggest, please post a comment.
Here's the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
namespace DimensionLibrary
{
public class DimensionCmds
{
class MLeaderJig : EntityJig
{
Point3dCollection m_pts;
Point3d m_tempPoint;
string m_contents;
int m_leaderIndex;
int m_leaderLineIndex;
public MLeaderJig(string contents)
: base(new MLeader())
{
// Store the string passed in
m_contents = contents;
// Create a point collection to store our vertices
m_pts = new Point3dCollection();
// Create mleader and set defaults
MLeader ml = Entity as MLeader;
ml.SetDatabaseDefaults();
// Set up the MText contents
ml.ContentType = ContentType.MTextContent;
MText mt = new MText();
mt.SetDatabaseDefaults();
mt.Contents = m_contents;
ml.MText = mt;
ml.TextAlignmentType =
TextAlignmentType.LeftAlignment;
ml.TextAttachmentType =
TextAttachmentType.AttachmentMiddle;
// Set the frame and landing properties
ml.EnableDogleg = true;
ml.EnableFrameText = true;
ml.EnableLanding = true;
// Reduce the standard landing gap
ml.LandingGap = 0.05;
// Add a leader, but not a leader line (for now)
m_leaderIndex = ml.AddLeader();
m_leaderLineIndex = -1;
}
protected override SamplerStatus Sampler(
JigPrompts prompts
)
{
JigPromptPointOptions opts =
new JigPromptPointOptions();
// Not all options accept null response
opts.UserInputControls =
(UserInputControls.Accept3dCoordinates |
UserInputControls.NoNegativeResponseAccepted
);
// Get the first point
if (m_pts.Count == 0)
{
opts.UserInputControls |=
UserInputControls.NullResponseAccepted;
opts.Message =
"\nStart point of multileader: ";
opts.UseBasePoint = false;
}
// And the second
else if (m_pts.Count == 1)
{
opts.BasePoint = m_pts;
opts.UseBasePoint = true;
opts.Message =
"\nSpecify multileader vertex: ";
}
// And subsequent points
else if (m_pts.Count > 1)
{
opts.UserInputControls |=
UserInputControls.NullResponseAccepted;
opts.BasePoint = m_pts;
opts.UseBasePoint = true;
opts.SetMessageAndKeywords(
"\nSpecify multileader vertex or : ",
"End"
);
}
else // Should never happen
return SamplerStatus.Cancel;
PromptPointResult res =
prompts.AcquirePoint(opts);
if (res.Status == PromptStatus.Keyword)
{
if (res.StringResult == "End")
{
return SamplerStatus.Cancel;
}
}
if (m_tempPoint == res.Value)
{
return SamplerStatus.NoChange;
}
else if (res.Status == PromptStatus.OK)
{
m_tempPoint = res.Value;
return SamplerStatus.OK;
}
return SamplerStatus.Cancel;
}
protected override bool Update()
{
try
{
if (m_pts.Count > 0)
{
// Set the last vertex to the new value
MLeader ml = Entity as MLeader;
ml.SetLastVertex(
m_leaderLineIndex,
m_tempPoint
);
// Adjust the text location
Vector3d dogvec =
ml.GetDogleg(m_leaderIndex);
double doglen =
ml.DoglegLength;
double landgap =
ml.LandingGap;
ml.TextLocation =
m_tempPoint +
((doglen + landgap) * dogvec);
}
}
catch (System.Exception ex)
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
doc.Editor.WriteMessage(
"\nException: " + ex.Message
);
return false;
}
return true;
}
public void AddVertex()
{
MLeader ml = Entity as MLeader;
// For the first point...
if (m_pts.Count == 0)
{
// Add a leader line
m_leaderLineIndex =
ml.AddLeaderLine(m_leaderIndex);
// And a start vertex
ml.AddFirstVertex(
m_leaderLineIndex,
m_tempPoint
);
// Add a second vertex that will be set
// within the jig
ml.AddLastVertex(
m_leaderLineIndex,
new Point3d(0, 0, 0)
);
}
else
{
// For subsequent points,
// just add a vertex
ml.AddLastVertex(
m_leaderLineIndex,
m_tempPoint
);
}
// Reset the attachment point, otherwise
// it seems to get forgotten
ml.TextAttachmentType =
TextAttachmentType.AttachmentMiddle;
// Add the latest point to our history
m_pts.Add(m_tempPoint);
}
public void RemoveLastVertex()
{
// We don't need to actually remove
// the vertex, just reset it
MLeader ml = Entity as MLeader;
if (m_pts.Count >= 1)
{
Vector3d dogvec =
ml.GetDogleg(m_leaderIndex);
double doglen =
ml.DoglegLength;
double landgap =
ml.LandingGap;
ml.TextLocation =
m_pts +
((doglen + landgap) * dogvec);
}
}
public Entity GetEntity()
{
return Entity;
}
}
public void MyMLeaderJig()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
// Get the text outside of the jig
PromptStringOptions pso =
new PromptStringOptions(
"\nEnter text: "
);
pso.AllowSpaces = true;
PromptResult pr =
ed.GetString(pso);
if (pr.Status == PromptStatus.OK)
{
// Create MleaderJig
MLeaderJig jig =
new MLeaderJig(pr.StringResult);
// Loop to set vertices
bool bSuccess = true, bComplete = false;
while (bSuccess && !bComplete)
{
PromptResult dragres = ed.Drag(jig);
bSuccess =
(dragres.Status == PromptStatus.OK);
if (bSuccess)
jig.AddVertex();
bComplete =
(dragres.Status == PromptStatus.None);
if (bComplete)
jig.RemoveLastVertex();
}
if (bComplete)
{
// Append entity
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
BlockTable bt =
(BlockTable)tr.GetObject(
db.BlockTableId,
OpenMode.ForRead,
false
);
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
bt,
OpenMode.ForWrite,
false
);
btr.AppendEntity(jig.GetEntity());
tr.AddNewlyCreatedDBObject(
jig.GetEntity(),
true
);
tr.Commit();
}
}
}
}
}
}Here's what you get when you run the MYML command:
八、在Jig中检测shift和control的按键状态
Detecting the use of the shift and control keys during an AutoCAD jig using .NET
Another interesting little problem, that of how to detect the use ofmodifier keys during a jig operation (to indicate different jig behaviour). In this case the specific task was to detect the use of the Control and Shift keys, which – if held down during the jig – should cause our object to display differently.
I started with the code from this previous post which uses a DrawJig to place text in the plane of the screen during the jig operation. I initially thought I’d have to use a message filter (as shown in this previous post), but I eventually realised it wasn’t needed: it’s simply a matter of querying the state of System.Windows.Forms.Control.ModifierKeys at the appropriate moment during our WorldDraw().
Here’s the C# code I used:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
namespace JigTextPlanarToScreen
{
public class TextJig : DrawJig
{
private Point3d _position;
public Point3d Position
{
get { return _position; }
}
// We'll keep our styles alive rather than recreating them
private TextStyle _normal;
private TextStyle _highlight;
public TextJig()
{
_normal = new TextStyle();
_normal.Font =
new FontDescriptor("Calibri", false, true, 0, 0);
_normal.TextSize = 10;
_highlight = new TextStyle();
_highlight.Font =
new FontDescriptor("Calibri", true, false, 0, 0);
_highlight.TextSize = 14;
}
protected override SamplerStatus Sampler(JigPrompts prompts)
{
JigPromptPointOptions opts = new JigPromptPointOptions();
opts.UserInputControls =
UserInputControls.Accept3dCoordinates |
UserInputControls.AcceptMouseUpAsPoint |
UserInputControls.GovernedByUCSDetect;
opts.Message = "\nSelect point: ";
PromptPointResult res = prompts.AcquirePoint(opts);
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
if (res.Status == PromptStatus.OK)
{
if (_position == res.Value)
{
return SamplerStatus.NoChange;
}
else
{
_position = res.Value;
return SamplerStatus.OK;
}
}
return SamplerStatus.Cancel;
}
protected override bool WorldDraw(WorldDraw draw)
{
// We make use of another interface to push our transforms
WorldGeometry wg = draw.Geometry as WorldGeometry;
if (wg != null)
{
// Push our transforms onto the stack
wg.PushOrientationTransform(
OrientationBehavior.Screen
);
wg.PushPositionTransform(
PositionBehavior.Screen,
new Point2d(30, 30)
);
System.Windows.Forms.Keys mods =
System.Windows.Forms.Control.ModifierKeys;
// Check whether the shift key is being pressed
bool shift =
(mods & System.Windows.Forms.Keys.Shift) > 0;
// Check whether the control key is being pressed
bool control =
(mods & System.Windows.Forms.Keys.Control) > 0;
// Draw our screen-fixed text
wg.Text(
new Point3d(0, 0, 0),// Position
new Vector3d(0, 0, 1), // Normal
new Vector3d(1, 0, 0), // Direction
_position.ToString(),// Text
true, // Rawness
(shift && // Textstyle
control ?
_highlight : // Highlight if Ctrl-Shift
_normal) // Normal, otherwise
);
// Remember to pop our transforms off the stack
wg.PopModelTransform();
wg.PopModelTransform();
}
return true;
}
static public void SelectPointWithJig()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
TextJig jig = new TextJig();
PromptResult res = ed.Drag(jig);
if (res.Status == PromptStatus.OK)
{
ed.WriteMessage(
"\nPoint selected: {0}",
jig.Position
);
}
}
}
}
You may have noticed the jig’s point acquisition has slightly different UserInputControls flags defined, mainly because I wanted to test out the jig’s ability to automatically switch the UCS when hovering over the faces of solids. It doesn’t have direct relevance to the rest of this post, but I’ve left it in as the application is actually quite a useful testbed for the 3D point acquisition capabilities of a jig. Plus it was easier to leave the code in than to change it. :-)
Here’s what happens when we run the SELPT command and just move the cursor around:
And here’s what happens when we do the same while pressing Ctrl-Shift – we see the text shown in a larger, bold, non-italic font:
I haven’t attempted to filter any key-down/key-up messages (we would have to go back an implement an IMessageFilter to do so), so use of the Shift key during our point input temporarily overrides the ORTHOMODE system variable (hence the glyph to the upper-right of the cursor). It’s possible to remove this temporary override via the CUI command, if needed.
本帖最后由 lzh741206 于 2010-12-19 18:17 编辑
九、动态绘制一个带有弧段的多段线
December 06, 2010
Jigging an AutoCAD polyline with arc segments using .NET
I was just easing back into post-AU work – dealing with my email backlog and thinking about possible blog posts for the week – when I received a very welcome email from Philippe Leefsma, a member of the DevTech team based in Prague. Philippe had a bit of time to spare during our annual DevDays tour and decided to polish up a sample he’d been working on for posting. It extends a post of mine from four years ago (I can’t believe it’s been that long, but anyway), which shows how to jig a polyline with keywords. Philippe adjusted the code to modify the bulge factor of the current arc segment dynamically based on the cursor position – very much as the standard PLINE command does, in fact.
Here’s the C# code, with a few minor – mostly formatting – modifications from my side:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
namespace PolylineJig
{
class JigUtils
{
// Custom ArcTangent method, as the Math.Atan
// doesn't handle specific cases
public static double Atan(double y, double x)
{
if (x > 0)
return Math.Atan(y / x);
else if (x < 0)
return Math.Atan(y / x) - Math.PI;
else// x == 0
{
if (y > 0)
return Math.PI;
else if (y < 0)
return -Math.PI;
else // if (y == 0) theta is undefined
return 0.0;
}
}
// Computes Angle between current direction
// (vector from last vertex to current vertex)
// and the last pline segment
public static double ComputeAngle(
Point3d startPoint, Point3d endPoint,
Vector3d xdir, Matrix3d ucs
)
{
Vector3d v =
new Vector3d(
(endPoint.X - startPoint.X) / 2,
(endPoint.Y - startPoint.Y) / 2,
(endPoint.Z - startPoint.Z) / 2
);
double cos = v.DotProduct(xdir);
double sin =
v.DotProduct(
Vector3d.ZAxis.TransformBy(ucs).CrossProduct(xdir)
);
return Atan(sin, cos);
}
}
public class BulgePolyJig : EntityJig
{
Point3d _tempPoint;
Plane _plane;
bool _isArcSeg = false;
bool _isUndoing = false;
Matrix3d _ucs;
public BulgePolyJig(Matrix3d ucs) : base(new Polyline())
{
_ucs = ucs;
Vector3d normal = Vector3d.ZAxis.TransformBy(ucs);
_plane = new Plane(Point3d.Origin, normal);
Polyline pline = Entity as Polyline;
pline.SetDatabaseDefaults();
pline.Normal = normal;
AddDummyVertex();
}
protected override SamplerStatus Sampler(JigPrompts prompts)
{
JigPromptPointOptions jigOpts = new JigPromptPointOptions();
jigOpts.UserInputControls =
(UserInputControls.Accept3dCoordinates |
UserInputControls.NullResponseAccepted |
UserInputControls.NoNegativeResponseAccepted);
_isUndoing = false;
Polyline pline = Entity as Polyline;
if (pline.NumberOfVertices == 1)
{
// For the first vertex, just ask for the point
jigOpts.Message = "\nSpecify start point: ";
}
else if (pline.NumberOfVertices > 1)
{
string msgAndKwds =
(_isArcSeg ?
"\nSpecify endpoint of arc or : " :
"\nSpecify next point or : "
);
string kwds = (_isArcSeg ? "Line Undo" : "Arc Undo");
jigOpts.SetMessageAndKeywords(msgAndKwds, kwds);
}
else
return SamplerStatus.Cancel; // Should never happen
// Get the point itself
PromptPointResult res = prompts.AcquirePoint(jigOpts);
if (res.Status == PromptStatus.Keyword)
{
if (res.StringResult.ToUpper() == "ARC")
_isArcSeg = true;
else if (res.StringResult.ToUpper() == "LINE")
_isArcSeg = false;
else if (res.StringResult.ToUpper() == "UNDO")
_isUndoing = true;
return SamplerStatus.OK;
}
else if (res.Status == PromptStatus.OK)
{
// Check if it has changed or not (reduces flicker)
if (_tempPoint == res.Value)
return SamplerStatus.NoChange;
else
{
_tempPoint = res.Value;
return SamplerStatus.OK;
}
}
return SamplerStatus.Cancel;
}
protected override bool Update()
{
// Update the dummy vertex to be our 3D point
// projected onto our plane
Polyline pl = Entity as Polyline;
if (_isArcSeg)
{
Point3d lastVertex =
pl.GetPoint3dAt(pl.NumberOfVertices - 2);
Vector3d refDir;
if (pl.NumberOfVertices < 3)
refDir = new Vector3d(1.0, 1.0, 0.0);
else
{
// Check bulge to see if last segment was an arc or a line
if (pl.GetBulgeAt(pl.NumberOfVertices - 3) != 0)
{
CircularArc3d arcSegment =
pl.GetArcSegmentAt(pl.NumberOfVertices - 3);
Line3d tangent = arcSegment.GetTangent(lastVertex);
// Reference direction is the invert of the arc tangent
// at last vertex
refDir = tangent.Direction.MultiplyBy(-1.0);
}
else
{
Point3d pt =
pl.GetPoint3dAt(pl.NumberOfVertices - 3);
refDir =
new Vector3d(
lastVertex.X - pt.X,
lastVertex.Y - pt.Y,
lastVertex.Z - pt.Z
);
}
}
double angle =
JigUtils.ComputeAngle(
lastVertex, _tempPoint, refDir, _ucs
);
// Bulge is defined as tan of one fourth of included angle
// Need to double the angle since it represents the included
// angle of the arc
// So formula is: bulge = Tan(angle * 2 * 0.25)
double bulge = Math.Tan(angle * 0.5);
pl.SetBulgeAt(pl.NumberOfVertices - 2, bulge);
}
else
{
// Line mode. Need to remove last bulge if there was one
if (pl.NumberOfVertices > 1)
pl.SetBulgeAt(pl.NumberOfVertices - 2, 0);
}
pl.SetPointAt(
pl.NumberOfVertices - 1, _tempPoint.Convert2d(_plane)
);
return true;
}
public bool IsUndoing
{
get
{
return _isUndoing;
}
}
public void AddDummyVertex()
{
// Create a new dummy vertex... can have any initial value
Polyline pline = Entity as Polyline;
pline.AddVertexAt(
pline.NumberOfVertices, new Point2d(0, 0), 0, 0, 0
);
}
public void RemoveLastVertex()
{
Polyline pline = Entity as Polyline;
// Let's first remove our dummy vertex
if (pline.NumberOfVertices > 0)
pline.RemoveVertexAt(pline.NumberOfVertices - 1);
// And then check the type of the last segment
if (pline.NumberOfVertices >= 2)
{
double blg = pline.GetBulgeAt(pline.NumberOfVertices - 2);
_isArcSeg = (blg != 0);
}
}
public void Append()
{
Database db = HostApplicationServices.WorkingDatabase;
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
BlockTable bt =
tr.GetObject(
db.BlockTableId, OpenMode.ForRead
) as BlockTable;
BlockTableRecord btr =
tr.GetObject(
bt, OpenMode.ForWrite
) as BlockTableRecord;
btr.AppendEntity(this.Entity);
tr.AddNewlyCreatedDBObject(this.Entity, true);
tr.Commit();
}
}
public static void RunBulgePolyJig()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
BulgePolyJig jig =
new BulgePolyJig(ed.CurrentUserCoordinateSystem);
while (true)
{
PromptResult res = ed.Drag(jig);
switch (res.Status)
{
// New point was added, keep going
case PromptStatus.OK:
jig.AddDummyVertex();
break;
// Keyword was entered
case PromptStatus.Keyword:
if (jig.IsUndoing)
jig.RemoveLastVertex();
break;
// If the jig completed successfully, add the polyline
case PromptStatus.None:
jig.RemoveLastVertex();
jig.Append();
return;
// User cancelled the command, get out of here
// and don't forget to dispose the jigged entity
default:
jig.Entity.Dispose();
return;
}
}
}
}
}
Here’s the BPJIG command in action, jigging a polyline with arc segments:
Now it must be noted that this implementation is some way from re-implementing the full PLINE command – for those of you who like to re-invent the wheel ;-) - but it should provide some idea of how to allow users to input bulge factors for polyline segments that are being jigged by your custom commands.
Thanks, Philippe! :-)
页:
1
[2]