From 612b9bbc7f8413f2d7169a15c9f5dd01f2bd0713 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 29 Mar 2021 21:50:26 +0200 Subject: [PATCH] Moving more documentation to use codegen (#1549) - Make use of more codegen in more classes - Fix verifier for Color4Verifier --- .../missions/newhorizons/fov.asset | 4 +- ext/ghoul | 2 +- include/openspace/scripting/scriptscheduler.h | 2 + .../base/rendering/renderabletrailorbit.cpp | 95 ++- .../rendering/renderabletrailtrajectory.cpp | 82 +-- modules/base/rotation/timelinerotation.cpp | 48 +- .../base/translation/timelinetranslation.cpp | 45 +- .../rendering/renderabledebugplane.cpp | 125 ++-- .../rendering/renderabledebugplane.h | 2 +- .../gaia/rendering/renderablegaiastars.cpp | 550 +++++---------- modules/gaia/tasks/constructoctreetask.cpp | 663 +++++++----------- modules/gaia/tasks/readfitstask.cpp | 143 ++-- modules/gaia/tasks/readspecktask.cpp | 51 +- modules/globebrowsing/globebrowsingmodule.cpp | 4 +- .../src/dashboarditemglobelocation.cpp | 61 +- .../src/globelabelscomponent.cpp | 312 +++------ .../globebrowsing/src/globelabelscomponent.h | 2 +- .../globebrowsing/src/globetranslation.cpp | 97 +-- modules/globebrowsing/src/layeradjustment.cpp | 92 ++- modules/globebrowsing/src/renderableglobe.cpp | 182 ++--- modules/globebrowsing/src/ringscomponent.cpp | 209 ++---- modules/globebrowsing/src/shadowcomponent.cpp | 76 +- modules/globebrowsing/src/shadowcomponent.h | 2 - .../rendering/renderablecrawlingline.cpp | 106 +-- .../rendering/renderablefov.cpp | 248 ++----- .../rendering/renderableplaneprojection.cpp | 1 - .../rendering/renderableplanetprojection.cpp | 183 ++--- modules/sync/syncs/httpsynchronization.cpp | 44 +- modules/sync/syncs/urlsynchronization.cpp | 126 ++-- .../rendering/renderabledistancelabel.cpp | 72 +- modules/volume/rawvolumemetadata.cpp | 141 ++-- .../volume/tasks/generaterawvolumetask.cpp | 127 ++-- src/CMakeLists.txt | 1 - src/interaction/navigationhandler.cpp | 135 ++-- src/mission/mission.cpp | 111 ++- src/rendering/screenspacerenderable.cpp | 232 +++--- src/scene/lightsource.cpp | 68 +- src/scene/rotation.cpp | 38 +- src/scene/scenegraphnode.cpp | 314 +++++---- src/scene/scenegraphnode_doc.inl | 145 ---- src/scene/translation.cpp | 38 +- src/scripting/scriptscheduler.cpp | 86 +-- src/scripting/scriptscheduler_lua.inl | 23 +- src/util/resourcesynchronization.cpp | 68 +- 44 files changed, 1877 insertions(+), 3279 deletions(-) delete mode 100644 src/scene/scenegraphnode_doc.inl diff --git a/data/assets/scene/solarsystem/missions/newhorizons/fov.asset b/data/assets/scene/solarsystem/missions/newhorizons/fov.asset index 1b46601397..093ee19356 100644 --- a/data/assets/scene/solarsystem/missions/newhorizons/fov.asset +++ b/data/assets/scene/solarsystem/missions/newhorizons/fov.asset @@ -348,8 +348,8 @@ local Rex = { Target = "EARTH", Instrument = "NH_REX", Color = { - Start = { 1.0, 0.7, 0.0, 1.0}, - End = {0.0, 0.0, 0.0, 0.0 } + Start = { 1.0, 0.7, 0.0, 1.0 }, + End = { 0.0, 0.0, 0.0, 0.0 } } }, Transform = { diff --git a/ext/ghoul b/ext/ghoul index 9cc4945a56..1991f5ef30 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 9cc4945a561dbea243ede04f23b14340fa7d9c8c +Subproject commit 1991f5ef30d2c878985cab279e4278afc3ee0af5 diff --git a/include/openspace/scripting/scriptscheduler.h b/include/openspace/scripting/scriptscheduler.h index 0375a38f11..a7ec3405d8 100644 --- a/include/openspace/scripting/scriptscheduler.h +++ b/include/openspace/scripting/scriptscheduler.h @@ -47,6 +47,8 @@ struct LuaLibrary; class ScriptScheduler { public: struct ScheduledScript { + ScheduledScript() = default; + ScheduledScript(const ghoul::Dictionary& dict); double time = -std::numeric_limits::max(); std::string forwardScript; diff --git a/modules/base/rendering/renderabletrailorbit.cpp b/modules/base/rendering/renderabletrailorbit.cpp index 7430b52092..681c4e14d0 100644 --- a/modules/base/rendering/renderabletrailorbit.cpp +++ b/modules/base/rendering/renderabletrailorbit.cpp @@ -30,6 +30,7 @@ #include #include #include +#include // This class is using a VBO ring buffer + a constantly updated point as follows: // Structure of the array with a _resolution of 16. FF denotes the floating position that @@ -104,36 +105,32 @@ namespace { "Opaque, Transparent, or Overlay rendering step. Default is Transparent." }; + struct [[codegen::Dictionary(RenderableTrailOrbit)]] Parameters { + // [[codegen::verbatim(PeriodInfo.description)]] + double period; + + // [[codegen::verbatim(ResolutionInfo.description)]] + int resolution; + + enum class RenderableType { + Background, + Opaque, + PreDeferredTransparent, + PostDeferredTransparent, + Overlay + }; + + // [[codegen::verbatim(RenderableTypeInfo.description)]] + std::optional renderableType; + }; +#include "renderabletrailorbit_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableTrailOrbit::Documentation() { - using namespace documentation; - documentation::Documentation doc { - "RenderableTrailOrbit", - "base_renderable_renderabletrailorbit", - { - { - PeriodInfo.identifier, - new DoubleVerifier, - Optional::No, - PeriodInfo.description - }, - { - ResolutionInfo.identifier, - new IntVerifier, - Optional::No, - ResolutionInfo.description - }, - { - RenderableTypeInfo.identifier, - new StringVerifier, - Optional::Yes, - RenderableTypeInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "base_renderable_renderabletrailorbit"; // Insert the parents documentation entries until we have a verifier that can deal // with class hierarchy @@ -152,46 +149,42 @@ RenderableTrailOrbit::RenderableTrailOrbit(const ghoul::Dictionary& dictionary) , _period(PeriodInfo, 0.0, 0.0, 1e9) , _resolution(ResolutionInfo, 10000, 1, 1000000) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableTrailOrbit" - ); + const Parameters p = codegen::bake(dictionary); _translation->onParameterChange([this]() { _needsFullSweep = true; }); // Period is in days using namespace std::chrono; - const long long sph = duration_cast(hours(24)).count(); - _period = dictionary.value(PeriodInfo.identifier) * sph; + _period = p.period * duration_cast(hours(24)).count(); _period.onChange([&] { _needsFullSweep = true; _indexBufferDirty = true; }); addProperty(_period); - _resolution = static_cast(dictionary.value(ResolutionInfo.identifier)); + _resolution = p.resolution; _resolution.onChange([&] { _needsFullSweep = true; _indexBufferDirty = true; }); addProperty(_resolution); // We store the vertices with (excluding the wrapping) decending temporal order _primaryRenderInformation.sorting = RenderInformation::VertexSorting::NewestFirst; - if (dictionary.hasKey(RenderableTypeInfo.identifier)) { - std::string renderType = dictionary.value( - RenderableTypeInfo.identifier - ); - if (renderType == "Background") { - setRenderBin(Renderable::RenderBin::Background); - } - else if (renderType == "Opaque") { - setRenderBin(Renderable::RenderBin::Opaque); - } - else if (renderType == "PreDeferredTransparent") { - setRenderBin(Renderable::RenderBin::PreDeferredTransparent); - } - else if (renderType == "PostDeferredTransparent") { - setRenderBin(Renderable::RenderBin::PostDeferredTransparent); - } - else if (renderType == "Overlay") { - setRenderBin(Renderable::RenderBin::Overlay); + if (p.renderableType.has_value()) { + switch (*p.renderableType) { + case Parameters::RenderableType::Background: + setRenderBin(Renderable::RenderBin::Background); + break; + case Parameters::RenderableType::Opaque: + setRenderBin(Renderable::RenderBin::Opaque); + break; + case Parameters::RenderableType::PreDeferredTransparent: + setRenderBin(Renderable::RenderBin::PreDeferredTransparent); + break; + case Parameters::RenderableType::PostDeferredTransparent: + setRenderBin(Renderable::RenderBin::PostDeferredTransparent); + break; + case Parameters::RenderableType::Overlay: + setRenderBin(Renderable::RenderBin::Overlay); + break; + default: + throw ghoul::MissingCaseException(); } } else { diff --git a/modules/base/rendering/renderabletrailtrajectory.cpp b/modules/base/rendering/renderabletrailtrajectory.cpp index 1a5284650a..ecd770c3ea 100644 --- a/modules/base/rendering/renderabletrailtrajectory.cpp +++ b/modules/base/rendering/renderabletrailtrajectory.cpp @@ -29,6 +29,7 @@ #include #include #include +#include // This class creates the entire trajectory at once and keeps it in memory the entire // time. This means that there is no need for updating the trail at runtime, but also that @@ -81,49 +82,31 @@ namespace { "If this value is set to 'true', the entire trail will be rendered; if it is " "'false', only the trail until the current time in the application will be shown." }; + + struct [[codegen::Dictionary(RenderableTrailTrajectory)]] Parameters { + // [[codegen::verbatim(StartTimeInfo.description)]] + std::string startTime [[codegen::annotation("A valid date in ISO 8601 format")]]; + + // [[codegen::verbatim(EndTimeInfo.description)]] + std::string endTime [[codegen::annotation("A valid date in ISO 8601 format")]]; + + // [[codegen::verbatim(SampleIntervalInfo.description)]] + double sampleInterval; + + // [[codegen::verbatim(TimeSubSampleInfo.description)]] + std::optional timeStampSubsampleFactor; + + // [[codegen::verbatim(RenderFullPathInfo.description)]] + std::optional showFullTrail; + }; +#include "renderabletrailtrajectory_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableTrailTrajectory::Documentation() { - using namespace documentation; - - documentation::Documentation doc { - "RenderableTrailTrajectory", - "base_renderable_renderabletrailtrajectory", - { - { - StartTimeInfo.identifier, - new StringAnnotationVerifier("A valid date in ISO 8601 format"), - Optional::No, - StartTimeInfo.description - }, - { - EndTimeInfo.identifier, - new StringAnnotationVerifier("A valid date in ISO 8601 format"), - Optional::No, - EndTimeInfo.description - }, - { - SampleIntervalInfo.identifier, - new DoubleVerifier, - Optional::No, - SampleIntervalInfo.description - }, - { - TimeSubSampleInfo.identifier, - new IntVerifier, - Optional::Yes, - TimeSubSampleInfo.description - }, - { - RenderFullPathInfo.identifier, - new BoolVerifier, - Optional::Yes, - RenderFullPathInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "base_renderable_renderabletrailtrajectory"; // @TODO cleanup // Insert the parents documentation entries until we have a verifier that can deal @@ -146,37 +129,28 @@ RenderableTrailTrajectory::RenderableTrailTrajectory(const ghoul::Dictionary& di , _timeStampSubsamplingFactor(TimeSubSampleInfo, 1, 1, 1000000000) , _renderFullTrail(RenderFullPathInfo, false) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableTrailTrajectory" - ); + const Parameters p = codegen::bake(dictionary); _translation->onParameterChange([this]() { _needsFullSweep = true; }); - _startTime = dictionary.value(StartTimeInfo.identifier); + _startTime = p.startTime; _startTime.onChange([this] { _needsFullSweep = true; }); addProperty(_startTime); - _endTime = dictionary.value(EndTimeInfo.identifier); + _endTime = p.endTime; _endTime.onChange([this] { _needsFullSweep = true; }); addProperty(_endTime); - _sampleInterval = dictionary.value(SampleIntervalInfo.identifier); + _sampleInterval = p.sampleInterval; _sampleInterval.onChange([this] { _needsFullSweep = true; }); addProperty(_sampleInterval); - if (dictionary.hasValue(TimeSubSampleInfo.identifier)) { - _timeStampSubsamplingFactor = static_cast( - dictionary.value(TimeSubSampleInfo.identifier) - ); - } + _timeStampSubsamplingFactor = + p.timeStampSubsampleFactor.value_or(_timeStampSubsamplingFactor); _timeStampSubsamplingFactor.onChange([this] { _subsamplingIsDirty = true; }); addProperty(_timeStampSubsamplingFactor); - if (dictionary.hasValue(RenderFullPathInfo.identifier)) { - _renderFullTrail = dictionary.value(RenderFullPathInfo.identifier); - } + _renderFullTrail = p.showFullTrail.value_or(_renderFullTrail); addProperty(_renderFullTrail); // We store the vertices with ascending temporal order diff --git a/modules/base/rotation/timelinerotation.cpp b/modules/base/rotation/timelinerotation.cpp index 39ef1dec06..8a60f99c8b 100644 --- a/modules/base/rotation/timelinerotation.cpp +++ b/modules/base/rotation/timelinerotation.cpp @@ -30,48 +30,32 @@ #include namespace { - constexpr const char* KeyKeyframes = "Keyframes"; + struct [[codegen::Dictionary(TimelineRotation)]] Parameters { + // A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS and values + // that are valid Rotation objects + std::map keyframes + [[codegen::reference("core_transform_rotation")]]; + }; +#include "timelinerotation_codegen.cpp" } // namespace namespace openspace { documentation::Documentation TimelineRotation::Documentation() { - using namespace documentation; - return { - "Timeline Rotation", - "base_transform_rotation_keyframe", - { - { - KeyKeyframes, - new TableVerifier({ - { "*", new TableVerifier(), Optional::No, "Any translation object" } - }), - Optional::No, - "A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS" - "and values that are valid Rotation objects." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "base_transform_rotation_keyframe"; + return doc; } TimelineRotation::TimelineRotation(const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "TimelineTranslation" - ); + const Parameters p = codegen::bake(dictionary); - const ghoul::Dictionary& keyframes = - dictionary.value(KeyKeyframes); - - for (std::string_view timeString : keyframes.keys()) { - const double t = Time::convertTime(std::string(timeString)); - - ghoul::mm_unique_ptr rotation = - Rotation::createFromDictionary( - keyframes.value(timeString) - ); + for (const std::pair& kf : p.keyframes) { + const double t = Time::convertTime(kf.first); + ghoul::mm_unique_ptr rotation = Rotation::createFromDictionary( + kf.second + ); if (rotation) { _timeline.addKeyframe(t, std::move(rotation)); } diff --git a/modules/base/translation/timelinetranslation.cpp b/modules/base/translation/timelinetranslation.cpp index fb6dafb14e..6067f91c5b 100644 --- a/modules/base/translation/timelinetranslation.cpp +++ b/modules/base/translation/timelinetranslation.cpp @@ -30,48 +30,31 @@ #include namespace { - constexpr const char* KeyKeyframes = "Keyframes"; + struct [[codegen::Dictionary(TimelineTranslation)]] Parameters { + // A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS and values + // that are valid Translation objects + std::map keyframes + [[codegen::reference("core_transform_translation")]]; + }; +#include "timelinetranslation_codegen.cpp" } // namespace namespace openspace { documentation::Documentation TimelineTranslation::Documentation() { - using namespace documentation; - return { - "Timeline Translation", - "base_transform_translation_keyframe", - { - { - KeyKeyframes, - new TableVerifier({ - { "*", new TableVerifier(), Optional::No, "Any translation object" } - }), - Optional::No, - "A table of keyframes, with keys formatted as YYYY-MM-DDTHH:MM:SS" - "and values that are valid Translation objects." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "base_transform_translation_keyframe"; + return doc; } TimelineTranslation::TimelineTranslation(const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "TimelineTranslation" - ); + const Parameters p = codegen::bake(dictionary); - const ghoul::Dictionary& keyframes = - dictionary.value(KeyKeyframes); - - for (std::string_view timeString : keyframes.keys()) { - const double t = Time::convertTime(std::string(timeString)); + for (const std::pair& kf : p.keyframes) { + const double t = Time::convertTime(kf.first); ghoul::mm_unique_ptr translation = - Translation::createFromDictionary( - keyframes.value(timeString) - ); - + Translation::createFromDictionary(kf.second); if (translation) { _timeline.addKeyframe(t, std::move(translation)); } diff --git a/modules/debugging/rendering/renderabledebugplane.cpp b/modules/debugging/rendering/renderabledebugplane.cpp index 3b2d0767a5..723458ed8a 100644 --- a/modules/debugging/rendering/renderabledebugplane.cpp +++ b/modules/debugging/rendering/renderabledebugplane.cpp @@ -37,6 +37,7 @@ #include #include #include +#include namespace { enum Origin { @@ -72,47 +73,38 @@ namespace { "Texture Coordinate Origin", "The origin of the texture coorinate system." }; + + struct [[codegen::Dictionary(RenderableDebugPlane)]] Parameters { + // [[codegen::verbatim(TextureInfo.description)]] + std::optional texture; + + // [[codegen::verbatim(BillboardInfo.description)]] + std::optional billboard; + + // [[codegen::verbatim(SizeInfo.description)]] + std::optional size; + + enum class Origin { + LowerLeft, + LowerRight, + UpperLeft, + UpperRight, + Center + }; + // [[codegen::verbatim(OriginInfo.description)]] + std::optional origin; + }; +#include "renderabledebugplane_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableDebugPlane::Documentation() { - using namespace documentation; - return { - "RenderableDebugPlane", - "debugging_renderable_debugplane", - { - { - TextureInfo.identifier, - new IntVerifier, - Optional::Yes, - TextureInfo.description - }, - { - BillboardInfo.identifier, - new BoolVerifier, - Optional::Yes, - BillboardInfo.description - }, - { - SizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - SizeInfo.description - }, - { - OriginInfo.identifier, - new StringInListVerifier( - { "LowerLeft", "LowerRight", "UpperLeft", "UpperRight", "Center" } - ), - Optional::Yes, - OriginInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "debugging_renderable_debugplane"; + return doc; } - RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _texture(TextureInfo, -1, -1, 512) @@ -120,17 +112,18 @@ RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary) , _size(SizeInfo, 10.f, 0.f, 1e25f) , _origin(OriginInfo, properties::OptionProperty::DisplayType::Dropdown) { - if (dictionary.hasKey(TextureInfo.identifier)) { - _texture = static_cast(dictionary.value(TextureInfo.identifier)); - } + const Parameters p = codegen::bake(dictionary); - if (dictionary.hasKey(SizeInfo.identifier)) { - _size = static_cast(dictionary.value(SizeInfo.identifier)); - } - - if (dictionary.hasKey(BillboardInfo.identifier)) { - _billboard = dictionary.value(BillboardInfo.identifier); - } + _texture = p.texture.value_or(_texture); + addProperty(_texture); + + _size.onChange([this](){ _planeIsDirty = true; }); + _size = p.size.value_or(_size); + setBoundingSphere(_size); + addProperty(_size); + + _billboard = p.billboard.value_or(_billboard); + addProperty(_billboard); _origin.addOptions({ { LowerLeft, "LowerLeft" }, @@ -141,36 +134,30 @@ RenderableDebugPlane::RenderableDebugPlane(const ghoul::Dictionary& dictionary) }); _origin.setValue(Center); - if (dictionary.hasKey(OriginInfo.identifier)) { - const std::string origin = dictionary.value(OriginInfo.identifier); - if (origin == "LowerLeft") { - _origin = LowerLeft; - } - else if (origin == "LowerRight") { - _origin = LowerRight; - } - else if (origin == "UpperLeft") { - _origin = UpperLeft; - } - else if (origin == "UpperRight") { - _origin = UpperRight; - } - else if (origin == "Center") { - _origin = Center; + if (p.origin.has_value()) { + switch (*p.origin) { + case Parameters::Origin::LowerLeft: + _origin = LowerLeft; + break; + case Parameters::Origin::LowerRight: + _origin = LowerRight; + break; + case Parameters::Origin::UpperLeft: + _origin = UpperLeft; + break; + case Parameters::Origin::UpperRight: + _origin = UpperRight; + break; + case Parameters::Origin::Center: + _origin = Center; + break; + default: + throw ghoul::MissingCaseException(); } } else { _origin = Center; } - - addProperty(_texture); - - addProperty(_billboard); - - addProperty(_size); - _size.onChange([this](){ _planeIsDirty = true; }); - - setBoundingSphere(_size); } bool RenderableDebugPlane::isReady() const { diff --git a/modules/debugging/rendering/renderabledebugplane.h b/modules/debugging/rendering/renderabledebugplane.h index 27294e2a02..321604073e 100644 --- a/modules/debugging/rendering/renderabledebugplane.h +++ b/modules/debugging/rendering/renderabledebugplane.h @@ -68,7 +68,7 @@ private: properties::FloatProperty _size; properties::OptionProperty _origin; - bool _planeIsDirty; + bool _planeIsDirty = true; std::unique_ptr _shader; diff --git a/modules/gaia/rendering/renderablegaiastars.cpp b/modules/gaia/rendering/renderablegaiastars.cpp index 722f90f8b2..e89dc71d06 100644 --- a/modules/gaia/rendering/renderablegaiastars.cpp +++ b/modules/gaia/rendering/renderablegaiastars.cpp @@ -300,199 +300,123 @@ namespace { "Report GL Errors", "If set to true, any OpenGL errors will be reported if encountered" }; + + struct [[codegen::Dictionary(RenderableGaiaStars)]] Parameters { + // [[codegen::verbatim(FilePathInfo.description)]] + std::string file; + + enum class FileReader { + Fits, + Speck, + BinaryRaw, + BinaryOctree, + StreamOctree + }; + // [[codegen::verbatim(FileReaderOptionInfo.description)]] + FileReader fileReaderOption; + + enum class RenderOption { + Static, + Color, + Motion + }; + // [[codegen::verbatim(RenderOptionInfo.description)]] + std::optional renderOption; + + enum class ShaderOption { + PointSSBO [[codegen::key("Point_SSBO")]], + PointVBO [[codegen::key("Point_VBO")]], + BillboardSSBO [[codegen::key("Billboard_SSBO")]], + BillboardVBO [[codegen::key("Billboard_VBO")]], + BillboardSSBONoFBO [[codegen::key("Billboard_SSBO_noFBO")]] + }; + // [codegen::verbatim(ShaderOptionInfo.description)]] + std::optional shaderOption; + + // [codegen::verbatim(PsfTextureInfo.description)]] + std::string texture; + + // [codegen::verbatim(ColorTextureInfo.description)]] + std::string colorMap; + + // [codegen::verbatim(LuminosityMultiplierInfo.description)]] + std::optional luminosityMultiplier; + + // [codegen::verbatim(MagnitudeBoostInfo.description)]] + std::optional magnitudeBoost; + + // [codegen::verbatim(CutOffThresholdInfo.description)]] + std::optional cutOffThreshold; + + // [codegen::verbatim(SharpnessInfo.description)]] + std::optional sharpness; + + // [codegen::verbatim(BillboardSizeInfo.description)]] + std::optional billboardSize; + + // [codegen::verbatim(CloseUpBoostDistInfo.description)]] + std::optional closeUpBoostDist; + + // [codegen::verbatim(TmPointFilterSizeInfo.description)]] + std::optional filterSize; + + // [codegen::verbatim(TmPointSigmaInfo.description)]] + std::optional sigma; + + // [codegen::verbatim(AdditionalNodesInfo.description)]] + std::optional additionalNodes; + + // [codegen::verbatim(TmPointPxThresholdInfo.description)]] + std::optional pixelWeightThreshold; + + // [codegen::verbatim(FirstRowInfo.description)]] + std::optional firstRow; + + // [codegen::verbatim(LastRowInfo.description)]] + std::optional lastRow; + + // [codegen::verbatim(ColumnNamesInfo.description)]] + std::optional> columnNames; + + // [codegen::verbatim(LodPixelThresholdInfo.description)]] + std::optional lodPixelThreshold; + + // [codegen::verbatim(MaxGpuMemoryPercentInfo.description)]] + std::optional maxGpuMemoryPercent; + + // [codegen::verbatim(MaxCpuMemoryPercentInfo.description)]] + std::optional maxCpuMemoryPercent; + + // [codegen::verbatim(FilterPosXInfo.description)]] + std::optional filterPosX; + + // [codegen::verbatim(FilterPosYInfo.description)]] + std::optional filterPosY; + + // [codegen::verbatim(FilterPosZInfo.description)]] + std::optional filterPosZ; + + // [codegen::verbatim(FilterGMagInfo.description)]] + std::optional filterGMag; + + // [codegen::verbatim(FilterBpRpInfo.description)]] + std::optional filterBpRp; + + // [codegen::verbatim(FilterDistInfo.description)]] + std::optional filterDist; + + // [codegen::verbatim(ReportGlErrorsInfo.description)]] + std::optional reportGlErrors; + }; +#include "renderablegaiastars_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableGaiaStars::Documentation() { - using namespace documentation; - return { - "RenderableGaiaStars", - "gaiamission_renderablegaiastars", - { - { - FilePathInfo.identifier, - new StringVerifier, - Optional::No, - FilePathInfo.description - }, - { - FileReaderOptionInfo.identifier, - new StringInListVerifier({ - "Fits", "Speck", "BinaryRaw", "BinaryOctree", "StreamOctree" - }), - Optional::No, - FileReaderOptionInfo.description - }, - { - RenderOptionInfo.identifier, - new StringInListVerifier({ - "Static", "Color", "Motion" - }), - Optional::Yes, - RenderOptionInfo.description - }, - { - ShaderOptionInfo.identifier, - new StringInListVerifier({ - "Point_SSBO", "Point_VBO", "Billboard_SSBO", "Billboard_VBO", - "Billboard_SSBO_noFBO" - }), - Optional::Yes, - ShaderOptionInfo.description - }, - { - PsfTextureInfo.identifier, - new StringVerifier, - Optional::No, - PsfTextureInfo.description - }, - { - ColorTextureInfo.identifier, - new StringVerifier, - Optional::No, - ColorTextureInfo.description - }, - { - LuminosityMultiplierInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LuminosityMultiplierInfo.description - }, - { - MagnitudeBoostInfo.identifier, - new DoubleVerifier, - Optional::Yes, - MagnitudeBoostInfo.description - }, - { - CutOffThresholdInfo.identifier, - new DoubleVerifier, - Optional::Yes, - CutOffThresholdInfo.description - }, - { - SharpnessInfo.identifier, - new DoubleVerifier, - Optional::Yes, - SharpnessInfo.description - }, - { - BillboardSizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - BillboardSizeInfo.description - }, - { - CloseUpBoostDistInfo.identifier, - new DoubleVerifier, - Optional::Yes, - CloseUpBoostDistInfo.description - }, - { - TmPointFilterSizeInfo.identifier, - new IntVerifier, - Optional::Yes, - TmPointFilterSizeInfo.description - }, - { - TmPointSigmaInfo.identifier, - new DoubleVerifier, - Optional::Yes, - TmPointSigmaInfo.description - }, - { - AdditionalNodesInfo.identifier, - new Vector2Verifier, - Optional::Yes, - AdditionalNodesInfo.description - }, - { - TmPointPxThresholdInfo.identifier, - new DoubleVerifier, - Optional::Yes, - TmPointPxThresholdInfo.description - }, - { - FirstRowInfo.identifier, - new IntVerifier, - Optional::Yes, - FirstRowInfo.description - }, - { - LastRowInfo.identifier, - new IntVerifier, - Optional::Yes, - LastRowInfo.description - }, - { - ColumnNamesInfo.identifier, - new StringListVerifier, - Optional::Yes, - ColumnNamesInfo.description - }, - { - LodPixelThresholdInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LodPixelThresholdInfo.description - }, - { - MaxGpuMemoryPercentInfo.identifier, - new DoubleVerifier, - Optional::Yes, - MaxGpuMemoryPercentInfo.description - }, - { - MaxCpuMemoryPercentInfo.identifier, - new DoubleVerifier, - Optional::Yes, - MaxCpuMemoryPercentInfo.description - }, - { - FilterPosXInfo.identifier, - new Vector2Verifier, - Optional::Yes, - FilterPosXInfo.description - }, - { - FilterPosYInfo.identifier, - new Vector2Verifier, - Optional::Yes, - FilterPosYInfo.description - }, - { - FilterPosZInfo.identifier, - new Vector2Verifier, - Optional::Yes, - FilterPosZInfo.description - }, - { - FilterGMagInfo.identifier, - new Vector2Verifier, - Optional::Yes, - FilterGMagInfo.description - }, - { - FilterBpRpInfo.identifier, - new Vector2Verifier, - Optional::Yes, - FilterBpRpInfo.description - }, - { - FilterDistInfo.identifier, - new Vector2Verifier, - Optional::Yes, - FilterDistInfo.description - }, - { - ReportGlErrorsInfo.identifier, - new BoolVerifier, - Optional::Yes, - ReportGlErrorsInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "gaiamission_renderablegaiastars"; + return doc; } RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) @@ -536,13 +460,9 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) { using File = ghoul::filesystem::File; - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableGaiaStars" - ); + const Parameters p = codegen::bake(dictionary); - _filePath = absPath(dictionary.value(FilePathInfo.identifier)); + _filePath = absPath(p.file); _dataFile = std::make_unique(_filePath); _dataFile->setCallback([&](const File&) { _dataIsDirty = true; }); @@ -556,25 +476,24 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) { gaia::FileReaderOption::BinaryOctree, "BinaryOctree" }, { gaia::FileReaderOption::StreamOctree, "StreamOctree" } }); - if (dictionary.hasKey(FileReaderOptionInfo.identifier)) { - const std::string fileReaderOption = dictionary.value( - FileReaderOptionInfo.identifier - ); - if (fileReaderOption == "Fits") { + switch (p.fileReaderOption) { + case Parameters::FileReader::Fits: _fileReaderOption = gaia::FileReaderOption::Fits; - } - else if (fileReaderOption == "Speck") { + break; + case Parameters::FileReader::Speck: _fileReaderOption = gaia::FileReaderOption::Speck; - } - else if (fileReaderOption == "BinaryRaw") { + break; + case Parameters::FileReader::BinaryRaw: _fileReaderOption = gaia::FileReaderOption::BinaryRaw; - } - else if (fileReaderOption == "BinaryOctree") { + break; + case Parameters::FileReader::BinaryOctree: _fileReaderOption = gaia::FileReaderOption::BinaryOctree; - } - else { + break; + case Parameters::FileReader::StreamOctree: _fileReaderOption = gaia::FileReaderOption::StreamOctree; - } + break; + default: + throw ghoul::MissingCaseException(); } _renderOption.addOptions({ @@ -582,18 +501,19 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) { gaia::RenderOption::Color, "Color" }, { gaia::RenderOption::Motion, "Motion" } }); - if (dictionary.hasKey(RenderOptionInfo.identifier)) { - const std::string renderOption = dictionary.value( - RenderOptionInfo.identifier - ); - if (renderOption == "Static") { - _renderOption = gaia::RenderOption::Static; - } - else if (renderOption == "Color") { - _renderOption = gaia::RenderOption::Color; - } - else { - _renderOption = gaia::RenderOption::Motion; + if (p.renderOption.has_value()) { + switch (*p.renderOption) { + case Parameters::RenderOption::Static: + _renderOption = gaia::RenderOption::Static; + break; + case Parameters::RenderOption::Color: + _renderOption = gaia::RenderOption::Color; + break; + case Parameters::RenderOption::Motion: + _renderOption = gaia::RenderOption::Motion; + break; + default: + throw ghoul::MissingCaseException(); } } _renderOption.onChange([&]() { _buffersAreDirty = true; }); @@ -614,31 +534,31 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) }); #endif // __APPLE__ - if (dictionary.hasKey(ShaderOptionInfo.identifier)) { - // Default shader option: - _shaderOption = gaia::ShaderOption::Billboard_VBO; - - const std::string shaderOption = dictionary.value( - ShaderOptionInfo.identifier - ); - -#ifndef __APPLE__ - if (shaderOption == "Point_SSBO") { - _shaderOption = gaia::ShaderOption::Point_SSBO; - } - else if (shaderOption == "Billboard_SSBO") { - _shaderOption = gaia::ShaderOption::Billboard_SSBO; - } - else if (shaderOption == "Billboard_SSBO_noFBO") { - _shaderOption = gaia::ShaderOption::Billboard_SSBO_noFBO; - } + if (p.shaderOption.has_value()) { + switch (*p.shaderOption) { + case Parameters::ShaderOption::PointSSBO: + _shaderOption = gaia::ShaderOption::Point_SSBO; + break; + case Parameters::ShaderOption::PointVBO: +#ifdef __APPLE__ + throw ghoul::RuntimeError("Shader option is not supported on MacOS"); #endif // __APPLE__ - - if (shaderOption == "Point_VBO") { - _shaderOption = gaia::ShaderOption::Point_VBO; - } - else if (shaderOption == "Billboard_VBO") { - _shaderOption = gaia::ShaderOption::Billboard_VBO; + _shaderOption = gaia::ShaderOption::Point_VBO; + break; + case Parameters::ShaderOption::BillboardSSBO: + _shaderOption = gaia::ShaderOption::Billboard_SSBO; + break; + case Parameters::ShaderOption::BillboardVBO: +#ifdef __APPLE__ + throw ghoul::RuntimeError("Shader option is not supported on MacOS"); +#endif // __APPLE__ + _shaderOption = gaia::ShaderOption::Billboard_VBO; + break; + case Parameters::ShaderOption::BillboardSSBONoFBO: + _shaderOption = gaia::ShaderOption::Billboard_SSBO_noFBO; + break; + default: + throw ghoul::MissingCaseException(); } } _shaderOption.onChange([&]() { @@ -647,95 +567,34 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) }); addProperty(_shaderOption); - _pointSpreadFunctionTexturePath = absPath(dictionary.value( - PsfTextureInfo.identifier - )); - _pointSpreadFunctionFile = std::make_unique(_pointSpreadFunctionTexturePath); - + _pointSpreadFunctionTexturePath = absPath(p.texture); _pointSpreadFunctionTexturePath.onChange( [&](){ _pointSpreadFunctionTextureIsDirty = true; } ); + _pointSpreadFunctionFile = std::make_unique(_pointSpreadFunctionTexturePath); _pointSpreadFunctionFile->setCallback( [&](const File&) { _pointSpreadFunctionTextureIsDirty = true; } ); - _colorTexturePath = absPath(dictionary.value( - ColorTextureInfo.identifier - )); + _colorTexturePath = absPath(p.colorMap); _colorTextureFile = std::make_unique(_colorTexturePath); _colorTexturePath.onChange([&]() { _colorTextureIsDirty = true; }); _colorTextureFile->setCallback([&](const File&) { _colorTextureIsDirty = true; }); - if (dictionary.hasKey(LuminosityMultiplierInfo.identifier)) { - _luminosityMultiplier = static_cast( - dictionary.value(LuminosityMultiplierInfo.identifier) - ); - } + _luminosityMultiplier = p.luminosityMultiplier.value_or(_luminosityMultiplier); + _magnitudeBoost = p.magnitudeBoost.value_or(_magnitudeBoost); + _cutOffThreshold = p.cutOffThreshold.value_or(_cutOffThreshold); + _sharpness = p.sharpness.value_or(_sharpness); + _billboardSize = p.billboardSize.value_or(_billboardSize); + _closeUpBoostDist = p.closeUpBoostDist.value_or(_closeUpBoostDist); + _tmPointFilterSize = p.filterSize.value_or(_tmPointFilterSize); + _tmPointSigma = p.sigma.value_or(_tmPointSigma); + _tmPointPixelWeightThreshold = + p.pixelWeightThreshold.value_or(_tmPointPixelWeightThreshold); + _additionalNodes = p.additionalNodes.value_or(_additionalNodes); + _lodPixelThreshold = p.lodPixelThreshold.value_or(_lodPixelThreshold); - if (dictionary.hasKey(MagnitudeBoostInfo.identifier)) { - _magnitudeBoost = static_cast( - dictionary.value(MagnitudeBoostInfo.identifier) - ); - } - - if (dictionary.hasKey(CutOffThresholdInfo.identifier)) { - _cutOffThreshold = static_cast( - dictionary.value(CutOffThresholdInfo.identifier) - ); - } - - if (dictionary.hasKey(SharpnessInfo.identifier)) { - _sharpness = static_cast( - dictionary.value(SharpnessInfo.identifier) - ); - } - - if (dictionary.hasKey(BillboardSizeInfo.identifier)) { - _billboardSize = static_cast( - dictionary.value(BillboardSizeInfo.identifier) - ); - } - - if (dictionary.hasKey(CloseUpBoostDistInfo.identifier)) { - _closeUpBoostDist = static_cast( - dictionary.value(CloseUpBoostDistInfo.identifier) - ); - } - - if (dictionary.hasKey(TmPointFilterSizeInfo.identifier)) { - _tmPointFilterSize = static_cast( - dictionary.value(TmPointFilterSizeInfo.identifier) - ); - } - - if (dictionary.hasKey(TmPointSigmaInfo.identifier)) { - _tmPointSigma = static_cast( - dictionary.value(TmPointSigmaInfo.identifier) - ); - } - if (dictionary.hasKey(TmPointPxThresholdInfo.identifier)) { - _tmPointPixelWeightThreshold = static_cast( - dictionary.value(TmPointPxThresholdInfo.identifier) - ); - } - - if (dictionary.hasKey(AdditionalNodesInfo.identifier)) { - _additionalNodes = static_cast( - dictionary.value(AdditionalNodesInfo.identifier) - ); - } - - if (dictionary.hasKey(LodPixelThresholdInfo.identifier)) { - _lodPixelThreshold = static_cast( - dictionary.value(LodPixelThresholdInfo.identifier) - ); - } - - if (dictionary.hasKey(MaxGpuMemoryPercentInfo.identifier)) { - _maxGpuMemoryPercent = static_cast( - dictionary.value(MaxGpuMemoryPercentInfo.identifier) - ); - } + _maxGpuMemoryPercent = p.maxGpuMemoryPercent.value_or(_maxGpuMemoryPercent); _maxGpuMemoryPercent.onChange([&]() { if (_ssboData != 0) { glDeleteBuffers(1, &_ssboData); @@ -761,59 +620,36 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) _maxStreamingBudgetInBytes = 0; }); - if (dictionary.hasKey(MaxCpuMemoryPercentInfo.identifier)) { - _maxCpuMemoryPercent = static_cast( - dictionary.value(MaxCpuMemoryPercentInfo.identifier) - ); - } - - if (dictionary.hasKey(FilterPosXInfo.identifier)) { - _posXThreshold = dictionary.value(FilterPosXInfo.identifier); - } + _maxCpuMemoryPercent = p.maxCpuMemoryPercent.value_or(_maxCpuMemoryPercent); + _posXThreshold = p.filterPosX.value_or(_posXThreshold); addProperty(_posXThreshold); - if (dictionary.hasKey(FilterPosYInfo.identifier)) { - _posXThreshold = dictionary.value(FilterPosYInfo.identifier); - } + _posYThreshold = p.filterPosY.value_or(_posYThreshold); addProperty(_posYThreshold); - if (dictionary.hasKey(FilterPosZInfo.identifier)) { - _posZThreshold = dictionary.value(FilterPosZInfo.identifier); - } + _posZThreshold = p.filterPosZ.value_or(_posZThreshold); addProperty(_posZThreshold); - if (dictionary.hasKey(FilterGMagInfo.identifier)) { - _gMagThreshold = dictionary.value(FilterGMagInfo.identifier); - } + _gMagThreshold = p.filterGMag.value_or(_gMagThreshold); addProperty(_gMagThreshold); - if (dictionary.hasKey(FilterBpRpInfo.identifier)) { - _bpRpThreshold = dictionary.value(FilterBpRpInfo.identifier); - } + _bpRpThreshold = p.filterBpRp.value_or(_bpRpThreshold); addProperty(_bpRpThreshold); - if (dictionary.hasKey(FilterDistInfo.identifier)) { - _distThreshold = dictionary.value(FilterDistInfo.identifier); - } + _distThreshold = p.filterDist.value_or(_distThreshold); addProperty(_distThreshold); // Only add properties correlated to fits files if we're reading from a fits file. if (_fileReaderOption == gaia::FileReaderOption::Fits) { - if (dictionary.hasKey(FirstRowInfo.identifier)) { - _firstRow = static_cast( - dictionary.value(FirstRowInfo.identifier) - ); - } + _firstRow = p.firstRow.value_or(_firstRow); _firstRow.onChange([&]() { _dataIsDirty = true; }); addProperty(_firstRow); - if (dictionary.hasKey(LastRowInfo.identifier)) { - _lastRow = static_cast(dictionary.value(LastRowInfo.identifier)); - } + _lastRow = p.lastRow.value_or(_lastRow); _lastRow.onChange([&]() { _dataIsDirty = true; }); addProperty(_lastRow); - if (dictionary.hasKey(ColumnNamesInfo.identifier)) { + if (p.columnNames.has_value()) { ghoul::Dictionary tmpDict = dictionary.value( ColumnNamesInfo.identifier ); @@ -834,13 +670,11 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) } if (_firstRow > _lastRow) { - throw ghoul::RuntimeError("User defined FirstRow is bigger than LastRow."); + throw ghoul::RuntimeError("User defined FirstRow is bigger than LastRow"); } } - if (dictionary.hasKey(ReportGlErrorsInfo.identifier)) { - _reportGlErrors = dictionary.value(ReportGlErrorsInfo.identifier); - } + _reportGlErrors = p.reportGlErrors.value_or(_reportGlErrors); addProperty(_reportGlErrors); // Add a read-only property for the number of rendered stars per frame. diff --git a/modules/gaia/tasks/constructoctreetask.cpp b/modules/gaia/tasks/constructoctreetask.cpp index a4edef1372..7fefa17517 100644 --- a/modules/gaia/tasks/constructoctreetask.cpp +++ b/modules/gaia/tasks/constructoctreetask.cpp @@ -35,164 +35,263 @@ #include namespace { - constexpr const char* KeyInFileOrFolderPath = "InFileOrFolderPath"; - constexpr const char* KeyOutFileOrFolderPath = "OutFileOrFolderPath"; - constexpr const char* KeyMaxDist = "MaxDist"; - constexpr const char* KeyMaxStarsPerNode = "MaxStarsPerNode"; - constexpr const char* KeySingleFileInput = "SingleFileInput"; - - constexpr const char* KeyFilterPosX = "FilterPosX"; - constexpr const char* KeyFilterPosY = "FilterPosY"; - constexpr const char* KeyFilterPosZ = "FilterPosZ"; - constexpr const char* KeyFilterGMag = "FilterGMag"; - constexpr const char* KeyFilterBpRp = "FilterBpRp"; - constexpr const char* KeyFilterVelX = "FilterVelX"; - constexpr const char* KeyFilterVelY = "FilterVelY"; - constexpr const char* KeyFilterVelZ = "FilterVelZ"; - constexpr const char* KeyFilterBpMag = "FilterBpMag"; - constexpr const char* KeyFilterRpMag = "FilterRpMag"; - constexpr const char* KeyFilterBpG = "FilterBpG"; - constexpr const char* KeyFilterGRp = "FilterGRp"; - constexpr const char* KeyFilterRa = "FilterRa"; - constexpr const char* KeyFilterRaError = "FilterRaError"; - constexpr const char* KeyFilterDec = "FilterDec"; - constexpr const char* KeyFilterDecError = "FilterDecError"; - constexpr const char* KeyFilterParallax = "FilterParallax"; - constexpr const char* KeyFilterParallaxError = "FilterParallaxError"; - constexpr const char* KeyFilterPmra = "FilterPmra"; - constexpr const char* KeyFilterPmraError = "FilterPmraError"; - constexpr const char* KeyFilterPmdec = "FilterPmdec"; - constexpr const char* KeyFilterPmdecError = "FilterPmdecError"; - constexpr const char* KeyFilterRv = "FilterRv"; - constexpr const char* KeyFilterRvError = "FilterRvError"; - constexpr const char* _loggerCat = "ConstructOctreeTask"; + + struct [[codegen::Dictionary(ConstructOctreeTask)]] Parameters { + // If SingleFileInput is set to true then this specifies the path to a single BIN + // file containing a full dataset. Otherwise this specifies the path to a folder + // with multiple BIN files containing subsets of sorted star data + std::string inFileOrFolderPath; + + // If SingleFileInput is set to true then this specifies the output file name + // (including full path). Otherwise this specifies the path to the folder which to + // save all files + std::string outFileOrFolderPath; + + // If set it determines what MAX_DIST to use when creating Octree + std::optional maxDist; + + // If set it determines what MAX_STAR_PER_NODE to use when creating Octree + std::optional maxStarsPerNode; + + // If true then task will read from a single file and output a single binary file + // with the full Octree. If false then task will read all files in specified + // folder and output multiple files for the Octree + std::optional singleFileInput; + + // If defined then only stars with Position X values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterPosX; + + // If defined then only stars with Position Y values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterPosY; + + // If defined then only stars with Position Z values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterPosZ; + + // If defined then only stars with G mean magnitude values between [min, max] will + // be inserted into Octree (if min is set to 20.0 it is read as -Inf, if max is + // set to 20.0 it is read as +Inf). If min = max then all values equal min|max + // will be filtered away. Default GMag = 20.0 if no value existed + std::optional filterGMag; + + // If defined then only stars with Bp-Rp color values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterBpRp; + + // If defined then only stars with Velocity X values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterVelX; + + // If defined then only stars with Velocity Y values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterVelY; + + // If defined then only stars with Velocity Z values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterVelZ; + + // If defined then only stars with Bp mean magnitude values between [min, max] + // will be inserted into Octree (if min is set to 20.0 it is read as -Inf, if max + // is set to 20.0 it is read as +Inf). If min = max then all values equal min|max + // will be filtered away. Default BpMag = 20.0 if no value existed + std::optional filterBpMag; + + // If defined then only stars with Rp mean magnitude values between [min, max] + // will be inserted into Octree (if min is set to 20.0 it is read as -Inf, if max + // is set to 20.0 it is read as +Inf). If min = max then all values equal min|max + // will be filtered away. Default RpMag = 20.0 if no value existed + std::optional filterRpMag; + + // If defined then only stars with Bp-G color values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterBpG; + + // If defined then only stars with G-Rp color values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterGRp; + + // If defined then only stars with RA values between [min, max] will be inserted + // into Octree (if min is set to 0.0 it is read as -Inf, if max is set to 0.0 it + // is read as +Inf). If min = max then all values equal min|max will be filtered + // away + std::optional filterRa; + + // If defined then only stars with RA Error values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterRaError; + + // If defined then only stars with DEC values between [min, max] will be inserted + // into Octree (if min is set to 0.0 it is read as -Inf, if max is set to 0.0 it + // is read as +Inf). If min = max then all values equal min|max will be filtered + // away + std::optional filterDec; + + // If defined then only stars with DEC Error values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterDecError; + + // If defined then only stars with Parallax values between [min, max] will be + // inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set to + // 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterParallax; + + // If defined then only stars with Parallax Error values between [min, max] will + // be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set + // to 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterParallaxError; + + // If defined then only stars with Proper Motion RA values between [min, max] will + // be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set + // to 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterPmra; + + // If defined then only stars with Proper Motion RA Error values between + // [min, max] will be inserted into Octree (if min is set to 0.0 it is read as + // -Inf, if max is set to 0.0 it is read as +Inf). If min = max then all values + // equal min|max will be filtered away + std::optional filterPmraError; + + // If defined then only stars with Proper Motion DEC values between [min, max] + // will be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max + // is set to 0.0 it is read as +Inf). If min = max then all values equal min|max + // will be filtered away + std::optional filterPmdec; + + // If defined then only stars with Proper Motion DEC Error values between + // [min, max] will be inserted into Octree (if min is set to 0.0 it is read as + // -Inf, if max is set to 0.0 it is read as +Inf). If min = max then all values + // equal min|max will be filtered away + std::optional filterPmdecError; + + // If defined then only stars with Radial Velocity values between [min, max] will + // be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max is set + // to 0.0 it is read as +Inf). If min = max then all values equal min|max will be + // filtered away + std::optional filterRv; + + // If defined then only stars with Radial Velocity Error values between [min, max] + // will be inserted into Octree (if min is set to 0.0 it is read as -Inf, if max + // is set to 0.0 it is read as +Inf). If min = max then all values equal min|max + // will be filtered away + std::optional filterRvError; + }; +#include "constructoctreetask_codegen.cpp" } // namespace namespace openspace { ConstructOctreeTask::ConstructOctreeTask(const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "ConstructOctreeTask" - ); + const Parameters p = codegen::bake(dictionary); - _inFileOrFolderPath = absPath(dictionary.value(KeyInFileOrFolderPath)); - _outFileOrFolderPath = absPath(dictionary.value(KeyOutFileOrFolderPath)); - - if (dictionary.hasKey(KeyMaxDist)) { - _maxDist = static_cast(dictionary.value(KeyMaxDist)); - } - - if (dictionary.hasKey(KeyMaxStarsPerNode)) { - _maxStarsPerNode = static_cast(dictionary.value(KeyMaxStarsPerNode)); - } - - if (dictionary.hasKey(KeySingleFileInput)) { - _singleFileInput = dictionary.value(KeySingleFileInput); - } + _inFileOrFolderPath = absPath(p.inFileOrFolderPath); + _outFileOrFolderPath = absPath(p.outFileOrFolderPath); + _maxDist = p.maxDist.value_or(_maxDist); + _maxStarsPerNode = p.maxStarsPerNode.value_or(_maxStarsPerNode); + _singleFileInput = p.singleFileInput.value_or(_singleFileInput); _octreeManager = std::make_shared(); _indexOctreeManager = std::make_shared(); - // Check for filter params. - if (dictionary.hasKey(KeyFilterPosX)) { - _posX = dictionary.value(KeyFilterPosX); - _filterPosX = true; - } - if (dictionary.hasKey(KeyFilterPosY)) { - _posY = dictionary.value(KeyFilterPosY); - _filterPosY = true; - } - if (dictionary.hasKey(KeyFilterPosZ)) { - _posZ = dictionary.value(KeyFilterPosZ); - _filterPosZ = true; - } - if (dictionary.hasKey(KeyFilterGMag)) { - _gMag = dictionary.value(KeyFilterGMag); - _filterGMag = true; - } - if (dictionary.hasKey(KeyFilterBpRp)) { - _bpRp = dictionary.value(KeyFilterBpRp); - _filterBpRp = true; - } - if (dictionary.hasKey(KeyFilterVelX)) { - _velX = dictionary.value(KeyFilterVelX); - _filterVelX = true; - } - if (dictionary.hasKey(KeyFilterVelY)) { - _velY = dictionary.value(KeyFilterVelY); - _filterVelY = true; - } - if (dictionary.hasKey(KeyFilterVelZ)) { - _velZ = dictionary.value(KeyFilterVelZ); - _filterVelZ = true; - } - if (dictionary.hasKey(KeyFilterBpMag)) { - _bpMag = dictionary.value(KeyFilterBpMag); - _filterBpMag = true; - } - if (dictionary.hasKey(KeyFilterRpMag)) { - _rpMag = dictionary.value(KeyFilterRpMag); - _filterRpMag = true; - } - if (dictionary.hasKey(KeyFilterBpG)) { - _bpG = dictionary.value(KeyFilterBpG); - _filterBpG = true; - } - if (dictionary.hasKey(KeyFilterGRp)) { - _gRp = dictionary.value(KeyFilterGRp); - _filterGRp = true; - } - if (dictionary.hasKey(KeyFilterRa)) { - _ra = dictionary.value(KeyFilterRa); - _filterRa = true; - } - if (dictionary.hasKey(KeyFilterRaError)) { - _raError = dictionary.value(KeyFilterRaError); - _filterRaError = true; - } - if (dictionary.hasKey(KeyFilterDec)) { - _dec = dictionary.value(KeyFilterDec); - _filterDec = true; - } - if (dictionary.hasKey(KeyFilterDecError)) { - _decError = dictionary.value(KeyFilterDecError); - _filterDecError = true; - } - if (dictionary.hasKey(KeyFilterParallax)) { - _parallax = dictionary.value(KeyFilterParallax); - _filterParallax = true; - } - if (dictionary.hasKey(KeyFilterParallaxError)) { - _parallaxError = dictionary.value(KeyFilterParallaxError); - _filterParallaxError = true; - } - if (dictionary.hasKey(KeyFilterPmra)) { - _pmra = dictionary.value(KeyFilterPmra); - _filterPmra = true; - } - if (dictionary.hasKey(KeyFilterPmraError)) { - _pmraError = dictionary.value(KeyFilterPmraError); - _filterPmraError = true; - } - if (dictionary.hasKey(KeyFilterPmdec)) { - _pmdec = dictionary.value(KeyFilterPmdec); - _filterPmdec = true; - } - if (dictionary.hasKey(KeyFilterPmdecError)) { - _pmdecError = dictionary.value(KeyFilterPmdecError); - _filterPmdecError = true; - } - if (dictionary.hasKey(KeyFilterRv)) { - _rv = dictionary.value(KeyFilterRv); - _filterRv = true; - } - if (dictionary.hasKey(KeyFilterRvError)) { - _rvError = dictionary.value(KeyFilterRvError); - _filterRvError = true; - } + + _posX = p.filterPosX.value_or(_posX); + _filterPosX = p.filterPosX.has_value(); + + _posY = p.filterPosY.value_or(_posY); + _filterPosY = p.filterPosY.has_value(); + + _posZ = p.filterPosZ.value_or(_posZ); + _filterPosZ = p.filterPosZ.has_value(); + + _gMag = p.filterGMag.value_or(_gMag); + _filterGMag = p.filterGMag.has_value(); + + _bpRp = p.filterBpRp.value_or(_bpRp); + _filterBpRp = p.filterBpRp.has_value(); + + _velX = p.filterVelX.value_or(_velX); + _filterVelX = p.filterVelX.has_value(); + + _velY = p.filterVelY.value_or(_velY); + _filterVelY = p.filterVelY.has_value(); + + _velZ = p.filterVelZ.value_or(_velZ); + _filterVelZ = p.filterVelZ.has_value(); + + _bpMag = p.filterBpMag.value_or(_bpMag); + _filterBpMag = p.filterBpMag.has_value(); + + _rpMag = p.filterRpMag.value_or(_rpMag); + _filterRpMag = p.filterRpMag.has_value(); + + _bpG = p.filterBpG.value_or(_bpG); + _filterBpG = p.filterBpG.has_value(); + + _gRp = p.filterGRp.value_or(_gRp); + _filterGRp = p.filterGRp.has_value(); + + _ra = p.filterRa.value_or(_ra); + _filterRa = p.filterRa.has_value(); + + _raError = p.filterRaError.value_or(_raError); + _filterRaError = p.filterRaError.has_value(); + + _dec = p.filterDec.value_or(_dec); + _filterDec = p.filterDec.has_value(); + + _decError = p.filterDecError.value_or(_decError); + _filterDecError = p.filterDecError.has_value(); + + _parallax = p.filterParallax.value_or(_parallax); + _filterParallax = p.filterParallax.has_value(); + + _parallaxError = p.filterParallaxError.value_or(_parallaxError); + _filterParallaxError = p.filterParallaxError.has_value(); + + _pmra = p.filterPmra.value_or(_pmra); + _filterPmra = p.filterPmra.has_value(); + + _pmraError = p.filterPmraError.value_or(_pmraError); + _filterPmraError = p.filterPmraError.has_value(); + + _pmdec = p.filterPmdec.value_or(_pmdec); + _filterPmdec = p.filterPmdec.has_value(); + + _pmdecError = p.filterPmdecError.value_or(_pmdecError); + _filterPmdecError = p.filterPmdecError.has_value(); + + _rv = p.filterRv.value_or(_rv); + _filterRv = p.filterRv.has_value(); + + _rvError = p.filterRvError.value_or(_rvError); + _filterRvError = p.filterRvError.has_value(); } std::string ConstructOctreeTask::description() { @@ -201,7 +300,7 @@ std::string ConstructOctreeTask::description() { } void ConstructOctreeTask::perform(const Task::ProgressCallback& onProgress) { - onProgress(0.0f); + onProgress(0.f); if (_singleFileInput) { constructOctreeFromSingleFile(onProgress); @@ -210,7 +309,7 @@ void ConstructOctreeTask::perform(const Task::ProgressCallback& onProgress) { constructOctreeFromFolder(onProgress); } - onProgress(1.0f); + onProgress(1.f); } void ConstructOctreeTask::constructOctreeFromSingleFile( @@ -543,269 +642,9 @@ bool ConstructOctreeTask::filterStar(const glm::vec2& range, float filterValue, } documentation::Documentation ConstructOctreeTask::Documentation() { - using namespace documentation; - return { - "ConstructOctreeTask", - "gaiamission_constructoctreefrombin", - { - { - KeyInFileOrFolderPath, - new StringVerifier, - Optional::No, - "If SingleFileInput is set to true then this specifies the path to a " - "single BIN file containing a full dataset. Otherwise this specifies the " - "path to a folder with multiple BIN files containing subsets of sorted " - "star data.", - }, - { - KeyOutFileOrFolderPath, - new StringVerifier, - Optional::No, - "If SingleFileInput is set to true then this specifies the output file " - "name (including full path). Otherwise this specifies the path to the " - "folder which to save all files.", - }, - { - KeyMaxDist, - new IntVerifier, - Optional::Yes, - "If set it determines what MAX_DIST to use when creating Octree." - }, - { - KeyMaxStarsPerNode, - new IntVerifier, - Optional::Yes, - "If set it determines what MAX_STAR_PER_NODE to use when creating Octree." - }, - { - KeySingleFileInput, - new BoolVerifier, - Optional::Yes, - "If true then task will read from a single file and output a single " - "binary file with the full Octree. If false then task will read all " - "files in specified folder and output multiple files for the Octree." - }, - { - KeyFilterPosX, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Position X values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterPosY, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Position Y values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterPosZ, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Position Z values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterGMag, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with G mean magnitude values between " - "[min, max] will be inserted into Octree (if min is set to 20.0 it is " - "read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away. Default " - "GMag = 20.0 if no value existed." - }, - { - KeyFilterBpRp, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Bp-Rp color values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterVelX, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Velocity X values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterVelY, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Velocity Y values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterVelZ, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Velocity Z values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterBpMag, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Bp mean magnitude values between " - "[min, max] will be inserted into Octree (if min is set to 20.0 it is " - "read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away. Default " - "BpMag = 20.0 if no value existed." - }, - { - KeyFilterRpMag, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Rp mean magnitude values between " - "[min, max] will be inserted into Octree (if min is set to 20.0 it is " - "read as -Inf, if max is set to 20.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away. Default RpMag = " - "20.0 if no value existed." - }, - { - KeyFilterBpG, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Bp-G color values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterGRp, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with G-Rp color values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterRa, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with RA values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterRaError, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with RA Error values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterDec, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with DEC values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterDecError, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with DEC Error values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterParallax, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Parallax values between [min, max] " - "will be inserted into Octree (if min is set to 0.0 it is read as -Inf, " - "if max is set to 0.0 it is read as +Inf). If min = max then all values " - "equal min|max will be filtered away." - }, - { - KeyFilterParallaxError, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Parallax Error values between " - "[min, max] will be inserted into Octree (if min is set to 0.0 it is " - "read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away." - }, - { - KeyFilterPmra, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Proper Motion RA values between " - "[min, max] will be inserted into Octree (if min is set to 0.0 it is " - "read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away." - }, - { - KeyFilterPmraError, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Proper Motion RA Error values between " - "[min, max] will be inserted into Octree (if min is set to 0.0 it is " - "read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away." - }, - { - KeyFilterPmdec, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Proper Motion DEC values between " - "[min, max] will be inserted into Octree (if min is set to 0.0 it is " - "read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away." - }, - { - KeyFilterPmdecError, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Proper Motion DEC Error values between " - "[min, max] will be inserted into Octree (if min is set to 0.0 it is " - "read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away." - }, - { - KeyFilterRv, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Radial Velocity values between " - "[min, max] will be inserted into Octree (if min is set to 0.0 it is " - "read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away." - }, - { - KeyFilterRvError, - new Vector2Verifier, - Optional::Yes, - "If defined then only stars with Radial Velocity Error values between " - "[min, max] will be inserted into Octree (if min is set to 0.0 it is " - "read as -Inf, if max is set to 0.0 it is read as +Inf). If min = max " - "then all values equal min|max will be filtered away." - }, - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "gaiamission_constructoctreefrombin"; + return doc; } } // namespace openspace diff --git a/modules/gaia/tasks/readfitstask.cpp b/modules/gaia/tasks/readfitstask.cpp index 5139ab6366..865433da6a 100644 --- a/modules/gaia/tasks/readfitstask.cpp +++ b/modules/gaia/tasks/readfitstask.cpp @@ -36,55 +36,61 @@ #include #include +#include namespace { - constexpr const char* KeyInFileOrFolderPath = "InFileOrFolderPath"; - constexpr const char* KeyOutFileOrFolderPath = "OutFileOrFolderPath"; - constexpr const char* KeySingleFileProcess = "SingleFileProcess"; - constexpr const char* KeyThreadsToUse = "ThreadsToUse"; - constexpr const char* KeyFirstRow = "FirstRow"; - constexpr const char* KeyLastRow = "LastRow"; constexpr const char* KeyFilterColumnNames = "FilterColumnNames"; constexpr const char* _loggerCat = "ReadFitsTask"; + + struct [[codegen::Dictionary(ReadFitsTask)]] Parameters { + // If SingleFileProcess is set to true then this specifies the path to a single + // FITS file that will be read. Otherwise it specifies the path to a folder with + // multiple FITS files that are to be read + std::string inFileOrFolderPath; + + // If SingleFileProcess is set to true then this specifies the name (including + // entire path) to the output file. Otherwise it specifies the path to the output + // folder which to export binary star data to + std::string outFileOrFolderPath; + + // If true then task will read from a single FITS file and output a single binary + // file. If false then task will read all files in specified folder and output + // multiple files sorted by location + std::optional singleFileProcess; + + // Defines how many threads to use when reading from multiple files + std::optional threadsToUse [[codegen::greater(1)]]; + + // Defines the first row that will be read from the specified FITS file(s). If not + // defined then reading will start at first row + std::optional firstRow; + + // Defines the last row that will be read from the specified FITS file(s). If not + // defined (or less than FirstRow) then full file(s) will be read + std::optional lastRow; + + // A list of strings with the names of all the additional columns that are to be + // read from the specified FITS file(s). These columns can be used for filtering + // while constructing Octree later + std::optional> filterColumnNames; + }; +#include "readfitstask_codegen.cpp" } // namespace namespace openspace { ReadFitsTask::ReadFitsTask(const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "ReadFitsTask" - ); + const Parameters p = codegen::bake(dictionary); - _inFileOrFolderPath = absPath(dictionary.value(KeyInFileOrFolderPath)); - _outFileOrFolderPath = absPath(dictionary.value(KeyOutFileOrFolderPath)); + _inFileOrFolderPath = absPath(p.inFileOrFolderPath); + _outFileOrFolderPath = absPath(p.outFileOrFolderPath); + _singleFileProcess = p.singleFileProcess.value_or(_singleFileProcess); + _threadsToUse = p.threadsToUse.value_or(_threadsToUse); + _firstRow = p.firstRow.value_or(_firstRow); + _lastRow = p.lastRow.value_or(_lastRow); - if (dictionary.hasKey(KeySingleFileProcess)) { - _singleFileProcess = dictionary.value(KeySingleFileProcess); - } - - if (dictionary.hasKey(KeyThreadsToUse)) { - _threadsToUse = static_cast(dictionary.value(KeyThreadsToUse)); - if (_threadsToUse < 1) { - LINFO(fmt::format( - "User defined ThreadsToUse was: {}. Will be set to 1", _threadsToUse - )); - _threadsToUse = 1; - } - } - - if (dictionary.hasKey(KeyFirstRow)) { - _firstRow = static_cast(dictionary.value(KeyFirstRow)); - } - - if (dictionary.hasKey(KeyLastRow)) { - _lastRow = static_cast(dictionary.value(KeyLastRow)); - } - - - if (dictionary.hasKey(KeyFilterColumnNames)) { + if (p.filterColumnNames.has_value()) { ghoul::Dictionary d = dictionary.value(KeyFilterColumnNames); // Ugly fix for ASCII sorting when there are more columns read than 10. @@ -322,66 +328,9 @@ int ReadFitsTask::writeOctantToFile(const std::vector& octantData, int in } documentation::Documentation ReadFitsTask::Documentation() { - using namespace documentation; - return { - "ReadFitsFile", - "gaiamission_fitsfiletorawdata", - { - { - KeyInFileOrFolderPath, - new StringVerifier, - Optional::No, - "If SingleFileProcess is set to true then this specifies the path to a " - "single FITS file that will be read. Otherwise it specifies the path to " - "a folder with multiple FITS files that are to be read.", - }, - { - KeyOutFileOrFolderPath, - new StringVerifier, - Optional::No, - "If SingleFileProcess is set to true then this specifies the name " - "(including entire path) to the output file. Otherwise it specifies the " - "path to the output folder which to export binary star data to.", - }, - { - KeySingleFileProcess, - new BoolVerifier, - Optional::Yes, - "If true then task will read from a single FITS file and output a single " - "binary file. If false then task will read all files in specified folder " - "and output multiple files sorted by location." - }, - { - KeyThreadsToUse, - new IntVerifier, - Optional::Yes, - "Defines how many threads to use when reading from multiple files." - }, - { - KeyFirstRow, - new IntVerifier, - Optional::Yes, - "Defines the first row that will be read from the specified FITS " - "file(s). If not defined then reading will start at first row.", - }, - { - KeyLastRow, - new IntVerifier, - Optional::Yes, - "Defines the last row that will be read from the specified FITS file(s). " - "If not defined (or less than FirstRow) then full file(s) will be read.", - }, - { - KeyFilterColumnNames, - new StringListVerifier, - Optional::Yes, - "A list of strings with the names of all the additional columns that are " - "to be read from the specified FITS file(s). These columns can be used " - "for filtering while constructing Octree later.", - }, - - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "gaiamission_fitsfiletorawdata"; + return doc; } } // namespace openspace diff --git a/modules/gaia/tasks/readspecktask.cpp b/modules/gaia/tasks/readspecktask.cpp index 8db543c2de..04526a33d9 100644 --- a/modules/gaia/tasks/readspecktask.cpp +++ b/modules/gaia/tasks/readspecktask.cpp @@ -34,23 +34,30 @@ #include namespace { - constexpr const char* KeyInFilePath = "InFilePath"; - constexpr const char* KeyOutFilePath = "OutFilePath"; - constexpr const char* _loggerCat = "ReadSpeckTask"; + + struct [[codegen::Dictionary(ReadSpeckTask)]] Parameters { + // The path to the SPECK file that are to be read + std::string inFilePath; + + // The path to the file to export raw VBO data to + std::string outFilePath; + }; +#include "readspecktask_codegen.cpp" } // namespace namespace openspace { -ReadSpeckTask::ReadSpeckTask(const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "ReadSpeckTask" - ); +documentation::Documentation ReadSpeckTask::Documentation() { + documentation::Documentation doc = codegen::doc(); + doc.id = "gaiamission_speckfiletorawdata"; + return doc; +} - _inFilePath = absPath(dictionary.value(KeyInFilePath)); - _outFilePath = absPath(dictionary.value(KeyOutFilePath)); +ReadSpeckTask::ReadSpeckTask(const ghoul::Dictionary& dictionary) { + const Parameters p = codegen::bake(dictionary); + _inFilePath = absPath(p.inFilePath); + _outFilePath = absPath(p.outFilePath); } std::string ReadSpeckTask::description() { @@ -92,26 +99,4 @@ void ReadSpeckTask::perform(const Task::ProgressCallback& onProgress) { onProgress(1.f); } -documentation::Documentation ReadSpeckTask::Documentation() { - using namespace documentation; - return { - "ReadSpeckTask", - "gaiamission_speckfiletorawdata", - { - { - KeyInFilePath, - new StringVerifier, - Optional::No, - "The path to the SPECK file that are to be read.", - }, - { - KeyOutFilePath, - new StringVerifier, - Optional::No, - "The path to the file to export raw VBO data to.", - }, - } - }; -} - } // namespace openspace diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index bdfdd16a6f..6db7ebdfdc 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -453,7 +453,9 @@ std::vector GlobeBrowsingModule::documentations() globebrowsing::Layer::Documentation(), globebrowsing::LayerAdjustment::Documentation(), globebrowsing::LayerManager::Documentation(), - GlobeLabelsComponent::Documentation() + GlobeLabelsComponent::Documentation(), + RingsComponent::Documentation(), + ShadowComponent::Documentation() }; } diff --git a/modules/globebrowsing/src/dashboarditemglobelocation.cpp b/modules/globebrowsing/src/dashboarditemglobelocation.cpp index 8c53564982..fe51c0fb9f 100644 --- a/modules/globebrowsing/src/dashboarditemglobelocation.cpp +++ b/modules/globebrowsing/src/dashboarditemglobelocation.cpp @@ -62,36 +62,25 @@ namespace { "Determines the number of significant digits that are shown in the location text." }; + struct [[codegen::Dictionary(DashboardItemGlobeLocation)]] Parameters { + // [[codegen::verbatim(FontNameInfo.description)]] + std::optional fontName; + + // [[codegen::verbatim(FontSizeInfo.description)]] + std::optional fontSize; + + // [[codegen::verbatim(SignificantDigitsInfo.description)]] + std::optional significantDigits; + }; +#include "dashboarditemglobelocation_codegen.cpp" } // namespace namespace openspace { documentation::Documentation DashboardItemGlobeLocation::Documentation() { - using namespace documentation; - return { - "DashboardItem Globe Location", - "globebrowsing_dashboarditem_globelocation", - { - { - FontNameInfo.identifier, - new StringVerifier, - Optional::Yes, - FontNameInfo.description - }, - { - FontSizeInfo.identifier, - new IntVerifier, - Optional::Yes, - FontSizeInfo.description - }, - { - SignificantDigitsInfo.identifier, - new IntVerifier, - Optional::Yes, - SignificantDigitsInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "globebrowsing_dashboarditem_globelocation"; + return doc; } DashboardItemGlobeLocation::DashboardItemGlobeLocation( @@ -102,30 +91,15 @@ DashboardItemGlobeLocation::DashboardItemGlobeLocation( , _significantDigits(SignificantDigitsInfo, 4, 1, 12) , _font(global::fontManager->font(KeyFontMono, 10)) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "DashboardItemGlobeLocation" - ); - - if (dictionary.hasKey(FontNameInfo.identifier)) { - _fontName = dictionary.value(FontNameInfo.identifier); - } - if (dictionary.hasKey(FontSizeInfo.identifier)) { - _fontSize = static_cast(dictionary.value(FontSizeInfo.identifier)); - } - if (dictionary.hasKey(SignificantDigitsInfo.identifier)) { - _significantDigits = static_cast( - dictionary.value(SignificantDigitsInfo.identifier) - ); - } - + const Parameters p = codegen::bake(dictionary); + _fontName = p.fontName.value_or(_fontName); _fontName.onChange([this]() { _font = global::fontManager->font(_fontName, _fontSize); }); addProperty(_fontName); + _fontSize = p.fontSize.value_or(_fontSize); _fontSize.onChange([this]() { _font = global::fontManager->font(_fontName, _fontSize); }); @@ -139,6 +113,7 @@ DashboardItemGlobeLocation::DashboardItemGlobeLocation( _significantDigits.value() ); }; + _significantDigits = p.significantDigits.value_or(_significantDigits); _significantDigits.onChange(updateFormatString); addProperty(_significantDigits); updateFormatString(); diff --git a/modules/globebrowsing/src/globelabelscomponent.cpp b/modules/globebrowsing/src/globelabelscomponent.cpp index 3975dfb0af..185dc0ce6b 100644 --- a/modules/globebrowsing/src/globelabelscomponent.cpp +++ b/modules/globebrowsing/src/globelabelscomponent.cpp @@ -42,14 +42,14 @@ #include #include #include +#include #include #include +#include namespace { constexpr const char* _loggerCat = "GlobeLabels"; - constexpr const char* KeyLabelsFileName = "FileName"; - constexpr const double LabelFadeOutLimitAltitudeMeters = 25000.0; constexpr const double RangeAngularCoefConst = 0.8; constexpr const float MinOpacityValueConst = 0.009f; @@ -163,114 +163,72 @@ namespace { "Label Alignment Option", "Labels are aligned horizontally or circularly related to the planet." }; + + struct [[codegen::Dictionary(GlobeLabelsComponent)]] Parameters { + // The path to the labels file + std::optional fileName; + + // [[codegen::verbatim(LabelsInfo.description)]] + std::optional labels; + + // [[codegen::verbatim(LabelsEnableInfo.description)]] + std::optional enable; + + // [[codegen::verbatim(LabelsFontSizeInfo.description)]] + std::optional labelsFontSize; + + // [[codegen::verbatim(LabelsMinSizeInfo.description)]] + std::optional labelsMinSize; + + // [[codegen::verbatim(LabelsMaxSizeInfo.description)]] + std::optional labelsMaxSize; + + // [[codegen::verbatim(LabelsSizeInfo.description)]] + std::optional labelsSize; + + // [[codegen::verbatim(LabelsMinHeightInfo.description)]] + std::optional labelsMinHeight; + + // [[codegen::verbatim(LabelsColorInfo.description)]] + std::optional labelsColor [[codegen::color()]]; + + // [[codegen::verbatim(LabelsOpacityInfo.description)]] + std::optional labelsOpacity [[codegen::inrange(0.f, 1.0)]]; + + // [[codegen::verbatim(LabelsFadeInStartingDistanceInfo.description)]] + std::optional fadeInStartingDistance; + + // [[codegen::verbatim(LabelsFadeOutStartingDistanceInfo.description)]] + std::optional fadeOutStartingDistance; + + // [[codegen::verbatim(LabelsFadeInEnabledInfo.description)]] + std::optional labelsFadeInEnabled; + + // [[codegen::verbatim(LabelsFadeOutEnabledInfo.description)]] + std::optional labelsFadeOutEnabled; + + // [[codegen::verbatim(LabelsDisableCullingEnabledInfo.description)]] + std::optional labelsDisableCullingEnabled; + + // [[codegen::verbatim(LabelsDistanceEPSInfo.description)]] + std::optional labelsDistanceEPS; + + enum class Alignment { + Horizontally, + Circularly + }; + // [[codegen::verbatim(LabelAlignmentOptionInfo.description)]] + std::optional labelAlignmentOption; + }; +#include "globelabelscomponent_codegen.cpp" } // namespace namespace openspace { documentation::Documentation GlobeLabelsComponent::Documentation() { - using namespace documentation; - return { - "GlobeLabels Component", - "globebrowsing_globelabelscomponent", - { - { - LabelsInfo.identifier, - new BoolVerifier, - Optional::Yes, - LabelsInfo.description - }, - { - LabelsEnableInfo.identifier, - new BoolVerifier, - Optional::Yes, - LabelsEnableInfo.description - }, - { - LabelsFontSizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsFontSizeInfo.description - }, - { - LabelsMaxSizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsMaxSizeInfo.description - }, - { - LabelsMinSizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsMinSizeInfo.description - }, - { - LabelsSizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsSizeInfo.description - }, - { - LabelsMinHeightInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsMinHeightInfo.description - }, - { - LabelsColorInfo.identifier, - new Color3Verifier, - Optional::Yes, - LabelsColorInfo.description - }, - { - LabelsOpacityInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsOpacityInfo.description - }, - { - LabelsFadeInStartingDistanceInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsFadeInStartingDistanceInfo.description - }, - { - LabelsFadeOutStartingDistanceInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsFadeOutStartingDistanceInfo.description - }, - { - LabelsFadeInEnabledInfo.identifier, - new BoolVerifier, - Optional::Yes, - LabelsFadeInEnabledInfo.description - }, - { - LabelsFadeOutEnabledInfo.identifier, - new BoolVerifier, - Optional::Yes, - LabelsFadeOutEnabledInfo.description - }, - { - LabelsDisableCullingEnabledInfo.identifier, - new BoolVerifier, - Optional::Yes, - LabelsDisableCullingEnabledInfo.description - }, - { - LabelsDistanceEPSInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LabelsDistanceEPSInfo.description - }, - { - LabelAlignmentOptionInfo.identifier, - new StringVerifier, - Optional::Yes, - LabelAlignmentOptionInfo.description - }, - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "globebrowsing_globelabelscomponent"; + return doc; } GlobeLabelsComponent::GlobeLabelsComponent() @@ -293,7 +251,7 @@ GlobeLabelsComponent::GlobeLabelsComponent() , _labelsFadeInEnabled(LabelsFadeInEnabledInfo, false) , _labelsFadeOutEnabled(LabelsFadeOutEnabledInfo, false) , _labelsDisableCullingEnabled(LabelsDisableCullingEnabledInfo, false) - , _labelsDistaneEPS(LabelsDistanceEPSInfo, 100000.f, 1000.f, 10000000.f) + , _labelsDistanceEPS(LabelsDistanceEPSInfo, 100000.f, 1000.f, 10000000.f) , _labelAlignmentOption( LabelAlignmentOptionInfo, properties::OptionProperty::DisplayType::Dropdown @@ -312,7 +270,7 @@ GlobeLabelsComponent::GlobeLabelsComponent() addProperty(_labelsFadeInEnabled); addProperty(_labelsFadeOutEnabled); addProperty(_labelsDisableCullingEnabled); - addProperty(_labelsDistaneEPS); + addProperty(_labelsDistanceEPS); _labelAlignmentOption.addOption(Horizontally, "Horizontally"); _labelAlignmentOption.addOption(Circularly, "Circularly"); @@ -324,125 +282,45 @@ void GlobeLabelsComponent::initialize(const ghoul::Dictionary& dictionary, globebrowsing::RenderableGlobe* globe) { ZoneScoped - - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "GlobeLabelsComponent" - ); - _globe = globe; - // Reads labels' file and build cache file if necessary - if (dictionary.isEmpty()) { - return; - } - if (!dictionary.hasValue(KeyLabelsFileName)) { + const Parameters p = codegen::bake(dictionary); + if (!p.fileName.has_value()) { return; } - std::string labelsFile = dictionary.value(KeyLabelsFileName); - bool loadSuccess = loadLabelsData(absPath(labelsFile)); + const bool loadSuccess = loadLabelsData(absPath(p.fileName->string())); if (!loadSuccess) { return; } - if (dictionary.hasKey(LabelsEnableInfo.identifier)) { - // In case of the label's dic is present but is disabled - _labelsEnabled = dictionary.value(LabelsEnableInfo.identifier); - } - else { - // Is the labels dic is enable in the configuration file, - // enables the label automatically. - _labelsEnabled = true; - } - if (dictionary.hasKey(LabelsFontSizeInfo.identifier)) { - _labelsFontSize = static_cast( - dictionary.value(LabelsFontSizeInfo.identifier) - ); - _labelsFontSize.onChange([this]() { initializeFonts(); }); - } + _labelsEnabled = p.enable.value_or(true); + _labelsFontSize = p.labelsFontSize.value_or(_labelsFontSize); + _labelsFontSize.onChange([this]() { initializeFonts(); }); + _labelsSize = p.labelsSize.value_or(_labelsSize); + _labelsMinHeight = p.labelsMinHeight.value_or(_labelsMinHeight); + _labelsColor = p.labelsColor.value_or(_labelsColor); + _labelsOpacity = p.labelsOpacity.value_or(_labelsOpacity); + _labelsFadeInEnabled = p.labelsFadeInEnabled.value_or(_labelsFadeInEnabled); + _labelsFadeInDist = p.fadeInStartingDistance.value_or(_labelsFadeInDist); + _labelsFadeOutEnabled = p.labelsFadeOutEnabled.value_or(_labelsFadeOutEnabled); + _labelsFadeOutDist = p.fadeOutStartingDistance.value_or(_labelsFadeOutDist); + _labelsMinSize = p.labelsMinSize.value_or(_labelsMinSize); + _labelsMaxSize = p.labelsMaxSize.value_or(_labelsMaxSize); + _labelsDisableCullingEnabled = + p.labelsDisableCullingEnabled.value_or(_labelsDisableCullingEnabled); + _labelsDistanceEPS = p.labelsDistanceEPS.value_or(_labelsDistanceEPS); - if (dictionary.hasKey(LabelsSizeInfo.identifier)) { - _labelsSize = static_cast( - dictionary.value(LabelsSizeInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelsMinHeightInfo.identifier)) { - _labelsMinHeight = static_cast( - dictionary.value(LabelsMinHeightInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelsColorInfo.identifier)) { - _labelsColor = dictionary.value(LabelsColorInfo.identifier); - } - - if (dictionary.hasKey(LabelsOpacityInfo.identifier)) { - _labelsOpacity = static_cast( - dictionary.value(LabelsOpacityInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelsFadeInEnabledInfo.identifier)) { - _labelsFadeInEnabled = dictionary.value(LabelsFadeInEnabledInfo.identifier); - } - - if (dictionary.hasKey(LabelsFadeInStartingDistanceInfo.identifier)) { - _labelsFadeInDist = static_cast( - dictionary.value(LabelsFadeInStartingDistanceInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelsFadeOutEnabledInfo.identifier)) { - _labelsFadeOutEnabled = dictionary.value( - LabelsFadeOutEnabledInfo.identifier - ); - } - - if (dictionary.hasKey(LabelsFadeOutStartingDistanceInfo.identifier)) { - _labelsFadeOutDist = static_cast( - dictionary.value(LabelsFadeOutStartingDistanceInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelsMinSizeInfo.identifier)) { - _labelsMinSize = static_cast( - dictionary.value(LabelsMinSizeInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelsMaxSizeInfo.identifier)) { - _labelsMaxSize = static_cast( - dictionary.value(LabelsMaxSizeInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelsDisableCullingEnabledInfo.identifier)) { - bool disabled = dictionary.value( - LabelsDisableCullingEnabledInfo.identifier - ); - _labelsDisableCullingEnabled = disabled; - } - - if (dictionary.hasKey(LabelsDistanceEPSInfo.identifier)) { - _labelsDistaneEPS = static_cast( - dictionary.value(LabelsDistanceEPSInfo.identifier) - ); - } - - if (dictionary.hasKey(LabelAlignmentOptionInfo.identifier)) { - std::string alignment = - dictionary.value(LabelAlignmentOptionInfo.identifier); - if (alignment == "Horizontally") { - _labelAlignmentOption = Horizontally; - } - else if (alignment == "Circularly" ) { - _labelAlignmentOption = Circularly; - } - else { - LERROR("Unknown alignment option: " + alignment); + if (p.labelAlignmentOption.has_value()) { + switch (*p.labelAlignmentOption) { + case Parameters::Alignment::Horizontally: + _labelAlignmentOption = Horizontally; + break; + case Parameters::Alignment::Circularly: + _labelAlignmentOption = Circularly; + break; + default: + throw ghoul::MissingCaseException(); } } @@ -732,7 +610,7 @@ void GlobeLabelsComponent::renderLabels(const RenderData& data, glm::length(locationPositionWorld - data.camera.positionVec3()); if (_labelsDisableCullingEnabled || - ((distToCamera > (distanceCameraToLabelWorld + _labelsDistaneEPS)) && + ((distToCamera > (distanceCameraToLabelWorld + _labelsDistanceEPS)) && isLabelInFrustum(VP, locationPositionWorld))) { if (_labelAlignmentOption == Circularly) { diff --git a/modules/globebrowsing/src/globelabelscomponent.h b/modules/globebrowsing/src/globelabelscomponent.h index 9c7bc4eb6f..ce56c80b0d 100644 --- a/modules/globebrowsing/src/globelabelscomponent.h +++ b/modules/globebrowsing/src/globelabelscomponent.h @@ -96,7 +96,7 @@ private: properties::BoolProperty _labelsFadeInEnabled; properties::BoolProperty _labelsFadeOutEnabled; properties::BoolProperty _labelsDisableCullingEnabled; - properties::FloatProperty _labelsDistaneEPS; + properties::FloatProperty _labelsDistanceEPS; properties::OptionProperty _labelAlignmentOption; private: diff --git a/modules/globebrowsing/src/globetranslation.cpp b/modules/globebrowsing/src/globetranslation.cpp index 633198ba48..6056b81a52 100644 --- a/modules/globebrowsing/src/globetranslation.cpp +++ b/modules/globebrowsing/src/globetranslation.cpp @@ -74,51 +74,33 @@ namespace { "as an offset from the heightmap. Otherwise, it will be an offset from the " "globe's reference ellipsoid. The default value is 'false'." }; + + struct [[codegen::Dictionary(GlobeTranslation)]] Parameters { + // [[codegen::verbatim(GlobeInfo.description)]] + std::string globe + [[codegen::annotation("A valid scene graph node with a RenderableGlobe")]]; + + // [[codegen::verbatim(LongitudeInfo.description)]] + std::optional longitude; + + // [[codegen::verbatim(LatitudeInfo.description)]] + std::optional latitude; + + // [[codegen::verbatim(AltitudeInfo.description)]] + std::optional altitude; + + // [[codegen::verbatim(UseHeightmapInfo.description)]] + std::optional useHeightmap; + }; +#include "globetranslation_codegen.cpp" } // namespace namespace openspace::globebrowsing { documentation::Documentation GlobeTranslation::Documentation() { - using namespace openspace::documentation; - - return { - "Globe Translation", - "space_translation_globetranslation", - { - { - GlobeInfo.identifier, - new StringAnnotationVerifier( - "A valid scene graph node with a RenderableGlobe" - ), - Optional::No, - GlobeInfo.description - }, - { - LongitudeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LongitudeInfo.description - }, - { - LatitudeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LatitudeInfo.description, - }, - { - AltitudeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - AltitudeInfo.description - }, - { - UseHeightmapInfo.identifier, - new BoolVerifier, - Optional::Yes, - UseHeightmapInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "space_translation_globetranslation"; + return doc; } GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary) @@ -128,39 +110,28 @@ GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary) , _altitude(AltitudeInfo, 0.0, 0.0, 1e12) , _useHeightmap(UseHeightmapInfo, false) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "GlobeTranslation" - ); - - _globe = dictionary.value(GlobeInfo.identifier); - if (dictionary.hasKey(LongitudeInfo.identifier)) { - _longitude = dictionary.value(LongitudeInfo.identifier); - } - if (dictionary.hasKey(LatitudeInfo.identifier)) { - _latitude = dictionary.value(LatitudeInfo.identifier); - } - if (dictionary.hasKey(AltitudeInfo.identifier)) { - _altitude = dictionary.value(AltitudeInfo.identifier); - } - if (dictionary.hasKey(UseHeightmapInfo.identifier)) { - _useHeightmap = dictionary.value(UseHeightmapInfo.identifier); - } + const Parameters p = codegen::bake(dictionary); + _globe = p.globe; _globe.onChange([this]() { fillAttachedNode(); _positionIsDirty = true; }); + _longitude = p.longitude.value_or(_longitude); _longitude.onChange([this]() { _positionIsDirty = true; }); - _latitude.onChange([this]() { _positionIsDirty = true; }); - _altitude.onChange([this]() { _positionIsDirty = true; }); - _useHeightmap.onChange([this]() { _positionIsDirty = true; }); - addProperty(_longitude); + + _latitude = p.latitude.value_or(_latitude); + _latitude.onChange([this]() { _positionIsDirty = true; }); addProperty(_latitude); + + _altitude = p.altitude.value_or(_altitude); + _altitude.onChange([this]() { _positionIsDirty = true; }); addProperty(_altitude); + + _useHeightmap = p.useHeightmap.value_or(_useHeightmap); + _useHeightmap.onChange([this]() { _positionIsDirty = true; }); addProperty(_useHeightmap); } diff --git a/modules/globebrowsing/src/layeradjustment.cpp b/modules/globebrowsing/src/layeradjustment.cpp index a572fab6d2..512fdaf88d 100644 --- a/modules/globebrowsing/src/layeradjustment.cpp +++ b/modules/globebrowsing/src/layeradjustment.cpp @@ -26,12 +26,9 @@ #include #include +#include namespace { - constexpr const char* KeyType = "Type"; - constexpr const char* KeyChromaKeyColor = "ChromaKeyColor"; - constexpr const char* KeyChromaKeyTolerance = "ChromaKeyTolerance"; - constexpr openspace::properties::Property::PropertyInfo ChromaKeyColorInfo = { "ChromaKeyColor", "Chroma Key Color", @@ -50,37 +47,32 @@ namespace { "Type", "The type of layer adjustment that is applied to the underlying layer." }; + + struct [[codegen::Dictionary(LayerAdjustment)]] Parameters { + enum class Type { + None, + ChromaKey, + TransferFunction + }; + // Specifies the type of the adjustment that is applied + std::optional type; + + // Specifies the chroma key used when selecting 'ChromaKey' for the 'Type' + std::optional chromaKeyColor [[codegen::color()]]; + + // Specifies the tolerance to match the color to the chroma key when the + // 'ChromaKey' type is selected for the 'Type' + std::optional chromaKeyTolerance; + }; +#include "layeradjustment_codegen.cpp" } // namespace namespace openspace::globebrowsing { documentation::Documentation LayerAdjustment::Documentation() { - using namespace documentation; - return { - "LayerAdjustment", - "globebrowsing_layeradjustment", - { - { - KeyType, - new StringInListVerifier({ "None", "ChromaKey", "TransferFunction" }), - Optional::Yes, - "Specifies the type of the adjustment that is applied" - }, - { - KeyChromaKeyColor, - new Color3Verifier, - Optional::Yes, - "Specifies the chroma key used when selecting 'ChromaKey' for the 'Type'." - }, - { - KeyChromaKeyTolerance, - new DoubleVerifier, - Optional::Yes, - "Specifies the tolerance to match the color to the chroma key when the " - "'ChromaKey' type is selected for the 'Type'." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "globebrowsing_layeradjustment"; + return doc; } LayerAdjustment::LayerAdjustment() @@ -121,31 +113,27 @@ LayerAdjustment::LayerAdjustment() } void LayerAdjustment::setValuesFromDictionary(const ghoul::Dictionary& adjustmentDict) { - documentation::testSpecificationAndThrow( - Documentation(), - adjustmentDict, - "LayerAdjustment" - ); + const Parameters p = codegen::bake(adjustmentDict); - if (adjustmentDict.hasValue(KeyType)) { - std::string dictType = adjustmentDict.value(KeyType); - _typeOption = static_cast( - ghoul::from_string(dictType) - ); + if (p.type.has_value()) { + switch (*p.type) { + case Parameters::Type::None: + _typeOption = static_cast(layergroupid::AdjustmentTypeID::None); + break; + case Parameters::Type::ChromaKey: + _typeOption = static_cast(layergroupid::AdjustmentTypeID::ChromaKey); + break; + case Parameters::Type::TransferFunction: + _typeOption = + static_cast(layergroupid::AdjustmentTypeID::TransferFunction); + break; + default: + throw ghoul::MissingCaseException(); + } } - if (adjustmentDict.hasValue(KeyChromaKeyColor)) { - glm::vec3 dictChromaKeyColor = - adjustmentDict.value(KeyChromaKeyColor); - _chromaKeyColor = std::move(dictChromaKeyColor); - } - - if (adjustmentDict.hasValue(KeyChromaKeyTolerance)) { - float dictChromaKeyTolerance = static_cast( - adjustmentDict.value(KeyChromaKeyTolerance) - ); - _chromaKeyTolerance = dictChromaKeyTolerance; - } + _chromaKeyColor = p.chromaKeyColor.value_or(_chromaKeyColor); + _chromaKeyTolerance = p.chromaKeyTolerance.value_or(_chromaKeyTolerance); } layergroupid::AdjustmentTypeID LayerAdjustment::type() const { diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index d2581bafdb..5306c49d32 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -79,11 +79,6 @@ namespace { bool isShadowing = false; }; - constexpr const char* KeyRadii = "Radii"; - constexpr const char* KeyLayers = "Layers"; - constexpr const char* KeyShadowGroup = "ShadowGroup"; - constexpr const char* KeyLabels = "Labels"; - const openspace::globebrowsing::AABB3 CullingFrustum{ glm::vec3(-1.f, -1.f, 0.f), glm::vec3( 1.f, 1.f, 1e35) @@ -232,6 +227,47 @@ namespace { "This is the number of currently active layers, if this value reaches the " "maximum, bad things will happen." }; + + struct [[codegen::Dictionary(RenderableGlobe)]] Parameters { + // Specifies the radii for this planet. If the Double version of this is used, all + // three radii are assumed to be equal + std::optional> radii; + + // Specifies whether the planet should be shaded by the primary light source or + // not. If it is disabled, all parts of the planet are illuminated + std::optional performShading; + + // A list of all the layers that should be added + std::map layers + [[codegen::reference("globebrowsing_layermanager")]]; + + // Specifies information about planetary labels that can be rendered on the + // object's surface + std::optional labels + [[codegen::reference("globebrowsing_globelabelscomponent")]]; + + struct ShadowGroup { + struct Source { + std::string name; + double radius; + }; + std::vector sources; + + struct Caster { + std::string name; + double radius; + }; + std::vector casters; + }; + std::optional shadowGroup; + + std::optional rings + [[codegen::reference("globebrowsing_rings_component")]]; + + std::optional shadows + [[codegen::reference("globebrowsing_shadows_component")]]; + }; +#include "renderableglobe_codegen.cpp" } // namespace using namespace openspace::properties; @@ -461,48 +497,9 @@ Chunk::Chunk(const TileIndex& ti) {} documentation::Documentation RenderableGlobe::Documentation() { - using namespace documentation; - return { - "RenderableGlobe", - "globebrowsing_renderableglobe", - { - { - KeyRadii, - new OrVerifier({ new DoubleVector3Verifier, new DoubleVerifier }), - Optional::Yes, - "Specifies the radii for this planet. If the Double version of this is " - "used, all three radii are assumed to be equal." - }, - { - "PerformShading", - new BoolVerifier, - Optional::Yes, - "Specifies whether the planet should be shaded by the primary light " - "source or not. If it is disabled, all parts of the planet are " - "illuminated." - }, - { - KeyLayers, - new TableVerifier({ - { - "*", - new ReferencingVerifier("globebrowsing_layermanager"), - Optional::Yes, - "Descriptions of the individual layer groups" - } - }), - Optional::Yes, - "A list of all the layers that should be added" - }, - { - KeyLabels, - new ReferencingVerifier("globebrowsing_globelabelscomponent"), - Optional::Yes, - "Specifies information about planetary labels that can be rendered on " - "the object's surface." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "globebrowsing_renderableglobe"; + return doc; } RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) @@ -539,75 +536,52 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) , _ringsComponent(dictionary) , _shadowComponent(dictionary) { + const Parameters p = codegen::bake(dictionary); + _generalProperties.currentLodScaleFactor.setReadOnly(true); // Read the radii in to its own dictionary - if (dictionary.hasValue(KeyRadii)) { - _ellipsoid = Ellipsoid(dictionary.value(KeyRadii)); - setBoundingSphere(static_cast(_ellipsoid.maximumRadius())); - } - else if (dictionary.hasValue(KeyRadii)) { - const double radius = dictionary.value(KeyRadii); - _ellipsoid = Ellipsoid({ radius, radius, radius }); - setBoundingSphere(static_cast(_ellipsoid.maximumRadius())); + if (p.radii.has_value()) { + if (std::holds_alternative(*p.radii)) { + _ellipsoid = Ellipsoid(std::get(*p.radii)); + setBoundingSphere(static_cast(_ellipsoid.maximumRadius())); + } + else if (std::holds_alternative(*p.radii)) { + const double radius = std::get(*p.radii); + _ellipsoid = Ellipsoid({ radius, radius, radius }); + setBoundingSphere(static_cast(_ellipsoid.maximumRadius())); + } + else { + throw ghoul::MissingCaseException(); + } } - if (dictionary.hasKey("PerformShading")) { - _generalProperties.performShading = dictionary.value("PerformShading"); - } + _generalProperties.performShading = + p.performShading.value_or(_generalProperties.performShading); + // Init layer manager - ghoul::Dictionary layersDictionary; - if (!dictionary.hasValue(KeyLayers)) { - throw ghoul::RuntimeError(std::string(KeyLayers) + " must be specified"); - } - layersDictionary = dictionary.value(KeyLayers); - + // @TODO (abock, 2021-03-25) The layermanager should be changed to take a + // std::map instead and then we don't need to get it + // as a bare dictionary anymore and can use the value from the struct directly + ghoul::Dictionary layersDictionary = dictionary.value("Layers"); _layerManager.initialize(layersDictionary); addProperty(_generalProperties.performShading); addProperty(_generalProperties.useAccurateNormals); - // ================================================================ - // ======== Reads Shadow (Eclipses) Entries in asset file ========= - // ================================================================ - if (dictionary.hasValue(KeyShadowGroup)) { - ghoul::Dictionary shadowDictionary = - dictionary.value(KeyShadowGroup); - - std::vector> sourceArray; - ghoul::Dictionary sources = shadowDictionary.value("Sources"); - for (std::string_view k : sources.keys()) { - ghoul::Dictionary source = sources.value(k); - - std::string name = source.value("Name"); - double radius = source.value("Radius"); - sourceArray.emplace_back(name, radius); - } - - std::vector> casterArray; - ghoul::Dictionary casters = shadowDictionary.value("Casters"); - for (std::string_view k : casters.keys()) { - ghoul::Dictionary caster = casters.value(k); - - std::string name = caster.value("Name"); - double radius = caster.value("Radius"); - casterArray.emplace_back(name, radius); - } - + if (p.shadowGroup.has_value()) { std::vector shadowConfArray; - for (const std::pair& source : sourceArray) { - for (const std::pair& caster : casterArray) { + for (const Parameters::ShadowGroup::Source& source : p.shadowGroup->sources) { + for (const Parameters::ShadowGroup::Caster& caster : p.shadowGroup->casters) { Ellipsoid::ShadowConfiguration sc; - sc.source = source; - sc.caster = caster; + sc.source = std::pair(source.name, source.radius); + sc.caster = std::pair(source.name, source.radius); shadowConfArray.push_back(sc); } } _ellipsoid.setShadowConfigurationArray(shadowConfArray); - } - if (!_ellipsoid.shadowConfigurationArray().empty()) { addProperty(_generalProperties.eclipseShadowsEnabled); addProperty(_generalProperties.eclipseHardShadows); } @@ -667,22 +641,20 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) _localChunkBuffer.resize(2048); _traversalMemory.resize(512); - // Labels Dictionary - if (dictionary.hasValue(KeyLabels)) { - _labelsDictionary = dictionary.value(KeyLabels); - } + _labelsDictionary = p.labels.value_or(_labelsDictionary); + // Components - if (dictionary.hasValue("Rings")) { + _hasRings = p.rings.has_value(); + if (_hasRings) { _ringsComponent.initialize(); addPropertySubOwner(_ringsComponent); - _hasRings = true; } - if (dictionary.hasKey("Shadows")) { + _hasShadows = p.shadows.has_value(); + if (_hasShadows) { _shadowComponent.initialize(); addPropertySubOwner(_shadowComponent); - _hasShadows = true; _generalProperties.shadowMapping = true; } _generalProperties.shadowMapping.onChange(notifyShaderRecompilation); diff --git a/modules/globebrowsing/src/ringscomponent.cpp b/modules/globebrowsing/src/ringscomponent.cpp index 144a058468..6f0855196e 100644 --- a/modules/globebrowsing/src/ringscomponent.cpp +++ b/modules/globebrowsing/src/ringscomponent.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -155,90 +156,53 @@ namespace { "The number of samples used during shadow mapping calculation " "(Percentage Closer Filtering)." }; + + struct [[codegen::Dictionary(RingsComponent)]] Parameters { + // [[codegen::verbatim(TextureInfo.description)]] + std::optional texture; + + // [[codegen::verbatim(TextureFwrdInfo.description)]] + std::optional textureFwrd; + + // [[codegen::verbatim(TextureBckwrdInfo.description)]] + std::optional textureBckwrd; + + // [[codegen::verbatim(TextureUnlitInfo.description)]] + std::optional textureUnlit; + + // [[codegen::verbatim(TextureColorInfo.description)]] + std::optional textureColor; + + // [[codegen::verbatim(TextureTransparencyInfo.description)]] + std::optional textureTransparency; + + // [[codegen::verbatim(SizeInfo.description)]] + std::optional size; + + // [[codegen::verbatim(OffsetInfo.description)]] + std::optional offset; + + // [[codegen::verbatim(NightFactorInfo.description)]] + std::optional nightFactor; + + // [[codegen::verbatim(ColorFilterInfo.description)]] + std::optional colorFilter; + + // [[codegen::verbatim(ZFightingPercentageInfo.description)]] + std::optional zFightingPercentage; + + // [[codegen::verbatim(NumberShadowSamplesInfo.description)]] + std::optional numberShadowSamples; + }; +#include "ringscomponent_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RingsComponent::Documentation() { - using namespace documentation; - return { - "Rings Component", - "globebrowsing_rings_component", - { - { - TextureInfo.identifier, - new StringVerifier, - Optional::Yes, - TextureInfo.description - }, - { - TextureFwrdInfo.identifier, - new StringVerifier, - Optional::Yes, - TextureFwrdInfo.description - }, - { - TextureBckwrdInfo.identifier, - new StringVerifier, - Optional::Yes, - TextureBckwrdInfo.description - }, - { - TextureUnlitInfo.identifier, - new StringVerifier, - Optional::Yes, - TextureUnlitInfo.description - }, - { - TextureColorInfo.identifier, - new StringVerifier, - Optional::Yes, - TextureColorInfo.description - }, - { - TextureTransparencyInfo.identifier, - new StringVerifier, - Optional::Yes, - TextureTransparencyInfo.description - }, - { - SizeInfo.identifier, - new DoubleVerifier, - Optional::Yes, - SizeInfo.description - }, - { - OffsetInfo.identifier, - new DoubleVector2Verifier, - Optional::Yes, - OffsetInfo.description - }, - { - NightFactorInfo.identifier, - new DoubleVerifier, - Optional::Yes, - NightFactorInfo.description - }, - { - ColorFilterInfo.identifier, - new DoubleVerifier, - Optional::Yes, - ColorFilterInfo.description - }, - { - ZFightingPercentageInfo.identifier, - new DoubleVerifier, - Optional::Yes, - ZFightingPercentageInfo.description - }, - { - NumberShadowSamplesInfo.identifier, - new IntVerifier, - Optional::Yes, - NumberShadowSamplesInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "globebrowsing_rings_component"; + return doc; } RingsComponent::RingsComponent(const ghoul::Dictionary& dictionary) @@ -265,14 +229,17 @@ RingsComponent::RingsComponent(const ghoul::Dictionary& dictionary) // term and rather extract the values directly here. This would require a bit of // a rewrite in the RenderableGlobe class to not create the RingsComponent in the // class-initializer list though + // @TODO (abock, 2021-03-25) Righto! The RenderableGlobe passes this dictionary + // in as-is so it would be easy to just pass it directly to the initialize method + // instead _ringsDictionary = dictionary.value("Rings"); - } - documentation::testSpecificationAndThrow( - Documentation(), - _ringsDictionary, - "RingsComponent" - ); + documentation::testSpecificationAndThrow( + Documentation(), + _ringsDictionary, + "RingsComponent" + ); + } } void RingsComponent::initialize() { @@ -280,106 +247,78 @@ void RingsComponent::initialize() { using ghoul::filesystem::File; + const Parameters p = codegen::bake(_ringsDictionary); + addProperty(_enabled); - _size = static_cast(_ringsDictionary.value(SizeInfo.identifier)); - //setBoundingSphere(_size); + _size = p.size.value_or(_size); _size.onChange([&]() { _planeIsDirty = true; }); addProperty(_size); - if (_ringsDictionary.hasKey(TextureInfo.identifier)) { - _texturePath = absPath( - _ringsDictionary.value(TextureInfo.identifier) - ); + if (p.texture.has_value()) { + _texturePath = absPath(p.texture->string()); _textureFile = std::make_unique(_texturePath); _texturePath.onChange([&]() { loadTexture(); }); addProperty(_texturePath); _textureFile->setCallback([&](const File&) { _textureIsDirty = true; }); } - - if (_ringsDictionary.hasKey(TextureFwrdInfo.identifier)) { - _textureFwrdPath = absPath( - _ringsDictionary.value(TextureFwrdInfo.identifier) - ); + + if (p.textureFwrd.has_value()) { + _textureFwrdPath = absPath(p.textureFwrd->string()); _textureFileForwards = std::make_unique(_textureFwrdPath); _textureFwrdPath.onChange([&]() { loadTexture(); }); addProperty(_textureFwrdPath); _textureFileForwards->setCallback([&](const File&) { _textureIsDirty = true; }); } - if (_ringsDictionary.hasKey(TextureBckwrdInfo.identifier)) { - _textureBckwrdPath = absPath( - _ringsDictionary.value(TextureBckwrdInfo.identifier) - ); + if (p.textureBckwrd.has_value()) { + _textureBckwrdPath = absPath(p.textureBckwrd->string()); _textureFileBackwards = std::make_unique(_textureBckwrdPath); _textureBckwrdPath.onChange([&]() { loadTexture(); }); addProperty(_textureBckwrdPath); _textureFileBackwards->setCallback([&](const File&) { _textureIsDirty = true; }); } - if (_ringsDictionary.hasKey(TextureUnlitInfo.identifier)) { - _textureUnlitPath = absPath( - _ringsDictionary.value(TextureUnlitInfo.identifier) - ); + if (p.textureUnlit.has_value()) { + _textureUnlitPath = absPath(p.textureUnlit->string()); _textureFileUnlit = std::make_unique(_textureUnlitPath); _textureUnlitPath.onChange([&]() { loadTexture(); }); addProperty(_textureUnlitPath); _textureFileUnlit->setCallback([&](const File&) { _textureIsDirty = true; }); } - if (_ringsDictionary.hasKey(TextureColorInfo.identifier)) { - _textureColorPath = absPath( - _ringsDictionary.value(TextureColorInfo.identifier) - ); + if (p.textureColor.has_value()) { + _textureColorPath = absPath(p.textureColor->string()); _textureFileColor = std::make_unique(_textureColorPath); _textureColorPath.onChange([&]() { loadTexture(); }); addProperty(_textureColorPath); _textureFileColor->setCallback([&](const File&) { _textureIsDirty = true; }); } - if (_ringsDictionary.hasKey(TextureTransparencyInfo.identifier)) { - _textureTransparencyPath = absPath( - _ringsDictionary.value(TextureTransparencyInfo.identifier) - ); + if (p.textureTransparency.has_value()) { + _textureTransparencyPath = absPath(p.textureTransparency->string()); _textureFileTransparency = std::make_unique(_textureTransparencyPath); _textureTransparencyPath.onChange([&]() { loadTexture(); }); addProperty(_textureTransparencyPath); _textureFileTransparency->setCallback([&](const File&) { _textureIsDirty = true; }); } - if (_ringsDictionary.hasValue(OffsetInfo.identifier)) { - _offset = _ringsDictionary.value(OffsetInfo.identifier); - } + _offset = p.offset.value_or(_offset); addProperty(_offset); - if (_ringsDictionary.hasValue(NightFactorInfo.identifier)) { - _nightFactor = static_cast( - _ringsDictionary.value(NightFactorInfo.identifier) - ); - } + _nightFactor = p.nightFactor.value_or(_nightFactor); addProperty(_nightFactor); - if (_ringsDictionary.hasValue(ColorFilterInfo.identifier)) { - _colorFilter = static_cast( - _ringsDictionary.value(ColorFilterInfo.identifier) - ); - } + _colorFilter = p.colorFilter.value_or(_colorFilter); + addProperty(_colorFilter); // Shadow Mapping Quality Controls - if (_ringsDictionary.hasKey(ZFightingPercentageInfo.identifier)) { - _zFightingPercentage = static_cast( - _ringsDictionary.value(ZFightingPercentageInfo.identifier) - ); - } + _zFightingPercentage = p.zFightingPercentage.value_or(_zFightingPercentage); addProperty(_zFightingPercentage); - if (_ringsDictionary.hasKey(NumberShadowSamplesInfo.identifier)) { - _nShadowSamples = _ringsDictionary.value(NumberShadowSamplesInfo.identifier); - } + _nShadowSamples = p.numberShadowSamples.value_or(_nShadowSamples); _nShadowSamples.onChange([this]() { compileShadowShader(); }); addProperty(_nShadowSamples); - - addProperty(_colorFilter); } bool RingsComponent::isReady() const { diff --git a/modules/globebrowsing/src/shadowcomponent.cpp b/modules/globebrowsing/src/shadowcomponent.cpp index 0655da2bde..55a5285480 100644 --- a/modules/globebrowsing/src/shadowcomponent.cpp +++ b/modules/globebrowsing/src/shadowcomponent.cpp @@ -141,30 +141,23 @@ namespace { } } } + + struct [[codegen::Dictionary(ShadowComponent)]] Parameters { + // [[codegen::verbatim(DistanceFractionInfo.description)]] + std::optional distanceFraction; + + // [[codegen::verbatim(DepthMapSizeInfo.description)]] + std::optional depthMapSize; + }; +#include "shadowcomponent_codegen.cpp" } // namespace namespace openspace { documentation::Documentation ShadowComponent::Documentation() { - using namespace documentation; - return { - "ShadowsRing Component", - "globebrowsing_shadows_component", - { - { - DistanceFractionInfo.identifier, - new DoubleVerifier, - Optional::Yes, - DistanceFractionInfo.description - }, - { - DepthMapSizeInfo.identifier, - new Vector2ListVerifier, - Optional::Yes, - DepthMapSizeInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "globebrowsing_shadows_component"; + return doc; } ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary) @@ -172,37 +165,30 @@ ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary) , _saveDepthTexture(SaveDepthTextureInfo) , _distanceFraction(DistanceFractionInfo, 20, 1, 10000) , _enabled({ "Enabled", "Enabled", "Enable/Disable Shadows" }, true) - , _shadowMapDictionary(dictionary) { using ghoul::filesystem::File; - if (dictionary.hasValue("Shadows")) { - // @TODO (abock, 2019-12-16) It would be better to not store the dictionary long - // term and rather extract the values directly here. This would require a bit of - // a rewrite in the RenderableGlobe class to not create the ShadowComponent in the - // class-initializer list though - _shadowMapDictionary = dictionary.value("Shadows"); + // @TODO (abock, 2021-03-25) This is not really a nice solution as this key name is + // coded into the RenderableGlobe. Instead, the parent should unpack the dictionary + // and pass the unpacked dictionary in here; Or maybe we don't want a dictionary at + // this state anyway? + if (!dictionary.hasValue("Shadows")) { + return; } + ghoul::Dictionary d = dictionary.value("Shadows"); + + const Parameters p = codegen::bake(d); - documentation::testSpecificationAndThrow( - Documentation(), - _shadowMapDictionary, - "ShadowComponent" - ); + addProperty(_enabled); + + _distanceFraction = p.distanceFraction.value_or(_distanceFraction); + addProperty(_distanceFraction); - if (_shadowMapDictionary.hasKey(DistanceFractionInfo.identifier)) { - _distanceFraction = static_cast( - _shadowMapDictionary.value(DistanceFractionInfo.identifier) - ); - } _saveDepthTexture.onChange([&]() { _executeDepthTextureSave = true; }); - - if (_shadowMapDictionary.hasKey(DepthMapSizeInfo.identifier)) { - glm::dvec2 depthMapSize = - _shadowMapDictionary.value(DepthMapSizeInfo.identifier); - _shadowDepthTextureWidth = static_cast(depthMapSize.x); - _shadowDepthTextureHeight = static_cast(depthMapSize.y); + if (p.depthMapSize.has_value()) { + _shadowDepthTextureWidth = p.depthMapSize->x; + _shadowDepthTextureHeight = p.depthMapSize->y; _dynamicDepthTextureRes = false; } else { @@ -212,13 +198,7 @@ ShadowComponent::ShadowComponent(const ghoul::Dictionary& dictionary) _dynamicDepthTextureRes = true; } - _saveDepthTexture.onChange([&]() { _executeDepthTextureSave = true; }); - - _viewDepthMap = false; - - addProperty(_enabled); addProperty(_saveDepthTexture); - addProperty(_distanceFraction); } void ShadowComponent::initialize() { diff --git a/modules/globebrowsing/src/shadowcomponent.h b/modules/globebrowsing/src/shadowcomponent.h index 30aabbe057..1862dfe7eb 100644 --- a/modules/globebrowsing/src/shadowcomponent.h +++ b/modules/globebrowsing/src/shadowcomponent.h @@ -105,8 +105,6 @@ private: properties::IntProperty _distanceFraction; properties::BoolProperty _enabled; - ghoul::Dictionary _shadowMapDictionary; - int _shadowDepthTextureHeight = 4096; int _shadowDepthTextureWidth = 4096; bool _dynamicDepthTextureRes = true; diff --git a/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp b/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp index 363969ed1c..1ed8bfcddd 100644 --- a/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp +++ b/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp @@ -34,19 +34,38 @@ #include #include #include +#include namespace { - constexpr const char* KeySource = "Source"; - constexpr const char* KeyTarget = "Target"; - constexpr const char* KeyInstrument = "Instrument"; - constexpr const char* KeyColor = "Color"; - constexpr const char* KeyColorStart = "Start"; - constexpr const char* KeyColorEnd = "End"; - struct VBOData { float position[3]; float color[4]; }; + + struct [[codegen::Dictionary(RenderableCrawlingLine)]] Parameters { + // Denotes the SPICE name of the source of the renderable crawling line, for + // example, the spacecraft + std::string source; + + // Denotes the SPICE name of the target of the crawling line + std::string target; + + // Denotes the SPICE name of the instrument that is used to render the crawling + // line + std::string instrument; + + struct Color { + // The color at the start of the line + glm::vec4 start [[codegen::color()]]; + + // The color at the end of the line + glm::vec4 end [[codegen::color()]]; + }; + // Specifies the colors that are used for the crawling line. One value determines + // the starting color of the line, the second value is the color at the end + Color color; + }; +#include "renderablecrawlingline_codegen.cpp" } // namespace // @TODO: This class is not properly working anymore and needs to be substantially @@ -56,76 +75,21 @@ namespace { namespace openspace { documentation::Documentation RenderableCrawlingLine::Documentation() { - using namespace documentation; - return { - "RenderableCrawlingLine", - "newhorizons_renderable_crawlingline", - { - { - KeySource, - new StringVerifier, - Optional::No, - "Denotes the SPICE name of the source of the renderable crawling line, " - "for example, the space craft" - }, - { - KeyTarget, - new StringVerifier, - Optional::Yes, - "Denotes the SPICE name of the target of the crawling line" - }, - { - KeyInstrument, - new StringVerifier, - Optional::No, - "Denotes the SPICE name of the instrument that is used to render the " - "crawling line" - }, - { - KeyColor, - new TableVerifier({ - { - KeyColorStart, - new Color4Verifier, - Optional::No, - "The color at the start of the line", - }, - { - KeyColorEnd, - new Color4Verifier, - Optional::No, - "The color at the end of the line" - } - }), - Optional::No, - "Specifies the colors that are used for the crawling line. One value " - "determines the starting color of the line, the second value is the " - "color at the end of the line." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "newhorizons_renderable_crawlingline"; + return doc; } RenderableCrawlingLine::RenderableCrawlingLine(const ghoul::Dictionary& dictionary) : Renderable(dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableCrawlingLine" - ); + const Parameters p = codegen::bake(dictionary); - _source = dictionary.value(KeySource); - _target = dictionary.value(KeyTarget); - _instrumentName = dictionary.value(KeyInstrument); - - _lineColorBegin = dictionary.value( - std::string(KeyColor) + "." + KeyColorStart - ); - - _lineColorEnd = dictionary.value( - std::string(KeyColor) + "." + KeyColorEnd - ); + _source = p.source; + _target = p.target; + _instrumentName = p.instrument; + _lineColorBegin = p.color.start; + _lineColorEnd = p.color.end; } bool RenderableCrawlingLine::isReady() const { diff --git a/modules/spacecraftinstruments/rendering/renderablefov.cpp b/modules/spacecraftinstruments/rendering/renderablefov.cpp index 25111acf83..30e0753aa5 100644 --- a/modules/spacecraftinstruments/rendering/renderablefov.cpp +++ b/modules/spacecraftinstruments/rendering/renderablefov.cpp @@ -35,17 +35,10 @@ #include #include #include +#include namespace { constexpr const char* ProgramName = "FovProgram"; - constexpr const char* KeyBody = "Body"; - constexpr const char* KeyFrame = "Frame"; - constexpr const char* KeyInstrument = "Instrument"; - constexpr const char* KeyInstrumentName = "Name"; - constexpr const char* KeyInstrumentAberration = "Aberration"; - constexpr const char* KeyPotentialTargets = "PotentialTargets"; - constexpr const char* KeyFrameConversions = "FrameConversions"; - constexpr const char* KeyBoundsSimplification = "SimplifyBounds"; constexpr const std::array UniformNames = { "modelViewProjectionTransform", "defaultColorStart", "defaultColorEnd", @@ -153,142 +146,55 @@ namespace { } } // Needs support for std::map first for the frameConversions -// struct [[codegen::Dictionary(RenderableFov)]] Parameters { -// // The SPICE name of the source body for which the field of view should be -// // rendered -// std::string body; -// -// // The SPICE name of the source body's frame in which the field of view should be -// // rendered -// std::string frame; -// -// struct Instrument { -// // The SPICE name of the instrument that is rendered -// std::string name; -// -// // The aberration correction that is used for this field of view. The default -// // is 'NONE' -// std::optional aberration [[codegen::inlist("NONE", -// "LT", "LT+S", "CN", "CN+S", "XLT", "XLT+S", "XCN", "XCN+S")]]; -// }; -// // A table describing the instrument whose field of view should be rendered -// Instrument instrument; -// -// // A list of potential targets (specified as SPICE names) that the field of view -// // should be tested against -// std::vector potentialTargets; -// -// // A list of frame conversions that should be registered with the SpiceManager -// std::optional> frameConversions; -// -// // [[codegen::verbatim(LineWidthInfo.description)]] -// std::optional lineWidth; -// -// // [[codegen::verbatim(StandoffDistanceInfo.description)]] -// std::optional standOffDistance; -// -// // If this value is set to 'true' the field-of-views bounds values will be -// // simplified on load. Bound vectors will be removed if they are the strict linear -// // interpolation between the two neighboring vectors. This value is disabled on -// // default -// std::optional simplifyBounds; -// }; -//#include "renderablefov_codegen.cpp" + struct [[codegen::Dictionary(RenderableFov)]] Parameters { + // The SPICE name of the source body for which the field of view should be + // rendered + std::string body; + + // The SPICE name of the source body's frame in which the field of view should be + // rendered + std::string frame; + + struct Instrument { + // The SPICE name of the instrument that is rendered + std::string name; + + // The aberration correction that is used for this field of view. The default + // is 'NONE' + std::optional aberration [[codegen::inlist("NONE", + "LT", "LT+S", "CN", "CN+S", "XLT", "XLT+S", "XCN", "XCN+S")]]; + }; + // A table describing the instrument whose field of view should be rendered + Instrument instrument; + + // A list of potential targets (specified as SPICE names) that the field of view + // should be tested against + std::vector potentialTargets; + + // A list of frame conversions that should be registered with the SpiceManager + std::optional> frameConversions; + + // [[codegen::verbatim(LineWidthInfo.description)]] + std::optional lineWidth; + + // [[codegen::verbatim(StandoffDistanceInfo.description)]] + std::optional standOffDistance; + + // If this value is set to 'true' the field-of-views bounds values will be + // simplified on load. Bound vectors will be removed if they are the strict linear + // interpolation between the two neighboring vectors. This value is disabled on + // default + std::optional simplifyBounds; + }; +#include "renderablefov_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableFov::Documentation() { - using namespace documentation; - return { - "RenderableFieldOfView", - "newhorizons_renderable_fieldofview", - { - { - KeyBody, - new StringVerifier, - Optional::No, - "The SPICE name of the source body for which the field of view should be " - "rendered." - }, - { - KeyFrame, - new StringVerifier, - Optional::No, - "The SPICE name of the source body's frame in which the field of view " - "should be rendered." - }, - { - KeyInstrument, - new TableVerifier({ - { - KeyInstrumentName, - new StringVerifier, - Optional::No, - "The SPICE name of the instrument that is rendered" - }, - { - KeyInstrumentAberration, - new StringInListVerifier({ - // Taken from SpiceManager::AberrationCorrection - "NONE", - "LT", "LT+S", - "CN", "CN+S", - "XLT", "XLT+S", - "XCN", "XCN+S" - }), - Optional::Yes, - "The aberration correction that is used for this field of view. " - "The default is 'NONE'." - } - }), - Optional::No, - "A table describing the instrument whose field of view should be " - "rendered." - }, - { - KeyPotentialTargets, - new StringListVerifier, - Optional::No, - "A list of potential targets (specified as SPICE names) that the field " - "of view should be tested against." - }, - { - KeyFrameConversions, - new TableVerifier({ - { - DocumentationEntry::Wildcard, - new StringVerifier, - Optional::No - } - }), - Optional::Yes, - "A list of frame conversions that should be registered with the " - "SpiceManager." - }, - { - LineWidthInfo.identifier, - new DoubleVerifier, - Optional::Yes, - LineWidthInfo.description - }, - { - StandoffDistanceInfo.identifier, - new DoubleVerifier, - Optional::Yes, - StandoffDistanceInfo.description - }, - { - KeyBoundsSimplification, - new BoolVerifier, - Optional::Yes, - "If this value is set to 'true' the field-of-views bounds values will be " - "simplified on load. Bound vectors will be removed if they are the " - "strict linear interpolation between the two neighboring vectors. This " - "value is disabled on default." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "newhorizons_renderable_fieldofview"; + return doc; } @@ -342,61 +248,37 @@ RenderableFov::RenderableFov(const ghoul::Dictionary& dictionary) } }) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableFov" - ); + const Parameters p = codegen::bake(dictionary); - _instrument.spacecraft = dictionary.value(KeyBody); - _instrument.referenceFrame = dictionary.value(KeyFrame); - - _instrument.name = dictionary.value( - std::string(KeyInstrument) + "." + KeyInstrumentName - ); - - std::string ia = std::string(KeyInstrument) + "." + KeyInstrumentAberration; - if (dictionary.hasValue(ia)) { - const std::string& ac = dictionary.value(ia); - _instrument.aberrationCorrection = SpiceManager::AberrationCorrection(ac); + _instrument.spacecraft = p.body; + _instrument.referenceFrame = p.frame; + _instrument.name = p.instrument.name; + if (p.instrument.aberration.has_value()) { + _instrument.aberrationCorrection = SpiceManager::AberrationCorrection( + *p.instrument.aberration + ); } - ghoul::Dictionary pt = dictionary.value(KeyPotentialTargets); - _instrument.potentialTargets.reserve(pt.size()); - for (size_t i = 1; i <= pt.size(); ++i) { - std::string target = pt.value(std::to_string(i)); - _instrument.potentialTargets.push_back(std::move(target)); - } + _instrument.potentialTargets = p.potentialTargets; - if (dictionary.hasKey(KeyFrameConversions)) { - ghoul::Dictionary fc = dictionary.value(KeyFrameConversions); - for (std::string_view key : fc.keys()) { + if (p.frameConversions.has_value()) { + for (const std::pair& fc : *p.frameConversions) { global::moduleEngine->module()->addFrame( - std::string(key), - fc.value(key) + fc.first, + fc.second ); } } - if (dictionary.hasKey(LineWidthInfo.identifier)) { - _lineWidth = static_cast(dictionary.value( - LineWidthInfo.identifier - )); - } - - if (dictionary.hasKey(StandoffDistanceInfo.identifier)) { - _standOffDistance = static_cast(dictionary.value( - StandoffDistanceInfo.identifier - )); - } - - if (dictionary.hasKey(KeyBoundsSimplification)) { - _simplifyBounds = dictionary.value(KeyBoundsSimplification); - } - + _lineWidth = p.lineWidth.value_or(_lineWidth); addProperty(_lineWidth); - addProperty(_drawSolid); + + _standOffDistance = p.standOffDistance.value_or(_standOffDistance); addProperty(_standOffDistance); + + _simplifyBounds = p.simplifyBounds.value_or(_simplifyBounds); + + addProperty(_drawSolid); addProperty(_colors.defaultStart); addProperty(_colors.defaultEnd); diff --git a/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp b/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp index a165d84c5d..5f2d224c3b 100644 --- a/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp @@ -279,7 +279,6 @@ void RenderablePlaneProjection::updatePlane(const Image& img, double currentTime glm::vec3(projection[1]), glm::vec3(projection[2]), glm::vec3(projection[3]) - }; const GLfloat vertex_data[] = { // square of two triangles drawn within fov in target coordinates diff --git a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp index 4c2f2479ee..147ce0c659 100644 --- a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp @@ -39,6 +39,7 @@ #include #include #include +#include namespace { constexpr const char* _loggerCat = "RenderablePlanetProjection"; @@ -56,8 +57,6 @@ namespace { "boresight", "_radius", "_segments" }; - constexpr const char* KeyGeometry = "Geometry"; - constexpr const char* KeyProjection = "Projection"; constexpr const char* KeyRadius = "Geometry.Radius"; constexpr const char* _mainFrame = "GALACTIC"; @@ -134,67 +133,42 @@ namespace { "Clear Projection Buffer", "Remove all pending projections from the buffer" }; + + struct [[codegen::Dictionary(RenderablePlanetProjection)]] Parameters { + // The geometry that is used for rendering this planet + ghoul::Dictionary geometry [[codegen::reference("space_geometry_planet")]]; + // Contains information about projecting onto this planet + ghoul::Dictionary projection + [[codegen::reference("newhorizons_projectioncomponent")]]; + + // [[codegen::verbatim(ColorTexturePathsInfo.description)]] + std::vector colorTexturePaths; + + // [[codegen::verbatim(HeightTexturePathsInfo.description)]] + std::vector heightTexturePaths; + + // [[codegen::verbatim(HeightExaggerationInfo.description)]] + std::optional heightExaggeration; + + // [[codegen::verbatim(MeridianShiftInfo.description)]] + std::optional meridianShift; + + // [[codegen::verbatim(AmbientBrightnessInfo.description)]] + std::optional ambientBrightness; + + // [[codegen::verbatim(MaxProjectionsPerFrameInfo.description)]] + std::optional maxProjectionsPerFrame; + }; +#include "renderableplanetprojection_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderablePlanetProjection::Documentation() { - using namespace openspace::documentation; - return { - "Renderable Planet Projection", - "newhorizons_renderable_planetprojection", - { - { - KeyGeometry, - new ReferencingVerifier("space_geometry_planet"), - Optional::No, - "The geometry that is used for rendering this planet.", - }, - { - KeyProjection, - new ReferencingVerifier("newhorizons_projectioncomponent"), - Optional::No, - "Contains information about projecting onto this planet.", - }, - { - ColorTexturePathsInfo.identifier, - new StringListVerifier, - Optional::No, - ColorTexturePathsInfo.description - }, - { - HeightTexturePathsInfo.identifier, - new StringListVerifier, - Optional::Yes, - HeightTexturePathsInfo.description - }, - { - HeightExaggerationInfo.identifier, - new DoubleVerifier, - Optional::Yes, - HeightExaggerationInfo.description - }, - { - MeridianShiftInfo.identifier, - new BoolVerifier, - Optional::Yes, - MeridianShiftInfo.description - }, - { - AmbientBrightnessInfo.identifier, - new DoubleVerifier, - Optional::Yes, - AmbientBrightnessInfo.description - }, - { - MaxProjectionsPerFrameInfo.identifier, - new DoubleVerifier, - Optional::Yes, - MaxProjectionsPerFrameInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "newhorizons_renderable_planetprojection"; + return doc; } RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& dict) @@ -210,43 +184,24 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& , _projectionsInBuffer(ProjectionsInBufferInfo, 0, 1, 32) , _clearProjectionBuffer(ClearProjectionBufferInfo) { - documentation::testSpecificationAndThrow( - Documentation(), - dict, - "RenderablePlanetProjection" - ); + const Parameters p = codegen::bake(dict); - ghoul::Dictionary geometryDictionary = dict.value(KeyGeometry); - _geometry = planetgeometry::PlanetGeometry::createFromDictionary(geometryDictionary); - - _projectionComponent.initialize( - identifier(), - dict.value(KeyProjection) - ); + _geometry = planetgeometry::PlanetGeometry::createFromDictionary(p.geometry); + _projectionComponent.initialize(identifier(), p.projection); _colorTexturePaths.addOption(0, NoImageText); _colorTexturePaths.onChange([this](){ _colorTextureDirty = true; }); addProperty(_colorTexturePaths); - if (dict.hasValue(ColorTexturePathsInfo.identifier)) { - const ghoul::Dictionary& value = dict.value( - ColorTexturePathsInfo.identifier + for (const std::string& t : p.colorTexturePaths) { + _colorTexturePaths.addOption( + static_cast(_colorTexturePaths.options().size()), + t ); - - for (size_t i = 1; i <= value.size(); ++i) { - std::string texture = absPath(value.value(std::to_string(i))); - - _colorTexturePaths.addOption( - // as we started with 0, this works - static_cast(_colorTexturePaths.options().size()), - texture - ); - - _colorTexturePaths = static_cast( - _colorTexturePaths.options().size() - 1 - ); - } } + _colorTexturePaths = static_cast( + _colorTexturePaths.options().size() - 1 + ); _addColorTexturePath.onChange([this]() { if (!_addColorTexturePath.value().empty()) { @@ -271,27 +226,15 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& _heightMapTexturePaths.onChange([this]() { _heightMapTextureDirty = true; }); addProperty(_heightMapTexturePaths); - - if (dict.hasValue(HeightTexturePathsInfo.identifier)) { - const ghoul::Dictionary& value = dict.value( - HeightTexturePathsInfo.identifier + for (const std::string& t : p.heightTexturePaths) { + _heightMapTexturePaths.addOption( + static_cast(_heightMapTexturePaths.options().size()), + t ); - - for (size_t i = 1; i <= value.size(); ++i) { - std::string texture = absPath(value.value(std::to_string(i))); - - _heightMapTexturePaths.addOption( - // as we started with 0, this works - static_cast(_heightMapTexturePaths.options().size()), - texture - ); - - _heightMapTexturePaths = static_cast( - _heightMapTexturePaths.options().size() - 1 - ); - } } - + _heightMapTexturePaths = static_cast( + _heightMapTexturePaths.options().size() - 1 + ); _addHeightMapTexturePath.onChange([this]() { if (!_addHeightMapTexturePath.value().empty()) { _heightMapTexturePaths.addOption( @@ -308,11 +251,12 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& }); addProperty(_addHeightMapTexturePath); + _meridianShift = p.meridianShift.value_or(_meridianShift); + addProperty(_meridianShift); - if (dict.hasValue(MeridianShiftInfo.identifier)) { - _meridianShift = dict.value(MeridianShiftInfo.identifier); - } - + // @TODO (abock, 2021-03-26) Poking into the Geometry dictionary is not really + // optimal as we don't have local control over how the dictionary is checked. We + // should instead ask the geometry whether it has a radius or not double radius = std::pow(10.0, 9.0); if (dict.hasValue(KeyRadius)) { radius = dict.value(KeyRadius); @@ -322,25 +266,16 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& addPropertySubOwner(_geometry.get()); addPropertySubOwner(_projectionComponent); - if (dict.hasKey(HeightExaggerationInfo.identifier)) { - _heightExaggeration = static_cast( - dict.value(HeightExaggerationInfo.identifier) - ); - } - - if (dict.hasKey(MaxProjectionsPerFrameInfo.identifier)) { - _maxProjectionsPerFrame = static_cast( - dict.value(MaxProjectionsPerFrameInfo.identifier) - ); - } - + _heightExaggeration = p.heightExaggeration.value_or(_heightExaggeration); addProperty(_heightExaggeration); - addProperty(_meridianShift); - addProperty(_ambientBrightness); - + + _maxProjectionsPerFrame = p.maxProjectionsPerFrame.value_or(_maxProjectionsPerFrame); addProperty(_maxProjectionsPerFrame); + + addProperty(_ambientBrightness); addProperty(_projectionsInBuffer); + _clearProjectionBuffer.onChange([this]() { _imageTimes.clear(); _projectionsInBuffer = static_cast(_imageTimes.size()); diff --git a/modules/sync/syncs/httpsynchronization.cpp b/modules/sync/syncs/httpsynchronization.cpp index 9a01580bd2..80acc90ddb 100644 --- a/modules/sync/syncs/httpsynchronization.cpp +++ b/modules/sync/syncs/httpsynchronization.cpp @@ -38,39 +38,29 @@ namespace { constexpr const char* _loggerCat = "HttpSynchronization"; - constexpr const char* KeyIdentifier = "Identifier"; - constexpr const char* KeyVersion = "Version"; - constexpr const char* TempSuffix = ".tmp"; constexpr const char* QueryKeyIdentifier = "identifier"; constexpr const char* QueryKeyFileVersion = "file_version"; constexpr const char* QueryKeyApplicationVersion = "application_version"; constexpr const int ApplicationVersion = 1; + + struct [[codegen::Dictionary(HttpSynchronization)]] Parameters { + // A unique identifier for this resource + std::string identifier; + + // The version of this resource + int version; + }; +#include "httpsynchronization_codegen.cpp" } // namespace namespace openspace { documentation::Documentation HttpSynchronization::Documentation() { - using namespace openspace::documentation; - return { - "HttpSynchronization", - "http_synchronization", - { - { - KeyIdentifier, - new StringVerifier, - Optional::No, - "A unique identifier for this resource" - }, - { - KeyVersion, - new IntVerifier, - Optional::No, - "The version of this resource" - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "http_synchronization"; + return doc; } HttpSynchronization::HttpSynchronization(const ghoul::Dictionary& dict, @@ -81,14 +71,10 @@ HttpSynchronization::HttpSynchronization(const ghoul::Dictionary& dict, , _synchronizationRoot(std::move(synchronizationRoot)) , _synchronizationRepositories(std::move(synchronizationRepositories)) { - documentation::testSpecificationAndThrow( - Documentation(), - dict, - "HttpSynchronization" - ); + const Parameters p = codegen::bake(dict); - _identifier = dict.value(KeyIdentifier); - _version = static_cast(dict.value(KeyVersion)); + _identifier = p.identifier; + _version = p.version; } HttpSynchronization::~HttpSynchronization() { diff --git a/modules/sync/syncs/urlsynchronization.cpp b/modules/sync/syncs/urlsynchronization.cpp index 2d88784903..0f6bc61aa9 100644 --- a/modules/sync/syncs/urlsynchronization.cpp +++ b/modules/sync/syncs/urlsynchronization.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include namespace { constexpr const char* KeyUrl = "Url"; @@ -46,61 +48,44 @@ namespace { constexpr const char* KeyFilename = "Filename"; constexpr const char* TempSuffix = ".tmp"; + + struct [[codegen::Dictionary(UrlSynchronization)]] Parameters { + // The URL or urls from where the files are downloaded. If multiple URLs are + // provided, all files will be downloaded to the same directory + std::variant> url; + + // This optional identifier will be part of the used folder structure and, if + // provided, can be used to manually find the downloaded folder in the + // synchronization folder. If this value is not specified, 'UseHash' has to be set + // to 'true' + std::optional identifier; + + // If this value is set to 'true' and it is not overwritten by the global + // settings, the file(s) pointed to by this URLSynchronization will always be + // downloaded, thus overwriting the local files. This is useful for files that are + // updated regularly remotely and should be fetch at every startup + std::optional forceOverride [[codegen::key("override")]]; + + // If this value is set to 'true' (the default), the hash of the URL is appended + // to the directory name to produce a unique directory under all circumstances. If + // this is not desired, the URLSynchronization use the bare directory name alone + // if this value is 'false'. If this value is 'false', the identifier has to be + // specified + std::optional useHash; + + // Optional to provide filename to override the one which is otherwise + // automatically created from the url + std::optional filename; + }; +#include "urlsynchronization_codegen.cpp" } // namespace namespace openspace { documentation::Documentation UrlSynchronization::Documentation() { - using namespace openspace::documentation; - return { - "Url Synchronization", - "sync_synchronization_url", - { - { - KeyUrl, - new OrVerifier({ new StringVerifier, new StringListVerifier }), - Optional::No, - "The URL or urls from where the files are downloaded. If multiple URLs " - "are provided, all files will be downloaded to the same directory." - }, - { - KeyIdentifier, - new StringVerifier, - Optional::Yes, - "This optional identifier will be part of the used folder structure and, " - "if provided, can be used to manually find the downloaded folder in the " - "synchronization folder. If this value is not specified, 'UseHash' has " - "to be set to 'true'." - }, - { - KeyOverride, - new BoolVerifier, - Optional::Yes, - "If this value is set to 'true' and it is not overwritten by the global " - "settings, the file(s) pointed to by this URLSynchronization will always " - "be downloaded, thus overwriting the local files. This is useful for " - "files that are updated regularly remotely and should be fetch at every " - "startup." - }, - { - KeyUseHash, - new BoolVerifier, - Optional::Yes, - "If this value is set to 'true' (the default), the hash of the URL is " - "appended to the directory name to produce a unique directory under all " - "circumstances. If this is not desired, the URLSynchronization use the " - "bare directory name alone if this value is 'false'. If this value is " - "'false', the identifier has to be specified." - }, - { - KeyFilename, - new StringVerifier, - Optional::Yes, - "Optional to provide filename to override the one which is otherwise " - "automatically created from the url. " - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "sync_synchronization_url"; + return doc; } UrlSynchronization::UrlSynchronization(const ghoul::Dictionary& dict, @@ -108,43 +93,33 @@ UrlSynchronization::UrlSynchronization(const ghoul::Dictionary& dict, : ResourceSynchronization(dict) , _synchronizationRoot(std::move(synchronizationRoot)) { - documentation::testSpecificationAndThrow( - Documentation(), - dict, - "UrlSynchroniztion" - ); + const Parameters p = codegen::bake(dict); - if (dict.hasValue(KeyUrl)) { - _urls.push_back(dict.value(KeyUrl)); + if (std::holds_alternative(p.url)) { + _urls.push_back(std::get(p.url)); + + } + else if (std::holds_alternative>(p.url)) { + _urls = std::get>(p.url); } else { - ghoul::Dictionary urls = dict.value(KeyUrl); - for (size_t i = 1; i <= urls.size(); ++i) { - std::string url = urls.value(std::to_string(i)); - _urls.push_back(std::move(url)); - } + throw ghoul::MissingCaseException(); } - if (dict.hasValue(KeyFilename)) { - _filename = dict.value(KeyFilename); - } + _filename = p.filename.value_or(_filename); - bool useHash = true; - if (dict.hasValue(KeyUseHash)) { - useHash = dict.value(KeyUseHash); - } + bool useHash = p.useHash.value_or(true); // We just merge all of the URLs together to generate a hash, it's not as stable to // reordering URLs, but every other solution would be more error prone std::string urlConcat = std::accumulate(_urls.begin(), _urls.end(), std::string()); size_t hash = std::hash{}(urlConcat); - if (dict.hasValue(KeyIdentifier)) { - std::string ident = dict.value(KeyIdentifier); + if (p.identifier.has_value()) { if (useHash) { - _identifier = std::move(ident) + "(" + std::to_string(hash) + ")"; + _identifier = *p.identifier + "(" + std::to_string(hash) + ")"; } else { - _identifier = std::move(ident); + _identifier = *p.identifier; } } else { @@ -162,9 +137,7 @@ UrlSynchronization::UrlSynchronization(const ghoul::Dictionary& dict, } } - if (dict.hasValue(KeyOverride)) { - _forceOverride = dict.value(KeyOverride); - } + _forceOverride = p.forceOverride.value_or(_forceOverride); } UrlSynchronization::~UrlSynchronization() { @@ -193,7 +166,6 @@ void UrlSynchronization::start() { std::vector> downloads; for (const std::string& url : _urls) { - if (_filename.empty()) { const size_t lastSlash = url.find_last_of('/'); std::string lastPartOfUrl = url.substr(lastSlash + 1); diff --git a/modules/vislab/rendering/renderabledistancelabel.cpp b/modules/vislab/rendering/renderabledistancelabel.cpp index 397798d431..8d8adaa56e 100644 --- a/modules/vislab/rendering/renderabledistancelabel.cpp +++ b/modules/vislab/rendering/renderabledistancelabel.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace { @@ -56,36 +57,26 @@ namespace { "Property to define a custom unit descriptor to use to describe the distance " "value. Defaults to the units SI descriptor if not specified." }; -} + + struct [[codegen::Dictionary(RenderableDistanceLabel)]] Parameters { + // [[codegen::verbatim(NodeLineInfo.description)]] + std::string nodeLine; + + // [[codegen::verbatim(DistanceUnitInfo.description)]] + std::optional distanceUnit; + + // [[codegen::verbatim(CustomUnitDescriptorInfo.description)]] + std::optional customUnitDescriptor; + }; +#include "renderabledistancelabel_codegen.cpp" +} // namespace namespace openspace { documentation::Documentation RenderableDistanceLabel::Documentation() { - using namespace documentation; - return { - "Renderable Distance Label", - "vislab_renderable_distance_label", - { - { - NodeLineInfo.identifier, - new StringVerifier, - Optional::No, - NodeLineInfo.description - }, - { - DistanceUnitInfo.identifier, - new IntVerifier, - Optional::Yes, - DistanceUnitInfo.description - }, - { - CustomUnitDescriptorInfo.identifier, - new StringVerifier, - Optional::Yes, - CustomUnitDescriptorInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "vislab_renderable_distance_label"; + return doc; } RenderableDistanceLabel::RenderableDistanceLabel(const ghoul::Dictionary& dictionary) @@ -94,27 +85,16 @@ RenderableDistanceLabel::RenderableDistanceLabel(const ghoul::Dictionary& dictio , _distanceUnit(DistanceUnitInfo, 1, 0, 11) , _customUnitDescriptor(CustomUnitDescriptorInfo) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderableDistanceLabel" - ); + const Parameters p = codegen::bake(dictionary); - if (dictionary.hasKey(NodeLineInfo.identifier)) { - _nodelineId = dictionary.value(NodeLineInfo.identifier); - addProperty(_nodelineId); - } - if (dictionary.hasKey(DistanceUnitInfo.identifier)) { - _distanceUnit = static_cast( - dictionary.value(DistanceUnitInfo.identifier) - ); - addProperty(_distanceUnit); - } - if (dictionary.hasKey(CustomUnitDescriptorInfo.identifier)) { - _customUnitDescriptor = - dictionary.value(CustomUnitDescriptorInfo.identifier); - addProperty(_customUnitDescriptor); - } + _nodelineId = p.nodeLine; + addProperty(_nodelineId); + + _distanceUnit = p.distanceUnit.value_or(_distanceUnit); + addProperty(_distanceUnit); + + _customUnitDescriptor = p.customUnitDescriptor.value_or(_customUnitDescriptor); + addProperty(_customUnitDescriptor); } void RenderableDistanceLabel::update(const UpdateData&) { diff --git a/modules/volume/rawvolumemetadata.cpp b/modules/volume/rawvolumemetadata.cpp index 9ad143a764..85c0a05bbd 100644 --- a/modules/volume/rawvolumemetadata.cpp +++ b/modules/volume/rawvolumemetadata.cpp @@ -28,20 +28,35 @@ #include #include #include +#include namespace { - constexpr const char* KeyDimensions = "Dimensions"; - constexpr const char* KeyLowerDomainBound = "LowerDomainBound"; - constexpr const char* KeyUpperDomainBound = "UpperDomainBound"; + struct [[codegen::Dictionary(RawVolumeMetaData)]] Parameters { + // Specifies the number of grid cells in each dimension + glm::ivec3 dimensions; - constexpr const char* KeyMinValue = "MinValue"; - constexpr const char* KeyMaxValue = "MaxValue"; + // Specifies the unit used to specity the domain + std::optional domainUnit; - constexpr const char* KeyTime = "Time"; - constexpr const char* KeyDomainUnit = "DomainUnit"; - constexpr const char* KeyValueUnit = "ValueUnit"; + // Specifies the lower domain bounds in the model coordinate system + std::optional lowerDomainBound; - constexpr const char* KeyGridType = "GridType"; + // Specifies the upper domain bounds in the model coordinate system + std::optional upperDomainBound; + + // Specifies the time on the format YYYY-MM-DDTHH:MM:SS.000Z + std::optional time; + + // Specifies the unit used to specity the value + std::optional valueUnit; + + // Specifies the minimum value stored in the volume + std::optional minValue; + + // Specifies the maximum value stored in the volume + std::optional maxValue; + }; +#include "rawvolumemetadata_codegen.cpp" } // namespace namespace openspace::volume { @@ -49,51 +64,53 @@ namespace openspace::volume { RawVolumeMetadata RawVolumeMetadata::createFromDictionary( const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RawVolumeMetadata" - ); + const Parameters p = codegen::bake(dictionary); RawVolumeMetadata metadata; - metadata.dimensions = dictionary.value(KeyDimensions); + metadata.dimensions = p.dimensions; - metadata.hasDomainBounds = dictionary.hasValue(KeyLowerDomainBound) && - dictionary.hasValue(KeyUpperDomainBound); + metadata.hasDomainBounds = + p.lowerDomainBound.has_value() && + p.upperDomainBound.has_value(); if (metadata.hasDomainBounds) { - metadata.lowerDomainBound = dictionary.value(KeyLowerDomainBound); - metadata.upperDomainBound = dictionary.value(KeyUpperDomainBound); + metadata.lowerDomainBound = *p.lowerDomainBound; + metadata.upperDomainBound = *p.upperDomainBound; } - metadata.hasDomainUnit = static_cast( - dictionary.hasValue(KeyDomainUnit) - ); + metadata.hasDomainUnit = p.domainUnit.has_value(); if (metadata.hasDomainUnit) { - metadata.domainUnit = dictionary.value(KeyDomainUnit); + metadata.domainUnit = *p.domainUnit; } - metadata.hasValueRange = dictionary.hasValue(KeyMinValue) && - dictionary.hasValue(KeyMaxValue); - + metadata.hasValueRange = p.minValue.has_value() && p.maxValue.has_value(); if (metadata.hasValueRange) { - metadata.minValue = static_cast(dictionary.value(KeyMinValue)); - metadata.maxValue = static_cast(dictionary.value(KeyMaxValue)); + metadata.minValue = *p.minValue; + metadata.maxValue = *p.maxValue; } - metadata.hasValueUnit = static_cast(dictionary.hasValue(KeyValueUnit)); + metadata.hasValueUnit = p.valueUnit.has_value(); if (metadata.hasValueUnit) { - metadata.valueUnit = dictionary.value(KeyValueUnit); + metadata.valueUnit = *p.valueUnit; } - metadata.hasTime = dictionary.hasValue(KeyTime); + metadata.hasTime = p.time.has_value(); if (metadata.hasTime) { - std::string timeString = dictionary.value(KeyTime); - metadata.time = Time::convertTime(timeString); + metadata.time = Time::convertTime(*p.time); } return metadata; } ghoul::Dictionary RawVolumeMetadata::dictionary() { + constexpr const char* KeyDimensions = "Dimensions"; + constexpr const char* KeyLowerDomainBound = "LowerDomainBound"; + constexpr const char* KeyUpperDomainBound = "UpperDomainBound"; + constexpr const char* KeyMinValue = "MinValue"; + constexpr const char* KeyMaxValue = "MaxValue"; + constexpr const char* KeyTime = "Time"; + constexpr const char* KeyDomainUnit = "DomainUnit"; + constexpr const char* KeyValueUnit = "ValueUnit"; + constexpr const char* KeyGridType = "GridType"; + ghoul::Dictionary dict; dict.setValue(KeyDimensions, glm::dvec3(dimensions)); dict.setValue(KeyGridType, gridTypeToString(gridType)); @@ -126,61 +143,9 @@ ghoul::Dictionary RawVolumeMetadata::dictionary() { } documentation::Documentation RawVolumeMetadata::Documentation() { - using namespace documentation; - return { - "RawVolumeMetadata", - "volume_rawvolumemetadata", - { - { - KeyDimensions, - new DoubleVector3Verifier, - Optional::No, - "Specifies the number of grid cells in each dimension", - }, - { - KeyDomainUnit, - new StringVerifier, - Optional::Yes, - "Specifies the unit used to specity the domain", - }, - { - KeyLowerDomainBound, - new DoubleVector3Verifier, - Optional::Yes, - "Specifies the lower domain bounds in the model coordinate system", - }, - { - KeyUpperDomainBound, - new DoubleVector3Verifier, - Optional::Yes, - "Specifies the upper domain bounds in the model coordinate system", - }, - { - KeyTime, - new StringVerifier, - Optional::Yes, - "Specifies the time on the format YYYY-MM-DDTHH:MM:SS.000Z", - }, - { - KeyValueUnit, - new StringVerifier, - Optional::Yes, - "Specifies the unit used to specity the value", - }, - { - KeyMinValue, - new DoubleVerifier, - Optional::Yes, - "Specifies the minimum value stored in the volume" - }, - { - KeyMaxValue, - new DoubleVerifier, - Optional::Yes, - "Specifies the maximum value stored in the volume" - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "volume_rawvolumemetadata"; + return doc; } } // namespace openspace::volume diff --git a/modules/volume/tasks/generaterawvolumetask.cpp b/modules/volume/tasks/generaterawvolumetask.cpp index 6de0d6af6a..e0774bec63 100644 --- a/modules/volume/tasks/generaterawvolumetask.cpp +++ b/modules/volume/tasks/generaterawvolumetask.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,6 @@ #include #include #include - #include namespace { @@ -48,41 +48,65 @@ namespace { constexpr const char* KeyValueFunction = "ValueFunction"; constexpr const char* KeyLowerDomainBound = "LowerDomainBound"; constexpr const char* KeyUpperDomainBound = "UpperDomainBound"; + + struct [[codegen::Dictionary(GenerateRawVolumeTask)]] Parameters { + // The Lua function used to compute the cell values + std::string valueFunction [[codegen::annotation("A Lua expression that returns a " + "function taking three numbers as arguments (x, y, z) and returning a " + "number")]]; + + // The raw volume file to export data to + std::string rawVolumeOutput [[codegen::annotation("A valid filepath")]]; + + // The lua dictionary file to export metadata to + std::string dictionaryOutput [[codegen::annotation("A valid filepath")]]; + + // The timestamp that is written to the metadata of this volume + std::string time; + + // A vector representing the number of cells in each dimension + glm::ivec3 dimensions; + + // A vector representing the lower bound of the domain + glm::dvec3 lowerDomainBound; + + // A vector representing the upper bound of the domain + glm::dvec3 upperDomainBound; + }; +#include "generaterawvolumetask_codegen.cpp" } // namespace namespace openspace::volume { -GenerateRawVolumeTask::GenerateRawVolumeTask(const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "GenerateRawVolumeTask" - ); +documentation::Documentation GenerateRawVolumeTask::documentation() { + documentation::Documentation doc = codegen::doc(); + doc.id = "generate_raw_volume_task"; + return doc; +} - _rawVolumeOutputPath = absPath(dictionary.value(KeyRawVolumeOutput)); - _dictionaryOutputPath = absPath(dictionary.value(KeyDictionaryOutput)); - _dimensions = glm::uvec3(dictionary.value(KeyDimensions)); - _time = dictionary.value(KeyTime); - _valueFunctionLua = dictionary.value(KeyValueFunction); - _lowerDomainBound = dictionary.value(KeyLowerDomainBound); - _upperDomainBound = dictionary.value(KeyUpperDomainBound); +GenerateRawVolumeTask::GenerateRawVolumeTask(const ghoul::Dictionary& dictionary) { + const Parameters p = codegen::bake(dictionary); + + _rawVolumeOutputPath = absPath(p.rawVolumeOutput); + _dictionaryOutputPath = absPath(p.dictionaryOutput); + _dimensions = p.dimensions; + _time = p.time; + _valueFunctionLua = p.valueFunction; + _lowerDomainBound = p.lowerDomainBound; + _upperDomainBound = p.upperDomainBound; } std::string GenerateRawVolumeTask::description() { - return "Generate a raw volume with dimenstions: (" + - std::to_string(_dimensions.x) + ", " + - std::to_string(_dimensions.y) + ", " + - std::to_string(_dimensions.z) + "). " + - "For each cell, set the value by evaluating the lua function: " + - "`" + _valueFunctionLua + "`, with three arguments (x, y, z) ranging from " + - "(" + std::to_string(_lowerDomainBound.x) + ", " - + std::to_string(_lowerDomainBound.y) + ", " + - std::to_string(_lowerDomainBound.z) + ") to (" + - std::to_string(_upperDomainBound.x) + ", " + - std::to_string(_upperDomainBound.y) + ", " + - std::to_string(_upperDomainBound.z) + ")." + - "Write raw volume data into " + _rawVolumeOutputPath + - " and dictionary with metadata to " + _dictionaryOutputPath; + return fmt::format( + "Generate a raw volume with dimenstions: ({}, {}, {}). For each cell, set the " + "value by evaluating the lua function: `{}`, with three arguments (x, y, z) " + "ranging from ({}, {}, {}) to ({}, {}, {}). Write raw volume data into {} and " + "dictionary with metadata to {}", + _dimensions.x, _dimensions.y, _dimensions.z, _valueFunctionLua, + _lowerDomainBound.x, _lowerDomainBound.y, _lowerDomainBound.z, + _upperDomainBound.x, _upperDomainBound.y, _upperDomainBound.z, + _rawVolumeOutputPath, _dictionaryOutputPath + ); } void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallback) { @@ -184,51 +208,4 @@ void GenerateRawVolumeTask::perform(const Task::ProgressCallback& progressCallba progressCallback(1.0f); } -documentation::Documentation GenerateRawVolumeTask::documentation() { - using namespace documentation; - return { - "GenerateRawVolumeTask", - "generate_raw_volume_task", - { - { - KeyValueFunction, - new StringAnnotationVerifier("A lua expression that returns a function " - "taking three numbers as arguments (x, y, z) and returning a number."), - Optional::No, - "The lua function used to compute the cell values", - }, - { - KeyRawVolumeOutput, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The raw volume file to export data to", - }, - { - KeyDictionaryOutput, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The lua dictionary file to export metadata to", - }, - { - KeyDimensions, - new DoubleVector3Verifier, - Optional::No, - "A vector representing the number of cells in each dimension", - }, - { - KeyLowerDomainBound, - new DoubleVector3Verifier, - Optional::No, - "A vector representing the lower bound of the domain" - }, - { - KeyUpperDomainBound, - new DoubleVector3Verifier, - Optional::No, - "A vector representing the upper bound of the domain" - } - } - }; -} - } // namespace openspace::volume diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1efeee4a4d..19d417efaa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,7 +161,6 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/scene/sceneinitializer.cpp ${OPENSPACE_BASE_DIR}/src/scene/scenelicensewriter.cpp ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode.cpp - ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode_doc.inl ${OPENSPACE_BASE_DIR}/src/scene/timeframe.cpp ${OPENSPACE_BASE_DIR}/src/scene/translation.cpp ${OPENSPACE_BASE_DIR}/src/scripting/lualibrary.cpp diff --git a/src/interaction/navigationhandler.cpp b/src/interaction/navigationhandler.cpp index fa3aefb28f..203a674f1e 100644 --- a/src/interaction/navigationhandler.cpp +++ b/src/interaction/navigationhandler.cpp @@ -44,13 +44,6 @@ namespace { constexpr const char* _loggerCat = "NavigationHandler"; - constexpr const char* KeyAnchor = "Anchor"; - constexpr const char* KeyAim = "Aim"; - constexpr const char* KeyPosition = "Position"; - constexpr const char* KeyUp = "Up"; - constexpr const char* KeyYaw = "Yaw"; - constexpr const char* KeyPitch = "Pitch"; - constexpr const char* KeyReferenceFrame = "ReferenceFrame"; const double Epsilon = 1E-7; using namespace openspace; @@ -73,6 +66,31 @@ namespace { "than using the mouse interaction." }; + struct [[codegen::Dictionary(NavigationHandler)]] Parameters { + // The identifier of the anchor node + std::string anchor; + + // The identifier of the aim node, if used + std::optional aim; + + // The identifier of the scene graph node to use as reference frame. If not + // specified, this will be the same as the anchor + std::optional referenceFrame; + + // The position of the camera relative to the anchor node, expressed in meters in + // the specified reference frame + glm::dvec3 position; + + // The up vector expressed in the coordinate system of the reference frame + std::optional up; + + // The yaw angle in radians. Positive angle means yawing camera to the right + std::optional yaw; + + // The pitch angle in radians. Positive angle means pitching camera upwards + std::optional pitch; + }; +#include "navigationhandler_codegen.cpp" } // namespace #include "navigationhandler_lua.inl" @@ -80,6 +98,14 @@ namespace { namespace openspace::interaction { ghoul::Dictionary NavigationHandler::NavigationState::dictionary() const { + constexpr const char* KeyAnchor = "Anchor"; + constexpr const char* KeyAim = "Aim"; + constexpr const char* KeyPosition = "Position"; + constexpr const char* KeyUp = "Up"; + constexpr const char* KeyYaw = "Yaw"; + constexpr const char* KeyPitch = "Pitch"; + constexpr const char* KeyReferenceFrame = "ReferenceFrame"; + ghoul::Dictionary cameraDict; cameraDict.setValue(KeyPosition, position); cameraDict.setValue(KeyAnchor, anchor); @@ -105,36 +131,19 @@ ghoul::Dictionary NavigationHandler::NavigationState::dictionary() const { } NavigationHandler::NavigationState::NavigationState(const ghoul::Dictionary& dictionary) { - const bool hasAnchor = dictionary.hasValue(KeyAnchor); - const bool hasPosition = dictionary.hasValue(KeyPosition); - if (!hasAnchor || !hasPosition) { - throw ghoul::RuntimeError( - "Position and Anchor need to be defined for navigation dictionary." - ); - } + const Parameters p = codegen::bake(dictionary); - anchor = dictionary.value(KeyAnchor); - position = dictionary.value(KeyPosition); + anchor = p.anchor; + position = p.position; - if (dictionary.hasValue(KeyReferenceFrame)) { - referenceFrame = dictionary.value(KeyReferenceFrame); - } - else { - referenceFrame = anchor; - } - if (dictionary.hasValue(KeyAim)) { - aim = dictionary.value(KeyAim); - } + referenceFrame = p.referenceFrame.value_or(anchor); + aim = p.aim.value_or(aim); - if (dictionary.hasValue(KeyUp)) { - up = dictionary.value(KeyUp); + if (p.up.has_value()) { + up = *p.up; - if (dictionary.hasValue(KeyYaw)) { - yaw = dictionary.value(KeyYaw); - } - if (dictionary.hasValue(KeyPitch)) { - pitch = dictionary.value(KeyPitch); - } + yaw = p.yaw.value_or(yaw); + pitch = p.pitch.value_or(pitch); } } @@ -142,8 +151,7 @@ NavigationHandler::NavigationState::NavigationState(std::string anchor_, std::st std::string referenceFrame_, glm::dvec3 position_, std::optional up_, - double yaw_, - double pitch_) + double yaw_, double pitch_) : anchor(std::move(anchor_)) , aim(std::move(aim_)) , referenceFrame(std::move(referenceFrame_)) @@ -545,60 +553,9 @@ std::vector NavigationHandler::joystickButtonCommand(int button) co } documentation::Documentation NavigationHandler::NavigationState::Documentation() { - using namespace documentation; - - return { - "Navigation State", - "core_navigation_state", - { - { - KeyAnchor, - new StringVerifier, - Optional::No, - "The identifier of the anchor node." - }, - { - KeyAim, - new StringVerifier, - Optional::Yes, - "The identifier of the aim node, if used." - }, - { - KeyReferenceFrame, - new StringVerifier, - Optional::Yes, - "The identifier of the scene graph node to use as reference frame. " - "If not specified, this will be the same as the anchor." - }, - { - KeyPosition, - new DoubleVector3Verifier, - Optional::No, - "The position of the camera relative to the anchor node, " - "expressed in meters in the specified reference frame." - }, - { - KeyUp, - new DoubleVector3Verifier, - Optional::Yes, - "The up vector expressed in the coordinate system of the reference frame." - }, - { - KeyYaw, - new DoubleVerifier, - Optional::Yes, - "The yaw angle in radians. " - "Positive angle means yawing camera to the right." - }, - { - KeyPitch, - new DoubleVerifier, - Optional::Yes, - "The pitch angle in radians. " - "Positive angle means pitching camera upwards." - }, - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "core_navigation_state"; + return doc; } scripting::LuaLibrary NavigationHandler::luaLibrary() { diff --git a/src/mission/mission.cpp b/src/mission/mission.cpp index 95c63cadf3..feadaac081 100644 --- a/src/mission/mission.cpp +++ b/src/mission/mission.cpp @@ -29,73 +29,47 @@ #include #include #include +#include namespace { - constexpr const char* KeyName = "Name"; - constexpr const char* KeyDescription = "Description"; - constexpr const char* KeyPhases = "Phases"; - constexpr const char* KeyTimeRange = "TimeRange"; + struct [[codegen::Dictionary(MissionPhase)]] Parameters { + // The human readable name of this mission or mission phase that is displayed to + // the user + std::string name; + + // A description of this mission or mission phase + std::optional description; + + // The time range for which this mission or mission phase is valid. If no time + // range is specified, the ranges of sub mission phases are used instead + std::optional timeRange + [[codegen::reference("core_util_timerange")]]; + + // The phases into which this mission or mission phase is separated + std::optional> phases + [[codegen::reference("core_mission_mission")]]; + }; +#include "mission_codegen.cpp" } // namespace namespace openspace { documentation::Documentation MissionPhase::Documentation() { - using namespace documentation; - - return { - "Missions and Mission Phases", - "core_mission_mission", - { - { - KeyName, - new StringVerifier, - Optional::No, - "The human readable name of this mission or mission phase that is " - "displayed to the user." - }, - { - KeyDescription, - new StringVerifier, - Optional::Yes, - "A description of this mission or mission phase." - }, - { - KeyTimeRange, - new ReferencingVerifier("core_util_timerange"), - Optional::Yes, - "The time range for which this mission or mission phase is valid. If no " - "time range is specified, the ranges of sub mission phases are used " - "instead." - }, - { - KeyPhases, - new TableVerifier({ - { - "*", - new ReferencingVerifier("core_mission_mission"), - Optional::Yes - } - }), - Optional::Yes, - "The phases into which this mission or mission phase is separated." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "core_mission_mission"; + return doc; } MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) { - _name = dictionary.value(KeyName); - if (dictionary.hasValue(KeyDescription)) { - _description = dictionary.value(KeyDescription); - } + const Parameters p = codegen::bake(dictionary); - if (dictionary.hasValue(KeyPhases)) { - ghoul::Dictionary childDicts = dictionary.value(KeyPhases); - // This is a nested mission phase - _subphases.reserve(childDicts.size()); - for (size_t i = 0; i < childDicts.size(); ++i) { - std::string key = std::to_string(i + 1); - _subphases.emplace_back(childDicts.value(key)); + _name = p.name; + _description = p.description.value_or(_description); + + if (p.phases.has_value()) { + _subphases.reserve(p.phases->size()); + for (const ghoul::Dictionary& phase : *p.phases) { + _subphases.emplace_back(phase); } // Ensure subphases are sorted @@ -112,15 +86,14 @@ MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) { timeRangeSubPhases.start = _subphases[0].timeRange().start; timeRangeSubPhases.end = _subphases.back().timeRange().end; - // user may specify an overall time range. In that case expand this timerange. - if (dictionary.hasValue(KeyTimeRange)) { - ghoul::Dictionary range = dictionary.value(KeyTimeRange); - TimeRange overallTimeRange(range); + // user may specify an overall time range. In that case expand this timerange + if (p.timeRange.has_value()) { + TimeRange overallTimeRange(*p.timeRange); if (!overallTimeRange.includes(timeRangeSubPhases)) { - throw ghoul::RuntimeError( + throw ghoul::RuntimeError(fmt::format( "User specified time range must at least include its subphases'", - "Mission (" + _name + ")" - ); + "Mission ({})", _name + )); } _timeRange.include(overallTimeRange); @@ -132,16 +105,14 @@ MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) { } } else { - if (dictionary.hasValue(KeyTimeRange)) { - ghoul::Dictionary timeRangeDict = - dictionary.value(KeyTimeRange); - _timeRange = TimeRange(timeRangeDict); // throws exception if unable to parse + if (p.timeRange.has_value()) { + _timeRange = TimeRange(*p.timeRange); // throws exception if unable to parse } else { - throw ghoul::RuntimeError( + throw ghoul::RuntimeError(fmt::format( "If there are no subphases specified, the time range has to be specified", - "Mission (" + _name + ")" - ); + "Mission ({})", _name + )); } } } diff --git a/src/rendering/screenspacerenderable.cpp b/src/rendering/screenspacerenderable.cpp index 9f913096a2..fa64370e68 100644 --- a/src/rendering/screenspacerenderable.cpp +++ b/src/rendering/screenspacerenderable.cpp @@ -38,11 +38,10 @@ #include #include #include +#include +#include namespace { - constexpr const char* KeyType = "Type"; - constexpr const char* KeyTag = "Tag"; - constexpr const std::array UniformNames = { "Alpha", "ModelTransform", "ViewProjectionMatrix", "texture1" }; @@ -207,109 +206,72 @@ namespace { wrap(elevation, -glm::pi(), glm::pi()) ); } + + struct [[codegen::Dictionary(ScreenSpaceRenderable)]] Parameters { + // The type of the Screenspace renderable that is to be created. The available + // types of Screenspace renderable depend on the configuration of the application + // and can be written to disk on application startup into the FactoryDocumentation + std::string type + [[codegen::annotation("Must name a valid Screenspace renderable")]]; + + // Specifies the name of this screenspace renderable. This does not have to be + // unique to the scene, but it is recommended to be + std::optional name; + + // This is the unique identifier for this screenspace renderable. It has to be + // unique amongst all existing screenspace nodes that already have been added to + // the scene. The identifier is not allowed to have any whitespace or '.' and must + // not be empty + std::optional identifier; + + // [[codegen::verbatim(EnabledInfo.description)]] + std::optional enabled; + + // [[codegen::verbatim(UseRadiusAzimuthElevationInfo.description)]] + std::optional useRadiusAzimuthElevation; + + // [[codegen::verbatim(FaceCameraInfo.description)]] + std::optional faceCamera; + + // [[codegen::verbatim(CartesianPositionInfo.description)]] + std::optional cartesianPosition; + + // [[codegen::verbatim(RadiusAzimuthElevationInfo.description)]] + std::optional radiusAzimuthElevation; + + // [[codegen::verbatim(ScaleInfo.description)]] + std::optional scale; + + // [[codegen::verbatim(UsePerspectiveProjectionInfo.description)]] + std::optional usePerspectiveProjection; + + // [codegen::verbatim(OpacityInfo.description)]] + std::optional opacity [[codegen::inrange(0.f, 1.f)]]; + + // Defines either a single or multiple tags that apply to this + // ScreenSpaceRenderable, thus making it possible to address multiple, separate + // Renderables with a single property change + std::optional>> tag; + }; +#include "screenspacerenderable_codegen.cpp" } // namespace namespace openspace { documentation::Documentation ScreenSpaceRenderable::Documentation() { - using namespace openspace::documentation; - - return { - "Screenspace Renderable", - "core_screenspacerenderable", - { - { - KeyType, - new StringAnnotationVerifier("Must name a valid Screenspace renderable"), - Optional::No, - "The type of the Screenspace renderable that is to be created. The " - "available types of Screenspace renderable depend on the configuration of" - "the application and can be written to disk on application startup into " - "the FactoryDocumentation." - }, - { - KeyName, - new StringVerifier, - Optional::Yes, - "Specifies the name of this screenspace renderable. This does not have " - "to be unique to the scene, but it is recommended to be." - }, - { - KeyIdentifier, - new StringVerifier, - Optional::Yes, - "This is the unique identifier for this screenspace renderable. It has " - "to be unique amongst all existing screenspace nodes that already have " - "been added to the scene. The identifier is not allowed to have any " - "whitespace or '.' and must not be empty." - }, - { - EnabledInfo.identifier, - new BoolVerifier, - Optional::Yes, - EnabledInfo.description - }, - { - UseRadiusAzimuthElevationInfo.identifier, - new BoolVerifier, - Optional::Yes, - UseRadiusAzimuthElevationInfo.description - }, - { - FaceCameraInfo.identifier, - new BoolVerifier, - Optional::Yes, - FaceCameraInfo.description - }, - { - CartesianPositionInfo.identifier, - new DoubleVector3Verifier, - Optional::Yes, - CartesianPositionInfo.description - }, - { - RadiusAzimuthElevationInfo.identifier, - new DoubleVector3Verifier, - Optional::Yes, - RadiusAzimuthElevationInfo.description - }, - { - ScaleInfo.identifier, - new DoubleVerifier, - Optional::Yes, - ScaleInfo.description - }, - { - OpacityInfo.identifier, - new DoubleVerifier, - Optional::Yes, - OpacityInfo.description - }, - { - KeyTag, - new OrVerifier({ new StringVerifier, new StringListVerifier }), - Optional::Yes, - "Defines either a single or multiple tags that apply to this " - "ScreenSpaceRenderable, thus making it possible to address multiple, " - "seprate Renderables with a single property change." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "core_screenspacerenderable"; + return doc; } std::unique_ptr ScreenSpaceRenderable::createFromDictionary( const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "ScreenSpaceRenderable" - ); + const Parameters p = codegen::bake(dictionary); - const std::string& renderableType = dictionary.value(KeyType); ScreenSpaceRenderable* ssr = FactoryManager::ref().factory()->create( - renderableType, + p.type, dictionary ); return std::unique_ptr(ssr); @@ -365,12 +327,14 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary , _opacity(OpacityInfo, 1.f, 0.f, 1.f) , _delete(DeleteInfo) { - if (dictionary.hasKey(KeyIdentifier)) { - setIdentifier(dictionary.value(KeyIdentifier)); + const Parameters p = codegen::bake(dictionary); + + if (p.identifier.has_value()) { + setIdentifier(*p.identifier); } - if (dictionary.hasKey(KeyName)) { - setGuiName(dictionary.value(KeyName)); + if (p.name.has_value()) { + setGuiName(*p.name); } addProperty(_enabled); @@ -394,62 +358,38 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary addProperty(_opacity); addProperty(_localRotation); - if (dictionary.hasKey(EnabledInfo.identifier)) { - _enabled = dictionary.value(EnabledInfo.identifier); - } - - if (dictionary.hasKey(UseRadiusAzimuthElevationInfo.identifier)) { - _useRadiusAzimuthElevation = dictionary.value( - UseRadiusAzimuthElevationInfo.identifier - ); - } + _enabled = p.enabled.value_or(_enabled); + _useRadiusAzimuthElevation = + p.useRadiusAzimuthElevation.value_or(_useRadiusAzimuthElevation); if (_useRadiusAzimuthElevation) { - if (dictionary.hasKey(RadiusAzimuthElevationInfo.identifier)) { - _raePosition = dictionary.value( - RadiusAzimuthElevationInfo.identifier - ); - } + _raePosition = p.radiusAzimuthElevation.value_or(_raePosition); } else { - if (dictionary.hasKey(CartesianPositionInfo.identifier)) { - _cartesianPosition = dictionary.value( - CartesianPositionInfo.identifier - ); + _cartesianPosition = p.cartesianPosition.value_or(_cartesianPosition); + } + + _scale = p.scale.value_or(_scale); + _opacity = p.opacity.value_or(_opacity); + _usePerspectiveProjection = + p.usePerspectiveProjection.value_or(_usePerspectiveProjection); + + _faceCamera = p.faceCamera.value_or(_faceCamera); + + if (p.tag.has_value()) { + if (std::holds_alternative(*p.tag)) { + addTag(std::get(*p.tag)); } - } - - if (dictionary.hasKey(ScaleInfo.identifier)) { - _scale = static_cast(dictionary.value(ScaleInfo.identifier)); - } - - if (dictionary.hasKey(OpacityInfo.identifier)) { - _opacity = static_cast(dictionary.value(OpacityInfo.identifier)); - } - - if (dictionary.hasKey(UsePerspectiveProjectionInfo.identifier)) { - _usePerspectiveProjection = - dictionary.value(UsePerspectiveProjectionInfo.identifier); - } - - if (dictionary.hasKey(FaceCameraInfo.identifier)) { - _faceCamera = dictionary.value(FaceCameraInfo.identifier); - } - - if (dictionary.hasValue(KeyTag)) { - std::string tagName = dictionary.value(KeyTag); - if (!tagName.empty()) { - addTag(std::move(tagName)); - } - } - else if (dictionary.hasValue(KeyTag)) { - const ghoul::Dictionary& tagNames = dictionary.value(KeyTag); - for (std::string_view key : tagNames.keys()) { - std::string tagName = tagNames.value(key); - if (!tagName.empty()) { - addTag(std::move(tagName)); + else if (std::holds_alternative>(*p.tag)) { + for (const std::string& t : std::get>(*p.tag)) { + if (!t.empty()) { + addTag(t); + } } } + else { + throw ghoul::MissingCaseException(); + } } _delete.onChange([this](){ diff --git a/src/scene/lightsource.cpp b/src/scene/lightsource.cpp index 61bb9fffd1..597cff1265 100644 --- a/src/scene/lightsource.cpp +++ b/src/scene/lightsource.cpp @@ -32,17 +32,28 @@ #include #include #include +#include namespace { - constexpr const char* KeyType = "Type"; - - constexpr const char* KeyIdentifier = "Identifier"; - constexpr openspace::properties::Property::PropertyInfo EnabledInfo = { "Enabled", "Enabled", "Whether the light source is enabled or not" }; + + struct [[codegen::Dictionary(LightSource)]] Parameters { + // The type of the light source that is described in this element. The available + // types of light sources depend on the configuration of the application and can + // be written to disk on application startup into the FactoryDocumentation + std::string type [[codegen::annotation("Must name a valid LightSource type")]]; + + // The identifier of the light source + std::string identifier; + + // [[codegen::verbatim(EnabledInfo.description)]] + std::optional enabled; + }; +#include "lightsource_codegen.cpp" } // namespace namespace openspace { @@ -52,49 +63,19 @@ bool LightSource::isEnabled() const { } documentation::Documentation LightSource::Documentation() { - using namespace openspace::documentation; - - return { - "Light Source", - "core_light_source", - { - { - KeyType, - new StringAnnotationVerifier("Must name a valid LightSource type"), - Optional::No, - "The type of the light source that is described in this element. " - "The available types of light sources depend on the configuration " - "of the application and can be written to disk on " - "application startup into the FactoryDocumentation." - }, - { - KeyIdentifier, - new StringVerifier, - Optional::No, - "The identifier of the light source." - }, - { - EnabledInfo.identifier, - new BoolVerifier, - Optional::Yes, - EnabledInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "core_light_source"; + return doc; } std::unique_ptr LightSource::createFromDictionary( - const ghoul::Dictionary& dictionary) + const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow(Documentation(), dictionary, "LightSource"); - - const std::string timeFrameType = dictionary.value(KeyType); + const Parameters p = codegen::bake(dictionary); auto factory = FactoryManager::ref().factory(); - LightSource* source = factory->create(timeFrameType, dictionary); - - const std::string identifier = dictionary.value(KeyIdentifier); - source->setIdentifier(identifier); + LightSource* source = factory->create(p.type, dictionary); + source->setIdentifier(p.identifier); return std::unique_ptr(source); } @@ -110,10 +91,9 @@ LightSource::LightSource(const ghoul::Dictionary& dictionary) : properties::PropertyOwner({ "LightSource" }) , _enabled(EnabledInfo, true) { - if (dictionary.hasValue(EnabledInfo.identifier)) { - _enabled = dictionary.value(EnabledInfo.identifier); - } + const Parameters p = codegen::bake(dictionary); + _enabled = p.enabled.value_or(_enabled); addProperty(_enabled); } diff --git a/src/scene/rotation.cpp b/src/scene/rotation.cpp index 59daaf823d..637b60029e 100644 --- a/src/scene/rotation.cpp +++ b/src/scene/rotation.cpp @@ -36,40 +36,30 @@ #include namespace { - constexpr const char* KeyType = "Type"; + struct [[codegen::Dictionary(Rotation)]] Parameters { + // The type of the rotation that is described in this element. The available types + // of rotations depend on the configuration of the application and can be written + // to disk on application startup into the FactoryDocumentation + std::string type [[codegen::annotation("Must name a valid Rotation type")]]; + }; +#include "rotation_codegen.cpp" } // namespace namespace openspace { documentation::Documentation Rotation::Documentation() { - using namespace openspace::documentation; - - return { - "Transformation Rotation", - "core_transform_rotation", - { - { - KeyType, - new StringAnnotationVerifier("Must name a valid Rotation type."), - Optional::No, - "The type of the rotation that is described in this element. The " - "available types of rotations depend on the configuration of the " - "application and can be written to disk on application startup into the " - "FactoryDocumentation." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "core_transform_rotation"; + return doc; } ghoul::mm_unique_ptr Rotation::createFromDictionary( const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow(Documentation(), dictionary, "Rotation"); + const Parameters p = codegen::bake(dictionary); - const std::string& rotationType = dictionary.value(KeyType); - auto factory = FactoryManager::ref().factory(); - Rotation* result = factory->create( - rotationType, + Rotation* result = FactoryManager::ref().factory()->create( + p.type, dictionary, &global::memoryManager->PersistentMemory ); @@ -78,6 +68,8 @@ ghoul::mm_unique_ptr Rotation::createFromDictionary( Rotation::Rotation() : properties::PropertyOwner({ "Rotation" }) {} +// @TODO (abock, 2021-03-25) This constructor can probably die since it doesn't do any +// above the default constructor Rotation::Rotation(const ghoul::Dictionary&) : properties::PropertyOwner({ "Rotation" }) {} diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 38ddc471bf..0f3b760444 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,23 +40,11 @@ #include #include #include -#include "scenegraphnode_doc.inl" - #include +#include namespace { constexpr const char* _loggerCat = "SceneGraphNode"; - constexpr const char* KeyRenderable = "Renderable"; - constexpr const char* KeyGuiName = "GUI.Name"; - constexpr const char* KeyGuiPath = "GUI.Path"; - constexpr const char* KeyGuiHidden = "GUI.Hidden"; - constexpr const char* KeyGuiDescription = "GUI.Description"; - - constexpr const char* KeyTransformTranslation = "Transform.Translation"; - constexpr const char* KeyTransformRotation = "Transform.Rotation"; - constexpr const char* KeyTransformScale = "Transform.Scale"; - - constexpr const char* KeyTimeFrame = "TimeFrame"; constexpr openspace::properties::Property::PropertyInfo ComputeScreenSpaceInfo = { @@ -138,6 +128,90 @@ namespace { openspace::properties::Property::Visibility::Hidden }; + struct [[codegen::Dictionary(SceneGraphNode)]] Parameters { + // The identifier of this scenegraph node. This name must be unique among all + // scene graph nodes that are loaded in a specific scene. If a duplicate is + // detected the loading of the node will fail, as will all childing that depend on + // the node. The identifier must not contain any whitespaces or '.' + std::string identifier; + + // This names the parent of the currently specified scenegraph node. The parent + // must already exist in the scene graph. If not specified, the node will be + // attached to the root of the scenegraph + std::optional parent + [[codegen::annotation( + "If specified, this must be a name for another scenegraph node" + )]]; + + // The renderable that is to be created for this scenegraph node. A renderable is + // a component of a scenegraph node that will lead to some visual result on the + // screen. The specifics heavily depend on the 'Type' of the renderable. If no + // Renderable is specified, this scenegraph node is an internal node and can be + // used for either group children, or apply common transformations to a group of + // children + std::optional renderable [[codegen::reference("renderable")]]; + + // A hard-coded bounding sphere to be used for the cases where the Renderable is + // not able to provide a reasonable bounding sphere or the calculated bounding + // sphere needs to be overwritten for some reason + std::optional boundingSphere; + + struct Transform { + // This node describes a translation that is applied to the scenegraph node + // and all its children. Depending on the 'Type' of the translation, this can + // either be a static translation or a time-varying one + std::optional translation + [[codegen::reference("core_transform_translation")]]; + + // This nodes describes a rotation that is applied to the scenegraph node and + // all its children. Depending on the 'Type' of the rotation, this can either + // be a static rotation or a time-varying one + std::optional rotation + [[codegen::reference("core_transform_rotation")]]; + + // This node describes a scaling that is applied to the scenegraph node and + // all its children. Depending on the 'Type' of the scaling, this can either + // be a static scaling or a time-varying one + std::optional scale + [[codegen::reference("core_transform_scaling")]]; + }; + + // This describes a set of transformations that are applied to this scenegraph + // node and all of its children. There are only three possible values + // corresponding to a 'Translation', a 'Rotation', and a 'Scale' + std::optional transform; + + // Specifies the time frame for when this node should be active + std::optional timeFrame + [[codegen::reference("core_time_frame")]]; + + // A tag or list of tags that can be used to reference to a group of scenegraph + // nodes. + std::optional>> tag; + + struct Gui { + // An optional user-facing name for this SceneGraphNode, which does not have + // to be unique, though it is recommended, and can contain any characters + std::optional name; + + // If this value is specified, this '/' separated URI specifies the location + // of this scenegraph node in a GUI representation, for instance + // '/SolarSystem/Earth/Moon' + std::optional path; + + // A user-facing description about this scene graph node + std::optional description; + + // If this value is specified, GUI applications are incouraged to ignore this + // scenegraph node. This is most useful to trim collective lists of nodes and + // not display, for example, barycenters + std::optional hidden; + }; + // Additional information that is passed to GUI applications. These are all hints + // and do not have any impact on the actual function of the scenegraph node + std::optional gui [[codegen::key("GUI")]]; + }; +#include "scenegraphnode_codegen.cpp" } // namespace namespace openspace { @@ -149,11 +223,7 @@ int SceneGraphNode::nextIndex = 0; ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - SceneGraphNode::Documentation(), - dictionary, - "SceneGraphNode" - ); + const Parameters p = codegen::bake(dictionary); SceneGraphNode* n = global::memoryManager->PersistentMemory.alloc(); ghoul::mm_unique_ptr result = ghoul::mm_unique_ptr(n); @@ -162,89 +232,102 @@ ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( result->index = nextIndex++; #endif // Debugging_Core_SceneGraphNode_Indices - std::string identifier = dictionary.value(KeyIdentifier); - result->setIdentifier(std::move(identifier)); + result->setIdentifier(p.identifier); - if (dictionary.hasKey(KeyGuiName)) { - result->setGuiName(dictionary.value(KeyGuiName)); - result->_guiDisplayName = result->guiName(); - result->addProperty(result->_guiDisplayName); + if (p.gui.has_value()) { + if (p.gui->name.has_value()) { + result->setGuiName(*p.gui->name); + result->_guiDisplayName = result->guiName(); + result->addProperty(result->_guiDisplayName); + } + + if (p.gui->description.has_value()) { + result->setDescription(*p.gui->description); + result->_guiDescription = result->description(); + result->addProperty(result->_guiDescription); + } + + if (p.gui->hidden.has_value()) { + result->_guiHidden = *p.gui->hidden; + result->addProperty(result->_guiHidden); + } + + if (p.gui->path.has_value()) { + result->_guiPath = *p.gui->path; + result->addProperty(result->_guiPath); + } } - if (dictionary.hasKey(KeyGuiDescription)) { - result->setDescription(dictionary.value(KeyGuiDescription)); - result->_guiDescription = result->description(); - result->addProperty(result->_guiDescription); - } - - if (dictionary.hasKey(KeyGuiHidden)) { - result->_guiHidden = dictionary.value(KeyGuiHidden); - result->addProperty(result->_guiHidden); - } - - if (dictionary.hasKey(BoundingSphereInfo.identifier)) { - result->_boundingSphere = dictionary.value(BoundingSphereInfo.identifier); + if (p.boundingSphere.has_value()) { + result->_boundingSphere = *p.boundingSphere; result->_boundingSphere.setVisibility(properties::Property::Visibility::All); } - if (dictionary.hasKey(KeyTransformTranslation)) { - ghoul::Dictionary translationDictionary = - dictionary.value(KeyTransformTranslation); - result->_transform.translation = Translation::createFromDictionary( - translationDictionary - ); - if (result->_transform.translation == nullptr) { - LERROR(fmt::format( - "Failed to create ephemeris for SceneGraphNode '{}'", result->identifier() + if (p.transform.has_value()) { + if (p.transform->translation.has_value()) { + result->_transform.translation = Translation::createFromDictionary( + *p.transform->translation + ); + + // @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we + // transitioned to throwing exceptions when the construction fails + if (result->_transform.translation == nullptr) { + LERROR(fmt::format( + "Failed to create ephemeris for SceneGraphNode '{}'", + result->identifier() + )); + return nullptr; + } + LDEBUG(fmt::format( + "Successfully created ephemeris for '{}'", result->identifier() )); - return nullptr; + result->addPropertySubOwner(result->_transform.translation.get()); + } + + if (p.transform->rotation.has_value()) { + result->_transform.rotation = Rotation::createFromDictionary( + *p.transform->rotation + ); + + // @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we + // transitioned to throwing exceptions when the construction fails + if (result->_transform.rotation == nullptr) { + LERROR(fmt::format( + "Failed to create rotation for SceneGraphNode '{}'", + result->identifier() + )); + return nullptr; + } + LDEBUG(fmt::format( + "Successfully created rotation for '{}'", result->identifier() + )); + result->addPropertySubOwner(result->_transform.rotation.get()); + } + + if (p.transform->scale.has_value()) { + result->_transform.scale = Scale::createFromDictionary(*p.transform->scale); + + // @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we + // transitioned to throwing exceptions when the construction fails + if (result->_transform.scale == nullptr) { + LERROR(fmt::format( + "Failed to create scale for SceneGraphNode '{}'", + result->identifier() + )); + return nullptr; + } + LDEBUG(fmt::format( + "Successfully created scale for '{}'", result->identifier() + )); + result->addPropertySubOwner(result->_transform.scale.get()); } - LDEBUG(fmt::format( - "Successfully created ephemeris for '{}'", result->identifier() - )); - } - if (result->_transform.translation) { - result->addPropertySubOwner(result->_transform.translation.get()); } - if (dictionary.hasKey(KeyTransformRotation)) { - ghoul::Dictionary rotationDictionary = - dictionary.value(KeyTransformRotation); - result->_transform.rotation = Rotation::createFromDictionary(rotationDictionary); - if (result->_transform.rotation == nullptr) { - LERROR(fmt::format( - "Failed to create rotation for SceneGraphNode '{}'", result->identifier() - )); - return nullptr; - } - LDEBUG(fmt::format( - "Successfully created rotation for '{}'", result->identifier() - )); - } - if (result->_transform.rotation) { - result->addPropertySubOwner(result->_transform.rotation.get()); - } + if (p.timeFrame.has_value()) { + result->_timeFrame = TimeFrame::createFromDictionary(*p.timeFrame); - if (dictionary.hasKey(KeyTransformScale)) { - ghoul::Dictionary scaleDictionary = - dictionary.value(KeyTransformScale); - result->_transform.scale = Scale::createFromDictionary(scaleDictionary); - if (result->_transform.scale == nullptr) { - LERROR(fmt::format( - "Failed to create scale for SceneGraphNode '{}'", result->identifier() - )); - return nullptr; - } - LDEBUG(fmt::format("Successfully created scale for '{}'", result->identifier())); - } - if (result->_transform.scale) { - result->addPropertySubOwner(result->_transform.scale.get()); - } - - if (dictionary.hasKey(KeyTimeFrame)) { - ghoul::Dictionary timeFrameDictionary = - dictionary.value(KeyTimeFrame); - result->_timeFrame = TimeFrame::createFromDictionary(timeFrameDictionary); + // @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we + // transitioned to throwing exceptions when the construction fails if (result->_timeFrame == nullptr) { LERROR(fmt::format( "Failed to create time frame for SceneGraphNode '{}'", @@ -252,30 +335,26 @@ ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( )); return nullptr; } - result->addPropertySubOwner(result->_timeFrame.get()); LDEBUG(fmt::format( - "Successfully created time frame for '{}'", - result->identifier() + "Successfully created time frame for '{}'", result->identifier() )); + result->addPropertySubOwner(result->_timeFrame.get()); } // We initialize the renderable last as it probably has the most dependencies - if (dictionary.hasValue(KeyRenderable)) { - ghoul::Dictionary renderableDictionary = - dictionary.value(KeyRenderable); - - result->_renderable = Renderable::createFromDictionary(renderableDictionary); + if (p.renderable.has_value()) { + result->_renderable = Renderable::createFromDictionary(*p.renderable); ghoul_assert(result->_renderable, "Failed to create Renderable"); result->addPropertySubOwner(result->_renderable.get()); LDEBUG(fmt::format( "Successfully created renderable for '{}'", result->identifier() )); - // If the renderable child has a larger bounding sphere, we allow it tooverride + // If the renderable child has a larger bounding sphere, we allow it to override if (result->_renderable->boundingSphere() > result->_boundingSphere) { result->_boundingSphere = result->_renderable->boundingSphere(); - if (dictionary.hasKey(BoundingSphereInfo.identifier)) { + if (p.boundingSphere.has_value()) { LWARNING(fmt::format( "The specified property 'BoundingSphere' for '{}' was overwritten " "by a child renderable", @@ -285,37 +364,34 @@ ghoul::mm_unique_ptr SceneGraphNode::createFromDictionary( } } - if (dictionary.hasKey(KeyTag)) { - if (dictionary.hasValue(KeyTag)) { - std::string tagName = dictionary.value(KeyTag); - if (!tagName.empty()) { - result->addTag(std::move(tagName)); - } + if (p.tag.has_value()) { + if (std::holds_alternative(*p.tag)) { + result->addTag(std::get(*p.tag)); } - else if (dictionary.hasValue(KeyTag)) { - ghoul::Dictionary tagNames = dictionary.value(KeyTag); - std::string tagName; - for (std::string_view key : tagNames.keys()) { - tagName = tagNames.value(key); - if (!tagName.empty()) { - result->addTag(std::move(tagName)); + else if (std::holds_alternative>(*p.tag)) { + for (const std::string& tag : std::get>(*p.tag)) { + if (!tag.empty()) { + result->addTag(tag); } } } + else { + throw ghoul::MissingCaseException(); + } } - if (dictionary.hasKey(KeyGuiPath)) { - result->_guiPath = dictionary.value(KeyGuiPath); - result->addProperty(result->_guiPath); - } - - LDEBUG(fmt::format("Successfully created SceneGraphNode '{}'", result->identifier())); result->_lastScreenSpaceUpdateTime = std::chrono::high_resolution_clock::now(); return result; } +documentation::Documentation SceneGraphNode::Documentation() { + documentation::Documentation doc = codegen::doc(); + doc.id = "core_scene_node"; + return doc; +} + SceneGraphNode::SceneGraphNode() : properties::PropertyOwner({ "" }) , _guiHidden(GuiHiddenInfo) diff --git a/src/scene/scenegraphnode_doc.inl b/src/scene/scenegraphnode_doc.inl deleted file mode 100644 index 29f89546e6..0000000000 --- a/src/scene/scenegraphnode_doc.inl +++ /dev/null @@ -1,145 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2021 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include -#include - -namespace openspace { - -documentation::Documentation SceneGraphNode::Documentation() { - using namespace documentation; - - return { - "Scenegraph Node", - "core_scene_node", - { - { - "Identifier", - new StringVerifier, - Optional::No, - "The identifier of this scenegraph node. This name must be unique among all " - "scene graph nodes that are loaded in a specific scene. If a duplicate is " - "detected the loading of the node will fail, as will all childing that " - "depend on the node. The identifier must not contain any whitespaces or '.'." - }, - { - "Parent", - new StringAnnotationVerifier( - "If specified, this must be a name for another scenegraph node" - ), - Optional::Yes, - "This names the parent of the currently specified scenegraph node. The " - "parent must already exist in the scene graph. If not specified, the node " - "will be attached to the root of the scenegraph.", - }, - { - "Renderable", - new ReferencingVerifier("renderable"), - Optional::Yes, - "The renderable that is to be created for this scenegraph node. A renderable " - "is a component of a scenegraph node that will lead to some visual result on " - "the screen. The specifics heavily depend on the 'Type' of the renderable. " - "If no Renderable is specified, this scenegraph node is an internal node and " - "can be used for either group children, or apply common transformations to a " - "group of children." - }, - { - "Transform", - new TableVerifier({ - { - "Translation", - new ReferencingVerifier("core_transform_translation"), - Optional::Yes, - "This node describes a translation that is applied to the scenegraph " - "node and all its children. Depending on the 'Type' of the " - "translation, this can either be a static translation or a " - "time-varying one." - }, - { - "Rotation", - new ReferencingVerifier("core_transform_rotation"), - Optional::Yes, - "This nodes describes a rotation that is applied to the scenegraph " - "node and all its children. Depending on the 'Type' of the rotation, " - "this can either be a static rotation or a time-varying one." - }, - { - "Scale", - new ReferencingVerifier("core_transform_scaling"), - Optional::Yes, - "This node describes a scaling that is applied to the scenegraph " - "node and all its children. Depending on the 'Type' of the scaling, " - "this can either be a static scaling or a time-varying one." - } - }), - Optional::Yes, - "This describes a set of transformations that are applied to this scenegraph " - "node and all of its children. There are only three possible values " - "corresponding to a 'Translation', a 'Rotation', and a 'Scale'." - }, - { - "TimeFrame", - new ReferencingVerifier("core_time_frame"), - Optional::Yes, - "Specifies the time frame for when this node should be active." - }, - { - "GUI", - new TableVerifier({ - { - "Name", - new StringVerifier, - Optional::Yes, - "An optional user-facing name for this SceneGraphNode, which does " - "not have to be unique, though it is recommended, and can contain " - "any characters." - }, - { - "Path", - new StringVerifier, - Optional::Yes, - "If this value is specified, this '/' separated URI specifies the " - "location of this scenegraph node in a GUI representation, for " - "instance '/SolarSystem/Earth/Moon'." - }, - { - "Hidden", - new BoolVerifier, - Optional::Yes, - "If this value is specified, GUI applications are incouraged to " - "ignore this scenegraph node. This is most useful to trim collective " - "lists of nodes and not display, for example, barycenters." - } - }), - Optional::Yes, - "Additional information that is passed to GUI applications. These are all " - "hints and do not have any impact on the actual function of the scenegraph " - "node." - }, - - } - }; -} - -} // namespace openspace diff --git a/src/scene/translation.cpp b/src/scene/translation.cpp index 55ff79b80d..878270f827 100644 --- a/src/scene/translation.cpp +++ b/src/scene/translation.cpp @@ -34,45 +34,33 @@ #include namespace { - const char* KeyType = "Type"; + struct [[codegen::Dictionary(Translation)]] Parameters { + // The type of translation that is described in this element. The available types + // of translations depend on the configuration of the application and can be + // written to disk on application startup into the FactoryDocumentation + std::string type [[codegen::annotation("Must name a valid Translation type")]]; + }; +#include "translation_codegen.cpp" } // namespace namespace openspace { documentation::Documentation Translation::Documentation() { - using namespace documentation; - - return { - "Transformation Translation", - "core_transform_translation", - { - { - KeyType, - new StringAnnotationVerifier("Must name a valid Translation type"), - Optional::No, - "The type of translation that is described in this element. " - "The available types of translations depend on the " - "configuration of the application and can be written to disk " - "on application startup into the FactoryDocumentation." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "core_transform_translation"; + return doc; } ghoul::mm_unique_ptr Translation::createFromDictionary( const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow(Documentation(), dictionary, "Translation"); + const Parameters p = codegen::bake(dictionary); - const std::string& translationType = dictionary.value(KeyType); - ghoul::TemplateFactory* factory - = FactoryManager::ref().factory(); - Translation* result = factory->create( - translationType, + Translation* result = FactoryManager::ref().factory()->create( + p.type, dictionary, &global::memoryManager->PersistentMemory ); - result->setIdentifier("Translation"); return ghoul::mm_unique_ptr(result); } diff --git a/src/scripting/scriptscheduler.cpp b/src/scripting/scriptscheduler.cpp index 7523f91e69..72b61e117d 100644 --- a/src/scripting/scriptscheduler.cpp +++ b/src/scripting/scriptscheduler.cpp @@ -37,64 +37,52 @@ namespace { constexpr const char* KeyForwardScript = "ForwardScript"; constexpr const char* KeyBackwardScript = "BackwardScript"; constexpr const char* KeyUniversalScript = "Script"; + + struct [[codegen::Dictionary(ScheduledScript)]] Parameters { + // The time at which, when the in game time passes it, the two scripts will + // be executed. If the traversal is forwards (towards + infinity), the + // ForwardScript will be executed, otherwise the BackwardScript will be + // executed instead + std::string time; + + // The Lua script that will be executed when the specified time is passed + // independent of its direction. This script will be executed before the + // specific scripts if both versions are specified + std::optional script; + + // The Lua script that is executed when OpenSpace passes the time in a + // forward direction + std::optional forwardScript; + + // The Lua script that is executed when OpenSpace passes the time in a + // backward direction + std::optional backwardScript; + }; +#include "scriptscheduler_codegen.cpp" } // namespace namespace openspace::scripting { documentation::Documentation ScriptScheduler::Documentation() { - using namespace openspace::documentation; - - using TimeVerifier = StringVerifier; - using LuaScriptVerifier = StringVerifier; - - return { - "Scheduled Scripts", - "core_scheduledscript", - { - { - "*", - new TableVerifier({ - { - KeyTime, - new TimeVerifier, - Optional::No, - "The time at which, when the in game time passes it, the two " - "scripts will be executed. If the traversal is forwards (towards " - "+ infinity), the ForwardScript will be executed, otherwise the " - "BackwardScript will be executed instead." - }, - { - KeyUniversalScript, - new LuaScriptVerifier, - Optional::Yes, - "The Lua script that will be executed when the specified time is " - "passed independent of its direction. This script will be " - "executed before the specific scripts if both versions are " - "specified" - }, - { - KeyForwardScript, - new LuaScriptVerifier, - Optional::Yes, - "The Lua script that is executed when OpenSpace passes the time " - "in a forward direction." - }, - { - KeyBackwardScript, - new LuaScriptVerifier, - Optional::Yes, - "The Lua script that is executed when OpenSpace passes the time " - "in a backward direction." - } - }), - Optional::No - } - } - }; + // @TODO (abock, 2021-03-25) This is not really correct. This function currently + // returns the documentation for the ScheduledScript, not for the ScriptScheduler + // itself. This should be cleaned up a bit + documentation::Documentation doc = codegen::doc(); + doc.id = "core_scheduledscript"; + return doc; } using namespace openspace::interaction; +ScriptScheduler::ScheduledScript::ScheduledScript(const ghoul::Dictionary& dict) { + const Parameters p = codegen::bake(dict); + + time = Time::convertTime(p.time); + forwardScript = p.forwardScript.value_or(forwardScript); + backwardScript = p.backwardScript.value_or(backwardScript); + universalScript = p.script.value_or(universalScript); +} + void ScriptScheduler::loadScripts(std::vector scheduledScripts) { // Sort scripts by time; use a stable_sort as the user might have had an intention // specifying multiple scripts for the same time in a specific order diff --git a/src/scripting/scriptscheduler_lua.inl b/src/scripting/scriptscheduler_lua.inl index 7c63502e9f..96be784b48 100644 --- a/src/scripting/scriptscheduler_lua.inl +++ b/src/scripting/scriptscheduler_lua.inl @@ -38,35 +38,20 @@ int loadFile(lua_State* L) { return ghoul::lua::luaError(L, "filepath string is empty"); } - ghoul::Dictionary scriptsDict = ghoul::lua::loadDictionaryFromFile(fileName, L); + ghoul::Dictionary scriptsDict; + scriptsDict.setValue("Scripts", ghoul::lua::loadDictionaryFromFile(fileName, L)); documentation::testSpecificationAndThrow( scripting::ScriptScheduler::Documentation(), scriptsDict, "ScriptScheduler" ); - std::vector scripts; for (int i = 1; i <= scriptsDict.size(); ++i) { ghoul::Dictionary d = scriptsDict.value(std::to_string(i)); - scripting::ScriptScheduler::ScheduledScript script; - constexpr const char* KeyTime = "Time"; - if (d.hasValue(KeyTime)) { - script.time = Time::convertTime(d.value(KeyTime)); - } - constexpr const char* KeyForwardScript = "ForwardScript"; - if (d.hasValue(KeyForwardScript)) { - script.forwardScript = d.value(KeyForwardScript); - } - constexpr const char* KeyBackwardScript = "BackwardScript"; - if (d.hasValue(KeyBackwardScript)) { - script.backwardScript = d.value(KeyBackwardScript); - } - constexpr const char* KeyUniversalScript = "Script"; - if (d.hasValue(KeyUniversalScript)) { - script.universalScript = d.value(KeyUniversalScript); - } + scripting::ScriptScheduler::ScheduledScript script = + scripting::ScriptScheduler::ScheduledScript(d); scripts.push_back(script); } diff --git a/src/util/resourcesynchronization.cpp b/src/util/resourcesynchronization.cpp index 0e9a61449b..6d391f3701 100644 --- a/src/util/resourcesynchronization.cpp +++ b/src/util/resourcesynchronization.cpp @@ -30,67 +30,41 @@ #include namespace { - constexpr const char* KeyType = "Type"; - constexpr const char* KeyName = "Name"; + struct [[codegen::Dictionary(ResourceSynchronization)]] Parameters { + // This key specifies the type of ResourceSyncrhonization that gets created. It + // has to be one of the valid ResourceSyncrhonizations that are available for + // creation (see the FactoryDocumentation for a list of possible + // ResourceSyncrhonizations), which depends on the configration of the application + std::string type + [[codegen::annotation("A ResourceSynchronization created by a factory")]]; + + // A user readable name of this synchronization + std::string name; + }; +#include "resourcesynchronization_codegen.cpp" } // namespace namespace openspace { documentation::Documentation ResourceSynchronization::Documentation() { - using namespace openspace::documentation; - - return { - "ResourceSynchronization", - "resourceSynchronization", - { - { - KeyType, - new StringAnnotationVerifier( - "A valid ResourceSyncrhonization created by a factory" - ), - Optional::No, - "This key specifies the type of ResourceSyncrhonization that gets " - "created. It has to be one of the valid ResourceSyncrhonizations that " - "are available for creation (see the FactoryDocumentation for a list of " - "possible ResourceSyncrhonizations), which depends on the configration " - "of the application" - }, - { - KeyName, - new StringVerifier, - Optional::No, - "A user readable name of this synchronization" - }, - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "resourceSynchronization"; + return doc; } std::unique_ptr ResourceSynchronization::createFromDictionary( const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "ResourceSynchronization" - ); - - const std::string& synchronizationType = dictionary.value(KeyType); + const Parameters p = codegen::bake(dictionary); auto factory = FactoryManager::ref().factory(); ghoul_assert(factory, "ResourceSynchronization factory did not exist"); - ResourceSynchronization* sync = factory->create(synchronizationType, dictionary); + ResourceSynchronization* sync = factory->create(p.type, dictionary); + sync->_name = p.name; return std::unique_ptr(sync); } -ResourceSynchronization::ResourceSynchronization(const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "ResourceSynchronization" - ); - - _name = dictionary.value(KeyName); -} +ResourceSynchronization::ResourceSynchronization(const ghoul::Dictionary&) {} ResourceSynchronization::State ResourceSynchronization::state() const { return _state; @@ -111,14 +85,14 @@ bool ResourceSynchronization::isSyncing() const { ResourceSynchronization::CallbackHandle ResourceSynchronization::addStateChangeCallback(StateChangeCallback cb) { - std::lock_guard guard(_callbackMutex); + std::lock_guard guard(_callbackMutex); CallbackHandle callbackId = _nextCallbackId++; _stateChangeCallbacks[callbackId] = std::move(cb); return callbackId; } void ResourceSynchronization::removeStateChangeCallback(CallbackHandle id) { - std::lock_guard guard(_callbackMutex); + std::lock_guard guard(_callbackMutex); _stateChangeCallbacks.erase(id); }