Files
OrcaSlicer/src/libslic3r/SketchConstraints.cpp
T
Tommaso BianchiandClaude Opus 4.8 0f4060c0a9 Orca-Cad: port SnapOrca Design (parametric CAD tab) onto mainline OrcaSlicer
Grafts the sketch-first CAD environment from snaporca-cad onto the mainline
OrcaSlicer/OrcaSlicer base (vs snaporca's Snapmaker/OrcaSlicer base):
- 133 new files: CadDocument/SketchEngine/GeometryEngine/SketchConstraints/
  SketchSolver/SketchInference/ThreadStandards + vendored libslvs solver;
  DesignPanel/DesignCanvas/DesignSketchTool/SketchInlineEditor GUI; GLGizmo
  Primitive/Sketch; 75 design icons; Catch2 tests.
- Integration hooks ported to mainline's diverged versions: Design tab in
  MainFrame, embedded design viewport + sketch overlay + per-canvas chrome
  suppression in GLCanvas3D/PartPlate, gizmo registration, Plater accessors,
  CMake wiring (libslvs subdir, CAD sources, OCCT ModelingAlgorithms=ON).

Structural integration complete; build verification pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
2026-06-28 12:40:38 +02:00

308 lines
8.1 KiB
C++

#include "SketchConstraints.hpp"
#include <Eigen/Dense>
#include <cmath>
namespace Slic3r {
int SketchConstraints::add_point(double x, double y)
{
m_vars.push_back(x);
m_vars.push_back(y);
return static_cast<int>(m_vars.size() / 2) - 1;
}
void SketchConstraints::set_point(int id, double x, double y)
{
size_t idx = 2 * id;
m_vars[idx] = x;
m_vars[idx + 1] = y;
}
Vec2d SketchConstraints::get_point(int id) const
{
size_t idx = 2 * id;
return Vec2d(m_vars[idx], m_vars[idx + 1]);
}
int SketchConstraints::point_count() const
{
return static_cast<int>(m_vars.size() / 2);
}
void SketchConstraints::fix_point(int id)
{
size_t idx = 2 * id;
Con c;
c.type = FIX_POINT;
c.a = id;
c.b = c.c = c.d = 0;
c.k0 = m_vars[idx];
c.k1 = m_vars[idx + 1];
m_cons.push_back(c);
}
void SketchConstraints::coincident(int a, int b)
{
Con c;
c.type = COINCIDENT;
c.a = a; c.b = b; c.c = c.d = 0;
c.k0 = c.k1 = 0;
m_cons.push_back(c);
}
void SketchConstraints::horizontal(int a, int b)
{
Con c;
c.type = HORIZONTAL;
c.a = a; c.b = b; c.c = c.d = 0;
c.k0 = c.k1 = 0;
m_cons.push_back(c);
}
void SketchConstraints::vertical(int a, int b)
{
Con c;
c.type = VERTICAL;
c.a = a; c.b = b; c.c = c.d = 0;
c.k0 = c.k1 = 0;
m_cons.push_back(c);
}
void SketchConstraints::distance(int a, int b, double d)
{
Con c;
c.type = DISTANCE;
c.a = a; c.b = b; c.c = c.d = 0;
c.k0 = d; c.k1 = 0;
m_cons.push_back(c);
}
void SketchConstraints::lock_x(int id, double x)
{
Con c;
c.type = LOCK_X;
c.a = id;
c.b = c.c = c.d = 0;
c.k0 = x; c.k1 = 0;
m_cons.push_back(c);
}
void SketchConstraints::lock_y(int id, double y)
{
Con c;
c.type = LOCK_Y;
c.a = id;
c.b = c.c = c.d = 0;
c.k0 = y; c.k1 = 0;
m_cons.push_back(c);
}
void SketchConstraints::equal_length(int a, int b, int c, int d)
{
Con con;
con.type = EQUAL_LENGTH;
con.a = a; con.b = b; con.c = c; con.d = d;
con.k0 = con.k1 = 0;
m_cons.push_back(con);
}
void SketchConstraints::parallel(int a, int b, int c, int d)
{
Con con;
con.type = PARALLEL;
con.a = a; con.b = b; con.c = c; con.d = d;
con.k0 = con.k1 = 0;
m_cons.push_back(con);
}
void SketchConstraints::perpendicular(int a, int b, int c, int d)
{
Con con;
con.type = PERPENDICULAR;
con.a = a; con.b = b; con.c = c; con.d = d;
con.k0 = con.k1 = 0;
m_cons.push_back(con);
}
void SketchConstraints::midpoint(int m, int a, int b)
{
Con con;
con.type = MIDPOINT;
con.a = m; con.b = a; con.c = b; con.d = -1;
con.k0 = con.k1 = 0;
m_cons.push_back(con);
}
void SketchConstraints::symmetric(int a, int b, int c, int d)
{
Con con;
con.type = SYMMETRIC;
con.a = a; con.b = b; con.c = c; con.d = d;
con.k0 = con.k1 = 0;
m_cons.push_back(con);
}
void SketchConstraints::angle(int a, int b, int c, int d, double radians)
{
Con con;
con.type = ANGLE;
con.a = a; con.b = b; con.c = c; con.d = d;
con.k0 = radians; con.k1 = 0;
m_cons.push_back(con);
}
void SketchConstraints::point_line_distance(int p, int a, int b, double dist)
{
Con con;
con.type = PT_LINE_DIST;
con.a = p; con.b = a; con.c = b; con.d = -1;
con.k0 = dist; con.k1 = 0;
m_cons.push_back(con);
}
Eigen::VectorXd SketchConstraints::residuals(const std::vector<double>& v) const
{
auto X = [&](int i) { return v[2 * i]; };
auto Y = [&](int i) { return v[2 * i + 1]; };
std::vector<double> res;
for (const auto& c : m_cons) {
switch (c.type) {
case FIX_POINT:
res.push_back(X(c.a) - c.k0);
res.push_back(Y(c.a) - c.k1);
break;
case COINCIDENT:
res.push_back(X(c.a) - X(c.b));
res.push_back(Y(c.a) - Y(c.b));
break;
case HORIZONTAL:
res.push_back(Y(c.a) - Y(c.b));
break;
case VERTICAL:
res.push_back(X(c.a) - X(c.b));
break;
case DISTANCE:
res.push_back(std::hypot(X(c.a) - X(c.b), Y(c.a) - Y(c.b)) - c.k0);
break;
case LOCK_X:
res.push_back(X(c.a) - c.k0);
break;
case LOCK_Y:
res.push_back(Y(c.a) - c.k0);
break;
case EQUAL_LENGTH:
res.push_back(std::hypot(X(c.a) - X(c.b), Y(c.a) - Y(c.b)) -
std::hypot(X(c.c) - X(c.d), Y(c.c) - Y(c.d)));
break;
case PARALLEL:
res.push_back((X(c.b) - X(c.a)) * (Y(c.d) - Y(c.c)) -
(Y(c.b) - Y(c.a)) * (X(c.d) - X(c.c)));
break;
case PERPENDICULAR:
res.push_back((X(c.b) - X(c.a)) * (X(c.d) - X(c.c)) +
(Y(c.b) - Y(c.a)) * (Y(c.d) - Y(c.c)));
break;
case MIDPOINT:
res.push_back(X(c.a) - 0.5 * (X(c.b) + X(c.c)));
res.push_back(Y(c.a) - 0.5 * (Y(c.b) + Y(c.c)));
break;
case SYMMETRIC: {
const double abx = X(c.b) - X(c.a), aby = Y(c.b) - Y(c.a);
const double cdx = X(c.d) - X(c.c), cdy = Y(c.d) - Y(c.c);
res.push_back(abx * cdx + aby * cdy);
const double mx = 0.5 * (X(c.a) + X(c.b));
const double my = 0.5 * (Y(c.a) + Y(c.b));
res.push_back((mx - X(c.c)) * cdy - (my - Y(c.c)) * cdx);
break;
}
case ANGLE: {
const double ux = X(c.b) - X(c.a), uy = Y(c.b) - Y(c.a);
const double wx = X(c.d) - X(c.c), wy = Y(c.d) - Y(c.c);
const double cross = ux * wy - uy * wx;
const double dot = ux * wx + uy * wy;
res.push_back(std::atan2(cross, dot) - c.k0);
break;
}
case PT_LINE_DIST: {
const double bx = X(c.b), by = Y(c.b);
const double cx = X(c.c), cy = Y(c.c);
const double L = std::hypot(cx - bx, cy - by);
const double num = (X(c.a) - bx) * (cy - by) - (Y(c.a) - by) * (cx - bx);
res.push_back((L > 1e-12 ? std::abs(num) / L : 0.0) - c.k0);
break;
}
}
}
Eigen::VectorXd r(static_cast<Eigen::Index>(res.size()));
for (size_t i = 0; i < res.size(); ++i)
r(static_cast<Eigen::Index>(i)) = res[i];
return r;
}
Eigen::MatrixXd SketchConstraints::jacobian(const std::vector<double>& v) const
{
int m = static_cast<int>(residuals(v).size());
int n = static_cast<int>(v.size());
Eigen::MatrixXd J(m, n);
const double eps = 1e-7;
std::vector<double> vp = v;
std::vector<double> vm = v;
for (int j = 0; j < n; ++j) {
vp[j] = v[j] + eps;
vm[j] = v[j] - eps;
Eigen::VectorXd rp = residuals(vp);
Eigen::VectorXd rm = residuals(vm);
vp[j] = v[j];
vm[j] = v[j];
J.col(j) = (rp - rm) / (2.0 * eps);
}
return J;
}
bool SketchConstraints::solve(int max_iter, double tol)
{
if (m_cons.empty()) return true;
double lambda = 1e-3;
Eigen::VectorXd r = residuals(m_vars);
for (int it = 0; it < max_iter; ++it) {
double rn = r.norm();
if (rn < tol) return true;
Eigen::MatrixXd J = jacobian(m_vars);
Eigen::MatrixXd A = J.transpose() * J;
Eigen::VectorXd g = J.transpose() * r;
bool stepped = false;
for (int t = 0; t < 12; ++t) {
Eigen::MatrixXd Ad = A;
for (int i = 0; i < Ad.rows(); ++i)
Ad(i, i) += lambda * (1.0 + Ad(i, i));
Eigen::VectorXd dx = Ad.ldlt().solve(-g);
std::vector<double> cand = m_vars;
for (size_t i = 0; i < cand.size(); ++i)
cand[i] += dx[static_cast<Eigen::Index>(i)];
Eigen::VectorXd rc = residuals(cand);
if (rc.norm() < rn) {
m_vars = cand;
r = rc;
lambda = std::max(lambda * 0.4, 1e-12);
stepped = true;
break;
}
lambda *= 3.0;
}
if (!stepped) break;
}
return r.norm() < tol * 100;
}
double SketchConstraints::residual_norm() const
{
return residuals(m_vars).norm();
}
} // namespace Slic3r