Skip to content
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions library/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,16 @@
"invert_zoom": {
"type": "bool",
"default_value": "false"
},
"axis_lock": {
"axis": {
"type": "direction",
"default_value": "0,0,0"
},
"enabled": {
"type": "bool",
"default_value": "false"
}
}
}
}
23 changes: 23 additions & 0 deletions library/src/interactor_impl.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,23 @@ interactor& interactor_impl::initCommands()
std::bind(complNames, std::placeholders::_1,
std::vector<std::string>{ "field", "array", "component" }));

this->addCommand("toggle_axis_lock",
[&](const std::vector<std::string>& args)
{
check_args(args, 1, "toggle_axis_lock");
const std::string val{ this->Internals->Options.getAsString("interactor.axis_lock.axis") };
if (args[0] == val && this->Internals->Options.interactor.axis_lock.enabled)
{
this->Internals->Options.interactor.axis_lock.enabled = false;
}
else
{
this->Internals->Options.interactor.axis_lock.axis = options::parse<f3d::direction_t>(args[0]);
this->Internals->Options.interactor.axis_lock.enabled = true;
}
},
command_documentation_t{ "toggle_axis_lock direction", "lock rotation to one direction" });

this->addCommand(
"roll_camera",
[&](const std::vector<std::string>& args)
Expand Down Expand Up @@ -1363,6 +1380,9 @@ interactor& interactor_impl::initBindings()
"Verbose level", this->Internals->VerboseLevelToString(log::getVerboseLevel()));
};

auto docAxisTgl = [&](const std::string& doc, const std::string& val)
{ return std::pair(doc, (this->Internals->Options.interactor.axis_lock.enabled && val == this->Internals->Options.getAsString("interactor.axis_lock.axis") ? "ON" : "OFF")); };

// clang-format off
this->addBinding({mod_t::NONE, "W"}, "cycle_animation", "Scene", docAnim, f3d::interactor::BindingType::CYCLIC);
this->addBinding({mod_t::NONE, "C"}, "cycle_coloring field", "Scene", docField, f3d::interactor::BindingType::CYCLIC);
Expand Down Expand Up @@ -1391,6 +1411,9 @@ interactor& interactor_impl::initBindings()
this->addBinding({mod_t::NONE, "O"}, "toggle model.point_sprites.enable","Scene", std::bind(docTgl, "Point sprites rendering", std::cref(opts.model.point_sprites.enable)), f3d::interactor::BindingType::TOGGLE);
this->addBinding({mod_t::NONE, "U"}, "toggle render.background.blur.enable","Scene", std::bind(docTgl, "Blur background", std::cref(opts.render.background.blur.enable)), f3d::interactor::BindingType::TOGGLE);
this->addBinding({mod_t::NONE, "K"}, "toggle interactor.trackball","Scene", std::bind(docTgl, "Trackball interaction", std::cref(opts.interactor.trackball)), f3d::interactor::BindingType::TOGGLE);
this->addBinding({mod_t::CTRL, "X"}, "toggle_axis_lock +X", "Scene", [=]() { return docAxisTgl("Toggle x axis lock", "+X"); });
this->addBinding({mod_t::CTRL, "Y"}, "toggle_axis_lock +Y", "Scene", [=]() { return docAxisTgl("Toggle y axis lock", "+Y"); });
this->addBinding({mod_t::CTRL, "Z"}, "toggle_axis_lock +Z", "Scene", [=]() { return docAxisTgl("Toggle z axis lock", "+Z"); });
this->addBinding({mod_t::NONE, "F"}, "toggle render.hdri.ambient","Scene", std::bind(docTgl, "HDRI ambient lighting", std::cref(opts.render.hdri.ambient)), f3d::interactor::BindingType::TOGGLE);
this->addBinding({mod_t::NONE, "J"}, "toggle render.background.skybox","Scene", std::bind(docTgl, "HDRI skybox", std::cref(opts.render.background.skybox)), f3d::interactor::BindingType::TOGGLE);
this->addBinding({mod_t::NONE, "L"}, "increase_light_intensity", "Scene", std::bind(docDbl, "Increase lights intensity", std::cref(opts.render.light.intensity)), f3d::interactor::BindingType::NUMERICAL);
Expand Down
1 change: 1 addition & 0 deletions library/src/window_impl.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ void window_impl::UpdateDynamicOptions()
renderer->ShowAxis(opt.ui.axis);
renderer->SetUseTrackball(opt.interactor.trackball);
renderer->SetInvertZoom(opt.interactor.invert_zoom);
renderer->SetRotationAxis(opt.interactor.axis_lock.enabled, opt.interactor.axis_lock.axis);
}

// XXX: model.point_sprites.type only has an effect on geometry scene
Expand Down
56 changes: 41 additions & 15 deletions vtkext/private/module/vtkF3DInteractorStyle.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -169,23 +169,49 @@ void vtkF3DInteractorStyle::Rotate()
this->InterpolateTemporaryUp(0.1, ren->GetUpVector(), up);
}

