FlowMap

timelines.FlowMap(
    source=None,
    *,
    id='',
    resolve=None,
    at=None,
    source_length=None,
    target_length=None,
)

Coordinate transformation map for flow control.

A FlowMap enables bidirectional coordinate conversion between a source timeline (with flow control) and a target (linearized) timeline:

  • Source -> Target conversion (1:N, since repeats duplicate coordinates)
  • Target -> Source lookup (N:1, always unique)

The single positional argument, source, is polymorphic:

  • None — an empty map (no sections). Used by internal construction such as :meth:inverse, which fills the section tables afterwards.
  • a :class:Flow — sections are derived from the flow’s measure-count ranges (integer MC space); id defaults to the flow’s mode value.
  • a single interval-like descriptor or an iterable of them — sections are built directly from the resulting (start, end) ranges with a cumulative target position, so the played spans concatenate in the target axis and any gaps between them map to nothing. id defaults to "default". Accepted descriptors are region names (resolved through resolve), Region objects, (start, end) coordinate pairs, Timeline objects, and interval events — see :func:~timetoalign.timelines.flow.sections._coerce_intervals.

Concatenation is only the default. Three interchangeable mechanisms place the played spans on the target axis instead, so the assembled timeline can hold holes — which is what an inverted cut needs:

  • :class:~timetoalign.timelines.flow.sections.Gap entries mixed into source, each pushing everything after it later by its own duration.
  • The at argument, giving the target coordinate of each played span outright.
  • A {target coordinate -> span} mapping as source, which pairs the two directly; see :meth:from_dict.

All three agree on the result, and no two may be combined in one call. See :meth:_build_from_entries for the placement rules.

FlowMap stores sections for efficient lookup:

  • _sections: List of FlowMapSection objects
  • _target_boundaries: Sorted list of target section starts for binary search

Attributes

Name Type Description
flow Flow | None The computed Flow, or None for interval-built maps.
id str Identifier for this FlowMap.
source_length Fraction | None Total extent of the source axis, when known. Recorded so that :meth:inverse can restore a target axis whose final stretch is a gap — material the flow drops off the end leaves no section to infer the length from.
target_length Fraction | None Total extent of the target axis, when known.

Methods

Name Description
fold Map target coordinate back to source coordinate.
from_dict Build a FlowMap from a {target coordinate -> span} mapping.
from_qb_sections Create a FlowMap with QB-space source coordinates.
inverse Create the inverse FlowMap (target -> source becomes source -> target).
iter_gaps Report every stretch of target time that no source material fills.
unfold_coordinate Map source coordinate to target coordinates.

fold

timelines.FlowMap.fold(coord)

Map target coordinate back to source coordinate.

The target timeline has unique coordinates, so this always returns a single value.

Parameters

Name Type Description Default
coord Fraction | float | int Coordinate in target timeline. required

Returns

Name Type Description
Fraction Coordinate in source timeline.

Raises

Name Type Description
ValueError If coordinate is outside the flow range.

Examples

>>> # Target coord 6 maps back to source coord 3
>>> flow_map.fold(6)
Fraction(3)

from_dict

timelines.FlowMap.from_dict(
    placements,
    *,
    id='default',
    resolve=None,
    source_length=None,
    target_length=None,
)

Build a FlowMap from a {target coordinate -> span} mapping.

The third way to state a placement, and the most direct: each key is the target coordinate at which its span begins. Where Gap entries describe the holes and at parallels the span list, a mapping puts coordinate and span side by side, which reads well when the placement comes from a table, a file, or an analysis::

FlowMap.from_dict({0: "A8_1", 129: "A8_2"}, resolve=tl.get_region)

Keys are sorted, so the mapping need not be written in target order. A value may also be a collection of spans, which are laid end to end from that coordinate on.

Parameters

