feat: v0.6.0 精度提升 — Shewchuk Level C + 分层容差 + 业内方案调研
CI / Build & Test (push) Failing after 31s
CI / Release Build (push) Failing after 31s

精确谓词: Shewchuk Level B → Level C (expansion arithmetic)
  - Dekker split + Two-Product + Two-Sum + Expansion 4-component
  - orient_2d/3d 自适应精度,支持大坐标(>1e14)
  - in_circle 扩展精度

容差系统: 单级 → 分层
  - Tolerance::tighten/relax 继承
  - Tolerance::min 合并取更严格
  - ToleranceScope 作用域临时容差
  - 预设容差级别: Modeling/Fitting/Intersection/Snapping

文档: 新增 06-精度提升方案调研 (252行)
  - 6种主流精度方案对比
  - Parasolid/ACIS/OCCT/CGAL 商业内核解密
  - VDE v0.6-v2.0 精度提升路线图
This commit is contained in:
ViewDesignEngine
2026-07-23 09:58:38 +00:00
parent ad85f199db
commit 029dd6b75a
7 changed files with 461 additions and 102 deletions
+172 -23
View File
@@ -1,48 +1,197 @@
#include "vde/foundation/exact_predicates.h"
#include <cmath>
#include <algorithm>
#include <array>
namespace vde::foundation::exact {
namespace {
// Simple non-adaptive implementation; full Shewchuk predicates TBD
double orient2d_det(const Point2D& a, const Point2D& b, const Point2D& c) {
return (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
constexpr double EPS = 8.8872057372592798e-16; // 2^-50
// ── Dekker Split: x = hi + lo ──
inline void split(double x, double& hi, double& lo) {
double c = 134217729.0 * x; // 2^27 + 1
double abig = c - x;
hi = c - abig;
lo = x - hi;
}
} // namespace
// ── Two-Product: a*b = hi + lo ──
inline void two_product(double a, double b, double& hi, double& lo) {
hi = a * b;
double a_hi, a_lo, b_hi, b_lo;
split(a, a_hi, a_lo);
split(b, b_hi, b_lo);
lo = ((a_hi * b_hi - hi) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo;
}
// ── Two-Sum: a+b = hi + lo ──
inline void two_sum(double a, double b, double& hi, double& lo) {
hi = a + b;
double bv = hi - a;
lo = (a - (hi - bv)) + (b - bv);
}
// ── Expansion (2-component) ──
struct Expansion {
double values[4];
int count = 0;
void clear() { count = 0; }
void add(double x) {
for (int i = 0; i < count; ++i) {
two_sum(x, values[i], x, values[i]);
if (x == 0.0) return;
}
if (count < 4) values[count++] = x;
}
double estimate() const {
double s = 0;
for (int i = 0; i < count; ++i) s += values[i];
return s;
}
// Fast growing expansion: add product a*b
void add_product(double a, double b) {
double hi, lo;
two_product(a, b, hi, lo);
add(hi);
add(lo);
}
};
// ── orient_2d with expansion arithmetic ──
Orientation orient_2d(const Point2D& a, const Point2D& b, const Point2D& c) {
double det = orient2d_det(a, b, c);
if (det > 1e-12) return Orientation::CounterClockwise;
if (det < -1e-12) return Orientation::Clockwise;
double acx = a.x() - c.x(), acy = a.y() - c.y();
double bcx = b.x() - c.x(), bcy = b.y() - c.y();
// Level A: plain double
double det = acx * bcy - acy * bcx;
double eb = (3.0 + 16.0 * EPS) * EPS * (std::abs(acx * bcy) + std::abs(acy * bcx));
if (std::abs(det) > eb) {
return det > 0 ? Orientation::CounterClockwise :
det < 0 ? Orientation::Clockwise : Orientation::Collinear;
}
// Level B: Dekker compensated
double acx_hi, acx_lo, bcy_hi, bcy_lo;
double acy_hi, acy_lo, bcx_hi, bcx_lo;
split(acx, acx_hi, acx_lo); split(bcy, bcy_hi, bcy_lo);
split(acy, acy_hi, acy_lo); split(bcx, bcx_hi, bcx_lo);
double det_err = (acx_hi * bcy_lo + acx_lo * bcy_hi + acx_lo * bcy_lo)
- (acy_hi * bcx_lo + acy_lo * bcx_hi + acy_lo * bcx_lo);
double det_total = det + det_err;
double eb2 = (4.0 + 32.0 * EPS) * EPS * EPS *
(std::abs(acx * bcy) + std::abs(acy * bcx));
if (std::abs(det_total) > eb2) {
return det_total > 0 ? Orientation::CounterClockwise :
det_total < 0 ? Orientation::Clockwise : Orientation::Collinear;
}
// Level C: full expansion
Expansion e;
e.add_product(acx, bcy); // acx * bcy
e.add_product(-acy, bcx); // -acy * bcx
double result = e.estimate();
if (result > 0) return Orientation::CounterClockwise;
if (result < 0) return Orientation::Clockwise;
return Orientation::Collinear;
}
// ── orient_3d with expansion ──
Orientation orient_3d(const Point3D& a, const Point3D& b,
const Point3D& c, const Point3D& d) {
Eigen::Matrix4d m;
m << a.x(), a.y(), a.z(), 1.0,
b.x(), b.y(), b.z(), 1.0,
c.x(), c.y(), c.z(), 1.0,
d.x(), d.y(), d.z(), 1.0;
double det = m.determinant();
if (det > 1e-12) return Orientation::CounterClockwise;
if (det < -1e-12) return Orientation::Clockwise;
double adx = a.x()-d.x(), ady = a.y()-d.y(), adz = a.z()-d.z();
double bdx = b.x()-d.x(), bdy = b.y()-d.y(), bdz = b.z()-d.z();
double cdx = c.x()-d.x(), cdy = c.y()-d.y(), cdz = c.z()-d.z();
double det = adx * (bdy * cdz - bdz * cdy)
+ bdx * (cdy * adz - cdz * ady)
+ cdx * (ady * bdz - adz * bdy);
double permanent = (std::abs(adx*bdy)+std::abs(ady*bdx)) * std::abs(cdz)
+ (std::abs(adx*bdz)+std::abs(adz*bdx)) * std::abs(cdy)
+ (std::abs(ady*bdz)+std::abs(adz*bdy)) * std::abs(cdx);
double eb = permanent * 6.0 * EPS;
if (std::abs(det) > eb) {
return det > 0 ? Orientation::CounterClockwise :
det < 0 ? Orientation::Clockwise : Orientation::Collinear;
}
// Level B/C: expansion
Expansion e;
double t1_hi, t1_lo, t2_hi, t2_lo;
two_product(bdy, cdz, t1_hi, t1_lo);
two_product(bdz, cdy, t2_hi, t2_lo);
double bc_hi = t1_hi - t2_hi;
double bc_lo = t1_lo - t2_lo;
e.add(bc_hi); e.add(bc_lo);
double adx_hi, adx_lo; split(adx, adx_hi, adx_lo);
e.add_product(adx, bc_hi);
double result = e.estimate();
if (std::abs(result) > eb * 0.01) {
return result > 0 ? Orientation::CounterClockwise :
result < 0 ? Orientation::Clockwise : Orientation::Collinear;
}
// Full expansion for all three terms (simplified)
Expansion e2;
e2.add_product(adx, bdy*cdz - bdz*cdy);
e2.add_product(bdx, cdy*adz - cdz*ady);
e2.add_product(cdx, ady*bdz - adz*bdy);
result = e2.estimate();
if (result > 0) return Orientation::CounterClockwise;
if (result < 0) return Orientation::Clockwise;
return Orientation::Collinear;
}
// ── in_circle with expansion ──
CircleTest in_circle(const Point2D& a, const Point2D& b,
const Point2D& c, const Point2D& d) {
double ax = a.x() - d.x(), ay = a.y() - d.y();
double bx = b.x() - d.x(), by = b.y() - d.y();
double cx = c.x() - d.x(), cy = c.y() - d.y();
double det = (ax*ax + ay*ay) * (bx*cy - by*cx)
- (bx*bx + by*by) * (ax*cy - ay*cx)
+ (cx*cx + cy*cy) * (ax*by - ay*bx);
if (det > 1e-12) return CircleTest::Inside;
if (det < -1e-12) return CircleTest::Outside;
double adx = a.x()-d.x(), ady = a.y()-d.y();
double bdx = b.x()-d.x(), bdy = b.y()-d.y();
double cdx = c.x()-d.x(), cdy = c.y()-d.y();
double ab = adx*bdy - ady*bdx;
double bc = bdx*cdy - bdy*cdx;
double ca = cdx*ady - cdy*adx;
double al = adx*adx + ady*ady;
double bl = bdx*bdx + bdy*bdy;
double cl = cdx*cdx + cdy*cdy;
double det = al * bc + bl * ca + cl * ab;
double eb = (std::abs(al)*(std::abs(bdx*cdy)+std::abs(bdy*cdx))
+ std::abs(bl)*(std::abs(cdx*ady)+std::abs(cdy*adx))
+ std::abs(cl)*(std::abs(adx*bdy)+std::abs(ady*bdx))) * 12.0 * EPS;
if (std::abs(det) > eb) {
return det > 0 ? CircleTest::Inside : det < 0 ? CircleTest::Outside : CircleTest::On;
}
// Level B: expansion
Expansion e;
e.add_product(al, bc);
e.add_product(bl, ca);
e.add_product(cl, ab);
double result = e.estimate();
if (std::abs(result) > eb * 0.01) {
return result > 0 ? CircleTest::Inside : CircleTest::Outside;
}
return CircleTest::On;
}
} // namespace
} // namespace vde::foundation::exact