// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
pub struct SpringCoefficients {
natural_frequency : @core.Real
damping_ratio : @core.Real
}
///|
pub fn SpringCoefficients::SpringCoefficients(
natural_frequency : @core.Real,
damping_ratio : @core.Real,
) -> SpringCoefficients {
{ natural_frequency, damping_ratio }
}
///|
pub fn SpringCoefficients::contact_defaults() -> SpringCoefficients {
{ natural_frequency: 30.0F, damping_ratio: 5.0F }
}
///|
pub fn SpringCoefficients::joint_defaults() -> SpringCoefficients {
{ natural_frequency: 1.0e6F, damping_ratio: 1.0F }
}
///|
pub fn SpringCoefficients::angular_frequency(
self : SpringCoefficients,
) -> @core.Real {
self.natural_frequency * @core.two_pi()
}
///|
pub fn SpringCoefficients::erp_inv_dt(
self : SpringCoefficients,
dt : @core.Real,
) -> @core.Real {
let ang_freq = self.angular_frequency()
ang_freq / (dt * ang_freq + 2.0F * self.damping_ratio)
}
///|
pub fn SpringCoefficients::erp(
self : SpringCoefficients,
dt : @core.Real,
) -> @core.Real {
dt * self.erp_inv_dt(dt)
}
///|
pub fn SpringCoefficients::cfm_coeff(
self : SpringCoefficients,
dt : @core.Real,
) -> @core.Real {
let one = 1.0F
let erp = self.erp(dt)
if erp == 0.0F {
0.0F
} else {
let inv_erp_minus_one = one / erp - one
let denom = (one + inv_erp_minus_one) *
4.0F *
self.damping_ratio *
self.damping_ratio
if denom == 0.0F {
0.0F
} else {
inv_erp_minus_one * inv_erp_minus_one / denom
}
}
}
///|
pub fn SpringCoefficients::cfm_factor(
self : SpringCoefficients,
dt : @core.Real,
) -> @core.Real {
let one = 1.0F
one / (one + self.cfm_coeff(dt))
}
///|
/// Friction models for contact constraints.
///
/// Rapier exposes this in 3D; we include it unconditionally for pub parity.
pub enum FrictionModel {
Simplified
Coulomb
}
///|
pub fn FrictionModel::default() -> FrictionModel {
// Rapier's 3D default is `Simplified`.
Simplified
}
///|
pub fn FrictionModel::simplified() -> FrictionModel {
Simplified
}
///|
pub fn FrictionModel::coulomb() -> FrictionModel {
Coulomb
}
///|
pub struct IntegrationParameters {
mut dt : @core.Real
mut min_ccd_dt : @core.Real
mut contact_softness : SpringCoefficients
mut warmstart_coefficient : @core.Real
mut length_unit : @core.Real
mut normalized_allowed_linear_error : @core.Real
mut normalized_max_corrective_velocity : @core.Real
mut normalized_prediction_distance : @core.Real
mut num_solver_iterations : Int
mut num_internal_pgs_iterations : Int
mut num_internal_stabilization_iterations : Int
mut min_island_size : Int
mut max_ccd_substeps : Int
friction_model : FrictionModel
}
///|
pub fn IntegrationParameters::default() -> IntegrationParameters {
{
dt: 1.0F / 60.0F,
min_ccd_dt: 1.0F / 60.0F / 100.0F,
contact_softness: SpringCoefficients::contact_defaults(),
warmstart_coefficient: 1.0F,
num_internal_pgs_iterations: 1,
num_internal_stabilization_iterations: 1,
num_solver_iterations: 4,
min_island_size: 128,
normalized_allowed_linear_error: 0.001F,
normalized_max_corrective_velocity: 10.0F,
normalized_prediction_distance: 0.002F,
max_ccd_substeps: 1,
friction_model: FrictionModel::default(),
length_unit: 1.0F,
}
}
///|
pub fn IntegrationParameters::IntegrationParameters() -> IntegrationParameters {
IntegrationParameters::default()
}
///|
pub fn IntegrationParameters::inv_dt(
self : IntegrationParameters,
) -> @core.Real {
if self.dt == 0.0F {
0.0F
} else {
1.0F / self.dt
}
}
///|
pub fn IntegrationParameters::set_dt(
self : IntegrationParameters,
dt : @core.Real,
) -> IntegrationParameters {
self.dt = dt
self
}
///|
/// Return a copy of these parameters with a different time-step, without mutating `self`.
pub fn IntegrationParameters::with_dt(
self : IntegrationParameters,
dt : @core.Real,
) -> IntegrationParameters {
{
dt,
min_ccd_dt: self.min_ccd_dt,
contact_softness: self.contact_softness,
warmstart_coefficient: self.warmstart_coefficient,
length_unit: self.length_unit,
normalized_allowed_linear_error: self.normalized_allowed_linear_error,
normalized_max_corrective_velocity: self.normalized_max_corrective_velocity,
normalized_prediction_distance: self.normalized_prediction_distance,
num_solver_iterations: self.num_solver_iterations,
num_internal_pgs_iterations: self.num_internal_pgs_iterations,
num_internal_stabilization_iterations: self.num_internal_stabilization_iterations,
min_island_size: self.min_island_size,
max_ccd_substeps: self.max_ccd_substeps,
friction_model: self.friction_model,
}
}
///|
pub fn IntegrationParameters::set_inv_dt(
self : IntegrationParameters,
inv_dt : @core.Real,
) -> IntegrationParameters {
if inv_dt == 0.0F {
self.dt = 0.0F
} else {
self.dt = 1.0F / inv_dt
}
self
}
///|
pub fn IntegrationParameters::set_min_ccd_dt(
self : IntegrationParameters,
min_ccd_dt : @core.Real,
) -> IntegrationParameters {
self.min_ccd_dt = min_ccd_dt
self
}
///|
pub fn IntegrationParameters::set_contact_softness(
self : IntegrationParameters,
contact_softness : SpringCoefficients,
) -> IntegrationParameters {
self.contact_softness = contact_softness
self
}
///|
pub fn IntegrationParameters::set_warmstart_coefficient(
self : IntegrationParameters,
warmstart_coefficient : @core.Real,
) -> IntegrationParameters {
self.warmstart_coefficient = warmstart_coefficient
self
}
///|
pub fn IntegrationParameters::set_length_unit(
self : IntegrationParameters,
length_unit : @core.Real,
) -> IntegrationParameters {
self.length_unit = length_unit
self
}
///|
pub fn IntegrationParameters::set_normalized_allowed_linear_error(
self : IntegrationParameters,
normalized_allowed_linear_error : @core.Real,
) -> IntegrationParameters {
self.normalized_allowed_linear_error = normalized_allowed_linear_error
self
}
///|
pub fn IntegrationParameters::set_normalized_max_corrective_velocity(
self : IntegrationParameters,
normalized_max_corrective_velocity : @core.Real,
) -> IntegrationParameters {
self.normalized_max_corrective_velocity = normalized_max_corrective_velocity
self
}
///|
pub fn IntegrationParameters::set_normalized_prediction_distance(
self : IntegrationParameters,
normalized_prediction_distance : @core.Real,
) -> IntegrationParameters {
self.normalized_prediction_distance = normalized_prediction_distance
self
}
///|
pub fn IntegrationParameters::set_num_solver_iterations(
self : IntegrationParameters,
num_solver_iterations : Int,
) -> IntegrationParameters {
self.num_solver_iterations = num_solver_iterations
self
}
///|
pub fn IntegrationParameters::set_num_internal_pgs_iterations(
self : IntegrationParameters,
num_internal_pgs_iterations : Int,
) -> IntegrationParameters {
self.num_internal_pgs_iterations = num_internal_pgs_iterations
self
}
///|
pub fn IntegrationParameters::set_num_internal_stabilization_iterations(
self : IntegrationParameters,
num_internal_stabilization_iterations : Int,
) -> IntegrationParameters {
self.num_internal_stabilization_iterations = num_internal_stabilization_iterations
self
}
///|
pub fn IntegrationParameters::set_min_island_size(
self : IntegrationParameters,
min_island_size : Int,
) -> IntegrationParameters {
self.min_island_size = min_island_size
self
}
///|
pub fn IntegrationParameters::set_max_ccd_substeps(
self : IntegrationParameters,
max_ccd_substeps : Int,
) -> IntegrationParameters {
self.max_ccd_substeps = max_ccd_substeps
self
}
///|
pub fn IntegrationParameters::allowed_linear_error(
self : IntegrationParameters,
) -> @core.Real {
self.normalized_allowed_linear_error * self.length_unit
}
///|
pub fn IntegrationParameters::max_corrective_velocity(
self : IntegrationParameters,
) -> @core.Real {
self.normalized_max_corrective_velocity * self.length_unit
}
///|
pub fn IntegrationParameters::prediction_distance(
self : IntegrationParameters,
) -> @core.Real {
self.normalized_prediction_distance * self.length_unit
}