Solvespace
409 строк · 13.4 Кб
1//-----------------------------------------------------------------------------
2// Export a STEP file describing our ratpoly shell.
3//
4// Copyright 2008-2013 Jonathan Westhues.
5//-----------------------------------------------------------------------------
6#include "solvespace.h"
7
8void StepFileWriter::WriteHeader() {
9fprintf(f,
10"ISO-10303-21;\n"
11"HEADER;\n"
12"\n"
13"FILE_DESCRIPTION((''), '2;1');\n"
14"\n"
15"FILE_NAME(\n"
16" 'output_file',\n"
17" '2009-06-07T17:44:47-07:00',\n"
18" (''),\n"
19" (''),\n"
20" 'SolveSpace',\n"
21" '',\n"
22" ''\n"
23");\n"
24"\n"
25"FILE_SCHEMA (('CONFIG_CONTROL_DESIGN'));\n"
26"ENDSEC;\n"
27"\n"
28"DATA;\n"
29"\n"
30"/**********************************************************\n"
31" * This defines the units and tolerances for the file. It\n"
32" * is always the same, independent of the actual data.\n"
33" **********************************************************/\n"
34"#158=(\n"
35"LENGTH_UNIT()\n"
36"NAMED_UNIT(*)\n"
37"SI_UNIT(.MILLI.,.METRE.)\n"
38");\n"
39"#161=(\n"
40"NAMED_UNIT(*)\n"
41"PLANE_ANGLE_UNIT()\n"
42"SI_UNIT($,.RADIAN.)\n"
43");\n"
44"#166=(\n"
45"NAMED_UNIT(*)\n"
46"SI_UNIT($,.STERADIAN.)\n"
47"SOLID_ANGLE_UNIT()\n"
48");\n"
49"#167=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.001),#158,\n"
50"'DISTANCE_ACCURACY_VALUE',\n"
51"'string');\n"
52"#168=(\n"
53"GEOMETRIC_REPRESENTATION_CONTEXT(3)\n"
54"GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#167))\n"
55"GLOBAL_UNIT_ASSIGNED_CONTEXT((#166,#161,#158))\n"
56"REPRESENTATION_CONTEXT('ID1','3D')\n"
57");\n"
58"#169=SHAPE_REPRESENTATION('',(#170),#168);\n"
59"#170=AXIS2_PLACEMENT_3D('',#173,#171,#172);\n"
60"#171=DIRECTION('',(0.,0.,1.));\n"
61"#172=DIRECTION('',(1.,0.,0.));\n"
62"#173=CARTESIAN_POINT('',(0.,0.,0.));\n"
63"\n"
64);
65
66// Start the ID somewhere beyond the header IDs.
67id = 200;
68}
69void StepFileWriter::WriteProductHeader() {
70fprintf(f,
71"#175 = SHAPE_DEFINITION_REPRESENTATION(#176, #169);\n"
72"#176 = PRODUCT_DEFINITION_SHAPE('Version', 'Test Part', #177);\n"
73"#177 = PRODUCT_DEFINITION('Version', 'Test Part', #182, #178);\n"
74"#178 = DESIGN_CONTEXT('3D Mechanical Parts', #181, 'design');\n"
75"#179 = PRODUCT('1', 'Product', 'Test Part', (#180));\n"
76"#180 = MECHANICAL_CONTEXT('3D Mechanical Parts', #181, 'mechanical');\n"
77"#181 = APPLICATION_CONTEXT(\n"
78"'configuration controlled 3d designs of mechanical parts and assemblies');\n"
79"#182 = PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('Version',\n"
80"'Test Part', #179, .MADE.);\n"
81"\n"
82);
83}
84int StepFileWriter::ExportCurve(SBezier *sb) {
85int i, ret = id;
86
87fprintf(f, "#%d=(\n", ret);
88fprintf(f, "BOUNDED_CURVE()\n");
89fprintf(f, "B_SPLINE_CURVE(%d,(", sb->deg);
90for(i = 0; i <= sb->deg; i++) {
91fprintf(f, "#%d", ret + i + 1);
92if(i != sb->deg) fprintf(f, ",");
93}
94fprintf(f, "),.UNSPECIFIED.,.F.,.F.)\n");
95fprintf(f, "B_SPLINE_CURVE_WITH_KNOTS((%d,%d),",
96(sb->deg + 1), (sb-> deg + 1));
97fprintf(f, "(0.000,1.000),.UNSPECIFIED.)\n");
98fprintf(f, "CURVE()\n");
99fprintf(f, "GEOMETRIC_REPRESENTATION_ITEM()\n");
100fprintf(f, "RATIONAL_B_SPLINE_CURVE((");
101for(i = 0; i <= sb->deg; i++) {
102fprintf(f, "%.10f", sb->weight[i]);
103if(i != sb->deg) fprintf(f, ",");
104}
105fprintf(f, "))\n");
106fprintf(f, "REPRESENTATION_ITEM('')\n);\n");
107
108for(i = 0; i <= sb->deg; i++) {
109fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
110id + 1 + i,
111CO(sb->ctrl[i]));
112}
113fprintf(f, "\n");
114
115id = ret + 1 + (sb->deg + 1);
116return ret;
117}
118
119int StepFileWriter::ExportCurveLoop(SBezierLoop *loop, bool inner) {
120ssassert(loop->l.n >= 1, "Expected at least one loop");
121
122List<int> listOfTrims = {};
123
124SBezier *sb = loop->l.Last();
125
126// Generate "exactly closed" contours, with the same vertex id for the
127// finish of a previous edge and the start of the next one. So we need
128// the finish of the last Bezier in the loop before we start our process.
129fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
130id, CO(sb->Finish()));
131fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+1, id);
132int lastFinish = id + 1, prevFinish = lastFinish;
133id += 2;
134
135for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) {
136int curveId = ExportCurve(sb);
137
138int thisFinish;
139if(loop->l.NextAfter(sb) != NULL) {
140fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
141id, CO(sb->Finish()));
142fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+1, id);
143thisFinish = id + 1;
144id += 2;
145} else {
146thisFinish = lastFinish;
147}
148
149fprintf(f, "#%d=EDGE_CURVE('',#%d,#%d,#%d,%s);\n",
150id, prevFinish, thisFinish, curveId, ".T.");
151fprintf(f, "#%d=ORIENTED_EDGE('',*,*,#%d,.T.);\n",
152id+1, id);
153
154int oe = id+1;
155listOfTrims.Add(&oe);
156id += 2;
157
158prevFinish = thisFinish;
159}
160
161fprintf(f, "#%d=EDGE_LOOP('',(", id);
162int *oe;
163for(oe = listOfTrims.First(); oe; oe = listOfTrims.NextAfter(oe)) {
164fprintf(f, "#%d", *oe);
165if(listOfTrims.NextAfter(oe) != NULL) fprintf(f, ",");
166}
167fprintf(f, "));\n");
168
169int fb = id + 1;
170fprintf(f, "#%d=%s('',#%d,.T.);\n",
171fb, inner ? "FACE_BOUND" : "FACE_OUTER_BOUND", id);
172
173id += 2;
174listOfTrims.Clear();
175
176return fb;
177}
178
179void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
180int i, j, srfid = id;
181
182// First, we create the untrimmed surface. We always specify a rational
183// B-spline surface (in fact, just a Bezier surface).
184fprintf(f, "#%d=(\n", srfid);
185fprintf(f, "BOUNDED_SURFACE()\n");
186fprintf(f, "B_SPLINE_SURFACE(%d,%d,(", ss->degm, ss->degn);
187for(i = 0; i <= ss->degm; i++) {
188fprintf(f, "(");
189for(j = 0; j <= ss->degn; j++) {
190fprintf(f, "#%d", srfid + 1 + j + i*(ss->degn + 1));
191if(j != ss->degn) fprintf(f, ",");
192}
193fprintf(f, ")");
194if(i != ss->degm) fprintf(f, ",");
195}
196fprintf(f, "),.UNSPECIFIED.,.F.,.F.,.F.)\n");
197fprintf(f, "B_SPLINE_SURFACE_WITH_KNOTS((%d,%d),(%d,%d),",
198(ss->degm + 1), (ss->degm + 1),
199(ss->degn + 1), (ss->degn + 1));
200fprintf(f, "(0.000,1.000),(0.000,1.000),.UNSPECIFIED.)\n");
201fprintf(f, "GEOMETRIC_REPRESENTATION_ITEM()\n");
202fprintf(f, "RATIONAL_B_SPLINE_SURFACE((");
203for(i = 0; i <= ss->degm; i++) {
204fprintf(f, "(");
205for(j = 0; j <= ss->degn; j++) {
206fprintf(f, "%.10f", ss->weight[i][j]);
207if(j != ss->degn) fprintf(f, ",");
208}
209fprintf(f, ")");
210if(i != ss->degm) fprintf(f, ",");
211}
212fprintf(f, "))\n");
213fprintf(f, "REPRESENTATION_ITEM('')\n");
214fprintf(f, "SURFACE()\n");
215fprintf(f, ");\n");
216
217// The control points for the untrimmed surface.
218for(i = 0; i <= ss->degm; i++) {
219for(j = 0; j <= ss->degn; j++) {
220fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
221srfid + 1 + j + i*(ss->degn + 1),
222CO(ss->ctrl[i][j]));
223}
224}
225fprintf(f, "\n");
226
227id = srfid + 1 + (ss->degm + 1)*(ss->degn + 1);
228
229// Now we do the trim curves. We must group each outer loop separately
230// along with its inner faces, so do that now.
231SBezierLoopSetSet sblss = {};
232SPolygon spxyz = {};
233bool allClosed;
234SEdge notClosedAt;
235// We specify a surface, so it doesn't check for coplanarity; and we
236// don't want it to give us any open contours. The polygon and chord
237// tolerance are required, because they are used to calculate the
238// contour directions and determine inner vs. outer contours.
239sblss.FindOuterFacesFrom(sbl, &spxyz, ss,
240SS.ExportChordTolMm(),
241&allClosed, ¬ClosedAt,
242NULL, NULL,
243NULL);
244
245// So in our list of SBezierLoopSet, each set contains at least one loop
246// (the outer boundary), plus any inner loops associated with that outer
247// loop.
248SBezierLoopSet *sbls;
249for(sbls = sblss.l.First(); sbls; sbls = sblss.l.NextAfter(sbls)) {
250SBezierLoop *loop = sbls->l.First();
251
252List<int> listOfLoops = {};
253// Create the face outer boundary from the outer loop.
254int fob = ExportCurveLoop(loop, /*inner=*/false);
255listOfLoops.Add(&fob);
256
257// And create the face inner boundaries from any inner loops that
258// lie within this contour.
259loop = sbls->l.NextAfter(loop);
260for(; loop; loop = sbls->l.NextAfter(loop)) {
261int fib = ExportCurveLoop(loop, /*inner=*/true);
262listOfLoops.Add(&fib);
263}
264
265// And now create the face that corresponds to this outer loop
266// and all of its holes.
267int advFaceId = id;
268fprintf(f, "#%d=ADVANCED_FACE('',(", advFaceId);
269int *fb;
270for(fb = listOfLoops.First(); fb; fb = listOfLoops.NextAfter(fb)) {
271fprintf(f, "#%d", *fb);
272if(listOfLoops.NextAfter(fb) != NULL) fprintf(f, ",");
273}
274
275fprintf(f, "),#%d,.T.);\n", srfid);
276advancedFaces.Add(&advFaceId);
277
278// Export the surface color and transparency
279// https://www.cax-if.org/documents/rec_prac_styling_org_v16.pdf sections 4.4.2 4.2.4 etc.
280// https://tracker.dev.opencascade.org/view.php?id=31550
281fprintf(f, "#%d=COLOUR_RGB('',%.2f,%.2f,%.2f);\n", ++id, ss->color.redF(),
282ss->color.greenF(), ss->color.blueF());
283
284/* // This works in Kisters 3DViewStation but not in KiCAD and Horison EDA,
285// it seems they do not support transparency so use the more verbose one below
286fprintf(f, "#%d=SURFACE_STYLE_TRANSPARENT(%.2f);\n", ++id, 1.0 - ss->color.alphaF());
287++id;
288fprintf(f, "#%d=SURFACE_STYLE_RENDERING_WITH_PROPERTIES(.NORMAL_SHADING.,#%d,(#%d));\n",
289id, id - 2, id - 1);
290++id;
291fprintf(f, "#%d=SURFACE_SIDE_STYLE('',(#%d));\n", id, id - 1);
292*/
293
294// This works in Horison EDA but is more verbose.
295++id;
296fprintf(f, "#%d=FILL_AREA_STYLE_COLOUR('',#%d);\n", id, id - 1);
297++id;
298fprintf(f, "#%d=FILL_AREA_STYLE('',(#%d));\n", id, id - 1);
299++id;
300fprintf(f, "#%d=SURFACE_STYLE_FILL_AREA(#%d);\n", id, id - 1);
301fprintf(f, "#%d=SURFACE_STYLE_TRANSPARENT(%.2f);\n", ++id, 1.0 - ss->color.alphaF());
302++id;
303fprintf(f, "#%d=SURFACE_STYLE_RENDERING_WITH_PROPERTIES(.NORMAL_SHADING.,#%d,(#%d));\n", id, id - 5, id - 1);
304++id;
305fprintf(f, "#%d=SURFACE_SIDE_STYLE('',(#%d, #%d));\n", id, id - 3, id - 1);
306
307++id;
308fprintf(f, "#%d=SURFACE_STYLE_USAGE(.BOTH.,#%d);\n", id, id - 1);
309++id;
310fprintf(f, "#%d=PRESENTATION_STYLE_ASSIGNMENT((#%d));\n", id, id - 1);
311++id;
312fprintf(f, "#%d=STYLED_ITEM('',(#%d),#%d);\n", id, id - 1, advFaceId);
313fprintf(f, "\n");
314
315id++;
316listOfLoops.Clear();
317}
318sblss.Clear();
319spxyz.Clear();
320}
321
322void StepFileWriter::WriteFooter() {
323fprintf(f,
324"\n"
325"ENDSEC;\n"
326"\n"
327"END-ISO-10303-21;\n"
328);
329}
330
331void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
332Group *g = SK.GetGroup(SS.GW.activeGroup);
333SShell *shell = &(g->runningShell);
334
335if(shell->surface.IsEmpty()) {
336Error("The model does not contain any surfaces to export.%s",
337!g->runningMesh.l.IsEmpty()
338? "\n\nThe model does contain triangles from a mesh, but "
339"a triangle mesh cannot be exported as a STEP file. Try "
340"File -> Export Mesh... instead."
341: "");
342return;
343}
344
345f = OpenFile(filename, "wb");
346if(!f) {
347Error("Couldn't write to '%s'", filename.raw.c_str());
348return;
349}
350
351WriteHeader();
352WriteProductHeader();
353
354advancedFaces = {};
355
356for(SSurface &ss : shell->surface) {
357if(ss.trim.IsEmpty())
358continue;
359
360// Get all of the loops of Beziers that trim our surface (with each
361// Bezier split so that we use the section as t goes from 0 to 1), and
362// the piecewise linearization of those loops in xyz space.
363SBezierList sbl = {};
364ss.MakeSectionEdgesInto(shell, NULL, &sbl);
365
366// Apply the export scale factor.
367ss.ScaleSelfBy(1.0/SS.exportScale);
368sbl.ScaleSelfBy(1.0/SS.exportScale);
369
370ExportSurface(&ss, &sbl);
371
372sbl.Clear();
373}
374
375fprintf(f, "#%d=CLOSED_SHELL('',(", id);
376int *af;
377for(af = advancedFaces.First(); af; af = advancedFaces.NextAfter(af)) {
378fprintf(f, "#%d", *af);
379if(advancedFaces.NextAfter(af) != NULL) fprintf(f, ",");
380}
381fprintf(f, "));\n");
382fprintf(f, "#%d=MANIFOLD_SOLID_BREP('brep',#%d);\n", id+1, id);
383fprintf(f, "#%d=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#%d,#170),#168);\n",
384id+2, id+1);
385fprintf(f, "#%d=SHAPE_REPRESENTATION_RELATIONSHIP($,$,#169,#%d);\n",
386id+3, id+2);
387
388WriteFooter();
389
390fclose(f);
391advancedFaces.Clear();
392}
393
394void StepFileWriter::WriteWireframe() {
395fprintf(f, "#%d=GEOMETRIC_CURVE_SET('curves',(", id);
396int *c;
397for(c = curves.First(); c; c = curves.NextAfter(c)) {
398fprintf(f, "#%d", *c);
399if(curves.NextAfter(c) != NULL) fprintf(f, ",");
400}
401fprintf(f, "));\n");
402fprintf(f, "#%d=GEOMETRICALLY_BOUNDED_WIREFRAME_SHAPE_REPRESENTATION"
403"('',(#%d,#170),#168);\n", id+1, id);
404fprintf(f, "#%d=SHAPE_REPRESENTATION_RELATIONSHIP($,$,#169,#%d);\n",
405id+2, id+1);
406
407id += 3;
408curves.Clear();
409}
410
411