// Rotate camera around the focal point about the environment's up vector
vtkNew<vtkTransform> Transform;
Transform->Identity();
const double* fp = camera->GetFocalPoint();
Transform->Translate(+fp[0], +fp[1], +fp[2]);
Transform->RotateWXYZ(rxf, ren->GetUpVector());
Transform->Translate(-fp[0], -fp[1], -fp[2]);
Transform->TransformPoint(camera->GetPosition(), camera->GetPosition());

camera->SetViewUp(up);

// Clamp parameter to `camera->Elevation()` to maintain -90 < elevation < +90
constexpr double maxAbsElevation = 90 - 1e-10;
const double elevation = vtkMath::DegreesFromRadians(
vtkMath::AngleBetweenVectors(ren->GetUpVector(), camera->GetDirectionOfProjection()) -
vtkMath::Pi() / 2);
camera->Elevation(std::clamp(ryf, -maxAbsElevation - elevation, +maxAbsElevation - elevation));
double newPos[3];
if (ren->GetUseRotationAxis())
{

// pick whichever mouse component is larger
bool use_dx = std::abs(dx) > std::abs(dy);
double delta = 20.0 * this->MotionFactor * (use_dx ? dx * 1.0 / size[0] : dy * -1.0 / size[1]);

Transform->Identity();
Transform->Translate(+fp[0], +fp[1], +fp[2]);
Transform->RotateWXYZ(delta, ren->GetRotationAxis());
Transform->Translate(-fp[0], -fp[1], -fp[2]);

Transform->TransformPoint(camera->GetPosition(), newPos);
camera->SetPosition(newPos);

double newViewUp[3];
Transform->TransformVector(camera->GetViewUp(), newViewUp);
camera->SetViewUp(newViewUp);
}
else
{
// Rotate camera around the focal point about the environment's up vector
Transform->Identity();
Transform->Translate(+fp[0], +fp[1], +fp[2]);
Transform->RotateWXYZ(rxf, ren->GetUpVector());
Transform->Translate(-fp[0], -fp[1], -fp[2]);

Transform->TransformPoint(camera->GetPosition(), newPos);
camera->SetPosition(newPos);

// Clamp parameter to `camera->Elevation()` to maintain -90 < elevation < +90
constexpr double maxAbsElevation = 90 - 1e-10;
const double elevation = vtkMath::DegreesFromRadians(
vtkMath::AngleBetweenVectors(ren->GetUpVector(), camera->GetDirectionOfProjection()) -
vtkMath::Pi() / 2);
camera->Elevation(
std::clamp(ryf, -maxAbsElevation - elevation, +maxAbsElevation - elevation));

camera->SetViewUp(up);
}

camera->OrthogonalizeViewUp();
}
Expand Down
19 changes: 19 additions & 0 deletions vtkext/private/module/vtkF3DRenderer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,25 @@ void vtkF3DRenderer::SetUseTrackball(bool use)
}
}

//----------------------------------------------------------------------------
void vtkF3DRenderer::SetRotationAxis(bool use, const std::vector<double>& axis)
{
std::array<double, 3> prev = { this->RotationAxis[0], this->RotationAxis[1],
this->RotationAxis[2] };
std::array<double, 3> curr = { axis[0], axis[1], axis[2] };

if (use != this->UseRotationAxis || curr != prev)
{
this->UseRotationAxis = use;

this->RotationAxis[0] = axis[0];
this->RotationAxis[1] = axis[1];
this->RotationAxis[2] = axis[2];

this->CheatSheetConfigured = false;
}
}

//----------------------------------------------------------------------------
void vtkF3DRenderer::UpdateActors()
{
Expand Down
9 changes: 9 additions & 0 deletions vtkext/private/module/vtkF3DRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ class vtkF3DRenderer : public vtkOpenGLRenderer
///@}

/**
* Set/Get RotationAxis
*/
void SetRotationAxis(bool use, const std::vector<double>& axis);
vtkGetMacro(UseRotationAxis, bool);
vtkGetVector3Macro(RotationAxis, double);

/**
* Reimplemented to configure:
* - ActorsProperties
* - Timer
Expand Down Expand Up @@ -580,6 +587,8 @@ class vtkF3DRenderer : public vtkOpenGLRenderer
std::optional<bool> UseOrthographicProjection = false;
bool UseTrackball = false;
bool InvertZoom = false;
bool UseRotationAxis = false;
double RotationAxis[3] = { 0.0, 0.0, 0.0 };
Comment on lines +605 to +606
Copy link
Member

@mwestphal mwestphal Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just store this as an std::optional<std::array<double, 3>>


int RaytracingSamples = 0;
double UpVector[3] = { 0.0, 1.0, 0.0 };
Expand Down