diff --git a/UvulaJS/UvulaJS.cpp b/UvulaJS/UvulaJS.cpp index 0818fd6..51f997f 100644 --- a/UvulaJS/UvulaJS.cpp +++ b/UvulaJS/UvulaJS.cpp @@ -18,31 +18,8 @@ using namespace emscripten; EMSCRIPTEN_DECLARE_VAL_TYPE(Float32Array); EMSCRIPTEN_DECLARE_VAL_TYPE(Uint32Array); EMSCRIPTEN_DECLARE_VAL_TYPE(Int32Array); - -// Wrapper for array of Float32Array to provide proper TypeScript typing -class PolygonArray { -public: - emscripten::val polygons; - - PolygonArray() : polygons(emscripten::val::array()) {} - explicit PolygonArray(emscripten::val arr) : polygons(arr) {} - - void push(const Float32Array& polygon) { - polygons.call("push", polygon); - } - - emscripten::val get(int index) const { - return polygons[index]; - } - - int size() const { - return polygons["length"].as(); - } - - emscripten::val toArray() const { - return polygons; - } -}; +EMSCRIPTEN_DECLARE_VAL_TYPE(UInt32Array); +EMSCRIPTEN_DECLARE_VAL_TYPE(PolygonArray); // Return type for unwrap function struct UnwrapResult @@ -52,37 +29,13 @@ struct UnwrapResult uint32_t textureHeight; }; -// Return type for project function (array of polygons as flat arrays) -struct ProjectResult -{ - PolygonArray polygons; -}; - -// Parameter structure for project function -struct ProjectParams -{ - Float32Array strokePolygon = Float32Array{emscripten::val::array()}; // float[] - Float32Array meshVertices = Float32Array{emscripten::val::array()}; // float[] - Uint32Array meshIndices = Uint32Array{emscripten::val::array()}; // uint32_t[] - Float32Array meshUV = Float32Array{emscripten::val::array()}; // float[] - Int32Array meshFacesConnectivity = Int32Array{emscripten::val::array()}; // int32_t[] - uint32_t textureWidth = 0; - uint32_t textureHeight = 0; - Float32Array cameraProjectionMatrix = Float32Array{emscripten::val::array()}; // float[16] - 4x4 matrix - bool isCameraPerspective = false; - uint32_t viewportWidth = 0; - uint32_t viewportHeight = 0; - Float32Array cameraNormal = Float32Array{emscripten::val::array()}; // float[3] - normal vector - uint32_t faceId = 0; -}; - std::string get_uvula_info() { return { UVULA_VERSION }; } // Typed wrapper functions for better TypeScript generation -UnwrapResult unwrapTyped(const Float32Array& vertices_js, const Uint32Array& indices_js) +UnwrapResult unwrap(const Float32Array& vertices_js, const Uint32Array& indices_js) { // Convert JavaScript arrays to C++ vectors std::vector vertex_points; @@ -103,7 +56,8 @@ UnwrapResult unwrapTyped(const Float32Array& vertices_js, const Uint32Array& ind // Convert indices (expecting flat array of [i1, i2, i3, i4, i5, i6, ...]) face_indices.reserve(indices_length / 3); - for (unsigned i = 0; i < indices_length; i += 3) { + for (unsigned i = 0; i < indices_length; i += 3) + { uint32_t i1 = indices_js[i].as(); uint32_t i2 = indices_js[i + 1].as(); uint32_t i3 = indices_js[i + 2].as(); @@ -118,14 +72,16 @@ UnwrapResult unwrapTyped(const Float32Array& vertices_js, const Uint32Array& ind // Perform unwrapping bool success = smartUnwrap(vertex_points, face_indices, uv_coords, texture_width, texture_height); - if (!success) { + if (!success) + { throw std::runtime_error("Couldn't unwrap UVs!"); } // Convert result to structured return type emscripten::val uv_array = emscripten::val::array(); - for (size_t i = 0; i < uv_coords.size(); ++i) { + for (size_t i = 0; i < uv_coords.size(); ++i) + { uv_array.set(i * 2, uv_coords[i].x); uv_array.set(i * 2 + 1, uv_coords[i].y); } @@ -137,7 +93,7 @@ UnwrapResult unwrapTyped(const Float32Array& vertices_js, const Uint32Array& ind }; } -ProjectResult projectTyped( +PolygonArray project( const Float32Array& stroke_polygon_js, const Float32Array& mesh_vertices_js, const Uint32Array& mesh_indices_js, @@ -149,8 +105,9 @@ ProjectResult projectTyped( bool is_camera_perspective, uint32_t viewport_width, uint32_t viewport_height, - const Float32Array& camera_normal_js, - uint32_t face_id) + const Vector3F& camera_normal, + uint32_t face_id +) { // Convert stroke polygon (flat array of [x1, y1, x2, y2, ...]) std::vector stroke_points; @@ -208,16 +165,10 @@ ProjectResult projectTyped( // Convert camera projection matrix (4x4 matrix as flat array) float matrix_data[4][4]; for (int i = 0; i < 16; ++i) { - matrix_data[i / 4][i % 4] = camera_projection_matrix_js[i].as(); + matrix_data[i % 4][i / 4] = camera_projection_matrix_js[i].as(); } Matrix44F projection_matrix(matrix_data); - // Convert camera normal (3-element array) - float normal_x = camera_normal_js[0].as(); - float normal_y = camera_normal_js[1].as(); - float normal_z = camera_normal_js[2].as(); - Vector3F normal(normal_x, normal_y, normal_z); - // Call the projection function std::vector result = doProject( stroke_points, @@ -231,233 +182,35 @@ ProjectResult projectTyped( is_camera_perspective, viewport_width, viewport_height, - normal, + camera_normal, face_id ); // Convert result to structured return type - PolygonArray result_polygons; + emscripten::val result_polygons = emscripten::val::array(); - for (size_t i = 0; i < result.size(); ++i) { + for (size_t i = 0; i < result.size(); ++i) + { emscripten::val polygon_array = emscripten::val::array(); const auto& polygon = result[i]; - for (size_t j = 0; j < polygon.size(); ++j) { - polygon_array.set(j * 2, polygon[j].x); - polygon_array.set(j * 2 + 1, polygon[j].y); + for (size_t j = 0; j < polygon.size(); ++j) + { + polygon_array.set(j, polygon[j]); } - // Add Float32Array to the result array - result_polygons.push(Float32Array{polygon_array}); - } - return ProjectResult{.polygons = result_polygons}; -} - -// Structured wrapper for project function using ProjectParams -ProjectResult projectWithParams(const ProjectParams& params) -{ - return projectTyped( - params.strokePolygon, - params.meshVertices, - params.meshIndices, - params.meshUV, - params.meshFacesConnectivity, - params.textureWidth, - params.textureHeight, - params.cameraProjectionMatrix, - params.isCameraPerspective, - params.viewportWidth, - params.viewportHeight, - params.cameraNormal, - params.faceId - ); -} - -// Wrapper for the unwrap function that works with JavaScript arrays -emscripten::val jsUnwrap(const emscripten::val& vertices_js, const emscripten::val& indices_js) -{ - // Convert JavaScript arrays to C++ vectors - std::vector vertices; - std::vector indices; - - // Get array lengths - unsigned vertices_length = vertices_js["length"].as(); - unsigned indices_length = indices_js["length"].as(); - - // Convert vertices (expecting flat array of [x1, y1, z1, x2, y2, z2, ...]) - vertices.reserve(vertices_length / 3); - for (unsigned i = 0; i < vertices_length; i += 3) { - float x = vertices_js[i].as(); - float y = vertices_js[i + 1].as(); - float z = vertices_js[i + 2].as(); - vertices.emplace_back(x, y, z); - } - - // Convert indices (expecting flat array of [i1, i2, i3, i4, i5, i6, ...]) - indices.reserve(indices_length / 3); - for (unsigned i = 0; i < indices_length; i += 3) { - uint32_t i1 = indices_js[i].as(); - uint32_t i2 = indices_js[i + 1].as(); - uint32_t i3 = indices_js[i + 2].as(); - indices.push_back({i1, i2, i3}); - } - - // Prepare output - std::vector uv_coords(vertices.size(), {0.0f, 0.0f}); - uint32_t texture_width; - uint32_t texture_height; - - // Perform unwrapping - bool success = smartUnwrap(vertices, indices, uv_coords, texture_width, texture_height); - - if (!success) { - throw std::runtime_error("Couldn't unwrap UVs!"); - } - - // Convert result to JavaScript object - emscripten::val result = emscripten::val::object(); - emscripten::val uv_array = emscripten::val::array(); - - for (size_t i = 0; i < uv_coords.size(); ++i) { - uv_array.set(i * 2, uv_coords[i].x); - uv_array.set(i * 2 + 1, uv_coords[i].y); + result_polygons.set(i, polygon_array); } - result.set("uvCoordinates", uv_array); - result.set("textureWidth", texture_width); - result.set("textureHeight", texture_height); - - return result; -} - -// Wrapper for the project function that works with JavaScript arrays -emscripten::val jsProject( - const emscripten::val& stroke_polygon_js, - const emscripten::val& mesh_vertices_js, - const emscripten::val& mesh_indices_js, - const emscripten::val& mesh_uv_js, - const emscripten::val& mesh_faces_connectivity_js, - uint32_t texture_width, - uint32_t texture_height, - const emscripten::val& camera_projection_matrix_js, - bool is_camera_perspective, - uint32_t viewport_width, - uint32_t viewport_height, - const emscripten::val& camera_normal_js, - uint32_t face_id) -{ - // Convert stroke polygon (flat array of [x1, y1, x2, y2, ...]) - std::vector stroke_polygon; - unsigned stroke_length = stroke_polygon_js["length"].as(); - stroke_polygon.reserve(stroke_length / 2); - for (unsigned i = 0; i < stroke_length; i += 2) { - float x = stroke_polygon_js[i].as(); - float y = stroke_polygon_js[i + 1].as(); - stroke_polygon.push_back({x, y}); - } - - // Convert mesh vertices (flat array of [x1, y1, z1, x2, y2, z2, ...]) - std::vector mesh_vertices; - unsigned vertices_length = mesh_vertices_js["length"].as(); - mesh_vertices.reserve(vertices_length / 3); - for (unsigned i = 0; i < vertices_length; i += 3) { - float x = mesh_vertices_js[i].as(); - float y = mesh_vertices_js[i + 1].as(); - float z = mesh_vertices_js[i + 2].as(); - mesh_vertices.emplace_back(x, y, z); - } - - // Convert mesh indices (flat array of [i1, i2, i3, i4, i5, i6, ...]) - std::vector mesh_indices; - unsigned indices_length = mesh_indices_js["length"].as(); - mesh_indices.reserve(indices_length / 3); - for (unsigned i = 0; i < indices_length; i += 3) { - uint32_t i1 = mesh_indices_js[i].as(); - uint32_t i2 = mesh_indices_js[i + 1].as(); - uint32_t i3 = mesh_indices_js[i + 2].as(); - mesh_indices.push_back({i1, i2, i3}); - } - - // Convert mesh UV coordinates (flat array of [u1, v1, u2, v2, ...]) - std::vector mesh_uv; - unsigned uv_length = mesh_uv_js["length"].as(); - mesh_uv.reserve(uv_length / 2); - for (unsigned i = 0; i < uv_length; i += 2) { - float u = mesh_uv_js[i].as(); - float v = mesh_uv_js[i + 1].as(); - mesh_uv.push_back({u, v}); - } - - // Convert mesh faces connectivity (flat array of [f1, f2, f3, f4, f5, f6, ...]) - std::vector mesh_faces_connectivity; - unsigned connectivity_length = mesh_faces_connectivity_js["length"].as(); - mesh_faces_connectivity.reserve(connectivity_length / 3); - for (unsigned i = 0; i < connectivity_length; i += 3) { - int32_t i1 = mesh_faces_connectivity_js[i].as(); - int32_t i2 = mesh_faces_connectivity_js[i + 1].as(); - int32_t i3 = mesh_faces_connectivity_js[i + 2].as(); - mesh_faces_connectivity.push_back({i1, i2, i3}); - } - - // Convert camera projection matrix (4x4 matrix as flat array) - float matrix_data[4][4]; - for (int i = 0; i < 16; ++i) { - matrix_data[i / 4][i % 4] = camera_projection_matrix_js[i].as(); - } - Matrix44F camera_projection_matrix(matrix_data); - - // Convert camera normal (3-element array) - float normal_x = camera_normal_js[0].as(); - float normal_y = camera_normal_js[1].as(); - float normal_z = camera_normal_js[2].as(); - Vector3F camera_normal(normal_x, normal_y, normal_z); - - // Call the projection function - std::vector result = doProject( - stroke_polygon, - mesh_vertices, - mesh_indices, - mesh_uv, - mesh_faces_connectivity, - texture_width, - texture_height, - camera_projection_matrix, - is_camera_perspective, - viewport_width, - viewport_height, - camera_normal, - face_id - ); - - // Convert result to JavaScript array of arrays - emscripten::val js_result = emscripten::val::array(); - for (size_t i = 0; i < result.size(); ++i) { - emscripten::val polygon_array = emscripten::val::array(); - const Polygon& polygon = result[i]; - for (size_t j = 0; j < polygon.size(); ++j) { - polygon_array.set(j * 2, polygon[j].x); - polygon_array.set(j * 2 + 1, polygon[j].y); - } - js_result.set(i, polygon_array); - } - - return js_result; + return PolygonArray{ result_polygons }; } EMSCRIPTEN_BINDINGS(uvula) { - // Register TypeScript-style typed arrays emscripten::register_type("Float32Array"); emscripten::register_type("Uint32Array"); emscripten::register_type("Int32Array"); - - // Register PolygonArray class for proper TypeScript typing - class_("PolygonArray") - .constructor<>() - .function("push", &PolygonArray::push) - .function("get", &PolygonArray::get) - .function("size", &PolygonArray::size) - .function("toArray", &PolygonArray::toArray); + emscripten::register_type("Point2F[][]"); // Register structured return types value_object("UnwrapResult") @@ -465,28 +218,9 @@ EMSCRIPTEN_BINDINGS(uvula) .field("textureWidth", &UnwrapResult::textureWidth) .field("textureHeight", &UnwrapResult::textureHeight); - value_object("ProjectResult") - .field("polygons", &ProjectResult::polygons); - - // Register ProjectParams structure - value_object("ProjectParams") - .field("strokePolygon", &ProjectParams::strokePolygon) - .field("meshVertices", &ProjectParams::meshVertices) - .field("meshIndices", &ProjectParams::meshIndices) - .field("meshUV", &ProjectParams::meshUV) - .field("meshFacesConnectivity", &ProjectParams::meshFacesConnectivity) - .field("textureWidth", &ProjectParams::textureWidth) - .field("textureHeight", &ProjectParams::textureHeight) - .field("cameraProjectionMatrix", &ProjectParams::cameraProjectionMatrix) - .field("isCameraPerspective", &ProjectParams::isCameraPerspective) - .field("viewportWidth", &ProjectParams::viewportWidth) - .field("viewportHeight", &ProjectParams::viewportHeight) - .field("cameraNormal", &ProjectParams::cameraNormal) - .field("faceId", &ProjectParams::faceId); - // Main typed functions with proper TypeScript signatures - function("unwrap", &unwrapTyped); - function("project", &projectWithParams); + function("unwrap", &unwrap); + function("project", &project); function("uvula_info", &get_uvula_info); @@ -503,7 +237,10 @@ EMSCRIPTEN_BINDINGS(uvula) .function("z", &Point3F::z); class_("Vector3F") - .constructor(); + .constructor() + .function("x", &Vector3F::x) + .function("y", &Vector3F::y) + .function("z", &Vector3F::z); value_object("Face") .field("i1", &Face::i1) diff --git a/conanfile.py b/conanfile.py index d813fec..5254a39 100644 --- a/conanfile.py +++ b/conanfile.py @@ -8,7 +8,7 @@ from conan.tools.build import check_min_cppstd from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout from conan.tools.env import VirtualBuildEnv -from conan.tools.files import copy, mkdir, AutoPackager, update_conandata +from conan.tools.files import copy, mkdir, update_conandata from conan.tools.microsoft import check_min_vs, is_msvc_static_runtime, is_msvc from conan.tools.scm import Version @@ -174,8 +174,12 @@ def package(self): copy(self, pattern="uvula_js.*", src=os.path.join(self.build_folder, "UvulaJS"), dst=os.path.join(self.package_folder, "lib")) 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() + copy(self, pattern="*.h", src=os.path.join(self.source_folder, "include"), dst=os.path.join(self.package_folder, "include")) + copy(self, pattern="*.a", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False) + copy(self, pattern="*.so", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=True) + copy(self, pattern="*.lib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False) + copy(self, pattern="*.dll", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path=True) + copy(self, pattern="*.dylib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=True) def package_info(self): if self.options.get_safe("with_python_bindings", False):