Skip to content

Commit 42e4e6b

Browse files
Ink Open Sourcecopybara-github
authored andcommitted
Add kTimeSinceStrokeEndInSeconds brush behavior source
This allows for one-shot whole-stroke animations that occur after input is finished. PiperOrigin-RevId: 872922797
1 parent 3273d67 commit 42e4e6b

28 files changed

+459
-191
lines changed

ink/brush/brush_behavior.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ bool IsValidBehaviorSource(BrushBehavior::Source source) {
6161
case BrushBehavior::Source::kPredictedTimeElapsedInSeconds:
6262
case BrushBehavior::Source::kDistanceRemainingInMultiplesOfBrushSize:
6363
case BrushBehavior::Source::kTimeSinceInputInSeconds:
64+
case BrushBehavior::Source::kTimeSinceStrokeEndInSeconds:
6465
case BrushBehavior::Source::
6566
kAccelerationInMultiplesOfBrushSizePerSecondSquared:
6667
case BrushBehavior::Source::
@@ -95,9 +96,10 @@ absl::Status ValidateSourceAndOutOfRangeCombination(
9596
BrushBehavior::Source source, BrushBehavior::OutOfRange out_of_range) {
9697
switch (source) {
9798
case BrushBehavior::Source::kTimeSinceInputInSeconds:
99+
case BrushBehavior::Source::kTimeSinceStrokeEndInSeconds:
98100
if (out_of_range != BrushBehavior::OutOfRange::kClamp) {
99101
return absl::InvalidArgumentError(
100-
"`Source::kTimeSinceInputInSeconds` must only be used with "
102+
"`kTimeSince*` sources can only be used with a "
101103
"`source_out_of_range_behavior` of `kClamp`.");
102104
}
103105
break;
@@ -514,6 +516,8 @@ std::string ToFormattedString(BrushBehavior::Source source) {
514516
return "kDistanceRemainingInMultiplesOfBrushSize";
515517
case BrushBehavior::Source::kTimeSinceInputInSeconds:
516518
return "kTimeSinceInputInSeconds";
519+
case BrushBehavior::Source::kTimeSinceStrokeEndInSeconds:
520+
return "kTimeSinceStrokeEndInSeconds";
517521
case BrushBehavior::Source::
518522
kAccelerationInMultiplesOfBrushSizePerSecondSquared:
519523
return "kAccelerationInMultiplesOfBrushSizePerSecondSquared";

ink/brush/brush_behavior.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ struct BrushBehavior {
107107
// List of input properties along with their units that can act as sources for
108108
// a `BrushBehavior`.
109109
//
110-
// This should match the enum in BrushBehavior.kt and
111-
// BrushFamilyExtensions.kt.
110+
// This should match the enum in BrushBehavior.kt.
112111
//
113112
// Behaviors that consider properties of the stroke input do not consider
114113
// alterations to the visible position of that point in the stroke by brush
@@ -180,10 +179,16 @@ struct BrushBehavior {
180179
kDistanceRemainingInMultiplesOfBrushSize,
181180
// Time elapsed since the modeled stroke input. This continues to increase
182181
// even after all stroke inputs have completed, and can be used to drive
183-
// stroke animations. These enumerators are only compatible with a
182+
// wet-layer stroke animations. This source is only compatible with a
184183
// `source_out_of_range_behavior` of `kClamp`, to ensure that the animation
185184
// will eventually end.
186185
kTimeSinceInputInSeconds,
186+
// Time elapsed since the final input of the stroke, or zero if the final
187+
// input hasn't arrived yet. This can be used to drive wet-layer stroke
188+
// animations that should occur after the final input. This source is only
189+
// compatible with a `source_out_of_range_behavior` of `kClamp`, to ensure
190+
// that the animation will eventually end.
191+
kTimeSinceStrokeEndInSeconds,
187192
// Absolute acceleration of the modeled stroke input in multiples of the
188193
// brush size per second squared. Note that this value doesn't take into
189194
// account brush behaviors that offset the position of that visible point in
@@ -247,8 +252,7 @@ struct BrushBehavior {
247252

248253
// List of tip properties that can be modified by a `BrushBehavior`.
249254
//
250-
// This should match the enums in BrushBehavior.kt and
251-
// BrushFamilyExtensions.kt.
255+
// This should match the enum in BrushBehavior.kt.
252256
enum class Target : int8_t {
253257
// `kWidthMultiplier` and `kHeightMultiplier` scale the brush-tip size along
254258
// one dimension, starting from the values calculated using
@@ -324,8 +328,7 @@ struct BrushBehavior {
324328

325329
// List of vector tip properties that can be modified by a `BrushBehavior`.
326330
//
327-
// This should match the enums in BrushBehavior.kt and
328-
// BrushFamilyExtensions.kt.
331+
// This should match the enum in BrushBehavior.kt.
329332
enum class PolarTarget : int8_t {
330333
// Adds the vector to the brush tip's absolute x/y position in stroke space,
331334
// where the angle input is measured in radians and the magnitude input is
@@ -349,8 +352,7 @@ struct BrushBehavior {
349352
// The desired behavior when an input value is outside the bounds of
350353
// `source_value_range`.
351354
//
352-
// This should match the enum in BrushBehavior.kt and
353-
// BrushFamilyExtensions.kt.
355+
// This should match the enum in BrushBehavior.kt.
354356
enum class OutOfRange : int8_t {
355357
// Values outside the range will be clamped to not exceed the bounds.
356358
kClamp,
@@ -387,8 +389,7 @@ struct BrushBehavior {
387389

388390
// List of input properties that might not be reported by `StrokeInput`.
389391
//
390-
// This should match the enums in BrushBehavior.kt and
391-
// BrushFamilyExtensions.kt.
392+
// This should match the enum in BrushBehavior.kt.
392393
enum OptionalInputProperty : int8_t {
393394
kPressure,
394395
kTilt,

ink/brush/brush_behavior_test.cc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ TEST(BrushBehaviorTest, StringifySource) {
8080
"kDistanceRemainingInMultiplesOfBrushSize");
8181
EXPECT_EQ(absl::StrCat(BrushBehavior::Source::kTimeSinceInputInSeconds),
8282
"kTimeSinceInputInSeconds");
83+
EXPECT_EQ(absl::StrCat(BrushBehavior::Source::kTimeSinceStrokeEndInSeconds),
84+
"kTimeSinceStrokeEndInSeconds");
8385
EXPECT_EQ(
8486
absl::StrCat(BrushBehavior::Source::
8587
kAccelerationInMultiplesOfBrushSizePerSecondSquared),
@@ -729,8 +731,18 @@ TEST(BrushBehaviorTest, ValidateSourceNode) {
729731
});
730732
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
731733
EXPECT_THAT(status.message(),
732-
HasSubstr("kTimeSinceInputInSeconds` must only be used with "
733-
"`source_out_of_range_behavior` of `kClamp"));
734+
HasSubstr("`kTimeSince*` sources can only be used with a "
735+
"`source_out_of_range_behavior` of `kClamp`"));
736+
737+
status = brush_internal::ValidateBrushBehaviorNode(BrushBehavior::SourceNode{
738+
.source = BrushBehavior::Source::kTimeSinceStrokeEndInSeconds,
739+
.source_out_of_range_behavior = BrushBehavior::OutOfRange::kMirror,
740+
.source_value_range = {0, 2},
741+
});
742+
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
743+
EXPECT_THAT(status.message(),
744+
HasSubstr("`kTimeSince*` sources can only be used with a "
745+
"`source_out_of_range_behavior` of `kClamp`"));
734746

735747
status = brush_internal::ValidateBrushBehaviorNode(BrushBehavior::SourceNode{
736748
.source = BrushBehavior::Source::kInputDistanceTraveledInCentimeters,

ink/brush/brush_family_test.cc

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,9 @@ TEST(BrushFamilyTest, CreateWithInvalidBehaviorSourceAndOutOfRangeBehavior) {
522522
BrushTip brush_tip = {
523523
.behaviors = {BrushBehavior{{
524524
BrushBehavior::SourceNode{
525+
.source = BrushBehavior::Source::kTimeSinceInputInSeconds,
526+
.source_out_of_range_behavior =
527+
BrushBehavior::OutOfRange::kRepeat,
525528
.source_value_range = {0, 1},
526529
},
527530
BrushBehavior::TargetNode{
@@ -530,17 +533,11 @@ TEST(BrushFamilyTest, CreateWithInvalidBehaviorSourceAndOutOfRangeBehavior) {
530533
},
531534
}}},
532535
};
533-
BrushBehavior::SourceNode* source_node =
534-
&std::get<BrushBehavior::SourceNode>(brush_tip.behaviors[0].nodes[0]);
535-
536-
source_node->source = BrushBehavior::Source::kTimeSinceInputInSeconds;
537-
source_node->source_out_of_range_behavior =
538-
BrushBehavior::OutOfRange::kRepeat;
539-
absl::Status status = BrushFamily::Create(brush_tip, BrushPaint{}).status();
540-
EXPECT_EQ(status.code(), kInvalidArgument);
541-
EXPECT_THAT(status.message(),
542-
HasSubstr("kTimeSinceInputInSeconds` must only be used with "
543-
"`source_out_of_range_behavior` of `kClamp"));
536+
EXPECT_THAT(
537+
BrushFamily::Create(brush_tip, BrushPaint{}).status(),
538+
StatusIs(kInvalidArgument,
539+
HasSubstr("`kTimeSince*` sources can only be used with a "
540+
"`source_out_of_range_behavior` of `kClamp`")));
544541
}
545542

546543
TEST(BrushFamilyTest, CreateWithInvalidBehaviorTargetModifierRange) {

ink/brush/fuzz_domains.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ Domain<BrushBehavior::OutOfRange> ValidBrushBehaviorOutOfRangeForSource(
177177
BrushBehavior::Source source) {
178178
switch (source) {
179179
case BrushBehavior::Source::kTimeSinceInputInSeconds:
180+
case BrushBehavior::Source::kTimeSinceStrokeEndInSeconds:
180181
return Just(BrushBehavior::OutOfRange::kClamp);
181182
default:
182183
return ArbitraryBrushBehaviorOutOfRange();
@@ -206,6 +207,7 @@ Domain<BrushBehavior::Source> ArbitraryBrushBehaviorSource() {
206207
BrushBehavior::Source::kPredictedTimeElapsedInSeconds,
207208
BrushBehavior::Source::kDistanceRemainingInMultiplesOfBrushSize,
208209
BrushBehavior::Source::kTimeSinceInputInSeconds,
210+
BrushBehavior::Source::kTimeSinceStrokeEndInSeconds,
209211
BrushBehavior::Source::
210212
kAccelerationInMultiplesOfBrushSizePerSecondSquared,
211213
BrushBehavior::Source::

ink/storage/brush.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ proto::BrushBehavior::Source EncodeBrushBehaviorSource(
187187
SOURCE_DISTANCE_REMAINING_IN_MULTIPLES_OF_BRUSH_SIZE;
188188
case BrushBehavior::Source::kTimeSinceInputInSeconds:
189189
return proto::BrushBehavior::SOURCE_TIME_SINCE_INPUT_IN_SECONDS;
190+
case BrushBehavior::Source::kTimeSinceStrokeEndInSeconds:
191+
return proto::BrushBehavior::SOURCE_TIME_SINCE_STROKE_END_IN_SECONDS;
190192
case BrushBehavior::Source::
191193
kAccelerationInMultiplesOfBrushSizePerSecondSquared:
192194
return proto::BrushBehavior::
@@ -372,6 +374,8 @@ absl::StatusOr<BrushBehavior::Source> DecodeBrushBehaviorSource(
372374
case proto::BrushBehavior::
373375
SOURCE_DISTANCE_REMAINING_AS_FRACTION_OF_STROKE_LENGTH:
374376
return BrushBehavior::Source::kDistanceRemainingAsFractionOfStrokeLength;
377+
case proto::BrushBehavior::SOURCE_TIME_SINCE_STROKE_END_IN_SECONDS:
378+
return BrushBehavior::Source::kTimeSinceStrokeEndInSeconds;
375379
default:
376380
return absl::InvalidArgumentError(absl::StrCat(
377381
"invalid ink.proto.BrushBehavior.Source value: ", source_proto));

ink/storage/proto/brush_family.proto

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,8 +651,8 @@ message BrushBehavior {
651651

652652
// Time elapsed (in seconds) since the modeled stroke input. This continues
653653
// to increase even after all stroke inputs have completed, and can be used
654-
// to drive stroke animations. These enumerators are only compatible with a
655-
// `source_out_of_range_behavior` of `OUT_OF_RANGE_CLAMP`, to ensure that
654+
// to drive wet-layer stroke animations. This source is only compatible with
655+
// a `source_out_of_range_behavior` of `OUT_OF_RANGE_CLAMP`, to ensure that
656656
// the animation will eventually end.
657657
SOURCE_TIME_SINCE_INPUT_IN_SECONDS = 19;
658658

@@ -755,6 +755,13 @@ message BrushBehavior {
755755
// the input path, as a fraction of the current total length of the stroke.
756756
// This value changes for each input as inputs are added.
757757
SOURCE_DISTANCE_REMAINING_AS_FRACTION_OF_STROKE_LENGTH = 39;
758+
759+
// Time elapsed (in seconds) since the final input of the stroke, or zero if
760+
// the final input hasn't arrived yet. This can be used to drive wet-layer
761+
// stroke animations that should occur after the final input. This source is
762+
// only compatible with a `source_out_of_range_behavior` of
763+
// `OUT_OF_RANGE_CLAMP`, to ensure that the animation will eventually end.
764+
SOURCE_TIME_SINCE_STROKE_END_IN_SECONDS = 40;
758765
}
759766
// LINT.ThenChange(../../brush/brush_behavior.h:source)
760767

ink/strokes/in_progress_stroke.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ absl::Status InProgressStroke::UpdateShape(Duration32 current_elapsed_time) {
165165

166166
input_modeler_.ExtendStroke(queued_real_inputs_, queued_predicted_inputs_,
167167
current_elapsed_time);
168+
if (inputs_are_finished_) {
169+
input_modeler_.FinishStrokeInputs();
170+
}
168171
uint32_t num_coats = BrushCoatCount();
169172
for (uint32_t i = 0; i < num_coats; ++i) {
170173
StrokeShapeUpdate update = shape_builders_[i].ExtendStroke(input_modeler_);

ink/strokes/internal/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ cc_test(
151151
"//ink/types:type_matchers",
152152
"@com_google_absl//absl/log:absl_check",
153153
"@com_google_absl//absl/status:status_matchers",
154+
"@com_google_absl//absl/status:statusor",
154155
"@com_google_fuzztest//fuzztest",
155156
"@com_google_googletest//:gtest_main",
156157
],
@@ -239,6 +240,7 @@ cc_library(
239240
":brush_tip_state",
240241
":easing_implementation",
241242
":modeled_stroke_input",
243+
":noise_generator",
242244
"//ink/brush:brush_behavior",
243245
"//ink/brush:brush_tip",
244246
"//ink/geometry:angle",
@@ -591,12 +593,14 @@ cc_test(
591593
":stroke_shape_builder",
592594
":stroke_shape_update",
593595
":stroke_vertex",
596+
"//ink/brush:brush_behavior",
594597
"//ink/brush:brush_coat",
595598
"//ink/brush:brush_family",
596599
"//ink/brush:brush_paint",
597600
"//ink/brush:brush_tip",
598601
"//ink/geometry:envelope",
599602
"//ink/geometry:mutable_mesh",
603+
"//ink/geometry:rect",
600604
"//ink/geometry:type_matchers",
601605
"//ink/geometry/internal:algorithms",
602606
"//ink/strokes/input:stroke_input_batch",

ink/strokes/internal/brush_tip_extruder.cc

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,8 @@ void BrushTipExtruder::StartStroke(float brush_epsilon, bool is_particle_brush,
6262
max_chord_height_ = GetMaxChordHeight(brush_epsilon);
6363
simplification_threshold_ = GetSimplificationThreshold(brush_epsilon);
6464
is_particle_brush_ = is_particle_brush;
65-
extrusions_.clear();
66-
saved_extrusion_data_count_ = 0;
67-
deleted_save_point_extrusions_.clear();
6865
geometry_.Reset(MutableMeshView(mesh));
69-
bounds_ = {};
70-
// Pre-allocate the first outline.
71-
num_outlines_ = 1;
72-
if (outlines_.empty()) {
73-
outlines_.resize(1);
74-
}
75-
// Clear all the outlines from the previous stroke.
76-
for (StrokeOutline& outline : outlines_) {
77-
outline.TruncateIndices({0, 0});
78-
}
66+
RestartStroke();
7967
}
8068

8169
namespace {
@@ -137,6 +125,24 @@ StrokeShapeUpdate BrushTipExtruder::ExtendStroke(
137125
vertex_count_before_update);
138126
}
139127

128+
void BrushTipExtruder::RestartStroke() {
129+
ABSL_CHECK_GT(brush_epsilon_, 0) << "`StartStroke()` has not been called";
130+
extrusions_.clear();
131+
saved_extrusion_data_count_ = 0;
132+
deleted_save_point_extrusions_.clear();
133+
geometry_.Reset(geometry_.GetMeshView());
134+
bounds_ = {};
135+
// Pre-allocate the first outline.
136+
num_outlines_ = 1;
137+
if (outlines_.empty()) {
138+
outlines_.resize(1);
139+
}
140+
// Clear all the outlines from the previous stroke.
141+
for (StrokeOutline& outline : outlines_) {
142+
outline.TruncateIndices({0, 0});
143+
}
144+
}
145+
140146
void BrushTipExtruder::ClearCachedPartialBounds() {
141147
bounds_.cached_partial_bounds.Reset();
142148
bounds_.cached_partial_bounds_left_index_count = 0;

0 commit comments

Comments
 (0)