mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Merge pull request #1982 from 3b1b/video-work
Various bug fixes and tweaks
This commit is contained in:
commit
c8b65d5621
13 changed files with 107 additions and 87 deletions
|
@ -26,12 +26,12 @@ class OpeningManimExample(Scene):
|
||||||
matrix = [[1, 1], [0, 1]]
|
matrix = [[1, 1], [0, 1]]
|
||||||
linear_transform_words = VGroup(
|
linear_transform_words = VGroup(
|
||||||
Text("This is what the matrix"),
|
Text("This is what the matrix"),
|
||||||
IntegerMatrix(matrix, include_background_rectangle=True),
|
IntegerMatrix(matrix, include_background_rectangle=True, h_buff=1.0),
|
||||||
Text("looks like")
|
Text("looks like")
|
||||||
)
|
)
|
||||||
linear_transform_words.arrange(RIGHT)
|
linear_transform_words.arrange(RIGHT)
|
||||||
linear_transform_words.to_edge(UP)
|
linear_transform_words.to_edge(UP)
|
||||||
linear_transform_words.set_stroke(BLACK, 10, background=True)
|
linear_transform_words.set_backstroke(width=5)
|
||||||
|
|
||||||
self.play(
|
self.play(
|
||||||
ShowCreation(grid),
|
ShowCreation(grid),
|
||||||
|
@ -52,7 +52,7 @@ class OpeningManimExample(Scene):
|
||||||
this is the map $z \\rightarrow z^2$
|
this is the map $z \\rightarrow z^2$
|
||||||
""")
|
""")
|
||||||
complex_map_words.to_corner(UR)
|
complex_map_words.to_corner(UR)
|
||||||
complex_map_words.set_stroke(BLACK, 5, background=True)
|
complex_map_words.set_backstroke(width=5)
|
||||||
|
|
||||||
self.play(
|
self.play(
|
||||||
FadeOut(grid),
|
FadeOut(grid),
|
||||||
|
@ -268,16 +268,8 @@ class UpdatersExample(Scene):
|
||||||
# that of the newly constructed object
|
# that of the newly constructed object
|
||||||
brace = always_redraw(Brace, square, UP)
|
brace = always_redraw(Brace, square, UP)
|
||||||
|
|
||||||
text, number = label = VGroup(
|
label = TexText("Width = 0.00")
|
||||||
Text("Width = "),
|
number = label.make_number_changable("0.00")
|
||||||
DecimalNumber(
|
|
||||||
0,
|
|
||||||
show_ellipsis=True,
|
|
||||||
num_decimal_places=2,
|
|
||||||
include_sign=True,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
label.arrange(RIGHT)
|
|
||||||
|
|
||||||
# This ensures that the method deicmal.next_to(square)
|
# This ensures that the method deicmal.next_to(square)
|
||||||
# is called on every frame
|
# is called on every frame
|
||||||
|
|
|
@ -98,11 +98,7 @@ class DrawBorderThenFill(Animation):
|
||||||
self.mobject = vmobject
|
self.mobject = vmobject
|
||||||
|
|
||||||
def begin(self) -> None:
|
def begin(self) -> None:
|
||||||
# Trigger triangulation calculation
|
self.mobject.set_animating_status(True)
|
||||||
for submob in self.mobject.get_family():
|
|
||||||
if not submob._use_winding_fill:
|
|
||||||
submob.get_triangulation()
|
|
||||||
|
|
||||||
self.outline = self.get_outline()
|
self.outline = self.get_outline()
|
||||||
super().begin()
|
super().begin()
|
||||||
self.mobject.match_style(self.outline)
|
self.mobject.match_style(self.outline)
|
||||||
|
|
|
@ -70,7 +70,7 @@ class Transform(Animation):
|
||||||
def finish(self) -> None:
|
def finish(self) -> None:
|
||||||
super().finish()
|
super().finish()
|
||||||
self.mobject.unlock_data()
|
self.mobject.unlock_data()
|
||||||
if self.target_mobject is not None:
|
if self.target_mobject is not None and self.rate_func(1) == 1:
|
||||||
self.mobject.become(self.target_mobject)
|
self.mobject.become(self.target_mobject)
|
||||||
|
|
||||||
def create_target(self) -> Mobject:
|
def create_target(self) -> Mobject:
|
||||||
|
|
|
@ -131,9 +131,10 @@ class TransformMatchingStrings(TransformMatchingParts):
|
||||||
target: StringMobject,
|
target: StringMobject,
|
||||||
matched_keys: Iterable[str] = [],
|
matched_keys: Iterable[str] = [],
|
||||||
key_map: dict[str, str] = dict(),
|
key_map: dict[str, str] = dict(),
|
||||||
|
matched_pairs: Iterable[tuple[Mobject, Mobject]] = [],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
matched_pairs = [
|
matched_pairs = list(matched_pairs) + [
|
||||||
*[(source[key], target[key]) for key in matched_keys],
|
*[(source[key], target[key]) for key in matched_keys],
|
||||||
*[(source[key1], target[key2]) for key1, key2 in key_map.items()],
|
*[(source[key1], target[key2]) for key1, key2 in key_map.items()],
|
||||||
*[
|
*[
|
||||||
|
|
|
@ -133,20 +133,11 @@ class Camera(object):
|
||||||
gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, dst_fbo.glo)
|
gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, dst_fbo.glo)
|
||||||
gl.glBlitFramebuffer(
|
gl.glBlitFramebuffer(
|
||||||
*src_fbo.viewport,
|
*src_fbo.viewport,
|
||||||
*src_fbo.viewport,
|
*dst_fbo.viewport,
|
||||||
gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR
|
gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_raw_fbo_data(self, dtype: str = 'f1') -> bytes:
|
def get_raw_fbo_data(self, dtype: str = 'f1') -> bytes:
|
||||||
# # Copy blocks from fbo into draw_fbo using Blit
|
|
||||||
# gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.fbo.glo)
|
|
||||||
# gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, self.draw_fbo.glo)
|
|
||||||
# src_viewport = self.fbo.viewport
|
|
||||||
# gl.glBlitFramebuffer(
|
|
||||||
# *src_viewport,
|
|
||||||
# *self.draw_fbo.viewport,
|
|
||||||
# gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR
|
|
||||||
# )
|
|
||||||
self.blit(self.fbo, self.draw_fbo)
|
self.blit(self.fbo, self.draw_fbo)
|
||||||
return self.draw_fbo.read(
|
return self.draw_fbo.read(
|
||||||
viewport=self.draw_fbo.viewport,
|
viewport=self.draw_fbo.viewport,
|
||||||
|
|
|
@ -29,15 +29,22 @@ class CameraFrame(Mobject):
|
||||||
):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.view_matrix = np.identity(4)
|
self.uniforms["orientation"] = Rotation.identity().as_quat()
|
||||||
|
self.uniforms["fovy"] = fovy
|
||||||
|
|
||||||
self.default_orientation = Rotation.identity()
|
self.default_orientation = Rotation.identity()
|
||||||
|
self.view_matrix = np.identity(4)
|
||||||
|
self.camera_location = OUT # This will be updated by set_points
|
||||||
|
|
||||||
self.set_points(np.array([ORIGIN, LEFT, RIGHT, DOWN, UP]))
|
self.set_points(np.array([ORIGIN, LEFT, RIGHT, DOWN, UP]))
|
||||||
self.set_width(frame_shape[0], stretch=True)
|
self.set_width(frame_shape[0], stretch=True)
|
||||||
self.set_height(frame_shape[1], stretch=True)
|
self.set_height(frame_shape[1], stretch=True)
|
||||||
self.move_to(center_point)
|
self.move_to(center_point)
|
||||||
self.uniforms["orientation"] = Rotation.identity().as_quat()
|
|
||||||
self.uniforms["fovy"] = fovy
|
def note_changed_data(self, recurse_up: bool = True):
|
||||||
|
super().note_changed_data(recurse_up)
|
||||||
|
self.get_view_matrix(refresh=True)
|
||||||
|
self.get_implied_camera_location(refresh=True)
|
||||||
|
|
||||||
def set_orientation(self, rotation: Rotation):
|
def set_orientation(self, rotation: Rotation):
|
||||||
self.uniforms["orientation"][:] = rotation.as_quat()
|
self.uniforms["orientation"][:] = rotation.as_quat()
|
||||||
|
@ -77,20 +84,34 @@ class CameraFrame(Mobject):
|
||||||
def get_inverse_camera_rotation_matrix(self):
|
def get_inverse_camera_rotation_matrix(self):
|
||||||
return self.get_orientation().as_matrix().T
|
return self.get_orientation().as_matrix().T
|
||||||
|
|
||||||
def get_view_matrix(self):
|
def get_view_matrix(self, refresh=False):
|
||||||
"""
|
"""
|
||||||
Returns a 4x4 for the affine transformation mapping a point
|
Returns a 4x4 for the affine transformation mapping a point
|
||||||
into the camera's internal coordinate system
|
into the camera's internal coordinate system
|
||||||
"""
|
"""
|
||||||
shift = Matrix44.from_translation(-self.get_center()).T
|
if refresh:
|
||||||
rotation = Matrix44.from_quaternion(self.uniforms["orientation"]).T
|
shift = np.identity(4)
|
||||||
scale = Matrix44(np.identity(3) / self.get_scale())
|
rotation = np.identity(4)
|
||||||
self.view_matrix[:] = shift * rotation * scale
|
scale_mat = np.identity(4)
|
||||||
|
|
||||||
|
shift[:3, 3] = -self.get_center()
|
||||||
|
rotation[:3, :3] = self.get_inverse_camera_rotation_matrix()
|
||||||
|
scale = self.get_scale()
|
||||||
|
if scale > 0:
|
||||||
|
scale_mat[:3, :3] /= self.get_scale()
|
||||||
|
|
||||||
|
self.view_matrix = np.dot(scale_mat, np.dot(rotation, shift))
|
||||||
|
|
||||||
return self.view_matrix
|
return self.view_matrix
|
||||||
|
|
||||||
def get_inv_view_matrix(self):
|
def get_inv_view_matrix(self):
|
||||||
return np.linalg.inv(self.get_view_matrix())
|
return np.linalg.inv(self.get_view_matrix())
|
||||||
|
|
||||||
|
@Mobject.affects_data
|
||||||
|
def interpolate(self, *args, **kwargs):
|
||||||
|
super().interpolate(*args, **kwargs)
|
||||||
|
|
||||||
|
@Mobject.affects_data
|
||||||
def rotate(self, angle: float, axis: np.ndarray = OUT, **kwargs):
|
def rotate(self, angle: float, axis: np.ndarray = OUT, **kwargs):
|
||||||
rot = Rotation.from_rotvec(angle * normalize(axis))
|
rot = Rotation.from_rotvec(angle * normalize(axis))
|
||||||
self.set_orientation(rot * self.get_orientation())
|
self.set_orientation(rot * self.get_orientation())
|
||||||
|
@ -181,10 +202,12 @@ class CameraFrame(Mobject):
|
||||||
def get_field_of_view(self) -> float:
|
def get_field_of_view(self) -> float:
|
||||||
return self.uniforms["fovy"]
|
return self.uniforms["fovy"]
|
||||||
|
|
||||||
def get_implied_camera_location(self) -> np.ndarray:
|
def get_implied_camera_location(self, refresh=False) -> np.ndarray:
|
||||||
|
if refresh:
|
||||||
to_camera = self.get_inverse_camera_rotation_matrix()[2]
|
to_camera = self.get_inverse_camera_rotation_matrix()[2]
|
||||||
dist = self.get_focal_distance()
|
dist = self.get_focal_distance()
|
||||||
return self.get_center() + dist * to_camera
|
self.camera_location = self.get_center() + dist * to_camera
|
||||||
|
return self.camera_location
|
||||||
|
|
||||||
def to_fixed_frame_point(self, point: Vect3, relative: bool = False):
|
def to_fixed_frame_point(self, point: Vect3, relative: bool = False):
|
||||||
view = self.get_view_matrix()
|
view = self.get_view_matrix()
|
||||||
|
|
|
@ -48,7 +48,7 @@ from manimlib.utils.space_ops import rotation_matrix_transpose
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Callable, Iterable, Union, Tuple, Optional, Self
|
from typing import Callable, Iterable, Iterator, Union, Tuple, Optional, Self
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
from manimlib.typing import ManimColor, Vect3, Vect4, Vect3Array, UniformDict
|
from manimlib.typing import ManimColor, Vect3, Vect4, Vect3Array, UniformDict
|
||||||
from moderngl.context import Context
|
from moderngl.context import Context
|
||||||
|
@ -350,7 +350,7 @@ class Mobject(object):
|
||||||
return GroupClass(*self.split().__getitem__(value))
|
return GroupClass(*self.split().__getitem__(value))
|
||||||
return self.split().__getitem__(value)
|
return self.split().__getitem__(value)
|
||||||
|
|
||||||
def __iter__(self) -> Iterable[Self]:
|
def __iter__(self) -> Iterator[Self]:
|
||||||
return iter(self.split())
|
return iter(self.split())
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
|
@ -617,7 +617,6 @@ class Mobject(object):
|
||||||
# copy.copy is only a shallow copy, so the internal
|
# copy.copy is only a shallow copy, so the internal
|
||||||
# data which are numpy arrays or other mobjects still
|
# data which are numpy arrays or other mobjects still
|
||||||
# need to be further copied.
|
# need to be further copied.
|
||||||
result.data = self.data.copy()
|
|
||||||
result.uniforms = {
|
result.uniforms = {
|
||||||
key: value.copy() if isinstance(value, np.ndarray) else value
|
key: value.copy() if isinstance(value, np.ndarray) else value
|
||||||
for key, value in self.uniforms.items()
|
for key, value in self.uniforms.items()
|
||||||
|
@ -636,15 +635,14 @@ class Mobject(object):
|
||||||
result.non_time_updaters = list(self.non_time_updaters)
|
result.non_time_updaters = list(self.non_time_updaters)
|
||||||
result.time_based_updaters = list(self.time_based_updaters)
|
result.time_based_updaters = list(self.time_based_updaters)
|
||||||
result._data_has_changed = True
|
result._data_has_changed = True
|
||||||
|
result._shaders_initialized = False
|
||||||
|
|
||||||
family = self.get_family()
|
family = self.get_family()
|
||||||
for attr, value in self.__dict__.items():
|
for attr, value in self.__dict__.items():
|
||||||
if isinstance(value, Mobject) and value is not self:
|
if isinstance(value, Mobject) and value is not self:
|
||||||
if value in family:
|
if value in family:
|
||||||
setattr(result, attr, result.family[self.family.index(value)])
|
setattr(result, attr, result.family[self.family.index(value)])
|
||||||
if isinstance(value, np.ndarray):
|
elif isinstance(value, np.ndarray):
|
||||||
setattr(result, attr, value.copy())
|
|
||||||
if isinstance(value, ShaderWrapper):
|
|
||||||
setattr(result, attr, value.copy())
|
setattr(result, attr, value.copy())
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -695,6 +693,7 @@ class Mobject(object):
|
||||||
sm1.texture_paths = sm2.texture_paths
|
sm1.texture_paths = sm2.texture_paths
|
||||||
sm1.depth_test = sm2.depth_test
|
sm1.depth_test = sm2.depth_test
|
||||||
sm1.render_primitive = sm2.render_primitive
|
sm1.render_primitive = sm2.render_primitive
|
||||||
|
sm1.needs_new_bounding_box = sm2.needs_new_bounding_box
|
||||||
# Make sure named family members carry over
|
# Make sure named family members carry over
|
||||||
for attr, value in list(mobject.__dict__.items()):
|
for attr, value in list(mobject.__dict__.items()):
|
||||||
if isinstance(value, Mobject) and value in family2:
|
if isinstance(value, Mobject) and value in family2:
|
||||||
|
@ -1326,7 +1325,7 @@ class Mobject(object):
|
||||||
data = mob.data if mob.has_points() > 0 else mob._data_defaults
|
data = mob.data if mob.has_points() > 0 else mob._data_defaults
|
||||||
if color is not None:
|
if color is not None:
|
||||||
rgbs = np.array(list(map(color_to_rgb, listify(color))))
|
rgbs = np.array(list(map(color_to_rgb, listify(color))))
|
||||||
if 1 < len(rgbs) < len(data):
|
if 1 < len(rgbs):
|
||||||
rgbs = resize_with_interpolation(rgbs, len(data))
|
rgbs = resize_with_interpolation(rgbs, len(data))
|
||||||
data[name][:, :3] = rgbs
|
data[name][:, :3] = rgbs
|
||||||
if opacity is not None:
|
if opacity is not None:
|
||||||
|
@ -1437,11 +1436,9 @@ class Mobject(object):
|
||||||
def add_background_rectangle(
|
def add_background_rectangle(
|
||||||
self,
|
self,
|
||||||
color: ManimColor | None = None,
|
color: ManimColor | None = None,
|
||||||
opacity: float = 0.75,
|
opacity: float = 1.0,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> Self:
|
) -> Self:
|
||||||
# TODO, this does not behave well when the mobject has points,
|
|
||||||
# since it gets displayed on top
|
|
||||||
from manimlib.mobject.shape_matchers import BackgroundRectangle
|
from manimlib.mobject.shape_matchers import BackgroundRectangle
|
||||||
self.background_rectangle = BackgroundRectangle(
|
self.background_rectangle = BackgroundRectangle(
|
||||||
self, color=color,
|
self, color=color,
|
||||||
|
|
|
@ -251,8 +251,6 @@ class Laptop(VGroup):
|
||||||
self.axis = axis
|
self.axis = axis
|
||||||
|
|
||||||
self.add(body, screen_plate, axis)
|
self.add(body, screen_plate, axis)
|
||||||
self.rotate(5 * np.pi / 12, LEFT, about_point=ORIGIN)
|
|
||||||
self.rotate(np.pi / 6, DOWN, about_point=ORIGIN)
|
|
||||||
|
|
||||||
|
|
||||||
class VideoIcon(SVGMobject):
|
class VideoIcon(SVGMobject):
|
||||||
|
|
|
@ -890,6 +890,11 @@ class VMobject(Mobject):
|
||||||
# Figure out what the subpaths are, and align
|
# Figure out what the subpaths are, and align
|
||||||
subpaths1 = self.get_subpaths()
|
subpaths1 = self.get_subpaths()
|
||||||
subpaths2 = vmobject.get_subpaths()
|
subpaths2 = vmobject.get_subpaths()
|
||||||
|
for subpaths in [subpaths1, subpaths2]:
|
||||||
|
subpaths.sort(key=lambda sp: -sum(
|
||||||
|
get_norm(p2 - p1)
|
||||||
|
for p1, p2 in zip(sp, sp[1:])
|
||||||
|
))
|
||||||
n_subpaths = max(len(subpaths1), len(subpaths2))
|
n_subpaths = max(len(subpaths1), len(subpaths2))
|
||||||
|
|
||||||
# Start building new ones
|
# Start building new ones
|
||||||
|
@ -898,7 +903,7 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def get_nth_subpath(path_list, n):
|
def get_nth_subpath(path_list, n):
|
||||||
if n >= len(path_list):
|
if n >= len(path_list):
|
||||||
return [path_list[-1][-1]]
|
return np.vstack([path_list[0][:-1], path_list[0][::-1]])
|
||||||
return path_list[n]
|
return path_list[n]
|
||||||
|
|
||||||
for n in range(n_subpaths):
|
for n in range(n_subpaths):
|
||||||
|
@ -922,14 +927,6 @@ class VMobject(Mobject):
|
||||||
mob.get_joint_products()
|
mob.get_joint_products()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def invisible_copy(self) -> Self:
|
|
||||||
result = self.copy()
|
|
||||||
if not result.has_fill() or result.get_num_points() == 0:
|
|
||||||
return result
|
|
||||||
result.append_vectorized_mobject(self.copy().reverse_points())
|
|
||||||
result.set_opacity(0)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def insert_n_curves(self, n: int, recurse: bool = True) -> Self:
|
def insert_n_curves(self, n: int, recurse: bool = True) -> Self:
|
||||||
for mob in self.get_family(recurse):
|
for mob in self.get_family(recurse):
|
||||||
if mob.get_num_curves() > 0:
|
if mob.get_num_curves() > 0:
|
||||||
|
@ -1223,6 +1220,15 @@ class VMobject(Mobject):
|
||||||
self.refresh_joint_products()
|
self.refresh_joint_products()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def set_animating_status(self, is_animating: bool, recurse: bool = True):
|
||||||
|
super().set_animating_status(is_animating, recurse)
|
||||||
|
if is_animating:
|
||||||
|
for submob in self.get_family(recurse):
|
||||||
|
submob.get_joint_products(refresh=True)
|
||||||
|
if not submob._use_winding_fill:
|
||||||
|
submob.get_triangulation()
|
||||||
|
return self
|
||||||
|
|
||||||
# For shaders
|
# For shaders
|
||||||
def init_shader_data(self, ctx: Context):
|
def init_shader_data(self, ctx: Context):
|
||||||
dtype = self.shader_dtype
|
dtype = self.shader_dtype
|
||||||
|
@ -1276,27 +1282,29 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
# Build up data lists
|
# Build up data lists
|
||||||
fill_datas = []
|
fill_datas = []
|
||||||
fill_border_datas = []
|
|
||||||
fill_indices = []
|
fill_indices = []
|
||||||
|
fill_border_datas = []
|
||||||
stroke_datas = []
|
stroke_datas = []
|
||||||
back_stroke_datas = []
|
back_stroke_datas = []
|
||||||
for submob in family:
|
for submob in family:
|
||||||
submob.get_joint_products()
|
submob.get_joint_products()
|
||||||
|
indices = submob.get_outer_vert_indices()
|
||||||
has_fill = submob.has_fill()
|
has_fill = submob.has_fill()
|
||||||
has_stroke = submob.has_stroke()
|
has_stroke = submob.has_stroke()
|
||||||
indices = submob.get_outer_vert_indices()
|
back_stroke = has_stroke and submob.stroke_behind
|
||||||
if has_stroke:
|
front_stroke = has_stroke and not submob.stroke_behind
|
||||||
lst = back_stroke_datas if submob.stroke_behind else stroke_datas
|
if back_stroke:
|
||||||
lst.append(submob.data[stroke_names][indices])
|
back_stroke_datas.append(submob.data[stroke_names][indices])
|
||||||
if has_fill:
|
if front_stroke:
|
||||||
|
stroke_datas.append(submob.data[stroke_names][indices])
|
||||||
|
if has_fill and self._use_winding_fill:
|
||||||
data = submob.data[fill_names]
|
data = submob.data[fill_names]
|
||||||
data["base_point"][:] = data["point"][0]
|
data["base_point"][:] = data["point"][0]
|
||||||
if self._use_winding_fill:
|
|
||||||
fill_datas.append(data[indices])
|
fill_datas.append(data[indices])
|
||||||
else:
|
if has_fill and not self._use_winding_fill:
|
||||||
fill_datas.append(data)
|
fill_datas.append(submob.data[fill_names])
|
||||||
fill_indices.append(submob.get_triangulation())
|
fill_indices.append(submob.get_triangulation())
|
||||||
if not has_stroke and has_fill:
|
if has_fill and not front_stroke:
|
||||||
# Add fill border
|
# Add fill border
|
||||||
names = list(stroke_names)
|
names = list(stroke_names)
|
||||||
names[names.index('stroke_rgba')] = 'fill_rgba'
|
names[names.index('stroke_rgba')] = 'fill_rgba'
|
||||||
|
@ -1307,11 +1315,9 @@ class VMobject(Mobject):
|
||||||
fill_border_datas.append(border_stroke_data[indices])
|
fill_border_datas.append(border_stroke_data[indices])
|
||||||
|
|
||||||
shader_wrappers = [
|
shader_wrappers = [
|
||||||
self.back_stroke_shader_wrapper.read_in(
|
self.back_stroke_shader_wrapper.read_in(back_stroke_datas),
|
||||||
[*back_stroke_datas, *fill_border_datas]
|
|
||||||
),
|
|
||||||
self.fill_shader_wrapper.read_in(fill_datas, fill_indices or None),
|
self.fill_shader_wrapper.read_in(fill_datas, fill_indices or None),
|
||||||
self.stroke_shader_wrapper.read_in(stroke_datas),
|
self.stroke_shader_wrapper.read_in([*fill_border_datas, *stroke_datas]),
|
||||||
]
|
]
|
||||||
# TODO, account for submob uniforms separately?
|
# TODO, account for submob uniforms separately?
|
||||||
self.uniforms.update(family[0].uniforms)
|
self.uniforms.update(family[0].uniforms)
|
||||||
|
|
|
@ -280,12 +280,10 @@ class FillShaderWrapper(ShaderWrapper):
|
||||||
self.fill_canvas = get_fill_canvas(self.ctx)
|
self.fill_canvas = get_fill_canvas(self.ctx)
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
vao = self.vao
|
|
||||||
assert(vao is not None)
|
|
||||||
winding = (len(self.vert_indices) == 0)
|
winding = (len(self.vert_indices) == 0)
|
||||||
vao.program['winding'].value = winding
|
self.program['winding'].value = winding
|
||||||
if not winding:
|
if not winding:
|
||||||
vao.render()
|
super().render()
|
||||||
return
|
return
|
||||||
|
|
||||||
original_fbo = self.ctx.fbo
|
original_fbo = self.ctx.fbo
|
||||||
|
@ -301,14 +299,13 @@ class FillShaderWrapper(ShaderWrapper):
|
||||||
gl.GL_ONE, gl.GL_ONE,
|
gl.GL_ONE, gl.GL_ONE,
|
||||||
)
|
)
|
||||||
gl.glBlendEquationSeparate(gl.GL_FUNC_ADD, gl.GL_MAX)
|
gl.glBlendEquationSeparate(gl.GL_FUNC_ADD, gl.GL_MAX)
|
||||||
self.ctx.blend_equation = moderngl.FUNC_ADD, moderngl.MAX
|
|
||||||
|
|
||||||
vao.render()
|
super().render()
|
||||||
|
|
||||||
original_fbo.use()
|
original_fbo.use()
|
||||||
gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
|
gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
|
||||||
gl.glBlendEquation(gl.GL_FUNC_ADD)
|
gl.glBlendEquation(gl.GL_FUNC_ADD)
|
||||||
|
|
||||||
texture_vao.render(moderngl.TRIANGLE_STRIP)
|
texture_vao.render()
|
||||||
|
|
||||||
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
|
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
|
||||||
|
|
|
@ -89,7 +89,7 @@ vec3 get_perp(int index, vec4 joint_product, vec3 point, vec3 tangent, float aaw
|
||||||
*/
|
*/
|
||||||
float buff = 0.5 * v_stroke_width[index] + aaw;
|
float buff = 0.5 * v_stroke_width[index] + aaw;
|
||||||
// Add correction for sharp angles to prevent weird bevel effects
|
// Add correction for sharp angles to prevent weird bevel effects
|
||||||
if(joint_product.w < -0.9) buff *= 10 * (joint_product.w + 1.0);
|
if(joint_product.w < -0.75) buff *= 4 * (joint_product.w + 1.0);
|
||||||
vec3 normal = get_joint_unit_normal(joint_product);
|
vec3 normal = get_joint_unit_normal(joint_product);
|
||||||
// Set global unit normal
|
// Set global unit normal
|
||||||
unit_normal = normal;
|
unit_normal = normal;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
def merge_dicts_recursively(*dicts):
|
def merge_dicts_recursively(*dicts):
|
||||||
|
@ -29,3 +30,19 @@ def soft_dict_update(d1, d2):
|
||||||
for key, value in list(d2.items()):
|
for key, value in list(d2.items()):
|
||||||
if key not in d1:
|
if key not in d1:
|
||||||
d1[key] = value
|
d1[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
def dict_eq(d1, d2):
|
||||||
|
if len(d1) != len(d2):
|
||||||
|
return False
|
||||||
|
for key in d1:
|
||||||
|
value1 = d1[key]
|
||||||
|
value2 = d2[key]
|
||||||
|
if type(value1) != type(value2):
|
||||||
|
return False
|
||||||
|
if type(d1[key]) == np.ndarray:
|
||||||
|
if any(d1[key] != d2[key]):
|
||||||
|
return False
|
||||||
|
elif d1[key] != d2[key]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
|
@ -159,6 +159,7 @@ def get_fill_canvas(ctx: moderngl.Context) -> Tuple[Framebuffer, VertexArray, Tu
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
color = texture(Texture, v_textcoord);
|
color = texture(Texture, v_textcoord);
|
||||||
|
if(color.a == 0) discard;
|
||||||
if(distance(color.rgb, null_rgb) < MIN_DIST_TO_NULL) discard;
|
if(distance(color.rgb, null_rgb) < MIN_DIST_TO_NULL) discard;
|
||||||
|
|
||||||
// Un-blend from the null value
|
// Un-blend from the null value
|
||||||
|
@ -180,5 +181,6 @@ def get_fill_canvas(ctx: moderngl.Context) -> Tuple[Framebuffer, VertexArray, Tu
|
||||||
simple_program,
|
simple_program,
|
||||||
ctx.buffer(verts.astype('f4').tobytes()),
|
ctx.buffer(verts.astype('f4').tobytes()),
|
||||||
'texcoord',
|
'texcoord',
|
||||||
|
mode=moderngl.TRIANGLE_STRIP
|
||||||
)
|
)
|
||||||
return (texture_fbo, fill_texture_vao, null_rgb)
|
return (texture_fbo, fill_texture_vao, null_rgb)
|
||||||
|
|
Loading…
Add table
Reference in a new issue