Skip to content

Commit

Permalink
Merge pull request #655 from Abjad/issue654/tuplet-simplification
Browse files Browse the repository at this point in the history
Refactor tuplet simplification
  • Loading branch information
josiah-wolf-oberholtzer committed Jul 2, 2016
2 parents 121b77d + 3d488c8 commit 6d28556
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 11 deletions.
79 changes: 78 additions & 1 deletion abjad/tools/agenttools/IterationAgent.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def by_leaf(
.. container:: example
**Example 1.** Iterates leaves:
**Example 1.** Iterates leaves:
::
Expand Down Expand Up @@ -550,6 +550,7 @@ def by_logical_tie(
nontrivial=False,
pitched=False,
reverse=False,
parentage_mask=None,
):
r'''Iterates client by logical tie.
Expand Down Expand Up @@ -626,26 +627,102 @@ def by_logical_tie(
LogicalTie(Note("c'4"), Note("c'16"))
LogicalTie(Note("f'4"), Note("f'16"))
.. container:: example
**Example 5.** Iterates logical ties masked by parentage.
.. note::
When iterating logical ties in a container, the yielded logical
ties may contain leaves outside that container's parentage. By
specifying a parentage mask, composers can constrain the
contents of the yielded logical ties to only those leaves
actually within the parentage of the container under iteration.
::
>>> staff = Staff("{ c'1 ~ } { c'2 d'2 ~ } { d'1 }")
>>> for logical_tie in iterate(staff[1]).by_logical_tie():
... logical_tie
...
LogicalTie(Note("c'1"), Note("c'2"))
LogicalTie(Note("d'2"), Note("d'1"))
::
>>> for logical_tie in iterate(staff[1]).by_logical_tie(
... parentage_mask=staff[1]):
... logical_tie
...
LogicalTie(Note("c'2"),)
LogicalTie(Note("d'2"),)
Returns generator.
'''
from abjad.tools import selectiontools
nontrivial = bool(nontrivial)
prototype = scoretools.Leaf
if pitched:
prototype = (scoretools.Chord, scoretools.Note)
leaf, yielded = None, False
if not reverse:
for leaf in self.by_class(prototype):
yielded = False
tie_spanners = leaf._get_spanners(spannertools.Tie)
if not tie_spanners or \
tuple(tie_spanners)[0]._is_my_last_leaf(leaf):
logical_tie = leaf._get_logical_tie()
if parentage_mask:
logical_tie = selectiontools.LogicalTie(
x for x in logical_tie
if parentage_mask in x._get_parentage()
)
if not logical_tie:
continue
if not nontrivial or not logical_tie.is_trivial:
yielded = True
yield logical_tie
if leaf is not None and not yielded:
if tie_spanners and \
tuple(tie_spanners)[0]._is_my_first_leaf(leaf):
logical_tie = leaf._get_logical_tie()
if parentage_mask:
logical_tie = selectiontools.LogicalTie(
x for x in logical_tie
if parentage_mask in x._get_parentage()
)
if not logical_tie:
return
if not nontrivial or not logical_tie.is_trivial:
yield logical_tie
else:
for leaf in self.by_class(prototype, reverse=True):
yielded = False
tie_spanners = leaf._get_spanners(spannertools.Tie)
if not(tie_spanners) or \
tuple(tie_spanners)[0]._is_my_first_leaf(leaf):
logical_tie = leaf._get_logical_tie()
if parentage_mask:
logical_tie = selectiontools.LogicalTie(
x for x in logical_tie
if parentage_mask in x._get_parentage()
)
if not logical_tie:
continue
if not nontrivial or not logical_tie.is_trivial:
yielded = True
yield logical_tie
if leaf is not None and not yielded:
if tie_spanners and \
tuple(tie_spanners)[0]._is_my_last_leaf(leaf):
logical_tie = leaf._get_logical_tie()
if parentage_mask:
logical_tie = selectiontools.LogicalTie(
x for x in logical_tie
if parentage_mask in x._get_parentage()
)
if not logical_tie:
return
if not nontrivial or not logical_tie.is_trivial:
yield logical_tie

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,40 @@ def test_agenttools_IterationAgent_by_logical_tie_08():

assert logical_ties[0] == selectiontools.LogicalTie(staff[:2])
assert logical_ties[1] == selectiontools.LogicalTie(staff[3:5])


def test_agenttools_IterationAgent_by_logical_tie_09():

staff = Staff("{ c'4 d'4 ~ } { d'4 e'4 ~ } { e'4 f'4 }")

logical_ties = list(iterate(staff[1]).by_logical_tie())

assert len(logical_ties) == 2
assert len(logical_ties[0]) == 2
assert len(logical_ties[1]) == 2
assert logical_ties[0][0] is staff[0][1]
assert logical_ties[0][1] is staff[1][0]
assert logical_ties[1][0] is staff[1][1]
assert logical_ties[1][1] is staff[2][0]


def test_agenttools_IterationAgent_by_logical_tie_10():

staff = Staff("{ c'4 d'4 ~ } { d'4 e'4 ~ } { e'4 f'4 }")

logical_ties = list(iterate(staff[1])
.by_logical_tie(parentage_mask=staff[1])
)

assert len(logical_ties) == 2
assert len(logical_ties[0]) == 1
assert len(logical_ties[1]) == 1
assert logical_ties[0][0] is staff[1][0]
assert logical_ties[1][0] is staff[1][1]


def test_agenttools_IterationAgent_by_logical_tie_11():
r'''No logical ties, but no errors either.'''
staff = Staff()
logical_ties = list(iterate(staff).by_logical_tie())
assert len(logical_ties) == 0
31 changes: 21 additions & 10 deletions abjad/tools/scoretools/Tuplet.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from abjad.tools import mathtools
from abjad.tools.scoretools.Container import Container
from abjad.tools.topleveltools import inspect_
from abjad.tools.topleveltools import iterate
from abjad.tools.topleveltools import mutate
from abjad.tools.topleveltools import override

Expand Down Expand Up @@ -354,11 +355,24 @@ def _simplify_redundant_tuplet(self):
from abjad.tools import scoretools
if not self.is_redundant:
return
leaves = self[:]
leaf_durations = [inspect_(_).get_duration() for _ in leaves]
tuplet_duration = sum(leaf_durations)
for leaf_duration, leaf in zip(leaf_durations, leaves):
leaf.written_duration = leaf_duration
leaves = []
logical_ties = list(iterate(self).by_logical_tie(parentage_mask=self))
durations = [_.get_duration() for _ in logical_ties]
tuplet_duration = sum(durations)
for i, logical_tie in enumerate(logical_ties):
duration = durations[i]
if i == len(logical_ties) - 1:
leaf = logical_tie[-1]
else:
leaf = logical_tie[0]
leaf.written_duration = duration
leaves.append(leaf)
self[:] = leaves
#leaves = self[:]
#leaf_durations = [inspect_(_).get_duration() for _ in leaves]
#tuplet_duration = sum(leaf_durations)
#for leaf_duration, leaf in zip(leaf_durations, leaves):
# leaf.written_duration = leaf_duration
if isinstance(self, scoretools.FixedDurationTuplet):
self.target_duration = tuplet_duration
else:
Expand Down Expand Up @@ -914,11 +928,8 @@ def is_redundant(self):
Returns true or false.
'''
from abjad.tools import scoretools
if not all(isinstance(_, scoretools.Leaf) for _ in self):
return False
leaf_durations = [inspect_(_).get_duration() for _ in self]
return all(_.is_assignable for _ in leaf_durations)
logical_ties = iterate(self).by_logical_tie(parentage_mask=self)
return all(_.get_duration().is_assignable for _ in logical_ties)

@property
def is_trivial(self):
Expand Down

0 comments on commit 6d28556

Please sign in to comment.