Skip to content

Commit 314d021

Browse files
committed
Add JS/WASM bindings for libnest2d via Emscripten
NP-1172
1 parent 67ac07e commit 314d021

File tree

4 files changed

+134
-16
lines changed

4 files changed

+134
-16
lines changed

.github/workflows/package.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: package
2+
3+
on:
4+
workflow_dispatch:
5+
repository_dispatch:
6+
types: [build]
7+
push:
8+
paths:
9+
- 'src/**'
10+
- 'include/**'
11+
- 'test_package/**'
12+
- 'conanfile.py'
13+
- 'conandata.yml'
14+
- 'CMakeLists.txt'
15+
- 'tools/**'
16+
- 'tests/**'
17+
- '.github/workflows/package.yml'
18+
branches:
19+
- main
20+
- dev
21+
- 'CURA-*'
22+
- 'PP-*'
23+
- 'NP-*'
24+
- 'GH-*'
25+
- '[0-9].[0-9]*'
26+
- '[0-9].[0-9][0-9]*'
27+
28+
jobs:
29+
conan-package:
30+
uses: ./.github/workflows/conan-package.yml
31+
32+
npm-package:
33+
needs: [ conan-package ]
34+
uses: ultimaker/cura-workflows/.github/workflows/npm-package.yml@main
35+
with:
36+
package_version_full: ${{ needs.conan-package.outputs.package_version_full }}
37+
secrets: inherit

CMakeLists.txt

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ find_package(standardprojectsettings REQUIRED)
66
option(BUILD_SHARED_LIBS "Build shared libs instead of static (applies for dependencies as well)" OFF)
77
option(HEADER_ONLY "If enabled static library will not be built." ON)
88
option(ENABLE_TESTING "Build with Google unittest" OFF)
9+
option(WITH_JS_BINDINGS "Build JS/WASM bindings for npm package" OFF)
910
set(GEOMETRIES clipper CACHE STRING "Geometry backend, available options: 'clipper' (default), 'boost'")
1011
set(OPTIMIZER nlopt CACHE STRING "Optimization backend, available options: 'nlopt' (default), 'optimlib'")
1112
set(THREADING std CACHE STRING "Multithreading, available options: 'std' (default), 'tbb', 'omp', 'none'")
@@ -89,34 +90,56 @@ endif()
8990
target_compile_definitions(project_options INTERFACE LIBNEST2D_THREADING_${THREADING})
9091

9192
set(libnest2d_SRCS
92-
src/libnest2d.cpp
93-
)
93+
src/libnest2d.cpp
94+
)
95+
96+
if(WITH_JS_BINDINGS OR EMSCRIPTEN)
97+
list(APPEND libnest2d_SRCS src/libnest2d_js.cpp)
98+
endif()
99+
94100

