Skip to content

Commit

Permalink
Update ImGui with MenuDef and NodeGraphGUI to use it
Browse files Browse the repository at this point in the history
  • Loading branch information
gwaldron committed Feb 24, 2025
1 parent 829b20f commit 5992475
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 87 deletions.
71 changes: 34 additions & 37 deletions src/osgEarthImGui/ImGuiPanel
Original file line number Diff line number Diff line change
Expand Up @@ -445,51 +445,46 @@ namespace ImGuiEx
return ImGui::InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, (void*)str);
}

struct MenuAction
/**
* Structure for quickly defining menus in ImGui.
* Create a top-level "empty" MenuDef, and start adding children as menus.
*
* To define it:
* MenuDef menu;
* menu["File"]["Open"] = []() { ... };
* menu["File"]["Save"] = []() { ... };
* menu["Edit"]["Copy"] = []() { ... };
* menu["Edit"]["Paste"] = []() { ... };
*
* To render it:
* DrawMenu(menu);
*/
struct MenuDef
{
using Func = std::function<void()>;

std::string name;
std::function<void()> function;
std::vector<MenuAction> children;
};
Func function;
std::vector<MenuDef> children;

namespace
{
inline MenuAction& get_or_create(std::vector<MenuAction>& v, const std::string& s) {
auto it = std::find_if(v.begin(), v.end(), [&](const MenuAction& a) { return a.name == s; });
if (it == v.end()) {
v.emplace_back(MenuAction{ s });
return v.back();
}
return *it;
MenuDef& operator = (const Func& f) {
function = f;
return *this;
}
}

static MenuAction CreateRootMenuAction(const std::map<std::string, std::function<void()>>& actions)
{
MenuAction root;

osgEarth::Util::StringTokenizer tok;
tok.delim("/").keepEmpties(false).trimTokens(false);

for (auto& a : actions)
{
auto tokens = tok.tokenize(a.first);
auto* menu = &root.children;
for (int i = 0; i < tokens.size(); ++i) {
auto& b = get_or_create(*menu, tokens[i]);
if (i < tokens.size() - 1)
menu = &b.children;
else
b.function = a.second;
MenuDef& operator [] (const std::string& name) {
auto it = std::find_if(children.begin(), children.end(), [&](const MenuDef& a) { return a.name == name; });
if (it == children.end()) {
children.emplace_back(MenuDef{ name });
return children.back();
}
return *it;
}
};

return root;
}

static bool DrawMenuAction(const MenuAction& root)
// Render a MenuDef.
static bool DrawMenu(const MenuDef& root)
{
std::function<void(const MenuAction&)> renderAction = [&renderAction](const MenuAction& a)
std::function<void(const MenuDef&)> renderAction = [&renderAction](const MenuDef& a)
{
ImGui::PushID((std::uintptr_t)&a);
for (auto& sub : a.children) {
Expand All @@ -506,5 +501,7 @@ namespace ImGuiEx
};

renderAction(root);

return true;
}
}
112 changes: 63 additions & 49 deletions src/osgEarthImGui/NodeGraphGUI
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,29 @@ namespace osgEarth
osg::observer_ptr< MapNode > _mapNode;
osg::observer_ptr< ProceduralTiledModelLayer > _proceduralModelLayer;
bool _autoUpdate = false;
ImGuiEx::MenuAction _rootMenu;
ImGuiEx::MenuDef _rootMenu;
std::string _lastFileName;

public:
NodeGraphGUI() : ImGuiPanel("NodeGraph")
{
std::map<std::string, std::function<void()>> actions;
//nop
}

void init()
{
_proceduralModelLayer = _mapNode->getMap()->getLayer<ProceduralTiledModelLayer>();
if (!_proceduralModelLayer.valid())
{
_proceduralModelLayer = new ProceduralTiledModelLayer();
_mapNode->getMap()->addLayer(_proceduralModelLayer.get());
}

auto& ops = _proceduralModelLayer->getNodeGraph()->operations;

#ifdef HAS_PFD
actions["File/Open"] = [&]()
auto& file_menu = _rootMenu["File"];
file_menu["Open"] = [&]()
{
auto f = pfd::open_file("Node Graph", pfd::path::home(), { "XML Files", "*.xml" });
if (!f.result().empty())
Expand All @@ -71,7 +84,7 @@ namespace osgEarth
}
};

actions["File/Save"] = [&]()
file_menu["Save"] = [&]()
{
if (_lastFileName.empty())
{
Expand All @@ -90,7 +103,7 @@ namespace osgEarth
}
};

actions["File/Save as"] = [&]()
file_menu["Save as"] = [&]()
{
auto f = pfd::save_file("Node Graph", pfd::path::home(), { "XML Files", "*.xml" });
if (!f.result().empty())
Expand All @@ -105,51 +118,57 @@ namespace osgEarth
}
};
#endif // HAS_PFD
actions["Operation/Node/Sphere"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.emplace_back(std::make_shared<SphereOperation>()); };
actions["Operation/Node/Transform"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.emplace_back(std::make_shared<TransformOperation>()); };
actions["Operation/Node/Join"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.emplace_back(std::make_shared<JoinNodesOperation>()); };
actions["Operation/Node/Simplify"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.emplace_back(std::make_shared<SimplifyOperation>()); };

actions["Operation/Value/Float"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.emplace_back(std::make_shared<FloatValue>()); };
actions["Operation/Value/Color"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.emplace_back(std::make_shared<ColorValue>()); };
actions["Operation/Value/Random value per feature"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<RandomValuePerFeature>()); };
actions["Operation/Value/Current LOD"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<CurrentLODOperation>()); };
actions["Operation/Value/Comparison"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<ComparisonOperator>()); };
actions["Operation/Value/Color from OSM highways"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<OSMHighwaysColorOperation>()); };

