Skip to main content

ScaleProof

Struct ScaleProof 

Source
#[non_exhaustive]
pub struct ScaleProof {
Show 17 fields pub tolerance_policy: ScaleTolerancePolicy, pub rest_translation: ScaleProofResidual, pub rest_rotation: ScaleProofResidual, pub unit_scale: ScaleProofResidual, pub transform_only_affine: ScaleProofResidual, pub track_value: ScaleProofResidual, pub mesh_position: ScaleProofResidual, pub key_translation: ScaleProofResidual, pub cubic_interior: ScaleProofResidual, pub trajectory: ScaleProofResidual, pub skin_matrix: ScaleProofResidual, pub bounds: ScaleProofResidual, pub unaffected_inverse_bind: ScaleProofResidual, pub observed_factor: f64, pub planned_observed_factor: f64, pub observed_factor_divergence: f64, pub sample_time_count: usize,
}
Expand description

Observed residual maxima from prove_scale, reported against ScalePlan::tolerance_policy, each paired with the number of comparisons that produced it.

Read the paired value, not just its maximum. A maximum alone cannot distinguish “compared, no deviation” from “nothing to compare”: both read 0.0, because every maximum starts there and is raised only by a loop that may have zero iterations. So a field for an obligation the plan does not require (see ScaleProofObligation) reports a ScaleProofResidual whose maximum and count are both zero. A 0.0 maximum with a count above zero is a measurement; a zero count is an absence, which DESIGN.md Appendix D §D.6 requires an evidence record to publish as an absence rather than as a checked zero.

The counts are measurements, not proxies derived from obligation presence: each is stored with its maximum by the single private recording method every comparison in prove_scale funnels through. The pair’s fields are private, so no producer can combine one claim’s count with another claim’s maximum. No comparison can raise a residual without being counted, and no count can rise without a comparison having been checked against the tolerance policy. A ScaleProofObligation may declare a proof walk, or name a row-driven claim discharged through the canonical field inventory; neither role substitutes for the comparison count recorded here.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§tolerance_policy: ScaleTolerancePolicy

The tolerance policy every residual below was checked against.

§rest_translation: ScaleProofResidual

Maximum rest-world translation residual, with one comparison per affected node.

§rest_rotation: ScaleProofResidual

Maximum rest-world rotation residual, in radians, directly comparable to ScaleTolerancePolicy::rotation_residual_radians.

Measured as a double-cover-aware quaternion chord length |q1 - q2| = 2 * sin(theta / 4) and reported as the angle theta = 4 * asin(chord / 2) that chord represents, so this value and the tolerance it is checked against carry the same unit. Its count is one per affected node.

§unit_scale: ScaleProofResidual

Maximum postcondition unit-scale residual, with one comparison per affected node when the rest/bind plan declares the postcondition.

§transform_only_affine: ScaleProofResidual

Maximum transform-only attachment full-affine residual (rest/bind only), with one probe-point comparison per attachment.

§track_value: ScaleProofResidual

Maximum per-element animation-track value residual, across rewritten translation elements and every retained rotation/scale element. Its count is one per track element across every clip.

§mesh_position: ScaleProofResidual

Maximum per-vertex base mesh POSITION residual, with one comparison per vertex.

§key_translation: ScaleProofResidual

Maximum residual at any affected-track keyframe time, with one comparison per affected translation track per key time.

§cubic_interior: ScaleProofResidual

Maximum residual at any bounded cubic-segment interior time, with one comparison per affected translation track per interior time.

§trajectory: ScaleProofResidual

Maximum sampled world-space trajectory residual, with one comparison per affected node per sample time.

§skin_matrix: ScaleProofResidual

Maximum skin-matrix (W * B) component residual, across rest and every sampled key/cubic-interior time. Its count is one per skin slot of every affected skinned instance at each evaluated pose.

§bounds: ScaleProofResidual

Maximum skinned mesh bounds residual, across rest and every sampled key/cubic-interior time. Its count is six per evaluated pose.

§unaffected_inverse_bind: ScaleProofResidual

Maximum stored inverse-bind residual over every skin slot outside the affected closure (see ProofResidualKind::UnaffectedInverseBind). Its count is one per stored slot compared outside the closure and zero unless the plan declares ScaleProofObligation::UnaffectedInverseBinds.

§observed_factor: f64

The operation’s observed factor, re-derived by this proof from the documents it was handed rather than copied from ScalePlan::observed_factor, so evidence does not depend on planning having recorded it.

