0032264: Data Exchange - STEP exporter generates bad geometry [REGRESSION since 7...
[occt.git] / src / TopoDSToStep / TopoDSToStep_MakeStepEdge.cxx
1 // Created on: 1994-11-30
2 // Created by: Frederic MAUPAS
3 // Copyright (c) 1994-1999 Matra Datavision
4 // Copyright (c) 1999-2014 OPEN CASCADE SAS
5 //
6 // This file is part of Open CASCADE Technology software library.
7 //
8 // This library is free software; you can redistribute it and/or modify it under
9 // the terms of the GNU Lesser General Public License version 2.1 as published
10 // by the Free Software Foundation, with special exception defined in the file
11 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
12 // distribution for complete text of the license and disclaimer of any warranty.
13 //
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
16
17
18 #include <Adaptor3d_CurveOnSurface.hxx>
19 #include <BRep_Tool.hxx>
20 #include <BRepAdaptor_Curve.hxx>
21 #include <BRepAdaptor_Surface.hxx>
22 #include <BRepLib.hxx>
23 #include <Geom2d_Curve.hxx>
24 #include <Geom2d_Line.hxx>
25 #include <Geom_BSplineCurve.hxx>
26 #include <Geom_Curve.hxx>
27 #include <Geom_Line.hxx>
28 #include <Geom_Plane.hxx>
29 #include <Geom_Surface.hxx>
30 #include <GeomToStep_MakeCurve.hxx>
31 #include <GeomToStep_MakeLine.hxx>
32 #include <gp_Vec.hxx>
33 #include <Interface_Static.hxx>
34 #include <StdFail_NotDone.hxx>
35 #include <StepGeom_HArray1OfPcurveOrSurface.hxx>
36 #include <StepGeom_Line.hxx>
37 #include <StepGeom_SeamCurve.hxx>
38 #include <StepGeom_SurfaceCurve.hxx>
39 #include <StepShape_EdgeCurve.hxx>
40 #include <StepShape_TopologicalRepresentationItem.hxx>
41 #include <StepShape_Vertex.hxx>
42 #include <TColgp_Array1OfPnt.hxx>
43 #include <TCollection_HAsciiString.hxx>
44 #include <TColStd_Array1OfInteger.hxx>
45 #include <TColStd_Array1OfReal.hxx>
46 #include <TopExp.hxx>
47 #include <TopExp_Explorer.hxx>
48 #include <TopoDS.hxx>
49 #include <TopoDS_Edge.hxx>
50 #include <TopoDSToStep_MakeStepEdge.hxx>
51 #include <TopoDSToStep_MakeStepVertex.hxx>
52 #include <TopoDSToStep_Tool.hxx>
53 #include <Transfer_FinderProcess.hxx>
54 #include <TransferBRep.hxx>
55 #include <TransferBRep_ShapeMapper.hxx>
56 #include <BRepTools.hxx>
57 #include <ShapeAnalysis_Curve.hxx>
58
59 // Processing of non-manifold topology (ssv; 11.11.2010)
60 // ----------------------------------------------------------------------------
61 // Constructors
62 // ----------------------------------------------------------------------------
63 TopoDSToStep_MakeStepEdge::TopoDSToStep_MakeStepEdge()
64 : myError(TopoDSToStep_EdgeOther)
65 {
66   done = Standard_False;
67 }
68
69 TopoDSToStep_MakeStepEdge::TopoDSToStep_MakeStepEdge
70 (const TopoDS_Edge& E,
71  TopoDSToStep_Tool& T,
72  const Handle(Transfer_FinderProcess)& FP)
73 {
74   done = Standard_False;
75   Init(E, T, FP);
76 }
77
78 // ----------------------------------------------------------------------------
79 // Method  : Init
80 // Purpose :
81 // ----------------------------------------------------------------------------
82
83 void TopoDSToStep_MakeStepEdge::Init(const TopoDS_Edge& aEdge, 
84                                      TopoDSToStep_Tool& aTool,
85                                      const Handle(Transfer_FinderProcess)& FP)
86 {
87   // ------------------------------------------------------------------
88   // The edge is given with its relative orientation (i.e. in the wire)
89   // ------------------------------------------------------------------
90
91   aTool.SetCurrentEdge(aEdge);
92
93   // [BEGIN] Processing non-manifold topology (ssv; 11.11.2010)
94   Standard_Boolean isNMMode = Interface_Static::IVal("write.step.nonmanifold") != 0;
95   if (isNMMode) {
96     Handle(StepShape_EdgeCurve) anEC;
97     Handle(TransferBRep_ShapeMapper) aSTEPMapper = TransferBRep::ShapeMapper(FP, aEdge);
98     if ( FP->FindTypedTransient(aSTEPMapper, STANDARD_TYPE(StepShape_EdgeCurve), anEC) ) {
99       // Non-manifold topology detected
100       myError  = TopoDSToStep_EdgeDone;
101       myResult = anEC;
102       done     = Standard_True;
103       return;
104     }
105   }
106   // [END] Processing non-manifold topology (ssv; 11.11.2010)
107
108   if (aTool.IsBound(aEdge)) {
109     myError  = TopoDSToStep_EdgeDone;
110     done     = Standard_True;
111     myResult = aTool.Find(aEdge);
112     return;
113   }
114
115 #define Nbpt 21
116   Standard_Integer i;
117   Standard_Real    U, U1, U2;
118   gp_Pnt           P;
119   
120   Standard_Boolean isSeam = BRep_Tool::IsClosed(aEdge, aTool.CurrentFace());
121   
122   //:i4 abv 02 Sep 98: ProSTEP TR8 Motor.rle f3 & f62: check that edge 
123   // participates twice in the wires of the face before making it seam
124   // (else it can have two pcurves on the same surface being shared by 
125   // two faces on that surface)
126   // This fix is necessary because sharing of surfaces is not preserved when 
127   // writing faces to STEP (see TopoDSToSTEP_MakeStepFace)
128   if ( isSeam ) {
129     Standard_Integer count = 0;
130     TopExp_Explorer exp ( aTool.CurrentFace(), TopAbs_EDGE );
131     for ( ; exp.More(); exp.Next() )
132       if ( aEdge.IsSame ( exp.Current() ) ) count++;
133     if ( count < 2 ) isSeam = Standard_False;
134   }
135
136   BRepAdaptor_Curve   CA = BRepAdaptor_Curve(aEdge);
137   BRepAdaptor_Surface SA = BRepAdaptor_Surface(aTool.CurrentFace());
138
139   if (aEdge.Orientation() == TopAbs_INTERNAL  ||
140       aEdge.Orientation() == TopAbs_EXTERNAL ) {
141     Handle(TransferBRep_ShapeMapper) errShape =
142       new TransferBRep_ShapeMapper(aEdge);
143     FP->AddWarning(errShape, " Edge(internal/external) from Non Manifold Topology");
144     myError = TopoDSToStep_NonManifoldEdge;
145     done    = Standard_False;
146     return;
147   }
148   
149   // Vertices
150
151   Handle(StepShape_Vertex) V1,V2;
152   Handle(StepShape_TopologicalRepresentationItem) Gpms2;
153   TopoDS_Vertex Vfirst, Vlast;
154
155   TopExp::Vertices(aEdge,Vfirst, Vlast);
156
157   TopoDSToStep_MakeStepVertex MkVertex;
158   
159   MkVertex.Init(Vfirst, aTool, FP);  
160   if (MkVertex.IsDone())
161     V1 = Handle(StepShape_Vertex)::DownCast(MkVertex.Value());
162   else {
163     Handle(TransferBRep_ShapeMapper) errShape =
164       new TransferBRep_ShapeMapper(aEdge);
165     FP->AddWarning(errShape, " First Vertex of Edge not mapped");
166     myError = TopoDSToStep_EdgeOther;
167     done    = Standard_False;
168     return;
169   }
170   
171   MkVertex.Init(Vlast, aTool, FP);
172   if (MkVertex.IsDone())
173     V2 = Handle(StepShape_Vertex)::DownCast(MkVertex.Value());
174   else {
175     Handle(TransferBRep_ShapeMapper) errShape =
176       new TransferBRep_ShapeMapper(aEdge);
177     FP->AddWarning(errShape, " Last Vertex of Edge not mapped");
178     myError = TopoDSToStep_EdgeOther;
179     done    = Standard_False;
180     return;
181   }
182   
183   // ---------------------------------------
184   // Translate 3D representation of the Edge
185   // ---------------------------------------
186   
187   Handle(StepGeom_Curve) Gpms;
188   Handle(Geom_Curve) C = CA.Curve().Curve();
189  
190   if (!C.IsNull()) {
191     C = Handle(Geom_Curve)::DownCast(C->Copy());
192     gp_Trsf Tr1 = CA.Trsf();
193     C->Transform(Tr1);
194     // Special treatment is needed for very short edges based on periodic curves.
195     // Since edge in STEP does not store its parametric range, parameters are computed
196     // on import by projecting vertices on a curve, and for periodic curve this may 
197     // lead to use of wrong part of the curve if end vertices are too close to each other
198     // (often whole curve is taken instead of its very small fragment).
199     if (C->IsPeriodic())
200     {
201       Standard_Real dpar = CA.LastParameter() - CA.FirstParameter();
202       if (dpar <= 0)
203       {
204         dpar += (ceil(fabs(dpar) / C->Period()) * C->Period());
205       }
206
207       // if range obtained from projection of vertices contradicts with range
208       // of the edge tnen vertices are swapped to keep results correct after import
209       // (see test de step_5 A1)
210       gp_Pnt aP1 = BRep_Tool::Pnt(Vfirst);
211       gp_Pnt aP2 = BRep_Tool::Pnt(Vlast);
212       gp_Pnt  pproj;
213       ShapeAnalysis_Curve sac;
214       sac.Project (C, aP1, Tolerance(), pproj, U1, Standard_False);
215       sac.Project (C, aP2, Tolerance(), pproj, U2, Standard_False);
216       Standard_Real dU = U2 - U1;
217       if (dU <= 0)
218       {
219         dU += (ceil(fabs(dU) / C->Period()) * C->Period());
220       }
221       if ((dU  > Precision::PConfusion() &&  dU <= 0.1 * C->Period() && dpar > 0.5 * C->Period()) ||
222           (dpar  > Precision::PConfusion() && dpar <= 0.1 * C->Period() && dU > 0.5 * C->Period()))
223       {
224         std::swap (V1, V2);
225       }
226
227       // If vertices overlap, we cut only needed part of the BSpline curve.
228       // Note that this method cannot be used for canonic curves 
229       // (STEP does not support trimmed curves in AIC 514).
230       if (C->IsKind(STANDARD_TYPE(Geom_BSplineCurve)))
231       {
232         Standard_Real aTolV1 = BRep_Tool::Tolerance(Vfirst);
233         Standard_Real aTolV2 = BRep_Tool::Tolerance(Vlast);
234         gp_Pnt aP11 = CA.Value(CA.FirstParameter());
235         gp_Pnt aP12 = CA.Value(CA.LastParameter());
236         gp_Pnt aPm = CA.Value((CA.FirstParameter() + CA.LastParameter()) * 0.5);
237         Standard_Real aDist11 = aP11.Distance(aP12);
238         Standard_Real aDist1m = aP11.Distance(aPm);
239         Standard_Real aDist2m = aP12.Distance(aPm);
240         Standard_Real aDistMax = Max(Max(aDist1m, aDist2m), aDist11);
241         Standard_Boolean isSmallCurve = (aDistMax <= aTolV1 || aDistMax <= aTolV2);
242         if (BRepTools::Compare(Vfirst, Vlast) && isSmallCurve  && dpar > Precision::PConfusion() && dpar <= 0.1 * C->Period())
243         {
244           Handle(Geom_BSplineCurve) aBspl1 = Handle(Geom_BSplineCurve)::DownCast(C->Copy());
245           aBspl1->Segment(CA.FirstParameter(), CA.LastParameter());
246           C = aBspl1;
247         }
248       }
249     }
250
251  
252     GeomToStep_MakeCurve MkCurve(C);
253     Gpms = MkCurve.Value();
254   }
255   else {
256     
257     // -------------------------
258     // a 3D Curve is constructed 
259     // -------------------------
260
261 #ifdef OCCT_DEBUG
262     std::cout << "Warning: TopoDSToStep_MakeStepEdge: edge without 3d curve; creating..." << std::endl;
263 #endif
264     if ((SA.GetType() == GeomAbs_Plane) &&
265         (CA.GetType() == GeomAbs_Line)) {
266       U1 = CA.FirstParameter();
267       U2 = CA.LastParameter();
268       gp_Vec V = gp_Vec( CA.Value(U1), CA.Value(U2) );
269       Handle(Geom_Line) L = 
270         new Geom_Line(CA.Value(U1), gp_Dir(V));
271       GeomToStep_MakeLine MkLine(L);
272       Gpms = MkLine.Value();
273     }
274     else {
275       // To Be Optimized : create an approximated BSpline
276       //                   using GeomAPI_PointsToBSpline
277       TColgp_Array1OfPnt Points(1,Nbpt);
278       TColStd_Array1OfReal Knots(1,Nbpt);
279       TColStd_Array1OfInteger Mult(1,Nbpt);
280       U1 = CA.FirstParameter();
281       U2 = CA.LastParameter();
282       for ( i=1; i<=Nbpt; i++ ) {
283         U = U1 + (i-1)*(U2 - U1)/(Nbpt - 1);
284         P = CA.Value(U);
285         Points.SetValue(i,P);
286         Knots.SetValue(i,U);
287         Mult.SetValue(i,1);
288       }
289       //Points.SetValue(1, BRep_Tool::Pnt(Vfirst));
290       //Points.SetValue(Nbpt, BRep_Tool::Pnt(Vlast));
291       Mult.SetValue(1,2);
292       Mult.SetValue(Nbpt,2);
293       Handle(Geom_Curve) Bs = 
294         new Geom_BSplineCurve(Points, Knots, Mult, 1);
295       GeomToStep_MakeCurve MkCurve(Bs);
296       Gpms = MkCurve.Value();
297     }
298   }
299   
300   // ---------------------------------------------------------
301   // Warning : if the edge is connected aGeom->Length = 2
302   //           otherwise = 1 ;
303   //           and enumeration is pscrPcurveS2 or pscrPcurveS1
304   // This is corrected in the Write File phases !
305   // ---------------------------------------------------------  
306
307   //:abv 25.01.00 CAX-IF TRJ3
308   // if PcurveMode is 1 (default), make surface_curve instead of simple 3d curve
309   if ( aTool.PCurveMode() != 0 ) {
310   
311     Handle(StepGeom_HArray1OfPcurveOrSurface) aGeom =
312       new StepGeom_HArray1OfPcurveOrSurface(1,2);
313     Handle(TCollection_HAsciiString) aName = new TCollection_HAsciiString("");
314   
315     if (!isSeam) {
316       Handle(StepGeom_SurfaceCurve) SurfaceCurve = new StepGeom_SurfaceCurve;
317       SurfaceCurve->Init(aName, Gpms, aGeom, StepGeom_pscrPcurveS1);
318       Gpms = SurfaceCurve;
319     }
320     else {
321       Handle(StepGeom_SeamCurve) SeamCurve = new StepGeom_SeamCurve;
322       SeamCurve->Init(aName, Gpms, aGeom, StepGeom_pscrPcurveS1);
323       Gpms = SeamCurve;
324     }
325   }
326   
327   // Edge curve
328   Handle(StepShape_EdgeCurve) Epms = new StepShape_EdgeCurve;
329   Handle(TCollection_HAsciiString) aName = new TCollection_HAsciiString("");
330   Epms->Init(aName, V1, V2, Gpms, Standard_True);
331   
332   aTool.Bind(aEdge, Epms);
333   myError  = TopoDSToStep_EdgeDone;
334   myResult = Epms;
335   done     = Standard_True;
336   return;
337 }
338
339 // ----------------------------------------------------------------------------
340 // Method  : Value
341 // Purpose :
342 // ----------------------------------------------------------------------------
343
344 const Handle(StepShape_TopologicalRepresentationItem)& TopoDSToStep_MakeStepEdge::Value() const 
345 {
346   StdFail_NotDone_Raise_if (!done, "TopoDSToStep_MakeStepEdge::Value() - no result");
347   return myResult;
348 }
349
350 // ----------------------------------------------------------------------------
351 // Method  : Error
352 // Purpose :
353 // ----------------------------------------------------------------------------
354
355 TopoDSToStep_MakeEdgeError TopoDSToStep_MakeStepEdge::Error() const 
356 {
357   return myError;
358 }