actions["Operation/Feature/Gen random points"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<RandomPointsOperation>()); };
actions["Operation/Feature/Gen points on tile edge"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<PointsOnEdgeOperation>()); };
actions["Operation/Feature/Gen grid of points"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<GriddedPointsOperation>()); };
actions["Operation/Feature/Substitute"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<PlaceNodesOperation>()); };
actions["Operation/Feature/Buffer"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<BufferOperation>()); };
actions["Operation/Feature/Offset curves"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<OffsetCurveOperation>()); };
actions["Operation/Feature/Offset"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<OffsetFeaturesOperation>()); };
actions["Operation/Feature/Filter"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<FilterFeaturesOperation>()); };
actions["Operation/Feature/Select with javascript"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<SelectFeaturesOperation>()); };
actions["Operation/Feature/Get features from file"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<GetFeaturesOperation>()); };
actions["Operation/Feature/Join"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<JoinFeaturesOperation>()); };
actions["Operation/Feature/Polygons to points"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<PolygonToPointsOperation>()); };
actions["Operation/Feature/Points along geometry"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<PointsAlongGeometryOperation>()); };

actions["Operation/Geometry/Features to lines"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<FeaturesToLinesOperation>()); };
actions["Operation/Geometry/Features to polygons"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<FeaturesToPolygonsOperation>()); };
actions["Operation/Geometry/Extrude features"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<ExtrudeOperation>()); };

actions["Operation/Image/Image mask"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<ImageMaskOperation>()); };
actions["Operation/Image/Image from layer"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<ImageFromLayerOperation>()); };

actions["Operation/Output/Features"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<FeatureOutputOperation>()); };
actions["Operation/Output/Node"] = [&]() { _proceduralModelLayer->getNodeGraph()->operations.push_back(std::make_shared<NodeOutputOperation>()); };

_rootMenu = ImGuiEx::CreateRootMenuAction(actions);

auto& node_menu = _rootMenu["Operation"]["Node"];
node_menu["Sphere"] = [&]() { ops.emplace_back(std::make_shared<SphereOperation>()); };
node_menu["Transform"] = [&]() { ops.emplace_back(std::make_shared<TransformOperation>()); };
node_menu["Join"] = [&]() { ops.emplace_back(std::make_shared<JoinNodesOperation>()); };
node_menu["Simplify"] = [&]() { ops.emplace_back(std::make_shared<SimplifyOperation>()); };

auto& value_menu = _rootMenu["Operation"]["Value"];
value_menu["Float"] = [&]() { ops.emplace_back(std::make_shared<FloatValue>()); };
value_menu["Color"] = [&]() { ops.emplace_back(std::make_shared<ColorValue>()); };
value_menu["Random value per feature"] = [&]() { ops.push_back(std::make_shared<RandomValuePerFeature>()); };
value_menu["Current LOD"] = [&]() { ops.push_back(std::make_shared<CurrentLODOperation>()); };
value_menu["Comparison"] = [&]() { ops.push_back(std::make_shared<ComparisonOperator>()); };
value_menu["Color from OSM highways"] = [&]() { ops.push_back(std::make_shared<OSMHighwaysColorOperation>()); };

