activate grid button and widget size

This commit is contained in:
AndreaRigoni
2026-03-17 22:56:56 +00:00
parent 553bb7fd61
commit 92a06f6274
7 changed files with 210 additions and 2 deletions

View File

@@ -38,6 +38,13 @@
#include <vtkObjectFactory.h>
#include <vtkTextProperty.h>
#include <vtkRenderWindow.h>
#include <vtkButtonWidget.h>
#include <vtkTexturedButtonRepresentation2D.h>
#include <vtkImageData.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkCallbackCommand.h>
#include <vtkCoordinate.h>
#include <vtkRenderer.h>
#include "uLibVtkViewer.h"
@@ -82,6 +89,9 @@ void Viewer::InstallPipe() {
// Common setup
Viewport::SetupPipeline(renderWindowInteractor);
// Setup native grid button
SetupGridButton();
// BUT we want to override the style with our custom NoSpin version
vtkSmartPointer<vtkInteractorStyleNoSpin> style =
@@ -114,6 +124,89 @@ Viewer::MakeCameraOrientationWidget(vtkRenderWindowInteractor *interactor,
return widget;
}
void Viewer::SetupGridButton() {
if (!m_RenderWindow || !m_RenderWindow->GetInteractor()) return;
// Create procedural textures for the button using canvas
vtkNew<vtkImageCanvasSource2D> canvas;
canvas->SetScalarTypeToUnsignedChar();
canvas->SetNumberOfScalarComponents(4);
canvas->SetExtent(0, 63, 0, 63, 0, 0);
// State 0: OFF (Gray circle, transparent background)
canvas->SetDrawColor(0, 0, 0, 0);
canvas->FillBox(0, 63, 0, 63);
canvas->SetDrawColor(120, 120, 120, 255);
canvas->DrawCircle(32, 32, 25);
canvas->Update();
vtkNew<vtkImageData> imgOff;
imgOff->DeepCopy(canvas->GetOutput());
// State 1: ON (White circle, transparent background)
canvas->SetDrawColor(0, 0, 0, 0);
canvas->FillBox(0, 63, 0, 63);
canvas->SetDrawColor(255, 255, 255, 255);
canvas->DrawCircle(32, 32, 25);
canvas->Update();
vtkNew<vtkImageData> imgOn;
imgOn->DeepCopy(canvas->GetOutput());
vtkNew<vtkTexturedButtonRepresentation2D> rep;
rep->SetNumberOfStates(2);
rep->SetButtonTexture(0, imgOff);
rep->SetButtonTexture(1, imgOn);
m_GridButton = vtkSmartPointer<vtkButtonWidget>::New();
m_GridButton->SetInteractor(m_RenderWindow->GetInteractor());
m_GridButton->SetRepresentation(rep);
// Position it initially
UpdateGridButtonPosition();
// Callback for resize (ModifiedEvent on RenderWindow)
vtkNew<vtkCallbackCommand> resizeCallback;
resizeCallback->SetClientData(this);
resizeCallback->SetCallback([](vtkObject*, unsigned long, void* clientdata, void*){
auto* v = static_cast<Viewer*>(clientdata);
v->UpdateGridButtonPosition();
});
m_RenderWindow->AddObserver(vtkCommand::ModifiedEvent, resizeCallback);
// Callback for state change
vtkNew<vtkCallbackCommand> stateCallback;
stateCallback->SetClientData(this);
stateCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){
auto* btn = vtkButtonWidget::SafeDownCast(caller);
auto* v = static_cast<Viewer*>(clientdata);
auto* r = vtkTexturedButtonRepresentation2D::SafeDownCast(btn->GetRepresentation());
v->SetGridVisible(r->GetState() == 1);
});
m_GridButton->AddObserver(vtkCommand::StateChangedEvent, stateCallback);
m_GridButton->On();
// Set initial state
rep->SetState(GetGridVisible() ? 1 : 0);
}
void Viewer::UpdateGridButtonPosition() {
if (!m_GridButton || !m_RenderWindow) return;
auto* rep = vtkTexturedButtonRepresentation2D::SafeDownCast(m_GridButton->GetRepresentation());
if (!rep) return;
int *sz = m_RenderWindow->GetSize();
if (sz[0] == 0 || sz[1] == 0) return; // Window not yet sized or hidden
int margin_rigth = 23;
int margin_top = 170;
int btnSz = 100; // Button size in display coordinates
double bds[6] = { (double)sz[0] - btnSz - margin_rigth, (double)sz[0] - margin_rigth,
(double)sz[1] - margin_top - btnSz/2.0, (double)sz[1] - margin_top + btnSz/2.0, 0, 0 };
rep->PlaceWidget(bds);
}
void Viewer::Start() { m_RenderWindow->GetInteractor()->Start(); }
vtkRenderWindow *Viewer::GetRenderWindow() { return m_RenderWindow; }

