https://mooseframework.inl.gov
SplineUtils.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "SplineUtils.h"
11 #include "MooseError.h"
13 #include "MooseUtils.h"
14 #include <algorithm>
15 
16 namespace SplineUtils
17 {
18 std::vector<Point>
20  const libMesh::Point & end_point,
21  const libMesh::RealVectorValue & parallel_direction,
22  const unsigned int num_cps)
23 {
24  // establish the direction vector between the two points
25  const auto orthogonal_direction = start_point - end_point;
26 
27  // circular control points will only honor the derivatives if the points are colinear along the
28  // orthogonal direction
29  if (!(MooseUtils::absoluteFuzzyEqual(parallel_direction.unit() * orthogonal_direction.unit(), 0)))
30  mooseError("Parallel lines cannot be interpolated using points along a circle! Direction "
31  "vectors are incompatible with point locations.\nStart point: ",
32  start_point,
33  "\nEnd point: ",
34  end_point,
35  "\nDirection: ",
36  parallel_direction);
37 
38  // define the distance between the two points
39  const auto distance_between = orthogonal_direction.norm();
40 
41  // connecting circle is assumed to be between each point along the orthogonal_direction vector
42  const auto radius = distance_between / 2.0;
43 
44  // normalize vectors
45  const auto unit_normal = orthogonal_direction / distance_between;
46  const auto unit_parallel = parallel_direction / parallel_direction.norm();
47 
48  // calculate circle center
49  const auto center_point = end_point + radius * unit_normal;
50 
51  // initialize vector of control points
52  std::vector<Point> control_points;
53 
54  // loop over the number of control points to generate the control points
55  mooseAssert(num_cps > 1, "This routine does not support a single control point");
56  for (const auto i : make_range(num_cps))
57  {
58  const auto t = (Real)i / (Real)(num_cps - 1);
59  control_points.push_back(center_point + radius * (std::cos(t * M_PI) * unit_normal +
60  std::sin(t * M_PI) * unit_parallel));
61  }
62 
63  return control_points;
64 }
65 
66 std::vector<Point>
68  const libMesh::Point & end_point,
69  const libMesh::RealVectorValue & start_direction,
70  const libMesh::RealVectorValue & end_direction,
71  const unsigned int cps_per_half,
72  const libMesh::Real sharpness)
73 {
74  // check that start_point is different from end_point
75  if ((start_point - end_point).norm_sq() < libMesh::TOLERANCE * libMesh::TOLERANCE)
76  mooseError("Start and end points must be different.");
77  // check that neither direction is the zero vector
78  if (start_direction.norm_sq() < libMesh::TOLERANCE * libMesh::TOLERANCE ||
79  end_direction.norm_sq() < libMesh::TOLERANCE * libMesh::TOLERANCE)
80  mooseError("Direction vectors must have a nonzero magnitude.");
81  mooseAssert(sharpness <= 1.0 && sharpness >= 0, "Sharpness must be in [0,1]!");
82 
83  // check if directions are parallel, use a special case if so
84  const bool parallel = ((start_direction.cross(end_direction)).norm() < libMesh::TOLERANCE);
85  if (parallel)
86  {
87  // We should not revert to a circle for this case
88  if (MooseUtils::absoluteFuzzyEqual(start_direction.unit(), -end_direction.unit()))
89  mooseError("Start and end directions are opposite, this is not currently supported");
90  mooseWarning("Directions are parallel! Attempting to use circular control points...");
91  unsigned int num_cps = 2 * cps_per_half + 1;
92  if (num_cps < 30)
93  mooseWarning("Number of control points required for circular control points is much greater "
94  "than for BSplines. Current num_cps: " +
95  std::to_string(num_cps));
96  return SplineUtils::circularControlPoints(start_point, end_point, start_direction, num_cps);
97  }
98 
99  // find closest points --> these will be identical if the extrapolated lines intersect
100  const auto closest_points_vector =
101  SplineUtils::closestPoints(start_point, end_point, start_direction, end_direction);
102  const auto & closest_point_1 = closest_points_vector.first;
103  const auto & closest_point_2 = closest_points_vector.second;
104 
105  std::vector<Point> first_half = SplineUtils::controlPointsAlongLine(
106  start_point, closest_point_1, start_direction, sharpness, cps_per_half);
107  std::vector<Point> second_half = SplineUtils::controlPointsAlongLine(
108  end_point, closest_point_2, end_direction, sharpness, cps_per_half, true);
109 
110  std::vector<Point> control_points;
111 
112  // put it all together
113  for (const auto i : index_range(first_half))
114  control_points.push_back(first_half[i]);
115  for (const auto i : index_range(second_half))
116  control_points.push_back(second_half[i]);
117 
118  return control_points;
119 }
120 
121 std::vector<Point>
123  const libMesh::Point & end_point,
124  const libMesh::RealVectorValue & direction_vector,
125  const libMesh::Real sharpness,
126  const unsigned int num_cps,
127  const bool reverse_order)
128 {
129  // initialize a vector of control points with the starting point inserted
130  std::vector<Point> control_points;
131 
132  mooseAssert(num_cps != 0, "Number of control points cannot be zero!");
133  if (num_cps == 1)
134  control_points.push_back(start_point);
135 
136  // create the control points by varying the sharpness linearly
137  else
138  {
139  for (const auto i : libMesh::make_range(num_cps))
140  {
141  const auto point_sharpness = (Real)i / (Real)(num_cps - 1) * sharpness;
142  control_points.push_back(
143  SplineUtils::makeControlPoint(start_point, end_point, direction_vector, point_sharpness));
144  }
145 
146  // reverse_order parameter only set to true when required
147  if (reverse_order)
148  std::reverse(control_points.begin(), control_points.end());
149  }
150  return control_points;
151 }
152 
154 makeControlPoint(const libMesh::Point & start_point,
155  const libMesh::Point & end_point,
156  const libMesh::RealVectorValue & direction_vector,
157  const libMesh::Real sharpness)
158 {
159  // check that sharpness value is ok
160  mooseAssert(sharpness <= 1.0 && sharpness >= 0, "Sharpness must be in [0,1]!");
161 
162  // normalize direction vector
163  const auto unit_direction = direction_vector.unit();
164 
165  // calculate the distance between points
166  const auto distance = (end_point - start_point).norm();
167  const auto distance_sq = distance * distance;
168 
169  // initialize return quantity and attempt to calculate control vector
170  libMesh::Point control_point = start_point + sharpness * distance * unit_direction;
171 
172  // check if we went the wrong way, further from the end_point
173  if ((end_point - control_point).norm_sq() > distance_sq)
174  {
175  control_point = start_point - sharpness * distance * unit_direction;
176  mooseAssert((end_point - control_point).norm_sq() <= distance_sq,
177  "Also going further from end point");
178  }
179 
180  return control_point;
181 }
182 
183 std::pair<Point, Point>
185  const libMesh::Point & point_2,
186  const libMesh::RealVectorValue & direction_1,
187  const libMesh::RealVectorValue & direction_2)
188 {
189  const auto n_vec = direction_1.cross(direction_2);
190 
191  // check if n_vec is the zero vector (indicates parallel lines)
192  if (n_vec.norm_sq() < libMesh::TOLERANCE * libMesh::TOLERANCE)
193  mooseError("Lines are parallel! Infinitely many closest points exist.");
194 
195  const auto n1_vec = direction_1.cross(n_vec);
196  const auto n2_vec = direction_2.cross(n_vec);
197 
198  // calculate closest points
199  // take absolute value to ensure specified directions are honored
200  libMesh::Real point_a_dir_scalar =
201  std::abs((point_2 - point_1) * n2_vec / (direction_1 * n2_vec));
202  libMesh::Real point_b_dir_scalar =
203  std::abs((point_1 - point_2) * n1_vec / (direction_2 * n1_vec));
204  libMesh::Point point_a = point_1 + point_a_dir_scalar * direction_1;
205  libMesh::Point point_b = point_2 + point_b_dir_scalar * direction_2;
206 
207  // points will be returned as a vector containing two points
208  const auto closest_points = std::make_pair(point_a, point_b);
209 
210  return closest_points;
211 }
212 }
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
Definition: EigenADReal.h:50
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sin(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tan
const Real radius
auto norm() const
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
auto norm_sq(const T &a)
static constexpr Real TOLERANCE
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition: MooseError.h:345
std::vector< Point > controlPointsAlongLine(const libMesh::Point &start_point, const libMesh::Point &end_point, const libMesh::RealVectorValue &direction_vector, const libMesh::Real sharpness, const unsigned int num_cps, const bool reverse_order=false)
Creates control points along an extrapolated line up to a certain intercept.
Definition: SplineUtils.C:122
Real distance(const Point &p)
Utilities useful for splines in general Reference for B-Spline implementation: https://mat.fsv.cvut.cz/gcg/sbornik/prochazkova.pdf.
Definition: SplineUtils.h:18
TypeVector< Real > unit() const
auto norm_sq() const
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template cos(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(cos
std::pair< Point, Point > closestPoints(const libMesh::Point &point_1, const libMesh::Point &point_2, const libMesh::RealVectorValue &direction_1, const libMesh::RealVectorValue &direction_2)
Determines the two points defining the shortest line segment between two lines in space...
Definition: SplineUtils.C:184
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
libMesh::Point makeControlPoint(const libMesh::Point &start_point, const libMesh::Point &end_point, const libMesh::RealVectorValue &direction_vector, const libMesh::Real sharpness)
Creates a control point.
Definition: SplineUtils.C:154
std::vector< Point > bSplineControlPoints(const libMesh::Point &start_point, const libMesh::Point &end_point, const libMesh::RealVectorValue &start_direction, const libMesh::RealVectorValue &end_direction, const unsigned int cps_per_half, const libMesh::Real sharpness)
Creates control points for an open uniform BSpline.
Definition: SplineUtils.C:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
auto norm(const T &a)
IntRange< T > make_range(T beg, T end)
std::vector< Point > circularControlPoints(const libMesh::Point &start_point, const libMesh::Point &end_point, const libMesh::RealVectorValue &parallel_direction, const unsigned int num_cps)
Creates control points for the special case of parallel lines using an interpolating circle...
Definition: SplineUtils.C:19
auto index_range(const T &sizable)