Harmonograph#

Harmonograph geometry — sums of damped sinusoids in 2-D and 3-D.

Per axis: x(t) = Σ_i A_i · sin(ω_i · t + φ_i) · exp(-d_i · t).

A classic two-pendulum harmonograph (lateral) traces a single 2-D path. A rotary harmonograph adds a slow rotation around the origin. The 3-D variant extends the same idea to three axes.

References

harmonograph_lateral(input: HarmonicInput, duration: float = 30.0, sr: int = 200, x_components: Sequence[int] | None = None, y_components: Sequence[int] | None = None) GeometryData[source]#

A two-pendulum lateral harmonograph trace.

Parameters:
  • input (HarmonicInput) – Provides peak frequencies, amplitudes, phases, and (optionally) damping per component.

  • duration (float, default=30.0) – Duration of the trace in seconds.

  • sr (int, default=200) – Sample rate in samples per second. Downstream renderers may resample.

  • x_components, y_components (sequence of int, optional) – Indices of components assigned to each axis. If both are None, components alternate (even indices → x, odd → y).

Returns:

GeometryDatageom_type='curve_2d' with shape (int(sr * duration), 2).

harmonograph_rotary(input: HarmonicInput, duration: float = 30.0, sr: int = 200, rotation_freq: float = 0.1) GeometryData[source]#

Lateral harmonograph with an additional slow rotation about the origin.

The lateral trace is rotated by an angle θ(t) = · rotation_freq · t, producing the rosette-like rotary patterns of a circular harmonograph.

Parameters:
  • input (HarmonicInput)

  • duration (float, default=30.0)

  • sr (int, default=200)

  • rotation_freq (float, default=0.1) – Angular drift in Hz applied to the entire trace.

Returns:

GeometryDatageom_type='curve_2d'.

harmonograph_3d(input: HarmonicInput, duration: float = 30.0, sr: int = 200, axis_assignment: str = 'cyclic') GeometryData[source]#

3-D harmonograph trace.

Parameters:
  • input (HarmonicInput)

  • duration (float, default=30.0)

  • sr (int, default=200)

  • axis_assignment ({‘cyclic’, ‘split’}, default=’cyclic’) –

    • 'cyclic': component i is assigned to axis i % 3.

    • 'split': components are split contiguously into three near-equal blocks for x, y, z.

Returns:

GeometryDatageom_type='curve_3d'.

harmonograph_from_peaks(peaks: Sequence[float], amps: Sequence[float] | None = None, phases: Sequence[float] | None = None, damping: Sequence[float] | None = None, duration: float = 30.0, sr: int = 200) GeometryData[source]#

Convenience: build a lateral harmonograph directly from peak Hz values.

Internally constructs a HarmonicInput and delegates to harmonograph_lateral(). If damping is None, a uniform default of DEFAULT_DAMPING is used.

derive_damping_from_linewidth(linewidths: Sequence[float], default: float = 0.01) ndarray[source]#

Convert spectral linewidths (FWHM, Hz) to damping coefficients (1/s).

For a Lorentzian peak with full width at half maximum Δf, the underlying decay rate is π · Δf (since the Lorentzian is the FT of a decaying exponential e^(-π Δf t)).

Non-positive or non-finite linewidths fall back to default.