1
|
|
/*
|
2
|
|
* Copyright (C) 2007-2013 German Aerospace Center (DLR/SC)
|
3
|
|
*
|
4
|
|
* Created: 2010-08-13 Markus Litz <Markus.Litz@dlr.de>
|
5
|
|
|
6
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
|
* you may not use this file except in compliance with the License.
|
8
|
|
* You may obtain a copy of the License at
|
9
|
|
*
|
10
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
11
|
|
*
|
12
|
|
* Unless required by applicable law or agreed to in writing, software
|
13
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
|
* See the License for the specific language governing permissions and
|
16
|
|
* limitations under the License.
|
17
|
|
*/
|
18
|
|
/**
|
19
|
|
* @file
|
20
|
|
* @brief Implementation of CPACS fuselage profile handling routines.
|
21
|
|
*/
|
22
|
|
|
23
|
|
#include "generated/TixiHelper.h"
|
24
|
|
#include "CCPACSFuselageProfile.h"
|
25
|
|
#include "CTiglError.h"
|
26
|
|
#include "CTiglTransformation.h"
|
27
|
|
#include "CTiglInterpolateBsplineWire.h"
|
28
|
|
#include "CTiglBSplineAlgorithms.h"
|
29
|
|
#include "tiglcommonfunctions.h"
|
30
|
|
#include "CTiglLogging.h"
|
31
|
|
#include "Debugging.h"
|
32
|
|
|
33
|
|
#include "TopoDS.hxx"
|
34
|
|
#include "TopoDS_Wire.hxx"
|
35
|
|
#include "gp_Pnt2d.hxx"
|
36
|
|
#include "gp_Vec2d.hxx"
|
37
|
|
#include "gp_Dir2d.hxx"
|
38
|
|
#include "GC_MakeSegment.hxx"
|
39
|
|
#include "BRepBuilderAPI_MakeEdge.hxx"
|
40
|
|
#include "BRepBuilderAPI_MakeWire.hxx"
|
41
|
|
#include "Geom_TrimmedCurve.hxx"
|
42
|
|
#include "GeomConvert.hxx"
|
43
|
|
#include "Geom_Plane.hxx"
|
44
|
|
#include "GCE2d_MakeSegment.hxx"
|
45
|
|
#include "Geom2d_Line.hxx"
|
46
|
|
#include "TopExp_Explorer.hxx"
|
47
|
|
#include "TopAbs_ShapeEnum.hxx"
|
48
|
|
#include "TopoDS_Edge.hxx"
|
49
|
|
#include "BRep_Tool.hxx"
|
50
|
|
#include "Geom2dAPI_InterCurveCurve.hxx"
|
51
|
|
#include "GeomAPI.hxx"
|
52
|
|
#include "gp_Pln.hxx"
|
53
|
|
#include "gce_MakeDir.hxx"
|
54
|
|
#include "gce_MakePln.hxx"
|
55
|
|
#include "BRepTools_WireExplorer.hxx"
|
56
|
|
#include "GeomAdaptor_Curve.hxx"
|
57
|
|
#include "GCPnts_AbscissaPoint.hxx"
|
58
|
|
#include "GeomAPI_IntCS.hxx"
|
59
|
|
|
60
|
|
#include "math.h"
|
61
|
|
#include <iostream>
|
62
|
|
#include <limits>
|
63
|
|
#include <sstream>
|
64
|
|
#include <algorithm>
|
65
|
|
|
66
|
|
namespace
|
67
|
|
{
|
68
|
|
// In case of a half profile, we have to compute the symmetric points and parameters
|
69
|
1
|
void SymmetrizeFuselageProfile(std::vector<tigl::CTiglPoint>& points, tigl::ParamMap& params,
|
70
|
|
std::vector<unsigned int>& kinks)
|
71
|
|
{
|
72
|
1
|
size_t n_points = points.size();
|
73
|
|
|
74
|
1
|
if (n_points == 0) {
|
75
|
0
|
return;
|
76
|
|
}
|
77
|
|
|
78
|
1
|
if (fabs(points[0].y) > 1e-6) {
|
79
|
0
|
throw tigl::CTiglError("Cannot create a symmetric fuselage profile. Y-Coordinate not zero!");
|
80
|
|
}
|
81
|
|
|
82
|
1
|
auto get_param_or = [¶ms](unsigned int idx, double def_value) -> double {
|
83
|
1
|
const auto& it = params.find(idx);
|
84
|
1
|
return it != params.end() ? it->second : def_value;
|
85
|
1
|
};
|
86
|
|
|
87
|
1
|
double umin = get_param_or(0, 0.);
|
88
|
1
|
double umax = umin + 2. * (get_param_or(static_cast<unsigned int>(n_points - 1), 0.5) - umin);
|
89
|
|
|
90
|
|
// y is already ~ 0, make it really zero!
|
91
|
1
|
points[0].y = 0.;
|
92
|
|
|
93
|
|
// mirror each point at x-z plane i.e. mirror y coordinate to close the profile
|
94
|
|
// and skip first point
|
95
|
1
|
for (size_t i = n_points - 1; i > 0; i--) {
|
96
|
1
|
auto curP = points[i];
|
97
|
1
|
if (i == n_points - 1 && std::abs(curP.y) < 1e-6) {
|
98
|
|
// do not add the same points twice
|
99
|
1
|
continue;
|
100
|
|
}
|
101
|
1
|
curP.y = -curP.y;
|
102
|
1
|
unsigned int currentIdx = static_cast<unsigned int>(points.size());
|
103
|
1
|
if (std::find(std::begin(kinks), std::end(kinks), i) != std::end(kinks)) {
|
104
|
0
|
kinks.push_back(currentIdx);
|
105
|
|
}
|
106
|
1
|
auto parm_it = params.find(static_cast<unsigned int>(i));
|
107
|
1
|
if (parm_it != params.end()) {
|
108
|
0
|
double param_new = umax + umin - parm_it->second;
|
109
|
0
|
params[currentIdx] = param_new;
|
110
|
|
}
|
111
|
1
|
points.push_back(curP);
|
112
|
|
}
|
113
|
|
|
114
|
1
|
points.push_back(points[0]);
|
115
|
1
|
params[0] = umin;
|
116
|
1
|
params[static_cast<unsigned int>(points.size() - 1)] = umax;
|
117
|
|
}
|
118
|
|
} // namespace
|
119
|
|
|
120
|
|
namespace tigl
|
121
|
|
{
|
122
|
|
// Constructor
|
123
|
1
|
CCPACSFuselageProfile::CCPACSFuselageProfile(CCPACSFuselageProfiles* parent, CTiglUIDManager* uidMgr)
|
124
|
|
: generated::CPACSProfileGeometry(parent, uidMgr)
|
125
|
|
, mirrorSymmetry(false)
|
126
|
|
, wireCache(*this, &CCPACSFuselageProfile::BuildWires)
|
127
|
|
, diameterPointsCache(*this, &CCPACSFuselageProfile::BuildDiameterPoints)
|
128
|
1
|
, profileWireAlgo(new CTiglInterpolateBsplineWire)
|
129
|
|
{
|
130
|
|
}
|
131
|
|
|
132
|
1
|
CCPACSFuselageProfile::~CCPACSFuselageProfile()
|
133
|
|
{
|
134
|
|
}
|
135
|
|
|
136
|
|
// Read fuselage profile file
|
137
|
1
|
void CCPACSFuselageProfile::ReadCPACS(const TixiDocumentHandle& tixiHandle, const std::string& xpath)
|
138
|
|
{
|
139
|
1
|
Invalidate();
|
140
|
1
|
generated::CPACSProfileGeometry::ReadCPACS(tixiHandle, xpath);
|
141
|
|
|
142
|
|
// symmetry element does not conform to CPACS spec
|
143
|
1
|
if (tixi::TixiCheckElement(tixiHandle, xpath + "/symmetry")) {
|
144
|
1
|
mirrorSymmetry = tixi::TixiGetTextElement(tixiHandle, xpath + "/symmetry") == "half";
|
145
|
|
}
|
146
|
|
}
|
147
|
|
|
148
|
1
|
const int CCPACSFuselageProfile::GetNumPoints() const
|
149
|
|
{
|
150
|
1
|
if (!m_pointList_choice1)
|
151
|
0
|
return 0;
|
152
|
1
|
return static_cast<int>(m_pointList_choice1->AsVector().size());
|
153
|
|
}
|
154
|
|
|
155
|
|
// Returns the flag for the mirror symmetry with respect to the x-z-plane in the fuselage profile
|
156
|
1
|
bool CCPACSFuselageProfile::GetMirrorSymmetry() const
|
157
|
|
{
|
158
|
1
|
return mirrorSymmetry;
|
159
|
|
}
|
160
|
|
|
161
|
|
// Invalidates internal fuselage profile state
|
162
|
1
|
void CCPACSFuselageProfile::InvalidateImpl(const boost::optional<std::string>& source) const
|
163
|
|
{
|
164
|
1
|
wireCache.clear();
|
165
|
1
|
diameterPointsCache.clear();
|
166
|
|
}
|
167
|
|
|
168
|
|
// Returns the fuselage profile wire
|
169
|
1
|
TopoDS_Wire CCPACSFuselageProfile::GetWire(bool forceClosed) const
|
170
|
|
{
|
171
|
1
|
return forceClosed ? wireCache->closed : wireCache->original;
|
172
|
|
}
|
173
|
|
|
174
|
|
// Builds the fuselage profile wire. The returned wire is already transformed by the
|
175
|
|
// fuselage profile element transformation.
|
176
|
1
|
void CCPACSFuselageProfile::BuildWires(WireCache& cache) const
|
177
|
|
{
|
178
|
1
|
if (!m_pointList_choice1)
|
179
|
0
|
throw CTiglError("No pointlist specified");
|
180
|
1
|
if (GetNumPoints() < 2) {
|
181
|
0
|
throw CTiglError("Number of points is less than 2 in CCPACSFuselageProfile::BuildWire", TIGL_ERROR);
|
182
|
|
}
|
183
|
|
|
184
|
1
|
auto points = m_pointList_choice1->AsVector();
|
185
|
1
|
auto params = m_pointList_choice1->GetParamsAsMap();
|
186
|
1
|
auto kinks = m_pointList_choice1->GetKinksAsVector();
|
187
|
1
|
if (mirrorSymmetry) {
|
188
|
1
|
SymmetrizeFuselageProfile(points, params, kinks);
|
189
|
|
}
|
190
|
|
|
191
|
|
// Build the B-Spline
|
192
|
1
|
auto occPoints = OccArray(points);
|
193
|
|
|
194
|
|
// we always want to include the endpoint, if it's the same as the startpoint
|
195
|
|
// we use the middle to enforce closing of the spline
|
196
|
1
|
gp_Pnt pStart = points.front().Get_gp_Pnt();
|
197
|
1
|
gp_Pnt pEnd = points.back().Get_gp_Pnt();
|
198
|
|
|
199
|
|
// this check allows some tolerance, based on the absolute size of the profile
|
200
|
1
|
if (pStart.Distance(pEnd) < 0.005 * CTiglBSplineAlgorithms::scale(occPoints->Array1())) {
|
201
|
1
|
gp_Pnt pMiddle = 0.5 * (pStart.XYZ() + pEnd.XYZ());
|
202
|
1
|
occPoints->SetValue(occPoints->Lower(), pMiddle);
|
203
|
1
|
occPoints->SetValue(occPoints->Upper(), pMiddle);
|
204
|
|
}
|
205
|
|
|
206
|
1
|
CTiglInterpolatePointsWithKinks interp(occPoints, kinks, params, 0.5, 3);
|
207
|
1
|
auto spline = interp.Curve();
|
208
|
|
|
209
|
1
|
if (mirrorSymmetry) {
|
210
|
1
|
double umin = spline->FirstParameter();
|
211
|
1
|
double umax = spline->LastParameter();
|
212
|
1
|
spline = CTiglBSplineAlgorithms::trimCurve(spline, umin, 0.5 * (umin + umax));
|
213
|
1
|
CTiglBSplineAlgorithms::reparametrizeBSpline(*spline, umin, umax);
|
214
|
|
}
|
215
|
|
|
216
|
|
// we reparametrize the spline to get better performing lofts.
|
217
|
|
// there might be a small accuracy loss though.
|
218
|
1
|
spline = CTiglBSplineAlgorithms::reparametrizeBSplineNiceKnots(spline).curve;
|
219
|
|
|
220
|
|
// Create wires
|
221
|
1
|
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(spline).Edge();
|
222
|
1
|
BRepBuilderAPI_MakeWire builder1(edge);
|
223
|
1
|
TopoDS_Wire tempWireOriginal = builder1.Wire();
|
224
|
|
|
225
|
1
|
BRepBuilderAPI_MakeWire builder2(edge);
|
226
|
1
|
if (!spline->IsClosed()) {
|
227
|
1
|
builder2.Add(BRepBuilderAPI_MakeEdge(spline->EndPoint(), spline->StartPoint()));
|
228
|
|
}
|
229
|
1
|
TopoDS_Wire tempWireClosed = builder2.Wire();
|
230
|
1
|
if (tempWireClosed.IsNull() == Standard_True || tempWireOriginal.IsNull() == Standard_True) {
|
231
|
0
|
throw CTiglError("TopoDS_Wire is null in CCPACSFuselageProfile::BuildWire", TIGL_ERROR);
|
232
|
|
}
|
233
|
|
|
234
|
1
|
cache.closed = tempWireClosed;
|
235
|
1
|
cache.original = tempWireOriginal;
|
236
|
|
}
|
237
|
|
|
238
|
|
// Transforms a point by the fuselage profile transformation
|
239
|
0
|
gp_Pnt CCPACSFuselageProfile::TransformPoint(const gp_Pnt& aPoint) const
|
240
|
|
{
|
241
|
0
|
CTiglTransformation transformation;
|
242
|
0
|
return transformation.Transform(aPoint);
|
243
|
|
}
|
244
|
|
|
245
|
|
// Gets a point on the fuselage profile wire in dependence of a parameter zeta with
|
246
|
|
// 0.0 <= zeta <= 1.0. For zeta = 0.0 this is the wire start point,
|
247
|
|
// for zeta = 1.0 the last wire point.
|
248
|
0
|
gp_Pnt CCPACSFuselageProfile::GetPoint(double zeta) const
|
249
|
|
{
|
250
|
0
|
if (zeta < 0.0 || zeta > 1.0) {
|
251
|
|
throw CTiglError("Parameter zeta not in the range 0.0 <= zeta <= 1.0 in CCPACSFuselageProfile::GetPoint",
|
252
|
0
|
TIGL_ERROR);
|
253
|
|
}
|
254
|
|
|
255
|
|
// Get the first edge of the wire
|
256
|
0
|
BRepTools_WireExplorer wireExplorer(wireCache->original);
|
257
|
0
|
if (!wireExplorer.More()) {
|
258
|
0
|
throw CTiglError("Not enough edges found in CCPACSFuselageProfile::GetPoint", TIGL_ERROR);
|
259
|
|
}
|
260
|
|
|
261
|
0
|
Standard_Real firstParam = 0.;
|
262
|
0
|
Standard_Real lastParam = 1.;
|
263
|
0
|
Handle(Geom_Curve) curve = BRep_Tool::Curve(wireExplorer.Current(), firstParam, lastParam);
|
264
|
|
|
265
|
0
|
gp_Pnt point = curve->Value(firstParam * (1 - zeta) + lastParam * zeta);
|
266
|
|
|
267
|
0
|
return point;
|
268
|
|
}
|
269
|
|
|
270
|
1
|
void CCPACSFuselageProfile::BuildDiameterPoints(DiameterPointsCache& cache) const
|
271
|
|
{
|
272
|
1
|
if (!m_pointList_choice1)
|
273
|
0
|
throw CTiglError("No pointlist specified");
|
274
|
1
|
const std::vector<CTiglPoint>& coordinates = m_pointList_choice1->AsVector();
|
275
|
|
|
276
|
1
|
if (mirrorSymmetry) {
|
277
|
1
|
cache.start = coordinates[0].Get_gp_Pnt();
|
278
|
1
|
cache.end = coordinates[coordinates.size() - 1].Get_gp_Pnt();
|
279
|
|
}
|
280
|
|
else {
|
281
|
|
// compute starting diameter point
|
282
|
1
|
gp_Pnt firstPnt = coordinates[0].Get_gp_Pnt();
|
283
|
1
|
gp_Pnt lastPnt = coordinates[coordinates.size() - 1].Get_gp_Pnt();
|
284
|
1
|
double x = (firstPnt.X() + lastPnt.X()) / 2.;
|
285
|
1
|
double y = (firstPnt.Y() + lastPnt.Y()) / 2.;
|
286
|
1
|
double z = (firstPnt.Z() + lastPnt.Z()) / 2.;
|
287
|
1
|
cache.start = gp_Pnt(x, y, z);
|
288
|
|
|
289
|
|
// find the point with the max dist to starting point
|
290
|
1
|
cache.end = cache.start;
|
291
|
1
|
for (std::vector<CTiglPoint>::const_iterator it = coordinates.begin(); it != coordinates.end(); ++it) {
|
292
|
1
|
gp_Pnt point = it->Get_gp_Pnt();
|
293
|
1
|
if (cache.start.Distance(point) > cache.start.Distance(cache.end)) {
|
294
|
1
|
cache.end = point;
|
295
|
|
}
|
296
|
|
}
|
297
|
|
}
|
298
|
|
}
|
299
|
|
|
300
|
1
|
TopoDS_Wire CCPACSFuselageProfile::GetDiameterWire() const
|
301
|
|
{
|
302
|
1
|
Handle(Geom_TrimmedCurve) diameterCurve = GC_MakeSegment(diameterPointsCache->start, diameterPointsCache->end);
|
303
|
1
|
TopoDS_Edge diameterEdge = BRepBuilderAPI_MakeEdge(diameterCurve);
|
304
|
1
|
TopoDS_Wire diameterWire = BRepBuilderAPI_MakeWire(diameterEdge);
|
305
|
1
|
return diameterWire;
|
306
|
|
}
|
307
|
|
|
308
|
|
} // end namespace tigl
|