Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions ink/brush/brush.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ class Brush {
sink.Append(brush.ToFormattedString());
}

bool operator==(const Brush& other) const {
return family_ == other.family_ && color_ == other.color_ &&
size_ == other.size_ && epsilon_ == other.epsilon_;
}

template <typename H>
friend H AbslHashValue(H h, const Brush& brush) {
return H::combine(std::move(h), brush.family_, brush.color_, brush.size_,
brush.epsilon_);
}

private:
Brush(const BrushFamily& family, const Color& color, float size,
float epsilon);
Expand Down
7 changes: 7 additions & 0 deletions ink/brush/brush_coat.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ namespace ink {
struct BrushCoat {
BrushTip tip;
absl::InlinedVector<BrushPaint, 1> paint_preferences = {BrushPaint{}};

bool operator==(const BrushCoat&) const = default;

template <typename H>
friend H AbslHashValue(H h, const BrushCoat& coat) {
return H::combine(std::move(h), coat.tip, coat.paint_preferences);
}
};

namespace brush_internal {
Expand Down
30 changes: 29 additions & 1 deletion ink/brush/brush_family.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ class BrushFamily {
// the modeled inputs. This can be useful as a point of comparison for other
// input models, or for callers who wish to do their own input modeling prior
// to passing inputs into Ink.
struct PassthroughModel {};
struct PassthroughModel {
bool operator==(const PassthroughModel&) const = default;
template <typename H>
friend H AbslHashValue(H h, const PassthroughModel&) {
return h;
}
};

// Averages nearby inputs together within a sliding time window. To be valid,
// the window size must be finite and strictly positive, and the upsampling
Expand All @@ -57,6 +63,14 @@ class BrushFamily {
// inserted between them. Set this to `Duration32::Infinite()` to disable
// upsampling.
Duration32 upsampling_period = Duration32::Seconds(1.0 / 180.0);

bool operator==(const SlidingWindowModel&) const = default;

template <typename H>
friend H AbslHashValue(H h, const SlidingWindowModel& model) {
return H::combine(std::move(h), model.window_size,
model.upsampling_period);
}
};

// Specifies a model for turning a sequence of raw hardware inputs (e.g. from
Expand Down Expand Up @@ -160,6 +174,20 @@ class BrushFamily {
sink.Append(family.ToFormattedString());
}

bool operator==(const BrushFamily& other) const {
return coats_ == other.coats_ && input_model_ == other.input_model_ &&
metadata_ == other.metadata_ &&
opaque_decoded_proto_bytes_with_fallbacks_ ==
other.opaque_decoded_proto_bytes_with_fallbacks_;
}

template <typename H>
friend H AbslHashValue(H h, const BrushFamily& family) {
return H::combine(std::move(h), family.coats_, family.input_model_,
family.metadata_,
family.opaque_decoded_proto_bytes_with_fallbacks_);
}

friend class BrushFamilyInternalAccessor;

private:
Expand Down
9 changes: 9 additions & 0 deletions ink/geometry/mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ class Mesh {
Mesh(const Mesh&) = default;
Mesh& operator=(const Mesh&) = default;

bool operator==(const Mesh& other) const {
return data_.get() == other.data_.get();
}

template <typename H>
friend H AbslHashValue(H h, const Mesh& mesh) {
return H::combine(std::move(h), mesh.data_.get());
}

// Returns the number of vertices in the mesh.
uint32_t VertexCount() const {
ABSL_DCHECK_EQ(data_->vertex_data.size() % VertexStride(), 0u);
Expand Down
27 changes: 27 additions & 0 deletions ink/geometry/mesh_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/hash/hash.h"
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/status/statusor.h"
Expand Down Expand Up @@ -1000,5 +1001,31 @@ TEST(MeshTest, CreateFromQuantizedDataErrorsWithAttributeOutOfBounds) {
StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("range")));
}

TEST(MeshTest, EqualityAndHashing) {
absl::StatusOr<Mesh> m1 = Mesh::Create(MeshFormat(),
{// Position
{5, 10, 20},
{50, -30, 12}},
// Triangles
{0, 1, 2});
ASSERT_THAT(m1, IsOk());

// Copy constructor should share data.
Mesh m2 = *m1;
EXPECT_EQ(*m1, m2);
EXPECT_EQ(absl::HashOf(*m1), absl::HashOf(m2));

// New instance with same content should NOT be equal because pointers differ
// (shallow equality).
absl::StatusOr<Mesh> m3 = Mesh::Create(MeshFormat(),
{// Position
{5, 10, 20},
{50, -30, 12}},
// Triangles
{0, 1, 2});
ASSERT_THAT(m3, IsOk());
EXPECT_NE(*m1, *m3);
}

} // namespace
} // namespace ink
9 changes: 9 additions & 0 deletions ink/geometry/partitioned_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ class PartitionedMesh {
PartitionedMesh& operator=(const PartitionedMesh&) = default;
PartitionedMesh& operator=(PartitionedMesh&&) = default;

bool operator==(const PartitionedMesh& other) const {
return data_.get() == other.data_.get();
}

template <typename H>
friend H AbslHashValue(H h, const PartitionedMesh& mesh) {
return H::combine(std::move(h), mesh.data_.get());
}

// Returns the number of render groups in this modeled shape.
uint32_t RenderGroupCount() const;

Expand Down
1 change: 1 addition & 0 deletions ink/strokes/input/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ cc_test(
"//ink/geometry:angle",
"//ink/types:duration",
"//ink/types:physical_distance",
"@com_google_absl//absl/hash:hash_testing",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
Expand Down
9 changes: 9 additions & 0 deletions ink/strokes/input/stroke_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ struct StrokeInput {
// is a separate condition from the orientation being indeterminant when
// `tilt` is 0.
Angle orientation = kNoOrientation;

bool operator==(const StrokeInput&) const = default;

template <typename H>
friend H AbslHashValue(H h, const StrokeInput& input) {
return H::combine(std::move(h), input.tool_type, input.position,
input.elapsed_time, input.stroke_unit_length,
input.pressure, input.tilt, input.orientation);
}
};

namespace stroke_input_internal {
Expand Down
13 changes: 13 additions & 0 deletions ink/strokes/input/stroke_input_batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,19 @@ class StrokeInputBatch {
sink.Append(batch.ToFormattedString());
}

friend bool operator==(const StrokeInputBatch& lhs,
const StrokeInputBatch& rhs) {
return lhs.data_.HasValue() && rhs.data_.HasValue()
? lhs.data_->data() == rhs.data_->data()
: lhs.data_.HasValue() == rhs.data_.HasValue();
}

template <typename H>
friend H AbslHashValue(H h, const StrokeInputBatch& batch) {
return H::combine(std::move(h),
batch.data_.HasValue() ? batch.data_->data() : nullptr);
}

private:
absl::Status PrepareForAppend(const StrokeInput& first_new_input);

Expand Down
24 changes: 24 additions & 0 deletions ink/strokes/input/stroke_input_batch_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "fuzztest/fuzztest.h"
#include "absl/hash/hash.h"
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/status/statusor.h"
Expand Down Expand Up @@ -1621,5 +1622,28 @@ TEST(StrokeInputBatchTest, AppendRangeWithIndexOutOfBounds) {
"");
}

TEST(StrokeInputBatchTest, EqualityAndHashing) {
std::vector<StrokeInput> input_vector = MakeValidTestInputSequence();
absl::StatusOr<StrokeInputBatch> batch1 =
StrokeInputBatch::Create(input_vector);
ASSERT_THAT(batch1, IsOk());

// Copy constructor should share data.
StrokeInputBatch batch2 = *batch1;
EXPECT_EQ(*batch1, batch2);
EXPECT_EQ(absl::HashOf(*batch1), absl::HashOf(batch2));

// Deep copy should NOT share data.
StrokeInputBatch batch3 = batch1->MakeDeepCopy();
EXPECT_NE(*batch1, batch3);

// Modification should trigger copy-on-write and make them unequal.
StrokeInput input = MakeValidTestInput();
input.elapsed_time =
input_vector.back().elapsed_time + Duration32::Seconds(1);
ASSERT_THAT(batch2.Append(input), IsOk());
EXPECT_NE(*batch1, batch2);
}

} // namespace
} // namespace ink
21 changes: 21 additions & 0 deletions ink/strokes/input/stroke_input_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "ink/strokes/input/stroke_input.h"

#include "gtest/gtest.h"
#include "absl/hash/hash_testing.h"
#include "absl/strings/str_cat.h"
#include "ink/geometry/angle.h"
#include "ink/types/duration.h"
Expand Down Expand Up @@ -107,5 +108,25 @@ TEST(StrokeInputTest, NoOrientation) {
EXPECT_FALSE(input.HasOrientation());
}

TEST(StrokeInputTest, EqualityAndHashing) {
StrokeInput input1 = {
.tool_type = StrokeInput::ToolType::kStylus,
.position = {1, 2},
.elapsed_time = Duration32::Seconds(3),
};
StrokeInput input2 = input1;
StrokeInput input3 = {
.tool_type = StrokeInput::ToolType::kStylus,
.position = {1, 2},
.elapsed_time = Duration32::Seconds(4),
};

EXPECT_EQ(input1, input2);
EXPECT_NE(input1, input3);

EXPECT_TRUE(
absl::VerifyTypeImplementsAbslHashCorrectly({input1, input2, input3}));
}

} // namespace
} // namespace ink
Loading