スポンサーリンク

VTKで表示するActorの手前にラベル表示

概要

・vtkTextActorを使って表示する文字列を作成。日本語は使えない(?)ので注意

・Actorの三次元座標を画面上のピクセル座標に変換するにはvtkCoordinateを使用する。

・再描画が行われるたびにテキストの位置の再計算が必要。AddObserverでイベントリスナを追加する

//VTK_MODULE_INITに必要
#include <vtkAutoInit.h>

#include <vtkSmartPointer.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>

//円筒とその表示に必要
#include <vtkCylinderSource.h>
#include <vtkConeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>

#include <vtkTextActor.h>
#include <vtkTextProperty.h>

#include <vtkCamera.h>

#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"psapi.lib")
#pragma comment(lib,"dbghelp.lib")
#pragma comment(lib,"ws2_32.lib")


//必須
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType); // テキスト表示に必要





// テキストの位置をアクタの中心に合わせる
void UpdateTextActorPosition(vtkSmartPointer<vtkRenderer> renderer, vtkSmartPointer<vtkActor> actor, vtkSmartPointer<vtkTextActor> textActor) {
  double* c = actor->GetPosition(); // アクターの中心座標を取得

  // cの三次元座標をクライアント領域のピクセル座標に変換
  vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
  coordinate->SetCoordinateSystemToWorld();
  coordinate->SetValue(c);
  int* xy = coordinate->GetComputedDisplayValue(renderer);
  int x = static_cast<int>(xy[0]);
  int y = static_cast<int>(xy[1]);

  // テキストの位置を設定
  textActor->SetPosition(x, y);
}

vtkSmartPointer<vtkTextActor> CreateTextActorFromActor(vtkSmartPointer<vtkRenderer> renderer, vtkSmartPointer<vtkActor> actor, const std::string& title) {

  auto textActor = vtkSmartPointer<vtkTextActor>::New();
  textActor->SetInput(title.c_str());
  // テキストプロパティを取得
  vtkTextProperty* textProperty2 = textActor->GetTextProperty();
  // テキストサイズ(フォントサイズ)を設定
  textProperty2->SetFontSize(24);
  // テキストの色を設定(オプション、例えば白色)
  textProperty2->SetColor(1.0, 1.0, 1.0);

  UpdateTextActorPosition(renderer, actor, textActor);

  return textActor;

}

//////////////////////////////////////////////////////////////
// VTK内のアクションに応じて呼び出すためのコールバッククラス
//////////////////////////////////////////////////////////////
class CameraModifiedCallback : public vtkCommand
{
  vtkSmartPointer<vtkActor> _actor;
  vtkSmartPointer<vtkTextActor> _textActor;
  vtkSmartPointer<vtkRenderer> _renderer;
public:
  static CameraModifiedCallback* New()
  {
    return new CameraModifiedCallback;
  }

  void SetRenderer(vtkSmartPointer<vtkRenderer> renderer, vtkSmartPointer<vtkActor> actor, vtkSmartPointer<vtkTextActor> textActor)
  {
    _renderer = renderer;
    _actor = actor;
    _textActor = textActor;
  }

  void Execute(vtkObject* caller, unsigned long eventId, void* callData) override
  {
    // イベントに応じて処理を分岐
    if (eventId == vtkCommand::ModifiedEvent)
    {
      vtkCamera* camera = dynamic_cast<vtkCamera*>(caller);
      if (!camera) return; // callerがvtkCameraでない場合は何もしない

      UpdateTextActorPosition(_renderer, _actor, _textActor);
    }
    else if (eventId == vtkCommand::ConfigureEvent)
    {
      // ウィンドウサイズ変更イベントの処理
      UpdateTextActorPosition(_renderer, _actor, _textActor);
    }
  }
};

int main(int /*argc*/, char** /*argv*/)
{

  //////////////////////////////////////
  auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();

  //////////////////////////////////////
  auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->SetInteractor(interactor);
  renderWindow->Render();

  //////////////////////////////////////
  //////////////////////////////////////
  auto renderer_1st = vtkSmartPointer<vtkRenderer>::New();
  auto renderer_2nd = vtkSmartPointer<vtkRenderer>::New();

  // レイヤー番号の指定
  // 番号が若いほど背面に描画される
  renderer_1st->SetLayer(0);
  renderer_2nd->SetLayer(1);

  renderWindow->SetNumberOfLayers(2);// レイヤー数を指定しておいたほうが行儀がいいらしい
  renderWindow->AddRenderer(renderer_1st);
  renderWindow->AddRenderer(renderer_2nd);

  renderer_1st->ResetCamera();
  // カメラの共有
  renderer_2nd->SetActiveCamera(renderer_1st->GetActiveCamera());

  //////////////////////////////////////
  //////////////////////////////////////

  //////////////////////////////////////
  // レイヤー1にCylinderを表示
  vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
  {
    vtkSmartPointer<vtkCylinderSource> cylinderSource = vtkSmartPointer<vtkCylinderSource>::New();
    cylinderSource->SetCenter(0.0, 0.0, 0.0);
    cylinderSource->SetRadius(5.0);
    cylinderSource->SetHeight(7.0);
    cylinderSource->SetResolution(100);

    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(cylinderSource->GetOutputPort());
    actor->SetMapper(mapper);
    actor->GetProperty()->SetColor(1.0, 0.0, 0.0);

    // レイヤー1にアクタを追加
    renderer_1st->AddActor(actor);
  }

  // 表示用の文字列オブジェクトを作成
  vtkSmartPointer<vtkTextActor> text = CreateTextActorFromActor(renderer_2nd, actor, "The Cylinder");
  renderer_2nd->AddActor(text);

  // カメラの変更時に呼び出すコールバックを作成
  vtkSmartPointer<CameraModifiedCallback> camera_callback = CameraModifiedCallback::New();
  camera_callback->SetRenderer(renderer_2nd,actor,text);

  // カメラの変更を検知したときにcamera_callbackを呼び出す
  renderer_2nd->GetActiveCamera()->AddObserver(vtkCommand::ModifiedEvent, camera_callback);

  // ウィンドウサイズが変更されたときにもcamera_callbackを呼び出したい
  interactor->AddObserver(vtkCommand::ConfigureEvent, camera_callback);
  // イベントループ
  interactor->Start(); 

  return 0;
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: