Horizontal and vertical distance dimensions

Port of snaporca 9fa304c77a. Parity OK: 17 files identical, 8 diverging at their
expected counts.

The everyday dimension in SolidWorks and Onshape, and this kernel had no form of
it. Distance constrains the straight-line gap; LockX/LockY pin one point's
ABSOLUTE coordinate. Neither relates two points along an axis.

DistanceX/DistanceY emit SLVS_C_PROJ_PT_DISTANCE against two unit direction
lines built in the solver's G_FIXED group, so they add no degrees of freedom.

THE DIRECTION IS SIGNED, and getting it backwards is silent. libslvs defines a
LINE_SEGMENT's direction as point[0] - point[1] (entity.cpp) and
PROJ_PT_DISTANCE constrains (pB - pA).dot(dir) (constrainteq.cpp:234), so the
reference lines are built head-first to mean +X and +Y.

The same signedness was a real defect in the GUI: the inline editor was
pre-filled with |delta|, so when the closest endpoint pair ran right-to-left,
opening the dimension and accepting the number shown would flip the point to the
other side of its anchor. Opening a dimension and accepting its own value must
be a no-op. The refs are now ordered so the shown value is positive.

On the CAD-1000-hours corpus, dimensioning and constraining is 31.9% of all
observed CAD time -- the largest single class, 7.6x feature operations. This is
the item in the constraint epic that lands most directly on it.

VERIFICATION LIMIT, as with the previous commit: this fork's kernel suite still
cannot run (find_package(assimp) fails at configure time, snaporca-w80c). The
shared sources are byte-identical to snaporca's, where kernel is 2603 assertions
/ 200 cases and ALL LADDERS HELD across all seven rungs.
This commit is contained in:
Tommaso Bianchi
2026-08-31 01:46:09 +02:00
parent a1f2a2687a
commit a63bba2d55
6 changed files with 142 additions and 2 deletions
+3 -1
View File
@@ -88,7 +88,9 @@ enum class SketchConstraintType {
// Append-only: cereal serializes this enum positionally as its underlying int, so
// inserting anywhere but the end reinterprets every constraint in every saved recipe.
EqualRadius,
Collinear
Collinear,
DistanceX, // |dx| between two points, projected onto the sketch X axis
DistanceY // |dy| between two points, projected onto the sketch Y axis
};
// Constraint on a SketchProfile, referencing profile point indices (a,b,c,d).
+20
View File
@@ -73,6 +73,15 @@ static SketchSolveResult solve_system(std::vector<SketchEntity>& entities,
b.P(G_FIXED, qw), b.P(G_FIXED, qx), b.P(G_FIXED, qy), b.P(G_FIXED, qz)));
b.wp = b.E(Slvs_MakeWorkplane(++b.eh, G_FIXED, origin, b.normal));
// Unit direction references for the axis-projected distance constraints. Both live in
// G_FIXED, so they are held constant and add no DOF to the system.
// libslvs defines a LINE_SEGMENT's direction as point[0] - point[1] (entity.cpp
// VectorGetExprs), so the unit vector's head is listed first to yield +X / +Y.
const Slvs_hEntity dir_x [[maybe_unused]] = b.E(Slvs_MakeLineSegment(++b.eh, G_FIXED, b.wp,
b.pt2d(G_FIXED, 1.0, 0.0), b.pt2d(G_FIXED, 0.0, 0.0)));
const Slvs_hEntity dir_y [[maybe_unused]] = b.E(Slvs_MakeLineSegment(++b.eh, G_FIXED, b.wp,
b.pt2d(G_FIXED, 0.0, 1.0), b.pt2d(G_FIXED, 0.0, 0.0)));
// ---- Entities -------------------------------------------------------------------
std::vector<Slots> slot(entities.size());
for (size_t i = 0; i < entities.size(); ++i) {
@@ -158,6 +167,9 @@ static SketchSolveResult solve_system(std::vector<SketchEntity>& entities,
switch (c.type) {
case CT::Coincident: case CT::Horizontal: case CT::Vertical: case CT::Distance:
ref_ok = ptOf(c.ea, c.ra) && ptOf(c.eb, c.rb); break;
case CT::DistanceX:
case CT::DistanceY:
ref_ok = ptOf(c.ea, c.ra) && ptOf(c.eb, c.rb); break;
case CT::Concentric:
ref_ok = ptOf(c.ea, Role::Center) && ptOf(c.eb, Role::Center); break;
case CT::Fix: case CT::LockX: case CT::LockY:
@@ -194,6 +206,14 @@ static SketchSolveResult solve_system(std::vector<SketchEntity>& entities,
case CT::Distance:
b.C(SLVS_C_PT_PT_DISTANCE, c.value, ptOf(c.ea, c.ra), ptOf(c.eb, c.rb), 0, 0);
break;
case CT::DistanceX:
// Distance between the two points measured along X only: project the vector
// between them onto the fixed unit X direction.
b.C(SLVS_C_PROJ_PT_DISTANCE, c.value, ptOf(c.ea, c.ra), ptOf(c.eb, c.rb), dir_x, 0);
break;
case CT::DistanceY:
b.C(SLVS_C_PROJ_PT_DISTANCE, c.value, ptOf(c.ea, c.ra), ptOf(c.eb, c.rb), dir_y, 0);
break;
case CT::Fix: {
const Vec2d p = coordOf(c.ea, c.ra);
b.C(SLVS_C_POINTS_COINCIDENT, 0, ptOf(c.ea, c.ra), fixedRef(p.x(), p.y()), 0, 0);