def test_sequencetools_truncate_sequence_to_weight_01(): r'''Truncate list l such that mathtools.weight(l) == total. ''' l = [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10] assert sequencetools.truncate_sequence_to_weight(l, 1) == [-1] assert sequencetools.truncate_sequence_to_weight(l, 2) == [-1, 1] assert sequencetools.truncate_sequence_to_weight(l, 3) == [-1, 2] assert sequencetools.truncate_sequence_to_weight(l, 4) == [-1, 2, -1] assert sequencetools.truncate_sequence_to_weight(l, 5) == [-1, 2, -2] assert sequencetools.truncate_sequence_to_weight(l, 6) == [-1, 2, -3] assert sequencetools.truncate_sequence_to_weight(l, 7) == [-1, 2, -3, 1] assert sequencetools.truncate_sequence_to_weight(l, 8) == [-1, 2, -3, 2] assert sequencetools.truncate_sequence_to_weight(l, 9) == [-1, 2, -3, 3] assert sequencetools.truncate_sequence_to_weight(l, 10) == [-1, 2, -3, 4]
def _split( self, durations, cyclic=False, fracture_spanners=False, tie_split_notes=True, ): from abjad.tools import iterationtools from abjad.tools import pitchtools from abjad.tools import selectiontools from abjad.tools import spannertools durations = [durationtools.Duration(x) for x in durations] if cyclic: durations = sequencetools.repeat_sequence_to_weight_exactly( durations, self._get_duration()) durations = [durationtools.Duration(x) for x in durations] if sum(durations) < self._get_duration(): last_duration = self._get_duration() - sum(durations) durations.append(last_duration) sequencetools.truncate_sequence_to_weight( durations, self._get_duration()) result = [] leaf_prolation = self._get_parentage(include_self=False).prolation leaf_copy = copy.copy(self) for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation shard = new_leaf._set_duration(preprolated_duration) shard = [x._get_parentage().root for x in shard] result.append(shard) flattened_result = sequencetools.flatten_sequence(result) flattened_result = selectiontools.SliceSelection(flattened_result) spanner_classes = (spannertools.TieSpanner,) parentage = self._get_parentage() if parentage._get_spanners(spanner_classes=spanner_classes): selection = selectiontools.select(flattened_result) for component in selection: for mark in component._get_spanners( spanner_classes=spanner_classes): mark.detach() # replace leaf with flattened result selection = selectiontools.SliceSelection(self) parent, start, stop = selection._get_parent_and_start_stop_indices() if parent: parent.__setitem__(slice(start, stop + 1), flattened_result) else: selection._give_dominant_spanners(flattened_result) selection._withdraw_from_crossing_spanners() # fracture spanners if fracture_spanners: first_shard = result[0] for spanner in first_shard[-1]._get_spanners(): index = spanner.index(first_shard[-1]) spanner.fracture(index, direction=Right) last_shard = result[-1] for spanner in last_shard[0]._get_spanners(): index = spanner.index(last_shard[0]) spanner.fracture(index, direction=Left) for middle_shard in result[1:-1]: for spanner in middle_shard[0]._get_spanners(): index = spanner.index(middle_shard[0]) spanner.fracture(index, direction=Left) for spanner in middle_shard[-1]._get_spanners(): index = spanner.index(middle_shard[-1]) spanner.fracture(index, direction=Right) # adjust first leaf first_leaf = flattened_result[0] self._detach_grace_containers(kind='after') # adjust any middle leaves for middle_leaf in flattened_result[1:-1]: middle_leaf._detach_grace_containers(kind='grace') self._detach_grace_containers(kind='after') for mark in middle_leaf._get_marks(): mark.detach() # adjust last leaf last_leaf = flattened_result[-1] last_leaf._detach_grace_containers(kind='grace') for mark in last_leaf._get_marks(): mark.detach() # tie split notes, rests and chords as specified if pitchtools.Pitch.is_pitch_carrier(self) and tie_split_notes: flattened_result_leaves = iterationtools.iterate_leaves_in_expr( flattened_result) # TODO: implement SliceSelection._attach_tie_spanner_to_leaves() for leaf_pair in sequencetools.iterate_sequence_pairwise_strict( flattened_result_leaves): selection = selectiontools.ContiguousSelection(leaf_pair) selection._attach_tie_spanner_to_leaf_pair() # return result return result
def test_sequencetools_truncate_sequence_to_weight_02(): r'''Returns empty list when total is zero. ''' assert sequencetools.truncate_sequence_to_weight([1, 2, 3, 4, 5], 0) == []
def split( self, durations, fracture_spanners=False, cyclic=False, tie_split_notes=True, ): r'''Splits component or selection by `durations`. .. container:: example **Example 1.** Split leaves: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 e'16 d'8 f'32 f'16. c'8 e'8 d'8 f'8 \f } .. container:: example **Example 2.** Split leaves and fracture crossing spanners: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... fracture_spanners=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 \f e'16 \< \p d'8 f'32 \f f'16. \< \p c'8 e'8 d'8 f'8 \f } .. container:: example **Example 3.** Split leaves cyclically: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... cyclic=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 e'16 d'8 f'32 f'16. c'16. c'32 e'8 d'16 d'16 f'8 \f } .. container:: example **Example 4.** Split leaves cyclically and fracture spanners: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... cyclic=True, ... fracture_spanners=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 \f e'16 \< \p d'8 f'32 \f f'16. \< \p c'16. \f c'32 \< \p e'8 d'16 \f d'16 \< \p f'8 \f } .. container:: example **Example 5.** Split tupletted leaves and fracture crossing spanners: :: >>> staff = Staff() >>> staff.append(Tuplet((2, 3), "c'4 d' e'")) >>> staff.append(Tuplet((2, 3), "c'4 d' e'")) >>> leaves = staff.select_leaves() >>> slur = spannertools.SlurSpanner() >>> attach(slur, leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \times 2/3 { c'4 ( d'4 e'4 } \times 2/3 { c'4 d'4 e'4 ) } } :: >>> durations = [Duration(1, 4)] >>> result = mutate(leaves).split( ... durations, ... fracture_spanners=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \times 2/3 { c'4 ( d'8 ) d'8 ( e'4 } \times 2/3 { c'4 d'4 e'4 ) } } Returns list of selections. ''' from abjad.tools import scoretools from abjad.tools import leaftools from abjad.tools import selectiontools # check input components = self._client single_component_input = False if isinstance(components, scoretools.Component): single_component_input = True components = selectiontools.Selection(components) assert all( isinstance(x, scoretools.Component) for x in components) if not isinstance(components, selectiontools.Selection): components = selectiontools.Selection(components) durations = [durationtools.Duration(x) for x in durations] # return if no split to be done if not durations: if single_component_input: return components else: return [], components # calculate total component duration total_component_duration = components.get_duration() total_split_duration = sum(durations) # calculate durations if cyclic: durations = sequencetools.repeat_sequence_to_weight_exactly( durations, total_component_duration) elif total_split_duration < total_component_duration: final_offset = total_component_duration - sum(durations) durations.append(final_offset) elif total_component_duration < total_split_duration: durations = sequencetools.truncate_sequence_to_weight( durations, total_component_duration) # keep copy of durations to partition result components durations_copy = durations[:] # calculate total split duration total_split_duration = sum(durations) assert total_split_duration == total_component_duration # initialize loop variables result, shard = [], [] offset_index, offset_count = 0, len(durations) current_shard_duration = durationtools.Duration(0) remaining_components = list(components[:]) advance_to_next_offset = True # loop and build shards by grabbing next component # and next duration each time through loop while True: # grab next split point if advance_to_next_offset: if durations: next_split_point = durations.pop(0) else: break advance_to_next_offset = True # grab next component from input stack of components if remaining_components: current_component = remaining_components.pop(0) else: break # find where current component endpoint will position us candidate_shard_duration = current_shard_duration + \ current_component._get_duration() # if current component would fill current shard exactly if candidate_shard_duration == next_split_point: shard.append(current_component) result.append(shard) shard = [] current_shard_duration = durationtools.Duration(0) offset_index += 1 # if current component would exceed current shard elif next_split_point < candidate_shard_duration: local_split_duration = \ next_split_point - current_shard_duration if isinstance(current_component, leaftools.Leaf): leaf_split_durations = [local_split_duration] current_duration = current_component._get_duration() additional_required_duration = \ current_duration - local_split_duration split_durations = sequencetools.split_sequence_by_weights( durations, [additional_required_duration], cyclic=False, overhang=True, ) additional_durations = split_durations[0] leaf_split_durations.extend(additional_durations) durations = split_durations[-1] leaf_shards = current_component._split( leaf_split_durations, cyclic=False, fracture_spanners=fracture_spanners, tie_split_notes=tie_split_notes, ) shard.extend(leaf_shards) result.append(shard) offset_index += len(additional_durations) else: left_list, right_list = \ current_component._split_by_duration( local_split_duration, fracture_spanners=fracture_spanners, tie_split_notes=tie_split_notes, ) shard.extend(left_list) result.append(shard) remaining_components.__setitem__(slice(0, 0), right_list) shard = [] offset_index += 1 current_shard_duration = durationtools.Duration(0) # if current component would not fill current shard elif candidate_shard_duration < next_split_point: shard.append(current_component) current_shard_duration += current_component._get_duration() advance_to_next_offset = False else: raise ValueError # append any stub shard if len(shard): result.append(shard) # append any unexamined components if len(remaining_components): result.append(remaining_components) # partition split components according to input durations result = sequencetools.flatten_sequence(result) result = selectiontools.ContiguousSelection(result) result = result.partition_by_durations_exactly(durations_copy) # return list of shards result = [selectiontools.Selection(x) for x in result] return result