December 16, 2013

Disabling snapping to specific AutoCAD objects using .NET – Part 2

In the last post, we introduced the idea of preventing object snapping on certain objects by tagging them with XData and then attaching an overrule within AutoCAD to stop it from getting osnap information for them.

This worked very well for standard, single-object snap modes – such as center, quad, mid, end, etc. – but didn’t work for intersection points.

Intersection points are determined by calling an object’s intersectsWith() method, which can thankfully also be overruled using a GeometryOverrule. Overruling this behaviour comes with a few important caveats, though:

intersectsWith() can be called in a number of different scenarios, so it’s recommended that you only overrule it for the absolutely minimum time you need to. This could be achieved by limiting the time you have either the overrule or the XData attached, and is left to the reader to worry about. But please do worry about it. :-)

This method also takes two entities as parameters, as it returns the intersection between two entities. If your entity is the primary entity, this will work fine. But if it ends up being the secondary one, it may slip through the net and have intersection points generated for it. The only real way to address this is to remove the XData filtering of the overrule and implement it yourself for each of the entities passed in. That said, if your entities are physically co-located and all have their snapping turned off, there shouldn’t be a need to jump through these additional hoops (and so I’ve chosen not to do so).

Here’s the updated C# code with the additional overrule implementation and the very slightly updated logic to manage it:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using System;

namespace ObjectSnapping

{

publicclassCommands

{

conststring regAppName = "TTIF_SNAP";

privatestaticOSOverrule _osOverrule = null;

privatestaticIntOverrule _geoOverrule = null;

// Object Snap Overrule to prevent snapping to objects

// with certain XData attached

publicclassOSOverrule : OsnapOverrule

{

public OSOverrule()

{

// Tell AutoCAD to filter on our application name

// (this should mean our overrule only gets called

// on objects possessing XData with this name)

SetXDataFilter(regAppName);

}

publicoverridevoid GetObjectSnapPoints(

Entity ent,

ObjectSnapModes mode,

IntPtr gsm,

Point3d pick,

Point3d last,

Matrix3d view,

Point3dCollection snap,

IntegerCollection geomIds

)

{

}

publicoverridevoid GetObjectSnapPoints(

Entity ent,

ObjectSnapModes mode,

IntPtr gsm,

Point3d pick,

Point3d last,

Matrix3d view,

Point3dCollection snaps,

IntegerCollection geomIds,

Matrix3d insertion

)

{

}

publicoverridebool IsContentSnappable(Entity entity)

{

returnfalse;

}

}

// Geometry Overrule to prevent IntersectsWith() working on

// objects with certain XData attached

publicclassIntOverrule : GeometryOverrule

{

public IntOverrule()

{

// Tell AutoCAD to filter on our application name

// (this should mean our overrule only gets called

// on objects possessing XData with this name)

SetXDataFilter(regAppName);

}

publicoverridevoid IntersectWith(

Entity ent1,

Entity ent2,

Intersect intType,

Plane proj,

Point3dCollection points,

IntPtr thisGsm,

IntPtr otherGsm

)

{

}

publicoverridevoid IntersectWith(

Entity ent1,

Entity ent2,

Intersect intType,

Point3dCollection points,

IntPtr thisGsm,

IntPtr otherGsm

)

{

}

}

privatestaticvoid ToggleOverruling(bool on)

{

if (on)

{

if (_osOverrule == null)

{

_osOverrule = newOSOverrule();

ObjectOverrule.AddOverrule(

RXObject.GetClass(typeof(Entity)),

_osOverrule,

false

);

}

if (_geoOverrule == null)

{

_geoOverrule = newIntOverrule();

ObjectOverrule.AddOverrule(

RXObject.GetClass(typeof(Entity)),

_geoOverrule,

false

);

}

ObjectOverrule.Overruling = true;

}

else

{

if (_osOverrule != null)

{

ObjectOverrule.RemoveOverrule(

RXObject.GetClass(typeof(Entity)),

_osOverrule

);

_osOverrule.Dispose();

_osOverrule = null;

}

if (_geoOverrule != null)

{

ObjectOverrule.RemoveOverrule(

RXObject.GetClass(typeof(Entity)),

_geoOverrule

);

_geoOverrule.Dispose();

_geoOverrule = null;

}

// I don't like doing this and so have commented it out:

// there's too much risk of stomping on other overrules...

// ObjectOverrule.Overruling = false;

}

}

[CommandMethod("DISNAP")]

publicstaticvoid DisableSnapping()

{

var doc = Application.DocumentManager.MdiActiveDocument;

var db = doc.Database;

var ed = doc.Editor;

// Start by getting the entities to disable snapping for.

// If none selected, turn off the overrule

var psr = ed.GetSelection();

if (psr.Status != PromptStatus.OK)

return;

ToggleOverruling(true);

// Start a transaction to modify the entities' XData

using (var tr = doc.TransactionManager.StartTransaction())

{

// Make sure our RegAppID is in the table

var rat =

(RegAppTable)tr.GetObject(

db.RegAppTableId,

OpenMode.ForRead

);

if (!rat.Has(regAppName))

{

rat.UpgradeOpen();

var ratr = newRegAppTableRecord();

ratr.Name = regAppName;

rat.Add(ratr);

tr.AddNewlyCreatedDBObject(ratr, true);

}

// Create the XData and set it on the object

using (

var rb =

newResultBuffer(

newTypedValue(

(int)DxfCode.ExtendedDataRegAppName, regAppName

),

newTypedValue(

(int)DxfCode.ExtendedDataInteger16, 1

)

)

)

{

foreach (SelectedObject so in psr.Value)

{

var ent =

tr.GetObject(so.ObjectId, OpenMode.ForWrite) asEntity;

if (ent != null)

{

ent.XData = rb;

}

}

};

tr.Commit();

}

}

[CommandMethod("ENSNAP")]

publicstaticvoid EnableSnapping()

{

var doc = Application.DocumentManager.MdiActiveDocument;

var db = doc.Database;

var ed = doc.Editor;

// Start by getting the entities to enable snapping for

var pso = newPromptSelectionOptions();

pso.MessageForAdding =

"Select objects (none to remove overrule)";

var psr = ed.GetSelection(pso);

if (psr.Status == PromptStatus.Error)

{

ToggleOverruling(false);

ed.WriteMessage("\nOverruling turned off.");

return;

}

elseif (psr.Status != PromptStatus.OK)

return;

// Start a transaction to modify the entities' XData

using (var tr = doc.TransactionManager.StartTransaction())

{

// Create a ResultBuffer and use it to remove the XData

// from the object

using (

var rb =

newResultBuffer(

newTypedValue(

(int)DxfCode.ExtendedDataRegAppName, regAppName

)

)

)

{

foreach (SelectedObject so in psr.Value)

{

var ent =

tr.GetObject(so.ObjectId, OpenMode.ForWrite) asEntity;

if (ent != null)

{

ent.XData = rb;

}

}

};

tr.Commit();

}

}

}

}

Here’s the revised code in action, the main difference being you no longer see intersection osnaps displayed between our tagged geometry: