Module Beloch.Flatten

The generator half of the flatten solver. Given a set of material creases ("rays") sharing one interior vertex O, an ODD ray count means one emergent ray is part of the solution space; candidates generates the geometric completions that could close the vertex — same-direction filter, per-line dedup, two-tier (`LineNew`/`OppositeRay`) preference tag. It does NOT check feasibility or M/V and does NOT pick a winner.

Choosing among them is Flatten_solve's job: it enumerates every Maekawa-consistent M/V pattern (mv_patterns, pure and unit-testable) over each candidate's full ray set, tries each via Collapse.collapse_all, pools the results, and disambiguates by tier then by `(toward)`'s moved-material-centroid score. Feasibility-filtering and `toward`-selection belong there rather than here because they need Collapse.collapse_all and Ast.mv_constraint, neither of which this module should depend on beyond the generator's own geometry.

val e_infeasible : string
val e_toward_ambiguous : string
val e_toward_ambiguous_hint : string
val axis_direction : Isometry.t -> Num.t * Num.t
val in_gap : Geom.point -> Geom.point -> Geom.point -> Geom.point -> bool
val candidates : Geom.point -> fixed:(Geom.point * Collapse.elem) list -> (Geom.line * Geom.point * [ `LineNew | `OppositeRay ]) list
val mv_patterns : Ast.mv_constraint list -> bool list list