Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue 1365: support binary deserializers in CLI #1366

Merged
merged 3 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 78 additions & 7 deletions packages/cli/cli.conversions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@ test.afterEach.always((t) => {
// remove files
try {
if (t.context.file1Path) fs.unlinkSync(t.context.file1Path)
} catch (err) {}
} catch (err) { }

try {
if (t.context.file2Path) fs.unlinkSync(t.context.file2Path)
} catch (err) {}
} catch (err) { }

try {
if (t.context.file3Path) fs.unlinkSync(t.context.file3Path)
} catch (err) {}
} catch (err) { }

try {
if (t.context.file4Path) fs.unlinkSync(t.context.file4Path)
} catch (err) {}
} catch (err) { }

try {
if (t.context.file5Path) fs.unlinkSync(t.context.file5Path)
} catch (err) { }
})

test.beforeEach((t) => {
Expand Down Expand Up @@ -68,6 +72,57 @@ module.exports = { main, getParameterDefinitions }
return filePath
}

const createImportJscad = (id, extension) => {
const jscadScript = `// test script ${id} -- import

const main = () => {
return require('./test${id}.${extension}')
}

module.exports = { main }
`

const fileName = `./test${id}-import.jscad`;
const filePath = path.resolve(__dirname, fileName);
fs.writeFileSync(filePath, jscadScript);
return filePath;
}

const testBackImport = (t, testID, extension) => {
const cliPath = t.context.cliPath;

const file4Path = createImportJscad(testID, extension);
t.context.file4Path = file4Path;
t.true(fs.existsSync(file4Path));

const file5Name = `./test${testID}-import.stl`;
const file5Path = path.resolve(__dirname, file5Name);
t.context.file5Path = file5Path;
t.false(fs.existsSync(file5Path));

cmd = `node ${cliPath} ${file4Path}`
execSync(cmd, { stdio: [0, 1, 2] })
t.true(fs.existsSync(file5Path));
}

const runOnFixture = (t, fixtureName) => {
const inputFile = path.resolve(
__dirname,
path.join('test_fixtures', fixtureName, 'index.js'));

const outputFile = inputFile.replace('.js', '.stl');
t.context.file1Path = outputFile;

t.false(fs.existsSync(outputFile));

const cmd = `node ${t.context.cliPath} ${inputFile}`
execSync(cmd, { stdio: [0, 1, 2] });
t.true(fs.existsSync(outputFile))

return outputFile;
}


test('cli (conversions STL)', (t) => {
const testID = 11

Expand All @@ -87,18 +142,20 @@ test('cli (conversions STL)', (t) => {

let cmd = `node ${cliPath} ${file1Path}`
execSync(cmd, { stdio: [0, 1, 2] })
t.true(fs.existsSync(file2Path))
t.true(fs.existsSync(file2Path));

// convert from STL to JSCAD script
const file3Name = `./test${testID}.js`
const file3Path = path.resolve(__dirname, file3Name)
t.false(fs.existsSync(file3Path))
t.false(fs.existsSync(file3Path));

t.context.file3Path = file3Path

cmd = `node ${cliPath} ${file2Path} -o ${file3Path} -v -add-metadata false`
execSync(cmd, { stdio: [0, 1, 2] })
t.true(fs.existsSync(file3Path))
t.true(fs.existsSync(file3Path));

testBackImport(t, testID, 'stl');
})

test('cli (conversions DXF)', (t) => {
Expand Down Expand Up @@ -132,6 +189,8 @@ test('cli (conversions DXF)', (t) => {
cmd = `node ${cliPath} ${file2Path} -of js`
execSync(cmd, { stdio: [0, 1, 2] })
t.true(fs.existsSync(file3Path))

testBackImport(t, testID, 'dxf');
})

test('cli (conversions AMF)', (t) => {
Expand Down Expand Up @@ -165,6 +224,8 @@ test('cli (conversions AMF)', (t) => {
cmd = `node ${cliPath} ${file2Path} -of js`
execSync(cmd, { stdio: [0, 1, 2] })
t.true(fs.existsSync(file3Path))

testBackImport(t, testID, 'amf');
})

test('cli (conversions JSON)', (t) => {
Expand Down Expand Up @@ -198,6 +259,8 @@ test('cli (conversions JSON)', (t) => {
cmd = `node ${cliPath} ${file2Path} -of js`
execSync(cmd, { stdio: [0, 1, 2] })
t.true(fs.existsSync(file3Path))

testBackImport(t, testID, 'json');
})

test('cli (conversions SVG)', (t) => {
Expand Down Expand Up @@ -264,4 +327,12 @@ test('cli (conversions X3D)', (t) => {
cmd = `node ${cliPath} ${file2Path} -of js`
execSync(cmd, { stdio: [0, 1, 2] })
t.true(fs.existsSync(file3Path))

testBackImport(t, testID, 'x3d');
})

test('cli (import STL)', (t) => {
const testID = 17

runOnFixture(t, 'stl_import')
})
Binary file not shown.
6 changes: 6 additions & 0 deletions packages/cli/test_fixtures/stl_import/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

function main() {
return require('./binary_stl.stl');
}

module.exports = { main }
9 changes: 7 additions & 2 deletions packages/core/src/io/registerExtensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ const registerDeserializer = (extension, fs, _require) => {
const deserializer = deserializers[extension]
const fileExtension = '.' + extension
_require.extensions[fileExtension] = (module, filename) => {
const content = fs.readFileSync(filename, 'utf8')
const parsed = deserializer({ filename, output: 'geometry' }, content)
const fileReadResult = fs.readFileSync(filename);
const fileContent = fileReadResult.buffer
z3dev marked this conversation as resolved.
Show resolved Hide resolved
? fileReadResult.buffer.slice(
fileReadResult.byteOffset,
fileReadResult.byteOffset + fileReadResult.length)
: fileReadResult;
const parsed = deserializer({ filename, output: 'geometry' }, fileContent)
module.exports = parsed
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/io/amf-deserializer/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ All code released under MIT license
const version = require('../package.json').version
const translate = require('./translate')
const instantiate = require('./deserialize')
const { ensureString } = require('../../io-utils')
z3dev marked this conversation as resolved.
Show resolved Hide resolved

/**
* Deserialize the given AMF source (XML) into either a script or an array of geometry
Expand All @@ -48,6 +49,7 @@ const deserialize = (options, input) => {
}
options = Object.assign({}, defaults, options)

input = ensureString(input);
return options.output === 'script' ? translate(options, input) : instantiate(options, input)
}

Expand Down
15 changes: 9 additions & 6 deletions packages/io/dxf-deserializer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All code released under MIT license
*/

const version = require('./package.json').version
const { ensureString } = require('../io-utils')
const { BYLAYER, getTLA } = require('./autocad')
const colorIndex = require('./colorindex2017')
const dxf = require('./DxfReader')
Expand Down Expand Up @@ -275,7 +276,7 @@ const handleXcoord = (reader, group, value) => {
const obj = reader.objstack.pop()
if ('type' in obj) {
if (obj.type === 'lwpolyline') {
// special handling to build a list of vertices
// special handling to build a list of vertices
if (obj.pptxs === undefined) {
obj.pptxs = []
obj.bulgs = []
Expand All @@ -284,7 +285,7 @@ const handleXcoord = (reader, group, value) => {
obj.bulgs.push(0)
} else {
if (obj.type === 'mesh') {
// special handling to build a list of vertices
// special handling to build a list of vertices
if (obj.pptxs === undefined) {
obj.pptxs = []
}
Expand All @@ -307,7 +308,7 @@ const handleYcoord = (reader, group, value) => {
const obj = reader.objstack.pop()
if ('type' in obj) {
if (obj.type === 'lwpolyline' || obj.type === 'mesh') {
// special handling to build a list of vertices
// special handling to build a list of vertices
if (obj.pptys === undefined) {
obj.pptys = []
}
Expand All @@ -329,7 +330,7 @@ const handleZcoord = (reader, group, value) => {
const obj = reader.objstack.pop()
if ('type' in obj) {
if (obj.type === 'mesh') {
// special handling to build a list of vertices
// special handling to build a list of vertices
if (obj.pptzs === undefined) {
obj.pptzs = []
}
Expand All @@ -351,7 +352,7 @@ const handleBulge = (reader, group, value) => {
const obj = reader.objstack.pop()
if ('type' in obj) {
if (obj.type === 'lwpolyline') {
// special handling to build a list of vertices
// special handling to build a list of vertices
const bulgs = obj.bulgs
if (bulgs !== undefined) {
const pptxs = obj.pptxs
Expand All @@ -376,7 +377,7 @@ const handleLen = (reader, group, value) => {
const obj = reader.objstack.pop()
if ('type' in obj) {
if (obj.type === 'mesh') {
// mesh has an order of lengths
// mesh has an order of lengths
const state = obj.state
// console.log('mesh len: '+group+','+value+','+state)
switch (group) {
Expand Down Expand Up @@ -600,6 +601,8 @@ const deserialize = (options, src) => {
}
}
options = Object.assign({}, defaults, options)

src = ensureString(src);
return options.output === 'script' ? translate(src, options) : instantiate(src, options)
}

Expand Down
9 changes: 9 additions & 0 deletions packages/io/io-utils/ensureString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const ensureString = (stringOrArrayBuffer, defaultBinaryEncoding) => {
z3dev marked this conversation as resolved.
Show resolved Hide resolved
if (typeof (stringOrArrayBuffer) === 'string') {
return stringOrArrayBuffer;
}

return new TextDecoder(defaultBinaryEncoding || 'utf-8').decode(new Uint8Array(stringOrArrayBuffer));
}

module.exports = ensureString;
3 changes: 2 additions & 1 deletion packages/io/io-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ module.exports = {
convertToBlob: require('./convertToBlob'),
makeBlob: require('./makeBlob'),
BinaryReader: require('./BinaryReader'),
Blob: require('./Blob')
Blob: require('./Blob'),
ensureString: require('./ensureString')
}
4 changes: 3 additions & 1 deletion packages/io/json-deserializer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ All code released under MIT license
*/

const { flatten, toArray } = require('@jscad/array-utils')
const { ensureString } = require('../io-utils')

const version = require('./package.json').version

Expand All @@ -45,6 +46,7 @@ const deserialize = (options, input) => {
options = Object.assign({}, defaults, options)

// convert the JSON notation into anonymous object(s)
input = ensureString(input);
let objects = JSON.parse(input)

// cleanup the objects
Expand All @@ -69,7 +71,7 @@ const translate = (options, objects) => {
: ''

script +=
`
`
const { geometries } = require('@jscad/modeling')

const main = () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/io/obj-deserializer/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { colors, primitives } = require('@jscad/modeling')
const { ensureString } = require('../io-utils')

const version = require('./package.json').version

Expand Down Expand Up @@ -32,6 +33,8 @@ const deserialize = (options, input) => {
options = Object.assign({}, defaults, options)
const { output } = options

input = ensureString(input);

options && options.statusCallback && options.statusCallback({ progress: 0 })

const { positions, groups } = getGroups(input, options)
Expand Down
6 changes: 4 additions & 2 deletions packages/io/svg-deserializer/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const { cagLengthX, cagLengthY, svgColorForTarget } = require('./helpers')
const { svgSvg, svgRect, svgCircle, svgGroup, svgLine, svgPath, svgEllipse, svgPolygon, svgPolyline, svgUse } = require('./svgElementHelpers')
const shapesMapGeometry = require('./shapesMapGeometry')
const shapesMapJscad = require('./shapesMapJscad')
const { ensureString } = require('../../io-utils')

/**
* Deserializer of SVG source data to JSCAD geometries.
Expand Down Expand Up @@ -58,6 +59,7 @@ const deserialize = (options, input) => {
version
}
options = Object.assign({}, defaults, options)
input = ensureString(input);
return options.output === 'script' ? translate(input, options) : instantiate(input, options)
}

Expand Down Expand Up @@ -395,7 +397,7 @@ const createSvgParser = (src, pxPmm) => {
if (svgGroups.length > 0) {
const group = svgGroups.pop()
if ('objects' in group) {
// TBD apply presentation attributes from the group
// TBD apply presentation attributes from the group
group.objects.push(obj)
}
svgGroups.push(group)
Expand All @@ -422,7 +424,7 @@ const createSvgParser = (src, pxPmm) => {
DEFS: () => { svgInDefs = false },
USE: popGroup,
G: popGroup,
undefined: () => {}
undefined: () => { }
}
const elementName = node.name.toUpperCase()
const obj = objMap[elementName] ? objMap[elementName]() : undefined
Expand Down
3 changes: 2 additions & 1 deletion packages/io/x3d-deserializer/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const version = require('../package.json').version

const translate = require('./translate')
const instantiate = require('./instantiate')
const { ensureString } = require('../../io-utils')

/**
* Deserialize the given X3D source (XML Encoding) into either a script or an array of geometry
Expand All @@ -48,7 +49,7 @@ const deserialize = (options, input) => {
addMetaData: true
}
options = Object.assign({}, defaults, options)

input = ensureString(input);
return options.output === 'script' ? translate(options, input) : instantiate(options, input)
}

Expand Down
Loading