Name Type Description Default
placements 'Mapping[Fraction | float | int, object]' Mapping from target coordinate to one interval-like descriptor — or a collection of them — in the same shapes the constructor accepts (region names, Region objects, (start, end) pairs, Timeline objects, interval events). required
id str Identifier for this FlowMap. 'default'
resolve Callable[[str], object] | None Callable mapping a region name to an interval-like object, needed only when the values carry region-name strings. None
source_length Fraction | float | int | None Total extent of the source axis, when known. None
target_length Fraction | float | int | None Total extent of the target axis, when known. None

Returns

Name Type Description
'FlowMap' The constructed FlowMap, its spans placed at the given coordinates
'FlowMap' and the space between them recorded as gaps.

Raises

Name Type Description
ValueError If two spans would overlap on the target axis.

Examples

>>> FlowMap.from_dict({0: (0, 123), 129: (123, 228)}, id="restored")
FlowMap(restored: 2 sections, 1 gap)

from_qb_sections

timelines.FlowMap.from_qb_sections(flow, qb_sections, *, id='')

Create a FlowMap with QB-space source coordinates.

Unlike the default constructor which derives source coordinates from MC numbers (integers), this factory accepts pre-computed quarterbeat boundaries. This is the correct approach for scores with non-uniform measure durations, where MC-number space != QB-coordinate space.

Parameters

Name Type Description Default
flow Flow The computed Flow (retained for metadata and inverse ops). required
qb_sections list[tuple[Fraction, Fraction]] List of (qb_start, qb_end) tuples giving the quarterbeat boundaries of each section in the folded source timeline. Must have the same length as flow.sections. required
id str Optional identifier. Defaults to flow.mode.value. ''

Returns

Name Type Description
'FlowMap' FlowMap with sections in QB-space.

Raises

Name Type Description
ValueError If len(qb_sections) != len(flow.sections).

See Also

compute_qb_sections: Computes QB boundaries from a Flow and ScoreFlowController.

inverse

timelines.FlowMap.inverse()

Create the inverse FlowMap (target -> source becomes source -> target).

The inverse FlowMap swaps the source and target coordinate systems. This is useful for attaching to a target timeline to enable tracing back to the original source.

Because each section carries both ranges outright, inverting is a plain swap of the two. That is what makes an inverse placing rather than concatenating: a flow that drops material leaves its spans at their original source coordinates, so the inverse puts them back where they came from and the dropped stretch reappears as a gap. Applying the inverse with :meth:~timetoalign.timelines.Timeline.apply_flow therefore rebuilds a timeline laid out like the original.

Note

The inverse FlowMap’s unfold_coordinate() returns coordinates in the original source space, which may yield multiple results if the source coord is visited multiple times.

Returns

Name Type Description
'FlowMap' A new FlowMap with inverted sections.

iter_gaps

timelines.FlowMap.iter_gaps()

Report every stretch of target time that no source material fills.

A hole reaches the map two ways, and both are reported here:

  • Recorded — an insertion section, built from a :class:~timetoalign.timelines.flow.sections.Gap entry or from an at placement, which may carry a label.
  • Implied — the space left between two placed sections, or between the last section and a longer recorded target_length. Inverting a FlowMap produces holes this way, since the inverse simply puts each span back at its source coordinates.

Returns

Name Type Description
list[tuple[Fraction, Fraction, str | None]] The (start, end, label) of each hole, in target order.
list[tuple[Fraction, Fraction, str | None]] Adjacent holes are merged, and label is the recorded gap’s label
list[tuple[Fraction, Fraction, str | None]] or None.

unfold_coordinate

timelines.FlowMap.unfold_coordinate(coord)

Map source coordinate to target coordinates.

Since a source coordinate may be visited multiple times (due to repeats), this returns a list of all corresponding target coordinates.

Parameters

Name Type Description Default
coord Fraction | float | int Coordinate in source timeline. required

Returns

Name Type Description
list[Fraction] List of coordinates in target timeline. Empty list if coord
list[Fraction] is not within any section.

Examples

>>> # Source coord 3 appears twice due to repeat
>>> flow_map.unfold_coordinate(3)
[Fraction(2), Fraction(6)]