#include "StdAfx.h" #include "ExDynamicBlocksModule.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern OdInt16 g_nBLOCKEDITOR; namespace { const OdConstString ACAD_ENHANCEDBLOCK(L"ACAD_ENHANCEDBLOCK"); const OdConstString ACDBDYNAMICBLOCKTRUENAME(L"AcDbDynamicBlockTrueName"); const OdConstString ACDBDYNAMICBLOCKGUID(L"AcDbDynamicBlockGUID"); const OdConstString ACADAUTHENVIRON(L"ACADAUTHENVIRON"); class BlockTableRecordCloner : public OdStaticRxObject { OdDbObjectId m_srcBlockId; public: BlockTableRecordCloner(OdDbObjectId srcBlockId) : m_srcBlockId(srcBlockId) { ::odrxEvent()->addReactor(this); } ~BlockTableRecordCloner() { ::odrxEvent()->removeReactor(this); } void beginDeepCloneXlation(OdDbIdMapping& idMap) override { OdDbIdPair pr(m_srcBlockId); if (idMap.compute(pr)) OdDbBlockTableRecordPtr(pr.value().safeOpenObject(OdDb::kForWrite))->setName(L"*U"); } }; OdDbBlockTableRecordPtr createBlockRepresentation(OdDbObjectId blockId) { OdDbIdMappingPtr im = OdDbIdMapping::createObject(); BlockTableRecordCloner btrc(blockId); // clone the original dynamic block along with all the xdata and xdictionary contents blockId.database()->deepCloneObjects({ blockId }, blockId.database()->getBlockTableId(), *im); OdDbIdPair ip(blockId); im->compute(ip); OdDbBlockTableRecordPtr btr = ip.value().safeOpenObject(OdDb::kForWrite); // for the nodes of the evaluation graph - create representation entities if (auto g = OdDbEvalGraph::getGraph(btr, ACAD_ENHANCEDBLOCK)) { OdDbEvalNodeIdArray nodes; g->getAllNodes(nodes); for (auto n : nodes) { if (auto be = OdDbBlockElement::cast(g->getNode(n))) { if (auto ent = OdDbBlockElementEntity::create(be)) btr->appendOdDbEntity(ent); } } } if (auto xd = OdDbDictionary::cast(btr->extensionDictionary().openObject(OdDb::kForWrite))) { if (auto preventerId = xd->getAt(L"AcDbDynamicBlockRoundtripPurgePreventer")) preventerId.safeOpenObject(OdDb::kForWrite)->erase(); } return btr; } void setDynamicBlockXdata(OdDbDatabase* db, OdDbBlockTableRecord* ms, OdDbBlockTableRecord* repBlock, const OdString& blockName) { db->newRegApp(ACDBDYNAMICBLOCKTRUENAME); auto nameXdata = repBlock->xData(ACDBDYNAMICBLOCKTRUENAME); if (!nameXdata) { nameXdata = OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACDBDYNAMICBLOCKTRUENAME); nameXdata->setNext(OdResBuf::newRb(OdResBuf::kDxfXdAsciiString, blockName)); } ms->setXData(nameXdata); db->newRegApp(ACDBDYNAMICBLOCKGUID); auto guidXdata = repBlock->xData(ACDBDYNAMICBLOCKGUID); if (!guidXdata) { guidXdata = OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACDBDYNAMICBLOCKGUID); guidXdata->setNext(OdResBuf::newRb(OdResBuf::kDxfXdAsciiString, ::odrxSystemServices()->createGuid())); } ms->setXData(guidXdata); // save the handle of the block that will hold modelspace contents during BEDIT db->newRegApp(ACADAUTHENVIRON); auto authXdata = OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACADAUTHENVIRON); authXdata->setNext(OdResBuf::newRb(OdResBuf::kDxfXdHandle, repBlock->objectId().getHandle().ascii())); ms->setXData(authXdata); } void clearDynamicBlockXdata(OdDbBlockTableRecord* ms) { ms->setXData(OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACDBDYNAMICBLOCKTRUENAME)); ms->setXData(OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACDBDYNAMICBLOCKGUID)); ms->setXData(OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACADAUTHENVIRON)); } void swapBlockContents(OdDbBlockTableRecord* ms, OdDbBlockTableRecord* repBlock) { OdDbObjectIdArray repBlockContents(repBlock->ids().begin(), repBlock->ids().end()); OdDbObjectIdArray msContents(ms->ids().begin(), ms->ids().end()); repBlock->assumeOwnershipOf(msContents); ms->assumeOwnershipOf(repBlockContents); } // move the evaluation graph from the clone to the model space void moveEvalGraph(OdDbBlockTableRecord* ms, OdDbBlockTableRecord* repBlock) { if (auto g = OdDbEvalGraph::getGraph(repBlock, ACAD_ENHANCEDBLOCK, OdDb::kForWrite)) { ms->createExtensionDictionary(); OdDbDictionaryPtr xd = ms->extensionDictionary().safeOpenObject(OdDb::kForWrite); xd->setAt(ACAD_ENHANCEDBLOCK, g); OdDbEvalGraph::removeGraph(repBlock, ACAD_ENHANCEDBLOCK); } else { OdDbEvalGraph::createGraph(ms, ACAD_ENHANCEDBLOCK); } } // add roundtrip preventer (otherwise old versions may purge that block, because it may have no references (only representations)) void addRoundtripPurgePrevernter(OdDbBlockTableRecord* block) { block->createExtensionDictionary(); OdDbDictionaryPtr xd = block->extensionDictionary().safeOpenObject(OdDb::kForWrite); xd->setAt(L"AcDbDynamicBlockRoundtripPurgePreventer", OdDbDynamicBlockPurgePreventer::createObject()); // also clear ACADAUTHENVIRON, because the block will persist block->setXData(OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACADAUTHENVIRON)); } void zoomExtents(OdDbDatabase* pDb) { OdDbObjectPtr pVpObj = pDb->activeViewportId().safeOpenObject(OdDb::kForWrite); OdDbAbstractViewportDataPtr pAVD(pVpObj); OdGsView* pView = pAVD->gsView(pVpObj); OdAbstractViewPEPtr pVpPE(pView); if (!pVpPE) return; OdGeBoundBlock3d bbox; if (!pVpPE->viewExtents(pView, bbox)) { if (pDb->getMEASUREMENT() == OdDb::kMetric) bbox.set(OdGePoint3d::kOrigin, OdGePoint3d(297., 210., 0.)); // ISO A4 (portrait) else bbox.set(OdGePoint3d::kOrigin, OdGePoint3d(11., 8.5, 0.)); // ANSI A (8.50 x 11.00) (landscape) bbox.transformBy(pView->viewingMatrix()); } pVpPE->zoomExtents(pView, &bbox); } typedef void (OdDbBlockElement::* Callback)(OdDbObjectId entityId, OdDbObjectId blockId); static void callReactors(OdDbBlockTableRecord* ms, Callback cb) { auto g = OdDbEvalGraph::getGraph(ms, ACAD_ENHANCEDBLOCK); OdDbEvalNodeIdArray nodes; g->getAllNodes(nodes); for (auto id: nodes) { if (auto be = OdDbBlockElement::cast(g->getNode(id, OdDb::kForWrite))) (be->*cb)(be->getEntity(), ms->objectId()); } } OdString getSelectedBlock(OdSelectionSet* ss) { for (auto i = ss->newIterator(); !i->done(); i->next()) { if (auto br = OdDbBlockReference::cast(OdDbObjectId(i->id()).openObject())) { OdDbBlockTableRecordPtr block = br->blockTableRecord().openObject(); if (block->isAnonymous()) { OdDbDynBlockReference dr(br); if (dr.isDynamicBlock()) return OdDbBlockTableRecordPtr(dr.dynamicBlockTableRecord().openObject())->getName(); } else return block->getName(); } } return OdString::kEmpty; } } void EXBEDITCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbDatabase* db = pDbCmdCtx->database(); OdDbBlockTableRecordPtr ms = db->getModelSpaceId().safeOpenObject(OdDb::kForWrite); if (g_nBLOCKEDITOR > 0) { pCmdCtx->userIO()->putString(L"Already in BEDIT"); return; } OdString blockName = getSelectedBlock(pDbCmdCtx->dbUserIO()->pickfirst()); if (blockName.isEmpty()) blockName = pCmdCtx->userIO()->getString(L"Enter block name:", OdEd::kGstNoEmpty); OdDbBlockTablePtr bt = db->getBlockTableId().safeOpenObject(OdDb::kForWrite); auto blockId = bt->getAt(blockName); if (!blockId) // add new one, if there is no such a block { auto newBlock = OdDbBlockTableRecord::createObject(); newBlock->setName(blockName); blockId = bt->add(newBlock); // mark the new block with ACADAUTHENVIRON to erase it later if the changes are discarded db->newRegApp(ACADAUTHENVIRON); auto authXdata = OdResBuf::newRb(OdResBuf::kDxfRegAppName, ACADAUTHENVIRON); authXdata->setNext(OdResBuf::newRb(OdResBuf::kDxfXdHandle, L"0")); newBlock->setXData(authXdata); } if (blockId.originalDatabase() != db) { pCmdCtx->userIO()->putString(L"Can't edit xref blocks"); return; } auto repBlock = createBlockRepresentation(blockId); swapBlockContents(ms, repBlock); moveEvalGraph(ms, repBlock); setDynamicBlockXdata(db, ms, repBlock, blockName); zoomExtents(db); g_nBLOCKEDITOR = 1; callReactors(ms, &OdDbBlockElement::onBeginEditEnded); } void EXBCLOSECmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbDatabase* db = pDbCmdCtx->database(); db->newRegApp(ACADAUTHENVIRON); OdDbBlockTableRecordPtr ms = db->getModelSpaceId().safeOpenObject(OdDb::kForWrite); if (g_nBLOCKEDITOR == 0) { pCmdCtx->userIO()->putString(L"Block edit session not found"); return; } auto nameXdata = ms->xData(ACDBDYNAMICBLOCKTRUENAME); OdString blockName = nameXdata->next()->getString(); OdDbBlockTablePtr bt = db->getBlockTableId().safeOpenObject(OdDb::kForWrite); auto blockId = bt->getAt(blockName); OdDbBlockTableRecordPtr block = blockId.safeOpenObject(OdDb::kForWrite); bool isNewBlock = !block->xData(ACADAUTHENVIRON).isNull(); if (0 == pCmdCtx->userIO()->getKeyword(L"Save the changes? [Yes/No] ", "Yes No")) { OdDbObjectIdArray ids; for (auto e : ms->entities()) { if (auto bee = OdDbBlockElementEntity::cast(e)) { OdDbBlockElementPtr be = bee->getElement().safeOpenObject(OdDb::kForWrite); bee->sync(be); bee->upgradeOpen(); bee->setElement(OdDbObjectId::kNull); bee->erase(); } else ids.append(e->objectId()); } callReactors(ms, &OdDbBlockElement::onBeginSaveStarted); for (auto e : block->entities()) { e->upgradeOpen(); e->erase(); } block->assumeOwnershipOf(ids); moveEvalGraph(block, ms); if (isNewBlock) addRoundtripPurgePrevernter(block); OdDbBlockRepresentationContext::tagEntitiesInBlock(blockId); // in this mode onBeginSaveEnded is not called because we don't continue editing after BCLOSE // callReactors(ms, &OdDbBlockElement::onBeginSaveEnded); } else { for (auto e : ms->entities()) { e->upgradeOpen(); e->erase(); } if (isNewBlock) block->erase(); } // restore model space auto authXdata = ms->xData(ACADAUTHENVIRON); OdDbBlockTableRecordPtr repBlock = db->getOdDbObjectId(authXdata->next()->getHandle()).safeOpenObject(OdDb::kForWrite); ms->assumeOwnershipOf({ repBlock->ids().begin(), repBlock->ids().end() }); clearDynamicBlockXdata(ms); repBlock->erase(); zoomExtents(db); g_nBLOCKEDITOR = 0; } enum class ParameterType { Alignment = 0, Base, Point, Linear, Polar, Xy, Rotation, Flip, Visibility, Lookup }; static OdString generateName(OdDbEvalGraph* g, OdString prefix) { if (OdDbBlockElement::isNameUnique(g, prefix + L"1", &prefix)) return prefix + L"1"; else return prefix; } static OdString generateLabel(OdDbEvalGraph* g, OdString prefix) { if (OdDbBlockParameter::isPropertyLabelUnique(g, prefix + L"1", &prefix)) return prefix + L"1"; else return prefix; } enum class PointParameterKeyword { Name = 0, Label, Chain, Description, Palette }; enum class LinearParameterKeyword { Name = 0, Label, Chain, Description, Base, Palette, Value_set }; enum class ValueSetType { None = 0, List, Increment }; OdGeDoubleArray parseValueList(const OdString& input) { std::string s = (const char*)input; static std::regex wscomma("\\s*\\,\\s*", std::regex::nosubs | std::regex::optimize); OdGeDoubleArray dd; auto endToken = std::sregex_token_iterator(); for (auto it = std::sregex_token_iterator(s.begin(), s.end(), wscomma, -1); it != endToken; ++it) { dd.push_back(odStrToD(std::string(*it).c_str())); } return dd; } OdDbBlockParamValueSetPtr handleValueSet(OdDbUserIO* pIO, OdDbBlockParamValueSet* vs, const wchar_t* name) { int oldType = 0; OdString prompt;prompt.format(L"Enter %ls value list type: [None/List/Increment] <", name); if (vs->useIncrement()) { prompt += L"Increment>:"; oldType = 2; } else if (vs->useValueList()) { prompt += L"List>:"; oldType = 1; } else prompt += L"None>:"; auto newVs = OdDbBlockParamValueSet::createObject(); switch ((ValueSetType)pIO->getKeyword(prompt, L"None List Increment", oldType)) { case ValueSetType::None: return newVs; case ValueSetType::List: try { prompt.format(L"Enter %ls value list (comma separated):", name); newVs->setValueList(parseValueList(pIO->getString(prompt, OdEd::kGstAllowSpaces)), true); return newVs; } catch (const OdError&) { } break; case ValueSetType::Increment: try { newVs->setIncrement(pIO->getReal(L"Enter increment value:"), true); newVs->setMaximum(pIO->getReal(L"Enter upper bound:"), true); newVs->setMinimum(pIO->getReal(L"Enter lower bound:"), true); return newVs; } catch (const OdError&) { } break; } return OdDbBlockParamValueSetPtr(); } void handleShowProperties(OdDbBlockParameter* lp, OdDbUserIO* pIO) { OdString prompt; prompt.format(L"Show parameter in property palette [Yes/No] <%ls>:", lp->showProperties() ? L"Yes" : L"No"); auto show = pIO->getKeyword(prompt, L"Yes No", lp->showProperties() ? 0 : 1); if (show == 0) lp->setShowProperties(true); else if (show == 1) lp->setShowProperties(false); } typedef bool (*PromptFunc)(OdDbBlockParameter* lp, OdDbUserIO* pIO, OdDbEvalGraph* g); bool promptForParameterName(OdDbBlockParameter* lp, OdDbUserIO* pIO, OdDbEvalGraph* g, PromptFunc pf) { OdString prompt; prompt.format(L"Enter parameter name: <%ls>", lp->name().c_str()); try { OdString newname = pIO->getString(prompt, OdEd::kGstAllowSpaces); if (!newname.isEmpty() && OdDbBlockElement::isNameUnique(g, newname)) lp->setName(newname); } catch (const OdError&) { } return pf(lp, pIO, g); } bool promptForParameterChain(OdDbBlockParameter* lp, OdDbUserIO* pIO, OdDbEvalGraph* g, PromptFunc pf) { OdString prompt; prompt.format(L"Evaluate associated actions when parameter is edited by another action [Yes/No] <%ls>:", lp->chainActions() ? L"Yes" : L"No"); auto chain = pIO->getKeyword(prompt, L"Yes No", lp->chainActions() ? 0 : 1); if (chain == 0) lp->setChainActions(true); else if (chain == 1) lp->setChainActions(false); return pf(lp, pIO, g); } bool promptForParameterLabel(OdDbBlockParameter* lp, OdDbUserIO* pIO, OdDbEvalGraph* g, const OdString& labelName, PromptFunc pf, const OdString& labelName2 = OdString::kEmpty) { OdString prompt; auto pi = OdDbParameterInterface::parameterInterface(lp, labelName, false); prompt.format(L"Enter %ls label: <%ls>", labelName.c_str(), pi->getName().c_str()); try { OdString newname = pIO->getString(prompt, OdEd::kGstAllowSpaces); if (!newname.isEmpty() && OdDbBlockParameter::isPropertyLabelUnique(g, newname)) pi->setName(newname); } catch (const OdError&) { } if (!labelName2.isEmpty()) { pi = OdDbParameterInterface::parameterInterface(lp, labelName2, false); prompt.format(L"Enter %ls label: <%ls>", labelName2.c_str(), pi->getName().c_str()); try { OdString newname = pIO->getString(prompt, OdEd::kGstAllowSpaces); if (!newname.isEmpty() && OdDbBlockParameter::isPropertyLabelUnique(g, newname)) pi->setName(newname); } catch (const OdError&) { } } return pf(lp, pIO, g); } bool promptForParameterDescription(OdDbBlockParameter* lp, OdDbUserIO* pIO, OdDbEvalGraph* g, const OdString& labelName, PromptFunc pf, const OdString& labelName2 = OdString::kEmpty) { OdString prompt; auto pi = OdDbParameterInterface::parameterInterface(lp, labelName, false); prompt.format(L"Enter %ls description: <%ls>", labelName.c_str(), pi->getDescription().c_str()); try { pi->setDescription(pIO->getString(prompt, OdEd::kGstAllowSpaces)); } catch (const OdError&) { } if (!labelName2.isEmpty()) { pi = OdDbParameterInterface::parameterInterface(lp, labelName2, false); prompt.format(L"Enter %ls description: <%ls>", labelName2.c_str(), pi->getDescription().c_str()); try { pi->setDescription(pIO->getString(prompt, OdEd::kGstAllowSpaces)); } catch (OdError&) { return false; } } return pf(lp, pIO, g); } struct RadiusTracker : OdStaticRxObject { OdDbCirclePtr m_pCircle; RadiusTracker(OdDbDatabase* db, OdGePoint3d center) { m_pCircle = OdDbCircle::createObject(); m_pCircle->setDatabaseDefaults(db); m_pCircle->setCenter(center); } void setValue(double dRadius) override { m_pCircle->setRadius(dRadius); } int addDrawables(OdGsView* pView) override { pView->add(m_pCircle, 0); return 1; } void removeDrawables(OdGsView* pView) override { pView->erase(m_pCircle); } }; template struct LabelOffsetTracker : OdStaticRxObject { Entity* m_pEntity; LabelOffsetTracker(Entity* e) : m_pEntity(e) {} void setValue(double d) override { m_pEntity->setOffset(d); } int addDrawables(OdGsView* pView) override { pView->add(m_pEntity, 0); return 1; } void removeDrawables(OdGsView* pView) override { pView->erase(m_pEntity); } }; struct AngleTracker : OdStaticRxObject { OdDbBlockRotationParameterEntity* m_pEntity; AngleTracker(OdDbBlockRotationParameterEntity* e) : m_pEntity(e) {} void setValue(double d) override { m_pEntity->setDefinitionEndPoint(m_pEntity->definitionBaseAnglePoint().rotateBy(d, OdGeVector3d::kZAxis, m_pEntity->definitionBasePoint())); } int addDrawables(OdGsView* pView) override { pView->add(m_pEntity, 0); return 1; } void removeDrawables(OdGsView* pView) override { pView->erase(m_pEntity); } }; bool promptForRotationPoints(OdDbBlockRotationParameter* p, OdDbUserIO* pIO, OdDbDatabase* pDb) { OdGePoint3d startPoint = pIO->getPoint("Specify base point or [Name/Label/Chain/Description/Palette/Value_set]", 0, nullptr, L"Name Label Chain Description Palette Value_set"); p->setDefinitionBasePoint(startPoint); try { RadiusTracker tracker(pDb, startPoint); double d = pIO->getDist(L"Specify radius of parameter:", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand | OdEd::kGdsNoZero, 0.0, OdString::kEmpty, &tracker); p->setDefinitionEndPoint(startPoint + OdGeVector3d::kXAxis * d); p->setDefinitionBaseAnglePoint(startPoint + OdGeVector3d::kXAxis * d); OdDbBlockRotationParameterEntityPtr bee = OdDbBlockParameterEntity::create(p); AngleTracker angleTracker(bee); pIO->setLASTPOINT(startPoint); pIO->getAngle(L"Enter default rotation angle", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand, 0.0, OdString::kEmpty, &angleTracker); LabelOffsetTracker offsetTracker(bee); pIO->setLASTPOINT(bee->definitionEndPoint()); pIO->getDist(L"Enter label offset:", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand, 0.0, OdString::kEmpty, &offsetTracker); bee->sync(p); } catch (OdError&) { return false; } return true; } bool promptForPoints(OdDbBlockPolarParameter* p, OdDbUserIO* pIO) { OdGePoint3d startPoint = pIO->getPoint("Specify start point or [Name/Label/Chain/Description/Palette/Value_set]", 0, nullptr, L"Name Label Chain Description Palette Value_set"); p->setDefinitionBasePoint(startPoint); try { p->setDefinitionEndPoint(pIO->getPoint(L"Specify end point", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand | OdEd::kGdsNoZero, &startPoint)); OdDbBlockPolarParameterEntityPtr bee = OdDbBlockParameterEntity::create(p); LabelOffsetTracker tracker(bee); p->setOffset(pIO->getDist(L"Enter label offset:", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand, 0.0, OdString::kEmpty, &tracker)); } catch (OdError&) { return false; } return true; } bool promptForPoints(OdDbBlockLinearParameter* p, OdDbUserIO* pIO) { OdGePoint3d startPoint = pIO->getPoint("Specify start point or [Name/Label/Chain/Description/Base/Palette/Value_set]", 0, nullptr, L"Name Label Chain Description Base Palette Value_set"); p->setDefinitionBasePoint(startPoint); try { p->setDefinitionEndPoint(pIO->getPoint(L"Specify end point", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand | OdEd::kGdsNoZero, &startPoint)); OdDbBlockLinearParameterEntityPtr bee = OdDbBlockParameterEntity::create(p); LabelOffsetTracker tracker(bee); p->setOffset(pIO->getDist(L"Enter label offset:", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand, 0.0, OdString::kEmpty, &tracker)); } catch (OdError&) { return false; } return true; } bool promptForLinearValues(OdDbBlockParameter* p, OdDbUserIO* pIO, OdDbEvalGraph* g) { OdDbBlockLinearParameterPtr lp = p; try { if (!promptForPoints(lp, pIO)) return false; } catch (const OdEdKeyword& kwd) { switch ((LinearParameterKeyword)kwd.keywordIndex()) { case LinearParameterKeyword::Name: return promptForParameterName(p, pIO, g, promptForLinearValues); case LinearParameterKeyword::Label: return promptForParameterLabel(p, pIO, g, lp->distanceName(), promptForLinearValues); case LinearParameterKeyword::Chain: return promptForParameterChain(p, pIO, g, promptForLinearValues); case LinearParameterKeyword::Description: return promptForParameterDescription(lp, pIO, g, lp->distanceName(), promptForLinearValues); case LinearParameterKeyword::Base: { OdString prompt; prompt.format(L"Set parameter starting point [Start/Mid] <%ls>:", lp->baseLocation() == OdDbBlock2PtParameter::start ? L"Start" : L"Mid"); auto base = pIO->getKeyword(prompt, L"Start Mid", (int)lp->baseLocation()); if (base == OdDbBlock2PtParameter::start) lp->setBaseLocation(OdDbBlock2PtParameter::start); else if (base == OdDbBlock2PtParameter::mid) lp->setBaseLocation(OdDbBlock2PtParameter::mid); return promptForLinearValues(lp, pIO, g); } case LinearParameterKeyword::Palette: handleShowProperties(lp, pIO); return promptForLinearValues(lp, pIO, g); case LinearParameterKeyword::Value_set: if (auto newVS = handleValueSet(pIO, lp->valueSet(), L"distance")) lp->setValueSet(*newVS); return promptForLinearValues(lp, pIO, g); } } catch (const OdError&) { return false; } return true; } enum class PolarParameterKeyword { Name = 0, Label, Chain, Description, Palette, Value_set }; bool promptForPolarValues(OdDbBlockParameter* p, OdDbUserIO* pIO, OdDbEvalGraph* g) { OdDbBlockPolarParameterPtr pp = p; try { if (!promptForPoints(pp, pIO)) return false; } catch (const OdEdKeyword& kwd) { switch ((PolarParameterKeyword)kwd.keywordIndex()) { case PolarParameterKeyword::Name: return promptForParameterName(p, pIO, g, promptForPolarValues); case PolarParameterKeyword::Label: return promptForParameterLabel(p, pIO, g, pp->distanceName(), promptForPolarValues, pp->angleName()); case PolarParameterKeyword::Chain: return promptForParameterChain(p, pIO, g, promptForPolarValues); case PolarParameterKeyword::Description: return promptForParameterDescription(pp, pIO, g, pp->distanceName(), promptForPolarValues, pp->angleName()); case PolarParameterKeyword::Palette: handleShowProperties(pp, pIO); return promptForPolarValues(pp, pIO, g); case PolarParameterKeyword::Value_set: if (auto newVS = handleValueSet(pIO, pp->distanceValueSet(), L"distance")) pp->setDistanceValueSet(*newVS); if (auto newVS = handleValueSet(pIO, pp->angleValueSet(), L"angle")) pp->setAngleValueSet(*newVS); return promptForPolarValues(pp, pIO, g); } } catch (const OdError&) { return false; } return true; } bool propmptForPositionValues(OdDbBlockParameter* p, OdDbUserIO* pIO, OdDbEvalGraph* g) { OdDbBlockPointParameterPtr pp = p; try { pp->setDefinitionPoint(pIO->getPoint("Specify parameter position or [Name/Label/Chain/Description/Palette]", 0, nullptr, L"Name Label Chain Description Palette")); } catch (const OdEdKeyword& kwd) { switch ((PointParameterKeyword)kwd.keywordIndex()) { case PointParameterKeyword::Name: return promptForParameterName(p, pIO, g, propmptForPositionValues); case PointParameterKeyword::Label: return promptForParameterLabel(p, pIO, g, pp->positionName() + L" X", propmptForPositionValues); case PointParameterKeyword::Chain: return promptForParameterChain(p, pIO, g, propmptForPositionValues); case PointParameterKeyword::Description: return promptForParameterDescription(p, pIO, g, pp->positionName() + L" X", propmptForPositionValues); case PointParameterKeyword::Palette: handleShowProperties(p, pIO); return propmptForPositionValues(p, pIO, g); } } catch (const OdError&) { return false; } return true; } bool promptForRotationValues(OdDbBlockParameter* p, OdDbUserIO* pIO, OdDbEvalGraph* g) { OdDbBlockRotationParameterPtr rp = p; try { if (!promptForRotationPoints(rp, pIO, g->database())) return false; } catch (const OdEdKeyword& kwd) { switch ((PolarParameterKeyword)kwd.keywordIndex()) { case PolarParameterKeyword::Name: return promptForParameterName(p, pIO, g, promptForRotationValues); case PolarParameterKeyword::Label: return promptForParameterLabel(p, pIO, g, rp->angleName(), promptForRotationValues); case PolarParameterKeyword::Chain: return promptForParameterChain(p, pIO, g, promptForRotationValues); case PolarParameterKeyword::Description: return promptForParameterDescription(rp, pIO, g, rp->angleName(), promptForRotationValues); case PolarParameterKeyword::Palette: handleShowProperties(rp, pIO); return promptForRotationValues(rp, pIO, g); case PolarParameterKeyword::Value_set: if (auto newVS = handleValueSet(pIO, rp->valueSet(), L"angle")) rp->setValueSet(*newVS); return promptForRotationValues(rp, pIO, g); } } catch (const OdError&) { return false; } return true; } static void addEntity(OdDbBlock2PtParameter* p, OdDbBlockTableRecord* ms, int numberOfGrips = 2) { auto bee = OdDbBlockElementEntity::create(p); bee->setDatabaseDefaults(ms->database()); ms->appendOdDbEntity(bee); p->setNumberOfGrips(numberOfGrips); if (auto grip = p->getAssociatedGrip(OdDbBlockParameter::Base)) { auto gripEntity = OdDbBlockElementEntity::create(grip); gripEntity->setDatabaseDefaults(ms->database()); ms->appendOdDbEntity(gripEntity); } if (auto grip = p->getAssociatedGrip(OdDbBlockParameter::End)) { auto gripEntity = OdDbBlockElementEntity::create(grip); gripEntity->setDatabaseDefaults(ms->database()); ms->appendOdDbEntity(gripEntity); } } static void addEntity(OdDbBlock1PtParameter* p, OdDbBlockTableRecord* ms) { auto bee = OdDbBlockElementEntity::create(p); bee->setDatabaseDefaults(ms->database()); ms->appendOdDbEntity(bee); p->setNumberOfGrips(1); if (auto grip = p->getAssociatedGrip()) { auto gripEntity = OdDbBlockElementEntity::create(grip); gripEntity->setDatabaseDefaults(ms->database()); ms->appendOdDbEntity(gripEntity); } } enum class FlipParameterKeyword { Name = 0, Label, Description, Palette }; struct LabelPositionTracker : OdStaticRxObject { OdDbBlockFlipParameterEntity* m_pEntity; LabelPositionTracker(OdDbBlockFlipParameterEntity* e) : m_pEntity(e) {} void setValue(const OdGePoint3d& p) override { m_pEntity->setDefinitionLabelPoint(p); } int addDrawables(OdGsView* pView) override { pView->add(m_pEntity, 0); return 1; } void removeDrawables(OdGsView* pView) override { pView->erase(m_pEntity); } }; bool promptForFlipPoints(OdDbBlockFlipParameter* p, OdDbUserIO* pIO) { OdGePoint3d startPoint = pIO->getPoint("Specify start point or [Name/Label/Description/Palette]", 0, nullptr, L"Name Label Description Palette"); p->setDefinitionBasePoint(startPoint); try { p->setDefinitionEndPoint(pIO->getPoint(L"Specify end point", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand | OdEd::kGdsNoZero, &startPoint)); OdDbBlockFlipParameterEntityPtr bee = OdDbBlockFlipParameterEntity::create(p); LabelPositionTracker tracker(bee); OdGePoint3d d = startPoint + ((p->definitionEndPoint() - startPoint) / 2.0); pIO->setLASTPOINT(d); p->setDefinitionLabelPoint(pIO->getPoint(L"Enter label position:", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand, &d, OdString::kEmpty, &tracker)); } catch (OdError&) { return false; } return true; } bool promptForFlipValues(OdDbBlockParameter* p, OdDbUserIO* pIO, OdDbEvalGraph* g) { OdDbBlockFlipParameterPtr fp = p; try { if (!promptForFlipPoints(fp, pIO)) return false; } catch (const OdEdKeyword& kwd) { switch ((FlipParameterKeyword)kwd.keywordIndex()) { case FlipParameterKeyword::Name: return promptForParameterName(p, pIO, g, promptForFlipValues); case FlipParameterKeyword::Label: return promptForParameterLabel(p, pIO, g, fp->flipLabel(), promptForFlipValues); case FlipParameterKeyword::Description: return promptForParameterDescription(fp, pIO, g, fp->flipLabel(), promptForFlipValues); case FlipParameterKeyword::Palette: handleShowProperties(p, pIO); return promptForFlipValues(p, pIO, g); } } catch (const OdError&) { return false; } return true; } bool promptForVisibilityValues(OdDbBlockParameter* p, OdDbUserIO* pIO, OdDbEvalGraph* g) { OdDbBlockVisibilityParameterPtr vp = p; try { OdGePoint3d defPoint = pIO->getPoint("Specify start point or [Name/Label/Description/Palette]", 0, nullptr, L"Name Label Description Palette"); vp->setDefinitionPoint(defPoint); } catch (const OdEdKeyword& kwd) { using VisibilityParameterKeyword = FlipParameterKeyword; switch ((VisibilityParameterKeyword)kwd.keywordIndex()) { case VisibilityParameterKeyword::Name: return promptForParameterName(p, pIO, g, promptForVisibilityValues); case VisibilityParameterKeyword::Label: return promptForParameterLabel(p, pIO, g, vp->visibilityName(), promptForVisibilityValues); case VisibilityParameterKeyword::Description: return promptForParameterDescription(vp, pIO, g, vp->visibilityName(), promptForVisibilityValues); case VisibilityParameterKeyword::Palette: handleShowProperties(p, pIO); return promptForVisibilityValues(p, pIO, g); } } catch (const OdError&) { return false; } return true; } bool promptForAlignmentPoints(OdDbBlockAlignmentParameter* p, OdDbUserIO* pIO) { OdGePoint3d startPoint = pIO->getPoint("Specify start point or [Name]", 0, nullptr, L"Name"); p->setDefinitionBasePoint(startPoint); try { p->setDefinitionEndPoint(pIO->getPoint(L"Specify end point", OdEd::kGanFromLastPoint | OdEd::kGptRubberBand | OdEd::kGdsNoZero, &startPoint)); } catch (OdError&) { return false; } return true; } bool promptForAlignmentValues(OdDbBlockParameter* p, OdDbUserIO* pIO, OdDbEvalGraph* g) { OdDbBlockAlignmentParameterPtr ap = p; try { if (!promptForAlignmentPoints(ap, pIO)) return false; } catch (const OdEdKeyword&) { return promptForParameterName(p, pIO, g, promptForAlignmentValues); } catch (const OdError&) { return false; } return true; } template OdSmartPtr findParameter(OdDbEvalGraph* g) { OdDbEvalNodeIdArray nodes; g->getAllNodes(nodes); for (auto id : nodes) { if (auto p = T::cast(g->getNode(id))) return p; } return OdSmartPtr(); } void collectBlockEntites(OdDbBlockTableRecord* ms, OdDbObjectIdArray& ents, OdDbObjectIdArray& nodes) { for (auto e : ms->entities()) { if (auto element = OdDbBlockElementEntity::cast(e)) { if (auto node = OdDbBlockElement::cast(element->getElement().openObject())) { if (!node->isKindOf(OdDbBlockAction::desc()) && !node->isKindOf(OdDbBlockVisibilityParameter::desc()) && !node->isKindOf(OdDbBlockVisibilityGrip::desc())) nodes.append(node->objectId()); } } else ents.append(e->objectId()); } } void EXBPARAMETERCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIOPtr pIO = pDbCmdCtx->userIO(); OdDbDatabase* db = pDbCmdCtx->database(); OdDbBlockTableRecordPtr ms = db->getModelSpaceId().safeOpenObject(OdDb::kForWrite); if (g_nBLOCKEDITOR == 0) { pIO->putString(L"Block edit session not found"); return; } auto g = OdDbEvalGraph::getGraph(ms, ACAD_ENHANCEDBLOCK, OdDb::kForWrite); switch ((ParameterType)pIO->getKeyword(L"Enter parameter type: [Alignment/Base/pOint/Linear/Polar/Xy/Rotation/Flip/Visibility/looKup]", L"Alignment Base Point Linear Polar Xy Rotation Flip Visibility looKup")) { case ParameterType::Linear: { auto lp = OdDbBlockLinearParameter::createObject(); lp->setName(generateName(g, L"Linear")); lp->setDistanceName(generateLabel(g, L"Distance")); g->addNode(lp); if (!promptForLinearValues(lp, pIO, g)) g->removeNode(lp); else addEntity(lp, ms); } break; case ParameterType::Point: { auto pp = OdDbBlockPointParameter::createObject(); pp->setName(generateName(g, L"Point")); pp->setPositionName(generateLabel(g, L"Position")); g->addNode(pp); if (!propmptForPositionValues(pp, pIO, g)) g->removeNode(pp); else addEntity(pp, ms); } break; case ParameterType::Polar: { auto pp = OdDbBlockPolarParameter::createObject(); pp->setName(generateName(g, L"Polar")); pp->setDistanceName(generateLabel(g, L"Distance")); pp->setAngleName(generateLabel(g, L"Angle")); g->addNode(pp); if (!promptForPolarValues(pp, pIO, g)) g->removeNode(pp); else addEntity(pp, ms); } break; case ParameterType::Rotation: { auto rp = OdDbBlockRotationParameter::createObject(); rp->setName(generateName(g, L"Rotation")); rp->setAngleName(generateLabel(g, L"Angle")); g->addNode(rp); if (!promptForRotationValues(rp, pIO, g)) g->removeNode(rp); else addEntity(rp, ms); } break; case ParameterType::Flip: { auto p = OdDbBlockFlipParameter::createObject(); p->setName(generateName(g, L"Flip")); p->setFlipLabel(generateLabel(g, L"Flip state")); p->setFlippedStateLabel(L"Flipped"); p->setBaseStateLabel(L"Not flipped"); g->addNode(p); if (!promptForFlipValues(p, pIO, g)) g->removeNode(p); else addEntity(p, ms); } break; case ParameterType::Alignment: { auto p = OdDbBlockAlignmentParameter::createObject(); p->setName(generateName(g, L"Alignment")); p->setAlignPerpendicular(true); g->addNode(p); if (!promptForAlignmentValues(p, pIO, g)) g->removeNode(p); else addEntity(p, ms, 1); } break; case ParameterType::Base: { if (findParameter(g)) { pIO->putString(L"Block already has a base parameter"); return; } auto p = OdDbBlockBasepointParameter::createObject(); p->setName(L"Base Point"); g->addNode(p); try { auto pos = pIO->getPoint("Specify base point:"); p->setDefinitionX(pos.x); p->setDefinitionY(pos.y); auto bee = OdDbBlockElementEntity::create(p); bee->setDatabaseDefaults(ms->database()); ms->appendOdDbEntity(bee); } catch (const OdError&) { g->removeNode(p); } } break; case ParameterType::Visibility: { if (findParameter(g)) { pIO->putString(L"Block already has a visibility parameter"); return; } auto p = OdDbBlockVisibilityParameter::createObject(); p->setName(generateName(g, L"Visibility State")); g->addNode(p); p->setVisibilityName(generateName(g, L"Visibility")); if (!promptForVisibilityValues(p, pIO, g)) g->removeNode(p); else { addEntity(p, ms); OdDbObjectIdArray ents, nodes; collectBlockEntites(ms, ents, nodes); p->setBlockEntityList(ents); p->setVisibilityState(0, L"VisibilityState0", ents, nodes); p->setInitialized(true); } } break; default: break; } } enum class ActionType { Array, Lookup, Flip, Move, Rotate, Scale, Stretch, Polar_stretch }; template struct ParameterTypeTracker; template<> struct ParameterTypeTracker<> : OdStaticRxObject { virtual bool append(OdDbStub*, const OdDbSelectionMethod*) { return false; } }; template struct ParameterTypeTracker : ParameterTypeTracker { virtual bool append(OdDbStub* id, const OdDbSelectionMethod* sm) { if (auto param = OdDbObjectId(id).openObject()) return param->isKindOf(Head::desc()) || ParameterTypeTracker::append(id, sm); else return false; } }; OdDbObjectIdArray filterSelectionSet(OdSelectionSet* ss, OdDbObjectId paramId, OdDbObjectIdArray* ss2 = nullptr) { OdDbObjectIdArray selection; for (auto it = ss->newIterator();!it->done();it->next()) { auto pEntity = OdDbObjectId(it->id()).openObject(); if (pEntity->objectId() == paramId) continue; if (ss2 && ss2->contains(pEntity->objectId())) continue; if (auto bee = OdDbBlockElementEntity::cast(pEntity)) selection.append(bee->getElement()); else selection.append(it->id()); } return selection; } static void setActionLocation(OdDbBlockAction* a, OdDbBlockParameter* param, OdDbBlockTableRecord* ms, int attachment = -1) { OdDbBlockActionEntityPtr bae = OdDbBlockElementEntity::create(a); bae->setDatabaseDefaults(ms->database()); ms->appendOdDbEntity(bae); if (attachment >= 0) { OdDbBlock2PtParameterPtr param2 = param; bae->setDisplayLocation(attachment == 0 ? param2->definitionBasePoint() : param2->definitionEndPoint()); } else { bae->setDisplayLocation(OdDbBlock1PtParameterPtr(param)->definitionPoint()); } } static int promptForAttachment(OdDbUserIO* pIO, OdDbBlockParameter* param) { int attachment = -1; if (param->isKindOf(OdDbBlock2PtParameter::desc())) { attachment = pIO->getKeyword(L"Specify parameter point to associate the action with [Start/End] :", L"Start End", 0); if (attachment < 0) throw OdError(eInvalidInput); } return attachment; } void addStretchAction(OdDbUserIO* pIO, OdDbEvalGraph* g, OdDbBlockTableRecord* ms) { try { ParameterTypeTracker tracker; auto ss = pIO->select(L"Select parameter:", OdEd::kSelSingleEntity | OdEd::kSelSinglePass | OdEd::kInpThrowEmpty, nullptr, OdString::kEmpty, &tracker); if (ss->numEntities() == 0) return; OdDbObjectId paramId(ss->newIterator()->id()); OdDbBlockParameterEntityPtr paramEntity = paramId.safeOpenObject(); OdDbBlockParameterPtr param = paramEntity->getElement().safeOpenObject(); int attachment = promptForAttachment(pIO, param); OdGePoint3d p1 = pIO->getPoint(L"Specify first corner of the stretch frame:"); OdGePoint3d p2 = pIO->getPoint(L"Specify second corner of the stretch frame:", OdEd::kGptRectFrame); auto ss1 = pIO->select(L"Select objects:"); auto a = OdDbBlockStretchAction::createObject(); a->setName(generateName(g, L"Stretch")); g->addNode(a); a->setStretchFrame({ { p1.x, p1.y },{ p2.x, p2.y } }); a->setSelectionSet(filterSelectionSet(ss1, paramEntity->objectId())); if (attachment >= 0) { a->connectTo(L"DeltaX", param->nodeId(), attachment == 0 ? L"BaseXDelta" : L"EndXDelta"); a->connectTo(L"DeltaY", param->nodeId(), attachment == 0 ? L"BaseYDelta" : L"EndYDelta"); } else { a->connectTo(L"DeltaX", param->nodeId(), L"XDelta"); a->connectTo(L"DeltaY", param->nodeId(), L"YDelta"); } setActionLocation(a, param, ms, attachment); } catch (const OdError&) {} } void addScaleAction(OdDbUserIO* pIO, OdDbEvalGraph* g, OdDbBlockTableRecord* ms) { try { ParameterTypeTracker tracker; auto ss = pIO->select(L"Select parameter:", OdEd::kSelSingleEntity | OdEd::kSelSinglePass | OdEd::kInpThrowEmpty, nullptr, OdString::kEmpty, &tracker); if (ss->numEntities() == 0) return; OdDbObjectId paramId(ss->newIterator()->id()); OdDbBlockParameterEntityPtr paramEntity = paramId.safeOpenObject(); OdDbBlock2PtParameterPtr param = paramEntity->getElement().safeOpenObject(); auto ss1 = pIO->select(L"Select objects:"); auto a = OdDbBlockScaleAction::createObject(); a->setName(generateName(g, L"Scale")); g->addNode(a); a->setSelectionSet(filterSelectionSet(ss1, paramEntity->objectId())); a->connectTo(L"Scale", param->nodeId(), L"Scale"); a->connectTo(L"XScale", param->nodeId(), L"XScale"); a->connectTo(L"YScale", param->nodeId(), L"YScale"); // otherwise parameter base point changes will be ignored a->connectTo(L"BaseX", param->nodeId(), L"UpdatedBaseX"); a->connectTo(L"BaseY", param->nodeId(), L"UpdatedBaseY"); setActionLocation(a, param, ms); } catch (const OdError&) {} } void addMoveAction(OdDbUserIO* pIO, OdDbEvalGraph* g, OdDbBlockTableRecord* ms) { try { ParameterTypeTracker tracker; auto ss = pIO->select(L"Select parameter:", OdEd::kSelSingleEntity | OdEd::kSelSinglePass | OdEd::kInpThrowEmpty, nullptr, OdString::kEmpty, &tracker); if (ss->numEntities() == 0) return; OdDbObjectId paramId(ss->newIterator()->id()); OdDbBlockParameterEntityPtr paramEntity = paramId.safeOpenObject(); OdDbBlockParameterPtr param = paramEntity->getElement().safeOpenObject(); int attachment = promptForAttachment(pIO, param); auto ss1 = pIO->select(L"Select objects:"); auto a = OdDbBlockMoveAction::createObject(); a->setName(generateName(g, L"Move")); g->addNode(a); a->setSelectionSet(filterSelectionSet(ss1, paramEntity->objectId())); if (attachment >= 0) { a->connectTo(L"DeltaX", param->nodeId(), attachment == 0 ? L"BaseXDelta" : L"EndXDelta"); a->connectTo(L"DeltaY", param->nodeId(), attachment == 0 ? L"BaseYDelta" : L"EndYDelta"); } else { a->connectTo(L"DeltaX", param->nodeId(), L"XDelta"); a->connectTo(L"DeltaY", param->nodeId(), L"YDelta"); } setActionLocation(a, param, ms, attachment); } catch (const OdError&) {} } void addPolarStretchAction(OdDbUserIO* pIO, OdDbEvalGraph* g, OdDbBlockTableRecord* ms) { try { ParameterTypeTracker tracker; auto ss = pIO->select(L"Select parameter:", OdEd::kSelSingleEntity | OdEd::kSelSinglePass | OdEd::kInpThrowEmpty, nullptr, OdString::kEmpty, &tracker); if (ss->numEntities() == 0) return; OdDbObjectId paramId(ss->newIterator()->id()); OdDbBlockParameterEntityPtr paramEntity = paramId.safeOpenObject(); OdDbBlockParameterPtr param = paramEntity->getElement().safeOpenObject(); int attachment = promptForAttachment(pIO, param); OdGePoint3d p1 = pIO->getPoint(L"Specify first corner of the stretch frame:"); OdGePoint3d p2 = pIO->getPoint(L"Specify second corner of the stretch frame:", OdEd::kGptRectFrame); auto ss1 = pIO->select(L"Select objects:"); auto ss2 = pIO->select(L"Select objects to rotate only:", OdEd::kSelAllowEmpty); auto a = OdDbBlockPolarStretchAction::createObject(); a->setName(generateName(g, L"PolarStretch")); g->addNode(a); a->setStretchFrame({ { p1.x, p1.y },{ p2.x, p2.y } }); auto ids = filterSelectionSet(ss1, paramEntity->objectId()); a->setSelectionSet(ids); a->setRotateOnlySelectionSet(filterSelectionSet(ss2, paramEntity->objectId(), &ids)); ODA_VERIFY(a->connectTo(L"DeltaX", param->nodeId(), attachment == 0 ? L"BaseXDelta" : L"EndXDelta")); ODA_VERIFY(a->connectTo(L"DeltaY", param->nodeId(), attachment == 0 ? L"BaseYDelta" : L"EndYDelta")); ODA_VERIFY(a->connectTo(L"Base", param->nodeId(), L"Base")); ODA_VERIFY(a->connectTo(L"End", param->nodeId(), L"End")); ODA_VERIFY(a->connectTo(L"UpdatedBase", param->nodeId(), L"UpdatedBase")); ODA_VERIFY(a->connectTo(L"UpdatedEnd", param->nodeId(), L"UpdatedEnd")); setActionLocation(a, param, ms, attachment); } catch (const OdError&) {} } void addRotateAction(OdDbUserIO* pIO, OdDbEvalGraph* g, OdDbBlockTableRecord* ms) { try { ParameterTypeTracker tracker; auto ss = pIO->select(L"Select parameter:", OdEd::kSelSingleEntity | OdEd::kSelSinglePass | OdEd::kInpThrowEmpty, nullptr, OdString::kEmpty, &tracker); if (ss->numEntities() == 0) return; OdDbObjectId paramId(ss->newIterator()->id()); OdDbBlockParameterEntityPtr paramEntity = paramId.safeOpenObject(); OdDbBlockParameterPtr param = paramEntity->getElement().safeOpenObject(); auto ss1 = pIO->select(L"Select objects:"); auto a = OdDbBlockRotateAction::createObject(); a->setName(generateName(g, L"PolarStretch")); g->addNode(a); a->setSelectionSet(filterSelectionSet(ss1, paramEntity->objectId())); ODA_VERIFY(a->connectTo(L"DeltaRotation", param->nodeId(), L"AngleDelta")); // otherwise parameter base point changes will be ignored ODA_VERIFY(a->connectTo(L"BaseX", param->nodeId(), L"UpdatedBaseX")); ODA_VERIFY(a->connectTo(L"BaseY", param->nodeId(), L"UpdatedBaseY")); setActionLocation(a, param, ms, OdDbBlockParameter::End); } catch (const OdError&) {} } void EXBACTIONTOOLCmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIOPtr pIO = pDbCmdCtx->userIO(); OdDbDatabase* db = pDbCmdCtx->database(); OdDbBlockTableRecordPtr ms = db->getModelSpaceId().safeOpenObject(OdDb::kForWrite); if (g_nBLOCKEDITOR == 0) { pIO->putString(L"Block edit session not found"); return; } auto g = OdDbEvalGraph::getGraph(ms, ACAD_ENHANCEDBLOCK, OdDb::kForWrite); switch ((ActionType)pIO->getKeyword(L"Enter action type: [Array/Lookup/Flip/Move/Rotate/Scale/sTretch/Polar stretch]", L"Array Lookup Flip Move Rotate Scale sTretch Polar_stretch")) { case ActionType::Stretch: addStretchAction(pIO, g, ms); break; case ActionType::Scale: addScaleAction(pIO, g, ms); break; case ActionType::Move: addMoveAction(pIO, g, ms); break; case ActionType::Polar_stretch: addPolarStretchAction(pIO, g, ms); break; case ActionType::Rotate: addRotateAction(pIO, g, ms); break; default: break; } } enum class VisibilityStateKeywords { New = 0, Set, Delete }; struct VisibilityState { OdString name; OdDbObjectIdArray ents; OdDbObjectIdArray nodes; }; OdString promptForStateName(const OdArray& states, OdDbUserIO* pIO) { auto name = pIO->getString(L"Enter new visibility state name:"); if (std::find_if(states.begin(), states.end(), [name](const VisibilityState& s) {return s.name.iCompare(name) == 0;})) { pIO->putString(L""); return promptForStateName(states, pIO); } return name; } void EXBVSTATECmd::execute(OdEdCommandContext* pCmdCtx) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIOPtr pIO = pDbCmdCtx->userIO(); OdDbDatabase* db = pDbCmdCtx->database(); OdDbBlockTableRecordPtr ms = db->getModelSpaceId().safeOpenObject(); if (g_nBLOCKEDITOR == 0) { pIO->putString(L"Block edit session not found"); return; } auto g = OdDbEvalGraph::getGraph(ms, ACAD_ENHANCEDBLOCK); auto vp = findParameter(g); if (!vp) { pIO->putString(L"No visibility parameter in block!"); return; } OdArray states; int N = vp->numberOfVisibilityStates(); states.resize(N); for (int i = 0; i < N; ++i) vp->visibilityState(i, states[i].name, states[i].ents, states[i].nodes); switch ((VisibilityStateKeywords)pIO->getKeyword(L"Enter an option: [New/Set/Delete]", L"New Set Delete")) { case VisibilityStateKeywords::New: { auto newName = promptForStateName(states, pIO); switch (pIO->getKeyword("Enter object visibility for the new state [Hide All/Show All/Current Visibility]", L"Hide_All Show_All Current_Visibility")) { case 0: //hide vp->setVisibilityState(N, newName, {}, {}); vp->setCurrentVisibilityState(N); break; case 1: { OdDbObjectIdArray ents, nodes; collectBlockEntites(ms, ents, nodes); vp->setVisibilityState(N, newName, ents, nodes); vp->setCurrentVisibilityState(N); } break; case 2: { int cv = vp->currentVisibilityState(); if (cv < 0) cv = 0; vp->setVisibilityState(N, newName, states[cv].ents, states[cv].nodes); vp->setCurrentVisibilityState(N); } break; } } break; case VisibilityStateKeywords::Set: { for (;;) { try { auto name = pIO->getString(L"Enter visibility state to make current or [?]:", 0, OdString::kEmpty, L"?"); auto i = std::find_if(states.begin(), states.end(), [name](const VisibilityState& s) {return s.name.iCompare(name) == 0; }); if (i != states.end()) { vp->setCurrentVisibilityState((int)std::distance(states.begin(), i)); break; } } catch(const OdEdKeyword&) { for (const auto& state : states) pIO->putString(state.name); } } } break; case VisibilityStateKeywords::Delete: { for (;;) { auto name = pIO->getString(L"Enter visibility state to delete:"); auto i = std::find_if(states.begin(), states.end(), [name](const VisibilityState& s) {return s.name.iCompare(name) == 0; }); if (i != states.end()) { vp->deleteVisibilityState((int)std::distance(states.begin(), i)); break; } } } break; } } static void getSelectionList(OdSelectionSet* ss, OdDbObjectIdArray& ents, OdDbObjectIdArray& nodes) { for (auto i = ss->newIterator(); !i->done(); i->next()) { if (auto obj = OdDbObjectId(i->id()).openObject()) { if (obj->isA()->isDerivedFrom(OdDbBlockElementEntity::desc())) nodes.append(i->id()); else ents.append(i->id()); } } } static void removeDuplicates(OdDbObjectIdArray& ents) { std::sort(ents.begin(), ents.end()); ents.erase(std::unique(ents.begin(), ents.end()), ents.end()); } static void removeSelected(OdDbObjectIdArray& current, const OdDbObjectIdArray& selected) { std::set s(selected.begin(), selected.end()); current.erase(std::remove_if(current.begin(), current.end(), [&s](OdDbObjectId id) { return s.find(id) != s.end();}), current.end()); } void updateVisibilityState(VisibilityState& s, const OdDbObjectIdArray& ents, const OdDbObjectIdArray& nodes, bool show) { if (show) { s.ents.append(ents); s.nodes.append(nodes); removeDuplicates(s.ents); removeDuplicates(s.nodes); } else { removeSelected(s.ents, ents); removeSelected(s.nodes, nodes); } } void BVHIDESHOW(OdEdCommandContext* pCmdCtx, bool show) { OdDbCommandContextPtr pDbCmdCtx(pCmdCtx); OdDbUserIOPtr pIO = pDbCmdCtx->userIO(); OdDbDatabase* db = pDbCmdCtx->database(); OdDbBlockTableRecordPtr ms = db->getModelSpaceId().safeOpenObject(); if (g_nBLOCKEDITOR == 0) { pIO->putString(L"Block edit session not found"); return; } auto g = OdDbEvalGraph::getGraph(ms, ACAD_ENHANCEDBLOCK); auto vp = findParameter(g); if (!vp) { pIO->putString(L"No visibility parameter in block!"); return; } OdDbObjectIdArray ents, nodes; getSelectionList(pIO->select(L"Select objects"), ents, nodes); if (!ents.isEmpty() || !nodes.isEmpty()) { OdArray states; int N = vp->numberOfVisibilityStates(); states.resize(N); for (int i = 0; i < N; ++i) vp->visibilityState(i, states[i].name, states[i].ents, states[i].nodes); if (pIO->getKeyword((show ? OdString(L"Make visible"): OdString(L"Make invisible")) + L" for all visibility states or just current [Current/All]", "Current All", 0) == 0) { int cvs = vp->currentVisibilityState(); updateVisibilityState(states[cvs], ents, nodes, show); vp->setVisibilityState(cvs, states[cvs].name, states[cvs].ents, states[cvs].nodes); } else { for (int cvs = 0; cvs < (int)states.length(); ++cvs) { updateVisibilityState(states[cvs], ents, nodes, show); vp->setVisibilityState(cvs, states[cvs].name, states[cvs].ents, states[cvs].nodes); } } } } void EXBVHIDECmd::execute(OdEdCommandContext* pCmdCtx) { BVHIDESHOW(pCmdCtx, false); } void EXBVSHOWCmd::execute(OdEdCommandContext* pCmdCtx) { BVHIDESHOW(pCmdCtx, true); }