/////////////////////////////////////////////////////////////////////////////// // 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. /////////////////////////////////////////////////////////////////////////////// // ODA Platform #include "OdaCommon.h" #include "ColorMapping.h" #include "TvDatabaseUtils.h" /** \details Fix inadmissible letters for the name */ OdString OdTvDatabaseUtils::fixNameForTvObject(OdString& name, OdUInt32 idx) { OdString res = name; res = res.trimLeft().trimRight(); res.replace('/', '-'); res.replace('\\', '-'); res.replace(':', '-'); res.replace(';', '-'); res.replace('*', 'x'); res.remove('?'); res.remove(','); res.remove('='); res.remove('\"'); res.remove('>'); res.remove('<'); res.remove('|'); if (res.isEmpty()) { res = OD_T("GeneratedName"); OdString str; str.format(L"_%d", idx++); res += str; } return res; } OdString OdTvDatabaseUtils::getFileNameFromPath(const OdString& filePath) { OdString path = filePath; path.replace('/', '\\'); OdString filename = path.mid(path.reverseFind('\\') + 1); OdString ext = filename.mid(filename.reverseFind('.')); filename.deleteChars(filename.find(ext), ext.getLength()); filename.replace('.', '_'); return filename; } void OdTvDatabaseUtils::writeFileNameToTvDatabase(const OdTvDatabaseId& dbId, const OdString& fileName) { // get buffer const OdChar *pStr = fileName.c_str(); //transfer to UTF-8 OdAnsiString sAnsiUtf8(pStr, CP_UTF_8); const char* pUtf8Str = sAnsiUtf8.c_str(); int size = sAnsiUtf8.getLength() + 1; //+1 for the trailing zero // create user data OdTvByteUserData *data = new OdTvByteUserData((void*)pUtf8Str, size, OdTvByteUserData::kCopyOwn, false); OdTvDatabasePtr pTvDb = dbId.openObject(OdTv::kForWrite); //register app bool alreadyExist = false; OdTvRegAppId regAppId = pTvDb->registerAppName(OD_T("Import to Visualize - name for save"), alreadyExist); // append user data to the database pTvDb->appendUserData(data, regAppId); } void OdTvDatabaseUtils::createDeviceAndView(const OdTvDatabaseId& dbId, const OdTvModelId& modelId, OdTvGsView::RenderMode renderMode, bool bAdvancedLightingSetup, bool bWhiteBackground, double lightIntents, const OdGeExtents3d* const pExtentsToZoom) { OdTvDatabasePtr pTvDb = dbId.openObject(OdTv::kForWrite); OdTvGsDeviceId tvDeviceId = pTvDb->createDevice(OD_TV_COMMON_DEVICE_NAME); OdTvGsDevicePtr pDevice = tvDeviceId.openObject(OdTv::kForWrite); // create view OdTvGsViewId tvViewId = pDevice->createView(OD_TV_COMMON_VIEW_NAME); pDevice->addView(tvViewId); //setup view OdTvGsViewPtr pView = tvViewId.openObject(OdTv::kForWrite); pView->addModel(modelId); pView->setMode(renderMode); pView->setView(OdTvPoint(0., 0., 1.), OdTvPoint(0., 0., 0.), OdTvVector(0., 1., 0.), 1., 1.); pView->setActive(true); //setup lighting and background if (bAdvancedLightingSetup) { pView->enableDefaultLighting(true, OdTvGsView::kUserDefined); pView->setUserDefinedLightDirection(OdTvVector(0.5, 0.0, -1.0)); pView->setDefaultLightingIntensity(lightIntents); pView->setAmbientLightColor(OdTvColorDef(50, 50, 50)); if (!bWhiteBackground) pDevice->setBackgroundColor(ODRGB(33, 108, 170)); } // since the GS is not setup yet, we can call empty zoom to extents to mark the view. // The action will be performed inside first setupGs if( pExtentsToZoom == NULL || !pExtentsToZoom->isValidExtents() ) { OdTvPoint minPt, maxPt; pView->zoomExtents( minPt, maxPt ); } else //If extents is specified, we will simulate ZoomToExtents { const OdTvExtents3d& extents = *pExtentsToZoom; OdTvPoint center = extents.center(); OdTvVector diag = extents.maxPoint() - extents.minPoint(); double w = Od_abs( diag.x ); if( w < 1e-5 ) w = 1.0; double h = Od_abs( diag.y ); if( h < 1e-5 ) h = 1.0; double depth = Od_abs( diag.z ); if( depth < 1e-5 ) depth = 1.0; OdTvVector viewDir = -1.0 * OdTvVector::kZAxis; OdTvPoint pos = center - ( viewDir * ( depth / 2.0 ) ); OdTvVector upV = OdTvVector::kYAxis; double field = ( w > h ) ? w : h; pView->setView( pos, center, upV, field, field, OdTvGsView::kParallel ); } } OdString OdTvDatabaseUtils::generateModelName(OdTvDatabaseId databaseId, const OdString& fileName) { OdString baseModelName, modelName; //fix model name at first OdString correctedFileName = fileName; correctedFileName = fixNameForTvObject(correctedFileName, 0); baseModelName = correctedFileName + OD_T("_Model"); OdUInt32 modelIndex = 0; bool bFound = true; while (bFound) { OdString sModelIndex; sModelIndex.format(OD_T("%d"), modelIndex); modelName = baseModelName + sModelIndex; bFound = false; OdTvModelsIteratorPtr modelsIterPtr = databaseId.openObject()->getModelsIterator(); while (!modelsIterPtr->done()) { OdString name = modelsIterPtr->getModel().openObject()->getName(); if (modelName == name) { bFound = true; break; } modelsIterPtr->step(); } if (bFound) { modelIndex++; bFound = true; } } return modelName; } bool OdTvDatabaseUtils::applyTransformToTheModel(const OdTvDatabaseId& databaseId, const OdString& modelSpaceName, const OdTvMatrix& transform) { if (transform == OdTvMatrix::kIdentity) return false; OdTvDatabasePtr pTvDb = databaseId.openObject(); // Get new model OdTvModelId newModel; OdTvModelsIteratorPtr modelsIterPtr = pTvDb->getModelsIterator(); while (!modelsIterPtr->done()) { newModel = modelsIterPtr->getModel(); OdString newModelName = newModel.openObject()->getName(); modelsIterPtr->step(); if (newModelName == modelSpaceName || modelsIterPtr->done()) break; } if (newModel.isNull()) return false; // transform the model OdTvModelPtr newModelPtr = newModel.openObject(OdTv::kForWrite); newModelPtr->setModelingMatrix(transform); return true; } void OdTvDatabaseUtils::collectViewsModelsAndBlocks(const OdTvDatabaseId& tvDbId, std::set& foreignViews, std::set& foreignModels, std::set& foreignBlocks) { OdTvDatabasePtr pTvDb = tvDbId.openObject(OdTv::kForWrite); OdTvDevicesIteratorPtr pDevicesIterator = pTvDb->getDevicesIterator(); while (!pDevicesIterator->done()) { OdTvGsDeviceId deviceId = pDevicesIterator->getDevice(); OdTvGsDevicePtr pDevice = deviceId.openObject(OdTv::kForWrite); for (int i = 0; i < pDevice->numViews(); i++) { OdTvGsViewId viewId = pDevice->viewAt(i); foreignViews.insert(viewId); } pDevicesIterator->step(); } OdTvModelsIteratorPtr pModelsIterator = pTvDb->getModelsIterator(); while (!pModelsIterator->done()) { OdTvModelId modelId = pModelsIterator->getModel(); pModelsIterator->step(); foreignModels.insert(modelId); } OdTvBlocksIteratorPtr pBlocksIterator = pTvDb->getBlocksIterator(); while (!pBlocksIterator->done()) { OdTvBlockId blockId = pBlocksIterator->getBlock(); pBlocksIterator->step(); foreignBlocks.insert(blockId); } } void OdTvDatabaseUtils::markCDANode(OdRxModelTreeBaseNode* pNode) { if (!pNode) return; OdRxModelTreeBaseNodePtr pNodePtr(pNode); OdTvCDATreeNodePtr pTvNode = pNodePtr; if (!pTvNode.isNull()) pTvNode->setNeedGroup(false); OdRxModelTreeBaseNodePtrArray childs = pNode->getChildren(); if (childs.isEmpty()) return; for (OdRxModelTreeBaseNodePtrArray::iterator it = childs.begin(); it != childs.end(); ++it) markCDANode((*it).get()); return; } void OdTvDatabaseUtils::resizeImportDevice(OdGsDevicePtr pDevice, const OdTvDCRect& rect) { if (pDevice.isNull()) return; if (rect.xmax > 0 || rect.xmin > 0 || rect.ymax > 0 || rect.ymin > 0) { OdGsDCRect gsRect(rect.xmin, rect.xmax, rect.ymin, rect.ymax); pDevice->onSize(gsRect); } return; } void OdTvDatabaseUtils::getPaletteForImportDevice(const ODCOLORREF* palette, OdArray >& resPalette) { bool bDefaultPalette = false; if (palette == 0) { bDefaultPalette = true; palette = odcmAcadPalette(ODRGB(255, 255, 255)); } resPalette.insert(resPalette.begin(), palette, palette + 256); return; } void OdTvDatabaseUtils::createAndApplyPreferableVS(OdTvDatabaseId& tvDbId, bool bUseBlack, bool bFlatShaded) { if (tvDbId.isNull()) return; OdTvDatabasePtr pTvDatabase = tvDbId.openObject(OdTv::kForWrite); if (pTvDatabase.isNull()) return; //1. OdTvVisualStyleId visualStyleIsolinesId = pTvDatabase->createVisualStyle(OD_T("ShadedWithIsolines"), bFlatShaded ? pTvDatabase->findVisualStyle(OD_T("FlatWithEdges")) : pTvDatabase->findVisualStyle(OD_T("GouraudWithEdges"))); { OdTvVisualStylePtr pVisualStyle = visualStyleIsolinesId.openObject(OdTv::kForWrite); if (pVisualStyle.isNull()) return; pVisualStyle->setOption(OdTvVisualStyleOptions::kEdgeModifiers, (OdInt32)OdTvVisualStyleOptions::kEdgeColor); pVisualStyle->setOption(OdTvVisualStyleOptions::kEdgeColorValue, OdTvColorDef(0, 0, 0)); } pTvDatabase->setPreferableVisualStyle(OdTvDatabase::kShadedWithIsolines, visualStyleIsolinesId); //2. OdTvVisualStyleId visualStyleHiddenId = pTvDatabase->createVisualStyle(bUseBlack ? OD_T("Hidden Line Black") : OD_T("Hidden Line Ex"), pTvDatabase->findVisualStyle(OD_T("Hidden"))); { OdTvVisualStylePtr pVisualStyle = visualStyleHiddenId.openObject(OdTv::kForWrite); if (pVisualStyle.isNull()) return; pVisualStyle->setOption(OdTvVisualStyleOptions::kEdgeCreaseAngle, 89.); if (bUseBlack) { pVisualStyle->setOption(OdTvVisualStyleOptions::kEdgeModifiers, (OdInt32)OdTvVisualStyleOptions::kEdgeColor); pVisualStyle->setOption(OdTvVisualStyleOptions::kEdgeColorValue, OdTvColorDef(0, 0, 0)); } } pTvDatabase->setPreferableVisualStyle(OdTvDatabase::kHiddenLine, visualStyleHiddenId); //3. if (bUseBlack) { OdTvVisualStyleId visualStyleWireId = pTvDatabase->createVisualStyle(OD_T("Wireframe Black"), pTvDatabase->findVisualStyle(OD_T("Wireframe"))); { OdTvVisualStylePtr pVisualStyle = visualStyleWireId.openObject(OdTv::kForWrite); if (pVisualStyle.isNull()) return; pVisualStyle->setOption(OdTvVisualStyleOptions::kEdgeModifiers, (OdInt32)OdTvVisualStyleOptions::kEdgeColor); pVisualStyle->setOption(OdTvVisualStyleOptions::kEdgeColorValue, OdTvColorDef(0, 0, 0)); } pTvDatabase->setPreferableVisualStyle(OdTvDatabase::kWireframe, visualStyleWireId); } else pTvDatabase->setPreferableVisualStyle(OdTvDatabase::kWireframe, pTvDatabase->findVisualStyle(OD_T("Wireframe"))); //4. if (bFlatShaded) { //a. OdTvVisualStyleId visIdShaded = pTvDatabase->findVisualStyle(OD_T("Flat")); if (!visIdShaded.isNull()) pTvDatabase->setPreferableVisualStyle(OdTvDatabase::kShaded, visIdShaded); //b. OdTvVisualStyleId tessVS = pTvDatabase->findVisualStyle(OD_T("ShadedWithTessellation")); if (!tessVS.isNull()) { OdTvVisualStylePtr pVisualStyle = tessVS.openObject(OdTv::kForWrite); if (!pVisualStyle.isNull()) { pVisualStyle->setOption(OdTvVisualStyleOptions::kFaceLightingQuality, (OdInt32)OdTvVisualStyleOptions::kPerFaceLighting); } } } //5. Add shadow support for (OdUInt32 i = OdUInt32(OdTvDatabase::kShaded); i <= OdUInt32(OdTvDatabase::kShadedWithIsolines); i++) { OdTvDatabase::PreferableVisualStylesType type = OdTvDatabase::PreferableVisualStylesType(i); OdTvVisualStyleId visStyleId = pTvDatabase->getPreferableVisualStyle(type); if (!visStyleId.isNull()) { OdTvVisualStylePtr pVisualStyle = visStyleId.openObject(OdTv::kForWrite); if (!pVisualStyle.isNull()) { pVisualStyle->setOption(OdTvVisualStyleOptions::kDisplayShadow, true); } } } return; } void OdTvDatabaseUtils::checkAndApplyCustomPreferableVS(OdTvDatabaseId& tvDbId, bool bSimplyAdd) { if (tvDbId.isNull()) return; OdTvDatabasePtr pTvDatabase = tvDbId.openObject(OdTv::kForWrite); if (pTvDatabase.isNull()) return; if (bSimplyAdd) pTvDatabase->setPreferableVisualStyle(OdTvDatabase::kCustom, pTvDatabase->findVisualStyle(L"2dWireframe")); else { OdTvVisualStyleId activeVisualStyleId; OdTvDevicesIteratorPtr pDevicesIterator = tvDbId.openObject()->getDevicesIterator(); while (!pDevicesIterator->done()) { OdTvGsDeviceId deviceId = pDevicesIterator->getDevice(); OdTvGsDevicePtr pDevice = deviceId.openObject(); if (pDevice.isNull() || pDevice->isBitmap()) { pDevicesIterator->step(); continue; } if (pDevice->getActive()) { OdTvGsViewId activeViewId = pDevice->getActiveView(); if (activeViewId.isNull()) continue; activeVisualStyleId = activeViewId.openObject(OdTv::kForRead)->getVisualStyle(); break; } pDevicesIterator->step(); } if (!activeVisualStyleId.isNull()) { bool bIsPreferable = false; for (OdUInt32 i = 0; i <= OdUInt32(OdTvDatabase::kCustom); i++) { OdTvDatabase::PreferableVisualStylesType type = OdTvDatabase::PreferableVisualStylesType(i); if (pTvDatabase->isPreferableVisualStyle(activeVisualStyleId, type)) { bIsPreferable = true; break; } } if (!bIsPreferable) pTvDatabase->setPreferableVisualStyle(OdTvDatabase::kCustom, activeVisualStyleId); } } return; } void applyShellsNotDisplayEdgesAsIsolines_entity(OdTvEntityPtr pEntity) { if (pEntity.isNull()) return; OdTvGeometryDataIteratorPtr pIter = pEntity->getGeometryDataIterator(); while (!pIter->done()) { OdTvGeometryDataId geomDataId = pIter->getGeometryData(); bool bNeedRemove = false; if (geomDataId.getType() == OdTv::kSubEntity) { OdTvEntityPtr pSubEnt = geomDataId.openAsSubEntity(OdTv::kForWrite); applyShellsNotDisplayEdgesAsIsolines_entity(pSubEnt); } else if (geomDataId.getType() == OdTv::kShell) { OdTvGeometryDataPtr pGeomDataPtr = geomDataId.openObject(); if (!pGeomDataPtr.isNull()) pGeomDataPtr->setTargetDisplayMode(OdTvGeometryData::kEveryWhereExceptIsolines); } pIter->step(); } return; } void OdTvDatabaseUtils::setShellsNotDisplayEdgesAsIsolines(const OdTvModelId& tvModelId) { if (tvModelId.isNull()) return; OdTvModelPtr pModel = tvModelId.openObject(OdTv::kForWrite); for (OdTvEntitiesIteratorPtr pEnts = pModel->getEntitiesIterator(); !pEnts->done(); pEnts->step()) { OdTvEntityId entId = pEnts->getEntity(); if (entId.getType() == OdTvEntityId::kEntity) { applyShellsNotDisplayEdgesAsIsolines_entity(entId.openObject(OdTv::kForWrite)); } } return; }