Custom Object Snap Mode Example

The following example demonstrates the creation of custom object snap modes:

#include "rxobject.h"
#include "ads.h"
#include "adslib.h"
#include "dbmain.h"
#include "dbents.h"
#include "dbosnap.h"
#include "acedinpt.h"
// Socket Osnap mode protocol extension class.
//
class AcmeSocketInfo : public AcDbCustomOsnapInfo {
public:
ACRX_DECLARE_MEMBERS(AcmeSocketInfo);
virtual Acad::ErrorStatus   
getOsnapInfo(
    AcDbEntity*           pickedObject,
    int                   gsSelectionMark,
    const AcGePoint3d&    pickPoint,
    const AcGePoint3d&    lastPoint,
    const AcGeMatrix3d&   viewXform,
    AcArray<AcGePoint3d>& snapPoints,
    AcArray<int>&         geomIdsForPts,
    AcArray<AcGeCurve3d>& snapCurves,
    AcArray<int>&         geomIdsForLines)
};
// This class is registered with AcRx, to be used by the host
// application to look up entity class-specific implementations.
//
ACRX_NO_CONS_DEFINE_MEMBERS(AcmeSocketInfo,AcDbCustomOsnapInfo);
Acad::ErrorStatus  
AcmeSocketInfo::getOsnapInfo(
    AcDbEntity*,
    int,
    const AcGePoint3d&,
    const AcGePoint3d&,
    const AcGePoint3d&,
    AcArray<AcGePoint3d>& snapPoints,
    AcArray<int>&         geomIdsForPts,
    AcArray<AcGeCurve3d>& snapCurves,
    AcArray<int>&         geomIdsForLines)
{
    // Associate with AcDbEntity to define default behavior.
    //
    snapPoints.setLogicalLength(0);
    geomIdsForPts.setLogicalLength(0);
    snapLiness.setLogicalLength(0);
    geomIdsForLines.setLogicalLength(0);
}
// Socket Osnap mode protocol extension object for AcDbLine.
//
class AcmeSocketForLine : public AcmeSocketInfo {
public:
virtual Acad::ErrorStatus   
getOsnapInfo(
    AcDbEntity*           pickedObject,
    int                   gsSelectionMark,
    const AcGePoint3d&    pickPoint,
    const AcGePoint3d&    lastPoint,
    const AcGeMatrix3d&   viewXform,
    AcArray<AcGePoint3d>& snapPoints,
    AcArray<int>&         geomIdsForPts,
    AcArray<AcGeCurve3d>& snapCurves,
    AcArray<int>&         geomIdsForLines)
};
Acad::ErrorStatus  
AcmeSocketForLine::getOsnapInfo(
    AcDbEntity*          pickedObject,
    int,
    const AcGePoint3d&    pickPoint,
    const AcGePoint3d&,
    const AcGeMatrix3d&   viewXform,
    AcArray<AcGePoint3d>& snapPoints,
    AcArray<int>&         geomIdsForPts,
    AcArray<AcGeCurve3d>& snapCurves,
    AcArray<int>&         geomIdsForLines)
{
    // Protocol extension ensures that the following assertion
    // is always true, but check in non-production versions
    // just to be safe.
    //
    ASSERT(pickedObject->isKindOf(AcDbLine::desc()));
    // In production, a hard cast is fastest.
    AcDbLine* lineEnt = (AcDbLine*)pickedObject;
    // Do computation using AcDbLine protocol, pickPoint, and
    // viewXform. For example, if you want to find the closest 
    // socket to the pick point and return just that, set 
    // snapPoints and geomIdsForPts accordingly.
    // But this isn't an AutoSnap mode...
    //
    snapLiness.setLogicalLength(0);
    geomIdsForLines.setLogicalLength(0);
}
// Actual protocol extension objects
//
static AcmeSocketInfo*          pDefaultSocketInfo = NULL;
static AcmeSocketForLine*       pSocketForLine = NULL;
// "SOCket" Osnap mode glyph object
//
class AcmeSocketGlyph : public AcGiGlyph 
{
public:
virtual Acad::ErrorStatus 
setLocation(const AcGePoint3d& dcsPoint);
virtual void              
viewportDraw(AcGiViewportDraw* vportDrawContext);
private:
    AcGePoint3d mCurDcsLoc;
};
Acad::ErrorStatus 
AcmeSocketGlyph::setLocation(const AcGePoint3d& dcsPoint)
{
    mCurDCSLoc = dcsPoint;
}
// These variables are extremely transient, and are
// made static to save constructor/destructor cost.
static AcGePoint2d&   sPixelArea;
AcArray<AcGePoint3d>  sSegmentPoints[2];
void 
AcmeSocketGlyph::viewportDraw(AcGiViewportDraw* vportDrawContext)
{
    // Taking mCurDCSLoc, the pixel size, and the AutoSnap
    // marker size into account, plus anything else, such as socket
    // orientation, draw the glyph.
    // If this ASSERT fails, then the pixel size is really position-
    // dependent.
    //
    ASSERT(!vportDrawContext->viewport()->isPerspective());
    vportDrawContext->viewport()->
        getNumPixelsInUnitSquare(
            AcGePoint3d::kOrigin, pixelArea);
    double halfGlyphSizeInDCS =
        acdbCustomOsnapManager->osnapGlyphSize() * pixelArea.x / 2.0;
    // Draw an asterisk with 4 segments. 
    //
    sSegmentPoints[0].set(
        mCurDCSLoc.x-halfGlyphSizeInDCS,
        mCurDCSLoc.y-halfGlyphSizeInDCS, 0.0);
    sSegmentPoints[1].set(
        mCurDCSLoc.x+halfGlyphSizeInDCS,
        mCurDCSLoc.y+halfGlyphSizeInDCS, 0.0);
    vportDrawContext->geometry().polylineDc(
        2, &(sSegmentPoints[0]));
    sSegmentPoints[0].set(
        mCurDCSLoc.x-halfGlyphSizeInDCS,
        mCurDCSLoc.y+halfGlyphSizeInDCS, 0.0);
    sSegmentPoints[1].set(
        mCurDCSLoc.x+halfGlyphSizeInDCS,
        mCurDCSLoc.y-halfGlyphSizeInDCS, 0.0);
    vportDrawContext->geometry().polylineDc(
        2, &(sSegmentPoints[0]));
    sSegmentPoints[0].set(
        mCurDCSLoc.x-halfGlyphSizeInDCS,
        mCurDCSLoc.y, 0.0);
    sSegmentPoints[1].set(
        mCurDCSLoc.x+halfGlyphSizeInDCS,
        mCurDCSLoc.y, 0.0);
    vportDrawContext->geometry().polylineDc(
        2, &(sSegmentPoints[0]));
    sSegmentPoints[0].set(
        mCurDCSLoc.x,
        mCurDCSLoc.y-halfGlyphSizeInDCS, 0.0);
    sSegmentPoints[1].set(
        mCurDCSLoc.x,
        mCurDCSLoc.y+halfGlyphSizeInDCS, 0.0);
    vportDrawContext->geometry().polylineDc(
        2, &(sSegmentPoints[0]));
};
AcmeSocketGlyph* pSocketGlyph = NULL;
// Master object for the socket custom Osnap mode.
//
class AcmeSocketMode : public AcDbCustomOsnapMode {
public:
virtual const char*     
localModeString() const  {return "SOCket"};
virtual const char*     
globalModeString() const {return "SOCket"};
virtual const AcRxClass* 
entityOsnapClass() const {return AcmeSocketInfo::desc());
virtual AcGiGlyph*       
glyph() const {return pSocketGlyph;);
virtual const char*      
tooltipString() const  {return "Socket to Me?" };
};
static AcmeSocketMode* pSocketMode = NULL;
/* ================ ObjectARX application interface ============ */
extern "C" AcRx::AppRetCode 
acrxEntryPoint(AcRx::AppMsgCode msg, void*) {
    switch(msg) {
    case AcRx::kInitAppMsg:
        // Register the class.
        //
        AcmeSocketInfo::rxInit();
        acrxBuildClassHierarchy();
        pDefaultSocketInfo = new AcmeSocketInfo;
        AcDbEntity::desc()->addX(AcmeSocketInfo::desc(),
            pDefaultSocketInfo);
        pSocketForLine = new AcmeSocketForLine;
        AcDbLine::desc()->addX(AcmeSocketInfo::desc(),
            pSocketForLine);
    };
        // Create the glyph object to be returned by the socket 
        // mode object.
        //
        pSocketGlyph = new AcmeSocketGlyph;
        // Create and register the custom Osnap Mode
        pSocketMode = new AcmeSocketMode;
        acdbCustomOsnapManager->addCustomOsnapMode(pSocketMode);
        // The socket Osnap mode is now plugged in and ready to
        // use.
        //
        break;
    case AcRx::kUnloadAppMsg:
        // Clean up.
        acdbCustomOsnapManager->removeCustomOsnapMode(pSocketMode);
        delete pSocketMode;
        // Unregister, then delete the protocol extension object.
        //
        AcDbEntity::desc()->delX(AcmeSocketInfo::desc());
        delete pDefaultSocketInfo;
        AcDbLine::desc()->delX(AcmeSocketInfo::desc());
        delete pSocketForLine;
        // Remove the protocol extension class definition.
        //
        acrxClassDictionary->remove("AcmeSocketInfo");
        break;
    default:
        // Between the initialization and termination of the
        // application, all registered objects will be directly
        // invoked as needed.  No commands or AutoLISP 
        // expressions are necessary.
        //
        break;
    }
    return AcRx::kRetOK;
}