View File

@@ -62,7 +62,11 @@ private:
void InstallPipe();
void UninstallPipe();
void SetupGridButton();
void UpdateGridButtonPosition();
vtkRenderWindow *m_RenderWindow;
vtkSmartPointer<class vtkButtonWidget> m_GridButton;
};
// template <> class Tie<Viewer> {

View File

@@ -545,12 +545,37 @@ void vtkHandlerWidget::UpdateGizmoPosition() {
if (!this->Prop3D)
return;
// Calculate scaling factor: min(object_bbox, 1/5 of viewport)
double bboxSize = 1.0;
double bounds[6];
this->Prop3D->GetBounds(bounds);
if (vtkMath::AreBoundsInitialized(bounds)) {
bboxSize = std::max({bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4]});
}
if (bboxSize < 1e-6) bboxSize = 1.0;
double screenLimit = bboxSize * 2.0; // Default if no renderer
if (this->CurrentRenderer) {
int *sz = this->CurrentRenderer->GetSize();
if (sz[1] > 0) {
double pixelSize = std::min(sz[0], sz[1]) / 5.0;
vtkCamera *cam = this->CurrentRenderer->GetActiveCamera();
if (cam->GetParallelProjection()) {
screenLimit = (pixelSize / (double)sz[1]) * 2.0 * cam->GetParallelScale();
} else {
double dist = cam->GetDistance();
double angleRad = vtkMath::Pi() * cam->GetViewAngle() / 180.0;
double viewHeightAtDist = 2.0 * dist * tan(angleRad / 2.0);
screenLimit = (pixelSize / (double)sz[1]) * viewHeightAtDist;
}
}
}
double scaleFactor = std::min(bboxSize, screenLimit);
vtkNew<vtkMatrix4x4> mat_gizmo;
mat_gizmo->Identity();
double center[3];
double bounds[6];
this->Prop3D->GetBounds(bounds);
center[0] = (bounds[0] + bounds[1]) / 2.0;
center[1] = (bounds[2] + bounds[3]) / 2.0;
center[2] = (bounds[4] + bounds[5]) / 2.0;
@@ -602,6 +627,13 @@ void vtkHandlerWidget::UpdateGizmoPosition() {
mat_gizmo->SetElement(2, 3, center[2]);
}
// Apply scaleFactor to the gizmo matrix (only top-left 3x3)
for (int j = 0; j < 3; ++j) {
for (int i = 0; i < 3; ++i) {
mat_gizmo->SetElement(i, j, mat_gizmo->GetElement(i, j) * scaleFactor);
}
}
m_AxesX->SetUserMatrix(mat_gizmo);
m_AxesY->SetUserMatrix(mat_gizmo);
m_AxesZ->SetUserMatrix(mat_gizmo);
@@ -635,6 +667,7 @@ void vtkHandlerWidget::UpdateGizmoPosition() {
// Orient RotCam actor to face 'dir'
vtkNew<vtkTransform> tcam;
tcam->PostMultiply();
tcam->Scale(scaleFactor, scaleFactor, scaleFactor);
// Default circle is in XY plane (Normal Z: 0,0,1)
double z[3] = {0, 0, 1};
double cross[3];

View File

@@ -1,6 +1,7 @@
#include "vtkQViewport.h"
#include <QVBoxLayout>
#include <QResizeEvent>
#include <vtkAxesActor.h>
#include <vtkCamera.h>
@@ -17,6 +18,7 @@ QViewport::QViewport(QWidget* parent)
: QWidget(parent)
, Viewport()
, m_VtkWidget(nullptr)
, m_GridButton(nullptr)
{
// Build the layout zero margins so VTK fills the entire widget
auto* layout = new QVBoxLayout(this);
@@ -26,6 +28,36 @@ QViewport::QViewport(QWidget* parent)
m_VtkWidget = new QVTKOpenGLNativeWidget(this);
layout->addWidget(m_VtkWidget);
// Grid Toggle Button
m_GridButton = new QPushButton(m_VtkWidget);
m_GridButton->setText("#");
m_GridButton->setFixedSize(40, 40);
m_GridButton->setToolTip("Toggle Grid");
m_GridButton->setStyleSheet(
"QPushButton {"
" border-radius: 20px;" // Perfectly circular
" background-color: rgba(40, 40, 40, 180);"
" color: white;"
" font-size: 22px;"
" border: 1.5px solid rgba(255, 255, 255, 60);"
"}"
"QPushButton:hover {"
" background-color: rgba(70, 70, 70, 200);"
" border: 1.5px solid rgba(255, 255, 255, 100);"
"}"
"QPushButton:checked {"
" background-color: rgba(0, 120, 215, 200);" // Nice "active" blue
" color: white;"
" border: 1.5px solid rgba(255, 255, 255, 120);"
"}"
"QPushButton:pressed {"
" background-color: rgba(0, 90, 160, 220);"
"}"
);
m_GridButton->setCheckable(true);
m_GridButton->setChecked(true); // Grid is on by default
connect(m_GridButton, &QPushButton::clicked, this, &QViewport::onGridButtonClicked);
// After the Qt widget exists but before the first paint,
// attach the renderer and configure the pipeline.
SetupPipeline();
@@ -64,5 +96,22 @@ vtkRenderWindowInteractor* QViewport::GetInteractor()
return m_VtkWidget->renderWindow()->GetInteractor();
}
void QViewport::onGridButtonClicked()
{
SetGridVisible(m_GridButton->isChecked());
}
void QViewport::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
if (m_GridButton) {
// Position under the gizmo (top-right corner).
// Standard CameraOrientationWidget is usually 150-180px.
int x = width() - m_GridButton->width() - 10;
int y = 160;
m_GridButton->move(x, y);
}
}
} // namespace Vtk
} // namespace uLib

