I want to put a spline widget on a plane and move the control points to show scalar on different position dynamically. It will be convenient for user to check the change of scalar on the model.
It’s easy to use vtkStructuredGrid and vtkGeometryFilter to create a plane with color (scalar).
The class vtkGeometryFilter helps us to convert vtkStructuredGrid object to vtkPolyData object.
We have to configure vtkSplineWidget object to make it lying on the plane in the left renderer.
The algorithm class vtkProbeFilter provides us a way to compute point attributes (e.g., scalars, vectors, etc.) at specified point positions.
Its calculating result will be displayed by vtkXYPlotActor object.
In order to make the right renderer to show the scalar graph on the left plane, we have to listen for InteractionEvent to update polydata of spline widget.
All implementation details are in the following code snippet.

#include <iostream>
#include <vtkSmartPointer.h>
#include <vtkStructuredGrid.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkLight.h>
#include <vtkCamera.h>
#include <vtkActor2D.h>
#include <vtkPointData.h>
#include <vtkStructuredGridGeometryFilter.h>
#include <vtkFloatArray.h>
#include <vtkGeometryFilter.h>
#include <vtkSplineWidget.h>
#include <vtkProbeFilter.h>
#include <vtkTextProperty.h>
#include <vtkProperty2D.h>
#include <vtkXYPlotActor.h>
#include <vtkImagePlaneWidget.h>
#include <vtkPlaneSource.h>
#include <vtkImageData.h>
#include <vtkKochanekSpline.h>
#include <vtkParametricSpline.h>

using namespace std;

#define vtkPtr( var, className ) vtkSmartPointer<className> var = \
    vtkSmartPointer<className>::New()

class vtkSWCallback : public vtkCommand
{
public:
    static vtkSWCallback *New()
    {
        return new vtkSWCallback;
    }
    void Execute(vtkObject *caller, unsigned long, void*) override
    {
        vtkSplineWidget *spline = reinterpret_cast<vtkSplineWidget*>(caller);
        spline->GetPolyData(Poly);
    }
    vtkSWCallback():Poly(nullptr){};
    vtkPolyData* Poly;
};

