diff --git a/Intern/rayx-ui/src/Application.cpp b/Intern/rayx-ui/src/Application.cpp index 302103a4..b4414bea 100644 --- a/Intern/rayx-ui/src/Application.cpp +++ b/Intern/rayx-ui/src/Application.cpp @@ -129,6 +129,7 @@ void Application::run() { if (m_UIParams.rmlReady) { m_RMLPath = m_UIParams.rmlPath.string(); + m_UIParams.beamlineInfo = {}; beamlineFuture = std::async(std::launch::async, &Application::loadBeamline, this, m_RMLPath); m_State = State::LoadingBeamline; m_UIParams.rmlReady = false; @@ -151,28 +152,10 @@ void Application::run() { m_Scene = std::make_unique(m_Device); // Update elements and sources for UI - m_UIParams.beamlineInfo.elements = m_Beamline->m_DesignElements; - m_UIParams.beamlineInfo.sources = m_Beamline->m_DesignSources; - - // Store source positions in UI parameters - m_UIParams.beamlineInfo.rSourcePositions.clear(); - for (auto& source : m_UIParams.beamlineInfo.sources) { - m_UIParams.beamlineInfo.rSourcePositions.push_back(source.getWorldPosition()); - } - - // Set default selection in UI based on available sources or elements - if (m_UIParams.beamlineInfo.sources.size() > 0) { - m_UIParams.beamlineInfo.selectedType = SelectedType::LightSource; - m_UIParams.beamlineInfo.selectedIndex = 0; - } else if (m_UIParams.beamlineInfo.elements.size() > 0) { - m_UIParams.beamlineInfo.selectedType = SelectedType::OpticalElement; - m_UIParams.beamlineInfo.selectedIndex = 0; - } else { - m_UIParams.beamlineInfo.selectedType = SelectedType::None; - } + m_UIParams.beamlineInfo.beamline = m_Beamline.get(); // Prepare for ray loading or element preparation - size_t numElements = m_Beamline->m_DesignElements.size(); + size_t numElements = m_Beamline->numElements(); m_sortedRays.resize(numElements); if (m_UIParams.h5Ready) { raysFuture = std::async(std::launch::async, &Application::loadRays, this, m_RMLPath, numElements); @@ -191,7 +174,7 @@ void Application::run() { case State::Simulating: if (simulationFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - raysFuture = std::async(std::launch::async, &Application::loadRays, this, m_RMLPath, m_Beamline->m_DesignElements.size()); + raysFuture = std::async(std::launch::async, &Application::loadRays, this, m_RMLPath, m_Beamline->numElements()); m_State = State::LoadingRays; } break; @@ -211,7 +194,7 @@ void Application::run() { } else { for (auto ray : m_rays) { size_t id = static_cast(ray.back().m_lastElement); - if (id > m_Beamline->m_DesignElements.size()) { + if (id > m_Beamline->numElements()) { m_UIParams.showH5NotExistPopup = true; break; } @@ -247,8 +230,8 @@ void Application::run() { // only start async task if one is not already running if (!getRObjInputsFuture.valid() || (getRObjInputsFuture.valid() && getRObjInputsFuture.wait_for(std::chrono::seconds(0)) != std::future_status::ready)) { - getRObjInputsFuture = std::async(std::launch::async, &Scene::getRObjectInputs, m_Scene.get(), - std::ref(m_UIParams.beamlineInfo.elements), m_sortedRays, m_buildTextureNeeded); + getRObjInputsFuture = std::async(std::launch::async, &Scene::getRObjectInputs, m_Scene.get(), std::ref(*m_Beamline), + m_sortedRays, m_buildTextureNeeded); } else { RAYX_VERB << "Skipping PrepareElements, async task already running."; } diff --git a/Intern/rayx-ui/src/RayProcessing.cpp b/Intern/rayx-ui/src/RayProcessing.cpp index 5d3367aa..61b816f9 100644 --- a/Intern/rayx-ui/src/RayProcessing.cpp +++ b/Intern/rayx-ui/src/RayProcessing.cpp @@ -9,6 +9,7 @@ #include #include "Application.h" +#include "Beamline/Beamline.h" #include "Colors.h" #include "Debug/Instrumentor.h" @@ -61,8 +62,6 @@ size_t getMaxEvents(const RAYX::BundleHistory& bundleHist) { * Depending on the event type associated with the ray, the function produces visual lines that represent * ray segments, colored based on the event type. */ -// Define the type of the filter function - std::vector getRays(const RAYX::BundleHistory& rayCache, const RAYX::Beamline& beamline, RayFilterFunction filterFunction, uint32_t amountOfRays) { RAYX_PROFILE_FUNCTION_STDOUT(); @@ -74,10 +73,9 @@ std::vector getRays(const RAYX::BundleHistory& rayCache, const RAYX::Beaml size_t maxRayIndex = rayCache.size(); // compile all elements - std::vector compiledElements; - for (const auto& element : beamline.m_DesignElements) { - compiledElements.push_back(element.compile()); - } + auto compiledElements = beamline.compileElements(); + std::vector sourceWorldPositions; + RAYX::Group::accumulateLightSourcesWorldPositions(beamline, glm::dvec4(0, 0, 0, 1), glm::dmat4(1), sourceWorldPositions); for (size_t i : rayIndices) { if (i >= maxRayIndex) { @@ -86,13 +84,13 @@ std::vector getRays(const RAYX::BundleHistory& rayCache, const RAYX::Beaml } auto& rayHist = rayCache[i]; - if (beamline.m_DesignSources.size() <= rayHist[0].m_sourceID) { + if (beamline.numSources() <= rayHist[0].m_sourceID) { RAYX_EXIT << "Trying to access out-of-bounds index with source ID: " << rayHist[0].m_sourceID; } - glm::vec4 rayLastPos = glm::vec4(beamline.m_DesignSources[static_cast(rayHist[0].m_sourceID)].getWorldPosition()); + glm::vec4 rayLastPos = sourceWorldPositions[static_cast(rayHist[0].m_sourceID)]; for (const RAYX::Ray& event : rayHist) { - if (event.m_lastElement >= beamline.m_DesignElements.size()) { + if (event.m_lastElement >= beamline.numElements()) { RAYX_EXIT << "Trying to access out-of-bounds index with element ID: " << event.m_lastElement; } glm::vec4 worldPos = compiledElements[static_cast(event.m_lastElement)].m_outTrans * glm::vec4(event.m_position, 1.0f); diff --git a/Intern/rayx-ui/src/RayProcessing.h b/Intern/rayx-ui/src/RayProcessing.h index 090e5e2a..58c6cad0 100644 --- a/Intern/rayx-ui/src/RayProcessing.h +++ b/Intern/rayx-ui/src/RayProcessing.h @@ -2,6 +2,7 @@ #include #include +#include "Beamline/Beamline.h" #include "RenderObject.h" #define MAX_RAYS 1000 diff --git a/Intern/rayx-ui/src/Scene.cpp b/Intern/rayx-ui/src/Scene.cpp index 2406db66..f96de87d 100644 --- a/Intern/rayx-ui/src/Scene.cpp +++ b/Intern/rayx-ui/src/Scene.cpp @@ -87,29 +87,30 @@ void Scene::buildRaysRObject(const RAYX::Beamline& beamline, UIRayInfo& rayInfo, } } -std::vector Scene::getRObjectInputs(const std::vector elements, - const std::vector>& sortedRays, bool buildTexture) { +std::vector Scene::getRObjectInputs(const RAYX::Beamline& beamline, const std::vector>& sortedRays, + bool buildTexture) { // RAYX_PROFILE_FUNCTION_STDOUT(); + auto elements = beamline.getElements(); + auto compiled = beamline.compileElements(); std::vector rObjectsInput; if (buildTexture) m_textureInputCache.clear(); for (uint32_t i = 0; i < elements.size(); i++) { - auto compiled = elements[i].compile(); std::vector vertices; std::vector indices; try { - triangulateObject(compiled, vertices, indices); + triangulateObject(compiled[i], vertices, indices); } catch (const std::exception& ex) { - RAYX_WARN << ex.what() << ". Object \"" << elements[i].getName() + RAYX_WARN << ex.what() << ". Object \"" << elements[i]->getName() << "\" can't be rendered due to triangulation issues. Make sure it is defined correctly in the RML file."; continue; // Input is not generated --> Object won't be built/rendered } - glm::dmat4& modelMatrix = compiled.m_outTrans; + glm::dmat4& modelMatrix = compiled[i].m_outTrans; if (buildTexture) { - auto [width, height] = getRectangularDimensions(compiled.m_cutout); + auto [width, height] = getRectangularDimensions(compiled[i].m_cutout); std::vector> footprint = makeFootprint(sortedRays[i], -width / 2, width / 2, -height / 2, height / 2, (uint32_t)(width * 10), (uint32_t)(height * 10)); diff --git a/Intern/rayx-ui/src/Scene.h b/Intern/rayx-ui/src/Scene.h index a832dce8..743ba5ad 100644 --- a/Intern/rayx-ui/src/Scene.h +++ b/Intern/rayx-ui/src/Scene.h @@ -27,8 +27,8 @@ class Scene { void buildRaysRObject(const RAYX::Beamline& beamline, UIRayInfo& rayInfo, std::shared_ptr setLayout, std::shared_ptr descriptorPool); - std::vector getRObjectInputs(const std::vector elements, - const std::vector>& sortedRays, bool buildTexture); + std::vector getRObjectInputs(const RAYX::Beamline& beamline, const std::vector>& sortedRays, + bool buildTexture); void buildRObjectsFromInput(std::vector&& inputs, std::shared_ptr setLayout, std::shared_ptr descriptorPool, bool buildTexture); diff --git a/Intern/rayx-ui/src/Simulator.cpp b/Intern/rayx-ui/src/Simulator.cpp index bcc96775..b6de4664 100644 --- a/Intern/rayx-ui/src/Simulator.cpp +++ b/Intern/rayx-ui/src/Simulator.cpp @@ -33,7 +33,6 @@ void Simulator::runSimulation() { if (notEnoughEvents) { RAYX_LOG << "Not enough events (" << m_maxEvents << ")! Consider increasing m_maxEvents."; } - // Export Rays to external data. std::string path = m_RMLPath.string(); @@ -46,10 +45,11 @@ void Simulator::runSimulation() { Format fmt = formatFromString(defaultFormatString()); std::vector names; - names.reserve(m_Beamline.m_DesignElements.size()); + auto elements = m_Beamline.getElements(); + names.reserve(elements.size()); - for (const auto& designElement : m_Beamline.m_DesignElements) { - names.push_back(designElement.getName()); + for (const auto designElement : elements) { + names.push_back(designElement->getName()); } path += ".h5"; @@ -69,7 +69,7 @@ void Simulator::setSimulationParameters(const std::filesystem::path& RMLPath, co } m_RMLPath = RMLPath; - m_Beamline = std::move(beamline); + m_Beamline = std::move(*static_cast(beamline.clone().get())); m_max_batch_size = simulationInfo.maxBatchSize; m_seq = simulationInfo.sequential ? RAYX::Sequential::Yes : RAYX::Sequential::No; m_maxEvents = simulationInfo.maxEvents; diff --git a/Intern/rayx-ui/src/Triangulation/TraceTriangulation.cpp b/Intern/rayx-ui/src/Triangulation/TraceTriangulation.cpp index 25e5c5b0..3b4b41bb 100644 --- a/Intern/rayx-ui/src/Triangulation/TraceTriangulation.cpp +++ b/Intern/rayx-ui/src/Triangulation/TraceTriangulation.cpp @@ -51,7 +51,7 @@ std::vector> createRayGrid(size_t size, double width, dou * cutout. Using CPU-based ray tracing, it computes the intersections between rays and the optical element's surface within the cutout. The ray * intersections are then grouped into triangles based on the grid, and a RenderObject representing these triangles is returned. */ -void traceTriangulation(const RAYX::Element compiled, std::vector& vertices, std::vector& indices) { +void traceTriangulation(const RAYX::OpticalElement compiled, std::vector& vertices, std::vector& indices) { using DeviceType = RAYX::DeviceConfig::DeviceType; auto tracer = RAYX::Tracer(RAYX::DeviceConfig(DeviceType::Cpu).enableBestDevice()); diff --git a/Intern/rayx-ui/src/Triangulation/TraceTriangulation.h b/Intern/rayx-ui/src/Triangulation/TraceTriangulation.h index 8cc63887..119c0de1 100644 --- a/Intern/rayx-ui/src/Triangulation/TraceTriangulation.h +++ b/Intern/rayx-ui/src/Triangulation/TraceTriangulation.h @@ -23,7 +23,7 @@ * RenderObject renderObj = traceTriangulation(element, device); * @endcode */ -void traceTriangulation(const RAYX::Element compiled, std::vector& vertices, std::vector& indices); +void traceTriangulation(const RAYX::OpticalElement compiled, std::vector& vertices, std::vector& indices); // ------ Helper functions ------ diff --git a/Intern/rayx-ui/src/Triangulation/Triangulate.cpp b/Intern/rayx-ui/src/Triangulation/Triangulate.cpp index cfb837a6..9e1b6982 100644 --- a/Intern/rayx-ui/src/Triangulation/Triangulate.cpp +++ b/Intern/rayx-ui/src/Triangulation/Triangulate.cpp @@ -417,7 +417,7 @@ PolygonSimple calculateOutlineFromCutout(const RAYX::Cutout& cutout, std::vector return indices; } -void planarTriangulation(const RAYX::Element compiled, std::vector& vertices, std::vector& indices) { +void planarTriangulation(const RAYX::OpticalElement compiled, std::vector& vertices, std::vector& indices) { // The slit behaviour needs special attention, since it is basically three cutouts (the slit, the beamstop and the opening) PolygonComplex poly; if (compiled.m_behaviour.m_type == RAYX::BTYPE_SLIT) { @@ -440,7 +440,7 @@ bool isPlanar(const RAYX::QuadricSurface& q) { /** * This function takes optical elements and categorizes them for efficient triangulation. */ -void triangulateObject(const RAYX::Element compiled, std::vector& vertices, std::vector& indices) { +void triangulateObject(const RAYX::OpticalElement compiled, std::vector& vertices, std::vector& indices) { // RAYX_PROFILE_FUNCTION_STDOUT(); switch (static_cast(compiled.m_surface.m_type)) { case RAYX::STYPE_PLANE_XZ: { diff --git a/Intern/rayx-ui/src/Triangulation/Triangulate.h b/Intern/rayx-ui/src/Triangulation/Triangulate.h index 59d72ffd..6dd7e735 100644 --- a/Intern/rayx-ui/src/Triangulation/Triangulate.h +++ b/Intern/rayx-ui/src/Triangulation/Triangulate.h @@ -11,4 +11,4 @@ * @param elements A vector of optical elements to be triangulated. * @return A vector of RenderObject, which are the triangulated version of the input elements. */ -void triangulateObject(const RAYX::Element compiled, std::vector& vertices, std::vector& indices); +void triangulateObject(const RAYX::OpticalElement compiled, std::vector& vertices, std::vector& indices); diff --git a/Intern/rayx-ui/src/UserInterface/BeamlineDesignHandler.cpp b/Intern/rayx-ui/src/UserInterface/BeamlineDesignHandler.cpp index 652014eb..3cc5a965 100644 --- a/Intern/rayx-ui/src/UserInterface/BeamlineDesignHandler.cpp +++ b/Intern/rayx-ui/src/UserInterface/BeamlineDesignHandler.cpp @@ -9,31 +9,33 @@ #include #include +#include "Beamline/Beamline.h" #include "Beamline/StringConversion.h" #include "Data/Strings.cpp" #include "Debug/Instrumentor.h" -void BeamlineDesignHandler::showBeamlineDesignWindow(UIBeamlineInfo& uiBeamlineInfo) { - // RAYX_PROFILE_FUNCTION_STDOUT(); +void BeamlineDesignHandler::showBeamlineDesignWindow(UIBeamlineInfo& uiInfo) { + // If no node is selected, do nothing + if (!uiInfo.selectedNode) { + ImGui::Text("No node selected"); + return; + } - if (uiBeamlineInfo.selectedType == SelectedType::LightSource) { // source - if (uiBeamlineInfo.selectedIndex >= 0 && uiBeamlineInfo.selectedIndex < static_cast(uiBeamlineInfo.sources.size())) { - auto sourceParameters = uiBeamlineInfo.sources[uiBeamlineInfo.selectedIndex].m_elementParameters; - showParameters(sourceParameters, uiBeamlineInfo.elementsChanged, uiBeamlineInfo.selectedType); - } else { - // Handle out-of-bounds access for sources - RAYX_EXIT << "Error: selectedIndex is out of bounds for sources."; + if (uiInfo.selectedNode->isSource()) { + const auto srcPtr = dynamic_cast(uiInfo.selectedNode); + if (srcPtr) { + // showParameters can now edit srcPtr->m_elementParameters + showParameters(srcPtr->m_elementParameters, uiInfo.elementsChanged, SelectedType::LightSource); } - } else if (uiBeamlineInfo.selectedType == SelectedType::OpticalElement) { // element - if (uiBeamlineInfo.selectedIndex >= 0 && uiBeamlineInfo.selectedIndex < static_cast(uiBeamlineInfo.elements.size())) { - auto elementParameters = uiBeamlineInfo.elements[uiBeamlineInfo.selectedIndex].m_elementParameters; - showParameters(elementParameters, uiBeamlineInfo.elementsChanged, uiBeamlineInfo.selectedType); - } else { - // Handle out-of-bounds access for elements - RAYX_EXIT << "Error: selectedIndex is out of bounds for elements."; + } else if (uiInfo.selectedNode->isElement()) { + const auto elemPtr = dynamic_cast(uiInfo.selectedNode); + if (elemPtr) { + showParameters(elemPtr->m_elementParameters, uiInfo.elementsChanged, SelectedType::OpticalElement); } - } else if (uiBeamlineInfo.selectedType == SelectedType::Group) { // group - // Handle group if needed + } else if (uiInfo.selectedNode->isGroup()) { + ImGui::Text("Group editing is to be implemented still..."); + } else { + throw std::runtime_error("Tree element of unknown type encountered!"); } } diff --git a/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.cpp b/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.cpp index 6852b101..28596bec 100644 --- a/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.cpp +++ b/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.cpp @@ -1,206 +1,75 @@ #include "BeamlineOutliner.h" #include - -#include - -#include "Data/Strings.h" - -BeamlineOutliner::BeamlineOutliner(/* args */) {} - +#include +#include +#include +#include +#include + +#include "Design/DesignElement.h" +#include "Design/DesignSource.h" +#include "Settings.h" + +// Constructor / Destructor +BeamlineOutliner::BeamlineOutliner() {} BeamlineOutliner::~BeamlineOutliner() {} -void BeamlineOutliner::renderImGuiTree(const TreeNode& treeNode, CameraController& camController, std::vector& elements, - std::vector& rSourcePositions, UIBeamlineInfo& beamlineInfo) const { - for (auto& child : treeNode.children) { - ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; - - if (child.children.empty()) { - nodeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; +// Recursive helper: render a Group and its children as an ImGui tree. +void BeamlineOutliner::renderImGuiTreeFromGroup(RAYX::Group* group, RAYX::BeamlineNode*& selected, CameraController& cam, int depth) { + if (!group) return; + int ctr = 0; + for (auto& child : *group) { + std::string label; + + // Use the node's name if possible. + if (child->isElement()) { + label = static_cast(child.get())->getName(); + } else if (child->isSource()) { + label = static_cast(child.get())->getName(); + } else if (child->isGroup()) { + label = "Group"; } - bool isSelected = (beamlineInfo.selectedIndex == child.index) && - ((beamlineInfo.selectedType == SelectedType::LightSource && child.category == SelectedType::LightSource) || - (beamlineInfo.selectedType == SelectedType::OpticalElement && child.category == SelectedType::OpticalElement) || - (beamlineInfo.selectedType == SelectedType::Group && !child.children.empty())); - - if (isSelected) { - nodeFlags |= ImGuiTreeNodeFlags_Selected; - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4((84.0f / 256.0f), (84.0f / 256.0f), (84.0f / 256.0f), 1.00f)); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4((84.0f / 256.0f), (84.0f / 256.0f), (84.0f / 256.0f), 1.00f)); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4((84.0f / 256.0f), (84.0f / 256.0f), (84.0f / 256.0f), 1.00f)); + std::string buttonLabel = "<-##" + std::to_string(ctr++); + if (ImGui::Button(buttonLabel.c_str())) { + const glm::dvec4& pos = child->getWorldPosition(); + cam.lookAtPoint({pos.x, pos.y, pos.z}); } - // Jump-to-element button - std::string buttonId = "<--##" + child.name + std::to_string(child.index); - bool validIndex = (child.index >= 0 && static_cast(child.index) < elements.size()); - if (!validIndex) ImGui::BeginDisabled(); // Groups are not fully supported yet, so child.index of groups is -1 - if (ImGui::Button(buttonId.c_str())) { - RAYX::Element compiled = elements[child.index].compile(); - if (child.category == SelectedType::OpticalElement && child.index >= 0 && static_cast(child.index) < elements.size()) { - glm::vec3 translationVec = {compiled.m_outTrans[3][0], compiled.m_outTrans[3][1], compiled.m_outTrans[3][2]}; - camController.lookAtPoint(translationVec); - } else if (child.category == SelectedType::LightSource && child.index >= 0 && - static_cast(child.index) < rSourcePositions.size()) { - camController.lookAtPoint(rSourcePositions[child.index]); - } - } - if (!validIndex) ImGui::EndDisabled(); + // Set tree node flags: if the node is a leaf (not a group), mark it as such. ImGui::SameLine(); - - // Label - std::string nodeLabel = child.name + "##" + std::to_string(child.index); - bool nodeOpen = ImGui::TreeNodeEx(nodeLabel.c_str(), nodeFlags); - - if (isSelected) { - ImGui::PopStyleColor(3); + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + if (!child->isGroup()) { + flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; } + bool nodeOpen = ImGui::TreeNodeEx(label.c_str(), flags, "%s", label.c_str()); - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { - RAYX_VERB << "Selected object: " << child.name << " with index " << child.index; - - if (child.category == SelectedType::LightSource) { - beamlineInfo.selectedType = SelectedType::LightSource; - } else if (child.category == SelectedType::OpticalElement) { - beamlineInfo.selectedType = SelectedType::OpticalElement; - } else { - beamlineInfo.selectedType = SelectedType::Group; - } - beamlineInfo.selectedIndex = child.index; + // If the user clicks the node, you can update selection info here. + if (ImGui::IsItemClicked()) { + selected = child.get(); } - if (nodeOpen && !child.children.empty()) { - this->renderImGuiTree(child, camController, elements, rSourcePositions, beamlineInfo); + // If this node is a Group and is open, recursively render its children. + if (nodeOpen && child->isGroup()) { + // Get the group pointer from the variant. + auto* childGroupPtr = dynamic_cast(child.get()); + // Dereference it to call the function. + renderImGuiTreeFromGroup(childGroupPtr, selected, cam, depth + 1); ImGui::TreePop(); } } } -void BeamlineOutliner::buildTreeFromXMLNode(rapidxml::xml_node<>* node, TreeNode& treeNode) { - using RAYX::ElementType; - - for (rapidxml::xml_node<>* xmlChild = node->first_node(); xmlChild; xmlChild = xmlChild->next_sibling()) { - SelectedType category = SelectedType::None; - - if (strcmp(xmlChild->name(), "object") == 0) { - rapidxml::xml_attribute<>* typeAttr = xmlChild->first_attribute("type"); - std::string type = typeAttr ? typeAttr->value() : ""; - ElementType elementType = RAYX::findElementString(type); - switch (elementType) { - case ElementType::PointSource: - case ElementType::MatrixSource: - case ElementType::DipoleSrc: - case ElementType::DipoleSource: - case ElementType::CircleSource: - case ElementType::SimpleUndulatorSource: - category = SelectedType::LightSource; - break; - case ElementType::ImagePlane: - case ElementType::PlaneMirror: - case ElementType::ToroidMirror: - case ElementType::Slit: - case ElementType::SphereGrating: - case ElementType::PlaneGrating: - case ElementType::Sphere: - case ElementType::ReflectionZoneplate: - case ElementType::EllipsoidMirror: - case ElementType::CylinderMirror: - case ElementType::ConeMirror: - case ElementType::ParaboloidMirror: - case ElementType::SphereMirror: - case ElementType::ExpertsMirror: - category = SelectedType::OpticalElement; - break; - default: - category = SelectedType::None; - break; - } - - TreeNode objectNode(xmlChild->first_attribute("name")->value(), type, category); - - if (category == SelectedType::LightSource) { - objectNode.index = m_lightSourceIndex++; - } else if (category == SelectedType::OpticalElement) { - objectNode.index = m_opticalElementIndex++; - } - - treeNode.children.emplace_back(objectNode); - } else if (strcmp(xmlChild->name(), "group") == 0) { - category = SelectedType::Group; - TreeNode groupNode(xmlChild->first_attribute("name")->value(), "", category); - buildTreeFromXMLNode(xmlChild, groupNode); - treeNode.children.push_back(groupNode); - } - } -} - -void BeamlineOutliner::renderImGuiTreeFromRML(const std::filesystem::path& filename, CameraController& camController, - std::vector& elements, std::vector& rSourcePositions, - UIBeamlineInfo& beamlineInfo) { - // Check if file exists - if (!std::filesystem::exists(filename)) { - ImGui::Text("Choose a file to display the beamline outline."); - return; - } - - // Read and parse the RML file - std::ifstream fileContent(filename); - if (!fileContent.is_open()) { - ImGui::Text("Error: Could not open file."); - return; - } - - std::stringstream buffer; - buffer << fileContent.rdbuf(); - std::string test = buffer.str(); - - // Check if the file is empty - if (test.empty()) { - ImGui::Text("Error: File is empty."); - return; - } - - std::vector cstr(test.c_str(), test.c_str() + test.size() + 1); - rapidxml::xml_document<> doc; - - try { - doc.parse<0>(cstr.data()); - } catch (rapidxml::parse_error& e) { - ImGui::Text("Error: XML Parsing failed:"); - ImGui::Text("%s", e.what()); - return; - } - - rapidxml::xml_node<>* xml_beamline = doc.first_node("lab")->first_node("beamline"); - if (xml_beamline == nullptr) { - ImGui::Text("Error: not found in XML."); - return; - } - - // Call recursive function to handle the object collection and render the ImGui tree - m_pTreeRoot = std::make_unique("Root"); - buildTreeFromXMLNode(xml_beamline, *m_pTreeRoot); - renderImGuiTree(*m_pTreeRoot, camController, elements, rSourcePositions, beamlineInfo); -} - +// This function displays the beamline outline window using the existing data structure. void BeamlineOutliner::showBeamlineOutlineWindow(UIParameters& uiParams) { ImGui::Begin("Beamline Outline"); - - if (uiParams.rmlPath.empty()) { - ImGui::Text("Choose a file to display the beamline outline."); - } else if (uiParams.rmlPath != m_currentRML) { - // Create and render new Tree - m_lightSourceIndex = 0; - m_opticalElementIndex = 0; - renderImGuiTreeFromRML(uiParams.rmlPath, uiParams.camController, uiParams.beamlineInfo.elements, uiParams.beamlineInfo.rSourcePositions, - uiParams.beamlineInfo); - m_currentRML = uiParams.rmlPath; - } else if (m_pTreeRoot == nullptr) { - RAYX_EXIT << "Error: Tree root is null."; + UIBeamlineInfo& blInfo = uiParams.beamlineInfo; + if (blInfo.beamline == nullptr) { + ImGui::Text("Beamline not loaded."); } else { - // Render same Tree - renderImGuiTree(*m_pTreeRoot, uiParams.camController, uiParams.beamlineInfo.elements, uiParams.beamlineInfo.rSourcePositions, - uiParams.beamlineInfo); + // Render the tree starting from the beamline root. + renderImGuiTreeFromGroup(blInfo.beamline, blInfo.selectedNode, uiParams.camController, 0); } ImGui::End(); diff --git a/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.h b/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.h index 17d5cd27..ea8ef74f 100644 --- a/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.h +++ b/Intern/rayx-ui/src/UserInterface/BeamlineOutliner.h @@ -1,42 +1,23 @@ #pragma once -#include +#include -#include -#include -#include +#include "Beamline/Beamline.h" -#include "UserInterface/Settings.h" - -// Simple TreeNode -struct TreeNode { - std::string name; - std::string type; - SelectedType category = SelectedType::None; - int index = -1; - std::vector children; - - TreeNode(const std::string& name, const std::string& type = "", SelectedType category = SelectedType::None) - : name(name), type(type), category(category) {} -}; +// Forward declarations for your UI types +class CameraController; +struct UIBeamlineInfo; +struct UIParameters; class BeamlineOutliner { public: BeamlineOutliner(); ~BeamlineOutliner(); + // Show the outline window (to be called every frame in your UI loop) void showBeamlineOutlineWindow(UIParameters& uiParams); private: - std::unique_ptr m_pTreeRoot = nullptr; - - int m_lightSourceIndex = 0; - int m_opticalElementIndex = 0; - std::filesystem::path m_currentRML; - - void renderImGuiTree(const TreeNode& treeNode, CameraController& camController, std::vector& rObjects, - std::vector& rSourcePositions, UIBeamlineInfo& beamlineInfo) const; - void buildTreeFromXMLNode(rapidxml::xml_node<>* node, TreeNode& treeNode); - void renderImGuiTreeFromRML(const std::filesystem::path& filename, CameraController& camController, std::vector& rObjects, - std::vector& rSourcePositions, UIBeamlineInfo& beamlineInfo); -}; \ No newline at end of file + // Recursive helper that renders the tree starting at a given Group. + void renderImGuiTreeFromGroup(RAYX::Group* group, RAYX::BeamlineNode*& selected, CameraController& cam, int depth = 0); +}; diff --git a/Intern/rayx-ui/src/UserInterface/Settings.h b/Intern/rayx-ui/src/UserInterface/Settings.h index 534178ee..3b129fd9 100644 --- a/Intern/rayx-ui/src/UserInterface/Settings.h +++ b/Intern/rayx-ui/src/UserInterface/Settings.h @@ -3,7 +3,10 @@ #include #include +#include +#include +#include "Beamline/Beamline.h" #include "Camera.h" #include "Debug/Debug.h" #include "Design/DesignElement.h" @@ -33,8 +36,8 @@ struct UISimulationInfo { bool fixedSeed = false; int seed; - UISimulationInfo(int maxEvents, int maxBatchSize, bool sequential, const std::vector& availableDevices, - int deviceIndex, bool fixedSeed = false, int seed = 0) + UISimulationInfo(int maxEvents, int maxBatchSize, bool sequential, const std::vector& availableDevices, int deviceIndex, + bool fixedSeed = false, int seed = 0) : maxEvents(maxEvents), maxBatchSize(maxBatchSize), sequential(sequential), @@ -45,14 +48,13 @@ struct UISimulationInfo { }; enum class SelectedType { None = -1, LightSource = 0, OpticalElement = 1, Group = 2 }; + struct UIBeamlineInfo { - std::vector rSourcePositions; - std::vector elements; - std::vector sources; - SelectedType selectedType = SelectedType::None; - int selectedIndex = -1; + RAYX::Beamline* beamline = nullptr; // Beamline optional, lifetime managed by Application + RAYX::BeamlineNode* selectedNode = nullptr; // Selection optional, lifetime managed by Beamline bool elementsChanged = false; }; + struct UIParameters { VkExtent2D sceneExtent; VkDescriptorSet sceneDescriptorSet; diff --git a/Intern/rayx-ui/src/UserInterface/UIHandler.cpp b/Intern/rayx-ui/src/UserInterface/UIHandler.cpp index f2bb6a9a..2eaa573c 100644 --- a/Intern/rayx-ui/src/UserInterface/UIHandler.cpp +++ b/Intern/rayx-ui/src/UserInterface/UIHandler.cpp @@ -584,7 +584,6 @@ void UIHandler::showSimulationSettingsPopupWindow(UIParameters& uiParams) { ImGui::Combo("Device", reinterpret_cast(&uiParams.simulationInfo.deviceIndex), &deviceItems[0], static_cast(deviceItems.size())); - // maxEvents selection ImGui::InputScalar("Max Events", ImGuiDataType_U32, &uiParams.simulationInfo.maxEvents);