From 314d021d371a557f42febcd044847916e7d52436 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 14:43:38 +0200 Subject: [PATCH 01/74] Add JS/WASM bindings for libnest2d via Emscripten NP-1172 --- .github/workflows/package.yml | 37 +++++++++++++++++++++++++ CMakeLists.txt | 51 +++++++++++++++++++++++++---------- conanfile.py | 12 +++++++-- src/libnest2d_js.cpp | 50 ++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/package.yml create mode 100644 src/libnest2d_js.cpp diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 00000000..e4fdc9f7 --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,37 @@ +name: package + +on: + workflow_dispatch: + repository_dispatch: + types: [build] + push: + paths: + - 'src/**' + - 'include/**' + - 'test_package/**' + - 'conanfile.py' + - 'conandata.yml' + - 'CMakeLists.txt' + - 'tools/**' + - 'tests/**' + - '.github/workflows/package.yml' + branches: + - main + - dev + - 'CURA-*' + - 'PP-*' + - 'NP-*' + - 'GH-*' + - '[0-9].[0-9]*' + - '[0-9].[0-9][0-9]*' + +jobs: + conan-package: + uses: ./.github/workflows/conan-package.yml + + npm-package: + needs: [ conan-package ] + uses: ultimaker/cura-workflows/.github/workflows/npm-package.yml@main + with: + package_version_full: ${{ needs.conan-package.outputs.package_version_full }} + secrets: inherit diff --git a/CMakeLists.txt b/CMakeLists.txt index 0baf41ea..f18287da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ find_package(standardprojectsettings REQUIRED) option(BUILD_SHARED_LIBS "Build shared libs instead of static (applies for dependencies as well)" OFF) option(HEADER_ONLY "If enabled static library will not be built." ON) option(ENABLE_TESTING "Build with Google unittest" OFF) +option(WITH_JS_BINDINGS "Build JS/WASM bindings for npm package" OFF) set(GEOMETRIES clipper CACHE STRING "Geometry backend, available options: 'clipper' (default), 'boost'") set(OPTIMIZER nlopt CACHE STRING "Optimization backend, available options: 'nlopt' (default), 'optimlib'") set(THREADING std CACHE STRING "Multithreading, available options: 'std' (default), 'tbb', 'omp', 'none'") @@ -89,34 +90,56 @@ endif() target_compile_definitions(project_options INTERFACE LIBNEST2D_THREADING_${THREADING}) set(libnest2d_SRCS - src/libnest2d.cpp - ) + src/libnest2d.cpp +) + +if(WITH_JS_BINDINGS OR EMSCRIPTEN) + list(APPEND libnest2d_SRCS src/libnest2d_js.cpp) +endif() + if(HEADER_ONLY) add_library(nest2d INTERFACE ${libnest2d_HDRS}) target_link_libraries(nest2d INTERFACE project_options) target_include_directories(nest2d - INTERFACE + INTERFACE + $ + $ + ) +else() + if(WITH_JS_BINDINGS OR EMSCRIPTEN) + add_library(nest2d_js SHARED ${libnest2d_SRCS} ${libnest2d_HDRS}) + target_link_libraries(nest2d_js PUBLIC project_options) + target_include_directories(nest2d_js + PUBLIC $ $ - ) -else() - if(BUILD_SHARED_LIBS) - add_library(nest2d SHARED ${libnest2d_SRCS} ${libnest2d_HDRS}) - if(WIN32) - set_target_properties(nest2d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + PRIVATE + $ + ) + set_target_properties(nest2d_js PROPERTIES OUTPUT_NAME "libnest2d_js") + # Emscripten-specific properties for npm packaging + if(EMSCRIPTEN) + set_target_properties(nest2d_js PROPERTIES SUFFIX ".js") endif() else() - add_library(nest2d STATIC ${libnest2d_SRCS} ${libnest2d_HDRS}) - endif() - target_link_libraries(nest2d PUBLIC project_options) - target_include_directories(nest2d + if(BUILD_SHARED_LIBS) + add_library(nest2d SHARED ${libnest2d_SRCS} ${libnest2d_HDRS}) + if(WIN32) + set_target_properties(nest2d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + endif() + else() + add_library(nest2d STATIC ${libnest2d_SRCS} ${libnest2d_HDRS}) + endif() + target_link_libraries(nest2d PUBLIC project_options) + target_include_directories(nest2d PUBLIC $ $ PRIVATE $ - ) + ) + endif() endif() if(ENABLE_TESTING) diff --git a/conanfile.py b/conanfile.py index 5fa545bf..37a5f6f2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -22,13 +22,15 @@ class Nest2DConan(ConanFile): package_type = "library" implements = ["auto_header_only"] + python_requires = "npmpackage/[>=1.0.0]" options = { "shared": [True, False], "fPIC": [True, False], "header_only": [True, False], "geometries": ["clipper", "boost"], "optimizer": ["nlopt", "optimlib"], - "threading": ["std", "tbb", "omp", "none"] + "threading": ["std", "tbb", "omp", "none"], + "with_js_bindings": [True, False] } default_options = { "shared": True, @@ -36,7 +38,8 @@ class Nest2DConan(ConanFile): "header_only": False, "geometries": "clipper", "optimizer": "nlopt", - "threading": "std" + "threading": "std", + "with_js_bindings": False } def set_version(self): @@ -133,6 +136,7 @@ def generate(self): tc.variables["GEOMETRIES"] = self.options.geometries tc.variables["OPTIMIZER"] = self.options.optimizer tc.variables["THREADING"] = self.options.threading + tc.variables["WITH_JS_BINDINGS"] = self.options.get_safe("with_js_bindings", False) tc.generate() @@ -162,3 +166,7 @@ def package_info(self): self.cpp_info.defines.append(f"LIBNEST2D_THREADING_{self.options.threading}") if self.settings.os in ["Linux", "FreeBSD", "Macos"] and self.options.threading == "std": self.cpp_info.system_libs.append("pthread") + + # npm package json for Emscripten builds + if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): + self.python_requires["npmpackage"].module.conf_package_json(self) diff --git a/src/libnest2d_js.cpp b/src/libnest2d_js.cpp new file mode 100644 index 00000000..0d0452fa --- /dev/null +++ b/src/libnest2d_js.cpp @@ -0,0 +1,50 @@ +// Expose libnest2d API to JavaScript via Emscripten +#ifndef LIBNEST2D_JS_H +#define LIBNEST2D_JS_H + +#include +#include +#include +#include +#include +#include +#include + +using namespace libnest2d; +using namespace emscripten; + +EMSCRIPTEN_BINDINGS(libnest2d_js) { + // Expose Point type + value_object("Point") + .field("x", &Point::x) + .field("y", &Point::y); + + // Expose Polygon type + value_object("Polygon") + .field("points", &Polygon::points); + + // Expose NesterConfig + value_object("NesterConfig") + .field("iterations", &NesterConfig::iterations) + .field("time_limit", &NesterConfig::time_limit); + + // Expose Nester class + class_("Nester") + .constructor<>() + .function("nest", &Nester::nest); + + // Expose OptimizerConfig + value_object("OptimizerConfig") + .field("max_evals", &OptimizerConfig::max_evals) + .field("tolerance", &OptimizerConfig::tolerance); + + // Expose Optimizer class + class_("Optimizer") + .constructor<>() + .function("optimize", &Optimizer::optimize); + + // Expose utility functions (example) + function("polygonArea", &polygon_area); +} + +#endif // LIBNEST2D_JS_H From 84f3e5a83fb6f4032fc96a45f89b089481fdf4b6 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 14:50:28 +0200 Subject: [PATCH 02/74] Update GitHub Actions to use external conan-package workflow and rename package.yml to npm-package.yml --- .github/workflows/{package.yml => npm-package.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{package.yml => npm-package.yml} (87%) diff --git a/.github/workflows/package.yml b/.github/workflows/npm-package.yml similarity index 87% rename from .github/workflows/package.yml rename to .github/workflows/npm-package.yml index e4fdc9f7..0917c414 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/npm-package.yml @@ -14,7 +14,7 @@ on: - 'CMakeLists.txt' - 'tools/**' - 'tests/**' - - '.github/workflows/package.yml' + - 'npm-package.yml' branches: - main - dev @@ -27,7 +27,7 @@ on: jobs: conan-package: - uses: ./.github/workflows/conan-package.yml + uses: ultimaker/libnest2d/.github/workflows/conan-package.yml@main npm-package: needs: [ conan-package ] From c5b57c527b219d0ca1505baa62983bc69aa9f63b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 14:58:42 +0200 Subject: [PATCH 03/74] Remove obsolete npm-package workflow and integrate it into conan-package workflow in GitHub Actions --- .github/workflows/conan-package.yml | 7 ++++++ .github/workflows/npm-package.yml | 37 ----------------------------- 2 files changed, 7 insertions(+), 37 deletions(-) delete mode 100644 .github/workflows/npm-package.yml diff --git a/.github/workflows/conan-package.yml b/.github/workflows/conan-package.yml index 9c621315..afd648c2 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/conan-package.yml @@ -26,3 +26,10 @@ jobs: conan-package: uses: ultimaker/cura-workflows/.github/workflows/conan-package.yml@main secrets: inherit + + npm-package: + needs: [ conan-package ] + uses: ultimaker/cura-workflows/.github/workflows/npm-package.yml@main + with: + package_version_full: ${{ needs.conan-package.outputs.package_version_full }} + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/npm-package.yml b/.github/workflows/npm-package.yml deleted file mode 100644 index 0917c414..00000000 --- a/.github/workflows/npm-package.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: package - -on: - workflow_dispatch: - repository_dispatch: - types: [build] - push: - paths: - - 'src/**' - - 'include/**' - - 'test_package/**' - - 'conanfile.py' - - 'conandata.yml' - - 'CMakeLists.txt' - - 'tools/**' - - 'tests/**' - - 'npm-package.yml' - branches: - - main - - dev - - 'CURA-*' - - 'PP-*' - - 'NP-*' - - 'GH-*' - - '[0-9].[0-9]*' - - '[0-9].[0-9][0-9]*' - -jobs: - conan-package: - uses: ultimaker/libnest2d/.github/workflows/conan-package.yml@main - - npm-package: - needs: [ conan-package ] - uses: ultimaker/cura-workflows/.github/workflows/npm-package.yml@main - with: - package_version_full: ${{ needs.conan-package.outputs.package_version_full }} - secrets: inherit From e137273a31f1aed76570b6f80954da52420b0f65 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 15:09:02 +0200 Subject: [PATCH 04/74] Update JS/WASM bindings to reflect template-based types in libnest2d with ClipperLib and opt:: integration NP-1172 --- src/libnest2d_js.cpp | 59 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/libnest2d_js.cpp b/src/libnest2d_js.cpp index 0d0452fa..5b0bf159 100644 --- a/src/libnest2d_js.cpp +++ b/src/libnest2d_js.cpp @@ -14,37 +14,36 @@ using namespace libnest2d; using namespace emscripten; EMSCRIPTEN_BINDINGS(libnest2d_js) { - // Expose Point type - value_object("Point") - .field("x", &Point::x) - .field("y", &Point::y); - - // Expose Polygon type - value_object("Polygon") - .field("points", &Polygon::points); - - // Expose NesterConfig - value_object("NesterConfig") - .field("iterations", &NesterConfig::iterations) - .field("time_limit", &NesterConfig::time_limit); - - // Expose Nester class - class_("Nester") + // Expose ClipperLib::IntPoint as Point + value_object("Point") + .field("x", &ClipperLib::IntPoint::X) + .field("y", &ClipperLib::IntPoint::Y); + + // Expose ClipperLib::Polygon + value_object("Polygon") + .field("contour", &ClipperLib::Polygon::Contour) + .field("holes", &ClipperLib::Polygon::Holes); + + // Expose NestConfig (template, so use default types) + value_object>("NestConfig") + .field("placer_config", &NestConfig<>::placer_config); + + // Expose _Nester (template, so use default types) + class_<_Nester>("Nester") .constructor<>() - .function("nest", &Nester::nest); - - // Expose OptimizerConfig - value_object("OptimizerConfig") - .field("max_evals", &OptimizerConfig::max_evals) - .field("tolerance", &OptimizerConfig::tolerance); - - // Expose Optimizer class - class_("Optimizer") - .constructor<>() - .function("optimize", &Optimizer::optimize); - - // Expose utility functions (example) - function("polygonArea", &polygon_area); + .function("nest", &_Nester::nest); + + // Expose StopCriteria as OptimizerConfig + value_object("OptimizerConfig") + .field("max_iterations", &opt::StopCriteria::max_iterations) + .field("absolute_score_difference", &opt::StopCriteria::absolute_score_difference) + .field("relative_score_difference", &opt::StopCriteria::relative_score_difference) + .field("stop_score", &opt::StopCriteria::stop_score); + + // Expose Optimizer (template, so use NloptOptimizer) + class_>("Optimizer") + .constructor() + .function("optimize_min", &opt::Optimizer::optimize_min); } #endif // LIBNEST2D_JS_H From f35dc682a179268deafff5c820a032c76d412e83 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 15:12:08 +0200 Subject: [PATCH 05/74] Enable WASM support in conan-package workflow --- .github/workflows/conan-package.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/conan-package.yml b/.github/workflows/conan-package.yml index afd648c2..a6108241 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/conan-package.yml @@ -25,6 +25,8 @@ on: jobs: conan-package: uses: ultimaker/cura-workflows/.github/workflows/conan-package.yml@main + with: + platform_wasm: true secrets: inherit npm-package: From d71374264add8284c2cdf927ad174bf81408cb45 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 15:22:28 +0200 Subject: [PATCH 06/74] Update JS/WASM bindings to expose `nest` function and enhance `optimize_min` with overload resolution in libnest2d --- src/libnest2d_js.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libnest2d_js.cpp b/src/libnest2d_js.cpp index 5b0bf159..4a148848 100644 --- a/src/libnest2d_js.cpp +++ b/src/libnest2d_js.cpp @@ -28,10 +28,17 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { value_object>("NestConfig") .field("placer_config", &NestConfig<>::placer_config); - // Expose _Nester (template, so use default types) - class_<_Nester>("Nester") - .constructor<>() - .function("nest", &_Nester::nest); + // Expose nest function from libnest2d + function("nest", + select_overload::iterator, + std::vector::iterator, + const Box&, + Coord, + const NestConfig&, + NestControl)>( + &nest::iterator> + ) + ); // Expose StopCriteria as OptimizerConfig value_object("OptimizerConfig") @@ -43,7 +50,9 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { // Expose Optimizer (template, so use NloptOptimizer) class_>("Optimizer") .constructor() - .function("optimize_min", &opt::Optimizer::optimize_min); + .function("optimize_min", + select_overload(std::function)>, opt::Input, opt::Bound)>(&opt::Optimizer::optimize_min) + ); } #endif // LIBNEST2D_JS_H From 79594539cbf41166ddd6a4da78610902b1dabedd Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 15:30:45 +0200 Subject: [PATCH 07/74] lambda-based override for `optimize_min` in libnest2d --- src/libnest2d_js.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libnest2d_js.cpp b/src/libnest2d_js.cpp index 4a148848..8f8dc93c 100644 --- a/src/libnest2d_js.cpp +++ b/src/libnest2d_js.cpp @@ -51,7 +51,12 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { class_>("Optimizer") .constructor() .function("optimize_min", - select_overload(std::function)>, opt::Input, opt::Bound)>(&opt::Optimizer::optimize_min) + optional_override([](opt::Optimizer& self, + std::function)> func, + opt::Input init, + opt::Bound bound) { + return self.optimize_min(func, init, bound); + }) ); } From 1a246cd3e192ee8c4752e4069f26a5e37378e40d Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 15:44:26 +0200 Subject: [PATCH 08/74] Define `user.libnest2d:libnest2d` configuration path in Conan package setup --- conanfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conanfile.py b/conanfile.py index 37a5f6f2..90bc1370 100644 --- a/conanfile.py +++ b/conanfile.py @@ -167,6 +167,8 @@ def package_info(self): if self.settings.os in ["Linux", "FreeBSD", "Macos"] and self.options.threading == "std": self.cpp_info.system_libs.append("pthread") + self.conf_info.define_path("user.libnest2d:libnest2d", + os.path.join(self.package_folder, "bin", f"libnest2d")) # npm package json for Emscripten builds if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): self.python_requires["npmpackage"].module.conf_package_json(self) From 8e58a72681b7aee5f6e299aec2478550643af5bd Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 15:58:59 +0200 Subject: [PATCH 09/74] Rename `conan-package.yml` to `package.yml` in GitHub Actions workflow to align with updated naming convention. --- .github/workflows/{conan-package.yml => package.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{conan-package.yml => package.yml} (94%) diff --git a/.github/workflows/conan-package.yml b/.github/workflows/package.yml similarity index 94% rename from .github/workflows/conan-package.yml rename to .github/workflows/package.yml index a6108241..d673d69d 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/package.yml @@ -12,7 +12,7 @@ on: - 'conandata.yml' - 'CMakeLists.txt' - 'requirements.txt' - - '.github/workflows/conan-package.yml' + - 'package.yml' - '.github/workflows/requirements*' branches: - main From 128e788084aea8253e4c6e3a169186445500916b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 17:00:44 +0200 Subject: [PATCH 10/74] Add `deploy` method to copy binaries in Conan package setup and remove unused `libnest2d` config path definition --- conanfile.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/conanfile.py b/conanfile.py index 90bc1370..8936147f 100644 --- a/conanfile.py +++ b/conanfile.py @@ -146,6 +146,10 @@ def build(self): cmake.build() cmake.install() + def deploy(self): + copy(self, "nest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + def package(self): packager = AutoPackager(self) packager.run() @@ -167,8 +171,6 @@ def package_info(self): if self.settings.os in ["Linux", "FreeBSD", "Macos"] and self.options.threading == "std": self.cpp_info.system_libs.append("pthread") - self.conf_info.define_path("user.libnest2d:libnest2d", - os.path.join(self.package_folder, "bin", f"libnest2d")) # npm package json for Emscripten builds if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): self.python_requires["npmpackage"].module.conf_package_json(self) From 04207bb6913e9f9e10173493a0cb3979582bf911 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 11 Sep 2025 17:01:39 +0200 Subject: [PATCH 11/74] Update workflow name from `conan-package` to `package` in GitHub Actions --- .github/workflows/package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index d673d69d..64c54734 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -1,4 +1,4 @@ -name: conan-package +name: package on: push: From ae13717d3bb2857ad08932ddb68982607d94d7b0 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 09:36:29 +0200 Subject: [PATCH 12/74] Update `deploy` method to copy `libnest2d*` binaries instead of `nest2d*` --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 8936147f..4c2acf24 100644 --- a/conanfile.py +++ b/conanfile.py @@ -147,7 +147,7 @@ def build(self): cmake.install() def deploy(self): - copy(self, "nest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + copy(self, "libnest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): From 6280a8c0ce2496fa3d17271ce2e39734ff9251cd Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 09:57:15 +0200 Subject: [PATCH 13/74] Define build and package paths for Emscripten and update includedirs in `conanfile.py`. --- conanfile.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/conanfile.py b/conanfile.py index 4c2acf24..c43bea0a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -73,7 +73,16 @@ def export_sources(self): def layout(self): cmake_layout(self) + self.cpp.build.bin = [] + self.cpp.build.bindirs = [] + self.cpp.package.bindirs = ["bin"] self.cpp.package.libs = ["nest2d"] + if self.settings.os == "Emscripten": + self.cpp.build.bin = ["libnest2d.js"] + self.cpp.package.bin = ["libnest2d.js"] + self.cpp.build.bindirs += ["libnest2d"] + + self.cpp.package.includedirs = ["include"] def requirements(self): if self.options.geometries == "clipper": From 52b1cf4da51dc91dab929906b091e048339c69e8 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 10:13:37 +0200 Subject: [PATCH 14/74] added author --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index c43bea0a..c5f9f3d5 100644 --- a/conanfile.py +++ b/conanfile.py @@ -15,6 +15,7 @@ class Nest2DConan(ConanFile): name = "nest2d" + author = "UltiMaker" description = "2D irregular bin packaging and nesting library written in modern C++" topics = ("conan", "cura", "prusaslicer", "nesting", "c++", "bin packaging") settings = "os", "compiler", "build_type", "arch" From a73024aa58b4ee57d22d4523529b7e5a837dfed4 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 10:26:08 +0200 Subject: [PATCH 15/74] Add `url` field and fix `deploy` method in `conanfile.py`. --- conanfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index c5f9f3d5..abc6a0ba 100644 --- a/conanfile.py +++ b/conanfile.py @@ -16,6 +16,7 @@ class Nest2DConan(ConanFile): name = "nest2d" author = "UltiMaker" + url = "https://github.com/Ultimaker/libnest2d" description = "2D irregular bin packaging and nesting library written in modern C++" topics = ("conan", "cura", "prusaslicer", "nesting", "c++", "bin packaging") settings = "os", "compiler", "build_type", "arch" @@ -157,7 +158,7 @@ def build(self): cmake.install() def deploy(self): - copy(self, "libnest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + copy(self, "liblibnest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): From f5e76272593607c8f0b0fad2196331bcd2e5299d Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 10:35:48 +0200 Subject: [PATCH 16/74] Fix typo in pattern for copying `libnest2d*` binaries in `deploy` method. --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index abc6a0ba..ef25c282 100644 --- a/conanfile.py +++ b/conanfile.py @@ -158,7 +158,7 @@ def build(self): cmake.install() def deploy(self): - copy(self, "liblibnest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + copy(self, "libnest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): From c89bda5c2eb7882dead9a1b0dba874dd267f0930 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 12:47:27 +0200 Subject: [PATCH 17/74] Update `package` method to copy `libnest2d_js` binaries and fix workflow file path in GitHub Actions --- .github/workflows/package.yml | 2 +- conanfile.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 64c54734..bd904e96 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -12,7 +12,7 @@ on: - 'conandata.yml' - 'CMakeLists.txt' - 'requirements.txt' - - 'package.yml' + - '.github/workflows/package.yml' - '.github/workflows/requirements*' branches: - main diff --git a/conanfile.py b/conanfile.py index ef25c282..b06aa6a8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -162,6 +162,8 @@ def deploy(self): copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): + copy(self, pattern="libnest2d*", src=os.path.join(self.build_folder, "libnest2d_js"), + dst=os.path.join(self.package_folder, "bin")) packager = AutoPackager(self) packager.run() From 46ea06ee27bb597173a902e258222724503bcdfe Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 13:30:49 +0200 Subject: [PATCH 18/74] Fix incorrect source path for copying `libnest2d*` binaries in `package` method --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index b06aa6a8..27f19eb5 100644 --- a/conanfile.py +++ b/conanfile.py @@ -162,7 +162,7 @@ def deploy(self): copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): - copy(self, pattern="libnest2d*", src=os.path.join(self.build_folder, "libnest2d_js"), + copy(self, pattern="libnest2d*", src=os.path.join(self.build_folder, "liblibnest2d_js"), dst=os.path.join(self.package_folder, "bin")) packager = AutoPackager(self) packager.run() From 18870ed514daa509538e12d40aaff8f5de80c031 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 13:51:01 +0200 Subject: [PATCH 19/74] Update Emscripten binary naming and adjust `package` method to copy `.js` and `.d.ts` files --- conanfile.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/conanfile.py b/conanfile.py index 27f19eb5..2824b676 100644 --- a/conanfile.py +++ b/conanfile.py @@ -80,9 +80,9 @@ def layout(self): self.cpp.package.bindirs = ["bin"] self.cpp.package.libs = ["nest2d"] if self.settings.os == "Emscripten": - self.cpp.build.bin = ["libnest2d.js"] - self.cpp.package.bin = ["libnest2d.js"] - self.cpp.build.bindirs += ["libnest2d"] + self.cpp.build.bin = ["libnest2d_js.js"] + self.cpp.package.bin = ["libnest2d_js.js"] + self.cpp.build.bindirs += ["libnest2d_js"] self.cpp.package.includedirs = ["include"] @@ -158,12 +158,12 @@ def build(self): cmake.install() def deploy(self): - copy(self, "libnest2d*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + copy(self, "libnest2d_js*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): - copy(self, pattern="libnest2d*", src=os.path.join(self.build_folder, "liblibnest2d_js"), - dst=os.path.join(self.package_folder, "bin")) + copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) + copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) packager = AutoPackager(self) packager.run() From b7524d7549de689eb0576e91eac175b7a23eaee0 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 14:29:05 +0200 Subject: [PATCH 20/74] Add `libnest2d_js` library with Emscripten bindings and update Conan build/package configuration --- conanfile.py | 4 ++++ libnest2d_js/CMakeLists.txt | 17 +++++++++++++++++ {src => libnest2d_js}/libnest2d_js.cpp | 0 3 files changed, 21 insertions(+) create mode 100644 libnest2d_js/CMakeLists.txt rename {src => libnest2d_js}/libnest2d_js.cpp (100%) diff --git a/conanfile.py b/conanfile.py index 2824b676..45182de2 100644 --- a/conanfile.py +++ b/conanfile.py @@ -72,6 +72,8 @@ def export_sources(self): copy(self, "*", path.join(self.recipe_folder, "include"), path.join(self.export_sources_folder, "include")) copy(self, "*", path.join(self.recipe_folder, "tests"), path.join(self.export_sources_folder, "tests")) copy(self, "*", path.join(self.recipe_folder, "tools"), path.join(self.export_sources_folder, "tools")) + copy(self, "*", path.join(self.recipe_folder, "libnest2d_js"), + os.path.join(self.export_sources_folder, "libnest2d_js")) def layout(self): cmake_layout(self) @@ -162,6 +164,8 @@ def deploy(self): copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): + copy(self, pattern="libnest2d_js*", src=os.path.join(self.build_folder, "libnest2d_js"), + dst=os.path.join(self.package_folder, "bin")) copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) packager = AutoPackager(self) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt new file mode 100644 index 00000000..cd239c81 --- /dev/null +++ b/libnest2d_js/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.10) +project(libnest2d_js) + +add_library(libnest2d_js STATIC libnest2d_js.cpp) + +# Emscripten bindings +set_target_properties(libnest2d_js PROPERTIES LINK_FLAGS "--bind") + +# Include directories +target_include_directories(libnest2d_js PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/../src +) + +# Link dependencies if needed +# target_link_libraries(libnest2d_js ) + diff --git a/src/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp similarity index 100% rename from src/libnest2d_js.cpp rename to libnest2d_js/libnest2d_js.cpp From fe6d100a4648cf00a70b3fa58a3d03ad7cedf172 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 14:30:48 +0200 Subject: [PATCH 21/74] Refactor CMake build: replace in-line `libnest2d_js` setup with `add_subdirectory` --- CMakeLists.txt | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f18287da..4697e93e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,7 @@ set(libnest2d_SRCS ) if(WITH_JS_BINDINGS OR EMSCRIPTEN) - list(APPEND libnest2d_SRCS src/libnest2d_js.cpp) + add_subdirectory(libnest2d_js) endif() @@ -107,39 +107,22 @@ if(HEADER_ONLY) $ ) else() - if(WITH_JS_BINDINGS OR EMSCRIPTEN) - add_library(nest2d_js SHARED ${libnest2d_SRCS} ${libnest2d_HDRS}) - target_link_libraries(nest2d_js PUBLIC project_options) - target_include_directories(nest2d_js - PUBLIC - $ - $ - PRIVATE - $ - ) - set_target_properties(nest2d_js PROPERTIES OUTPUT_NAME "libnest2d_js") - # Emscripten-specific properties for npm packaging - if(EMSCRIPTEN) - set_target_properties(nest2d_js PROPERTIES SUFFIX ".js") + if(BUILD_SHARED_LIBS) + add_library(nest2d SHARED ${libnest2d_SRCS} ${libnest2d_HDRS}) + if(WIN32) + set_target_properties(nest2d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) endif() else() - if(BUILD_SHARED_LIBS) - add_library(nest2d SHARED ${libnest2d_SRCS} ${libnest2d_HDRS}) - if(WIN32) - set_target_properties(nest2d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) - endif() - else() - add_library(nest2d STATIC ${libnest2d_SRCS} ${libnest2d_HDRS}) - endif() - target_link_libraries(nest2d PUBLIC project_options) - target_include_directories(nest2d - PUBLIC - $ - $ - PRIVATE - $ - ) + add_library(nest2d STATIC ${libnest2d_SRCS} ${libnest2d_HDRS}) endif() + target_link_libraries(nest2d PUBLIC project_options) + target_include_directories(nest2d + PUBLIC + $ + $ + PRIVATE + $ + ) endif() if(ENABLE_TESTING) From d774e42a4ae5b411fcfaae436553098c54a1adae Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 14:40:07 +0200 Subject: [PATCH 22/74] Add Emscripten-specific build options and define backend macro in `libnest2d_js` CMake setup --- libnest2d_js/CMakeLists.txt | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index cd239c81..4f7aa934 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -1,3 +1,4 @@ +message(STATUS "Building for Emscripten") cmake_minimum_required(VERSION 3.10) project(libnest2d_js) @@ -12,6 +13,22 @@ target_include_directories(libnest2d_js PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src ) -# Link dependencies if needed -# target_link_libraries(libnest2d_js ) +# Define backend macro for Emscripten +target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_GEOMETRIES_clipper) +# Emscripten-specific options (optional, but recommended) +target_link_options(libnest2d_js + PUBLIC + "SHELL:-lembind" + "SHELL:-sEXPORTED_RUNTIME_METHODS=[FS]" + "SHELL:-sENVIRONMENT=web" + "SHELL:-sMODULARIZE=1" + "SHELL:-sEXPORT_ES6=1" + "SHELL:-sWASMFS=1" + "SHELL:-sALLOW_MEMORY_GROWTH=1" + "SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=0" +) + +# If you want to enable debug options, add them conditionally: +# $<$:SHELL:-g3> +# $<$:SHELL:-gsource-map> \ No newline at end of file From 41a7b7a61245ddbe49ffde65c0f2b784d6bef7a2 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 14:41:34 +0200 Subject: [PATCH 23/74] Update GitHub Actions to include `libnest2d_js` in package workflow --- .github/workflows/package.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index bd904e96..cc7898e8 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -14,6 +14,7 @@ on: - 'requirements.txt' - '.github/workflows/package.yml' - '.github/workflows/requirements*' + - 'libnest2d_js/**' branches: - main - 'CURA-*' From 89ecdd05c48dc20afded83a0202acefbeaa0c12b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 14:48:20 +0200 Subject: [PATCH 24/74] Link Clipper library in `libnest2d_js` and update include directories in CMake configuration --- libnest2d_js/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index 4f7aa934..f6312362 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -7,12 +7,19 @@ add_library(libnest2d_js STATIC libnest2d_js.cpp) # Emscripten bindings set_target_properties(libnest2d_js PROPERTIES LINK_FLAGS "--bind") +# Find Clipper library (required for polyclipping/clipper.hpp) +find_package(clipper REQUIRED) + # Include directories target_include_directories(libnest2d_js PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/../src + ${clipper_INCLUDE_DIRS} ) +# Link Clipper library +target_link_libraries(libnest2d_js PRIVATE clipper) + # Define backend macro for Emscripten target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_GEOMETRIES_clipper) From 19c9229837f62726246fc058041f49b23507b094 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 14:54:35 +0200 Subject: [PATCH 25/74] Add Boost library support in CMake configuration for libnest2d_js --- libnest2d_js/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index f6312362..d104c8dd 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -10,13 +10,20 @@ set_target_properties(libnest2d_js PROPERTIES LINK_FLAGS "--bind") # Find Clipper library (required for polyclipping/clipper.hpp) find_package(clipper REQUIRED) +# Find Boost library (required for boost/geometry.hpp) +find_package(Boost REQUIRED) + # Include directories target_include_directories(libnest2d_js PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/../src ${clipper_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ) +# Link Boost headers +target_link_libraries(libnest2d_js PRIVATE Boost::headers) + # Link Clipper library target_link_libraries(libnest2d_js PRIVATE clipper) From 4adbb80e9a486395c721932a15671de20294a8a6 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 15:18:56 +0200 Subject: [PATCH 26/74] Add nlopt library support and update CMake configuration for libnest2d_js --- libnest2d_js/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index d104c8dd..072391c0 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -13,12 +13,16 @@ find_package(clipper REQUIRED) # Find Boost library (required for boost/geometry.hpp) find_package(Boost REQUIRED) +# Find nlopt library (required for NloptOptimizer) +find_package(nlopt REQUIRED) + # Include directories target_include_directories(libnest2d_js PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include ${CMAKE_CURRENT_SOURCE_DIR}/../src ${clipper_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} + ${nlopt_INCLUDE_DIRS} ) # Link Boost headers @@ -29,6 +33,7 @@ target_link_libraries(libnest2d_js PRIVATE clipper) # Define backend macro for Emscripten target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_GEOMETRIES_clipper) +target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_OPTIMIZER_nlopt) # Emscripten-specific options (optional, but recommended) target_link_options(libnest2d_js @@ -45,4 +50,7 @@ target_link_options(libnest2d_js # If you want to enable debug options, add them conditionally: # $<$:SHELL:-g3> -# $<$:SHELL:-gsource-map> \ No newline at end of file +# $<$:SHELL:-gsource-map> + +# Link nlopt library +target_link_libraries(libnest2d_js PRIVATE nlopt) \ No newline at end of file From c87a7850bcb4a11eb230b1be74f91f51d6a7a90d Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 15:26:33 +0200 Subject: [PATCH 27/74] Remove `nlopt` library linkage and adjust include directory in `libnest2d_js` CMake configuration --- libnest2d_js/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index 072391c0..b4751e6b 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -13,8 +13,7 @@ find_package(clipper REQUIRED) # Find Boost library (required for boost/geometry.hpp) find_package(Boost REQUIRED) -# Find nlopt library (required for NloptOptimizer) -find_package(nlopt REQUIRED) + # Include directories target_include_directories(libnest2d_js PRIVATE @@ -22,7 +21,7 @@ target_include_directories(libnest2d_js PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src ${clipper_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} - ${nlopt_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR}/_deps/nlopt-src/include ) # Link Boost headers @@ -52,5 +51,4 @@ target_link_options(libnest2d_js # $<$:SHELL:-g3> # $<$:SHELL:-gsource-map> -# Link nlopt library target_link_libraries(libnest2d_js PRIVATE nlopt) \ No newline at end of file From 61085a95117e899b61ff0843e48f3ef5ffd4c950 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 16:06:47 +0200 Subject: [PATCH 28/74] Update `libnest2d_js` CMake configuration to require NLopt as a dependency and clean up legacy references --- libnest2d_js/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index b4751e6b..b4271ffc 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -13,7 +13,7 @@ find_package(clipper REQUIRED) # Find Boost library (required for boost/geometry.hpp) find_package(Boost REQUIRED) - +find_package(NLopt REQUIRED) # Include directories target_include_directories(libnest2d_js PRIVATE @@ -21,7 +21,6 @@ target_include_directories(libnest2d_js PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src ${clipper_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR}/_deps/nlopt-src/include ) # Link Boost headers @@ -30,10 +29,13 @@ target_link_libraries(libnest2d_js PRIVATE Boost::headers) # Link Clipper library target_link_libraries(libnest2d_js PRIVATE clipper) +target_link_libraries(libnest2d_js PRIVATE NLopt::nlopt) # Define backend macro for Emscripten target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_GEOMETRIES_clipper) target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_OPTIMIZER_nlopt) + + # Emscripten-specific options (optional, but recommended) target_link_options(libnest2d_js PUBLIC @@ -51,4 +53,3 @@ target_link_options(libnest2d_js # $<$:SHELL:-g3> # $<$:SHELL:-gsource-map> -target_link_libraries(libnest2d_js PRIVATE nlopt) \ No newline at end of file From fe0d37a00e32ecb81656912a29c0719c16c66693 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 16:17:59 +0200 Subject: [PATCH 29/74] Change libnest2d_js from a static library to an executable in CMake configuration --- libnest2d_js/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index b4271ffc..38093d28 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -2,7 +2,7 @@ message(STATUS "Building for Emscripten") cmake_minimum_required(VERSION 3.10) project(libnest2d_js) -add_library(libnest2d_js STATIC libnest2d_js.cpp) +add_executable(libnest2d_js libnest2d_js.cpp) # Emscripten bindings set_target_properties(libnest2d_js PROPERTIES LINK_FLAGS "--bind") From 4d2551217205fb537ff329bba0efb52844ea84f6 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 16:29:55 +0200 Subject: [PATCH 30/74] Enhance CMake configuration for libnest2d_js: add thread support for non-Emscripten builds, update Clipper library linkage, and include additional Emscripten-specific options --- libnest2d_js/CMakeLists.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index 38093d28..4a82e004 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -4,6 +4,10 @@ project(libnest2d_js) add_executable(libnest2d_js libnest2d_js.cpp) +if (NOT CMAKE_CXX_PLATFORM_ID STREQUAL "emscripten") + use_threads(libnest2d_js) +endif () + # Emscripten bindings set_target_properties(libnest2d_js PROPERTIES LINK_FLAGS "--bind") @@ -27,15 +31,13 @@ target_include_directories(libnest2d_js PRIVATE target_link_libraries(libnest2d_js PRIVATE Boost::headers) # Link Clipper library -target_link_libraries(libnest2d_js PRIVATE clipper) +target_link_libraries(libnest2d_js PRIVATE clipper::clipper) target_link_libraries(libnest2d_js PRIVATE NLopt::nlopt) # Define backend macro for Emscripten target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_GEOMETRIES_clipper) target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_OPTIMIZER_nlopt) - - # Emscripten-specific options (optional, but recommended) target_link_options(libnest2d_js PUBLIC @@ -46,7 +48,11 @@ target_link_options(libnest2d_js "SHELL:-sEXPORT_ES6=1" "SHELL:-sWASMFS=1" "SHELL:-sALLOW_MEMORY_GROWTH=1" + "SHELL:-sEXPORT_NAME=libnest2d_js" "SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=0" + "SHELL:-lembind" + "SHELL:--closure 1" + "SHELL:--emit-tsd libnest2d_js.d.ts" ) # If you want to enable debug options, add them conditionally: From 0fe45891fe25e0b5ad87bfeefda9b7649636ba4d Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 16:38:30 +0200 Subject: [PATCH 31/74] Expose `IntPointVector` type in libnest2d_js bindings. --- libnest2d_js/libnest2d_js.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 8f8dc93c..412badd6 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -19,6 +19,7 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .field("x", &ClipperLib::IntPoint::X) .field("y", &ClipperLib::IntPoint::Y); + register_vector("IntPointVector"); // Expose ClipperLib::Polygon value_object("Polygon") .field("contour", &ClipperLib::Polygon::Contour) From eaf640758ecc141f97e33875dd16b80d0ce9efdb Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 16:48:56 +0200 Subject: [PATCH 32/74] Expose additional vector types (`IntPointVectorVector`, `PolygonVector`, `PolygonVectorVector`, `ItemVector`, `BoxVector`) in `libnest2d_js` bindings. --- libnest2d_js/libnest2d_js.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 412badd6..04a09c53 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -20,6 +20,11 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .field("y", &ClipperLib::IntPoint::Y); register_vector("IntPointVector"); + register_vector>("IntPointVectorVector"); + register_vector("PolygonVector"); + register_vector>("PolygonVectorVector"); + register_vector("ItemVector"); + register_vector("BoxVector"); // Expose ClipperLib::Polygon value_object("Polygon") .field("contour", &ClipperLib::Polygon::Contour) From 9cb5d5e7ca420de0f5894436f3e922abc9173861 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 16:56:15 +0200 Subject: [PATCH 33/74] Expose `PolygonItem` and `OptionalPolygonItem` types in `libnest2d_js` bindings. --- libnest2d_js/libnest2d_js.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 04a09c53..afa5b482 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -64,6 +64,14 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { return self.optimize_min(func, init, bound); }) ); + + // Bind Item + class_>("PolygonItem") + .constructor() + .function("rawShape", &libnest2d::Item::rawShape); + + // Bind std::optional> + emscripten::optional_override>>("OptionalPolygonItem"); } #endif // LIBNEST2D_JS_H From 48665ea5fa1892d2c1d01d81b802951f80c3a838 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 17:03:07 +0200 Subject: [PATCH 34/74] Simplify `PolygonItem` binding in `libnest2d_js` by replacing explicit template usage with type alias. --- libnest2d_js/libnest2d_js.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index afa5b482..a6da8adf 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -66,12 +66,9 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { ); // Bind Item - class_>("PolygonItem") + class_("PolygonItem") .constructor() - .function("rawShape", &libnest2d::Item::rawShape); - - // Bind std::optional> - emscripten::optional_override>>("OptionalPolygonItem"); + .function("rawShape", &Item::rawShape); } #endif // LIBNEST2D_JS_H From d09d7231f6e765d5a9bf0e370c951f58945c942c Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 12 Sep 2025 17:11:53 +0200 Subject: [PATCH 35/74] Expose `Box` type in `libnest2d_js` bindings. --- libnest2d_js/libnest2d_js.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index a6da8adf..5bc8c96a 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -69,6 +69,12 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { class_("PolygonItem") .constructor() .function("rawShape", &Item::rawShape); + + // Bind Box + class_("Box") + .constructor<>() + .function("minCorner", &Box::minCorner) + .function("maxCorner", &Box::maxCorner); } #endif // LIBNEST2D_JS_H From fd2f888214a8626d54201e2860ca2617f3663458 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 15 Sep 2025 09:48:09 +0200 Subject: [PATCH 36/74] Remove unused type bindings and simplify `libnest2d_js` exports. --- libnest2d_js/libnest2d_js.cpp | 70 ++++++----------------------------- 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 5bc8c96a..762a1cc4 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -14,67 +14,19 @@ using namespace libnest2d; using namespace emscripten; EMSCRIPTEN_BINDINGS(libnest2d_js) { - // Expose ClipperLib::IntPoint as Point - value_object("Point") - .field("x", &ClipperLib::IntPoint::X) - .field("y", &ClipperLib::IntPoint::Y); - register_vector("IntPointVector"); - register_vector>("IntPointVectorVector"); - register_vector("PolygonVector"); - register_vector>("PolygonVectorVector"); - register_vector("ItemVector"); - register_vector("BoxVector"); - // Expose ClipperLib::Polygon - value_object("Polygon") - .field("contour", &ClipperLib::Polygon::Contour) - .field("holes", &ClipperLib::Polygon::Holes); + // Expose nest function from libnest2d + function("nest", + select_overload::iterator, + std::vector::iterator, + const Box&, + Coord, + const NestConfig&, + NestControl)>( + &nest::iterator> + ) + ); - // Expose NestConfig (template, so use default types) - value_object>("NestConfig") - .field("placer_config", &NestConfig<>::placer_config); - - // Expose nest function from libnest2d - function("nest", - select_overload::iterator, - std::vector::iterator, - const Box&, - Coord, - const NestConfig&, - NestControl)>( - &nest::iterator> - ) - ); - - // Expose StopCriteria as OptimizerConfig - value_object("OptimizerConfig") - .field("max_iterations", &opt::StopCriteria::max_iterations) - .field("absolute_score_difference", &opt::StopCriteria::absolute_score_difference) - .field("relative_score_difference", &opt::StopCriteria::relative_score_difference) - .field("stop_score", &opt::StopCriteria::stop_score); - - // Expose Optimizer (template, so use NloptOptimizer) - class_>("Optimizer") - .constructor() - .function("optimize_min", - optional_override([](opt::Optimizer& self, - std::function)> func, - opt::Input init, - opt::Bound bound) { - return self.optimize_min(func, init, bound); - }) - ); - - // Bind Item - class_("PolygonItem") - .constructor() - .function("rawShape", &Item::rawShape); - - // Bind Box - class_("Box") - .constructor<>() - .function("minCorner", &Box::minCorner) - .function("maxCorner", &Box::maxCorner); } #endif // LIBNEST2D_JS_H From a989e195cf347459e026b32a123ad8f9e5ad15b3 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 15 Sep 2025 11:53:48 +0200 Subject: [PATCH 37/74] Enhance `libnest2d_js` bindings: register vector and core types, add `nestItems` wrapper for improved usability. --- libnest2d_js/libnest2d_js.cpp | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 762a1cc4..59d172c2 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -1,4 +1,3 @@ -// Expose libnest2d API to JavaScript via Emscripten #ifndef LIBNEST2D_JS_H #define LIBNEST2D_JS_H @@ -14,19 +13,29 @@ using namespace libnest2d; using namespace emscripten; EMSCRIPTEN_BINDINGS(libnest2d_js) { + // Register vector types for JS + register_vector("ItemVector"); + register_vector("BoxVector"); + register_vector("PackGroupVector"); + register_vector("CoordVector"); - // Expose nest function from libnest2d - function("nest", - select_overload::iterator, - std::vector::iterator, - const Box&, - Coord, - const NestConfig&, - NestControl)>( - &nest::iterator> - ) - ); + // Register main types + emscripten::register_type("Box"); + emscripten::register_type>("NestConfig"); + emscripten::register_type("NestControl"); + emscripten::register_optional(); + emscripten::register_optional>(); + emscripten::register_optional(); + + // Provide a wrapper for nest that takes a vector directly + function("nestItems", optional_override([](std::vector& items, + const Box& bin, + Coord spacing, + const NestConfig& config, + NestControl control) { + return nest(items.begin(), items.end(), bin, spacing, config, control); + })); } -#endif // LIBNEST2D_JS_H +#endif // LIBNEST2D_JS_H \ No newline at end of file From f6f90071f4f421cbc47b1abc1cfded45806128d4 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 10:14:05 +0200 Subject: [PATCH 38/74] Expand `libnest2d_js` bindings add extensive type conversions, new helper functions, extended object bindings, and enhanced CMake configuration with source map generation. NP-1172 --- libnest2d_js/CMakeLists.txt | 1 + libnest2d_js/libnest2d_js.cpp | 265 ++++++++++++++++++++++++++++++---- 2 files changed, 237 insertions(+), 29 deletions(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index 4a82e004..75acbd99 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -53,6 +53,7 @@ target_link_options(libnest2d_js "SHELL:-lembind" "SHELL:--closure 1" "SHELL:--emit-tsd libnest2d_js.d.ts" + "SHELL:-sGENERATE_SOURCE_MAP=1" ) # If you want to enable debug options, add them conditionally: diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 59d172c2..f781162b 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -1,41 +1,248 @@ #ifndef LIBNEST2D_JS_H #define LIBNEST2D_JS_H +//Copyright (c) 2022 Ultimaker B.V. +//libnest2d_js is released under the terms of the LGPLv3 or higher. +// Emscripten Embind bindings for libnest2d #include -#include -#include -#include #include -#include -#include +#include +#include +#include + -using namespace libnest2d; using namespace emscripten; +using namespace libnest2d; +using namespace placers; + +// Declare value types for TypeScript generation +EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); +EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); +EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); + +// Helper function to convert JavaScript arrays to std::vector +std::vector jsArrayToPointVector(const emscripten::val& jsArray) { + std::vector vertices; + unsigned length = jsArray["length"].as(); + vertices.reserve(length); + + for (unsigned i = 0; i < length; i++) { + emscripten::val jsPoint = jsArray[i]; + long x = jsPoint.call("x"); + long y = jsPoint.call("y"); + vertices.emplace_back(x, y); + } + + return vertices; +} + +// Helper function to convert JavaScript arrays to std::vector +std::vector jsArrayToItemPtrVector(const emscripten::val& jsArray) { + std::vector items; + unsigned length = jsArray["length"].as(); + items.reserve(length); + + for (unsigned i = 0; i < length; i++) { + Item* item = jsArray[i].as(); + items.push_back(item); + } + + return items; +} + +// Helper function to convert std::vector to JavaScript array +emscripten::val vectorDoubleToJsArray(const std::vector& vec) { + emscripten::val jsArray = emscripten::val::array(); + for (size_t i = 0; i < vec.size(); ++i) { + jsArray.call("push", vec[i]); + } + return jsArray; +} + +// Helper function to convert JavaScript array to std::vector +std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { + std::vector vec; + unsigned length = jsArray["length"].as(); + vec.reserve(length); + + for (unsigned i = 0; i < length; i++) { + vec.push_back(jsArray[i].as()); + } + + return vec; +} + +// Wrapper function for nest() to handle JavaScript arrays +long nestWrapper(const emscripten::val& jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { + // Convert JavaScript array to std::vector + std::vector itemPtrs = jsArrayToItemPtrVector(jsItems); + + // Convert to std::vector for the nest function + std::vector items; + items.reserve(itemPtrs.size()); + for (Item* item : itemPtrs) { + items.push_back(*item); + } + + // Pre-process distance + if (distance <= 0) { + distance = 1; + } + + // Create nest config + NestConfig<> nestConfig(config); + + // Call the nest function + long result = nest(items, bin, distance, nestConfig); + + // Copy results back to original items + for (size_t i = 0; i < itemPtrs.size(); ++i) { + *(itemPtrs[i]) = items[i]; + } + + return result; +} EMSCRIPTEN_BINDINGS(libnest2d_js) { - // Register vector types for JS - register_vector("ItemVector"); - register_vector("BoxVector"); - register_vector("PackGroupVector"); - register_vector("CoordVector"); - - // Register main types - emscripten::register_type("Box"); - emscripten::register_type>("NestConfig"); - emscripten::register_type("NestControl"); - - emscripten::register_optional(); - emscripten::register_optional>(); - emscripten::register_optional(); - - // Provide a wrapper for nest that takes a vector directly - function("nestItems", optional_override([](std::vector& items, - const Box& bin, - Coord spacing, - const NestConfig& config, - NestControl control) { - return nest(items.begin(), items.end(), bin, spacing, config, control); - })); + // Register TypeScript types for arrays + emscripten::register_type("Point[]"); + emscripten::register_type("Item[]"); + emscripten::register_type("number[]"); + + // Point class + emscripten::value_object("Point") + .field("x", emscripten::optional_override([](const Point& self) { return self.X; }), + emscripten::optional_override([](Point& self, long value) { self.X = value; })) + .field("y", emscripten::optional_override([](const Point& self) { return self.Y; }), + emscripten::optional_override([](Point& self, long value) { self.Y = value; })); + + // Box class + class_("Box") + .constructor() + .constructor() + .constructor() + .constructor() + .class_function("infinite", &Box::infinite) + .function("minCorner", &Box::minCorner) + .function("maxCorner", &Box::maxCorner) + .function("width", &Box::width) + .function("height", &Box::height) + .function("area", &Box::area) + .function("center", &Box::center) + ; + + // Circle class + class_("Circle") + .constructor<>() + .constructor() + .function("center", select_overload(&Circle::center)) + .function("setCenter", select_overload(&Circle::center)) + .function("radius", select_overload(&Circle::radius)) + .function("setRadius", select_overload(&Circle::radius)) + .function("area", &Circle::area) + ; + + // NfpConfig::Alignment enum + enum_("Alignment") + .value("CENTER", NfpConfig::Alignment::CENTER) + .value("BOTTOM_LEFT", NfpConfig::Alignment::BOTTOM_LEFT) + .value("BOTTOM_RIGHT", NfpConfig::Alignment::BOTTOM_RIGHT) + .value("TOP_LEFT", NfpConfig::Alignment::TOP_LEFT) + .value("TOP_RIGHT", NfpConfig::Alignment::TOP_RIGHT) + .value("DONT_ALIGN", NfpConfig::Alignment::DONT_ALIGN) + ; + + // NfpConfig class + emscripten::value_object("NfpConfig") + .field("alignment", &NfpConfig::alignment) + .field("starting_point", &NfpConfig::starting_point) + .field("accuracy", &NfpConfig::accuracy) + .field("explore_holes", &NfpConfig::explore_holes) + .field("parallel", &NfpConfig::parallel) + .field("rotations", emscripten::optional_override( + [](const NfpConfig& self) { return vectorDoubleToJsArray(self.rotations); }), + emscripten::optional_override( + [](NfpConfig& self, const emscripten::val& jsArray) { + self.rotations = jsArrayToVectorDouble(jsArray); + })); + + // BottomLeftConfig class + emscripten::value_object("BottomLeftConfig") + .field("min_obj_distance", &BottomLeftConfig::min_obj_distance) + .field("epsilon", &BottomLeftConfig::epsilon) + .field("allow_rotations", &BottomLeftConfig::allow_rotations) + ; + + // DJDHeuristicConfig class + emscripten::value_object("DJDHeuristicConfig") + .field("try_reverse_order", &DJDHeuristicConfig::try_reverse_order) + .field("try_pairs", &DJDHeuristicConfig::try_pairs) + .field("try_triplets", &DJDHeuristicConfig::try_triplets) + .field("initial_fill_proportion", &DJDHeuristicConfig::initial_fill_proportion) + .field("waste_increment", &DJDHeuristicConfig::waste_increment) + .field("allow_parallel", &DJDHeuristicConfig::allow_parallel) + .field("force_parallel", &DJDHeuristicConfig::force_parallel) + ; + + // Item class + class_("Item") + .constructor<>() + .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) { + std::vector vertices = jsArrayToPointVector(jsVertices); + return new Item(vertices); + }), allow_raw_pointers()) + .function("binId", select_overload(&Item::binId)) + .function("setBinId", select_overload(&Item::binId)) + .function("isFixed", &Item::isFixed) + .function("isDisallowedArea", &Item::isDisallowedArea) + .function("markAsFixedInBin", &Item::markAsFixedInBin) + .function("markAsDisallowedAreaInBin", &Item::markAsDisallowedAreaInBin) + .function("priority", select_overload(&Item::priority)) + .function("setPriority", select_overload(&Item::priority)) + .function("toString", &Item::toString) + .function("vertex", &Item::vertex) + .function("setVertex", &Item::setVertex) + .function("area", &Item::area) + .function("isContourConvex", &Item::isContourConvex) + .function("isHoleConvex", &Item::isHoleConvex) + .function("areHolesConvex", &Item::areHolesConvex) + .function("vertexCount", &Item::vertexCount) + .function("holeCount", &Item::holeCount) + .function("isInside", select_overload(&Item::isInside)) + .function("isInsideItem", select_overload(&Item::isInside)) + .function("isInsideBox", select_overload(&Item::isInside)) + .function("isInsideCircle", select_overload(&Item::isInside)) + .function("boundingBox", &Item::boundingBox) + .function("referenceVertex", &Item::referenceVertex) + .function("rightmostTopVertex", &Item::rightmostTopVertex) + .function("leftmostBottomVertex", &Item::leftmostBottomVertex) + .function("translate", &Item::translate) + .function("translation", &Item::translation) + .function("rotate", &Item::rotate) + .function("rotation", &Item::rotation) + .function("inflation", select_overload(&Item::inflation)) + .function("setInflation", select_overload(&Item::inflation)) + .function("transformedShape", &Item::transformedShape) + .function("resetTransformation", &Item::resetTransformation) + .class_function("intersects", &Item::intersects) + .class_function("touches", &Item::touches) + ; + + // Rectangle class (inherits from Item) + class_>("Rectangle") + .constructor() + .function("width", &Rectangle::width) + .function("height", &Rectangle::height) + // Inherited methods from Item are automatically available + ; + + // register_vector for JavaScript array conversion + register_vector("VectorItem"); + register_vector("VectorPoint"); + register_vector("VectorDouble"); + + // Main nest function + function("nest", &nestWrapper); } #endif // LIBNEST2D_JS_H \ No newline at end of file From 0321362b9c5ab6dd04613ee03aa62396e5795058 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 10:43:00 +0200 Subject: [PATCH 39/74] add type aliases for compatibility adjust function bindings with `return_value_policy`, and improve `Item` construction. --- libnest2d_js/libnest2d_js.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index f781162b..dca34af1 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -10,11 +10,15 @@ #include #include - using namespace emscripten; using namespace libnest2d; using namespace placers; +// Type aliases to match Python bindings +using NfpConfig = NfpPConfig; +using BottomLeftConfig = BLConfig; +using DJDHeuristicConfig = DJDHeuristic::Config; + // Declare value types for TypeScript generation EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); @@ -123,8 +127,8 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .constructor() .constructor() .class_function("infinite", &Box::infinite) - .function("minCorner", &Box::minCorner) - .function("maxCorner", &Box::maxCorner) + .function("minCorner", &Box::minCorner, return_value_policy::reference()) + .function("maxCorner", &Box::maxCorner, return_value_policy::reference()) .function("width", &Box::width) .function("height", &Box::height) .function("area", &Box::area) @@ -135,8 +139,8 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { class_("Circle") .constructor<>() .constructor() - .function("center", select_overload(&Circle::center)) - .function("setCenter", select_overload(&Circle::center)) + .function("center", select_overload(&Circle::center)) + .function("setCenter", select_overload(&Circle::center)) .function("radius", select_overload(&Circle::radius)) .function("setRadius", select_overload(&Circle::radius)) .function("area", &Circle::area) @@ -187,9 +191,12 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { // Item class class_("Item") .constructor<>() + .constructor() .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) { std::vector vertices = jsArrayToPointVector(jsVertices); - return new Item(vertices); + PolygonImpl polygon; + polygon.Contour = vertices; + return new Item(polygon); }), allow_raw_pointers()) .function("binId", select_overload(&Item::binId)) .function("setBinId", select_overload(&Item::binId)) From 045e48cd7ab87771b49b9d45879d851551c6575f Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 10:59:54 +0200 Subject: [PATCH 40/74] remove unused helper, add `Radians` conversions, and improve function const correctness. --- libnest2d_js/libnest2d_js.cpp | 80 +++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index dca34af1..528a2f0d 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -40,20 +40,6 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { return vertices; } -// Helper function to convert JavaScript arrays to std::vector -std::vector jsArrayToItemPtrVector(const emscripten::val& jsArray) { - std::vector items; - unsigned length = jsArray["length"].as(); - items.reserve(length); - - for (unsigned i = 0; i < length; i++) { - Item* item = jsArray[i].as(); - items.push_back(item); - } - - return items; -} - // Helper function to convert std::vector to JavaScript array emscripten::val vectorDoubleToJsArray(const std::vector& vec) { emscripten::val jsArray = emscripten::val::array(); @@ -63,6 +49,15 @@ emscripten::val vectorDoubleToJsArray(const std::vector& vec) { return jsArray; } +// Helper function to convert std::vector to JavaScript array +emscripten::val vectorRadiansToJsArray(const std::vector& vec) { + emscripten::val jsArray = emscripten::val::array(); + for (size_t i = 0; i < vec.size(); ++i) { + jsArray.call("push", static_cast(vec[i])); + } + return jsArray; +} + // Helper function to convert JavaScript array to std::vector std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { std::vector vec; @@ -76,16 +71,29 @@ std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { return vec; } +// Helper function to convert JavaScript array to std::vector +std::vector jsArrayToVectorRadians(const emscripten::val& jsArray) { + std::vector vec; + unsigned length = jsArray["length"].as(); + vec.reserve(length); + + for (unsigned i = 0; i < length; i++) { + vec.push_back(Radians(jsArray[i].as())); + } + + return vec; +} + // Wrapper function for nest() to handle JavaScript arrays long nestWrapper(const emscripten::val& jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { - // Convert JavaScript array to std::vector - std::vector itemPtrs = jsArrayToItemPtrVector(jsItems); - - // Convert to std::vector for the nest function + // Convert JavaScript array to std::vector std::vector items; - items.reserve(itemPtrs.size()); - for (Item* item : itemPtrs) { - items.push_back(*item); + unsigned length = jsItems["length"].as(); + items.reserve(length); + + for (unsigned i = 0; i < length; i++) { + Item item = jsItems[i].as(); + items.push_back(item); } // Pre-process distance @@ -99,9 +107,9 @@ long nestWrapper(const emscripten::val& jsItems, const Box& bin, long distance = // Call the nest function long result = nest(items, bin, distance, nestConfig); - // Copy results back to original items - for (size_t i = 0; i < itemPtrs.size(); ++i) { - *(itemPtrs[i]) = items[i]; + // Copy results back to original JavaScript items + for (size_t i = 0; i < items.size() && i < length; ++i) { + jsItems[i] = items[i]; } return result; @@ -139,9 +147,9 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { class_("Circle") .constructor<>() .constructor() - .function("center", select_overload(&Circle::center)) + .function("center", select_overload(&Circle::center)) .function("setCenter", select_overload(&Circle::center)) - .function("radius", select_overload(&Circle::radius)) + .function("radius", select_overload(&Circle::radius)) .function("setRadius", select_overload(&Circle::radius)) .function("area", &Circle::area) ; @@ -164,10 +172,10 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .field("explore_holes", &NfpConfig::explore_holes) .field("parallel", &NfpConfig::parallel) .field("rotations", emscripten::optional_override( - [](const NfpConfig& self) { return vectorDoubleToJsArray(self.rotations); }), + [](const NfpConfig& self) { return vectorRadiansToJsArray(self.rotations); }), emscripten::optional_override( [](NfpConfig& self, const emscripten::val& jsArray) { - self.rotations = jsArrayToVectorDouble(jsArray); + self.rotations = jsArrayToVectorRadians(jsArray); })); // BottomLeftConfig class @@ -192,19 +200,19 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { class_("Item") .constructor<>() .constructor() - .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) { + .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) -> Item* { std::vector vertices = jsArrayToPointVector(jsVertices); PolygonImpl polygon; polygon.Contour = vertices; return new Item(polygon); }), allow_raw_pointers()) - .function("binId", select_overload(&Item::binId)) + .function("binId", select_overload(&Item::binId)) .function("setBinId", select_overload(&Item::binId)) .function("isFixed", &Item::isFixed) .function("isDisallowedArea", &Item::isDisallowedArea) .function("markAsFixedInBin", &Item::markAsFixedInBin) .function("markAsDisallowedAreaInBin", &Item::markAsDisallowedAreaInBin) - .function("priority", select_overload(&Item::priority)) + .function("priority", select_overload(&Item::priority)) .function("setPriority", select_overload(&Item::priority)) .function("toString", &Item::toString) .function("vertex", &Item::vertex) @@ -215,10 +223,10 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("areHolesConvex", &Item::areHolesConvex) .function("vertexCount", &Item::vertexCount) .function("holeCount", &Item::holeCount) - .function("isInside", select_overload(&Item::isInside)) - .function("isInsideItem", select_overload(&Item::isInside)) - .function("isInsideBox", select_overload(&Item::isInside)) - .function("isInsideCircle", select_overload(&Item::isInside)) + .function("isInside", select_overload(&Item::isInside)) + .function("isInsideItem", select_overload(&Item::isInside)) + .function("isInsideBox", select_overload(&Item::isInside)) + .function("isInsideCircle", select_overload(&Item::isInside)) .function("boundingBox", &Item::boundingBox) .function("referenceVertex", &Item::referenceVertex) .function("rightmostTopVertex", &Item::rightmostTopVertex) @@ -227,7 +235,7 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("translation", &Item::translation) .function("rotate", &Item::rotate) .function("rotation", &Item::rotation) - .function("inflation", select_overload(&Item::inflation)) + .function("inflation", select_overload(&Item::inflation)) .function("setInflation", select_overload(&Item::inflation)) .function("transformedShape", &Item::transformedShape) .function("resetTransformation", &Item::resetTransformation) From f15dd2a0b82f24b723f8513d6329c4da5a468ae7 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 11:09:24 +0200 Subject: [PATCH 41/74] Refine function bindings with `select_overload`, --- libnest2d_js/libnest2d_js.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 528a2f0d..6c07a1d4 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -109,7 +109,7 @@ long nestWrapper(const emscripten::val& jsItems, const Box& bin, long distance = // Copy results back to original JavaScript items for (size_t i = 0; i < items.size() && i < length; ++i) { - jsItems[i] = items[i]; + jsItems.set(i, val(items[i])); } return result; @@ -135,12 +135,12 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .constructor() .constructor() .class_function("infinite", &Box::infinite) - .function("minCorner", &Box::minCorner, return_value_policy::reference()) - .function("maxCorner", &Box::maxCorner, return_value_policy::reference()) - .function("width", &Box::width) - .function("height", &Box::height) - .function("area", &Box::area) - .function("center", &Box::center) + .function("minCorner", select_overload(&Box::minCorner)) + .function("maxCorner", select_overload(&Box::maxCorner)) + .function("width", select_overload(&Box::width)) + .function("height", select_overload(&Box::height)) + .function("area", select_overload(&Box::area)) + .function("center", select_overload(&Box::center)) ; // Circle class @@ -232,11 +232,10 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("rightmostTopVertex", &Item::rightmostTopVertex) .function("leftmostBottomVertex", &Item::leftmostBottomVertex) .function("translate", &Item::translate) - .function("translation", &Item::translation) + .function("translation", select_overload(&Item::translation)) .function("rotate", &Item::rotate) - .function("rotation", &Item::rotation) + .function("rotation", select_overload(&Item::rotation)) .function("inflation", select_overload(&Item::inflation)) - .function("setInflation", select_overload(&Item::inflation)) .function("transformedShape", &Item::transformedShape) .function("resetTransformation", &Item::resetTransformation) .class_function("intersects", &Item::intersects) From a151ef2aef74a3a86d9bd37c10b824c076e6db9e Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 11:22:36 +0200 Subject: [PATCH 42/74] removing `select_overload` where unnecessary. --- libnest2d_js/libnest2d_js.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 6c07a1d4..a731620d 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -85,7 +85,7 @@ std::vector jsArrayToVectorRadians(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -long nestWrapper(const emscripten::val& jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { +long nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { // Convert JavaScript array to std::vector std::vector items; unsigned length = jsItems["length"].as(); @@ -137,9 +137,9 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .class_function("infinite", &Box::infinite) .function("minCorner", select_overload(&Box::minCorner)) .function("maxCorner", select_overload(&Box::maxCorner)) - .function("width", select_overload(&Box::width)) - .function("height", select_overload(&Box::height)) - .function("area", select_overload(&Box::area)) + .function("width", &Box::width) + .function("height", &Box::height) + .function("area", &Box::area) .function("center", select_overload(&Box::center)) ; @@ -232,10 +232,10 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("rightmostTopVertex", &Item::rightmostTopVertex) .function("leftmostBottomVertex", &Item::leftmostBottomVertex) .function("translate", &Item::translate) - .function("translation", select_overload(&Item::translation)) + .function("translation", &Item::translation) .function("rotate", &Item::rotate) - .function("rotation", select_overload(&Item::rotation)) - .function("inflation", select_overload(&Item::inflation)) + .function("rotation", &Item::rotation) + .function("inflation", &Item::inflation) .function("transformedShape", &Item::transformedShape) .function("resetTransformation", &Item::resetTransformation) .class_function("intersects", &Item::intersects) From cb7442ddc0dd8bc3c24e60082dce3d5c7566c295 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 11:37:42 +0200 Subject: [PATCH 43/74] update constructor signature, remove unused transformation-related functions. --- libnest2d_js/libnest2d_js.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index a731620d..2d7729b7 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -199,7 +199,7 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { // Item class class_("Item") .constructor<>() - .constructor() + .constructor() .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) -> Item* { std::vector vertices = jsArrayToPointVector(jsVertices); PolygonImpl polygon; @@ -232,10 +232,7 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("rightmostTopVertex", &Item::rightmostTopVertex) .function("leftmostBottomVertex", &Item::leftmostBottomVertex) .function("translate", &Item::translate) - .function("translation", &Item::translation) .function("rotate", &Item::rotate) - .function("rotation", &Item::rotation) - .function("inflation", &Item::inflation) .function("transformedShape", &Item::transformedShape) .function("resetTransformation", &Item::resetTransformation) .class_function("intersects", &Item::intersects) From 2e4aaa581cc2d6047e6051116929ff34c19bfe7f Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 11:50:23 +0200 Subject: [PATCH 44/74] Add type aliases for core entities in `libnest2d_js`, adjust `Item` bindings. --- libnest2d_js/libnest2d_js.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 2d7729b7..256d43d7 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -15,6 +15,10 @@ using namespace libnest2d; using namespace placers; // Type aliases to match Python bindings +using Point = PointImpl; +using Box = _Box; +using Circle = _Circle; +using Item = _Item; using NfpConfig = NfpPConfig; using BottomLeftConfig = BLConfig; using DJDHeuristicConfig = DJDHeuristic::Config; @@ -198,7 +202,6 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { // Item class class_("Item") - .constructor<>() .constructor() .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) -> Item* { std::vector vertices = jsArrayToPointVector(jsVertices); From 39c9f2bc7c3ecf8d7ae4ee124afbc59776feedce Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 11:58:26 +0200 Subject: [PATCH 45/74] update cmakelist --- libnest2d_js/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index 75acbd99..4a82e004 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -53,7 +53,6 @@ target_link_options(libnest2d_js "SHELL:-lembind" "SHELL:--closure 1" "SHELL:--emit-tsd libnest2d_js.d.ts" - "SHELL:-sGENERATE_SOURCE_MAP=1" ) # If you want to enable debug options, add them conditionally: From 25c883804de474eabe69a6b1f3a5410a914d3a04 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 12:07:48 +0200 Subject: [PATCH 46/74] Remove redundant helper functions and simplify `rotations` field binding in `libnest2d_js`. --- libnest2d_js/libnest2d_js.cpp | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 256d43d7..2b62a5bd 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -53,15 +53,6 @@ emscripten::val vectorDoubleToJsArray(const std::vector& vec) { return jsArray; } -// Helper function to convert std::vector to JavaScript array -emscripten::val vectorRadiansToJsArray(const std::vector& vec) { - emscripten::val jsArray = emscripten::val::array(); - for (size_t i = 0; i < vec.size(); ++i) { - jsArray.call("push", static_cast(vec[i])); - } - return jsArray; -} - // Helper function to convert JavaScript array to std::vector std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { std::vector vec; @@ -75,19 +66,6 @@ std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { return vec; } -// Helper function to convert JavaScript array to std::vector -std::vector jsArrayToVectorRadians(const emscripten::val& jsArray) { - std::vector vec; - unsigned length = jsArray["length"].as(); - vec.reserve(length); - - for (unsigned i = 0; i < length; i++) { - vec.push_back(Radians(jsArray[i].as())); - } - - return vec; -} - // Wrapper function for nest() to handle JavaScript arrays long nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { // Convert JavaScript array to std::vector @@ -175,12 +153,7 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .field("accuracy", &NfpConfig::accuracy) .field("explore_holes", &NfpConfig::explore_holes) .field("parallel", &NfpConfig::parallel) - .field("rotations", emscripten::optional_override( - [](const NfpConfig& self) { return vectorRadiansToJsArray(self.rotations); }), - emscripten::optional_override( - [](NfpConfig& self, const emscripten::val& jsArray) { - self.rotations = jsArrayToVectorRadians(jsArray); - })); + ; // BottomLeftConfig class emscripten::value_object("BottomLeftConfig") From 49703ac9191bfa29a38d67f84e64693dd4415a18 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 12:29:25 +0200 Subject: [PATCH 47/74] Refine `libnest2d_js` bindings: fix getter/setter types, replace property access for `Point`, and remove unused `Item` functions. --- libnest2d_js/libnest2d_js.cpp | 54 +++++++++-------------------------- 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 2b62a5bd..bfde299b 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -1,7 +1,7 @@ #ifndef LIBNEST2D_JS_H #define LIBNEST2D_JS_H //Copyright (c) 2022 Ultimaker B.V. -//libnest2d_js is released under the terms of the LGPLv3 or higher. +//libnest2d_js is released und .function("setY", optional_override([](Point& self, long value) { setY(self, value); }))r the terms of the LGPLv3 or higher. // Emscripten Embind bindings for libnest2d #include @@ -36,8 +36,9 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { for (unsigned i = 0; i < length; i++) { emscripten::val jsPoint = jsArray[i]; - long x = jsPoint.call("x"); - long y = jsPoint.call("y"); + // Use property access instead of method calls for better compatibility + long x = jsPoint["x"].as(); + long y = jsPoint["y"].as(); vertices.emplace_back(x, y); } @@ -103,12 +104,15 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { emscripten::register_type("Item[]"); emscripten::register_type("number[]"); - // Point class - emscripten::value_object("Point") - .field("x", emscripten::optional_override([](const Point& self) { return self.X; }), - emscripten::optional_override([](Point& self, long value) { self.X = value; })) - .field("y", emscripten::optional_override([](const Point& self) { return self.Y; }), - emscripten::optional_override([](Point& self, long value) { self.Y = value; })); + // Point class - fix the getter/setter type issue + class_("Point") + .constructor<>() + .constructor() + .function("x", optional_override([](const Point& self) -> long { return getX(self); })) + .function("y", optional_override([](const Point& self) -> long { return getY(self); })) + .function("setX", optional_override([](Point& self, long value) { setX(self, value); })) + .function("setY", optional_override([](Point& self, long value) { setY(self, value); })) + ; // Box class class_("Box") @@ -184,43 +188,11 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { }), allow_raw_pointers()) .function("binId", select_overload(&Item::binId)) .function("setBinId", select_overload(&Item::binId)) - .function("isFixed", &Item::isFixed) - .function("isDisallowedArea", &Item::isDisallowedArea) - .function("markAsFixedInBin", &Item::markAsFixedInBin) - .function("markAsDisallowedAreaInBin", &Item::markAsDisallowedAreaInBin) - .function("priority", select_overload(&Item::priority)) - .function("setPriority", select_overload(&Item::priority)) - .function("toString", &Item::toString) - .function("vertex", &Item::vertex) - .function("setVertex", &Item::setVertex) .function("area", &Item::area) - .function("isContourConvex", &Item::isContourConvex) - .function("isHoleConvex", &Item::isHoleConvex) - .function("areHolesConvex", &Item::areHolesConvex) .function("vertexCount", &Item::vertexCount) - .function("holeCount", &Item::holeCount) - .function("isInside", select_overload(&Item::isInside)) - .function("isInsideItem", select_overload(&Item::isInside)) - .function("isInsideBox", select_overload(&Item::isInside)) - .function("isInsideCircle", select_overload(&Item::isInside)) .function("boundingBox", &Item::boundingBox) - .function("referenceVertex", &Item::referenceVertex) - .function("rightmostTopVertex", &Item::rightmostTopVertex) - .function("leftmostBottomVertex", &Item::leftmostBottomVertex) .function("translate", &Item::translate) .function("rotate", &Item::rotate) - .function("transformedShape", &Item::transformedShape) - .function("resetTransformation", &Item::resetTransformation) - .class_function("intersects", &Item::intersects) - .class_function("touches", &Item::touches) - ; - - // Rectangle class (inherits from Item) - class_>("Rectangle") - .constructor() - .function("width", &Rectangle::width) - .function("height", &Rectangle::height) - // Inherited methods from Item are automatically available ; // register_vector for JavaScript array conversion From 992315a938659e1e14f8174a941238500b07dd4c Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 13:27:39 +0200 Subject: [PATCH 48/74] Refine `libnest2d_js` bindings: add `Polygon`, `Radians`, and `Degrees` classes, remove unused `Circle` and `DJDHeuristicConfig` bindings, and introduce type aliases for rotation angles. --- libnest2d_js/libnest2d_js.cpp | 48 ++++++++++++++++------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index bfde299b..36c7d3db 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -1,13 +1,14 @@ #ifndef LIBNEST2D_JS_H #define LIBNEST2D_JS_H //Copyright (c) 2022 Ultimaker B.V. -//libnest2d_js is released und .function("setY", optional_override([](Point& self, long value) { setY(self, value); }))r the terms of the LGPLv3 or higher. +//libnest2d_js is released under the terms of the LGPLv3 or higher. // Emscripten Embind bindings for libnest2d #include #include #include #include +#include #include using namespace emscripten; @@ -21,7 +22,11 @@ using Circle = _Circle; using Item = _Item; using NfpConfig = NfpPConfig; using BottomLeftConfig = BLConfig; -using DJDHeuristicConfig = DJDHeuristic::Config; +using Polygon = PolygonImpl; + +// Add aliases for angle types +using Radians = libnest2d::Radians; +using Degrees = libnest2d::Degrees; // Declare value types for TypeScript generation EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); @@ -83,7 +88,7 @@ long nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, con if (distance <= 0) { distance = 1; } - + // Create nest config NestConfig<> nestConfig(config); @@ -129,17 +134,6 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("center", select_overload(&Box::center)) ; - // Circle class - class_("Circle") - .constructor<>() - .constructor() - .function("center", select_overload(&Circle::center)) - .function("setCenter", select_overload(&Circle::center)) - .function("radius", select_overload(&Circle::radius)) - .function("setRadius", select_overload(&Circle::radius)) - .function("area", &Circle::area) - ; - // NfpConfig::Alignment enum enum_("Alignment") .value("CENTER", NfpConfig::Alignment::CENTER) @@ -166,16 +160,6 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .field("allow_rotations", &BottomLeftConfig::allow_rotations) ; - // DJDHeuristicConfig class - emscripten::value_object("DJDHeuristicConfig") - .field("try_reverse_order", &DJDHeuristicConfig::try_reverse_order) - .field("try_pairs", &DJDHeuristicConfig::try_pairs) - .field("try_triplets", &DJDHeuristicConfig::try_triplets) - .field("initial_fill_proportion", &DJDHeuristicConfig::initial_fill_proportion) - .field("waste_increment", &DJDHeuristicConfig::waste_increment) - .field("allow_parallel", &DJDHeuristicConfig::allow_parallel) - .field("force_parallel", &DJDHeuristicConfig::force_parallel) - ; // Item class class_("Item") @@ -192,8 +176,20 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("vertexCount", &Item::vertexCount) .function("boundingBox", &Item::boundingBox) .function("translate", &Item::translate) - .function("rotate", &Item::rotate) - ; + .function("rotate", &Item::rotate); + + // Polygon class for internal type compatibility + class_("Polygon"); + + // Radians class for rotation angles + class_("Radians") + .constructor() + .function("toDegrees", &Radians::toDegrees); + + // Degrees class for rotation angles + class_("Degrees") + .constructor() + .function("toRadians", &Degrees::toRadians); // register_vector for JavaScript array conversion register_vector("VectorItem"); From b68e11dc04399e74e33f6b039e2747b42ee1e393 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 13:48:01 +0200 Subject: [PATCH 49/74] update `nestWrapper` return type to `size_t`, add `Circle` class, enhance `Box` constructors, and improve consistency in function bindings. --- libnest2d_js/libnest2d_js.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 36c7d3db..06915ee4 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -73,7 +73,7 @@ std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -long nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { +size_t nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { // Convert JavaScript array to std::vector std::vector items; unsigned length = jsItems["length"].as(); @@ -93,7 +93,7 @@ long nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, con NestConfig<> nestConfig(config); // Call the nest function - long result = nest(items, bin, distance, nestConfig); + size_t result = nest(items, bin, distance, nestConfig); // Copy results back to original JavaScript items for (size_t i = 0; i < items.size() && i < length; ++i) { @@ -123,9 +123,11 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { class_("Box") .constructor() .constructor() - .constructor() .constructor() .class_function("infinite", &Box::infinite) + .class_function("fromDimensions", optional_override([](long width, long height) -> Box { + return Box(width, height); + })) .function("minCorner", select_overload(&Box::minCorner)) .function("maxCorner", select_overload(&Box::maxCorner)) .function("width", &Box::width) @@ -134,6 +136,17 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("center", select_overload(&Box::center)) ; + // Circle class + class_("Circle") + .constructor<>() + .constructor() + .function("center", select_overload(&Circle::center)) + .function("setCenter", select_overload(&Circle::center)) + .function("radius", select_overload(&Circle::radius)) + .function("setRadius", select_overload(&Circle::radius)) + .function("area", &Circle::area) + ; + // NfpConfig::Alignment enum enum_("Alignment") .value("CENTER", NfpConfig::Alignment::CENTER) From 6136549a6a88654d91947df6dd50685284acd983 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 13:58:24 +0200 Subject: [PATCH 50/74] remove `BottomLeftConfig`, add new `Item` functions (`isFixed`, `isDisallowedArea`, `markAsFixedInBin`, `markAsDisallowedAreaInBin`), and enhance priority handling. --- libnest2d_js/libnest2d_js.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 06915ee4..81a6c7ac 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -21,7 +21,6 @@ using Box = _Box; using Circle = _Circle; using Item = _Item; using NfpConfig = NfpPConfig; -using BottomLeftConfig = BLConfig; using Polygon = PolygonImpl; // Add aliases for angle types @@ -166,13 +165,6 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .field("parallel", &NfpConfig::parallel) ; - // BottomLeftConfig class - emscripten::value_object("BottomLeftConfig") - .field("min_obj_distance", &BottomLeftConfig::min_obj_distance) - .field("epsilon", &BottomLeftConfig::epsilon) - .field("allow_rotations", &BottomLeftConfig::allow_rotations) - ; - // Item class class_("Item") @@ -189,7 +181,13 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("vertexCount", &Item::vertexCount) .function("boundingBox", &Item::boundingBox) .function("translate", &Item::translate) - .function("rotate", &Item::rotate); + .function("rotate", &Item::rotate) + .function("isFixed", &Item::isFixed) + .function("isDisallowedArea", &Item::isDisallowedArea) + .function("markAsFixedInBin", &Item::markAsFixedInBin) + .function("markAsDisallowedAreaInBin", &Item::markAsDisallowedAreaInBin) + .function("priority", select_overload(&Item::priority)) + .function("setPriority", select_overload(&Item::priority)); // Polygon class for internal type compatibility class_("Polygon"); From 9d8a1daadd804cd9f89e4c4adba4a0acb2eb573b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 14:05:28 +0200 Subject: [PATCH 51/74] Format CMakeLists.txt: Adjust indentation and add `-sWASM_BIGINT=1` linker flag. --- libnest2d_js/CMakeLists.txt | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index 4a82e004..b1a7686d 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -21,10 +21,10 @@ find_package(NLopt REQUIRED) # Include directories target_include_directories(libnest2d_js PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../include - ${CMAKE_CURRENT_SOURCE_DIR}/../src - ${clipper_INCLUDE_DIRS} - ${Boost_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/../src + ${clipper_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ) # Link Boost headers @@ -40,19 +40,20 @@ target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_OPTIMIZER_nlopt) # Emscripten-specific options (optional, but recommended) target_link_options(libnest2d_js - PUBLIC - "SHELL:-lembind" - "SHELL:-sEXPORTED_RUNTIME_METHODS=[FS]" - "SHELL:-sENVIRONMENT=web" - "SHELL:-sMODULARIZE=1" - "SHELL:-sEXPORT_ES6=1" - "SHELL:-sWASMFS=1" - "SHELL:-sALLOW_MEMORY_GROWTH=1" - "SHELL:-sEXPORT_NAME=libnest2d_js" - "SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=0" - "SHELL:-lembind" - "SHELL:--closure 1" - "SHELL:--emit-tsd libnest2d_js.d.ts" + PUBLIC + "SHELL:-lembind" + "SHELL:-sEXPORTED_RUNTIME_METHODS=[FS]" + "SHELL:-sENVIRONMENT=web" + "SHELL:-sMODULARIZE=1" + "SHELL:-sEXPORT_ES6=1" + "SHELL:-sWASMFS=1" + "SHELL:-sALLOW_MEMORY_GROWTH=1" + "SHELL:-sEXPORT_NAME=libnest2d_js" + "SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=0" + "SHELL:-lembind" + "SHELL:--closure 1" + "SHELL:--emit-tsd libnest2d_js.d.ts" + "SHELL:-sWASM_BIGINT=1" ) # If you want to enable debug options, add them conditionally: From 226325592a25d31f2653503d029b9ad033cf6411 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 16:28:49 +0200 Subject: [PATCH 52/74] Refine `libnest2d_js` bindings: introduce `ItemArray` type, optimize array handling in `nestWrapper`, and improve code style consistency. --- libnest2d_js/libnest2d_js.cpp | 45 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 81a6c7ac..d0a49516 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -32,12 +32,15 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); +// Define a more specific type for Item arrays +using ItemArray = Item[]; + // Helper function to convert JavaScript arrays to std::vector std::vector jsArrayToPointVector(const emscripten::val& jsArray) { std::vector vertices; unsigned length = jsArray["length"].as(); vertices.reserve(length); - + for (unsigned i = 0; i < length; i++) { emscripten::val jsPoint = jsArray[i]; // Use property access instead of method calls for better compatibility @@ -45,7 +48,7 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { long y = jsPoint["y"].as(); vertices.emplace_back(x, y); } - + return vertices; } @@ -63,26 +66,31 @@ std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { std::vector vec; unsigned length = jsArray["length"].as(); vec.reserve(length); - + for (unsigned i = 0; i < length; i++) { vec.push_back(jsArray[i].as()); } - + return vec; } // Wrapper function for nest() to handle JavaScript arrays -size_t nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { - // Convert JavaScript array to std::vector - std::vector items; - unsigned length = jsItems["length"].as(); - items.reserve(length); - - for (unsigned i = 0; i < length; i++) { - Item item = jsItems[i].as(); - items.push_back(item); - } +size_t nestWrapper(const ItemArray& jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { + // Validate that jsItems is actually an array + // if (!jsItems.isArray()) { + // throw std::invalid_argument("First parameter must be an array of Items"); + // } + // // Convert JavaScript array to std::vector + // std::vector items; + // unsigned length = jsItems["length"].as(); + // items.reserve(length); + + // for (unsigned i = 0; i < length; i++) { + // Item item = jsItems[i]; + // items.push_back(item); + // } + // Pre-process distance if (distance <= 0) { distance = 1; @@ -90,14 +98,9 @@ size_t nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, c // Create nest config NestConfig<> nestConfig(config); - + // Call the nest function - size_t result = nest(items, bin, distance, nestConfig); - - // Copy results back to original JavaScript items - for (size_t i = 0; i < items.size() && i < length; ++i) { - jsItems.set(i, val(items[i])); - } + size_t result = nest(jsItems, bin, distance, nestConfig); return result; } From 623e9407ef329b83d4b8091356518de5c4d22351 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 16:40:48 +0200 Subject: [PATCH 53/74] update `ItemArray` to `emscripten::val`, enhance array validation and conversion in `nestWrapper`, and improve result handling. --- libnest2d_js/libnest2d_js.cpp | 39 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index d0a49516..6d19b5eb 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -32,8 +32,9 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); -// Define a more specific type for Item arrays -using ItemArray = Item[]; +// Define a more specific type for Item arrays (JavaScript Array) +// Represents: Item[] in JavaScript/TypeScript +using ItemArray = emscripten::val; // This will be bound as Item[] in TypeScript // Helper function to convert JavaScript arrays to std::vector std::vector jsArrayToPointVector(const emscripten::val& jsArray) { @@ -75,21 +76,20 @@ std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -size_t nestWrapper(const ItemArray& jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { - // Validate that jsItems is actually an array - // if (!jsItems.isArray()) { - // throw std::invalid_argument("First parameter must be an array of Items"); - // } +size_t nestWrapper(ItemArray& jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { - // // Convert JavaScript array to std::vector - // std::vector items; - // unsigned length = jsItems["length"].as(); - // items.reserve(length); + if (!jsItems.isArray()) { + throw std::invalid_argument("First parameter must be an array of Items"); + } + // Convert JavaScript array to std::vector + std::vector items; + auto length = jsItems["length"].as(); + items.reserve(length); - // for (unsigned i = 0; i < length; i++) { - // Item item = jsItems[i]; - // items.push_back(item); - // } + for (unsigned i = 0; i < length; i++) { + Item item = jsItems[i].as(); + items.push_back(item); + } // Pre-process distance if (distance <= 0) { @@ -97,10 +97,15 @@ size_t nestWrapper(const ItemArray& jsItems, const Box& bin, long distance = 1, } // Create nest config - NestConfig<> nestConfig(config); + const NestConfig<> nestConfig(config); // Call the nest function - size_t result = nest(jsItems, bin, distance, nestConfig); + const size_t result = nest(items, bin, distance, nestConfig); + + // Copy results back to original JavaScript items + for (size_t i = 0; i < items.size() && i < length; ++i) { + jsItems.set(i, val(items[i])); + } return result; } From a0885fb379312bfe55d8ed0215d9dcb9c49d3ba4 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 17:05:14 +0200 Subject: [PATCH 54/74] remove redundant `const` qualifiers in `nestConfig` and `result` declarations for improved consistency. --- libnest2d_js/libnest2d_js.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 6d19b5eb..4d35cf5d 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -97,10 +97,10 @@ size_t nestWrapper(ItemArray& jsItems, const Box& bin, long distance = 1, const } // Create nest config - const NestConfig<> nestConfig(config); + NestConfig<> nestConfig(config); // Call the nest function - const size_t result = nest(items, bin, distance, nestConfig); + size_t result = nest(items, bin, distance, nestConfig); // Copy results back to original JavaScript items for (size_t i = 0; i < items.size() && i < length; ++i) { From 5c9677381a1aa7d36efdd56811c76b1da8f2a6ed Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 16 Sep 2025 17:20:44 +0200 Subject: [PATCH 55/74] remove `ItemArray` type alias, update `nestWrapper` parameter to `emscripten::val`, and simplify array validation and processing. --- libnest2d_js/libnest2d_js.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 4d35cf5d..208ea169 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -32,10 +32,6 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); -// Define a more specific type for Item arrays (JavaScript Array) -// Represents: Item[] in JavaScript/TypeScript -using ItemArray = emscripten::val; // This will be bound as Item[] in TypeScript - // Helper function to convert JavaScript arrays to std::vector std::vector jsArrayToPointVector(const emscripten::val& jsArray) { std::vector vertices; @@ -76,11 +72,8 @@ std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -size_t nestWrapper(ItemArray& jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { - - if (!jsItems.isArray()) { - throw std::invalid_argument("First parameter must be an array of Items"); - } +// Wrapper function for nest() to handle JavaScript arrays +size_t nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { // Convert JavaScript array to std::vector std::vector items; auto length = jsItems["length"].as(); @@ -95,18 +88,17 @@ size_t nestWrapper(ItemArray& jsItems, const Box& bin, long distance = 1, const if (distance <= 0) { distance = 1; } - // Create nest config NestConfig<> nestConfig(config); // Call the nest function size_t result = nest(items, bin, distance, nestConfig); - + // Copy results back to original JavaScript items for (size_t i = 0; i < items.size() && i < length; ++i) { jsItems.set(i, val(items[i])); } - + return result; } From decbdab33c956d4edcba9bb95e3fc3c35c83dd9e Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 17 Sep 2025 09:39:37 +0200 Subject: [PATCH 56/74] Add *.wasm copying to Conan package outputs in `libnest2d_js`. --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 45182de2..f1fded66 100644 --- a/conanfile.py +++ b/conanfile.py @@ -168,6 +168,7 @@ def package(self): dst=os.path.join(self.package_folder, "bin")) copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) + copy(self, f"*.wasm", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) packager = AutoPackager(self) packager.run() From dcf133a304afedde62007959a0d47a3dc0854310 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 17 Sep 2025 10:13:37 +0200 Subject: [PATCH 57/74] Add Emscripten support in Conan package: handle JS/WASM assets and set threading option --- conanfile.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/conanfile.py b/conanfile.py index f1fded66..975cdffa 100644 --- a/conanfile.py +++ b/conanfile.py @@ -121,6 +121,8 @@ def build_requirements(self): def config_options(self): if self.settings.os == "Windows": del self.options.fPIC + if self.settings.os == "Emscripten": + self.options.threading = "std" # Default to a supported option def configure(self): if self.options.shared: @@ -164,14 +166,17 @@ def deploy(self): copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): - copy(self, pattern="libnest2d_js*", src=os.path.join(self.build_folder, "libnest2d_js"), - dst=os.path.join(self.package_folder, "bin")) - copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) - copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) - copy(self, f"*.wasm", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) packager = AutoPackager(self) packager.run() + # Specifically handle the Emscripten assets + if self.settings.os == "Emscripten": + bin_dir = os.path.join(self.package_folder, "bin") + build_dir = self.build_folder + copy(self, "libnest2d_js.js", build_dir, bin_dir) + copy(self, "libnest2d_js.wasm", build_dir, bin_dir) + copy(self, "*.d.ts", build_dir, bin_dir, keep_path=False) + # Remove the header files from options not used in this package if self.options.geometries != "clipper": files.rmdir(self, os.path.join(self.package_folder, "include", "libnest2d", "backends", "clipper")) @@ -192,3 +197,6 @@ def package_info(self): # npm package json for Emscripten builds if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): self.python_requires["npmpackage"].module.conf_package_json(self) + # Expose the path to the JS/WASM assets for consumers + js_asset_path = os.path.join(self.package_folder, "bin") + self.conf_info.define("user.nest2d:js_path", js_asset_path) From 2060f171a1a42c9fdd2d6f8ca8c9b45c45528de8 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 17 Sep 2025 10:22:59 +0200 Subject: [PATCH 58/74] Update Emscripten target link options and clean up Conan configuration --- conanfile.py | 5 ----- libnest2d_js/CMakeLists.txt | 24 +++++++++++------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/conanfile.py b/conanfile.py index 975cdffa..dd939bac 100644 --- a/conanfile.py +++ b/conanfile.py @@ -121,8 +121,6 @@ def build_requirements(self): def config_options(self): if self.settings.os == "Windows": del self.options.fPIC - if self.settings.os == "Emscripten": - self.options.threading = "std" # Default to a supported option def configure(self): if self.options.shared: @@ -197,6 +195,3 @@ def package_info(self): # npm package json for Emscripten builds if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): self.python_requires["npmpackage"].module.conf_package_json(self) - # Expose the path to the JS/WASM assets for consumers - js_asset_path = os.path.join(self.package_folder, "bin") - self.conf_info.define("user.nest2d:js_path", js_asset_path) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index b1a7686d..ca2ab4ca 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -41,22 +41,20 @@ target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_OPTIMIZER_nlopt) # Emscripten-specific options (optional, but recommended) target_link_options(libnest2d_js PUBLIC - "SHELL:-lembind" - "SHELL:-sEXPORTED_RUNTIME_METHODS=[FS]" - "SHELL:-sENVIRONMENT=web" - "SHELL:-sMODULARIZE=1" - "SHELL:-sEXPORT_ES6=1" - "SHELL:-sWASMFS=1" - "SHELL:-sALLOW_MEMORY_GROWTH=1" - "SHELL:-sEXPORT_NAME=libnest2d_js" - "SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=0" - "SHELL:-lembind" - "SHELL:--closure 1" - "SHELL:--emit-tsd libnest2d_js.d.ts" + "SHELL:-s USE_ES6_IMPORT_META=1" + "SHELL:-s FORCE_FILESYSTEM=1" + "SHELL:-s EXPORT_NAME=libnest2d_js" + "SHELL:-s MODULARIZE=1" + "SHELL:-s EXPORT_ES6=1" + "SHELL:-s SINGLE_FILE=1" + "SHELL:-s ALLOW_MEMORY_GROWTH=1" + "SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=0" + "SHELL:--bind" + "SHELL:-l embind" + "SHELL: --embind-emit-tsd libnest2d_js.d.ts" "SHELL:-sWASM_BIGINT=1" ) # If you want to enable debug options, add them conditionally: # $<$:SHELL:-g3> # $<$:SHELL:-gsource-map> - From 18b22d29c55764e038b3c556dc1e3831365731e1 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 17 Sep 2025 10:26:09 +0200 Subject: [PATCH 59/74] Enhance package function to copy JS/WASM assets and define asset path for consumers --- conanfile.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/conanfile.py b/conanfile.py index dd939bac..a36a323c 100644 --- a/conanfile.py +++ b/conanfile.py @@ -164,17 +164,14 @@ def deploy(self): copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): + copy(self, pattern="libnest2d_js*", src=os.path.join(self.build_folder, "libnest2d_js"), + dst=os.path.join(self.package_folder, "bin")) + copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) + copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) + copy(self, f"*.wasm", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) packager = AutoPackager(self) packager.run() - # Specifically handle the Emscripten assets - if self.settings.os == "Emscripten": - bin_dir = os.path.join(self.package_folder, "bin") - build_dir = self.build_folder - copy(self, "libnest2d_js.js", build_dir, bin_dir) - copy(self, "libnest2d_js.wasm", build_dir, bin_dir) - copy(self, "*.d.ts", build_dir, bin_dir, keep_path=False) - # Remove the header files from options not used in this package if self.options.geometries != "clipper": files.rmdir(self, os.path.join(self.package_folder, "include", "libnest2d", "backends", "clipper")) @@ -195,3 +192,6 @@ def package_info(self): # npm package json for Emscripten builds if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): self.python_requires["npmpackage"].module.conf_package_json(self) + # Expose the path to the JS/WASM assets for consumers + js_asset_path = os.path.join(self.package_folder, "bin") + self.conf_info.define("user.nest2d:js_path", js_asset_path) From 2c9a888011d508dd775508b658f11f27eab93f70 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 17 Sep 2025 10:48:19 +0200 Subject: [PATCH 60/74] Refactor nestWrapper to accept ItemList and remove unused JS array conversion functions --- libnest2d_js/libnest2d_js.cpp | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 208ea169..81a3d755 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -49,31 +49,8 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { return vertices; } -// Helper function to convert std::vector to JavaScript array -emscripten::val vectorDoubleToJsArray(const std::vector& vec) { - emscripten::val jsArray = emscripten::val::array(); - for (size_t i = 0; i < vec.size(); ++i) { - jsArray.call("push", vec[i]); - } - return jsArray; -} - -// Helper function to convert JavaScript array to std::vector -std::vector jsArrayToVectorDouble(const emscripten::val& jsArray) { - std::vector vec; - unsigned length = jsArray["length"].as(); - vec.reserve(length); - - for (unsigned i = 0; i < length; i++) { - vec.push_back(jsArray[i].as()); - } - - return vec; -} - -// Wrapper function for nest() to handle JavaScript arrays // Wrapper function for nest() to handle JavaScript arrays -size_t nestWrapper(emscripten::val jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { +size_t nestWrapper(ItemList jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { // Convert JavaScript array to std::vector std::vector items; auto length = jsItems["length"].as(); From 71d0993d0b9fad7b948497a25d049c672a12973c Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 17 Sep 2025 17:31:11 +0200 Subject: [PATCH 61/74] Add helper functions for Point conversion to JS objects and arrays --- libnest2d_js/libnest2d_js.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 81a3d755..b56e4140 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -9,7 +9,6 @@ #include #include #include -#include using namespace emscripten; using namespace libnest2d; @@ -32,6 +31,23 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); +// Helper function to convert a Point to a JavaScript object +// Helper function to convert a Point to a JavaScript object +emscripten::val pointToJSObject(const Point& point) { + emscripten::val obj = emscripten::val::object(); + obj.set("x", getX(point)); + obj.set("y", getY(point)); + return obj; +} + +// Helper function to convert a vector of Points to a JavaScript array +emscripten::val pointVectorToJSArray(const std::vector& points) { + emscripten::val jsArray = emscripten::val::array(); + for (size_t i = 0; i < points.size(); ++i) { + jsArray.set(i, pointToJSObject(points[i])); + } + return jsArray; +} // Helper function to convert JavaScript arrays to std::vector std::vector jsArrayToPointVector(const emscripten::val& jsArray) { std::vector vertices; @@ -69,7 +85,7 @@ size_t nestWrapper(ItemList jsItems, const Box& bin, long distance = 1, const Nf NestConfig<> nestConfig(config); // Call the nest function - size_t result = nest(items, bin, distance, nestConfig); + size_t result = nest(items, bin); // Copy results back to original JavaScript items for (size_t i = 0; i < items.size() && i < length; ++i) { @@ -164,7 +180,11 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("markAsFixedInBin", &Item::markAsFixedInBin) .function("markAsDisallowedAreaInBin", &Item::markAsDisallowedAreaInBin) .function("priority", select_overload(&Item::priority)) - .function("setPriority", select_overload(&Item::priority)); + .function("setPriority", select_overload(&Item::priority)) + .function("transformedShape", optional_override([](const Item& self) { + const auto& poly = self.transformedShape(); + return pointVectorToJSArray(poly.Contour); + })); // Polygon class for internal type compatibility class_("Polygon"); From 83a504a63c6e3fe122a930580b76cb98f954d49b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 17 Sep 2025 17:32:57 +0200 Subject: [PATCH 62/74] Refactor nestWrapper to simplify parameters and remove unused distance handling --- libnest2d_js/libnest2d_js.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index b56e4140..8b21067d 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -66,7 +66,7 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -size_t nestWrapper(ItemList jsItems, const Box& bin, long distance = 1, const NfpConfig& config = NfpConfig()) { +size_t nestWrapper(ItemList jsItems, const Box& bin) { // Convert JavaScript array to std::vector std::vector items; auto length = jsItems["length"].as(); @@ -77,14 +77,6 @@ size_t nestWrapper(ItemList jsItems, const Box& bin, long distance = 1, const Nf items.push_back(item); } - // Pre-process distance - if (distance <= 0) { - distance = 1; - } - // Create nest config - NestConfig<> nestConfig(config); - - // Call the nest function size_t result = nest(items, bin); // Copy results back to original JavaScript items From 41b061661a4ab06391f2799a871cc292e6d5e97f Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 18 Sep 2025 13:41:37 +0200 Subject: [PATCH 63/74] Fix pointToJSObject to cast coordinates to long and add rotation/translation functions for Item --- libnest2d_js/libnest2d_js.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 8b21067d..d88bb012 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -35,8 +35,8 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); // Helper function to convert a Point to a JavaScript object emscripten::val pointToJSObject(const Point& point) { emscripten::val obj = emscripten::val::object(); - obj.set("x", getX(point)); - obj.set("y", getY(point)); + obj.set("x", long{ static_cast(getX(point)) } ); + obj.set("y", long{ static_cast(getY(point)) } ); return obj; } @@ -167,6 +167,8 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("boundingBox", &Item::boundingBox) .function("translate", &Item::translate) .function("rotate", &Item::rotate) + .function("rotation", optional_override([](const Item& self) { return self.rotation(); })) + .function("translation", optional_override([](const Item& self) { return self.translation(); })) .function("isFixed", &Item::isFixed) .function("isDisallowedArea", &Item::isDisallowedArea) .function("markAsFixedInBin", &Item::markAsFixedInBin) From 0fb1807dd12a5ea63367ef46c4563ce8a778391c Mon Sep 17 00:00:00 2001 From: Casper Lamboo Date: Thu, 18 Sep 2025 15:21:32 +0200 Subject: [PATCH 64/74] Make `ItemList` a refference --- libnest2d_js/libnest2d_js.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index d88bb012..d264fa47 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -66,7 +66,7 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -size_t nestWrapper(ItemList jsItems, const Box& bin) { +size_t nestWrapper(ItemList& jsItems, const Box& bin) { // Convert JavaScript array to std::vector std::vector items; auto length = jsItems["length"].as(); From 42256999848bc2cf03ec7a7c7a66ad51832f97ec Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 18 Sep 2025 15:30:34 +0200 Subject: [PATCH 65/74] make the reult of nest as bin and items --- libnest2d_js/libnest2d_js.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index d264fa47..128b260b 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -30,8 +30,8 @@ using Degrees = libnest2d::Degrees; EMSCRIPTEN_DECLARE_VAL_TYPE(PointList); EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); +EMSCRIPTEN_DECLARE_VAL_TYPE(ResultAndItem); -// Helper function to convert a Point to a JavaScript object // Helper function to convert a Point to a JavaScript object emscripten::val pointToJSObject(const Point& point) { emscripten::val obj = emscripten::val::object(); @@ -40,6 +40,12 @@ emscripten::val pointToJSObject(const Point& point) { return obj; } +ResultAndItem resultAndItems(const size_t result, const ItemList& items) { + emscripten::val obj = emscripten::val::object(); + obj.set("result", result); + obj.set("items", items); + return ResultAndItem { obj }; +} // Helper function to convert a vector of Points to a JavaScript array emscripten::val pointVectorToJSArray(const std::vector& points) { emscripten::val jsArray = emscripten::val::array(); @@ -66,7 +72,7 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -size_t nestWrapper(ItemList& jsItems, const Box& bin) { +ResultAndItem nestWrapper(ItemList jsItems, const Box& bin) { // Convert JavaScript array to std::vector std::vector items; auto length = jsItems["length"].as(); @@ -84,7 +90,7 @@ size_t nestWrapper(ItemList& jsItems, const Box& bin) { jsItems.set(i, val(items[i])); } - return result; + return resultAndItems(result, jsItems); } EMSCRIPTEN_BINDINGS(libnest2d_js) { @@ -92,6 +98,7 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { emscripten::register_type("Point[]"); emscripten::register_type("Item[]"); emscripten::register_type("number[]"); + emscripten::register_type("{ result: number, items: Item[] }"); // Point class - fix the getter/setter type issue class_("Point") From 336723f2093796e94f1cb4df64c40a6c2ce94489 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 18 Sep 2025 15:49:51 +0200 Subject: [PATCH 66/74] Add spdlog dependency and update CMake configuration for JS bindings --- CMakeLists.txt | 3 ++- conanfile.py | 1 + libnest2d_js/CMakeLists.txt | 4 +++- libnest2d_js/libnest2d_js.cpp | 7 ++++--- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4697e93e..1ccf7626 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.20) cmake_policy(SET CMP0091 NEW) project(libnest2d) find_package(standardprojectsettings REQUIRED) +find_package(spdlog REQUIRED) option(BUILD_SHARED_LIBS "Build shared libs instead of static (applies for dependencies as well)" OFF) option(HEADER_ONLY "If enabled static library will not be built." ON) @@ -55,7 +56,7 @@ target_compile_definitions(project_options INTERFACE LIBNEST2D_GEOMETRIES_${GEOM if("${OPTIMIZER}" STREQUAL "nlopt") find_package(NLopt REQUIRED) - target_link_libraries(project_options INTERFACE NLopt::nlopt) + target_link_libraries(project_options INTERFACE NLopt::nlopt spdlog::spdlog) list(APPEND nest2d_HDRS include/libnest2d/optimizers/nlopt/simplex.hpp include/libnest2d/optimizers/nlopt/subplex.hpp diff --git a/conanfile.py b/conanfile.py index a36a323c..4b72f7d6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -89,6 +89,7 @@ def layout(self): self.cpp.package.includedirs = ["include"] def requirements(self): + self.requires("spdlog/[>=1.14.1]", transitive_headers=True) if self.options.geometries == "clipper": self.requires("clipper/6.4.2@ultimaker/stable", transitive_headers=True) if self.options.geometries == "boost" or self.options.geometries == "clipper": diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index ca2ab4ca..d7e1ae15 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -34,6 +34,8 @@ target_link_libraries(libnest2d_js PRIVATE Boost::headers) target_link_libraries(libnest2d_js PRIVATE clipper::clipper) target_link_libraries(libnest2d_js PRIVATE NLopt::nlopt) + +target_link_libraries(libnest2d_js PUBLIC spdlog::spdlog) # Define backend macro for Emscripten target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_GEOMETRIES_clipper) target_compile_definitions(libnest2d_js PRIVATE LIBNEST2D_OPTIMIZER_nlopt) @@ -51,7 +53,7 @@ target_link_options(libnest2d_js "SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=0" "SHELL:--bind" "SHELL:-l embind" - "SHELL: --embind-emit-tsd libnest2d_js.d.ts" + "SHELL: --emit-tsd libnest2d_js.d.ts" "SHELL:-sWASM_BIGINT=1" ) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 128b260b..dd99ab24 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -161,12 +161,12 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { // Item class class_("Item") .constructor() - .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) -> Item* { + .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) -> Item { std::vector vertices = jsArrayToPointVector(jsVertices); PolygonImpl polygon; polygon.Contour = vertices; - return new Item(polygon); - }), allow_raw_pointers()) + return Item(polygon); + })) .function("binId", select_overload(&Item::binId)) .function("setBinId", select_overload(&Item::binId)) .function("area", &Item::area) @@ -187,6 +187,7 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { return pointVectorToJSArray(poly.Contour); })); + // Polygon class for internal type compatibility class_("Polygon"); From 5e514f082e44d0f0598ac1483f1ff704beb43777 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 19 Sep 2025 11:56:09 +0200 Subject: [PATCH 67/74] Add non-parallel execution for WASM builds in parallel.hpp NP-1172 --- include/libnest2d/parallel.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/libnest2d/parallel.hpp b/include/libnest2d/parallel.hpp index 2eaf86c0..6f1f59ec 100644 --- a/include/libnest2d/parallel.hpp +++ b/include/libnest2d/parallel.hpp @@ -14,7 +14,7 @@ #endif namespace libnest2d { namespace __parallel { - + template using TIteratorValue = typename std::iterator_traits::value_type; @@ -56,8 +56,14 @@ inline void enumerate( for(TN fi = 0; fi < N; ++fi) rets[fi].wait(); #endif + +#ifdef __EMSCRIPTEN__ + // For WASM/Emscripten builds, always use non-parallel execution + // due to limited threading support in WebAssembly + for(TN n = 0; n < N; n++) fn(*(from + n), n); +#endif } }} -#endif //LIBNEST2D_PARALLEL_HPP +#endif //LIBNEST2D_PARALLEL_HPP \ No newline at end of file From 02f49c495d8cc234d9ad46f83309266d21f30b5d Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 19 Sep 2025 12:22:34 +0200 Subject: [PATCH 68/74] Add tests functions NP-1172 --- libnest2d_js/libnest2d_js.cpp | 44 +++++++++++++++++++++++++++++++---- tests/test.cpp | 36 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index dd99ab24..acee25c5 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -32,6 +32,39 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); EMSCRIPTEN_DECLARE_VAL_TYPE(ResultAndItem); +void testFunction() { + std::vector input; + + auto volume = libnest2d::Box(1000, 1000); + + std::vector items; + + items.emplace_back(libnest2d::Item({ + Point(5, 10), + Point(10, 10), + Point(0, 0) + })); + auto& long_thin_triangle = items.back(); + + items.emplace_back(libnest2d::Item({ + Point(0, 10), + Point(10, 10), + Point(10, 0), + Point(0, 0), + })); + auto& square = items.back(); + + items.emplace_back(libnest2d::Item({ + Point(5, 10), + Point(10, 0), + Point(0, 0) + })); + auto& equilateral_triangle = items.back(); + + auto num_bins = libnest2d::nest(items, volume); + std::cout << "Number of bins used: " << num_bins << std::endl; +} + // Helper function to convert a Point to a JavaScript object emscripten::val pointToJSObject(const Point& point) { emscripten::val obj = emscripten::val::object(); @@ -72,7 +105,7 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -ResultAndItem nestWrapper(ItemList jsItems, const Box& bin) { +ResultAndItem nestWrapper(const ItemList& jsItems, const Box& bin) { // Convert JavaScript array to std::vector std::vector items; auto length = jsItems["length"].as(); @@ -85,12 +118,13 @@ ResultAndItem nestWrapper(ItemList jsItems, const Box& bin) { size_t result = nest(items, bin); + emscripten::val jsItemsResult = emscripten::val::array(); // Copy results back to original JavaScript items for (size_t i = 0; i < items.size() && i < length; ++i) { - jsItems.set(i, val(items[i])); + jsItemsResult.set(i, items[i]); } - return resultAndItems(result, jsItems); + return resultAndItems(result, ItemList(jsItemsResult)); } EMSCRIPTEN_BINDINGS(libnest2d_js) { @@ -208,6 +242,8 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { // Main nest function function("nest", &nestWrapper); + + function("testFunction", &testFunction); } -#endif // LIBNEST2D_JS_H \ No newline at end of file +#endif // LIBNEST2D_JS_H diff --git a/tests/test.cpp b/tests/test.cpp index bbc4af8f..dfafaefa 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1097,3 +1097,39 @@ TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") { REQUIRE(succ); } } + +TEST_CASE("SimpleNesting", "[Nesting]") { + std::vector input; + + auto volume = libnest2d::Box(1000, 1000); + + std::vector items; + + items.emplace_back(libnest2d::Item({ + Point(5, 10), + Point(10, 10), + Point(0, 0) + })); + auto& long_thin_triangle = items.back(); + + items.emplace_back(libnest2d::Item({ + Point(0, 10), + Point(10, 10), + Point(10, 0), + Point(0, 0), + })); + auto& square = items.back(); + + items.emplace_back(libnest2d::Item({ + Point(5, 10), + Point(10, 0), + Point(0, 0) + })); + auto& equilateral_triangle = items.back(); + + auto num_bins = libnest2d::nest(items, volume); + REQUIRE(num_bins == 1u); + REQUIRE(long_thin_triangle.binId() != libnest2d::BIN_ID_UNSET); + REQUIRE(square.binId() != libnest2d::BIN_ID_UNSET); + REQUIRE(equilateral_triangle.binId() != libnest2d::BIN_ID_UNSET); +} From c14627474358095cd0d2debdedfed30d88dd5c4c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 19 Sep 2025 13:49:36 +0200 Subject: [PATCH 69/74] Don't allow rotations when calling the default nest arranger NP-1172 --- libnest2d_js/libnest2d_js.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index acee25c5..5ee813b5 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -116,7 +116,10 @@ ResultAndItem nestWrapper(const ItemList& jsItems, const Box& bin) { items.push_back(item); } - size_t result = nest(items, bin); + NestConfig<> cfg; + cfg.placer_config.rotations = { 0 }; + + size_t result = nest(items, bin, 10, cfg); emscripten::val jsItemsResult = emscripten::val::array(); // Copy results back to original JavaScript items @@ -235,11 +238,6 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .constructor() .function("toRadians", &Degrees::toRadians); - // register_vector for JavaScript array conversion - register_vector("VectorItem"); - register_vector("VectorPoint"); - register_vector("VectorDouble"); - // Main nest function function("nest", &nestWrapper); From 4fc5eda9a8e92f7496cbc8a6466441f605b7fe85 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 19 Sep 2025 14:17:39 +0200 Subject: [PATCH 70/74] Update nestWrapper to include scale factor and adjust test cases for long integers --- libnest2d_js/libnest2d_js.cpp | 4 ++-- tests/test.cpp | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 5ee813b5..6743159b 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -105,7 +105,7 @@ std::vector jsArrayToPointVector(const emscripten::val& jsArray) { } // Wrapper function for nest() to handle JavaScript arrays -ResultAndItem nestWrapper(const ItemList& jsItems, const Box& bin) { +ResultAndItem nestWrapper(const ItemList& jsItems, const Box& bin, double scaleFactor) { // Convert JavaScript array to std::vector std::vector items; auto length = jsItems["length"].as(); @@ -119,7 +119,7 @@ ResultAndItem nestWrapper(const ItemList& jsItems, const Box& bin) { NestConfig<> cfg; cfg.placer_config.rotations = { 0 }; - size_t result = nest(items, bin, 10, cfg); + size_t result = nest(items, bin, scaleFactor * 5, cfg); emscripten::val jsItemsResult = emscripten::val::array(); // Copy results back to original JavaScript items diff --git a/tests/test.cpp b/tests/test.cpp index dfafaefa..be6de926 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1098,32 +1098,32 @@ TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") { } } -TEST_CASE("SimpleNesting", "[Nesting]") { +TEST_CASE("SimpleNesting", "[Geometry]") { std::vector input; - auto volume = libnest2d::Box(1000, 1000); + auto volume = libnest2d::Box(1000l, 1000l); std::vector items; items.emplace_back(libnest2d::Item({ - Point(5, 10), - Point(10, 10), - Point(0, 0) + Point(5l, 10l), + Point(10l, 10l), + Point(0l, 0l) })); auto& long_thin_triangle = items.back(); items.emplace_back(libnest2d::Item({ - Point(0, 10), - Point(10, 10), - Point(10, 0), - Point(0, 0), + Point(0l, 10l), + Point(10l, 10l), + Point(10l, 0l), + Point(0l, 0l), })); auto& square = items.back(); items.emplace_back(libnest2d::Item({ - Point(5, 10), - Point(10, 0), - Point(0, 0) + Point(5l, 10l), + Point(10l, 0l), + Point(0l, 0l) })); auto& equilateral_triangle = items.back(); From 2eb5d9dd2952e7dd799ab9c57479ba05c9e4d70f Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 23 Sep 2025 11:32:56 +0200 Subject: [PATCH 71/74] Enable assertions in Emscripten build for debugging (CMake configuration). NP-1172 --- libnest2d_js/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libnest2d_js/CMakeLists.txt b/libnest2d_js/CMakeLists.txt index d7e1ae15..b5bf699e 100644 --- a/libnest2d_js/CMakeLists.txt +++ b/libnest2d_js/CMakeLists.txt @@ -55,6 +55,8 @@ target_link_options(libnest2d_js "SHELL:-l embind" "SHELL: --emit-tsd libnest2d_js.d.ts" "SHELL:-sWASM_BIGINT=1" + "SHELL:-sASSERTIONS=1" + ) # If you want to enable debug options, add them conditionally: From fe819ee66bebd638632448681ba1a20d675253aa Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 25 Sep 2025 12:10:43 +0200 Subject: [PATCH 72/74] Conditionally copy JS/WASM artifacts and assets only when working with Emscripten or JS bindings. Remove unused test functions and legacy code from `libnest2d_js` and tests. NP-1172 --- conanfile.py | 17 ++++++++++------- libnest2d_js/libnest2d_js.cpp | 34 --------------------------------- tests/test.cpp | 36 ----------------------------------- 3 files changed, 10 insertions(+), 77 deletions(-) diff --git a/conanfile.py b/conanfile.py index 4b72f7d6..bebf606c 100644 --- a/conanfile.py +++ b/conanfile.py @@ -161,15 +161,18 @@ def build(self): cmake.install() def deploy(self): - copy(self, "libnest2d_js*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) - copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): + copy(self, "libnest2d_js*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) + copy(self, "*", src=os.path.join(self.package_folder, "bin"), dst=self.install_folder) def package(self): - copy(self, pattern="libnest2d_js*", src=os.path.join(self.build_folder, "libnest2d_js"), - dst=os.path.join(self.package_folder, "bin")) - copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) - copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) - copy(self, f"*.wasm", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) + + if self.settings.os == "Emscripten" or self.options.get_safe("with_js_bindings", False): + copy(self, pattern="libnest2d_js*", src=os.path.join(self.build_folder, "libnest2d_js"), + dst=os.path.join(self.package_folder, "bin")) + copy(self, f"*.d.ts", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) + copy(self, f"*.js", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) + copy(self, f"*.wasm", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path = False) packager = AutoPackager(self) packager.run() diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 6743159b..4a96353a 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -32,38 +32,6 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); EMSCRIPTEN_DECLARE_VAL_TYPE(ResultAndItem); -void testFunction() { - std::vector input; - - auto volume = libnest2d::Box(1000, 1000); - - std::vector items; - - items.emplace_back(libnest2d::Item({ - Point(5, 10), - Point(10, 10), - Point(0, 0) - })); - auto& long_thin_triangle = items.back(); - - items.emplace_back(libnest2d::Item({ - Point(0, 10), - Point(10, 10), - Point(10, 0), - Point(0, 0), - })); - auto& square = items.back(); - - items.emplace_back(libnest2d::Item({ - Point(5, 10), - Point(10, 0), - Point(0, 0) - })); - auto& equilateral_triangle = items.back(); - - auto num_bins = libnest2d::nest(items, volume); - std::cout << "Number of bins used: " << num_bins << std::endl; -} // Helper function to convert a Point to a JavaScript object emscripten::val pointToJSObject(const Point& point) { @@ -240,8 +208,6 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { // Main nest function function("nest", &nestWrapper); - - function("testFunction", &testFunction); } #endif // LIBNEST2D_JS_H diff --git a/tests/test.cpp b/tests/test.cpp index be6de926..bbc4af8f 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1097,39 +1097,3 @@ TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") { REQUIRE(succ); } } - -TEST_CASE("SimpleNesting", "[Geometry]") { - std::vector input; - - auto volume = libnest2d::Box(1000l, 1000l); - - std::vector items; - - items.emplace_back(libnest2d::Item({ - Point(5l, 10l), - Point(10l, 10l), - Point(0l, 0l) - })); - auto& long_thin_triangle = items.back(); - - items.emplace_back(libnest2d::Item({ - Point(0l, 10l), - Point(10l, 10l), - Point(10l, 0l), - Point(0l, 0l), - })); - auto& square = items.back(); - - items.emplace_back(libnest2d::Item({ - Point(5l, 10l), - Point(10l, 0l), - Point(0l, 0l) - })); - auto& equilateral_triangle = items.back(); - - auto num_bins = libnest2d::nest(items, volume); - REQUIRE(num_bins == 1u); - REQUIRE(long_thin_triangle.binId() != libnest2d::BIN_ID_UNSET); - REQUIRE(square.binId() != libnest2d::BIN_ID_UNSET); - REQUIRE(equilateral_triangle.binId() != libnest2d::BIN_ID_UNSET); -} From b23997e4c9496ff631f0bd48c9812cce36fbc8ad Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 29 Sep 2025 08:31:26 +0200 Subject: [PATCH 73/74] Use point list over custom js type point list NP-1172 --- libnest2d_js/libnest2d_js.cpp | 45 ++++++----------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index 4a96353a..c97b3c5b 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -32,45 +32,12 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(ItemList); EMSCRIPTEN_DECLARE_VAL_TYPE(DoubleList); EMSCRIPTEN_DECLARE_VAL_TYPE(ResultAndItem); - -// Helper function to convert a Point to a JavaScript object -emscripten::val pointToJSObject(const Point& point) { - emscripten::val obj = emscripten::val::object(); - obj.set("x", long{ static_cast(getX(point)) } ); - obj.set("y", long{ static_cast(getY(point)) } ); - return obj; -} - ResultAndItem resultAndItems(const size_t result, const ItemList& items) { emscripten::val obj = emscripten::val::object(); obj.set("result", result); obj.set("items", items); return ResultAndItem { obj }; } -// Helper function to convert a vector of Points to a JavaScript array -emscripten::val pointVectorToJSArray(const std::vector& points) { - emscripten::val jsArray = emscripten::val::array(); - for (size_t i = 0; i < points.size(); ++i) { - jsArray.set(i, pointToJSObject(points[i])); - } - return jsArray; -} -// Helper function to convert JavaScript arrays to std::vector -std::vector jsArrayToPointVector(const emscripten::val& jsArray) { - std::vector vertices; - unsigned length = jsArray["length"].as(); - vertices.reserve(length); - - for (unsigned i = 0; i < length; i++) { - emscripten::val jsPoint = jsArray[i]; - // Use property access instead of method calls for better compatibility - long x = jsPoint["x"].as(); - long y = jsPoint["y"].as(); - vertices.emplace_back(x, y); - } - - return vertices; -} // Wrapper function for nest() to handle JavaScript arrays ResultAndItem nestWrapper(const ItemList& jsItems, const Box& bin, double scaleFactor) { @@ -162,14 +129,12 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .field("parallel", &NfpConfig::parallel) ; - // Item class class_("Item") .constructor() - .class_function("createFromVertices", optional_override([](const emscripten::val& jsVertices) -> Item { - std::vector vertices = jsArrayToPointVector(jsVertices); + .class_function("createFromVertices", optional_override([](const PointList& vertices) -> Item { PolygonImpl polygon; - polygon.Contour = vertices; + polygon.Contour = emscripten::vecFromJSArray(vertices); return Item(polygon); })) .function("binId", select_overload(&Item::binId)) @@ -189,7 +154,11 @@ EMSCRIPTEN_BINDINGS(libnest2d_js) { .function("setPriority", select_overload(&Item::priority)) .function("transformedShape", optional_override([](const Item& self) { const auto& poly = self.transformedShape(); - return pointVectorToJSArray(poly.Contour); + emscripten::val shape = emscripten::val::array(); + for (const auto& point : poly.Contour) { + shape.call("push", point); + } + return PointList { shape }; })); From a8d81f15abfc8610f17bbbef435bf39249d447f7 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 1 Oct 2025 10:09:29 +0200 Subject: [PATCH 74/74] Adjust distance between the two models NP-1172 --- libnest2d_js/libnest2d_js.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnest2d_js/libnest2d_js.cpp b/libnest2d_js/libnest2d_js.cpp index c97b3c5b..a5ea31f1 100644 --- a/libnest2d_js/libnest2d_js.cpp +++ b/libnest2d_js/libnest2d_js.cpp @@ -54,7 +54,7 @@ ResultAndItem nestWrapper(const ItemList& jsItems, const Box& bin, double scaleF NestConfig<> cfg; cfg.placer_config.rotations = { 0 }; - size_t result = nest(items, bin, scaleFactor * 5, cfg); + size_t result = nest(items, bin, scaleFactor, cfg); emscripten::val jsItemsResult = emscripten::val::array(); // Copy results back to original JavaScript items