95101
if(HEADER_ONLY)
96102
add_library(nest2d INTERFACE ${libnest2d_HDRS})
97103
target_link_libraries(nest2d INTERFACE project_options)
98104
target_include_directories(nest2d
99-
INTERFACE
105+
INTERFACE
106+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
107+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
108+
)
109+
else()
110+
if(WITH_JS_BINDINGS OR EMSCRIPTEN)
111+
add_library(nest2d_js SHARED ${libnest2d_SRCS} ${libnest2d_HDRS})
112+
target_link_libraries(nest2d_js PUBLIC project_options)
113+
target_include_directories(nest2d_js
114+
PUBLIC
100115
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
101116
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
102-
)
103-
else()
104-
if(BUILD_SHARED_LIBS)
105-
add_library(nest2d SHARED ${libnest2d_SRCS} ${libnest2d_HDRS})
106-
if(WIN32)
107-
set_target_properties(nest2d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
117+
PRIVATE
118+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
119+
)
120+
set_target_properties(nest2d_js PROPERTIES OUTPUT_NAME "libnest2d_js")
121+
# Emscripten-specific properties for npm packaging
122+
if(EMSCRIPTEN)
123+
set_target_properties(nest2d_js PROPERTIES SUFFIX ".js")
108124
endif()
109125
else()
110-
add_library(nest2d STATIC ${libnest2d_SRCS} ${libnest2d_HDRS})
111-
endif()
112-
target_link_libraries(nest2d PUBLIC project_options)
113-
target_include_directories(nest2d
126+
if(BUILD_SHARED_LIBS)
127+
add_library(nest2d SHARED ${libnest2d_SRCS} ${libnest2d_HDRS})
128+
if(WIN32)
129+
set_target_properties(nest2d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
130+
endif()
131+
else()
132+
add_library(nest2d STATIC ${libnest2d_SRCS} ${libnest2d_HDRS})
133+
endif()
134+
target_link_libraries(nest2d PUBLIC project_options)
135+
target_include_directories(nest2d
114136
PUBLIC
115137
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
116138
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
117139
PRIVATE
118140
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
119-
)
141+
)
142+
endif()
120143
endif()
121144

122145
if(ENABLE_TESTING)

conanfile.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,24 @@ class Nest2DConan(ConanFile):
2222
package_type = "library"
2323
implements = ["auto_header_only"]
2424

25+
python_requires = "npmpackage/[>=1.0.0]"
2526
options = {
2627
"shared": [True, False],
2728
"fPIC": [True, False],
2829
"header_only": [True, False],
2930
"geometries": ["clipper", "boost"],
3031
"optimizer": ["nlopt", "optimlib"],
31-
"threading": ["std", "tbb", "omp", "none"]
32+
"threading": ["std", "tbb", "omp", "none"],
33+
"with_js_bindings": [True, False]
3234
}
3335
default_options = {
3436
"shared": True,
3537
"fPIC": True,
3638
"header_only": False,
3739
"geometries": "clipper",
3840
"optimizer": "nlopt",
39-
"threading": "std"
41+
"threading": "std",
42+
"with_js_bindings": False
4043
}
4144

4245
def set_version(self):
@@ -133,6 +136,7 @@ def generate(self):
133136
tc.variables["GEOMETRIES"] = self.options.geometries
134137
tc.variables["OPTIMIZER"] = self.options.optimizer
135138
tc.variables["THREADING"] = self.options.threading
139+
tc.variables["WITH_JS_BINDINGS"] = self.options.get_safe("with_js_bindings", False)
136140

137141
tc.generate()
138142

@@ -162,3 +166,7 @@ def package_info(self):
162166
self.cpp_info.defines.append(f"LIBNEST2D_THREADING_{self.options.threading}")
163167
if self.settings.os in ["Linux", "FreeBSD", "Macos"] and self.options.threading == "std":
164168
self.cpp_info.system_libs.append("pthread")
169+
170+
# npm package json for Emscripten builds
171+
if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False):
172+
self.python_requires["npmpackage"].module.conf_package_json(self)

src/libnest2d_js.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Expose libnest2d API to JavaScript via Emscripten
2+
#ifndef LIBNEST2D_JS_H
3+
#define LIBNEST2D_JS_H
4+
5+
#include <libnest2d/libnest2d.hpp>
6+
#include <libnest2d/nester.hpp>
7+
#include <libnest2d/optimizer.hpp>
8+
#include <libnest2d/geometry_traits.hpp>
9+
#include <emscripten/bind.h>
10+
#include <vector>
11+
#include <string>
12+
13+
using namespace libnest2d;
14+
using namespace emscripten;
15+
16+
EMSCRIPTEN_BINDINGS(libnest2d_js) {
17+
// Expose Point type
18+
value_object<Point>("Point")
19+
.field("x", &Point::x)
20+
.field("y", &Point::y);
21+
22+
// Expose Polygon type
23+
value_object<Polygon>("Polygon")
24+
.field("points", &Polygon::points);
25+
26+
// Expose NesterConfig
27+
value_object<NesterConfig>("NesterConfig")
28+
.field("iterations", &NesterConfig::iterations)
29+
.field("time_limit", &NesterConfig::time_limit);
30+
31+
// Expose Nester class
32+
class_<Nester>("Nester")
33+
.constructor<>()
34+
.function("nest", &Nester::nest);
35+
36+
// Expose OptimizerConfig
37+
value_object<OptimizerConfig>("OptimizerConfig")
38+
.field("max_evals", &OptimizerConfig::max_evals)
39+
.field("tolerance", &OptimizerConfig::tolerance);
40+
41+
// Expose Optimizer class
42+
class_<Optimizer>("Optimizer")
43+
.constructor<>()
44+
.function("optimize", &Optimizer::optimize);
45+
46+
// Expose utility functions (example)
47+
function("polygonArea", &polygon_area);
48+
}
49+
50+
#endif // LIBNEST2D_JS_H

0 commit comments

Comments
 (0)