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; }