///|
fn validate_data_aspect(value : Double) -> Unit raise @core.DataError {
if value <= 0.0 || value.is_nan() || value.is_inf() {
raise @core.DataError::InvalidReference(
message="aspect must be finite and positive",
)
}
}
///|
fn Axes::apply_data_aspect(self : Axes, value : Double?, share : Bool) -> Unit {
self.state.val.data_aspect = value
if share {
for state in self.x_group_states() {
state.val.data_aspect = value
}
for state in self.y_group_states() {
state.val.data_aspect = value
}
}
}
///|
/// Set the display size of one y data unit relative to one x data unit.
/// Values must be finite and positive; invalid input raises InvalidReference
/// without changing this panel or its shared siblings. With box adjustment,
/// the allocated box shrinks using the anchor, preserving data limits.
/// Datalim adjustment keeps the box and changes x or y view limits instead.
/// share=true updates the union of the panel's direct x/y shared siblings.
/// Use set_aspect_auto() for automatic aspect, or set_aspect_equal() for 1.
/// An optional anchor is validated and applied along with the ratio.
/// adjustable accepts "box" or "datalim"; omitted settings inherit this panel's
/// strategy and are propagated with share. The default anchor is the center.
///
/// ```mbt check
/// test {
/// let fig = @scene.figure(width=400, height=200)
/// let ax = try! fig.add_axes(0.25, 0.25, 0.5, 0.5)
/// try! ax.set_xlim(0.0, 1.0)
/// try! ax.set_ylim(0.0, 1.0)
/// try! ax.set_aspect(2.0)
/// assert_eq(ax.get_aspect(), Some(2.0))
/// assert_eq(ax.get_position().unwrap().width(), 0.125)
/// assert_eq(ax.get_position(original=true).unwrap().width(), 0.5)
/// }
/// ```
pub fn Axes::set_aspect(
self : Axes,
value : Double,
share~ : Bool = false,
anchor? : Anchor,
adjustable? : String,
) -> Unit raise @core.DataError {
validate_data_aspect(value)
if anchor is Some(anchor) {
ignore(anchor.coordinates())
}
let mode = adjustable.unwrap_or(self.state.val.adjustable)
self.validate_adjustable(mode, share, fixed_aspect=true)
self.apply_data_aspect(Some(value), share)
self.apply_adjustable(mode, share)
if anchor is Some(anchor) {
self.apply_anchor(anchor, share)
}
}
///|
/// Return the numeric y/x display scale ratio, or None for automatic aspect.
/// Matplotlib's "auto" maps to None and "equal" to Some(1.0).
///
/// ```mbt check
/// test {
/// let ax = @scene.figure().axes()
/// assert_true(ax.get_aspect() is None)
/// ax.set_aspect_equal()
/// assert_eq(ax.get_aspect(), Some(1.0))
/// ax.set_aspect_auto()
/// assert_true(ax.get_aspect() is None)
/// }
/// ```
pub fn Axes::get_aspect(self : Axes) -> Double? {
self.state.val.data_aspect
}
///|
/// Set numeric aspect on the first/default panel, preserving gca selection.
///
/// ```mbt check
/// test {
/// let fig = @scene.figure()
/// try! fig.set_aspect(0.5)
/// assert_eq(fig.axes().get_aspect(), Some(0.5))
/// }
/// ```
pub fn Figure::set_aspect(
self : Figure,
value : Double,
share~ : Bool = false,
anchor? : Anchor,
adjustable? : String,
) -> Unit raise @core.DataError {
validate_data_aspect(value)
if anchor is Some(anchor) {
ignore(anchor.coordinates())
}
if adjustable is Some(mode) {
validate_adjustable_name(mode)
}
self.axes().set_aspect(value, share~, anchor?, adjustable?)
}
///|
/// Return the first/default panel's numeric aspect, or None for auto.
///
/// ```mbt check
/// test {
/// let fig = @scene.figure()
/// fig.set_aspect_equal()
/// assert_eq(fig.get_aspect(), Some(1.0))
/// }
/// ```
pub fn Figure::get_aspect(self : Figure) -> Double? {
self.axes().get_aspect()
}