1function nrb2iges (nurbs, filename)
2% NRB2IGES : Write a NURBS curve or surface to an IGES file.
3%
4% Calling Sequence:
5%
6%   nrb2iges (nurbs, filename);
7%
8% INPUT:
9%
10%   nurbs    : NURBS curve or surface, see nrbmak.
11%   filename : name of the output file.
12%
13% Description:
14%
15%   The data of the nurbs structure is written in a file following the IGES
16%   format. For a more in-depth explanation see, for example:
17%   <http://engineeronadisk.com/V2/notes_design/engineeronadisk-76.html>.
18%
19%    Copyright (C) 2014 Jacopo Corno
20%
21%    This program is free software: you can redistribute it and/or modify
22%    it under the terms of the GNU General Public License as published by
23%    the Free Software Foundation, either version 3 of the License, or
24%    (at your option) any later version.
25
26%    This program is distributed in the hope that it will be useful,
27%    but WITHOUT ANY WARRANTY; without even the implied warranty of
28%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29%    GNU General Public License for more details.
30%
31%    You should have received a copy of the GNU General Public License
32%    along with this program.  If not, see <http://www.gnu.org/licenses/>.
33%
34%    This file is based on nurbs2iges.m, (C) 2006 Fu Qiang, originally
35%    released under the MIT license.
36
37  dt = datestr (now, 'yyyy.mm.dd');
38
39  dim = numel (nurbs(1).order);
40
41  % START SECTION
42  S{1} = '';
43  S{2} = 'IGES obtained from Nurbs toolbox.';
44  S{3} = 'See <http://octave.sourceforge.net/nurbs/>.';
45  S{4} = '';
46
47  % GLOBAL SECTION
48  G{1} = '1H,';                           % Parameter Deliminator Character
49  G{2} = '1H;';                           % Record Delimiter Character
50  G{3} = HString ('Nurbs toolbox');       % Product ID from Sender
51  G{4} = HString (filename);              % File Name
52  G{5} = HString ('Octave Nurbs');        % System ID
53  G{6} = HString ('nrb2iges');            % Pre-processor Version
54  G{7} = '32';                            % Number of Bits for Integers (No. of bits present in the integer representation of the sending system)
55  G{8} = '75';                            % Single Precision Magnitude (Maximum power of 10 which may be represented as a single precision floating point number from the sending system)
56  G{9} = '6';                             % Single Precision Significance (No. of significant digits of a single precision floating point number on the sending system)
57  G{10}= '75';                            % Double Precision Magnitude (Maximum power of 10 which may be represented as a double precision floating point number from the sending system)
58  G{11}= '15';                            % Double Precision Significance (No. of significant digits of a double precision floating point number on the sending system)
59  G{12}= HString('Nurbs from Octave');    % Product ID for Receiver
60  G{13}= '1.0';                           % Model Space Scale
61  G{14}= '6';                             % Unit Flag (6 = metres)
62  G{15}= HString('M');                    % Units  (metres = "M")
63  G{16}= '1000';                          % Maximum Number of Line Weights
64  G{17}= '1.0';                           % Size of Maximum Line Width
65  G{18}= HString(dt);                     % Date and Time of file generation
66  G{19}= '0.000001';                      % Minimum User-intended Resolution
67  G{20}= '10000.0';                       % Approximate Maximum Coordinate
68  G{21}= HString('Jacopo Corno');         % Name of Author
69  G{22}= HString('GSCE - TU Darmstadt');  % Author's Organization
70  G{23}= '3';                             % IGES Version Number (3 = IGES version 2.0)
71  G{24}= '0';                             % Drafting Standard Code (0 = no standard)
72
73  % Convert section array to lines (maximum lenght 72)
74  SectionG = make_section (G, 72);
75
76  % DIRECTORY ENTRY SECTION
77  % Each directory entry consists of two, 80 character, fixed formatted lines
78  D = [];
79  for ii = 1:length (nurbs)
80    switch (dim)
81      case 1 % NURBS curve
82        D(ii).type = 126;
83      case 2 % NURBS surface
84        D(ii).type = 128;
85      otherwise
86        error ('Only curves and surfaces can be saved in IGES format.')
87    end
88    D(ii).id = 2*ii - 1; % odd counter (see Parameter data section)
89    D(ii).p_start = 0;
90    D(ii).p_count = 0;
91  end
92
93  % PARAMETER DATA SECTION
94  % The structure is a free formatted data entry from columns 1 to 64.
95  % Each line of free formatted data consists of the entity type number
96  % followed by the parameter data describing the entity.
97  % Columns 65 to 72 are reserved for a parameter data index which is an
98  % odd number counter, right justified in the field, which begins at the
99  % number 1 and progresses in odd increments for each entity entered.
100  % Column 73 is reserved for the letter "P" to indicate the data element
101  % belongs to the parameter data section.
102  % Columns 74 to 80 are reserved for the sequence number. Each line of
103  % data corresponds to the entity type as specified in the global section.
104  SectionP = {};
105  for ii = 1:length (nurbs)
106    P = make_section_array (nurbs(ii)); % finish one entity
107    % Convert section array to lines
108    SP = make_section (P, 64);
109    D(ii).p_count = length (SP);
110    if (ii == 1)
111        D(ii).p_start = 1;
112    else
113        D(ii).p_start = D(ii-1).p_start + D(ii-1).p_count;
114    end
115    SectionP{ii} = SP;
116  end
117
118  % SAVE
119  fid = fopen (filename, 'w');
120
121  % Save Start Section
122  for ii = 1:length (S)
123    fprintf (fid, '%-72sS%7d\n', S{ii}, ii);
124  end
125
126  % Save Global Section
127  for ii = 1:length (SectionG)
128    fprintf (fid, '%-72sG%7d\n', SectionG{ii}, ii);
129  end
130
131  % Save Directory Entry Section
132  for i = 1:length (D)
133    fprintf (fid, '%8d%8d%8d%8d%8d%8d%8d%8d%8dD%7d\n', ...
134             D(i).type, D(i).p_start, 0, 0 ,0, 0, 0, 0, 0, i*2-1);
135    fprintf (fid, '%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n', ...
136             D(i).type, 0, 0, D(i).p_count, 0, ' ', ' ', ' ', 0, i*2);
137  end
138
139  % Save Parameter Data Section
140  lines_p = 0;
141  for jj = 1:length (D)
142    sec = SectionP{jj};
143    for ii = 1:length (sec)
144      lines_p = lines_p + 1;
145      fprintf (fid, '%-64s %7dP%7d\n', sec{ii}, D(jj).id, lines_p);
146    end
147  end
148
149  % Save Terminate Section
150  sec_t = sprintf ('%7dS%7dG%7dD%7dP%7d', length (S), length(SectionG), 2*length(D), lines_p);
151  fprintf (fid, '%-72sT%7d\n', sec_t, 1);
152
153  fclose(fid);
154
155end
156
157function P = make_section_array (nurbs)
158  dim = numel (nurbs.order);
159
160  % in IGES the control points are stored in the format [x, y, z, w]
161  % instead of [w*x, w*y, w*z, w]
162  for idim = 1:3
163    nurbs.coefs(idim,:) = nurbs.coefs(idim,:) ./ nurbs.coefs(4,:);
164  end
165
166  P = {};
167  switch dim
168    case 1
169      % Rational B-Spline Curve Entity
170      cp = nurbs.coefs;
171      deg = nurbs.order - 1;
172      knots = nurbs.knots;
173      uspan = [0 1];
174      isplanar = ~any(cp(3,:));
175      P{1} = '126';                       % NURBS curve
176      P{2} = int2str (size (cp, 2) - 1);  % Number of control points
177      P{3} = int2str (deg);               % Degree
178      P{4} = int2str (isplanar);          % Curve on xy plane
179      P{5} = '0';
180      P{6} = '0';
181      P{7} = '0';
182      index = 8;
183      for ii = 1:length (knots)
184        P{index} = sprintf ('%f', knots(ii));
185        index = index + 1;
186      end
187      for ii = 1:size (cp, 2)
188        P{index} = sprintf ('%f', cp(4,ii));
189        index = index + 1;
190      end
191      for ii = 1:size (cp, 2)
192        P{index} = sprintf ('%f', cp(1,ii));
193        index = index + 1;
194        P{index} = sprintf ('%f', cp(2,ii));
195        index = index + 1;
196        P{index} = sprintf ('%f', cp(3,ii));
197        index = index + 1;
198      end
199      P{index} = sprintf ('%f', uspan(1));
200      index = index +1;
201      P{index} = sprintf ('%f', uspan(2));
202      index = index +1;
203      P{index} = '0.0';
204      index = index +1;
205      P{index} = '0.0';
206      index = index +1;
207      if isplanar
208        P{index} = '1.0';
209      else
210        P{index} = '0.0';
211      end
212      index = index + 1;
213      P{index} = '0';
214      index = index + 1;
215      P{index} = '0';
216    case 2
217      % Rational B-Spline Surface Entity
218      cp = nurbs.coefs;
219      degU = nurbs.order(1) - 1;
220      degV = nurbs.order(2) - 1;
221      knotsU = nurbs.knots{1};
222      knotsV = nurbs.knots{2};
223      uspan = [0 1];
224      vspan = [0 1];
225      P{1} = '128';                       % NURBS surface
226      P{2} = int2str (size (cp, 2) - 1);  % Number of control points in U
227      P{3} = int2str (size (cp, 3) - 1);  % Number of control points in V
228      P{4} = int2str (degU);              % Degree in U
229      P{5} = int2str (degV);              % Degree in V
230      P{6} = '0';
231      P{7} = '0';
232      P{8} = '0';
233      P{9} = '0';
234      P{10} = '0';
235      index = 11;
236      for ii = 1:length (knotsU)
237        P{index} = sprintf ('%f', knotsU(ii));
238        index = index + 1;
239      end
240      for ii = 1:length (knotsV)
241        P{index} = sprintf ('%f', knotsV(ii));
242        index = index + 1;
243      end
244      for jj = 1:size (cp, 3)
245        for ii = 1:size (cp, 2)
246          P{index} = sprintf ('%f', cp(4,ii,jj));
247          index = index + 1;
248        end
249      end
250      for jj = 1:size (cp, 3)
251        for ii = 1:size (cp, 2)
252          P{index} = sprintf ('%f',cp(1,ii,jj));
253          index = index + 1;
254          P{index} = sprintf ('%f',cp(2,ii,jj));
255          index = index + 1;
256          P{index} = sprintf ('%f',cp(3,ii,jj));
257          index = index + 1;
258        end
259      end
260      P{index} = sprintf('%f',uspan(1));
261      index = index +1;
262      P{index} = sprintf('%f',uspan(2));
263      index = index +1;
264      P{index} = sprintf('%f',vspan(1));
265      index = index +1;
266      P{index} = sprintf('%f',vspan(2));
267      index = index +1;
268      P{index} = '0';
269      index = index + 1;
270      P{index} = '0';
271    otherwise
272
273  end
274end
275
276function hs = HString (str)
277% HString : Convert the string STR to the Hollerith format.
278
279  hs = sprintf ('%dH%s', length(str), str);
280end
281
282function sec = make_section (fields, linewidth)
283  sec = {};
284  index = 1;
285  line = '';
286  num = length (fields);
287  for i = 1:num
288    if (i < num)
289      newitem = [fields{i} ','];
290    else
291      newitem = [fields{i} ';'];
292    end
293    len = length (line) + length (newitem);
294    if ( len > linewidth )
295      % new line
296      sec{index} = line;
297      index = index + 1;
298      line = '';
299    end
300    line = [line newitem];
301  end
302  sec{index} = line;
303end
304