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

Suggestion: WebAssembly build of libavoid? #44

Open
planger opened this issue Feb 11, 2021 · 13 comments
Open

Suggestion: WebAssembly build of libavoid? #44

planger opened this issue Feb 11, 2021 · 13 comments

Comments

@planger
Copy link

planger commented Feb 11, 2021

I'd be very interested in using the libavoid library in a browser-based technology stack. Thus, I was thinking whether it would make sense to have a WebAssembly build of libavoid. Is that something that would be of interest for the adaptagrams community?

Or are there JavaScript-based alternatives to libavoid's object-avoiding routing library?

Thanks in advance!

@jtiai
Copy link

jtiai commented Feb 17, 2021 via email

@planger
Copy link
Author

planger commented Feb 18, 2021

Thanks, yeah I was thinking about emscripten too. Do you happen to still have the concrete steps, if you've already tried it?
I think it'd be very nice to have a dockerfile that builds the wasm for libavoid (or also the other libraries).
I'd be happy to look into providing e.g. the typescript bindings.
Thanks again!

@jtiai
Copy link

jtiai commented Feb 18, 2021 via email

@planger
Copy link
Author

planger commented Feb 19, 2021

Thanks anyway! Here is a docker command that seems to successfully create a wasm from libavoid:

  docker run \
  --rm \
  -v $(pwd):/src -w /src/cola/libavoid \
  -u $(id -u):$(id -g) \
  emscripten/emsdk \
  emcc connectionpin.cpp \
                        connector.cpp \
                        connend.cpp \
                        geometry.cpp \
                        geomtypes.cpp \
                        graph.cpp \
                        junction.cpp \
                        makepath.cpp \
                        obstacle.cpp \
                        orthogonal.cpp \
                        router.cpp \
                        shape.cpp \
                        timer.cpp \
                        vertices.cpp \
                        viscluster.cpp \
                        visibility.cpp \
                        vpsc.cpp \
                        hyperedge.cpp \
                        hyperedgeimprover.cpp \
                        mtst.cpp \
                        hyperedgetree.cpp \
                        scanline.cpp \
                        actioninfo.cpp -I/src/cola/ -fPIC -o libavoid.js  

I'll try to look into creating the bindings in the next few weeks. If anyone is interested in that, please feel free to comment here.

@koahmad
Copy link

koahmad commented Sep 21, 2021

@planger Thanks for the docker command, was very useful. I'm interesting in the bindings, were you able to create them? I'm new to WASM so still getting a hang of things.

@planger
Copy link
Author

planger commented Sep 22, 2021

@Aksem is actually working on providing the bindings and already has a proof of concept for integrating them with https://github.com/eclipse/sprotty. So I hope they will be available soon.

@clemmy
Copy link

clemmy commented Apr 21, 2022

Very interested in this as well for libdialect. The emscripten idea sounds interesting.

@Aksem
Copy link

Aksem commented Apr 21, 2022

@clemmy, WebAssembly build of libavoid is already available : libavoid-js. Without many details, usage guide and similar, but it works(small demo). More is also planned, but I am currently working on improving the algorithm of orthogonal routing in libavoid(my fork).
I would like also to port libdialect, but after finishing the first version of libavoid. If you are interested to do this by yourself, all scripts and steps of how I did it for libavoid are available in the repository, the build is automated, so the build process is transparent. I could also help if you have questions.

@clemmy
Copy link

clemmy commented Apr 22, 2022

@Aksem, that's very exciting to hear! Where would you recommend starting when looking at the libavoid-js repo?

@Aksem
Copy link

Aksem commented Apr 22, 2022

The main source where almost the whole build process is implemented is tools/generate.py script, I would recommend starting with it.

Basically, the build process includes following steps(if the same approach is used as in libavoid-js):

  1. Write WebIDL definitions of the interface that should be available in JavaScript. The result in the case of libavoid-js is in webidl_definitions/libavoid.
  2. Generate C++ and JS glue code using WebIDL Binder. See generate_bindings function.
  3. Compile original C++ and generated glue code with emcc. See compile function. Arguments for emcc is not the easiest theme, I would recommend either starting with minimal set or trying to use the same as for libavoid-js. And start with all possible debug options(you can find them in generate script under debug flag) for emcc and without optimizations, the probability to get working code is higher. In some cases, I had problems with stability and the current command is the best one I found until now.

Another simple tip based on my experience with libavoid-js: don't try to create bindings for the whole API of library. In libavoid-js it's enough to have ~3-4 classes and a few methods in each of them to be able to use its whole power. All other classes are used mostly internally and are useful for example for custom solutions/modifications, which is not the main use case. So creating one example in C++ or analyzing library API in more details is really right way, I realized that later.

Good luck!:)

@vlovich
Copy link

vlovich commented Apr 7, 2023

@Aksem How do I use libavoid-js from node? Am I correct that I have to adjust generate.py and hard-code ENVIRONMENT="node"? Is there some way to have a universal package?

Even when I do that, when I try to await AvoidLib.load() or AvoidLib.getInstance(), it throws:

ReferenceError: self is not defined
    at t (node_modules/libavoid-js/dist/index.js:1:198)
    at Object.<anonymous> (node_modules/libavoid-js/dist/index.js:1:217526)
    at Module._compile (node:internal/modules/cjs/loader:1275:14)
    at Object.F (node_modules/@esbuild-kit/cjs-loader/dist/index.js:1:941)
    at Module.load (node:internal/modules/cjs/loader:1133:32)
    at Module._load (node:internal/modules/cjs/loader:972:12)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:168:29)
    at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
