/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include #include "OverrulingSample.h" #include "Ed/EdCommandContext.h" #include "Ed/EdUserIO.h" #include "RxDynamicModule.h" #include "DbCommandContext.h" #include "DbLine.h" #include "Db3dSolid.h" #include "DbMaterial.h" #include "DbHostAppServices.h" #include "Gi/GiMaterial.h" #include "Gi/GiViewportDraw.h" #include "DbGripPoints.h" inline bool isSampleOverruleApplicable(const OdRxObject* pOverruledSubject) { return pOverruledSubject->isKindOf(OdDbLine::desc()); // second condition will be checked in the overrule implementation, so we avoid redundant XData unwinding //ODA_ASSERT(((OdDbLine*)pOverruledSubject)->hasXData(OVERRULE_SAMPLE_APPNAME)); } bool SampleDrawableOverrule::isApplicable(const OdRxObject* pOverruledSubject) const { // the implementation is common return isSampleOverruleApplicable(pOverruledSubject); } OdUInt32 SampleDrawableOverrule::setAttributes(const OdGiDrawable* pSubject, OdGiDrawableTraits * traits) { // we might change line rendering attributes here return OdGiDrawableOverrule::setAttributes(pSubject, traits); } // We overrule line rendering, to draw pipe with a given radius OdDb3dSolidPtr createPipe(const OdDbLine* pLine) { // check if we overruled this line OdResBufPtr xdata = pLine->xData(OVERRULE_SAMPLE_APPNAME); if (xdata.isNull() || xdata->next().isNull()) return OdDb3dSolidPtr(); // extract pipe radius double r = xdata->next()->getDouble(); if (OdZero(r)) return OdDb3dSolidPtr(); // cache line start... OdGePoint3d p1 = pLine->startPoint(); // ... and end points OdGePoint3d p2 = pLine->endPoint(); // line length double len = p1.distanceTo(p2); // zero length lines are not overruled if (OdZero(len)) return OdDb3dSolidPtr(); // new empty 3d solid object OdDb3dSolidPtr solid = OdDb3dSolid::createObject(); // crash in getGeomExtents\worldDraw is possible without the next line. solid->setDatabaseDefaults(pLine->database()); // create cylinder (height = line length, constant radius) solid->createFrustum(len, r, r, r); // cylinder is created centered in WCS origin => place it on the line: set orientation first... solid->transformBy(OdGeMatrix3d::planeToWorld(p2 - p1)); // ...then move to the line center solid->transformBy(OdGeMatrix3d::translation((p1.asVector() + p2.asVector())/2)); // set yellow color //solid->setColorIndex(2); return solid; } bool SampleDrawableOverrule::worldDraw(const OdGiDrawable* pSubject, OdGiWorldDraw* wd) { // we use this overruling only on line class => blind cast is ok const OdDbLine* pLine = static_cast(pSubject); // create the pipe OdDb3dSolidPtr solid = createPipe(pLine); if (solid.isNull()) return OdGiDrawableOverrule::worldDraw(pSubject, wd); solid->worldDraw(wd); // don't call viewportDraw return true; } bool SampleMaterialOverrule::isApplicable(const OdRxObject* ) const { return true; } OdUInt32 SampleMaterialOverrule::setAttributes(const OdGiDrawable* pSubject, OdGiDrawableTraits* traits) { OdUInt32 nFlags = OdGiDrawableOverrule::setAttributes(pSubject, traits); OdGiMaterialTraitsPtr pMatTraits = OdGiMaterialTraits::cast(traits); if (pMatTraits.isNull()) return nFlags; const OdDbMaterial* pMat = static_cast(pSubject); OdSharedPtr pAsset; OdResult retVal = pMat->getFbx(pAsset); if (retVal == eOk && !pAsset.isNull()) { OdRxModulePtr pModuleXML = ::odrxDynamicLinker()->getModule(L"TD_AssetXMLParser"); OdRxModulePtr pModuleMaterial = ::odrxDynamicLinker()->getModule(L"TD_MaterialReader"); if (!pModuleXML.isNull() && !pModuleMaterial.isNull()) { OdGiAssetPtr giAsset = ::odrxServiceDictionary()->getAt(OdGiAsset::m_serviceDictionary); OdRxObjectPtr pCache; giAsset->applyAssetTo(pMat->database()->appServices(), pMat->database(), pAsset, pMatTraits, pCache); } } return nFlags; } bool SampleGeometryOverrule::isApplicable(const OdRxObject* pOverruledSubject) const { // the implementation is common return isSampleOverruleApplicable(pOverruledSubject); } OdResult SampleGeometryOverrule::getGeomExtents(const OdDbEntity* pSubject, OdGeExtents3d& extents) { // we use this overruling only on line class => blind cast is ok const OdDbLine* pLine = static_cast(pSubject); // that implementation is wildly inefficient, but it is short and clear :) OdDb3dSolidPtr solid = createPipe(pLine); if (solid.isNull()) return OdDbGeometryOverrule::getGeomExtents(pSubject, extents); return solid->getGeomExtents(extents); } bool SampleTransformOverrule::isApplicable(const OdRxObject* pOverruledSubject) const { // the implementation is common return isSampleOverruleApplicable(pOverruledSubject); } OdResult SampleTransformOverrule::explode(const OdDbEntity* pSubject, OdRxObjectPtrArray& entitySet) { // we use this overruling only on line class => blind cast is ok const OdDbLine* pLine = static_cast(pSubject); // create the pipe OdDb3dSolidPtr solid = createPipe(pLine); if (solid.isNull()) return OdDbTransformOverrule::explode(pSubject, entitySet); // append the pipe as an explosion result entitySet.append(solid.get()); return eOk; } bool SampleDbObjectOverrule::isApplicable(const OdRxObject* pOverruledSubject) const { // the implementation is common return isSampleOverruleApplicable(pOverruledSubject); } OdDbObjectPtr SampleDbObjectOverrule::deepClone(const OdDbObject* pSubject, OdDbIdMapping& idMap, OdDbObject* owner, bool bPrimary) { // we might remove pipe radius from the cloned object here return OdDbObjectOverrule::deepClone(pSubject, idMap, owner, bPrimary); } OdDbObjectPtr SampleDbObjectOverrule::wblockClone(const OdDbObject* pSubject, OdDbIdMapping& idMap, OdDbObject* owner, bool bPrimary) { // we might remove pipe radius from the cloned object here return OdDbObjectOverrule::wblockClone(pSubject, idMap, owner, bPrimary); } bool SampleGripOverrule::isApplicable(const OdRxObject* pOverruledSubject) const { // the implementation is common return isSampleOverruleApplicable(pOverruledSubject); } static void SampleGripViewportDraw(OdDbGripData* pThis, OdGiViewportDraw* pWd, OdDbStub*, OdDbGripOperations::DrawType type, OdGePoint3d*, int gripSize) { // Get the grip point location. OdGePoint3d p = pThis->gripPoint(); // Transform the grip point from world coordinates to screen coordinates. p.transformBy(pWd->viewport().getWorldToEyeTransform()); // Change the grip color based on its current state. switch (type) { // If the mouse is hovering over the grip, set the color to green. case OdDbGripOperations::kHoverGrip: pWd->subEntityTraits().setColor(OdCmEntityColor::kACIGreen); break; // For all other states, set the color to blue. default: pWd->subEntityTraits().setColor(OdCmEntityColor::kACIBlue); break; } // Set the drawing properties to create a solid-filled polygon. pWd->subEntityTraits().setFillType(kOdGiFillAlways); pWd->subEntityTraits().setDrawFlags(OdGiSubEntityTraits::kDrawSolidFill | OdGiSubEntityTraits::kDrawPolygonFill); // Calculate the pixel density at the grip point's location to ensure consistent size on screen regardless of zoom. OdGePoint2d ptDim; pWd->viewport().getNumPixelsInUnitSquare(p, ptDim); // Create a vector representing the desired grip size in world units. OdGeVector3d v(gripSize / ptDim.x, 0.0, 0.0); // Transform the size vector to screen coordinates to correctly calculate the radius. v.transformBy(pWd->viewport().getWorldToEyeTransform()); // Calculate the radius. The length of the vector is multiplied by 1.5 for a larger appearance. const double radius = v.length() * 1.5; // Define the angle step for the vertices. const double angleStep = OdaPI / 3.0; // Create an array to store the 6 vertices of the hexagon. OdGePoint3d pp[6]; // Loop to calculate the position of each of the six vertices. for (int i = 0; i < 6; ++i) { // Start with an upward direction vector. OdGeVector3d vertexDir = OdGeVector3d::kYAxis; // Rotate the vector around the Z-axis to find the position for the current vertex. vertexDir.rotateBy(i * angleStep, OdGeVector3d::kZAxis); // Set the vector's length to the calculated radius. vertexDir.setLength(radius); // Calculate the final vertex position by adding the direction vector to the grip center point. pp[i] = p + vertexDir; } // Draw the filled hexagon using the calculated vertices in screen coordinates. pWd->geometry().polygonEye(6, pp); } inline void appendGrip(OdDbGripDataPtrArray& grips, const OdGePoint3d& pnt) { // Create a new grip data object. OdDbGripDataPtr aGrip(new OdDbGripData()); // Set a custom function to draw this grip. This will draw a hexagon. aGrip->setViewportDraw(SampleGripViewportDraw); // Set the location for this grip point. aGrip->setGripPoint(pnt); // Add the configured grip to the array of grips. grips.append(aGrip); } OdResult SampleGripOverrule::getGripPoints(const OdDbEntity* pSubject, OdDbGripDataPtrArray& gripsData, const double, const int, const OdGeVector3d&, const int) { // Get the start and end points of the line. OdDbLinePtr pLine = pSubject; const OdGePoint3d stPnt = pLine->startPoint(); const OdGePoint3d enPnt = pLine->endPoint(); // Add a custom grip point at the start of the line. appendGrip(gripsData, stPnt); // Add a custom grip point at the end of the line. appendGrip(gripsData, enPnt); // Add a custom grip point at the midpoint of the line. appendGrip(gripsData, stPnt + (enPnt - stPnt) / 2); return eOk; } OdResult SampleGripOverrule::moveGripPointsAt(OdDbEntity* pSubject, const OdDbVoidPtrArray&, const OdGeVector3d& offset, int) { // Create a translation matrix based on the offset vector, which represents the distance and direction the grip was moved. // Apply this transformation matrix to the entire entity, moving the object along with the grip. return pSubject->transformBy(OdGeMatrix3d::translation(offset)); } const OdString SampleOverruleCommand::groupName() const { return L"ODA Example Commands"; } const OdString SampleOverruleCommand::globalName() const { // less typing return L"ovr"; } void SampleOverruleCommand::execute(OdEdCommandContext* pCmdCtx) { try { // our overrule makes sense only for DWG database OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); // register regapp, for XData operations pDbCmdCtx->database()->newRegApp(OVERRULE_SAMPLE_APPNAME); // ask user for 'A' or 'R', i.e. Add or Remove overule OdDbSelectionSetPtr ss = pDbCmdCtx->dbUserIO()->select(L"Select a line"); for (OdDbSelectionSetIteratorPtr si = ss->newIterator(); !si->done(); si->next()) { OdDbEntityPtr ent = si->objectId().safeOpenObject(OdDb::kForWrite); if (!ent->isKindOf(OdDbLine::desc())) { pCmdCtx->userIO()->putString(L"Non line object skipped"); continue; } OdDbLinePtr line(ent); if (0 == pCmdCtx->userIO()->getKeyword(L"Add/Remove overrule from selected line", L"A R", 0)) { double r = pCmdCtx->userIO()->getReal(L"Enter pipe radius: <1.0>", OdEd::kInpNonNeg, 1.0); OdResBufPtr xdata = OdResBuf::newRb(OdResBuf::kDxfRegAppName, OVERRULE_SAMPLE_APPNAME); xdata->setNext(OdResBuf::newRb(OdResBuf::kDxfXdReal, r)); line->setXData(xdata); } else { line->setXData(OdResBuf::newRb(OdResBuf::kDxfRegAppName, OVERRULE_SAMPLE_APPNAME)); } } } catch (const OdError&) // just ignore the errors { } } void OverrulingSampleModule::initApp() { OdEdCommandStackPtr pCommands = odedRegCmds(); OdRxOverrule::setIsOverruling(true); pCommands->addCommand(&m_SampleOverruleCommand); OdRxOverrule::addOverrule(OdDbLine::desc(), &m_SampleDrawableOverrule); OdRxOverrule::addOverrule(OdDbLine::desc(), &m_SampleTransformOverrule); OdRxOverrule::addOverrule(OdDbLine::desc(), &m_SampleDbObjectOverrule); OdRxOverrule::addOverrule(OdDbLine::desc(), &m_SampleGeometryOverrule); OdRxOverrule::addOverrule(OdDbMaterial::desc(), &m_SampleMaterialOverrule); OdRxOverrule::addOverrule(OdDbLine::desc(), &m_SampleGripOverrule); } void OverrulingSampleModule::uninitApp() { OdEdCommandStackPtr pCommands = odedRegCmds(); pCommands->removeCmd(m_SampleOverruleCommand.groupName(), m_SampleOverruleCommand.globalName()); OdRxOverrule::removeOverrule(OdDbLine::desc(), &m_SampleGripOverrule); OdRxOverrule::removeOverrule(OdDbMaterial::desc(), &m_SampleMaterialOverrule); OdRxOverrule::removeOverrule(OdDbLine::desc(), &m_SampleDrawableOverrule); OdRxOverrule::removeOverrule(OdDbLine::desc(), &m_SampleTransformOverrule); OdRxOverrule::removeOverrule(OdDbLine::desc(), &m_SampleDbObjectOverrule); OdRxOverrule::removeOverrule(OdDbLine::desc(), &m_SampleGeometryOverrule); } // define TX module entry point ODRX_DEFINE_DYNAMIC_MODULE(OverrulingSampleModule); // the stuff below is irrelevant to the sample #if defined(_TOOLKIT_IN_DLL_) && defined(_MSC_VER) extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID ) { switch ( dwReason ) { case DLL_PROCESS_ATTACH: // remove this if you need per-thread initialization DisableThreadLibraryCalls( (HMODULE)hInstance ); break; } return TRUE; } #endif //_TOOLKIT_IN_DLL_