With Autodesk.AutoCAD.DatabaseServices.OspanOverrule, this is actually a very easy achievable thing to do.
Here is the code sample of the custom OsnapOverrule:
using System;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
namespace CentroidSnap
{
public class MyCentroidSnap : OsnapOverrule
{
private static MyCentroidSnap _instance = null;
private bool _overruling = false;
private bool _started = false;
public static MyCentroidSnap Instance
{
get
{
if (_instance == null) _instance = new MyCentroidSnap();
return _instance;
}
}
public bool Started
{
get { return _started; }
}
public void StartOSnap()
{
_overruling = Overruling;
Overrule.AddOverrule(RXClass.GetClass(typeof(Curve)), this, true);
Overruling = true;
_started = true;
}
public void StopOSnap()
{
Overrule.RemoveOverrule(RXClass.GetClass(typeof(Curve)), this);
Overruling = _overruling;
_started = false;
}
#region overriden methods
public override void GetObjectSnapPoints(
Entity entity, ObjectSnapModes snapMode, IntPtr gsSelectionMark,
Point3d pickPoint, Point3d lastPoint, Matrix3d viewTransform,
Point3dCollection snapPoints, IntegerCollection geometryIds)
{
bool hasCentre = true;
Point3d snapPt = new Point3d();
if (entity is Polyline)
{
Polyline pl = (Polyline)entity;
if (pl.Closed)
{
snapPt = GetPolylineCentre(pl);
}
else
{
hasCentre = false;
}
}
else if (entity is Circle)
{
snapPt = ((Circle)entity).Center;
}
else
{
hasCentre = false;
}
if (hasCentre)
{
//Clear all possible existing snap points
snapPoints.Clear();
//Set OSnap mode to NEA
snapMode = ObjectSnapModes.ModeNear;
snapPoints.Add(snapPt);
}
}
public override bool IsContentSnappable(Entity entity)
{
return false;
}
#endregion
#region private methods
private Point3d GetPolylineCentre(Polyline pl)
{
double x = 0.0, y = 0.0, z = 0.0;
for (int i=0; i<pl.NumberOfVertices; i++)
{
Point3d p = pl.GetPoint3dAt(i);
x += p.X;
y += p.Y;
z += p.Z;
}
return new Point3d(
x / pl.NumberOfVertices,
y / pl.NumberOfVertices,
z / pl.NumberOfVertices);
}
#endregion
}
}
Here is the command class to start/stop the custom MyCentroidSnap overrule:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
[assembly: CommandClass(typeof(CentroidSnap.MyCadCommands))]
namespace CentroidSnap
{
public class MyCadCommands
{
[CommandMethod("CSnap")]
public static void RunMyCadCommand()
{
Document dwg = CadApp.DocumentManager.MdiActiveDocument;
Editor ed = dwg.Editor;
if (!MyCentroidSnap.Instance.Started)
{
MyCentroidSnap.Instance.StartOSnap();
ed.WriteMessage(
"\nMYCentroidOSnap is started.");
}
else
{
MyCentroidSnap.Instance.StopOSnap();
ed.WriteMessage(
"\nMYCentroidOSnap is stopped.");
}
Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
}
}
}
I also posted on the same topic a couple of years ago here, here and here. Obviously, I could have also create this MyCentroidSnap class by deriving it from Autodesk.AutoCAD.GraphicsInterface.Glyph class.
As usual, see this video clip for the MyCentroidSnap's action.