Skip to content

Commit 77e4a0d

Browse files
committed
Refactor UvulaJS JavaScript bindings for TypeScript integration
- Replaced custom structures with standardized TypeScript-like types (Float32Array, Uint32Array, Int32Array). - Refactored wrappers and return types to improve type safety in TypeScript. - Enhanced `project` and `unwrap` methods with new structured return types. - Updated Emscripten bindings to support TypeScript-friendly structures and arrays. NP-1250
1 parent d3f77fe commit 77e4a0d

File tree

1 file changed

+139
-88
lines changed

1 file changed

+139
-88
lines changed

UvulaJS/UvulaJS.cpp

Lines changed: 139 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -14,53 +14,75 @@
1414

1515
using namespace emscripten;
1616

17-
// Info structure for library version information
18-
struct uvula_info_t
17+
// TypeScript type aliases for better type safety
18+
EMSCRIPTEN_DECLARE_VAL_TYPE(Float32Array);
19+
EMSCRIPTEN_DECLARE_VAL_TYPE(Uint32Array);
20+
EMSCRIPTEN_DECLARE_VAL_TYPE(Int32Array);
21+
22+
// Return type for unwrap function
23+
struct UnwrapResult
24+
{
25+
std::vector<float> uvCoordinates;
26+
uint32_t textureWidth;
27+
uint32_t textureHeight;
28+
};
29+
30+
// Return type for project function (array of polygons as flat arrays)
31+
struct ProjectResult
1932
{
20-
std::string uvula_version;
21-
std::string git_hash;
33+
std::vector<std::vector<float>> polygons;
2234
};
2335

2436
// Parameter structure for project function
2537
struct ProjectParams
2638
{
27-
std::vector<float> strokePolygon;
28-
std::vector<float> meshVertices;
29-
std::vector<uint32_t> meshIndices;
30-
std::vector<float> meshUV;
31-
std::vector<int> meshFacesConnectivity;
32-
uint32_t textureWidth;
33-
uint32_t textureHeight;
34-
std::vector<float> cameraProjectionMatrix;
35-
bool isCameraPerspective;
36-
uint32_t viewportWidth;
37-
uint32_t viewportHeight;
38-
std::vector<float> cameraNormal;
39-
uint32_t faceId;
39+
Float32Array strokePolygon = Float32Array{emscripten::val::array()}; // float[]
40+
Float32Array meshVertices = Float32Array{emscripten::val::array()}; // float[]
41+
Uint32Array meshIndices = Uint32Array{emscripten::val::array()}; // uint32_t[]
42+
Float32Array meshUV = Float32Array{emscripten::val::array()}; // float[]
43+
Int32Array meshFacesConnectivity = Int32Array{emscripten::val::array()}; // int32_t[]
44+
uint32_t textureWidth = 0;
45+
uint32_t textureHeight = 0;
46+
Float32Array cameraProjectionMatrix = Float32Array{emscripten::val::array()}; // float[16] - 4x4 matrix
47+
bool isCameraPerspective = false;
48+
uint32_t viewportWidth = 0;
49+
uint32_t viewportHeight = 0;
50+
Float32Array cameraNormal = Float32Array{emscripten::val::array()}; // float[3] - normal vector
51+
uint32_t faceId = 0;
4052
};
4153

42-
uvula_info_t get_uvula_info()
54+
std::string get_uvula_info()
4355
{
44-
return { UVULA_VERSION, "" }; // Git hash can be added later if needed
56+
return { UVULA_VERSION };
4557
}
4658

