Skip to content

Commit

Permalink
Merge pull request #8 from lucmos/feature/better-bulletlayout
Browse files Browse the repository at this point in the history
Refactor bullet lists with indent and group capabilities
  • Loading branch information
lucmos authored Mar 26, 2023
2 parents 3f118ab + 3a3f1f6 commit 35cb4cf
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 73 deletions.
26 changes: 0 additions & 26 deletions src/powermanim/layouts/arrangedbulletlist.py

This file was deleted.

124 changes: 124 additions & 0 deletions src/powermanim/layouts/arrangedbullets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import typing as T
from collections import defaultdict

from manim import *


class Bullet(VGroup):
def __init__(
self,
*text: T.Union[Tex, Text, MathTex],
level: int = 0,
group: T.Optional[int] = None,
adjustment: float = 0.0,
symbol: T.Optional[str] = r"$\bullet$",
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
text_components_buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
):
"""A class to represent a bullet point.
Args:
text: The text to be displayed.
level: The indent level of the bullet point.
group: The group the bullet point belongs to, controls the animations.
adjustment: The adjustment of the bullet.
symbol: The symbol to be displayed as the bullet.
buff: The spacing between the bullet and the text.
text_components_buff: The spacing between the text components.
"""
self.level = level
self.adjustment = adjustment
self.group = group

self.text = VGroup(*text).arrange(RIGHT, buff=text_components_buff)
self.bullet = symbol

if symbol is not None:
self.bullet = Tex(symbol)
self.bullet.next_to(self.text, LEFT, buff=buff)
super().__init__(VGroup(self.bullet, self.text) if self.bullet is not None else self.text)

def indent(self, indent_buff: float = MED_LARGE_BUFF * 1.5):
"""Indent the bullet point.
Args:
indent_buff: The spacing between the bullet and the text.
"""
self.shift(RIGHT * indent_buff * self.level)

def unindent(self, indent_buff: float = MED_LARGE_BUFF * 1.5):
"""Unindent the bullet point.
Args:
indent_buff: The spacing between the bullet and the text.
"""
self.shift(LEFT * indent_buff * self.intend_level)

def adjust(self, adjustment: T.Optional[float] = None):
"""Adjust the bullet point.
Args:
adjustment: The shift of the bullet.
"""
if adjustment is None:
adjustment = self.adjustment
self.shift(self.adjustment)


class ArrangedBullets(VGroup):
def __init__(
self,
*rows: T.Union[Bullet, MathTex, Tex, Text],
line_spacing: float = MED_LARGE_BUFF * 1.5,
indent_buff: float = MED_LARGE_BUFF * 1.5,
left_buff: float = MED_LARGE_BUFF * 1.5,
global_shift: float = 0.0,
):
"""A VGroup that arranges the rows in a list of bullet points.
Args:
rows: A list of items to be displayed.
line_spacing: The spacing between the rows.
indent_buff: The spacing between the bullet and the text.
left_buff: The spacing between the left edge of the rows and the left edge of the screen.
global_shift: The global_shift of the rows.
"""
self.line_spacing = line_spacing
self.indent_buff = indent_buff
self.left_buff = left_buff
self.global_shift = global_shift

rows = [(row if isinstance(row, Bullet) else Bullet(row)) for row in rows]
bullet_rows: T.Iterable[Bullet] = (
VGroup(*rows)
.arrange(DOWN, aligned_edge=LEFT, buff=line_spacing)
.to_edge(LEFT, buff=left_buff)
.shift(global_shift)
)

for row in bullet_rows:
row.indent(indent_buff=indent_buff)
row.adjust()

groups = [row.group for row in bullet_rows]

# If there is a None and aso something else
if (None in groups) and len(set(groups)) != 1:
raise ValueError("The groups must be specified for all or no bullets at all.")

if None in groups:
groups = list(range(len(bullet_rows)))

group2bullet = defaultdict(list)
for i, row in enumerate(bullet_rows):
group = row.group
if group is None:
group = i
group2bullet[group].append(row)

group_rows = []
for _, bullets in group2bullet.items():
group_rows.append(VGroup(*bullets))

super().__init__(*group_rows)
self.ngroups = len(self)
34 changes: 0 additions & 34 deletions src/powermanim/showcase/layouts/arrangedbulletlist.py

This file was deleted.

33 changes: 33 additions & 0 deletions src/powermanim/showcase/layouts/arrangedbullets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from manim import *

from powermanim.layouts.arrangedbullets import ArrangedBullets, Bullet
from powermanim.showcase.showcasescene import ShowcaseScene


