1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkQuadricDecimation.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 // Comments from Brad---
16 // FIXME: I do not have a very good method for detecting the stability of a
17 // matrix
18
19 // FIXME: improve the combination of boundary constraints and texture
20 // coordinates
21
22 // FIXME: I want to turn off copying an attribute by default and then enable
23 // it in the ComputeNumberOfComponets function
24
25 // ISSUE: CheckPlacement is not really a manifold check, should there be a
26 // topological manifold check?
27
28 // ISSUE: I know I use to think that there was an error in the way Hugues
29 // desribed the area coefficient, but it now seems wrong to me and seems to
30 // produce better results with it not squared, may be this should be some
31 // kind of user parameter? Both seem useful ie uniform area vs. more
32 // curvature dependent
33
34 // ISSUE: policy on attributes, normals should be renomrlized, should texture
35 // coordinates be clamped? or just indicate that one might want to use the
36 // array calculator to fix these?
37
38 // ISSUE: the initial value of the Attribute weights is one, this is generally
39 // not useful, ussually set around .1, but I did this because the the
40 // toggling on and off sets it to 1 and 0
41
42 #include "vtkQuadricDecimation.h"
43 #include "vtkCellArray.h"
44 #include "vtkCellData.h"
45 #include "vtkEdgeTable.h"
46 #include "vtkDoubleArray.h"
47 #include "vtkGenericCell.h"
48 #include "vtkIdList.h"
49 #include "vtkMath.h"
50 #include "vtkInformation.h"
51 #include "vtkInformationVector.h"
52 #include "vtkObjectFactory.h"
53 #include "vtkPolyData.h"
54 #include "vtkPointData.h"
55 #include "vtkPriorityQueue.h"
56 #include "vtkTriangle.h"
57
58 vtkStandardNewMacro(vtkQuadricDecimation);
59
60
61 //----------------------------------------------------------------------------
vtkQuadricDecimation()62 vtkQuadricDecimation::vtkQuadricDecimation()
63 {
64 this->Edges = vtkEdgeTable::New();
65 this->EdgeCosts = vtkPriorityQueue::New();
66 this->EndPoint1List = vtkIdList::New();
67 this->EndPoint2List = vtkIdList::New();
68 this->ErrorQuadrics = NULL;
69 this->TargetPoints = vtkDoubleArray::New();
70
71 this->TargetReduction = 0.9;
72 this->NumberOfEdgeCollapses = 0;
73 this->NumberOfComponents = 0;
74
75 this->AttributeErrorMetric = 0;
76 this->ScalarsAttribute = 1;
77 this->VectorsAttribute = 1;
78 this->NormalsAttribute = 1;
79 this->TCoordsAttribute = 1;
80 this->TensorsAttribute = 1;
81
82 this->ScalarsWeight = 0.1;
83 this->VectorsWeight = 0.1;
84 this->NormalsWeight = 0.1;
85 this->TCoordsWeight = 0.1;
86 this->TensorsWeight = 0.1;
87
88 this->ActualReduction = 0.0;
89 }
90
91 //----------------------------------------------------------------------------
~vtkQuadricDecimation()92 vtkQuadricDecimation::~vtkQuadricDecimation()
93 {
94 this->Edges->Delete();
95 this->EdgeCosts->Delete();
96 this->EndPoint1List->Delete();
97 this->EndPoint2List->Delete();
98 this->TargetPoints->Delete();
99 }
100
SetPointAttributeArray(vtkIdType ptId,const double * x)101 void vtkQuadricDecimation::SetPointAttributeArray(vtkIdType ptId,
102 const double *x)
103 {
104 int i;
105 this->Mesh->GetPoints()->SetPoint(ptId, x);
106
107 for (i = 0; i < this->NumberOfComponents; i++)
108 {
109 if (i < this->AttributeComponents[0])
110 {
111 this->Mesh->GetPointData()->GetScalars()->
112 SetComponent(ptId, i, x[3+i]/this->AttributeScale[0]);
113 }
114 else if (i < this->AttributeComponents[1])
115 {
116 this->Mesh->GetPointData()->GetVectors()->
117 SetComponent(ptId, i-this->AttributeComponents[0], x[3+i]/this->AttributeScale[1]);
118 }
119 else if (i < this->AttributeComponents[2])
120 {
121 this->Mesh->GetPointData()->GetNormals()->
122 SetComponent(ptId, i-this->AttributeComponents[1], x[3+i]/this->AttributeScale[2]);
123 }
124 else if (i < this->AttributeComponents[3])
125 {
126 this->Mesh->GetPointData()->GetTCoords()->
127 SetComponent(ptId, i-this->AttributeComponents[2], x[3+i]/this->AttributeScale[3]);
128 }
129 else if (i < this->AttributeComponents[4])
130 {
131 this->Mesh->GetPointData()->GetTensors()->
132 SetComponent(ptId, i-this->AttributeComponents[3], x[3+i]/this->AttributeScale[4]);
133 }
134 }
135 }
136
GetPointAttributeArray(vtkIdType ptId,double * x)137 void vtkQuadricDecimation::GetPointAttributeArray(vtkIdType ptId, double *x)
138 {
139 int i;
140 this->Mesh->GetPoints()->GetPoint(ptId, x);
141
142 for (i = 0; i < this->NumberOfComponents; i++)
143 {
144 if (i < this->AttributeComponents[0])
145 {
146 x[3+i] = this->Mesh->GetPointData()->GetScalars()->
147 GetComponent(ptId, i) * this->AttributeScale[0];
148 }
149 else if (i < this->AttributeComponents[1])
150 {
151 x[3+i] = this->Mesh->GetPointData()->GetVectors()->
152 GetComponent(ptId, i-this->AttributeComponents[0]) * this->AttributeScale[1];
153 }
154 else if (i < this->AttributeComponents[2])
155 {
156 x[3+i] = this->Mesh->GetPointData()->GetNormals()->
157 GetComponent(ptId, i-this->AttributeComponents[1]) * this->AttributeScale[2];
158 }
159 else if (i < this->AttributeComponents[3])
160 {
161 x[3+i] = this->Mesh->GetPointData()->GetTCoords()->
162 GetComponent(ptId, i-this->AttributeComponents[2]) * this->AttributeScale[3];
163 }
164 else if (i < this->AttributeComponents[4])
165 {
166 x[3+i] = this->Mesh->GetPointData()->GetTensors()->
167 GetComponent(ptId, i-this->AttributeComponents[3]) * this->AttributeScale[4];
168 }
169 }
170 }
171
172 //----------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)173 int vtkQuadricDecimation::RequestData(
174 vtkInformation *vtkNotUsed(request),
175 vtkInformationVector **inputVector,
176 vtkInformationVector *outputVector)
177 {
178 // get the info objects
179 vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
180 vtkInformation *outInfo = outputVector->GetInformationObject(0);
181
182 // get the input and output
183 vtkPolyData *input = vtkPolyData::SafeDownCast(
184 inInfo->Get(vtkDataObject::DATA_OBJECT()));
185 vtkPolyData *output = vtkPolyData::SafeDownCast(
186 outInfo->Get(vtkDataObject::DATA_OBJECT()));
187
188 vtkIdType numPts = input->GetNumberOfPoints();
189 vtkIdType numTris = input->GetNumberOfPolys();
190 vtkIdType edgeId, i;
191 int j;
192 double cost;
193 double *x;
194 vtkCellArray *polys;
195 vtkDataArray *attrib;
196 vtkPoints *points;
197 vtkPointData *pointData;
198 vtkIdType endPtIds[2];
199 vtkIdList *outputCellList;
200 vtkIdType npts, *pts;
201 vtkIdType numDeletedTris=0;
202
203 // check some assuptiona about the data
204 if (input->GetPolys() == NULL || input->GetPoints() == NULL ||
205 input->GetPointData() == NULL || input->GetFieldData() == NULL)
206 {
207 vtkErrorMacro("Nothing to decimate");
208 return 1;
209 }
210
211 if (input->GetPolys()->GetMaxCellSize() > 3)
212 {
213 vtkErrorMacro("Can only decimate triangles");
214 return 1;
215 }
216
217 polys = vtkCellArray::New();
218 points = vtkPoints::New();
219 pointData = vtkPointData::New();
220 outputCellList = vtkIdList::New();
221
222 // copy the input (only polys) to our working mesh
223 this->Mesh = vtkPolyData::New();
224 points->DeepCopy(input->GetPoints());
225 this->Mesh->SetPoints(points);
226 points->Delete();
227 polys->DeepCopy(input->GetPolys());
228 this->Mesh->SetPolys(polys);
229 polys->Delete();
230 if (this->AttributeErrorMetric)
231 {
232 this->Mesh->GetPointData()->DeepCopy(input->GetPointData());
233 }
234 pointData->Delete();
235 this->Mesh->GetFieldData()->PassData(input->GetFieldData());
236 this->Mesh->BuildCells();
237 this->Mesh->BuildLinks();
238
239 this->ErrorQuadrics =
240 new vtkQuadricDecimation::ErrorQuadric[numPts];
241
242 vtkDebugMacro(<<"Computing Edges");
243 this->Edges->InitEdgeInsertion(numPts, 1); // storing edge id as attribute
244 this->EdgeCosts->Allocate(this->Mesh->GetPolys()->GetNumberOfCells() * 3);
245 for (i = 0; i < this->Mesh->GetNumberOfCells(); i++)
246 {
247 this->Mesh->GetCellPoints(i, npts, pts);
248
249 for (j = 0; j < 3; j++)
250 {
251 if (this->Edges->IsEdge(pts[j], pts[(j+1)%3]) == -1)
252 {
253 // If this edge has not been processed, get an id for it, add it to
254 // the edge list (Edges), and add its endpoints to the EndPoint1List
255 // and EndPoint2List (the 2 endpoints to different lists).
256 edgeId = this->Edges->GetNumberOfEdges();
257 this->Edges->InsertEdge(pts[j], pts[(j+1)%3], edgeId);
258 this->EndPoint1List->InsertId(edgeId, pts[j]);
259 this->EndPoint2List->InsertId(edgeId, pts[(j+1)%3]);
260 }
261 }
262 }
263
264 this->UpdateProgress(0.1);
265
266 this->NumberOfComponents = 0;
267 if (this->AttributeErrorMetric)
268 {
269 this->ComputeNumberOfComponents();
270 }
271 x = new double [3+this->NumberOfComponents];
272 this->CollapseCellIds = vtkIdList::New();
273 this->TempX = new double [3+this->NumberOfComponents];
274 this->TempQuad = new double[11 + 4 * this->NumberOfComponents];
275
276 this->TempB = new double [3 + this->NumberOfComponents];
277 this->TempA = new double*[3 + this->NumberOfComponents];
278 this->TempData = new double [(3 + this->NumberOfComponents)*(3 + this->NumberOfComponents)];
279 for (i = 0; i < 3 + this->NumberOfComponents; i++)
280 {
281 this->TempA[i] = this->TempData+i*(3 + this->NumberOfComponents);
282 }
283 this->TargetPoints->SetNumberOfComponents(3+this->NumberOfComponents);
284
285 vtkDebugMacro(<<"Computing Quadrics");
286 this->InitializeQuadrics(numPts);
287 this->AddBoundaryConstraints();
288 this->UpdateProgress(0.15);
289
290 vtkDebugMacro(<<"Computing Costs");
291 // Compute the cost of and target point for collapsing each edge.
292 for (i = 0; i < this->Edges->GetNumberOfEdges(); i++)
293 {
294 if (this->AttributeErrorMetric)
295 {
296 cost = this->ComputeCost2(i, x);
297 }
298 else
299 {
300 cost = this->ComputeCost(i, x);
301 }
302 this->EdgeCosts->Insert(cost, i);
303 this->TargetPoints->InsertTuple(i, x);
304 }
305 this->UpdateProgress(0.20);
306
307 // Okay collapse edges until desired reduction is reached
308 this->ActualReduction = 0.0;
309 this->NumberOfEdgeCollapses = 0;
310 edgeId = this->EdgeCosts->Pop(0,cost);
311
312 int abort = 0;
313 while ( !abort && edgeId >= 0 && cost < VTK_DOUBLE_MAX &&
314 this->ActualReduction < this->TargetReduction )
315 {
316 if ( ! (this->NumberOfEdgeCollapses % 10000) )
317 {
318 vtkDebugMacro(<<"Collapsing edge#" << this->NumberOfEdgeCollapses);
319 this->UpdateProgress (0.20 + 0.80*this->NumberOfEdgeCollapses/numPts);
320 abort = this->GetAbortExecute();
321 }
322
323 endPtIds[0] = this->EndPoint1List->GetId(edgeId);
324 endPtIds[1] = this->EndPoint2List->GetId(edgeId);
325 this->TargetPoints->GetTuple(edgeId, x);
326
327 // check for a poorly placed point
328 if ( !this->IsGoodPlacement(endPtIds[0], endPtIds[1], x))
329 {
330 vtkDebugMacro(<<"Poor placement detected " << edgeId << " " << cost);
331 // return the point to the queue but with the max cost so that
332 // when it is recomputed it will be reconsidered
333 this->EdgeCosts->Insert(VTK_DOUBLE_MAX, edgeId);
334
335 edgeId = this->EdgeCosts->Pop(0, cost);
336 continue;
337 }
338
339 this->NumberOfEdgeCollapses++;
340
341 // Set the new coordinates of point0.
342 this->SetPointAttributeArray(endPtIds[0], x);
343 vtkDebugMacro(<<"Cost: " << cost << " Edge: "
344 << endPtIds[0] << " " << endPtIds[1]);
345
346 // Merge the quadrics of the two points.
347 this->AddQuadric(endPtIds[1], endPtIds[0]);
348
349 this->UpdateEdgeData(endPtIds[0], endPtIds[1]);
350
351 // Update the output triangles.
352 numDeletedTris += this->CollapseEdge(endPtIds[0], endPtIds[1]);
353 this->ActualReduction = (double) numDeletedTris / numTris;
354 edgeId = this->EdgeCosts->Pop(0, cost);
355 }
356
357 vtkDebugMacro(<<"Number Of Edge Collapses: "
358 << this->NumberOfEdgeCollapses << " Cost: " << cost);
359
360 // clean up working data
361 for (i = 0; i < numPts; i++)
362 {
363 delete [] this->ErrorQuadrics[i].Quadric;
364 }
365 delete [] this->ErrorQuadrics;
366 delete [] x;
367 this->CollapseCellIds->Delete();
368 delete [] this->TempX;
369 delete [] this->TempQuad;
370 delete [] this->TempB;
371 delete [] this->TempA;
372 delete [] this->TempData;
373
374 // copy the simplified mesh from the working mesh to the output mesh
375 for (i = 0; i < this->Mesh->GetNumberOfCells(); i++)
376 {
377 if (this->Mesh->GetCell(i)->GetCellType() != VTK_EMPTY_CELL)
378 {
379 outputCellList->InsertNextId(i);
380 }
381 }
382
383 output->Reset();
384 output->Allocate(this->Mesh, outputCellList->GetNumberOfIds());
385 output->GetPointData()->CopyAllocate(this->Mesh->GetPointData(),1);
386 output->CopyCells(this->Mesh, outputCellList);
387
388 this->Mesh->DeleteLinks();
389 this->Mesh->Delete();
390 outputCellList->Delete();
391
392 // renormalize, clamp attributes
393 if (this->AttributeErrorMetric)
394 {
395 if (NULL != (attrib = output->GetPointData()->GetNormals()))
396 {
397 for (i = 0; i < attrib->GetNumberOfTuples(); i++)
398 {
399 vtkMath::Normalize(attrib->GetTuple3(i));
400 }
401 }
402 // might want to add clamping texture coordinates??
403 }
404
405 return 1;
406 }
407
408 //----------------------------------------------------------------------------
InitializeQuadrics(vtkIdType numPts)409 void vtkQuadricDecimation::InitializeQuadrics(vtkIdType numPts)
410 {
411 vtkPolyData *input = this->Mesh;
412 double *QEM;
413 vtkIdType ptId;
414 int i, j;
415 vtkCellArray *polys;
416 vtkIdType npts, *pts=NULL;
417 double point0[3], point1[3], point2[3];
418 double n[3];
419 double tempP1[3], tempP2[3], d, triArea2;
420 double data[16];
421 double *A[4], x[4];
422 int index[4];
423 A[0] = data;
424 A[1] = data+4;
425 A[2] = data+8;
426 A[3] = data+12;
427
428 // allocate local QEM sparce matrix
429 QEM = new double[11 + 4 * this->NumberOfComponents];
430
431 // clear and allocate global QEM array
432 for (ptId = 0; ptId < numPts; ptId++)
433 {
434 this->ErrorQuadrics[ptId].Quadric =
435 new double[11 + 4 * this->NumberOfComponents];
436 for (i = 0; i < 11 + 4 * this->NumberOfComponents; i++)
437 {
438 this->ErrorQuadrics[ptId].Quadric[i] = 0.0;
439 }
440 }
441
442 polys = input->GetPolys();
443 // compute the QEM for each face
444 for (polys->InitTraversal(); polys->GetNextCell(npts, pts); )
445 {
446 input->GetPoint(pts[0], point0);
447 input->GetPoint(pts[1], point1);
448 input->GetPoint(pts[2], point2);
449 for (i = 0; i < 3; i++)
450 {
451 tempP1[i] = point1[i] - point0[i];
452 tempP2[i] = point2[i] - point0[i];
453 }
454 vtkMath::Cross(tempP1, tempP2, n);
455 triArea2 = vtkMath::Normalize(n);
456 //triArea2 = (triArea2 * triArea2 * 0.25);
457 triArea2 = triArea2 * 0.5;
458 // I am unsure whether this should be squared or not??
459 d = -vtkMath::Dot(n, point0);
460 // could possible add in angle weights??
461
462 // set the geometric part of the QEM
463 QEM[0] = n[0] * n[0];
464 QEM[1] = n[0] * n[1];
465 QEM[2] = n[0] * n[2];
466 QEM[3] = d * n[0];
467
468 QEM[4] = n[1] * n[1];
469 QEM[5] = n[1] * n[2];
470 QEM[6] = d * n[1];
471
472 QEM[7] = n[2] * n[2];
473 QEM[8] = d * n[2];
474
475 QEM[9] = d * d;
476 QEM[10] = 1;
477
478 if (this->AttributeErrorMetric)
479 {
480 for (i = 0; i < 3; i++)
481 {
482 A[0][i] = point0[i];
483 A[1][i] = point1[i];
484 A[2][i] = point2[i];
485 A[3][i] = n[i];
486 }
487 A[0][3] = A[1][3] = A[2][3] = 1;
488 A[3][3] = 0;
489
490 // should handle poorly condition matrix better
491 if (vtkMath::LUFactorLinearSystem(A, index, 4))
492 {
493 for (i = 0; i < this->NumberOfComponents; i++)
494 {
495 x[3] = 0;
496 if (i < this->AttributeComponents[0])
497 {
498 x[0] = input->GetPointData()->GetScalars()->GetComponent(pts[0], i) * this->AttributeScale[0];
499 x[1] = input->GetPointData()->GetScalars()->GetComponent(pts[1], i) * this->AttributeScale[0];
500 x[2] = input->GetPointData()->GetScalars()->GetComponent(pts[2], i) * this->AttributeScale[0];
501 }
502 else if (i < this->AttributeComponents[1])
503 {
504 x[0] = input->GetPointData()->GetVectors()->GetComponent(pts[0], i - this->AttributeComponents[0]) * this->AttributeScale[1];
505 x[1] = input->GetPointData()->GetVectors()->GetComponent(pts[1], i - this->AttributeComponents[0]) * this->AttributeScale[1];
506 x[2] = input->GetPointData()->GetVectors()->GetComponent(pts[2], i - this->AttributeComponents[0]) * this->AttributeScale[1];
507 }
508 else if (i < this->AttributeComponents[2])
509 {
510 x[0] = input->GetPointData()->GetNormals()->GetComponent(pts[0], i - this->AttributeComponents[1]) * this->AttributeScale[2];
511 x[1] = input->GetPointData()->GetNormals()->GetComponent(pts[1], i - this->AttributeComponents[1]) * this->AttributeScale[2];
512 x[2] = input->GetPointData()->GetNormals()->GetComponent(pts[2], i - this->AttributeComponents[1]) * this->AttributeScale[2];
513 }
514 else if (i < this->AttributeComponents[3])
515 {
516 x[0] = input->GetPointData()->GetTCoords()->GetComponent(pts[0], i - this->AttributeComponents[2]) * this->AttributeScale[3];
517 x[1] = input->GetPointData()->GetTCoords()->GetComponent(pts[1], i - this->AttributeComponents[2])* this->AttributeScale[3];
518 x[2] = input->GetPointData()->GetTCoords()->GetComponent(pts[2], i - this->AttributeComponents[2])* this->AttributeScale[3];
519 }
520 else if (i < this->AttributeComponents[4])
521 {
522 x[0] = input->GetPointData()->GetTensors()->GetComponent(pts[0], i - this->AttributeComponents[3])* this->AttributeScale[4];
523 x[1] = input->GetPointData()->GetTensors()->GetComponent(pts[1], i - this->AttributeComponents[3])* this->AttributeScale[4];
524 x[2] = input->GetPointData()->GetTensors()->GetComponent(pts[2], i - this->AttributeComponents[3])* this->AttributeScale[4];
525 }
526 vtkMath::LUSolveLinearSystem(A, index, x, 4);
527
528 // add in the contribution of this element into the QEM
529 QEM[0] += x[0] * x[0];
530 QEM[1] += x[0] * x[1];
531 QEM[2] += x[0] * x[2];
532 QEM[3] += x[3] * x[0];
533
534 QEM[4] += x[1] * x[1];
535 QEM[5] += x[1] * x[2];
536 QEM[6] += x[3] * x[1];
537
538 QEM[7] += x[2] * x[2];
539 QEM[8] += x[3] * x[2];
540
541 QEM[9] += x[3] * x[3];
542
543 QEM[11+i*4] = -x[0];
544 QEM[12+i*4] = -x[1];
545 QEM[13+i*4] = -x[2];
546 QEM[14+i*4] = -x[3];
547 }
548 }
549 else
550 {
551 vtkErrorMacro(<<"Unable to factor attribute matrix!");
552 }
553 }
554
555 // add the QEM to all point of the face
556 for (i = 0; i < 3; i++)
557 {
558 for (j = 0; j < 11 + 4 * this->NumberOfComponents; j++)
559 {
560 this->ErrorQuadrics[pts[i]].Quadric[j] += QEM[j] * triArea2;
561 }
562 }
563 }//for all triangles
564
565 delete [] QEM;
566 }
567
568
AddBoundaryConstraints(void)569 void vtkQuadricDecimation::AddBoundaryConstraints(void)
570 {
571 vtkPolyData *input = this->Mesh;
572 double *QEM;
573 vtkIdType cellId;
574 int i, j;
575 vtkIdType npts, *pts;
576 double t0[3], t1[3], t2[3];
577 double e0[3], e1[3], n[3], c, d, w;
578 vtkIdList *cellIds = vtkIdList::New();
579
580 // allocate local QEM space matrix
581 QEM = new double[11 + 4 * this->NumberOfComponents];
582
583 for (cellId = 0; cellId < input->GetNumberOfCells(); cellId++)
584 {
585 input->GetCellPoints(cellId, npts, pts);
586
587 for (i = 0; i < 3; i++)
588 {
589 input->GetCellEdgeNeighbors(cellId, pts[i], pts[(i+1)%3], cellIds);
590 if (cellIds->GetNumberOfIds() == 0)
591 {
592 // this is a boundary
593 input->GetPoint(pts[(i+2)%3], t0);
594 input->GetPoint(pts[i], t1);
595 input->GetPoint(pts[(i+1)%3], t2);
596
597 // computing a plane which is orthogonal to line t1, t2 and incident
598 // with it
599 for (j = 0; j < 3; j++)
600 {
601 e0[j] = t2[j] - t1[j];
602 }
603 for (j = 0; j < 3; j++)
604 {
605 e1[j] = t0[j] - t1[j];
606 }
607
608 // compute n so that it is orthogonal to e0 and parallel to the
609 // triangle
610 c = vtkMath::Dot(e0,e1)/(e0[0]*e0[0]+e0[1]*e0[1]+e0[2]*e0[2]);
611 for (j = 0; j < 3; j++)
612 {
613 n[j] = e1[j] - c*e0[j];
614 }
615 vtkMath::Normalize(n);
616 d = -vtkMath::Dot(n, t1);
617 w = vtkMath::Norm(e0);
618
619 //w *= w;
620 // area issue ??
621 // could possible add in angle weights??
622 QEM[0] = n[0] * n[0];
623 QEM[1] = n[0] * n[1];
624 QEM[2] = n[0] * n[2];
625 QEM[3] = d * n[0];
626
627 QEM[4] = n[1] * n[1];
628 QEM[5] = n[1] * n[2];
629 QEM[6] = d * n[1];
630
631 QEM[7] = n[2] * n[2];
632 QEM[8] = d * n[2];
633
634 QEM[9] = d * d;
635
636 QEM[10] = 1;
637
638 // need to add orthogonal plane with the other Attributes, but this
639 // is not clear??
640 // check to interaction with attribute data
641 for (j = 0; j < 11; j++)
642 {
643 this->ErrorQuadrics[pts[i]].Quadric[j] += QEM[j]*w;
644 this->ErrorQuadrics[pts[(i+1)%3]].Quadric[j] += QEM[j]*w;
645 }
646 }
647 }
648 }
649 cellIds->Delete();
650 delete [] QEM;
651 }
652
653 //----------------------------------------------------------------------------
AddQuadric(vtkIdType oldPtId,vtkIdType newPtId)654 void vtkQuadricDecimation::AddQuadric(vtkIdType oldPtId, vtkIdType newPtId)
655 {
656 int i;
657
658 for (i = 0; i < 11 + 4*this->NumberOfComponents; i++)
659 {
660 this->ErrorQuadrics[newPtId].Quadric[i] +=
661 this->ErrorQuadrics[oldPtId].Quadric[i];
662 }
663 }
664
665 //----------------------------------------------------------------------------
FindAffectedEdges(vtkIdType p1Id,vtkIdType p2Id,vtkIdList * edges)666 void vtkQuadricDecimation::FindAffectedEdges(vtkIdType p1Id, vtkIdType p2Id,
667 vtkIdList *edges)
668 {
669 unsigned short ncells;
670 vtkIdType *cells, npts, *pts, edgeId;
671 unsigned short i, j;
672
673 edges->Reset();
674 this->Mesh->GetPointCells(p2Id, ncells, cells);
675 for (i = 0; i < ncells; i++)
676 {
677 this->Mesh->GetCellPoints(cells[i], npts, pts);
678 for (j = 0; j < 3; j++)
679 {
680 if (pts[j] != p1Id && pts[j] != p2Id &&
681 (edgeId = this->Edges->IsEdge(pts[j], p2Id)) >= 0 &&
682 edges->IsId(edgeId) == -1)
683 {
684 edges->InsertNextId(edgeId);
685 }
686 }
687 }
688
689 this->Mesh->GetPointCells(p1Id, ncells, cells);
690 for (i = 0; i < ncells; i++)
691 {
692 this->Mesh->GetCellPoints(cells[i], npts, pts);
693 for (j = 0; j < 3; j++)
694 {
695 if (pts[j] != p1Id && pts[j] != p2Id &&
696 (edgeId = this->Edges->IsEdge(pts[j], p1Id)) >= 0 &&
697 edges->IsId(edgeId) == -1)
698 {
699 edges->InsertNextId(edgeId);
700 }
701 }
702 }
703 }
704
705 // FIXME: memory allocation clean up
UpdateEdgeData(vtkIdType pt0Id,vtkIdType pt1Id)706 void vtkQuadricDecimation::UpdateEdgeData(vtkIdType pt0Id, vtkIdType pt1Id)
707 {
708 vtkIdList *changedEdges = vtkIdList::New();
709 vtkIdType i, edgeId, edge[2];
710 double cost;
711
712 // Find all edges with exactly either of these 2 endpoints.
713 this->FindAffectedEdges(pt0Id, pt1Id, changedEdges);
714
715 // Reset the endpoints for these edges to reflect the new point from the
716 // collapsed edge.
717 // Add these new edges to the edge table.
718 // Remove the the changed edges from the priority queue.
719 for (i = 0; i < changedEdges->GetNumberOfIds(); i++)
720 {
721 edge[0] = this->EndPoint1List->GetId(changedEdges->GetId(i));
722 edge[1] = this->EndPoint2List->GetId(changedEdges->GetId(i));
723
724 // Remove all affected edges from the priority queue.
725 // This does not include collapsed edge.
726 this->EdgeCosts->DeleteId(changedEdges->GetId(i));
727
728 // Determine the new set of edges
729 if (edge[0] == pt1Id)
730 {
731 if (this->Edges->IsEdge(edge[1], pt0Id) == -1)
732 { // The edge will be completely new, add it.
733 edgeId = this->Edges->GetNumberOfEdges();
734 this->Edges->InsertEdge(edge[1], pt0Id, edgeId);
735 this->EndPoint1List->InsertId(edgeId, edge[1]);
736 this->EndPoint2List->InsertId(edgeId, pt0Id);
737 // Compute cost (target point/data) and add to priority cue.
738 if (this->AttributeErrorMetric)
739 {
740 cost = this->ComputeCost2(edgeId, this->TempX);
741 }
742 else
743 {
744 cost = this->ComputeCost(edgeId, this->TempX);
745 }
746 this->EdgeCosts->Insert(cost, edgeId);
747 this->TargetPoints->InsertTuple(edgeId, this->TempX);
748 }
749 }
750 else if (edge[1] == pt1Id)
751 { // The edge will be completely new, add it.
752 if (this->Edges->IsEdge(edge[0], pt0Id) == -1)
753 {
754 edgeId = this->Edges->GetNumberOfEdges();
755 this->Edges->InsertEdge(edge[0], pt0Id, edgeId);
756 this->EndPoint1List->InsertId(edgeId, edge[0]);
757 this->EndPoint2List->InsertId(edgeId, pt0Id);
758 // Compute cost (target point/data) and add to priority cue.
759 if (this->AttributeErrorMetric)
760 {
761 cost = this->ComputeCost2(edgeId, this->TempX);
762 }
763 else
764 {
765 cost = this->ComputeCost(edgeId, this->TempX);
766 }
767 this->EdgeCosts->Insert(cost, edgeId);
768 this->TargetPoints->InsertTuple(edgeId, this->TempX);
769 }
770 }
771 else
772 { // This edge already has one point as the merged point.
773 if (this->AttributeErrorMetric)
774 {
775 cost = this->ComputeCost2(changedEdges->GetId(i), this->TempX);
776 }
777 else
778 {
779 cost = this->ComputeCost(changedEdges->GetId(i), this->TempX);
780 }
781 this->EdgeCosts->Insert(cost, changedEdges->GetId(i));
782 this->TargetPoints->InsertTuple(changedEdges->GetId(i), this->TempX);
783 }
784 }
785
786 changedEdges->Delete();
787 return;
788 }
789
790 //----------------------------------------------------------------------------
ComputeCost(vtkIdType edgeId,double * x)791 double vtkQuadricDecimation::ComputeCost(vtkIdType edgeId, double *x)
792 {
793 static const double errorNumber = 1e-10;
794 double temp[3], A[3][3], b[3];
795 vtkIdType pointIds[2];
796 double cost = 0.0;
797 double *index;
798 int i, j;
799 double newPoint [4];
800 double v[3], c, norm, normTemp, temp2[3];
801 double pt1[3], pt2[3];
802
803 pointIds[0] = this->EndPoint1List->GetId(edgeId);
804 pointIds[1] = this->EndPoint2List->GetId(edgeId);
805
806 for (i = 0; i < 11 + 4 * this->NumberOfComponents; i++)
807 {
808 this->TempQuad[i] = this->ErrorQuadrics[pointIds[0]].Quadric[i] +
809 this->ErrorQuadrics[pointIds[1]].Quadric[i];
810 }
811
812 A[0][0] = this->TempQuad[0];
813 A[0][1] = A[1][0] = this->TempQuad[1];
814 A[0][2] = A[2][0] = this->TempQuad[2];
815 A[1][1] = this->TempQuad[4];
816 A[1][2] = A[2][1] = this->TempQuad[5];
817 A[2][2] = this->TempQuad[7];
818
819 b[0] = -this->TempQuad[3];
820 b[1] = -this->TempQuad[6];
821 b[2] = -this->TempQuad[8];
822
823 norm = vtkMath::Norm(A[0]);
824 normTemp = vtkMath::Norm(A[1]);
825 norm = norm > normTemp ? norm : normTemp;
826 normTemp = vtkMath::Norm(A[2]);
827 norm = norm > normTemp ? norm : normTemp;
828
829 if (fabs(vtkMath::Determinant3x3(A))/(norm*norm*norm) > errorNumber)
830 {
831 // it would be better to use the normal of the matrix to test singularity??
832 vtkMath::LinearSolve3x3(A, b, x);
833 vtkMath::Multiply3x3(A,x,temp);
834 // error too high, backup plans
835 }
836 else
837 {
838 // cheapest point along the edge
839 this->Mesh->GetPoints()->GetPoint(pointIds[0], pt1);
840 this->Mesh->GetPoints()->GetPoint(pointIds[1], pt2);
841 v[0] = pt2[0] - pt1[0];
842 v[1] = pt2[1] - pt1[1];
843 v[2] = pt2[2] - pt1[2];
844
845 // equation for the edge pt1 + c * v
846 // attempt least squares fit for c for A*(pt1 + c * v) = b
847 vtkMath::Multiply3x3(A,v,temp2);
848 if (vtkMath::Dot(temp2, temp2) > errorNumber)
849 {
850 vtkMath::Multiply3x3(A,pt1,temp);
851 for (i = 0; i < 3; i++)
852 temp[i] = b[i] - temp[i];
853 c = vtkMath::Dot(temp2, temp) / vtkMath::Dot(temp2, temp2);
854 for (i = 0; i < 3; i++)
855 x[i] = pt1[i]+c*v[i];
856 }
857 else
858 {
859 // use mid point
860 // might want to change to best of mid and end points??
861 for (i = 0; i < 3; i++)
862 {
863 x[i] = 0.5*(pt1[i]+pt2[i]);
864 }
865 }
866 }
867
868 newPoint[0] = x[0];
869 newPoint[1] = x[1];
870 newPoint[2] = x[2];
871 newPoint[3] = 1;
872
873 // Compute the cost
874 // x'*quad*x
875 index = this->TempQuad;
876 for (i = 0; i < 4; i++)
877 {
878 cost += (*index++)*newPoint[i]*newPoint[i];
879 for (j = i +1; j < 4; j++)
880 {
881 cost += 2.0*(*index++)*newPoint[i]*newPoint[j];
882 }
883 }
884
885 return cost;
886 }
887
888
889 //----------------------------------------------------------------------------
ComputeCost2(vtkIdType edgeId,double * x)890 double vtkQuadricDecimation::ComputeCost2(vtkIdType edgeId, double *x)
891 {
892 // this function is so ugly because the functionality of converting an QEM
893 // into a dence matrix was not extracted into a separate function and
894 // neither was multiplication and some other matrix and vector primitives
895 static const double errorNumber = 1e-10;
896 vtkIdType pointIds[2];
897 double cost = 0.0;
898 int i, j;
899 int solveOk;
900
901 pointIds[0] = this->EndPoint1List->GetId(edgeId);
902 pointIds[1] = this->EndPoint2List->GetId(edgeId);
903
904 for (i = 0; i < 11 + 4 * this->NumberOfComponents; i++)
905 {
906 this->TempQuad[i] = this->ErrorQuadrics[pointIds[0]].Quadric[i] +
907 this->ErrorQuadrics[pointIds[1]].Quadric[i];
908 }
909
910 // copy the temp quad into TempA
911 // converting from the sparce matrix format into a dence
912 this->TempA[0][0] = this->TempQuad[0];
913 this->TempA[0][1] = this->TempA[1][0] = this->TempQuad[1];
914 this->TempA[0][2] = this->TempA[2][0] = this->TempQuad[2];
915 this->TempA[1][1] = this->TempQuad[4];
916 this->TempA[1][2] = this->TempA[2][1] = this->TempQuad[5];
917 this->TempA[2][2] = this->TempQuad[7];
918
919 this->TempB[0] = -this->TempQuad[3];
920 this->TempB[1] = -this->TempQuad[6];
921 this->TempB[2] = -this->TempQuad[8];
922
923 for (i = 3; i < 3 + this->NumberOfComponents; i++)
924 {
925 this->TempA[0][i] = this->TempA[i][0] = this->TempQuad[11+4*(i-3)];
926 this->TempA[1][i] = this->TempA[i][1] = this->TempQuad[11+4*(i-3)+1];
927 this->TempA[2][i] = this->TempA[i][2] = this->TempQuad[11+4*(i-3)+2];
928 this->TempB[i] = -this->TempQuad[11+4*(i-3)+3];
929 }
930
931 for (i = 3; i < 3 + this->NumberOfComponents; i++)
932 {
933 for (j = 3; j < 3 + this->NumberOfComponents; j++)
934 {
935 if (i == j)
936 {
937 this->TempA[i][j] = this->TempQuad[10];
938 }
939 else
940 {
941 this->TempA[i][j] = 0;
942 }
943 }
944 }
945
946 for (i = 0; i < 3 + this->NumberOfComponents; i++)
947 {
948 x[i] = this->TempB[i];
949 }
950
951 // solve A*x = b
952 // this clobers A
953 // need to develop a quality of the solution test??
954 solveOk = vtkMath::SolveLinearSystem(this->TempA, x, 3 + this->NumberOfComponents);
955
956 // need to copy back into A
957 this->TempA[0][0] = this->TempQuad[0];
958 this->TempA[0][1] = this->TempA[1][0] = this->TempQuad[1];
959 this->TempA[0][2] = this->TempA[2][0] = this->TempQuad[2];
960 this->TempA[1][1] = this->TempQuad[4];
961 this->TempA[1][2] = this->TempA[2][1] = this->TempQuad[5];
962 this->TempA[2][2] = this->TempQuad[7];
963
964 for (i = 3; i < 3 + this->NumberOfComponents; i++)
965 {
966 this->TempA[0][i] = this->TempA[i][0] = this->TempQuad[11+4*(i-3)];
967 this->TempA[1][i] = this->TempA[i][1] = this->TempQuad[11+4*(i-3)+1];
968 this->TempA[2][i] = this->TempA[i][2] = this->TempQuad[11+4*(i-3)+2];
969 }
970
971 for (i = 3; i < 3 + this->NumberOfComponents; i++)
972 {
973 for (j = 3; j < 3 + this->NumberOfComponents; j++)
974 {
975 if (i == j)
976 {
977 this->TempA[i][j] = this->TempQuad[10];
978 }
979 else
980 {
981 this->TempA[i][j] = 0;
982 }
983 }
984 }
985
986 // check for failure to solve the system
987 if (!solveOk)
988 {
989 // cheapest point along the edge
990 // this should not frequently occur, so I am using dynamic allocation
991 double *pt1 = new double [3+this->NumberOfComponents];
992 double *pt2 = new double [3+this->NumberOfComponents];
993 double *v = new double [3+this->NumberOfComponents];
994 double *temp = new double [3+this->NumberOfComponents];
995 double *temp2 = new double [3+this->NumberOfComponents];
996 double d = 0;
997 double c = 0;
998
999 this->GetPointAttributeArray(pointIds[0], pt1);
1000 this->GetPointAttributeArray(pointIds[1], pt2);
1001 for (i = 0; i < 3+this->NumberOfComponents; ++i)
1002 {
1003 v[i] = pt2[i] - pt1[i];
1004 }
1005
1006 // equation for the edge pt1 + c * v
1007 // attempt least squares fit for c for A*(pt1 + c * v) = b
1008 // temp2 = A*v
1009 for (i = 0; i < 3 + this->NumberOfComponents; ++i)
1010 {
1011 temp2[i] = 0;
1012 for (j = 0; j < 3 + this->NumberOfComponents; ++j)
1013 {
1014 temp2[i] += this->TempA[i][j]*v[j];
1015 }
1016 }
1017
1018 // c = v dot v
1019 for (i = 0; i < 3 + this->NumberOfComponents; ++i)
1020 {
1021 d += temp2[i]*temp2[i];
1022 }
1023
1024 if ( d > errorNumber)
1025 {
1026 // temp = A*pt1
1027 for (i = 0; i < 3 + this->NumberOfComponents; ++i)
1028 {
1029 temp[i] = 0;
1030 for (j = 0; j < 3 + this->NumberOfComponents; ++j)
1031 {
1032 temp[i] += this->TempA[i][j]*pt1[j];
1033 }
1034 }
1035
1036 for (i = 0; i < 3 + this->NumberOfComponents; i++)
1037 {
1038 temp[i] = this->TempB[i] - temp[i];
1039 }
1040
1041 for (i = 0; i < 3 + this->NumberOfComponents; i++)
1042 {
1043 c += temp2[i]*temp[i];
1044 }
1045 c = c/d;
1046
1047 for (i = 0; i < 3 + this->NumberOfComponents; i++)
1048 {
1049 x[i] = pt1[i]+c*v[i];
1050 }
1051 }
1052 else
1053 {
1054 // use mid point
1055 // might want to change to best of mid and end points??
1056 for (i = 0; i < 3 + this->NumberOfComponents; i++)
1057 {
1058 x[i] = 0.5*(pt1[i]+pt2[i]);
1059 }
1060 }
1061 delete[] pt1;
1062 delete[] pt2;
1063 delete[] v;
1064 delete[] temp;
1065 delete[] temp2;
1066 }
1067
1068 // Compute the cost
1069 // x'*A*x - 2*b*x + d
1070 for (i = 0; i < 3+this->NumberOfComponents; i++)
1071 {
1072 cost += this->TempA[i][i]*x[i]*x[i];
1073 for (j = i+1; j < 3+this->NumberOfComponents; j++)
1074 {
1075 cost += 2.0*this->TempA[i][j]*x[i]*x[j];
1076 }
1077 }
1078 for (i = 0; i < 3+this->NumberOfComponents; i++)
1079 {
1080 cost -= 2.0 * this->TempB[i]*x[i];
1081 }
1082
1083 cost += this->TempQuad[9];
1084
1085 return cost;
1086 }
1087
1088
CollapseEdge(vtkIdType pt0Id,vtkIdType pt1Id)1089 int vtkQuadricDecimation::CollapseEdge(vtkIdType pt0Id, vtkIdType pt1Id)
1090 {
1091 int j, numDeleted=0;
1092 vtkIdType i, npts, *pts, cellId;
1093
1094 this->Mesh->GetPointCells(pt0Id, this->CollapseCellIds);
1095 for (i = 0; i < this->CollapseCellIds->GetNumberOfIds(); i++)
1096 {
1097 cellId = this->CollapseCellIds->GetId(i);
1098 this->Mesh->GetCellPoints(cellId, npts, pts);
1099 for (j = 0; j < 3; j++)
1100 {
1101 if (pts[j] == pt1Id)
1102 {
1103 this->Mesh->RemoveCellReference(cellId);
1104 this->Mesh->DeleteCell(cellId);
1105 numDeleted++;
1106 }
1107 }
1108 }
1109
1110 this->Mesh->GetPointCells(pt1Id, this->CollapseCellIds);
1111 this->Mesh->ResizeCellList(pt0Id, this->CollapseCellIds->GetNumberOfIds());
1112 for (i=0; i < this->CollapseCellIds->GetNumberOfIds(); i++)
1113 {
1114 cellId = this->CollapseCellIds->GetId(i);
1115 this->Mesh->GetCellPoints(cellId, npts, pts);
1116 // making sure we don't already have the triangle we're about to
1117 // change this one to
1118 if ((pts[0] == pt1Id && this->Mesh->IsTriangle(pt0Id, pts[1], pts[2])) ||
1119 (pts[1] == pt1Id && this->Mesh->IsTriangle(pts[0], pt0Id, pts[2])) ||
1120 (pts[2] == pt1Id && this->Mesh->IsTriangle(pts[0], pts[1], pt0Id)))
1121 {
1122 this->Mesh->RemoveCellReference(cellId);
1123 this->Mesh->DeleteCell(cellId);
1124 numDeleted++;
1125 }
1126 else
1127 {
1128 this->Mesh->AddReferenceToCell(pt0Id, cellId);
1129 this->Mesh->ReplaceCellPoint(cellId, pt1Id, pt0Id);
1130 }
1131 }
1132 this->Mesh->DeletePoint(pt1Id);
1133
1134 return numDeleted;
1135 }
1136
1137
1138 // triangle t0, t1, t2 and point x
1139 // determins if t0 and x are on the same side of the plane defined by
1140 // t1 and t2, and parallel to the normal of the triangle
TrianglePlaneCheck(const double t0[3],const double t1[3],const double t2[3],const double * x)1141 int vtkQuadricDecimation::TrianglePlaneCheck(const double t0[3],
1142 const double t1[3],
1143 const double t2[3],
1144 const double *x) {
1145 double e0[3], e1[3], n[3], e2[3];
1146 double c;
1147 int i;
1148
1149 for (i = 0; i < 3; i++)
1150 {
1151 e0[i] = t2[i] - t1[i];
1152 }
1153 for (i = 0; i < 3; i++)
1154 {
1155 e1[i] = t0[i] - t1[i];
1156 }
1157
1158 // projection of e0 onto e1
1159 c = vtkMath::Dot(e0,e1)/(e0[0]*e0[0]+e0[1]*e0[1]+e0[2]*e0[2]);
1160 for (i = 0; i < 3; i++)
1161 {
1162 n[i] = e1[i] - c*e0[i];
1163 }
1164
1165 for ( i = 0; i < 3; i++)
1166 {
1167 e2[i] = x[i] - t1[i];
1168 }
1169
1170 vtkMath::Normalize(n);
1171 vtkMath::Normalize(e2);
1172 if (vtkMath::Dot(n, e2) > 1e-5)
1173 {
1174 return 1;
1175 }
1176 else
1177 {
1178 return 0;
1179 }
1180 }
1181
IsGoodPlacement(vtkIdType pt0Id,vtkIdType pt1Id,const double * x)1182 int vtkQuadricDecimation::IsGoodPlacement(vtkIdType pt0Id, vtkIdType pt1Id,
1183 const double *x)
1184 {
1185 unsigned short ncells, i;
1186 vtkIdType npts, *pts, ptId, *cells;
1187 double pt1[3], pt2[3], pt3[3];
1188
1189 this->Mesh->GetPointCells(pt0Id, ncells, cells);
1190 for (i = 0; i < ncells; i++) {
1191 this->Mesh->GetCellPoints(cells[i], npts, pts);
1192 // assume triangle
1193 if (pts[0] != pt1Id && pts[1] != pt1Id && pts[2] != pt1Id)
1194 {
1195 for (ptId = 0; ptId < 3; ptId++)
1196 {
1197 if (pts[ptId] == pt0Id)
1198 {
1199 this->Mesh->GetPoint(pts[ptId], pt1);
1200 this->Mesh->GetPoint(pts[(ptId+1)%3], pt2);
1201 this->Mesh->GetPoint(pts[(ptId+2)%3], pt3);
1202 if(!this->TrianglePlaneCheck(pt1, pt2, pt3, x))
1203 {
1204 return 0;
1205 }
1206 }
1207 }
1208 }
1209 }
1210
1211 this->Mesh->GetPointCells(pt1Id, ncells, cells);
1212 for (i = 0; i < ncells; i++)
1213 {
1214 this->Mesh->GetCellPoints(cells[i], npts, pts);
1215 // assume triangle
1216 if (pts[0] != pt0Id && pts[1] != pt0Id && pts[2] != pt0Id)
1217 {
1218 for (ptId = 0; ptId < 3; ptId++)
1219 {
1220 if (pts[ptId] == pt1Id)
1221 {
1222 this->Mesh->GetPoint(pts[ptId], pt1);
1223 this->Mesh->GetPoint(pts[(ptId+1)%3], pt2);
1224 this->Mesh->GetPoint(pts[(ptId+2)%3], pt3);
1225 if(!this->TrianglePlaneCheck(pt1, pt2, pt3, x))
1226 {
1227 return 0;
1228 }
1229 }
1230 }
1231 }
1232 }
1233
1234 return 1;
1235 }
1236
1237
ComputeNumberOfComponents(void)1238 void vtkQuadricDecimation::ComputeNumberOfComponents(void)
1239 {
1240 vtkPointData *pd = this->Mesh->GetPointData();
1241 int i, j;
1242 double range[2], maxRange=0.0;
1243
1244 this->NumberOfComponents = 0;
1245 pd->CopyAllOff();
1246
1247 for (i = 0; i < 6; i++)
1248 {
1249 this->AttributeComponents[i] = 0;
1250 this->AttributeScale[i] = 1.0;
1251 }
1252
1253 // Scalar attributes
1254 if (pd->GetScalars() != NULL && this->ScalarsAttribute)
1255 {
1256 for (j = 0; j < pd->GetScalars()->GetNumberOfComponents(); j++)
1257 {
1258 pd->GetScalars()->GetRange(range, j);
1259 maxRange = (maxRange < (range[1] - range[0]) ?
1260 (range[1] - range[0]) : maxRange);
1261 }
1262 if (maxRange != 0.0)
1263 {
1264 this->NumberOfComponents += pd->GetScalars()->GetNumberOfComponents();
1265 pd->CopyScalarsOn();
1266 this->AttributeScale[0] = this->ScalarsWeight/maxRange;
1267 maxRange = 0.0;
1268 }
1269 vtkDebugMacro("scalars "<< this->NumberOfComponents << " "
1270 << this->AttributeScale[0]);
1271 }
1272 this->AttributeComponents[0] = this->NumberOfComponents;
1273
1274 // Vector attributes
1275 if (pd->GetVectors() != NULL && this->VectorsAttribute)
1276 {
1277 for (j = 0; j < pd->GetVectors()->GetNumberOfComponents(); j++)
1278 {
1279 pd->GetVectors()->GetRange(range, j);
1280 maxRange = (maxRange < (range[1] - range[0]) ?
1281 (range[1] - range[0]) : maxRange);
1282 }
1283 if (maxRange != 0.0)
1284 {
1285 this->NumberOfComponents += pd->GetVectors()->GetNumberOfComponents();
1286 pd->CopyVectorsOn();
1287 this->AttributeScale[1] = this->VectorsWeight/maxRange;
1288 maxRange = 0.0;
1289 }
1290 vtkDebugMacro("vectors "<< this->NumberOfComponents << " "
1291 << this->AttributeScale[1]);
1292 }
1293 this->AttributeComponents[1] = this->NumberOfComponents;
1294
1295 // Normals attributes -- normals are assumed normalized
1296 if (pd->GetNormals() != NULL && this->NormalsAttribute)
1297 {
1298 this->NumberOfComponents += 3;
1299 pd->CopyNormalsOn();
1300 this->AttributeScale[2] = 0.5*this->NormalsWeight;
1301 vtkDebugMacro("normals "<< this->NumberOfComponents << " "
1302 << this->AttributeScale[2]);
1303 }
1304 this->AttributeComponents[2] = this->NumberOfComponents;
1305
1306 // Texture coords attributes
1307 if (pd->GetTCoords() != NULL && this->TCoordsAttribute)
1308 {
1309 for (j = 0; j < pd->GetTCoords()->GetNumberOfComponents(); j++)
1310 {
1311 pd->GetTCoords()->GetRange(range, j);
1312 maxRange = (maxRange < (range[1] - range[0]) ?
1313 (range[1] - range[0]) : maxRange);
1314 }
1315 if (maxRange != 0.0)
1316 {
1317 this->NumberOfComponents += pd->GetTCoords()->GetNumberOfComponents();
1318 pd->CopyTCoordsOn();
1319 this->AttributeScale[3] = this->TCoordsWeight/maxRange;
1320 maxRange = 0.0;
1321 }
1322 vtkDebugMacro("tcoords "<< this->NumberOfComponents << " "
1323 << this->AttributeScale[3]);
1324 }
1325 this->AttributeComponents[3] = this->NumberOfComponents;
1326
1327 // Tensors attributes
1328 if (pd->GetTensors() != NULL && this->TensorsAttribute)
1329 {
1330 for (j = 0; j < 9; j++)
1331 {
1332 pd->GetTensors()->GetRange(range, j);
1333 maxRange = (maxRange < (range[1] - range[0]) ?
1334 (range[1] - range[0]) : maxRange);
1335 }
1336 if (maxRange != 0.0)
1337 {
1338 this->NumberOfComponents += 9;
1339 pd->CopyTensorsOn();
1340 this->AttributeScale[4] = this->TensorsWeight/maxRange;
1341 }
1342 vtkDebugMacro("tensors "<< this->NumberOfComponents << " "
1343 << this->AttributeScale[4]);
1344 }
1345 this->AttributeComponents[4] = this->NumberOfComponents;
1346
1347 vtkDebugMacro("Number of components: " << this->NumberOfComponents);
1348 }
1349
1350 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)1351 void vtkQuadricDecimation::PrintSelf(ostream& os, vtkIndent indent)
1352 {
1353 this->Superclass::PrintSelf(os,indent);
1354
1355 os << indent << "Target Reduction: " << this->TargetReduction << "\n";
1356 os << indent << "Actual Reduction: " << this->ActualReduction << "\n";
1357
1358 os << indent << "Attribute Error Metric: "
1359 << (this->AttributeErrorMetric ? "On\n" : "Off\n");
1360 os << indent << "Scalars Attribute: "
1361 << (this->ScalarsAttribute ? "On\n" : "Off\n");
1362 os << indent << "Vectors Attribute: "
1363 << (this->VectorsAttribute ? "On\n" : "Off\n");
1364 os << indent << "Normals Attribute: "
1365 << (this->NormalsAttribute ? "On\n" : "Off\n");
1366 os << indent << "TCoords Attribute: "
1367 << (this->TCoordsAttribute ? "On\n" : "Off\n");
1368 os << indent << "Tensors Attribute: "
1369 << (this->TensorsAttribute ? "On\n" : "Off\n");
1370
1371 os << indent << "Scalars Weight: " << this->ScalarsWeight << "\n";
1372 os << indent << "Vectors Weight: " << this->VectorsWeight << "\n";
1373 os << indent << "Normals Weight: " << this->NormalsWeight << "\n";
1374 os << indent << "TCoords Weight: " << this->TCoordsWeight << "\n";
1375 os << indent << "Tensors Weight: " << this->TensorsWeight << "\n";
1376 }
1377