comparison OrthancFramework/Sources/Images/ImageProcessing.cpp @ 4874:bee6da5155dc

fix polygon filling
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Jan 2022 13:04:47 +0100
parents d68dbe6590ce
children 5dae41084ab7
comparison
equal deleted inserted replaced
4873:d68dbe6590ce 4874:bee6da5155dc
1910 { 1910 {
1911 yUpper = upper.GetY(); 1911 yUpper = upper.GetY();
1912 } 1912 }
1913 } 1913 }
1914 1914
1915 float GetActualX() const
1916 {
1917 #if USE_POLYGON_FRACTIONS == 1
1918 return (static_cast<float>(x) +
1919 static_cast<float>(xOffset) /
1920 static_cast<float>(dxPerScanDenominator));
1921 #else
1922 return xIntersect;
1923 #endif
1924 }
1925
1926 void NextScanLine() 1915 void NextScanLine()
1927 { 1916 {
1928 #if USE_POLYGON_FRACTIONS == 1 1917 #if USE_POLYGON_FRACTIONS == 1
1929 xOffset += dxPerScanNumerator; 1918 xOffset += dxPerScanNumerator;
1930 1919
1957 else 1946 else
1958 { 1947 {
1959 return x + 1; 1948 return x + 1;
1960 } 1949 }
1961 #else 1950 #else
1962 return std::ceil(GetActualX()); // TODO 1951 return static_cast<int>(std::ceil(xIntersect));
1963 #endif 1952 #endif
1964 } 1953 }
1965 1954
1966 int GetExitX() const 1955 int GetExitX() const
1967 { 1956 {
1968 #if USE_POLYGON_FRACTIONS == 1 1957 #if USE_POLYGON_FRACTIONS == 1
1969 assert(xOffset >= 0 && xOffset < dxPerScanDenominator); 1958 assert(xOffset >= 0 && xOffset < dxPerScanDenominator);
1970 return x; 1959 return x;
1971 #else 1960 #else
1972 return std::floor(GetActualX()); // TODO 1961 return static_cast<int>(std::floor(xIntersect));
1973 #endif 1962 #endif
1974 } 1963 }
1975 1964
1976 int GetUpperY() const 1965 int GetUpperY() const
1977 { 1966 {
1978 return yUpper; 1967 return yUpper;
1979 } 1968 }
1980 1969
1981 bool operator< (const PolygonEdge& other) const 1970 bool operator< (const PolygonEdge& other) const
1982 { 1971 {
1972 #if USE_POLYGON_FRACTIONS == 1
1973 assert(xOffset >= 0 && xOffset < dxPerScanDenominator);
1974 assert(other.xOffset >= 0 && other.xOffset < other.dxPerScanDenominator);
1975 return x < other.x;
1976 #else
1983 // cf. "insertEdge()" in textbook 1977 // cf. "insertEdge()" in textbook
1984 return (GetActualX() < other.GetActualX()); 1978 return (xIntersect < other.xIntersect);
1979 #endif
1985 } 1980 }
1986 }; 1981 };
1987 } 1982 }
1988 1983
1989 1984
1990 // For an index, return y-coordinate of next nonhorizontal line 1985 // For an index, return y-coordinate of next nonhorizontal line
1991 static size_t GetPolygonNextY(const std::vector<ImageProcessing::ImagePoint>& points, 1986 static size_t GetPolygonNextY(const std::vector<ImageProcessing::ImagePoint>& points,
1992 size_t k) 1987 size_t k)
1993 { 1988 {
1994 // cf. "yNext()" in textbook 1989 // cf. "yNext()" in textbook
1995 size_t j = k + 1; 1990 size_t j = k;
1996 1991
1997 for (;;) 1992 for (;;)
1998 { 1993 {
1999 if (j >= points.size()) 1994 j++;
1995 if (j == points.size())
2000 { 1996 {
2001 j = 0; 1997 j = 0;
2002 } 1998 }
2003 1999
2004 if (points[k].GetY() == points[j].GetY()) 2000 if (points[k].GetY() != points[j].GetY())
2005 { 2001 {
2006 j ++; 2002 return points[j].GetY();
2003 }
2004 }
2005 }
2006
2007
2008 static size_t GetPolygonPreviousY(const std::vector<ImageProcessing::ImagePoint>& points,
2009 size_t k)
2010 {
2011 size_t j = k;
2012
2013 for (;;)
2014 {
2015 if (j > 0)
2016 {
2017 j --;
2007 } 2018 }
2008 else 2019 else
2009 { 2020 {
2021 j = points.size() - 1;
2022 }
2023
2024 if (points[k].GetY() != points[j].GetY())
2025 {
2010 return points[j].GetY(); 2026 return points[j].GetY();
2011 } 2027 }
2012 } 2028 }
2013 } 2029 }
2014 2030
2015 2031
2016 2032
2017 void ImageProcessing::FillPolygon(IPolygonFiller& filler, 2033 void ImageProcessing::FillPolygon(IPolygonFiller& filler,
2018 const std::vector<ImagePoint>& points) 2034 const std::vector<ImagePoint>& points)
2019 { 2035 {
2020 /** 2036 /**
2021 * This implementation is a C++ adaption of Section 3.11 (pages 2037 * This implementation is a C++ adaption of Section 3.11 (pages
2031 } 2047 }
2032 2048
2033 EdgeTable globalEdgeTable; 2049 EdgeTable globalEdgeTable;
2034 2050
2035 // cf. "buildEdgeList()" in textbook 2051 // cf. "buildEdgeList()" in textbook
2036 int yPrev = points[points.size() - 2].GetY(); 2052
2053 // Error in the textbook: we use "GetPolygonPreviousY()" instead of "points.size() - 2"
2054 int yPrev = GetPolygonPreviousY(points, points.size() - 1);
2037 ImagePoint v1(points[points.size() - 1]); 2055 ImagePoint v1(points[points.size() - 1]);
2038 2056
2039 for (size_t i = 0; i < points.size(); i++) 2057 for (size_t i = 0; i < points.size(); i++)
2040 { 2058 {
2041 ImagePoint v2(points[i]); 2059 ImagePoint v2(points[i]);
2042 2060
2043 if (v1.GetY() < v2.GetY()) 2061 if (v1.GetY() != v2.GetY())
2044 { 2062 {
2045 // Up-going edge 2063 // Non-horizontal line
2046 PolygonEdge edge(v1, v2, GetPolygonNextY(points, i)); 2064 if (v1.GetY() < v2.GetY())
2047 globalEdgeTable[v1.GetY()].push_back(edge); 2065 {
2048 } 2066 // Up-going edge
2049 else if (v1.GetY() > v2.GetY()) 2067 PolygonEdge edge(v1, v2, GetPolygonNextY(points, i));
2050 { 2068 globalEdgeTable[v1.GetY()].push_back(edge);
2051 // Down-going edge 2069 }
2052 PolygonEdge edge(v2, v1, yPrev); 2070 else if (v1.GetY() > v2.GetY())
2053 globalEdgeTable[v2.GetY()].push_back(edge); 2071 {
2054 } 2072 // Down-going edge
2055 2073 PolygonEdge edge(v2, v1, yPrev);
2056 yPrev = v1.GetY(); 2074 globalEdgeTable[v2.GetY()].push_back(edge);
2075 }
2076
2077 // Error in the textbook: "yPrev" must NOT be updated on horizontal lines
2078 yPrev = v1.GetY();
2079 }
2080
2057 v1 = v2; 2081 v1 = v2;
2058 } 2082 }
2059 2083
2060 if (globalEdgeTable.empty()) 2084 if (globalEdgeTable.empty())
2061 { 2085 {
2062 // Degenerate case: There were only horizontal lines 2086 // Degenerate case: There were only horizontal lines
2063 int x1 = points[0].GetX(); 2087 int x1 = points[0].GetX();
2064 int x2 = points[0].GetX(); 2088 int x2 = points[0].GetX();
2116 stillActive.push_back(activeEdges[i]); 2140 stillActive.push_back(activeEdges[i]);
2117 } 2141 }
2118 } 2142 }
2119 2143
2120 activeEdges.swap(stillActive); 2144 activeEdges.swap(stillActive);
2145
2146 assert(activeEdges.size() % 2 == 0);
2121 std::sort(activeEdges.begin(), activeEdges.end()); 2147 std::sort(activeEdges.begin(), activeEdges.end());
2122 2148
2123 // cf. "fillScan()" in textbook 2149 // cf. "fillScan()" in textbook
2124 for (size_t k = 0; k + 1 < activeEdges.size(); k += 2) 2150 for (size_t k = 0; k + 1 < activeEdges.size(); k += 2)
2125 { 2151 {