{"id":652,"date":"2012-08-22T16:10:33","date_gmt":"2012-08-22T20:10:33","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=652"},"modified":"2019-10-31T14:54:10","modified_gmt":"2019-10-31T18:54:10","slug":"visualizing-the-floating-point-behavior-of-the-point-on-line-test","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2012\/08\/22\/visualizing-the-floating-point-behavior-of-the-point-on-line-test\/","title":{"rendered":"Visualizing the floating-point behavior of the point-on-line test"},"content":{"rendered":"<div class=\"content\"><p>Today's post is about this picture, which is from the paper \"Classroom Examples of Robustness Problems in Geometric Computations,\" by L. Kettner, K. Mehlhorn, S. Pion, S. Schirra, and C. Yap, <i>Computational Geometry<\/i>, vol. 40, n. 1, May 2008, pp. 61-78.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2012\/robustness-problems-fig.png\" alt=\"\"> <\/p><p>Yes, I know, I was supposed to do another follow-up on <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/cody\/problems\/820-eliminate-unnecessary-polygon-vertices\">my Cody problem of eliminating unnecessary polygon vertices<\/a>. But I really am following up on the Cody problem, if a bit indirectly.<\/p><p>I was looking at <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/cody\/problems\/820-eliminate-unnecessary-polygon-vertices\/solutions\/108897\">solution 108897<\/a> by <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/cody\/players\/210779-cris-luengo\">Cris<\/a>, and I paused over these lines:<\/p><pre class=\"language-matlab\">v1 = p2-p1;\r\nv2 = p3-p2;\r\n<span class=\"keyword\">if<\/span> ( v1(1)*v2(2) == v1(2)*v2(1) ) &amp;&amp; any(v1.*v2&gt;0)\r\n<\/pre><p>I recognized the expression <tt>( v1(1)*v2(2) == v1(2)*v2(1) )<\/tt> as a way to see if the point <tt>p2<\/tt> is on the line segment formed by <tt>p1<\/tt> and <tt>p3<\/tt>. It reminded me of Loren's <a href=\"https:\/\/blogs.mathworks.com\/loren\/2008\/06\/06\/collinearity\/\">06-Jun-2008 blog post<\/a> on methods for determining collinearity, and it also reminded me of the paper above.<\/p><p>I decided to see if I could reproduce the figure in MATLAB.<\/p><p>The figure concerns the behavior of floating-point methods for computing the <i>planar orientation predicate<\/i>. Given three points $p$, $q$, and $r$ in a plane, does the point $r$ lie to the left of the line through $p$ and $q$, to the right of that line, or on the line? One way to compute this is via the equation<\/p><p>$$O(p,q,r) = \\mbox{sign}((q_x - p_x)(r_y - p_y) - (q_y - p_y)(r_x - p_x))$$<\/p><p>The figure in the paper represents an experiment using $q=(12,12)$, $r=(24,24)$, and $256^2$ different locations for the point $p$, all of which are <i>extremely<\/i> close to the point $p=(0.5,0.5)$. More specifically, the figure is a visualization of $O((p_x+xu, p_y+yu),q,r)$ for $0 \\leq x,y \\leq 255$ and $u=2^{-53}$. This value of $u$ is the increment between adjacent double-precision floating-point numbers in the interval $0.5 &lt; x \\leq 1$.<\/p><p>Here's a straightforward implementation of the orientation equation above.<\/p><pre class=\"language-matlab\"><span class=\"keyword\">function<\/span> out = floatOrient(p,q,r)\r\n<\/pre><pre class=\"language-matlab\">out = sign((q(1) - p(1))*(r(2) - p(2)) - <span class=\"keyword\">...<\/span>\r\n    (q(2) - p(2))*(r(1) - p(1)));\r\n<\/pre><p>Let's see what we get when we compute the orientation for all those different point locations $p'$ that are extremely close to $(0.5,0.5)$.<\/p><pre class=\"codeinput\">[m,n] = meshgrid(0:255);\r\nPx = 0.5 + n*eps(0.5);\r\nPy = 0.5 + m*eps(0.5);\r\n\r\nq = [12 12];\r\nr = [24 24];\r\n\r\nout = zeros(size(Px));\r\n<span class=\"keyword\">for<\/span> u = 1:256\r\n    <span class=\"keyword\">for<\/span> v = 1:256\r\n        out(u,v) = floatOrient([Px(u,v), Py(u,v)], q, r);\r\n    <span class=\"keyword\">end<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre><p>(Yes, I know ... <tt>floatOrient<\/tt> and the calling code above can all be vectorized.)<\/p><p>I'll show in yellow the locations $p'$ where $r$ has been determined to be on the line containing $p'$ and $q$. I'll use blue and red to show where $r$ has been determined to lie on one side or the other of the line.<\/p><pre class=\"codeinput\">image(out + 2);\r\naxis <span class=\"string\">equal<\/span>;\r\nset(gca,<span class=\"string\">'ydir'<\/span>,<span class=\"string\">'normal'<\/span>)\r\naxis <span class=\"string\">off<\/span>\r\ncolormap([0 0 1; 1 1 0; 1 0 0])\r\nhold <span class=\"string\">on<\/span>\r\nplot([.5 256.5],[.5 256.6],<span class=\"string\">'k'<\/span>,<span class=\"string\">'LineWidth'<\/span>,3)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2012\/point_on_line_vis_01.png\" alt=\"\"> <p>How do we interpret this? First, I want to emphasize that this matrix represents the results for $p'$ that are all <i>extremely<\/i> close to $(0.5,0.5)$. The lower left corner corresponds exactly to the $(0.5,0.5)$, while the upper right corner corresponds to approximately $(0.500000000000028,0.500000000000028)$. Ideally, only the results lying along the black line <i>should<\/i> be yellow; these are the locations where $p'$, $q$, and $r$ are collinear. Our floating-point computation method is showing many other locations in yellow. Notice also that a few pairs of locations $p'$ are actually being computed <i>reversed<\/i>. A left turn has been determined to be a right turn, and vice versa.<\/p><p>In the comment thread on Loren's collinearity post, Tim Davis <a href=\"https:\/\/blogs.mathworks.com\/loren\/2008\/06\/06\/collinearity\/#comment-29478\">suggested<\/a> using a rank test to determine collinearity. It looks something like this:<\/p><pre class=\"language-matlab\"><span class=\"keyword\">function<\/span> out = rankCollinear(p,q,r)\r\nout = rank([p-q ; q-r]) &lt; 2;\r\n<\/pre><p>Let's see how this test does on our collection of points.<\/p><pre class=\"codeinput\"><span class=\"keyword\">for<\/span> u = 1:256\r\n    <span class=\"keyword\">for<\/span> v = 1:256\r\n        out(u,v) = rankCollinear([Px(u,v), Py(u,v)], q, r);\r\n    <span class=\"keyword\">end<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n\r\nimage(out + 1);\r\naxis <span class=\"string\">equal<\/span>;\r\nset(gca,<span class=\"string\">'ydir'<\/span>,<span class=\"string\">'normal'<\/span>)\r\naxis <span class=\"string\">off<\/span>\r\ncolormap([0 0 0; 1 1 0])\r\nhold <span class=\"string\">on<\/span>\r\nplot([.5 256.5],[.5 256.6],<span class=\"string\">'k'<\/span>,<span class=\"string\">'LineWidth'<\/span>,3)\r\nhold <span class=\"string\">off<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/steve\/2012\/point_on_line_vis_02.png\" alt=\"\"> <p>We only see yellow pixels, not red and blue, because <tt>rankCollinear<\/tt> only checks for collinearity, not the left or right turning of noncollinear points. The width of the section of yellow pixels depends on the numerical tolerance used by the <tt>rank<\/tt> function.<\/p><p>I am fascinated by this particular experiment and visualization. It reminds me once again that seemingly straightforward geometric ideas can be devilishly difficult to implement with a computer program.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_0309c8857b5842fea3c09bb001d408a5() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='0309c8857b5842fea3c09bb001d408a5 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 0309c8857b5842fea3c09bb001d408a5';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2012 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_0309c8857b5842fea3c09bb001d408a5()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; 7.14<br><\/p><p class=\"footer\"><br>\r\n      Published with MATLAB&reg; 7.14<br><\/p><\/div><!--\r\n0309c8857b5842fea3c09bb001d408a5 ##### SOURCE BEGIN #####\r\n%%\r\n% Today's post is about this picture, which is from the paper \"Classroom\r\n% Examples of Robustness Problems in Geometric Computations,\" by L.\r\n% Kettner, K. Mehlhorn, S. Pion, S. Schirra, and C. Yap, _Computational\r\n% Geometry_, vol. 40, n. 1, May 2008, pp. 61-78.\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/images\/steve\/2012\/robustness-problems-fig.png>>\r\n%\r\n% Yes, I know, I was supposed to do another follow-up on\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/cody\/problems\/820-eliminate-unnecessary-polygon-vertices\r\n% my Cody problem of eliminating unnecessary polygon vertices>. But I\r\n% really am following up on the Cody problem, if a bit indirectly.\r\n%\r\n% I was looking at\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/cody\/problems\/820-eliminate-unnecessary-polygon-vertices\/solutions\/108897\r\n% solution 108897> by\r\n% <https:\/\/www.mathworks.com\/matlabcentral\/cody\/players\/210779-cris-luengo\r\n% Cris>, and I paused over these lines:\r\n%\r\n%   v1 = p2-p1;\r\n%   v2 = p3-p2;\r\n%   if ( v1(1)*v2(2) == v1(2)*v2(1) ) && any(v1.*v2>0)\r\n%\r\n% I recognized the expression |( v1(1)*v2(2) == v1(2)*v2(1) )| as a way to\r\n% see if the point |p2| is on the line segment formed by |p1| and |p3|. It\r\n% reminded me of Loren's\r\n% <https:\/\/blogs.mathworks.com\/loren\/2008\/06\/06\/collinearity\/ 06-Jun-2008\r\n% blog post> on methods for determining collinearity, and it also reminded\r\n% me of the paper above.\r\n%\r\n% I decided to see if I could reproduce the figure in MATLAB.\r\n%\r\n% The figure concerns the behavior of floating-point methods for computing\r\n% the _planar orientation predicate_. Given three points $p$, $q$, and $r$\r\n% in a plane, does the point $r$ lie to the left of the line through $p$\r\n% and $q$, to the right of that line, or on the line? One way to compute\r\n% this is via the equation\r\n%\r\n% $$O(p,q,r) = \\mbox{sign}((q_x - p_x)(r_y - p_y) - (q_y - p_y)(r_x - p_x))$$\r\n%\r\n% The figure in the paper represents an experiment using $q=(12,12)$,\r\n% $r=(24,24)$, and $256^2$ different locations for the point $p$, all of\r\n% which are _extremely_ close to the point $p=(0.5,0.5)$. More\r\n% specifically, the figure is a visualization of $O((p_x+xu, p_y+yu),q,r)$\r\n% for $0 \\leq x,y \\leq 255$ and $u=2^{-53}$. This value of $u$ is the\r\n% increment between adjacent double-precision floating-point numbers in the\r\n% interval $0.5 < x \\leq 1$.\r\n%\r\n% Here's a straightforward implementation of the orientation equation\r\n% above.\r\n%\r\n%   function out = floatOrient(p,q,r)\r\n%   \r\n%   out = sign((q(1) - p(1))*(r(2) - p(2)) - ...\r\n%       (q(2) - p(2))*(r(1) - p(1)));\r\n%\r\n% Let's see what we get when we compute the orientation for all those\r\n% different point locations $p'$ that are extremely close to $(0.5,0.5)$.\r\n\r\n[m,n] = meshgrid(0:255);\r\nPx = 0.5 + n*eps(0.5);\r\nPy = 0.5 + m*eps(0.5);\r\n\r\nq = [12 12];\r\nr = [24 24];\r\n\r\nout = zeros(size(Px));\r\nfor u = 1:256\r\n    for v = 1:256\r\n        out(u,v) = floatOrient([Px(u,v), Py(u,v)], q, r);\r\n    end\r\nend\r\n\r\n%%\r\n% (Yes, I know ... |floatOrient| and the calling code above can all be\r\n% vectorized.)\r\n%\r\n% I'll show in yellow the locations $p'$ where $r$ has been determined to\r\n% be on the line containing $p'$ and $q$. I'll use blue and red to show\r\n% where $r$ has been determined to lie on one side or the other of the\r\n% line.\r\n\r\nimage(out + 2); \r\naxis equal; \r\nset(gca,'ydir','normal')\r\naxis off\r\ncolormap([0 0 1; 1 1 0; 1 0 0])\r\nhold on\r\nplot([.5 256.5],[.5 256.6],'k','LineWidth',3)\r\nhold off\r\n\r\n%%\r\n% How do we interpret this? First, I want to emphasize that this matrix\r\n% represents the results for $p'$ that are all _extremely_ close to\r\n% $(0.5,0.5)$. The lower left corner corresponds exactly to the\r\n% $(0.5,0.5)$, while the upper right corner corresponds to approximately\r\n% $(0.500000000000028,0.500000000000028)$. Ideally, only the results lying\r\n% along the black line _should_ be yellow; these are the locations where\r\n% $p'$, $q$, and $r$ are collinear. Our floating-point computation method\r\n% is showing many other locations in yellow. Notice also that a few pairs\r\n% of locations $p'$ are actually being computed _reversed_. A left turn has\r\n% been determined to be a right turn, and vice versa.\r\n\r\n%%\r\n% In the comment thread on Loren's collinearity post, Tim Davis\r\n% <https:\/\/blogs.mathworks.com\/loren\/2008\/06\/06\/collinearity\/#comment-29478\r\n% suggested> using a rank test to determine collinearity. It looks\r\n% something like this:\r\n%\r\n%   function out = rankCollinear(p,q,r)\r\n%   out = rank([p-q ; q-r]) < 2;\r\n%\r\n% Let's see how this test does on our collection of points.\r\n\r\nfor u = 1:256\r\n    for v = 1:256\r\n        out(u,v) = rankCollinear([Px(u,v), Py(u,v)], q, r);\r\n    end\r\nend\r\n\r\nimage(out + 1); \r\naxis equal; \r\nset(gca,'ydir','normal')\r\naxis off\r\ncolormap([0 0 0; 1 1 0])\r\nhold on\r\nplot([.5 256.5],[.5 256.6],'k','LineWidth',3)\r\nhold off\r\n\r\n%%\r\n% We only see yellow pixels, not red and blue, because |rankCollinear| only\r\n% checks for collinearity, not the left or right turning of noncollinear\r\n% points. The width of the section of yellow pixels depends on the\r\n% numerical tolerance used by the |rank| function.\r\n%\r\n% I am fascinated by this particular experiment and visualization. It\r\n% reminds me once again that seemingly straightforward geometric ideas can\r\n% be devilishly difficult to implement with a computer program.\r\n% \r\n##### SOURCE END ##### 0309c8857b5842fea3c09bb001d408a5\r\n-->","protected":false},"excerpt":{"rendered":"<p>Today's post is about this picture, which is from the paper \"Classroom Examples of Robustness Problems in Geometric Computations,\" by L. Kettner, K. Mehlhorn, S. Pion, S. Schirra, and C. Yap,... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2012\/08\/22\/visualizing-the-floating-point-behavior-of-the-point-on-line-test\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[907,50,58,286,80,90,48,30,68,290,909,190,130],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/652"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/comments?post=652"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/652\/revisions"}],"predecessor-version":[{"id":3795,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/652\/revisions\/3795"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=652"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=652"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=652"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}