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