#include "paint/stroke.h" #include "test_harness.h" #include #include #include using pp::foundation::StatusCode; using pp::paint::StrokePoint; using pp::paint::StrokeSamplingConfig; using pp::paint::max_stroke_samples; using pp::paint::sample_stroke; namespace { bool near(float a, float b) { return std::fabs(a - b) < 0.0001F; } void samples_straight_line_at_fixed_spacing(pp::tests::Harness& h) { const std::array points { StrokePoint { .x = 0.0F, .y = 0.0F, .pressure = 0.25F }, StrokePoint { .x = 10.0F, .y = 0.0F, .pressure = 0.75F }, }; const auto result = sample_stroke(points, StrokeSamplingConfig { .spacing = 2.5F }); PP_EXPECT(h, result.ok()); PP_EXPECT(h, result.value().size() == 5U); PP_EXPECT(h, near(result.value()[0].x, 0.0F)); PP_EXPECT(h, near(result.value()[1].x, 2.5F)); PP_EXPECT(h, near(result.value()[2].x, 5.0F)); PP_EXPECT(h, near(result.value()[3].x, 7.5F)); PP_EXPECT(h, near(result.value()[4].x, 10.0F)); PP_EXPECT(h, near(result.value()[2].pressure, 0.5F)); PP_EXPECT(h, near(result.value()[4].distance, 10.0F)); } void carries_spacing_across_segments(pp::tests::Harness& h) { const std::array points { StrokePoint { .x = 0.0F, .y = 0.0F, .pressure = 1.0F }, StrokePoint { .x = 3.0F, .y = 0.0F, .pressure = 1.0F }, StrokePoint { .x = 3.0F, .y = 4.0F, .pressure = 0.0F }, }; const auto result = sample_stroke(points, StrokeSamplingConfig { .spacing = 2.0F }); PP_EXPECT(h, result.ok()); PP_EXPECT(h, result.value().size() == 5U); PP_EXPECT(h, near(result.value()[1].x, 2.0F)); PP_EXPECT(h, near(result.value()[1].y, 0.0F)); PP_EXPECT(h, near(result.value()[2].x, 3.0F)); PP_EXPECT(h, near(result.value()[2].y, 1.0F)); PP_EXPECT(h, near(result.value()[3].x, 3.0F)); PP_EXPECT(h, near(result.value()[3].y, 3.0F)); PP_EXPECT(h, near(result.value()[4].distance, 7.0F)); } void can_skip_endpoint_and_clamps_pressure(pp::tests::Harness& h) { const std::array points { StrokePoint { .x = 0.0F, .y = 0.0F, .pressure = -1.0F }, StrokePoint { .x = 5.0F, .y = 0.0F, .pressure = 2.0F }, }; const auto result = sample_stroke( points, StrokeSamplingConfig { .spacing = 2.0F, .include_endpoint = false, }); PP_EXPECT(h, result.ok()); PP_EXPECT(h, result.value().size() == 3U); PP_EXPECT(h, near(result.value()[0].pressure, 0.0F)); PP_EXPECT(h, near(result.value()[2].pressure, 1.0F)); PP_EXPECT(h, near(result.value()[2].distance, 4.0F)); } void skips_zero_length_segments_without_resetting_spacing(pp::tests::Harness& h) { const std::array points { StrokePoint { .x = 0.0F, .y = 0.0F, .pressure = 0.2F }, StrokePoint { .x = 0.0F, .y = 0.0F, .pressure = 0.8F }, StrokePoint { .x = 6.0F, .y = 0.0F, .pressure = 0.8F }, StrokePoint { .x = 6.0F, .y = 0.0F, .pressure = 0.1F }, StrokePoint { .x = 6.0F, .y = 4.0F, .pressure = 0.1F }, }; const auto result = sample_stroke(points, StrokeSamplingConfig { .spacing = 3.0F }); PP_EXPECT(h, result.ok()); PP_EXPECT(h, result.value().size() == 5U); PP_EXPECT(h, near(result.value()[1].x, 3.0F)); PP_EXPECT(h, near(result.value()[1].y, 0.0F)); PP_EXPECT(h, near(result.value()[2].x, 6.0F)); PP_EXPECT(h, near(result.value()[2].y, 0.0F)); PP_EXPECT(h, near(result.value()[3].x, 6.0F)); PP_EXPECT(h, near(result.value()[3].y, 3.0F)); PP_EXPECT(h, near(result.value()[4].distance, 10.0F)); } void samples_large_stroke_with_deterministic_count(pp::tests::Harness& h) { const std::array points { StrokePoint { .x = 0.0F, .y = 0.0F, .pressure = 0.0F }, StrokePoint { .x = 1000.0F, .y = 0.0F, .pressure = 1.0F }, }; const auto result = sample_stroke( points, StrokeSamplingConfig { .spacing = 1.0F, .max_samples = 1001U, }); PP_EXPECT(h, result.ok()); PP_EXPECT(h, result.value().size() == 1001U); PP_EXPECT(h, near(result.value()[0].distance, 0.0F)); PP_EXPECT(h, near(result.value()[500].x, 500.0F)); PP_EXPECT(h, near(result.value()[500].pressure, 0.5F)); PP_EXPECT(h, near(result.value().back().x, 1000.0F)); PP_EXPECT(h, near(result.value().back().distance, 1000.0F)); } void rejects_invalid_sampling_inputs(pp::tests::Harness& h) { const std::array one_point { StrokePoint { .x = 0.0F, .y = 0.0F }, }; const std::array zero_length { StrokePoint { .x = 1.0F, .y = 1.0F }, StrokePoint { .x = 1.0F, .y = 1.0F }, }; const std::array non_finite { StrokePoint { .x = 0.0F, .y = 0.0F }, StrokePoint { .x = std::nanf(""), .y = 1.0F }, }; const std::array non_finite_pressure { StrokePoint { .x = 0.0F, .y = 0.0F }, StrokePoint { .x = 1.0F, .y = 1.0F, .pressure = std::nanf("") }, }; const std::array valid { StrokePoint { .x = 0.0F, .y = 0.0F }, StrokePoint { .x = 10.0F, .y = 0.0F }, }; const auto missing_points = sample_stroke(one_point, StrokeSamplingConfig {}); const auto bad_spacing = sample_stroke(valid, StrokeSamplingConfig { .spacing = 0.0F }); const auto infinite_spacing = sample_stroke( valid, StrokeSamplingConfig { .spacing = std::numeric_limits::infinity() }); const auto bad_limit = sample_stroke(valid, StrokeSamplingConfig { .max_samples = max_stroke_samples + 1U }); const auto no_distance = sample_stroke(zero_length, StrokeSamplingConfig {}); const auto bad_point = sample_stroke(non_finite, StrokeSamplingConfig {}); const auto bad_pressure = sample_stroke(non_finite_pressure, StrokeSamplingConfig {}); const auto too_many = sample_stroke(valid, StrokeSamplingConfig { .spacing = 1.0F, .max_samples = 2U }); PP_EXPECT(h, !missing_points.ok()); PP_EXPECT(h, missing_points.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !bad_spacing.ok()); PP_EXPECT(h, bad_spacing.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !infinite_spacing.ok()); PP_EXPECT(h, infinite_spacing.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !bad_limit.ok()); PP_EXPECT(h, bad_limit.status().code == StatusCode::out_of_range); PP_EXPECT(h, !no_distance.ok()); PP_EXPECT(h, no_distance.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !bad_point.ok()); PP_EXPECT(h, bad_point.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !bad_pressure.ok()); PP_EXPECT(h, bad_pressure.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !too_many.ok()); PP_EXPECT(h, too_many.status().code == StatusCode::out_of_range); } } int main() { pp::tests::Harness harness; harness.run("samples_straight_line_at_fixed_spacing", samples_straight_line_at_fixed_spacing); harness.run("carries_spacing_across_segments", carries_spacing_across_segments); harness.run("can_skip_endpoint_and_clamps_pressure", can_skip_endpoint_and_clamps_pressure); harness.run("skips_zero_length_segments_without_resetting_spacing", skips_zero_length_segments_without_resetting_spacing); harness.run("samples_large_stroke_with_deterministic_count", samples_large_stroke_with_deterministic_count); harness.run("rejects_invalid_sampling_inputs", rejects_invalid_sampling_inputs); return harness.finish(); }