Skip to content

Commit c3d2de3

Browse files
authored
Merge pull request #12 from Ultimaker/NP-1172-wasm-bindings
Np 1172 wasm bindings
2 parents 67ac07e + a8d81f1 commit c3d2de3

File tree

6 files changed

+325
-19
lines changed

6 files changed

+325
-19
lines changed
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: conan-package
1+
name: package
22

33
on:
44
push:
@@ -12,8 +12,9 @@ on:
1212
- 'conandata.yml'
1313
- 'CMakeLists.txt'
1414
- 'requirements.txt'
15-
- '.github/workflows/conan-package.yml'
15+
- '.github/workflows/package.yml'
1616
- '.github/workflows/requirements*'
17+
- 'libnest2d_js/**'
1718
branches:
1819
- main
1920
- 'CURA-*'
@@ -25,4 +26,13 @@ on:
2526
jobs:
2627
conan-package:
2728
uses: ultimaker/cura-workflows/.github/workflows/conan-package.yml@main
29+
with:
30+
platform_wasm: true
2831
secrets: inherit
32+
33+
npm-package:
34+
needs: [ conan-package ]
35+
uses: ultimaker/cura-workflows/.github/workflows/npm-package.yml@main
36+
with:
37+
package_version_full: ${{ needs.conan-package.outputs.package_version_full }}
38+
secrets: inherit

CMakeLists.txt

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.20)
22
cmake_policy(SET CMP0091 NEW)
33
project(libnest2d)
44
find_package(standardprojectsettings REQUIRED)
5+
find_package(spdlog REQUIRED)
56

67
option(BUILD_SHARED_LIBS "Build shared libs instead of static (applies for dependencies as well)" OFF)
78
option(HEADER_ONLY "If enabled static library will not be built." ON)
89
option(ENABLE_TESTING "Build with Google unittest" OFF)
10+
option(WITH_JS_BINDINGS "Build JS/WASM bindings for npm package" OFF)
911
set(GEOMETRIES clipper CACHE STRING "Geometry backend, available options: 'clipper' (default), 'boost'")
1012
set(OPTIMIZER nlopt CACHE STRING "Optimization backend, available options: 'nlopt' (default), 'optimlib'")
1113
set(THREADING std CACHE STRING "Multithreading, available options: 'std' (default), 'tbb', 'omp', 'none'")
@@ -54,7 +56,7 @@ target_compile_definitions(project_options INTERFACE LIBNEST2D_GEOMETRIES_${GEOM
5456

5557
if("${OPTIMIZER}" STREQUAL "nlopt")
5658
find_package(NLopt REQUIRED)
57-
target_link_libraries(project_options INTERFACE NLopt::nlopt)
59+
target_link_libraries(project_options INTERFACE NLopt::nlopt spdlog::spdlog)
5860
list(APPEND nest2d_HDRS
5961
include/libnest2d/optimizers/nlopt/simplex.hpp
6062
include/libnest2d/optimizers/nlopt/subplex.hpp
@@ -89,17 +91,22 @@ endif()
8991
target_compile_definitions(project_options INTERFACE LIBNEST2D_THREADING_${THREADING})
9092

9193
set(libnest2d_SRCS
92-
src/libnest2d.cpp
93-
)
94+
src/libnest2d.cpp
95+
)
96+
97+
if(WITH_JS_BINDINGS OR EMSCRIPTEN)
98+
add_subdirectory(libnest2d_js)
99+
endif()
100+
94101

95102
if(HEADER_ONLY)
96103
add_library(nest2d INTERFACE ${libnest2d_HDRS})
97104
target_link_libraries(nest2d INTERFACE project_options)
98105
target_include_directories(nest2d
99-
INTERFACE
100-
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
101-
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
102-
)
106+
INTERFACE
107+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
108+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
109+
)
103110
else()
104111
if(BUILD_SHARED_LIBS)
105112
add_library(nest2d SHARED ${libnest2d_SRCS} ${libnest2d_HDRS})
@@ -111,12 +118,12 @@ else()
111118
endif()
112119
target_link_libraries(nest2d PUBLIC project_options)
113120
target_include_directories(nest2d
114-
PUBLIC
115-
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
116-
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
117-
PRIVATE
118-
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
119-
)
121+
PUBLIC
122+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
123+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
124+
PRIVATE
125+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
126+
)
120127
endif()
121128

122129
if(ENABLE_TESTING)

conanfile.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,33 @@
1515

1616
class Nest2DConan(ConanFile):
1717
name = "nest2d"
18+
author = "UltiMaker"
19+
url = "https://github.com/Ultimaker/libnest2d"
1820
description = "2D irregular bin packaging and nesting library written in modern C++"
1921
topics = ("conan", "cura", "prusaslicer", "nesting", "c++", "bin packaging")
2022
settings = "os", "compiler", "build_type", "arch"
2123
build_policy = "missing"
2224
package_type = "library"
2325
implements = ["auto_header_only"]
2426

27+
python_requires = "npmpackage/[>=1.0.0]"
2528
options = {
2629
"shared": [True, False],
2730
"fPIC": [True, False],
2831
"header_only": [True, False],
2932
"geometries": ["clipper", "boost"],
3033
"optimizer": ["nlopt", "optimlib"],
31-
"threading": ["std", "tbb", "omp", "none"]
34+
"threading": ["std", "tbb", "omp", "none"],
35+
"with_js_bindings": [True, False]
3236
}
3337
default_options = {
3438
"shared": True,
3539
"fPIC": True,
3640
"header_only": False,
3741
"geometries": "clipper",
3842
"optimizer": "nlopt",
39-
"threading": "std"
43+
"threading": "std",
44+
"with_js_bindings": False
4045
}
4146

4247
def set_version(self):
@@ -67,12 +72,24 @@ def export_sources(self):
6772
copy(self, "*", path.join(self.recipe_folder, "include"), path.join(self.export_sources_folder, "include"))
6873
copy(self, "*", path.join(self.recipe_folder, "tests"), path.join(self.export_sources_folder, "tests"))
6974
copy(self, "*", path.join(self.recipe_folder, "tools"), path.join(self.export_sources_folder, "tools"))
75+
copy(self, "*", path.join(self.recipe_folder, "libnest2d_js"),
76+
os.path.join(self.export_sources_folder, "libnest2d_js"))
7077

7178
def layout(self):
7279
cmake_layout(self)
80+
self.cpp.build.bin = []
81+
self.cpp.build.bindirs = []
82+
self.cpp.package.bindirs = ["bin"]
7383
self.cpp.package.libs = ["nest2d"]
84+
if self.settings.os == "Emscripten":
85+
self.cpp.build.bin = ["libnest2d_js.js"]
86+
self.cpp.package.bin = ["libnest2d_js.js"]
87+
self.cpp.build.bindirs += ["libnest2d_js"]
88+
89+
self.cpp.package.includedirs = ["include"]
7490

7591
def requirements(self):
92+
self.requires("spdlog/[>=1.14.1]", transitive_headers=True)
7693
if self.options.geometries == "clipper":
7794
self.requires("clipper/6.4.2@ultimaker/stable", transitive_headers=True)
7895
if self.options.geometries == "boost" or self.options.geometries == "clipper":
@@ -133,6 +150,7 @@ def generate(self):
133150
tc.variables["GEOMETRIES"] = self.options.geometries
134151
tc.variables["OPTIMIZER"] = self.options.optimizer
135152
tc.variables["THREADING"] = self.options.threading
153+
tc.variables["WITH_JS_BINDINGS"] = self.options.get_safe("with_js_bindings", False)
136154

137155
tc.generate()
138156

@@ -142,7 +160,19 @@ def build(self):
142160
cmake.build()
143161
cmake.install()
144162

163+
def deploy(self):
164+
if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False):
165+
copy(self, "libnest2d_js*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder)
166+
copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder)
167+
145168
def package(self):
169+
170+
if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False):
171+
copy(self, pattern="libnest2d_js*", src=os.path.join(self.build_folder, "libnest2d_js"),
172+
dst=os.path.join(self.package_folder, "bin"))
173+
copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False)
174+
copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False)
175+
copy(self, f"*.wasm", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False)
146176
packager = AutoPackager(self)
147177
packager.run()
148178

@@ -162,3 +192,10 @@ def package_info(self):
162192
self.cpp_info.defines.append(f"LIBNEST2D_THREADING_{self.options.threading}")
163193
if self.settings.os in ["Linux", "FreeBSD", "Macos"] and self.options.threading == "std":
164194
self.cpp_info.system_libs.append("pthread")
195+
196+
# npm package json for Emscripten builds
197+
if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False):
198+
self.python_requires["npmpackage"].module.conf_package_json(self)
199+
# Expose the path to the JS/WASM assets for consumers
200+
js_asset_path = os.path.join(self.package_folder, "bin")
201+
self.conf_info.define("user.nest2d:js_path", js_asset_path)

include/libnest2d/parallel.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#endif
1515

1616
namespace libnest2d { namespace __parallel {
17-
17+
1818
template<class It>
1919
using TIteratorValue = typename std::iterator_traits<It>::value_type;
2020

@@ -56,8 +56,14 @@ inline void enumerate(
5656

5757
for(TN fi = 0; fi < N; ++fi) rets[fi].wait();
5858
#endif
59+
60+
#ifdef __EMSCRIPTEN__
61+
// For WASM/Emscripten builds, always use non-parallel execution
62+
// due to limited threading support in WebAssembly
63+
for(TN n = 0; n < N; n++) fn(*(from + n), n);
64+
#endif
5965
}
6066

6167
}}
6268

63-
#endif //LIBNEST2D_PARALLEL_HPP
69+
#endif //LIBNEST2D_PARALLEL_HPP

libnest2d_js/CMakeLists.txt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
message(STATUS "Building for Emscripten")
2+
cmake_minimum_required(VERSION 3.10)
3+
project(libnest2d_js)
4+
5+
add_executable(libnest2d_js libnest2d_js.cpp)
6+
7+
if (NOT CMAKE_CXX_PLATFORM_ID STREQUAL "emscripten")
8+
use_threads(libnest2d_js)
9+
endif ()
10+
11+
# Emscripten bindings
12+
set_target_properties(libnest2d_js PROPERTIES LINK_FLAGS "--bind")
13+
14+
# Find Clipper library (required for polyclipping/clipper.hpp)
15+
find_package(clipper REQUIRED)
16+
17+
# Find Boost library (required for boost/geometry.hpp)
18+
find_package(Boost REQUIRED)
19+
20+
find_package(NLopt REQUIRED)
21+
22+
# Include directories
23+
target_include_directories(libnest2d_js PRIVATE
24+
${CMAKE_CURRENT_SOURCE_DIR}/../include
25+
${CMAKE_CURRENT_SOURCE_DIR}/../src
26+
${clipper_INCLUDE_DIRS}
27+
${Boost_INCLUDE_DIRS}
28+
)
29+
30+
# Link Boost headers
31+
target_link_libraries(libnest2d_js PRIVATE Boost::headers)
32+
33+
# Link Clipper library
34+
target_link_libraries(libnest2d_js PRIVATE clipper::clipper)
35+
36+
target_link_libraries(libnest2d_js PRIVATE NLopt::nlopt)
37+
38+
target_link_libraries(libnest2d_js PUBLIC spdlog::spdlog)
39+
# Define backend macro for Emscripten
40+
target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_GEOMETRIES_clipper)
41+
target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_OPTIMIZER_nlopt)
42+
43+
# Emscripten-specific options (optional, but recommended)
44+
target_link_options(libnest2d_js
45+
PUBLIC
46+
"SHELL:-s USE_ES6_IMPORT_META=1"
47+
"SHELL:-s FORCE_FILESYSTEM=1"
48+
"SHELL:-s EXPORT_NAME=libnest2d_js"
49+
"SHELL:-s MODULARIZE=1"
50+
"SHELL:-s EXPORT_ES6=1"
51+
"SHELL:-s SINGLE_FILE=1"
52+
"SHELL:-s ALLOW_MEMORY_GROWTH=1"
53+
"SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=0"
54+
"SHELL:--bind"
55+
"SHELL:-l embind"
56+
"SHELL: --emit-tsd libnest2d_js.d.ts"
57+
"SHELL:-sWASM_BIGINT=1"
58+
"SHELL:-sASSERTIONS=1"
59+
60+
)
61+
62+
# If you want to enable debug options, add them conditionally:
63+
# $<$<CONFIG:Debug>:SHELL:-g3>
64+
# $<$<CONFIG:Debug>:SHELL:-gsource-map>

0 commit comments

Comments
 (0)