Skip to content

Commit

Permalink
Merge pull request f3d-app#304 from mwestphal/window_doc_test
Browse files Browse the repository at this point in the history
    Add window testing
    Add window doc
    Add error ref param to image compare
    Improve TestSDKHelpers
  • Loading branch information
mwestphal authored May 30, 2022
2 parents 7776445 + 18a9a55 commit 4a985f9
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 78 deletions.
8 changes: 4 additions & 4 deletions application/F3DStarter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ int F3DStarter::Start(int argc, char** argv)
this->Internals->AppOptions.NoBackground);
f3d::image ref(this->Internals->AppOptions.Reference);
f3d::image diff;
if (!img.compare(ref, diff, this->Internals->AppOptions.RefThreshold))
double error;
if (!img.compare(ref, diff, this->Internals->AppOptions.RefThreshold, error))
{
if (this->Internals->AppOptions.Output.empty())
{
Expand All @@ -188,9 +189,8 @@ int F3DStarter::Start(int argc, char** argv)
}
else
{
f3d::log::error(
"Current rendering difference with reference image is higher than the threshold of ",
this->Internals->AppOptions.RefThreshold, ".\n");
f3d::log::error("Current rendering difference with reference image: ", error,
" is higher than the threshold of ", this->Internals->AppOptions.RefThreshold, ".\n");

img.save(this->Internals->AppOptions.Output);
diff.save(std::filesystem::path(this->Internals->AppOptions.Output)
Expand Down
28 changes: 16 additions & 12 deletions library/VTKExtensions/Rendering/vtkF3DRenderer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -219,20 +219,24 @@ void vtkF3DRenderer::ShowAxis(bool show)
// Dynamic visible axis
if (this->AxisVisible != show)
{
this->AxisWidget = nullptr;
if (show)
{
vtkNew<vtkAxesActor> axes;
this->AxisWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
this->AxisWidget->SetOrientationMarker(axes);
this->AxisWidget->SetInteractor(this->RenderWindow->GetInteractor());
this->AxisWidget->SetViewport(0.85, 0.0, 1.0, 0.15);
this->AxisWidget->On();
this->AxisWidget->InteractiveOff();
this->AxisWidget->SetKeyPressActivation(false);
}
else
{
this->AxisWidget = nullptr;
if (this->RenderWindow->GetInteractor())
{
vtkNew<vtkAxesActor> axes;
this->AxisWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
this->AxisWidget->SetOrientationMarker(axes);
this->AxisWidget->SetInteractor(this->RenderWindow->GetInteractor());
this->AxisWidget->SetViewport(0.85, 0.0, 1.0, 0.15);
this->AxisWidget->On();
this->AxisWidget->InteractiveOff();
this->AxisWidget->SetKeyPressActivation(false);
}
else
{
F3DLog::Print(F3DLog::Severity::Error, "Axis widget cannot be shown without an interactor");
}
}

this->AxisVisible = show;
Expand Down
2 changes: 1 addition & 1 deletion library/public/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class F3D_EXPORT image
image& setData(unsigned char* buffer);
unsigned char* getData() const;

bool compare(const image& reference, image& result, double threshold) const;
bool compare(const image& reference, image& result, double threshold, double& error) const;

bool operator==(const image& reference) const;
bool operator!=(const image& reference) const;
Expand Down
39 changes: 38 additions & 1 deletion library/public/window.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* @class window
* @brief Abstract class to render in a window or an image
*
* A class to render things in a window or an image.
* An icon and windowName can be set which can be shown by a window manager.
*/

#ifndef f3d_window_h
#define f3d_window_h

Expand All @@ -6,16 +14,45 @@

#include <string>

// TODO DOC
namespace f3d
{
class F3D_EXPORT window
{
public:
/**
* Use all the rendering related options to update the configuration of the window
* and the rendering stack below. This also initialize the rendering stack if needed.
* This will be called automatically when calling loader::loadFile but can also be called manually
* when needed. This must be called, either manually or automatically, before any render call.
* Return true on success, false otherwise.
*/
virtual bool update() = 0;

/**
* Perform a render of the window to the screen.
* Return true on success, false otherwise.
*/
virtual bool render() = 0;

/**
* Perform a render of the window to the screen and save the result in a f3d::image.
* Set noBackground to true to have a transparent background.
* Return the resulting f3d::image.
*/
virtual image renderToImage(bool noBackground = false) = 0;

/**
* Set the icon to be shown by a window manager.
* icon should be an unsigned char array
* iconSize should be the sizeof(icon)
* Return true on success, false otherwise.
*/
virtual bool setIcon(const void* icon, size_t iconSize) = 0;

/**
* Set the window name to be shown by a window manager.
* Return true on success, false otherwise.
*/
virtual bool setWindowName(const std::string& windowName) = 0;

protected:
Expand Down
19 changes: 13 additions & 6 deletions library/src/image.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ unsigned char* image::getData() const
}

//----------------------------------------------------------------------------
bool image::compare(const image& reference, image& result, double threshold) const
bool image::compare(const image& reference, image& result, double threshold, double& error) const
{
auto importerThis = this->Internals->GetVTKImporter();
auto importerRef = reference.Internals->GetVTKImporter();
Expand All @@ -178,24 +178,31 @@ bool image::compare(const image& reference, image& result, double threshold) con
diff->SetInputConnection(importerThis->GetOutputPort());
diff->SetImageConnection(importerRef->GetOutputPort());
diff->UpdateInformation();
double error = diff->GetThresholdedError();
error = diff->GetThresholdedError();

if (error <= threshold)
{
diff->Update();
error = diff->GetThresholdedError();
}

result.Internals->SetFromVTK(diff);

return error <= threshold;
if (error <= threshold)
{
return true;
}
else
{
result.Internals->SetFromVTK(diff);
return false;
}
}

//----------------------------------------------------------------------------
bool image::operator==(const image& reference) const
{
f3d::image diff;
return this->compare(reference, diff, 0);
double error;
return this->compare(reference, diff, 0, error);
}

//----------------------------------------------------------------------------
Expand Down
88 changes: 45 additions & 43 deletions library/src/window_impl_standard.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -172,56 +172,58 @@ void window_impl_standard::Initialize(bool withColoring, std::string fileInfo)
//----------------------------------------------------------------------------
bool window_impl_standard::update()
{
if (!this->Internals->Renderer)
{
// Renderer is missing, create a default one
this->Initialize(false, "");
}

this->Internals->RenWin->SetSize(this->Options.getAsIntVector("resolution").data());
this->Internals->RenWin->SetFullScreen(this->Options.getAsBool("fullscreen"));

if (this->Internals->Renderer)
{
this->Internals->Renderer->ShowAxis(this->Options.getAsBool("axis"));
this->Internals->Renderer->ShowEdge(this->Options.getAsBool("edges"));
this->Internals->Renderer->ShowTimer(this->Options.getAsBool("fps"));
this->Internals->Renderer->ShowFilename(this->Options.getAsBool("filename"));
this->Internals->Renderer->ShowMetaData(this->Options.getAsBool("metadata"));
this->Internals->Renderer->SetUseRaytracing(this->Options.getAsBool("raytracing"));
this->Internals->Renderer->SetRaytracingSamples(this->Options.getAsInt("samples"));
this->Internals->Renderer->SetUseRaytracingDenoiser(this->Options.getAsBool("denoise"));
this->Internals->Renderer->SetUseSSAOPass(this->Options.getAsBool("ssao"));
this->Internals->Renderer->SetUseFXAAPass(this->Options.getAsBool("fxaa"));
this->Internals->Renderer->SetUseToneMappingPass(this->Options.getAsBool("tone-mapping"));
this->Internals->Renderer->SetUseBlurBackground(this->Options.getAsBool("blur-background"));
this->Internals->Renderer->SetUseTrackball(this->Options.getAsBool("trackball"));
this->Internals->Renderer->SetHDRIFile(this->Options.getAsString("hdri"));
this->Internals->Renderer->SetUseDepthPeelingPass(this->Options.getAsBool("depth-peeling"));
this->Internals->Renderer->SetBackground(
this->Options.getAsDoubleVector("background-color").data());
this->Internals->Renderer->SetFontFile(this->Options.getAsString("font-file"));

vtkF3DRendererWithColoring* renWithColor =
vtkF3DRendererWithColoring::SafeDownCast(this->Internals->Renderer);

if (renWithColor)
{
renWithColor->SetUsePointSprites(this->Options.getAsBool("point-sprites"), false);
renWithColor->SetUseVolume(this->Options.getAsBool("volume"), false);
renWithColor->SetUseInverseOpacityFunction(this->Options.getAsBool("inverse"), false);
renWithColor->ShowScalarBar(this->Options.getAsBool("bar"), false);
renWithColor->SetScalarBarRange(this->Options.getAsDoubleVector("range"), false);
renWithColor->SetColormap(this->Options.getAsDoubleVector("colormap"), false);
renWithColor->UpdateColoringActors();
}
this->Internals->Renderer->ShowAxis(this->Options.getAsBool("axis"));
this->Internals->Renderer->ShowEdge(this->Options.getAsBool("edges"));
this->Internals->Renderer->ShowTimer(this->Options.getAsBool("fps"));
this->Internals->Renderer->ShowFilename(this->Options.getAsBool("filename"));
this->Internals->Renderer->ShowMetaData(this->Options.getAsBool("metadata"));
this->Internals->Renderer->SetUseRaytracing(this->Options.getAsBool("raytracing"));
this->Internals->Renderer->SetRaytracingSamples(this->Options.getAsInt("samples"));
this->Internals->Renderer->SetUseRaytracingDenoiser(this->Options.getAsBool("denoise"));
this->Internals->Renderer->SetUseSSAOPass(this->Options.getAsBool("ssao"));
this->Internals->Renderer->SetUseFXAAPass(this->Options.getAsBool("fxaa"));
this->Internals->Renderer->SetUseToneMappingPass(this->Options.getAsBool("tone-mapping"));
this->Internals->Renderer->SetUseBlurBackground(this->Options.getAsBool("blur-background"));
this->Internals->Renderer->SetUseTrackball(this->Options.getAsBool("trackball"));
this->Internals->Renderer->SetHDRIFile(this->Options.getAsString("hdri"));
this->Internals->Renderer->SetUseDepthPeelingPass(this->Options.getAsBool("depth-peeling"));
this->Internals->Renderer->SetBackground(
this->Options.getAsDoubleVector("background-color").data());
this->Internals->Renderer->SetFontFile(this->Options.getAsString("font-file"));

// Show grid last as it needs to know the bounding box to be able to compute its size
this->Internals->Renderer->ShowGrid(this->Options.getAsBool("grid"));
vtkF3DRendererWithColoring* renWithColor =
vtkF3DRendererWithColoring::SafeDownCast(this->Internals->Renderer);

// Print coloring info when available
if (this->Options.getAsBool("verbose"))
{
log::info(this->Internals->Renderer->GetRenderingDescription());
}
if (renWithColor)
{
renWithColor->SetUsePointSprites(this->Options.getAsBool("point-sprites"), false);
renWithColor->SetUseVolume(this->Options.getAsBool("volume"), false);
renWithColor->SetUseInverseOpacityFunction(this->Options.getAsBool("inverse"), false);
renWithColor->ShowScalarBar(this->Options.getAsBool("bar"), false);
renWithColor->SetScalarBarRange(this->Options.getAsDoubleVector("range"), false);
renWithColor->SetColormap(this->Options.getAsDoubleVector("colormap"), false);
renWithColor->UpdateColoringActors();
}

return this->Internals->UpdateCamera(this->Options);
// Show grid last as it needs to know the bounding box to be able to compute its size
this->Internals->Renderer->ShowGrid(this->Options.getAsBool("grid"));

// Print coloring info when available
if (this->Options.getAsBool("verbose"))
{
log::info(this->Internals->Renderer->GetRenderingDescription());
}
return false;

return this->Internals->UpdateCamera(this->Options);
}

//----------------------------------------------------------------------------
Expand Down
7 changes: 6 additions & 1 deletion library/testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ if(VTK_VERSION VERSION_GREATER 9.0.1)
TestSDKCompareWithFile.cxx
TestSDKInteractionDirectory.cxx
TestSDKImage.cxx
TestSDKWindowStandard.cxx
)
endif()

Expand Down Expand Up @@ -46,4 +47,8 @@ endforeach ()
set_tests_properties(libf3d::TestSDKLog PROPERTIES PASS_REGULAR_EXPRESSION "Test Info\nTest Warning\nTest Error\nTest Info Coloring")

target_link_libraries(libf3dSDKTests libf3d)
target_compile_features(libf3dSDKTests PRIVATE cxx_std_11)
if (UNIX AND NOT APPLE)
target_link_libraries(libf3dSDKTests stdc++fs)
endif ()

target_compile_features(libf3dSDKTests PRIVATE cxx_std_17)
32 changes: 27 additions & 5 deletions library/testing/TestSDKHelpers.h
Original file line number Diff line number Diff line change
@@ -1,26 +1,48 @@
#ifndef TestSDKHelpers_h
#define TestSDKHelpers_h

#include <filesystem>
#include <iostream>

class TestSDKHelpers
{
public:
static bool RenderTest(f3d::window& win, const std::string& baselinePath,
const std::string& outputPath, const std::string& name, double threshold)
const std::string& outputPath, const std::string& name, double threshold = 50,
bool noBackground = false)
{
if (baselinePath.empty() || outputPath.empty() || name.empty())
{
std::cerr << "A path or name is empty, aborting" << std::endl;
return false;
}

std::string baseline = baselinePath + name + ".png";
std::string output = outputPath + name + ".png";
std::string diff = outputPath + name + ".diff.png";

f3d::image result = win.renderToImage();
if (!std::filesystem::exists(baseline))
{
win.renderToImage(noBackground).save(output);
std::cerr << "Reference image "
<< baseline + " does not exists, current rendering has been outputted to " << output
<< std::endl;
return false;
}

f3d::image result = win.renderToImage(noBackground);
f3d::image diffRes;
double error;

bool ret = result.compare(f3d::image(baseline), diffRes, threshold);
if (!ret)
if (!result.compare(f3d::image(baseline), diffRes, threshold, error))
{
std::cerr << "Current rendering difference with reference image: " << error
<< " is higher than the threshold of " << threshold << std::endl;
result.save(output);
diffRes.save(diff);
return false;
}
return ret;
return true;
}
};
#endif
24 changes: 24 additions & 0 deletions library/testing/TestSDKWindowStandard.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <engine.h>
#include <options.h>
#include <window.h>

#include "TestSDKHelpers.h"

int TestSDKWindowStandard(int argc, char* argv[])
{
f3d::engine eng(f3d::engine::CREATE_WINDOW);
f3d::window& win = eng.getWindow();
win.setWindowName("Test");

f3d::options& options = eng.getOptions();
options.set("resolution", { 300, 300 })
.set("background-color", { 0.8, 0.2, 0.9 })
.set("verbose", true);
win.update();

// Use a higher threshold as background difference can be strong with mesa
return TestSDKHelpers::RenderTest(win, std::string(argv[1]) + "baselines/", std::string(argv[2]),
"TestSDKWindowStandard", 150)
? EXIT_SUCCESS
: EXIT_FAILURE;
}
4 changes: 2 additions & 2 deletions python/testing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# TODO: add more tests to cover all bindings
list(APPEND pyf3dTests_list
TestOptions.py
TestPythonOptions.py
)

# Test image comparison only with VTK > 9.0.1
if(VTK_VERSION VERSION_GREATER 9.0.1)
list(APPEND pyf3dTests_list
TestRender.py
TestPythonCompareWithFile.py
)
endif()

Expand Down
Loading

0 comments on commit 4a985f9

Please sign in to comment.