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
3133namespace {
@@ -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 ());
0 commit comments