Reported, not checked. This is a measurement, not an obligation: nothing here compares it against ScalePlan::common_factor. The declared/observed agreement is the input contract §D.1 states, and planning already enforces it (ScaleError::FactorMismatch); what binds the candidate is the postcondition (ProofResidualKind::UnitScale) that §D.1 derives from that band.

Measured from source, not from candidate. That is a choice, not an impossibility: a rest/bind candidate does record what its source measured. build_rest_bind rebases an affected node’s local scale by s_parent / s_node with s_node the declared factor, and the affected closure never contains the scaled root’s parent, so s_parent is one there and the candidate’s composed root scale is exactly s_observed / s_declared — unit only when the two agree, which the input band admits without requiring. The candidate route is real and it is accurate: plan.common_factor() * average_affine_axis_length( affine_axis_lengths(candidate root world linear)) recovers the measurement to a relative error below 2^-24, the half-ulp of the one binary32 rounding that round trip costs at unit magnitude. Swept over all 215 binary32 values the 1e-5 band admits around a declared 0.01, the worst is 5.9604613e-8; over this module’s own 0.01-factor fixtures it is 4.84e-8, at a source root of 0.010_000_099.

It is not the route taken, for four reasons:

  • Independence. prove_scale does not require candidate to have come from reference construction; checking one that did not is the reason it exists. Reading the reported measurement off the artifact under test would let that artifact pick its own evidence value. The observed factor is a fact about the input, so the input is where it is measured.
  • Precision. The candidate route divides by the declared factor in f32 and multiplies back in f64, so it reports a rounded neighbour of the source measurement rather than the measurement.
  • Sign. The nearest already-published proxy, Self::unit_scale’s max(), is an absolute value, so common_factor * (1 + unit_scale.max()) reconstructs s_observed only when the observed factor is the larger of the two and reflects it about the declared factor when it is not.
  • Attribution. That residual is also a maximum over every affected node rather than a value read at the scaled root, so it does not name the node §D.6 defines the observed factor at.

See ScalePlan::observed_factor for how each operation defines it; for a whole-document conversion this is the declared factor, there being nothing to measure — and there the candidate route genuinely does not exist, because build_whole_document rewrites translations only and leaves every composed scale exactly as it found it.

§planned_observed_factor: f64

The observed factor super::plan_scale measured, copied from ScalePlan::observed_factor.

The record carries both witnesses because they are measured from genuinely different state and neither is derivable from the other: this one from the raw source projection (SourceNodeAsset::local_rest composed through parent_source_node_index), Self::observed_factor from the normalized skeleton (Bone::rest composed through world_rest_matrices). That independence is the property DESIGN.md Appendix D §D.6 wants of a second witness, and it is why the two are generally not equal. Carrying only one of them would leave a reader unable to tell which was reported; carrying both with no stated relationship would leave them unable to tell which to trust, which is what Self::observed_factor_divergence answers.

For ScaleOperation::WholeDocumentLinearUnits both are the declared factor, there being nothing to measure, and the divergence is exactly zero.

§observed_factor_divergence: f64

How far apart the two observed factors are: abs(planned - proved) / max(abs(planned), abs(proved)).

Recorded explicitly rather than left for a consumer to compute, so the evidence record states the relationship between its own two witnesses instead of presenting two numbers that both answer to “the observed factor”. Compare it against ScaleTolerancePolicy::observed_factor_divergence_ceiling, which is how far apart the design expects them to be and why — a consumer does not have to re-derive that ceiling by summing two separate policy fields.

Reported, not checked. Nothing refuses a document for exceeding the ceiling; see that method for what the ceiling does and does not guarantee. The two chains the witnesses compose through are already required to agree: under crate::model::SourceSkeletonCoverage::Complete coverage a document whose projection and skeleton describe different trees is refused before either witness is taken. What nothing reconciles is the two readingscrate::model::SourceNodeAsset::local_rest and crate::model::Bone::rest stay separately stored and separately composed, which is why both witnesses exist at all. A divergence beyond the ceiling is therefore a fact about how far apart the input’s two stored descriptions of one rest pose are, worth surfacing, not a residual this proof owns.

§sample_time_count: usize

Number of distinct times sampled across all clips.

Trait Implementations§

Source§

impl Clone for ScaleProof

Source§

fn clone(&self) -> ScaleProof

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for ScaleProof

Source§

impl Debug for ScaleProof

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ScaleProof

Source§

fn eq(&self, other: &ScaleProof) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for ScaleProof

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.