# HG changeset patch
# User Eddy Xiao <bewantbe@gmail.com>
# Date 1552821210 -28800
#      Sun Mar 17 19:13:30 2019 +0800
# Node ID 95136a29818d274cb4773d818945d77325b8d0c9
# Parent  db59dabf1685822d691ab5abfff99b5c9a995e29
Properly handle NaN vertex in coplanar test for patch(). (Bug #55895)

* graphics.cc (patch::properties::update_data, coplanar_partition): Skip the coplanar test when any of the face corner is NaN or Inf, because it is not a face and the coplanar test result for it will not be used.
* graphics.cc (coplanar_partition): New function for coplanar test and partition. Was moved out from patch::properties::update_data.
* gl-render.cc (opengl_renderer::draw_patch): Correct the movement of iterator for coplanar_last_idx, so that coplanar and non-coplanar face groups are processed in order.

diff -r db59dabf1685 -r 95136a29818d libinterp/corefcn/gl-render.cc
--- a/libinterp/corefcn/gl-render.cc	Thu Mar 14 06:09:29 2019 +0000
+++ b/libinterp/corefcn/gl-render.cc	Sun Mar 17 19:13:30 2019 +0800
@@ -3371,8 +3371,11 @@
                 if (clip_f(i))
                   continue;
 
+                bool tested_coplanarity = props.coplanar_last_idx.size () > 0
+                                          && ! octave::math::isnan (f(i,3)) 
+                                          && ! clip_f(i);
                 bool is_non_planar = false;
-                if (props.coplanar_last_idx.size () > 0 && (*it1).size () > 1)
+                if (tested_coplanarity && (*it1).size () > 1)
                   {
                     is_non_planar = true;
                     it2 = (*it1).end ();
@@ -3452,7 +3455,7 @@
                     tess.end_polygon ();
                   } while (i_start > 0);
 
-                if (is_non_planar)
+                if (tested_coplanarity)
                   it1++;
               }
 
@@ -3512,8 +3515,11 @@
 
             for (int i = 0; i < nf; i++)
               {
+                bool tested_coplanarity = props.coplanar_last_idx.size () > 0
+                                          && ! octave::math::isnan (f(i,3)) 
+                                          && ! clip_f(i);
                 bool is_non_planar = false;
-                if (props.coplanar_last_idx.size () > 0 && (*it1).size () > 1)
+                if (tested_coplanarity && (*it1).size () > 1)
                   is_non_planar = true;
                 if (clip_f(i) || is_non_planar)
                   {
@@ -3588,7 +3594,7 @@
                     tess.end_contour ();
                     tess.end_polygon ();
                   }
-                if (is_non_planar)
+                if (tested_coplanarity)
                   it1++;
               }
 
diff -r db59dabf1685 -r 95136a29818d libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc	Thu Mar 14 06:09:29 2019 +0000
+++ b/libinterp/corefcn/graphics.cc	Sun Mar 17 19:13:30 2019 +0800
@@ -9444,6 +9444,92 @@
   return ev.min () <= tol * ev.max ();
 }
 
