C++ Qt/VTK assembly forms a linkage connecting rod

Effect

Key code

#include "View3D.h"
#include "Axis.h"

#include <vtkActor.h>
#include <vtkAppendPolyData.h >
#include <vtkAreaPicker.h>
#include <vtkAxesActor.h>
#include <vtkBox.h>
#include <vtkCamera.h>
#include <vtkCaptionActor2D.h>
#include <vtkCellArray.h>
#include <vtkCleanPolyData.h>
#include <vtkContourFilter.h>
#include <vtkCubeSource.h>
#include <vtkCylinder.h>
#include <vtkCylinderSource.h>
#include <vtkDataSet.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkImplicitBoolean.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkLine.h>
#include <vtkMinimalStandardRandomSequence.h>
#include <vtkNamedColors.h>
#include <vtkObjectFactory.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkPlane.h>
#include <vtkPolyDataMapper.h>
#include <vtkPolyDataNormals.h>
#include <vtkPropPicker.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkRendererCollection.h>
#include <vtkReverseSense.h>
#include <vtkSTLReader.h>
#include <vtkSampleFunction.h>
#include <vtkSmartPointer.h>
#include <vtkSphere.h>
#include <vtkTransform.h>
#include <vtkTransformPolyDataFilter.h>

//The main body is a rectangular parallelepiped, with two semicircles at the ends, and the center of the semicircles is a hollow
decltype(auto) View3D::buildBoard(double r, double g, double b)
{<!-- -->
    //Create a cuboid
    vtkNew<vtkCubeSource> cube;
    // Length 1, width 0.2, thickness 0.05
    cube->SetXLength(1);
    cube->SetYLength(0.05);
    cube->SetZLength(0.2);
    cube->Update();

    //Create semicircle
    vtkNew<vtkCylinderSource> cy;
    // Radius 0.1, height 0.05, center 0.5, 0, 0
    cy->SetRadius(0.1);
    cy->SetHeight(0.05);
    cy->SetCenter(0.5, 0, 0);
    cy->SetResolution(100);
    cy->Update();

    //Create independent symmetrical semicircles

    vtkNew<vtkCylinderSource> cy1;
    // Radius 0.1, height 0.05, center -0.5, 0, 0
    cy1->SetRadius(0.1);
    cy1->SetHeight(0.05);
    cy1->SetResolution(100);
    cy1->SetCenter(-0.5, 0, 0);
    cy1->Update();

    // small cylinder
    vtkNew<vtkCylinderSource> cy2;
    cy2->SetRadius(0.05);
    cy2->SetHeight(0.06);
    cy2->SetResolution(100);
    cy2->SetCenter(0.5, 0, 0);
    cy2->Update();

    // small cylinder
    vtkNew<vtkCylinderSource> cy3;
    cy3->SetRadius(0.05);
    cy3->SetHeight(0.06);
    cy3->SetResolution(100);
    cy3->SetCenter(-0.5, 0, 0);
    cy3->Update();

    //Create a combination of cuboid and semicircle
    vtkNew<vtkAssembly> assembly;
    //cube actor
    vtkNew<vtkPolyDataMapper> cudeMapper;
    cudeMapper->SetInputConnection(cube->GetOutputPort());
    vtkNew<vtkActor> cudeActor;
    cudeActor->SetMapper(cudeMapper);
    cudeActor->GetProperty()->SetColor(r, g, b);
    assembly->AddPart(cudeActor);

    vtkNew<vtkPolyDataMapper> cyMapper;
    cyMapper->SetInputConnection(cy->GetOutputPort());
    vtkNew<vtkActor> cyActor;
    cyActor->SetMapper(cyMapper);
    cyActor->GetProperty()->SetColor(r, g, b);
    assembly->AddPart(cyActor);

    vtkNew<vtkPolyDataMapper> cy1Mapper;
    cy1Mapper->SetInputConnection(cy1->GetOutputPort());
    vtkNew<vtkActor> cy1Actor;
    cy1Actor->SetMapper(cy1Mapper);
    cy1Actor->GetProperty()->SetColor(r, g, b);
    assembly->AddPart(cy1Actor);

    vtkNew<vtkPolyDataMapper> cy2Mapper;
    cy2Mapper->SetInputConnection(cy2->GetOutputPort());
    vtkNew<vtkActor> cy2Actor;
    cy2Actor->SetMapper(cy2Mapper);
    cy2Actor->GetProperty()->SetColor(0.8, 0, 0);
    assembly->AddPart(cy2Actor);

    vtkNew<vtkPolyDataMapper> cy3Mapper;
    cy3Mapper->SetInputConnection(cy3->GetOutputPort());
    vtkNew<vtkActor> cy3Actor;
    cy3Actor->SetMapper(cy3Mapper);
    cy3Actor->GetProperty()->SetColor(0.8, 0, 0);
    assembly->AddPart(cy3Actor);
    return assembly;
}

