MAIDR Documentation - v4.4.0
    Preparing search index...

    Class StepTrace

    Trace implementation for step (stairs) charts, where the value is piecewise constant: it is held across an interval and then jumps, rather than being interpolated between samples as a line chart implies.

    Everything numeric is inherited from LineTrace unchanged. In particular the audio stays one discrete tone per point, which is already the right sonification for piecewise-constant data — a run of equal levels sounds as a repeated pitch, and a transition is an audible jump. The continuous glissando that SmoothTrace uses would interpolate between levels, which is exactly what a step chart does not do.

    What this class adds is what a step chart has that a line chart does not: navigation by transition rather than by sample, and a description written in terms of runs. Announcing an ordinal level by name ("REM" rather than "3") used to live here too, but a line or path over the same ordinal y needs it just as much, so LineTrace reads the point's label and this class inherits that unchanged.

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    observers: Observer<TraceState>[]
    isWarning: boolean
    isComputingStateAt: boolean

    True while AbstractTrace.getStateAt computes state at a temporarily moved cursor. Enforces (structurally, not just by documentation) that state getters never notify observers.

    id: string
    type: TraceType
    title: string
    name: string | undefined

    What this layer is, when the producer named it. See MaidrLayer.name.

    xAxis: string
    yAxis: string
    z: string
    navigationService: NavigationService
    layer: MaidrLayer
    supportsExtrema: true

    Abstract property that subclasses must implement to indicate extrema support

    rotorSupport: true
    movable: MovableGraph
    points: LinePoint[][]
    lineValues: number[][]
    highlightValues: SVGElement[][] | null
    highlightCenters:
        | {
            x: number;
            y: number;
            row: number;
            col: number;
            element: SVGElement;
        }[]
        | null
    min: number[]
    max: number[]

    Accessors

    • get isInitialEntry(): boolean

      Returns boolean

    • set isInitialEntry(value: boolean): void

      Parameters

      • value: boolean

      Returns void

    • get row(): number

      Returns number

    • set row(value: number): void

      Parameters

      • value: number

      Returns void

    • get col(): number

      Returns number

    • set col(value: number): void

      Parameters

      • value: number

      Returns void

    • get isEmptyAtCursor(): boolean

      Whether the cursor has no point to report.

      Read from dimension rather than from any one trace's data, because that is the one shape every trace already answers in. It is also evaluated at the cursorLineTrace returns the current series' length as cols — so this covers a ragged layer, where one series has points and another has none, and not only a layer that is empty throughout.

      Returns boolean

      True when there is nothing at (row, col) to describe

    • get outOfBoundsState(): TraceEmptyState

      The state pushed to observers when navigation leaves the data.

      Declared as TraceEmptyState rather than the whole TraceState union because that is what every implementation actually returns — the accessor exists so AbstractPlot.notifyOutOfBounds has an empty state to push. Narrowing it here is what lets the braille and highlight callers hand the value straight on: those states accept the empty trace shape but not a populated one, so a wider declaration would force each of them to cast, and a cast is exactly what stops the compiler from noticing if an override ever starts returning a populated state.

      Returns TraceEmptyState

      The empty trace state, positioned for out-of-bounds audio panning.

    • get hasMultiPoints(): boolean

      Returns boolean

    • get traceType(): TraceType

      The trace's chart type. Lightweight alternative to reading state.traceType, which eagerly computes the full audio/braille/text/ highlight state just to expose this one string.

      Returns TraceType

    • get values(): number[][]

      Returns number[][]

    • get groupFallbackLabel(): string

      What this chart calls one of its series, when the layer names no z axis.

      Overridable for the reason LineTrace.seriesLabels is: a subclass navigates the same grid but draws something else, and this label is announced literally beside the series' own name. A chart that named its own noun everywhere else and then said "Group is Honda Civic" -- or "Group is Ash" -- would use two words for one referent in a single sentence.

      Returns string

      The fallback label

    • get seriesLabels(): {
          count: string;
          perSeries: string;
          names: string;
          column: string;
      }

      What this chart calls its series and its samples, in the description.

      A subclass navigates the same grid but draws something else, and the description dialog renders these labels literally -- so a radar inheriting "Number of lines" tells a reader they are on a chart they are not, which is the same problem the spoken plot type is overridden to avoid.

      Defaulted to the line's own wording, so nothing changes for the chart this class is named after.

      Returns { count: string; perSeries: string; names: string; column: string }

      The four labels the description uses

    • get state(): TraceState

      Announces this trace as a step plot in the instruction text, layer-switch cue and subplot entry cue, rather than falling back to the raw layer type.

      Returns TraceState

      The trace state with plotType set to 'step'

    • get description(): DescriptionState

      Describes the step chart in terms of its runs rather than its points: how many times the level changes, which levels occur, and how long the longest unbroken run is. That is the shape of the data a step chart encodes, and it is what the point-by-point data table cannot show at a glance.

      Returns DescriptionState

      The description state containing chart metadata and data table

    Methods

    • Gets safe row and column indices to prevent accessing undefined values

      Returns { row: number; col: number }

      Object with safe row and column indices

    • Parameters

      • a: number

        Utility function to compare point values for rotor functionality

      • b: number
      • type: "lower" | "higher"

      Returns boolean

      boolean value

    • Moves left in rotor mode, optionally filtering by lower or higher values.

      Parameters

      • Optional_mode: "lower" | "higher"

        Optional mode for filtering (lower or higher)

      Returns boolean

      Error always - subclasses must override this method

    • Moves right in rotor mode, optionally filtering by lower or higher values.

      Parameters

      • Optional_mode: "lower" | "higher"

        Optional mode for filtering (lower or higher)

      Returns boolean

      Error always - subclasses must override this method

    • Returns true if this trace supports compare (lower/higher value) navigation. Override to false for trace types that don't use compare modes (e.g., scatter, which is all we currently have).

      Returns boolean

    • Returns the display name for the default data navigation mode. Override to provide a trace-specific name (e.g., "ROW AND COLUMN NAVIGATION" for scatter).

      Returns string

    • Get all highlight SVG elements for this trace Used by HighlightService for high contrast mode

      Returns SVGElement[]

      Array of all SVG elements, or empty array if none

    • Get all original (visible) SVG elements for this trace. These are the actual rendered elements, not the hidden clones used for highlighting. Used by HighlightService for high contrast mode color changes.

      Returns SVGElement[]

      Array of all original SVG elements, or empty array if none

    • Computes the trace state at an arbitrary position without moving the user's cursor or notifying observers. Used by monitor mode to sonify and announce a newly appended point while the user stays put.

      The state getters read this.row/this.col internally, so the cursor is moved temporarily and always restored in a finally block — this method is the single owner of that pattern.

      Re-entrancy hazard: this is only safe because the entire call chain is synchronous (no await points), so timers (e.g. autoplay ticks) cannot interleave before the finally-restore, and state getters never notify observers. If a getter ever becomes async or triggers notifications, callers could observe the temporary cursor.

      Parameters

      • row: number

        The row of the position to compute state for

      • col: number

        The column of the position to compute state for

      Returns TraceState

      The trace state at the requested position

    • Returns a human-readable label for this trace's chart type (e.g., 'Bar Chart', 'Scatter Plot') for display in the description modal. Falls back to the raw layer type if no mapping is registered.

      Returns string

    • Builds the axes object for the description state, including z only when the layer explicitly provides a z-axis label. Subclasses should call this instead of constructing the axes object inline so charts without a real z dimension don't surface the placeholder default.

      Returns { x?: string; y?: string; z?: string }

    • Returns true if this trace supports point-by-point navigation mode. Opt-in per trace type: override to return true for traces that expose individual data points navigable in reading order (left/right) and column-major order (up/down). Currently only ScatterTrace.

      Returns boolean

    • Notifies the trace that the rotor is entering or leaving INTERSECTION_MODE. Default is a no-op; line-style traces don't need to track mode state because their state output is unchanged by the rotor mode. Traces whose audio/text output differs in intersection mode (e.g. ScatterTrace, which normally plays the whole x-column as a chord and must instead focus a single point) override this to flip an internal flag.

      Parameters

      • _enabled: boolean

        True when entering intersection mode, false when leaving.

      Returns void

    • Moves the active point to the pointer location and returns directional guidance toward the nearest data geometry in a single call.

      Combining both operations avoids running findNearestPoint twice per pointermove event — important on dense plots where the scan is the hot path.

      Parameters

      • x: number

        Screen-space x position of the pointer/finger

      • y: number

        Screen-space y position of the pointer/finger

      Returns PointerGuidanceState | null

      Guidance state relative to nearest point, or null when unavailable

    • Moves the trace to the nearest point when the pointer is within its bounds and the trace is not already focused on that point.

      onCurve is intentionally non-optional: the caller has already paid the cost of isPointInBounds to assemble guidance state, and forcing subclasses to accept the value makes the contract explicit so a future override cannot silently recompute (or worse, ignore) it.

      Subclasses override this to customise hover-driven navigation:

      • Box / ViolinBox no-op the move while still surfacing guidance.
      • Scatter switches into column navigation mode before delegating.

      Parameters

      Returns void

    • Checks if the specified coordinates are within bounds of the element.

      Parameters

      • x: number

        The x-coordinate

      • y: number

        The y-coordinate

      • element: NearestPoint

        Object containing the SVG element and its position

        • element: SVGElement
        • row: number
        • col: number
        • centerX: number
        • centerY: number

      Returns boolean

      True if the point is in bounds, false otherwise

    • Name a line carries in the spec, or undefined when the data authors none. Consumers that have nothing meaningful to say without a real name (e.g. the position announcement) can then stay silent about groups.

      Protected for the same reason LineTrace.groupNameAt is: a subclass that names its series from its own data -- a contour from its level -- still has to answer for a series the layer named and nothing else did, and calling groupNameAt for that would recurse.

      Parameters

      • row: number

      Returns string | undefined

    • Human-readable name of a single line: the authored name when the spec provides one, otherwise a positional fallback ("Line 2").

      Protected rather than private because a subclass naming a series in its own description -- which competitor led, which observation is an outlier -- has to name it the way every other announcement does, and reaching for points[row][0].z directly would skip the fallback and report undefined for an unnamed series.

      Parameters

      • row: number

      Returns string

    • Gets the line series with human-readable labels. Used by the candlestick delta feature to list reference-line candidates (e.g., moving averages).

      Returns { label: string; points: readonly LinePoint[] }[]

      One entry per series with its label and points

    • Cleans up trace resources including values and highlighted SVG elements.

      Only removes elements MAIDR created (hidden clones, synthetic markers); traces that highlight the chart's original live elements in place (e.g. heatmap cells, line dots) merely drop their references so the visible chart geometry survives focusout and live-data rebuilds.

      Returns void

    • Where a cell sits in the stereo field.

      Split out because two places need it and they must agree: the tone for the cursor's own point, and the tones of every series a chord sounds at an intersection. A subclass that hears its columns somewhere other than a straight left-to-right sweep -- a radar's spokes go out and back around a circle -- overrides this one method and both follow, rather than one of them keeping the line's sweep and contradicting the other.

      Parameters

      • row: number

        The series index

      • col: number

        The column index

      Returns { y: number; x: number; rows: number; cols: number }

      The panning for that cell

    • Returns { x: number; y: number; row: number; col: number; element: SVGElement }[] | null

    • Move the line plot to the position that matches the given X value

      Parameters

      • xValue: XValue

        The X value to move to

      Returns boolean

      true if the position was found and set, false otherwise

    • Base implementation of navigation in HIGHER and LOWER modes of ROTOR, default is no-op Needs to be implemented in Line, Bar, Heatmap, Candlestick

      Parameters

      • direction: string
      • type: "lower" | "higher"

      Returns boolean

    • Moves up in rotor mode, optionally filtering by lower or higher values.

      Parameters

      • Optional_mode: "lower" | "higher"

        Optional mode for filtering (lower or higher)

      Returns boolean

      Error always - subclasses must override this method

    • Moves down in rotor mode, optionally filtering by lower or higher values.

      Parameters

      • Optional_mode: "lower" | "higher"

        Optional mode for filtering (lower or higher)

      Returns boolean

      Error always - subclasses must override this method

    • Intersection navigation mode is available only when the plot has multiple lines. points is a 2-D array whose outer dimension is the line index, so length > 1 means at least two lines exist — a prerequisite for any crossing between sampled series to be possible.

      Returns boolean

    • Maps the vertices of a rendered step path back onto the data points.

      A step chart is drawn with the corner vertices the steps introduce, so the rendered path carries more vertices than the series has points — matplotlib emits 2N - 1 for hv/vh and 2N for mid. The inherited reconciliation drops the surplus from the end, which would place every highlight on the first half of the chart. Here the surplus is interleaved rather than trailing, so it is removed by stride instead:

      • 2N - 1 vertices (hv/vh): the even-indexed vertices are exactly the data points; the odd-indexed ones are the corners.
      • 2N vertices (mid): each data point owns a horizontal pair. The first and last runs are half-width — steps-mid starts at x[0] and ends at x[N-1] rather than at a midpoint — so those two samples sit at the outer end of their run and are read off directly. An interior run spans midpoint to midpoint, and its centre is the sample only when the spacing either side is equal: in general the centre is (x[i-1] + 2x[i] + x[i+1]) / 4, off by a quarter of the local second difference. The highlight therefore always lands inside the sample's own run — never on a neighbour's — but on an irregularly sampled mid chart it is not exactly on the sample. Recovering x[i] exactly would mean iterating x[i+1] = 2m[i] - x[i] from one end, which is numerically unstable over a long series; a bounded sub-run offset is the better trade.

      Any other count is left to the inherited handling — a library that simplifies collinear vertices (a long flat run in a hypnogram is exactly that) produces neither shape, and interpolating along the surviving segments is the right recovery there.

      Parameters

      • coordinates: LinePoint[]

        Vertices parsed from the path, mutated in place

      • row: number

        Index of the series these coordinates belong to

      Returns void

    • Offers a "Transitions" rotor mode whenever the current series actually changes level. Point-by-point navigation across a long step chart — a whole night of sleep epochs, say — is unusable; jumping between the moments the level changes is the navigation this chart type calls for.

      Returns readonly RotorFilterUnit[]

      The transition rotor unit, or none when the series never changes

    • Jumps to the previous or next point at which the level changes, staying within the current series.

      Parameters

      • key: string

        The RotorFilterUnit.key of the active unit

      • direction: "left" | "right"

        The direction to search

      Returns boolean

      True if the cursor moved, false when no further transition exists