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