/////////////////////////////////////////////////////////////////////////////// // 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 "OdaCommon.h" #include "DbGrip.h" #include "Gi/GiViewportDraw.h" #include "DbAttribute.h" #include "Ge/GeCircArc3d.h" #include "DynamicBlocks/DbBlockRepresentation.h" #include "DynamicBlocks/DbBlockAction.h" #include "DynamicBlocks/DbBlockConstraintParameters.h" #include "DynamicBlocks/DbBlockPropertiesTable.h" #include "DynamicBlocks/DbBlockVisibilityParameter.h" #include "DynamicBlocks/DbBlockRotationGrip.h" #include "DynamicBlocks/DbBlockLinearGrip.h" #include "DynamicBlocks/DbBlockVisibilityGrip.h" #include "DynamicBlocks/DbBlockLookupGrip.h" #include "DynamicBlocks/DbBlockFlipGrip.h" #include "DynamicBlocks/DbBlockArrayAction.h" #include "DynamicBlocks/DbBlockLinearParameter.h" #include "DbDynBlockReference.h" #include "GripPoints.h" #include "DbBlockTableRecord.h" #include "DbTransactionWrapper.h" #include #include "DbSymUtl.h" ODRX_CONS_DEFINE_MEMBERS(OdDbSBAppData, OdRxObject, RXIMPL_CONSTR); static double getGripSize(OdGiViewportDraw* pWd, const OdGePoint3d& eyePt, int gripSize) { OdGePoint2d ptDim; OdGePoint3d wcsPt(eyePt); wcsPt.transformBy(pWd->viewport().getEyeToWorldTransform()); pWd->viewport().getNumPixelsInUnitSquare(wcsPt, ptDim); OdGeVector3d v(gripSize / ptDim.x, 0, 0); v.transformBy(pWd->viewport().getWorldToEyeTransform()); return v.length(); } static void drawFlipArrow(OdGiViewportDraw* pWd, const OdGePoint3d& p, int gripSize, OdDbBlockFlipGrip* grip) { if (auto param = grip->getAssociatedFlipParameter()) { OdGeVector3d v(param->endPoint() - param->basePoint()); if ((grip->flipState() == OdDbBlockFlipParameter::Flipped) != grip->isParameterIsMirrored()) v = -v; v.normalize(); v.rotateBy(OdaPI2, OdGeVector3d::kZAxis); double dGripSize = getGripSize(pWd, p, gripSize); v *= dGripSize; OdGeVector3d n(v); n.rotateBy(OdaPI2, OdGeVector3d::kZAxis); n *= 0.75; OdGePoint3d pp[7]; pp[0] = p + n * 0.5; pp[1] = p + n; pp[3] = p - n; pp[4] = p - n * 0.5; pp[2] = p + v; pp[5] = p - v - n * 0.5;; pp[6] = p - v + n * 0.5;; pWd->geometry().polygonEye(7, pp); } } static void drawTriangle(OdGiViewportDraw* pWd, const OdGePoint3d& p, int gripSize, const OdGeVector3d& orient) { OdGeVector3d v(orient); v.normalize(); double dGripSize = getGripSize(pWd, p, gripSize); v *= dGripSize; OdGePoint3d pp[3]; pp[0] = p + v; v.rotateBy(OdaPI * 0.75, OdGeVector3d::kZAxis); pp[1] = p + v; v.rotateBy(OdaPI2, OdGeVector3d::kZAxis); pp[2] = p + v; pWd->geometry().polygonEye(3, pp); } static void drawSquare(OdGiViewportDraw* pWd, const OdGePoint3d& p, int gripSize) { double dGripSize = getGripSize(pWd, p, gripSize); OdGePoint3d pp[4]; pp[0] = OdGePoint3d(p.x - dGripSize, p.y - dGripSize, p.z); pp[1] = OdGePoint3d(p.x - dGripSize, p.y + dGripSize, p.z); pp[2] = OdGePoint3d(p.x + dGripSize, p.y + dGripSize, p.z); pp[3] = OdGePoint3d(p.x + dGripSize, p.y - dGripSize, p.z); pWd->geometry().polygonEye(4, pp); } static void drawCircle(OdGiViewportDraw* pWd, const OdGePoint3d& p, int gripSize) { double dGripSize = getGripSize(pWd, p, gripSize); OdGeCircArc3d c(p, OdGeVector3d::kZAxis, dGripSize); OdGePoint3dArray pp; c.getSamplePoints(8, pp); pWd->geometry().polygonEye(pp.size(), pp.asArrayPtr()); } static void drawMenuTriangle(OdGiViewportDraw* pWd, const OdGePoint3d& p, int gripSize) { double dGripSize = getGripSize(pWd, p, gripSize); OdGePoint3d pp[4]; pp[0] = OdGePoint3d(p.x - dGripSize, p.y - dGripSize/6, p.z); pp[1] = OdGePoint3d(p.x - dGripSize, p.y + dGripSize/6, p.z); pp[2] = OdGePoint3d(p.x + dGripSize, p.y + dGripSize/6, p.z); pp[3] = OdGePoint3d(p.x + dGripSize, p.y - dGripSize/6, p.z); pWd->geometry().polygonEye(4, pp); OdGePoint3d pp1[3]; pp1[0] = OdGePoint3d(p.x - dGripSize, p.y - 3*dGripSize/6, p.z); pp1[1] = OdGePoint3d(p.x + dGripSize, p.y - 3*dGripSize/6, p.z); pp1[2] = OdGePoint3d(p.x, p.y - dGripSize*2, p.z); pWd->geometry().polygonEye(3, pp1); } static void SbGripViewportDraw(OdDbGripData* pThis, OdGiViewportDraw* pWd, OdDbStub* entId, OdDbGripOperations::DrawType type, OdGePoint3d* imageGripPoint, int gripSize) { ODA_ASSERT(pThis->appDataOdRxClass() == OdDbSBAppData::desc()); OdGePoint3d p = imageGripPoint == 0 ? pThis->gripPoint() : *imageGripPoint; p.transformBy(pWd->viewport().getWorldToEyeTransform()); OdDbSBAppData* pAppData = (OdDbSBAppData*) pThis->appData(); if (!pAppData) return; // CORE-26794 OdDbBlockGripPtr grip = pAppData->m_pGrip; switch (type) { case OdDbGripOperations::kWarmGrip: if (grip.isNull()) pWd->subEntityTraits().setColor(OdCmEntityColor::kACIBlue); else pWd->subEntityTraits().setColor(OdCmEntityColor::kACICyan); break; case OdDbGripOperations::kHoverGrip: pWd->subEntityTraits().setColor(OdCmEntityColor::kACIRed); break; case OdDbGripOperations::kHotGrip: pWd->subEntityTraits().setColor(OdCmEntityColor::kACIMagenta); break; case OdDbGripOperations::kDragImageGrip: pWd->subEntityTraits().setColor(OdCmEntityColor::kACIBlue); break; } pWd->subEntityTraits().setFillType(kOdGiFillAlways); if (grip.isNull()) drawSquare(pWd, p, gripSize); else if (grip->isKindOf(OdDbBlockRotationGrip::desc())) drawCircle(pWd, p, gripSize); else if (auto lg = OdDbBlockLinearGrip::cast(grip)) { if (auto param = OdDbBlock2PtParameter::cast(lg->getAssociatedParameter())) { OdGeVector3d orient = param->updatedEndPoint() - param->updatedBasePoint(); OdDbBlockReferencePtr br = OdDbObjectId(entId).safeOpenObject(); orient.transformBy(br->blockTransform()); orient.transformBy(pWd->viewport().getWorldToEyeTransform()); drawTriangle(pWd, p, gripSize, orient); } } else if (grip->isKindOf(OdDbBlockVisibilityGrip::desc())||grip->isKindOf(OdDbBlockLookupGrip::desc())) drawMenuTriangle(pWd, p, gripSize); else if (grip->isKindOf(OdDbBlockFlipGrip::desc())) drawFlipArrow(pWd, p, gripSize, OdDbBlockFlipGripPtr(grip)); else // polar, XY drawSquare(pWd, p, gripSize); } OdResult updateGraph(OdDbBlockRepresentationContext* ctx, OdDbEvalNodeId id, const OdString& propertyName, const OdDbEvalVariant& value); static OdDbSBAppData* s_currentData = 0; void SbContextMenuItemIndexPtr( unsigned itemIndex ) { if (s_currentData) { if (itemIndex != 0) { OdDbEvalVariant value = s_currentData->m_pParameter->isKindOf(OdDbBlockPropertiesTable::desc()) ? OdDbEvalVariant(OdInt16(itemIndex - 1)) : OdDbEvalVariant(s_currentData->m_sValues[itemIndex - 1]); OdString historyRecordName; if (auto vp = OdDbBlockVisibilityParameter::cast(s_currentData->m_pParameter)) historyRecordName = vp->visibilityName(); else if (auto bpt = OdDbBlockPropertiesTable::cast(s_currentData->m_pParameter)) historyRecordName = bpt->tableName(); else if (auto lp = OdDbBlockLookUpParameter::cast(s_currentData->m_pParameter)) historyRecordName = lp->lookupName(); else { ODA_FAIL_ONCE(); return; } updateGraph(s_currentData->m_pContext, s_currentData->m_pParameter->nodeId(), historyRecordName, value); } } s_currentData = 0; } static ODHMENU createMenu() { #if defined(WINVER) && (WINVER >= 0x0500) && defined(OD_WINDOWS_DESKTOP) return (ODHMENU)::CreatePopupMenu(); #else return nullptr; #endif } static ODHMENU addMenuItem(ODHMENU hm, unsigned id, unsigned position, const OdString& s, bool addSubmenu) { #if defined(WINVER) && (WINVER >= 0x0500) && defined(OD_WINDOWS_DESKTOP) MENUITEMINFO mi = { sizeof(MENUITEMINFO), 0 }; mi.fMask = MIIM_STRING | MIIM_ID; if (addSubmenu) mi.fMask |= MIIM_SUBMENU; mi.wID = id; mi.dwTypeData = (LPTSTR)s.c_str(); mi.cch = s.getLength() + 1; if (addSubmenu) mi.hSubMenu = ::CreatePopupMenu(); ::InsertMenuItem((HMENU)hm, position, TRUE, &mi); return mi.hSubMenu; #else return nullptr; #endif } struct MenuGroup { OdString value; std::vector rows; }; static void groupRowIndices(OdDbBlockPropertiesTable* bpt, int column, const std::vector& input, std::vector& output) { for (int row : input) { OdString value; bpt->getCellValueString(column, row, value); auto i = std::find_if(output.begin(), output.end(), [value](const MenuGroup& m) { return m.value == value; }); if (i == output.end()) output.push_back({ value, {row} }); else i->rows.push_back(row); } } static void addBPTColumnMenu(OdDbBlockPropertiesTable* bpt, ODHMENU menu, int column, const std::vector& input) { if (column >= bpt->numberOfColumns()) return; std::vector groups; groupRowIndices(bpt, column, input, groups); for (unsigned i = 0; i < groups.size(); ++i) { if (auto submemnu = addMenuItem(menu, groups[i].rows[0]+1, i, groups[i].value, groups[i].rows.size() > 1)) addBPTColumnMenu(bpt, submemnu, column + 1, groups[i].rows); } } OdResult SbGripRtClkHandler(OdDbGripDataArray& hotGrips, const OdDbStubPtrArray& /*ents*/, OdString& /*menuName*/, ODHMENU& menu, ContextMenuItemIndexPtr& cb) { for (unsigned i = 0; i < hotGrips.size(); ++i) { ODA_ASSERT(hotGrips[i].appDataOdRxClass() == OdDbSBAppData::desc()); if ((((OdDbSBAppData*)hotGrips[i].appData())->m_pParameter->isKindOf(OdDbBlockPropertiesTable::desc()))) { OdDbBlockPropertiesTablePtr bpt = ((OdDbSBAppData*)hotGrips[i].appData())->m_pParameter; menu = createMenu(); std::vector initialIndices(bpt->numberOfRows()); for (int row = 0; row < bpt->numberOfRows(); ++row) initialIndices[row] = row; addBPTColumnMenu(bpt, menu, 0, initialIndices); s_currentData = (OdDbSBAppData*)hotGrips[i].appData(); cb = SbContextMenuItemIndexPtr; } else { const OdStringArray& values = ((OdDbSBAppData*)hotGrips[i].appData())->m_sValues; if (!values.isEmpty()) { s_currentData = (OdDbSBAppData*)hotGrips[i].appData(); cb = SbContextMenuItemIndexPtr; menu = createMenu(); for (unsigned j = 0; j < values.size(); ++j) addMenuItem(menu, j + 1, j, values[j], false); } } } return eOk; } void SbGripOpStatus(OdDbGripData* pThis, OdDbStub*, OdDbGripOperations::GripStatus status) { if (pThis->appDataOdRxClass() == OdDbSBAppData::desc() && (status == OdDbGripOperations::kGripEnd || status == OdDbGripOperations::kGripAbort)) { ((OdDbSBAppData*)pThis->appData())->release(); pThis->setAppData(0); } } OdResult SbHotGrip(OdDbGripData* pThis, OdDbStub* /*entId*/, int status) { if (status & OdDbGripOperations::kSharedGrip) return eOk; OdDbSBAppData* appdata = (OdDbSBAppData*)pThis->appData(); if (appdata->m_pGrip.isNull() || !appdata->m_pGrip->isKindOf(OdDbBlockFlipGrip::desc())) return eOk; OdDbBlockFlipGripPtr fg = appdata->m_pGrip; auto ctx = appdata->m_pContext; return updateGraph(ctx, fg->nodeId(), L"GRIPFLIP", (OdInt16)(fg->updatedFlipState() == OdDbBlockFlipParameter::NotFlipped ? OdDbBlockFlipParameter::Flipped : OdDbBlockFlipParameter::NotFlipped)); } static bool isActiveGrip(OdDbBlockGrip* p) { if (!p) return false; OdDbBlockParameterPtr param = p->getAssociatedParameter(); if (param.isNull()) return false; if (OdDbBlockVisibilityGrip::cast(p) || OdDbBlockPropertiesTableGrip::cast(p)) return true; if (!param->isMemberOfCurrentVisibilitySet()) return false; if (auto lg = OdDbBlockLookupGrip::cast(p)) return lg->showGrip(); if (param->isKindOf(OdDbBlockConstraintParameter::desc())) // may drive no actions return true; OdDbEvalEdgeInfoArray outEdges; OdDbEvalGraphPtr gr = param->getGraph(); gr->getOutgoingEdges(param->nodeId(), outEdges); bool is2ptParam = param->isKindOf(OdDbBlock2PtParameter::desc()) && !param->isKindOf(OdDbBlockFlipParameter::desc()); for (unsigned int i = 0; i < outEdges.size(); ++i) { OdDbEvalExprPtr ex = gr->getNode(outEdges[i].to()); if (!ex.isNull() && ex->isKindOf(OdDbBlockAction::desc())) { if (is2ptParam) { if (OdDbBlock2PtParameterPtr(param)->parameterComponentHasAction(p->getAssociatedParameterComponent(), (const OdDbBlockAction*)ex.get())) return true; } else return true; } } return false; } bool OdDbDynBlockReferenceGripPointsOverrule::isApplicable(const OdRxObject* pOverruledSubject) const { OdDbBlockReferencePtr br(pOverruledSubject); if (br.isNull()) return false; return OdDbDynBlockReference(br).isDynamicBlock(); } static void getDynamicGrips( OdDbDynBlockReference &dr, OdDbBlockReferencePtr br, OdDbGripDataPtrArray &grips ); OdResult OdDbDynBlockReferenceGripPointsOverrule::getGripPoints( const OdDbEntity* pEntity, OdDbGripDataPtrArray& grips, const double curViewUnitSize, const int gripSize, const OdGeVector3d& curViewDir, const int bitFlags ) { if (auto br = OdDbBlockReference::cast(pEntity)) { OdDbDynBlockReference dinBr = OdDbDynBlockReference(br); getDynamicGrips(dinBr, br, grips); } return OdDbGripOverrule::getGripPoints(pEntity, grips, curViewUnitSize, gripSize, curViewDir, bitFlags); } #undef ODA_NON_TRACING OdResult updateGraph(OdDbBlockRepresentationContext* ctx, OdDbEvalNodeId id, const OdString& propertyName, const OdDbEvalVariant& value) { ctx->createRepresentation(); OdDbEvalGraphPtr gr = ctx->getGraph(); OdResult res = ctx->makeNodeActive(id); if (eOk != res) return res; ctx->setPropertyValue(propertyName, value); return ctx->evaluateActiveNode(); } OdResult OdDbDynBlockReferenceGripPointsOverrule::moveGripPointsAt(OdDbEntity* pEntity, const OdDbVoidPtrArray& grips, const OdGeVector3d& offset, int bitFlags ) { if (grips.size() == 1 && grips[0] != 0 && ((OdRxObject*)grips[0])->isA() == OdDbSBAppData::desc()) { OdDbBlockReferencePtr br(pEntity); OdSmartPtr ctx = OdDbBlockRepresentationContext::getRepresentationContext(br); OdDbBlockGripPtr g = ((OdDbSBAppData*)grips[0])->m_pGrip; OdGeVector3d localOffset(offset); localOffset.transformBy(br->blockTransform().invert()); return updateGraph(ctx, g->nodeId(), L"GRIPLOC", localOffset.asPoint()); } else return OdDbGripOverrule::moveGripPointsAt(pEntity, grips, offset, bitFlags); } static void getDynamicGrips( OdDbDynBlockReference &dr, OdDbBlockReferencePtr br, OdDbGripDataPtrArray &grips ) { OdDbBlockTableRecordPtr dbtr = dr.dynamicBlockTableRecord().safeOpenObject(); OdDbEvalGraphPtr gr = OdDbEvalGraph::getGraph(dbtr, ACAD_ENHANCEDBLOCK); if (gr.isNull()) return; OdSmartPtr repContext = OdDbBlockRepresentationContext::getRepresentationContext(br); if (repContext.isNull()) { repContext = OdRxObjectImpl::createObject(); try { repContext->init(&dr, br, gr); // block may be invalid } catch (const OdError&) { return; } } else { // repContext->loadInstanceData(false) ? repContext->initializeNodes(); } OdGeMatrix3d bt(br->blockTransform()); OdDbEvalNodeIdArray nodes; gr->getAllNodes( nodes ); for ( unsigned i = 0; i < nodes.size(); i++ ) { OdDbBlockGripPtr p = OdDbBlockGrip::cast(repContext->getRepresentationNode(nodes[i])); if (!isActiveGrip(p)) continue; OdDbGripDataPtr data(new OdDbGripData()); OdGePoint3d gripLoc(p->location()); gripLoc.transformBy(bt); data->setGripPoint(gripLoc); data->setViewportDraw(SbGripViewportDraw); data->setGripOpStatFunc(SbGripOpStatus); data->setSkipWhenShared(true); data->disableModeKeywords(true); data->setAppDataOdRxClass(OdDbSBAppData::desc()); OdSmartPtr appData(OdRxObjectImpl::createObject()); appData->m_pGrip = p; appData->m_pContext = repContext.get(); if (p->isKindOf(OdDbBlockVisibilityGrip::desc()) || p->isKindOf(OdDbBlockLookupGrip::desc()) || p->isKindOf(OdDbBlockPropertiesTableGrip::desc())) { data->disableRubberBandLine(true); data->setMapGripHotToRtClk(true); data->setRtClk(SbGripRtClkHandler); OdDbEvalEdgeInfoArray outNodes; if (auto param = p->getAssociatedParameter()) { appData->m_pParameter = repContext->getRepresentationNode(param->nodeId()); if (!param->isKindOf(OdDbBlockPropertiesTable::desc())) { OdDbBlkParamPropertyDescriptorArray props; param->getPropertyDescription(props); for (unsigned j = 0; j < props[0].m_pAllowedValues.size(); ++j) { appData->m_sValues.append(props[0].m_pAllowedValues[j].getString()); } if (p->isKindOf(OdDbBlockLookupGrip::desc())) appData->m_sPropertyName = OdDbBlockLookUpParameterPtr(appData->m_pParameter)->lookupName(); else appData->m_sPropertyName = OdDbBlockVisibilityParameterPtr(appData->m_pParameter)->visibilityName(); OdString current = appData->m_pParameter->getPropertyValue(appData->m_sPropertyName).getString(); if (!appData->m_sValues.contains(current)) appData->m_sValues.append(current); } else appData->m_sPropertyName = L"currentRow"; } if (appData->m_sValues.size() < 2 && !p->isKindOf(OdDbBlockPropertiesTableGrip::desc())) continue; } else if (p->isKindOf(OdDbBlockFlipGrip::desc())) { data->setHotGripFunc(SbHotGrip); } data->setAppData(appData.detach()); grips.append(data); } }