CEFをオフスクリーンレンダリングしているときに、キー入出力を行う。各種キーイベントを取得してCEFに与えることでキーボード対応できる。日本語入力も出来るがキャレットがテキストボックスの位置に出ないことに注意。
///////////////////////////////////////////////////////////////////////// // キー修飾フラグ変換 int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam) { int modifiers = 0; // 押下状態 if (GetKeyState(VK_SHIFT) & 0x8000) modifiers |= EVENTFLAG_SHIFT_DOWN; if (GetKeyState(VK_CONTROL) & 0x8000) modifiers |= EVENTFLAG_CONTROL_DOWN; if (GetKeyState(VK_MENU) & 0x8000) modifiers |= EVENTFLAG_ALT_DOWN; // Lock 状態 if (GetKeyState(VK_CAPITAL) & 1) modifiers |= EVENTFLAG_CAPS_LOCK_ON; if (GetKeyState(VK_NUMLOCK) & 1) modifiers |= EVENTFLAG_NUM_LOCK_ON; // 左右キー switch (wparam) { case VK_LSHIFT: case VK_LCONTROL: case VK_LMENU: modifiers |= EVENTFLAG_IS_LEFT; break; case VK_RSHIFT: case VK_RCONTROL: case VK_RMENU: modifiers |= EVENTFLAG_IS_RIGHT; break; default: break; } // 拡張ビット (bit 24) const bool extended = (lparam & 0x01000000) != 0; // テンキー判定 if ((wparam >= VK_NUMPAD0 && wparam <= VK_NUMPAD9) || wparam == VK_DECIMAL || wparam == VK_ADD || wparam == VK_SUBTRACT || wparam == VK_MULTIPLY || wparam == VK_DIVIDE) { modifiers |= EVENTFLAG_IS_KEY_PAD; } else if (wparam == VK_RETURN) { // Enter: Numpad Enter なら extended が true(Winで Numpad Enter は拡張付き) if (extended) modifiers |= EVENTFLAG_IS_KEY_PAD; } return modifiers; }
// キー入力 void OnKeyDown(HWND hwnd, UINT msg, int wp,int lp) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); CefKeyEvent ev{}; ev.type = KEYEVENT_RAWKEYDOWN; ev.windows_key_code = (int)wp; ev.native_key_code = (int)lp; ev.is_system_key = (msg == WM_SYSKEYDOWN); ev.modifiers = GetCefKeyboardModifiers(wp, lp); /* Ctrl/Shift/Alt を反映 */; handler->GetBrowser()->GetHost()->SendKeyEvent(ev); } void OnKeyUp(HWND hwnd, UINT msg, int wp, int lp) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); CefKeyEvent ev{}; ev.type = KEYEVENT_KEYUP; ev.windows_key_code = (int)wp; ev.native_key_code = (int)lp; ev.is_system_key = (msg == WM_SYSKEYUP); ev.modifiers = GetCefKeyboardModifiers(wp, lp); handler->GetBrowser()->GetHost()->SendKeyEvent(ev); } void OnChar(HWND hwnd, UINT msg, int wp, int lp) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); CefKeyEvent ev; ev.type = KEYEVENT_CHAR; ev.character = static_cast<wchar_t>(wp); // 入力文字 ev.unmodified_character = static_cast<wchar_t>(wp); // 修飾なし ev.windows_key_code = static_cast<int>(wp); // 互換目的 ev.modifiers = GetCefKeyboardModifiers(wp, lp); handler->GetBrowser()->GetHost()->SendKeyEvent(ev); }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { MyHandler* handler = nullptr; LPCREATESTRUCT pcs; PAINTSTRUCT ps; HDC hdc; switch (msg) { case WM_CLOSE: OnClose(hwnd); DestroyWindow(hwnd); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); OnPaint(hwnd, hdc); EndPaint(hwnd, &ps); return 0; case WM_CREATE: return 0; case WM_DESTROY: PostQuitMessage(0); return 0; case WM_MOUSEMOVE: OnMouseMove(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); return 0; case WM_LBUTTONDOWN: OnLButtonDown(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); return 0; case WM_LBUTTONUP: OnLButtonUp(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); return 0; case WM_KEYDOWN: case WM_SYSKEYDOWN: OnKeyDown(hwnd,msg,wp,lp); // キーダウン return 0; case WM_KEYUP: case WM_SYSKEYUP: OnKeyUp(hwnd, msg, wp, lp); // キーアップ return 0; case WM_CHAR: OnChar(hwnd, msg, wp, lp); // 文字入力 return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
#pragma once #include <vtkeigen/eigen/Dense> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/depth_first_search.hpp> #include <vtkGraphToPolyData.h> #include <vtkAssembly.h> #include <vtkProperty.h> #include <vtkGlyph3DMapper.h> #include <vtkSphereSource.h> #include <vtkActor.h> #include <vtkNew.h> #include <vtkPolyDataMapper.h>
// ------------------------------------------------------------------ // ノード情報 // ------------------------------------------------------------------ struct Node { Eigen::Vector3d coord; // 3次元座標 };
// ------------------------------------------------------------------ // グラフの型定義(無向グラフ) // ------------------------------------------------------------------ using boostGraph = boost::adjacency_list< boost::vecS, // OutEdgeList boost::vecS, // VertexList boost::undirectedS, // 無向グラフ Node, // 頂点プロパティ boost::no_property // 辺プロパティ(必要なら追加可能) >;
// ------------------------------------------------------------------ // 2次元等間隔グリッドグラフを作成する関数 // ------------------------------------------------------------------ //! @brief Nx × Ny の 2次元グリッドグラフを作成 //! @param Nx X方向のノード数 //! @param Ny Y方向のノード数 //! @param spacing 隣接ノード間の距離(デフォルト 1.0) //! @return 完成したグラフ boostGraph CreateGridGraph(int Nx, int Ny, double spacing = 1.0) { if (Nx <= 0 || Ny <= 0) { throw std::invalid_argument("Nx and Ny must be positive"); } boostGraph mygraph; // 1. 頂点を作成しつつ座標を格納 std::vector<boostGraph::vertex_descriptor> vertices; vertices.reserve(static_cast<std::size_t>(Nx * Ny)); for (int ix = 0; ix < Nx; ++ix) { for (int iy = 0; iy < Ny; ++iy) { size_t v = boost::add_vertex(mygraph); mygraph[v].coord = Eigen::Vector3d( ix * spacing, iy * spacing, 0.0 // 2次元なので z = 0 ); vertices.push_back(v); } } // 2. インデックス → 頂点記述子の変換 auto indexToID = [&](int ix, int iy) -> boostGraph::vertex_descriptor { return vertices[static_cast<std::size_t>(iy + Ny * ix)]; }; // 3. 4方向(上下左右)の辺を張る for (int ix = 0; ix < Nx; ++ix) { for (int iy = 0; iy < Ny; ++iy) { boostGraph::vertex_descriptor v = indexToID(ix, iy); // 右 (+x) if (ix + 1 < Nx) { boost::add_edge(v, indexToID(ix + 1, iy), mygraph); } // 上 (+y) if (iy + 1 < Ny) { boost::add_edge(v, indexToID(ix, iy + 1), mygraph); } } } return mygraph; }
vtkSmartPointer<vtkAssembly> CreateVTKActor(const boostGraph& mygraph) { // ============================================================== // 1. vtkPoints と vtkCellArray を手動で作る // ============================================================== vtkNew<vtkPoints> points; vtkNew<vtkCellArray> lines; // 辺(ライン) vtkNew<vtkCellArray> verts; // 頂点(Glyph用) // 頂点インデックス → VTKのpointId のマップは不要(順番に追加するだけ) points->SetNumberOfPoints(boost::num_vertices(mygraph)); // まず全頂点の座標を登録 boost::graph_traits<boostGraph>::vertex_iterator vi, vi_end; vtkIdType pointId = 0; for (boost::tie(vi, vi_end) = boost::vertices(mygraph); vi != vi_end; ++vi, ++pointId) { const Eigen::Vector3d& pos = mygraph[*vi].coord; points->SetPoint(pointId, pos.x(), pos.y(), pos.z()); // Glyph用に頂点セルも作っておく verts->InsertNextCell(1, &pointId); } // 次に全辺をラインとして登録 boost::graph_traits<boostGraph>::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = boost::edges(mygraph); ei != ei_end; ++ei) { auto u = boost::source(*ei, mygraph); auto v = boost::target(*ei, mygraph); vtkIdType ids[2] = { static_cast<vtkIdType>(boost::get(boost::vertex_index, mygraph, u)), static_cast<vtkIdType>(boost::get(boost::vertex_index, mygraph, v)) }; lines->InsertNextCell(2, ids); } // ============================================================== // 2. PolyData 作成 // ============================================================== vtkNew<vtkPolyData> polyData; polyData->SetPoints(points); polyData->SetLines(lines); polyData->SetVerts(verts); // Glyph3DMapper がこれを使う // ============================================================== // 3. ノード(球)のActor // ============================================================== vtkNew<vtkSphereSource> sphere; sphere->SetRadius(0.12); sphere->SetPhiResolution(18); sphere->SetThetaResolution(18); vtkNew<vtkGlyph3DMapper> glyphMapper; glyphMapper->SetInputData(polyData); glyphMapper->SetSourceConnection(sphere->GetOutputPort()); glyphMapper->ScalingOff(); // 全部同じ大きさ glyphMapper->Update(); vtkNew<vtkActor> nodeActor; nodeActor->SetMapper(glyphMapper); nodeActor->GetProperty()->SetColor(0.1, 0.7, 1.0); // 青系 nodeActor->GetProperty()->SetSpecular(0.6); nodeActor->GetProperty()->SetSpecularPower(30); // ============================================================== // 4. 辺(ライン)のActor // ============================================================== vtkNew<vtkPolyDataMapper> lineMapper; lineMapper->SetInputData(polyData); vtkNew<vtkActor> lineActor; lineActor->SetMapper(lineMapper); lineActor->GetProperty()->SetColor(0.65, 0.65, 0.65); lineActor->GetProperty()->SetLineWidth(2.5); // ============================================================== // 5. まとめて返す(Assembly) // ============================================================== vtkNew<vtkAssembly> assembly; assembly->AddPart(nodeActor); assembly->AddPart(lineActor); return assembly.GetPointer(); }
