Back in March, I received an email from Thomas Fitou suggesting an interesting blog topic:
I was thinking about a cool feature in jigs:
- You invoke a command to enter an Mtext or text
- The editor is asking for some text
- You enter the text
- Then a jig is dragged asking for position
- But in the editor appear some options:[R]egular [B]old [I]talic [R]otate 90
- If the user hits "B" the text becomes bold
- If the user hits "R" the text is rotated 90 degrees
- If the user hits "R" again, another 90 degrees and so on...
It struck me as an interesting idea, but wasn’t something I managed to get to until now. It turned out to be a pretty interesting mini-project, too – so much so that I decided to split it across 3 separate posts:
- A basic jig to position, rotate and size a DBText object
- An extended version that supports font-level formatting (bold, italic, etc.)
- For extra points: interactive alignment adjustment (left, middle, right)
In today’s post, then, we’re going to see a basic jig implementation that allows the user to position, rotate and adjust the size of the created text.
A few notes on design decisions related to this code:
- I decided to stick with DBText rather than including MText
- A few previous implementations on this blog have made sure geometry is database-resident before jigging it
- While not needed for this initial post, it is needed later on, so I’ve adopted that same approach, here
- The proposed implementation suggested the use of single keystrokes to enable different modes (at least that’s how I read it)
- The code uses the standard keyword mechanism, which means Enter is needed to submit the mode change
- I’ve placed the UI paths (i.e. the keywords) in the code for the latter parts – for now they don’t do anything, though
- I dropped the “Regular” keyword, with the idea that “Bold” and “Italic” should really be toggles (and “R” is already fairly busy between “ROtate90” and “RIght”, for that matter)
Here’s the C# code for this initial version:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
public class Commands
{
[CommandMethod("QT")]
static public void QuickText()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
PromptStringOptions pso =
new PromptStringOptions("\nEnter text string");
pso.AllowSpaces = true;
PromptResult pr = ed.GetString(pso);
if (pr.Status != PromptStatus.OK)
return;
Transaction tr =
doc.TransactionManager.StartTransaction();
using (tr)
{
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId, OpenMode.ForWrite
);
// Create the text object, set its normal and contents
DBText txt = new DBText();
txt.Normal =
ed.CurrentUserCoordinateSystem.
CoordinateSystem3d.Zaxis;
txt.TextString = pr.StringResult;
// We'll add the text to the database before jigging
// it - this allows alignment adjustments to be
// reflected
btr.AppendEntity(txt);
tr.AddNewlyCreatedDBObject(txt, true);
// Create our jig
TextPlacementJig pj = new TextPlacementJig(tr, db, txt);
// Loop as we run our jig, as we may have keywords
PromptStatus stat = PromptStatus.Keyword;
while (stat == PromptStatus.Keyword)
{
PromptResult res = ed.Drag(pj);
stat = res.Status;
if (
stat != PromptStatus.OK &&
stat != PromptStatus.Keyword
)
return;
}
tr.Commit();
}
}
class TextPlacementJig : EntityJig
{
// Declare some internal state
Database _db;
Transaction _tr;
Point3d _position;
double _angle, _txtSize;
// Constructor
public TextPlacementJig(
Transaction tr, Database db, Entity ent
) : base(ent)
{
_db = db;
_tr = tr;
_angle = 0;
_txtSize = 1;
}
protected override SamplerStatus Sampler(
JigPrompts jp
)
{
// We acquire a point but with keywords
JigPromptPointOptions po =
new JigPromptPointOptions(
"\nPosition of text"
);
po.UserInputControls =
(UserInputControls.Accept3dCoordinates |
UserInputControls.NullResponseAccepted |
UserInputControls.NoNegativeResponseAccepted |
UserInputControls.GovernedByOrthoMode);
po.SetMessageAndKeywords(
"\nSpecify position of text or " +
"[Bold/Italic/LArger/Smaller/" +
"ROtate90/LEft/Middle/RIght]: ",
"Bold Italic LArger Smaller " +
"ROtate90 LEft Middle RIght"
);
PromptPointResult ppr = jp.AcquirePoint(po);
if (ppr.Status == PromptStatus.Keyword)
{
switch (ppr.StringResult)
{
case "Bold":
{
// TODO
break;
}
case "Italic":
{
// TODO
break;
}
case "LArger":
{
// Multiple the text size by two
_txtSize *= 2;
break;
}
case "Smaller":
{
// Divide the text size by two
_txtSize /= 2;
break;
}
case "ROtate90":
{
// To rotate clockwise we subtract 90 degrees and
// then normalise the angle between 0 and 360
_angle -= Math.PI / 2;
while (_angle < Math.PI * 2)
{
_angle += Math.PI * 2;
}
break;
}
case "LEft":
{
// TODO
break;
}
case "RIght":
{
// TODO
break;
}
case "Middle":
{
// TODO
break;
}
}
return SamplerStatus.OK;
}
else if (ppr.Status == PromptStatus.OK)
{
// Check if it has changed or not (reduces flicker)
if (
_position.DistanceTo(ppr.Value) <
Tolerance.Global.EqualPoint
)
return SamplerStatus.NoChange;
_position = ppr.Value;
return SamplerStatus.OK;
}
return SamplerStatus.Cancel;
}
protected override bool Update()
{
// Set properties on our text object
DBText txt = (DBText)Entity;
txt.Position = _position;
txt.Height = _txtSize;
txt.Rotation = _angle;
return true;
}
}
}
When we run our QT (which stands for QuickText) command, we can use it to quickly enter, preview and place a text object:
Command: QT
Enter text string: Some text
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: LA
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: RO
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]:
In the next post, we’ll implement the font-formatting options for bold, italic and bold-italic text (which, as it requires modification to the associated text style, is harder than it sounds).