mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
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:
co-authored by
Claude Opus 5
parent
8c4b05ae9d
commit
1bde448f51
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,14 @@ public:
|
||||
// In-canvas bounding-box transform for imported Text/SVG art:
|
||||
TransformArt,
|
||||
Constrain };
|
||||
// Which tool is armed, and how many anchors it has down. Read-only, for the offer ladder:
|
||||
// "the menu armed the verb I chose" is otherwise unassertable, and a menu walk that lands one
|
||||
// row off arms a NEIGHBOURING tool and then grades whatever that drew. snaporca-ekt9.
|
||||
Mode mode() const { return m_mode; }
|
||||
int pending_points() const { return int(m_points.size()); }
|
||||
// Is an in-canvas value field open? While one is, the canvas is frozen and every letter is
|
||||
// swallowed — the single most common reason a driven gesture "does nothing".
|
||||
bool value_field_open() const { return m_awaiting_length; }
|
||||
bool is_edit_op_mode() const { return m_mode == Mode::Fillet || m_mode == Mode::Chamfer ||
|
||||
m_mode == Mode::Offset || m_mode == Mode::Mirror; }
|
||||
bool is_transform_mode() const { return m_mode == Mode::Move || m_mode == Mode::Rotate ||
|
||||
|
||||
@@ -1271,9 +1271,38 @@ json sketch_entity_to(const SketchEntity& e, int index)
|
||||
j["type"] = "point";
|
||||
j["p"] = json::array({e.p0.x(), e.p0.y()});
|
||||
break;
|
||||
case SketchEntity::Type::Ellipse: j["type"] = "ellipse"; break;
|
||||
case SketchEntity::Type::EllipseArc: j["type"] = "ellipse_arc"; break;
|
||||
case SketchEntity::Type::BSpline: j["type"] = "spline"; break;
|
||||
// Ellipses and splines used to serialise as a TYPE NAME and nothing else, so every
|
||||
// parameter they have was invisible to the only read-back this project has. A ladder could
|
||||
// count them and grade the faceted area of the loop they close (2e-2, the faceting error) —
|
||||
// it could not check a single axis, angle or pole. "Precise definition of every aspect"
|
||||
// cannot be asserted about an entity whose aspects the instrument cannot see.
|
||||
case SketchEntity::Type::Ellipse:
|
||||
j["type"] = "ellipse";
|
||||
j["center"] = json::array({e.center.x(), e.center.y()});
|
||||
j["radius"] = e.radius; // semi-major (a)
|
||||
j["rminor"] = e.rminor; // semi-minor (b)
|
||||
j["rotation"] = e.rotation; // major-axis angle, radians
|
||||
break;
|
||||
case SketchEntity::Type::EllipseArc:
|
||||
j["type"] = "ellipse_arc";
|
||||
j["center"] = json::array({e.center.x(), e.center.y()});
|
||||
j["radius"] = e.radius;
|
||||
j["rminor"] = e.rminor;
|
||||
j["rotation"] = e.rotation;
|
||||
j["start_angle"] = e.start_angle;
|
||||
j["end_angle"] = e.end_angle;
|
||||
j["p0"] = json::array({e.p0.x(), e.p0.y()});
|
||||
j["p1"] = json::array({e.p1.x(), e.p1.y()});
|
||||
break;
|
||||
case SketchEntity::Type::BSpline: {
|
||||
j["type"] = "spline";
|
||||
json poles = json::array();
|
||||
for (const Vec2d& c : e.ctrl) poles.push_back(json::array({c.x(), c.y()}));
|
||||
j["ctrl"] = poles;
|
||||
j["p0"] = json::array({e.p0.x(), e.p0.y()});
|
||||
j["p1"] = json::array({e.p1.x(), e.p1.y()});
|
||||
break;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
@@ -1462,11 +1491,29 @@ json action_sketch_describe(DesignPanel* panel, const json& params)
|
||||
json ents = json::array();
|
||||
for (int i = 0; i < int(t.entities().size()); ++i)
|
||||
ents.push_back(sketch_entity_to(t.entities()[i], i));
|
||||
// The armed TOOL and its pending anchors. Without these the only way to tell which tool a
|
||||
// menu row actually armed is to draw with it and infer from what came out — which is how a
|
||||
// menu walk that lands one row off gets diagnosed as "the tool is broken".
|
||||
static const char* const kModeNames[] = {
|
||||
"select", "dimension", "polyline", "line", "rect_corner", "rect_center", "rect_oblique",
|
||||
"rect_rounded", "circle_center", "circle_2pt", "point",
|
||||
"circle_3pt", "arc_3pt", "arc_tangent", "arc_center", "slot", "slot_arc", "polygon",
|
||||
"ellipse", "ellipse_arc", "spline",
|
||||
"fillet", "chamfer", "offset", "mirror",
|
||||
"trim", "extend",
|
||||
"move", "rotate", "scale", "array", "array_polar",
|
||||
"transform_art",
|
||||
"constrain" };
|
||||
const int mi = int(t.mode());
|
||||
json out{{"ok", true},
|
||||
{"entities", ents},
|
||||
{"constraints", int(t.constraints().size())},
|
||||
{"dof", t.dof()},
|
||||
{"solve_ok", t.solve_ok()},
|
||||
{"tool", (mi >= 0 && mi < int(sizeof(kModeNames) / sizeof(kModeNames[0])))
|
||||
? kModeNames[mi] : "unknown"},
|
||||
{"pending", t.pending_points()},
|
||||
{"editing", t.value_field_open()},
|
||||
{"selection", t.selection()}};
|
||||
out.update(sketch_report(t));
|
||||
return out;
|
||||
|
||||
Reference in New Issue
Block a user