Design tab: move the CAD sources into their own folder

Review request on PR #15238: "Place CAD-related files (e.g. CadDocument/
GeometryEngine) into a separate folder."

  src/libslic3r/CAD/     the kernel — CadDocument, GeometryEngine, the four
                         Sketch* units, SketchSolver, ThreadStandards
  src/slic3r/GUI/CAD/    the tab — DesignPanel, DesignCanvas, DesignSketchTool,
                         SketchInlineEditor, McpControl, generated DesignOffer

Pure relocation: no line of logic changes. Two include rewrites follow from it —
files that moved re-spell their own neighbours against src/ (already on the
include path), and files that did not move pick up the new folder. docs and
docs/ux/mockups/gen_offer_table.py follow the same paths.

Verified: libslic3r, libslic3r_gui and libslic3r_tests all build, CAD suite green
at 2518 assertions in 194 test cases, and the sibling fork builds identically —
17 shared sources still byte-identical, 8 diverging by their expected counts.
This commit is contained in:
Tommaso Bianchi
2026-08-20 17:44:34 +02:00
parent 5d120921d5
commit 2b02a8e3dd
44 changed files with 135 additions and 135 deletions
+120
View File
@@ -0,0 +1,120 @@
#include "libslic3r/CAD/SketchInference.hpp"
#include <cmath>
namespace Slic3r {
// Candidate target collected during the scan; we keep the closest within each
// priority tier and resolve ties by tier then distance.
namespace {
struct Cand {
InferenceSnap::Kind kind{InferenceSnap::Kind::None};
int entity{-1};
SketchPointRole role{SketchPointRole::P0};
Vec2d point{0, 0};
double dist{0.0};
};
// Lower number = higher priority.
int tier(InferenceSnap::Kind k)
{
switch (k) {
case InferenceSnap::Kind::Endpoint: return 0;
case InferenceSnap::Kind::Center: return 1;
case InferenceSnap::Kind::Origin: return 2;
case InferenceSnap::Kind::Midpoint: return 3;
case InferenceSnap::Kind::OnEdge: return 4;
default: return 9;
}
}
} // namespace
InferenceSnap infer_point_snap(const std::vector<SketchEntity>& entities,
const Vec2d& query, double tol,
bool include_origin)
{
Cand best;
best.kind = InferenceSnap::Kind::None;
best.point = query;
auto offer = [&](InferenceSnap::Kind k, int ent, SketchPointRole r, const Vec2d& q) {
const double d = (q - query).norm();
if (d > tol) return;
const bool better = (best.kind == InferenceSnap::Kind::None) ||
(tier(k) < tier(best.kind)) ||
(tier(k) == tier(best.kind) && d < best.dist);
if (better) { best.kind = k; best.entity = ent; best.role = r; best.point = q; best.dist = d; }
};
for (size_t i = 0; i < entities.size(); ++i) {
const SketchEntity& e = entities[i];
const int ei = int(i);
switch (e.type) {
case SketchEntity::Type::Line: {
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P0, e.p0);
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P1, e.p1);
offer(InferenceSnap::Kind::Midpoint, ei, SketchPointRole::P0, 0.5 * (e.p0 + e.p1));
// Projection onto the segment interior (PointOnObject candidate).
const Vec2d d = e.p1 - e.p0;
const double L2 = d.squaredNorm();
if (L2 > 1e-12) {
double t = (query - e.p0).dot(d) / L2;
if (t > 0.02 && t < 0.98)
offer(InferenceSnap::Kind::OnEdge, ei, SketchPointRole::P0, e.p0 + t * d);
}
break;
}
case SketchEntity::Type::Arc:
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P0, e.p0);
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P1, e.p1);
offer(InferenceSnap::Kind::Center, ei, SketchPointRole::Center, e.center);
break;
case SketchEntity::Type::Circle: {
offer(InferenceSnap::Kind::Center, ei, SketchPointRole::Center, e.center);
// Nearest point on the circle rim (PointOnObject candidate).
const Vec2d v = query - e.center;
const double n = v.norm();
if (n > 1e-9 && e.radius > 1e-9)
offer(InferenceSnap::Kind::OnEdge, ei, SketchPointRole::Center,
e.center + v * (e.radius / n));
break;
}
case SketchEntity::Type::Point:
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P0, e.p0);
break;
case SketchEntity::Type::EllipseArc:
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P0, e.p0);
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P1, e.p1);
offer(InferenceSnap::Kind::Center, ei, SketchPointRole::Center, e.center);
break;
case SketchEntity::Type::Ellipse:
offer(InferenceSnap::Kind::Center, ei, SketchPointRole::Center, e.center);
break;
case SketchEntity::Type::BSpline:
// Endpoints (first/last pole) snap for loop closure.
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P0, e.p0);
offer(InferenceSnap::Kind::Endpoint, ei, SketchPointRole::P1, e.p1);
break;
}
}
if (include_origin)
offer(InferenceSnap::Kind::Origin, -1, SketchPointRole::P0, Vec2d(0, 0));
InferenceSnap r;
r.kind = best.kind; r.entity = best.entity; r.role = best.role; r.point = best.point;
return r;
}
std::optional<SketchConstraintType>
infer_axis_constraint(const Vec2d& anchor, const Vec2d& tip, double ang_tol_rad)
{
const Vec2d d = tip - anchor;
if (d.squaredNorm() < 1e-12) return std::nullopt;
const double ang = std::atan2(std::abs(d.y()), std::abs(d.x())); // 0=horizontal, pi/2=vertical
if (ang <= ang_tol_rad) return SketchConstraintType::Horizontal;
if (ang >= M_PI / 2.0 - ang_tol_rad) return SketchConstraintType::Vertical;
return std::nullopt;
}
} // namespace Slic3r