I had an email from Martin Duke about this old post a couple of weeks ago. I started to update the original post but then realised that a) I couldn’t easily go that far back using Windows Live Writer and the built-in Typepad editor often messes up code in old posts when I edit them and b) there was some value in revisiting this topic again now that nearly 6 years has passed. I’ll hopefully manage to link to this updated post from the old one without things going awry.
Martin was having some trouble with text display in object snap glyphs: it worked well enough once some geometry has been drawn – even if it didn’t automatically use the colour of the other object snap glyph graphics – but it didn’t work well when a drawing has just been created or loaded. If the previously implemented object snap was used in a fresh drawing, the text didn’t get displayed.
As soon as additional geometry was created in the drawing, however, the text did get displayed as expected.
The answer, it turns out, was to use an SHX font: this allowed the transient text to be displayed properly from the start and with the colour the glyph markers should have.
I also took the opportunity to centre the text properly on the snap point, which is ultimately a matter of taste: I think I even moved it away from the snap point on purpose 6 years ago. ;-)
It would clearly be nice to have TrueType fonts display properly, but this is an artifact of the 2D graphics system (and is logged as an issue in our internal tracking system). The behaviour is different when using a 3D Visual Style, for whatever that’s worth.
Here’s the updated C# code that shows how this can be made to work in this way:
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using AcGi = Autodesk.AutoCAD.GraphicsInterface;
[assembly: ExtensionApplication(
typeof(OsnapApp.CustomOSnapApp))
]
namespace OsnapApp
{
// Register and unregister custom osnap
public class CustomOSnapApp : IExtensionApplication
{
private QuarterOsnapInfo _info =
new QuarterOsnapInfo();
private QuarterGlyph _glyph =
new QuarterGlyph();
private CustomObjectSnapMode _mode;
public void Initialize()
{
// Register custom osnap on initialize
_mode =
new CustomObjectSnapMode(
"Quarter",
"Quarter",
"Quarter of length",
_glyph
);
// Which kind of entity will use the osnap
_mode.ApplyToEntityType(
RXObject.GetClass(typeof(Polyline)),
new AddObjectSnapInfo(_info.SnapInfoPolyline)
);
_mode.ApplyToEntityType(
RXObject.GetClass(typeof(Curve)),
new AddObjectSnapInfo(_info.SnapInfoCurve)
);
_mode.ApplyToEntityType(
RXObject.GetClass(typeof(Entity)),
new AddObjectSnapInfo(_info.SnapInfoEntity)
);
// Activate the osnap
CustomObjectSnapMode.Activate("_Quarter");
}
// Unregister custom osnap on terminate
public void Terminate()
{
CustomObjectSnapMode.Deactivate("_Quarter");
}
}
// Create new quarter object snap
public class QuarterGlyph : AcGi.Glyph
{
private Point3d _pt;
public override void SetLocation(Point3d point)
{
_pt = point;
}
protected override void SubViewportDraw(AcGi.ViewportDraw vd)
{
int glyphPixels = CustomObjectSnapMode.GlyphSize;
var glyphSize = vd.Viewport.GetNumPixelsInUnitSquare(_pt);
// Calculate the size of the glyph in WCS
// (use for text height factor)
// We'll add 10% to the size, as otherwise
// it looks a little too small
double glyphHeight =
(glyphPixels / glyphSize.Y) * 1.1;
string text = "¼";
// Translate the X-axis of the DCS to WCS
// (for the text direction) and the snap
// point itself (for the text location)
var dist = -glyphHeight / 2.0;
var offset = new Vector3d(dist, dist, 0);
var e2w = vd.Viewport.EyeToWorldTransform;
var dir = Vector3d.XAxis.TransformBy(e2w);
var pt = (_pt + offset).TransformBy(e2w);
// Draw the centered text representing the glyph
var style = new AcGi.TextStyle();
var fd =
new AcGi.FontDescriptor("txt.shx", false, false, 0, 0);
style.Font = fd;
style.TextSize = glyphHeight;
vd.Geometry.Text(
pt,
vd.Viewport.ViewDirection,
dir,
text,
false,
style
);
}
}
// OSnap info
public class QuarterOsnapInfo
{
public void SnapInfoEntity(
ObjectSnapContext context,
ObjectSnapInfo result)
{
// Nothing here
}
public void SnapInfoCurve(
ObjectSnapContext context,
ObjectSnapInfo result
)
{
// For any curve
var cv = context.PickedObject as Curve;
if (cv == null)
return;
double startParam = cv.StartParam;
double endParam = cv.EndParam;
// Added a check to avoid zero-length curve problems
if (startParam == endParam)
return;
// Add osnap at first quarter
double param =
startParam + ((endParam - startParam) * 0.25);
var pt = cv.GetPointAtParameter(param);
result.SnapPoints.Add(pt);
// Add osnap at third quarter
param =
startParam + ((endParam - startParam) * 0.75);
pt = cv.GetPointAtParameter(param);
result.SnapPoints.Add(pt);
if (cv.Closed)
{
pt = cv.StartPoint;
result.SnapPoints.Add(pt);
}
}
public void SnapInfoPolyline(
ObjectSnapContext context,
ObjectSnapInfo result)
{
// For polylines
var pl = context.PickedObject as Polyline;
if (pl == null)
return;
// Get the overall start and end parameters
double plStartParam = pl.StartParam;
double plEndParam = pl.EndParam;
// Get the local
double startParam = plStartParam;
double endParam = startParam + 1.0;
while (endParam <= plEndParam)
{
// Calculate the snap point per vertex...
// Add osnap at first quarter
double param =
startParam + ((endParam - startParam) * 0.25);
var pt = pl.GetPointAtParameter(param);
result.SnapPoints.Add(pt);
// Add osnap at third quarter
param =
startParam + ((endParam - startParam) * 0.75);
pt = pl.GetPointAtParameter(param);
result.SnapPoints.Add(pt);
startParam = endParam;
endParam += 1.0;
}
}
}
}
I’m easing back into the swing of things after my 10+ days out of the office. I’ll hopefully get the time to post on more new AutoCAD 2015 APIs during the coming few days.
Update:
Added a fix for a crash when hovering over zero-length lines.