forked from Kitware/VTK
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vtkContinuousScatterplot.h
228 lines (209 loc) · 11.5 KB
/
vtkContinuousScatterplot.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPolyDataAlgorithm.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
/**
* @class vtkContinuousScatterplot
* @brief Given a 3D domain space represented by an
* unstructured grid composed of tetrahedral cells with bivariate fields, this filter
* tessellates each cell in the domain to polyhedral fragments by intersecting the
* projection of the cell into 2-D range space against two sets of cutting planes, one set
* is defined along the first field, the second set is defined along the second field. The
* volume of these subdivided polyhedral fragments can be computed and aggregated over
* cells to depict the density distribution of the data projection in the bivariate range
* space.
*
* @section Introduction
* Given a bivariate field (f1,f2) defined on an unstructured grid which is
* composed of tetrahedral cells, we can initially subdivide each cell based on its
* projection in the range into a number of fragments along the first field f1, we refer
* to these polyhedral fragments as Frag(f1) = {frag(f1)_1, frag(f1)_2, ... , frag(f1)_n},
* where frag(f1)_n refers to the nth fragment along the first field subdivision. Each
* fragment has a range value and the value difference between the neighbouring fragments
* is represented as fragment width fw_f1, which is uniformly distributed across the
* range.
* Based on the structure of Frag(f1), for each of its cell "frag(f1)_n", we
* can further subdivide this cell based on the second field f2 using fragment width
* fw_f2. The tessellation along the second field results in an even finer fragment
* collection which we refer to as Frag(f1,f2) = {frag(f1,f2)_1, frag(f1,f2)_2, ... ,
* frag(f1,f2)_m}. We can observe that Frag(f1,f2) is a finer tessellation of the domain
* than Frag(f1) and will be used to compute the density distribution in the bivariate
* range space. The algorithm for fragment computation is similar to the first stage of
* the work in [0].
* Each fragment "s" in Frag(f1,f2) has range values (f1(s), f2(s)) in the bivariate
* fields. These values can be further mapped to a 2-D bin with a resolution rexX * resY.
* The mapped bin index (binIndexX, binIndexY) of the fragment can be computed by linear
* interpolation on its range values :
* binIndexX = (int) resX * (f1(s) - f1_min) / (f1_max - f1_min)
* binIndexY = (int) resY * (f2(s) - f2_min) / (f2_max - f2_min),
* where (f1_min, f1_max) is the range in first field.
* Once we know which bin a fragment coincides, the density value in each bin equals to
* the total geometric volume of the fragments in this bin. This volume distribution
* over the bins will be exported as a point data array in the output data structure.
* If we map this 2-D bin to a 2-D image with each bin corresponding to a pixel and
* bin density to pixel transparency, then the image can be displayed as a continuous
* scatterplot.
* @section Algorithm
* The algorithm of this filter can be described as:
* Require: R.1 The domain space is an unstructured grid data set composed of
* tetrahedral cells;
* R.2 The range space contains two scalar fields, say f1 and f2.
*
* The most important step is to compute the fragments. The implementation processes
* the input grid one cell at a time, explicitly computing the intersection of the cell
* with the cutting planes defined by the fragment boundaries in each scalar field.
* In order to subdivide the cell, we need to define a list of cutting planes in each
* field. The interval between neighbouring cutting planes is related to the output 2-D
* bin resolution (resX, resY) and can be computed as :
* fw_f1 = (f1_max - f1_min) / resX
* fw_f2 = (f2_max - f2_min) / resY,
* where (f1_max,f1_min) is the scalar range of first field.
*
* 1. For each tetrahedron T in the input grid:
*
* 1.1 Subdivide the cell T based on the first field f1, we will obtain a list
* of fragments: Frag(f1) = {frag(f1)_1, frag(f1)_2, ... , frag(f1)_n}. The
* steps for subdivision can be described as:
*
* 1.1.1 For each cutting plane s with respect to the first field f1,
* its field value f1(s) = f1_min + n * fw_f1, where n refers to the n-th
* cutting plane:
*
* 1.1.2. Traverse each edge e starting from point a to b in the cell, we
* will maintain three data classes, namely fragmentFace,
* residualFace and cutSet:
* A. fragmentFace contains vertices in the current fragment.
* B. cutSet contains vertices whose range values equal to f1(s).
* This set contains the current cutting plane.
* C. residualFace contains the rest of the vertices in the cell.
* In order to classify edge vertices into these classes, the
* following case table is used for each vertex "a" :
* case 0 : f1(a)------ f1(s) ------f1(b)
* condition: f1(a) < f1(s) , f1(b) > f1(s)
* class: p(s,e), a -> fragmentFace
* p(s,e) -> cutSet
* p(s,e) -> residualFace
*
* case 1 : f1(b)------ f1(s) ------f1(a)
* condition: f1(a) > f1(s) , f1(b) < f1(s)
* class: p(s,e) -> fragmentFace
* p(s,e) -> cutSet
* a -> residualFace
*
* case 2 : f1(s),f1(a)-------------------f1(b)
* condition: f1(s) == f1(a), f1(s) <= f1(b)
* class: a -> fragmentFace
* a -> residualFace
* a -> cutSet
*
* case 3 : f1(a)-------------------f1(b), f1(s)
* condition: f1(s) > f1(a), f1(s) == f1(b)
* class: a -> fragmentFace
*
* case 4 : f1(s),f1(b)-------------------f1(a)
* condition: f1(s) < f1(a), f1(s) == f1(b)
* class: a -> residualFace
* Remark: 1. we use "->" to indicate "belongs to" relation.
* 2. p(s,e) refers to the interpolated point of range value
* f1(s) on the edge e.
*
* 1.1.3. After we have traversed every edge in a cell for the cutting plane
* s, three classes for storing fragment, cutting plane and residual
* faces are updated. The faces of the current fragment frag(f1)
* are the union of all elements in fragmentFace and cutSet.
*
* 1.2 Take the output of step 1.1, traverse each fragment in Frag(f1), define a list
* of cutting planes with respect to field f2, further subdivide the fragments in
* Frag(f1) following steps from 1.1.2 to 1.1.3. The output of this step will be
* the fragment collection Frag(f1,f2). Each fragment in Frag(f1,f2) can be further
* mapped to a 2-D bin based on its range values. The density value in each bin
* equals to the total geometric volume of the fragments in this bin. This volume
* distribution over the bins will be exported as a point data array in the output
* data structure.
*
* @section VTK Filter Design
* The input and output ports of the filter:
* Input port : the input data set should be a vtkUnstructuredGrid, with each of its
* cell defined as a tetrahedron. At least two scalar fields are
* associated with the data. The user needs to specify the name of the
* two scalar arrays beforehand.
* Output port: the output data set is a 2D image stored as a vtkImageData.
* The resolution of the output image can be set by the user.
* The volume distribution of fragments in each pixel or bin
* is stored in an point data array named "volume" in the output
* vtkImageData.
*
* @section How To Use This Filter
* Suppose we have a tetrahedral mesh stored in a vtkUnstructuredGrid, we call this
* data set "inputData". This data set has two scalar arrays whose names are "f1"
* and "f2" respectively. We would like the resolution of output image set to (resX,resY).
* Given these input, this filter can be called as follows in c++ sample code:
*
* vtkSmartPointer<vtkContinuousScatterplot> csp =
* vtkSmartPointer<vtkContinuousScatterplot>::New();
* csp->SetInputData(inputData);
* csp->SetField1("f1",resX);
* csp->SetField2("f2",resY);
* csp->Update();
*
* Then the output, "csp->GetOutput()", will be a vtkImageData containing a scalar
* array whose name is "volume". This array contains the volume distribution of the
* fragments.
*
* [0] H.Carr and D.Duke, Joint contour nets: Topological analysis of multivariate data.
* IEEE Transactions on Visualization and Computer Graphics, volume 20,
* issue 08, pages 1100-1113, 2014
*/
#ifndef vtkContinuousScatterplot_h
#define vtkContinuousScatterplot_h
#include "vtkInfovisCoreModule.h" // For export macro
#include "vtkImageAlgorithm.h"
class VTKINFOVISCORE_EXPORT vtkContinuousScatterplot : public vtkImageAlgorithm
{
public:
static vtkContinuousScatterplot* New();
vtkTypeMacro(vtkContinuousScatterplot, vtkImageAlgorithm);
void PrintSelf(ostream& os, vtkIndent indent) override;
/**
* Get the tolerance used when comparing floating point numbers for equality.
*/
vtkGetMacro(Epsilon, double);
/**
* Set the tolerance used when comparing floating point numbers for equality.
*/
vtkSetMacro(Epsilon, double);
/**
* Specify the name of the first field to be used in subdividing the dataset.
* Specify the resolution along x axis of the output image.
*/
void SetField1(const char* fieldName, vtkIdType ResX);
/**
* Specify the name of the second field to be used in subdividing the dataset.
* Specify the resolution along y axis of the output image.
*/
void SetField2(const char* fieldName, vtkIdType ResY);
protected:
vtkContinuousScatterplot();
// Configure input port to accept only vtkUnstructuredGrid.
int FillInputPortInformation(int port, vtkInformation* info) override;
// Configure out port to be a vtkImageData data set.
int FillOutputPortInformation(int port, vtkInformation* info) override;
int RequestData(
vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
// Set the tolerance used when comparing floating numbers for equality.
double Epsilon;
// Names of the scalar fields to be used in the filter.
const char* Fields[2];
// Resolution of the output image.
vtkIdType ResX, ResY;
private:
vtkContinuousScatterplot(const vtkContinuousScatterplot&) = delete;
void operator=(const vtkContinuousScatterplot&) = delete;
};
#endif