class ArrangedBulletsShowcase(ShowcaseScene):
def showcasing():
return ArrangedBullets

def construct(self):
rows = [
Bullet(Text("First row")),
Bullet(Text("Second row")),
Bullet(Text("Elements:")),
Bullet(Text("First element"), level=1, symbol="(1)"),
Bullet(Text("Second element"), level=1, symbol="(2)"),
Bullet(Text("Third element"), level=1, symbol="(3)"),
]
g_rows = VGroup(*rows).set_opacity(0.25).scale(0.9)
g_rows.target = ArrangedBullets(
*g_rows.copy(),
line_spacing=MED_LARGE_BUFF * 1.25,
left_buff=MED_LARGE_BUFF * 3,
).set_opacity(1)

self.play(
MoveToTarget(g_rows),
rate_func=there_and_back_with_pause,
run_time=3,
)

self.wait()
18 changes: 11 additions & 7 deletions src/powermanim/showcase/templates/bulletlist.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from manim import *

from powermanim.layouts.arrangedbullets import Bullet
from powermanim.showcase.showcasescene import ShowcaseScene
from powermanim.templates.bulletlist import BulletList

Expand All @@ -10,13 +11,12 @@ def showcasing():

def construct(self):
rows = [
Text(row)
for row in [
"First row",
"Second row",
"Third row",
"Fourth row",
]
Bullet(Text("First row"), group=0),
Bullet(Text("Second row"), group=1),
Bullet(Text("Elements:"), group=2),
Bullet(Text("First element"), level=1, symbol="(1)", group=3),
Bullet(Text("Second element"), level=1, symbol="(2)", group=4),
Bullet(Text("Third element"), level=1, symbol="(3)", group=5),
]
VGroup(*rows).set_opacity(0.5).scale(0.8)

Expand All @@ -30,10 +30,14 @@ def construct(self):
bullets.also_next(self)
bullets.also_next(self)
bullets.also_next(self)
bullets.also_next(self)
bullets.also_next(self)
bullets.clear(self)
bullets.only_next(self)
bullets.only_next(self)
bullets.only_next(self)
bullets.only_next(self)
bullets.only_next(self)
bullets.only_next(self)
bullets.clear(self)
self.wait()
21 changes: 15 additions & 6 deletions src/powermanim/templates/bulletlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
from manim import *

from powermanim.components.vgrouphighlight import VGroupHighlight
from powermanim.layouts.arrangedbulletlist import ArrangedBulletList
from powermanim.layouts.arrangedbullets import ArrangedBullets


class BulletList(VGroup):
def __init__(
self,
*rows: T.Union[Tex, Text],
line_spacing: float = MED_LARGE_BUFF * 1.5,
indent_buff: float = MED_LARGE_BUFF * 1.5,
left_buff: float = MED_LARGE_BUFF * 2,
shift: float = 0.0,
global_shift: float = 0.0,
inactive_opacity: float = 0.5,
active_opacity: float = 1.0,
scale_active: float = 1.0,
Expand All @@ -22,21 +23,23 @@ def __init__(
Args:
rows: A list of items to be displayed.
line_spacing: The spacing between the rows.
indent_buff: The spacing between the bullet and the text.
left_buff: The spacing between the left edge of the rows and the left edge of the screen.
shift: The shift to apply to the rows.
global_shift: The global_shift to apply to the rows.
inactive_opacity: The opacity of the inactive items.
active_opacity: The opacity of the active items.
scale_active: The scale of the active items.
"""
ArrangedBulletList(
self.arranged_list = ArrangedBullets(
*rows,
line_spacing=line_spacing,
indent_buff=indent_buff,
left_buff=left_buff,
shift=shift,
global_shift=global_shift,
).set_opacity(inactive_opacity)

self.rows = VGroupHighlight(
*rows,
*self.arranged_list,
active_opacity=active_opacity,
scale_active=scale_active,
scale_about_edge=LEFT,
Expand All @@ -49,10 +52,16 @@ def also_next(self, scene: Scene) -> None:
"""Highlights also the next item in the list."""
self.highlighted += 1

if self.highlighted > self.arranged_list.ngroups:
raise StopIteration("No more elements to highlight.")

self.rows.highlight(scene=scene, indices=list(range(self.highlighted)))

def only_next(self, scene: Scene) -> None:
"""Highlights only the next item in the list."""
if self.highlighted > self.arranged_list.ngroups:
raise StopIteration("No more elements to highlight.")

self.rows.highlight(scene=scene, indices=self.highlighted)
self.highlighted += 1

Expand Down

0 comments on commit 35cb4cf

Please sign in to comment.