Skip to content

Commit 9a0cac7

Browse files
MrDiverpre-commit-ci[bot]behackl
authored
OpenGLVMobject fix pointwise_become_partial to fix stroke rendering (#2714)
* fix(get_nth_curve_function_with_length) removed code duplication with get_nth_curve_function_length_pieces * fix(OpenGLVMobject) removed duplcate assignment of self.n_points_per_curve * fix(OpenGLMobject): changed to total assignment in interpolate function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update manim/mobject/opengl/opengl_vectorized_mobject.py Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at> * Update manim/mobject/opengl/opengl_vectorized_mobject.py Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at> * Update opengl_vectorized_mobject.py * Update opengl_vectorized_mobject.py * use remapping instead of copying points * added documentation * (opengl_mobject): reverted interpolate * cleanup * revamping remap, now considering not only adding a single curve per triplet Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
1 parent 001dd3e commit 9a0cac7

File tree

3 files changed

+102
-51
lines changed

3 files changed

+102
-51
lines changed

manim/mobject/opengl/opengl_mobject.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,6 +2394,7 @@ def construct(self):
23942394
func = interpolate
23952395

23962396
self.data[key][:] = func(mobject1.data[key], mobject2.data[key], alpha)
2397+
23972398
for key in self.uniforms:
23982399
if key != "fixed_orientation_center":
23992400
self.uniforms[key] = interpolate(

manim/mobject/opengl/opengl_vectorized_mobject.py

Lines changed: 45 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
interpolate,
2222
partial_quadratic_bezier_points,
2323
proportions_along_bezier_curve_for_point,
24+
quadratic_bezier_remap,
2425
)
2526
from manim.utils.color import *
2627
from manim.utils.config_ops import _Data
@@ -120,7 +121,6 @@ def __init__(
120121
self.flat_stroke = flat_stroke
121122
self.render_primitive = render_primitive
122123
self.triangulation_locked = triangulation_locked
123-
self.n_points_per_curve = n_points_per_curve
124124

125125
self.needs_new_triangulation = True
126126
self.triangulation = np.zeros(0, dtype="i4")
@@ -783,10 +783,7 @@ def get_nth_curve_function_with_length(
783783
sample_points = 10
784784

785785
curve = self.get_nth_curve_function(n)
786-
787-
points = np.array([curve(a) for a in np.linspace(0, 1, sample_points)])
788-
diffs = points[1:] - points[:-1]
789-
norms = np.apply_along_axis(np.linalg.norm, 1, diffs)
786+
norms = self.get_nth_curve_length_pieces(n, sample_points)
790787

791788
length = np.sum(norms)
792789

@@ -1285,10 +1282,7 @@ def interpolate(self, mobject1, mobject2, alpha, *args, **kwargs):
12851282
return self
12861283

12871284
def pointwise_become_partial(
1288-
self,
1289-
vmobject: OpenGLVMobject,
1290-
a: float,
1291-
b: float,
1285+
self, vmobject: OpenGLVMobject, a: float, b: float, remap: bool = True
12921286
) -> OpenGLVMobject:
12931287
"""Given two bounds a and b, transforms the points of the self vmobject into the points of the vmobject
12941288
passed as parameter with respect to the bounds. Points here stand for control points of the bezier curves (anchors and handles)
@@ -1301,58 +1295,58 @@ def pointwise_become_partial(
13011295
upper-bound.
13021296
b : float
13031297
lower-bound
1298+
remap : bool
1299+
if the point amount should be kept the same (True)
1300+
This option should be manually set to False if keeping the number of points is not needed
13041301
"""
13051302
assert isinstance(vmobject, OpenGLVMobject)
1306-
if a <= 0 and b >= 1:
1307-
self.become(vmobject)
1308-
return self
1309-
num_curves = vmobject.get_num_curves()
1310-
nppc = self.n_points_per_curve
1311-
13121303
# Partial curve includes three portions:
13131304
# - A middle section, which matches the curve exactly
1314-
# - A start, which is some ending portion of an inner quadratic
1315-
# - An end, which is the starting portion of a later inner quadratic
1316-
1317-
lower_index, lower_residue = integer_interpolate(0, num_curves, a)
1318-
upper_index, upper_residue = integer_interpolate(0, num_curves, b)
1319-
i1 = nppc * lower_index
1320-
i2 = nppc * (lower_index + 1)
1321-
i3 = nppc * upper_index
1322-
i4 = nppc * (upper_index + 1)
1323-
1324-
vm_points = vmobject.points
1325-
new_points = vm_points.copy()
1326-
if num_curves == 0:
1327-
new_points[:] = 0
1305+
# - A start, which is some ending portion of an inner cubic
1306+
# - An end, which is the starting portion of a later inner cubic
1307+
if a <= 0 and b >= 1:
1308+
self.set_points(vmobject.points)
1309+
return self
1310+
bezier_triplets = vmobject.get_bezier_tuples()
1311+
num_quadratics = len(bezier_triplets)
1312+
1313+
# The following two lines will compute which bezier curves of the given mobject need to be processed.
1314+
# The residue basically indicates the proportion of the selected Bèzier curve.
1315+
# Ex: if lower_index is 3, and lower_residue is 0.4, then the algorithm will append to the points 0.4 of the third bezier curve
1316+
lower_index, lower_residue = integer_interpolate(0, num_quadratics, a)
1317+
upper_index, upper_residue = integer_interpolate(0, num_quadratics, b)
1318+
self.clear_points()
1319+
if num_quadratics == 0:
13281320
return self
13291321
if lower_index == upper_index:
1330-
tup = partial_quadratic_bezier_points(
1331-
vm_points[i1:i2],
1332-
lower_residue,
1333-
upper_residue,
1322+
self.append_points(
1323+
partial_quadratic_bezier_points(
1324+
bezier_triplets[lower_index],
1325+
lower_residue,
1326+
upper_residue,
1327+
),
13341328
)
1335-
new_points[:i1] = tup[0]
1336-
new_points[i1:i4] = tup
1337-
new_points[i4:] = tup[2]
1338-
new_points[nppc:] = new_points[nppc - 1]
13391329
else:
1340-
low_tup = partial_quadratic_bezier_points(
1341-
vm_points[i1:i2],
1342-
lower_residue,
1343-
1,
1330+
self.append_points(
1331+
partial_quadratic_bezier_points(
1332+
bezier_triplets[lower_index], lower_residue, 1
1333+
),
13441334
)
1345-
high_tup = partial_quadratic_bezier_points(
1346-
vm_points[i3:i4],
1347-
0,
1348-
upper_residue,
1335+
inner_points = bezier_triplets[lower_index + 1 : upper_index]
1336+
if len(inner_points) > 0:
1337+
if remap:
1338+
new_triplets = quadratic_bezier_remap(
1339+
inner_points, num_quadratics - 2
1340+
)
1341+
else:
1342+
new_triplets = bezier_triplets
1343+
1344+
self.append_points(np.asarray(new_triplets).reshape(-1, 3))
1345+
self.append_points(
1346+
partial_quadratic_bezier_points(
1347+
bezier_triplets[upper_index], 0, upper_residue
1348+
),
13491349
)
1350-
new_points[0:i1] = low_tup[0]
1351-
new_points[i1:i2] = low_tup
1352-
# Keep new_points i2:i3 as they are
1353-
new_points[i3:i4] = high_tup
1354-
new_points[i4:] = high_tup[2]
1355-
self.set_points(new_points)
13561350
return self
13571351

13581352
def get_subcurve(self, a: float, b: float) -> OpenGLVMobject:

manim/utils/bezier.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,62 @@ def subdivide_quadratic_bezier(points: Iterable[float], n: int) -> np.ndarray:
175175
return np.asarray(beziers).reshape(-1, 3)
176176

177177

178+
def quadratic_bezier_remap(
179+
triplets: Iterable[Iterable[float]], new_number_of_curves: int
180+
):
181+
"""Remaps the number of curves to a higher amount by splitting bezier curves
182+
183+
Parameters
184+
----------
185+
triplets
186+
The triplets of the quadratic bezier curves to be remapped
187+
188+
new_number_of_curves
189+
The number of curves that the output will contain. This needs to be higher than the current number.
190+
191+
Returns
192+
-------
193+
The new triplets for the quadratic bezier curves.
194+
"""
195+
difference = new_number_of_curves - len(triplets)
196+
if difference <= 0:
197+
return triplets
198+
new_triplets = np.zeros((new_number_of_curves, 3, 3))
199+
idx = 0
200+
for triplet in triplets:
201+
if difference > 0:
202+
tmp_noc = int(np.ceil(difference / len(triplets))) + 1
203+
tmp = subdivide_quadratic_bezier(triplet, tmp_noc).reshape(-1, 3, 3)
204+
for i in range(tmp_noc):
205+
new_triplets[idx + i] = tmp[i]
206+
difference -= tmp_noc - 1
207+
idx += tmp_noc
208+
else:
209+
new_triplets[idx] = triplet
210+
idx += 1
211+
return new_triplets
212+
213+
"""
214+
This is an alternate version of the function just for documentation purposes
215+
--------
216+
217+
difference = new_number_of_curves - len(triplets)
218+
if difference <= 0:
219+
return triplets
220+
new_triplets = []
221+
for triplet in triplets:
222+
if difference > 0:
223+
tmp_noc = int(np.ceil(difference / len(triplets))) + 1
224+
tmp = subdivide_quadratic_bezier(triplet, tmp_noc).reshape(-1, 3, 3)
225+
for i in range(tmp_noc):
226+
new_triplets.append(tmp[i])
227+
difference -= tmp_noc - 1
228+
else:
229+
new_triplets.append(triplet)
230+
return new_triplets
231+
"""
232+
233+
178234
# Linear interpolation variants
179235

180236

0 commit comments

Comments
 (0)