# HG changeset patch # User Sebastien Jodogne # Date 1641932807 -3600 # Node ID b1f510e601d29bd637a9fddccae8cfce2acf6f85 # Parent b896f20d24cab0737acef138d412f778a5aaffe4 more unit tests diff -r b896f20d24ca -r b1f510e601d2 OrthancStone/Sources/Toolbox/Internals/OrientedIntegerLine2D.h --- a/OrthancStone/Sources/Toolbox/Internals/OrientedIntegerLine2D.h Tue Jan 11 19:59:40 2022 +0100 +++ b/OrthancStone/Sources/Toolbox/Internals/OrientedIntegerLine2D.h Tue Jan 11 21:26:47 2022 +0100 @@ -23,6 +23,7 @@ #pragma once +#include // For size_t #include #include diff -r b896f20d24ca -r b1f510e601d2 OrthancStone/Sources/Toolbox/UnionOfRectangles.cpp --- a/OrthancStone/Sources/Toolbox/UnionOfRectangles.cpp Tue Jan 11 19:59:40 2022 +0100 +++ b/OrthancStone/Sources/Toolbox/UnionOfRectangles.cpp Tue Jan 11 21:26:47 2022 +0100 @@ -84,7 +84,7 @@ class UnionOfRectangles::Factory : public SegmentTree::IPayloadFactory { public: - virtual Orthanc::IDynamicObject* Create() + virtual Orthanc::IDynamicObject* Create() ORTHANC_OVERRIDE { return new Payload; } @@ -103,7 +103,7 @@ } virtual void Visit(const SegmentTree& node, - bool fullyInside) + bool fullyInside) ORTHANC_OVERRIDE { Payload& payload = node.GetTypedPayload(); @@ -384,6 +384,8 @@ void UnionOfRectangles::Apply(std::list< std::vector >& contours, const std::list& rectangles) { + contours.clear(); + /** * STEP 1 **/ @@ -392,10 +394,14 @@ assert(horizontalProjection.GetProjectedRectanglesCount() == verticalProjection.GetProjectedRectanglesCount()); - /** * STEP 2 **/ + if (verticalProjection.GetEndpointsCount() == 0) + { + return; + } + Factory factory; SegmentTree tree(0, verticalProjection.GetEndpointsCount() - 1, factory); @@ -528,8 +534,6 @@ std::list chains; Internals::OrientedIntegerLine2D::ExtractChains(chains, allEdges); - contours.clear(); - for (std::list::const_iterator it = chains.begin(); it != chains.end(); ++it) { diff -r b896f20d24ca -r b1f510e601d2 UnitTestsSources/ComputationalGeometryTests.cpp --- a/UnitTestsSources/ComputationalGeometryTests.cpp Tue Jan 11 19:59:40 2022 +0100 +++ b/UnitTestsSources/ComputationalGeometryTests.cpp Tue Jan 11 21:26:47 2022 +0100 @@ -577,10 +577,70 @@ #endif -TEST(UnionOfRectangles, Complex) +TEST(UnionOfRectangles, EdgeCases) { { std::list rectangles; + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(0u, contours.size()); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(3, 3, 4, 3)); // empty rectangle + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(0u, contours.size()); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(1, 1, 2, 2)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(5u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(1, 2))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(2, 1))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(1, 1))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(1, 2))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(1, 1, 2, 2)); + rectangles.push_back(OrthancStone::Extent2D(1, 3, 2, 4)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(2u, contours.size()); + + ASSERT_EQ(5u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(1, 4))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(2, 4))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(1, 3))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(1, 4))); + + ASSERT_EQ(5u, contours.back().size()); + ASSERT_TRUE(contours.back()[0].IsEqual(OrthancStone::ScenePoint2D(1, 2))); + ASSERT_TRUE(contours.back()[1].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.back()[2].IsEqual(OrthancStone::ScenePoint2D(2, 1))); + ASSERT_TRUE(contours.back()[3].IsEqual(OrthancStone::ScenePoint2D(1, 1))); + ASSERT_TRUE(contours.back()[4].IsEqual(OrthancStone::ScenePoint2D(1, 2))); + } + + { + std::list rectangles; rectangles.push_back(OrthancStone::Extent2D(1, 4, 4, 6)); rectangles.push_back(OrthancStone::Extent2D(4, 6, 7, 8)); rectangles.push_back(OrthancStone::Extent2D(4, 2, 7, 4)); @@ -670,7 +730,6 @@ ASSERT_TRUE(contours.back()[4].IsEqual(OrthancStone::ScenePoint2D(1, 2))); } -#if 0 { std::list rectangles; rectangles.push_back(OrthancStone::Extent2D(1, 4, 4, 6)); @@ -681,23 +740,235 @@ std::list< std::vector > contours; OrthancStone::UnionOfRectangles::Apply(contours, rectangles); - SaveSvg(contours); - ASSERT_EQ(1u, contours.size()); ASSERT_EQ(13u, contours.front().size()); ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(4, 8))); ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(7, 8))); ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(7, 6))); ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(9, 6))); - ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(7, 6))); - ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(4, 4))); - ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(1, 4))); - ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D())); - ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D())); - ASSERT_TRUE(contours.front()[9].IsEqual(OrthancStone::ScenePoint2D())); - ASSERT_TRUE(contours.front()[10].IsEqual(OrthancStone::ScenePoint2D())); - ASSERT_TRUE(contours.front()[11].IsEqual(OrthancStone::ScenePoint2D())); - ASSERT_TRUE(contours.front()[12].IsEqual(OrthancStone::ScenePoint2D())); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(9, 4))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(7, 4))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(7, 2))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(4, 2))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(4, 4))); + ASSERT_TRUE(contours.front()[9].IsEqual(OrthancStone::ScenePoint2D(1, 4))); + ASSERT_TRUE(contours.front()[10].IsEqual(OrthancStone::ScenePoint2D(1, 6))); + ASSERT_TRUE(contours.front()[11].IsEqual(OrthancStone::ScenePoint2D(4, 6))); + ASSERT_TRUE(contours.front()[12].IsEqual(OrthancStone::ScenePoint2D(4, 8))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(1, 4, 4, 6)); + rectangles.push_back(OrthancStone::Extent2D(4, 6, 7, 8)); + rectangles.push_back(OrthancStone::Extent2D(4, 2, 7, 6)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(4, 8))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(7, 8))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(7, 2))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(4, 2))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(4, 4))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(1, 4))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(1, 6))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(4, 6))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(4, 8))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 4, 4)); + rectangles.push_back(OrthancStone::Extent2D(3, 3, 5, 5)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(3, 5))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(5, 5))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(5, 3))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(4, 3))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(4, 2))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(2, 4))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(3, 4))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(3, 5))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 4, 4)); + rectangles.push_back(OrthancStone::Extent2D(3, 1, 5, 3)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(2, 4))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(4, 4))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(4, 3))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(5, 3))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(5, 1))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(3, 1))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(2, 4))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 4, 4)); + rectangles.push_back(OrthancStone::Extent2D(1, 1, 3, 3)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(2, 4))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(4, 4))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(4, 2))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(3, 1))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(1, 1))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(1, 3))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(2, 4))); } -#endif + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 4, 4)); + rectangles.push_back(OrthancStone::Extent2D(1, 3, 3, 5)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(1, 5))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(3, 5))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(3, 4))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(4, 4))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(4, 2))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(1, 3))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(1, 5))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 3, 3)); + rectangles.push_back(OrthancStone::Extent2D(3, 1, 4, 2)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(3, 3))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(4, 2))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(4, 1))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(3, 1))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 3, 3)); + rectangles.push_back(OrthancStone::Extent2D(3, 3, 4, 4)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(3, 4))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(4, 4))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(4, 3))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(3, 3))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(3, 3))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(3, 4))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 3, 3)); + rectangles.push_back(OrthancStone::Extent2D(1, 3, 2, 4)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(1, 4))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(2, 4))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(3, 3))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(1, 3))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(1, 4))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 3, 3)); + rectangles.push_back(OrthancStone::Extent2D(1, 1, 2, 2)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(9u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(3, 3))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(2, 1))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(1, 1))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(1, 2))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + } + + { + std::list rectangles; + rectangles.push_back(OrthancStone::Extent2D(2, 2, 3, 5)); + rectangles.push_back(OrthancStone::Extent2D(1, 3, 4, 4)); + + std::list< std::vector > contours; + OrthancStone::UnionOfRectangles::Apply(contours, rectangles); + + ASSERT_EQ(1u, contours.size()); + ASSERT_EQ(13u, contours.front().size()); + ASSERT_TRUE(contours.front()[0].IsEqual(OrthancStone::ScenePoint2D(2, 5))); + ASSERT_TRUE(contours.front()[1].IsEqual(OrthancStone::ScenePoint2D(3, 5))); + ASSERT_TRUE(contours.front()[2].IsEqual(OrthancStone::ScenePoint2D(3, 4))); + ASSERT_TRUE(contours.front()[3].IsEqual(OrthancStone::ScenePoint2D(4, 4))); + ASSERT_TRUE(contours.front()[4].IsEqual(OrthancStone::ScenePoint2D(4, 3))); + ASSERT_TRUE(contours.front()[5].IsEqual(OrthancStone::ScenePoint2D(3, 3))); + ASSERT_TRUE(contours.front()[6].IsEqual(OrthancStone::ScenePoint2D(3, 2))); + ASSERT_TRUE(contours.front()[7].IsEqual(OrthancStone::ScenePoint2D(2, 2))); + ASSERT_TRUE(contours.front()[8].IsEqual(OrthancStone::ScenePoint2D(2, 3))); + ASSERT_TRUE(contours.front()[9].IsEqual(OrthancStone::ScenePoint2D(1, 3))); + ASSERT_TRUE(contours.front()[10].IsEqual(OrthancStone::ScenePoint2D(1, 4))); + ASSERT_TRUE(contours.front()[11].IsEqual(OrthancStone::ScenePoint2D(2, 4))); + ASSERT_TRUE(contours.front()[12].IsEqual(OrthancStone::ScenePoint2D(2, 5))); + } }