mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-19 06:53:02 +00:00
Carries snaporca 971320e129, 6b049f0dc6, 4aae782029, 444d59f212, 74cf3d7e54 and the build guards from 597557a6e4. Parity re-verified after every hunk: 17 files identical, 8 diverging by their expected counts — DesignPanel.cpp still 32, DesignCanvas.cpp still 16, which is the proof each hunk landed on the right side rather than being copied over a real divergence. OFFSET OFFSETS THE CHAIN. Per-entity offsetting returned a closed rectangle as four parallel segments that no longer touch, so entities_to_wires gave four OPEN wires and nothing could be extruded. offset_entities now chains by shared endpoints and repairs each seam by mitering the neighbours to their intersection. Second bug, invisible to any single-entity test: +d meant "left of travel" for a line but "radius + d" for an arc regardless of sweep, so a slot outline offset with its straights going one way and its caps the other. The convention is now written on the declaration and pinned by a test. tests/libslic3r/test_sketchprofile.cpp is new and asserts the LOOP rather than coordinates — the property that decides whether a profile can be built, and the one the existing single-entity [SketchEdit] cases cannot see. Its include is catch2/catch_all.hpp here: this fork ships Catch2 v3 while snaporca is on v2, which is why the test files are a tolerated divergence. RIGHT-CLICK PICKS WHAT YOU POINTED AT, so a line's own verbs are offered instead of the empty-selection vocabulary; sk_delete stops sharing btn:delete with the feature tree; and an element's defining number (length / radius / diameter / angle / distance) can be typed, from the menu or from V. TWELVE MCP SKETCH VERBS. The socket had ~40 verbs and none touched a sketch, so the 2D layer could only be exercised by driving a GUI with synthetic clicks. sketch_describe reports each closed loop, the loops it encloses as voids, exact areas, and where a chain is still open; sketch_validate/sketch_heal are FreeCAD's ValidateSketch — find vertices that overlap within a tolerance but carry no coincidence, then weld them AND record the constraint, so a loop closed by floating-point luck becomes one closed by construction. scripts/mcp-sketch-smoke.py is the loop that asserts all of it. Kernel suite on this fork: all tests passed, 2677 assertions in 230 test cases. The GUI target links against the rebuilt deps image (the wxInspector blockage is gone) and the binary carries the new verbs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
494 lines
15 KiB
C++
494 lines
15 KiB
C++
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
|
|
#include "libslic3r/CAD/SketchEngine.hpp"
|
|
#include <cmath>
|
|
|
|
using namespace Slic3r;
|
|
|
|
using Catch::Matchers::WithinAbs;
|
|
|
|
TEST_CASE("Mirror Line across Y axis", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(3, 2);
|
|
e.p1 = Vec2d(5, 4);
|
|
|
|
Vec2d a(0, -1);
|
|
Vec2d b(0, 1);
|
|
|
|
auto result = SketchEngine::mirror_entities({e}, a, b);
|
|
REQUIRE(result.size() == 1);
|
|
|
|
const auto& m = result[0];
|
|
REQUIRE(m.type == SketchEntity::Type::Line);
|
|
REQUIRE_THAT(m.p0.x(), WithinAbs(-3.0, 1e-9));
|
|
REQUIRE_THAT(m.p0.y(), WithinAbs(2.0, 1e-9));
|
|
REQUIRE_THAT(m.p1.x(), WithinAbs(-5.0, 1e-9));
|
|
REQUIRE_THAT(m.p1.y(), WithinAbs(4.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Mirror Circle across Y axis", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Circle;
|
|
e.center = Vec2d(5, 0);
|
|
e.p0 = Vec2d(5, 0);
|
|
e.radius = 3;
|
|
|
|
Vec2d a(0, -1);
|
|
Vec2d b(0, 1);
|
|
|
|
auto result = SketchEngine::mirror_entities({e}, a, b);
|
|
REQUIRE(result.size() == 1);
|
|
|
|
const auto& m = result[0];
|
|
REQUIRE(m.type == SketchEntity::Type::Circle);
|
|
REQUIRE_THAT(m.center.x(), WithinAbs(-5.0, 1e-9));
|
|
REQUIRE_THAT(m.center.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(m.radius, WithinAbs(3.0, 1e-9));
|
|
REQUIRE_THAT(m.p0.x(), WithinAbs(-5.0, 1e-9));
|
|
REQUIRE_THAT(m.p0.y(), WithinAbs(0.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Mirror Arc across X axis", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Arc;
|
|
e.center = Vec2d(0, 0);
|
|
e.radius = 1.0;
|
|
e.start_angle = 0.0;
|
|
e.end_angle = M_PI / 2.0;
|
|
e.p0 = Vec2d(1, 0);
|
|
e.p1 = Vec2d(0, 1);
|
|
|
|
Vec2d a(-1, 0);
|
|
Vec2d b(1, 0);
|
|
|
|
auto result = SketchEngine::mirror_entities({e}, a, b);
|
|
REQUIRE(result.size() == 1);
|
|
|
|
const auto& m = result[0];
|
|
REQUIRE(m.type == SketchEntity::Type::Arc);
|
|
|
|
REQUIRE_THAT(m.p0.x(), WithinAbs(1.0, 1e-9));
|
|
REQUIRE_THAT(m.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(m.p1.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(m.p1.y(), WithinAbs(-1.0, 1e-9));
|
|
|
|
double sweep = m.end_angle - m.start_angle;
|
|
double orig_sweep = e.end_angle - e.start_angle;
|
|
REQUIRE(orig_sweep > 0.0);
|
|
REQUIRE(sweep < 0.0);
|
|
}
|
|
|
|
TEST_CASE("Offset Line by positive d", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(0, 0);
|
|
e.p1 = Vec2d(10, 0);
|
|
|
|
auto result = SketchEngine::offset_entities({e}, 2.0);
|
|
REQUIRE(result.size() == 1);
|
|
|
|
const auto& o = result[0];
|
|
REQUIRE(o.type == SketchEntity::Type::Line);
|
|
REQUIRE_THAT(o.p0.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(o.p0.y(), WithinAbs(2.0, 1e-9));
|
|
REQUIRE_THAT(o.p1.x(), WithinAbs(10.0, 1e-9));
|
|
REQUIRE_THAT(o.p1.y(), WithinAbs(2.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Offset Circle: expand and collapse", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Circle;
|
|
e.center = Vec2d(0, 0);
|
|
e.p0 = Vec2d(0, 0);
|
|
e.radius = 5;
|
|
|
|
auto expanded = SketchEngine::offset_entities({e}, 2.0);
|
|
REQUIRE(expanded.size() == 1);
|
|
REQUIRE_THAT(expanded[0].radius, WithinAbs(7.0, 1e-9));
|
|
|
|
auto collapsed = SketchEngine::offset_entities({e}, -5.0);
|
|
REQUIRE(collapsed.empty());
|
|
}
|
|
|
|
// CONTRACT CHANGED: +d used to mean "radius + d" for every arc regardless of its sweep, while
|
|
// for a line it meant "left of the direction of travel". The two disagreed, so a profile made
|
|
// of lines AND arcs (any slot outline) offset with its straights going one way and its caps the
|
|
// other, and could never come back closed. The arc now follows the line's rule: +d is left of
|
|
// travel, which for this CCW quarter-arc is inward -> r = 3. See [SketchProfile].
|
|
TEST_CASE("Offset Arc by positive d (left of travel: a CCW arc shrinks)", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Arc;
|
|
e.center = Vec2d(0, 0);
|
|
e.radius = 4.0;
|
|
e.start_angle = 0.0;
|
|
e.end_angle = M_PI / 2.0;
|
|
e.p0 = Vec2d(4, 0);
|
|
e.p1 = Vec2d(0, 4);
|
|
|
|
auto result = SketchEngine::offset_entities({e}, 1.0);
|
|
REQUIRE(result.size() == 1);
|
|
|
|
const auto& o = result[0];
|
|
REQUIRE(o.type == SketchEntity::Type::Arc);
|
|
REQUIRE_THAT(o.radius, WithinAbs(3.0, 1e-9));
|
|
REQUIRE_THAT(o.p0.x(), WithinAbs(3.0, 1e-9));
|
|
REQUIRE_THAT(o.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(o.p1.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(o.p1.y(), WithinAbs(3.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Fillet right-angle corner", "[SketchEdit]")
|
|
{
|
|
SketchEntity a;
|
|
a.type = SketchEntity::Type::Line;
|
|
a.p0 = Vec2d(0, 0);
|
|
a.p1 = Vec2d(10, 0);
|
|
|
|
SketchEntity b;
|
|
b.type = SketchEntity::Type::Line;
|
|
b.p0 = Vec2d(10, 0);
|
|
b.p1 = Vec2d(10, 10);
|
|
|
|
SketchEntity a_out, b_out, arc_out;
|
|
bool ok = SketchEngine::fillet_lines(a, b, 2.0, a_out, b_out, arc_out);
|
|
REQUIRE(ok);
|
|
|
|
REQUIRE_THAT(a_out.p0.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(a_out.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(a_out.p1.x(), WithinAbs(8.0, 1e-9));
|
|
REQUIRE_THAT(a_out.p1.y(), WithinAbs(0.0, 1e-9));
|
|
|
|
REQUIRE_THAT(b_out.p0.x(), WithinAbs(10.0, 1e-9));
|
|
REQUIRE_THAT(b_out.p0.y(), WithinAbs(2.0, 1e-9));
|
|
REQUIRE_THAT(b_out.p1.x(), WithinAbs(10.0, 1e-9));
|
|
REQUIRE_THAT(b_out.p1.y(), WithinAbs(10.0, 1e-9));
|
|
|
|
REQUIRE(arc_out.type == SketchEntity::Type::Arc);
|
|
REQUIRE_THAT(arc_out.radius, WithinAbs(2.0, 1e-9));
|
|
REQUIRE_THAT(arc_out.center.x(), WithinAbs(8.0, 1e-9));
|
|
REQUIRE_THAT(arc_out.center.y(), WithinAbs(2.0, 1e-9));
|
|
REQUIRE_THAT((arc_out.p0 - arc_out.center).norm(), WithinAbs(2.0, 1e-9));
|
|
REQUIRE_THAT((arc_out.p1 - arc_out.center).norm(), WithinAbs(2.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Fillet parallel lines returns false", "[SketchEdit]")
|
|
{
|
|
SketchEntity a;
|
|
a.type = SketchEntity::Type::Line;
|
|
a.p0 = Vec2d(0, 0);
|
|
a.p1 = Vec2d(10, 0);
|
|
|
|
SketchEntity b;
|
|
b.type = SketchEntity::Type::Line;
|
|
b.p0 = Vec2d(0, 5);
|
|
b.p1 = Vec2d(10, 5);
|
|
|
|
SketchEntity a_out, b_out, arc_out;
|
|
REQUIRE_FALSE(SketchEngine::fillet_lines(a, b, 1.0, a_out, b_out, arc_out));
|
|
}
|
|
|
|
TEST_CASE("Fillet arc too big returns false", "[SketchEdit]")
|
|
{
|
|
SketchEntity a;
|
|
a.type = SketchEntity::Type::Line;
|
|
a.p0 = Vec2d(0, 0);
|
|
a.p1 = Vec2d(1, 0);
|
|
|
|
SketchEntity b;
|
|
b.type = SketchEntity::Type::Line;
|
|
b.p0 = Vec2d(1, 0);
|
|
b.p1 = Vec2d(1, 1);
|
|
|
|
SketchEntity a_out, b_out, arc_out;
|
|
REQUIRE_FALSE(SketchEngine::fillet_lines(a, b, 5.0, a_out, b_out, arc_out));
|
|
}
|
|
|
|
TEST_CASE("Trim right arm", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(-5, 0);
|
|
e.p1 = Vec2d(5, 0);
|
|
|
|
SketchEntity vc;
|
|
vc.type = SketchEntity::Type::Line;
|
|
vc.p0 = Vec2d(0, -5);
|
|
vc.p1 = Vec2d(0, 5);
|
|
|
|
bool ok = SketchEngine::trim_entity(e, {vc}, Vec2d(3, 0));
|
|
REQUIRE(ok);
|
|
REQUIRE_THAT(e.p0.x(), WithinAbs(-5.0, 1e-9));
|
|
REQUIRE_THAT(e.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.y(), WithinAbs(0.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Trim left arm", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(-5, 0);
|
|
e.p1 = Vec2d(5, 0);
|
|
|
|
SketchEntity vc;
|
|
vc.type = SketchEntity::Type::Line;
|
|
vc.p0 = Vec2d(0, -5);
|
|
vc.p1 = Vec2d(0, 5);
|
|
|
|
bool ok = SketchEngine::trim_entity(e, {vc}, Vec2d(-3, 0));
|
|
REQUIRE(ok);
|
|
REQUIRE_THAT(e.p0.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.x(), WithinAbs(5.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.y(), WithinAbs(0.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Trim no cut (u out of range)", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(-5, 0);
|
|
e.p1 = Vec2d(5, 0);
|
|
|
|
SketchEntity other;
|
|
other.type = SketchEntity::Type::Line;
|
|
other.p0 = Vec2d(0, 3);
|
|
other.p1 = Vec2d(0, 8);
|
|
|
|
REQUIRE_FALSE(SketchEngine::trim_entity(e, {other}, Vec2d(3, 0)));
|
|
}
|
|
|
|
TEST_CASE("Extend forward to line", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(0, 0);
|
|
e.p1 = Vec2d(2, 0);
|
|
|
|
SketchEntity other;
|
|
other.type = SketchEntity::Type::Line;
|
|
other.p0 = Vec2d(5, -5);
|
|
other.p1 = Vec2d(5, 5);
|
|
|
|
bool ok = SketchEngine::extend_entity(e, {other}, Vec2d(2, 0));
|
|
REQUIRE(ok);
|
|
REQUIRE_THAT(e.p0.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.x(), WithinAbs(5.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.y(), WithinAbs(0.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Extend forward to circle", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(0, 0);
|
|
e.p1 = Vec2d(2, 0);
|
|
|
|
SketchEntity other;
|
|
other.type = SketchEntity::Type::Circle;
|
|
other.center = Vec2d(10, 0);
|
|
other.p0 = Vec2d(10, 0);
|
|
other.radius = 3;
|
|
|
|
bool ok = SketchEngine::extend_entity(e, {other}, Vec2d(2, 0));
|
|
REQUIRE(ok);
|
|
REQUIRE_THAT(e.p0.x(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.x(), WithinAbs(7.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.y(), WithinAbs(0.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Extend backward", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(0, 0);
|
|
e.p1 = Vec2d(2, 0);
|
|
|
|
SketchEntity other;
|
|
other.type = SketchEntity::Type::Line;
|
|
other.p0 = Vec2d(-3, -5);
|
|
other.p1 = Vec2d(-3, 5);
|
|
|
|
bool ok = SketchEngine::extend_entity(e, {other}, Vec2d(0, 0));
|
|
REQUIRE(ok);
|
|
REQUIRE_THAT(e.p0.x(), WithinAbs(-3.0, 1e-9));
|
|
REQUIRE_THAT(e.p0.y(), WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.x(), WithinAbs(2.0, 1e-9));
|
|
REQUIRE_THAT(e.p1.y(), WithinAbs(0.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Extend no target", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Line;
|
|
e.p0 = Vec2d(0, 0);
|
|
e.p1 = Vec2d(2, 0);
|
|
|
|
SketchEntity other;
|
|
other.type = SketchEntity::Type::Line;
|
|
other.p0 = Vec2d(5, -5);
|
|
other.p1 = Vec2d(5, -1);
|
|
|
|
REQUIRE_FALSE(SketchEngine::extend_entity(e, {other}, Vec2d(2, 0)));
|
|
}
|
|
|
|
// --- Arc/Circle subject trim & extend (Fase 4.5 kernel) -------------------
|
|
|
|
TEST_CASE("Trim arc drops the picked (start) side", "[SketchEdit]")
|
|
{
|
|
// Upper semicircle r=5, ccw from (5,0) to (-5,0); cutter = vertical axis.
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Arc;
|
|
e.center = Vec2d(0, 0);
|
|
e.radius = 5;
|
|
e.start_angle = 0.0;
|
|
e.end_angle = M_PI;
|
|
|
|
SketchEntity cut;
|
|
cut.type = SketchEntity::Type::Line;
|
|
cut.p0 = Vec2d(0, -10);
|
|
cut.p1 = Vec2d(0, 10);
|
|
|
|
// Pick the right quarter (phi=pi/4) -> it is removed, left quarter kept.
|
|
bool ok = SketchEngine::trim_entity(e, {cut}, Vec2d(5 * std::cos(M_PI/4), 5 * std::sin(M_PI/4)));
|
|
REQUIRE(ok);
|
|
REQUIRE(e.type == SketchEntity::Type::Arc);
|
|
REQUIRE_THAT(e.radius, WithinAbs(5.0, 1e-9));
|
|
REQUIRE_THAT(e.start_angle, WithinAbs(M_PI / 2.0, 1e-9));
|
|
REQUIRE_THAT(e.end_angle, WithinAbs(M_PI, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Trim arc drops the picked (end) side", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Arc;
|
|
e.center = Vec2d(0, 0);
|
|
e.radius = 5;
|
|
e.start_angle = 0.0;
|
|
e.end_angle = M_PI;
|
|
|
|
SketchEntity cut;
|
|
cut.type = SketchEntity::Type::Line;
|
|
cut.p0 = Vec2d(0, -10);
|
|
cut.p1 = Vec2d(0, 10);
|
|
|
|
// Pick the left quarter (phi=3pi/4) -> removed, right quarter kept.
|
|
bool ok = SketchEngine::trim_entity(e, {cut}, Vec2d(5 * std::cos(3*M_PI/4), 5 * std::sin(3*M_PI/4)));
|
|
REQUIRE(ok);
|
|
REQUIRE(e.type == SketchEntity::Type::Arc);
|
|
REQUIRE_THAT(e.start_angle, WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.end_angle, WithinAbs(M_PI / 2.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Trim circle opens into an arc excluding the pick", "[SketchEdit]")
|
|
{
|
|
// Full circle r=5; vertical axis cuts it at (0,+-5). Pick the right side
|
|
// (5,0): the kept arc is the left half, sweeping pi and centred on (-5,0).
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Circle;
|
|
e.center = Vec2d(0, 0);
|
|
e.p0 = Vec2d(5, 0);
|
|
e.radius = 5;
|
|
|
|
SketchEntity cut;
|
|
cut.type = SketchEntity::Type::Line;
|
|
cut.p0 = Vec2d(0, -10);
|
|
cut.p1 = Vec2d(0, 10);
|
|
|
|
bool ok = SketchEngine::trim_entity(e, {cut}, Vec2d(5, 0));
|
|
REQUIRE(ok);
|
|
REQUIRE(e.type == SketchEntity::Type::Arc);
|
|
REQUIRE_THAT(e.radius, WithinAbs(5.0, 1e-9));
|
|
REQUIRE_THAT(e.end_angle - e.start_angle, WithinAbs(M_PI, 1e-9));
|
|
// Midpoint of the kept arc must point left (away from the pick).
|
|
double mid = 0.5 * (e.start_angle + e.end_angle);
|
|
REQUIRE_THAT(5 * std::cos(mid), WithinAbs(-5.0, 1e-9));
|
|
REQUIRE_THAT(5 * std::sin(mid), WithinAbs(0.0, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Extend arc forward (end) to a crossing", "[SketchEdit]")
|
|
{
|
|
// Quarter arc (5,0)->(0,5); cutter crosses the circle at (-5,0). Picking
|
|
// near the end grows the sweep ccw to pi.
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Arc;
|
|
e.center = Vec2d(0, 0);
|
|
e.radius = 5;
|
|
e.start_angle = 0.0;
|
|
e.end_angle = M_PI / 2.0;
|
|
|
|
SketchEntity cut;
|
|
cut.type = SketchEntity::Type::Line;
|
|
cut.p0 = Vec2d(-10, 0);
|
|
cut.p1 = Vec2d(0, 0);
|
|
|
|
bool ok = SketchEngine::extend_entity(e, {cut}, Vec2d(0, 5));
|
|
REQUIRE(ok);
|
|
REQUIRE(e.type == SketchEntity::Type::Arc);
|
|
REQUIRE_THAT(e.start_angle, WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.end_angle, WithinAbs(M_PI, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Extend arc backward (start) to a crossing", "[SketchEdit]")
|
|
{
|
|
// Quarter arc (0,5)->(-5,0); cutter crosses at (5,0). Picking near the
|
|
// start grows the sweep cw to start_angle 0.
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Arc;
|
|
e.center = Vec2d(0, 0);
|
|
e.radius = 5;
|
|
e.start_angle = M_PI / 2.0;
|
|
e.end_angle = M_PI;
|
|
|
|
SketchEntity cut;
|
|
cut.type = SketchEntity::Type::Line;
|
|
cut.p0 = Vec2d(10, 0);
|
|
cut.p1 = Vec2d(0, 0);
|
|
|
|
bool ok = SketchEngine::extend_entity(e, {cut}, Vec2d(0, 5));
|
|
REQUIRE(ok);
|
|
REQUIRE_THAT(e.start_angle, WithinAbs(0.0, 1e-9));
|
|
REQUIRE_THAT(e.end_angle, WithinAbs(M_PI, 1e-9));
|
|
}
|
|
|
|
TEST_CASE("Extend circle returns false (closed)", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Circle;
|
|
e.center = Vec2d(0, 0);
|
|
e.p0 = Vec2d(5, 0);
|
|
e.radius = 5;
|
|
|
|
SketchEntity cut;
|
|
cut.type = SketchEntity::Type::Line;
|
|
cut.p0 = Vec2d(0, -10);
|
|
cut.p1 = Vec2d(0, 10);
|
|
|
|
REQUIRE_FALSE(SketchEngine::extend_entity(e, {cut}, Vec2d(5, 0)));
|
|
}
|
|
|
|
TEST_CASE("Trim arc with no crossing returns false", "[SketchEdit]")
|
|
{
|
|
SketchEntity e;
|
|
e.type = SketchEntity::Type::Arc;
|
|
e.center = Vec2d(0, 0);
|
|
e.radius = 5;
|
|
e.start_angle = 0.0;
|
|
e.end_angle = M_PI / 2.0;
|
|
|
|
SketchEntity cut; // far away, never reaches the r=5 circle
|
|
cut.type = SketchEntity::Type::Line;
|
|
cut.p0 = Vec2d(20, -5);
|
|
cut.p1 = Vec2d(20, 5);
|
|
|
|
REQUIRE_FALSE(SketchEngine::trim_entity(e, {cut}, Vec2d(5 * std::cos(M_PI/4), 5 * std::sin(M_PI/4))));
|
|
}
|