diff --git a/manim/mobject/graphing/number_line.py b/manim/mobject/graphing/number_line.py index 66772e74d4..d063f6d3b6 100644 --- a/manim/mobject/graphing/number_line.py +++ b/manim/mobject/graphing/number_line.py @@ -138,6 +138,21 @@ def construct(self): line_group = VGroup(l0, l1, l2, l3).arrange(DOWN, buff=1) self.add(line_group) + + .. manim:: NumberLineWithExcluded + :save_last_frame: + + class NumberLineWithExcluded(Scene): + def construct(self): + # Example showing how to use numbers_to_exclude parameter + number_line = NumberLine( + x_range=[-5, 5, 1], + length=8, + include_numbers=True, + numbers_to_exclude=[-3, 0, 2], # Exclude -3, 0, and 2 from display + font_size=24, + ) + self.add(number_line) """ def __init__( @@ -503,6 +518,23 @@ def get_number_mobject( num_mob.shift(num_mob[0].width * LEFT / 2) return num_mob + def default_numbers_to_display(self) -> np.ndarray: + """Returns the default numbers to display on the number line. + + This method returns the tick range excluding numbers that are in + the numbers_to_exclude list. + + Returns + ------- + np.ndarray + Array of numbers that should be displayed by default. + """ + tick_range = self.get_tick_range() + if self.numbers_to_exclude: + # Filter out excluded numbers + return np.array([x for x in tick_range if x not in self.numbers_to_exclude]) + return tick_range + def get_number_mobjects(self, *numbers: float, **kwargs: Any) -> VGroup: if len(numbers) == 0: numbers = self.default_numbers_to_display() diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py index 206a114cc3..da38c21c54 100644 --- a/manim/mobject/opengl/opengl_mobject.py +++ b/manim/mobject/opengl/opengl_mobject.py @@ -2683,7 +2683,34 @@ def construct(self): func = path_func if key in ("points", "bounding_box") else interpolate - self.data[key][:] = func(mobject1.data[key], mobject2.data[key], alpha) + # Check for shape compatibility before interpolation + arr1 = mobject1.data[key] + arr2 = mobject2.data[key] + target_arr = self.data[key] + + try: + interpolated_data = func(arr1, arr2, alpha) + # Ensure the interpolated data has compatible shape with target array + if target_arr.shape != interpolated_data.shape: # type: ignore[union-attr] + # If shapes don't match, try to resize target array to match interpolated data + if hasattr(interpolated_data, "shape"): + # For numpy arrays, copy the data directly + self.data[key] = interpolated_data.copy() # type: ignore[union-attr] + else: + # For other data types, assign directly + self.data[key] = interpolated_data + else: + target_arr[:] = interpolated_data + except (ValueError, TypeError): + # If interpolation fails due to shape mismatch, fall back to mobject2 data + # This ensures animations don't crash with very short run times + if hasattr(arr2, "shape"): + if target_arr.shape != arr2.shape: + self.data[key] = arr2.copy() + else: + target_arr[:] = arr2 + else: + self.data[key] = arr2 for key in self.uniforms: if key != "fixed_orientation_center":