ManimCommunity / ManimCommunity/manim

Support for Removable Discontinuities in 3D Surface Plotting

Open
#4,290 1 comment 1 reaction 0 assignees View on GitHub
new feature
Dominant language
Python
Stars
40.9k
Forks
3.1k
Avg merge
3d 12h
Merged PRs (30d)
25

Description

When graphing with a function of one variable with a _removable_ discontinuity (function is not defined at a point but otherwise continuous), we can pass in a `discontinuities` parameter into `Axes.plot()` such that we "skip" over that point and treat the discontinuous function as a piecewise continuous function such that it can be graphed successfully.

For example, for a function $f$ given by

$$ f(x) = \frac{x^2 - 9}{x-3} $$

with an evitable discontinuity at $x = 3$, the following code works perfectly:

```python
from manim import *

class OneVariable(Scene):
def construct(self):
ax = Axes(
x_range=[0, 10],
y_range=[0, 10],
x_length=12,
y_length=6
).scale(0.8)
x_label, y_label = ax.get_axis_labels("x", "f(x)")

def func(x):
return (x**2 - 9) / (x - 3) - 2

self.add(ax.plot(func, discontinuities=[3]))
```

This creates the following image:
![Image](https://github.com/user-attachments/assets/e6e8230a-fb53-427f-b436-1e787d2be869)

## Description of Proposed Feature

Essentially the same feature for a function of two variables—whether using `Surface`, or `plot_surface` directly—having a parameter for the coordinates at which that function has a _removable_ discontinuity, such that the graph "skips" graphing that point for $f$.

This would expand the range of functions that can be graphed in 3D.

## Why the Workaround Fails

Say you want to create a 3D surface representing the function:

$$ f(x, y) = \frac{x^2y}{x^2 + y^2} $$

At the point $(0, 0)$, this function has a denominator of $0$, and thus is not defined. It's impossible to graph it. Hence we attempt to "skip" graphing that point by returning `None` for the undefined point:

```python
from manim import *

class SurfaceTest(ThreeDScene):
def construct(self):
ax = ThreeDAxes()
self.add(ax)

epsilon = 0.001

def func(x, y): # removable discontinuity at (0, 0)
if x == 0 and y == 0:
return None
else:
return ((x**2)*y) / (x**2 + y**2)

surface = Surface(
lambda u, v: ax.c2p(u, v, func(u, v)),
u_range=[-5, 5],
v_range=[-5, 5],
resolution=(16, 16),
fill_color=ORANGE,
fill_opacity=0.8,
checkerboard_colors=None
)
self.play(FadeIn(surface))
self.wait()
self.play(FadeOut(surface))
```

But this does not work, for the same reason that it wouldn't work in a function of one variable. The lambda function in `Surface` does not know how to handle `None`, so it's actually impossible to skip graphing a point. The analogous solution to the one-variable scenario would be to split up the surface into different pieces, which would themselves be surfaces or curves. The issue with this is that if we have multiple discontinuities, this becomes a very cumbersome process.

## Additional Comments

I'm basing my knowledge of handling discontinuities on the following source:
https://github.com/ManimCommunity/manim/blob/main/manim/mobject/graphing/functions.py

Contributor guide

Open the contributing guide

Research direction

Start with manim/mobject/graphing/functions.py to compare the existing discontinuities handling for Axes.plot(), then trace Surface and plot_surface behavior for two-variable functions. Determine how removable-discontinuity coordinates should be supplied and represented without passing None through the surface mapping. Done means a 3D surface can skip specified undefined points, with coverage for the example behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
computer-graphics
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.