mirror of
https://github.com/3b1b/manim.git
synced 2025-09-01 00:48:45 +00:00
Rewrite earclip_triangulation
This commit is contained in:
parent
8aa004b0b1
commit
2ed78c6e0f
2 changed files with 48 additions and 70 deletions
|
@ -80,11 +80,6 @@ class Text(SVGMobject):
|
||||||
if self.height is None:
|
if self.height is None:
|
||||||
self.scale(TEXT_MOB_SCALE_FACTOR)
|
self.scale(TEXT_MOB_SCALE_FACTOR)
|
||||||
|
|
||||||
# Just a temporary hack to get better triangulation
|
|
||||||
# See pr #1552 for details
|
|
||||||
for i in self.submobjects:
|
|
||||||
i.insert_n_curves(len(i.get_points()))
|
|
||||||
|
|
||||||
def remove_empty_path(self, file_name):
|
def remove_empty_path(self, file_name):
|
||||||
with open(file_name, 'r') as fpr:
|
with open(file_name, 'r') as fpr:
|
||||||
content = fpr.read()
|
content = fpr.read()
|
||||||
|
|
|
@ -359,81 +359,64 @@ def earclip_triangulation(verts, ring_ends):
|
||||||
the ends of new paths are
|
the ends of new paths are
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# First, connect all the rings so that the polygon
|
|
||||||
# with holes is instead treated as a (very convex)
|
|
||||||
# polygon with one edge. Do this by drawing connections
|
|
||||||
# between rings close to each other
|
|
||||||
rings = [
|
rings = [
|
||||||
list(range(e0, e1))
|
list(range(e0, e1))
|
||||||
for e0, e1 in zip([0, *ring_ends], ring_ends)
|
for e0, e1 in zip([0, *ring_ends], ring_ends)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def is_in(point, ring_id):
|
||||||
|
return abs(abs(get_winding_number([i-point for i in verts[rings[ring_id]]]))-1)<1e-5
|
||||||
|
|
||||||
|
def ring_area(ring_id):
|
||||||
|
ring = rings[ring_id]
|
||||||
|
s = 0
|
||||||
|
for i, j in zip(ring[1:], ring):
|
||||||
|
s += cross2d(verts[i], verts[j])
|
||||||
|
return abs(s)/2
|
||||||
|
|
||||||
# Points at the same position may cause problems
|
# Points at the same position may cause problems
|
||||||
for i in rings:
|
for i in rings:
|
||||||
verts[i[0]] += (verts[i[1]]-verts[i[0]])*5e-6
|
verts[i[0]] += (verts[i[1]]-verts[i[0]])*1e-6
|
||||||
verts[i[-1]] += (verts[i[-2]]-verts[i[-1]])*5e-6
|
verts[i[-1]] += (verts[i[-2]]-verts[i[-1]])*1e-6
|
||||||
attached_rings = rings[:1]
|
|
||||||
detached_rings = rings[1:]
|
|
||||||
loop_connections = dict()
|
|
||||||
|
|
||||||
while detached_rings:
|
# First, we should know which rings are directly contained in it for each ring
|
||||||
i_range, j_range = [
|
|
||||||
list(filter(
|
|
||||||
# Ignore indices that are already being
|
|
||||||
# used to draw some connection
|
|
||||||
lambda i: i not in loop_connections,
|
|
||||||
it.chain(*ring_group)
|
|
||||||
))
|
|
||||||
for ring_group in (attached_rings, detached_rings)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Closet point on the atttached rings to an estimated midpoint
|
right = [max(verts[rings[i], 0]) for i in range(len(rings))]
|
||||||
# of the detached rings
|
left = [min(verts[rings[i], 0]) for i in range(len(rings))]
|
||||||
tmp_j_vert = midpoint(
|
top = [max(verts[rings[i], 1]) for i in range(len(rings))]
|
||||||
verts[j_range[0]],
|
bottom = [min(verts[rings[i], 1]) for i in range(len(rings))]
|
||||||
verts[j_range[len(j_range) // 2]]
|
area = [ring_area(i) for i in range(len(rings))]
|
||||||
)
|
|
||||||
i = min(i_range, key=lambda i: norm_squared(verts[i] - tmp_j_vert))
|
|
||||||
# Closet point of the detached rings to the aforementioned
|
|
||||||
# point of the attached rings
|
|
||||||
j = min(j_range, key=lambda j: norm_squared(verts[i] - verts[j]))
|
|
||||||
# Recalculate i based on new j
|
|
||||||
i = min(i_range, key=lambda i: norm_squared(verts[i] - verts[j]))
|
|
||||||
|
|
||||||
# Remember to connect the polygon at these points
|
# The larger ring must be outside
|
||||||
loop_connections[i] = j
|
rings_sorted = list(range(len(rings)))
|
||||||
loop_connections[j] = i
|
rings_sorted.sort(key=lambda x: area[x], reverse=True)
|
||||||
|
|
||||||
# Move the ring which j belongs to from the
|
def is_in_fast(ring_a, ring_b):
|
||||||
# attached list to the detached list
|
# Whether a is in b
|
||||||
new_ring = next(filter(
|
return (left[ring_b] <= left[ring_a] <= right[ring_a] <= right[ring_b] and
|
||||||
lambda ring: ring[0] <= j <= ring[-1],
|
bottom[ring_b] <= bottom[ring_a] <= top[ring_a] <= top[ring_b] and
|
||||||
detached_rings
|
is_in(verts[rings[ring_a][0]], ring_b))
|
||||||
))
|
|
||||||
detached_rings.remove(new_ring)
|
|
||||||
attached_rings.append(new_ring)
|
|
||||||
|
|
||||||
# Setup linked list
|
chilren = [[] for i in rings]
|
||||||
after = []
|
for idx, i in enumerate(rings_sorted):
|
||||||
end0 = 0
|
for j in rings_sorted[:idx][::-1]:
|
||||||
for end1 in ring_ends:
|
if is_in_fast(i, j):
|
||||||
after.extend(range(end0 + 1, end1))
|
chilren[j].append(i)
|
||||||
after.append(end0)
|
break
|
||||||
end0 = end1
|
|
||||||
|
|
||||||
# Find an ordering of indices walking around the polygon
|
res = []
|
||||||
indices = []
|
|
||||||
i = 0
|
|
||||||
for x in range(len(verts) + len(ring_ends) - 1):
|
|
||||||
# starting = False
|
|
||||||
if i in loop_connections:
|
|
||||||
j = loop_connections[i]
|
|
||||||
indices.extend([i, j])
|
|
||||||
i = after[j]
|
|
||||||
else:
|
|
||||||
indices.append(i)
|
|
||||||
i = after[i]
|
|
||||||
if i == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
meta_indices = earcut(verts[indices, :2], [len(indices)])
|
# Then, we can use earcut for each part
|
||||||
return [indices[mi] for mi in meta_indices]
|
used = [False]*len(rings)
|
||||||
|
for i in rings_sorted:
|
||||||
|
if used[i]:
|
||||||
|
continue
|
||||||
|
v = rings[i]
|
||||||
|
ring_ends = [len(v)]
|
||||||
|
for j in chilren[i]:
|
||||||
|
used[j] = True
|
||||||
|
v += rings[j]
|
||||||
|
ring_ends.append(len(v))
|
||||||
|
res += [v[i] for i in earcut(verts[v, :2], ring_ends)]
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
Loading…
Add table
Reference in a new issue