0030058: Visualization, Select3D_SensitivePrimitiveArray - the selection is not fast...
[occt.git] / src / Font / Font_BRepFont.cxx
CommitLineData
b514beda 1// Created on: 2013-09-16
d5f74e42 2// Copyright (c) 2013-2014 OPEN CASCADE SAS
b514beda 3//
973c2be1 4// This file is part of Open CASCADE Technology software library.
b514beda 5//
d5f74e42 6// This library is free software; you can redistribute it and/or modify it under
7// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 8// by the Free Software Foundation, with special exception defined in the file
9// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10// distribution for complete text of the license and disclaimer of any warranty.
b514beda 11//
973c2be1 12// Alternatively, this file may be used under the terms of Open CASCADE
13// commercial license or contractual agreement.
b514beda 14
15#include <Font_BRepFont.hxx>
16
17#include <BRep_Tool.hxx>
3388cf17 18#include <BRepTopAdaptor_FClass2d.hxx>
b514beda 19#include <BRepBuilderAPI_MakeFace.hxx>
20#include <BRepBuilderAPI_MakeWire.hxx>
21#include <BRepLib_MakeEdge.hxx>
f9801cf9 22#include <Font_FTLibrary.hxx>
ac84fcf6 23#include <Font_TextFormatter.hxx>
b514beda 24#include <GCE2d_MakeSegment.hxx>
25#include <GC_MakeSegment.hxx>
26#include <Geom_BezierCurve.hxx>
27#include <Geom_BSplineCurve.hxx>
c04c30b3 28#include <Geom2d_TrimmedCurve.hxx>
b514beda 29#include <Geom_Plane.hxx>
30#include <Geom2d_BezierCurve.hxx>
31#include <Geom2d_BSplineCurve.hxx>
543a9964 32#include <Geom2d_TrimmedCurve.hxx>
b514beda 33#include <Geom2d_Line.hxx>
34#include <GeomAPI.hxx>
35#include <GeomAdaptor_HSurface.hxx>
36#include <GeomLib.hxx>
37#include <gp_Pln.hxx>
b514beda 38#include <TColGeom2d_HSequenceOfBoundedCurve.hxx>
39#include <TCollection_AsciiString.hxx>
40#include <TCollection_HAsciiString.hxx>
41#include <TopExp.hxx>
42#include <TopExp_Explorer.hxx>
43#include <TopoDS.hxx>
44#include <TopoDS_Compound.hxx>
45#include <TopoDS_Vertex.hxx>
3388cf17 46#include <TopTools_DataMapOfShapeInteger.hxx>
47#include <TopTools_DataMapOfShapeSequenceOfShape.hxx>
b514beda 48
f9801cf9 49#include <ft2build.h>
50#include FT_FREETYPE_H
b514beda 51#include FT_OUTLINE_H
52
92efcf78 53IMPLEMENT_STANDARD_RTTIEXT(Font_BRepFont,Font_FTFont)
54
b514beda 55namespace
56{
57 // pre-defined font rendering options
58 static const unsigned int THE_FONT_SIZE = 72;
59 static const unsigned int THE_RESOLUTION_DPI = 4800;
60
61 // compute scaling factor for specified font size
62 inline Standard_Real getScale (const Standard_Real theSize)
63 {
64 return theSize / Standard_Real(THE_FONT_SIZE) * 72.0 / Standard_Real(THE_RESOLUTION_DPI);
65 }
66
f9801cf9 67 //! Auxiliary method to convert FT_Vector to gp_XY
68 static gp_XY readFTVec (const FT_Vector& theVec,
151da08b 69 const Standard_Real theScaleUnits,
70 const Standard_Real theWidthScaling = 1.0)
f9801cf9 71 {
151da08b 72 return gp_XY (theScaleUnits * Standard_Real(theVec.x) * theWidthScaling / 64.0, theScaleUnits * Standard_Real(theVec.y) / 64.0);
f9801cf9 73 }
74
3388cf17 75 //! Auxiliary method for classification wire theW2 with respect to wire theW1
76 static TopAbs_State classifyWW (const TopoDS_Wire& theW1,
77 const TopoDS_Wire& theW2,
78 const TopoDS_Face& theF)
79 {
80 TopAbs_State aRes = TopAbs_UNKNOWN;
81
82 TopoDS_Face aF = TopoDS::Face (theF.EmptyCopied());
83 aF.Orientation (TopAbs_FORWARD);
84 BRep_Builder aB;
85 aB.Add (aF, theW1);
86 BRepTopAdaptor_FClass2d aClass2d (aF, ::Precision::PConfusion());
87 for (TopoDS_Iterator anEdgeIter (theW2); anEdgeIter.More(); anEdgeIter.Next())
88 {
89 const TopoDS_Edge& anEdge = TopoDS::Edge (anEdgeIter.Value());
90 Standard_Real aPFirst = 0.0, aPLast = 0.0;
91 Handle(Geom2d_Curve) aCurve2d = BRep_Tool::CurveOnSurface (anEdge, theF, aPFirst, aPLast);
92 if (aCurve2d.IsNull())
93 {
94 continue;
95 }
96
97 gp_Pnt2d aPnt2d = aCurve2d->Value ((aPFirst + aPLast) / 2.0);
98 TopAbs_State aState = aClass2d.Perform (aPnt2d, Standard_False);
99 if (aState == TopAbs_OUT
100 || aState == TopAbs_IN)
101 {
102 if (aRes == TopAbs_UNKNOWN)
103 {
104 aRes = aState;
105 }
106 else if (aRes != aState)
107 {
108 return TopAbs_UNKNOWN;
109 }
110 }
111 }
112 return aRes;
113 }
114
a3f6f591 115}
b514beda 116
117// =======================================================================
118// function : Constructor
119// purpose :
120// =======================================================================
121Font_BRepFont::Font_BRepFont ()
122: myPrecision (Precision::Confusion()),
123 myScaleUnits (1.0),
124 myIsCompositeCurve (Standard_False),
125 my3Poles (1, 3),
126 my4Poles (1, 4)
127{
128 init();
129}
130
131// =======================================================================
132// function : init
133// purpose :
134// =======================================================================
135void Font_BRepFont::init()
136{
137 mySurface = new Geom_Plane (gp_Pln (gp::XOY()));
138 myCurve2dAdaptor = new Geom2dAdaptor_HCurve();
543a9964 139 Handle(Adaptor3d_HSurface) aSurfAdaptor = new GeomAdaptor_HSurface (mySurface);
b514beda 140 myCurvOnSurf.Load (aSurfAdaptor);
b514beda 141}
142
143// =======================================================================
144// function : Constructor
145// purpose :
146// =======================================================================
147Font_BRepFont::Font_BRepFont (const NCollection_String& theFontPath,
148 const Standard_Real theSize)
149: myPrecision (Precision::Confusion()),
150 myScaleUnits (1.0),
151 myIsCompositeCurve (Standard_False),
152 my3Poles (1, 3),
153 my4Poles (1, 4)
154{
155 init();
156 if (theSize <= myPrecision * 100.0)
157 {
158 return;
159 }
160
161 myScaleUnits = getScale (theSize);
162 Font_FTFont::Init (theFontPath, THE_FONT_SIZE, THE_RESOLUTION_DPI);
163}
164
165// =======================================================================
166// function : Constructor
167// purpose :
168// =======================================================================
169Font_BRepFont::Font_BRepFont (const NCollection_String& theFontName,
170 const Font_FontAspect theFontAspect,
171 const Standard_Real theSize)
172: myPrecision (Precision::Confusion()),
173 myScaleUnits (1.0),
174 myIsCompositeCurve (Standard_False),
175 my3Poles (1, 3),
176 my4Poles (1, 4)
177{
178 init();
179 if (theSize <= myPrecision * 100.0)
180 {
181 return;
182 }
183
184 myScaleUnits = getScale (theSize);
185 Font_FTFont::Init (theFontName, theFontAspect, THE_FONT_SIZE, THE_RESOLUTION_DPI);
186}
187
188// =======================================================================
189// function : Release
190// purpose :
191// =======================================================================
192void Font_BRepFont::Release()
193{
194 myCache.Clear();
195 Font_FTFont::Release();
196}
197
198// =======================================================================
199// function : SetCompositeCurveMode
200// purpose :
201// =======================================================================
202void Font_BRepFont::SetCompositeCurveMode (const Standard_Boolean theToConcatenate)
203{
204 if (myIsCompositeCurve != theToConcatenate)
205 {
206 myIsCompositeCurve = theToConcatenate;
207 myCache.Clear();
208 }
209}
210
211// =======================================================================
212// function : Init
213// purpose :
214// =======================================================================
215bool Font_BRepFont::Init (const NCollection_String& theFontPath,
216 const Standard_Real theSize)
217{
218 if (theSize <= myPrecision * 100.0)
219 {
220 return false;
221 }
222
223 myScaleUnits = getScale (theSize);
224 return Font_FTFont::Init (theFontPath, THE_FONT_SIZE, THE_RESOLUTION_DPI);
225}
226
227// =======================================================================
228// function : Init
229// purpose :
230// =======================================================================
231bool Font_BRepFont::Init (const NCollection_String& theFontName,
232 const Font_FontAspect theFontAspect,
233 const Standard_Real theSize)
234{
235 if (theSize <= myPrecision * 100.0)
236 {
237 return false;
238 }
239
240 myScaleUnits = getScale (theSize);
241 return Font_FTFont::Init (theFontName, theFontAspect, THE_FONT_SIZE, THE_RESOLUTION_DPI);
242}
243
244// =======================================================================
245// function : RenderGlyph
246// purpose :
247// =======================================================================
248TopoDS_Shape Font_BRepFont::RenderGlyph (const Standard_Utf32Char& theChar)
249{
250 TopoDS_Shape aShape;
251 Standard_Mutex::Sentry aSentry (myMutex);
252 renderGlyph (theChar, aShape);
253 return aShape;
254}
255
256// =======================================================================
257// function : to3d
258// purpose :
259// =======================================================================
543a9964 260bool Font_BRepFont::to3d (const Handle(Geom2d_Curve)& theCurve2d,
b514beda 261 const GeomAbs_Shape theContinuity,
262 Handle(Geom_Curve)& theCurve3d)
263{
264 Standard_Real aMaxDeviation = 0.0;
265 Standard_Real anAverDeviation = 0.0;
266 myCurve2dAdaptor->ChangeCurve2d().Load (theCurve2d);
543a9964 267 const Handle(Adaptor2d_HCurve2d)& aCurve = myCurve2dAdaptor; // to avoid ambiguity
268 myCurvOnSurf.Load (aCurve);
b514beda 269 GeomLib::BuildCurve3d (myPrecision, myCurvOnSurf,
270 myCurve2dAdaptor->FirstParameter(), myCurve2dAdaptor->LastParameter(),
271 theCurve3d, aMaxDeviation, anAverDeviation, theContinuity);
272 return !theCurve3d.IsNull();
273}
274
3388cf17 275
276// =======================================================================
277// function : buildFaces
278// purpose :
279// =======================================================================
280Standard_Boolean Font_BRepFont::buildFaces (const NCollection_Sequence<TopoDS_Wire>& theWires,
281 TopoDS_Shape& theRes)
282{
283 // classify wires
284 NCollection_DataMap<TopoDS_Shape, NCollection_Sequence<TopoDS_Wire>, TopTools_ShapeMapHasher> aMapOutInts;
285 TopTools_DataMapOfShapeInteger aMapNbOuts;
286 TopoDS_Face aF;
287 myBuilder.MakeFace (aF, mySurface, myPrecision);
288 Standard_Integer aWireIter1Index = 1;
289 for (NCollection_Sequence<TopoDS_Wire>::Iterator aWireIter1 (theWires); aWireIter1.More(); ++aWireIter1Index, aWireIter1.Next())
290 {
291 const TopoDS_Wire& aW1 = aWireIter1.Value();
292 if (!aMapNbOuts.IsBound (aW1))
293 {
294 const Standard_Integer aNbOuts = 0;
295 aMapNbOuts.Bind (aW1, aNbOuts);
296 }
297
298 NCollection_Sequence<TopoDS_Wire>* anIntWs = aMapOutInts.Bound (aW1, NCollection_Sequence<TopoDS_Wire>());
299 Standard_Integer aWireIter2Index = 1;
300 for (NCollection_Sequence<TopoDS_Wire>::Iterator aWireIter2 (theWires); aWireIter2.More(); ++aWireIter2Index, aWireIter2.Next())
301 {
302 if (aWireIter1Index == aWireIter2Index)
303 {
304 continue;
305 }
306
307 const TopoDS_Wire& aW2 = aWireIter2.Value();
308 const TopAbs_State aClass = classifyWW (aW1, aW2, aF);
309 if (aClass == TopAbs_IN)
310 {
311 anIntWs->Append (aW2);
312 if (Standard_Integer* aNbOutsPtr = aMapNbOuts.ChangeSeek (aW2))
313 {
314 ++(*aNbOutsPtr);
315 }
316 else
317 {
318 const Standard_Integer aNbOuts = 1;
319 aMapNbOuts.Bind (aW2, aNbOuts);
320 }
321 }
322 }
323 }
324
325 // check out wires and remove "not out" wires from maps
326 for (TopTools_DataMapIteratorOfDataMapOfShapeInteger anOutIter (aMapNbOuts); anOutIter.More(); anOutIter.Next())
327 {
328 const Standard_Integer aTmp = anOutIter.Value() % 2;
329 if (aTmp > 0)
330 {
331 // not out wire
332 aMapOutInts.UnBind (anOutIter.Key());
333 }
334 }
335
336 // create faces for out wires
337 TopTools_MapOfShape anUsedShapes;
338 TopoDS_Compound aFaceComp;
339 myBuilder.MakeCompound (aFaceComp);
340 for (; !aMapOutInts.IsEmpty(); )
341 {
342 // find out wire with max number of outs
343 TopoDS_Shape aW;
344 Standard_Integer aMaxNbOuts = -1;
345 for (NCollection_DataMap<TopoDS_Shape, NCollection_Sequence<TopoDS_Wire>, TopTools_ShapeMapHasher>::Iterator itMOI (aMapOutInts);
346 itMOI.More(); itMOI.Next())
347 {
348 const TopoDS_Shape& aKey = itMOI.Key();
349 const Standard_Integer aNbOuts = aMapNbOuts.Find (aKey);
350 if (aNbOuts > aMaxNbOuts)
351 {
352 aMaxNbOuts = aNbOuts;
353 aW = aKey;
354 }
355 }
356
357 // create face for selected wire
358 TopoDS_Face aNewF;
359 myBuilder.MakeFace (aNewF, mySurface, myPrecision);
360 myBuilder.Add (aNewF, aW);
361 anUsedShapes.Add (aW);
362 const NCollection_Sequence<TopoDS_Wire>& anIns = aMapOutInts.Find (aW);
363 for (NCollection_Sequence<TopoDS_Wire>::Iterator aWireIter (anIns); aWireIter.More(); aWireIter.Next())
364 {
365 TopoDS_Wire aWin = aWireIter.Value();
366 if (anUsedShapes.Contains (aWin))
367 {
368 continue;
369 }
370
371 aWin.Reverse();
372 myBuilder.Add (aNewF, aWin);
373 anUsedShapes.Add (aWin);
374 }
375
376 myBuilder.Add (aFaceComp, aNewF);
377 aMapOutInts.UnBind (aW);
378 }
379
380 if (aFaceComp.NbChildren() == 0)
381 {
382 return Standard_False;
383 }
384
385 if (aFaceComp.NbChildren() == 1)
386 {
387 theRes = TopoDS_Iterator (aFaceComp).Value();
388 }
389 else
390 {
391 theRes = aFaceComp;
392 }
393 return Standard_True;
394}
395
396
b514beda 397// =======================================================================
398// function : renderGlyph
399// purpose :
400// =======================================================================
401Standard_Boolean Font_BRepFont::renderGlyph (const Standard_Utf32Char theChar,
402 TopoDS_Shape& theShape)
403{
404 theShape.Nullify();
405 if (!loadGlyph (theChar)
406 || myFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
407 {
408 return Standard_False;
409 }
410 else if (myCache.Find (theChar, theShape))
411 {
412 return !theShape.IsNull();
413 }
414
be7d4aa2 415 FT_Outline& anOutline = myFTFace->glyph->outline;
416
417 if (!anOutline.n_contours)
418 return Standard_False;
419
b514beda 420 TopLoc_Location aLoc;
3388cf17 421 NCollection_Sequence<TopoDS_Wire> aWires;
e4f0cc46 422 TopoDS_Compound aFaceCompDraft;
be7d4aa2 423
b514beda 424 // Get orientation is useless since it doesn't retrieve any in-font information and just computes orientation.
425 // Because it fails in some cases - leave this to ShapeFix.
426 //const FT_Orientation anOrient = FT_Outline_Get_Orientation (&anOutline);
427 for (short aContour = 0, aStartIndex = 0; aContour < anOutline.n_contours; ++aContour)
428 {
429 const FT_Vector* aPntList = &anOutline.points[aStartIndex];
430 const char* aTags = &anOutline.tags[aStartIndex];
431 const short anEndIndex = anOutline.contours[aContour];
432 const short aPntsNb = (anEndIndex - aStartIndex) + 1;
433 aStartIndex = anEndIndex + 1;
e4f0cc46 434 if (aPntsNb < 3 && !myIsSingleLine)
b514beda 435 {
436 // closed contour can not be constructed from < 3 points
437 continue;
438 }
439
440 BRepBuilderAPI_MakeWire aWireMaker;
441
442 gp_XY aPntPrev;
151da08b 443 gp_XY aPntCurr = readFTVec (aPntList[aPntsNb - 1], myScaleUnits, myWidthScaling);
444 gp_XY aPntNext = readFTVec (aPntList[0], myScaleUnits, myWidthScaling);
b514beda 445
e4f0cc46 446 bool isLineSeg = !myIsSingleLine
447 && FT_CURVE_TAG(aTags[aPntsNb - 1]) == FT_Curve_Tag_On;
b514beda 448 gp_XY aPntLine1 = aPntCurr;
449
450 // see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html
451 // for a full description of FreeType tags.
452 for (short aPntId = 0; aPntId < aPntsNb; ++aPntId)
453 {
454 aPntPrev = aPntCurr;
455 aPntCurr = aPntNext;
151da08b 456 aPntNext = readFTVec (aPntList[(aPntId + 1) % aPntsNb], myScaleUnits, myWidthScaling);
b514beda 457
458 // process tags
459 if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_On)
460 {
e4f0cc46 461 if (!isLineSeg)
b514beda 462 {
463 aPntLine1 = aPntCurr;
e4f0cc46 464 isLineSeg = true;
b514beda 465 continue;
466 }
467
468 const gp_XY aDirVec = aPntCurr - aPntLine1;
469 const Standard_Real aLen = aDirVec.Modulus();
470 if (aLen <= myPrecision)
471 {
472 aPntLine1 = aPntCurr;
e4f0cc46 473 isLineSeg = true;
b514beda 474 continue;
475 }
476
477 if (myIsCompositeCurve)
478 {
479 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (gp_Pnt2d (aPntLine1), gp_Pnt2d (aPntCurr));
480 myConcatMaker.Add (aLine, myPrecision);
481 }
482 else
483 {
484 Handle(Geom_Curve) aCurve3d;
485 Handle(Geom2d_Line) aCurve2d = new Geom2d_Line (gp_Pnt2d (aPntLine1), gp_Dir2d (aDirVec));
486 if (to3d (aCurve2d, GeomAbs_C1, aCurve3d))
487 {
488 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d, 0.0, aLen);
489 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
490 aWireMaker.Add (anEdge);
491 }
492 }
493 aPntLine1 = aPntCurr;
494 }
495 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Conic)
496 {
e4f0cc46 497 isLineSeg = false;
b514beda 498 gp_XY aPntPrev2 = aPntPrev;
499 gp_XY aPntNext2 = aPntNext;
500
501 // previous point is either the real previous point (an "on" point),
502 // or the midpoint between the current one and the previous "conic off" point
503 if (FT_CURVE_TAG(aTags[(aPntId - 1 + aPntsNb) % aPntsNb]) == FT_Curve_Tag_Conic)
504 {
505 aPntPrev2 = (aPntCurr + aPntPrev) * 0.5;
506 }
507
508 // next point is either the real next point or the midpoint
509 if (FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Conic)
510 {
511 aPntNext2 = (aPntCurr + aPntNext) * 0.5;
512 }
513
514 my3Poles.SetValue (1, aPntPrev2);
515 my3Poles.SetValue (2, aPntCurr);
516 my3Poles.SetValue (3, aPntNext2);
517 Handle(Geom2d_BezierCurve) aBezierArc = new Geom2d_BezierCurve (my3Poles);
518 if (myIsCompositeCurve)
519 {
520 myConcatMaker.Add (aBezierArc, myPrecision);
521 }
522 else
523 {
524 Handle(Geom_Curve) aCurve3d;
525 if (to3d (aBezierArc, GeomAbs_C1, aCurve3d))
526 {
527 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
528 myBuilder.UpdateEdge (anEdge, aBezierArc, mySurface, aLoc, myPrecision);
529 aWireMaker.Add (anEdge);
530 }
531 }
532 }
533 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Cubic
534 && FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Cubic)
535 {
e4f0cc46 536 isLineSeg = false;
b514beda 537 my4Poles.SetValue (1, aPntPrev);
538 my4Poles.SetValue (2, aPntCurr);
539 my4Poles.SetValue (3, aPntNext);
151da08b 540 my4Poles.SetValue (4, gp_Pnt2d(readFTVec (aPntList[(aPntId + 2) % aPntsNb], myScaleUnits, myWidthScaling)));
b514beda 541 Handle(Geom2d_BezierCurve) aBezier = new Geom2d_BezierCurve (my4Poles);
542 if (myIsCompositeCurve)
543 {
544 myConcatMaker.Add (aBezier, myPrecision);
545 }
546 else
547 {
548 Handle(Geom_Curve) aCurve3d;
549 if (to3d (aBezier, GeomAbs_C1, aCurve3d))
550 {
551 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
552 myBuilder.UpdateEdge (anEdge, aBezier, mySurface, aLoc, myPrecision);
553 aWireMaker.Add (anEdge);
554 }
555 }
556 }
557 }
558
559 if (myIsCompositeCurve)
560 {
561 Handle(Geom2d_BSplineCurve) aDraft2d = myConcatMaker.BSplineCurve();
562 if (aDraft2d.IsNull())
563 {
564 continue;
565 }
566
567 const gp_Pnt2d aFirstPnt = aDraft2d->StartPoint();
568 const gp_Pnt2d aLastPnt = aDraft2d->EndPoint();
e4f0cc46 569 if (!myIsSingleLine
570 && !aFirstPnt.IsEqual (aLastPnt, myPrecision))
b514beda 571 {
572 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (aLastPnt, aFirstPnt);
573 myConcatMaker.Add (aLine, myPrecision);
574 }
575
576 Handle(Geom2d_BSplineCurve) aCurve2d = myConcatMaker.BSplineCurve();
577 Handle(Geom_Curve) aCurve3d;
578 if (to3d (aCurve2d, GeomAbs_C0, aCurve3d))
579 {
580 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
581 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
582 aWireMaker.Add (anEdge);
583 }
584 myConcatMaker.Clear();
585 }
586 else
587 {
588 if (!aWireMaker.IsDone())
589 {
590 continue;
591 }
592
593 TopoDS_Vertex aFirstV, aLastV;
594 TopExp::Vertices (aWireMaker.Wire(), aFirstV, aLastV);
595 gp_Pnt aFirstPoint = BRep_Tool::Pnt (aFirstV);
596 gp_Pnt aLastPoint = BRep_Tool::Pnt (aLastV);
e4f0cc46 597 if (!myIsSingleLine
598 && !aFirstPoint.IsEqual (aLastPoint, myPrecision))
b514beda 599 {
600 aWireMaker.Add (BRepLib_MakeEdge (aFirstV, aLastV));
601 }
602 }
603
604 if (!aWireMaker.IsDone())
605 {
606 continue;
607 }
608
609 TopoDS_Wire aWireDraft = aWireMaker.Wire();
e4f0cc46 610 if (!myIsSingleLine)
611 {
3388cf17 612 // collect all wires and set CCW orientation
613 TopoDS_Face aFace;
614 myBuilder.MakeFace (aFace, mySurface, myPrecision);
615 myBuilder.Add (aFace, aWireDraft);
616 BRepTopAdaptor_FClass2d aClass2d (aFace, ::Precision::PConfusion());
617 TopAbs_State aState = aClass2d.PerformInfinitePoint();
618 if (aState != TopAbs_OUT)
e4f0cc46 619 {
3388cf17 620 // need to reverse
621 aWireDraft.Reverse();
e4f0cc46 622 }
3388cf17 623 aWires.Append (aWireDraft);
e4f0cc46 624 }
625 else
626 {
627 if (aFaceCompDraft.IsNull())
628 {
629 myBuilder.MakeCompound (aFaceCompDraft);
630 }
631 myBuilder.Add (aFaceCompDraft, aWireDraft);
632 }
b514beda 633 }
634
3388cf17 635 if (!aWires.IsEmpty())
b514beda 636 {
3388cf17 637 buildFaces (aWires, theShape);
e4f0cc46 638 }
639 else if (!aFaceCompDraft.IsNull())
640 {
641 theShape = aFaceCompDraft;
b514beda 642 }
643
644 myCache.Bind (theChar, theShape);
645 return !theShape.IsNull();
646}