grid-apps-cmms/docs/void/plan-constraints-v2.md
2026-03-03 12:04:39 -05:00

150 lines
6 KiB
Markdown

# Void Constraints V2 Plan (Planegcs-First, Fallback as Safety Net)
## Goal
Move sketch solving to a clean planegcs-first architecture, remove dual-solver behavioral drift, and improve drag/tangent stability.
## Current Findings
1. Constraint solving is currently dual-mode by default:
- `enforceWithPlanegcs()` runs, then a fallback settle pass is still applied.
- This can reintroduce different motion/priority behavior after planegcs already converged.
2. Drag interactions frequently force fallback:
- During drag, `useFallback: tangentDriven || !pointDrag` is used in pointer drag paths.
- This bypasses planegcs exactly where stable incremental behavior matters most.
3. Tangent constraints are not mapped into planegcs in the current mapper:
- `toPlanegcsConstraint()` covers many constraints but not sketch `tangent`.
- Tangency currently depends on fallback heuristics (`constraints_tangent.js`).
4. The planegcs wrapper already supports temporary constraints:
- Constraint objects with `temporary: true` are supported by the wrapper path.
- This enables proper drag-driving constraints with lower priority solving semantics.
5. `angle_via_point` is available in the solver bindings:
- Suitable for robust endpoint tangency encoding (angle = 0 at shared endpoint).
- This aligns with FreeCAD guidance for improved stability vs direct tangent formulations in corner cases.
## Root Cause Summary
1. Two solvers are actively shaping geometry during interaction.
2. Drag logic has explicit fallback preference in key paths.
3. Tangency is solved outside planegcs, creating inconsistent convergence and corner-case instability.
## Target Architecture
1. Planegcs is the primary and default solver for all live interaction and final settle.
2. Fallback solver is retained only as failure recovery.
3. Drag uses temporary constraints in planegcs:
- Temporary point-to-point/point-to-line style guidance constraints to cursor/ghost references.
- No permanent topology mutation from drag constraints.
4. Tangency uses planegcs-native representation:
- Shared-endpoint tangent: `angle_via_point` with angle = 0.
- Non-shared cases: use direct tangent primitives where stable (`tangent_la`, `tangent_aa`, etc.), with endpoint-angle fallback where needed.
## Phased Execution
## Phase 1: Instrumentation and Guardrails
1. Add solver telemetry per enforce call:
- planegcs used, fallback used, solve status, elapsed time.
2. Add debug toggle to display active temporary constraints during drag.
3. Add deterministic logs for tangent constraint path selection.
Exit Criteria:
1. We can observe when and why fallback is invoked.
## Phase 2: Temporary Drag Constraints
1. Add drag-time temporary constraints (`temporary: true`) in planegcs solve graph.
2. Remove drag-path forced fallback defaults.
3. Keep fallback only if planegcs solve fails or returns non-converged status.
Exit Criteria:
1. Drag no longer “snaps back” from dual-pass disagreement.
2. Solver path during normal drag is planegcs-only.
## Phase 3: Tangent Migration
1. Implement tangent mapping in `toPlanegcsConstraint()`:
- line-arc, arc-arc, line-circle as available.
2. For shared-endpoint tangent pairs, map to `angle_via_point` (angle=0).
3. Keep old tangent fallback path behind a temporary feature flag for rollback.
Exit Criteria:
1. Tangent drag corner cases no longer require tangent-specific fallback aggressiveness.
2. Shared-endpoint tangent cases are stable under repeated edits/drag.
## Phase 4: Remove Default Dual Settle
1. Remove unconditional fallback settle after successful planegcs solve.
2. Fallback runs only on explicit planegcs failure paths.
3. Keep compatibility switch (`constraints_v2_force_fallback`) for emergency rollback.
Exit Criteria:
1. Single primary solver behavior in normal operation.
2. Fewer constraint jitter/regressions from solver disagreement.
## Phase 5: Cleanup
1. Simplify pointer drag enforcement call sites.
2. Remove tangent-specific fallback tuning knobs that become obsolete.
3. Document canonical constraint mapping table and temporary-constraint rules.
Exit Criteria:
1. Constraint code paths are materially simpler and easier to reason about.
## Proposed Code Touchpoints
1. `src/void/sketch/constraints.js`
- Add temporary constraint plumbing and tangent mapping in `toPlanegcsConstraint()`.
- Remove unconditional fallback settle on success.
2. `src/void/sketch/pointer.js`
- Replace drag-time fallback preference with planegcs temporary constraints.
3. `src/void/sketch/constraints_actions.js`
- Ensure apply/edit flows use planegcs-first, fallback-on-failure behavior.
4. `src/void/sketch/constraints_tangent.js`
- Transition from primary solver role to compatibility fallback only.
5. `src/void/solver/sketch/gcs_wrapper.js`
- Confirm temporary constraint lifecycle handling and cleanup.
## Risks and Mitigations
1. Risk: Regression in legacy sketches tuned around fallback behavior.
- Mitigation: feature flag + staged rollout + telemetry.
2. Risk: Performance regressions during drag with added temporary constraints.
- Mitigation: limit temporary constraint count to active dragged subset; cap iterations.
3. Risk: Incorrect tangent mapping for mixed entity types.
- Mitigation: explicit mapping matrix tests per constraint subtype.
## Validation Plan
1. Unit tests:
- Tangent mapping (shared endpoint and non-shared).
- Temporary constraint injection/removal lifecycle.
- Planegcs success path without fallback pass.
2. Interaction tests:
- Drag with dimensions, coincident, perpendicular, and tangent combos.
- Repeated drag/release cycles without geometric drift.
- Circular/grid/polygon pattern interactions under drag.
3. Regression scenarios:
- Known tangent corner cases.
- Previously flaky dual-solver “snap back” sketches.
## Recommendation
Start with Phase 2 (temporary drag constraints + fallback-on-failure only for drag) before full tangent migration. This yields immediate UX improvement and reduces dual-solver interference while keeping rollback safety.