1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //  This software is distributed WITHOUT ANY WARRANTY; without even
6 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
7 //  PURPOSE.  See the above copyright notice for more information.
8 //
9 //  Copyright 2017 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
10 //  Copyright 2017 UT-Battelle, LLC.
11 //  Copyright 2017 Los Alamos National Security.
12 //
13 //  Under the terms of Contract DE-NA0003525 with NTESS,
14 //  the U.S. Government retains certain rights in this software.
15 //
16 //  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
17 //  Laboratory (LANL), the U.S. Government retains certain rights in
18 //  this software.
19 //============================================================================
20 #ifndef vtk_m_ImplicitFunction_h
21 #define vtk_m_ImplicitFunction_h
22 
23 #include <vtkm/Math.h>
24 #include <vtkm/VectorAnalysis.h>
25 #include <vtkm/VirtualObjectBase.h>
26 
27 namespace vtkm
28 {
29 
30 //============================================================================
31 class VTKM_ALWAYS_EXPORT ImplicitFunction : public vtkm::VirtualObjectBase
32 {
33 public:
34   using Scalar = vtkm::FloatDefault;
35   using Vector = vtkm::Vec<Scalar, 3>;
36 
37   VTKM_EXEC_CONT virtual Scalar Value(const Vector& point) const = 0;
38   VTKM_EXEC_CONT virtual Vector Gradient(const Vector& point) const = 0;
39 
Value(Scalar x,Scalar y,Scalar z)40   VTKM_EXEC_CONT Scalar Value(Scalar x, Scalar y, Scalar z) const
41   {
42     return this->Value(Vector(x, y, z));
43   }
44 
Gradient(Scalar x,Scalar y,Scalar z)45   VTKM_EXEC_CONT Vector Gradient(Scalar x, Scalar y, Scalar z) const
46   {
47     return this->Gradient(Vector(x, y, z));
48   }
49 };
50 
51 //============================================================================
52 /// A helpful functor that calls the (virtual) value method of a given ImplicitFunction. Can be
53 /// passed to things that expect a functor instead of an ImplictFunction class (like an array
54 /// transform).
55 ///
56 class VTKM_ALWAYS_EXPORT ImplicitFunctionValue
57 {
58 public:
59   using Scalar = vtkm::ImplicitFunction::Scalar;
60   using Vector = vtkm::ImplicitFunction::Vector;
61 
ImplicitFunctionValue()62   VTKM_EXEC_CONT ImplicitFunctionValue()
63     : Function(nullptr)
64   {
65   }
66 
ImplicitFunctionValue(const ImplicitFunction * function)67   VTKM_EXEC_CONT ImplicitFunctionValue(const ImplicitFunction* function)
68     : Function(function)
69   {
70   }
71 
operator()72   VTKM_EXEC_CONT Scalar operator()(const Vector& point) const
73   {
74     return this->Function->Value(point);
75   }
76 
77 private:
78   const vtkm::ImplicitFunction* Function;
79 };
80 
81 /// A helpful functor that calls the (virtual) gradient method of a given ImplicitFunction. Can be
82 /// passed to things that expect a functor instead of an ImplictFunction class (like an array
83 /// transform).
84 ///
85 class VTKM_ALWAYS_EXPORT ImplicitFunctionGradient
86 {
87 public:
88   using Scalar = vtkm::ImplicitFunction::Scalar;
89   using Vector = vtkm::ImplicitFunction::Vector;
90 
ImplicitFunctionGradient()91   VTKM_EXEC_CONT ImplicitFunctionGradient()
92     : Function(nullptr)
93   {
94   }
95 
ImplicitFunctionGradient(const ImplicitFunction * function)96   VTKM_EXEC_CONT ImplicitFunctionGradient(const ImplicitFunction* function)
97     : Function(function)
98   {
99   }
100 
operator()101   VTKM_EXEC_CONT Vector operator()(const Vector& point) const
102   {
103     return this->Function->Gradient(point);
104   }
105 
106 private:
107   const vtkm::ImplicitFunction* Function;
108 };
109 
110 //============================================================================
111 /// \brief Implicit function for a box
112 class VTKM_ALWAYS_EXPORT Box : public ImplicitFunction
113 {
114 public:
Box()115   VTKM_EXEC_CONT Box()
116     : MinPoint(Vector(Scalar(0)))
117     , MaxPoint(Vector(Scalar(0)))
118   {
119   }
120 
Box(const Vector & minPoint,const Vector & maxPoint)121   VTKM_EXEC_CONT Box(const Vector& minPoint, const Vector& maxPoint)
122     : MinPoint(minPoint)
123     , MaxPoint(maxPoint)
124   {
125   }
126 
Box(Scalar xmin,Scalar xmax,Scalar ymin,Scalar ymax,Scalar zmin,Scalar zmax)127   VTKM_EXEC_CONT Box(Scalar xmin, Scalar xmax, Scalar ymin, Scalar ymax, Scalar zmin, Scalar zmax)
128     : MinPoint(xmin, ymin, zmin)
129     , MaxPoint(xmax, ymax, zmax)
130   {
131   }
132 
SetMinPoint(const Vector & point)133   VTKM_CONT void SetMinPoint(const Vector& point)
134   {
135     this->MinPoint = point;
136     this->Modified();
137   }
138 
SetMaxPoint(const Vector & point)139   VTKM_CONT void SetMaxPoint(const Vector& point)
140   {
141     this->MaxPoint = point;
142     this->Modified();
143   }
144 
GetMinPoint()145   VTKM_EXEC_CONT const Vector& GetMinPoint() const { return this->MinPoint; }
146 
GetMaxPoint()147   VTKM_EXEC_CONT const Vector& GetMaxPoint() const { return this->MaxPoint; }
148 
Value(const Vector & point)149   VTKM_EXEC_CONT Scalar Value(const Vector& point) const final
150   {
151     Scalar minDistance = vtkm::NegativeInfinity32();
152     Scalar diff, t, dist;
153     Scalar distance = Scalar(0.0);
154     vtkm::IdComponent inside = 1;
155 
156     for (vtkm::IdComponent d = 0; d < 3; d++)
157     {
158       diff = this->MaxPoint[d] - this->MinPoint[d];
159       if (diff != Scalar(0.0))
160       {
161         t = (point[d] - this->MinPoint[d]) / diff;
162         // Outside before the box
163         if (t < Scalar(0.0))
164         {
165           inside = 0;
166           dist = this->MinPoint[d] - point[d];
167         }
168         // Outside after the box
169         else if (t > Scalar(1.0))
170         {
171           inside = 0;
172           dist = point[d] - this->MaxPoint[d];
173         }
174         else
175         {
176           // Inside the box in lower half
177           if (t <= Scalar(0.5))
178           {
179             dist = MinPoint[d] - point[d];
180           }
181           // Inside the box in upper half
182           else
183           {
184             dist = point[d] - MaxPoint[d];
185           }
186           if (dist > minDistance)
187           {
188             minDistance = dist;
189           }
190         }
191       }
192       else
193       {
194         dist = vtkm::Abs(point[d] - MinPoint[d]);
195         if (dist > Scalar(0.0))
196         {
197           inside = 0;
198         }
199       }
200       if (dist > Scalar(0.0))
201       {
202         distance += dist * dist;
203       }
204     }
205 
206     distance = vtkm::Sqrt(distance);
207     if (inside)
208     {
209       return minDistance;
210     }
211     else
212     {
213       return distance;
214     }
215   }
216 
Gradient(const Vector & point)217   VTKM_EXEC_CONT Vector Gradient(const Vector& point) const final
218   {
219     vtkm::IdComponent minAxis = 0;
220     Scalar dist = 0.0;
221     Scalar minDist = vtkm::Infinity32();
222     vtkm::Vec<vtkm::IdComponent, 3> location;
223     Vector normal(Scalar(0));
224     Vector inside(Scalar(0));
225     Vector outside(Scalar(0));
226     Vector center((this->MaxPoint + this->MinPoint) * Scalar(0.5));
227 
228     // Compute the location of the point with respect to the box
229     // Point will lie in one of 27 separate regions around or within the box
230     // Gradient vector is computed differently in each of the regions.
231     for (vtkm::IdComponent d = 0; d < 3; d++)
232     {
233       if (point[d] < this->MinPoint[d])
234       {
235         // Outside the box low end
236         location[d] = 0;
237         outside[d] = -1.0;
238       }
239       else if (point[d] > this->MaxPoint[d])
240       {
241         // Outside the box high end
242         location[d] = 2;
243         outside[d] = 1.0;
244       }
245       else
246       {
247         location[d] = 1;
248         if (point[d] <= center[d])
249         {
250           // Inside the box low end
251           dist = point[d] - this->MinPoint[d];
252           inside[d] = -1.0;
253         }
254         else
255         {
256           // Inside the box high end
257           dist = this->MaxPoint[d] - point[d];
258           inside[d] = 1.0;
259         }
260         if (dist < minDist) // dist is negative
261         {
262           minDist = dist;
263           minAxis = d;
264         }
265       }
266     }
267 
268     vtkm::Id indx = location[0] + 3 * location[1] + 9 * location[2];
269     switch (indx)
270     {
271       // verts - gradient points away from center point
272       case 0:
273       case 2:
274       case 6:
275       case 8:
276       case 18:
277       case 20:
278       case 24:
279       case 26:
280         for (vtkm::IdComponent d = 0; d < 3; d++)
281         {
282           normal[d] = point[d] - center[d];
283         }
284         vtkm::Normalize(normal);
285         break;
286 
287       // edges - gradient points out from axis of cube
288       case 1:
289       case 3:
290       case 5:
291       case 7:
292       case 9:
293       case 11:
294       case 15:
295       case 17:
296       case 19:
297       case 21:
298       case 23:
299       case 25:
300         for (vtkm::IdComponent d = 0; d < 3; d++)
301         {
302           if (outside[d] != 0.0)
303           {
304             normal[d] = point[d] - center[d];
305           }
306           else
307           {
308             normal[d] = 0.0;
309           }
310         }
311         vtkm::Normalize(normal);
312         break;
313 
314       // faces - gradient points perpendicular to face
315       case 4:
316       case 10:
317       case 12:
318       case 14:
319       case 16:
320       case 22:
321         for (vtkm::IdComponent d = 0; d < 3; d++)
322         {
323           normal[d] = outside[d];
324         }
325         break;
326 
327       // interior - gradient is perpendicular to closest face
328       case 13:
329         normal[0] = normal[1] = normal[2] = 0.0;
330         normal[minAxis] = inside[minAxis];
331         break;
332       default:
333         VTKM_ASSERT(false);
334         break;
335     }
336     return normal;
337   }
338 
339 private:
340   Vector MinPoint;
341   Vector MaxPoint;
342 };
343 
344 //============================================================================
345 /// \brief Implicit function for a cylinder
346 class VTKM_ALWAYS_EXPORT Cylinder final : public vtkm::ImplicitFunction
347 {
348 public:
Cylinder()349   VTKM_EXEC_CONT Cylinder()
350     : Center(Scalar(0))
351     , Axis(Scalar(1), Scalar(0), Scalar(0))
352     , Radius(Scalar(0.2))
353   {
354   }
355 
Cylinder(const Vector & axis,Scalar radius)356   VTKM_EXEC_CONT Cylinder(const Vector& axis, Scalar radius)
357     : Center(Scalar(0))
358     , Axis(axis)
359     , Radius(radius)
360   {
361   }
362 
Cylinder(const Vector & center,const Vector & axis,Scalar radius)363   VTKM_EXEC_CONT Cylinder(const Vector& center, const Vector& axis, Scalar radius)
364     : Center(center)
365     , Axis(axis)
366     , Radius(radius)
367   {
368   }
369 
SetCenter(const Vector & center)370   VTKM_CONT void SetCenter(const Vector& center)
371   {
372     this->Center = center;
373     this->Modified();
374   }
375 
SetAxis(const Vector & axis)376   VTKM_CONT void SetAxis(const Vector& axis)
377   {
378     this->Axis = axis;
379     this->Modified();
380   }
381 
SetRadius(Scalar radius)382   VTKM_CONT void SetRadius(Scalar radius)
383   {
384     this->Radius = radius;
385     this->Modified();
386   }
387 
Value(const Vector & point)388   VTKM_EXEC_CONT Scalar Value(const Vector& point) const final
389   {
390     Vector x2c = point - this->Center;
391     FloatDefault proj = vtkm::Dot(this->Axis, x2c);
392     return vtkm::Dot(x2c, x2c) - (proj * proj) - (this->Radius * this->Radius);
393   }
394 
Gradient(const Vector & point)395   VTKM_EXEC_CONT Vector Gradient(const Vector& point) const final
396   {
397     Vector x2c = point - this->Center;
398     FloatDefault t = this->Axis[0] * x2c[0] + this->Axis[1] * x2c[1] + this->Axis[2] * x2c[2];
399     vtkm::Vec<FloatDefault, 3> closestPoint = this->Center + (this->Axis * t);
400     return (point - closestPoint) * FloatDefault(2);
401   }
402 
403 
404 private:
405   Vector Center;
406   Vector Axis;
407   Scalar Radius;
408 };
409 
410 //============================================================================
411 /// \brief Implicit function for a frustum
412 class VTKM_ALWAYS_EXPORT Frustum final : public vtkm::ImplicitFunction
413 {
414 public:
415   Frustum() = default;
416 
Frustum(const Vector points[6],const Vector normals[6])417   VTKM_EXEC_CONT Frustum(const Vector points[6], const Vector normals[6])
418   {
419     this->SetPlanes(points, normals);
420   }
421 
Frustum(const Vector points[8])422   VTKM_EXEC_CONT explicit Frustum(const Vector points[8]) { this->CreateFromPoints(points); }
423 
SetPlanes(const Vector points[6],const Vector normals[6])424   VTKM_EXEC void SetPlanes(const Vector points[6], const Vector normals[6])
425   {
426     for (vtkm::Id index : { 0, 1, 2, 3, 4, 5 })
427     {
428       this->Points[index] = points[index];
429     }
430     for (vtkm::Id index : { 0, 1, 2, 3, 4, 5 })
431     {
432       this->Normals[index] = normals[index];
433     }
434     this->Modified();
435   }
436 
SetPlane(int idx,const Vector & point,const Vector & normal)437   VTKM_EXEC void SetPlane(int idx, const Vector& point, const Vector& normal)
438   {
439     VTKM_ASSERT((idx >= 0) && (idx < 6));
440     this->Points[idx] = point;
441     this->Normals[idx] = normal;
442     this->Modified();
443   }
444 
GetPlanes(Vector points[6],Vector normals[6])445   VTKM_EXEC_CONT void GetPlanes(Vector points[6], Vector normals[6]) const
446   {
447     for (vtkm::Id index : { 0, 1, 2, 3, 4, 5 })
448     {
449       points[index] = this->Points[index];
450     }
451     for (vtkm::Id index : { 0, 1, 2, 3, 4, 5 })
452     {
453       normals[index] = this->Normals[index];
454     }
455   }
456 
GetPoints()457   VTKM_EXEC_CONT const Vector* GetPoints() const { return this->Points; }
458 
GetNormals()459   VTKM_EXEC_CONT const Vector* GetNormals() const { return this->Normals; }
460 
461   // The points should be specified in the order of hex-cell vertices
CreateFromPoints(const Vector points[8])462   VTKM_EXEC_CONT void CreateFromPoints(const Vector points[8])
463   {
464     // XXX(clang-format-3.9): 3.8 is silly. 3.9 makes it look like this.
465     // clang-format off
466     int planes[6][3] = {
467       { 3, 2, 0 }, { 4, 5, 7 }, { 0, 1, 4 }, { 1, 2, 5 }, { 2, 3, 6 }, { 3, 0, 7 }
468     };
469     // clang-format on
470 
471     for (int i = 0; i < 6; ++i)
472     {
473       const Vector& v0 = points[planes[i][0]];
474       const Vector& v1 = points[planes[i][1]];
475       const Vector& v2 = points[planes[i][2]];
476 
477       this->Points[i] = v0;
478       this->Normals[i] = vtkm::Normal(vtkm::Cross(v2 - v0, v1 - v0));
479     }
480     this->Modified();
481   }
482 
Value(const Vector & point)483   VTKM_EXEC_CONT Scalar Value(const Vector& point) const final
484   {
485     Scalar maxVal = vtkm::NegativeInfinity<Scalar>();
486     for (vtkm::Id index : { 0, 1, 2, 3, 4, 5 })
487     {
488       const Vector& p = this->Points[index];
489       const Vector& n = this->Normals[index];
490       const Scalar val = vtkm::Dot(point - p, n);
491       maxVal = vtkm::Max(maxVal, val);
492     }
493     return maxVal;
494   }
495 
Gradient(const Vector & point)496   VTKM_EXEC_CONT Vector Gradient(const Vector& point) const final
497   {
498     Scalar maxVal = vtkm::NegativeInfinity<Scalar>();
499     vtkm::Id maxValIdx = 0;
500     for (vtkm::Id index : { 0, 1, 2, 3, 4, 5 })
501     {
502       const Vector& p = this->Points[index];
503       const Vector& n = this->Normals[index];
504       Scalar val = vtkm::Dot(point - p, n);
505       if (val > maxVal)
506       {
507         maxVal = val;
508         maxValIdx = index;
509       }
510     }
511     return this->Normals[maxValIdx];
512   }
513 
514 private:
515   Vector Points[6];
516   Vector Normals[6];
517 };
518 
519 //============================================================================
520 /// \brief Implicit function for a plane
521 class VTKM_ALWAYS_EXPORT Plane final : public vtkm::ImplicitFunction
522 {
523 public:
Plane()524   VTKM_EXEC_CONT Plane()
525     : Origin(Scalar(0))
526     , Normal(Scalar(0), Scalar(0), Scalar(1))
527   {
528   }
529 
Plane(const Vector & normal)530   VTKM_EXEC_CONT explicit Plane(const Vector& normal)
531     : Origin(Scalar(0))
532     , Normal(normal)
533   {
534   }
535 
Plane(const Vector & origin,const Vector & normal)536   VTKM_EXEC_CONT Plane(const Vector& origin, const Vector& normal)
537     : Origin(origin)
538     , Normal(normal)
539   {
540   }
541 
SetOrigin(const Vector & origin)542   VTKM_CONT void SetOrigin(const Vector& origin)
543   {
544     this->Origin = origin;
545     this->Modified();
546   }
547 
SetNormal(const Vector & normal)548   VTKM_CONT void SetNormal(const Vector& normal)
549   {
550     this->Normal = normal;
551     this->Modified();
552   }
553 
GetOrigin()554   VTKM_EXEC_CONT const Vector& GetOrigin() const { return this->Origin; }
GetNormal()555   VTKM_EXEC_CONT const Vector& GetNormal() const { return this->Normal; }
556 
Value(const Vector & point)557   VTKM_EXEC_CONT Scalar Value(const Vector& point) const final
558   {
559     return vtkm::Dot(point - this->Origin, this->Normal);
560   }
561 
Gradient(const Vector &)562   VTKM_EXEC_CONT Vector Gradient(const Vector&) const final { return this->Normal; }
563 
564 private:
565   Vector Origin;
566   Vector Normal;
567 };
568 
569 //============================================================================
570 /// \brief Implicit function for a sphere
571 class VTKM_ALWAYS_EXPORT Sphere final : public vtkm::ImplicitFunction
572 {
573 public:
Sphere()574   VTKM_EXEC_CONT Sphere()
575     : Radius(Scalar(0.2))
576     , Center(Scalar(0))
577   {
578   }
579 
Sphere(Scalar radius)580   VTKM_EXEC_CONT explicit Sphere(Scalar radius)
581     : Radius(radius)
582     , Center(Scalar(0))
583   {
584   }
585 
Sphere(Vector center,Scalar radius)586   VTKM_EXEC_CONT Sphere(Vector center, Scalar radius)
587     : Radius(radius)
588     , Center(center)
589   {
590   }
591 
SetRadius(Scalar radius)592   VTKM_CONT void SetRadius(Scalar radius)
593   {
594     this->Radius = radius;
595     this->Modified();
596   }
597 
SetCenter(const Vector & center)598   VTKM_CONT void SetCenter(const Vector& center)
599   {
600     this->Center = center;
601     this->Modified();
602   }
603 
GetRadius()604   VTKM_EXEC_CONT Scalar GetRadius() const { return this->Radius; }
605 
GetCenter()606   VTKM_EXEC_CONT const Vector& GetCenter() const { return this->Center; }
607 
Value(const Vector & point)608   VTKM_EXEC_CONT Scalar Value(const Vector& point) const final
609   {
610     return vtkm::MagnitudeSquared(point - this->Center) - (this->Radius * this->Radius);
611   }
612 
Gradient(const Vector & point)613   VTKM_EXEC_CONT Vector Gradient(const Vector& point) const final
614   {
615     return Scalar(2) * (point - this->Center);
616   }
617 
618 private:
619   Scalar Radius;
620   Vector Center;
621 };
622 
623 } // namespace vtkm
624 
625 #ifdef VTKM_CUDA
626 
627 // Cuda seems to have a bug where it expects the template class VirtualObjectTransfer
628 // to be instantiated in a consistent order among all the translation units of an
629 // executable. Failing to do so results in random crashes and incorrect results.
630 // We workaroud this issue by explicitly instantiating VirtualObjectTransfer for
631 // all the implicit functions here.
632 
633 #include <vtkm/cont/cuda/internal/VirtualObjectTransferCuda.h>
634 
635 VTKM_EXPLICITLY_INSTANTIATE_TRANSFER(vtkm::Box);
636 VTKM_EXPLICITLY_INSTANTIATE_TRANSFER(vtkm::Cylinder);
637 VTKM_EXPLICITLY_INSTANTIATE_TRANSFER(vtkm::Frustum);
638 VTKM_EXPLICITLY_INSTANTIATE_TRANSFER(vtkm::Plane);
639 VTKM_EXPLICITLY_INSTANTIATE_TRANSFER(vtkm::Sphere);
640 
641 #endif
642 
643 #endif //vtk_m_ImplicitFunction_h
644