Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

WarpOut.cpp 8.31 KB
Newer Older
promayon's avatar
promayon committed
1
2
3
4
/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
5
 * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
promayon's avatar
promayon committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
saubatn's avatar
saubatn committed
25
// CamiTK includes
promayon's avatar
promayon committed
26
27
28
#include "WarpOut.h"
#include <Application.h>
#include <MeshComponent.h>
29
#include <Log.h>
saubatn's avatar
saubatn committed
30
#include <Property.h>
promayon's avatar
promayon committed
31

saubatn's avatar
saubatn committed
32
// Vtk includes
promayon's avatar
promayon committed
33
34
35
36
37
38
39
40
#include <vtkPointSet.h>
#include <vtkPolyDataNormals.h>
#include <vtkDataSetSurfaceFilter.h>
#include <vtkPointData.h>
#include <vtkIdTypeArray.h>
#include <vtkCellLocator.h>
#include <vtkPolyDataConnectivityFilter.h>
#include <vtkUnstructuredGrid.h>
41
#include <vtkCallbackCommand.h>
42
#include <vtkFeatureEdges.h>
promayon's avatar
promayon committed
43

saubatn's avatar
saubatn committed
44
45
using namespace camitk;

promayon's avatar
promayon committed
46
47
48
49
50
51
//#include <vtkFeatureEdges.h>

// -------------------- WarpOut --------------------
WarpOut::WarpOut(ActionExtension* extension) : Action(extension) {
    // Setting name, description and input component
    setName("Warp Out");
52
    setDescription(tr("Move the outside points along the normal in order to thicken a volumic mesh (works only with closed- centered- mesh)."));
promayon's avatar
promayon committed
53
54
55
56
    setComponent("MeshComponent");

    // Setting classification family and tags
    setFamily("Mesh Processing");
57
58
    addTag(tr("Grow"));
    addTag(tr("Thicken"));
promayon's avatar
promayon committed
59

saubatn's avatar
saubatn committed
60
61
62
63
64
    Property* displacementProperty = new Property(tr("Displacement"), 1.0, tr("The length of the displacement of the mesh toward its normals."), "");
    displacementProperty->setAttribute("minimum", 0.0);
    displacementProperty->setAttribute("singleStep", 0.1);
    addParameter(displacementProperty);

promayon's avatar
promayon committed
65
66
67
68
69
70
}


