//------------------------------------------------------------------------------ // VERSION 0.9.1 // // LICENSE // This software is dual-licensed to the public domain and under the following // license: you are granted a perpetual, irrevocable license to copy, modify, // publish, and distribute this file as you see fit. // // CREDITS // Written by Michal Cichon //------------------------------------------------------------------------------ # include "imgui_node_editor_internal.h" # include # include # include # include # include # include # include # include # include namespace ed = ax::NodeEditor::Detail; //------------------------------------------------------------------------------ // // Node Settings // //------------------------------------------------------------------------------ void ed::NodeSettings::ClearDirty() { m_IsDirty = false; m_DirtyReason = SaveReasonFlags::None; } void ed::NodeSettings::MakeDirty(SaveReasonFlags reason) { m_IsDirty = true; m_DirtyReason = m_DirtyReason | reason; } ed::json::value ed::NodeSettings::Serialize() { json::value result; result["location"]["x"] = m_Location.x; result["location"]["y"] = m_Location.y; if (m_GroupSize.x > 0 || m_GroupSize.y > 0) { result["group_size"]["x"] = m_GroupSize.x; result["group_size"]["y"] = m_GroupSize.y; } return result; } bool ed::NodeSettings::Parse(const std::string& string, NodeSettings& settings) { auto settingsValue = json::value::parse(string); if (settingsValue.is_discarded()) return false; return Parse(settingsValue, settings); } bool ed::NodeSettings::Parse(const json::value& data, NodeSettings& result) { if (!data.is_object()) return false; auto tryParseVector = [](const json::value& v, ImVec2& result) -> bool { if (v.is_object()) { auto xValue = v["x"]; auto yValue = v["y"]; if (xValue.is_number() && yValue.is_number()) { result.x = static_cast(xValue.get()); result.y = static_cast(yValue.get()); return true; } } return false; }; if (!tryParseVector(data["location"], result.m_Location)) return false; if (data.contains("group_size") && !tryParseVector(data["group_size"], result.m_GroupSize)) return false; return true; } //------------------------------------------------------------------------------ // // Settings // //------------------------------------------------------------------------------ ed::NodeSettings* ed::Settings::AddNode(NodeId id) { m_Nodes.push_back(NodeSettings(id)); return &m_Nodes.back(); } ed::NodeSettings* ed::Settings::FindNode(NodeId id) { for (auto& settings : m_Nodes) if (settings.m_ID == id) return &settings; return nullptr; } void ed::Settings::RemoveNode(NodeId id) { auto node = FindNode(id); if (!node) return; *node = NodeSettings(id); } ed::LinkSettings* ed::Settings::AddLink(LinkId id) { m_Links.push_back(LinkSettings(id)); return &m_Links.back(); } ed::LinkSettings* ed::Settings::FindLink(LinkId id) { for (auto& settings : m_Links) if (settings.m_ID == id) return &settings; return nullptr; } void ed::Settings::RemoveLink(LinkId id) { auto link = FindLink(id); if (!link) return; *link = LinkSettings(id); } void ed::Settings::ClearDirty(Node* node) { if (node) { auto settings = FindNode(node->m_ID); IM_ASSERT(settings); settings->ClearDirty(); } else { m_IsDirty = false; m_DirtyReason = SaveReasonFlags::None; for (auto& knownNode : m_Nodes) knownNode.ClearDirty(); } } void ed::Settings::MakeDirty(SaveReasonFlags reason, Node* node) { m_IsDirty = true; m_DirtyReason = m_DirtyReason | reason; if (node) { auto settings = FindNode(node->m_ID); IM_ASSERT(settings); settings->MakeDirty(reason); } } std::string ed::Settings::Serialize() { json::value result; auto serializeObjectId = [](ObjectId id) { auto value = std::to_string(reinterpret_cast(id.AsPointer())); switch (id.Type()) { default: case NodeEditor::Detail::ObjectType::None: return value; case NodeEditor::Detail::ObjectType::Node: return "node:" + value; case NodeEditor::Detail::ObjectType::Link: return "link:" + value; case NodeEditor::Detail::ObjectType::Pin: return "pin:" + value; } }; auto& nodes = result["nodes"]; for (auto& node : m_Nodes) { if (node.m_WasUsed) nodes[serializeObjectId(node.m_ID)] = node.Serialize(); } auto& links = result["links"]; for (auto& link : m_Links) { // Save all non-Auto modes (Straight, Guided) if (link.m_Mode != LinkMode::Auto) links[serializeObjectId(link.m_ID)] = link.Serialize(); } auto& selection = result["selection"]; for (auto& id : m_Selection) selection.push_back(serializeObjectId(id)); auto& view = result["view"]; view["scroll"]["x"] = m_ViewScroll.x; view["scroll"]["y"] = m_ViewScroll.y; view["zoom"] = m_ViewZoom; view["visible_rect"]["min"]["x"] = m_VisibleRect.Min.x; view["visible_rect"]["min"]["y"] = m_VisibleRect.Min.y; view["visible_rect"]["max"]["x"] = m_VisibleRect.Max.x; view["visible_rect"]["max"]["y"] = m_VisibleRect.Max.y; return result.dump(4); // Pretty print with 4 space indent } bool ed::Settings::Parse(const std::string& string, Settings& settings) { Settings result = settings; auto settingsValue = json::value::parse(string); if (settingsValue.is_discarded()) return false; if (!settingsValue.is_object()) return false; auto tryParseVector = [](const json::value& v, ImVec2& result) -> bool { if (v.is_object() && v.contains("x") && v.contains("y")) { auto xValue = v["x"]; auto yValue = v["y"]; if (xValue.is_number() && yValue.is_number()) { result.x = static_cast(xValue.get()); result.y = static_cast(yValue.get()); return true; } } return false; }; auto deserializeObjectId = [](const std::string& str) { auto separator = str.find_first_of(':'); auto idStart = str.c_str() + ((separator != std::string::npos) ? separator + 1 : 0); auto id = reinterpret_cast(strtoull(idStart, nullptr, 10)); if (str.compare(0, separator, "node") == 0) return ObjectId(NodeId(id)); else if (str.compare(0, separator, "link") == 0) return ObjectId(LinkId(id)); else if (str.compare(0, separator, "pin") == 0) return ObjectId(PinId(id)); else // fallback to old format return ObjectId(NodeId(id)); //return ObjectId(); }; //auto& settingsObject = settingsValue.get(); auto& nodesValue = settingsValue["nodes"]; if (nodesValue.is_object()) { for (auto& node : nodesValue.get()) { auto id = deserializeObjectId(node.first.c_str()).AsNodeId(); auto nodeSettings = result.FindNode(id); if (!nodeSettings) nodeSettings = result.AddNode(id); NodeSettings::Parse(node.second, *nodeSettings); } } auto& linksValue = settingsValue["links"]; if (linksValue.is_object()) { for (auto& link : linksValue.get()) { auto id = deserializeObjectId(link.first.c_str()).AsLinkId(); auto linkSettings = result.FindLink(id); if (!linkSettings) linkSettings = result.AddLink(id); LinkSettings::Parse(link.second, *linkSettings); } } auto& selectionValue = settingsValue["selection"]; if (selectionValue.is_array()) { const auto selectionArray = selectionValue.get(); result.m_Selection.reserve(selectionArray.size()); result.m_Selection.resize(0); for (auto& selection : selectionArray) { if (selection.is_string()) result.m_Selection.push_back(deserializeObjectId(selection.get())); } } auto& viewValue = settingsValue["view"]; if (viewValue.is_object()) { auto& viewScrollValue = viewValue["scroll"]; auto& viewZoomValue = viewValue["zoom"]; if (!tryParseVector(viewScrollValue, result.m_ViewScroll)) result.m_ViewScroll = ImVec2(0, 0); result.m_ViewZoom = viewZoomValue.is_number() ? static_cast(viewZoomValue.get()) : 1.0f; if (!viewValue.contains("visible_rect") || !tryParseVector(viewValue["visible_rect"]["min"], result.m_VisibleRect.Min) || !tryParseVector(viewValue["visible_rect"]["max"], result.m_VisibleRect.Max)) result.m_VisibleRect = {}; } settings = std::move(result); return true; }