@@ -137,6 +137,11 @@ double XYZ::dot(const XYZ& other) const
137137 return x*other.x + y*other.y + z*other.z ;
138138}
139139
140+ double XYZ::length_squared () const
141+ {
142+ return x*x + y*y + z*z;
143+ }
144+
140145XYZ XYZ::operator -(const XYZ& other) const
141146{
142147 return XYZ (x - other.x , y - other.y , z - other.z );
@@ -375,6 +380,95 @@ void Triangulation::calculate_neighbors()
375380 // boundary edges, but the boundaries are calculated separately elsewhere.
376381}
377382
383+ Py::Object Triangulation::calculate_plane_coefficients (const Py::Tuple &args)
384+ {
385+ _VERBOSE (" Triangulation::calculate_plane_coefficients" );
386+ args.verify_length (1 );
387+
388+ PyArrayObject* z = (PyArrayObject*)PyArray_ContiguousFromObject (
389+ args[0 ].ptr (), PyArray_DOUBLE, 1 , 1 );
390+ if (z == 0 || PyArray_DIM (z,0 ) != PyArray_DIM (_x,0 )) {
391+ Py_XDECREF (z);
392+ throw Py::ValueError (
393+ " z array must have same length as triangulation x and y arrays" );
394+ }
395+ const double * zs = (const double *)PyArray_DATA (z);
396+
397+ npy_intp dims[2 ] = {_ntri, 3 };
398+ PyArrayObject* planes_array = (PyArrayObject*)PyArray_SimpleNew (
399+ 2 , dims, PyArray_DOUBLE);
400+ double * planes = (double *)PyArray_DATA (planes_array);
401+ const int * tris = get_triangles_ptr ();
402+ const double * xs = (const double *)PyArray_DATA (_x);
403+ const double * ys = (const double *)PyArray_DATA (_y);
404+ for (int tri = 0 ; tri < _ntri; ++tri)
405+ {
406+ if (is_masked (tri))
407+ {
408+ *planes++ = 0.0 ;
409+ *planes++ = 0.0 ;
410+ *planes++ = 0.0 ;
411+ tris += 3 ;
412+ }
413+ else
414+ {
415+ // Equation of plane for all points r on plane is r.normal = p
416+ // where normal is vector normal to the plane, and p is a constant.
417+ // Rewrite as r_x*normal_x + r_y*normal_y + r_z*normal_z = p
418+ // and rearrange to give
419+ // r_z = (-normal_x/normal_z)*r_x + (-normal_y/normal_z)*r_y +
420+ // p/normal_z
421+ XYZ point0 (xs[*tris], ys[*tris], zs[*tris]);
422+ tris++;
423+ XYZ point1 (xs[*tris], ys[*tris], zs[*tris]);
424+ tris++;
425+ XYZ point2 (xs[*tris], ys[*tris], zs[*tris]);
426+ tris++;
427+
428+ XYZ normal = (point1 - point0).cross (point2 - point0);
429+
430+ if (normal.z == 0.0 )
431+ {
432+ // Normal is in x-y plane which means triangle consists of
433+ // colinear points. Try to do the best we can by taking plane
434+ // through longest side of triangle.
435+ double length_sqr_01 = (point1 - point0).length_squared ();
436+ double length_sqr_12 = (point2 - point1).length_squared ();
437+ double length_sqr_20 = (point0 - point2).length_squared ();
438+ if (length_sqr_01 > length_sqr_12)
439+ {
440+ if (length_sqr_01 > length_sqr_20)
441+ normal = normal.cross (point1 - point0);
442+ else
443+ normal = normal.cross (point0 - point2);
444+ }
445+ else
446+ {
447+ if (length_sqr_12 > length_sqr_20)
448+ normal = normal.cross (point2 - point1);
449+ else
450+ normal = normal.cross (point0 - point2);
451+ }
452+
453+ if (normal.z == 0.0 )
454+ {
455+ // The 3 triangle points have identical x and y! The best
456+ // we can do here is take normal = (0,0,1) and for the
457+ // constant p take the mean of the 3 points' z-values.
458+ normal = XYZ (0.0 , 0.0 , 1.0 );
459+ point0.z = (point0.z + point1.z + point2.z ) / 3.0 ;
460+ }
461+ }
462+
463+ *planes++ = -normal.x / normal.z ; // x
464+ *planes++ = -normal.y / normal.z ; // y
465+ *planes++ = normal.dot (point0) / normal.z ; // constant
466+ }
467+ }
468+
469+ return Py::asObject ((PyObject*)planes_array);
470+ }
471+
378472void Triangulation::correct_triangles ()
379473{
380474 int * triangles_ptr = (int *)PyArray_DATA (_triangles);
@@ -506,6 +600,9 @@ void Triangulation::init_type()
506600 behaviors ().name (" Triangulation" );
507601 behaviors ().doc (" Triangulation" );
508602
603+ add_varargs_method (" calculate_plane_coefficients" ,
604+ &Triangulation::calculate_plane_coefficients,
605+ " calculate_plane_coefficients(z)" );
509606 add_noargs_method (" get_edges" , &Triangulation::get_edges,
510607 " get_edges()" );
511608 add_noargs_method (" get_neighbors" , &Triangulation::get_neighbors,
0 commit comments