169 lines
9.3 KiB
Markdown
169 lines
9.3 KiB
Markdown
# Surface Level — Implementation Attack Plan
|
|
|
|
**Status:** Audited; ready for implementation after this revision.
|
|
**Scope:** Surface Level only. Do not change Angle, Edge, ruler, or billing in this stage.
|
|
|
|
## Outcome
|
|
|
|
Build a trustworthy two-axis surface-level tool for a phone lying flat,
|
|
screen-up, on a surface. It must feel like a physical bullseye vial while
|
|
giving clearer, more actionable information than a physical level.
|
|
|
|
The user should be able to answer three questions at a glance or by sound:
|
|
|
|
1. Is this surface level?
|
|
2. How far off is it?
|
|
3. Which side/corner needs adjustment, and by roughly how much?
|
|
|
|
## Existing foundation to preserve
|
|
|
|
- Unified device-frame world-up sensor contract.
|
|
- Raw sensor -> stable measurement -> animated display separation.
|
|
- Vector-space Surface calibration model and two-sample calibration math.
|
|
- EMA smoothing, lock hysteresis/dwell/debounce, deadband, and current tests.
|
|
- The current numeric Surface reading, pitch/roll values, and haptic lock hook.
|
|
|
|
## Product decisions to implement
|
|
|
|
- Surface means the phone is intended to lie flat, screen-up, on the plane being
|
|
measured. Its level-lock behavior is valid only in that geometry.
|
|
- Numeric measurements are never hidden because the phone is steeply oriented.
|
|
Surface tilt magnitude remains authoritative from 0 to 180 degrees. Outside
|
|
the ideal face-up geometry, show the actual magnitude and a quiet placement
|
|
hint; suppress level-lock feedback rather than replacing the reading with `—`.
|
|
- Above 80 degrees of tilt, de-emphasize/suppress Pitch and Roll because their
|
|
off-axis parameterization becomes noisy near vertical. Above 90 degrees,
|
|
identify the screen-down-side state rather than pretending the bullseye is a
|
|
useful placement tool. The magnitude remains visible in both cases.
|
|
- The central visualization is a bullseye vial. It has a finite visual range:
|
|
beyond that range the bubble rests at the rim, while numeric information remains
|
|
full-range and authoritative.
|
|
- Calibration corrects device/setup bias. It is not "make this desk level."
|
|
A later relative-reference feature is separate and out of scope here.
|
|
|
|
## Work sequence
|
|
|
|
### 1. Make measurement behavior honest at every angle
|
|
|
|
- Refactor Surface placement gating so it only controls the level-lock state,
|
|
lock haptic, and geometry-specific guidance.
|
|
- Keep the primary tilt magnitude visible from 0-180 degrees. Keep Pitch and
|
|
Roll visible only where their meaning is stable (below 80 degrees tilt).
|
|
- Add tests at flat, near level, 30 degrees, 80 degrees, near vertical, and
|
|
screen-down. Verify magnitude stays correct, off-axis values are suppressed
|
|
in their ambiguous range, and invalid placement never locks.
|
|
- Keep the existing lock tolerance semantics: a lock label states the tolerance
|
|
rather than pretending any non-zero value is exactly flat.
|
|
|
|
### 2. Add the guided Surface calibration flow
|
|
|
|
- Add a Surface Calibration route from the Level header.
|
|
- Explain that the phone may be calibrated on any firm, stable, *roughly level*
|
|
surface; it need not be perfectly level. Warn and ask for a flatter surface
|
|
when the measured tilt exceeds 5 degrees.
|
|
- Capture the two samples from the uncalibrated sensor path. Never derive a new
|
|
calibration from readings that an existing calibration has already corrected.
|
|
- Step 1: settle, collect a 1.5-second stable window, capture.
|
|
- Step 2: rotate the phone exactly 180 degrees in the same plane, settle, then
|
|
collect and capture another 1.5-second stable window.
|
|
- Use the shared settling detector: enter Settling above 1.0 degrees/second of
|
|
two-axis plane movement; leave it only after movement is at or below 0.3
|
|
degrees/second for 500 ms. Reject/retry windows that are not settled.
|
|
- Reject/retry a derived calibration bias above 3 degrees as evidence that the
|
|
device, setup, or turn was inconsistent. Suppress lock feedback during the flow.
|
|
- Persist only the resulting Surface calibration; never alter Edge calibration.
|
|
- State that the calibration applies to the current phone/case/contact setup.
|
|
- On completion, show a concise success result and a way to clear/re-run it.
|
|
|
|
### 3. Build the tactile bullseye instrument
|
|
|
|
- Use Compose Canvas (or an equivalent composable drawing layer) for a scalable
|
|
circular vial, etched rings, crosshairs, target zone, and bubble.
|
|
- Glass, fluid, and meniscus effects must improve depth/readability; avoid a
|
|
decorative or excessively glossy treatment.
|
|
- Bubble target position derives directly from stable calibrated pitch/roll.
|
|
- Derive bubble target, high/low instruction, and rise/run direction from one
|
|
pure SurfaceGuidance function. Its hysteretic next-state input must prevent
|
|
quadrant labels from flapping at an axis boundary. Use an edge label when the
|
|
minor axis is under 25% of the dominant axis; otherwise use a corner label.
|
|
- Bubble movement uses a critically damped spring (damping ratio >= 1) toward
|
|
that real target: no overshoot, random drift, autonomous wobble, or animation
|
|
that contradicts the number. Clamp the target to the vial's visual range before
|
|
applying the spring so the bubble settles at the rim.
|
|
- Etched rings are real visual calibration marks, not decoration: map them to
|
|
explicit degree values (initially 1, 2, and 5 degrees) using the same pure
|
|
measurement-to-position mapping as the bubble.
|
|
- Respect disabled system animations and the app's reduced-motion preference by
|
|
direct-positioning the deadbanded bubble value instead of running the spring.
|
|
- Show a large total tilt beside/below the vial, then Pitch and Roll below it.
|
|
- At level lock, use the restrained lime state and an explicit text label.
|
|
|
|
### 4. Add actionable adjustment information
|
|
|
|
- Derive a screen-relative high/low direction from calibrated Pitch and Roll.
|
|
Use language such as `High: lower-left`; do not claim a compass direction.
|
|
- Display slope as both degrees and a useful normalized rise/run value:
|
|
millimetres per metre and/or inches per foot based on the selected unit system.
|
|
- Treat exact board/counter dimensions and calculated shim/foot height as a later
|
|
Adjustment Assist feature, not a requirement of this stage.
|
|
- Use the shared settling detector from calibration. When it is settling, show a
|
|
subtle `Settling` state and defer strong correction guidance until the stable
|
|
measurement is trustworthy.
|
|
- Add a persisted units preference in core/settings for rise/run formatting;
|
|
the Tools settings UI remains deferred.
|
|
|
|
### 5. Add optional hands-free Audio Level Assist
|
|
|
|
- Surface-only, off by default, explicitly user-enabled.
|
|
- Use a soft sonar-like pulse with a gentle attack and naturally fading tail;
|
|
never a harsh alarm or rapid smoke-detector cadence.
|
|
- Silent while the shared settling detector is active or materially outside the
|
|
usable placement geometry.
|
|
- Near level: occasional low-key proximity pulses with hysteresis between audio
|
|
proximity bands, so band changes cannot chatter.
|
|
- Locked: one distinct soft lock ping; optionally provide a sparse repeat-while-
|
|
locked preference so a user adjusting a large plank can hear that it remains level.
|
|
- Reuse the existing lock dwell/hysteresis/debounce so sound cannot chatter.
|
|
- Use SoundPool with `USAGE_ASSISTANCE_SONIFICATION`; mix with media rather than
|
|
taking audio focus for intermittent pulses. Respect media volume, system sound
|
|
settings, app lifecycle, and the user's Audio Assist preference. Release audio
|
|
resources with the tool composable/flow collection.
|
|
|
|
## Surface acceptance criteria
|
|
|
|
- A stable phone on a known level plane achieves lock without repeated feedback.
|
|
- A 180-degree calibration from uncalibrated stable samples on a stable,
|
|
roughly-level plane improves repeatability without zeroing that plane's real
|
|
slope; inconsistent/moving captures are rejected.
|
|
- A screen-up phone at 30, 80, near 90, and screen-down angles always shows a
|
|
correct magnitude. Pitch/Roll are suppressed at their documented ambiguous
|
|
range, and the Surface tool never falsely claims a lock.
|
|
- Bubble direction, high/low instruction, and rise/run direction agree in all
|
|
four quadrants through one pure, unit-tested guidance function.
|
|
- Bubble position is a pure mapping of stable measurement plus a no-overshoot
|
|
spring. Its etched rings match that mapping's documented degree marks.
|
|
- Audio can guide a user to lock without viewing the screen and stays quiet when
|
|
disabled, settling, backgrounded, or outside the Surface geometry.
|
|
- Manual checks cover phone-with-case, phone-without-case, a firm tabletop, and
|
|
a large board adjusted by one person.
|
|
|
|
## Explicitly deferred
|
|
|
|
- Angle-screen redesign and signed relative-angle semantics.
|
|
- Edge/Plumb visual and geometry changes.
|
|
- Saved measurements, target-angle alerts, screen ruler, and Play Billing.
|
|
- Board dimension/shim calculator and automatic flat/edge switching.
|
|
|
|
## Questions for Claude's audit
|
|
|
|
1. Does the relaxed placement gate preserve truthful full-range Surface numbers
|
|
while preventing false lock feedback?
|
|
2. Is the two-sample calibration flow mathematically and UX-wise safe for the
|
|
stated use case, including phone cases and non-level calibration surfaces?
|
|
3. Is high/low mapping unambiguous and testable for all pitch/roll quadrants?
|
|
4. Does the bullseye animation remain derived from measurement truth and respect
|
|
reduced-motion/system-animation settings?
|
|
5. Can the audio state machine reuse lock semantics without audio-focus,
|
|
lifecycle, or repeated-feedback problems?
|
|
6. Is any proposed work outside this single-stage scope?
|