Infer parallel, perpendicular, equal radius and tangent while drawing

Port of snaporca 2d36d28770. Parity OK: 17 files identical, 8 diverging at their
expected counts.

infer_axis_constraint returned only Horizontal or Vertical. On the
CAD-1000-hours corpus the top two transitions are sketch_dim -> sketch_draw
(5896) and back (5756): the signature of geometry that does not self-constrain
as it is drawn.

Every rule requires the relation to be ALREADY TRUE within tolerance, so nothing
the user drew is moved; parallel/perpendicular and tangent additionally require a
shared endpoint.

TWO LIMITS THE CORPUS RUNG FORCED, neither visible to the unit tests:

1. At most ONE constraint per rule per new entity, not one per PAIR, and no
   one-at-a-time fallback for the relations batch. EqualRadius has no locality
   restriction, so 200 equal holes produced ~20000 candidates; the rejected batch
   then cost a solve per constraint and pinned the app at 95% of a core with the
   MCP socket unresponsive.

2. Relations only for gesture-sized batches. "A scripted add is not a drawn
   gesture" is already this file's rule at its bulk call site (snaporca-8xg1), and
   EqualRadius also couples geometrically distant entities, merging independent
   connected components and defeating the partitioning that makes large sketches
   solvable (snaporca-yww4). With the cap alone geometry stayed correct (32/32
   sheets clean) but seven of the largest timed out, including MPD681 -- the sheet
   that call site's own comment names.

Also fixes the tolerance leak behind 2: the bulk path asks for exact inference
with ang_tol_rad = 0 but len_tol_frac kept its 0.01 default.

ALSO independent of this feature: run-kernel-tests.sh defaulted to
TAGS=[CadDocument] while four CAD test files carry their own tags and nothing
selected them (2624 assertions / 206 cases reported, 7648 / 264 actual). All 58
dark cases were passing; the coverage was never exercised.

VERIFICATION LIMIT, as with the previous three commits: this fork's kernel suite
still cannot run (find_package(assimp) at configure time, snaporca-w80c). Shared
sources are byte-identical to snaporca's, where kernel is 7651 assertions / 265
cases and ALL LADDERS HELD 7/7.
This commit is contained in:
Tommaso Bianchi
2026-08-31 03:43:08 +02:00
parent 45494c6035
commit fac3cf44df
5 changed files with 259 additions and 1 deletions
+33
View File
@@ -2411,6 +2411,39 @@ void DesignSketchTool::infer_auto_constraints(int base, double ang_tol_rad, doub
if (!try_add_constraints(axes))
for (const auto& c : axes) try_add_constraints({ c });
// 3) Relational constraints on the new entities (parallel/perpendicular on connected
// lines, equal radius, tangent). Same batch-then-one-at-a-time fallback as above:
// try_add_constraints rolls a conflicting batch back, so a single rejected relation
// never costs the others. Every rule only pins a relation that is ALREADY true, so
// nothing the user drew is moved by this.
// A SCRIPTED ADD IS NOT A DRAWN GESTURE — the rule this function already states at its
// bulk call site, which passes zero tolerances for exactly that reason (snaporca-8xg1).
// Relational inference must obey it too, and for a second reason beyond tolerance:
// EqualRadius couples entities that are geometrically far apart, so on a real drawing it
// merges independent connected components into one huge system and defeats the
// component partitioning that makes large sketches solvable at all (snaporca-yww4).
// Measured 2026-08-31 on the corpus rung: geometry stayed correct (32/32 sheets clean)
// but seven of the largest sheets hit main-thread timeout — MPD681 among them, the very
// sheet named in the comment at the bulk call site. Exact-equality would not save it
// either: patterned holes in real drawings ARE exactly equal.
// So: relations only for batches the size of a human gesture. A polyline segment is 1, a
// rectangle 4, a polygon a dozen; a scripted or imported add is hundreds.
constexpr int kRelInferMaxBatch = 16;
std::vector<SketchEntityConstraintDef> rels;
if (n - base <= kRelInferMaxBatch) {
const double rel_len_tol = ang_tol_rad > 0.0 ? 0.01 : 0.0; // exact-only when the caller asked for exact
for (int i = base; i < n; ++i) {
auto r = infer_relations(m_entities, i, ang_tol_rad, rel_len_tol);
rels.insert(rels.end(), r.begin(), r.end());
}
}
// NO one-at-a-time fallback here, unlike the two batches above. Relations are a
// convenience: nothing is incorrect without them. The fallback costs one SOLVE PER
// CONSTRAINT, which is what the warning on the axes batch is about, and paying it for
// optional constraints is how a bulk add pins the app at 100% CPU with the MCP socket
// unresponsive (measured 2026-08-31). If the batch conflicts, drop the batch.
if (!rels.empty()) try_add_constraints(rels);
resolve_live();
}