Every 2D verb without a shortcut, driven from the offer — and three more defects

The 2D vocabulary is 46 verbs: 22 have a shortcut and the gesture ladder drives them, 24 have
none and nothing had ever exercised those. They are reachable only from the right-click offer, so
a key-driven ladder could not have touched them whatever it did. Four new rungs drive all 24, and
the coverage claim itself is now arithmetic against DesignOffer.hpp (rung O8) rather than a
sentence in a comment that rots when a verb is added.

The assertions are CONSTRUCTION invariants wherever a click cannot be exact — a regular polygon's
sides are equal to 1e-9 and its vertices lie on one circle; a tangent arc's radius at the shared
endpoint is perpendicular to the line to 1e-9 (measured cos 5.97e-17); the three clicks of a
3-point circle all lie on it; a circumscribed pentagon's circumradius is the inscribed one's over
cos(pi/5), 1.236067977 against 1.236067977. Where a value field opens, the typed value is graded
exactly: a moved line travels +25.000000000 in X and 0 in Y, a rotation turns 30.000000000 deg
and leaves the length alone, a scale multiplies it by exactly 3, a linear array's pitch is
[20.0, 20.0, 20.0] and a polar one's spokes are 60 deg apart all the way round.

Three defects found doing it, all fixed here:

snaporca-ua9g (P1) — delete_selected left three things behind. The AUTO-EDIT QUEUE, so a queued
field opened on a deleted entity and its commit went nowhere: draw a rounded rectangle, delete
everything, draw a 2-point circle, type 30 — the field opens, the digits are accepted, and the
radius stays 32.992020763. reset_autoedit() exists for exactly this and its own comment says so;
it was simply never called from here. The FEATURE GROUPS, whose [begin,end) ranges all shift on a
delete, so feature_of() answered with a group the user never drew — survivors are now remapped
and any group that lost a member is dropped, the rule the placed quotes already followed. And the
SOLVER STATE: no re-solve, so sketch_describe reported dof=16 for a document holding one circle.

snaporca-ekt9 (P2) — the read-back could not see three of its seven entity types. Ellipse,
EllipseArc and BSpline serialised as a bare type name: no centre, no semi-axes, no rotation, no
sweep, no poles. gui-ladder's ellipse rung had to grade the faceted area of the loop at 2e-2 —
that tolerance IS the faceting error — and its spline rung could only count entities. Now they
carry their parameters, and the ellipse arc's ends are asserted to satisfy (x/a)^2+(y/b)^2 = 1 to
1e-9.

Also read-only, and the reason the other two were found at all: sketch_describe now reports the
armed TOOL, the count of PENDING anchors, and whether a value field is EDITING. A menu walk that
lands one row off arms a neighbouring tool and then draws something plausible — the first run of
the authoring rung drew a circle of area 45238.93 and graded it as a rectangle. Every menu pick
now asserts which tool it armed, and the polyline rung (a per-segment Length field freezes the
canvas after every click) could only be written once the driver could ask whether a field was open.

Offer ladder 102/102 -> 105/105 with coverage. Gesture ladder 93/93 and the kernel suite
188 cases / 2532 assertions, both unchanged.

snaporca-ua9g snaporca-ekt9

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MrMzTpAf78U4NG2M8jfvHY
This commit is contained in:
Tommaso Bianchi
2026-08-23 05:11:15 +02:00
co-authored by Claude Opus 5
parent 8c4b05ae9d
commit 1bde448f51
4 changed files with 724 additions and 4 deletions
+36
View File
@@ -381,6 +381,42 @@ void DesignSketchTool::delete_selected()
// a stale m_dim_e0 would dereference out of range on the next click. Drop it too.
m_dim_e0 = -1;
m_dim_r0 = SketchPointRole::P0;
// FEATURE GROUPS hold [begin,end) ranges into m_entities, and every index past a deletion has
// just moved. Left alone they point at other people's geometry: feature_of() then answers with
// a group the user never drew, and the rect/slot/polygon handles and live quotes follow it.
// Survivors are remapped (a contiguous range stays contiguous, since the remap preserves
// order); a group that lost any member is dropped, the same rule the placed quotes above
// already follow — dangling is worse than absent.
{
std::vector<Feature> kept_f;
for (const Feature& f : m_features) {
if (f.begin < 0 || f.end > n || f.end <= f.begin) continue;
bool whole = true;
for (int k = f.begin; k < f.end; ++k)
if (del[k]) { whole = false; break; }
if (!whole) continue;
Feature g = f;
g.begin = remap[f.begin];
g.end = remap[f.end - 1] + 1;
kept_f.push_back(g);
}
m_features.swap(kept_f);
m_open_feature = -1;
}
// The draw-then-edit QUEUE outlives the entities it was queued for. Its own helper says so:
// "Removing an entity that still has a deferred auto-edit would otherwise open a field on a
// now-deleted entity and freeze the flow" — it was simply never called from here. Measured:
// delete a rectangle whose Width/Height were still queued, draw a circle, type its radius —
// the field opens, the digits go in, and the radius does not move, because the field belongs
// to a rectangle that no longer exists. snaporca-ua9g.
reset_autoedit();
// And re-solve, so the sketch's reported degrees of freedom describe the sketch that is
// actually there. Without this, sketch_describe answered dof=16 for a document holding one
// circle — the DoF of the geometry that had just been deleted.
resolve_live();
if (on_selection_changed) on_selection_changed(0);
}