Skip to content

Latest commit

 

History

History
203 lines (164 loc) · 10.5 KB

File metadata and controls

203 lines (164 loc) · 10.5 KB

Implementation validation

Verified on 2026-09-14 with Flutter 3.44.2 and Dart 3.12.2.

Automated checks

  • sloshgen: 46 passing tests; dart analyze reports no issues.
  • slosh: 30 passing tests plus the opt-in hybrid gallery; flutter analyze reports no issues.
  • Example: 2 passing widget tests; flutter analyze reports no issues.
  • macOS profile-mode build and execution succeed.

Coverage includes required recipe fields and numeric limits, SVG command support and rejection, contour selection and concavity, curve extrema, unwrapped rotation, speed-dependent response, damping, area correction, empty/full fills, loop convergence/failure, deterministic bytes, compression, decoded output error, CLI/library equivalence and output preservation.

Flutter tests cover immutable decoding and malformed assets, shared dense reference agreement, endpoint/midpoint/seeking behavior, controls and ownership, asset replacement, TickerMode cleanup, stable CustomPaint across ticks, and pixel containment through rotated concave poses at 48 and 240 pixels. The example test switches scenes and scrolls to the playback controls.

dart run tool/regenerate.dart recreates all v1/v2 recipe outputs and the five v1 dense numeric references. Dense references include independently integrated midpoint samples at 480 Hz. No source path from the developer's machine is baked into an asset. All example inputs are inside this repository.

Baked assets

Scene Layers Retained frames Encoded bytes Max area error
Rectangle tilt 1 27 18,481 < 1e-12
Quick turn 1 29 19,730 0.000390
Off-center pivot 1 29 20,259 0.000633
PathVerse single 1 33 26,504 0.001782
PathVerse layered 3 33 67,124 0.002103

Area error is an absolute fraction of the vessel area, measured on interpolated playback at validation samples. All scenes meet the requested 0.005 limit and 0.002 normalized surface-displacement tolerance. The layered loop's position seam is 0.00000170 and its velocity seam is 0.000697 normalized units/second, below 0.002 and 0.02 respectively. See fixtures/assets/*.report.json for exact values and validation cadence. These are sampled bounds, not proofs over all continuous time.

Visual inspection

The rotation contact sheet samples 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 3.99 and 4 seconds. The observed surface stays approximately horizontal in presentation space while the vessel turns; the notch remains clear. Multiple slightly offset surfaces give a narrow translucent depth band. The final and initial poses agree visually, with a numeric velocity check as well. The live macOS player was inspected at enlarged size; pixel tests cover both icon and enlarged sizes. The model provides gentle stylized sloshing, not breaking waves or independently trapped liquid pools.

The gallery is reproducible with:

cd packages/slosh
flutter test --dart-define=RECORD_GALLERY=true test/gallery_test.dart

On macOS the recording test loads the installed Arial font for readable labels; that font is not copied or distributed in the repository. Elsewhere the image can use the Flutter test font. This affects labels, not liquid rendering.

macOS profile measurements

Hardware: MacBook Pro (Mac16,1), Apple M4, 10 CPU cores, 16 GB RAM. OS: macOS 26.6.2 (25G83). Profile mode, Flutter 3.44.2 / Dart 3.12.2. The active Flutter view reported 60 Hz, a 16.67 ms frame interval. Renderer: Skia using Metal. This toolchain's macOS desktop launcher sets enable-impeller=false for platform-default operation; the generated app does not override FLTEnableImpeller. The engine's macOS renderer uses a Metal renderer configuration. No renderer override was supplied for this run.

Each case had two seconds of warm-up and six seconds of frame-timing collection. The harness repaints the liquid without rebuilding a control panel on each frame. Raw captured results and environment metadata are in profile-macos.json.

Layers Icons Size Samples UI p50 / p95 (ms) Raster p50 / p95 (ms)
1 1 48 364 0.334 / 0.429 0.934 / 1.383
1 12 48 357 0.619 / 0.836 1.524 / 2.319
3 1 48 357 0.366 / 0.467 0.986 / 1.312
3 12 48 363 0.631 / 1.055 1.404 / 2.400
3 1 240 356 0.349 / 0.468 1.197 / 1.689

Every measured p95 UI and raster duration is below the active display's frame interval. This is one short desktop measurement, not an FPS guarantee for phones, web, busy applications or other renderers. Android/iOS/web runtime performance and total decoded heap usage were not measured.

The single-layer asset is 26,504 encoded bytes and has 10,528 bytes of decoded numeric payload; three layers use 67,124 and 27,424 bytes respectively. The numeric figures count coordinates and samples at eight bytes each. They exclude VM object/list overhead, strings, paints, paths and GPU resources and must not be reported as total heap memory. Multiple players can share the immutable asset; each adds its own interpolation buffers and rendering objects.