View File

@@ -3,6 +3,7 @@
#include <QWidget>
#include <QVTKOpenGLNativeWidget.h>
#include <QPushButton>
#include <vtkCornerAnnotation.h>
#include <vtkOrientationMarkerWidget.h>
@@ -41,10 +42,17 @@ public:
virtual vtkRenderWindowInteractor* GetInteractor() override;
QVTKOpenGLNativeWidget* GetWidget() { return m_VtkWidget; }
protected:
virtual void resizeEvent(QResizeEvent* event) override;
private slots:
void onGridButtonClicked();
private:
void SetupPipeline();
QVTKOpenGLNativeWidget* m_VtkWidget;
QPushButton* m_GridButton;
};
} // namespace Vtk

View File

@@ -251,6 +251,22 @@ void Viewport::SelectPuppet(Puppet* prop)
Render();
}
void Viewport::SetGridVisible(bool visible)
{
if (m_GridActor) {
m_GridActor->SetVisibility(visible);
Render();
}
}
bool Viewport::GetGridVisible() const
{
if (m_GridActor) {
return m_GridActor->GetVisibility() != 0;
}
return false;
}
void Viewport::addProp(vtkProp* prop)
{
if (m_Renderer) {
@@ -270,6 +286,7 @@ void Viewport::RemoveProp(vtkProp* prop)
void Viewport::UpdateGrid()
{
if (!m_Renderer || !m_GridSource) return;
if (m_GridActor && !m_GridActor->GetVisibility()) return;
vtkCamera* camera = m_Renderer->GetActiveCamera();
if (!camera) return;

View File

@@ -61,6 +61,10 @@ public:
vtkCornerAnnotation* GetAnnotation() { return m_Annotation; }
vtkCameraOrientationWidget* GetCameraWidget(){ return m_CameraWidget; }
// Grid control
void SetGridVisible(bool visible);
bool GetGridVisible() const;
protected:
void SetupPipeline(vtkRenderWindowInteractor* iren);