int main()
{
    // Create a grid
    vtkPtr( structuredGrid, vtkStructuredGrid );
    vtkPtr( points, vtkPoints );

    unsigned int numi = 20;
    unsigned int numj = 30;
    unsigned int numk = 1;
    for (unsigned int k = 0; k < numk; k++) {
        for (unsigned int j = 0; j < numj; j++) {
            for (unsigned int i = 0; i < numi; i++) {
                points->InsertNextPoint(i, j, k);
            }
        }
    }

    structuredGrid->SetDimensions(numi, numj, numk);
    structuredGrid->SetPoints(points);

    vtkPtr( scalars, vtkFloatArray );
    for (int i=0; i< numi*numj*numk; i++)
    {
        scalars->InsertTuple1(i, i * 1.0 /(numi*numj*numk));
    }
    structuredGrid->GetPointData()->SetScalars( scalars );

    // convert vtkStructuredGrid object to vtkPolyData object.
    vtkPtr( geometryFilter, vtkGeometryFilter );
    geometryFilter->SetInputData( structuredGrid );
    geometryFilter->Update();

    vtkPtr( structuredGridMapper, vtkPolyDataMapper );
    structuredGridMapper->SetInputData( geometryFilter->GetOutput() );

    vtkPtr( structuredGridActor, vtkActor );
    structuredGridActor->SetMapper( structuredGridMapper );

    vtkPtr( renderer1, vtkRenderer );
    renderer1->AddActor( structuredGridActor );
    renderer1->SetBackground( 0.1, 0.2, 0.4 );
    renderer1->SetViewport( 0, 0, 0.5, 1);

    vtkPtr( renderer2, vtkRenderer );
    //renderer2->AddActor( structuredGridActor );
    renderer2->SetBackground( 1, 1, 1 );
    renderer2->SetViewport( 0.5, 0, 1, 1 );

    vtkPtr( renderWindow, vtkRenderWindow );
    renderWindow->AddRenderer( renderer1 );
    renderWindow->AddRenderer( renderer2 );

    vtkPtr( renderWindowInteractor, vtkRenderWindowInteractor );
    renderWindowInteractor->SetRenderWindow( renderWindow );

    vtkPtr( spline, vtkSplineWidget );
    spline->SetInteractor( renderWindowInteractor );
    spline->SetInputData( geometryFilter->GetOutput() );
    spline->KeyPressActivationOff();
    spline->PlaceWidget();
    spline->SetCurrentRenderer( renderer1 );
    spline->ProjectToPlaneOn();
    spline->SetProjectionNormalToZAxes();
    spline->SetProjectionPosition( 0 ); //initial plane oriented position
    spline->EnabledOn();
    spline->SetNumberOfHandles( 5 );
    spline->SetResolution( 399 );

    vtkPtr( poly, vtkPolyData );
    spline->GetPolyData( poly );

    vtkPtr( probe, vtkProbeFilter );
    probe->SetInputData( poly );
    probe->SetSourceConnection( geometryFilter->GetOutputPort() );
    probe->Update();

    vtkPolyData* data = geometryFilter->GetOutput();
    double* range = data->GetPointData()->GetScalars()->GetRange();
    cout << "range: " << range[0] << ", " << range[1] << endl;

    vtkPtr( profile, vtkXYPlotActor );
    cout << probe->GetOutput()->GetNumberOfCells() << endl;
    profile->AddDataSetInputConnection( probe->GetOutputPort() );
    profile->GetPositionCoordinate()->SetValue( 0.05, 0.05, 0 );
    profile->GetPosition2Coordinate()->SetValue( 0.95, 0.95, 0 );
    profile->SetXValuesToNormalizedArcLength();
    profile->SetTitle( "Scalar Data ");
    profile->SetXTitle( "s");
    profile->SetYTitle( "I(s)");
    profile->SetXRange( 0, 1 );
    profile->SetYRange( range[0], range[1]);
    profile->GetProperty()->SetColor( 0, 0, 0);
    profile->GetProperty()->SetLineWidth( 2 );
    profile->SetLabelFormat("%g");
    vtkTextProperty* tprop = profile->GetTitleTextProperty();
    tprop->SetColor( 1, 0, 0 );
    tprop->SetFontFamilyToArial();
    profile->SetAxisTitleTextProperty(tprop);
    profile->SetAxisLabelTextProperty(tprop);
    profile->SetTitleTextProperty(tprop);

    vtkPtr( swcb, vtkSWCallback );
    swcb->Poly = poly;
    spline->AddObserver(vtkCommand::InteractionEvent,swcb);

    renderer2->AddActor2D( profile );
    renderer1->ResetCamera();
    renderWindow->Render();
    renderWindow->SetSize( 600, 600 );
    renderWindowInteractor->Start();
    return 0;
}


A few keyboard and mouse modifiers of vtkSplineWidget, comes from VTKUsersGuide:

1) left button down on and drag one of the spherical handles to change the shape of the spline: the handles act as “control points”.
2) left button or middle button down on a line segment forming the spline allows uniform translation of the widget.
3) ctrl + middle button down on the widget enables spinning of the widget about its center.
4) right button down on the widget enables scaling of the widget. By moving the mouse “up” the render window the spline will be made bigger; by moving “down” the render window the widget will be made smaller.
5) ctrl key + right button down on any handle will erase it providing there will be two or more points remaining to form a spline.
6) shift key + right button down on any line segment will insert a handle onto the spline at the cursor position.

We realize that scalar value is bigger to close to 1, the color becomes blue, on the other hand, the scalar value is smaller to close to 0, the color becomes red.



0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You cannot copy content of this page