decltype(auto) View3D::buildConnectRod()
{<!-- -->
    // light green
    auto actor1 = buildBoard(0.5, 1, 0.5);
    // light yellow
    auto actor2 = buildBoard(1, 1, 0.5);
    // light blue
    auto actor3 = buildBoard(0.5, 1, 1);
    // Translate the actor
    actor1->AddPosition(0.5, 0, 0);
    actor2->AddPosition(1.5, 0.05, 0);
    actor3->AddPosition(2.5, 0.1, 0);
    vtkNew<vtkAssembly> assembly1, assembly2, assembly3;
    assembly1->AddPart(actor1);
    assembly1->AddPart(assembly2);
    assembly2->AddPart(actor2);
    assembly2->AddPart(assembly3);
    assembly3->AddPart(actor3);
    assembly2->SetOrigin(1, 0.05, 0);
    assembly3->SetOrigin(2, 0, 0);
    return std::make_tuple(vtkSmartPointer<vtkAssembly>(assembly1),
        vtkSmartPointer<vtkAssembly>(assembly2),
        vtkSmartPointer<vtkAssembly>(assembly3));
}

View3D::View3D(QWidget* parent)
    : QVTKOpenGLNativeWidget(parent)
{<!-- -->
    vtkNew<vtkRenderer> renderer;
    this->renderWindow()->AddRenderer(renderer);
    renderer->AddActor(baseAxes = getBaseAxes());
    addGuideLine(3, 0.2);
    connectRods = buildConnectRod();
    auto & amp; & amp; [actor1, actor2, actor3] = connectRods;
    renderer->AddActor(actor1);

    // rotate horizontally
    renderer->GetActiveCamera()->Azimuth(90);
    renderer->GetActiveCamera()->SetRoll(-90);
    renderer->GetActiveCamera()->Azimuth(45);
    renderer->GetActiveCamera()->Elevation(15);
    renderer->GetActiveCamera()->SetPosition(3, 3, 1);

    vtkSmartPointer<vtkOrientationMarkerWidget> widget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
    ;
    this->borderWidget = widget;
    vtkSmartPointer<vtkAxesActor> widgetAxesActor = vtkSmartPointer<vtkAxesActor>::New();
    widgetAxesActor->SetPosition(0, 0, 0);
    widgetAxesActor->SetShaftType(0);
    widgetAxesActor->SetCylinderRadius(0.02);
    //Set size
    widgetAxesActor->SetTotalLength(2, 2, 2);
    widget->SetOrientationMarker(widgetAxesActor);
    widget->SetInteractor(this->interactor());
    widget->SetEnabled(1);
    widget->InteractiveOn();

    // Connect signal slot
    connect(this, & amp;View3D::rotateBased, this, & amp;View3D::rotateBaseSlot);
    connect(this, & amp;View3D::rotateBigArmed, this, & amp;View3D::rotateBigArmSlot);
    connect(this, & amp;View3D::rotateMiddleArmed, this, & amp;View3D::rotateMiddleArmSlot);
    connect(this, & View3D::rotateSmallArmed, this, & View3D::rotateSmallArmSlot);
}

