mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 14:20:58 +00:00
* Ignore Clipper, libpng, mcut and Boost.Polygon Internals in clang-tidy Each only works through a wrapper or umbrella header: libslic3r/clipper.hpp or clipper_z.hpp configure Clipper before including it, png.h pulls in libpng's config headers, and Boost.Polygon's headers only compile through polygon.hpp or voronoi.hpp. * Ignore minilzo's Config Headers in clang-tidy lzoconf.h and lzodefs.h are internal to minilzo.h, which is what the code includes. * Add Missing Includes Across the Remaining Sources and Tests Covers src/slic3r/Utils, src/slic3r/plugin, src/slic3r/Config, src/libvgcode, src/dev-utils, src/OrcaSlicer.cpp and tests/, the directories left after src/slic3r/GUI and src/libslic3r. Generated with clang-tidy misc-include-cleaner. libvgcode's own headers are included by relative path as in the rest of that library, and Catch2 and pybind11 with angle brackets as elsewhere in the repo. * Make the GUI and Test Headers Compile on Their Own Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Headers that only compile on one platform, or that nothing built includes, are left alone. * Keep Windows and nanosvg Setup Ahead of the Added Includes OrcaSlicer.cpp and several tests set _WIN32_WINNT, WIN32_LEAN_AND_MEAN or NOMINMAX before including Windows.h, and the profile validator defines NANOSVG_IMPLEMENTATION before any libslic3r header. The added includes had landed above those blocks, which broke the Windows build. * Add the GUI Includes the First Pass Missed Covers headers that only became editable once they compiled on their own, and wx symbols whose suggested header changed as the clang-tidy ignore list grew after the src/slic3r/GUI pass. * Keep the Added Test Includes Below the NOMINMAX Guard test_marchingsquares.cpp and test_texture_displacement.cpp had includes inside #ifndef NOMINMAX, which the tests inherit as defined on Windows from libslic3r, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory, as in #16068.
404 lines
19 KiB
C++
404 lines
19 KiB
C++
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers.hpp>
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
#include <cstdlib>
|
|
#include <math.h>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include "libslic3r/CAD/SketchConstraints.hpp"
|
|
#include "libslic3r/CAD/SketchEngine.hpp"
|
|
|
|
using namespace Slic3r;
|
|
|
|
namespace {
|
|
SketchEntity mk_line(double x0, double y0, double x1, double y1)
|
|
{
|
|
SketchEntity e; e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(x0, y0); e.p1 = Vec2d(x1, y1); return e;
|
|
}
|
|
SketchEntity mk_point(double x, double y)
|
|
{
|
|
SketchEntity e; e.type = SketchEntity::Type::Point; e.p0 = Vec2d(x, y); return e;
|
|
}
|
|
SketchEntity mk_circle(double cx, double cy, double r)
|
|
{
|
|
SketchEntity e; e.type = SketchEntity::Type::Circle;
|
|
e.center = Vec2d(cx, cy); e.radius = r; return e;
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Coincident with anchor", "[SketchConstraints]")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(0, 0);
|
|
int b = sc.add_point(5, 5);
|
|
sc.fix_point(a);
|
|
sc.coincident(a, b);
|
|
REQUIRE(sc.solve());
|
|
Vec2d pb = sc.get_point(b);
|
|
REQUIRE_THAT(pb.x(), Catch::Matchers::WithinAbs(0.0, 1e-4));
|
|
REQUIRE_THAT(pb.y(), Catch::Matchers::WithinAbs(0.0, 1e-4));
|
|
}
|
|
|
|
TEST_CASE("Horizontal + distance", "[SketchConstraints]")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(0, 0);
|
|
int b = sc.add_point(5, 3);
|
|
sc.fix_point(a);
|
|
sc.horizontal(a, b);
|
|
sc.distance(a, b, 10);
|
|
REQUIRE(sc.solve());
|
|
Vec2d pb = sc.get_point(b);
|
|
REQUIRE_THAT(pb.y(), Catch::Matchers::WithinAbs(0.0, 1e-3));
|
|
REQUIRE_THAT(std::abs(pb.x()), Catch::Matchers::WithinAbs(10.0, 1e-3));
|
|
}
|
|
|
|
TEST_CASE("Rectangle", "[SketchConstraints]")
|
|
{
|
|
SketchConstraints sc;
|
|
int p0 = sc.add_point(0, 0);
|
|
int p1 = sc.add_point(8, 1);
|
|
int p2 = sc.add_point(9, 5);
|
|
int p3 = sc.add_point(-1, 4);
|
|
sc.fix_point(p0);
|
|
sc.lock_x(p0, 0);
|
|
sc.lock_y(p0, 0);
|
|
sc.horizontal(p0, p1);
|
|
sc.vertical(p1, p2);
|
|
sc.horizontal(p2, p3);
|
|
sc.vertical(p3, p0);
|
|
sc.distance(p0, p1, 10);
|
|
sc.distance(p1, p2, 6);
|
|
REQUIRE(sc.solve());
|
|
Vec2d pp1 = sc.get_point(p1);
|
|
Vec2d pp2 = sc.get_point(p2);
|
|
Vec2d pp3 = sc.get_point(p3);
|
|
REQUIRE_THAT(pp1.x(), Catch::Matchers::WithinAbs(10.0, 1e-3));
|
|
REQUIRE_THAT(pp1.y(), Catch::Matchers::WithinAbs(0.0, 1e-3));
|
|
REQUIRE_THAT(pp2.x(), Catch::Matchers::WithinAbs(10.0, 1e-3));
|
|
REQUIRE_THAT(pp2.y(), Catch::Matchers::WithinAbs(6.0, 1e-3));
|
|
REQUIRE_THAT(pp3.x(), Catch::Matchers::WithinAbs(0.0, 1e-3));
|
|
REQUIRE_THAT(pp3.y(), Catch::Matchers::WithinAbs(6.0, 1e-3));
|
|
}
|
|
|
|
TEST_CASE("residual_norm after each solve", "[SketchConstraints]")
|
|
{
|
|
SECTION("coincident case")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(0, 0);
|
|
int b = sc.add_point(5, 5);
|
|
sc.fix_point(a);
|
|
sc.coincident(a, b);
|
|
REQUIRE(sc.solve());
|
|
REQUIRE(sc.residual_norm() < 1e-5);
|
|
}
|
|
SECTION("horizontal+distance case")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(0, 0);
|
|
int b = sc.add_point(5, 3);
|
|
sc.fix_point(a);
|
|
sc.horizontal(a, b);
|
|
sc.distance(a, b, 10);
|
|
REQUIRE(sc.solve());
|
|
REQUIRE(sc.residual_norm() < 1e-5);
|
|
}
|
|
SECTION("rectangle case")
|
|
{
|
|
SketchConstraints sc;
|
|
int p0 = sc.add_point(0, 0);
|
|
int p1 = sc.add_point(8, 1);
|
|
int p2 = sc.add_point(9, 5);
|
|
int p3 = sc.add_point(-1, 4);
|
|
sc.fix_point(p0);
|
|
sc.lock_x(p0, 0);
|
|
sc.lock_y(p0, 0);
|
|
sc.horizontal(p0, p1);
|
|
sc.vertical(p1, p2);
|
|
sc.horizontal(p2, p3);
|
|
sc.vertical(p3, p0);
|
|
sc.distance(p0, p1, 10);
|
|
sc.distance(p1, p2, 6);
|
|
REQUIRE(sc.solve());
|
|
REQUIRE(sc.residual_norm() < 1e-5);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("midpoint", "[SketchConstraints]")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(0, 0);
|
|
int b = sc.add_point(10, 0);
|
|
int m = sc.add_point(3, 7);
|
|
sc.fix_point(a);
|
|
sc.fix_point(b);
|
|
sc.midpoint(m, a, b);
|
|
REQUIRE(sc.solve());
|
|
Vec2d pm = sc.get_point(m);
|
|
REQUIRE_THAT(pm.x(), Catch::Matchers::WithinAbs(5.0, 1e-3));
|
|
REQUIRE_THAT(pm.y(), Catch::Matchers::WithinAbs(0.0, 1e-3));
|
|
}
|
|
|
|
TEST_CASE("symmetric across Y axis", "[SketchConstraints]")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(2, 3);
|
|
int b = sc.add_point(-1, 1);
|
|
int c = sc.add_point(0, 0);
|
|
int d = sc.add_point(0, 1);
|
|
sc.fix_point(a);
|
|
sc.fix_point(c);
|
|
sc.fix_point(d);
|
|
sc.symmetric(a, b, c, d);
|
|
REQUIRE(sc.solve());
|
|
Vec2d pb = sc.get_point(b);
|
|
REQUIRE_THAT(pb.x(), Catch::Matchers::WithinAbs(-2.0, 1e-3));
|
|
REQUIRE_THAT(pb.y(), Catch::Matchers::WithinAbs(3.0, 1e-3));
|
|
}
|
|
|
|
TEST_CASE("angle 90 degrees", "[SketchConstraints]")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(0, 0);
|
|
int b = sc.add_point(1, 0);
|
|
int c = sc.add_point(0, 0);
|
|
int d = sc.add_point(1, 1);
|
|
sc.fix_point(a);
|
|
sc.fix_point(b);
|
|
sc.fix_point(c);
|
|
sc.angle(a, b, c, d, M_PI / 2);
|
|
REQUIRE(sc.solve());
|
|
Vec2d pd = sc.get_point(d);
|
|
Vec2d pc = sc.get_point(c);
|
|
REQUIRE_THAT(pd.x() - pc.x(), Catch::Matchers::WithinAbs(0.0, 1e-3));
|
|
REQUIRE(pd.y() > pc.y());
|
|
}
|
|
|
|
TEST_CASE("point-line distance", "[SketchConstraints]")
|
|
{
|
|
SketchConstraints sc;
|
|
int a = sc.add_point(0, 0);
|
|
int b = sc.add_point(10, 0);
|
|
int p = sc.add_point(3, 1);
|
|
sc.fix_point(a);
|
|
sc.fix_point(b);
|
|
sc.lock_x(p, 3.0);
|
|
sc.point_line_distance(p, a, b, 5.0);
|
|
REQUIRE(sc.solve());
|
|
Vec2d pp = sc.get_point(p);
|
|
REQUIRE_THAT(std::abs(pp.y()), Catch::Matchers::WithinAbs(5.0, 1e-3));
|
|
REQUIRE_THAT(pp.x(), Catch::Matchers::WithinAbs(3.0, 1e-3));
|
|
}
|
|
|
|
// ---- entity-constraint planner (kernel port of DesignPanel::apply_entity_constraint) ----
|
|
|
|
TEST_CASE("sketch_entity_ends exposes real roles only", "[SketchConstraints]")
|
|
{
|
|
std::pair<SketchPointRole, Vec2d> out[2];
|
|
REQUIRE(sketch_entity_ends(mk_point(3, 4), out) == 1);
|
|
REQUIRE(out[0].first == SketchPointRole::P0);
|
|
REQUIRE(sketch_entity_ends(mk_circle(1, 2, 5), out) == 1);
|
|
REQUIRE(out[0].first == SketchPointRole::Center);
|
|
REQUIRE_THAT(out[0].second.x(), Catch::Matchers::WithinAbs(1.0, 1e-9));
|
|
REQUIRE_THAT(out[0].second.y(), Catch::Matchers::WithinAbs(2.0, 1e-9));
|
|
REQUIRE(sketch_entity_ends(mk_line(0, 0, 10, 0), out) == 2);
|
|
REQUIRE(out[0].first == SketchPointRole::P0);
|
|
REQUIRE(out[1].first == SketchPointRole::P1);
|
|
}
|
|
|
|
TEST_CASE("Coincident on two Points binds P0/P0, not phantom p1", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_point(0, 0), mk_point(5, 5) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, SketchConstraintType::Coincident);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Apply);
|
|
REQUIRE(p.defs.size() == 1);
|
|
REQUIRE(p.defs[0].type == SketchConstraintType::Coincident);
|
|
REQUIRE(p.defs[0].ea == 0);
|
|
REQUIRE(p.defs[0].ra == SketchPointRole::P0);
|
|
REQUIRE(p.defs[0].eb == 1);
|
|
REQUIRE(p.defs[0].rb == SketchPointRole::P0);
|
|
}
|
|
|
|
TEST_CASE("DistanceX on two Points binds real roles with non-negative prefill", "[SketchConstraints]")
|
|
{
|
|
// e0 is right of e1, so the raw projected delta is negative: the plan must swap the
|
|
// refs so accepting the shown (positive) value is a no-op, not a sign flip.
|
|
std::vector<SketchEntity> ents = { mk_point(5, 1), mk_point(2, 3) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, SketchConstraintType::DistanceX);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::AskValue);
|
|
REQUIRE(p.defs.size() == 1);
|
|
REQUIRE(p.defs[0].type == SketchConstraintType::DistanceX);
|
|
REQUIRE(p.defs[0].ra == SketchPointRole::P0);
|
|
REQUIRE(p.defs[0].rb == SketchPointRole::P0);
|
|
REQUIRE(p.prefill >= 0.0);
|
|
REQUIRE(p.defs[0].ea == 1);
|
|
REQUIRE(p.defs[0].eb == 0);
|
|
}
|
|
|
|
TEST_CASE("Horizontal on a Point rejects with NeedALine", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_point(1, 2) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, -1, -1, SketchConstraintType::Horizontal);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Reject);
|
|
REQUIRE(p.reason == ConstraintReject::NeedALine);
|
|
}
|
|
|
|
TEST_CASE("Angle on two Circles rejects with NeedTwoLines", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_circle(0, 0, 1), mk_circle(5, 0, 1) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, SketchConstraintType::Angle);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Reject);
|
|
REQUIRE(p.reason == ConstraintReject::NeedTwoLines);
|
|
}
|
|
|
|
TEST_CASE("Parallel on a Line + Circle rejects with NeedTwoLines (new guard)", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_line(0, 0, 1, 0), mk_circle(5, 0, 1) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, SketchConstraintType::Parallel);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Reject);
|
|
REQUIRE(p.reason == ConstraintReject::NeedTwoLines);
|
|
}
|
|
|
|
TEST_CASE("Equal on two Circles promotes to EqualRadius", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_circle(0, 0, 1), mk_circle(5, 0, 2) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, SketchConstraintType::EqualLength);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Apply);
|
|
REQUIRE(p.defs.size() == 1);
|
|
REQUIRE(p.defs[0].type == SketchConstraintType::EqualRadius);
|
|
REQUIRE(p.defs[0].ea == 0);
|
|
REQUIRE(p.defs[0].eb == 1);
|
|
}
|
|
|
|
TEST_CASE("Symmetric on two Lines returns two defs with ec set to the axis", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_line(0, 1, 5, 1), mk_line(0, -1, 5, -1), mk_line(0, 0, 0, 1) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, 2, SketchConstraintType::Symmetric);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Apply);
|
|
REQUIRE(p.defs.size() == 2);
|
|
for (const auto& d : p.defs) {
|
|
REQUIRE(d.type == SketchConstraintType::Symmetric);
|
|
REQUIRE(d.ea == 0);
|
|
REQUIRE(d.eb == 1);
|
|
REQUIRE(d.ec == 2);
|
|
}
|
|
REQUIRE(p.defs[0].ra == SketchPointRole::P0);
|
|
REQUIRE(p.defs[0].rb == SketchPointRole::P0);
|
|
REQUIRE(p.defs[1].ra == SketchPointRole::P1);
|
|
REQUIRE(p.defs[1].rb == SketchPointRole::P1);
|
|
}
|
|
|
|
TEST_CASE("Symmetric with no axis rejects with NeedAxisLine", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_point(0, 0), mk_point(5, 0) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, SketchConstraintType::Symmetric);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Reject);
|
|
REQUIRE(p.reason == ConstraintReject::NeedAxisLine);
|
|
}
|
|
|
|
TEST_CASE("SymmetricAboutY on two Points returns one def with ec == kSketchRefAxisY", "[SketchConstraints]")
|
|
{
|
|
std::vector<SketchEntity> ents = { mk_point(1, 0), mk_point(-2, 0) };
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, SketchConstraintType::SymmetricAboutY);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Apply);
|
|
REQUIRE(p.defs.size() == 1);
|
|
REQUIRE(p.defs[0].type == SketchConstraintType::SymmetricAboutY);
|
|
REQUIRE(p.defs[0].ec == kSketchRefAxisY);
|
|
REQUIRE(p.defs[0].ea == 0);
|
|
REQUIRE(p.defs[0].eb == 1);
|
|
}
|
|
|
|
TEST_CASE("constraint planner apply/askvalue matrix", "[SketchConstraints]")
|
|
{
|
|
struct C {
|
|
const char* name; SketchConstraintType type; std::vector<SketchEntity> ents;
|
|
int e0, e1, e2; ConstraintPlan::Kind kind;
|
|
};
|
|
const std::vector<C> cases = {
|
|
{ "Fix", SketchConstraintType::Fix, { mk_point(1, 2) }, 0, -1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Coincident", SketchConstraintType::Coincident, { mk_point(0, 0), mk_point(5, 5) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Horizontal", SketchConstraintType::Horizontal, { mk_line(0, 0, 5, 0) }, 0, -1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Vertical", SketchConstraintType::Vertical, { mk_line(0, 0, 0, 5) }, 0, -1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Parallel", SketchConstraintType::Parallel, { mk_line(0, 0, 1, 0), mk_line(0, 1, 1, 1) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Perpendicular", SketchConstraintType::Perpendicular, { mk_line(0, 0, 1, 0), mk_line(0, 0, 0, 1) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "EqualLength", SketchConstraintType::EqualLength, { mk_line(0, 0, 1, 0), mk_line(0, 1, 2, 1) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Concentric", SketchConstraintType::Concentric, { mk_circle(0, 0, 1), mk_circle(5, 0, 1) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Tangent", SketchConstraintType::Tangent, { mk_line(0, 0, 1, 0), mk_circle(0, 1, 1) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Midpoint", SketchConstraintType::Midpoint, { mk_point(2, 0), mk_line(0, 0, 5, 0) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Symmetric", SketchConstraintType::Symmetric, { mk_point(0, 0), mk_point(5, 0), mk_line(0, -1, 0, 1) }, 0, 1, 2, ConstraintPlan::Kind::Apply },
|
|
{ "SymmetricAboutY", SketchConstraintType::SymmetricAboutY, { mk_point(1, 0), mk_point(-2, 0) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "SymmetricAboutX", SketchConstraintType::SymmetricAboutX, { mk_point(0, 1), mk_point(0, -2) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "EqualRadius", SketchConstraintType::EqualRadius, { mk_circle(0, 0, 1), mk_circle(5, 0, 2) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Collinear", SketchConstraintType::Collinear, { mk_line(0, 0, 1, 0), mk_line(2, 0, 3, 0) }, 0, 1, -1, ConstraintPlan::Kind::Apply },
|
|
{ "Angle", SketchConstraintType::Angle, { mk_line(0, 0, 1, 0), mk_line(0, 0, 0, 1) }, 0, 1, -1, ConstraintPlan::Kind::AskValue },
|
|
{ "Radius", SketchConstraintType::Radius, { mk_circle(0, 0, 2.5) }, 0, -1, -1, ConstraintPlan::Kind::AskValue },
|
|
{ "Diameter", SketchConstraintType::Diameter, { mk_circle(0, 0, 2.5) }, 0, -1, -1, ConstraintPlan::Kind::AskValue },
|
|
{ "DistanceX", SketchConstraintType::DistanceX, { mk_point(0, 0), mk_point(5, 3) }, 0, 1, -1, ConstraintPlan::Kind::AskValue },
|
|
{ "DistanceY", SketchConstraintType::DistanceY, { mk_point(0, 0), mk_point(5, 3) }, 0, 1, -1, ConstraintPlan::Kind::AskValue },
|
|
};
|
|
for (const C& c : cases) {
|
|
DYNAMIC_SECTION("apply " << c.name) {
|
|
ConstraintPlan p = plan_entity_constraint(c.ents, c.e0, c.e1, c.e2, c.type);
|
|
REQUIRE(p.kind == c.kind);
|
|
REQUIRE(p.defs.size() >= 1);
|
|
for (const auto& d : p.defs) REQUIRE(d.type == c.type);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("constraint planner reject matrix", "[SketchConstraints]")
|
|
{
|
|
struct C {
|
|
const char* name; SketchConstraintType type; std::vector<SketchEntity> ents;
|
|
int e0, e1, e2; ConstraintReject reason;
|
|
};
|
|
const std::vector<C> cases = {
|
|
{ "Fix", SketchConstraintType::Fix, {}, 0, -1, -1, ConstraintReject::NeedOneEntity },
|
|
{ "Coincident", SketchConstraintType::Coincident, { mk_point(0, 0) }, 0, -1, -1, ConstraintReject::NeedTwoEntities },
|
|
{ "Horizontal", SketchConstraintType::Horizontal, { mk_point(1, 2) }, 0, -1, -1, ConstraintReject::NeedALine },
|
|
{ "Vertical", SketchConstraintType::Vertical, { mk_circle(0, 0, 1) }, 0, -1, -1, ConstraintReject::NeedALine },
|
|
{ "Parallel", SketchConstraintType::Parallel, { mk_line(0, 0, 1, 0), mk_circle(5, 0, 1) }, 0, 1, -1, ConstraintReject::NeedTwoLines },
|
|
{ "Perpendicular", SketchConstraintType::Perpendicular, { mk_circle(0, 0, 1), mk_line(0, 0, 1, 0) }, 0, 1, -1, ConstraintReject::NeedTwoLines },
|
|
{ "EqualLength", SketchConstraintType::EqualLength, { mk_line(0, 0, 1, 0), mk_circle(5, 0, 1) }, 0, 1, -1, ConstraintReject::NeedTwoLines },
|
|
{ "Concentric", SketchConstraintType::Concentric, { mk_line(0, 0, 1, 0), mk_circle(5, 0, 1) }, 0, 1, -1, ConstraintReject::NeedTwoRounds },
|
|
{ "Tangent", SketchConstraintType::Tangent, { mk_line(0, 0, 1, 0), mk_line(0, 1, 1, 1) }, 0, 1, -1, ConstraintReject::NeedTangentPair },
|
|
{ "Midpoint", SketchConstraintType::Midpoint, { mk_line(0, 0, 1, 0), mk_line(0, 1, 1, 1) }, 0, 1, -1, ConstraintReject::NeedPointAndLine },
|
|
{ "Symmetric", SketchConstraintType::Symmetric, { mk_line(0, 0, 1, 0), mk_point(1, 1), mk_line(0, -1, 0, 1) }, 0, 1, 2, ConstraintReject::NeedTwoPointsOrLines },
|
|
{ "SymmetricAboutY", SketchConstraintType::SymmetricAboutY, { mk_line(0, 0, 1, 0), mk_point(1, 1) }, 0, 1, -1, ConstraintReject::NeedTwoPointsOrLines },
|
|
{ "SymmetricAboutX", SketchConstraintType::SymmetricAboutX, { mk_point(1, 1), mk_line(0, 0, 1, 0) }, 0, 1, -1, ConstraintReject::NeedTwoPointsOrLines },
|
|
{ "EqualRadius", SketchConstraintType::EqualRadius, { mk_line(0, 0, 1, 0), mk_circle(5, 0, 1) }, 0, 1, -1, ConstraintReject::NeedTwoRounds },
|
|
{ "Collinear", SketchConstraintType::Collinear, { mk_line(0, 0, 1, 0), mk_circle(5, 0, 1) }, 0, 1, -1, ConstraintReject::NeedTwoLines },
|
|
{ "Angle", SketchConstraintType::Angle, { mk_circle(0, 0, 1), mk_circle(5, 0, 1) }, 0, 1, -1, ConstraintReject::NeedTwoLines },
|
|
{ "Radius", SketchConstraintType::Radius, { mk_line(0, 0, 1, 0) }, 0, -1, -1, ConstraintReject::NeedRound },
|
|
{ "Diameter", SketchConstraintType::Diameter, { mk_point(1, 2) }, 0, -1, -1, ConstraintReject::NeedRound },
|
|
{ "DistanceX", SketchConstraintType::DistanceX, { mk_point(0, 0) }, 0, -1, -1, ConstraintReject::NeedTwoEntities },
|
|
{ "DistanceY", SketchConstraintType::DistanceY, { mk_point(0, 0) }, 0, -1, -1, ConstraintReject::NeedTwoEntities },
|
|
};
|
|
for (const C& c : cases) {
|
|
DYNAMIC_SECTION("reject " << c.name) {
|
|
ConstraintPlan p = plan_entity_constraint(c.ents, c.e0, c.e1, c.e2, c.type);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Reject);
|
|
REQUIRE(p.reason == c.reason);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("constraint planner rejects types with no entity binding", "[SketchConstraints]")
|
|
{
|
|
const SketchConstraintType unsupported[] = {
|
|
SketchConstraintType::Distance, SketchConstraintType::LockX, SketchConstraintType::LockY,
|
|
SketchConstraintType::PointOnLine, SketchConstraintType::PointOnObject,
|
|
};
|
|
std::vector<SketchEntity> ents = { mk_point(0, 0), mk_point(1, 1) };
|
|
for (SketchConstraintType t : unsupported) {
|
|
DYNAMIC_SECTION("unsupported " << int(t)) {
|
|
ConstraintPlan p = plan_entity_constraint(ents, 0, 1, -1, t);
|
|
REQUIRE(p.kind == ConstraintPlan::Kind::Reject);
|
|
REQUIRE(p.reason == ConstraintReject::Unsupported);
|
|
}
|
|
}
|
|
}
|