4759
// Typed wrapper functions for better TypeScript generation
48-
emscripten::val unwrapTyped(const std::vector<float>& vertices, const std::vector<uint32_t>& indices)
60+
UnwrapResult unwrapTyped(const Float32Array& vertices_js, const Uint32Array& indices_js)
4961
{
50-
// Convert flat arrays to Point3F and Face vectors
62+
// Convert JavaScript arrays to C++ vectors
5163
std::vector<Point3F> vertex_points;
5264
std::vector<Face> face_indices;
53-
65+
66+
// Get array lengths
67+
unsigned vertices_length = vertices_js["length"].as<unsigned>();
68+
unsigned indices_length = indices_js["length"].as<unsigned>();
69+
5470
// Convert vertices (expecting flat array of [x1, y1, z1, x2, y2, z2, ...])
55-
vertex_points.reserve(vertices.size() / 3);
56-
for (size_t i = 0; i < vertices.size(); i += 3) {
57-
vertex_points.emplace_back(vertices[i], vertices[i + 1], vertices[i + 2]);
71+
vertex_points.reserve(vertices_length / 3);
72+
for (unsigned i = 0; i < vertices_length; i += 3) {
73+
float x = vertices_js[i].as<float>();
74+
float y = vertices_js[i + 1].as<float>();
75+
float z = vertices_js[i + 2].as<float>();
76+
vertex_points.emplace_back(x, y, z);
5877
}
5978

6079
// Convert indices (expecting flat array of [i1, i2, i3, i4, i5, i6, ...])
61-
face_indices.reserve(indices.size() / 3);
62-
for (size_t i = 0; i < indices.size(); i += 3) {
63-
face_indices.push_back({indices[i], indices[i + 1], indices[i + 2]});
80+
face_indices.reserve(indices_length / 3);
81+
for (unsigned i = 0; i < indices_length; i += 3) {
82+
uint32_t i1 = indices_js[i].as<uint32_t>();
83+
uint32_t i2 = indices_js[i + 1].as<uint32_t>();
84+
uint32_t i3 = indices_js[i + 2].as<uint32_t>();
85+
face_indices.push_back({i1, i2, i3});
6486
}
6587

6688
// Prepare output
@@ -70,87 +92,107 @@ emscripten::val unwrapTyped(const std::vector<float>& vertices, const std::vecto
7092

7193
// Perform unwrapping
7294
bool success = smartUnwrap(vertex_points, face_indices, uv_coords, texture_width, texture_height);
73-
95+
7496
if (!success) {
7597
throw std::runtime_error("Couldn't unwrap UVs!");
7698
}
7799

78-
// Convert result to JavaScript object
79-
emscripten::val result = emscripten::val::object();
100+
// Convert result to structured return type
80101
std::vector<float> uv_array;
81102
uv_array.reserve(uv_coords.size() * 2);
82-
103+
83104
for (const auto& coord : uv_coords) {
84105
uv_array.push_back(coord.x);
85106
uv_array.push_back(coord.y);
86107
}
87-
88-
result.set("uvCoordinates", emscripten::val::array(uv_array));
89-
result.set("textureWidth", texture_width);
90-
result.set("textureHeight", texture_height);
91-
92-
return result;
108+
109+
return UnwrapResult{
110+
.uvCoordinates = uv_array,
111+
.textureWidth = texture_width,
112+
.textureHeight = texture_height
113+
};
93114
}
94115

95-
emscripten::val projectTyped(
96-
const std::vector<float>& stroke_polygon,
97-
const std::vector<float>& mesh_vertices,
98-
const std::vector<uint32_t>& mesh_indices,
99-
const std::vector<float>& mesh_uv,
100-
const std::vector<int>& mesh_faces_connectivity,
116+
ProjectResult projectTyped(
117+
const Float32Array& stroke_polygon_js,
118+
const Float32Array& mesh_vertices_js,
119+
const Uint32Array& mesh_indices_js,
120+
const Float32Array& mesh_uv_js,
121+
const Int32Array& mesh_faces_connectivity_js,
101122
uint32_t texture_width,
102123
uint32_t texture_height,
103-
const std::vector<float>& camera_projection_matrix,
124+
const Float32Array& camera_projection_matrix_js,
104125
bool is_camera_perspective,
105126
uint32_t viewport_width,
106127
uint32_t viewport_height,
107-
const std::vector<float>& camera_normal,
128+
const Float32Array& camera_normal_js,
108129
uint32_t face_id)
109130
{
110-
// Convert stroke polygon
131+
// Convert stroke polygon (flat array of [x1, y1, x2, y2, ...])
111132
std::vector<Point2F> stroke_points;
112-
stroke_points.reserve(stroke_polygon.size() / 2);
113-
for (size_t i = 0; i < stroke_polygon.size(); i += 2) {
114-
stroke_points.push_back({stroke_polygon[i], stroke_polygon[i + 1]});
133+
unsigned stroke_length = stroke_polygon_js["length"].as<unsigned>();
134+
stroke_points.reserve(stroke_length / 2);
135+
for (unsigned i = 0; i < stroke_length; i += 2) {
136+
float x = stroke_polygon_js[i].as<float>();
137+
float y = stroke_polygon_js[i + 1].as<float>();
138+
stroke_points.push_back({x, y});
115139
}
116140

117-
// Convert mesh vertices
141+
// Convert mesh vertices (flat array of [x1, y1, z1, x2, y2, z2, ...])
118142
std::vector<Point3F> vertex_points;
119-
vertex_points.reserve(mesh_vertices.size() / 3);
120-
for (size_t i = 0; i < mesh_vertices.size(); i += 3) {
121-
vertex_points.emplace_back(mesh_vertices[i], mesh_vertices[i + 1], mesh_vertices[i + 2]);
143+
unsigned vertices_length = mesh_vertices_js["length"].as<unsigned>();
144+
vertex_points.reserve(vertices_length / 3);
145+
for (unsigned i = 0; i < vertices_length; i += 3) {
146+
float x = mesh_vertices_js[i].as<float>();
147+
float y = mesh_vertices_js[i + 1].as<float>();
148+
float z = mesh_vertices_js[i + 2].as<float>();
149+
vertex_points.emplace_back(x, y, z);
122150
}
123151

124-
// Convert mesh indices
152+
// Convert mesh indices (flat array of [i1, i2, i3, i4, i5, i6, ...])
125153
std::vector<Face> face_indices;
126-
face_indices.reserve(mesh_indices.size() / 3);
127-
for (size_t i = 0; i < mesh_indices.size(); i += 3) {
128-
face_indices.push_back({mesh_indices[i], mesh_indices[i + 1], mesh_indices[i + 2]});
154+
unsigned indices_length = mesh_indices_js["length"].as<unsigned>();
155+
face_indices.reserve(indices_length / 3);
156+
for (unsigned i = 0; i < indices_length; i += 3) {
157+
uint32_t i1 = mesh_indices_js[i].as<uint32_t>();
158+
uint32_t i2 = mesh_indices_js[i + 1].as<uint32_t>();
159+
uint32_t i3 = mesh_indices_js[i + 2].as<uint32_t>();
160+
face_indices.push_back({i1, i2, i3});
129161
}
130162

131-
// Convert mesh UV coordinates
163+
// Convert mesh UV coordinates (flat array of [u1, v1, u2, v2, ...])
132164
std::vector<Point2F> uv_points;
133-
uv_points.reserve(mesh_uv.size() / 2);
134-
for (size_t i = 0; i < mesh_uv.size(); i += 2) {
135-
uv_points.push_back({mesh_uv[i], mesh_uv[i + 1]});
165+
unsigned uv_length = mesh_uv_js["length"].as<unsigned>();
166+
uv_points.reserve(uv_length / 2);
167+
for (unsigned i = 0; i < uv_length; i += 2) {
168+
float u = mesh_uv_js[i].as<float>();
169+
float v = mesh_uv_js[i + 1].as<float>();
170+
uv_points.push_back({u, v});
136171
}
137172

138-
// Convert mesh faces connectivity
173+
// Convert mesh faces connectivity (flat array of [f1, f2, f3, f4, f5, f6, ...])
139174
std::vector<FaceSigned> connectivity;
140-
connectivity.reserve(mesh_faces_connectivity.size() / 3);
141-
for (size_t i = 0; i < mesh_faces_connectivity.size(); i += 3) {
142-
connectivity.push_back({mesh_faces_connectivity[i], mesh_faces_connectivity[i + 1], mesh_faces_connectivity[i + 2]});
175+
unsigned connectivity_length = mesh_faces_connectivity_js["length"].as<unsigned>();
176+
connectivity.reserve(connectivity_length / 3);
177+
for (unsigned i = 0; i < connectivity_length; i += 3) {
178+
int32_t i1 = mesh_faces_connectivity_js[i].as<int32_t>();
179+
int32_t i2 = mesh_faces_connectivity_js[i + 1].as<int32_t>();
180+
int32_t i3 = mesh_faces_connectivity_js[i + 2].as<int32_t>();
181+
connectivity.push_back({i1, i2, i3});
143182
}
144183

145184
// Convert camera projection matrix (4x4 matrix as flat array)
146185
float matrix_data[4][4];
147186
for (int i = 0; i < 16; ++i) {
148-
matrix_data[i / 4][i % 4] = camera_projection_matrix[i];
187+
matrix_data[i / 4][i % 4] = camera_projection_matrix_js[i].as<float>();
149188
}
150189
Matrix44F projection_matrix(matrix_data);
151190

152191
// Convert camera normal (3-element array)
153-
Vector3F normal(camera_normal[0], camera_normal[1], camera_normal[2]);
192+
float normal_x = camera_normal_js[0].as<float>();
193+
float normal_y = camera_normal_js[1].as<float>();
194+
float normal_z = camera_normal_js[2].as<float>();
195+
Vector3F normal(normal_x, normal_y, normal_z);
154196

155197
// Call the projection function
156198
std::vector<Polygon> result = doProject(
@@ -169,24 +211,25 @@ emscripten::val projectTyped(
169211
face_id
170212
);
171213

172-
// Convert result to JavaScript array of arrays
173-
emscripten::val js_result = emscripten::val::array();
174-
for (size_t i = 0; i < result.size(); ++i) {
214+
// Convert result to structured return type
215+
std::vector<std::vector<float>> result_polygons;
216+
result_polygons.reserve(result.size());
217+
218+
for (const auto& polygon : result) {
175219
std::vector<float> polygon_flat;
176-
const Polygon& polygon = result[i];
177220
polygon_flat.reserve(polygon.size() * 2);
178221
for (const auto& point : polygon) {
179222
polygon_flat.push_back(point.x);
180223
polygon_flat.push_back(point.y);
181224
}
182-
js_result.set(i, emscripten::val::array(polygon_flat));
225+
result_polygons.push_back(std::move(polygon_flat));
183226
}
184227

185-
return js_result;
228+
return ProjectResult{.polygons = result_polygons};
186229
}
187230

188231
// Structured wrapper for project function using ProjectParams
189-
emscripten::val projectWithParams(const ProjectParams& params)
232+
ProjectResult projectWithParams(const ProjectParams& params)
190233
{
191234
return projectTyped(
192235
params.strokePolygon,
@@ -241,24 +284,24 @@ emscripten::val jsUnwrap(const emscripten::val& vertices_js, const emscripten::v
241284

242285
// Perform unwrapping
243286
bool success = smartUnwrap(vertices, indices, uv_coords, texture_width, texture_height);
244-
287+
245288
if (!success) {
246289
throw std::runtime_error("Couldn't unwrap UVs!");
247290
}
248291

249292
// Convert result to JavaScript object
250293
emscripten::val result = emscripten::val::object();
251294
emscripten::val uv_array = emscripten::val::array();
252-
295+
253296
for (size_t i = 0; i < uv_coords.size(); ++i) {
254297
uv_array.set(i * 2, uv_coords[i].x);
255298
uv_array.set(i * 2 + 1, uv_coords[i].y);
256299
}
257-
300+
258301
result.set("uvCoordinates", uv_array);
259302
result.set("textureWidth", texture_width);
260303
result.set("textureHeight", texture_height);
261-
304+
262305
return result;
263306
}
264307

@@ -378,10 +421,23 @@ emscripten::val jsProject(
378421

379422
EMSCRIPTEN_BINDINGS(uvula)
380423
{
381-
// Register array types for better TypeScript generation
382-
register_vector<float>("FloatArray");
383-
register_vector<int>("IntArray");
384-
register_vector<uint32_t>("UInt32Array");
424+
// Register typed array types
425+
register_vector<float>("Float32Array");
426+
register_vector<std::vector<float>>("Float32ArrayArray");
427+
428+
// Register TypeScript-style typed arrays
429+
emscripten::register_type<Float32Array>("Float32Array");
430+
emscripten::register_type<Uint32Array>("Uint32Array");
431+
emscripten::register_type<Int32Array>("Int32Array");
432+
433+
// Register structured return types
434+
value_object<UnwrapResult>("UnwrapResult")
435+
.field("uvCoordinates", &UnwrapResult::uvCoordinates)
436+
.field("textureWidth", &UnwrapResult::textureWidth)
437+
.field("textureHeight", &UnwrapResult::textureHeight);
438+
439+
value_object<ProjectResult>("ProjectResult")
440+
.field("polygons", &ProjectResult::polygons);
385441

386442
// Register ProjectParams structure
387443
value_object<ProjectParams>("ProjectParams")
@@ -403,11 +459,6 @@ EMSCRIPTEN_BINDINGS(uvula)
403459
function("unwrap", &unwrapTyped);
404460
function("project", &projectWithParams);
405461

406-
// Version information
407-
value_object<uvula_info_t>("uvula_info_t")
408-
.field("uvula_version", &uvula_info_t::uvula_version)
409-
.field("git_hash", &uvula_info_t::git_hash);
410-
411462
function("uvula_info", &get_uvula_info);
412463

413464
// Utility classes for direct access if needed

0 commit comments

Comments
 (0)