UNPKG

@thi.ng/iges

Version:

IGES 5.3 serializer for (currently only) polygonal geometry, both open & closed

323 lines (322 loc) 8.02 kB
import { isArray } from "@thi.ng/checks/is-array"; import { defmulti } from "@thi.ng/defmulti/defmulti"; import { float } from "@thi.ng/strings/float"; import { hstr } from "@thi.ng/strings/hollerith"; import { padLeft } from "@thi.ng/strings/pad-left"; import { padRight } from "@thi.ng/strings/pad-right"; import { comp } from "@thi.ng/transducers/comp"; import { map } from "@thi.ng/transducers/map"; import { mapIndexed } from "@thi.ng/transducers/map-indexed"; import { mapcat } from "@thi.ng/transducers/mapcat"; import { partition } from "@thi.ng/transducers/partition"; import { push } from "@thi.ng/transducers/push"; import { transduce } from "@thi.ng/transducers/transduce"; import { wordWrap } from "@thi.ng/transducers/word-wrap"; import { wrapSides } from "@thi.ng/transducers/wrap-sides"; import { DEFAULT_GLOBALS, EntityType, PolylineMode, Type, Unit } from "./api.js"; const LINEWIDTH_GLOBALS = 72; const LINEWIDTH_PARAMS = 64; const $Z2 = padLeft(2, "0"); const $Z7 = padLeft(7, "0"); const $F8 = padLeft(8, " "); const $SEQ = padLeft(7, " "); const $BODY = padRight(72, " "); const $PBODY = padRight(64, " "); const $DATE = (d) => hstr( [ d.getUTCFullYear(), $Z2(d.getUTCMonth() + 1), $Z2(d.getUTCDate()), ".", $Z2(d.getUTCHours()), $Z2(d.getUTCMinutes()), $Z2(d.getUTCSeconds()) ].join("") ); const newDocument = (g, start) => { const globals = { ...DEFAULT_GLOBALS, ...g }; const $FF = float(globals.precision); const $PARAM = defmulti( (x) => x[1], {}, { [Type.INT]: (x) => x[0].toString(), [Type.POINTER]: (x) => (-x[0]).toString(), [Type.FLOAT]: (x) => $FF(x[0]), [Type.STR]: (x) => x[0], [Type.HSTR]: (x) => hstr(x[0]), [Type.DATE]: (x) => $DATE(x[0]) } ); return { globals, start: start || [], dict: [], param: [], offsets: { S: 0, G: 0, P: 1, D: 0 }, $FF, $PARAM }; }; const serialize = (doc) => [ ...__formatStart(doc), ...__formatGlobals(doc), ...doc.dict, ...doc.param, __formatTerminate(doc) ].join("\n"); const __formatLine = (body, type, i) => `${$BODY(body)}${type}${$SEQ(i + 1)}`; const __formatStart = (doc) => { const res = [ ...mapIndexed((i, x) => __formatLine(x, "S", i), doc.start) ]; doc.offsets.S += res.length; return res; }; const __formatGlobals = (doc) => { const g = doc.globals; const res = __formatParams( doc, [ [g.delimParam, Type.HSTR], [g.delimRecord, Type.HSTR], [g.senderProductID, Type.HSTR], [g.fileName, Type.HSTR], [g.generator, Type.HSTR], [g.generatorVersion, Type.HSTR], [g.intBits, Type.INT], [g.singleMaxPow, Type.INT], [g.singleDigits, Type.INT], [g.doubleMaxPow, Type.INT], [g.doubleDigits, Type.INT], [g.receiverProductID, Type.HSTR], [g.modelScale, Type.FLOAT], [g.units, Type.INT], [Unit[g.units], Type.HSTR], [g.numLineWeights, Type.INT], [g.maxLineWeight, Type.FLOAT], [g.created || /* @__PURE__ */ new Date(), Type.DATE], [1 / Math.pow(10, g.precision), Type.FLOAT], [g.maxCoord || 1e3, Type.FLOAT], [g.author, Type.HSTR], [g.authorOrg, Type.HSTR], [g.specVersion, Type.INT], [g.draftVersion, Type.INT], [g.modified || /* @__PURE__ */ new Date(), Type.DATE] ], (body, i) => `${$BODY(body)}G${$SEQ(i + 1)}`, LINEWIDTH_GLOBALS ); doc.offsets.G += res.length; return res; }; const __formatTerminate = (doc) => __formatLine( `S${$Z7(doc.offsets.S)}G${$Z7(doc.offsets.G)}D${$Z7( doc.offsets.D )}P${$Z7(doc.offsets.P - 1)}`, "T", 0 ); const __formatStatus = (s) => [s.blank || 0, s.subord || 0, s.usage || 0, s.hierarchy || 0].map((x) => $Z2(x)).join(""); const __formatDictEntry = (e) => transduce( comp( map((x) => $F8(x)), partition(9), mapIndexed( (i, x) => __formatLine(x.join(""), "D", i), e.index ) ), push(), [ e.type, e.param || 0, e.struct || 0, e.pattern || 0, e.level || 0, e.view || 0, e.matrix || 0, e.labelAssoc || 0, __formatStatus(e.status || {}), // e.type, e.lineWeight || 0, e.color || 0, e.lineCount || 1, e.form || 0, 0, 0, e.label || "", e.subscript || 0 ] ); const __formatParam = (did, pid) => (body, i) => `${$PBODY(body)} ${$Z7(did + 1)}P${$SEQ(i + pid)}`; const __formatParams = (doc, params, fmtBody, bodyWidth = LINEWIDTH_PARAMS) => { const lines = transduce( comp( map(doc.$PARAM), wordWrap(bodyWidth, { delim: 1, always: true }), map((p) => p.join(doc.globals.delimParam)) ), push(), params ); const n = lines.length - 1; return lines.map((line, i) => { const d = i < n ? doc.globals.delimParam : doc.globals.delimRecord; return fmtBody(line + d, i); }); }; const __addEntity = (doc, type, entry, params, opts = {}) => { const did = doc.offsets.D; const pid = doc.offsets.P; const fparams = __formatParams( doc, [[type, Type.INT]].concat(params), __formatParam(did, pid) ); doc.offsets.P += fparams.length; doc.offsets.D += 2; doc.dict.push( ...__formatDictEntry({ type, pattern: opts.pattern || 0, color: opts.color || 0, param: pid, index: did, lineCount: fparams.length, ...entry }) ); doc.param.push(...fparams); return did + 1; }; const addPolyline = (doc, pts, form = PolylineMode.OPEN, opts) => { const is2D = pts[0].length == 2; const params = [ [is2D ? 1 : 2, Type.INT], [pts.length + (form === PolylineMode.CLOSED ? 1 : 0), Type.INT] ]; is2D && params.push([0, Type.FLOAT]); return __addEntity( doc, EntityType.POLYLINE, { form: form === PolylineMode.FILLED ? 63 : 11 }, [ ...params, ...mapcat( (p) => map((x) => [x, Type.FLOAT], p), form === PolylineMode.CLOSED ? wrapSides(pts, 0, 1) : pts ) ], opts ); }; const addPolygon = (doc, pts, opts) => addPolyline(doc, pts, PolylineMode.FILLED, opts); const addPoint = (doc, p, opts) => __addEntity( doc, EntityType.POINT, null, [ [p[0], Type.FLOAT], [p[1], Type.FLOAT], [p[2] || 0, Type.FLOAT], [0, Type.POINTER] ], opts ); const addLine = (doc, a, b, opts) => __addEntity( doc, EntityType.LINE, null, [ [a[0], Type.FLOAT], [a[1], Type.FLOAT], [a[2] || 0, Type.FLOAT], [b[0], Type.FLOAT], [b[1], Type.FLOAT], [b[2] || 0, Type.FLOAT] ], opts ); const addBooleanTree = (doc, tree, opts) => { const params = __postOrder([], tree); return __addEntity( doc, EntityType.BOOLEAN_TREE, null, [[params.length, Type.INT], ...params], opts ); }; const __postOrder = (acc, tree) => { if (isArray(tree)) { __postOrder(acc, tree[1]); __postOrder(acc, tree[2]); acc.push([tree[0], Type.INT]); } else { acc.push([tree, Type.POINTER]); } return acc; }; const addCSGBox = (doc, pos, size, xaxis = [1, 0, 0], zaxis = [0, 0, 1], opts) => __addEntity( doc, EntityType.CSG_BOX, null, [ [size[0], Type.FLOAT], [size[1], Type.FLOAT], [size[2], Type.FLOAT], [pos[0], Type.FLOAT], [pos[1], Type.FLOAT], [pos[2], Type.FLOAT], [xaxis[0], Type.FLOAT], [xaxis[1], Type.FLOAT], [xaxis[2], Type.FLOAT], [zaxis[0], Type.FLOAT], [zaxis[1], Type.FLOAT], [zaxis[2], Type.FLOAT] ], opts ); const addCSGCylinder = (doc, pos, normal, radius, height, opts) => __addEntity( doc, EntityType.CSG_CYLINDER, null, [ [pos[0], Type.FLOAT], [pos[1], Type.FLOAT], [pos[2], Type.FLOAT], [normal[0], Type.FLOAT], [normal[1], Type.FLOAT], [normal[2], Type.FLOAT], [radius, Type.FLOAT], [height, Type.FLOAT] ], opts ); export * from "./api.js"; export { addBooleanTree, addCSGBox, addCSGCylinder, addLine, addPoint, addPolygon, addPolyline, newDocument, serialize };