+std::list<octave_idx_type>
+coplanar_partition (const Matrix &vert, const Matrix &idx,
+                    octave_idx_type nc, octave_idx_type jj)
+{
+  std::list<octave_idx_type> coplanar_ends;
+  
+  Matrix plane_pivot = Matrix (1, 3, 0.0);
+  for (octave_idx_type i = 0; i < 3; i++)
+    plane_pivot(0,i) = vert(idx(0,jj)-1,i);
+
+  Matrix fc = Matrix (0, 3, 0.0);  // face corner vertex coordinates
+  Matrix fa = Matrix (1, 3, 0.0);  // for append face corner
+  Matrix coor_cov = Matrix (3, 3, 0.0);
+
+  if (nc >= 5)
+    {
+      // Coplanar test that involves all points.
+      // For nc == 4, this initial test is not beneficial at all.
+      // If the probability of coplanar input is more than half, for
+      // the best average performance, we should use nc >= 5.
+      // Higher threshold is meaningful only when input is known to be
+      // non-coplanar and nc is small.
+
+      fc.resize (nc - 1, 3);
+      for (octave_idx_type j = 1; j < nc; j++)
+        for (octave_idx_type i = 0; i < 3; i++)
+          fc(j-1,i) = vert(idx(j,jj)-1,i) - plane_pivot(i);
+
+      coor_cov = fc.transpose () * fc;
+      if (is_coplanar (coor_cov))
+        {
+          coplanar_ends.push_back (nc - 1);
+          return coplanar_ends;
+        }
+    }
+
+  fc.resize (3, 3);
+  octave_idx_type i_start = 1;
+  octave_idx_type i_end = 2;
+
+  // Split the polygon into coplanar segments.
+  // The first point is common corner of all planes.
+  while (i_start < nc - 1)
+    {
+      i_end = i_start + 2;
+      if (i_end > nc - 1)
+        {
+          coplanar_ends.push_back (nc - 1);
+          break;
+        }
+
+      // Algorithm: Start from 3 points, keep adding points until the
+      // point set is no more in a plane. Record the coplanar point
+      // set, then advance i_start.
+
+      // Prepare 1+3 points for coplanar test.
+      // The first point is implicitly included.
+      for (octave_idx_type j = 0; j < 3; j++)
+        for (octave_idx_type i = 0; i < 3; i++)
+          fc(j,i) = vert(idx(j+i_start,jj)-1,i) - plane_pivot(i);
+
+      // covariance matrix between coordinates of vertex
+      coor_cov = fc.transpose () * fc;
+
+      while (true)
+        {
+          // coplanar test
+          if (! is_coplanar (coor_cov))
+            break;
+
+          i_end++;
+          if (i_end > nc - 1)
+            break;
+
+          // add a point to plane
+          for (octave_idx_type i = 0; i < 3; i++)
+            fa(0,i) = vert(idx(i_end,jj)-1,i) - plane_pivot(i);
+          coor_cov += fa.transpose () * fa;
+        }
+
+      i_start = i_end - 1;
+      coplanar_ends.push_back (i_start);
+    }
+  return coplanar_ends;
+}
+
 void
 patch::properties::update_data (void)
 {
@@ -9500,87 +9586,24 @@
           while (nc < fcmax && ! octave::math::isnan (idx(nc,jj)))
             nc++;
 
-          std::list<octave_idx_type> coplanar_ends;
-
-          Matrix plane_pivot = Matrix (1, 3, 0.0);
-          for (octave_idx_type i = 0; i < 3; i++)
-            plane_pivot(0,i) = vert(idx(0,jj)-1,i);
-
-          Matrix fc = Matrix (0, 3, 0.0);  // face corner vertex coordinates
-          Matrix fa = Matrix (1, 3, 0.0);  // for append face corner
-          Matrix coor_cov = Matrix (3, 3, 0.0);
-
-          if (nc >= 5)
-            {
-              // Coplanar test that involves all points.
-              // For nc == 4, this initial test is not beneficial at all.
-              // If the probability of coplanar input is more than half, for
-              // the best average performance, we should use nc >= 5.
-              // Higher threshold is meaningful only when input is known to be
-              // non-coplanar and nc is small.
-
-              fc.resize (nc - 1, 3);
-              for (octave_idx_type j = 1; j < nc; j++)
-                for (octave_idx_type i = 0; i < 3; i++)
-                  fc(j-1,i) = vert(idx(j,jj)-1,i) - plane_pivot(i);
-
-              coor_cov = fc.transpose () * fc;
-              if (is_coplanar (coor_cov))
+          // If any of the corner is NaN or Inf, skip coplanar test.
+          // Because it is an unclosed contour, not a face.
+          bool is_face = true;
+          for (octave_idx_type j = 0; j < nc; j++)
+            {
+              const octave_idx_type k = octave_idx_type (idx(j, jj) - 1);
+              if (! (octave::math::isfinite (vert(k, 0))
+                  && octave::math::isfinite (vert(k, 1))
+                  && octave::math::isfinite (vert(k, 2))))
                 {
-                  coplanar_ends.push_back (nc - 1);
-                  coplanar_last_idx.push_back (coplanar_ends);
-                  continue;
-                }
-            }
-
-          fc.resize (3, 3);
-          octave_idx_type i_start = 1;
-          octave_idx_type i_end = 2;
-
-          // Split the polygon into coplanar segments.
-          // The first point is common corner of all planes.
-          while (i_start < nc - 1)
-            {
-              i_end = i_start + 2;
-              if (i_end > nc - 1)
-                {
-                  coplanar_ends.push_back (nc - 1);
+                  is_face = false;
                   break;
                 }
-
-              // Algorithm: Start from 3 points, keep adding points until the
-              // point set is no more in a plane. Record the coplanar point
-              // set, then advance i_start.
-
-              // Prepare 1+3 points for coplanar test.
-              // The first point is implicitly included.
-              for (octave_idx_type j = 0; j < 3; j++)
-                for (octave_idx_type i = 0; i < 3; i++)
-                  fc(j,i) = vert(idx(j+i_start,jj)-1,i) - plane_pivot(i);
-
-              // covariance matrix between coordinates of vertex
-              coor_cov = fc.transpose () * fc;
-
-              while (true)
-                {
-                  // coplanar test
-                  if (! is_coplanar (coor_cov))
-                    break;
-
-                  i_end++;
-                  if (i_end > nc - 1)
-                    break;
-
-                  // add a point to plane
-                  for (octave_idx_type i = 0; i < 3; i++)
-                    fa(0,i) = vert(idx(i_end,jj)-1,i) - plane_pivot(i);
-                  coor_cov += fa.transpose () * fa;
-                }
-
-              i_start = i_end - 1;
-              coplanar_ends.push_back (i_start);
-            }
-          coplanar_last_idx.push_back (coplanar_ends);
+            }
+          if (! is_face)
+            continue;
+          
+          coplanar_last_idx.push_back (coplanar_partition (vert, idx, nc, jj));
         }
     }
 
