#include #include #include #include #include "I3dSceneView.hpp" #include "VtkSceneController.hpp" #include "model/ColorScale.hpp" #include "model/Field.hpp" #include "repo/I3dSceneRepository.hpp" #include "repo/IDatasetRepository.hpp" using namespace geopro; using namespace geopro::controller; namespace { // 记录视图收到的图元调用类型/数量。 struct FakeView : I3dSceneView { int clears = 0; int surveyLines = 0; int curtains = 0; int volumes = 0; int terrains = 0; int renders = 0; bool lastIs2D = false; double ve = -1.0; // clear 模型化"移除所有图元":图元计数归零(反映当前场景状态),clears 累加。 void clear() override { ++clears; surveyLines = curtains = volumes = terrains = 0; } void setVerticalExaggeration(double v) override { ve = v; } void addSurveyLine(const core::Grid&) override { ++surveyLines; } void addCurtain(const core::Grid&, const core::ColorScale&) override { ++curtains; } void addVolume(const data::VolumeGrid&, const core::ColorScale&) override { ++volumes; } void addTerrain(const data::TerrainPaths&) override { ++terrains; } void render(bool is2D) override { ++renders; lastIs2D = is2D; } int props() const { return surveyLines + curtains + volumes + terrains; } }; // 同步小数据仓储:loadGrid 返回 2x2 grid,loadColorScale 返回两段色阶。 struct FakeDsRepo : data::IDatasetRepository { std::vector loadStructure() override { return {}; } core::Grid loadGrid(const std::string&) override { core::Grid g(2, 2); g.lat = {22.0, 22.001}; g.lon = {114.0, 114.001}; return g; } core::ScatterField loadScatter(const std::string&) override { return {}; } core::ColorScale loadColorScale(const std::string&) override { core::ColorScale cs; cs.addStop(0.0, core::Rgba{0, 0, 255, 255}); cs.addStop(1.0, core::Rgba{255, 0, 0, 255}); return cs; } core::ColorScale loadScatterColorScale(const std::string&) override { return loadColorScale(""); } std::vector loadAnomalies(const std::string&) override { return {}; } }; // 同步三维仓储:dimensionOf 全当 3D;loadVolume 立即回调一个最小有效体。 struct FakeSceneRepo : data::I3dSceneRepository { data::DsDimension dimensionOf(const data::DsRow&) const override { return data::DsDimension::Dim3D; } void loadVolume(const std::string&, std::function onOk, OnError) override { data::VolumeGrid g; g.vol = core::ScalarVolume(2, 2, 2); g.spacing = {{1.0, 1.0, 1.0}}; g.vmin = 0.0; g.vmax = 1.0; onOk(std::move(g)); // 同步回调(异步壳) } void loadTerrainPaths(std::function onOk, OnError) override { onOk(data::TerrainPaths{"dem.tif", "image.tif"}); } }; } // namespace // 2D 模式 + 勾选 1 ds → 1 个测线 actor,无帘面/体素/地形。 TEST(VtkSceneController, Map2DWithOneDatasetAddsSurveyLine) { FakeDsRepo ds; FakeSceneRepo sc; FakeView view; VtkSceneController c(ds, sc, view); c.setViewMode(ViewMode::Map2D); c.setCheckedDatasets({"ds1"}); EXPECT_EQ(view.surveyLines, 1); EXPECT_EQ(view.curtains, 0); EXPECT_EQ(view.volumes, 0); EXPECT_GE(view.renders, 1); EXPECT_TRUE(view.lastIs2D); } // 3D 模式 + 帘面图层 → 1 帘面 actor。 TEST(VtkSceneController, View3DCurtainAddsCurtain) { FakeDsRepo ds; FakeSceneRepo sc; FakeView view; VtkSceneController c(ds, sc, view); c.setViewMode(ViewMode::View3D); c.setCheckedDatasets({"ds1"}); EXPECT_EQ(view.curtains, 1); EXPECT_EQ(view.surveyLines, 0); EXPECT_FALSE(view.lastIs2D); } // 3D + 帘面 + 体素 → 帘面 1 + 体素 1(体素经异步回调进场)。 TEST(VtkSceneController, View3DWithVoxelAddsVolume) { FakeDsRepo ds; FakeSceneRepo sc; FakeView view; VtkSceneController c(ds, sc, view); c.setViewMode(ViewMode::View3D); c.setLayer(SceneLayer::Voxel, true); c.setCheckedDatasets({"ds1"}); EXPECT_EQ(view.curtains, 1); EXPECT_EQ(view.volumes, 1); } // 3D + 地形 → 地形 1(与勾选数据集无关,地形是场景图层)。 TEST(VtkSceneController, View3DWithTerrainAddsTerrain) { FakeDsRepo ds; FakeSceneRepo sc; FakeView view; VtkSceneController c(ds, sc, view); c.setViewMode(ViewMode::View3D); c.setLayer(SceneLayer::Terrain, true); c.setCheckedDatasets({"ds1"}); EXPECT_EQ(view.terrains, 1); EXPECT_EQ(view.curtains, 1); } // 取消勾选 → clear 后无任何图元。 TEST(VtkSceneController, UncheckAllClearsScene) { FakeDsRepo ds; FakeSceneRepo sc; FakeView view; VtkSceneController c(ds, sc, view); c.setViewMode(ViewMode::View3D); c.setCheckedDatasets({"ds1"}); ASSERT_EQ(view.curtains, 1); c.setCheckedDatasets({}); // 取消全部勾选 EXPECT_EQ(view.curtains, 0); EXPECT_EQ(view.volumes, 0); // 最后一次重建仍调用 clear。 EXPECT_GE(view.clears, 2); } // 纵向比例传到视图。 TEST(VtkSceneController, VerticalExaggerationForwarded) { FakeDsRepo ds; FakeSceneRepo sc; FakeView view; VtkSceneController c(ds, sc, view); c.setViewMode(ViewMode::View3D); c.setVerticalExaggeration(3.5); c.setCheckedDatasets({"ds1"}); EXPECT_DOUBLE_EQ(view.ve, 3.5); } // 多个数据集 → 每个一个帘面。 TEST(VtkSceneController, MultipleDatasetsAddMultipleCurtains) { FakeDsRepo ds; FakeSceneRepo sc; FakeView view; VtkSceneController c(ds, sc, view); c.setViewMode(ViewMode::View3D); c.setCheckedDatasets({"ds1", "ds2", "ds3"}); EXPECT_EQ(view.curtains, 3); }