Skip to content

Commit

Permalink
feat(bindgen): support typescript mesh interface type
Browse files Browse the repository at this point in the history
  • Loading branch information
thewtex committed Jul 31, 2023
1 parent 75fef2a commit 085c8d6
Show file tree
Hide file tree
Showing 16 changed files with 4,414 additions and 1 deletion.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"test:node": "ava test/node/core/*.js test/node/io/image/*.js test/node/io/mesh/*.js test/node/pipeline/*.js",
"test:cliRun": "node src/itk-wasm-cli.js -b wasi-build -s ./test/pipelines/stdout-stderr-pipeline run -r wasmtime stdout-stderr-test.wasi.wasm",
"test:cliTest": "node src/itk-wasm-cli.js -b wasi-build -s ./test/pipelines/stdout-stderr-pipeline test",
"test:cliBindgen:typescript": "node src/itk-wasm-cli.js -b emscripten-build -s ./test/pipelines/bindgen-interface-types-pipeline bindgen --package-version 1.0.0 --package-name @itk-wasm/bindgen-interface-types-test --package-description \"Exercise interface types for bindgen\"",
"test:browser": "karma start ./karma.conf.cjs",
"test:browser:debug": "karma start ./karma.conf.cjs --no-single-run",
"test:browser:debug:cypress": "start-server-and-test start http-get://localhost:8083 cypress:open",
Expand Down
2 changes: 2 additions & 0 deletions src/bindgen/typescript/demo/all-demo-types-supported.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const demoSupportedInputTypes = new Set([
'INT',
'UINT',
'BOOL',
'INPUT_MESH',
])
const demoSupportedOutputTypes = new Set([
'OUTPUT_TEXT_FILE:FILE',
Expand All @@ -19,6 +20,7 @@ const demoSupportedOutputTypes = new Set([
'UINT',
'BOOL',
'OUTPUT_JSON',
'OUTPUT_MESH',
])

function allDemoTypesSupported(interfaceJson) {
Expand Down
5 changes: 5 additions & 0 deletions src/bindgen/typescript/demo/input-parameters-demo-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ function inputParametersDemoHtml(prefix, indent, parameter, required, useCamelCa
result += `${prefix}${indent}<sl-checkbox name="${parameter.name}">${label} - <i>${parameter.description}</i></sl-checkbox>\n`
result += `<br /><br />\n`
break
case 'INPUT_MESH':
result += `${prefix}${indent}<sl-details id="${parameter.name}-input" summary="${label}: ${parameter.description}" disabled></sl-details>\n`
result += `${prefix}${indent}<label for="${parameter.name}-file"><sl-button variant="primary" outline onclick="this.parentElement.nextElementSibling.click()">Upload</sp-button></label><input type="file" name="${parameter.name}-file" style="display: none"/>\n`
result += `<br /><br />\n`
break
default:
console.error(`Unexpected interface type: ${parameter.type}`)
process.exit(1)
Expand Down
13 changes: 13 additions & 0 deletions src/bindgen/typescript/demo/input-parameters-demo-typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ function inputParametersDemoTypeScript(functionName, indent, parameter, required
result += `${indent}${indent}model.${modelProperty}.set("${parameterName}", parseInt(${inputIdentifier}.value))\n`
result += `${indent}})\n\n`
break
case 'INPUT_MESH':
result += `${indent}const ${inputIdentifier} = document.querySelector('#${functionName}Inputs input[name=${parameter.name}-file]')\n`
result += `${indent}${inputIdentifier}.addEventListener('change', async (event) => {\n`
result += `${indent}${indent}const dataTransfer = event.dataTransfer\n`
result += `${indent}${indent}const files = event.target.files || dataTransfer.files\n\n`
result += `${indent}${indent}const { mesh, webWorker } = await readMeshFile(null, files[0])\n`
result += `${indent}${indent}webWorker.terminate()\n`
result += `${indent}${indent}model.${modelProperty}.set("${parameterName}", mesh)\n`
result += `${indent}${indent}const details = document.getElementById("${parameter.name}-input")\n`
result += `${indent}${indent}details.innerHTML = \`<pre>$\{globalThis.escapeHtml(JSON.stringify(mesh, globalThis.interfaceTypeJsonReplacer, 2))}</pre>\`\n`
result += `${indent}${indent}details.disabled = false\n`
result += `${indent}})\n\n`
break
default:
console.error(`Unexpected interface type: ${parameter.type}`)
process.exit(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,29 @@ import packageToBundleName from "../package-to-bundle-name.js"
import writeIfOverrideNotPresent from "../../write-if-override-not-present.js"
import outputDemoRunTypeScript from './output-demo-run-typescript.js'
import outputDemoTypeScript from './output-demo-typescript.js'
import interfaceJsonTypeToInterfaceType from '../../interface-json-type-to-interface-type.js'

function interfaceFunctionsDemoTypeScript(packageName, interfaceJson, outputPath) {
let result = ''
let indent = ' '
const bundleName = packageToBundleName(packageName)
const functionName = camelCase(interfaceJson.name)
const functionNamePascalCase = pascalCase(interfaceJson.name)

let result = `import * as ${camelCase(bundleName)} from '../../../dist/bundles/${bundleName}.js'\n`
let needReadMesh = false
const pipelineComponents = ['inputs', 'parameters']
pipelineComponents.forEach((pipelineComponent) => {
needReadMesh = needReadMesh || interfaceJson[pipelineComponent].filter((value) => interfaceJsonTypeToInterfaceType.get(value.type) === 'Mesh').length > 0
})
if (needReadMesh) {
result += `import { readMeshFile } from 'itk-wasm'\n`
}
const needWriteMesh = interfaceJson.outputs.filter((value) => interfaceJsonTypeToInterfaceType.get(value.type) === 'Mesh').length > 0
if (needReadMesh) {
result += `import { writeMeshArrayBuffer } from 'itk-wasm'\n`
}

result += `import * as ${camelCase(bundleName)} from '../../../dist/bundles/${bundleName}.js'\n`

result += `import ${functionName}LoadSampleInputs from "./${interfaceJson.name}-load-sample-inputs.js"\n`
const loadSampleInputsModulePath = path.join(outputPath, `${interfaceJson.name}-load-sample-inputs.ts`)
Expand Down
12 changes: 12 additions & 0 deletions src/bindgen/typescript/demo/output-demo-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ function outputDemoHtml(prefix, indent, parameter) {
result += `${prefix}${indent}<sl-button variant="neutral" outline name="${parameter.name}-download" disabled>${camelCase(parameter.name)}</sl-button>\n`
result += `<br /><br />\n`
break
case 'OUTPUT_MESH':
result += `${prefix}${indent}<sl-details disabled id="${parameter.name}-output" summary="${camelCase(parameter.name)}: ${parameter.description}"><sl-skeleton effect="none"></sl-skeleton></sl-details>\n`

result += `${prefix}${indent}<sl-select id="${parameter.name}-output-format" placeholder="Format">\n`
const formats = ['vtk', 'byu', 'fsa', 'fsb', 'obj', 'off', 'stl', 'swc'];
formats.forEach((format) => {
result += `${prefix}${indent}${indent}<sl-option value="${format}">${format}</sl-option>\n`
})
result += `${prefix}${indent}</sl-select>\n`
result += `${prefix}${indent}<sl-button variant="neutral" outline name="${parameter.name}-download" disabled>Download</sl-button>\n`
result += `<br /><br />\n`
break
default:
console.error(`Unexpected interface type: ${parameter.type}`)
process.exit(1)
Expand Down
8 changes: 8 additions & 0 deletions src/bindgen/typescript/demo/output-demo-run-typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ function outputDemoRunTypeScript(functionName, prefix, indent, parameter) {
// result += `${prefix}${indent}<sl-button variant="neutral" name="${parameter.name}-download">${camelCase(parameter.name)}</sl-button>\n`
// result += `<br /><br />\n`
// break
case 'OUTPUT_MESH':
result += `${prefix}${indent}${parameterName}OutputDownload.variant = "success"\n`
result += `${prefix}${indent}${parameterName}OutputDownload.disabled = false\n`
result += `${indent}${indent}const details = document.getElementById("${parameter.name}-output")\n`
result += `${indent}${indent}details.innerHTML = \`<pre>$\{globalThis.escapeHtml(JSON.stringify(${parameterName}, globalThis.interfaceTypeJsonReplacer, 2))}</pre>\`\n`
result += `${indent}${indent}details.disabled = false\n`
result += `${prefix}${indent}const ${parameterName}Output = document.querySelector('#${functionName}Outputs sl-details[name=${parameter.name}]')\n`
break
default:
console.error(`Unexpected interface type: ${parameter.type}`)
process.exit(1)
Expand Down
15 changes: 15 additions & 0 deletions src/bindgen/typescript/demo/output-demo-typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ function outputDemoTypeScript(functionName, prefix, indent, parameter) {
// result += `${prefix}${indent}<sl-button variant="neutral" name="${parameter.name}-download">${camelCase(parameter.name)}</sl-button>\n`
// result += `<br /><br />\n`
// break
case 'OUTPUT_MESH':
result += `${prefix}${indent}const ${parameterName}OutputDownload = document.querySelector('#${functionName}Outputs sl-button[name=${parameter.name}-download]')\n`
result += `${prefix}${indent}${parameterName}OutputDownload.addEventListener('click', async (event) => {\n`
result += `${prefix}${indent}${indent}event.preventDefault()\n`
result += `${prefix}${indent}${indent}event.stopPropagation()\n`
result += `${prefix}${indent}${indent}if (model.outputs.has("${parameterName}")) {\n`
result += `${prefix}${indent}${indent}${indent}const ${parameterName}DownloadFormat = document.getElementById('${parameter.name}-output-format')\n`
result += `${prefix}${indent}${indent}${indent}const downloadFormat = ${parameterName}DownloadFormat.value || 'vtk'\n`
result += `${prefix}${indent}${indent}${indent}const fileName = \`${parameterName}.\${downloadFormat}\`\n`
result += `${prefix}${indent}${indent}${indent}const { webWorker, arrayBuffer } = await writeMeshArrayBuffer(null, model.outputs.get("${parameterName}"), fileName)\n\n`
result += `${prefix}${indent}${indent}${indent}webWorker.terminate()\n`
result += `${prefix}${indent}${indent}${indent}globalThis.downloadFile(arrayBuffer, fileName)\n`
result += `${prefix}${indent}${indent}}\n`
result += `${prefix}${indent}})\n`
break
default:
console.error(`Unexpected interface type: ${parameter.type}`)
process.exit(1)
Expand Down
7 changes: 7 additions & 0 deletions src/bindgen/typescript/resources/demo-app/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ function downloadFile(content, filename) {
return a
}

function interfaceTypeJsonReplacer (key, value) {
if (!!value && value.byteLength !== undefined) {
return String(value.slice(0, 6)) + '...'
}
return value
}

function escapeHtml(html) {
const div = document.createElement('div');
div.textContent = html;
Expand Down
6 changes: 6 additions & 0 deletions src/bindgen/typescript/typescript-bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,13 @@ const shoelaceScript = `
import '@shoelace-style/shoelace/dist/components/textarea/textarea.js';
import '@shoelace-style/shoelace/dist/components/alert/alert.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/icon-button/icon-button.js';
import '@shoelace-style/shoelace/dist/components/divider/divider.js';
import '@shoelace-style/shoelace/dist/components/details/details.js';
import '@shoelace-style/shoelace/dist/components/popup/popup.js';
import '@shoelace-style/shoelace/dist/components/tag/tag.js';
import '@shoelace-style/shoelace/dist/components/select/select.js';
import '@shoelace-style/shoelace/dist/components/option/option.js';
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path';
setBasePath('/');
Expand Down
1 change: 1 addition & 0 deletions src/build-emscripten.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ const testPipelines = [
path.join('test', 'pipelines', 'input-output-files-pipeline'),
path.join('test', 'pipelines', 'input-output-json-pipeline'),
path.join('test', 'pipelines', 'mesh-read-write-pipeline'),
path.join('test', 'pipelines', 'bindgen-interface-types-pipeline'),
]

if (options.buildTestPipelines) {
Expand Down
2 changes: 2 additions & 0 deletions test/pipelines/bindgen-interface-types-pipeline/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
emscripten-build/
typescript/
24 changes: 24 additions & 0 deletions test/pipelines/bindgen-interface-types-pipeline/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.10)
project(bindgen-interface-types-test)

set(CMAKE_CXX_STANDARD 17)

set(io_components)
if(NOT EMSCRIPTEN)
set(io_components ITKIOMeshVTK)
endif()
find_package(ITK REQUIRED
COMPONENTS
${io_components}
WebAssemblyInterface
)
include(${ITK_USE_FILE})

add_executable(bindgen-interface-types-test bindgen-interface-types-test.cxx)
target_link_libraries(bindgen-interface-types-test PUBLIC ${ITK_LIBRARIES})

enable_testing()
add_test(NAME bindgen-interface-types-test
COMMAND bindgen-interface-types-test ${CMAKE_CURRENT_SOURCE_DIR}/cow.vtk
${CMAKE_CURRENT_BINARY_DIR}/cow.written.vtk
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#include "itkMesh.h"
#include "itkInputMesh.h"
#include "itkOutputMesh.h"
#include "itkPipeline.h"

int main( int argc, char * argv[] )
{
itk::wasm::Pipeline pipeline("bindgen-interface-types-test", "A test to exercise interface types for bindgen", argc, argv);

using PixelType = float;
constexpr unsigned int Dimension = 3;
using MeshType = itk::Mesh< PixelType, Dimension >;

using InputMeshType = itk::wasm::InputMesh<MeshType>;
InputMeshType inputMesh;
pipeline.add_option("input-mesh", inputMesh, "The input mesh")->required()->type_name("INPUT_MESH");

using OutputMeshType = itk::wasm::OutputMesh<MeshType>;
OutputMeshType outputMesh;
pipeline.add_option("output-mesh", outputMesh, "The output mesh")->required()->type_name("OUTPUT_MESH");

ITK_WASM_PARSE(pipeline);

outputMesh.Set(inputMesh.Get());

return EXIT_SUCCESS;
}
Loading

0 comments on commit 085c8d6

Please sign in to comment.