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 Classes to define a Winged Edge data structure.
20 */
21
22 #include <iostream>
23
24 #include "WEdge.h"
25
26 namespace Freestyle {
27
28 /*! Temporary structures */
29 class vertexdata {
30 public:
31 WVertex *_copy;
32 };
33
34 class oedgedata {
35 public:
36 WOEdge *_copy;
37 };
38
39 class edgedata {
40 public:
41 WEdge *_copy;
42 };
43
44 class facedata {
45 public:
46 WFace *_copy;
47 };
48
49 /**********************************
50 * *
51 * *
52 * WVertex *
53 * *
54 * *
55 **********************************/
56
WVertex(WVertex & iBrother)57 WVertex::WVertex(WVertex &iBrother)
58 {
59 _Id = iBrother._Id;
60 _Vertex = iBrother._Vertex;
61 _EdgeList = iBrother._EdgeList;
62
63 _Shape = iBrother._Shape;
64 _Smooth = iBrother._Smooth;
65 _Border = iBrother._Border;
66 userdata = NULL;
67 iBrother.userdata = new vertexdata;
68 ((vertexdata *)(iBrother.userdata))->_copy = this;
69 }
70
duplicate()71 WVertex *WVertex::duplicate()
72 {
73 WVertex *clone = new WVertex(*this);
74 return clone;
75 }
76
operator *()77 WOEdge *WVertex::incoming_edge_iterator::operator*()
78 {
79 return _current;
80 }
81
increment()82 void WVertex::incoming_edge_iterator::increment()
83 {
84 WOEdge *twin = _current->twin();
85 if (!twin) {
86 // we reached a hole
87 _current = 0;
88 return;
89 }
90 WOEdge *next = twin->getPrevOnFace();
91 if (next == _begin) {
92 next = NULL;
93 }
94 _current = next;
95 }
96
operator *()97 WFace *WVertex::face_iterator::operator*()
98 {
99 WOEdge *woedge = *_edge_it;
100 if (!woedge) {
101 return NULL;
102 }
103 return (woedge)->GetbFace();
104 }
105
106 #if 0
107 bool WVertex::isBoundary() const
108 {
109 return _Border;
110 }
111 #endif
isBoundary()112 bool WVertex::isBoundary()
113 {
114 if (_Border == 1) {
115 return true;
116 }
117 if (_Border == 0) {
118 return false;
119 }
120
121 vector<WEdge *>::const_iterator it;
122 for (it = _EdgeList.begin(); it != _EdgeList.end(); it++) {
123 if ((*it)->GetNumberOfOEdges() == 1) {
124 _Border = 1;
125 return true;
126 }
127 }
128 #if 0
129 if (!(*it)->GetaOEdge()->GetaFace()) {
130 return true;
131 }
132 #endif
133 _Border = 0;
134 return false;
135 }
136
AddEdge(WEdge * iEdge)137 void WVertex::AddEdge(WEdge *iEdge)
138 {
139 _EdgeList.push_back(iEdge);
140 }
141
incoming_edges_begin()142 WVertex::incoming_edge_iterator WVertex::incoming_edges_begin()
143 {
144 WOEdge *begin;
145 WEdge *wedge = _EdgeList.front();
146 WOEdge *aOEdge = wedge->GetaOEdge();
147 if (aOEdge->GetbVertex() == this) {
148 begin = aOEdge;
149 }
150 else {
151 begin = _EdgeList.front()->GetbOEdge();
152 }
153 return incoming_edge_iterator(this, begin, begin);
154 }
155
incoming_edges_end()156 WVertex::incoming_edge_iterator WVertex::incoming_edges_end()
157 {
158 WOEdge *begin;
159 WOEdge *aOEdge = _EdgeList.front()->GetaOEdge();
160 if (aOEdge->GetbVertex() == this) {
161 begin = aOEdge;
162 }
163 else {
164 begin = _EdgeList.front()->GetbOEdge();
165 }
166 return incoming_edge_iterator(this, begin, 0);
167 }
168 #if 0
169 WOEdge **WVertex::incoming_edge_iterator::operator->()
170 {
171 WOEdge **ppaOEdge = (*_iter)->GetaOEdge();
172 if (aOEdge->GetbVertex() == _vertex) {
173 return ppaOEdge;
174 }
175 else {
176 WOEdge *bOEdge = (*_iter)->GetbOEdge();
177 return &bOEdge;
178 }
179 }
180 #endif
181
182 /**********************************
183 * *
184 * *
185 * WOEdge *
186 * *
187 * *
188 **********************************/
189
WOEdge(WOEdge & iBrother)190 WOEdge::WOEdge(WOEdge &iBrother)
191 {
192 _paVertex = iBrother.GetaVertex();
193 _pbVertex = iBrother.GetbVertex();
194 _paFace = iBrother.GetaFace();
195 _pbFace = iBrother.GetbFace();
196 _pOwner = iBrother.GetOwner();
197 userdata = NULL;
198 iBrother.userdata = new oedgedata;
199 ((oedgedata *)(iBrother.userdata))->_copy = this;
200
201 _vec = iBrother._vec;
202 _angle = iBrother._angle;
203 }
204
duplicate()205 WOEdge *WOEdge::duplicate()
206 {
207 WOEdge *clone = new WOEdge(*this);
208 return clone;
209 }
210
twin()211 WOEdge *WOEdge::twin()
212 {
213 return GetOwner()->GetOtherOEdge(this);
214 }
215
getPrevOnFace()216 WOEdge *WOEdge::getPrevOnFace()
217 {
218 return _pbFace->GetPrevOEdge(this);
219 }
220
221 /**********************************
222 * *
223 * *
224 * WEdge *
225 * *
226 * *
227 **********************************/
228
WEdge(WEdge & iBrother)229 WEdge::WEdge(WEdge &iBrother)
230 {
231 _paOEdge = NULL;
232 _pbOEdge = NULL;
233 WOEdge *aoedge = iBrother.GetaOEdge();
234 WOEdge *boedge = iBrother.GetbOEdge();
235 userdata = NULL;
236
237 if (aoedge) {
238 //_paOEdge = new WOEdge(*aoedge);
239 _paOEdge = aoedge->duplicate();
240 }
241 if (boedge) {
242 //_pbOEdge = new WOEdge(*boedge);
243 _pbOEdge = boedge->duplicate();
244 }
245
246 _nOEdges = iBrother.GetNumberOfOEdges();
247 _Id = iBrother.GetId();
248 iBrother.userdata = new edgedata;
249 ((edgedata *)(iBrother.userdata))->_copy = this;
250 }
251
duplicate()252 WEdge *WEdge::duplicate()
253 {
254 WEdge *clone = new WEdge(*this);
255 return clone;
256 }
257
258 /**********************************
259 * *
260 * *
261 * WFace *
262 * *
263 * *
264 **********************************/
265
WFace(WFace & iBrother)266 WFace::WFace(WFace &iBrother)
267 {
268 _OEdgeList = iBrother.getEdgeList();
269 _Normal = iBrother.GetNormal();
270 _VerticesNormals = iBrother._VerticesNormals;
271 _VerticesTexCoords = iBrother._VerticesTexCoords;
272 _Id = iBrother.GetId();
273 _FrsMaterialIndex = iBrother._FrsMaterialIndex;
274 _Mark = iBrother._Mark;
275 userdata = NULL;
276 iBrother.userdata = new facedata;
277 ((facedata *)(iBrother.userdata))->_copy = this;
278 }
279
duplicate()280 WFace *WFace::duplicate()
281 {
282 WFace *clone = new WFace(*this);
283 return clone;
284 }
285
frs_material()286 const FrsMaterial &WFace::frs_material()
287 {
288 return getShape()->frs_material(_FrsMaterialIndex);
289 }
290
MakeEdge(WVertex * v1,WVertex * v2)291 WOEdge *WFace::MakeEdge(WVertex *v1, WVertex *v2)
292 {
293 // First check whether the same oriented edge already exists or not:
294 vector<WEdge *> &v1Edges = v1->GetEdges();
295 for (vector<WEdge *>::iterator it1 = v1Edges.begin(), end = v1Edges.end(); it1 != end; it1++) {
296 WEdge *we = (*it1);
297 WOEdge *woea = we->GetaOEdge();
298
299 if ((woea->GetaVertex() == v1) && (woea->GetbVertex() == v2)) {
300 // The oriented edge already exists
301 cerr << "Warning: edge " << v1->GetId() << " - " << v2->GetId()
302 << " appears twice, correcting" << endl;
303 // Adds the edge to the face
304 AddEdge(woea);
305 (*it1)->setNumberOfOEdges((*it1)->GetNumberOfOEdges() + 1);
306 // sets these vertices as border:
307 v1->setBorder(true);
308 v2->setBorder(true);
309 return woea;
310 }
311
312 WOEdge *woeb = we->GetbOEdge();
313 if (woeb && (woeb->GetaVertex() == v1) && (woeb->GetbVertex() == v2)) {
314 // The oriented edge already exists
315 cerr << "Warning: edge " << v1->GetId() << " - " << v2->GetId()
316 << " appears twice, correcting" << endl;
317 // Adds the edge to the face
318 AddEdge(woeb);
319 (*it1)->setNumberOfOEdges((*it1)->GetNumberOfOEdges() + 1);
320 // sets these vertices as border:
321 v1->setBorder(true);
322 v2->setBorder(true);
323 return woeb;
324 }
325 }
326
327 // the oriented edge we're about to build
328 WOEdge *pOEdge = new WOEdge;
329 // The edge containing the oriented edge.
330 WEdge *edge;
331
332 // checks whether this edge already exists or not
333 // If it exists, it points outward v2
334 bool exist = false;
335 WOEdge *pInvertEdge = NULL; // The inverted edge if it exists
336 vector<WEdge *> &v2Edges = v2->GetEdges();
337 vector<WEdge *>::iterator it;
338 for (it = v2Edges.begin(); it != v2Edges.end(); it++) {
339 if ((*it)->GetbVertex() == v1) {
340 // The invert edge already exists
341 exist = true;
342 pInvertEdge = (*it)->GetaOEdge();
343 break;
344 }
345 }
346
347 // DEBUG:
348 if (true == exist) { // The invert edge already exists
349 // Retrieves the corresponding edge
350 edge = pInvertEdge->GetOwner();
351
352 // Sets the a Face (retrieved from pInvertEdge
353 pOEdge->setaFace(pInvertEdge->GetbFace());
354
355 // Updates the invert edge:
356 pInvertEdge->setaFace(this);
357 }
358 else { // The invert edge does not exist yet
359 // we must create a new edge
360 // edge = new WEdge;
361 edge = instanciateEdge();
362
363 // updates the a,b vertex edges list:
364 v1->AddEdge(edge);
365 v2->AddEdge(edge);
366 }
367
368 pOEdge->setOwner(edge);
369 // Add the vertices:
370 pOEdge->setaVertex(v1);
371 pOEdge->setbVertex(v2);
372
373 // Debug:
374 if (v1->GetId() == v2->GetId()) {
375 cerr << "Warning: edge " << this << " null with vertex " << v1->GetId() << endl;
376 }
377
378 edge->AddOEdge(pOEdge);
379 // edge->setNumberOfOEdges(edge->GetNumberOfOEdges() + 1);
380
381 // Add this face (the b face)
382 pOEdge->setbFace(this);
383
384 // Adds the edge to the face
385 AddEdge(pOEdge);
386
387 return pOEdge;
388 }
389
getOppositeEdge(const WVertex * v,WOEdge * & e)390 bool WFace::getOppositeEdge(const WVertex *v, WOEdge *&e)
391 {
392 if (_OEdgeList.size() != 3) {
393 return false;
394 }
395
396 vector<WOEdge *>::iterator it;
397 e = NULL;
398 for (it = _OEdgeList.begin(); it != _OEdgeList.end(); it++) {
399 if ((*it)->GetaVertex() == v) {
400 e = *it;
401 }
402 }
403 if (!e) {
404 return false;
405 }
406 e = NULL;
407 for (it = _OEdgeList.begin(); it != _OEdgeList.end(); it++) {
408 if (((*it)->GetaVertex() != v) && ((*it)->GetbVertex() != v)) {
409 e = *it;
410 }
411 }
412 if (!e) {
413 return false;
414 }
415
416 return true;
417 }
418
getArea()419 float WFace::getArea()
420 {
421 vector<WOEdge *>::iterator it;
422 Vec3f origin = (*(_OEdgeList.begin()))->GetaVertex()->GetVertex();
423 it = _OEdgeList.begin();
424 float a = 0;
425 for (it = it++; it != _OEdgeList.end(); it++) {
426 Vec3f v1 = Vec3f((*it)->GetaVertex()->GetVertex() - origin);
427 Vec3f v2 = Vec3f((*it)->GetbVertex()->GetVertex() - origin);
428 a += (v1 ^ v2).norm() / 2.0f;
429 }
430 return a;
431 }
432
GetPrevOEdge(WOEdge * iOEdge)433 WOEdge *WFace::GetPrevOEdge(WOEdge *iOEdge)
434 {
435 vector<WOEdge *>::iterator woe, woend, woefirst;
436 woefirst = _OEdgeList.begin();
437 woend = _OEdgeList.end();
438 WOEdge *prev = *woefirst;
439 woe = woefirst;
440 ++woe;
441 for (; woe != woend; woe++) {
442 if ((*woe) == iOEdge) {
443 return prev;
444 }
445 prev = *woe;
446 }
447 // We left the loop. That means that the first OEdge was the good one:
448 if ((*woefirst) == iOEdge) {
449 return prev;
450 }
451
452 return NULL;
453 }
454
getShape()455 WShape *WFace::getShape()
456 {
457 return GetVertex(0)->shape();
458 }
459
460 /**********************************
461 * *
462 * *
463 * WShape *
464 * *
465 * *
466 **********************************/
467
468 unsigned WShape::_SceneCurrentId = 0;
469
duplicate()470 WShape *WShape::duplicate()
471 {
472 WShape *clone = new WShape(*this);
473 return clone;
474 }
475
WShape(WShape & iBrother)476 WShape::WShape(WShape &iBrother)
477 {
478 _Id = iBrother.GetId();
479 _Name = iBrother._Name;
480 _LibraryPath = iBrother._LibraryPath;
481 _FrsMaterials = iBrother._FrsMaterials;
482 #if 0
483 _meanEdgeSize = iBrother._meanEdgeSize;
484 iBrother.bbox(_min, _max);
485 #endif
486 vector<WVertex *> &vertexList = iBrother.getVertexList();
487 vector<WVertex *>::iterator v = vertexList.begin(), vend = vertexList.end();
488 for (; v != vend; ++v) {
489 // WVertex *newVertex = new WVertex(*(*v));
490 WVertex *newVertex = (*v)->duplicate();
491
492 newVertex->setShape(this);
493 AddVertex(newVertex);
494 }
495
496 vector<WEdge *> &edgeList = iBrother.getEdgeList();
497 vector<WEdge *>::iterator e = edgeList.begin(), eend = edgeList.end();
498 for (; e != eend; ++e) {
499 // WEdge *newEdge = new WEdge(*(*e));
500 WEdge *newEdge = (*e)->duplicate();
501 AddEdge(newEdge);
502 }
503
504 vector<WFace *> &faceList = iBrother.GetFaceList();
505 vector<WFace *>::iterator f = faceList.begin(), fend = faceList.end();
506 for (; f != fend; ++f) {
507 // WFace *newFace = new WFace(*(*f));
508 WFace *newFace = (*f)->duplicate();
509 AddFace(newFace);
510 }
511
512 // update all pointed addresses thanks to the newly created objects:
513 vend = _VertexList.end();
514 for (v = _VertexList.begin(); v != vend; ++v) {
515 const vector<WEdge *> &vedgeList = (*v)->GetEdges();
516 vector<WEdge *> newvedgelist;
517 unsigned int i;
518 for (i = 0; i < vedgeList.size(); i++) {
519 WEdge *current = vedgeList[i];
520 edgedata *currentvedata = (edgedata *)current->userdata;
521 newvedgelist.push_back(currentvedata->_copy);
522 }
523 (*v)->setEdges(newvedgelist);
524 }
525
526 eend = _EdgeList.end();
527 for (e = _EdgeList.begin(); e != eend; ++e) {
528 // update aOedge:
529 WOEdge *aoEdge = (*e)->GetaOEdge();
530 aoEdge->setaVertex(((vertexdata *)(aoEdge->GetaVertex()->userdata))->_copy);
531 aoEdge->setbVertex(((vertexdata *)(aoEdge->GetbVertex()->userdata))->_copy);
532 if (aoEdge->GetaFace()) {
533 aoEdge->setaFace(((facedata *)(aoEdge->GetaFace()->userdata))->_copy);
534 }
535 aoEdge->setbFace(((facedata *)(aoEdge->GetbFace()->userdata))->_copy);
536 aoEdge->setOwner(((edgedata *)(aoEdge->GetOwner()->userdata))->_copy);
537
538 // update bOedge:
539 WOEdge *boEdge = (*e)->GetbOEdge();
540 if (boEdge) {
541 boEdge->setaVertex(((vertexdata *)(boEdge->GetaVertex()->userdata))->_copy);
542 boEdge->setbVertex(((vertexdata *)(boEdge->GetbVertex()->userdata))->_copy);
543 if (boEdge->GetaFace()) {
544 boEdge->setaFace(((facedata *)(boEdge->GetaFace()->userdata))->_copy);
545 }
546 boEdge->setbFace(((facedata *)(boEdge->GetbFace()->userdata))->_copy);
547 boEdge->setOwner(((edgedata *)(boEdge->GetOwner()->userdata))->_copy);
548 }
549 }
550
551 fend = _FaceList.end();
552 for (f = _FaceList.begin(); f != fend; ++f) {
553 unsigned int i;
554 const vector<WOEdge *> &oedgeList = (*f)->getEdgeList();
555 vector<WOEdge *> newoedgelist;
556
557 unsigned int n = oedgeList.size();
558 for (i = 0; i < n; i++) {
559 WOEdge *current = oedgeList[i];
560 oedgedata *currentoedata = (oedgedata *)current->userdata;
561 newoedgelist.push_back(currentoedata->_copy);
562 // oedgeList[i] = currentoedata->_copy;
563 // oedgeList[i] = ((oedgedata *)(oedgeList[i]->userdata))->_copy;
564 }
565 (*f)->setEdgeList(newoedgelist);
566 }
567
568 // Free all memory (arghh!)
569 // Vertex
570 vend = iBrother.getVertexList().end();
571 for (v = iBrother.getVertexList().begin(); v != vend; ++v) {
572 delete (vertexdata *)((*v)->userdata);
573 (*v)->userdata = NULL;
574 }
575
576 // Edges and OEdges:
577 eend = iBrother.getEdgeList().end();
578 for (e = iBrother.getEdgeList().begin(); e != eend; ++e) {
579 delete (edgedata *)((*e)->userdata);
580 (*e)->userdata = NULL;
581 // OEdge a:
582 delete (oedgedata *)((*e)->GetaOEdge()->userdata);
583 (*e)->GetaOEdge()->userdata = NULL;
584 // OEdge b:
585 WOEdge *oedgeb = (*e)->GetbOEdge();
586 if (oedgeb) {
587 delete (oedgedata *)(oedgeb->userdata);
588 oedgeb->userdata = NULL;
589 }
590 }
591
592 // Faces
593 fend = iBrother.GetFaceList().end();
594 for (f = iBrother.GetFaceList().begin(); f != fend; ++f) {
595 delete (facedata *)((*f)->userdata);
596 (*f)->userdata = NULL;
597 }
598 }
599
MakeFace(vector<WVertex * > & iVertexList,vector<bool> & iFaceEdgeMarksList,unsigned iMaterial)600 WFace *WShape::MakeFace(vector<WVertex *> &iVertexList,
601 vector<bool> &iFaceEdgeMarksList,
602 unsigned iMaterial)
603 {
604 // allocate the new face
605 WFace *face = instanciateFace();
606
607 WFace *result = MakeFace(iVertexList, iFaceEdgeMarksList, iMaterial, face);
608 if (!result) {
609 delete face;
610 }
611 return result;
612 }
613
MakeFace(vector<WVertex * > & iVertexList,vector<Vec3f> & iNormalsList,vector<Vec2f> & iTexCoordsList,vector<bool> & iFaceEdgeMarksList,unsigned iMaterial)614 WFace *WShape::MakeFace(vector<WVertex *> &iVertexList,
615 vector<Vec3f> &iNormalsList,
616 vector<Vec2f> &iTexCoordsList,
617 vector<bool> &iFaceEdgeMarksList,
618 unsigned iMaterial)
619 {
620 // allocate the new face
621 WFace *face = MakeFace(iVertexList, iFaceEdgeMarksList, iMaterial);
622
623 if (!face) {
624 return NULL;
625 }
626
627 // set the list of per-vertex normals
628 face->setNormalList(iNormalsList);
629 // set the list of per-vertex tex coords
630 face->setTexCoordsList(iTexCoordsList);
631
632 return face;
633 }
634
MakeFace(vector<WVertex * > & iVertexList,vector<bool> & iFaceEdgeMarksList,unsigned iMaterial,WFace * face)635 WFace *WShape::MakeFace(vector<WVertex *> &iVertexList,
636 vector<bool> &iFaceEdgeMarksList,
637 unsigned iMaterial,
638 WFace *face)
639 {
640 int id = _FaceList.size();
641
642 face->setFrsMaterialIndex(iMaterial);
643
644 // Check whether we have a degenerated face:
645
646 // LET'S HACK IT FOR THE TRIANGLE CASE:
647
648 if (3 == iVertexList.size()) {
649 if ((iVertexList[0] == iVertexList[1]) || (iVertexList[0] == iVertexList[2]) ||
650 (iVertexList[2] == iVertexList[1])) {
651 cerr << "Warning: degenerated triangle detected, correcting" << endl;
652 return NULL;
653 }
654 }
655
656 vector<WVertex *>::iterator it;
657
658 // compute the face normal (v1v2 ^ v1v3)
659 // Double precision numbers are used here to avoid truncation errors [T47705]
660 Vec3r v1, v2, v3;
661 it = iVertexList.begin();
662 v1 = (*it)->GetVertex();
663 it++;
664 v2 = (*it)->GetVertex();
665 it++;
666 v3 = (*it)->GetVertex();
667
668 Vec3r vector1(v2 - v1);
669 Vec3r vector2(v3 - v1);
670
671 Vec3r normal(vector1 ^ vector2);
672 normal.normalize();
673 face->setNormal(normal);
674
675 vector<bool>::iterator mit = iFaceEdgeMarksList.begin();
676 face->setMark(*mit);
677 mit++;
678
679 // vertex pointers used to build each edge
680 vector<WVertex *>::iterator va, vb;
681
682 va = iVertexList.begin();
683 vb = va;
684 for (; va != iVertexList.end(); va = vb) {
685 ++vb;
686 // Adds va to the vertex list:
687 // face->AddVertex(*va);
688
689 WOEdge *oedge;
690 if (*va == iVertexList.back()) {
691 oedge = face->MakeEdge(*va, iVertexList.front()); // for the last (closing) edge
692 }
693 else {
694 oedge = face->MakeEdge(*va, *vb);
695 }
696
697 if (!oedge) {
698 return NULL;
699 }
700
701 WEdge *edge = oedge->GetOwner();
702 if (1 == edge->GetNumberOfOEdges()) {
703 // means that we just created a new edge and that we must add it to the shape's edges list
704 edge->setId(_EdgeList.size());
705 AddEdge(edge);
706 #if 0
707 // compute the mean edge value:
708 _meanEdgeSize += edge->GetaOEdge()->GetVec().norm();
709 #endif
710 }
711
712 edge->setMark(*mit);
713 ++mit;
714 }
715
716 // Add the face to the shape's faces list:
717 face->setId(id);
718 AddFace(face);
719
720 return face;
721 }
722
ComputeMeanEdgeSize() const723 real WShape::ComputeMeanEdgeSize() const
724 {
725 real meanEdgeSize = 0.0;
726 for (vector<WEdge *>::const_iterator it = _EdgeList.begin(), itend = _EdgeList.end();
727 it != itend;
728 it++) {
729 meanEdgeSize += (*it)->GetaOEdge()->GetVec().norm();
730 }
731 return meanEdgeSize / (real)_EdgeList.size();
732 }
733
734 } /* namespace Freestyle */
735