mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Drop redundant OpenGL files and add metaclass support for Surface (#1843)
* replace old with new * dropped redundant files * replace examples in example_Scenes * replace opengl_ in test * ammend __init__ * opengl_compatibilty for Surface * make resolution a tuple for opengl compat * resolution changes in opengl.py * rework sphere and enable dual renderer for 3d shapes * adjust res for Torus * actually drop file * flip cairo_sphere resolution * changes * render each submobject individually * make BraceLabel appear * remove if config.renderer==opengl from mobject.py * rewrite tests * properly deprecate ParametricSurface * revert rendering each submobject individually * initialize OpenGLRenderer in Scene if config.renderer is opengl * fix bases when config.renderer is changed * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * actually push test * remove unused bounding_box + data from mobject.py * overwrite tests * use #1899 to switch renderer at runtime to make tests work * attempt at fixing tests * add renderer=cairo at the end of the test?? * rewrite failing test * remove self.attr = self.attr for bracelabel * remove accidental line in test * remove unecessary bloat and make set_fill_by_value dual compatible * update to remove deprecation warning * add ParametricSurface to __all__ * deprecate ParametricSurface * change some examples * adjust docs Co-authored-by: Darylgolden <darylgolden@gmail.com> Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Darylgolden <darylgolden@gmail.com>
This commit is contained in:
parent
a558d3751f
commit
223c2f86d0
24 changed files with 203 additions and 351 deletions
|
|
@ -518,13 +518,13 @@ Special Camera Settings
|
|||
|
||||
.. manim:: ThreeDLightSourcePosition
|
||||
:save_last_frame:
|
||||
:ref_classes: ThreeDScene ThreeDAxes ParametricSurface
|
||||
:ref_classes: ThreeDScene ThreeDAxes Surface
|
||||
:ref_methods: ThreeDScene.set_camera_orientation
|
||||
|
||||
class ThreeDLightSourcePosition(ThreeDScene):
|
||||
def construct(self):
|
||||
axes = ThreeDAxes()
|
||||
sphere = ParametricSurface(
|
||||
sphere = Surface(
|
||||
lambda u, v: np.array([
|
||||
1.5 * np.cos(u) * np.cos(v),
|
||||
1.5 * np.cos(u) * np.sin(v),
|
||||
|
|
@ -569,7 +569,7 @@ Special Camera Settings
|
|||
|
||||
.. manim:: ThreeDSurfacePlot
|
||||
:save_last_frame:
|
||||
:ref_classes: ThreeDScene ParametricSurface
|
||||
:ref_classes: ThreeDScene Surface
|
||||
|
||||
class ThreeDSurfacePlot(ThreeDScene):
|
||||
def construct(self):
|
||||
|
|
@ -584,7 +584,7 @@ Special Camera Settings
|
|||
z = np.exp(-(d ** 2 / (2.0 * sigma ** 2)))
|
||||
return np.array([x, y, z])
|
||||
|
||||
gauss_plane = ParametricSurface(
|
||||
gauss_plane = Surface(
|
||||
param_gauss,
|
||||
resolution=(resolution_fa, resolution_fa),
|
||||
v_range=[-2, +2],
|
||||
|
|
|
|||
|
|
@ -75,11 +75,9 @@ class TextTest(Scene):
|
|||
def construct(self):
|
||||
import string
|
||||
|
||||
text = OpenGLText(
|
||||
string.ascii_lowercase, stroke_width=4, stroke_color=BLUE
|
||||
).scale(2)
|
||||
text = Text(string.ascii_lowercase, stroke_width=4, stroke_color=BLUE).scale(2)
|
||||
text2 = (
|
||||
OpenGLText(string.ascii_uppercase, stroke_width=4, stroke_color=BLUE)
|
||||
Text(string.ascii_uppercase, stroke_width=4, stroke_color=BLUE)
|
||||
.scale(2)
|
||||
.next_to(text, DOWN)
|
||||
)
|
||||
|
|
@ -136,10 +134,10 @@ class ThreeDMobjectTest(Scene):
|
|||
def construct(self):
|
||||
# config["background_color"] = "#333333"
|
||||
|
||||
s = OpenGLSquare(fill_opacity=0.5).shift(2 * RIGHT)
|
||||
s = Square(fill_opacity=0.5).shift(2 * RIGHT)
|
||||
self.add(s)
|
||||
|
||||
sp = OpenGLSphere().shift(2 * LEFT)
|
||||
sp = Sphere().shift(2 * LEFT)
|
||||
self.add(sp)
|
||||
|
||||
mesh = get_plane_mesh(self.renderer.context)
|
||||
|
|
@ -288,7 +286,7 @@ class InlineShaderExample(Scene):
|
|||
def construct(self):
|
||||
config["background_color"] = "#333333"
|
||||
|
||||
c = OpenGLCircle(fill_opacity=0.7).shift(UL)
|
||||
c = Circle(fill_opacity=0.7).shift(UL)
|
||||
self.add(c)
|
||||
|
||||
shader = Shader(
|
||||
|
|
@ -395,10 +393,10 @@ class NamedShaderExample(Scene):
|
|||
|
||||
class InteractiveDevelopment(Scene):
|
||||
def construct(self):
|
||||
circle = OpenGLCircle()
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
square = OpenGLSquare()
|
||||
square = Square()
|
||||
|
||||
self.play(Create(square))
|
||||
self.wait()
|
||||
|
|
@ -449,9 +447,9 @@ class SurfaceExample(Scene):
|
|||
# self.add(surface_text)
|
||||
# self.wait(0.1)
|
||||
|
||||
torus1 = OpenGLTorus(r1=1, r2=1)
|
||||
torus2 = OpenGLTorus(r1=3, r2=1)
|
||||
sphere = OpenGLSphere(radius=3, resolution=torus1.resolution)
|
||||
torus1 = Torus(major_radius=1, minor_radius=1)
|
||||
torus2 = Torus(major_radius=3, minor_radius=1)
|
||||
sphere = Sphere(radius=3, resolution=torus1.resolution)
|
||||
# You can texture a surface with up to two images, which will
|
||||
# be interpreted as the side towards the light, and away from
|
||||
# the light. These can be either urls, or paths to a local file
|
||||
|
|
|
|||
|
|
@ -107,20 +107,6 @@ class Mobject:
|
|||
self.generate_points()
|
||||
self.init_colors()
|
||||
|
||||
# OpenGL data.
|
||||
self.data = {}
|
||||
self.depth_test = False
|
||||
self.is_fixed_in_frame = False
|
||||
self.gloss = 0.0
|
||||
self.shadow = 0.0
|
||||
self.needs_new_bounding_box = True
|
||||
self.parents = []
|
||||
self.family = [self]
|
||||
|
||||
self.init_gl_data()
|
||||
self.init_gl_points()
|
||||
self.init_gl_colors()
|
||||
|
||||
@classmethod
|
||||
def animation_override_for(
|
||||
cls, animation_class: Type["Animation"]
|
||||
|
|
@ -191,41 +177,6 @@ class Mobject:
|
|||
f"{override_func.__qualname__}."
|
||||
)
|
||||
|
||||
def init_gl_data(self):
|
||||
pass
|
||||
|
||||
def init_gl_points(self):
|
||||
pass
|
||||
|
||||
def init_gl_colors(self):
|
||||
pass
|
||||
|
||||
def get_bounding_box(self):
|
||||
if self.needs_new_bounding_box:
|
||||
self.data["bounding_box"] = self.compute_bounding_box()
|
||||
self.needs_new_bounding_box = False
|
||||
return self.data["bounding_box"]
|
||||
|
||||
def compute_bounding_box(self):
|
||||
all_points = np.vstack(
|
||||
[
|
||||
self.data["points"],
|
||||
*(
|
||||
mob.get_bounding_box()
|
||||
for mob in self.get_family()[1:]
|
||||
if mob.has_points()
|
||||
),
|
||||
]
|
||||
)
|
||||
if len(all_points) == 0:
|
||||
return np.zeros((3, self.dim))
|
||||
else:
|
||||
# Lower left and upper right corners
|
||||
mins = all_points.min(0)
|
||||
maxs = all_points.max(0)
|
||||
mids = (mins + maxs) / 2
|
||||
return np.array([mins, mids, maxs])
|
||||
|
||||
@property
|
||||
def animate(self):
|
||||
"""Used to animate the application of a method.
|
||||
|
|
@ -349,14 +300,6 @@ class Mobject:
|
|||
"""
|
||||
pass
|
||||
|
||||
def refresh_bounding_box(self, recurse_down=False, recurse_up=True):
|
||||
for mob in self.get_family(recurse_down):
|
||||
mob.needs_new_bounding_box = True
|
||||
if recurse_up:
|
||||
for parent in self.parents:
|
||||
parent.refresh_bounding_box()
|
||||
return self
|
||||
|
||||
def add(self, *mobjects: "Mobject") -> "Mobject":
|
||||
"""Add mobjects as submobjects.
|
||||
|
||||
|
|
@ -416,24 +359,13 @@ class Mobject:
|
|||
ValueError: Mobject cannot contain self
|
||||
|
||||
"""
|
||||
if config.renderer == "opengl":
|
||||
if self in mobjects:
|
||||
raise Exception("Mobject cannot contain self")
|
||||
for mobject in mobjects:
|
||||
if mobject not in self.submobjects:
|
||||
self.submobjects.append(mobject)
|
||||
if self not in mobject.parents:
|
||||
mobject.parents.append(self)
|
||||
self.assemble_family()
|
||||
return self
|
||||
else:
|
||||
for m in mobjects:
|
||||
if not isinstance(m, Mobject):
|
||||
raise TypeError("All submobjects must be of type Mobject")
|
||||
if m is self:
|
||||
raise ValueError("Mobject cannot contain self")
|
||||
self.submobjects = list_update(self.submobjects, mobjects)
|
||||
return self
|
||||
for m in mobjects:
|
||||
if not isinstance(m, Mobject):
|
||||
raise TypeError("All submobjects must be of type Mobject")
|
||||
if m is self:
|
||||
raise ValueError("Mobject cannot contain self")
|
||||
self.submobjects = list_update(self.submobjects, mobjects)
|
||||
return self
|
||||
|
||||
def __add__(self, mobject):
|
||||
raise NotImplementedError
|
||||
|
|
@ -1127,21 +1059,12 @@ class Mobject:
|
|||
:meth:`move_to`
|
||||
"""
|
||||
|
||||
if config.renderer == "opengl":
|
||||
self.apply_points_function(
|
||||
lambda points: points + vectors[0],
|
||||
about_edge=None,
|
||||
works_on_bounding_box=True,
|
||||
)
|
||||
return self
|
||||
else:
|
||||
total_vector = reduce(op.add, vectors)
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points = mob.points.astype("float")
|
||||
mob.points += total_vector
|
||||
if hasattr(mob, "data") and "points" in mob.data:
|
||||
mob.data["points"] += total_vector
|
||||
return self
|
||||
total_vector = reduce(op.add, vectors)
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points = mob.points.astype("float")
|
||||
mob.points += total_vector
|
||||
|
||||
return self
|
||||
|
||||
def scale(self, scale_factor: float, **kwargs) -> "Mobject":
|
||||
r"""Scale the size by a factor.
|
||||
|
|
@ -1184,18 +1107,10 @@ class Mobject:
|
|||
:meth:`move_to`
|
||||
|
||||
"""
|
||||
if config.renderer == "opengl":
|
||||
self.apply_points_function(
|
||||
lambda points: scale_factor * points,
|
||||
works_on_bounding_box=True,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
else:
|
||||
self.apply_points_function_about_point(
|
||||
lambda points: scale_factor * points, **kwargs
|
||||
)
|
||||
return self
|
||||
self.apply_points_function_about_point(
|
||||
lambda points: scale_factor * points, **kwargs
|
||||
)
|
||||
return self
|
||||
|
||||
def rotate_about_origin(self, angle, axis=OUT, axes=[]):
|
||||
"""Rotates the :class:`~.Mobject` about the ORIGIN, which is at [0,0,0]."""
|
||||
|
|
@ -1209,18 +1124,11 @@ class Mobject:
|
|||
**kwargs,
|
||||
):
|
||||
"""Rotates the :class:`~.Mobject` about a certain point."""
|
||||
if config.renderer == "opengl":
|
||||
rot_matrix_T = rotation_matrix_transpose(angle, axis)
|
||||
self.apply_points_function(
|
||||
lambda points: np.dot(points, rot_matrix_T), about_point, **kwargs
|
||||
)
|
||||
return self
|
||||
else:
|
||||
rot_matrix = rotation_matrix(angle, axis)
|
||||
self.apply_points_function_about_point(
|
||||
lambda points: np.dot(points, rot_matrix.T), about_point, **kwargs
|
||||
)
|
||||
return self
|
||||
rot_matrix = rotation_matrix(angle, axis)
|
||||
self.apply_points_function_about_point(
|
||||
lambda points: np.dot(points, rot_matrix.T), about_point, **kwargs
|
||||
)
|
||||
return self
|
||||
|
||||
def flip(self, axis=UP, **kwargs):
|
||||
"""Flips/Mirrors an mobject about its center.
|
||||
|
|
@ -1343,31 +1251,6 @@ class Mobject:
|
|||
# In place operations.
|
||||
# Note, much of these are now redundant with default behavior of
|
||||
# above methods
|
||||
def apply_points_function(
|
||||
self, func, about_point=None, about_edge=ORIGIN, works_on_bounding_box=False
|
||||
):
|
||||
if about_point is None and about_edge is not None:
|
||||
about_point = self.get_bounding_box_point(about_edge)
|
||||
|
||||
for mob in self.get_family():
|
||||
arrs = []
|
||||
if len(self.data["points"]):
|
||||
arrs.append(mob.data["points"])
|
||||
if works_on_bounding_box:
|
||||
arrs.append(mob.get_bounding_box())
|
||||
|
||||
for arr in arrs:
|
||||
if about_point is None:
|
||||
arr[:] = func(arr)
|
||||
else:
|
||||
arr[:] = func(arr - about_point) + about_point
|
||||
|
||||
if not works_on_bounding_box:
|
||||
self.refresh_bounding_box(recurse_down=True)
|
||||
else:
|
||||
for parent in self.parents:
|
||||
parent.refresh_bounding_box()
|
||||
return self
|
||||
|
||||
def apply_points_function_about_point(
|
||||
self, func, about_point=None, about_edge=None
|
||||
|
|
@ -1888,10 +1771,7 @@ class Mobject:
|
|||
return self.get_all_points()
|
||||
|
||||
def get_num_points(self):
|
||||
if config.renderer == "opengl":
|
||||
return len(self.data["points"])
|
||||
else:
|
||||
return len(self.points)
|
||||
return len(self.points)
|
||||
|
||||
def get_extremum_along_dim(self, points=None, dim=0, key=0):
|
||||
if points is None:
|
||||
|
|
@ -2023,18 +1903,12 @@ class Mobject:
|
|||
def get_start(self):
|
||||
"""Returns the point, where the stroke that surrounds the :class:`~.Mobject` starts."""
|
||||
self.throw_error_if_no_points()
|
||||
if config.renderer == "opengl":
|
||||
return np.array(self.data["points"][0])
|
||||
else:
|
||||
return np.array(self.points[0])
|
||||
return np.array(self.points[0])
|
||||
|
||||
def get_end(self):
|
||||
"""Returns the point, where the stroke that surrounds the :class:`~.Mobject` ends."""
|
||||
self.throw_error_if_no_points()
|
||||
if config.renderer == "opengl":
|
||||
return np.array(self.data["points"][-1])
|
||||
else:
|
||||
return np.array(self.points[-1])
|
||||
return np.array(self.points[-1])
|
||||
|
||||
def get_start_and_end(self):
|
||||
"""Returns starting and ending point of a stroke as a ``tuple``."""
|
||||
|
|
@ -2158,15 +2032,9 @@ class Mobject:
|
|||
return result + self.submobjects
|
||||
|
||||
def get_family(self, recurse=True):
|
||||
if config.renderer == "opengl":
|
||||
if recurse:
|
||||
return self.family
|
||||
else:
|
||||
return [self]
|
||||
else:
|
||||
sub_families = list(map(Mobject.get_family, self.submobjects))
|
||||
all_mobjects = [self] + list(it.chain(*sub_families))
|
||||
return remove_list_redundancies(all_mobjects)
|
||||
sub_families = list(map(Mobject.get_family, self.submobjects))
|
||||
all_mobjects = [self] + list(it.chain(*sub_families))
|
||||
return remove_list_redundancies(all_mobjects)
|
||||
|
||||
def family_members_with_points(self):
|
||||
return [m for m in self.get_family() if m.get_num_points() > 0]
|
||||
|
|
@ -2624,12 +2492,7 @@ class Mobject:
|
|||
|
||||
self.add(dotL, dotR, dotMiddle)
|
||||
"""
|
||||
if config.renderer == "opengl":
|
||||
self.data["points"][:] = path_func(
|
||||
mobject1.data["points"], mobject2.data["points"], alpha
|
||||
)
|
||||
else:
|
||||
self.points = path_func(mobject1.points, mobject2.points, alpha)
|
||||
self.points = path_func(mobject1.points, mobject2.points, alpha)
|
||||
self.interpolate_color(mobject1, mobject2, alpha)
|
||||
return self
|
||||
|
||||
|
|
@ -2683,18 +2546,11 @@ class Mobject:
|
|||
|
||||
# Errors
|
||||
def throw_error_if_no_points(self):
|
||||
if config.renderer == "opengl":
|
||||
if len(self.data["points"]) == 0:
|
||||
caller_name = sys._getframe(1).f_code.co_name
|
||||
raise Exception(
|
||||
f"Cannot call Mobject.{caller_name} for a Mobject with no points"
|
||||
)
|
||||
else:
|
||||
if self.has_no_points():
|
||||
caller_name = sys._getframe(1).f_code.co_name
|
||||
raise Exception(
|
||||
f"Cannot call Mobject.{caller_name} for a Mobject with no points"
|
||||
)
|
||||
if self.has_no_points():
|
||||
caller_name = sys._getframe(1).f_code.co_name
|
||||
raise Exception(
|
||||
f"Cannot call Mobject.{caller_name} for a Mobject with no points"
|
||||
)
|
||||
|
||||
# About z-index
|
||||
def set_z_index(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from abc import ABCMeta
|
|||
|
||||
from .. import config
|
||||
from .opengl_mobject import OpenGLMobject
|
||||
from .opengl_three_dimensions import OpenGLSurface
|
||||
from .types.opengl_vectorized_mobject import OpenGLVMobject
|
||||
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ class ConvertToOpenGL(ABCMeta):
|
|||
base_names_to_opengl = {
|
||||
"Mobject": OpenGLMobject,
|
||||
"VMobject": OpenGLVMobject,
|
||||
"Surface": OpenGLSurface,
|
||||
}
|
||||
|
||||
bases = tuple(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from colour import Color
|
|||
|
||||
from .. import config
|
||||
from ..constants import *
|
||||
from ..utils.bezier import interpolate
|
||||
from ..utils.bezier import integer_interpolate, interpolate
|
||||
from ..utils.color import *
|
||||
from ..utils.config_ops import _Data, _Uniforms
|
||||
|
||||
|
|
@ -275,6 +275,9 @@ class OpenGLMobject:
|
|||
mob.data[key] = mob.data[key][::-1]
|
||||
return self
|
||||
|
||||
def get_midpoint(self):
|
||||
return self.point_from_proportion(0.5)
|
||||
|
||||
def apply_points_function(
|
||||
self, func, about_point=None, about_edge=ORIGIN, works_on_bounding_box=False
|
||||
):
|
||||
|
|
|
|||
|
|
@ -51,32 +51,3 @@ class OpenGLSurfaceMesh(OpenGLVGroup):
|
|||
path = OpenGLVMobject()
|
||||
path.set_points_smoothly(nudged_points[vi::full_nv])
|
||||
self.add(path)
|
||||
|
||||
|
||||
class OpenGLSphere(OpenGLSurface):
|
||||
def __init__(self, resolution=None, radius=1, u_range=None, v_range=None, **kwargs):
|
||||
resolution = resolution if resolution is not None else (101, 51)
|
||||
u_range = u_range if u_range is not None else (0, TAU)
|
||||
v_range = v_range if v_range is not None else (0, PI)
|
||||
self.radius = radius
|
||||
super().__init__(
|
||||
resolution=resolution, u_range=u_range, v_range=v_range, **kwargs
|
||||
)
|
||||
|
||||
def uv_func(self, u, v):
|
||||
return self.radius * np.array(
|
||||
[np.cos(u) * np.sin(v), np.sin(u) * np.sin(v), -np.cos(v)]
|
||||
)
|
||||
|
||||
|
||||
class OpenGLTorus(OpenGLSurface):
|
||||
def __init__(self, u_range=None, v_range=None, r1=3, r2=1, **kwargs):
|
||||
u_range = u_range if u_range is not None else (0, TAU)
|
||||
v_range = v_range if v_range is not None else (0, TAU)
|
||||
self.r1 = r1
|
||||
self.r2 = r2
|
||||
super().__init__(u_range=u_range, v_range=v_range, **kwargs)
|
||||
|
||||
def uv_func(self, u, v):
|
||||
P = np.array([math.cos(u), math.sin(u), 0])
|
||||
return (self.r1 - self.r2 * math.cos(v)) * P - math.sin(v) * OUT
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class Polyhedron(VGroup):
|
|||
faces_config: Dict[str, Union[str, int, float, bool]] = {},
|
||||
graph_config: Dict[str, Union[str, int, float, bool]] = {},
|
||||
):
|
||||
VGroup.__init__(self)
|
||||
super().__init__()
|
||||
self.faces_config = dict(
|
||||
{"fill_opacity": 0.5, "shade_in_3d": True}, **faces_config
|
||||
)
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ class BraceLabel(VMobject, metaclass=ConvertToOpenGL):
|
|||
self.label = self.label_constructor(str(text), font_size=font_size)
|
||||
|
||||
self.brace.put_at_tip(self.label)
|
||||
self.submobjects = [self.brace, self.label]
|
||||
self.add(self.brace, self.label)
|
||||
|
||||
def creation_anim(self, label_anim=FadeIn, brace_anim=GrowFromCenter):
|
||||
return AnimationGroup(brace_anim(self.brace), label_anim(self.label))
|
||||
|
|
@ -195,14 +195,12 @@ class BraceLabel(VMobject, metaclass=ConvertToOpenGL):
|
|||
obj = self.get_group_class()(*obj)
|
||||
self.brace = Brace(obj, self.brace_direction, **kwargs)
|
||||
self.brace.put_at_tip(self.label)
|
||||
self.submobjects[0] = self.brace
|
||||
return self
|
||||
|
||||
def change_label(self, *text, **kwargs):
|
||||
self.label = self.label_constructor(*text, **kwargs)
|
||||
|
||||
self.brace.put_at_tip(self.label)
|
||||
self.submobjects[1] = self.label
|
||||
return self
|
||||
|
||||
def change_brace_label(self, obj, *text, **kwargs):
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
from ...constants import *
|
||||
from ..svg.svg_mobject import SVGMobject
|
||||
from ..types.opengl_vectorized_mobject import OpenGLVMobject
|
||||
from .opengl_svg_path import OpenGLSVGPathMobject
|
||||
from .style_utils import cascade_element_style, parse_style
|
||||
|
||||
|
||||
class OpenGLSVGMobject(SVGMobject):
|
||||
def __init__(
|
||||
self,
|
||||
file_name=None,
|
||||
should_center=True,
|
||||
height=2,
|
||||
width=None,
|
||||
unpack_groups=True, # if False, creates a hierarchy of VGroups
|
||||
stroke_width=DEFAULT_STROKE_WIDTH,
|
||||
fill_opacity=1.0,
|
||||
should_subdivide_sharp_curves=False,
|
||||
should_remove_null_curves=False,
|
||||
**kwargs,
|
||||
):
|
||||
self.def_map = {}
|
||||
self.file_name = file_name or self.file_name
|
||||
self.ensure_valid_file()
|
||||
self.should_center = should_center
|
||||
self.unpack_groups = unpack_groups
|
||||
self.path_string_config = {
|
||||
"should_subdivide_sharp_curves": should_subdivide_sharp_curves,
|
||||
"should_remove_null_curves": should_remove_null_curves,
|
||||
}
|
||||
OpenGLVMobject.__init__(
|
||||
self, stroke_width=stroke_width, fill_opacity=fill_opacity, **kwargs
|
||||
)
|
||||
self.move_into_position(width, height)
|
||||
|
||||
def init_points(self):
|
||||
self.generate_points()
|
||||
|
||||
def path_string_to_mobject(self, path_string: str, style: dict):
|
||||
return OpenGLSVGPathMobject(
|
||||
path_string, **self.path_string_config, **parse_style(style)
|
||||
)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import numpy as np
|
||||
|
||||
from ..types.opengl_vectorized_mobject import OpenGLVMobject
|
||||
from .svg_path import SVGPathMobject
|
||||
|
||||
|
||||
class OpenGLSVGPathMobject(SVGPathMobject):
|
||||
def __init__(
|
||||
self,
|
||||
path_string,
|
||||
should_subdivide_sharp_curves=False,
|
||||
should_remove_null_curves=False,
|
||||
**kwargs
|
||||
):
|
||||
self.path_string = path_string
|
||||
OpenGLVMobject.__init__(
|
||||
self,
|
||||
long_lines=True,
|
||||
should_subdivide_sharp_curves=should_subdivide_sharp_curves,
|
||||
should_remove_null_curves=should_remove_null_curves,
|
||||
**kwargs
|
||||
)
|
||||
self.current_path_start = np.zeros((1, self.dim))
|
||||
|
||||
def init_points(self):
|
||||
self.generate_points()
|
||||
|
||||
def start_new_path(self, point):
|
||||
SVGPathMobject.start_new_path(self, point)
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
__all__ = [
|
||||
"ThreeDVMobject",
|
||||
"Surface",
|
||||
"ParametricSurface",
|
||||
"Sphere",
|
||||
"Dot3D",
|
||||
|
|
@ -22,13 +23,14 @@ from colour import Color
|
|||
|
||||
from manim.mobject.opengl_compatibility import ConvertToOpenGL
|
||||
|
||||
from .. import config
|
||||
from ..constants import *
|
||||
from ..mobject.geometry import Circle, Square
|
||||
from ..mobject.mobject import *
|
||||
from ..mobject.opengl_mobject import OpenGLMobject
|
||||
from ..mobject.types.vectorized_mobject import VGroup, VMobject
|
||||
from ..utils.color import *
|
||||
from ..utils.deprecation import deprecated_params
|
||||
from ..utils.deprecation import deprecated, deprecated_params
|
||||
from ..utils.iterables import tuplify
|
||||
from ..utils.space_ops import normalize, z_to_vector
|
||||
|
||||
|
|
@ -38,8 +40,8 @@ class ThreeDVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
super().__init__(shade_in_3d=shade_in_3d, **kwargs)
|
||||
|
||||
|
||||
class ParametricSurface(VGroup):
|
||||
"""Creates a Parametric Surface
|
||||
class Surface(VGroup, metaclass=ConvertToOpenGL):
|
||||
"""Creates a Parametric Surface using a checkerboard pattern.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -65,7 +67,7 @@ class ParametricSurface(VGroup):
|
|||
|
||||
def construct(self):
|
||||
axes = ThreeDAxes(x_range=[-4,4], x_length=8)
|
||||
surface = ParametricSurface(
|
||||
surface = Surface(
|
||||
lambda u, v: axes.c2p(*self.func(u, v)),
|
||||
u_range=[-PI, PI],
|
||||
v_range=[0, TAU]
|
||||
|
|
@ -188,7 +190,7 @@ class ParametricSurface(VGroup):
|
|||
|
||||
Returns
|
||||
-------
|
||||
:class:`~.ParametricSurface`
|
||||
:class:`~.Surface`
|
||||
The parametric surface with a gradient applied by value. For chaining.
|
||||
|
||||
Examples
|
||||
|
|
@ -206,13 +208,12 @@ class ParametricSurface(VGroup):
|
|||
y = v
|
||||
z = np.sin(x) * np.cos(y)
|
||||
return z
|
||||
surface_plane = ParametricSurface(
|
||||
surface_plane = Surface(
|
||||
lambda u, v: axes.c2p(u, v, param_surface(u, v)),
|
||||
resolution=(resolution_fa, resolution_fa),
|
||||
v_min=0,
|
||||
v_max=5,
|
||||
u_min=0,
|
||||
u_max=5)
|
||||
v_range=[0, 5],
|
||||
u_range=[0, 5],
|
||||
)
|
||||
surface_plane.set_style(fill_opacity=1)
|
||||
surface_plane.set_fill_by_value(axes=axes, colors=[(RED, -0.4), (YELLOW, 0), (GREEN, 0.4)])
|
||||
self.add(axes, surface_plane)
|
||||
|
|
@ -245,16 +246,25 @@ class ParametricSurface(VGroup):
|
|||
mob_color = interpolate_color(
|
||||
new_colors[i - 1], new_colors[i], color_index
|
||||
)
|
||||
mob.set_color(mob_color, family=False)
|
||||
if config.renderer == "opengl":
|
||||
mob.set_color(mob_color, recurse=False)
|
||||
else:
|
||||
mob.set_color(mob_color, family=False)
|
||||
break
|
||||
|
||||
return self
|
||||
|
||||
|
||||
@deprecated(since="v0.10.0", replacement=Surface)
|
||||
class ParametricSurface(Surface):
|
||||
# shifts inheritance from Surface/OpenGLSurface depending on the renderer.
|
||||
"""Creates a parametric surface"""
|
||||
|
||||
|
||||
# Specific shapes
|
||||
|
||||
|
||||
class Sphere(ParametricSurface):
|
||||
class Sphere(Surface):
|
||||
"""A mobject representing a three-dimensional sphere.
|
||||
|
||||
Examples
|
||||
|
|
@ -287,27 +297,34 @@ class Sphere(ParametricSurface):
|
|||
self,
|
||||
center=ORIGIN,
|
||||
radius=1,
|
||||
resolution=(12, 24),
|
||||
u_range=[0.001, PI - 0.001],
|
||||
v_range=[0, TAU],
|
||||
resolution=None,
|
||||
u_range=(0, TAU),
|
||||
v_range=(0, PI),
|
||||
**kwargs
|
||||
):
|
||||
ParametricSurface.__init__(
|
||||
self,
|
||||
if config.renderer == "opengl":
|
||||
res_value = (101, 51)
|
||||
else:
|
||||
res_value = (24, 12)
|
||||
|
||||
resolution = resolution if resolution is not None else res_value
|
||||
|
||||
self.radius = radius
|
||||
|
||||
super().__init__(
|
||||
self.func,
|
||||
resolution=resolution,
|
||||
u_range=u_range,
|
||||
v_range=v_range,
|
||||
**kwargs,
|
||||
)
|
||||
self.radius = radius
|
||||
self.scale(self.radius)
|
||||
|
||||
self.shift(center)
|
||||
|
||||
def func(
|
||||
self, u, v
|
||||
): # FIXME: An attribute defined in manim.mobject.three_dimensions line 56 hides this method
|
||||
return np.array([np.cos(v) * np.sin(u), np.sin(v) * np.sin(u), np.cos(u)])
|
||||
def func(self, u, v):
|
||||
return self.radius * np.array(
|
||||
[np.cos(u) * np.sin(v), np.sin(u) * np.sin(v), -np.cos(v)]
|
||||
)
|
||||
|
||||
|
||||
class Dot3D(Sphere):
|
||||
|
|
@ -400,7 +417,7 @@ class Prism(Cube):
|
|||
|
||||
def __init__(self, dimensions=[3, 2, 1], **kwargs):
|
||||
self.dimensions = dimensions
|
||||
Cube.__init__(self, **kwargs)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
Cube.generate_points(self)
|
||||
|
|
@ -408,7 +425,7 @@ class Prism(Cube):
|
|||
self.rescale_to_fit(value, dim, stretch=True)
|
||||
|
||||
|
||||
class Cone(ParametricSurface):
|
||||
class Cone(Surface):
|
||||
"""A circular cone.
|
||||
Can be defined using 2 parameters: its height, and its base radius.
|
||||
The polar angle, theta, can be calculated using arctan(base_radius /
|
||||
|
|
@ -459,8 +476,7 @@ class Cone(ParametricSurface):
|
|||
self.direction = direction
|
||||
self.theta = PI - np.arctan(base_radius / height)
|
||||
|
||||
ParametricSurface.__init__(
|
||||
self,
|
||||
super().__init__(
|
||||
self.func,
|
||||
v_range=v_range,
|
||||
u_range=[u_min, np.sqrt(base_radius ** 2 + height ** 2)],
|
||||
|
|
@ -540,7 +556,7 @@ class Cone(ParametricSurface):
|
|||
return self.direction
|
||||
|
||||
|
||||
class Cylinder(ParametricSurface):
|
||||
class Cylinder(Surface):
|
||||
"""A cylinder, defined by its height, radius and direction,
|
||||
|
||||
Examples
|
||||
|
|
@ -576,13 +592,12 @@ class Cylinder(ParametricSurface):
|
|||
direction=Z_AXIS,
|
||||
v_range=[0, TAU],
|
||||
show_ends=True,
|
||||
resolution=24,
|
||||
resolution=(24, 24),
|
||||
**kwargs
|
||||
):
|
||||
self._height = height
|
||||
self.radius = radius
|
||||
ParametricSurface.__init__(
|
||||
self,
|
||||
super().__init__(
|
||||
self.func,
|
||||
resolution=resolution,
|
||||
u_range=[-self._height / 2, self._height / 2],
|
||||
|
|
@ -715,8 +730,7 @@ class Line3D(Cylinder):
|
|||
# start and end, if they're mobjects
|
||||
self.start = self.pointify(start, self.direction)
|
||||
self.end = self.pointify(end, -self.direction)
|
||||
Cylinder.__init__(
|
||||
self,
|
||||
super().__init__(
|
||||
height=np.linalg.norm(self.vect),
|
||||
radius=self.thickness,
|
||||
direction=self.direction,
|
||||
|
|
@ -779,8 +793,8 @@ class Arrow3D(Line3D):
|
|||
color=WHITE,
|
||||
**kwargs
|
||||
):
|
||||
Line3D.__init__(
|
||||
self, start=start, end=end, thickness=thickness, color=color, **kwargs
|
||||
super().__init__(
|
||||
start=start, end=end, thickness=thickness, color=color, **kwargs
|
||||
)
|
||||
|
||||
self.length = np.linalg.norm(self.vect)
|
||||
|
|
@ -798,7 +812,7 @@ class Arrow3D(Line3D):
|
|||
self.set_color(color)
|
||||
|
||||
|
||||
class Torus(ParametricSurface):
|
||||
class Torus(Surface):
|
||||
"""A torus.
|
||||
|
||||
Examples
|
||||
|
|
@ -825,15 +839,21 @@ class Torus(ParametricSurface):
|
|||
self,
|
||||
major_radius=3,
|
||||
minor_radius=1,
|
||||
u_range=[0, TAU],
|
||||
v_range=[0, TAU],
|
||||
resolution=24,
|
||||
u_range=(0, TAU),
|
||||
v_range=(0, TAU),
|
||||
resolution=None,
|
||||
**kwargs
|
||||
):
|
||||
if config.renderer == "opengl":
|
||||
res_value = (101, 101)
|
||||
else:
|
||||
res_value = (24, 24)
|
||||
|
||||
resolution = resolution if resolution is not None else res_value
|
||||
|
||||
self.R = major_radius
|
||||
self.r = minor_radius
|
||||
ParametricSurface.__init__(
|
||||
self,
|
||||
super().__init__(
|
||||
self.func,
|
||||
u_range=u_range,
|
||||
v_range=v_range,
|
||||
|
|
|
|||
|
|
@ -204,6 +204,83 @@ class OpenGLSurface(OpenGLMobject):
|
|||
def get_shader_vert_indices(self):
|
||||
return self.get_triangle_indices()
|
||||
|
||||
def set_fill_by_value(self, axes, colors):
|
||||
# directly copied from three_dimensions.py with some compatibility changes.
|
||||
"""Sets the color of each mobject of a parametric surface to a color relative to its z-value
|
||||
|
||||
Parameters
|
||||
----------
|
||||
axes :
|
||||
The axes for the parametric surface, which will be used to map z-values to colors.
|
||||
colors :
|
||||
A list of colors, ordered from lower z-values to higher z-values. If a list of tuples is passed
|
||||
containing colors paired with numbers, then those numbers will be used as the pivots.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`~.Surface`
|
||||
The parametric surface with a gradient applied by value. For chaining.
|
||||
|
||||
Examples
|
||||
--------
|
||||
.. manim:: FillByValueExample
|
||||
:save_last_frame:
|
||||
|
||||
class FillByValueExample(ThreeDScene):
|
||||
def construct(self):
|
||||
resolution_fa = 42
|
||||
self.set_camera_orientation(phi=75 * DEGREES, theta=-120 * DEGREES)
|
||||
axes = ThreeDAxes(x_range=(0, 5, 1), y_range=(0, 5, 1), z_range=(-1, 1, 0.5))
|
||||
def param_surface(u, v):
|
||||
x = u
|
||||
y = v
|
||||
z = np.sin(x) * np.cos(y)
|
||||
return z
|
||||
surface_plane = Surface(
|
||||
lambda u, v: axes.c2p(u, v, param_surface(u, v)),
|
||||
resolution=(resolution_fa, resolution_fa),
|
||||
v_range=[0, 5],
|
||||
u_range=[0, 5],
|
||||
)
|
||||
# surface_plane.set_style(fill_opacity=1)
|
||||
surface_plane.set_fill_by_value(axes=axes, colors=[(RED, -0.4), (YELLOW, 0), (GREEN, 0.4)])
|
||||
self.add(axes, surface_plane)
|
||||
"""
|
||||
if type(colors[0]) is tuple:
|
||||
new_colors, pivots = [[i for i, j in colors], [j for i, j in colors]]
|
||||
else:
|
||||
new_colors = colors
|
||||
|
||||
pivot_min = axes.z_range[0]
|
||||
pivot_max = axes.z_range[1]
|
||||
pivot_frequency = (pivot_max - pivot_min) / (len(new_colors) - 1)
|
||||
pivots = np.arange(
|
||||
start=pivot_min, stop=pivot_max + pivot_frequency, step=pivot_frequency
|
||||
)
|
||||
|
||||
for mob in self.family_members_with_points():
|
||||
# import ipdb; ipdb.set_trace(context=7)
|
||||
z_value = axes.point_to_coords(mob.get_midpoint())[2]
|
||||
if z_value <= pivots[0]:
|
||||
mob.set_color(new_colors[0])
|
||||
elif z_value >= pivots[-1]:
|
||||
mob.set_color(new_colors[-1])
|
||||
else:
|
||||
for i, pivot in enumerate(pivots):
|
||||
if pivot > z_value:
|
||||
color_index = (z_value - pivots[i - 1]) / (
|
||||
pivots[i] - pivots[i - 1]
|
||||
)
|
||||
color_index = min(color_index, 1)
|
||||
mob_color = interpolate_color(
|
||||
new_colors[i - 1], new_colors[i], color_index
|
||||
)
|
||||
mob.set_color(mob_color, recurse=False)
|
||||
|
||||
break
|
||||
|
||||
return self
|
||||
|
||||
|
||||
class OpenGLSurfaceGroup(OpenGLSurface):
|
||||
def __init__(self, *parametric_surfaces, resolution=None, **kwargs):
|
||||
|
|
|
|||
|
|
@ -3,12 +3,8 @@ try:
|
|||
except ImportError:
|
||||
pass
|
||||
|
||||
from ..mobject.opengl_geometry import *
|
||||
from ..mobject.opengl_mobject import *
|
||||
from ..mobject.opengl_three_dimensions import *
|
||||
from ..mobject.svg.opengl_svg_mobject import *
|
||||
from ..mobject.svg.opengl_tex_mobject import *
|
||||
from ..mobject.svg.opengl_text_mobject import *
|
||||
from ..mobject.types.opengl_surface import *
|
||||
from ..mobject.types.opengl_vectorized_mobject import *
|
||||
from ..renderer.shader import *
|
||||
|
|
|
|||
|
|
@ -406,11 +406,11 @@ class SpecialThreeDScene(ThreeDScene):
|
|||
Parameters
|
||||
----------
|
||||
**kwargs
|
||||
Any valid parameter of :class:`.Sphere` or :class:`.ParametricSurface`.
|
||||
Any valid parameter of :class:`~.Sphere` or :class:`~.Surface`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`.Sphere`
|
||||
:class:`~.Sphere`
|
||||
The sphere object.
|
||||
"""
|
||||
config = merge_dicts_recursively(self.sphere_config, kwargs)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,7 +1,6 @@
|
|||
import pytest
|
||||
|
||||
from manim import *
|
||||
from manim.opengl import *
|
||||
from manim.renderer.opengl_renderer import OpenGLRenderer
|
||||
from tests.test_graphical_units.testing.frames_comparison import frames_comparison
|
||||
|
||||
|
|
@ -10,6 +9,6 @@ __module_test__ = "opengl"
|
|||
|
||||
@frames_comparison(renderer_class=OpenGLRenderer, renderer="opengl")
|
||||
def test_Circle(scene):
|
||||
circle = OpenGLCircle().set_color(RED)
|
||||
circle = Circle().set_color(RED)
|
||||
scene.add(circle)
|
||||
scene.wait()
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ def test_SurfaceColorscale(scene):
|
|||
z = y ** 2 / 2 - x ** 2 / 2
|
||||
return z
|
||||
|
||||
trig_plane = ParametricSurface(
|
||||
trig_plane = Surface(
|
||||
lambda x, y: axes.c2p(x, y, param_trig(x, y)),
|
||||
resolution=(resolution_fa, resolution_fa),
|
||||
v_min=-3,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from manim import config
|
||||
from manim.constants import RIGHT
|
||||
from manim.opengl import OpenGLSquare
|
||||
from manim.mobject.geometry import Square
|
||||
|
||||
|
||||
def test_Data():
|
||||
a = OpenGLSquare().move_to(RIGHT)
|
||||
config.renderer = "opengl"
|
||||
a = Square().move_to(RIGHT)
|
||||
data_bb = a.data["bounding_box"]
|
||||
assert np.array_equal(
|
||||
data_bb, np.array([[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [2.0, 1.0, 0.0]])
|
||||
|
|
@ -34,3 +36,4 @@ def test_Data():
|
|||
)
|
||||
|
||||
assert np.array_equal(a.bounding_box, data_bb)
|
||||
config.renderer = "cairo" # needs to be here or else the following cairo tests fail
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue