mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 18:31:11 +00:00
Ladder rung 9: grade the engine against 50 real drawings, not against my taste
Ported from snaporca 5b82c846f3.
This commit is contained in:
@@ -2311,7 +2311,7 @@ bool DesignSketchTool::try_add_constraints(const std::vector<SketchEntityConstra
|
||||
return false;
|
||||
}
|
||||
|
||||
void DesignSketchTool::infer_auto_constraints(int base)
|
||||
void DesignSketchTool::infer_auto_constraints(int base, double ang_tol_rad)
|
||||
{
|
||||
const int n = int(m_entities.size());
|
||||
if (base < 0 || base >= n) return;
|
||||
@@ -2357,7 +2357,7 @@ void DesignSketchTool::infer_auto_constraints(int base)
|
||||
// so a single conflict never drops the others).
|
||||
for (int i = base; i < n; ++i) {
|
||||
if (m_entities[i].type != SketchEntity::Type::Line) continue;
|
||||
auto ax = infer_axis_constraint(m_entities[i].p0, m_entities[i].p1);
|
||||
auto ax = infer_axis_constraint(m_entities[i].p0, m_entities[i].p1, ang_tol_rad);
|
||||
if (!ax) continue;
|
||||
SketchEntityConstraintDef c;
|
||||
c.type = *ax;
|
||||
@@ -8896,7 +8896,15 @@ int DesignSketchTool::add_entities_scripted(const std::vector<SketchEntity>& ent
|
||||
if (ents.empty()) return -1;
|
||||
const int base = int(m_entities.size());
|
||||
for (const SketchEntity& e : ents) m_entities.push_back(e);
|
||||
infer_auto_constraints(base); // the same auto-coincidence/H/V pass a gesture runs
|
||||
// Auto-constrain, but do NOT let the inference move what the caller specified. A gesture
|
||||
// gets 3 degrees of slack because a hand cannot click an exact horizontal; a scripted add
|
||||
// has already said exactly what it means, and snapping a segment 2 degrees off to exactly
|
||||
// horizontal silently rewrites it. Measured on a real drawing: feeding the 64 flattened
|
||||
// segments of one circle moved vertices by up to 0.058 mm and shrank the enclosed area by
|
||||
// 0.067%, because several segments of the polygon fell inside that 3 degree window. Exact
|
||||
// coincidence inference is unaffected — it already tests to 1e-6 — so chains still weld
|
||||
// and genuinely axis-aligned scripted geometry still gets its Horizontal/Vertical.
|
||||
infer_auto_constraints(base, 1e-4);
|
||||
resolve_live();
|
||||
return base;
|
||||
}
|
||||
|
||||
@@ -628,7 +628,11 @@ private:
|
||||
// After entities [base, end) were committed, auto-emit the constraints that make
|
||||
// the new geometry stick: Coincident between co-located endpoints (so loops close
|
||||
// on their own) and Horizontal/Vertical on axis-aligned new segments.
|
||||
void infer_auto_constraints(int base);
|
||||
// ang_tol_rad is how far from an axis a segment may be and still be CALLED axis-aligned.
|
||||
// A gesture needs the default 3 degrees — nobody clicks a horizontal line exactly — but
|
||||
// that same slack MOVES geometry that was given exactly, so the scripted path passes a
|
||||
// tolerance tight enough to recognise only what is already true. See add_entities_scripted.
|
||||
void infer_auto_constraints(int base, double ang_tol_rad = 3.0 * M_PI / 180.0);
|
||||
|
||||
// Selection helpers (Mode::Select).
|
||||
int hit_test(const Vec2d& p, double tol) const; // nearest entity within tol, or -1
|
||||
|
||||
@@ -1204,18 +1204,30 @@ SketchEntity sketch_entity_from(const json& j)
|
||||
return Vec2d(a[0].get<double>(), a[1].get<double>());
|
||||
};
|
||||
e.construction = j.value("construction", false);
|
||||
// A circle and an arc are defined BY their centre, so a request that does not carry one is
|
||||
// incomplete, not a request for a circle at the origin. Defaulting it silently put geometry
|
||||
// somewhere the caller never asked for and then reported perfectly consistent loops, areas
|
||||
// and hole attribution ABOUT THAT WRONG GEOMETRY — which is far more expensive to disbelieve
|
||||
// than an error would have been. `p0` is accepted as an alias because that is exactly what a
|
||||
// circle stores internally (e.p0 = e.center below), so a caller who writes p0 means centre.
|
||||
auto centre_of = [&](const char* what) {
|
||||
if (j.contains("center")) return p("center", 0, 0);
|
||||
if (j.contains("centre")) return p("centre", 0, 0);
|
||||
if (j.contains("p0")) return p("p0", 0, 0);
|
||||
throw std::runtime_error(std::string(what) + " needs a 'center' (or 'p0')");
|
||||
};
|
||||
if (t == "line") {
|
||||
e.type = SketchEntity::Type::Line;
|
||||
e.p0 = p("p0", 0, 0); e.p1 = p("p1", 0, 0);
|
||||
} else if (t == "circle") {
|
||||
e.type = SketchEntity::Type::Circle;
|
||||
e.center = p("center", 0, 0);
|
||||
e.center = centre_of("circle");
|
||||
e.radius = j.value("radius", 0.0);
|
||||
e.p0 = e.center;
|
||||
if (e.radius <= 0.0) throw std::runtime_error("circle needs a positive 'radius'");
|
||||
} else if (t == "arc") {
|
||||
e.type = SketchEntity::Type::Arc;
|
||||
e.center = p("center", 0, 0);
|
||||
e.center = centre_of("arc");
|
||||
e.radius = j.value("radius", 0.0);
|
||||
e.start_angle = j.value("start_angle", 0.0);
|
||||
e.end_angle = j.value("end_angle", 0.0);
|
||||
|
||||
Reference in New Issue
Block a user