auto& feature_menu = _rootMenu["Operation"]["Feature"];
feature_menu["Gen random points"] = [&]() { ops.push_back(std::make_shared<RandomPointsOperation>()); };
feature_menu["Gen points on tile edge"] = [&]() { ops.push_back(std::make_shared<PointsOnEdgeOperation>()); };
feature_menu["Gen grid of points"] = [&]() { ops.push_back(std::make_shared<GriddedPointsOperation>()); };
feature_menu["Substitute"] = [&]() { ops.push_back(std::make_shared<PlaceNodesOperation>()); };
feature_menu["Buffer"] = [&]() { ops.push_back(std::make_shared<BufferOperation>()); };
feature_menu["Offset curves"] = [&]() { ops.push_back(std::make_shared<OffsetCurveOperation>()); };
feature_menu["Offset"] = [&]() { ops.push_back(std::make_shared<OffsetFeaturesOperation>()); };
feature_menu["Filter"] = [&]() { ops.push_back(std::make_shared<FilterFeaturesOperation>()); };
feature_menu["Select with javascript"] = [&]() { ops.push_back(std::make_shared<SelectFeaturesOperation>()); };
feature_menu["Get features from file"] = [&]() { ops.push_back(std::make_shared<GetFeaturesOperation>()); };
feature_menu["Join"] = [&]() { ops.push_back(std::make_shared<JoinFeaturesOperation>()); };
feature_menu["Polygons to points"] = [&]() { ops.push_back(std::make_shared<PolygonToPointsOperation>()); };
feature_menu["Points along geometry"] = [&]() { ops.push_back(std::make_shared<PointsAlongGeometryOperation>()); };

auto& geometry_menu = _rootMenu["Operation"]["Geometry"];
geometry_menu["Features to lines"] = [&]() { ops.push_back(std::make_shared<FeaturesToLinesOperation>()); };
geometry_menu["Features to polygons"] = [&]() { ops.push_back(std::make_shared<FeaturesToPolygonsOperation>()); };
geometry_menu["Extrude features"] = [&]() { ops.push_back(std::make_shared<ExtrudeOperation>()); };

auto& image_menu = _rootMenu["Operation"]["Image"];
image_menu["Image mask"] = [&]() { ops.push_back(std::make_shared<ImageMaskOperation>()); };
image_menu["Image from layer"] = [&]() { ops.push_back(std::make_shared<ImageFromLayerOperation>()); };

auto& output_menu = _rootMenu["Operation"]["Output"];
output_menu["Features"] = [&]() { ops.push_back(std::make_shared<FeatureOutputOperation>()); };
output_menu["Node"] = [&]() { ops.push_back(std::make_shared<NodeOutputOperation>()); };

}


void DrawMenuBar()
{
if (ImGui::BeginMenuBar())
{
ImGuiEx::DrawMenuAction(_rootMenu);
ImGuiEx::DrawMenu(_rootMenu);
ImGui::EndMenuBar();
}
}
Expand Down Expand Up @@ -194,12 +213,7 @@ namespace osgEarth
// Find a PML, and if the map doesn't have one, add one for us to use.
if (!_proceduralModelLayer.valid())
{
_proceduralModelLayer = _mapNode->getMap()->getLayer<ProceduralTiledModelLayer>();
if (!_proceduralModelLayer.valid())
{
_proceduralModelLayer = new ProceduralTiledModelLayer();
_mapNode->getMap()->addLayer(_proceduralModelLayer.get());
}
init();
}

if (ImGui::Begin(name(), visible(), ImGuiWindowFlags_MenuBar))
Expand Down
2 changes: 1 addition & 1 deletion src/osgEarthProcedural/ProceduralTiledModelLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ ProceduralTiledModelLayer::init()

// Make a final node output
auto output = std::make_shared< NodeOutputOperation >();
_nodeGraph->addOperation(output);
_nodeGraph->operations.emplace_back(output);

if (getProfile() == nullptr)
{
Expand Down

0 comments on commit 5992475

Please sign in to comment.