Changeset 2614
- Timestamp:
- 01/18/08 16:28:30 (17 years ago)
- Location:
- GTP/trunk/Lib/Vis/Preprocessing
- Files:
-
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
GTP/trunk/Lib/Vis/Preprocessing/scripts/demo1.env
r2604 r2614 189 189 MeshKdTree { 190 190 Termination { 191 minCost 150191 minCost 20 192 192 maxDepth 18 193 193 maxCostRatio 0.9 … … 195 195 } 196 196 197 #splitMethod spatialMedian198 splitMethod SAH197 splitMethod spatialMedian 198 # splitMethod SAH 199 199 splitBorder 0.01 200 200 } -
GTP/trunk/Lib/Vis/Preprocessing/src/GlRenderer.cpp
r2613 r2614 368 368 369 369 Face *face = mesh->mFaces[i]; 370 Vector3 normal = mesh->GetNormal(i); 371 372 glNormal3f(normal.x, normal.y, normal.z); 370 373 for (int j = 0; j < face->mVertexIndices.size(); j++) { 371 374 glVertex3fv(&mesh->mVertices[face->mVertexIndices[j]].x); … … 696 699 697 700 mViewDirection = Normalize(Vector3(sin(dVector.x), 698 // 701 // cos(dVector.y), 699 702 0.0f, 700 703 cos(dVector.x))); -
GTP/trunk/Lib/Vis/Preprocessing/src/Mesh.cpp
r2609 r2614 104 104 root->mFaces.push_back(i); 105 105 106 cout<<"KD"; 106 //cout<<"KD"; 107 cout<<"constructed mesh kd tree for " << (int)mFaces.size() << " faces" << endl; 107 108 108 109 mKdTree->Construct(); -
GTP/trunk/Lib/Vis/Preprocessing/src/Mesh.h
r2609 r2614 42 42 } 43 43 44 Face(const VertexIndexContainer &vertices): mVertexIndices(vertices.size()) {44 Face(const VertexIndexContainer &vertices): mVertexIndices(vertices.size()) { 45 45 for (int i=0; i < vertices.size(); i++) 46 46 mVertexIndices[i] = vertices[i]; 47 47 } 48 48 49 49 50 //////////////////////////// 50 51 … … 178 179 int GetId() const { return mId; } 179 180 180 181 /** Returns normal of specified face. 182 */ 181 183 Vector3 GetNormal(const int idx) const; 182 184 -
GTP/trunk/Lib/Vis/Preprocessing/src/MeshKdTree.cpp
r2176 r2614 147 147 vector<int>::const_iterator fi; 148 148 vector<int> objectsFront, objectsBack; 149 150 for ( fi = leaf->mFaces.begin(); 151 fi != leaf->mFaces.end(); 152 fi++) { 153 // determine the side of this ray with respect to the plane 154 AxisAlignedBox3 box = mMesh->GetFaceBox(*fi); 155 156 if (box.Max(axis) > position ) 157 objectsFront.push_back(*fi); 158 159 if (box.Min(axis) < position ) 160 objectsBack.push_back(*fi); 161 } 162 149 150 for ( fi = leaf->mFaces.begin(); fi != leaf->mFaces.end(); fi++) 151 { 152 // determine the side of this ray with respect to the plane 153 AxisAlignedBox3 box = mMesh->GetFaceBox(*fi); 154 155 if (box.Max(axis) > position ) 156 objectsFront.push_back(*fi); 157 158 if (box.Min(axis) < position ) 159 objectsBack.push_back(*fi); 160 } 161 163 162 MeshKdLeaf *back = new MeshKdLeaf(objectsBack); 164 163 MeshKdLeaf *front = new MeshKdLeaf(objectsFront); … … 419 418 mRoot = Subdivide(TraversalData(leaf, NULL, GetBox(), 0)); 420 419 420 cout << "created " << 421 421 // remove the allocated array 422 422 delete mSubdivisionCandidates; -
GTP/trunk/Lib/Vis/Preprocessing/src/Preprocessor.cpp
r2610 r2614 186 186 int numTriangles; 187 187 188 const int t = 500000; 188 189 inStream.read(reinterpret_cast<char *>(&numTriangles), sizeof(int)); 189 190 root->mGeometry.reserve(numTriangles); … … 209 210 root->mGeometry.push_back(obj); 210 211 211 if ((i ++) % 500000 == 499999)212 if ((i ++) % t == t) 212 213 cout<<"\r"<<i<<"/"<<numTriangles<<"\r"; 213 214 } … … 1179 1180 // !!!!!!!!!!!!!!!! VH no sorting 1180 1181 if ( 1181 1182 rays.size() > 10000 1182 1183 ) 1183 { 1184 mRayCaster->SortRays(rays); 1185 cout<<"Rays sorted in "<<TimeDiff(t1, GetTime())<<" ms."<<endl; 1186 } 1184 { 1185 mRayCaster->SortRays(rays); 1186 cout<<"Rays sorted in "<<TimeDiff(t1, GetTime())<<" ms."<<endl; 1187 } 1188 1189 int numTransformed = 0; 1187 1190 1188 1191 … … 1208 1211 1209 1212 static Ray hRay; 1210 1211 1213 VssRayContainer::iterator vit, vit_end = vssRays.end(); 1212 1214 … … 1219 1221 1220 1222 bool foundIntersect = false; 1223 1221 1224 for (tit = mDynamicObjects.begin(); tit != tit_end; ++ tit) 1222 1225 { 1223 1226 TransformedMeshInstance *tmi = *tit; 1224 if (tmi->CastRay(hRay)) //cout << "z"; else cout<< "r"; 1227 1228 float maxT, minT; 1229 1230 // test with bounding box 1231 if (tmi->GetBox().GetMinMaxT(hRay, &minT, &maxT)) 1225 1232 { 1226 cout << "i: " << Intersectable::GetTypeName(hRay.intersections[0].mObject); 1227 foundIntersect = true; 1233 if (tmi->CastRay(hRay)) //cout << "z"; else cout<< "r"; 1234 { 1235 //cout << "i: " << Intersectable::GetTypeName(hRay.intersections[0].mObject); 1236 foundIntersect = true; 1237 } 1228 1238 } 1229 1239 } 1230 1240 1241 //if (foundIntersect) cout << "x: " << Intersectable::GetTypeName(hRay.intersections[0].mObject); 1242 1231 1243 if (foundIntersect) 1232 cout << "x: " << Intersectable::GetTypeName(hRay.intersections[0].mObject); 1233 1234 if (foundIntersect) 1244 { 1245 ++ numTransformed; 1235 1246 // write new results back into vssRay 1236 1247 vssRay->Init(hRay); 1237 1238 if (foundIntersect) 1239 cout << "y : " << Intersectable::GetTypeName(vssRay->mTerminationObject); 1248 } 1240 1249 1241 1250 hRay.intersections.clear(); … … 1258 1267 1259 1268 } 1260 1261 // 1269 //cout << "numDummy: " << numTransformed << endl; 1270 //cerr<<"Determining PVS objects"<<endl; 1262 1271 DeterminePvsObjects(vssRays); 1263 1272 // cerr<<"done."<<endl; … … 1620 1629 cout << "loading " << numTriangles << " triangles into mesh" << endl; 1621 1630 1622 int i = 0, j = 0;1623 1624 1631 Mesh *mesh = new Mesh(numTriangles * 3, numTriangles); 1625 1632 1626 const int t = 500000;1627 while (1)1633 const int t = 1000; 1634 for (int j = 0, i = 0; j < numTriangles; ++ j, i += 3) 1628 1635 { 1629 1636 Triangle3 tri; … … 1633 1640 inStream.read(reinterpret_cast<char *>(&mesh->mVertices[i + 2]), sizeof(Vector3)); 1634 1641 1635 1636 mesh->mVertices[i].x += 20; 1642 /*mesh->mVertices[i].x += 20; 1637 1643 mesh->mVertices[i + 1].x += 20; 1638 1644 mesh->mVertices[i + 2].x += 20; … … 1641 1647 mesh->mVertices[i + 1].z -= 10; 1642 1648 mesh->mVertices[i + 2].z -= 10; 1643 1644 mesh->mVertices[i] *= 50;1645 mesh->mVertices[i + 1] *= 50;1646 mesh->mVertices[i + 2] *= 50;1649 */ 1650 mesh->mVertices[i] *= 100; 1651 mesh->mVertices[i + 1] *= 100; 1652 mesh->mVertices[i + 2] *= 100; 1647 1653 1648 1654 mesh->AddFace(new Face(i + 0, i + 1, i + 2)); 1649 1655 1650 // end of file reached 1651 if (inStream.eof()) 1652 break; 1653 1654 i += 3; 1655 if (((j ++) % t) == (t - 1)) 1656 if ((j % t) == (t - 1)) 1656 1657 cout<<"\r"<<i<<"/"<<numTriangles<<"\r"; 1657 1658 } 1658 1659 if (j != numTriangles) 1660 { 1661 cout << "warning: triangle size does not match with loaded triangle size" << endl; 1662 return false; 1663 } 1659 1660 cout << "loaded " << numTriangles << " triangles" << endl; 1664 1661 1665 1662 mesh->Preprocess(); 1666 cout << "loaded " << numTriangles << " triangles" << endl; 1663 1664 cout << "finished preprocessing" << endl; 1667 1665 1668 1666 return mesh; -
GTP/trunk/Lib/Vis/Preprocessing/src/QtInterface/QtGlRenderer.cpp
r2613 r2614 386 386 for (dit = mDynamicObjects.begin(); dit != dit_end; ++ dit) 387 387 { 388 //cerr << "here45\n****"<<endl; 388 389 RenderIntersectable(*dit); 389 390 } … … 659 660 int diffy = -(mousePoint.y - y); 660 661 662 const float t = 1.0f; 663 661 664 if (e->modifiers() & Qt::ControlModifier) 662 665 { … … 664 667 mViewPoint.x += (x-mousePoint.x)*MOVE_SENSITIVITY / 2.0; 665 668 } 666 else 669 else if (e->modifiers() & Qt::AltModifier) 670 { 671 if (mCurrentDynamicObjectIdx >= 0) 672 { 673 Matrix4x4 tm; 674 TransformedMeshInstance *tmi; 675 676 switch (mTrafoType) 677 { 678 case 0: 679 { 680 const Vector3 transl(diffx, 0, diffy); 681 tmi = mViewCellsManager->GetPreprocessor()->mDynamicObjects[mCurrentDynamicObjectIdx]; 682 tm = TranslationMatrix(transl); 683 } 684 break; 685 case 1: 686 { 687 float scalef = 1.0f + 0.01f * (diffx + diffy); 688 if (scalef < 0.9) scalef = 0.9f; 689 else if (scalef > 1.1f) scalef = 1.1f; 690 tmi = mViewCellsManager->GetPreprocessor()->mDynamicObjects[mCurrentDynamicObjectIdx]; 691 tm = ScaleMatrix(scalef, scalef, scalef); 692 } 693 break; 694 case 2: 695 { 696 tmi = mViewCellsManager->GetPreprocessor()->mDynamicObjects[mCurrentDynamicObjectIdx]; 697 tm = RotationXMatrix(diffx) * RotationYMatrix(diffy); 698 } 699 break; 700 default: 701 cerr << "not implemented" << endl; 702 } 703 704 tmi->ApplyWorldTransform(tm); 705 updateGL(); 706 } 707 } 708 else 667 709 { 668 710 mViewPoint += mViewDirection*((mousePoint.y - y)*MOVE_SENSITIVITY); … … 889 931 mShowWeightedTriangles = false; 890 932 mShowDistribution = 15; 933 mCurrentDynamicObjectIdx = -1; 891 934 892 935 mSpatialFilterSize = 0.01; 893 936 mPvsSize = 0; 894 937 mRayVisualizationMethod = 0; 938 mTrafoType = 0; 939 895 940 mRenderError = 0.0f; 896 941 mShowRays = false; … … 950 995 connect(mControlWidget, SIGNAL(SetShowDistribution4(bool)), this, SLOT(SetShowDistribution4(bool))); 951 996 952 953 997 connect(mControlWidget, SIGNAL(SetShowRays(bool)), this, SLOT(SetShowRays(bool))); 954 998 999 connect(mControlWidget, SIGNAL(SetTranslation(bool)), this, SLOT(SetTranslation(bool))); 1000 connect(mControlWidget, SIGNAL(SetRotation(bool)), this, SLOT(SetRotation(bool))); 1001 connect(mControlWidget, SIGNAL(SetScale(bool)), this, SLOT(SetScale(bool))); 1002 955 1003 // setting the size here 956 1004 resize(800, 600); … … 1009 1057 void QtGlRendererWidget::LoadObject() 1010 1058 { 1011 string filename("../data/ bunny.bn");1059 string filename("../data/teapot.bn"); 1012 1060 1013 1061 cout << "Loading model << " << filename << endl; 1062 1063 ++ mCurrentDynamicObjectIdx; 1014 1064 1015 1065 if (mViewCellsManager->GetPreprocessor()->LoadDynamicGeometry(filename)) … … 1587 1637 1588 1638 rb1 = new QRadioButton("random colors", parent); 1589 rb1->setText(" wireframe");1639 rb1->setText("random"); 1590 1640 connect(rb1, SIGNAL(toggled(bool)), SIGNAL(SetShowWireFrame(bool))); 1591 1641 … … 1627 1677 1628 1678 1679 QGroupBox *QtRendererControlWidget::CreateTrafoPanel(QWidget *parent) 1680 { 1681 QRadioButton *rb1, *rb2, *rb3; 1682 1683 rb1 = new QRadioButton("translation", parent); 1684 rb1->setText("translation"); 1685 connect(rb1, SIGNAL(toggled(bool)), SIGNAL(SetTranslation(bool))); 1686 1687 // Create a check box to be in the group box 1688 rb2 = new QRadioButton("scale", parent); 1689 rb2->setText("scale"); 1690 //vl->addWidget(rb1); 1691 connect(rb2, SIGNAL(toggled(bool)), SIGNAL(SetScale(bool))); 1692 1693 rb3 = new QRadioButton("rotation", parent); 1694 rb3->setText("rotation"); 1695 connect(rb3, SIGNAL(toggled(bool)), SIGNAL(SetRotation(bool))); 1696 1697 QVBoxLayout *vbox2 = new QVBoxLayout; 1698 QGroupBox *groupBox = new QGroupBox("Trafo types"); 1699 1700 vbox2->addWidget(rb1); 1701 vbox2->addWidget(rb2); 1702 vbox2->addWidget(rb3); 1703 1704 rb1->setChecked(true); 1705 1706 vbox2->addStretch(1); 1707 groupBox->setLayout(vbox2); 1708 1709 return groupBox; 1710 } 1711 1712 1629 1713 1630 1714 QGroupBox *QtRendererControlWidget::CreateRenderCostPanel(QWidget *parent) … … 1646 1730 1647 1731 QGroupBox *groupBox = new QGroupBox("Render cost options"); 1648 1649 1732 QVBoxLayout *vbox2 = new QVBoxLayout; 1650 1733 … … 1857 1940 QGroupBox *groupBox3 = CreateRayVisualizationPanel(hbox); 1858 1941 vh->addWidget(groupBox3, 0, 0); 1942 1943 QGroupBox *groupBox4 = CreateTrafoPanel(hbox); 1944 vh->addWidget(groupBox4, 0, 0); 1859 1945 1860 1946 ////////////////////////////////// … … 2032 2118 cb->setChecked(false); 2033 2119 connect(cb, SIGNAL(toggled(bool)), SIGNAL(StoreStatistics()));*/ 2120 2034 2121 if (0) 2035 2122 { -
GTP/trunk/Lib/Vis/Preprocessing/src/QtInterface/QtGlRenderer.h
r2609 r2614 91 91 92 92 QGroupBox *CreateVisualizationPanel(QWidget *parent); 93 QGroupBox *CreateTrafoPanel(QWidget *parent); 93 94 94 95 public slots: … … 156 157 void SetShowDistribution3(bool); 157 158 void SetShowDistribution4(bool); 159 160 void SetTranslation(bool); 161 void SetRotation(bool); 162 void SetScale(bool); 158 163 }; 159 164 … … 204 209 int mShowDistribution; 205 210 211 int mCurrentDynamicObjectIdx; 212 206 213 // some statistics 207 214 int mPvsSize; … … 214 221 215 222 int mRayVisualizationMethod; 223 int mTrafoType; 216 224 217 225 QtRendererControlWidget *mControlWidget; … … 267 275 virtual int GetHeight() const { return height(); } 268 276 269 // virtual void270 // SetupProjection(const int w, const int h, const float angle = 70.0f);271 272 277 virtual void 273 278 SetupCameraProjection(const int w, const int h, const float angle = 70.0f); … … 479 484 mRayVisualizationMethod = 3; 480 485 } 481 486 487 void SetTranslation(bool b) 488 { 489 mTrafoType = 0; 490 } 491 492 void SetScale(bool b) 493 { 494 mTrafoType = 1; 495 } 496 497 void SetRotation(bool b) 498 { 499 mTrafoType = 2; 500 } 501 482 502 void SetShowDistribution1(bool b) 483 503 { -
GTP/trunk/Lib/Vis/Preprocessing/src/QtInterface/QtGlViewer.cpp
r2609 r2614 242 242 QtGlViewer::mouseMoveEvent(QMouseEvent *event) 243 243 { 244 int dx = event->x() - lastPos.x(); 245 int dy = event->y() - lastPos.y(); 246 247 if (event->modifiers() & Qt::CTRL) 248 { 249 scale = scale*(1.0f - dy/(float)height()); 250 if (scale < 0.01f) 251 scale = 0.01f; 252 updateGL(); 253 } 254 else { 244 int dx = event->x() - lastPos.x(); 245 int dy = event->y() - lastPos.y(); 246 247 if (event->modifiers() & Qt::CTRL) 248 { 249 scale = scale*(1.0f - dy/(float)height()); 250 if (scale < 0.01f) 251 scale = 0.01f; 252 updateGL(); 253 } 254 else 255 { 255 256 float W = width(); 256 257 float H = height(); … … 259 260 int y = event->y(); 260 261 int lastY = lastPos.y(); 261 262 262 263 float quat[4]; 263 264 trackball(quat, 264 265 266 267 268 265 (2.0 * lastX - W) / W, 266 (H - 2.0 * lastY) / H, 267 (2.0 * x - W) / W, 268 (H - 2.0 * y) / H 269 ); 269 270 270 271 add_quats(quat, manipulatorLastQuat, manipulatorLastQuat); 271 272 273 274 275 } 276 277 void QtGlViewer::timerEvent(QTimerEvent *event)278 {279 update();280 }281 282 283 } 284 272 } 273 274 lastPos = event->pos(); 275 updateGL(); 276 } 277 278 void QtGlViewer::timerEvent(QTimerEvent *event) 279 { 280 update(); 281 } 282 283 284 } 285 -
GTP/trunk/Lib/Vis/Preprocessing/src/QtInterface/QtGlViewer.h
r2567 r2614 58 58 float scale; 59 59 QPoint lastPos; 60 61 60 }; 62 61 -
GTP/trunk/Lib/Vis/Preprocessing/src/SamplingStrategy.cpp
r2588 r2614 584 584 } 585 585 586 586 587 for(; it != vssRays.end(); ++it) 587 588 { 588 589 VssRay *ray = *it; 589 590 590 //cout << " here4 " << ray->GetDir(); 591 //if (ray->mTerminationObject->Type() == Intersectable::TRANSFORMED_MESH_INSTANCE) 592 // cout << "found transofrmed mesh instance: " << Intersectable::GetTypeName(ray->mTerminationObject) << " " << endl; 591 593 592 594 for (i=0; i < mDistributions.size()-1; i++) … … 596 598 } 597 599 598 float contribution = 599 mPreprocessor.mViewCellsManager->ComputeSampleContribution(*ray, 600 true, 601 false); 600 const float contribution = 601 mPreprocessor.mViewCellsManager->ComputeSampleContribution(*ray, true, false); 602 602 603 603 mDistributions[i]->mContribution += contribution; -
GTP/trunk/Lib/Vis/Preprocessing/src/ViewCellsManager.cpp
r2609 r2614 2407 2407 Intersectable *obj, 2408 2408 const Vector3 &pt, 2409 constbool addSamplesToPvs)2409 bool addSamplesToPvs) 2410 2410 { 2411 2411 // check if we are outside of view space … … 2424 2424 if (addSamplesToPvs) 2425 2425 { 2426 //if (obj->Type() == Intersectable::TRANSFORMED_MESH_INSTANCE)cout << "here12 " << endl; 2427 2426 2428 hasAbsContribution = viewCell->GetPvs().AddSampleDirtyCheck(obj, ray.mPdf); 2427 2429 //hasAbsContribution = viewCell->GetPvs().AddSample(obj,ray.mPdf); … … 2437 2439 viewCell->GetPvs().GetSampleContribution(obj, ray.mPdf, relContribution); 2438 2440 } 2439 2440 //cout << "here6: " << ray.GetDir() << " ";2441 2441 2442 2442 // $$ clear the relative contribution as it is currently not correct anyway … … 2483 2483 2484 2484 void 2485 ViewCellsManager::DeterminePvsObjects( 2486 VssRayContainer &rays, 2487 const bool useHitObjects) 2485 ViewCellsManager::DeterminePvsObjects(VssRayContainer &rays, 2486 bool useHitObjects) 2488 2487 { 2489 2488 if (!useHitObjects) … … 2498 2497 // set only the termination object 2499 2498 vssRay->mTerminationObject = GetIntersectable(*vssRay, true); 2499 2500 if (vssRay->mTerminationObject->Type() == Intersectable::TRANSFORMED_MESH_INSTANCE) 2501 cout << "r"; 2502 2500 2503 #if 0 2501 if (vssRay->mTerminationObject == NULL) { 2502 cerr<<"Error in DeterminePvsObjects - termination object maps to NULL!"<<endl; 2503 } 2504 if (vssRay->mTerminationObject == NULL) 2505 cerr<<"Error in DeterminePvsObjects - termination object maps to NULL!"<<endl; 2504 2506 #endif 2505 2507 } 2506 2508 } 2507 2508 2509 } 2509 2510 … … 2547 2548 float 2548 2549 ViewCellsManager::ComputeSampleContribution(VssRay &ray, 2549 constbool addRays,2550 constbool storeViewCells,2551 constbool useHitObjects)2550 bool addRays, 2551 bool storeViewCells, 2552 bool useHitObjects) 2552 2553 { 2553 2554 ray.mPvsContribution = 0; … … 2595 2596 // traverse the view space subdivision 2596 2597 CastLineSegment(origin, termination, viewCells); 2597 2598 2598 lastVssRay = &ray; 2599 2599 } … … 2631 2631 pvsTimer.Entry(); 2632 2632 #endif 2633 2633 //if (terminationObj->Type() == Intersectable::TRANSFORMED_MESH_INSTANCE) 2634 // cout << "found tmi: " << Intersectable::GetTypeName(terminationObj) << " " << viewCells.size() << endl; 2634 2635 bool contri = false; 2635 2636 ViewCellContainer::const_iterator it = viewCells.begin(); … … 2638 2639 { 2639 2640 if (ComputeViewCellContribution(*it, 2640 ray,2641 terminationObj,2642 ray.mTermination,2643 addRays))2641 ray, 2642 terminationObj, 2643 ray.mTermination, 2644 addRays)) 2644 2645 { 2645 2646 contri = true; … … 3166 3167 void ViewCellsManager::MergeViewCellsRecursivly(ObjectPvs &pvs, 3167 3168 const ViewCellContainer &viewCells, 3168 constint leftIdx,3169 constint rightIdx) const3169 int leftIdx, 3170 int rightIdx) const 3170 3171 { 3171 3172 if (leftIdx == rightIdx) -
GTP/trunk/Lib/Vis/Preprocessing/src/ViewCellsManager.h
r2604 r2614 206 206 */ 207 207 virtual float ComputeSampleContribution(VssRay &ray, 208 constbool addContributions,209 constbool storeViewCells,210 constbool useHitObject = false);208 bool addContributions, 209 bool storeViewCells, 210 bool useHitObject = false); 211 211 /** Compute sample contribution only for current view cell. 212 212 */ … … 388 388 */ 389 389 void DeterminePvsObjects(VssRayContainer &rays, 390 constbool useHitObjects = false);390 bool useHitObjects = false); 391 391 /** Sets validity of view cell 392 392 */ … … 640 640 Intersectable *obj, 641 641 const Vector3 &pt, 642 constbool addRays);642 bool addRays); 643 643 644 644 void MergeViewCellsRecursivly(ObjectPvs &pvs, 645 645 const ViewCellContainer &viewCells, 646 const int leftIdx, 647 const int rightIdx) const; 646 int leftIdx, 647 int rightIdx) const; 648 648 649 /** Intersects box with the tree and returns the number of intersected boxes. 649 650 @returns number of view cells found
Note: See TracChangeset
for help on using the changeset viewer.