// --------------- apply -------------------
Action::ApplyStatus WarpOut::apply() {
    // set waiting cursor and status bar
71
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
promayon's avatar
promayon committed
72
73
    Application::showStatusBarMessage("Warping out...");
    Application::resetProgressBar();
74
75
    vtkSmartPointer<vtkCallbackCommand> progressCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    progressCallback->SetCallback(&Application::vtkProgressFunction);
promayon's avatar
promayon committed
76
77

    // use the last target
78
    MeshComponent* targetMesh = dynamic_cast<MeshComponent*>(getTargets().last());
promayon's avatar
promayon committed
79
80
    vtkSmartPointer<vtkPointSet> resultPointSet;

81
    if (vtkUnstructuredGrid::SafeDownCast(targetMesh->getPointSet())) {
promayon's avatar
promayon committed
82
        resultPointSet = vtkSmartPointer<vtkUnstructuredGrid>::New();
83
    }
84
85
86
87
88
89
90
91
    else if (vtkPolyData::SafeDownCast(targetMesh->getPointSet())) {
        resultPointSet = vtkSmartPointer<vtkPolyData>::New();
    }
    else {
        QApplication::restoreOverrideCursor();
        CAMITK_WARNING(tr("Invalid mesh: the selected component \"%1\" is not a vtkUnstructuredGrid nor a vtkPolyData. Action aborted.").arg(targetMesh->getName()))
        return ABORTED;
    }
92
93

    if (resultPointSet->GetNumberOfPoints() == 0) {
94
        QApplication::restoreOverrideCursor();
95
        CAMITK_WARNING(tr("Invalid mesh: no points in the point set. Action aborted."))
96
        return ABORTED;
97
    }
98

99
100
    resultPointSet->AddObserver(vtkCommand::ProgressEvent, progressCallback);
    resultPointSet->DeepCopy(targetMesh->getPointSet());
promayon's avatar
promayon committed
101
102
103
104
105
    //-- add the id as point data in order to match normal to the points
    vtkSmartPointer<vtkIdTypeArray> pointId = vtkSmartPointer<vtkIdTypeArray>::New();
    pointId->SetNumberOfComponents(1);
    pointId->SetName("PointId");

106
    for (vtkIdType id = 0; id < resultPointSet->GetNumberOfPoints(); id++) {
promayon's avatar
promayon committed
107
108
109
110
111
112
113
        pointId->InsertNextValue(id);
    }

    resultPointSet->GetPointData()->AddArray(pointId);

    //-- extract the surface
    vtkSmartPointer<vtkDataSetSurfaceFilter> surfaceFilter = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
114
    surfaceFilter->SetInputData(resultPointSet);
115
    surfaceFilter->AddObserver(vtkCommand::ProgressEvent, progressCallback);
promayon's avatar
promayon committed
116
117
118
119
120
121
122
123
    surfaceFilter->Update();

    //-- generate the normals
    vtkSmartPointer<vtkPolyDataNormals> normalGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
    normalGenerator->SetInputConnection(surfaceFilter->GetOutputPort());
    normalGenerator->ComputePointNormalsOn();
    normalGenerator->ComputeCellNormalsOff();
    normalGenerator->SplittingOff();
124
    normalGenerator->AddObserver(vtkCommand::ProgressEvent, progressCallback);
promayon's avatar
promayon committed
125
126
127
128
129
130
131
132
    normalGenerator->Update();

    //-- Find the outer surface (see ExtractOutsideSurface Cxx example)
    vtkSmartPointer<vtkCellLocator> cellLocator = vtkSmartPointer<vtkCellLocator>::New();
    cellLocator->SetDataSet(normalGenerator->GetOutput());
    cellLocator->BuildLocator();

    // computer center and bounds
133
    double center[3] = {0.0};
promayon's avatar
promayon committed
134
    normalGenerator->GetOutput()->GetCenter(center);
135
    double bounds[6] = {0.0};
136
    if (normalGenerator->GetOutput()->GetNumberOfPoints() > 0) {
137
        normalGenerator->GetOutput()->GetPoints()->GetBounds(bounds);
138
    }
promayon's avatar
promayon committed
139
140
141
142
143
144
145
146
147
148
149
150
151

    // Now fire a ray from outside the bounds to the center and find a
    // cell. This cell should be on the outside surface
    double rayStart[3];

    for (unsigned int i = 0; i < 3; i++) {
        rayStart[i] = bounds[2 * i + 1] * 1.1;
    }

    vtkIdType cellId = -1;
    double xyz[3], t, pcoords[3];
    int subId;

152
    cellLocator->IntersectWithLine(rayStart, center, 0.0001, t, xyz, pcoords, subId, cellId);
promayon's avatar
promayon committed
153
154
155
156
157
158
159

    // use the connectivity filter to select only the outer surface
    vtkSmartPointer<vtkPolyDataConnectivityFilter> connectivityFilter = vtkSmartPointer<vtkPolyDataConnectivityFilter>::New();
    connectivityFilter->SetInputConnection(normalGenerator->GetOutputPort());
    connectivityFilter->SetExtractionModeToCellSeededRegions();
    connectivityFilter->InitializeSeedList();
    connectivityFilter->AddSeed(cellId);
160
    connectivityFilter->AddObserver(vtkCommand::ProgressEvent, progressCallback);
promayon's avatar
promayon committed
161
162
163
164
165
166
167
168
169
170
171
172
    connectivityFilter->Update();

    // Check if selected surface is closed or not
    vtkSmartPointer<vtkFeatureEdges> featureEdges = vtkSmartPointer<vtkFeatureEdges>::New();
    featureEdges->FeatureEdgesOff();
    featureEdges->BoundaryEdgesOn();
    featureEdges->NonManifoldEdgesOn();
    featureEdges->SetInputConnection(connectivityFilter->GetOutputPort());
    featureEdges->Update();

    int numberOfOpenEdges = featureEdges->GetOutput()->GetNumberOfCells();

173
    if (numberOfOpenEdges > 0) {
174
        CAMITK_INFO(tr("Surface is not closed."))
promayon's avatar
promayon committed
175
176
    }
    else {
177
        CAMITK_INFO(tr("Surface is closed."))
promayon's avatar
promayon committed
178
179
180
181
182
183
184
185
186
187
    }

    //-- get the original point id
    pointId = vtkIdTypeArray::SafeDownCast(connectivityFilter->GetOutput()->GetPointData()->GetArray("PointId"));

    //-- move along the normal (warp it)
    vtkSmartPointer<vtkDataArray> pointNormals = vtkDataArray::SafeDownCast(connectivityFilter->GetOutput()->GetPointData()->GetNormals());
    vtkIdType id;
    double pos[3];
    double normal[3];
188
    double displacement = property("Displacement").toDouble();
promayon's avatar
promayon committed
189

190
    for (vtkIdType i = 0; i < connectivityFilter->GetOutput()->GetNumberOfPoints(); i++) {
promayon's avatar
promayon committed
191
        // get the id of the point in the original point set
192
#if VTK_MAJOR_VERSION >= 7
193
194
        pointId->GetTypedTuple(i, &id);
#else         
195
        pointId->GetTupleValue(i, &id);
196
#endif
promayon's avatar
promayon committed
197
        // get the initial point position
198
        resultPointSet->GetPoint(id, pos);
promayon's avatar
promayon committed
199
200
201
202
        // get the normal
        pointNormals->GetTuple(i, normal);

        // move point
203
204
        for (unsigned int j = 0; j < 3; j++) {
            pos[j] += displacement * normal[j];
205
        }
promayon's avatar
promayon committed
206

207
        resultPointSet->GetPoints()->SetPoint(id, pos);
promayon's avatar
promayon committed
208
209
210
    }

    // Create a mesh Component
211
    new MeshComponent(resultPointSet, targetMesh->getName() + " warped");
promayon's avatar
promayon committed
212
213
214
215
216
217
218
219
    Application::refresh();

    // restore the normal cursor and progress bar
    Application::resetProgressBar();
    QApplication::restoreOverrideCursor();

    return SUCCESS;
}