diff --git a/src/app/main.cpp b/src/app/main.cpp index 760ddfe..84b15f6 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -102,9 +102,9 @@ VoxelBuildResult buildVoxelFromScatters( using geopro::core::IdwInterpolator; using geopro::core::PointSet; - constexpr double kDxy = 1.0; // 水平步长(米) - constexpr double kDz = 0.5; // 垂向步长(米) - constexpr double kPower = 2.0; // IDW 幂 + constexpr double kDxy = 2.0; // 水平步长(米);粗化体素网格保证实时(~1.4万格) + constexpr double kDz = 1.0; // 垂向步长(米) + constexpr double kPower = 2.0; // IDW 幂(=2 走 1/d² 快速路径) constexpr double kMaxDist = 4.0; // 超距留空(NaN) // 合并两剖面点为一组 (X=projX, Y=projY, Z=z)。 @@ -219,7 +219,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re cameraGroup->addAction(act3D); act2D->setChecked(true); // 默认二维 viewToolBar->addSeparator(); - auto* actVoxel = viewToolBar->addAction(QStringLiteral("dd_voxel")); + auto* actVoxel = viewToolBar->addAction(QStringLiteral("三维体素")); centerLayout->addWidget(viewToolBar); centerLayout->addWidget(vtkWidget, 1); @@ -301,9 +301,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re QObject::connect(actVoxel, &QAction::triggered, vtkWidget, [&repo, scene, rendererPtr, renderWindowPtr, cameraMode, act3D, propLabel, sliceWidget, voxelImage]() { + QApplication::setOverrideCursor(Qt::WaitCursor); const auto scatters = repo.loadVoxelScatters(); const auto cs = repo.loadColorScale("grid1"); auto result = buildVoxelFromScatters(scatters, cs); + QApplication::restoreOverrideCursor(); if (!result.volume) { propLabel->setText(QStringLiteral("dd_voxel: 无可用散点数据")); return; diff --git a/src/core/algo/IdwInterpolator.cpp b/src/core/algo/IdwInterpolator.cpp index c2e59a3..e7eff4f 100644 --- a/src/core/algo/IdwInterpolator.cpp +++ b/src/core/algo/IdwInterpolator.cpp @@ -7,22 +7,28 @@ ScalarVolume IdwInterpolator::interpolate(const PointSet& pts, const GridSpec& s ScalarVolume vol(s.nx, s.ny, s.nz); const double nan = std::numeric_limits::quiet_NaN(); const size_t n = pts.v.size(); + // 局部 IDW + 性能优化:只累加 maxDist 内的点(平方距离比较,免 sqrt); + // power==2 时权重 = 1/d²,免 sqrt 和 pow(最常见路径,约 10x 提速)。 + const double maxd2 = s.maxDist * s.maxDist; + const bool fastPow2 = (s.power == 2.0); + const double halfPow = s.power * 0.5; // 1/d^power = (d²)^(-power/2) for (int k = 0; k < s.nz; ++k) for (int j = 0; j < s.ny; ++j) for (int i = 0; i < s.nx; ++i) { const double gx = s.ox + i * s.dx, gy = s.oy + j * s.dy, gz = s.oz + k * s.dz; - double wsum = 0.0, vsum = 0.0, nearest = std::numeric_limits::max(); - bool hit = false; double hitVal = 0.0; + double wsum = 0.0, vsum = 0.0; + bool any = false, hit = false; double hitVal = 0.0; for (size_t p = 0; p < n; ++p) { const double ddx = gx - pts.x[p], ddy = gy - pts.y[p], ddz = gz - pts.z[p]; - const double d = std::sqrt(ddx * ddx + ddy * ddy + ddz * ddz); - if (d < nearest) nearest = d; - if (d < 1e-12) { hit = true; hitVal = pts.v[p]; break; } - const double w = 1.0 / std::pow(d, s.power); + const double d2 = ddx * ddx + ddy * ddy + ddz * ddz; + if (d2 > maxd2) continue; // 超距点不参与(局部 IDW)+ 跳过昂贵的权重计算 + any = true; + if (d2 < 1e-24) { hit = true; hitVal = pts.v[p]; break; } + const double w = fastPow2 ? (1.0 / d2) : std::pow(d2, -halfPow); wsum += w; vsum += w * pts.v[p]; } if (hit) vol.at(i, j, k) = hitVal; - else if (nearest > s.maxDist || wsum == 0.0) vol.at(i, j, k) = nan; + else if (!any || wsum == 0.0) vol.at(i, j, k) = nan; // maxDist 内无点 → blank else vol.at(i, j, k) = vsum / wsum; } return vol; diff --git a/src/render/actors/VoxelActor.cpp b/src/render/actors/VoxelActor.cpp index 63bf16b..0037171 100644 --- a/src/render/actors/VoxelActor.cpp +++ b/src/render/actors/VoxelActor.cpp @@ -5,8 +5,8 @@ #include #include -#include #include +#include #include #include #include @@ -71,7 +71,8 @@ vtkSmartPointer buildVoxel(const geopro::core::ScalarVolume& vol, opacity->AddPoint(vmin, 0.0); opacity->AddPoint(vmax, kMaxOpacity); - vtkNew mapper; + // SmartVolumeMapper:有 GPU 走 GPU ray cast,否则自动回退 CPU,避免无 GPU 时卡死/失败。 + vtkNew mapper; mapper->SetInputData(img); vtkNew prop;