# The Clear BSD License
#
# Copyright (c) 2026 Tobias Heibges
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted (subject to the limitations in the disclaimer
# below) provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""
Gerstner Wave Parameters
Dataclass for configuring individual Gerstner wave components.
Used by both GerstnerWaveSurface and CurvedWaveSurface.
"""
from dataclasses import dataclass
import numpy as np
[docs]
@dataclass
class GerstnerWaveParams:
"""
Parameters for a single Gerstner wave component.
Gerstner waves describe the motion of water particles in circular
orbits, producing realistic wave shapes with sharp crests and
flat troughs.
Parameters
----------
amplitude : float
Wave amplitude (vertical displacement) in meters.
wavelength : float
Wave wavelength (crest-to-crest distance) in meters.
direction : tuple of float, optional
Wave propagation direction as (dx, dy), will be normalized.
Default is (1.0, 0.0) for propagation in +x direction.
phase : float, optional
Initial phase offset in radians. Default is 0.0.
steepness : float, optional
Wave steepness Q (0 to 1). Controls horizontal displacement.
Q=0 gives pure vertical sinusoidal motion.
Q=1 gives maximum sharpening (breaking wave limit).
Default is 0.5.
Attributes
----------
wave_number : float
Wave number k = 2π/λ in radians per meter.
angular_frequency : float
Angular frequency from deep water dispersion: ω = √(gk).
direction_normalized : tuple of float
Normalized direction vector.
Examples
--------
>>> # Simple wave propagating in +x direction
>>> wave = GerstnerWaveParams(amplitude=1.0, wavelength=50.0)
>>> # Steep wave at 45 degrees
>>> wave = GerstnerWaveParams(
... amplitude=2.0,
... wavelength=30.0,
... direction=(1.0, 1.0),
... steepness=0.8
... )
"""
amplitude: float
wavelength: float
direction: tuple[float, float] = (1.0, 0.0)
phase: float = 0.0
steepness: float = 0.5
@property
def wave_number(self) -> float:
"""Wave number k = 2π/λ."""
return 2.0 * np.pi / self.wavelength
@property
def angular_frequency(self) -> float:
"""Angular frequency from deep water dispersion: ω = √(gk)."""
g = 9.81 # gravitational acceleration m/s²
return np.sqrt(g * self.wave_number)
@property
def direction_normalized(self) -> tuple[float, float]:
"""Normalized direction vector."""
norm = np.sqrt(self.direction[0] ** 2 + self.direction[1] ** 2)
if norm < 1e-10:
return (1.0, 0.0)
return (self.direction[0] / norm, self.direction[1] / norm)