void View3D::addAxis(const Axis & amp; axis)
{<!-- -->
    vtkNew<vtkTransform> transform;
    transform->Translate(axis.xyz.x(), axis.xyz.y(), axis.xyz.z());

    vtkAxesActor* axes = vtkAxesActor::New();
    transform->RotateX(axis.xyzR.x());
    transform->RotateY(axis.xyzR.y());
    transform->RotateZ(axis.xyzR.z());
    axes->SetUserTransform(transform);
    //Set size
    axes->SetTotalLength(axis.xyzL.x(), axis.xyzL.y(), axis.xyzL.z());

    //Set arrow size
    axes->SetConeRadius(axis.xyzL[0] * 0.1);
    axes->SetCylinderRadius(axis.xyzL[0] * 0.1);
    axes->SetConeResolution(100);
    axes->SetCylinderResolution(100);

    axes->SetXAxisLabelText(axis.labels[0].toStdString().c_str());
    axes->SetYAxisLabelText(axis.labels[1].toStdString().c_str());
    axes->SetZAxisLabelText(axis.labels[2].toStdString().c_str());

    auto xLabelProperty = axes->GetXAxisCaptionActor2D();
    // zoom out
    xLabelProperty->SetWidth(axis.xyzL.x() * 0.1);

    auto yLabelProperty = axes->GetYAxisCaptionActor2D();
    yLabelProperty->SetWidth(axis.xyzL.y() * 0.1);

    auto zLabelProperty = axes->GetZAxisCaptionActor2D();
    zLabelProperty->SetWidth(axis.xyzL.z() * 0.1);

    //Add to first render
    this->renderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(axes);
    this->renderWindow()->Render();
}

void View3D::rotateBaseSlot(double angle)
{<!-- -->
    auto & amp; & amp; [a, b, c] = connectRods;
    //Reset the rotation angle of a world coordinate system to zero
    a->SetOrientation(0, 0, 0);
    // a rotates around the world coordinate system to angle
    a->RotateWXYZ(angle, 0, 0, 1);
    refresh();
}

void View3D::rotateBigArmSlot(double angle)
{<!-- -->
    auto & amp; & amp; [a, b, c] = connectRods;
    a->RotateY(angle - a->GetOrientation()[1]);
    refresh();
}

void View3D::rotateMiddleArmSlot(double angle)
{<!-- -->
    auto & amp; & amp; [a, b, c] = connectRods;
    b->RotateY(angle - b->GetOrientation()[1]);
    refresh();
}

void View3D::rotateSmallArmSlot(double angle)
{<!-- -->
    auto & amp; & amp; [a, b, c] = connectRods;
    c->RotateY(angle - c->GetOrientation()[1]);
    refresh();
}

void View3D::addGuideLine(float r, float space)
{<!-- -->
    // Create grid lines, spacing space, range -r~r
    vtkNew<vtkCellArray> lines;
    vtkNew<vtkPoints> points;
    for (size_t i = 0; i <= static_cast<unsigned long long>(r / space) * 2; i + + ) {<!-- -->
        points->InsertNextPoint(-r, r - space * i, 0);
        points->InsertNextPoint(r, r - space * i, 0);
        points->InsertNextPoint(-r + space * i, r, 0);
        points->InsertNextPoint(-r + space * i, -r, 0);
    }
    for (size_t i = 0; i <= static_cast<unsigned long long>(r / space) * 4 + 1; i + + ) {<!-- -->
        vtkNew<vtkLine> line;
        line->GetPointIds()->SetId(0, i * 2);
        line->GetPointIds()->SetId(1, i * 2 + 1);
        lines->InsertNextCell(line);
    }
    vtkNew<vtkPolyData> linesPolyData;
    linesPolyData->SetPoints(points);
    linesPolyData->SetLines(lines);
    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputData(linesPolyData);

    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);
    //Set transparency
    actor->GetProperty()->SetOpacity(0.5);
    this->renderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(actor);
    this->renderWindow()->Render();
}

vtkNew<vtkAxesActor> View3D::getBaseAxes()
{<!-- -->
    vtkNew<vtkAxesActor> axes;
    axes->SetTotalLength(1.0, 1.0, 1.0);
    axes->SetPosition(0, 0, 0);
    axes->SetShaftType(0);
    axes->SetConeResolution(100);
    axes->SetCylinderResolution(100);
    axes->SetConeRadius(0.1);
    axes->SetCylinderRadius(0.01);
    return axes;
}

void View3D::refresh()
{<!-- -->
    this->renderWindow()->Render();
}