1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 /** \file
18  * \ingroup freestyle
19  * \brief Detects/flags/builds extended features edges on the WXEdge structure
20  */
21 
22 #include <float.h>
23 
24 #include "FEdgeXDetector.h"
25 
26 #include "../geometry/GeomUtils.h"
27 #include "../geometry/normal_cycle.h"
28 
29 #include "BKE_global.h"
30 
31 namespace Freestyle {
32 
processShapes(WingedEdge & we)33 void FEdgeXDetector::processShapes(WingedEdge &we)
34 {
35   bool progressBarDisplay = false;
36 #if 0
37   Vec3r Min, Max;
38 #endif
39   vector<WShape *> wshapes = we.getWShapes();
40   WXShape *wxs;
41 
42   if (_pProgressBar != NULL) {
43     _pProgressBar->reset();
44     _pProgressBar->setLabelText("Detecting feature lines");
45     _pProgressBar->setTotalSteps(wshapes.size() * 3);
46     _pProgressBar->setProgress(0);
47     progressBarDisplay = true;
48   }
49 
50   for (vector<WShape *>::const_iterator it = wshapes.begin(); it != wshapes.end(); it++) {
51     if (_pRenderMonitor && _pRenderMonitor->testBreak()) {
52       break;
53     }
54     wxs = dynamic_cast<WXShape *>(*it);
55 #if 0
56     wxs->bbox(Min, Max);
57     _bbox_diagonal = (Max - Min).norm();
58 #endif
59     if (_changes) {
60       vector<WFace *> &wfaces = wxs->GetFaceList();
61       for (vector<WFace *>::iterator wf = wfaces.begin(), wfend = wfaces.end(); wf != wfend;
62            ++wf) {
63         WXFace *wxf = dynamic_cast<WXFace *>(*wf);
64         wxf->Clear();
65       }
66       _computeViewIndependent = true;
67     }
68     else if (!(wxs)->getComputeViewIndependentFlag()) {
69       wxs->Reset();
70       _computeViewIndependent = false;
71     }
72     else {
73       _computeViewIndependent = true;
74     }
75     preProcessShape(wxs);
76     if (progressBarDisplay) {
77       _pProgressBar->setProgress(_pProgressBar->getProgress() + 1);
78     }
79     processBorderShape(wxs);
80     if (_computeMaterialBoundaries) {
81       processMaterialBoundaryShape(wxs);
82     }
83     processCreaseShape(wxs);
84     if (_computeRidgesAndValleys) {
85       processRidgesAndValleysShape(wxs);
86     }
87     if (_computeSuggestiveContours) {
88       processSuggestiveContourShape(wxs);
89     }
90     processSilhouetteShape(wxs);
91     processEdgeMarksShape(wxs);
92     if (progressBarDisplay) {
93       _pProgressBar->setProgress(_pProgressBar->getProgress() + 1);
94     }
95 
96     // build smooth edges:
97     buildSmoothEdges(wxs);
98 
99     // Post processing for suggestive contours
100     if (_computeSuggestiveContours) {
101       postProcessSuggestiveContourShape(wxs);
102     }
103     if (progressBarDisplay) {
104       _pProgressBar->setProgress(_pProgressBar->getProgress() + 1);
105     }
106 
107     wxs->setComputeViewIndependentFlag(false);
108     _computeViewIndependent = false;
109     _changes = false;
110 
111     // reset user data
112     (*it)->ResetUserData();
113   }
114 }
115 
116 // GENERAL STUFF
117 ////////////////
preProcessShape(WXShape * iWShape)118 void FEdgeXDetector::preProcessShape(WXShape *iWShape)
119 {
120   _meanK1 = 0;
121   _meanKr = 0;
122   _minK1 = FLT_MAX;
123   _maxK1 = -FLT_MAX;
124   _minKr = FLT_MAX;
125   _maxKr = -FLT_MAX;
126   _nPoints = 0;
127 #if 0
128   _meanEdgeSize = iWShape->getMeanEdgeSize();
129 #else
130   _meanEdgeSize = iWShape->ComputeMeanEdgeSize();
131 #endif
132 
133   vector<WFace *> &wfaces = iWShape->GetFaceList();
134   vector<WFace *>::iterator f, fend;
135   // view dependent stuff
136   for (f = wfaces.begin(), fend = wfaces.end(); f != fend; ++f) {
137     preProcessFace((WXFace *)(*f));
138   }
139 
140   if (_computeRidgesAndValleys || _computeSuggestiveContours) {
141     vector<WVertex *> &wvertices = iWShape->getVertexList();
142     for (vector<WVertex *>::iterator wv = wvertices.begin(), wvend = wvertices.end(); wv != wvend;
143          ++wv) {
144       // Compute curvatures
145       WXVertex *wxv = dynamic_cast<WXVertex *>(*wv);
146       computeCurvatures(wxv);
147     }
148     _meanK1 /= (real)(_nPoints);
149     _meanKr /= (real)(_nPoints);
150   }
151 }
152 
preProcessFace(WXFace * iFace)153 void FEdgeXDetector::preProcessFace(WXFace *iFace)
154 {
155   Vec3f firstPoint = iFace->GetVertex(0)->GetVertex();
156   Vec3f N = iFace->GetNormal();
157 
158   // Compute the dot product between V (=_Viewpoint - firstPoint) and N:
159   Vec3f V;
160   if (_orthographicProjection) {
161     V = Vec3f(0.0f, 0.0f, _Viewpoint.z() - firstPoint.z());
162   }
163   else {
164     V = Vec3f(_Viewpoint - firstPoint);
165   }
166   N.normalize();
167   V.normalize();
168   iFace->setDotP(N * V);
169 
170   // compute the distance between the face center and the viewpoint:
171   if (_orthographicProjection) {
172     iFace->setZ(iFace->center().z() - _Viewpoint.z());
173   }
174   else {
175     Vec3f dist_vec(iFace->center() - _Viewpoint);
176     iFace->setZ(dist_vec.norm());
177   }
178 }
179 
computeCurvatures(WXVertex * vertex)180 void FEdgeXDetector::computeCurvatures(WXVertex *vertex)
181 {
182   // TODO: for some reason, the 'vertex' may have no associated edges
183   // (i.e., WVertex::_EdgeList is empty), which causes a crash due to
184   // a subsequent call of WVertex::_EdgeList.front().
185   if (vertex->GetEdges().empty()) {
186     if (G.debug & G_DEBUG_FREESTYLE) {
187       printf("Warning: WVertex %d has no associated edges.\n", vertex->GetId());
188     }
189     return;
190   }
191 
192   // CURVATURE LAYER
193   // store all the curvature datas for each vertex
194 
195   // soc unused - real K1, K2
196   real cos2theta, sin2theta;
197   Vec3r e1, n, v;
198   // one vertex curvature info :
199   CurvatureInfo *C;
200   float radius = _sphereRadius * _meanEdgeSize;
201 
202   // view independent stuff
203   if (_computeViewIndependent) {
204     C = new CurvatureInfo();
205     vertex->setCurvatures(C);
206     OGF::NormalCycle ncycle;
207     ncycle.begin();
208     if (radius > 0) {
209       OGF::compute_curvature_tensor(vertex, radius, ncycle);
210     }
211     else {
212       OGF::compute_curvature_tensor_one_ring(vertex, ncycle);
213     }
214     ncycle.end();
215     C->K1 = ncycle.kmin();
216     C->K2 = ncycle.kmax();
217     C->e1 = ncycle.Kmax();  // ncycle.kmin() * ncycle.Kmax();
218     C->e2 = ncycle.Kmin();  // ncycle.kmax() * ncycle.Kmin();
219 
220     real absK1 = fabs(C->K1);
221     _meanK1 += absK1;
222     if (absK1 > _maxK1) {
223       _maxK1 = absK1;
224     }
225     if (absK1 < _minK1) {
226       _minK1 = absK1;
227     }
228   }
229   // view dependent
230   C = vertex->curvatures();
231   if (C == 0) {
232     return;
233   }
234 
235   // compute radial curvature :
236   n = C->e1 ^ C->e2;
237   if (_orthographicProjection) {
238     v = Vec3r(0.0, 0.0, _Viewpoint.z() - vertex->GetVertex().z());
239   }
240   else {
241     v = Vec3r(_Viewpoint - vertex->GetVertex());
242   }
243   C->er = v - (v * n) * n;
244   C->er.normalize();
245   e1 = C->e1;
246   e1.normalize();
247   cos2theta = C->er * e1;
248   cos2theta *= cos2theta;
249   sin2theta = 1 - cos2theta;
250   C->Kr = C->K1 * cos2theta + C->K2 * sin2theta;
251   real absKr = fabs(C->Kr);
252   _meanKr += absKr;
253   if (absKr > _maxKr) {
254     _maxKr = absKr;
255   }
256   if (absKr < _minKr) {
257     _minKr = absKr;
258   }
259 
260   ++_nPoints;
261 }
262 
263 // SILHOUETTE
264 /////////////
processSilhouetteShape(WXShape * iWShape)265 void FEdgeXDetector::processSilhouetteShape(WXShape *iWShape)
266 {
267   // Make a first pass on every polygons in order to compute all their silhouette relative values:
268   vector<WFace *> &wfaces = iWShape->GetFaceList();
269   vector<WFace *>::iterator f, fend;
270   for (f = wfaces.begin(), fend = wfaces.end(); f != fend; ++f) {
271     ProcessSilhouetteFace((WXFace *)(*f));
272   }
273 
274   // Make a pass on the edges to detect the silhouette edges that are not smooth
275   vector<WEdge *>::iterator we, weend;
276   vector<WEdge *> &wedges = iWShape->getEdgeList();
277   for (we = wedges.begin(), weend = wedges.end(); we != weend; ++we) {
278     ProcessSilhouetteEdge((WXEdge *)(*we));
279   }
280 }
281 
ProcessSilhouetteFace(WXFace * iFace)282 void FEdgeXDetector::ProcessSilhouetteFace(WXFace *iFace)
283 {
284   // SILHOUETTE LAYER
285   Vec3f normal;
286   // Compute the dot products between View direction and N at each vertex of the face:
287   Vec3f point;
288   int closestPointId = 0;
289   float dist, minDist = FLT_MAX;
290   int numVertices = iFace->numberOfVertices();
291   WXFaceLayer *faceLayer = new WXFaceLayer(iFace, Nature::SILHOUETTE, true);
292   for (int i = 0; i < numVertices; i++) {
293     point = iFace->GetVertex(i)->GetVertex();
294     normal = iFace->GetVertexNormal(i);
295     normal.normalize();
296     Vec3f V;
297     if (_orthographicProjection) {
298       V = Vec3f(0.0f, 0.0f, _Viewpoint.z() - point.z());
299     }
300     else {
301       V = Vec3f(_Viewpoint - point);
302     }
303     V.normalize();
304     float d = normal * V;
305     faceLayer->PushDotP(d);
306     // Find the point the closest to the viewpoint
307     if (_orthographicProjection) {
308       dist = point.z() - _Viewpoint.z();
309     }
310     else {
311       Vec3f dist_vec(point - _Viewpoint);
312       dist = dist_vec.norm();
313     }
314     if (dist < minDist) {
315       minDist = dist;
316       closestPointId = i;
317     }
318   }
319   // Set the closest point id:
320   faceLayer->setClosestPointIndex(closestPointId);
321   // Add this layer to the face:
322   iFace->AddSmoothLayer(faceLayer);
323 }
324 
ProcessSilhouetteEdge(WXEdge * iEdge)325 void FEdgeXDetector::ProcessSilhouetteEdge(WXEdge *iEdge)
326 {
327   if (iEdge->nature() & Nature::BORDER) {
328     return;
329   }
330   // SILHOUETTE ?
331   //-------------
332   WXFace *fA = (WXFace *)iEdge->GetaOEdge()->GetaFace();
333   WXFace *fB = (WXFace *)iEdge->GetaOEdge()->GetbFace();
334 
335   if ((fA->front()) ^
336       (fB->front())) {  // fA->visible XOR fB->visible (true if one is 0 and the other is 1)
337     // The only edges we want to set as silhouette edges in this way are the ones with 2 different
338     // normals for 1 vertex for these two faces
339     //--------------------
340     // In reality we only test the normals for 1 of the 2 vertices.
341     if (fA->GetVertexNormal(iEdge->GetaVertex()) == fB->GetVertexNormal(iEdge->GetaVertex())) {
342       return;
343     }
344     iEdge->AddNature(Nature::SILHOUETTE);
345     if (fB->front()) {
346       iEdge->setOrder(1);
347     }
348     else {
349       iEdge->setOrder(-1);
350     }
351   }
352 }
353 
354 // BORDER
355 /////////
processBorderShape(WXShape * iWShape)356 void FEdgeXDetector::processBorderShape(WXShape *iWShape)
357 {
358   if (!_computeViewIndependent) {
359     return;
360   }
361   // Make a pass on the edges to detect the BORDER
362   vector<WEdge *>::iterator we, weend;
363   vector<WEdge *> &wedges = iWShape->getEdgeList();
364   for (we = wedges.begin(), weend = wedges.end(); we != weend; ++we) {
365     ProcessBorderEdge((WXEdge *)(*we));
366   }
367 }
368 
ProcessBorderEdge(WXEdge * iEdge)369 void FEdgeXDetector::ProcessBorderEdge(WXEdge *iEdge)
370 {
371   // first check whether it is a border edge: BORDER ?
372   //---------
373   if (iEdge->GetaFace() == 0) {
374     // it is a border edge
375     iEdge->AddNature(Nature::BORDER);
376   }
377 }
378 
379 // CREASE
380 /////////
processCreaseShape(WXShape * iWShape)381 void FEdgeXDetector::processCreaseShape(WXShape *iWShape)
382 {
383   if (!_computeViewIndependent) {
384     return;
385   }
386 
387   // Make a pass on the edges to detect the CREASE
388   vector<WEdge *>::iterator we, weend;
389   vector<WEdge *> &wedges = iWShape->getEdgeList();
390   for (we = wedges.begin(), weend = wedges.end(); we != weend; ++we) {
391     ProcessCreaseEdge((WXEdge *)(*we));
392   }
393 }
394 
ProcessCreaseEdge(WXEdge * iEdge)395 void FEdgeXDetector::ProcessCreaseEdge(WXEdge *iEdge)
396 {
397   // CREASE ?
398   //---------
399   if (iEdge->nature() & Nature::BORDER) {
400     return;
401   }
402   WXFace *fA = (WXFace *)iEdge->GetaOEdge()->GetaFace();
403   WXFace *fB = (WXFace *)iEdge->GetaOEdge()->GetbFace();
404 
405   WVertex *aVertex = iEdge->GetaVertex();
406   if ((fA->GetVertexNormal(aVertex) * fB->GetVertexNormal(aVertex)) <= _creaseAngle) {
407     iEdge->AddNature(Nature::CREASE);
408   }
409 }
410 
411 // RIDGES AND VALLEYS
412 /////////////////////
processRidgesAndValleysShape(WXShape * iWShape)413 void FEdgeXDetector::processRidgesAndValleysShape(WXShape *iWShape)
414 {
415   // Don't forget to add the built layer to the face at the end of the ProcessFace:
416   // iFace->AddSmoothLayer(faceLayer);
417 
418   if (!_computeViewIndependent) {
419     return;
420   }
421 
422   // Here the curvatures must already have been computed
423   vector<WFace *> &wfaces = iWShape->GetFaceList();
424   vector<WFace *>::iterator f, fend;
425   for (f = wfaces.begin(), fend = wfaces.end(); f != fend; ++f) {
426     ProcessRidgeFace((WXFace *)(*f));
427   }
428 }
429 
430 // RIDGES
431 /////////
ProcessRidgeFace(WXFace * iFace)432 void FEdgeXDetector::ProcessRidgeFace(WXFace *iFace)
433 {
434   WXFaceLayer *flayer = new WXFaceLayer(iFace, Nature::RIDGE | Nature::VALLEY, false);
435   iFace->AddSmoothLayer(flayer);
436 
437   unsigned int numVertices = iFace->numberOfVertices();
438   for (unsigned int i = 0; i < numVertices; ++i) {
439     WVertex *wv = iFace->GetVertex(i);
440     WXVertex *wxv = dynamic_cast<WXVertex *>(wv);
441     flayer->PushDotP(wxv->curvatures()->K1);
442   }
443 
444 #if 0  // XXX fabs(flayer->dotP(i)) < threshold cannot be true
445   real threshold = 0;
446   //real threshold = _maxK1 - (_maxK1 - _meanK1) / 20.0;
447 
448   if (flayer->nPosDotP() != numVertices) {
449     if ((fabs(flayer->dotP(0)) < threshold) && (fabs(flayer->dotP(1)) < threshold) &&
450         (fabs(flayer->dotP(2)) < threshold)) {
451       flayer->ReplaceDotP(0, 0);
452       flayer->ReplaceDotP(1, 0);
453       flayer->ReplaceDotP(2, 0);
454     }
455   }
456 #endif
457 }
458 
459 #if 0
460 void FEdgeXDetector::ProcessRidgeFace(WXFace *iFace)
461 {
462   // RIDGE LAYER
463   // Compute the RidgeFunction, that is the derivative of the ppal curvature along e1 at each vertex of the face
464   WVertex *v;
465   Vec3r v1v2;
466   real t;
467   vector<WXFaceLayer *> SmoothLayers;
468   WXFaceLayer *faceLayer;
469   Face_Curvature_Info *layer_info;
470   real K1_a(0), K1_b(0);
471   Vec3r Inter_a, Inter_b;
472 
473   // find the ridge layer of the face
474   iFace->retrieveSmoothLayers(Nature::RIDGE, SmoothLayers);
475   if (SmoothLayers.size() != 1) {
476     return;
477   }
478   faceLayer = SmoothLayers[0];
479   // retrieve the curvature info of this layer
480   layer_info = (Face_Curvature_Info *)faceLayer->userdata;
481 
482   int numVertices = iFace->numberOfVertices();
483   for (int i = 0; i < numVertices; i++) {
484     v = iFace->GetVertex(i);
485     // vec_curvature_info[i] contains the curvature info of this vertex
486     Vec3r e2 = layer_info->vec_curvature_info[i]->K2 * layer_info->vec_curvature_info[i]->e2;
487     Vec3r e1 = layer_info->vec_curvature_info[i]->K1 * layer_info->vec_curvature_info[i]->e1;
488     e2.normalize();
489 
490     WVertex::face_iterator fit = v->faces_begin();
491     WVertex::face_iterator fitend = v->faces_end();
492     for (; fit != fitend; ++fit) {
493       WXFace *wxf = dynamic_cast<WXFace *>(*fit);
494       WOEdge *oppositeEdge;
495       if (!(wxf->getOppositeEdge(v, oppositeEdge))) {
496         continue;
497       }
498       v1v2 = oppositeEdge->GetbVertex()->GetVertex() - oppositeEdge->GetaVertex()->GetVertex();
499       GeomUtils::intersection_test res;
500       res = GeomUtils::intersectRayPlane(
501           oppositeEdge->GetaVertex()->GetVertex(), v1v2, e2, -(v->GetVertex() * e2), t, 1.0e-06);
502       if ((res == GeomUtils::DO_INTERSECT) && (t >= 0.0) && (t <= 1.0)) {
503         vector<WXFaceLayer *> second_ridge_layer;
504         wxf->retrieveSmoothLayers(Nature::RIDGE, second_ridge_layer);
505         if (second_ridge_layer.size() != 1) {
506           continue;
507         }
508         Face_Curvature_Info *second_layer_info =
509             (Face_Curvature_Info *)second_ridge_layer[0]->userdata;
510 
511         unsigned index1 = wxf->GetIndex(oppositeEdge->GetaVertex());
512         unsigned index2 = wxf->GetIndex(oppositeEdge->GetbVertex());
513         real K1_1 = second_layer_info->vec_curvature_info[index1]->K1;
514         real K1_2 = second_layer_info->vec_curvature_info[index2]->K1;
515         real K1 = (1.0 - t) * K1_1 + t * K1_2;
516         Vec3r inter((1.0 - t) * oppositeEdge->GetaVertex()->GetVertex() +
517                     t * oppositeEdge->GetbVertex()->GetVertex());
518         Vec3r vtmp(inter - v->GetVertex());
519         // is it K1_a or K1_b ?
520         if (vtmp * e1 > 0) {
521           K1_b = K1;
522           Inter_b = inter;
523         }
524         else {
525           K1_a = K1;
526           Inter_a = inter;
527         }
528       }
529     }
530     // Once we have K1 along the ppal direction compute the derivative : K1b - K1a put it in DotP
531     //real d = fabs(K1_b) - fabs(K1_a);
532     real d = 0;
533     real threshold = _meanK1 + (_maxK1 - _meanK1) / 7.0;
534     //real threshold = _meanK1;
535     //if ((fabs(K1_b) > threshold) || ((fabs(K1_a) > threshold)))
536     d = (K1_b) - (K1_a) / (Inter_b - Inter_a).norm();
537     faceLayer->PushDotP(d);
538     //faceLayer->PushDotP(layer_info->vec_curvature_info[i]->K1);
539   }
540 
541   // Make the values relevant by checking whether all principal directions have the "same" direction:
542   Vec3r e0((layer_info->vec_curvature_info[0]->K1 * layer_info->vec_curvature_info[0]->e1));
543   e0.normalize();
544   Vec3r e1((layer_info->vec_curvature_info[1]->K1 * layer_info->vec_curvature_info[1]->e1));
545   e1.normalize();
546   Vec3r e2((layer_info->vec_curvature_info[2]->K1 * layer_info->vec_curvature_info[2]->e1));
547   e2.normalize();
548   if (e0 * e1 < 0) {
549     // invert dotP[1]
550     faceLayer->ReplaceDotP(1, -faceLayer->dotP(1));
551   }
552   if (e0 * e2 < 0) {
553     // invert dotP[2]
554     faceLayer->ReplaceDotP(2, -faceLayer->dotP(2));
555   }
556 
557 #  if 0  // remove the weakest values;
558   real minDiff = (_maxK1 - _minK1) / 10.0;
559   real minDiff = _meanK1;
560   if ((faceLayer->dotP(0) < minDiff) && (faceLayer->dotP(1) < minDiff) && (faceLayer->dotP(2) < minDiff)) {
561     faceLayer->ReplaceDotP(0, 0);
562     faceLayer->ReplaceDotP(1, 0);
563     faceLayer->ReplaceDotP(2, 0);
564   }
565 #  endif
566 }
567 #endif
568 
569 // SUGGESTIVE CONTOURS
570 //////////////////////
571 
processSuggestiveContourShape(WXShape * iWShape)572 void FEdgeXDetector::processSuggestiveContourShape(WXShape *iWShape)
573 {
574   // Here the curvatures must already have been computed
575   vector<WFace *> &wfaces = iWShape->GetFaceList();
576   vector<WFace *>::iterator f, fend;
577   for (f = wfaces.begin(), fend = wfaces.end(); f != fend; ++f) {
578     ProcessSuggestiveContourFace((WXFace *)(*f));
579   }
580 }
581 
ProcessSuggestiveContourFace(WXFace * iFace)582 void FEdgeXDetector::ProcessSuggestiveContourFace(WXFace *iFace)
583 {
584   WXFaceLayer *faceLayer = new WXFaceLayer(iFace, Nature::SUGGESTIVE_CONTOUR, true);
585   iFace->AddSmoothLayer(faceLayer);
586 
587   unsigned int numVertices = iFace->numberOfVertices();
588   for (unsigned int i = 0; i < numVertices; ++i) {
589     WVertex *wv = iFace->GetVertex(i);
590     WXVertex *wxv = dynamic_cast<WXVertex *>(wv);
591     faceLayer->PushDotP(wxv->curvatures()->Kr);
592   }
593 
594 #if 0  // FIXME: find a more clever way to compute the threshold
595   real threshold = _meanKr;
596   if (faceLayer->nPosDotP() != numVertices) {
597     if ((fabs(faceLayer->dotP(0)) < threshold) && (fabs(faceLayer->dotP(1)) < threshold) &&
598         (fabs(faceLayer->dotP(2)) < threshold)) {
599       faceLayer->ReplaceDotP(0, 0);
600       faceLayer->ReplaceDotP(1, 0);
601       faceLayer->ReplaceDotP(2, 0);
602     }
603   }
604 #endif
605 }
606 
postProcessSuggestiveContourShape(WXShape * iShape)607 void FEdgeXDetector::postProcessSuggestiveContourShape(WXShape *iShape)
608 {
609   vector<WFace *> &wfaces = iShape->GetFaceList();
610   vector<WFace *>::iterator f, fend;
611   for (f = wfaces.begin(), fend = wfaces.end(); f != fend; ++f) {
612     postProcessSuggestiveContourFace((WXFace *)(*f));
613   }
614 }
615 
postProcessSuggestiveContourFace(WXFace * iFace)616 void FEdgeXDetector::postProcessSuggestiveContourFace(WXFace *iFace)
617 {
618   // Compute the derivative of the radial curvature in the radial direction, at the two extremities
619   // of the smooth edge.
620   // If the derivative is smaller than a given threshold _kr_derivative_epsilon, discard the edge.
621 
622   // Find the suggestive contour layer of the face (zero or one edge).
623   vector<WXFaceLayer *> sc_layers;
624   iFace->retrieveSmoothEdgesLayers(Nature::SUGGESTIVE_CONTOUR, sc_layers);
625   if (sc_layers.empty()) {
626     return;
627   }
628 
629   WXFaceLayer *sc_layer;
630   sc_layer = sc_layers[0];
631 
632   // Compute the derivative value at each vertex of the face, and add it in a vector.
633   vector<real> kr_derivatives;
634 
635   unsigned vertices_nb = iFace->numberOfVertices();
636   WXVertex *v, *opposite_vertex_a, *opposite_vertex_b;
637   WXFace *wxf;
638   WOEdge *opposite_edge;
639   Vec3r normal_vec, radial_normal_vec, er_vec, v_vec, inter, inter1, inter2, tmp_vec;
640   GeomUtils::intersection_test res;
641   real kr(0), kr1(0), kr2(0), t;
642 
643   for (unsigned int i = 0; i < vertices_nb; ++i) {
644     v = (WXVertex *)(iFace->GetVertex(i));
645 
646     // v is a singular vertex, skip it.
647     if (v->isBoundary()) {
648       kr_derivatives.push_back(0);
649       continue;
650     }
651 
652     v_vec = v->GetVertex();
653     er_vec = v->curvatures()->er;
654 
655     // For each vertex, iterate on its adjacent faces.
656     for (WVertex::face_iterator fit = v->faces_begin(), fitend = v->faces_end(); fit != fitend;
657          ++fit) {
658       wxf = dynamic_cast<WXFace *>(*fit);
659       if (!wxf->getOppositeEdge(v, opposite_edge)) {
660         continue;
661       }
662 
663       opposite_vertex_a = (WXVertex *)opposite_edge->GetaVertex();
664       opposite_vertex_b = (WXVertex *)opposite_edge->GetbVertex();
665       normal_vec = wxf->GetVertexNormal(v);  // FIXME: what about e1 ^ e2 ?
666       radial_normal_vec = er_vec ^ normal_vec;
667 
668       // Test whether the radial plan intersects with the edge at the opposite of v.
669       res = GeomUtils::intersectRayPlane(opposite_vertex_a->GetVertex(),
670                                          opposite_edge->GetVec(),
671                                          radial_normal_vec,
672                                          -(v_vec * radial_normal_vec),
673                                          t,
674                                          1.0e-06);
675 
676       // If there is an intersection, compute the value of the derivative ath that point.
677       if ((res == GeomUtils::DO_INTERSECT) && (t >= 0) && (t <= 1)) {
678         kr = t * opposite_vertex_a->curvatures()->Kr +
679              (1 - t) * opposite_vertex_b->curvatures()->Kr;
680         inter = opposite_vertex_a->GetVertex() + t * opposite_edge->GetVec();
681         tmp_vec = inter - v->GetVertex();
682         // Is it kr1 or kr2?
683         if (tmp_vec * er_vec > 0) {
684           kr2 = kr;
685           inter2 = inter;
686         }
687         else {
688           kr1 = kr;
689           inter1 = inter;
690         }
691       }
692     }
693 
694     // Now we have kr1 and kr2 along the radial direction, for one vertex of iFace.
695     // We have to compute the derivative of kr for that vertex, equal to:
696     // (kr2 - kr1) / dist(inter1, inter2).
697     // Then we add it to the vector of derivatives.
698     v->curvatures()->dKr = (kr2 - kr1) / (inter2 - inter1).norm();
699     kr_derivatives.push_back(v->curvatures()->dKr);
700   }
701 
702   // At that point, we have the derivatives for each vertex of iFace.
703   // All we have to do now is to use linear interpolation to compute the values at the extremities
704   // of the smooth edge.
705   WXSmoothEdge *sc_edge = sc_layer->getSmoothEdge();
706   WOEdge *sc_oedge = sc_edge->woea();
707   t = sc_edge->ta();
708   if (t * kr_derivatives[iFace->GetIndex(sc_oedge->GetaVertex())] +
709           (1 - t) * kr_derivatives[iFace->GetIndex(sc_oedge->GetbVertex())] <
710       _kr_derivative_epsilon) {
711     sc_layer->removeSmoothEdge();
712     return;
713   }
714   sc_oedge = sc_edge->woeb();
715   t = sc_edge->tb();
716   if (t * kr_derivatives[iFace->GetIndex(sc_oedge->GetaVertex())] +
717           (1 - t) * kr_derivatives[iFace->GetIndex(sc_oedge->GetbVertex())] <
718       _kr_derivative_epsilon) {
719     sc_layer->removeSmoothEdge();
720   }
721 }
722 
723 // MATERIAL_BOUNDARY
724 ////////////////////
processMaterialBoundaryShape(WXShape * iWShape)725 void FEdgeXDetector::processMaterialBoundaryShape(WXShape *iWShape)
726 {
727   if (!_computeViewIndependent) {
728     return;
729   }
730   // Make a pass on the edges to detect material boundaries
731   vector<WEdge *>::iterator we, weend;
732   vector<WEdge *> &wedges = iWShape->getEdgeList();
733   for (we = wedges.begin(), weend = wedges.end(); we != weend; ++we) {
734     ProcessMaterialBoundaryEdge((WXEdge *)(*we));
735   }
736 }
737 
ProcessMaterialBoundaryEdge(WXEdge * iEdge)738 void FEdgeXDetector::ProcessMaterialBoundaryEdge(WXEdge *iEdge)
739 {
740   // check whether the edge is a material boundary?
741   WFace *aFace = iEdge->GetaFace();
742   WFace *bFace = iEdge->GetbFace();
743   if (aFace && bFace && aFace->frs_materialIndex() != bFace->frs_materialIndex()) {
744     iEdge->AddNature(Nature::MATERIAL_BOUNDARY);
745   }
746 }
747 
748 // EDGE MARKS
749 /////////////
processEdgeMarksShape(WXShape * iShape)750 void FEdgeXDetector::processEdgeMarksShape(WXShape *iShape)
751 {
752   // Make a pass on the edges to detect material boundaries
753   vector<WEdge *>::iterator we, weend;
754   vector<WEdge *> &wedges = iShape->getEdgeList();
755   for (we = wedges.begin(), weend = wedges.end(); we != weend; ++we) {
756     ProcessEdgeMarks((WXEdge *)(*we));
757   }
758 }
759 
ProcessEdgeMarks(WXEdge * iEdge)760 void FEdgeXDetector::ProcessEdgeMarks(WXEdge *iEdge)
761 {
762   if (iEdge->GetMark()) {
763     iEdge->AddNature(Nature::EDGE_MARK);
764   }
765 }
766 
767 // Build Smooth edges
768 /////////////////////
buildSmoothEdges(WXShape * iShape)769 void FEdgeXDetector::buildSmoothEdges(WXShape *iShape)
770 {
771   bool hasSmoothEdges = false;
772 
773   // Make a last pass to build smooth edges from the previous stored values:
774   //--------------------------------------------------------------------------
775   vector<WFace *> &wfaces = iShape->GetFaceList();
776   for (vector<WFace *>::iterator f = wfaces.begin(), fend = wfaces.end(); f != fend; ++f) {
777     vector<WXFaceLayer *> &faceLayers = ((WXFace *)(*f))->getSmoothLayers();
778     for (vector<WXFaceLayer *>::iterator wxfl = faceLayers.begin(), wxflend = faceLayers.end();
779          wxfl != wxflend;
780          ++wxfl) {
781       if ((*wxfl)->BuildSmoothEdge()) {
782         hasSmoothEdges = true;
783       }
784     }
785   }
786 
787   if (hasSmoothEdges && !_computeRidgesAndValleys && !_computeSuggestiveContours) {
788     vector<WVertex *> &wvertices = iShape->getVertexList();
789     for (vector<WVertex *>::iterator wv = wvertices.begin(), wvend = wvertices.end(); wv != wvend;
790          ++wv) {
791       // Compute curvatures
792       WXVertex *wxv = dynamic_cast<WXVertex *>(*wv);
793       computeCurvatures(wxv);
794     }
795     _meanK1 /= (real)(_nPoints);
796     _meanKr /= (real)(_nPoints);
797   }
798 }
799 
800 } /* namespace Freestyle */
801