Thrown at:
    at node_modules/libavoid-js/dist/index.js:2:312
    at node_modules/libavoid-js/dist/index.js:3:3
    at Module._compile (node:internal/modules/cjs/loader:1275:14)
    at F (node_modules/@esbuild-kit/cjs-loader/dist/index.js:1:941)
    at Module.load (node:internal/modules/cjs/loader:1133:32)
    at Module._load (node:internal/modules/cjs/loader:972:12)
    at node:internal/modules/esm/translators:168:29
    at run (node:internal/modules/esm/module_job:193:25)

If I hack package.json to use the examples/ variant, then a) the types are not the same as for release b) importing throws an exception:

asynchronously preparing wasm
run() called, but dependencies remain, so not running
node_modules/libavoid-js/examples/libavoid.js:1368
      throw ex;
      ^


TypeError: Failed to parse URL from node_modules/libavoid-js/examples/libavoid.wasm
    at Object.fetch (node:internal/deps/undici/undici:11413:11) {
  [cause]: TypeError [ERR_INVALID_URL]: Invalid URL
      at __node_internal_captureLargerStackTrace (node:internal/errors:490:5)
      at new NodeError (node:internal/errors:399:5)
      at new URL (node:internal/url:588:13)
      at new Request (node:internal/deps/undici/undici:7002:25)
      at fetch2 (node:internal/deps/undici/undici:10554:25)
      at Object.fetch (node:internal/deps/undici/undici:11411:18)
      at fetch (node:internal/process/pre_execution:234:25)
      at instantiateAsync (node_modules/libavoid-js/examples/libavoid.js:2796:14)
      at createWasm (node_modules/libavoid-js/examples/libavoid.js:2835:3)
      at Object.<anonymous> (node_modules/libavoid-js/examples/libavoid.js:3900:11)
      at <anonymous> (path-layout.ts:4:30)
      at ModuleJob.run (node:internal/modules/esm/module_job:193:25) {
    input: 'node_modules/libavoid-js/examples/libavoid.wasm',
    code: 'ERR_INVALID_URL'
  }
}
Thrown at:
    at fetch (node:internal/deps/undici/undici:11413:11)

@Aksem
Copy link

Aksem commented Apr 7, 2023

@vlovich, yes, creating a universal build of the package should be possible. I already have a test version and will finish it over the weekend. I need to do more tests in different environments.

@Aksem
Copy link

Aksem commented Apr 19, 2023

@vlovich , sorry it took longer as expected and was not so straightforward. Anyway new v0.2.0 release is out and supports node.js(Aksem/libavoid-js#5). I would be happy to receive a feedback in libavoid-js repository if you try it.

In case of new issues, please report them in https://github.com/Aksem/libavoid-js repo.

@Aksem How do I use libavoid-js from node? Am I correct that I have to adjust generate.py and hard-code ENVIRONMENT="node"? Is there some way to have a universal package?

Even when I do that, when I try to await AvoidLib.load() or AvoidLib.getInstance(), it throws:

ReferenceError: self is not defined
    at t (node_modules/libavoid-js/dist/index.js:1:198)
    at Object.<anonymous> (node_modules/libavoid-js/dist/index.js:1:217526)
    at Module._compile (node:internal/modules/cjs/loader:1275:14)
    at Object.F (node_modules/@esbuild-kit/cjs-loader/dist/index.js:1:941)
    at Module.load (node:internal/modules/cjs/loader:1133:32)
    at Module._load (node:internal/modules/cjs/loader:972:12)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:168:29)
    at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
Thrown at:
    at node_modules/libavoid-js/dist/index.js:2:312
    at node_modules/libavoid-js/dist/index.js:3:3
    at Module._compile (node:internal/modules/cjs/loader:1275:14)
    at F (node_modules/@esbuild-kit/cjs-loader/dist/index.js:1:941)
    at Module.load (node:internal/modules/cjs/loader:1133:32)
    at Module._load (node:internal/modules/cjs/loader:972:12)
    at node:internal/modules/esm/translators:168:29
    at run (node:internal/modules/esm/module_job:193:25)

If I hack package.json to use the examples/ variant, then a) the types are not the same as for release b) importing throws an exception:

asynchronously preparing wasm
run() called, but dependencies remain, so not running
node_modules/libavoid-js/examples/libavoid.js:1368
      throw ex;
      ^


TypeError: Failed to parse URL from node_modules/libavoid-js/examples/libavoid.wasm
    at Object.fetch (node:internal/deps/undici/undici:11413:11) {
  [cause]: TypeError [ERR_INVALID_URL]: Invalid URL
      at __node_internal_captureLargerStackTrace (node:internal/errors:490:5)
      at new NodeError (node:internal/errors:399:5)
      at new URL (node:internal/url:588:13)
      at new Request (node:internal/deps/undici/undici:7002:25)
      at fetch2 (node:internal/deps/undici/undici:10554:25)
      at Object.fetch (node:internal/deps/undici/undici:11411:18)
      at fetch (node:internal/process/pre_execution:234:25)
      at instantiateAsync (node_modules/libavoid-js/examples/libavoid.js:2796:14)
      at createWasm (node_modules/libavoid-js/examples/libavoid.js:2835:3)
      at Object.<anonymous> (node_modules/libavoid-js/examples/libavoid.js:3900:11)
      at <anonymous> (path-layout.ts:4:30)
      at ModuleJob.run (node:internal/modules/esm/module_job:193:25) {
    input: 'node_modules/libavoid-js/examples/libavoid.wasm',
    code: 'ERR_INVALID_URL'
  }
}
Thrown at:
    at fetch (node:internal/deps/undici/undici:11413:11)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants