diff --git a/src/app/Application.cpp b/src/app/Application.cpp index 1adb4a48..887fe11d 100644 --- a/src/app/Application.cpp +++ b/src/app/Application.cpp @@ -3,8 +3,10 @@ #include "common/RecourceHelper.h" #include "workspace/WorkSpaceManager.h" #include "entities/EntitiesManager.h" +#include "entities/EntityFactory.h" #include "viewer/OsgViewer.h" #include "scene/MeshManager.h" +#include "workspace/PresetModelConfigParser.h" #include "network/NetDriver.h" #include "python/PythonModule.h" @@ -30,9 +32,11 @@ QString Application::GetBinPath() { void Application::Init() { Singleton::Create(this); Singleton::Create(this); + Singleton::Create(this); Singleton::Create(this); Singleton::Create(this); Singleton::Create(this); + Singleton::Create(this); //Singleton::Create(this); connect(&timer_, &QTimer::timeout, this, &Application::OnTimeout); @@ -46,8 +50,10 @@ void Application::OnTimeout() { void Application::Uninit() { //Singleton::Destory(); Singleton::Destory(); + Singleton::Destory(); Singleton::Destory(); Singleton::Destory(); + Singleton::Destory(); Singleton::Destory(); Singleton::Destory(); } diff --git a/src/entities/EntitiesManager.cpp b/src/entities/EntitiesManager.cpp index a2c47199..c13abace 100644 --- a/src/entities/EntitiesManager.cpp +++ b/src/entities/EntitiesManager.cpp @@ -7,6 +7,8 @@ #include "common/SpdLogger.h" #include "entities/Entity.h" #include "entities/ComponentFactory.h" +#include "entities/EntityFactory.h" +#include "entities/EntityRegistration.h" #include "xml/tinyxml2.h" @@ -14,6 +16,7 @@ template<> EntitiesManager* Singleton::instance_ = nullptr; EntitiesManager::EntitiesManager(QObject* parent) : QObject(parent) { + Initialize(); } EntitiesManager::~EntitiesManager() { @@ -106,9 +109,6 @@ Entity* EntitiesManager::CreateMesh(const QString& mesh) { SceneComponent* conponent = ComponentFactory::Create("MeshComponent", nullptr); conponent->SetAttribute("mesh", mesh.toStdString().c_str()); conponent->AttachEntity(entity); - - //conponent = ComponentFactory::Create("PathComponent", conponent); - //conponent->AttachEntity(entity); return entity; } @@ -148,3 +148,78 @@ void EntitiesManager::RemoveEntity(Entity* entity) { entities_.erase(entity->GetUUid()); } + +void EntitiesManager::Initialize() { + if (!initialized_) { + EntityRegistration::RegisterAllEntities(); + initialized_ = true; + LOG_INFO("EntitiesManager::Initialize - Entity factory initialized"); + } +} + +Entity* EntitiesManager::CreateEntity(const QString& type, WorkSpace* workspace) { + if (!initialized_) { + Initialize(); + } + + if (!workspace) { + workspace = WorkSpaceManager::Get().GetCurrent(); + } + + Entity* entity = EntityFactory::Get().CreateEntity(type, workspace); + if (entity) { + AddEntity(entity); + if (workspace) { + workspace->AddEntity(entity); + } + LOG_INFO("EntitiesManager::CreateEntity - Created entity of type: {}", type.toStdString()); + } else { + LOG_ERROR("EntitiesManager::CreateEntity - Failed to create entity of type: {}", type.toStdString()); + } + + return entity; +} + +Entity* EntitiesManager::CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace) { + if (!initialized_) { + Initialize(); + } + + if (!workspace) { + workspace = WorkSpaceManager::Get().GetCurrent(); + } + + Entity* entity = EntityFactory::Get().CreateEntityWithComponents(type, mesh, useLabel, workspace); + if (entity) { + AddEntity(entity); + if (workspace) { + workspace->AddEntity(entity); + } + LOG_INFO("EntitiesManager::CreateEntityWithComponents - Created entity with components of type: {}", type.toStdString()); + } else { + LOG_ERROR("EntitiesManager::CreateEntityWithComponents - Failed to create entity of type: {}", type.toStdString()); + } + + return entity; +} + +QStringList EntitiesManager::GetSupportedEntityTypes() const { + if (!initialized_) { + return QStringList(); + } + return EntityFactory::Get().GetRegisteredTypes(); +} + +QString EntitiesManager::GetEntityDisplayName(const QString& type) const { + if (!initialized_) { + return type; + } + return EntityFactory::Get().GetDisplayName(type); +} + +QString EntitiesManager::GetEntityDescription(const QString& type) const { + if (!initialized_) { + return QString(); + } + return EntityFactory::Get().GetDescription(type); +} diff --git a/src/entities/EntitiesManager.h b/src/entities/EntitiesManager.h index 80bdd69a..5d223127 100644 --- a/src/entities/EntitiesManager.h +++ b/src/entities/EntitiesManager.h @@ -9,6 +9,7 @@ #include "xml/tinyxml2.h" class Entity; +class WorkSpace; class EntitiesManager : public QObject, public Singleton { Q_OBJECT @@ -18,12 +19,26 @@ public: ~EntitiesManager(); void OnDestory(); + // Initialize entity factory + void Initialize(); + bool Contains(const QString& name) const; bool Parse(const tinyxml2::XMLElement* element, class WorkSpace* workspce); + // Legacy methods (for backward compatibility) Entity* Create(const QString& name); Entity* CreateMesh(const QString& mesh); + + // New factory methods + Entity* CreateEntity(const QString& type, WorkSpace* workspace = nullptr); + Entity* CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace = nullptr); + + // Get supported entity types + QStringList GetSupportedEntityTypes() const; + QString GetEntityDisplayName(const QString& type) const; + QString GetEntityDescription(const QString& type) const; + bool DeleteEntity(Entity* entity); Entity* GetEntity(const QString& uuid); @@ -36,5 +51,5 @@ protected: private: std::unordered_map entities_; - + bool initialized_{false}; }; \ No newline at end of file diff --git a/src/entities/EntityFactory.cpp b/src/entities/EntityFactory.cpp new file mode 100644 index 00000000..9a46e28a --- /dev/null +++ b/src/entities/EntityFactory.cpp @@ -0,0 +1,129 @@ +#include "entities/EntityFactory.h" +#include "entities/ComponentFactory.h" +#include "common/SpdLogger.h" +#include "workspace/WorkSpace.h" +#include + +template<> EntityFactory* Singleton::instance_ = nullptr; + +Entity* EntityFactory::CreateEntity(const QString& type, WorkSpace* workspace) { + auto it = creators_.find(type); + if (it == creators_.end()) { + LOG_WARN("EntityFactory::CreateEntity - Unknown entity type: {}", type.toStdString()); + return nullptr; + } + + Entity* entity = it->second->CreateEntity(workspace); + if (!entity) { + LOG_ERROR("EntityFactory::CreateEntity - Failed to create entity of type: {}", type.toStdString()); + return nullptr; + } + + LOG_INFO("EntityFactory::CreateEntity - Successfully created entity of type: {}", type.toStdString()); + return entity; +} + +Entity* EntityFactory::CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace) { + Entity* entity = CreateEntity(type, workspace); + if (!entity) { + return nullptr; + } + + auto it = creators_.find(type); + if (it == creators_.end()) { + return entity; + } + + // Get default mesh + QString defaultMesh = it->second->GetDefaultMesh(); + if (!mesh.isEmpty()) { + defaultMesh = mesh; + } + if (!defaultMesh.isEmpty()) { + SceneComponent* meshComponent = ComponentFactory::Create("MeshComponent", nullptr); + if (meshComponent) { + meshComponent->SetAttribute("mesh", defaultMesh.toLocal8Bit()); + meshComponent->AttachEntity(entity); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added MeshComponent with mesh: {}", defaultMesh.toStdString()); + } + } + + if (useLabel) { + SceneComponent* rootComponent = entity->GetRootComponent(); + if (nullptr == rootComponent) { + SceneComponent* meshComponent = ComponentFactory::Create("LabelComponent", nullptr); + if (meshComponent) { + meshComponent->AttachEntity(entity); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added LabelComponent with mesh: {}", defaultMesh.toStdString()); + } + } + else { + SceneComponent* LabelComponent = ComponentFactory::Create("LabelComponent", rootComponent); + if (LabelComponent) { + LabelComponent->AttachTo(rootComponent); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added LabelComponent with mesh: {}", defaultMesh.toStdString()); + } + } + } + + // Add required components + QStringList requiredComponents = it->second->GetRequiredComponents(); + SceneComponent* rootComponent = entity->GetRootComponent(); + + for (int i = 0; i < requiredComponents.size(); ++i) { + QString componentType = requiredComponents.at(i); + SceneComponent* component = ComponentFactory::Create(componentType, rootComponent); + if (component && rootComponent) { + component->AttachTo(rootComponent); + LOG_INFO("EntityFactory::CreateEntityWithComponents - Added component: {}", componentType.toStdString()); + } else { + LOG_WARN("EntityFactory::CreateEntityWithComponents - Failed to create component: {}", componentType.toStdString()); + } + } + + return entity; +} + +QStringList EntityFactory::GetRegisteredTypes() const { + QStringList types; + for (auto it = creators_.begin(); it != creators_.end(); ++it) { + types.append(it->first); + } + return types; +} + +QString EntityFactory::GetDisplayName(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetDisplayName(); + } + return type; // 返回类型名作为默认显示名 +} + +QString EntityFactory::GetDescription(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetDescription(); + } + return QString(); +} + +QString EntityFactory::GetDefaultMesh(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetDefaultMesh(); + } + return QString(); +} + +QStringList EntityFactory::GetRequiredComponents(const QString& type) const { + auto it = creators_.find(type); + if (it != creators_.end()) { + return it->second->GetRequiredComponents(); + } + return QStringList(); +} + +bool EntityFactory::IsTypeSupported(const QString& type) const { + return creators_.find(type) != creators_.end(); +} \ No newline at end of file diff --git a/src/entities/EntityFactory.h b/src/entities/EntityFactory.h new file mode 100644 index 00000000..33e02898 --- /dev/null +++ b/src/entities/EntityFactory.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include + +#include "app/Singleton.h" +#include "entities/Entity.h" + +class WorkSpace; + +class IEntityCreator { +public: + virtual ~IEntityCreator() = default; + virtual Entity* CreateEntity(WorkSpace* workspace) = 0; + virtual QString GetEntityType() const = 0; + virtual QString GetDisplayName() const = 0; + virtual QString GetDescription() const = 0; + virtual QString GetDefaultMesh() const = 0; + virtual QStringList GetRequiredComponents() const = 0; +}; + +template +class EntityCreator : public IEntityCreator { +public: + EntityCreator(const QString& type, const QString& displayName, + const QString& description, const QString& defaultMesh, + const QStringList& requiredComponents = QStringList()) + : entityType_(type), displayName_(displayName), description_(description), + defaultMesh_(defaultMesh), requiredComponents_(requiredComponents) {} + + Entity* CreateEntity(WorkSpace* workspace) override { + return new T(workspace); + } + + QString GetEntityType() const override { return entityType_; } + QString GetDisplayName() const override { return displayName_; } + QString GetDescription() const override { return description_; } + QString GetDefaultMesh() const override { return defaultMesh_; } + QStringList GetRequiredComponents() const override { return requiredComponents_; } + +private: + QString entityType_; + QString displayName_; + QString description_; + QString defaultMesh_; + QStringList requiredComponents_; +}; + +class EntityFactory : public Singleton { +public: + EntityFactory() = default; + explicit EntityFactory(class Application* app) : app_(app) {} + ~EntityFactory() = default; + + void OnDestory() { } + + template + void RegisterEntity(const QString& type, const QString& displayName, + const QString& description, const QString& defaultMesh, + const QStringList& requiredComponents = QStringList()) { + auto creator = std::make_unique>(type, displayName, description, defaultMesh, requiredComponents); + creators_[type] = std::move(creator); + } + + Entity* CreateEntity(const QString& type, WorkSpace* workspace); + + Entity* CreateEntityWithComponents(const QString& type, const QString& mesh, bool useLabel, WorkSpace* workspace); + + QStringList GetRegisteredTypes() const; + + QString GetDisplayName(const QString& type) const; + QString GetDescription(const QString& type) const; + QString GetDefaultMesh(const QString& type) const; + QStringList GetRequiredComponents(const QString& type) const; + + bool IsTypeSupported(const QString& type) const; + +private: + std::unordered_map> creators_; + class Application* app_ = nullptr; +}; + +#define REGISTER_ENTITY(EntityClass, Type, DisplayName, Description, DefaultMesh, ...) \ + EntityFactory::Get().RegisterEntity(Type, DisplayName, Description, DefaultMesh, QStringList{__VA_ARGS__}) \ No newline at end of file diff --git a/src/entities/EntityRegistration.cpp b/src/entities/EntityRegistration.cpp new file mode 100644 index 00000000..ae6a086e --- /dev/null +++ b/src/entities/EntityRegistration.cpp @@ -0,0 +1,88 @@ +#include "entities/EntityRegistration.h" +#include "common/SpdLogger.h" + +void EntityRegistration::RegisterAllEntities() { + EntityFactory& factory = EntityFactory::Get(); + + // Register missile entity + factory.RegisterEntity("Missile", + "Missile", + "Military missile entity with target tracking and flight capabilities", + "models/missile.osg", + QStringList() << "LabelComponent" << "TrajectoryComponent" + ); + + // Register satellite entity + factory.RegisterEntity("Satellite", + "Satellite", + "Artificial satellite entity with orbital motion and communication capabilities", + "models/satellite.osg", + QStringList() << "LabelComponent" << "ConeWaveComponent" << "OrbitComponent" + ); + + // Register ship entity + factory.RegisterEntity("Ship", + "Ship", + "Naval vessel entity with maritime navigation and combat capabilities", + "models/ship.osg", + QStringList() << "LabelComponent" << "NavigationComponent" + ); + + // Register radar entity + factory.RegisterEntity("Radar", + "Radar", + "Radar system entity with target detection and tracking capabilities", + "models/radar.osg", + QStringList() << "LabelComponent" << "RadarComponent" << "ScanComponent" + ); + + // Register vehicle entity + factory.RegisterEntity("Vehicle", + "Vehicle", + "Generic vehicle entity configurable for different types of transportation", + "models/vehicle.osg", + QStringList() << "LabelComponent" << "MovementComponent" + ); + + // Register aircraft entity + factory.RegisterEntity("Aircraft", + "Aircraft", + "Aviation entity with flight and aerial combat capabilities", + "models/aircraft.osg", + QStringList() << "LabelComponent" << "FlightComponent" << "NavigationComponent" + ); + + // Register tank entity + factory.RegisterEntity("Tank", + "Tank", + "Armored vehicle entity with ground combat capabilities", + "models/tank.osg", + QStringList() << "LabelComponent" << "ArmorComponent" << "WeaponComponent" + ); + + // Register submarine entity + factory.RegisterEntity("Submarine", + "Submarine", + "Submarine entity with underwater combat capabilities", + "models/submarine.osg", + QStringList() << "LabelComponent" << "SonarComponent" << "TorpedoComponent" + ); + + // Register base entity + factory.RegisterEntity("Base", + "Base", + "Military base entity serving as command and logistics center", + "models/base.osg", + QStringList() << "LabelComponent" << "CommandComponent" << "DefenseComponent" + ); + + // Register generic entity (for backward compatibility) + factory.RegisterEntity("Entity", + "Entity", + "Basic entity type extensible through component system", + "models/default.osg", + QStringList() << "LabelComponent" + ); + + LOG_INFO("EntityRegistration::RegisterAllEntities - All entity types registered successfully"); +} \ No newline at end of file diff --git a/src/entities/EntityRegistration.h b/src/entities/EntityRegistration.h new file mode 100644 index 00000000..832cd7bf --- /dev/null +++ b/src/entities/EntityRegistration.h @@ -0,0 +1,11 @@ +#pragma once + +#include "entities/EntityFactory.h" +#include "entities/SpecializedEntities.h" + +// Entity registration class +class EntityRegistration { +public: + // Register all entity types + static void RegisterAllEntities(); +}; \ No newline at end of file diff --git a/src/entities/SpecializedEntities.cpp b/src/entities/SpecializedEntities.cpp new file mode 100644 index 00000000..58288a1e --- /dev/null +++ b/src/entities/SpecializedEntities.cpp @@ -0,0 +1,60 @@ +#include "entities/SpecializedEntities.h" + +#include + +#include "common/SpdLogger.h" + +MissileEntity::MissileEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Missile"); + LOG_INFO("MissileEntity created"); +} + +MissileEntity::MissileEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("MissileEntity created with name: {}", name.toStdString()); +} + +SatelliteEntity::SatelliteEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Satellite"); + LOG_INFO("SatelliteEntity created"); +} + +SatelliteEntity::SatelliteEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("SatelliteEntity created with name: {}", name.toStdString()); +} + +ShipEntity::ShipEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Ship"); + LOG_INFO("ShipEntity created"); +} + +ShipEntity::ShipEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("ShipEntity created with name: {}", name.toStdString()); +} + +RadarEntity::RadarEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Radar"); + LOG_INFO("RadarEntity created"); +} + +RadarEntity::RadarEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("RadarEntity created with name: {}", name.toStdString()); +} + +VehicleEntity::VehicleEntity(WorkSpace* workspace) + : Entity(workspace) { + SetName("Vehicle"); + LOG_INFO("VehicleEntity created"); +} + +VehicleEntity::VehicleEntity(const QString& name, QObject* parent) + : Entity(name, parent) { + LOG_INFO("VehicleEntity created with name: {}", name.toStdString()); +} diff --git a/src/entities/SpecializedEntities.h b/src/entities/SpecializedEntities.h new file mode 100644 index 00000000..44c16011 --- /dev/null +++ b/src/entities/SpecializedEntities.h @@ -0,0 +1,58 @@ +#pragma once + +#include "entities/Entity.h" + +// 导弹实体 +class MissileEntity : public Entity { + Q_OBJECT + +public: + explicit MissileEntity(WorkSpace* workspace); + explicit MissileEntity(const QString& name, QObject* parent = nullptr); + ~MissileEntity() override = default; + +}; + +// 卫星实体 +class SatelliteEntity : public Entity { + Q_OBJECT + +public: + explicit SatelliteEntity(WorkSpace* workspace); + explicit SatelliteEntity(const QString& name, QObject* parent = nullptr); + ~SatelliteEntity() override = default; + +}; + +// 舰船实体 +class ShipEntity : public Entity { + Q_OBJECT + +public: + explicit ShipEntity(WorkSpace* workspace); + explicit ShipEntity(const QString& name, QObject* parent = nullptr); + ~ShipEntity() override = default; + +}; + +// 雷达实体 +class RadarEntity : public Entity { + Q_OBJECT + +public: + explicit RadarEntity(WorkSpace* workspace); + explicit RadarEntity(const QString& name, QObject* parent = nullptr); + ~RadarEntity() override = default; + +}; + +// 通用载具实体 +class VehicleEntity : public Entity { + Q_OBJECT + +public: + explicit VehicleEntity(WorkSpace* workspace); + explicit VehicleEntity(const QString& name, QObject* parent = nullptr); + ~VehicleEntity() override = default; + +}; \ No newline at end of file diff --git a/src/scene/OEScene.cpp b/src/scene/OEScene.cpp index f5ce9c5e..2c595a4e 100644 --- a/src/scene/OEScene.cpp +++ b/src/scene/OEScene.cpp @@ -103,6 +103,64 @@ osg::Viewport* OEScene::GetViewport() const { return curentView_->getCamera()->getViewport(); } +bool OEScene::ScreenToWorldCoordinate(int x, int y, osg::Vec3d* output) const { + if (nullptr == output) { + LOG_WARN("OEScene::ScreenToWorldCoordinate - worldPoint is null"); + return false; + } + + osg::Camera* camera = curentView_->getCamera(); + if (!camera) { + LOG_WARN("OEScene::ScreenToWorldCoordinate - camera is null"); + return false; + } + + osg::ref_ptr intersector = + new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y); + + osgUtil::IntersectionVisitor iv(intersector.get()); + camera->accept(iv); + + if (intersector->containsIntersections()) { + osgUtil::LineSegmentIntersector::Intersections& intersections = intersector->getIntersections(); + osgUtil::LineSegmentIntersector::Intersection intersection = *intersections.begin(); + + osg::Vec3d worldPoint = intersection.getWorldIntersectPoint(); + + Vec3ToLocation(worldPoint, output); + + LOG_INFO("OsgWidget::ScreenToWorldCoordinate - Screen({}, {}) -> World({:.6f}, {:.6f}, {:.2f})", + x, y, output->x(), output->y(), output->z()); + return true; + } + + LOG_WARN("OsgWidget::ScreenToWorldCoordinate - No intersection found for screen coordinates ({}, {})", x, y); + return false; +} + +bool OEScene::Vec3ToLocation(const osg::Vec3& input, osg::Vec3d* output) const { + if (nullptr == output) { + LOG_WARN("OEScene::vec3ToLocation - output is null"); + return false; + } + const osgEarth::SpatialReference* srs = OEScene::GetSrs(); + if (!srs) { + LOG_WARN("OEScene::vec3ToLocation - spatial reference system is null"); + return false; + } + + osgEarth::GeoPoint geoPoint; + geoPoint.fromWorld(srs, input); + + const osgEarth::SpatialReference* wgs84 = osgEarth::SpatialReference::get("wgs84"); + if (wgs84) { + geoPoint = geoPoint.transform(wgs84); + } + + output->set(geoPoint.x(), geoPoint.y(),geoPoint.z()); + return true; +} + const osgEarth::SpatialReference* OEScene::GetSrs() { dyt_check(nullptr != g_srs_); return g_srs_; diff --git a/src/scene/OEScene.h b/src/scene/OEScene.h index d56a954c..f3ba50c6 100644 --- a/src/scene/OEScene.h +++ b/src/scene/OEScene.h @@ -35,6 +35,8 @@ public: } osg::Viewport* GetViewport() const; + bool ScreenToWorldCoordinate(int x, int y, osg::Vec3d* output) const; + bool Vec3ToLocation(const osg::Vec3& input, osg::Vec3d* output) const; static const osgEarth::SpatialReference* GetSrs(); diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index 457d64d7..a151b381 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -2110,12 +2110,12 @@ OsgWidget - + warning - + open dyt file failed @@ -2216,31 +2216,6 @@ Preset Models - - - PresetModelToolBox - - - - - Ships - - - - - Satellites - - - - - Missiles - - - - - Jammers - - PropertyBrowser diff --git a/src/ui/Menu/FileManagerMenu.cpp b/src/ui/Menu/FileManagerMenu.cpp index 2b7d4e8c..3bb3fadd 100644 --- a/src/ui/Menu/FileManagerMenu.cpp +++ b/src/ui/Menu/FileManagerMenu.cpp @@ -42,6 +42,7 @@ void FileManagerMenu::InitConnect() { connect(ui->menu_table_file, &QToolButton::clicked, this, &FileManagerMenu::AddTableFile); connect(ui->menu_light_file, &QToolButton::clicked, this, &FileManagerMenu::AddLightFile); connect(ui->menu_polar_file, &QToolButton::clicked, this, &FileManagerMenu::AddPolarFile); + connect(ui->menu_image_file, &QToolButton::clicked, this, &FileManagerMenu::AddImageFile); } void FileManagerMenu::NewWorkSpace() { @@ -277,7 +278,8 @@ void FileManagerMenu::AddLightFile() { } } -void FileManagerMenu::AddPolarFile() { +void FileManagerMenu::AddPolarFile() +{ auto current = WorkSpaceManager::Get().GetCurrent(); if (nullptr == current) { QMessageBox::information(&MainFrame::Get(), QObject::tr("prompt"), QObject::tr("please create workspace first")); @@ -290,4 +292,9 @@ void FileManagerMenu::AddPolarFile() { SaveWorkSpace(); } +} + +void FileManagerMenu::AddImageFile() +{ + } \ No newline at end of file diff --git a/src/ui/Menu/FileManagerMenu.h b/src/ui/Menu/FileManagerMenu.h index 5162b618..b8e8ce92 100644 --- a/src/ui/Menu/FileManagerMenu.h +++ b/src/ui/Menu/FileManagerMenu.h @@ -26,6 +26,7 @@ protected: void AddTableFile(); void AddLightFile(); void AddPolarFile(); + void AddImageFile(); signals: void LoadDyt(const QString& path); diff --git a/src/ui/Menu/FileManagerMenu.ui b/src/ui/Menu/FileManagerMenu.ui index c92bbb90..fa90a83a 100644 --- a/src/ui/Menu/FileManagerMenu.ui +++ b/src/ui/Menu/FileManagerMenu.ui @@ -104,6 +104,16 @@ + + + + new image file + + + + + + diff --git a/src/ui/ModelBrowser/PresetModelListWidget.cpp b/src/ui/ModelBrowser/PresetModelListWidget.cpp new file mode 100644 index 00000000..c79632ca --- /dev/null +++ b/src/ui/ModelBrowser/PresetModelListWidget.cpp @@ -0,0 +1,146 @@ +#include "ui/ModelBrowser/PresetModelListWidget.h" + +#include +#include +#include +#include + +#include "common/RecourceHelper.h" +#include "common/SpdLogger.h" + +PresetModelListWidget::PresetModelListWidget(QWidget* parent) + : QListWidget(parent) +{ + setDragEnabled(true); + setDragDropMode(QAbstractItemView::DragOnly); + + // Configure list widget for icon-text vertical layout + setViewMode(QListView::IconMode); + setResizeMode(QListView::Adjust); + setMovement(QListView::Static); + setGridSize(QSize(100, 80)); // Width x Height for each item + setIconSize(QSize(48, 48)); // Icon size + setSpacing(5); // Spacing between items +} + +void PresetModelListWidget::startDrag(Qt::DropActions supportedActions) { + QListWidgetItem* item = currentItem(); + if (nullptr == item) { + return; + } + + QString modelType = property("modelType").toString(); + QString modelName = item->data(Qt::UserRole).toString(); + if (modelName.isEmpty()) { + modelName = item->text(); // Fallback to item text + } + + QMimeData* mimeData = new QMimeData; + + QString dragData = QString("%1|%2").arg(modelType, modelName); + mimeData->setData("application/x-preset-model", dragData.toUtf8()); + + // Create drag object + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + + // Create drag pixmap with icon on top and text below + const int iconSize = 32; + const int textHeight = 20; + const int totalWidth = 80; + const int totalHeight = iconSize + textHeight + 5; // 5px spacing between icon and text + + QPixmap pixmap(totalWidth, totalHeight); + pixmap.fill(Qt::transparent); + + QPainter painter(&pixmap); + painter.setRenderHint(QPainter::Antialiasing); + + // Draw semi-transparent background + painter.setBrush(QColor(255, 255, 255, 50)); // White with 70% opacity + painter.setPen(QPen(QColor(200, 200, 200, 200), 1)); // Light gray border + painter.drawRoundedRect(2, 2, totalWidth - 4, totalHeight - 4, 4, 4); + + // Draw icon at the top center + QPixmap iconPixmap = item->icon().pixmap(iconSize, iconSize); + if (!iconPixmap.isNull()) { + int iconX = (totalWidth - iconSize) / 2; + painter.drawPixmap(iconX, 0, iconPixmap); + } else { + // Draw a default placeholder icon + int iconX = (totalWidth - iconSize) / 2; + painter.setPen(QPen(Qt::gray, 2)); + painter.setBrush(Qt::lightGray); + painter.drawRect(iconX, 0, iconSize, iconSize); + painter.setPen(Qt::black); + painter.drawText(iconX, 0, iconSize, iconSize, Qt::AlignCenter, "?"); + } + + // Draw text below the icon + painter.setPen(Qt::black); + QFont font = painter.font(); + font.setPointSize(8); + painter.setFont(font); + + QRect textRect(0, iconSize + 5, totalWidth, textHeight); + painter.drawText(textRect, Qt::AlignCenter | Qt::TextWordWrap, item->text()); + drag->setPixmap(pixmap); + + // Set hot spot to the center of the icon (not the entire pixmap) + int hotSpotX = totalWidth / 2; + int hotSpotY = iconSize / 2; // Center of the icon part only + drag->setHotSpot(QPoint(hotSpotX, hotSpotY)); + + // Execute drag + drag->exec(Qt::CopyAction); + +} + +void PresetModelListWidget::setModelType(const QString& modelType) { + modelType_ = modelType; + setProperty("modelType", modelType_); +} + +QString PresetModelListWidget::getModelType() const { + return modelType_; +} + +void PresetModelListWidget::setModelList(const QVector& models) { + modelList_ = models; + + clear(); + + for (const ModelInfo& modelInfo : models) { + if (!modelInfo.enabled) continue; // Skip disabled models + + QListWidgetItem* item = new QListWidgetItem(modelInfo.displayName.isEmpty() ? modelInfo.name : modelInfo.displayName); + item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + + // Use icon from ModelInfo if available + if (!modelInfo.icon.isEmpty()) { + QString presetsPath = QString("%1/%2").arg(RecourceHelper::Get().GetResourcesPath(), modelInfo.icon); + QIcon icon(presetsPath); + item->setIcon(icon); + } else { + // Create a default icon if no icon is specified + QPixmap pixmap(48, 48); + pixmap.fill(Qt::lightGray); + QPainter painter(&pixmap); + painter.setPen(Qt::black); + painter.drawRect(0, 0, 47, 47); + painter.drawText(pixmap.rect(), Qt::AlignCenter, "?"); + item->setIcon(QIcon(pixmap)); + } + + item->setToolTip(modelInfo.description.isEmpty() ? modelInfo.name : modelInfo.description); + + // Store model name as simple string + item->setData(Qt::UserRole, modelInfo.name); + + addItem(item); + } +} + +QVector PresetModelListWidget::getModelList() const { + return modelList_; +} diff --git a/src/ui/ModelBrowser/PresetModelListWidget.h b/src/ui/ModelBrowser/PresetModelListWidget.h new file mode 100644 index 00000000..96d30d32 --- /dev/null +++ b/src/ui/ModelBrowser/PresetModelListWidget.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "workspace/ModelInfo.h" + +class PresetModelListWidget : public QListWidget +{ + Q_OBJECT +public: + explicit PresetModelListWidget(QWidget* parent = nullptr); + + void setModelType(const QString& modelType); + QString getModelType() const; + + void setModelList(const QVector& models); + QVector getModelList() const; + +protected: + void startDrag(Qt::DropActions supportedActions); + +private: + QString modelType_; + QVector modelList_; +}; diff --git a/src/ui/ModelBrowser/PresetModelPanel.cpp b/src/ui/ModelBrowser/PresetModelPanel.cpp index 01d8d18a..2fd26676 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.cpp +++ b/src/ui/ModelBrowser/PresetModelPanel.cpp @@ -8,14 +8,19 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include "common/RecourceHelper.h" #include "common/SpdLogger.h" #include "ui/DockTitleBar.h" #include "ui/DockWidget.h" +#include "workspace/PresetModelConfigParser.h" #include "ui_PresetModelPanel.h" @@ -23,6 +28,7 @@ PresetModelPanel::PresetModelPanel(QWidget *parent) : QWidget(parent) , ui(new Ui::PresetModelPanel) + , dragSourceWidget_(nullptr) { ui->setupUi(this); InitUI(); @@ -30,6 +36,7 @@ PresetModelPanel::PresetModelPanel(QWidget *parent) PresetModelPanel::~PresetModelPanel() { + ClearDynamicUI(); delete ui; } @@ -50,175 +57,100 @@ void PresetModelPanel::AttachDock(DockWidget* dockWidget) void PresetModelPanel::InitUI() { // Try to load model data from configuration files - if (m_configParser.LoadAllConfigs("workspace/presets")) { + QString presetsPath = QString("%1/presets").arg(RecourceHelper::Get().GetResourcesPath()); + if (PresetModelConfigParser::Get().LoadAllConfigs(presetsPath)) { // Successfully loaded from configuration files + CreateDynamicUI(); LoadModelsFromConfig(); } else { // Failed to load configuration files, use hardcoded defaults as fallback LOG_WARN("Failed to load preset models from config files, using hardcoded defaults. Error: {}", - m_configParser.GetLastError().toStdString()); - LoadDefaultModels(); + PresetModelConfigParser::Get().GetLastError().toStdString()); } } -void PresetModelPanel::LoadModelsFromConfig() +void PresetModelPanel::CreateDynamicUI() { - // Load ship models - QStringList shipModels = m_configParser.GetModelNames("Ship"); - if (!shipModels.isEmpty()) { - PopulateModelList(ui->shipList, "Ship", shipModels); + // Clear any existing dynamic UI + ClearDynamicUI(); + + // Get all category names from configuration + QStringList categoryNames = PresetModelConfigParser::Get().GetCategoryNames(); + + if (categoryNames.isEmpty()) { + LOG_WARN("No categories found in configuration"); + return; } - // Load satellite models - QStringList satelliteModels = m_configParser.GetModelNames("Satellite"); - if (!satelliteModels.isEmpty()) { - PopulateModelList(ui->satelliteList, "Satellite", satelliteModels); - } - - // Load missile models - QStringList missileModels = m_configParser.GetModelNames("Missile"); - if (!missileModels.isEmpty()) { - PopulateModelList(ui->missileList, "Missile", missileModels); - } - - // Load jammer models - QStringList jammerModels = m_configParser.GetModelNames("Jammer"); - if (!jammerModels.isEmpty()) { - PopulateModelList(ui->jammerList, "Jammer", jammerModels); - } -} - -void PresetModelPanel::LoadDefaultModels() -{ - // Use hardcoded default models as fallback mechanism - QStringList shipModels = { - "Destroyer", - "Aircraft Carrier", - "Frigate", - "Submarine" - }; - PopulateModelList(ui->shipList, "Ship", shipModels); - - QStringList satelliteModels = { - "Communication Satellite", - "Weather Satellite", - "GPS Satellite", - "Spy Satellite" - }; - PopulateModelList(ui->satelliteList, "Satellite", satelliteModels); - - QStringList missileModels = { - "Cruise Missile", - "Ballistic Missile", - "Anti-Ship Missile", - "Surface-to-Air Missile" - }; - PopulateModelList(ui->missileList, "Missile", missileModels); - - QStringList jammerModels = { - "Electronic Jammer", - "Communication Jammer", - "Radar Jammer", - "GPS Jammer" - }; - PopulateModelList(ui->jammerList, "Jammer", jammerModels); -} - -void PresetModelPanel::PopulateModelList(QListWidget* listWidget, const QString& modelType, const QStringList& models) -{ - if (!listWidget) return; - - // Set the model type as a property for later use - listWidget->setProperty("modelType", modelType); - - for (const QString& model : models) { - QListWidgetItem* item = new QListWidgetItem(model); - item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + // Create UI for each category + for (const QString& categoryName : categoryNames) { + ModelCategory category = PresetModelConfigParser::Get().GetCategory(categoryName); - if (modelType == "Ship") { - item->setIcon(QIcon(":/icons/ship.png")); - } else if (modelType == "Satellite") { - item->setIcon(QIcon(":/icons/satellite.png")); - } else if (modelType == "Missile") { - item->setIcon(QIcon(":/icons/missile.png")); - } else if (modelType == "Jammer") { - item->setIcon(QIcon(":/icons/jammer.png")); + // Create page widget + QWidget* page = new QWidget(); + page->setObjectName(QString("%1Page").arg(categoryName.toLower())); + + // Create layout for the page + QVBoxLayout* layout = new QVBoxLayout(page); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + // Create list widget + PresetModelListWidget* listWidget = new PresetModelListWidget(page); + listWidget->setObjectName(QString("%1List").arg(categoryName.toLower())); + listWidget->setDragDropMode(QAbstractItemView::DragOnly); + listWidget->setDefaultDropAction(Qt::CopyAction); + + // Add list widget to layout + layout->addWidget(listWidget); + + // Add page to toolbox with display name + QString displayName = category.displayName.isEmpty() ? categoryName : category.displayName; + ui->PresetModelToolBox->addItem(page, displayName); + + // Store references for later use + categoryWidgets_[categoryName] = listWidget; + categoryPages_[categoryName] = page; + + LOG_INFO("Created dynamic UI for category: {}", categoryName.toStdString()); + } + + // Set first page as current if available + if (ui->PresetModelToolBox->count() > 0) { + ui->PresetModelToolBox->setCurrentIndex(0); + } +} + +void PresetModelPanel::LoadModelsFromConfig() { + // Load models for each dynamically created category + QStringList categoryNames = PresetModelConfigParser::Get().GetCategoryNames(); + + for (const QString& categoryName : categoryNames) { + ModelCategory category = PresetModelConfigParser::Get().GetCategory(categoryName); + + if (!category.models.isEmpty() && categoryWidgets_.contains(categoryName)) { + PresetModelListWidget* listWidget = categoryWidgets_[categoryName]; + listWidget->setModelType(categoryName); + listWidget->setModelList(category.models); + + LOG_INFO("Loaded {} models for category: {}", + category.models.size(), categoryName.toStdString()); } - - listWidget->addItem(item); } - - connect(listWidget, &QListWidget::itemDoubleClicked, this, &PresetModelPanel::OnModelItemDoubleClicked); } -void PresetModelPanel::SetupDragAndDrop(QListWidget* listWidget) +void PresetModelPanel::ClearDynamicUI() { - listWidget->setDragDropMode(QAbstractItemView::DragOnly); - listWidget->setDefaultDropAction(Qt::CopyAction); + // Clear all dynamic UI elements + categoryWidgets_.clear(); + categoryPages_.clear(); + + // Remove all pages from toolbox + while (ui->PresetModelToolBox->count() > 0) { + QWidget* widget = ui->PresetModelToolBox->widget(0); + ui->PresetModelToolBox->removeItem(0); + if (widget) { + widget->deleteLater(); + } + } } - -void PresetModelPanel::OnModelItemDoubleClicked(QListWidgetItem* item) -{ - if (!item) return; - - QListWidget* listWidget = item->listWidget(); - if (!listWidget) return; - - QString modelType = listWidget->property("modelType").toString(); - QString modelName = item->text(); - - emit ModelDropped(modelType, modelName); -} - -// DraggableListWidget implementation -DraggableListWidget::DraggableListWidget(const QString& modelType, QWidget* parent) - : QListWidget(parent), modelType_(modelType) -{ - setProperty("modelType", modelType); -} - -void DraggableListWidget::mousePressEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) { - dragStartPosition_ = event->pos(); - } - QListWidget::mousePressEvent(event); -} - -void DraggableListWidget::mouseMoveEvent(QMouseEvent* event) { - if (!(event->buttons() & Qt::LeftButton)) { - return; - } - - if ((event->pos() - dragStartPosition_).manhattanLength() < QApplication::startDragDistance()) { - return; - } - - QListWidgetItem* item = itemAt(dragStartPosition_); - if (!item) { - return; - } - - QDrag* drag = new QDrag(this); - QMimeData* mimeData = new QMimeData; - - QString modelType = this->property("modelType").toString(); - QString modelName = item->text(); - QString dragData = QString("%1|%2").arg(modelType, modelName); - - mimeData->setData("application/x-preset-model", dragData.toUtf8()); - drag->setMimeData(mimeData); - - QPixmap pixmap(item->icon().pixmap(32, 32)); - if (pixmap.isNull()) { - pixmap = QPixmap(100, 30); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - painter.setPen(Qt::black); - painter.drawText(pixmap.rect(), Qt::AlignCenter, modelName); - } - drag->setPixmap(pixmap); - - Qt::DropAction dropAction = drag->exec(Qt::CopyAction); - Q_UNUSED(dropAction); -} \ No newline at end of file diff --git a/src/ui/ModelBrowser/PresetModelPanel.h b/src/ui/ModelBrowser/PresetModelPanel.h index d73543c3..bd0e24d5 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.h +++ b/src/ui/ModelBrowser/PresetModelPanel.h @@ -10,22 +10,25 @@ #include #include #include -#include "PresetModelConfigParser.h" +#include +#include +#include +#include +#include + +#include "workspace/ModelInfo.h" +#include "ui/ModelBrowser/PresetModelListWidget.h" -QT_BEGIN_NAMESPACE namespace Ui { class PresetModelPanel; } -QT_END_NAMESPACE class DockWidget; + /** * @brief Preset model panel class * - * Provides preset 3D models for users to drag into the 3D scene, including: - * - Ships: Destroyer, Aircraft Carrier - * - Satellites: Geostationary Satellites - * - Missiles: Hypersonic Missiles - * - Jammers: Passive Jammers, Active Jammers + * Provides preset 3D models for users to drag into the 3D scene. + * Dynamically creates UI based on configuration files, supporting any number of categories. */ class PresetModelPanel : public QWidget { Q_OBJECT @@ -40,22 +43,6 @@ public: */ void AttachDock(DockWidget* dockWidget); -Q_SIGNALS: - /** - * @brief Signal emitted when model is dragged to scene - * @param modelType Model type - * @param modelName Model name - * @param position Scene position (optional) - */ - void ModelDropped(const QString& modelType, const QString& modelName, const QPointF& position = QPointF()); - -private slots: - /** - * @brief Handle model item double click event - * @param item Clicked item - */ - void OnModelItemDoubleClicked(QListWidgetItem* item); - private: /** * @brief Initialize user interface @@ -63,18 +50,9 @@ private: void InitUI(); /** - * @brief Populate model list with items - * @param listWidget List widget to populate - * @param modelType Model type - * @param models Model name list + * @brief Create dynamic UI based on configuration */ - void PopulateModelList(QListWidget* listWidget, const QString& modelType, const QStringList& models); - - /** - * @brief Setup drag and drop functionality - * @param listWidget List widget to setup drag and drop - */ - void SetupDragAndDrop(QListWidget* listWidget); + void CreateDynamicUI(); /** * @brief Load models from configuration file @@ -82,32 +60,18 @@ private: void LoadModelsFromConfig(); /** - * @brief Load default models when config is unavailable + * @brief Clear all dynamic UI elements */ - void LoadDefaultModels(); + void ClearDynamicUI(); private: Ui::PresetModelPanel *ui; - PresetModelConfigParser m_configParser; -}; - -/** - * @brief Custom list widget with drag and drop support - */ -class DraggableListWidget : public QListWidget { - Q_OBJECT - -public: - explicit DraggableListWidget(const QString& modelType, QWidget* parent = nullptr); - -Q_SIGNALS: - void ItemDragged(const QString& modelType, const QString& modelName); - -protected: - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - -private: - QString modelType_; + + // Dynamic UI management + QMap categoryWidgets_; + QMap categoryPages_; + + // Drag and drop support QPoint dragStartPosition_; -}; \ No newline at end of file + QListWidget* dragSourceWidget_; +}; diff --git a/src/ui/ModelBrowser/PresetModelPanel.ui b/src/ui/ModelBrowser/PresetModelPanel.ui index 8df142c3..8cb54ce0 100644 --- a/src/ui/ModelBrowser/PresetModelPanel.ui +++ b/src/ui/ModelBrowser/PresetModelPanel.ui @@ -14,133 +14,30 @@ Preset Models - - 5 - - - 5 - - - 5 - - - 5 - 5 + + 0 + + + 0 + + + 0 + + + 0 + - - - PresetModelToolBox - + - 0 + -1 - - - - 0 - 0 - 290 - 540 - - - - Ships - - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - - - - - - 0 - 0 - 290 - 540 - - - - Satellites - - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - - - - - - 0 - 0 - 290 - 540 - - - - Missiles - - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - - - - - - 0 - 0 - 290 - 540 - - - - Jammers - - - - - - QAbstractItemView::DragOnly - - - Qt::CopyAction - - - - - - \ No newline at end of file + diff --git a/src/ui/Panel/CurvePanel.cpp b/src/ui/Panel/CurvePanel.cpp index cffb39f1..ff2e15ca 100644 --- a/src/ui/Panel/CurvePanel.cpp +++ b/src/ui/Panel/CurvePanel.cpp @@ -434,6 +434,12 @@ void CurvePanel::updateParseWaveFile(const QString& strFile, int nT, FileEntryCu return; } + curveChart->removeAllSeries(); + m_seriesIDMap.clear(); + + m_dataWava.clear(); + m_dataReport.clear(); + QFile file(strFile); if (file.open(QIODevice::ReadOnly)) { @@ -546,6 +552,12 @@ void CurvePanel::updateParseReportFile(const QString & strFile, int nT, FileEntr return; } + curveChart->removeAllSeries(); + m_seriesIDMap.clear(); + + m_dataWava.clear(); + m_dataReport.clear(); + QFile file(strFile); if (file.open(QIODevice::ReadOnly)) { diff --git a/src/ui/Panel/LightPanel.cpp b/src/ui/Panel/LightPanel.cpp index 2a23104d..2e786bc4 100644 --- a/src/ui/Panel/LightPanel.cpp +++ b/src/ui/Panel/LightPanel.cpp @@ -45,7 +45,9 @@ void LightPanel::RefreshPanel() void LightPanel::InitUI() { - + QGridLayout* pMainLyt = new QGridLayout(this); + pMainLyt->setContentsMargins(5, 0, 5, 0); + setLayout(pMainLyt); } QString LightPanel::GetTypeDisplayName() const @@ -111,10 +113,19 @@ void LightPanel::updateParseFile(const QString & strFile, int nT, FileEntryLight return; } + m_dataLamp.clear(); + + clearLightPanel(); + + QGridLayout* layout = qobject_cast(this->layout()); + if (!layout) + { + return; + } + QFile file(strFile); if (file.open(QIODevice::ReadOnly)) { - QGridLayout* pMainLyt = new QGridLayout(this); for (int nI = 0; nI < listCurve.size(); nI++) { FileEntryLight::LightRowProperty prop = listCurve.at(nI); @@ -134,14 +145,12 @@ void LightPanel::updateParseFile(const QString & strFile, int nT, FileEntryLight pLyt->addWidget(pLampLab); pLyt->addWidget(pTextLab); - pMainLyt->addLayout(pLyt, nI, i); + layout->addLayout(pLyt, nI, i); QString strKey = QString::number(nI) + "-" + QString::number(i); m_mapLamp.insert(strKey, pLampLab); } } - pMainLyt->setContentsMargins(5, 0, 5, 0); - setLayout(pMainLyt); while (!file.atEnd()) { @@ -174,6 +183,8 @@ void LightPanel::updateParseFile(const QString & strFile, int nT, FileEntryLight void LightPanel::updateLampColor(const QString & strOpenColor, const QString & strCloseColor) { + m_lampColor.clear(); + { QString strStyle = "QLabel{background-color: rgb(" + strCloseColor + ");border-radius: 10px;}; "; m_lampColor.insert(0, strStyle); @@ -182,4 +193,33 @@ void LightPanel::updateLampColor(const QString & strOpenColor, const QString & s QString strStyle = "QLabel{background-color: rgb(" + strOpenColor + ");border-radius: 10px;}; "; m_lampColor.insert(1, strStyle); } +} + +void LightPanel::clearLightPanel() +{ + if (auto* layout = qobject_cast(this->layout())) + { + while (layout->count() > 0) + { + QLayoutItem* item = layout->takeAt(0); + if (item) + { + auto* childLayout = item->layout(); + while (childLayout->count() > 0) + { + QLayoutItem* itemChild = childLayout->takeAt(0); + if (itemChild) + { + if (auto* w = itemChild->widget()) + { + w->deleteLater(); + } + delete itemChild; + } + } + delete item; + } + } + } + m_mapLamp.clear(); } \ No newline at end of file diff --git a/src/ui/Panel/LightPanel.h b/src/ui/Panel/LightPanel.h index 9fe085a5..e13b56e1 100644 --- a/src/ui/Panel/LightPanel.h +++ b/src/ui/Panel/LightPanel.h @@ -64,6 +64,8 @@ private: void updateParseFile(const QString& strFile, int nT, FileEntryLight::LightRowProperties listCurve); void updateLampColor(const QString& strOpenColor, const QString& strCloseColor); + void clearLightPanel(); + private: QMap m_lampColor; QMap m_mapLamp; diff --git a/src/ui/Panel/SurfacePanel.cpp b/src/ui/Panel/SurfacePanel.cpp index 4076060f..a4c7aa0f 100644 --- a/src/ui/Panel/SurfacePanel.cpp +++ b/src/ui/Panel/SurfacePanel.cpp @@ -234,6 +234,9 @@ void SurfacePanel::updateParseFile(const QString & strFile, int nT, FileEntrySur return; } + m_data.clear(); + m_pSeries->dataProxy()->resetArray(nullptr); + QFile file(strFile); if (file.open(QIODevice::ReadOnly)) { diff --git a/src/ui/Panel/TablePanel.cpp b/src/ui/Panel/TablePanel.cpp index 99beb1f3..5c665a97 100644 --- a/src/ui/Panel/TablePanel.cpp +++ b/src/ui/Panel/TablePanel.cpp @@ -122,6 +122,10 @@ void TablePanel::updateParseFile(const QString & strFile, int nT, FileEntryTable return; } + m_tableSetting.clear(); + m_dataTable.clear(); + clearTable(); + QFile file(strFile); if (file.open(QIODevice::ReadOnly)) { diff --git a/src/ui/WorkSpace/AddCurveFileDlg.cpp b/src/ui/WorkSpace/AddCurveFileDlg.cpp index 7ea24b7b..60602ac6 100644 --- a/src/ui/WorkSpace/AddCurveFileDlg.cpp +++ b/src/ui/WorkSpace/AddCurveFileDlg.cpp @@ -54,8 +54,8 @@ void AddCurveFileDlg::setupConnections() { connect(ui->curveNameEdit, &QLineEdit::textChanged, this, &AddCurveFileDlg::onCurveNameChanged); connect(ui->dataStartSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); connect(ui->dataStopSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); - connect(ui->xValueSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); - connect(ui->yValueSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); + connect(ui->xValueSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); + connect(ui->yValueSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); // Dialog buttons connect(ui->addBtn, &QPushButton::clicked, this, &AddCurveFileDlg::onSure); @@ -87,9 +87,11 @@ void AddCurveFileDlg::updateFileInfo(const QString& filePath) { ui->filePathEdit->setText(filePath); } -void AddCurveFileDlg::onAddCurveClicked() { +void AddCurveFileDlg::onAddCurveClicked() +{ // Save current curve properties if any curve is selected - if (currentCurveIndex_ >= 0) { + if (currentCurveIndex_ >= 0) + { saveCurveProperties(); } @@ -99,10 +101,13 @@ void AddCurveFileDlg::onAddCurveClicked() { newCurve.color = generateCurveColor(); // Set default values based on chart type - if (chartProperties_.chartType == ChartType::Wave) { + if (chartProperties_.chartType == ChartType::Wave) + { newCurve.data.wave.start = 1; newCurve.data.wave.stop = 241; - } else { + } + else + { newCurve.data.report.x = 0.0; newCurve.data.report.y = 0.0; } @@ -121,7 +126,7 @@ void AddCurveFileDlg::onAddCurveClicked() { .arg(newCurve.color.green()) .arg(newCurve.color.blue()); } else { - displayText = QString("%1 (%.2f,%.2f) (%4,%5,%6)") + displayText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(newCurve.name) .arg(newCurve.data.report.x) .arg(newCurve.data.report.y) @@ -134,6 +139,8 @@ void AddCurveFileDlg::onAddCurveClicked() { ui->curveListWidget->addItem(item); ++currentCurveIndex_; + ui->curveNameEdit->setText(newCurve.name); + // Select the new curve ui->curveListWidget->setCurrentRow(curves_.size() - 1); } @@ -169,21 +176,36 @@ void AddCurveFileDlg::onRemoveCurveClicked() { } void AddCurveFileDlg::onCurveListWidgetItemClicked(QListWidgetItem* item) { - if (!item) { + if (!item) + { return; } int clickedIndex = ui->curveListWidget->row(item); - if (clickedIndex == currentCurveIndex_) { + if (clickedIndex == currentCurveIndex_) + { ui->curveNameEdit->setText(curves_[currentCurveIndex_].name); - if (chartProperties_.chartType == ChartType::Wave) { + if (chartProperties_.chartType == ChartType::Wave) + { + ui->dataStartSpinBox->blockSignals(true); ui->dataStartSpinBox->setValue(curves_[currentCurveIndex_].data.wave.start); + ui->dataStartSpinBox->blockSignals(false); + + ui->dataStopSpinBox->blockSignals(true); ui->dataStopSpinBox->setValue(curves_[currentCurveIndex_].data.wave.stop); - } else { + ui->dataStopSpinBox->blockSignals(false); + } + else + { + ui->xValueSpinBox->blockSignals(true); ui->xValueSpinBox->setValue(curves_[currentCurveIndex_].data.report.x); + ui->xValueSpinBox->blockSignals(false); + + ui->yValueSpinBox->blockSignals(true); ui->yValueSpinBox->setValue(curves_[currentCurveIndex_].data.report.y); + ui->yValueSpinBox->blockSignals(false); } updateColorPreview(curves_[currentCurveIndex_].color); @@ -234,7 +256,7 @@ void AddCurveFileDlg::onCurveNameChanged() { .arg(curves_[currentCurveIndex_].color.green()) .arg(curves_[currentCurveIndex_].color.blue()); } else { - displayText = QString("%1 (%.2f,%.2f) (%4,%5,%6)") + displayText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(newName) .arg(curves_[currentCurveIndex_].data.report.x) .arg(curves_[currentCurveIndex_].data.report.y) @@ -343,18 +365,32 @@ void AddCurveFileDlg::addCurveToList(const FileEntryCurve::CurveProperty& curve) } void AddCurveFileDlg::updateCurveProperties() { - if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { + if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) + { const FileEntryCurve::CurveProperty& curve = curves_[currentCurveIndex_]; ui->curveNameEdit->setText(curve.name); // Update properties based on chart type - if (chartProperties_.chartType == ChartType::Wave) { + if (chartProperties_.chartType == ChartType::Wave) + { + ui->dataStartSpinBox->blockSignals(true); ui->dataStartSpinBox->setValue(curve.data.wave.start); + ui->dataStartSpinBox->blockSignals(false); + + ui->dataStopSpinBox->blockSignals(true); ui->dataStopSpinBox->setValue(curve.data.wave.stop); - } else { + ui->dataStopSpinBox->blockSignals(false); + } + else + { + ui->xValueSpinBox->blockSignals(true); ui->xValueSpinBox->setValue(curve.data.report.x); + ui->xValueSpinBox->blockSignals(false); + + ui->yValueSpinBox->blockSignals(true); ui->yValueSpinBox->setValue(curve.data.report.y); + ui->yValueSpinBox->blockSignals(false); } selectedColor_ = curve.color; @@ -561,12 +597,12 @@ bool AddCurveFileDlg::validateSpecificParams() { double yMin = ui->yMinSpinBox->value(); double yMax = ui->yMaxSpinBox->value(); - if (xMin >= xMax) { + if (xMin > xMax) { QMessageBox::warning(this, tr("Validation Error"), tr("X axis minimum value must be less than maximum value.")); return false; } - if (yMin >= yMax) { + if (yMin > yMax) { QMessageBox::warning(this, tr("Validation Error"), tr("Y axis minimum value must be less than maximum value.")); return false; } @@ -580,11 +616,11 @@ bool AddCurveFileDlg::validateSpecificParams() { } // X axis tick count validation - int xTickCount = ui->xCountSpinBox->value(); - if (xTickCount < 2) { - QMessageBox::warning(this, tr("Validation Error"), tr("X axis tick count must be at least 2.")); - return false; - } + //int xTickCount = ui->xCountSpinBox->value(); + //if (xTickCount < 2) { + // QMessageBox::warning(this, tr("Validation Error"), tr("X axis tick count must be at least 2.")); + // return false; + //} return true; } diff --git a/src/ui/WorkSpace/AddCurveFileDlg.ui b/src/ui/WorkSpace/AddCurveFileDlg.ui index ff42ec12..d38df86f 100644 --- a/src/ui/WorkSpace/AddCurveFileDlg.ui +++ b/src/ui/WorkSpace/AddCurveFileDlg.ui @@ -2,12 +2,15 @@ AddCurveFileDlg + + true + 0 0 600 - 789 + 842 @@ -287,7 +290,7 @@ - 2 + 0 50 @@ -377,7 +380,7 @@ - 2 + 0 50 @@ -543,7 +546,7 @@ Selected Curve Properties - + @@ -609,7 +612,7 @@ 50 - 25 + 0 @@ -701,8 +704,11 @@ - - false + + + 0 + 25 + X Value: @@ -710,25 +716,31 @@ - - - false + + + + 0 + 25 + - -999999.000000000000000 + 1 - 999999.000000000000000 + 999999 - 0.000000000000000 + 1 - - false + + + 0 + 25 + Y Value: @@ -736,18 +748,21 @@ - - - false + + + + 0 + 25 + - -999999.000000000000000 + 1 - 999999.000000000000000 + 999999 - 0.000000000000000 + 2 diff --git a/src/ui/WorkSpace/AddLightFileDlg.cpp b/src/ui/WorkSpace/AddLightFileDlg.cpp index a5986229..587a3bd4 100644 --- a/src/ui/WorkSpace/AddLightFileDlg.cpp +++ b/src/ui/WorkSpace/AddLightFileDlg.cpp @@ -230,17 +230,24 @@ void AddLightFileDlg::addLightToList(const FileEntryLight::LightRowProperty& lig ui->lightListWidget->addItem(item); } -void AddLightFileDlg::updateLightProperties() { - if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { +void AddLightFileDlg::updateLightProperties() +{ + if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) + { const FileEntryLight::LightRowProperty& light = lights_[currentLightIndex_]; + + ui->lightNameEdit->blockSignals(true); ui->lightNameEdit->setText(light.name.join(", ")); + ui->lightNameEdit->blockSignals(false); // Update data edit QStringList dataStrings; for (int dataValue : light.data) { dataStrings.append(QString::number(dataValue)); } + ui->lightDataEdit->blockSignals(true); ui->lightDataEdit->setText(dataStrings.join(", ")); + ui->lightDataEdit->blockSignals(false); // Update row index display ui->rowIndexValue->setText(QString::number(currentLightIndex_)); @@ -290,15 +297,22 @@ QString AddLightFileDlg::generateLightName() { return QString("Light_%1").arg(lights_.size() + 1); } -void AddLightFileDlg::updateDataDisplay() { - if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) { +void AddLightFileDlg::updateDataDisplay() +{ + if (currentLightIndex_ >= 0 && currentLightIndex_ < lights_.size()) + { const FileEntryLight::LightRowProperty& light = lights_[currentLightIndex_]; QStringList dataStrings; - for (int dataValue : light.data) { + for (int dataValue : light.data) + { dataStrings.append(QString::number(dataValue)); } + ui->lightDataEdit->blockSignals(true); ui->lightDataEdit->setText(dataStrings.join(", ")); - } else { + ui->lightDataEdit->blockSignals(false); + } + else + { ui->lightDataEdit->clear(); } } @@ -376,6 +390,7 @@ void AddLightFileDlg::onSure() { if (lightEntry) { lightEntry->SetName(sName); + colorProperties_.timeParam = ui->SpinBox_time->value(); // Set color properties lightEntry->SetColorProperties(colorProperties_); diff --git a/src/ui/WorkSpace/AddLightFileDlg.ui b/src/ui/WorkSpace/AddLightFileDlg.ui index c5b7db95..9bbb3997 100644 --- a/src/ui/WorkSpace/AddLightFileDlg.ui +++ b/src/ui/WorkSpace/AddLightFileDlg.ui @@ -123,7 +123,7 @@ - Chart Names: + Chart Name: @@ -135,11 +135,37 @@ 25 + + Chart 1 + + + + + + 0 + 25 + + + + Time: + + + + + + + + 0 + 25 + + + + diff --git a/src/ui/WorkSpace/AddPolarFileDlg.cpp b/src/ui/WorkSpace/AddPolarFileDlg.cpp index 15d355a1..d428d0d4 100644 --- a/src/ui/WorkSpace/AddPolarFileDlg.cpp +++ b/src/ui/WorkSpace/AddPolarFileDlg.cpp @@ -539,7 +539,7 @@ void AddPolarFileDlg::onSure() QString errorMsg; switch (result) { case WorkSpace::FileEntryResult::LimitExceeded: - errorMsg = tr("Curve file count has reached the limit (9 files)"); + errorMsg = tr("Polar file count has reached the limit (9 files)"); break; case WorkSpace::FileEntryResult::Duplicate: errorMsg = tr("File already exists"); @@ -560,6 +560,10 @@ void AddPolarFileDlg::onSure() accept(); } + else + { + QMessageBox::critical(this, tr("Error"), tr("Failed to create Polar file entry.")); + } } void AddPolarFileDlg::onCurveDataChanged() diff --git a/src/ui/WorkSpace/AddSurfaceFileDlg.cpp b/src/ui/WorkSpace/AddSurfaceFileDlg.cpp index b62842e6..d4bcbc95 100644 --- a/src/ui/WorkSpace/AddSurfaceFileDlg.cpp +++ b/src/ui/WorkSpace/AddSurfaceFileDlg.cpp @@ -291,13 +291,29 @@ void AddSurfaceFileDlg::updateSurfaceProperties() if (currentSurfaceIndex_ >= 0 && currentSurfaceIndex_ < surfaces_.size()) { const auto& surface = surfaces_[currentSurfaceIndex_]; + ui->surfaceNameLineEdit->blockSignals(true); ui->surfaceNameLineEdit->setText(surface.name); - ui->surfaceStartSpinBox->setValue(surface.start); - ui->surfaceStopSpinBox->setValue(surface.stop); + ui->surfaceNameLineEdit->blockSignals(false); + ui->surfaceStartSpinBox->blockSignals(true); + ui->surfaceStartSpinBox->setValue(surface.start); + ui->surfaceStartSpinBox->blockSignals(false); + + ui->surfaceStopSpinBox->blockSignals(true); + ui->surfaceStopSpinBox->setValue(surface.stop); + ui->surfaceStopSpinBox->blockSignals(false); + + ui->comboBox_x->blockSignals(true); ui->comboBox_x->setCurrentText(surface.x); + ui->comboBox_x->blockSignals(false); + + ui->comboBox_y->blockSignals(true); ui->comboBox_y->setCurrentText(surface.y); + ui->comboBox_y->blockSignals(false); + + ui->comboBox_z->blockSignals(true); ui->comboBox_z->setCurrentText(surface.z); + ui->comboBox_z->blockSignals(false); selectedColor_ = surface.color; updateColorPreview(ui->surfaceColorButton, selectedColor_); diff --git a/src/viewer/OsgWidget.cpp b/src/viewer/OsgWidget.cpp index 748e2420..938059c8 100644 --- a/src/viewer/OsgWidget.cpp +++ b/src/viewer/OsgWidget.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -23,6 +25,13 @@ #include "workspace/WorkSpaceManager.h" #include "workspace/WorkSpace.h" #include "scutcheon/osgScutcheon.h" +#include "workspace/PresetModelConfigParser.h" + +#include "entities/EntitiesManager.h" +#include "entities/EntityFactory.h" +#include "entities/ComponentFactory.h" +#include "entities/Entity.h" +#include "entities/Component.h" static void ConfigureView( osgViewer::View* view ) { view->addEventHandler(new osgViewer::StatsHandler()); @@ -191,10 +200,14 @@ void OsgWidget::paintEvent(QPaintEvent*) { } void OsgWidget::dragEnterEvent(QDragEnterEvent* event) { - // 检查是否是预制模型拖拽 + LOG_INFO("OsgWidget::dragEnterEvent - Available formats:{}", event->mimeData()->formats().join(",").toStdString()); + + // Check if it's a preset model drag if (event->mimeData()->hasFormat("application/x-preset-model")) { + LOG_INFO("OsgWidget::dragEnterEvent - Accept preset model drag"); event->acceptProposedAction(); } else { + LOG_INFO("OsgWidget::dragEnterEvent - Ignore drag, not preset model format"); event->ignore(); } } @@ -209,40 +222,98 @@ void OsgWidget::dragMoveEvent(QDragMoveEvent* event) { } void OsgWidget::dropEvent(QDropEvent* event) { - // 检查是否是预制模型拖拽 + LOG_INFO("OsgWidget::dropEvent - Start processing drop event"); + if (event->mimeData()->hasFormat("application/x-preset-model")) { QByteArray data = event->mimeData()->data("application/x-preset-model"); QString modelInfo = QString::fromUtf8(data); - // 解析模型信息 (格式: "modelType|modelName") + LOG_INFO("OsgWidget::dropEvent - Received model info:{}", modelInfo.toStdString()); + QStringList parts = modelInfo.split("|"); if (parts.size() == 2) { QString modelType = parts[0]; QString modelName = parts[1]; - // 获取拖拽位置 QPointF position = event->posF(); - // 处理预制模型拖拽 + LOG_INFO("OsgWidget::dropEvent - Parse success, type:{} name:{} position:({},{})", modelType.toStdString(), modelName.toStdString(), position.x(), position.y()); + OnPresetModelDropped(modelType, modelName, position); event->acceptProposedAction(); + LOG_INFO("OsgWidget::dropEvent - Drag processing completed, accepted"); } else { + LOG_ERROR("OsgWidget::dropEvent - Data format error, expected: type|name, actual:{}", modelInfo.toStdString()); event->ignore(); } } else { + LOG_INFO("OsgWidget::dropEvent - Not preset model format, ignore drag"); event->ignore(); } } void OsgWidget::OnPresetModelDropped(const QString& modelType, const QString& modelName, const QPointF& position) { - // TODO: 实现预制模型拖拽处理逻辑 - // 1. 将屏幕坐标转换为世界坐标 - // 2. 创建对应的Entity和MeshComponent - // 3. 添加到场景中 + LOG_INFO("OsgWidget::OnPresetModelDropped - Dropped model type:{} name:{} position:({}, {})", modelType.toStdString(), modelName.toStdString(), position.x(), position.y()); - qDebug() << "预制模型拖拽:" << modelType << modelName << "位置:" << position; + WorkSpace* currentWorkSpace = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == currentWorkSpace) { + LOG_WARN("OsgWidget::OnPresetModelDropped - Current workspace is nullptr"); + return; + } - // 这里暂时只是输出调试信息,后续需要集成实体系统 + // 将屏幕坐标转换为世界坐标 + double longitude, latitude, height; + if (!ScreenToWorldCoordinate(static_cast(position.x()), static_cast(position.y()), longitude, latitude, height)) { + LOG_WARN("OsgWidget::OnPresetModelDropped - Failed to convert screen coordinates to world coordinates"); + // 使用默认位置 + longitude = 0.0; + latitude = 0.0; + height = 0.0; + } + + bool success = false; + ModelInfo modelInfo = PresetModelConfigParser::Get().GetModelInfo(modelType, modelName, &success); + if (!success) { + LOG_ERROR("OsgWidget::OnPresetModelDropped - Failed to get model info of type:{} name:{}", modelType.toStdString(), modelName.toStdString()); + return; + } + + Entity* entity = EntitiesManager::Get().CreateEntityWithComponents(modelType, modelInfo.meshFile, modelInfo.useLabel, currentWorkSpace); + if (nullptr == entity) { + LOG_ERROR("OsgWidget::OnPresetModelDropped - Failed to create entity of type: {}", modelType.toStdString()); + return; + } + + int count = currentWorkSpace->GetEntities().size(); + entity->SetName(QString("%1_%2").arg(modelName).arg(count)); + + SceneComponent* rootComponent = entity->GetRootComponent(); + if (rootComponent) { + osg::Vec3 worldPos(longitude, latitude, height); + rootComponent->SetLocation(worldPos); + LOG_INFO("OsgWidget::OnPresetModelDropped - Set entity position to ({:.6f}, {:.6f}, {:.2f})", longitude, latitude, height); + } + + LOG_INFO("OsgWidget::OnPresetModelDropped - Successfully created {} entity: {} at position ({:.6f}, {:.6f}, {:.2f})", + modelType.toStdString(), entity->GetName().toStdString(), longitude, latitude, height); +} + +bool OsgWidget::ScreenToWorldCoordinate(int x, int y, double& longitude, double& latitude, double& height) { + if (!activeScene_.valid()) { + LOG_WARN("OsgWidget::ScreenToWorldCoordinate - active scene is null"); + return false; + } + + osg::Vec3d output; + if (!activeScene_->ScreenToWorldCoordinate(x, y, &output)) { + LOG_WARN("OsgWidget::ScreenToWorldCoordinate - Failed to convert screen coordinates to world coordinates"); + return false; + } + + longitude = output.x(); + latitude = output.y(); + height = output.z(); + return true; } diff --git a/src/viewer/OsgWidget.h b/src/viewer/OsgWidget.h index b8664ac4..bb973ae4 100644 --- a/src/viewer/OsgWidget.h +++ b/src/viewer/OsgWidget.h @@ -43,6 +43,8 @@ public slots: */ void OnPresetModelDropped(const QString& modelType, const QString& modelName, const QPointF& position = QPointF()); + bool ScreenToWorldCoordinate(int x, int y, double& longitude, double& latitude, double& altitude); + private: QTimer timer_; diff --git a/src/workspace/FileEntry.cpp b/src/workspace/FileEntry.cpp index 9a827d69..4b702d1b 100644 --- a/src/workspace/FileEntry.cpp +++ b/src/workspace/FileEntry.cpp @@ -510,6 +510,8 @@ bool FileEntryLight::SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocumen chartElement->SetAttribute("openColor", QColorToString(colorProperties_.openColor).toUtf8().constData()); chartElement->SetAttribute("closeColor", QColorToString(colorProperties_.closeColor).toUtf8().constData()); + chartElement->SetAttribute("t", colorProperties_.timeParam); + // 为每个LightRowProperty创建元素 for (const auto& lightRow : lightProperties_) { tinyxml2::XMLElement* lightElement = doc->NewElement("curve"); @@ -559,6 +561,8 @@ bool FileEntryLight::ParseFiles(const tinyxml2::XMLElement* chartElement) { colorProperties_.closeColor = StringToQColor(QString::fromUtf8(closeColorAttr)); } + colorProperties_.timeParam = chartElement->DoubleAttribute("t", 0.0); + // 解析所有元素 lightProperties_.clear(); for (const tinyxml2::XMLElement* lightElement = chartElement->FirstChildElement("curve"); diff --git a/src/workspace/ModelInfo.h b/src/workspace/ModelInfo.h new file mode 100644 index 00000000..bf8ea4b8 --- /dev/null +++ b/src/workspace/ModelInfo.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +/** + * @brief Model information structure + * + * Contains all information for a single preset model, including name, display name, + * description, mesh file path, icon path, enabled status, and entity registration info. + */ +struct ModelInfo { + QString name; ///< Model name (unique identifier) + QString displayName; ///< Display name (for UI display) + QString description; ///< Model description + QString meshFile; ///< 3D mesh file path + QString icon; ///< Icon file path + bool useLabel; + bool enabled; ///< Whether this model is enabled + + /** + * @brief Default constructor + * Model is enabled by default + */ + ModelInfo() : useLabel(true), enabled(true) {} + + /** + * @brief Constructor with parameters + */ + ModelInfo(const QString& name, const QString& displayName, const QString& description, + const QString& meshFile, const QString& icon, bool enabled = true, + const QString& entityType = "", const QString& entityClass = "", + const QStringList& requiredComponents = QStringList()) + : name(name), displayName(displayName), description(description), + meshFile(meshFile), icon(icon), useLabel(true), enabled(enabled) {} +}; + +/** + * @brief Model category structure + * + * Contains information for a model category, such as ships, satellites, missiles, jammers, etc. + * Each category contains multiple specific models. + */ +struct ModelCategory { + QString name; ///< Category name (unique identifier) + QString displayName; ///< Display name (for UI display) + QString icon; ///< Category icon path + QVector models; ///< All models in this category +}; \ No newline at end of file diff --git a/src/ui/ModelBrowser/PresetModelConfigParser.cpp b/src/workspace/PresetModelConfigParser.cpp similarity index 84% rename from src/ui/ModelBrowser/PresetModelConfigParser.cpp rename to src/workspace/PresetModelConfigParser.cpp index 2b02c3c0..67f27ac0 100644 --- a/src/ui/ModelBrowser/PresetModelConfigParser.cpp +++ b/src/workspace/PresetModelConfigParser.cpp @@ -1,19 +1,21 @@ -#include "PresetModelConfigParser.h" -#include "../../xml/tinyxml2.h" -#include "../../common/SpdLogger.h" +#include "workspace/PresetModelConfigParser.h" + #include #include -PresetModelConfigParser::PresetModelConfigParser() -{ +#include "xml/tinyxml2.h" +#include "common/SpdLogger.h" + +template<> PresetModelConfigParser* Singleton::instance_ = nullptr; + +PresetModelConfigParser::PresetModelConfigParser(QObject* parent) + : QObject(parent) { } -PresetModelConfigParser::~PresetModelConfigParser() -{ +PresetModelConfigParser::~PresetModelConfigParser() { } -bool PresetModelConfigParser::LoadAllConfigs(const QString& configDir) -{ +bool PresetModelConfigParser::LoadAllConfigs(const QString& configDir) { Clear(); QDir dir(configDir); @@ -102,8 +104,15 @@ bool PresetModelConfigParser::ParseXmlFile(const QString& filePath) const char* modelDisplayName = modelElement->Attribute("displayName"); const char* description = modelElement->Attribute("description"); const char* meshFile = modelElement->Attribute("meshFile"); + const char* modelIcon = modelElement->Attribute("icon"); + const char* label = modelElement->Attribute("label"); const char* enabled = modelElement->Attribute("enabled"); + // Entity registration attributes + const char* entityType = modelElement->Attribute("entityType"); + const char* entityClass = modelElement->Attribute("entityClass"); + const char* requiredComponents = modelElement->Attribute("requiredComponents"); + if (!modelName) { LOG_WARN("Model element missing name attribute in file: {}", filePath.toStdString()); modelElement = modelElement->NextSiblingElement("model"); @@ -114,6 +123,8 @@ bool PresetModelConfigParser::ParseXmlFile(const QString& filePath) model.displayName = modelDisplayName ? QString::fromUtf8(modelDisplayName) : model.name; model.description = description ? QString::fromUtf8(description) : ""; model.meshFile = meshFile ? QString::fromUtf8(meshFile) : ""; + model.icon = modelIcon ? QString::fromUtf8(modelIcon) : ""; + model.useLabel = label ? (strcmp(label, "true") == 0) : true; model.enabled = enabled ? (strcmp(enabled, "true") == 0) : true; category.models.append(model); @@ -156,16 +167,18 @@ ModelCategory PresetModelConfigParser::GetCategory(const QString& categoryName) return ModelCategory(); } -ModelInfo PresetModelConfigParser::GetModelInfo(const QString& categoryName, const QString& modelName) const -{ +ModelInfo PresetModelConfigParser::GetModelInfo(const QString& categoryName, const QString& modelName, bool* success) const { if (m_categories.contains(categoryName)) { const ModelCategory& category = m_categories[categoryName]; for (const ModelInfo& model : category.models) { if (model.name == modelName) { + if (success) *success = true; return model; } } } + + if (success) *success = false; return ModelInfo(); } diff --git a/src/ui/ModelBrowser/PresetModelConfigParser.h b/src/workspace/PresetModelConfigParser.h similarity index 74% rename from src/ui/ModelBrowser/PresetModelConfigParser.h rename to src/workspace/PresetModelConfigParser.h index ffa556c2..cb785a38 100644 --- a/src/ui/ModelBrowser/PresetModelConfigParser.h +++ b/src/workspace/PresetModelConfigParser.h @@ -1,33 +1,22 @@ #pragma once +#include #include #include #include -#include -struct ModelInfo { - QString name; - QString displayName; - QString description; - QString meshFile; - bool enabled; - - ModelInfo() : enabled(true) {} -}; +#include "app/Singleton.h" -struct ModelCategory { - QString name; - QString displayName; - QString icon; - QVector models; -}; +#include "ModelInfo.h" -class PresetModelConfigParser -{ +class PresetModelConfigParser : public QObject, public Singleton { + Q_OBJECT public: - PresetModelConfigParser(); + explicit PresetModelConfigParser(QObject* parent = nullptr); ~PresetModelConfigParser(); + void OnDestory() { } + // Load all preset model configurations bool LoadAllConfigs(const QString& configDir = "workspace/presets"); @@ -44,7 +33,7 @@ public: ModelCategory GetCategory(const QString& categoryName) const; // Get model information - ModelInfo GetModelInfo(const QString& categoryName, const QString& modelName) const; + ModelInfo GetModelInfo(const QString& categoryName, const QString& modelName, bool* success) const; // Check if configuration data exists bool HasData() const; diff --git a/test_chart_parsing.cpp b/test_chart_parsing.cpp deleted file mode 100644 index f67d5917..00000000 --- a/test_chart_parsing.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include -#include "workspace/WorkSpaceXMLParse.h" -#include "common/ChartData.h" - -void printChartData(const std::shared_ptr& chartData) { - if (!chartData) { - std::cout << " Chart data is null" << std::endl; - return; - } - - std::cout << " Chart Name: " << chartData->name.toStdString() << std::endl; - std::cout << " Chart Path: " << chartData->path.toStdString() << std::endl; - - // Handle different chart types - if (auto curveChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Curve" << std::endl; - std::cout << " X Title: " << curveChart->xTitle.toStdString() << std::endl; - std::cout << " Y Title: " << curveChart->yTitle.toStdString() << std::endl; - std::cout << " X Range: [" << curveChart->xMin << " - " << curveChart->xMax << "]" << std::endl; - std::cout << " Y Range: [" << curveChart->yMin << " - " << curveChart->yMax << "]" << std::endl; - std::cout << " X Count: " << curveChart->xCount << std::endl; - std::cout << " Time: " << curveChart->t << std::endl; - std::cout << " Curves (" << curveChart->curves.size() << "):" << std::endl; - - for (const auto& curve : curveChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Range: [" << curve.start << " - " << curve.stop << "]" - << ", Color: " << curve.color.toStdString() << std::endl; - } - - } else if (auto surfaceChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Surface" << std::endl; - std::cout << " X Title: " << surfaceChart->xTitle.toStdString() << std::endl; - std::cout << " Y Title: " << surfaceChart->yTitle.toStdString() << std::endl; - std::cout << " Z Title: " << surfaceChart->zTitle.toStdString() << std::endl; - std::cout << " X Range: [" << surfaceChart->xMin << " - " << surfaceChart->xMax << "]" << std::endl; - std::cout << " Y Range: [" << surfaceChart->yMin << " - " << surfaceChart->yMax << "]" << std::endl; - std::cout << " Z Range: [" << surfaceChart->zMin << " - " << surfaceChart->zMax << "]" << std::endl; - std::cout << " X Count: " << surfaceChart->xCount << std::endl; - std::cout << " Y Count: " << surfaceChart->yCount << std::endl; - std::cout << " Z Count: " << surfaceChart->zCount << std::endl; - std::cout << " Time: " << surfaceChart->t << std::endl; - std::cout << " Surface Curves (" << surfaceChart->curves.size() << "):" << std::endl; - - for (const auto& curve : surfaceChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Range: [" << curve.start << " - " << curve.stop << "]" - << ", Color: " << curve.color.toStdString() - << ", Position: (" << curve.x << "," << curve.y << "," << curve.z << ")" << std::endl; - } - - } else if (auto tableChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Table" << std::endl; - std::cout << " Head: " << tableChart->head.toStdString() << std::endl; - std::cout << " Time: " << tableChart->t << std::endl; - std::cout << " Table Data (" << tableChart->curves.size() << "):" << std::endl; - - for (const auto& curve : tableChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Color: " << curve.color.toStdString() - << ", Data: " << curve.data.toStdString() << std::endl; - } - - } else if (auto lightChart = std::dynamic_pointer_cast(chartData)) { - std::cout << " Chart Type: Light" << std::endl; - std::cout << " Open Color: " << lightChart->openColor.toStdString() << std::endl; - std::cout << " Close Color: " << lightChart->closeColor.toStdString() << std::endl; - std::cout << " Time: " << lightChart->t << std::endl; - std::cout << " Light Data (" << lightChart->curves.size() << "):" << std::endl; - - for (const auto& curve : lightChart->curves) { - std::cout << " - Name: " << curve.name.toStdString() - << ", Data: " << curve.data.toStdString() << std::endl; - } - } else { - std::cout << " Chart Type: Unknown" << std::endl; - } -} - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - - std::cout << "Testing XML Chart Parsing with New Inheritance Structure..." << std::endl; - - // Test XML file path - QString xmlFilePath = "test_workspace.xml"; - - // Create parser instance - WorkSpaceXMLParse parser; - - // Parse the XML file - FileTypeData fileData; - bool success = parser.ParseFiles(xmlFilePath, fileData); - - if (!success) { - std::cout << "Failed to parse XML file: " << xmlFilePath.toStdString() << std::endl; - return -1; - } - - std::cout << "Successfully parsed XML file!" << std::endl; - std::cout << "=== File Type Data ===" << std::endl; - std::cout << "Files count: " << fileData.files.size() << std::endl; - std::cout << "Charts count: " << fileData.charts.size() << std::endl; - - // Print file information - std::cout << "\n=== Files ===" << std::endl; - for (int i = 0; i < fileData.files.size(); ++i) { - const auto& file = fileData.files[i]; - std::cout << "File " << i << ":" << std::endl; - std::cout << " Name: " << file.name.toStdString() << std::endl; - std::cout << " Path: " << file.path.toStdString() << std::endl; - std::cout << " Type: " << static_cast(file.type) << std::endl; - } - - // Print chart information - std::cout << "\n=== Charts ===" << std::endl; - for (int i = 0; i < fileData.charts.size(); ++i) { - std::cout << "Chart " << i << ":" << std::endl; - printChartData(fileData.charts[i]); - std::cout << std::endl; - } - - std::cout << "Test completed successfully!" << std::endl; - - return 0; -} \ No newline at end of file diff --git a/test_workspace.xml b/test_workspace.xml deleted file mode 100644 index 4bf37e07..00000000 --- a/test_workspace.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/workspace/presets/jammers.xml b/workspace/presets/jammers.xml index cd936285..1e3ca715 100644 --- a/workspace/presets/jammers.xml +++ b/workspace/presets/jammers.xml @@ -3,21 +3,11 @@ - - - + icon="icons/signal_jammer_icon.png" + enabled="true"/> \ No newline at end of file diff --git a/workspace/presets/missiles.xml b/workspace/presets/missiles.xml index d38d5838..f3b873ed 100644 --- a/workspace/presets/missiles.xml +++ b/workspace/presets/missiles.xml @@ -1,23 +1,8 @@ - - - - \ No newline at end of file diff --git a/workspace/presets/satellites.xml b/workspace/presets/satellites.xml index 07f48118..00e6057b 100644 --- a/workspace/presets/satellites.xml +++ b/workspace/presets/satellites.xml @@ -1,23 +1,8 @@ - - - - \ No newline at end of file diff --git a/workspace/presets/ships.xml b/workspace/presets/ships.xml index 98af9f07..7acee27e 100644 --- a/workspace/presets/ships.xml +++ b/workspace/presets/ships.xml @@ -3,21 +3,11 @@ + icon="icons/destroyer_icon.png" + enabled="true"/> - - - + icon="icons/carrier_icon.png" + enabled="true"/> \ No newline at end of file