[Astro] #112 Point Cloud Viewer v7 — 任意直線断面+SOR進捗表示

[Astro] #112 Point Cloud Viewer v7 — 任意直線断面+SOR進捗表示

概要

前回[#111]で計測ラベル3D表示・元座標復元・Voxel Downsamplingを追加した。今回は「任意直線断面」と「SOR進捗表示」の2機能を追加する。

今回の追加機能

機能概要
任意直線断面2点クリックで自由角度の垂直断面を定義→プロファイル生成→DXFエクスポート
SOR進捗表示K近傍計算をチャンク分割し、進捗パーセンテージをリアルタイム表示

スクリーンショット

任意直線断面

Point Cloud Viewer — 任意直線断面

SOR進捗表示

「Apply SOR Filter」でフリーズしていたのを

Point Cloud Viewer — SOR進捗表示

動画(GIF)

Point Cloud Viewer v7

任意直線断面

v5の軸平行スライスとの違い

v5の断面機能は軸平行(X/Y/Z軸に平行な面)でしか断面を取れなかった。道路の横断面を取りたい場合、道路がX軸やZ軸と平行でなければ正しい断面にならない。

今回の「任意直線断面」は、ユーザーが3Dビュー上で2点をクリックするだけで、その2点を通る任意角度の垂直断面を定義できる。

使い方

  1. TOOL → Section をクリック
  2. 点群上で2点クリック(S1, S2がオレンジで表示される)
  3. Thicknessスライダーで断面の厚さを調整(デフォルト0.01)
  4. Generate Section → 断面プロファイルが緑ラインで表示
  5. Export DXF → CADソフトで開ける

断面の数学

S1→S2のXZ方向ベクトルに対して法線方向を計算し、垂直平面を定義する。

// S1→S2のXZ方向ベクトル
const lineDirXZ = new THREE.Vector2(p2.x - p1.x, p2.z - p1.z);
lineDirXZ.normalize();

// 法線(水平面内で直交する方向)
const normalX = -lineDirXZ.y;
const normalZ = lineDirXZ.x;

各点について:

  1. 面までの距離を計算 → thickness/2以内なら抽出
  2. ライン方向の位置を計算 → ソートに使用
  3. ビン分割して各ビンの最高点(地表面)を抽出
for (let i = 0; i < count; i++) {
  const qx = positions.getX(i) - p1.x;
  const qz = positions.getZ(i) - p1.z;

  // 面までの距離
  const dist = qx * normalX + qz * normalZ;
  if (Math.abs(dist) > halfThick) continue;

  // ライン方向の位置
  const along = qx * lineDirXZ.x + qz * lineDirXZ.y;
  extracted.push({ along, height: positions.getY(i), pos: ... });
}

土木での用途

  • 道路の横断面: 斜めに走る道路でも正しい断面が取れる
  • 河川の断面: 蛇行する河川の任意地点での断面
  • 法面の傾斜確認: 崖の任意方向での断面形状
  • 施工前後の比較: 同じラインで切った断面を重ね合わせ

SOR進捗表示

問題:200万点のSORが15分フリーズ

SOR(統計的外れ値除去)は各点について近傍K点を探索し、平均距離を計算する。200万点でK=10の場合、約200万×近傍探索×ソートの計算量になる。これがメインスレッドで同期実行されるため、UIが15分間固まっていた。

解決:チャンク分割+非同期進捗表示

K近傍計算ループを50,000点ごとのチャンクに分割し、各チャンク間でsetTimeout(0)を挟んでUIスレッドに制御を返す。既存のloadingオーバーレイに進捗を表示する。

const CHUNK = 50000;

for (let start = 0; start < count; start += CHUNK) {
  const end = Math.min(start + CHUNK, count);
  const pct = Math.round((start / count) * 100);
  setLoading(`SOR: K近傍計算中... ${pct}% (${start.toLocaleString()} / ${count.toLocaleString()})`);
  await new Promise((r) => setTimeout(r, 0));

  for (let i = start; i < end; i++) {
    // K近傍計算...
  }
}

これによりSOR実行中もスピナーと進捗パーセンテージが表示され、処理がフリーズしたのか進行中なのかが分かるようになった。

今後の改善

SOR自体の処理速度は変わっていない(15分は15分のまま)。本質的な高速化にはWeb Workerでのバックグラウンド実行が必要。メインスレッドから完全に分離すれば、SOR中もカメラ操作が可能になる。

また、SORのフィルタリングで従来はcolorのみ保持していたが、intensity/classificationも保持するように修正した。

変更ファイル

ファイル変更内容
PointCloudApp.tsxcrossSection state/handler追加、SOR async化+チャンク分割+intensity/classification保持
PointCloudScene.tsxcrossSection click対応、S1/S2マーカー+ライン描画
ControlPanel.tsxSectionボタン、thickness/generate/clear/DXF UI追加

まとめ

  • v1: PLY読み込み、ヒートマップ、スライス、計測、SOR
  • v2: LAS/PCD読み込み、200万点サンプリング、正規化
  • v3: LAZ読み込み、XYZ/CSV入出力、ローディング
  • v4: Intensity/Classification表示、LAS/PCD/スクショエクスポート
  • v5: ボックス選択+削除、断面ポリライン+DXF、Octree LOD、座標系変換
  • v6: 計測ラベル3D表示、元座標復元、Voxel Downsampling
  • v7: 任意直線断面+DXF、SOR進捗表示

今後の課題

  • SOR高速化(Web Worker化 — メインスレッドから完全分離)
  • Octreeに全点対応(サンプリング前の数千万点を扱う)
  • カメラパスアニメーション(ウォークスルー録画)
  • メッシュ化(Delaunay / Poisson Reconstruction)