Reproduce from packages/slosh/example:

flutter run -d macos --profile --dart-define=SLOSH_PROFILE=true

The SLOSH_PROFILE_DONE console line contains all eight cases (three v2 cases plus the five v1 comparisons). Profile another target before making a performance claim about that target.

Version 2: pool and rounded droplets

This correction replaces the previously reviewed launch effect. The model stays small: a pool waterline, rounded droplets, and attached/free wall state. Earlier parcel experiments and launch-based visual/performance acceptance are superseded.

Overview at 240/48 pixels · Wall carry, return, breakup and recombination

The clockwise rolling reference uses friction 0.85, adhesion 0.5 and a 1.4-second pickup cooldown. The first bead starts submerged at the rising left wall, travels upward while attached, releases at 0.692 s with its carried velocity, and returns to the pool (visible contact around 1.827 s, scalar absorption at 1.975 s). A second pickup starts at 1.4 s, so an amber bead remains visible after the first returns. Zero friction produces no pickups. Higher adhesion retains the bead farther around the wall. Counterclockwise motion lifts the right wall.

The combined shake uses x/y translation and angular reversals. It stretches, fragments and finally recombines around 2.612 s. Translation-only and rotation-only controls also split and merge; sustained spin and constant translation do not continuously add fragmentation strain. The low-fill bead settles, colored seeds merge using the documented style inheritance, and the supported PathVerse basin retains its pool. Pool and droplet colors remain independently configurable.

The rendered overview and event sides were inspected at both sizes; the live 240-pixel rolling animation and twelve 48-pixel icons were also inspected. The tiny breakup neck becomes subpixel at 48 pixels. Remesh boundaries preserve visual shape within the configured error budget. These are stylized circular beads, not wetting films or a physically calibrated fluid simulation.

Scene Encoded bytes Retained frames Max active vertices Max visible area error Max boundary error
rolling-circle 1,892,375 375 384 0.788% 0.003780
violent-shake 958,184 232 512 4.095% 0.002370
shake-translation 791,476 185 512 4.107% 0.003321
shake-rotation 581,645 180 256 3.141% 0.003288
droplet-bead 150,554 56 128 1.258% 0.000293
droplet-split 581,645 180 256 3.141% 0.003288
droplet-merge 170,156 41 256 1.236% 0.000321
hybrid-pathverse 58,703 21 128 1.330% 0.000233
rolling-circle-no-friction 919,749 351 128 0.622% 0.000337

Errors use normalized source distance. Rolling and shaking recipes use quality.surface: 0.004; the other examples use 0.002. Each simple contour has 128 points. Scalar amounts are conserved within floating-point tolerance; visible area is approximate and meets the configured 5% relative budget. Geometry is checked at dense reference times, independent retained midpoints and event sides, including simplicity, containment, overlap and correspondence. Translation offsets are sampled alongside contours, motion knots survive compression, and fixed swept bounds keep the fit scale stable.

Generator tests cover linear/angular velocity knots at 30/60/120 Hz, one-time strain injection, steady-motion controls, friction/adhesion comparisons, caps, amounts, styles, compact translated states, offset budgets and loop rejection. Player tests cover translated liquid/shell/clipping at pixel level, empty/full states, reverse seeking, incoming event selection, colors, holes and lifecycle. CLI/library parity and output preservation remain covered for both versions. All nine v2 fixtures regenerated byte-identically, and their playground copies match. The 15 historical v1 fixture hashes remain unchanged after regeneration.

Reproduce the images with:

cd packages/slosh
SLOSH_WRITE_GALLERY=true flutter test test/hybrid_gallery_test.dart

Corrected macOS profile

M4 MacBook Pro, Flutter 3.44.2 / Dart 3.12.2, profile mode, Skia/Metal. The display reported 120 Hz. Each case has two seconds of warm-up and six seconds of samples. One-shot effects restart explicitly for measurement, without claiming loop safety. The profiling harness now initializes its controller before registering/starting callbacks; a regression test covers startup and restart. The clean run contains no reported framework errors. Full results and device metadata: profile-hybrid-macos.json.

Rolling icons Size Frame reports UI p50 / p95 (ms) Raster p50 / p95 (ms)
1 48 715 0.179 / 0.381 0.614 / 1.126
12 48 359 0.393 / 0.692 1.508 / 2.148
1 240 729 0.180 / 0.322 0.791 / 1.169

The twelve-icon case reported 359 frames over roughly six seconds; these timings do not establish sustained 120 fps. Startup asset validation/decoding is excluded. The rolling asset is 1,892,375 encoded bytes and 1,510,472 bytes of numeric payload, with at most 384 active vertices. Numeric payload excludes object/string and renderer overhead. Players share the decoded asset and keep bounded interpolation buffers. The asset is larger than a v1 waterline animation; playback remains interpolation and painting. Mobile and web performance remain unmeasured.