activate grid button and widget size
This commit is contained in:
@@ -38,6 +38,13 @@
|
|||||||
#include <vtkObjectFactory.h>
|
#include <vtkObjectFactory.h>
|
||||||
#include <vtkTextProperty.h>
|
#include <vtkTextProperty.h>
|
||||||
#include <vtkRenderWindow.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"
|
#include "uLibVtkViewer.h"
|
||||||
|
|
||||||
@@ -83,6 +90,9 @@ void Viewer::InstallPipe() {
|
|||||||
// Common setup
|
// Common setup
|
||||||
Viewport::SetupPipeline(renderWindowInteractor);
|
Viewport::SetupPipeline(renderWindowInteractor);
|
||||||
|
|
||||||
|
// Setup native grid button
|
||||||
|
SetupGridButton();
|
||||||
|
|
||||||
// BUT we want to override the style with our custom NoSpin version
|
// BUT we want to override the style with our custom NoSpin version
|
||||||
vtkSmartPointer<vtkInteractorStyleNoSpin> style =
|
vtkSmartPointer<vtkInteractorStyleNoSpin> style =
|
||||||
vtkSmartPointer<vtkInteractorStyleNoSpin>::New();
|
vtkSmartPointer<vtkInteractorStyleNoSpin>::New();
|
||||||
@@ -114,6 +124,89 @@ Viewer::MakeCameraOrientationWidget(vtkRenderWindowInteractor *interactor,
|
|||||||
return widget;
|
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(); }
|
void Viewer::Start() { m_RenderWindow->GetInteractor()->Start(); }
|
||||||
|
|
||||||
vtkRenderWindow *Viewer::GetRenderWindow() { return m_RenderWindow; }
|
vtkRenderWindow *Viewer::GetRenderWindow() { return m_RenderWindow; }
|
||||||
|
|||||||
@@ -62,7 +62,11 @@ private:
|
|||||||
void InstallPipe();
|
void InstallPipe();
|
||||||
void UninstallPipe();
|
void UninstallPipe();
|
||||||
|
|
||||||
|
void SetupGridButton();
|
||||||
|
void UpdateGridButtonPosition();
|
||||||
|
|
||||||
vtkRenderWindow *m_RenderWindow;
|
vtkRenderWindow *m_RenderWindow;
|
||||||
|
vtkSmartPointer<class vtkButtonWidget> m_GridButton;
|
||||||
};
|
};
|
||||||
|
|
||||||
// template <> class Tie<Viewer> {
|
// template <> class Tie<Viewer> {
|
||||||
|
|||||||
@@ -545,12 +545,37 @@ void vtkHandlerWidget::UpdateGizmoPosition() {
|
|||||||
if (!this->Prop3D)
|
if (!this->Prop3D)
|
||||||
return;
|
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;
|
vtkNew<vtkMatrix4x4> mat_gizmo;
|
||||||
mat_gizmo->Identity();
|
mat_gizmo->Identity();
|
||||||
|
|
||||||
double center[3];
|
double center[3];
|
||||||
double bounds[6];
|
|
||||||
this->Prop3D->GetBounds(bounds);
|
|
||||||
center[0] = (bounds[0] + bounds[1]) / 2.0;
|
center[0] = (bounds[0] + bounds[1]) / 2.0;
|
||||||
center[1] = (bounds[2] + bounds[3]) / 2.0;
|
center[1] = (bounds[2] + bounds[3]) / 2.0;
|
||||||
center[2] = (bounds[4] + bounds[5]) / 2.0;
|
center[2] = (bounds[4] + bounds[5]) / 2.0;
|
||||||
@@ -602,6 +627,13 @@ void vtkHandlerWidget::UpdateGizmoPosition() {
|
|||||||
mat_gizmo->SetElement(2, 3, center[2]);
|
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_AxesX->SetUserMatrix(mat_gizmo);
|
||||||
m_AxesY->SetUserMatrix(mat_gizmo);
|
m_AxesY->SetUserMatrix(mat_gizmo);
|
||||||
m_AxesZ->SetUserMatrix(mat_gizmo);
|
m_AxesZ->SetUserMatrix(mat_gizmo);
|
||||||
@@ -635,6 +667,7 @@ void vtkHandlerWidget::UpdateGizmoPosition() {
|
|||||||
// Orient RotCam actor to face 'dir'
|
// Orient RotCam actor to face 'dir'
|
||||||
vtkNew<vtkTransform> tcam;
|
vtkNew<vtkTransform> tcam;
|
||||||
tcam->PostMultiply();
|
tcam->PostMultiply();
|
||||||
|
tcam->Scale(scaleFactor, scaleFactor, scaleFactor);
|
||||||
// Default circle is in XY plane (Normal Z: 0,0,1)
|
// Default circle is in XY plane (Normal Z: 0,0,1)
|
||||||
double z[3] = {0, 0, 1};
|
double z[3] = {0, 0, 1};
|
||||||
double cross[3];
|
double cross[3];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "vtkQViewport.h"
|
#include "vtkQViewport.h"
|
||||||
|
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
|
||||||
#include <vtkAxesActor.h>
|
#include <vtkAxesActor.h>
|
||||||
#include <vtkCamera.h>
|
#include <vtkCamera.h>
|
||||||
@@ -17,6 +18,7 @@ QViewport::QViewport(QWidget* parent)
|
|||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, Viewport()
|
, Viewport()
|
||||||
, m_VtkWidget(nullptr)
|
, m_VtkWidget(nullptr)
|
||||||
|
, m_GridButton(nullptr)
|
||||||
{
|
{
|
||||||
// Build the layout – zero margins so VTK fills the entire widget
|
// Build the layout – zero margins so VTK fills the entire widget
|
||||||
auto* layout = new QVBoxLayout(this);
|
auto* layout = new QVBoxLayout(this);
|
||||||
@@ -26,6 +28,36 @@ QViewport::QViewport(QWidget* parent)
|
|||||||
m_VtkWidget = new QVTKOpenGLNativeWidget(this);
|
m_VtkWidget = new QVTKOpenGLNativeWidget(this);
|
||||||
layout->addWidget(m_VtkWidget);
|
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,
|
// After the Qt widget exists but before the first paint,
|
||||||
// attach the renderer and configure the pipeline.
|
// attach the renderer and configure the pipeline.
|
||||||
SetupPipeline();
|
SetupPipeline();
|
||||||
@@ -64,5 +96,22 @@ vtkRenderWindowInteractor* QViewport::GetInteractor()
|
|||||||
return m_VtkWidget->renderWindow()->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 Vtk
|
||||||
} // namespace uLib
|
} // namespace uLib
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QVTKOpenGLNativeWidget.h>
|
#include <QVTKOpenGLNativeWidget.h>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
#include <vtkCornerAnnotation.h>
|
#include <vtkCornerAnnotation.h>
|
||||||
#include <vtkOrientationMarkerWidget.h>
|
#include <vtkOrientationMarkerWidget.h>
|
||||||
@@ -41,10 +42,17 @@ public:
|
|||||||
virtual vtkRenderWindowInteractor* GetInteractor() override;
|
virtual vtkRenderWindowInteractor* GetInteractor() override;
|
||||||
QVTKOpenGLNativeWidget* GetWidget() { return m_VtkWidget; }
|
QVTKOpenGLNativeWidget* GetWidget() { return m_VtkWidget; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onGridButtonClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupPipeline();
|
void SetupPipeline();
|
||||||
|
|
||||||
QVTKOpenGLNativeWidget* m_VtkWidget;
|
QVTKOpenGLNativeWidget* m_VtkWidget;
|
||||||
|
QPushButton* m_GridButton;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vtk
|
} // namespace Vtk
|
||||||
|
|||||||
@@ -251,6 +251,22 @@ void Viewport::SelectPuppet(Puppet* prop)
|
|||||||
Render();
|
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)
|
void Viewport::addProp(vtkProp* prop)
|
||||||
{
|
{
|
||||||
if (m_Renderer) {
|
if (m_Renderer) {
|
||||||
@@ -270,6 +286,7 @@ void Viewport::RemoveProp(vtkProp* prop)
|
|||||||
void Viewport::UpdateGrid()
|
void Viewport::UpdateGrid()
|
||||||
{
|
{
|
||||||
if (!m_Renderer || !m_GridSource) return;
|
if (!m_Renderer || !m_GridSource) return;
|
||||||
|
if (m_GridActor && !m_GridActor->GetVisibility()) return;
|
||||||
|
|
||||||
vtkCamera* camera = m_Renderer->GetActiveCamera();
|
vtkCamera* camera = m_Renderer->GetActiveCamera();
|
||||||
if (!camera) return;
|
if (!camera) return;
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ public:
|
|||||||
vtkCornerAnnotation* GetAnnotation() { return m_Annotation; }
|
vtkCornerAnnotation* GetAnnotation() { return m_Annotation; }
|
||||||
vtkCameraOrientationWidget* GetCameraWidget(){ return m_CameraWidget; }
|
vtkCameraOrientationWidget* GetCameraWidget(){ return m_CameraWidget; }
|
||||||
|
|
||||||
|
// Grid control
|
||||||
|
void SetGridVisible(bool visible);
|
||||||
|
bool GetGridVisible() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetupPipeline(vtkRenderWindowInteractor* iren);
|
void SetupPipeline(vtkRenderWindowInteractor* iren);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user