mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-06 03:29:44 -06:00
Merge pull request #2604 from OpenSpace/feature/documentation-overhaul
Feature/documentation overhaul
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -32,3 +32,6 @@
|
||||
[submodule "support/coding/codegen"]
|
||||
path = support/coding/codegen
|
||||
url = https://github.com/OpenSpace/codegen
|
||||
[submodule "documentation"]
|
||||
path = documentation
|
||||
url = https://github.com/OpenSpace/OpenSpace-Documentation-Dist.git
|
||||
|
||||
1
documentation
Submodule
1
documentation
Submodule
Submodule documentation added at e5320f7802
@@ -28,6 +28,7 @@
|
||||
#include <openspace/documentation/documentationgenerator.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/json.h>
|
||||
#include <ghoul/misc/exception.h>
|
||||
|
||||
namespace openspace::documentation {
|
||||
@@ -109,6 +110,8 @@ public:
|
||||
|
||||
std::string generateJson() const override;
|
||||
|
||||
nlohmann::json generateJsonJson() const;
|
||||
|
||||
private:
|
||||
|
||||
/// The list of all Documentation%s that are stored by the DocumentationEngine
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
void keyboardCallback(Key key, KeyModifier modifier, KeyAction action);
|
||||
|
||||
std::string generateJson() const override;
|
||||
nlohmann::json generateJsonJson() const;
|
||||
|
||||
const std::multimap<KeyWithModifier, std::string>& keyBindings() const;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <openspace/documentation/documentationgenerator.h>
|
||||
|
||||
#include <openspace/json.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -99,6 +100,13 @@ public:
|
||||
*/
|
||||
const std::string& identifier() const;
|
||||
|
||||
/**
|
||||
* Returns the type of this PropertyOwner.
|
||||
*
|
||||
* \return The type of this PropertyOwner
|
||||
*/
|
||||
const std::string& type() const;
|
||||
|
||||
/**
|
||||
* Sets the user-facing name of this PropertyOwner. This name does not have to be
|
||||
* unique, but it is recommended to be.
|
||||
@@ -291,6 +299,8 @@ public:
|
||||
// Generate JSON for documentation
|
||||
std::string generateJson() const override;
|
||||
|
||||
nlohmann::json generateJsonJson() const;
|
||||
|
||||
protected:
|
||||
/// The unique identifier of this PropertyOwner
|
||||
std::string _identifier;
|
||||
@@ -298,6 +308,8 @@ protected:
|
||||
std::string _guiName;
|
||||
/// The description for this PropertyOwner
|
||||
std::string _description;
|
||||
/// The type for this PropertyOwner
|
||||
std::string _type;
|
||||
|
||||
/// The owner of this PropertyOwner
|
||||
PropertyOwner* _owner = nullptr;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <openspace/documentation/documentationgenerator.h>
|
||||
|
||||
#include <openspace/json.h>
|
||||
#include <vector>
|
||||
|
||||
namespace openspace {
|
||||
@@ -35,6 +36,7 @@ class SceneLicenseWriter : public DocumentationGenerator {
|
||||
public:
|
||||
SceneLicenseWriter();
|
||||
std::string generateJson() const override;
|
||||
nlohmann::json generateJsonJson() const;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/util/syncable.h>
|
||||
#include <openspace/documentation/documentationgenerator.h>
|
||||
|
||||
#include <openspace/scripting/lualibrary.h>
|
||||
#include <openspace/json.h>
|
||||
#include <ghoul/lua/luastate.h>
|
||||
#include <ghoul/misc/boolean.h>
|
||||
#include <filesystem>
|
||||
@@ -96,6 +96,7 @@ public:
|
||||
std::vector<std::string> allLuaFunctions() const;
|
||||
|
||||
std::string generateJson() const override;
|
||||
nlohmann::json generateJsonJson() const;
|
||||
|
||||
private:
|
||||
BooleanType(Replace);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <openspace/documentation/documentationgenerator.h>
|
||||
|
||||
#include <openspace/json.h>
|
||||
#include <ghoul/misc/exception.h>
|
||||
#include <ghoul/misc/templatefactory.h>
|
||||
#include <memory>
|
||||
@@ -109,6 +110,7 @@ public:
|
||||
ghoul::TemplateFactory<T>* factory() const;
|
||||
|
||||
std::string generateJson() const override;
|
||||
nlohmann::json generateJsonJson() const;
|
||||
|
||||
private:
|
||||
/// Singleton member for the Factory Manager
|
||||
|
||||
@@ -83,6 +83,7 @@ StaticRotation::StaticRotation()
|
||||
_matrixIsDirty = true;
|
||||
requireUpdate();
|
||||
});
|
||||
_type = "StaticRotation";
|
||||
}
|
||||
|
||||
StaticRotation::StaticRotation(const ghoul::Dictionary& dictionary) : StaticRotation() {
|
||||
@@ -101,6 +102,7 @@ StaticRotation::StaticRotation(const ghoul::Dictionary& dictionary) : StaticRota
|
||||
_eulerRotation = rotationMatrixToEulerAngles(std::get<glm::dmat3>(p.rotation));
|
||||
}
|
||||
_matrixIsDirty = true;
|
||||
_type = "StaticRotation";
|
||||
}
|
||||
|
||||
glm::dmat3 StaticRotation::matrix(const UpdateData&) const {
|
||||
|
||||
@@ -58,11 +58,13 @@ StaticScale::StaticScale() : _scaleValue(ScaleInfo, 1.f, 0.1f, 100.f) {
|
||||
_scaleValue.onChange([this]() {
|
||||
requireUpdate();
|
||||
});
|
||||
_type = "StaticScale";
|
||||
}
|
||||
|
||||
StaticScale::StaticScale(const ghoul::Dictionary& dictionary) : StaticScale() {
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_scaleValue = p.scale;
|
||||
_type = "StaticScale";
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -60,6 +60,7 @@ StaticTranslation::StaticTranslation()
|
||||
requireUpdate();
|
||||
notifyObservers();
|
||||
});
|
||||
_type = "StaticTranslation";
|
||||
}
|
||||
|
||||
StaticTranslation::StaticTranslation(const ghoul::Dictionary& dictionary)
|
||||
@@ -67,6 +68,7 @@ StaticTranslation::StaticTranslation(const ghoul::Dictionary& dictionary)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_position = p.position;
|
||||
_type = "StaticTranslation";
|
||||
}
|
||||
|
||||
glm::dvec3 StaticTranslation::position(const UpdateData&) const {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <openspace/openspace.h>
|
||||
#include <openspace/documentation/core_registration.h>
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/json.h>
|
||||
#include <openspace/util/json_helper.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
@@ -87,19 +88,20 @@ DocumentationEngine& DocumentationEngine::ref() {
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
std::string generateJsonDocumentation(const Documentation& d) {
|
||||
std::stringstream result;
|
||||
result << "{";
|
||||
nlohmann::json generateJsonDocumentation(const Documentation& d) {
|
||||
nlohmann::json json;
|
||||
|
||||
json["name"] = d.name;
|
||||
json["id"] = d.id;
|
||||
json["properties"] = nlohmann::json::array();
|
||||
|
||||
result << R"("name": ")" << d.name << "\",";
|
||||
result << R"("id": ")" << d.id << "\",";
|
||||
result << R"("entries": [)";
|
||||
for (const DocumentationEntry& p : d.entries) {
|
||||
result << '{';
|
||||
result << R"("key": ")" << p.key << "\",";
|
||||
result << R"("optional": )" << (p.optional ? "true" : "false") << ',';
|
||||
result << R"("type": ")" << p.verifier->type() << "\",";
|
||||
result << R"("documentation": ")" << escapedJson(p.documentation) << "\",";
|
||||
nlohmann::json entry;
|
||||
entry["key"] = p.key;
|
||||
entry["optional"] = p.optional ? true : false;
|
||||
entry["type"] = p.verifier->type();
|
||||
entry["documentation"] = p.documentation;
|
||||
|
||||
TableVerifier* tv = dynamic_cast<TableVerifier*>(p.verifier.get());
|
||||
ReferencingVerifier* rv = dynamic_cast<ReferencingVerifier*>(p.verifier.get());
|
||||
|
||||
@@ -112,53 +114,52 @@ std::string generateJsonDocumentation(const Documentation& d) {
|
||||
);
|
||||
|
||||
if (it == documentations.end()) {
|
||||
result << R"("reference": { "found": false })";
|
||||
entry["reference"]["found"] = false;
|
||||
}
|
||||
else {
|
||||
result << R"("reference": {)"
|
||||
<< R"("found": true,)"
|
||||
<< R"("name": ")" << it->name << "\","
|
||||
<< R"("identifier": ")" << rv->identifier << '\"'
|
||||
<< '}';
|
||||
nlohmann::json reference;
|
||||
reference["found"] = true;
|
||||
reference["name"] = it->name;
|
||||
reference["identifier"] = rv->identifier;
|
||||
|
||||
entry["reference"] = reference;
|
||||
}
|
||||
}
|
||||
else if (tv) {
|
||||
std::string json = generateJsonDocumentation({ "", "", tv->documentations });
|
||||
nlohmann::json json = generateJsonDocumentation(tv->documentations);
|
||||
// We have a TableVerifier, so we need to recurse
|
||||
result << R"("restrictions": )" << json;
|
||||
entry["restrictions"] = json;
|
||||
}
|
||||
else {
|
||||
result << R"("description": ")" << p.verifier->documentation() << '\"';
|
||||
entry["description"] = p.verifier->documentation();
|
||||
}
|
||||
result << '}';
|
||||
if (&p != &d.entries.back()) {
|
||||
result << ", ";
|
||||
}
|
||||
|
||||
json["properties"].push_back(entry);
|
||||
}
|
||||
|
||||
result << ']';
|
||||
result << '}';
|
||||
|
||||
return result.str();
|
||||
return json;
|
||||
}
|
||||
|
||||
std::string DocumentationEngine::generateJson() const {
|
||||
std::stringstream json;
|
||||
json << "[";
|
||||
nlohmann::json json;
|
||||
|
||||
for (const Documentation& d : _documentations) {
|
||||
json << generateJsonDocumentation(d);
|
||||
if (&d != &_documentations.back()) {
|
||||
json << ", ";
|
||||
}
|
||||
json["data"].push_back(generateJsonDocumentation(d));
|
||||
}
|
||||
|
||||
json << "]";
|
||||
|
||||
return json.str();
|
||||
return json.dump();
|
||||
}
|
||||
|
||||
nlohmann::json DocumentationEngine::generateJsonJson() const {
|
||||
nlohmann::json json;
|
||||
|
||||
for (const Documentation& d : _documentations) {
|
||||
json.push_back(generateJsonDocumentation(d));
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
void DocumentationEngine::addDocumentation(Documentation documentation) {
|
||||
if (documentation.id.empty()) {
|
||||
_documentations.push_back(std::move(documentation));
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <openspace/interaction/interactionmonitor.h>
|
||||
#include <openspace/interaction/keybindingmanager.h>
|
||||
#include <openspace/interaction/sessionrecording.h>
|
||||
#include <openspace/json.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/navigation/orbitalnavigator.h>
|
||||
#include <openspace/network/parallelpeer.h>
|
||||
@@ -1029,13 +1030,13 @@ void OpenSpaceEngine::writeDocumentation() {
|
||||
path = absPath(path).string() + '/';
|
||||
|
||||
// Start the async requests as soon as possible so they are finished when we need them
|
||||
std::future<std::string> root = std::async(
|
||||
&properties::PropertyOwner::generateJson,
|
||||
std::future<nlohmann::json> settings = std::async(
|
||||
&properties::PropertyOwner::generateJsonJson,
|
||||
global::rootPropertyOwner
|
||||
);
|
||||
|
||||
std::future<std::string> scene = std::async(
|
||||
&properties::PropertyOwner::generateJson,
|
||||
std::future<nlohmann::json> scene = std::async(
|
||||
&properties::PropertyOwner::generateJsonJson,
|
||||
_scene.get()
|
||||
);
|
||||
|
||||
@@ -1044,56 +1045,41 @@ void OpenSpaceEngine::writeDocumentation() {
|
||||
DocEng.addHandlebarTemplates(FactoryManager::ref().templatesToRegister());
|
||||
DocEng.addHandlebarTemplates(DocEng.templatesToRegister());
|
||||
|
||||
std::string json = "{\"documentation\":[";
|
||||
nlohmann::json scripting;
|
||||
scripting["Name"] = "Scripting API";
|
||||
scripting["Data"] = global::scriptEngine->generateJsonJson();
|
||||
|
||||
json += fmt::format(
|
||||
R"({{"name":"{}","identifier":"{}","data":{}}},)",
|
||||
"Scripting",
|
||||
global::scriptEngine->jsonName(),
|
||||
global::scriptEngine->generateJson()
|
||||
);
|
||||
nlohmann::json factory;
|
||||
factory["Name"] = "Asset Types";
|
||||
factory["Data"] = FactoryManager::ref().generateJsonJson();
|
||||
|
||||
json += fmt::format(
|
||||
R"({{"name":"{}","identifier":"{}","data":{}}},)",
|
||||
"Top Level", DocEng.jsonName(), DocEng.generateJson()
|
||||
);
|
||||
|
||||
json += fmt::format(
|
||||
R"({{"name":"{}","identifier":"{}","data":{}}},)",
|
||||
"Factory", FactoryManager::ref().jsonName(), FactoryManager::ref().generateJson()
|
||||
);
|
||||
|
||||
json += fmt::format(
|
||||
R"({{"name":"{}","identifier":"{}","data":{}}},)",
|
||||
"Keybindings",
|
||||
global::keybindingManager->jsonName(),
|
||||
global::keybindingManager->generateJson()
|
||||
);
|
||||
nlohmann::json keybindings;
|
||||
keybindings["Name"] = "Keybindings";
|
||||
keybindings["Keybindings"] = global::keybindingManager->generateJsonJson();
|
||||
|
||||
SceneLicenseWriter writer;
|
||||
json += fmt::format(
|
||||
R"({{"name":"{}","identifier":"{}","data":{}}},)",
|
||||
"Scene License Information", writer.jsonName(), writer.generateJson()
|
||||
);
|
||||
nlohmann::json license;
|
||||
license["Name"] = "Licenses";
|
||||
license["Data"] = writer.generateJsonJson();
|
||||
|
||||
json += fmt::format(
|
||||
R"({{"name":"{}","identifier":"{}","data":{}}},)",
|
||||
"Scene Properties", "propertylist", root.get()
|
||||
);
|
||||
nlohmann::json sceneProperties;
|
||||
sceneProperties["Name"] = "Settings";
|
||||
sceneProperties["Data"] = settings.get();
|
||||
|
||||
json += fmt::format(
|
||||
R"({{"name":"{}","identifier":"{}","data":{}}})",
|
||||
"Scene Graph Information", "propertylist", scene.get()
|
||||
);
|
||||
nlohmann::json sceneGraph;
|
||||
sceneGraph["Name"] = "Scene";
|
||||
sceneGraph["Data"] = scene.get();
|
||||
|
||||
json += "]}";
|
||||
nlohmann::json documentation = {
|
||||
sceneGraph, sceneProperties, keybindings, license, scripting, factory
|
||||
};
|
||||
|
||||
// Add templates for the JSONs we just registered
|
||||
DocEng.addHandlebarTemplates(global::keybindingManager->templatesToRegister());
|
||||
DocEng.addHandlebarTemplates(writer.templatesToRegister());
|
||||
DocEng.addHandlebarTemplates(global::rootPropertyOwner->templatesToRegister());
|
||||
nlohmann::json result;
|
||||
result["documentation"] = documentation;
|
||||
|
||||
DocEng.writeDocumentationHtml(path, json);
|
||||
std::ofstream out(absPath("${DOCUMENTATION}/documentationData.js"));
|
||||
out << "var data = " << result.dump();
|
||||
out.close();
|
||||
}
|
||||
|
||||
void OpenSpaceEngine::preSynchronization() {
|
||||
|
||||
@@ -38,6 +38,30 @@
|
||||
|
||||
namespace openspace::interaction {
|
||||
|
||||
void sortJson(nlohmann::json& json) {
|
||||
std::sort(
|
||||
json.begin(),
|
||||
json.end(),
|
||||
[](const nlohmann::json& lhs, const nlohmann::json& rhs) {
|
||||
std::string lhsString = lhs["Name"];
|
||||
std::string rhsString = rhs["Name"];
|
||||
std::transform(
|
||||
lhsString.begin(),
|
||||
lhsString.end(),
|
||||
lhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
std::transform(
|
||||
rhsString.begin(),
|
||||
rhsString.end(),
|
||||
rhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
|
||||
return rhsString > lhsString;
|
||||
});
|
||||
}
|
||||
|
||||
KeybindingManager::KeybindingManager()
|
||||
: DocumentationGenerator(
|
||||
"Keybindings",
|
||||
@@ -140,6 +164,22 @@ std::string KeybindingManager::generateJson() const {
|
||||
return json.str();
|
||||
}
|
||||
|
||||
nlohmann::json KeybindingManager::generateJsonJson() const {
|
||||
ZoneScoped;
|
||||
|
||||
nlohmann::json json;
|
||||
|
||||
for (const std::pair<const KeyWithModifier, std::string>& p : _keyLua) {
|
||||
nlohmann::json keybind;
|
||||
keybind["Name"] = ghoul::to_string(p.first);
|
||||
keybind["Action"] = p.second;
|
||||
json.push_back(std::move(keybind));
|
||||
}
|
||||
sortJson(json);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
scripting::LuaLibrary KeybindingManager::luaLibrary() {
|
||||
return {
|
||||
"",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/events/event.h>
|
||||
#include <openspace/events/eventengine.h>
|
||||
#include <openspace/json.h>
|
||||
#include <openspace/properties/property.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/util/json_helper.h>
|
||||
@@ -40,66 +41,64 @@
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "PropertyOwner";
|
||||
|
||||
void createJson(openspace::properties::PropertyOwner* owner, std::vector<char>& buf) {
|
||||
ZoneScoped;
|
||||
void sortJson(nlohmann::json& json) {
|
||||
std::sort(
|
||||
json.begin(),
|
||||
json.end(),
|
||||
[](const nlohmann::json& lhs, const nlohmann::json& rhs) {
|
||||
std::string lhsString = lhs["Name"];
|
||||
std::string rhsString = rhs["Name"];
|
||||
std::transform(
|
||||
lhsString.begin(),
|
||||
lhsString.end(),
|
||||
lhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
std::transform(
|
||||
rhsString.begin(),
|
||||
rhsString.end(),
|
||||
rhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
|
||||
return rhsString > lhsString;
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json createJson(openspace::properties::PropertyOwner* owner) {
|
||||
ZoneScoped
|
||||
|
||||
using namespace openspace;
|
||||
nlohmann::json json;
|
||||
json["Name"] = owner->identifier();
|
||||
|
||||
constexpr std::string_view replStr = R"("{}": "{}")";
|
||||
json["Description"] = owner->description();
|
||||
json["Properties"] = nlohmann::json::array();
|
||||
json["PropertyOwners"] = nlohmann::json::array();
|
||||
json["Type"] = owner->type();
|
||||
json["Tags"] = owner->tags();
|
||||
|
||||
buf.push_back('{');
|
||||
fmt::format_to(std::back_inserter(buf), replStr, "name", owner->identifier());
|
||||
buf.push_back(',');
|
||||
|
||||
constexpr std::string_view propertiesText = "\"properties\": [";
|
||||
buf.insert(buf.end(), propertiesText.begin(), propertiesText.end());
|
||||
const std::vector<properties::Property*>& properties = owner->properties();
|
||||
for (properties::Property* p : properties) {
|
||||
//json << "{";
|
||||
buf.push_back('{');
|
||||
//json << fmt::format(replStr, "id", p->identifier()) << ",";
|
||||
fmt::format_to(std::back_inserter(buf), replStr, "id", p->identifier());
|
||||
buf.push_back(',');
|
||||
//json << fmt::format(replStr, "type", p->className()) << ",";
|
||||
fmt::format_to(std::back_inserter(buf), replStr, "type", p->className());
|
||||
buf.push_back(',');
|
||||
nlohmann::json propertyJson;
|
||||
propertyJson["Name"] = p->identifier();
|
||||
propertyJson["Type"] = p->className();
|
||||
propertyJson["URI"] = p->fullyQualifiedIdentifier();
|
||||
propertyJson["Gui Name"] = p->guiName();
|
||||
propertyJson["Description"] = p->description();
|
||||
|
||||
fmt::format_to(
|
||||
std::back_inserter(buf),
|
||||
replStr, "fullyQualifiedId", p->fullyQualifiedIdentifier()
|
||||
);
|
||||
buf.push_back(',');
|
||||
|
||||
fmt::format_to(std::back_inserter(buf), replStr, "guiName", p->guiName());
|
||||
buf.push_back(',');
|
||||
|
||||
fmt::format_to(
|
||||
std::back_inserter(buf),
|
||||
replStr, "description", escapedJson(p->description())
|
||||
);
|
||||
buf.push_back('}');
|
||||
if (p != properties.back()) {
|
||||
buf.push_back(',');
|
||||
}
|
||||
json["Properties"].push_back(propertyJson);
|
||||
}
|
||||
buf.push_back(']');
|
||||
buf.push_back(',');
|
||||
sortJson(json["Properties"]);
|
||||
|
||||
constexpr std::string_view propertyOwnersText = "\"propertyOwners\": [";
|
||||
buf.insert(
|
||||
buf.end(),
|
||||
propertyOwnersText.begin(),
|
||||
propertyOwnersText.end()
|
||||
);
|
||||
auto propertyOwners = owner->propertySubOwners();
|
||||
for (properties::PropertyOwner* o : propertyOwners) {
|
||||
createJson(o, buf);
|
||||
if (o != propertyOwners.back()) {
|
||||
buf.push_back(',');
|
||||
}
|
||||
nlohmann::json propertyOwner;
|
||||
json["PropertyOwners"].push_back(createJson(o));
|
||||
}
|
||||
buf.push_back(']');
|
||||
buf.push_back('}');
|
||||
sortJson(json["PropertyOwners"]);
|
||||
|
||||
return json;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -387,6 +386,10 @@ const std::string& PropertyOwner::identifier() const {
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
const std::string& PropertyOwner::type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
void PropertyOwner::setGuiName(std::string guiName) {
|
||||
_guiName = std::move(guiName);
|
||||
}
|
||||
@@ -418,17 +421,28 @@ void PropertyOwner::removeTag(const std::string& tag) {
|
||||
std::string PropertyOwner::generateJson() const {
|
||||
ZoneScoped;
|
||||
|
||||
std::vector<char> res;
|
||||
res.reserve(5 * 51024 * 1024); // 5 MB
|
||||
res.push_back('[');
|
||||
nlohmann::json json;
|
||||
std::vector<PropertyOwner*> subOwners = propertySubOwners();
|
||||
for (PropertyOwner* owner : subOwners) {
|
||||
createJson(owner, res);
|
||||
res.push_back(',');
|
||||
json["Data"].push_back(createJson(owner));
|
||||
}
|
||||
res.back() = ']';
|
||||
|
||||
return std::string(res.begin(), res.end());
|
||||
return json.dump();
|
||||
}
|
||||
|
||||
nlohmann::json PropertyOwner::generateJsonJson() const {
|
||||
ZoneScoped
|
||||
|
||||
nlohmann::json json;
|
||||
std::vector<PropertyOwner*> subOwners = propertySubOwners();
|
||||
for (PropertyOwner* owner : subOwners) {
|
||||
if (owner->identifier() != "Scene") {
|
||||
json.push_back(createJson(owner));
|
||||
}
|
||||
}
|
||||
sortJson(json);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
} // namespace openspace::properties
|
||||
|
||||
@@ -68,6 +68,8 @@ std::unique_ptr<DashboardItem> DashboardItem::createFromDictionary(
|
||||
const std::string& dashboardType = dictionary.value<std::string>(KeyType);
|
||||
|
||||
DashboardItem* item = factory->create(dashboardType, std::move(dictionary));
|
||||
item->_type = dashboardType;
|
||||
|
||||
return std::unique_ptr<DashboardItem>(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ ghoul::mm_unique_ptr<Renderable> Renderable::createFromDictionary(
|
||||
dictionary,
|
||||
&global::memoryManager->PersistentMemory
|
||||
);
|
||||
result->_type = renderableType;
|
||||
return ghoul::mm_unique_ptr<Renderable>(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@ std::unique_ptr<ScreenSpaceRenderable> ScreenSpaceRenderable::createFromDictiona
|
||||
p.type,
|
||||
dictionary
|
||||
);
|
||||
ssr->_type = p.type;
|
||||
return std::unique_ptr<ScreenSpaceRenderable>(ssr);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ std::unique_ptr<LightSource> LightSource::createFromDictionary(
|
||||
LightSource* source = factory->create(p.type, dictionary);
|
||||
source->setIdentifier(p.identifier);
|
||||
|
||||
source->_type = p.type;
|
||||
return std::unique_ptr<LightSource>(source);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ ghoul::mm_unique_ptr<Rotation> Rotation::createFromDictionary(
|
||||
dictionary,
|
||||
&global::memoryManager->PersistentMemory
|
||||
);
|
||||
result->_type = p.type;
|
||||
return ghoul::mm_unique_ptr<Rotation>(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ ghoul::mm_unique_ptr<Scale> Scale::createFromDictionary(
|
||||
&global::memoryManager->PersistentMemory
|
||||
);
|
||||
result->setIdentifier("Scale");
|
||||
result->_type = p.type;
|
||||
|
||||
return ghoul::mm_unique_ptr<Scale>(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -491,6 +491,7 @@ ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
|
||||
LDEBUG(fmt::format("Successfully created SceneGraphNode '{}'", result->identifier()));
|
||||
|
||||
result->_lastScreenSpaceUpdateTime = std::chrono::high_resolution_clock::now();
|
||||
result->_type = "SceneGraphNode";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,107 @@ SceneLicenseWriter::SceneLicenseWriter()
|
||||
)
|
||||
{}
|
||||
|
||||
void sortJson(nlohmann::json& json) {
|
||||
std::sort(
|
||||
json.begin(),
|
||||
json.end(),
|
||||
[](const nlohmann::json& lhs, const nlohmann::json& rhs) {
|
||||
std::string lhsString = lhs["Name"];
|
||||
std::string rhsString = rhs["Name"];
|
||||
std::transform(
|
||||
lhsString.begin(),
|
||||
lhsString.end(),
|
||||
lhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
std::transform(
|
||||
rhsString.begin(),
|
||||
rhsString.end(),
|
||||
rhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
|
||||
return rhsString > lhsString;
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json SceneLicenseWriter::generateJsonJson() const {
|
||||
nlohmann::json json;
|
||||
|
||||
std::vector<const Asset*> assets =
|
||||
global::openSpaceEngine->assetManager().allAssets();
|
||||
|
||||
int metaTotal = 0;
|
||||
int metaCount = 0;
|
||||
for (const Asset* asset : assets) {
|
||||
std::optional<Asset::MetaInformation> meta = asset->metaInformation();
|
||||
if (!meta.has_value()) {
|
||||
continue;
|
||||
}
|
||||
metaTotal++;
|
||||
}
|
||||
|
||||
if (global::profile->meta.has_value()) {
|
||||
metaTotal++;
|
||||
nlohmann::json metaJson;
|
||||
metaJson["Name"] = "Profile";
|
||||
metaJson["ProfileName"] = global::profile->meta->name.value_or("");
|
||||
metaJson["Version"] = global::profile->meta->version.value_or("");
|
||||
metaJson["Description"] = global::profile->meta->description.value_or("");
|
||||
metaJson["Author"] = global::profile->meta->author.value_or("");
|
||||
metaJson["Url"] = global::profile->meta->url.value_or("");
|
||||
metaJson["License"] = global::profile->meta->license.value_or("");
|
||||
metaJson["Type"] = "license";
|
||||
json.push_back(std::move(metaJson));
|
||||
}
|
||||
|
||||
std::map<std::string, nlohmann::json> assetLicenses;
|
||||
for (const Asset* asset : assets) {
|
||||
std::optional<Asset::MetaInformation> meta = asset->metaInformation();
|
||||
|
||||
nlohmann::json assetJson;
|
||||
if (!meta.has_value()) {
|
||||
assetJson["Name"] = "";
|
||||
assetJson["Version"] = "";
|
||||
assetJson["Description"] = "";
|
||||
assetJson["Author"] = "";
|
||||
assetJson["Url"] = "";
|
||||
assetJson["License"] = "No license";
|
||||
assetJson["Identifiers"] = "";
|
||||
assetJson["Path"] = asset->path().string();
|
||||
|
||||
assetLicenses["No license"].push_back(assetJson);
|
||||
}
|
||||
else {
|
||||
std::string license = meta->license == "" ? "No license" : meta->license;
|
||||
assetJson["Name"] = meta->name;
|
||||
assetJson["Version"] = meta->version;
|
||||
assetJson["Description"] = meta->description;
|
||||
assetJson["Author"] = meta->author;
|
||||
assetJson["Url"] = meta->url;
|
||||
assetJson["License"] = license;
|
||||
assetJson["Identifiers"] = meta->identifiers;
|
||||
assetJson["Path"] = asset->path().string();
|
||||
|
||||
assetLicenses[license].push_back(assetJson);
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json assetsJson;
|
||||
assetsJson["Name"] = "Assets";
|
||||
assetsJson["Type"] = "Licenses";
|
||||
|
||||
for (const std::pair<std::string, nlohmann::json>& assetLicense : assetLicenses) {
|
||||
nlohmann::json entry;
|
||||
entry["Name"] = assetLicense.first;
|
||||
entry["Assets"] = assetLicense.second;
|
||||
sortJson(entry["Assets"]);
|
||||
assetsJson["Licenses"].push_back(entry);
|
||||
}
|
||||
json.push_back(assetsJson);
|
||||
return json;
|
||||
}
|
||||
|
||||
std::string SceneLicenseWriter::generateJson() const {
|
||||
ZoneScoped;
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ ghoul::mm_unique_ptr<TimeFrame> TimeFrame::createFromDictionary(
|
||||
|
||||
TimeFrame* result = FactoryManager::ref().factory<TimeFrame>()->create(p.type, dict);
|
||||
result->setIdentifier("TimeFrame");
|
||||
result->_type = p.type;
|
||||
|
||||
return ghoul::mm_unique_ptr<TimeFrame>(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ ghoul::mm_unique_ptr<Translation> Translation::createFromDictionary(
|
||||
dictionary,
|
||||
&global::memoryManager->PersistentMemory
|
||||
);
|
||||
result->_type = p.type;
|
||||
return ghoul::mm_unique_ptr<Translation>(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <ghoul/ext/assimp/contrib/zip/src/zip.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "scriptengine_lua.inl"
|
||||
|
||||
namespace {
|
||||
@@ -82,85 +81,50 @@ namespace {
|
||||
return result;
|
||||
}
|
||||
|
||||
void toJson(const openspace::scripting::LuaLibrary& library, std::stringstream& json)
|
||||
{
|
||||
constexpr std::string_view replStr = R"("{}": "{}", )";
|
||||
constexpr std::string_view replStr2 = R"("{}": "{}")";
|
||||
void sortJson(nlohmann::json& json) {
|
||||
std::sort(
|
||||
json.begin(),
|
||||
json.end(),
|
||||
[](const nlohmann::json& lhs, const nlohmann::json& rhs) {
|
||||
std::string lhsString = lhs["Name"];
|
||||
std::string rhsString = rhs["Name"];
|
||||
std::transform(
|
||||
lhsString.begin(),
|
||||
lhsString.end(),
|
||||
lhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
std::transform(
|
||||
rhsString.begin(),
|
||||
rhsString.end(),
|
||||
rhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
|
||||
return rhsString > lhsString;
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json toJson(const openspace::scripting::LuaLibrary::Function& f) {
|
||||
using namespace openspace;
|
||||
using namespace openspace::scripting;
|
||||
nlohmann::json function;
|
||||
function["Name"] = f.name;
|
||||
nlohmann::json arguments = nlohmann::json::array();
|
||||
|
||||
json << "{";
|
||||
json << fmt::format(replStr, "library", library.name);
|
||||
json << "\"functions\": [";
|
||||
|
||||
for (const LuaLibrary::Function& f : library.functions) {
|
||||
json << "{";
|
||||
json << fmt::format(replStr, "name", f.name);
|
||||
json << "\"arguments\": [";
|
||||
for (const LuaLibrary::Function::Argument& arg : f.arguments) {
|
||||
json << "{";
|
||||
json << fmt::format(replStr, "name", escapedJson(arg.name));
|
||||
json << fmt::format(replStr, "type", escapedJson(arg.type));
|
||||
json << fmt::format(
|
||||
replStr2, "defaultValue", escapedJson(arg.defaultValue.value_or(""))
|
||||
);
|
||||
json << "}";
|
||||
|
||||
if (&arg != &f.arguments.back()) {
|
||||
json << ",";
|
||||
}
|
||||
}
|
||||
json << "],";
|
||||
json << fmt::format(replStr, "returnType", escapedJson(f.returnType));
|
||||
json << fmt::format(replStr, "help", escapedJson(f.helpText));
|
||||
json << fmt::format(
|
||||
"\"sourceLocation\": {{ \"file\": \"{}\", \"line\": {} }}",
|
||||
escapedJson(f.sourceLocation.file), f.sourceLocation.line
|
||||
);
|
||||
json << "}";
|
||||
if (&f != &library.functions.back() || !library.documentations.empty()) {
|
||||
json << ",";
|
||||
}
|
||||
for (const LuaLibrary::Function::Argument& arg : f.arguments) {
|
||||
nlohmann::json argument;
|
||||
argument["Name"] = arg.name;
|
||||
argument["Type"] = arg.type;
|
||||
argument["Default Value"] = arg.defaultValue.value_or("");
|
||||
arguments.push_back(argument);
|
||||
}
|
||||
|
||||
function["Arguments"] = arguments;
|
||||
function["Return Type"] = f.returnType;
|
||||
function["Help"] = f.helpText;
|
||||
|
||||
for (const LuaLibrary::Function& f : library.documentations) {
|
||||
json << "{";
|
||||
json << fmt::format(replStr, "name", f.name);
|
||||
json << "\"arguments\": [";
|
||||
for (const LuaLibrary::Function::Argument& arg : f.arguments) {
|
||||
json << "{";
|
||||
json << fmt::format(replStr, "name", escapedJson(arg.name));
|
||||
json << fmt::format(replStr, "type", escapedJson(arg.type));
|
||||
json << fmt::format(
|
||||
replStr2, "defaultValue", escapedJson(arg.defaultValue.value_or(""))
|
||||
);
|
||||
json << "}";
|
||||
|
||||
if (&arg != &f.arguments.back()) {
|
||||
json << ",";
|
||||
}
|
||||
}
|
||||
json << "],";
|
||||
json << fmt::format(replStr, "returnType", escapedJson(f.returnType));
|
||||
json << fmt::format(replStr2, "help", escapedJson(f.helpText));
|
||||
json << "}";
|
||||
if (&f != &library.documentations.back()) {
|
||||
json << ",";
|
||||
}
|
||||
}
|
||||
|
||||
json << "],";
|
||||
|
||||
json << "\"subLibraries\": [";
|
||||
for (const LuaLibrary& sl : library.subLibraries) {
|
||||
toJson(sl, json);
|
||||
if (&sl != &library.subLibraries.back()) {
|
||||
json << ",";
|
||||
}
|
||||
}
|
||||
json << "]}";
|
||||
return function;
|
||||
}
|
||||
|
||||
#include "scriptengine_codegen.cpp"
|
||||
@@ -510,24 +474,35 @@ std::vector<std::string> ScriptEngine::allLuaFunctions() const {
|
||||
}
|
||||
|
||||
std::string ScriptEngine::generateJson() const {
|
||||
ZoneScoped;
|
||||
return "";
|
||||
}
|
||||
|
||||
// Create JSON
|
||||
std::stringstream json;
|
||||
json << "[";
|
||||
nlohmann::json ScriptEngine::generateJsonJson() const {
|
||||
ZoneScoped
|
||||
|
||||
nlohmann::json json;
|
||||
|
||||
bool first = true;
|
||||
for (const LuaLibrary& l : _registeredLibraries) {
|
||||
if (!first) {
|
||||
json << ",";
|
||||
using namespace openspace;
|
||||
using namespace openspace::scripting;
|
||||
|
||||
nlohmann::json library;
|
||||
std::string libraryName = l.name.empty() ? "openspace" : "openspace." + l.name;
|
||||
library["Name"] = libraryName;
|
||||
|
||||
for (const LuaLibrary::Function& f : l.functions) {
|
||||
library["Functions"].push_back(toJson(f));
|
||||
}
|
||||
first = false;
|
||||
|
||||
toJson(l, json);
|
||||
for (const LuaLibrary::Function& f : l.documentations) {
|
||||
library["Functions"].push_back(toJson(f));
|
||||
}
|
||||
sortJson(library["Functions"]);
|
||||
json.push_back(library);
|
||||
|
||||
sortJson(json);
|
||||
}
|
||||
json << "]";
|
||||
|
||||
return json.str();
|
||||
return json;
|
||||
}
|
||||
|
||||
void ScriptEngine::writeLog(const std::string& script) {
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
|
||||
#include <openspace/util/factorymanager.h>
|
||||
|
||||
#include <openspace/documentation/documentationengine.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/json.h>
|
||||
#include <openspace/rendering/dashboarditem.h>
|
||||
#include <openspace/rendering/renderable.h>
|
||||
#include <openspace/scene/lightsource.h>
|
||||
@@ -35,6 +38,87 @@
|
||||
#include <openspace/util/task.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
using namespace openspace;
|
||||
using namespace openspace::documentation;
|
||||
|
||||
void sortJson(nlohmann::json& json) {
|
||||
std::sort(
|
||||
json.begin(),
|
||||
json.end(),
|
||||
[](const nlohmann::json& lhs, const nlohmann::json& rhs) {
|
||||
std::string lhsString = lhs["Name"];
|
||||
std::string rhsString = rhs["Name"];
|
||||
std::transform(
|
||||
lhsString.begin(),
|
||||
lhsString.end(),
|
||||
lhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
std::transform(
|
||||
rhsString.begin(),
|
||||
rhsString.end(),
|
||||
rhsString.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); }
|
||||
);
|
||||
|
||||
return rhsString > lhsString;
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json generateJsonDocumentation(const Documentation& d) {
|
||||
nlohmann::json json;
|
||||
|
||||
json["Name"] = d.name;
|
||||
json["Identifier"] = d.id;
|
||||
json["Members"] = nlohmann::json::array();
|
||||
|
||||
for (const DocumentationEntry& p : d.entries) {
|
||||
nlohmann::json entry;
|
||||
entry["Name"] = p.key;
|
||||
entry["Optional"] = p.optional.value;
|
||||
entry["Type"] = p.verifier->type();
|
||||
entry["Documentation"] = p.documentation;
|
||||
|
||||
TableVerifier* tv = dynamic_cast<TableVerifier*>(p.verifier.get());
|
||||
ReferencingVerifier* rv = dynamic_cast<ReferencingVerifier*>(p.verifier.get());
|
||||
|
||||
if (rv) {
|
||||
const std::vector<Documentation>& documentations = DocEng.documentations();
|
||||
auto it = std::find_if(
|
||||
documentations.begin(),
|
||||
documentations.end(),
|
||||
[rv](const Documentation& doc) { return doc.id == rv->identifier; }
|
||||
);
|
||||
|
||||
if (it == documentations.end()) {
|
||||
entry["Reference"]["Found"] = false;
|
||||
}
|
||||
else {
|
||||
nlohmann::json reference;
|
||||
reference["Found"] = true;
|
||||
reference["Name"] = it->name;
|
||||
reference["Identifier"] = rv->identifier;
|
||||
|
||||
entry["Reference"] = reference;
|
||||
}
|
||||
}
|
||||
else if (tv) {
|
||||
nlohmann::json json = generateJsonDocumentation(tv->documentations);
|
||||
// We have a TableVerifier, so we need to recurse
|
||||
entry["Restrictions"] = json;
|
||||
}
|
||||
else {
|
||||
entry["Description"] = p.verifier->documentation();
|
||||
}
|
||||
json["Members"].push_back(entry);
|
||||
}
|
||||
sortJson(json["Members"]);
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
|
||||
FactoryManager* FactoryManager::_manager = nullptr;
|
||||
@@ -89,33 +173,90 @@ FactoryManager& FactoryManager::ref() {
|
||||
}
|
||||
|
||||
std::string FactoryManager::generateJson() const {
|
||||
std::stringstream json;
|
||||
nlohmann::json json;
|
||||
|
||||
json << "[";
|
||||
for (const FactoryInfo& factoryInfo : _factories) {
|
||||
json << "{";
|
||||
json << "\"name\": \"" << factoryInfo.name << "\",";
|
||||
json << "\"classes\": [";
|
||||
nlohmann::json factory;
|
||||
factory["Name"] = factoryInfo.name;
|
||||
|
||||
ghoul::TemplateFactoryBase* f = factoryInfo.factory.get();
|
||||
const std::vector<std::string>& registeredClasses = f->registeredClasses();
|
||||
for (const std::string& c : registeredClasses) {
|
||||
json << "\"" << c << "\"";
|
||||
if (&c != ®isteredClasses.back()) {
|
||||
json << ",";
|
||||
}
|
||||
}
|
||||
|
||||
json << "]}";
|
||||
if (&factoryInfo != &_factories.back()) {
|
||||
json << ",";
|
||||
json["Classes"].push_back(c);
|
||||
}
|
||||
json["Data"].push_back(factory);
|
||||
}
|
||||
|
||||
json << "]";
|
||||
return json.dump();
|
||||
}
|
||||
|
||||
nlohmann::json FactoryManager::generateJsonJson() const {
|
||||
nlohmann::json json;
|
||||
std::vector<Documentation> docs = DocEng.documentations();
|
||||
|
||||
for (const FactoryInfo& factoryInfo : _factories) {
|
||||
nlohmann::json factory;
|
||||
factory["Name"] = factoryInfo.name;
|
||||
factory["Identifier"] = "category" + factoryInfo.name;
|
||||
|
||||
ghoul::TemplateFactoryBase* f = factoryInfo.factory.get();
|
||||
// Add documentation about base class
|
||||
auto factoryDoc = std::find_if(
|
||||
docs.begin(),
|
||||
docs.end(),
|
||||
[&factoryInfo](const Documentation& d) {
|
||||
return d.name == factoryInfo.name;
|
||||
});
|
||||
if (factoryDoc != docs.end()) {
|
||||
nlohmann::json documentation = generateJsonDocumentation(*factoryDoc);
|
||||
factory["Classes"].push_back(documentation);
|
||||
// Remove documentation from list check at the end if all docs got put in
|
||||
docs.erase(factoryDoc);
|
||||
}
|
||||
else {
|
||||
nlohmann::json documentation;
|
||||
documentation["Name"] = factoryInfo.name;
|
||||
documentation["Identifier"] = factoryInfo.name;
|
||||
factory["Classes"].push_back(documentation);
|
||||
}
|
||||
|
||||
// Add documentation about derived classes
|
||||
const std::vector<std::string>& registeredClasses = f->registeredClasses();
|
||||
for (const std::string& c : registeredClasses) {
|
||||
auto found = std::find_if(
|
||||
docs.begin(),
|
||||
docs.end(),
|
||||
[&c](const Documentation& d) {
|
||||
return d.name == c;
|
||||
});
|
||||
if (found != docs.end()) {
|
||||
nlohmann::json documentation = generateJsonDocumentation(*found);
|
||||
factory["Classes"].push_back(documentation);
|
||||
docs.erase(found);
|
||||
}
|
||||
else {
|
||||
nlohmann::json documentation;
|
||||
documentation["Name"] = c;
|
||||
documentation["Identifier"] = c;
|
||||
factory["Classes"].push_back(documentation);
|
||||
}
|
||||
}
|
||||
sortJson(factory["Classes"]);
|
||||
json.push_back(factory);
|
||||
}
|
||||
// Add all leftover docs
|
||||
nlohmann::json leftovers;
|
||||
leftovers["Name"] = "Other";
|
||||
leftovers["Identifier"] = "other";
|
||||
|
||||
for (const Documentation& doc : docs) {
|
||||
leftovers["Classes"].push_back(generateJsonDocumentation(doc));
|
||||
}
|
||||
sortJson(leftovers["Classes"]);
|
||||
json.push_back(leftovers);
|
||||
sortJson(json);
|
||||
// I did not check the output of this for correctness ---abock
|
||||
return json.str();
|
||||
return json;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
Reference in New Issue
Block a user