Skip to content

Commit d0de0c3

Browse files
Ink Open Sourcecopybara-github
authored andcommitted
Mesh Creation Error Handling
PiperOrigin-RevId: 720327720
1 parent 871d876 commit d0de0c3

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

ink/geometry/internal/jni/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,11 +343,13 @@ cc_library(
343343
deps = [
344344
":vec_jni_helper",
345345
"//ink/geometry:mesh",
346+
"//ink/geometry:mesh_format",
346347
"//ink/geometry:partitioned_mesh",
347348
"//ink/geometry:point",
348349
"//ink/geometry:tessellator",
349350
"//ink/geometry/internal:polyline_processing",
350351
"//ink/jni/internal:jni_defines",
352+
"//ink/jni/internal:jni_throw_util",
351353
"//ink/strokes/input:stroke_input_batch",
352354
"@com_google_absl//absl/status:statusor",
353355
"@com_google_absl//absl/types:span",

ink/geometry/internal/jni/polyline_processing_jni.cc

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
#include "absl/types/span.h"
2323
#include "ink/geometry/internal/polyline_processing.h"
2424
#include "ink/geometry/mesh.h"
25+
#include "ink/geometry/mesh_format.h"
2526
#include "ink/geometry/partitioned_mesh.h"
2627
#include "ink/geometry/point.h"
2728
#include "ink/geometry/tessellator.h"
2829
#include "ink/jni/internal/jni_defines.h"
30+
#include "ink/jni/internal/jni_throw_util.h"
2931
#include "ink/strokes/input/stroke_input_batch.h"
3032

3133
namespace {
@@ -45,6 +47,14 @@ JNI_METHOD(strokes, MeshCreationNative, jlong,
4547
const auto* input = reinterpret_cast<const StrokeInputBatch*>(
4648
stroke_input_batch_native_pointer);
4749

50+
// If the input is empty then this will return an empty PartitionedMesh with
51+
// no location and no area. This will not intersect with anything if used for
52+
// hit testing.
53+
if (input->IsEmpty()) {
54+
return reinterpret_cast<jlong>(
55+
std::make_unique<PartitionedMesh>(PartitionedMesh()).release());
56+
}
57+
4858
std::vector<Point> points;
4959
points.reserve(input->Size());
5060
for (size_t i = 0; i < input->Size(); ++i) {
@@ -54,10 +64,38 @@ JNI_METHOD(strokes, MeshCreationNative, jlong,
5464
std::vector<Point> processed_points =
5565
ink::geometry_internal::CreateClosedShape(points);
5666

57-
absl::StatusOr<Mesh> mesh = ink::CreateMeshFromPolyline(processed_points);
67+
absl::StatusOr<Mesh> mesh;
68+
// If there are fewer than 3 points the tessellator can't be used to create a
69+
// mesh. Instead, the mesh is created with a single triangle that has repeated
70+
// and overlapping points. This effectively creates a point-like or
71+
// segment-like mesh. The resulting mesh will have an area of 0 but can still
72+
// be used for hit testing via intersection.
73+
if (processed_points.size() < 3) {
74+
const size_t size = processed_points.size();
75+
std::vector<float> x_values;
76+
std::vector<float> y_values;
77+
x_values.reserve(3);
78+
y_values.reserve(3);
79+
for (int i = 0; i < 3; ++i) {
80+
// If there are 2 points remaining then the first point will appear twice.
81+
// If there is only 1 point all 3 points will be the same.
82+
x_values.push_back(processed_points[i % size].x);
83+
y_values.push_back(processed_points[i % size].y);
84+
}
85+
mesh = Mesh::Create(ink::MeshFormat(), {x_values, y_values}, {0, 1, 2});
86+
} else {
87+
mesh = ink::CreateMeshFromPolyline(processed_points);
88+
}
89+
if (!ink::jni::CheckOkOrThrow(env, mesh.status())) {
90+
return 0;
91+
}
5892

5993
absl::StatusOr<PartitionedMesh> partitioned_mesh =
6094
PartitionedMesh::FromMeshes(absl::MakeSpan(&mesh.value(), 1));
95+
if (!ink::jni::CheckOkOrThrow(env, partitioned_mesh.status())) {
96+
return 0;
97+
}
98+
6199
auto partitioned_mesh_ptr =
62100
std::make_unique<PartitionedMesh>(partitioned_mesh.value());
63101
return reinterpret_cast<jlong>(partitioned_mesh_ptr.release());

ink/geometry/internal/polyline_processing.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,17 @@ std::vector<Point> ProcessPolylineForMeshCreation(
457457
}
458458

459459
std::vector<Point> CreateClosedShape(absl::Span<const Point> points) {
460+
if (points.size() < 3) {
461+
return std::vector<Point>(points.begin(), points.end());
462+
}
460463
PolylineData polyline = CreateNewPolylineData(points);
461-
464+
if (polyline.segments.size() < 2) {
465+
if (polyline.segments.size() == 1) {
466+
return {polyline.segments.front().segment.start,
467+
polyline.segments.front().segment.end};
468+
}
469+
return {points.front()};
470+
}
462471
// Calculate the total walk distance of the polyline.
463472
for (size_t i = 0; i < polyline.segments.size(); ++i) {
464473
polyline.total_walk_distance += polyline.segments[i].length;

ink/geometry/internal/polyline_processing.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ std::vector<Point> ProcessPolylineForMeshCreation(
114114
float min_trimming_ratio);
115115

116116
// A version of ProcessPolylineForMeshCreation thats uses default parameters
117-
// which have been tested to work well for most shapes.
117+
// which have been tested to work well for most shapes. If there are fewer than
118+
// 3 input points, or if there are fewer than 3 points remaining after removing
119+
// points with the same (x,y) coordinates as the previous point, this function
120+
// will return the remaining points.
118121
std::vector<Point> CreateClosedShape(absl::Span<const Point> points);
119122
} // namespace ink::geometry_internal
120123

0 commit comments

Comments
 (0)