@@ -96,6 +96,8 @@ Duration32 GetTimeSinceInput(const InputModelerState& input_modeler_state,
9696 return input_modeler_state.complete_elapsed_time - input.elapsed_time ;
9797}
9898
99+ // Returns the value of the given `Source` at the given modeled input, or
100+ // `std::nullopt` if the source value is indeterminate at that input.
99101std::optional<float > GetSourceValue (
100102 const ModeledStrokeInput& input, std::optional<Angle> travel_direction,
101103 float brush_size, const InputModelerState& input_modeler_state,
@@ -313,14 +315,19 @@ void ProcessBehaviorNodeImpl(const BrushBehavior::SourceNode& node,
313315 return ;
314316 }
315317
316- context. stack . push_back ( ApplyOutOfRangeBehavior (
318+ float value = ApplyOutOfRangeBehavior (
317319 node.source_out_of_range_behavior ,
318320 InverseLerp (node.source_value_range [0 ], node.source_value_range [1 ],
319- *source_value)));
321+ *source_value));
322+ if (!std::isfinite (value)) {
323+ value = kNullBehaviorNodeValue ;
324+ }
325+ context.stack .push_back (value);
320326}
321327
322328void ProcessBehaviorNodeImpl (const BrushBehavior::ConstantNode& node,
323329 const BehaviorNodeContext& context) {
330+ ABSL_DCHECK (std::isfinite (node.value ));
324331 context.stack .push_back (node.value );
325332}
326333
@@ -364,7 +371,12 @@ void ProcessBehaviorNodeImpl(const NoiseNodeImplementation& node,
364371 } break ;
365372 }
366373 NoiseGenerator& generator = context.noise_generators [node.generator_index ];
367- generator.AdvanceInputBy (advance_by);
374+ // If the above calculation produces an undefined `advance_by` value (e.g. due
375+ // to extreme input values overflowing and producing ill-defined computed
376+ // values), just don't advance the noise generator.
377+ if (!std::isnan (advance_by)) {
378+ generator.AdvanceInputBy (advance_by);
379+ }
368380 context.stack .push_back (generator.CurrentOutputValue ());
369381}
370382
@@ -389,17 +401,19 @@ void ProcessBehaviorNodeImpl(const BrushBehavior::ToolTypeFilterNode& node,
389401void ProcessBehaviorNodeImpl (const DampingNodeImplementation& node,
390402 const BehaviorNodeContext& context) {
391403 ABSL_DCHECK (!context.stack .empty ());
392- float & damped_value = context.damped_values [node.damping_index ];
404+ float old_damped_value = context.damped_values [node.damping_index ];
405+ float new_damped_value = kNullBehaviorNodeValue ;
393406 float input = context.stack .back ();
394407 if (IsNullBehaviorNodeValue (input)) {
395408 // Input is null, so use previous damped value unchanged.
396- } else if (IsNullBehaviorNodeValue (damped_value) ||
409+ new_damped_value = old_damped_value;
410+ } else if (IsNullBehaviorNodeValue (old_damped_value) ||
397411 node.damping_gap == 0 .0f ) {
398412 // Input is non-null. If previous damped value is null, then this is the
399413 // first non-null input, so snap the damped value to the input. Or, if the
400414 // damping_gap is zero, then there's no damping to be done, so also snap the
401415 // damped value to the input.
402- damped_value = input;
416+ new_damped_value = input;
403417 } else {
404418 // Input and previous damped value are both non-null, so move the damped
405419 // value towards the input according to the damping settings. Note that a
@@ -411,7 +425,7 @@ void ProcessBehaviorNodeImpl(const DampingNodeImplementation& node,
411425 // If no mapping from stroke units to physical units is available, then
412426 // don't perform any damping (i.e. snap damped value to input).
413427 if (!context.input_modeler_state .stroke_unit_length .has_value ()) {
414- damped_value = input;
428+ new_damped_value = input;
415429 break ;
416430 }
417431 PhysicalDistance damping_distance =
@@ -420,34 +434,49 @@ void ProcessBehaviorNodeImpl(const DampingNodeImplementation& node,
420434 *context.input_modeler_state .stroke_unit_length *
421435 (context.current_input .traveled_distance -
422436 context.previous_input_metrics ->traveled_distance );
423- damped_value = DampOffsetTransition (
424- input, damped_value , traveled_distance_delta, damping_distance);
437+ new_damped_value = DampOffsetTransition (
438+ input, old_damped_value , traveled_distance_delta, damping_distance);
425439 } break ;
426440 case BrushBehavior::DampingSource::kDistanceInMultiplesOfBrushSize : {
427441 float damping_distance = context.brush_size * node.damping_gap ;
428442 float traveled_distance_delta =
429443 context.current_input .traveled_distance -
430444 context.previous_input_metrics ->traveled_distance ;
431- damped_value = DampOffsetTransition (
432- input, damped_value , traveled_distance_delta, damping_distance);
445+ new_damped_value = DampOffsetTransition (
446+ input, old_damped_value , traveled_distance_delta, damping_distance);
433447 } break ;
434448 case BrushBehavior::DampingSource::kTimeInSeconds : {
435449 Duration32 damping_time = Duration32::Seconds (node.damping_gap );
436450 Duration32 elapsed_time_delta =
437451 context.current_input .elapsed_time -
438452 context.previous_input_metrics ->elapsed_time ;
439- damped_value = DampOffsetTransition (input, damped_value,
440- elapsed_time_delta, damping_time);
453+ new_damped_value = DampOffsetTransition (
454+ input, old_damped_value, elapsed_time_delta, damping_time);
441455 } break ;
442456 }
443457 }
444- context.stack .back () = damped_value;
458+ // If the calculation above produced a null or non-finite damped value
459+ // (e.g. due to float overflow or a null input), then leave the old damped
460+ // value unchanged.
461+ if (!std::isfinite (new_damped_value)) {
462+ new_damped_value = old_damped_value;
463+ }
464+ context.damped_values [node.damping_index ] = new_damped_value;
465+ context.stack .back () = new_damped_value;
445466}
446467
447468void ProcessBehaviorNodeImpl (const EasingImplementation& node,
448469 const BehaviorNodeContext& context) {
449470 ABSL_DCHECK (!context.stack .empty ());
450- context.stack .back () = node.GetY (context.stack .back ());
471+ float input = context.stack .back ();
472+ float * result = &context.stack .back ();
473+ if (IsNullBehaviorNodeValue (input)) return ;
474+ *result = node.GetY (input);
475+ // If the easing function resulted in a non-finite value (e.g. due to overflow
476+ // to infinity), treat the result as null.
477+ if (!std::isfinite (*result)) {
478+ *result = kNullBehaviorNodeValue ;
479+ }
451480}
452481
453482void ProcessBehaviorNodeImpl (const BrushBehavior::BinaryOpNode& node,
@@ -515,8 +544,14 @@ void ProcessBehaviorNodeImpl(const TargetNodeImplementation& node,
515544 float input = context.stack .back ();
516545 context.stack .pop_back ();
517546 if (IsNullBehaviorNodeValue (input)) return ;
518- context.target_modifiers [node.target_index ] =
547+
548+ float modifier =
519549 Lerp (node.target_modifier_range [0 ], node.target_modifier_range [1 ], input);
550+ // If the new modifier is non-finite (e.g. due to float overflow), then leave
551+ // the previous modifier unchanged.
552+ if (!std::isfinite (modifier)) return ;
553+
554+ context.target_modifiers [node.target_index ] = modifier;
520555}
521556
522557void ProcessBehaviorNodeImpl (const PolarTargetNodeImplementation& node,
@@ -530,10 +565,15 @@ void ProcessBehaviorNodeImpl(const PolarTargetNodeImplementation& node,
530565 IsNullBehaviorNodeValue (magnitude_input)) {
531566 return ;
532567 }
568+
533569 Vec modifier = Vec::FromDirectionAndMagnitude (
534570 Angle::Radians (
535571 Lerp (node.angle_range [0 ], node.angle_range [1 ], angle_input)),
536572 Lerp (node.magnitude_range [0 ], node.magnitude_range [1 ], magnitude_input));
573+ // If the new modifier vector is non-finite (e.g. due to float overflow), then
574+ // leave the previous modifier vector unchanged.
575+ if (!std::isfinite (modifier.x ) || !std::isfinite (modifier.y )) return ;
576+
537577 context.target_modifiers [node.target_x_index ] = modifier.x ;
538578 context.target_modifiers [node.target_y_index ] = modifier.y ;
539579}
0 commit comments