Writes B-spline curves and surfaces to an IGES file in millimeters and reads them back, converting the file's unit.
How the precision is kept
Numbers are written with JavaScript's shortest text that reads back to the same 64-bit value, with D as the exponent letter, and parsed back with the same rule. Nothing is rounded on the way out. On reading, control points are multiplied by a factor chosen from the file's unit flag; knots and weights are not scaled.
Open item. Check the unit table against the IGES 5.3 unit flags (4 feet, 5 miles, 8 mils at 0.0254 mm; 3 means the unit is named in the next parameter): the entries at 3, 4, 5 and 8 do not match that order.
Calculations under this policy
5 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
whole numbers below 10¹⁵: digits and a point; otherwise String(x) with E → D
String(x) gives the shortest decimal that reads back to the same 64-bit number, up to 17 significant digits, so the file loses nothing. A value that is not finite stops the export.
nurbs/iges.js
lines 22 to 26
ModelCraft 0.8.1
const num = (x) => {
if (!Number.isFinite(x)) fail('nurbs.err.iges_number');
if (Number.isInteger(x) && Math.abs(x) < 1e15) return x + '.';
return String(x).toUpperCase().replace('E', 'D');
};
Global section
64-bit
States the file's number sizes, unit, resolution and largest coordinate.
unit flag 2 (MM); scale 1; resolution 10⁻⁶; largest coordinate = max |x| over all control points
The section declares 15 significant digits for double precision, while num() writes as many digits as the value needs, up to 17. The largest coordinate starts at 1 and is the maximum over control points only.
nurbs/iges.js
lines 53 to 58
ModelCraft 0.8.1
let maxc = 1;
for (const it of d.items) for (const q of it.kind === 'curve' ? it.curve.cvs : it.surface.cvs.flat()) for (const x of q) maxc = Math.max(maxc, Math.abs(x));
const global = [
'1H,', '1H;', holl('CadCraft'), holl(name + '.igs'), holl('CadCraft cc-nurbs'), holl('cc-nurbs 0.8'), '32', '38', '6', '308', '15', holl(name),
'1.', '2', '2HMM', '1', '1.', holl(when), '1.D-6', num(maxc), holl(author), holl(org), '11', '0', holl(when),
];
A number from IGES text
64-bit
Reads one real number from the file.
The first D or d is replaced by E and the text is converted by the language's own parser, which rounds correctly to 64-bit. An empty field reads as 0; text that is not a finite number stops the import.
nurbs/iges.js
lines 113 to 118
ModelCraft 0.8.1
const fnum = (s) => {
if (typeof s !== 'string' || s === '') return 0;
const v = +s.replace(/[Dd]/, 'E');
if (!Number.isFinite(v)) fail('nurbs.err.iges_number');
return v;
};
Unit factors
64-bit
Millimeters per file unit, by the file's unit flag.
25.4, 304.8 and 1609344 are exact; 1e-3 and 25.4e-6 are rounded binary values. The table gives feet's factor (304.8) at flag 3, the mile's (1609344) at flag 4, the kilometer's (1e6) at both 5 and 7, and the same 0.001 at 8 and 9. A flag that is not in the table reads as millimeters.
Scales a curve's control points by the file's unit as it is read.
scale = UNITS[flag] (1 when unknown); p′ = p × scale
The flag is the rounded value of Global parameter 14, taken as 2 when the field is not text. The second factor in the scale expression is 1 in both branches, so the file's model space scale is not used. Each coordinate is multiplied once; knots and weights are left as read.
nurbs/iges.js
lines 135 to 152
ModelCraft 0.8.1
const unitFlag = Math.round(fnum(typeof G[13] === 'string' ? G[13] : '2'));
const scale = (UNITS[unitFlag] ?? 1) * (typeof G[12] === 'string' && G[12] ? 1 : 1);
let doc = D.newDoc();
for (let k = 0; k + 1 < sec.D.length; k += 2) {
const a = sec.D[k], b = sec.D[k + 1];
const type = +a.slice(0, 8), ptr = +a.slice(8, 16), cnt = +b.slice(24, 32), label = b.slice(56, 64).trim();
if (type !== 126 && type !== 128) continue;
if (!(ptr >= 1) || !(cnt >= 1) || ptr - 1 + cnt > sec.P.length) fail('nurbs.err.iges_pointer');
const body = sec.P.slice(ptr - 1, ptr - 1 + cnt).map((l) => l.slice(0, 64)).join('');
const P = freeParams(body, pd, rd).map((x) => (typeof x === 'string' ? x : '0'));
if (+P[0] !== type) fail('nurbs.err.iges_entity');
let at = 1;
const take = (n) => { if (n < 0 || at + n > P.length || n > 5e6) fail('nurbs.err.iges_entity'); const o = P.slice(at, at + n).map(fnum); at += n; return o; };
if (type === 126) {
const [K, M, , , poly] = take(6);
if (!Number.isInteger(K) || !Number.isInteger(M) || K < 1 || M < 1) fail('nurbs.err.iges_entity');
const knots = take(K + M + 2), w = take(K + 1), xyz = take(3 * (K + 1));
const cvs = []; for (let i = 0; i <= K; i++) cvs.push([xyz[3 * i] * scale, xyz[3 * i + 1] * scale, xyz[3 * i + 2] * scale]);
In detail
Method
Entities 126 and 128 written from the IGES 5.3 specification, which the file's header cites. Unit factor from a table keyed by the Global section's unit flag.
Number types
64-bit numbers; text in the file.
Tolerances
The Global section declares a minimum resolution of 1.D-6 and the largest coordinate in the file.
Limits
The Global section's model space scale (parameter 13) is not applied when reading. Only entities 126 and 128 are read. The unit table holds 304.8 at flag 3, 1609344 at flag 4, 1e6 at flag 5 and 0.001 at flag 8.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.