Mercurial > hg > orthanc-stone
changeset 2156:340bde744884
added DebugDrawing2D and GeometryToolbox::IntersectLineAndSegment()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 27 Sep 2024 22:14:36 +0200 |
parents | 71d6ad7036b7 |
children | 917e40af6b45 |
files | OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake OrthancStone/Sources/Toolbox/DebugDrawing2D.cpp OrthancStone/Sources/Toolbox/DebugDrawing2D.h OrthancStone/Sources/Toolbox/GeometryToolbox.cpp OrthancStone/Sources/Toolbox/GeometryToolbox.h OrthancStone/UnitTestsSources/ComputationalGeometryTests.cpp OrthancStone/UnitTestsSources/GeometryToolboxTests.cpp |
diffstat | 7 files changed, 323 insertions(+), 65 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Fri Sep 27 15:59:17 2024 +0200 +++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Fri Sep 27 22:14:36 2024 +0200 @@ -404,6 +404,8 @@ ${ORTHANC_STONE_ROOT}/Toolbox/BucketAccumulator2D.h ${ORTHANC_STONE_ROOT}/Toolbox/CoordinateSystem3D.cpp ${ORTHANC_STONE_ROOT}/Toolbox/CoordinateSystem3D.h + ${ORTHANC_STONE_ROOT}/Toolbox/DebugDrawing2D.cpp + ${ORTHANC_STONE_ROOT}/Toolbox/DebugDrawing2D.h ${ORTHANC_STONE_ROOT}/Toolbox/DicomInstanceParameters.cpp ${ORTHANC_STONE_ROOT}/Toolbox/DicomInstanceParameters.h ${ORTHANC_STONE_ROOT}/Toolbox/DicomStructureSet.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Toolbox/DebugDrawing2D.cpp Fri Sep 27 22:14:36 2024 +0200 @@ -0,0 +1,160 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#include "DebugDrawing2D.h" + + +namespace OrthancStone +{ + class DebugDrawing2D::Segment + { + private: + double x1_; + double y1_; + double x2_; + double y2_; + std::string color_; + bool arrow_; + + public: + Segment(double x1, + double y1, + double x2, + double y2, + const std::string& color, + bool arrow) : + x1_(x1), + y1_(y1), + x2_(x2), + y2_(y2), + color_(color), + arrow_(arrow) + { + } + + double GetX1() const + { + return x1_; + } + + double GetY1() const + { + return y1_; + } + + double GetX2() const + { + return x2_; + } + + double GetY2() const + { + return y2_; + } + + const std::string& GetColor() const + { + return color_; + } + + bool IsArrow() const + { + return arrow_; + } + }; + + + void DebugDrawing2D::AddSegment(double x1, + double y1, + double x2, + double y2, + const std::string& color, + bool arrow, + bool addToExtent) + { + if (addToExtent) + { + extent_.AddPoint(x1, y1); + extent_.AddPoint(x2, y2); + } + + segments_.push_back(Segment(x1, y1, x2, y2, color, arrow)); + } + + + void DebugDrawing2D::SaveSvg(const std::string& path) + { + // Size in pixels + float ww, hh; + if (extent_.IsEmpty()) + { + ww = 2048.0f; + hh = 2048.0f; + } + else if (extent_.GetWidth() > extent_.GetHeight()) + { + ww = 2048.0f; + hh = ww * extent_.GetHeight() / extent_.GetWidth(); + } + else + { + hh = 2048.0f; + ww = hh * extent_.GetWidth() / extent_.GetHeight(); + } + + FILE* fp = fopen(path.c_str(), "w"); + fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); + fprintf(fp, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + fprintf(fp, "<svg width=\"%f\" height=\"%f\" viewBox=\"0 0 %f %f\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", ww, hh, extent_.GetWidth(), extent_.GetHeight()); + + // http://thenewcode.com/1068/Making-Arrows-in-SVG + fprintf(fp, "<defs>\n"); + fprintf(fp, "<marker id=\"arrowhead\" markerWidth=\"2\" markerHeight=\"3\" \n"); + fprintf(fp, "refX=\"2\" refY=\"1.5\" orient=\"auto\">\n"); + fprintf(fp, "<polygon points=\"0 0, 2 1.5, 0 3\" />\n"); + fprintf(fp, "</marker>\n"); + fprintf(fp, "</defs>\n"); + + fprintf(fp, "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" width=\"%f\" height=\"%f\"/>\n", extent_.GetWidth(), extent_.GetHeight()); + + for (std::list<Segment>::const_iterator it = segments_.begin(); it != segments_.end(); ++it) + { + float strokeWidth = 0.1; + + std::string s; + if (it->IsArrow()) + { + s = "marker-end=\"url(#arrowhead)\""; + } + + fprintf(fp, "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" stroke=\"%s\" stroke-width=\"%f\" %s/>\n", + it->GetX1() - extent_.GetX1(), it->GetY1() - extent_.GetY1(), + it->GetX2() - extent_.GetX1(), it->GetY2() - extent_.GetY1(), + it->GetColor().c_str(), strokeWidth, s.c_str()); + } + + fprintf(fp, "</svg>\n"); + + fclose(fp); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Toolbox/DebugDrawing2D.h Fri Sep 27 22:14:36 2024 +0200 @@ -0,0 +1,54 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "Extent2D.h" + +#include <boost/noncopyable.hpp> +#include <list> +#include <string> + + +namespace OrthancStone +{ + class DebugDrawing2D : public boost::noncopyable + { + private: + class Segment; + + Extent2D extent_; + std::list<Segment> segments_; + + public: + void AddSegment(double x1, + double y1, + double x2, + double y2, + const std::string& color, + bool arrow, + bool addToExtent); + + void SaveSvg(const std::string& path); + }; +}
--- a/OrthancStone/Sources/Toolbox/GeometryToolbox.cpp Fri Sep 27 15:59:17 2024 +0200 +++ b/OrthancStone/Sources/Toolbox/GeometryToolbox.cpp Fri Sep 27 22:14:36 2024 +0200 @@ -565,5 +565,78 @@ return false; } } + + + static bool SolveLineIntersectionSystem(double& x, + double& y, + double& s, + double& t, + double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + // https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments + const double a1 = x2 - x1; + const double b1 = -x4 + x3; + const double c1 = x3 - x1; + const double a2 = y2 - y1; + const double b2 = -y4 + y3; + const double c2 = y3 - y1; + + const double denominator = a1 * b2 - a2 * b1; + if (LinearAlgebra::IsCloseToZero(denominator)) + { + return false; + } + else + { + // This is Cramer's rule + s = (c1 * b2 - c2 * b1) / denominator; + t = (a1 * c2 - a2 * c1) / denominator; + x = x1 + s * (x2 - x1); + y = y1 + s * (y2 - y1); + return true; + } + } + + + bool IntersectTwoLines(double& x, + double& y, + double ax1, + double ay1, + double ax2, + double ay2, + double bx1, + double by1, + double bx2, + double by2) + { + double s, t; + return SolveLineIntersectionSystem(x, y, s, t, ax1, ay1, ax2, ay2, bx1, by1, bx2, by2); + } + + + bool IntersectLineAndSegment(double& x, + double& y, + double lineX1, + double lineY1, + double lineX2, + double lineY2, + double segmentX1, + double segmentY1, + double segmentX2, + double segmentY2) + { + double s, t; + if (SolveLineIntersectionSystem(x, y, s, t, lineX1, lineY1, lineX2, lineY2, segmentX1, segmentY1, segmentX2, segmentY2)) + { + return (t >= 0 && t <= 1); + } + else + { + return false; + } + } } }
--- a/OrthancStone/Sources/Toolbox/GeometryToolbox.h Fri Sep 27 15:59:17 2024 +0200 +++ b/OrthancStone/Sources/Toolbox/GeometryToolbox.h Fri Sep 27 22:14:36 2024 +0200 @@ -117,6 +117,28 @@ bool ComputeNormal(Vector& normal, const Orthanc::DicomMap& dicom); + bool IntersectTwoLines(double& x, + double& y, + double ax1, + double ay1, + double ax2, + double ay2, + double bx1, + double by1, + double bx2, + double by2); + + bool IntersectLineAndSegment(double& x, + double& y, + double lineX1, + double lineY1, + double lineX2, + double lineY2, + double segmentX1, + double segmentY1, + double segmentX2, + double segmentY2); + inline float ComputeBilinearInterpolationUnitSquare(float x, float y, float f00, // source(0, 0)
--- a/OrthancStone/UnitTestsSources/ComputationalGeometryTests.cpp Fri Sep 27 15:59:17 2024 +0200 +++ b/OrthancStone/UnitTestsSources/ComputationalGeometryTests.cpp Fri Sep 27 22:14:36 2024 +0200 @@ -540,71 +540,6 @@ } -#if 0 -static void SaveSvg(const std::list< std::vector<OrthancStone::ScenePoint2D> >& contours) -{ - float ww = 15; - float hh = 13; - - FILE* fp = fopen("test.svg", "w"); - fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); - fprintf(fp, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); - fprintf(fp, "<svg width=\"%f\" height=\"%f\" viewBox=\"0 0 %f %f\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", 100.0f*ww, 100.0f*hh, ww, hh); - - // http://thenewcode.com/1068/Making-Arrows-in-SVG - fprintf(fp, "<defs>\n"); - fprintf(fp, "<marker id=\"arrowhead\" markerWidth=\"2\" markerHeight=\"3\" \n"); - fprintf(fp, "refX=\"2\" refY=\"1.5\" orient=\"auto\">\n"); - fprintf(fp, "<polygon points=\"0 0, 2 1.5, 0 3\" />\n"); - fprintf(fp, "</marker>\n"); - fprintf(fp, "</defs>\n"); - - fprintf(fp, "<rect fill=\"#fff\" stroke=\"#000\" x=\"0\" y=\"0\" width=\"%f\" height=\"%f\"/>\n", ww, hh); - - unsigned int count = 0; - - for (std::list< std::vector<OrthancStone::ScenePoint2D> >::const_iterator - it = contours.begin(); it != contours.end(); ++it, count++) - { - std::string color; - if (count == 0) - { - color = "blue"; - } - else if (count == 1) - { - color = "red"; - } - else if (count == 2) - { - color = "green"; - } - else if (count == 3) - { - color = "orange"; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - for (size_t i = 0; i + 1 < it->size(); i++) - { - float x1 = (*it)[i].GetX(); - float x2 = (*it)[i + 1].GetX(); - float y1 = (*it)[i].GetY(); - float y2 = (*it)[i + 1].GetY(); - - fprintf(fp, "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" stroke=\"%s\" stroke-width=\"0.05\" marker-end=\"url(#arrowhead)\"/>\n", x1, y1, x2, y2, color.c_str()); - } - } - fprintf(fp, "</svg>\n"); - - fclose(fp); -} -#endif - - TEST(UnionOfRectangles, EdgeCases) { {
--- a/OrthancStone/UnitTestsSources/GeometryToolboxTests.cpp Fri Sep 27 15:59:17 2024 +0200 +++ b/OrthancStone/UnitTestsSources/GeometryToolboxTests.cpp Fri Sep 27 22:14:36 2024 +0200 @@ -1174,3 +1174,15 @@ ASSERT_EQ("LHP", right); } } + + +TEST(GeometryToolbox, IntersectTwoLines) +{ + double x, y; + ASSERT_TRUE(OrthancStone::GeometryToolbox::IntersectTwoLines(x, y, 1, 1, 3, 2, 1, 4, 2, -1)); + ASSERT_DOUBLE_EQ(x, 17.0 / 11.0); + ASSERT_DOUBLE_EQ(y, 14.0 / 11.0); + ASSERT_TRUE(OrthancStone::GeometryToolbox::IntersectLineAndSegment(x, y, 1, 1, 3, 2, 1, 4, 2, -1)); + ASSERT_DOUBLE_EQ(x, 17.0 / 11.0); + ASSERT_DOUBLE_EQ(y, 14.0 / 11.0); +}