1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkSurfaceLICComposite.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15 #include "vtkSurfaceLICComposite.h"
16
17 #include "vtkObjectFactory.h"
18 #include "vtkPixelExtent.h"
19 #include "vtkPixelExtentIO.h"
20
21 #include <algorithm>
22 #include <cmath>
23
24 using std::deque;
25 using std::vector;
26
27 // Enable debug output
28 // 0 -- off
29 // 1 -- dump extents
30 // 2 -- all
31 #define vtkSurfaceLICCompositeDEBUG 0
32
33 //------------------------------------------------------------------------------
34 vtkObjectFactoryNewMacro(vtkSurfaceLICComposite);
35
36 //------------------------------------------------------------------------------
vtkSurfaceLICComposite()37 vtkSurfaceLICComposite::vtkSurfaceLICComposite()
38 : Pass(0)
39 , WindowExt()
40 , Strategy(COMPOSITE_AUTO)
41 , StepSize(0)
42 , NumberOfSteps(0)
43 , NormalizeVectors(1)
44 , NumberOfGuardLevels(1)
45 {
46 }
47
48 //------------------------------------------------------------------------------
49 vtkSurfaceLICComposite::~vtkSurfaceLICComposite() = default;
50
51 //------------------------------------------------------------------------------
Initialize(const vtkPixelExtent & winExt,const deque<vtkPixelExtent> & blockExts,int strategy,double stepSize,int nSteps,int normalizeVectors,int enhancedLIC,int antialias)52 void vtkSurfaceLICComposite::Initialize(const vtkPixelExtent& winExt,
53 const deque<vtkPixelExtent>& blockExts, int strategy, double stepSize, int nSteps,
54 int normalizeVectors, int enhancedLIC, int antialias)
55 {
56 this->Pass = 0;
57 this->WindowExt = winExt;
58 this->BlockExts = blockExts;
59 this->CompositeExt.clear();
60 this->GuardExt.clear();
61 this->DisjointGuardExt.clear();
62 this->Strategy = strategy;
63 this->StepSize = stepSize;
64 this->NumberOfSteps = nSteps;
65 this->NormalizeVectors = normalizeVectors;
66 // TODO -- FIXME
67 // type of NumberOfGuardLevels should be float. The change is
68 // fairly involved and needs to be thoroughly tested. Note too
69 // few guard pixels and you get an incorrect result, too many
70 // and you destroy performance and scaling. while getting this
71 // right the following will quiets dashboard warnings and keeps
72 // the existing well tested behavior.
73 this->NumberOfGuardLevels = 1;
74 // this->NumberOfGuardLevels = enhancedLIC ? 1.5 : 1;
75 this->NumberOfEEGuardPixels = enhancedLIC ? 1 : 0;
76 this->NumberOfAAGuardPixels = 2 * antialias;
77 }
78
79 //------------------------------------------------------------------------------
VectorMax(const deque<vtkPixelExtent> & exts,float * vectors,vector<float> & vMax)80 int vtkSurfaceLICComposite::VectorMax(
81 const deque<vtkPixelExtent>& exts, float* vectors, vector<float>& vMax)
82 {
83 #if vtkSurfaceLICCompositeDEBUG >= 2
84 cerr << "=====vtkSurfaceLICComposite::VectorMax" << endl;
85 #endif
86
87 // find the max on each extent
88 size_t nBlocks = exts.size();
89 vector<float> tmpMax(nBlocks, 0.0f);
90 for (size_t b = 0; b < nBlocks; ++b)
91 {
92 tmpMax[b] = this->VectorMax(exts[b], vectors);
93 }
94
95 // use larger of this extent and its neighbors
96 vMax.resize(nBlocks, 0.0f);
97 for (size_t a = 0; a < nBlocks; ++a)
98 {
99 vtkPixelExtent extA = exts[a];
100 extA.Grow(1);
101 for (size_t b = 0; b < nBlocks; ++b)
102 {
103 vtkPixelExtent extB = exts[b];
104 extB &= extA;
105
106 // it's a neighbor(or self) use the larger of ours and theirs
107 if (!extB.Empty())
108 {
109 vMax[a] = vMax[a] < tmpMax[b] ? tmpMax[b] : vMax[a];
110 }
111 }
112 }
113
114 return 0;
115 }
116
117 //------------------------------------------------------------------------------
VectorMax(const vtkPixelExtent & ext,float * vectors)118 float vtkSurfaceLICComposite::VectorMax(const vtkPixelExtent& ext, float* vectors)
119 {
120 #if vtkSurfaceLICCompositeDEBUG >= 2
121 cerr << "=====vtkSurfaceLICComposite::VectorMax" << endl;
122 #endif
123
124 int nx[2];
125 this->WindowExt.Size(nx);
126
127 // find the max over this region
128 // scaling by 1/nx since that's what LIC'er does.
129 float eMax = 0.0;
130 for (int j = ext[2]; j <= ext[3]; ++j)
131 {
132 int idx = 4 * (nx[0] * j + ext[0]);
133 for (int i = ext[0]; i <= ext[1]; ++i, idx += 4)
134 {
135 float eMag = 0.0;
136 for (int c = 0; c < 2; ++c)
137 {
138 float eVec = vectors[idx + c] / static_cast<float>(nx[c]);
139 eMag += eVec * eVec;
140 }
141 eMag = sqrt(eMag);
142 eMax = eMax < eMag ? eMag : eMax;
143 }
144 }
145
146 return eMax;
147 }
148
149 //------------------------------------------------------------------------------
MakeDecompDisjoint(const deque<vtkPixelExtent> & in,deque<vtkPixelExtent> & out,float * vectors)150 int vtkSurfaceLICComposite::MakeDecompDisjoint(
151 const deque<vtkPixelExtent>& in, deque<vtkPixelExtent>& out, float* vectors)
152 {
153 #if vtkSurfaceLICCompositeDEBUG >= 2
154 cerr << "=====vtkSurfaceLICComposite::MakeDecompDisjoint" << endl;
155 #endif
156
157 // serial implementation
158
159 // sort by size
160 deque<vtkPixelExtent> tmpIn(in);
161 sort(tmpIn.begin(), tmpIn.end());
162
163 // from largest to smallest, make it disjoint
164 // to others. This order has the best chance of
165 // leaving each rank with some data.
166 deque<vtkPixelExtent> tmpOut0;
167
168 vtkSurfaceLICComposite::MakeDecompDisjoint(tmpIn, tmpOut0);
169
170 // minimize and remove empty extents.
171 int nx[2];
172 this->WindowExt.Size(nx);
173 while (!tmpOut0.empty())
174 {
175 vtkPixelExtent outExt = tmpOut0.back();
176 tmpOut0.pop_back();
177
178 GetPixelBounds(vectors, nx[0], outExt);
179 if (!outExt.Empty())
180 {
181 out.push_back(outExt);
182 }
183 }
184
185 /*
186 // merge compatible extents
187 vtkPixelExtent::Merge(tmpOut0);
188 */
189
190 return 0;
191 }
192
193 //------------------------------------------------------------------------------
MakeDecompDisjoint(deque<vtkPixelExtent> & in,deque<vtkPixelExtent> & out)194 int vtkSurfaceLICComposite::MakeDecompDisjoint(
195 deque<vtkPixelExtent>& in, deque<vtkPixelExtent>& out)
196 {
197 while (!in.empty())
198 {
199 // for each element
200 deque<vtkPixelExtent> tmpOut(1, in.back());
201 in.pop_back();
202
203 // subtract other elements
204 // to make it disjoint
205 size_t ns = in.size();
206 for (size_t se = 0; se < ns; ++se)
207 {
208 vtkPixelExtent& selem = in[se];
209 deque<vtkPixelExtent> tmpOut2;
210 size_t nl = tmpOut.size();
211 for (size_t le = 0; le < nl; ++le)
212 {
213 vtkPixelExtent& lelem = tmpOut[le];
214 vtkPixelExtent::Subtract(lelem, selem, tmpOut2);
215 }
216 tmpOut = tmpOut2;
217 }
218
219 // append new disjoint elements
220 out.insert(out.end(), tmpOut.begin(), tmpOut.end());
221 }
222
223 return 0;
224 }
225
226 // TODO -- this is needed in part because our step size is incorrect
227 // due to anisotropic (in aspect ratio) trasnsform to texture
228 // space. see how we transform step size in surface lic painter.
229 // also there is bleeding at the edges so you do need a bit extra
230 // paddding.
231 //------------------------------------------------------------------------------
GetFudgeFactor(int nx[2])232 float vtkSurfaceLICComposite::GetFudgeFactor(int nx[2])
233 {
234 float aspect = float(nx[0]) / float(nx[1]);
235 float fudge = (aspect > 4.0f) ? 3.0f
236 : (aspect > 1.0f)
237 ? (2.0f / 3.0f) * aspect + (5.0f / 6.0f)
238 : (aspect < 0.25) ? 3.0f : (aspect < 1.0f) ? (-8.0f / 3.0f) * aspect + (25.0f / 6.0f) : 1.5f;
239 return fudge;
240 }
241
242 //------------------------------------------------------------------------------
AddGuardPixels(const deque<vtkPixelExtent> & exts,deque<vtkPixelExtent> & guardExts,deque<vtkPixelExtent> & disjointGuardExts,float * vectors)243 int vtkSurfaceLICComposite::AddGuardPixels(const deque<vtkPixelExtent>& exts,
244 deque<vtkPixelExtent>& guardExts, deque<vtkPixelExtent>& disjointGuardExts, float* vectors)
245 {
246 #if vtkSurfaceLICCompositeDEBUG >= 2
247 cerr << "=====vtkSurfaceLICComposite::AddGuardPixles" << endl;
248 #endif
249
250 int nx[2];
251 this->WindowExt.Size(nx);
252 float fudge = this->GetFudgeFactor(nx);
253 float arc = this->StepSize * this->NumberOfSteps * this->NumberOfGuardLevels * fudge;
254
255 if (this->NormalizeVectors)
256 {
257 // when normalizing velocity is always 1, all extents have the
258 // same number of guard cells.
259 int ng = static_cast<int>(arc) + this->NumberOfEEGuardPixels + this->NumberOfAAGuardPixels;
260 ng = ng < 2 ? 2 : ng;
261 // cerr << "ng=" << ng << endl;
262 deque<vtkPixelExtent> tmpExts(exts);
263 size_t nExts = tmpExts.size();
264 // add guard pixels
265 for (size_t b = 0; b < nExts; ++b)
266 {
267 tmpExts[b].Grow(ng);
268 tmpExts[b] &= this->DataSetExt;
269 }
270 guardExts = tmpExts;
271 // make sure it's disjoint
272 disjointGuardExts.clear();
273 this->MakeDecompDisjoint(tmpExts, disjointGuardExts);
274 }
275 else
276 {
277 // when not normailzing during integration we need max(V) on the LIC
278 // decomp. Each domain has the potential to require a unique number
279 // of guard cells.
280 vector<float> vectorMax;
281 this->VectorMax(exts, vectors, vectorMax);
282 // cerr << "ng=";
283 deque<vtkPixelExtent> tmpExts(exts);
284 size_t nExts = tmpExts.size();
285 // add guard pixels
286 for (size_t b = 0; b < nExts; ++b)
287 {
288 int ng = static_cast<int>(
289 vectorMax[b] * arc + this->NumberOfEEGuardPixels + this->NumberOfAAGuardPixels);
290 ng = ng < 2 ? 2 : ng;
291 // cerr << " " << ng;
292 tmpExts[b].Grow(ng);
293 tmpExts[b] &= this->DataSetExt;
294 }
295 guardExts = tmpExts;
296 // cerr << endl;
297 // make sure it's disjoint
298 disjointGuardExts.clear();
299 this->MakeDecompDisjoint(tmpExts, disjointGuardExts);
300 }
301
302 return 0;
303 }
304
305 //------------------------------------------------------------------------------
GetPixelBounds(float * rgba,int ni,vtkPixelExtent & ext)306 void vtkSurfaceLICComposite::GetPixelBounds(float* rgba, int ni, vtkPixelExtent& ext)
307 {
308 vtkPixelExtent text;
309 for (int j = ext[2]; j <= ext[3]; ++j)
310 {
311 for (int i = ext[0]; i <= ext[1]; ++i)
312 {
313 if (rgba[4 * (j * ni + i) + 3] > 0.0f)
314 {
315 text[0] = text[0] > i ? i : text[0];
316 text[1] = text[1] < i ? i : text[1];
317 text[2] = text[2] > j ? j : text[2];
318 text[3] = text[3] < j ? j : text[3];
319 }
320 }
321 }
322 ext = text;
323 }
324
325 //------------------------------------------------------------------------------
InitializeCompositeExtents(float * vectors)326 int vtkSurfaceLICComposite::InitializeCompositeExtents(float* vectors)
327 {
328 // determine screen bounds of all blocks
329 size_t nBlocks = this->BlockExts.size();
330 for (size_t b = 0; b < nBlocks; ++b)
331 {
332 this->DataSetExt |= this->BlockExts[b];
333 }
334
335 // Make all of the input block extents disjoint so that
336 // LIC is computed once per pixel.
337 this->MakeDecompDisjoint(this->BlockExts, this->CompositeExt, vectors);
338
339 // add guard cells to the new decomp that prevent artifacts
340 this->AddGuardPixels(this->CompositeExt, this->GuardExt, this->DisjointGuardExt, vectors);
341
342 #if vtkSurfaceLICCompositeDEBUG >= 1
343 vtkPixelExtentIO::Write(0, "SerViewExtent.vtk", this->WindowExt);
344 vtkPixelExtentIO::Write(0, "SerGeometryDecomp.vtk", this->BlockExts);
345 vtkPixelExtentIO::Write(0, "SerLICDecomp.vtk", this->CompositeExt);
346 vtkPixelExtentIO::Write(0, "SerLICDecompGuard.vtk", this->GuardExt);
347 vtkPixelExtentIO::Write(0, "SerLICDecompDisjointGuard.vtk", this->DisjointGuardExt);
348 #endif
349
350 return 0;
351 }
352
353 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)354 void vtkSurfaceLICComposite::PrintSelf(ostream& os, vtkIndent indent)
355 {
356 vtkObject::PrintSelf(os, indent);
357 os << *this << endl;
358 }
359
360 // ****************************************************************************
operator <<(ostream & os,vtkSurfaceLICComposite & ss)361 ostream& operator<<(ostream& os, vtkSurfaceLICComposite& ss)
362 {
363 os << "winExt=" << ss.WindowExt << endl;
364 os << "blockExts=" << endl;
365 size_t nExts = ss.BlockExts.size();
366 for (size_t i = 0; i < nExts; ++i)
367 {
368 os << " " << ss.BlockExts[i] << endl;
369 }
370 os << "compositeExts=" << endl;
371 nExts = ss.CompositeExt.size();
372 for (size_t i = 0; i < nExts; ++i)
373 {
374 os << ss.CompositeExt[i] << endl;
375 }
376 os << "guardExts=" << endl;
377 for (size_t i = 0; i < nExts; ++i)
378 {
379 os << ss.GuardExt[i] << endl;
380 }
381 os << "disjointGuardExts=" << endl;
382 for (size_t i = 0; i < nExts; ++i)
383 {
384 os << ss.DisjointGuardExt[i] << endl;
385 }
386 return os;
387 }
388