This is a continuation of the post written by my colleague.
In this blog we will explore the Associative mechanism applied to custom solid, previous blog solution may not be applicable when Dimensions are applied using snap points on the sub-entities of a solid. I have created a Frustum solid with Axis of revolution.
To exploit Associative framework, we need to reveal the our subEntities by implementing associative protocol extension AcDbAssocPersSubentIdPE.
The Associative Framework queries this protocol extension when creating new associative dimensions based on AcDbAssocAnnotationActionBody.
Below is only a partial code that exposes sub-entities by implementing persistent protocol.
AcDbEntity* MyEnt1::subSubentPtr(const AcDbFullSubentPath& path) const { const AcDbSubentId subEntId = path.subentId(); if (isCustomSubentId(subEntId)) { AcDbEntity* pSubEntity = nullptr; const Adesk::GsMarker subentIndex = subEntId.index(); switch (subEntId.type()) { case AcDb::kEdgeSubentType: { if (subentIndex == kCustomEdgeSubentIndex) { pSubEntity = new AcDbLine(m_ptSP, m_ptEP); } } break; case AcDb::kVertexSubentType: { if (subentIndex == kCustomVertex1SubentIndex) { pSubEntity = new AcDbPoint(m_ptSP); } else if (subentIndex == kCustomVertex2SubentIndex) { pSubEntity = new AcDbPoint(m_ptEP); } } break; } ASSERT(pSubEntity != nullptr); return pSubEntity; } // If not data of the derived class, use the base class implementation // return AcDb3dSolid::subSubentPtr(path); } Acad::ErrorStatus MyEnt1::subGetSubentPathsAtGsMarker(AcDb::SubentType type, Adesk::GsMarker gsMark, const AcGePoint3d & pickPoint, const AcGeMatrix3d & viewXform, int & numPaths, AcDbFullSubentPath *& subentPaths, int numInserts, AcDbObjectId * entAndInsertStack) const { assertReadEnabled(); numPaths = 0; subentPaths = NULL; if (gsMark == 0) return Acad::eInvalidInput; if (isCustomGsMarker(gsMark)) { switch (type) { case AcDb::kEdgeSubentType: { if (gsMark == kCustomEdgeGsMarker) { numPaths = 1; subentPaths = new AcDbFullSubentPath[1]; subentPaths[0] = AcDbFullSubentPath(objectId(), AcDb::kEdgeSubentType, kCustomEdgeSubentIndex); } } break; case AcDb::kVertexSubentType: { if (gsMark == kCustomEdgeGsMarker) { numPaths = 2; subentPaths = new AcDbFullSubentPath[2]; subentPaths[0] = AcDbFullSubentPath(objectId(), AcDb::kVertexSubentType, kCustomVertex1SubentIndex); subentPaths[1] = AcDbFullSubentPath(objectId(), AcDb::kVertexSubentType, kCustomVertex2SubentIndex); } } break; } ASSERT(numPaths != 0); return Acad::eOk; } // If not data of the derived class, use the base class implementation // return AcDb3dSolid::subGetSubentPathsAtGsMarker(type, gsMark, pickPoint, viewXform, numPaths, subentPaths, numInserts, entAndInsertStack); } Acad::ErrorStatus MyEnt1::subGetGsMarkersAtSubentPath(const AcDbFullSubentPath& path, AcDbIntPtrArray& gsMarkers) const { const AcDbSubentId subEntId = path.subentId(); if (isCustomSubentId(subEntId)) { gsMarkers.removeAll(); const Adesk::GsMarker subentIndex = subEntId.index(); switch (subEntId.type()) { case AcDb::kEdgeSubentType: { if (subentIndex == kCustomEdgeSubentIndex) { gsMarkers.append(kCustomEdgeGsMarker); } } break; case AcDb::kVertexSubentType: { if (subentIndex == kCustomVertex1SubentIndex || subentIndex == kCustomVertex2SubentIndex) { gsMarkers.append(kCustomEdgeGsMarker); } } break; } ASSERT(!gsMarkers.isEmpty()); return Acad::eOk; } // If not data of the derived class, use the base class implementation // return AcDb3dSolid::subGetGsMarkersAtSubentPath(path, gsMarkers); } Adesk::UInt32 MyEnt1::subSetAttributes(AcGiDrawableTraits *traits) { assertReadEnabled(); return AcDb3dSolid::subSetAttributes(traits); } Acad::ErrorStatus MyEnt1::subGetOsnapPoints(AcDb::OsnapMode osnapMode, Adesk::GsMarker gsSelectionMark, const AcGePoint3d& pickPoint, const AcGePoint3d& lastPoint, const AcGeMatrix3d& viewXform, AcGePoint3dArray& snapPoints, AcDbIntArray& geomIds) const { assertReadEnabled(); if (MyEnt1::isCustomGsMarker(gsSelectionMark)) { switch (osnapMode) { case AcDb::kOsModeEnd: { snapPoints.append(m_ptSP); snapPoints.append(m_ptEP); } break; case AcDb::kOsModeNear: { AcGeVector3d viewDir(viewXform(Z, 0), viewXform(Z, 1), viewXform(Z, 2)); AcGeLine3d line3d(m_ptSP, m_ptEP); snapPoints.append(line3d.projClosestPointTo(pickPoint, viewDir)); } break; } return Acad::eOk; } // If not data of the derived class, use the base class implementation // return AcDb3dSolid::subGetOsnapPoints(osnapMode, gsSelectionMark, pickPoint, lastPoint, viewXform, snapPoints, geomIds); } Acad::ErrorStatus MyEnt1::subTransformBy(const AcGeMatrix3d& xform) { assertWriteEnabled(); m_ptSP.transformBy(xform); m_ptEP.transformBy(xform); return AcDb3dSolid::subTransformBy(xform); } //Always check iff 100000 <= gsMarker <= 1000002 bool MyEnt1::isCustomGsMarker(Adesk::GsMarker gsMarker) { return kCustomGsMarkerMin <= gsMarker && gsMarker <= kCustomGsMarkerMax; } bool MyEnt1::isCustomSubentId(const AcDbSubentId& subentId) { const Adesk::GsMarker index = subentId.index(); return kCustomSubentIdIndexMin <= index && index <= kCustomSubentIdIndexMax; }
Here is a screencast and source project is available at GitHub.