activate grid button and widget size
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user