https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
16namespace SplineUtils
17{
18std::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
66std::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 const std::string & caller_name)
74{
75 // check that start_point is different from end_point
76 if ((start_point - end_point).norm_sq() < libMesh::TOLERANCE * libMesh::TOLERANCE)
77 mooseError(caller_name + ": Start and end points must be different.");
78 // check that neither direction is the zero vector
79 if (start_direction.norm_sq() < libMesh::TOLERANCE * libMesh::TOLERANCE ||
81 mooseError(caller_name + ": Direction vectors must have a nonzero magnitude.");
82 mooseAssert(sharpness <= 1.0 && sharpness >= 0, "Sharpness must be in [0,1]!");
83
84 // check if directions are parallel, use a special case if so
85 const bool parallel = ((start_direction.cross(end_direction)).norm() < libMesh::TOLERANCE);
86 const bool aligned =
87 ((start_direction.cross(end_point - start_point)).norm() < libMesh::TOLERANCE);
88 if (parallel && !aligned)
89 {
90 // We should not revert to a circle for this case
91 if (MooseUtils::absoluteFuzzyEqual(start_direction.unit(), -end_direction.unit()))
92 mooseError(caller_name +
93 ": Start and end directions are opposite, this is not currently supported");
94 mooseWarning("Directions are parallel! Attempting to use circular control points...");
95 unsigned int num_cps = 2 * cps_per_half + 1;
96 if (num_cps < 30)
97 mooseWarning(caller_name +
98 ": Number of control points required for circular control points "
99 "is much greater than for BSplines. Current num_cps: " +
100 std::to_string(num_cps));
101 return SplineUtils::circularControlPoints(start_point, end_point, start_direction, num_cps);
102 }
103
104 // find closest points --> these will be identical if the extrapolated lines intersect
105 const auto closest_points_vector =
106 SplineUtils::closestPoints(start_point, end_point, start_direction, end_direction);
107 const auto & closest_point_1 = closest_points_vector.first;
108 const auto & closest_point_2 = closest_points_vector.second;
109
110 std::vector<Point> first_half = SplineUtils::controlPointsAlongLine(
111 start_point, closest_point_1, start_direction, sharpness, cps_per_half);
112 std::vector<Point> second_half = SplineUtils::controlPointsAlongLine(
113 end_point, closest_point_2, end_direction, sharpness, cps_per_half, true);
114
115 std::vector<Point> control_points;
116
117 // put it all together
118 for (const auto i : index_range(first_half))
119 control_points.push_back(first_half[i]);
120 for (const auto i : index_range(second_half))
121 control_points.push_back(second_half[i]);
122
123 return control_points;
124}
125
126std::vector<Point>
128 const libMesh::Point & end_point,
129 const libMesh::RealVectorValue & direction_vector,
130 const libMesh::Real sharpness,
131 const unsigned int num_cps,
132 const bool reverse_order)
133{
134 // initialize a vector of control points with the starting point inserted
135 std::vector<Point> control_points;
136
137 mooseAssert(num_cps != 0, "Number of control points cannot be zero!");
138 if (num_cps == 1)
139 control_points.push_back(start_point);
140
141 // create the control points by varying the sharpness linearly
142 else
143 {
144 for (const auto i : libMesh::make_range(num_cps))
145 {
146 const auto point_sharpness = (Real)i / (Real)(num_cps - 1) * sharpness;
147 control_points.push_back(
148 SplineUtils::makeControlPoint(start_point, end_point, direction_vector, point_sharpness));
149 }
150
151 // reverse_order parameter only set to true when required
152 if (reverse_order)
153 std::reverse(control_points.begin(), control_points.end());
154 }
155 return control_points;
156}
157
160 const libMesh::Point & end_point,
161 const libMesh::RealVectorValue & direction_vector,
162 const libMesh::Real sharpness)
163{
164 // check that sharpness value is ok
165 mooseAssert(sharpness <= 1.0 && sharpness >= 0, "Sharpness must be in [0,1]!");
166
167 // normalize direction vector
168 const auto unit_direction = direction_vector.unit();
169
170 // calculate the distance between points
171 const auto distance = (end_point - start_point).norm();
172 const auto distance_sq = distance * distance;
173
174 // initialize return quantity and attempt to calculate control vector
175 libMesh::Point control_point = start_point + sharpness * distance * unit_direction;
176
177 // check if we went the wrong way, further from the end_point
178 if ((end_point - control_point).norm_sq() > distance_sq)
179 {
180 control_point = start_point - sharpness * distance * unit_direction;
181 mooseAssert((end_point - control_point).norm_sq() <= distance_sq,
182 "Also going further from end point");
183 }
184
185 return control_point;
186}
187
188std::pair<Point, Point>
190 const libMesh::Point & point_2,
191 const libMesh::RealVectorValue & direction_1,
192 const libMesh::RealVectorValue & direction_2)
193{
194 const auto n_vec = direction_1.cross(direction_2);
195 const bool aligned = ((direction_1.cross(point_2 - point_1)).norm() < libMesh::TOLERANCE);
196
197 // check if n_vec is the zero vector (indicates parallel lines)
198 if (n_vec.norm_sq() < libMesh::TOLERANCE * libMesh::TOLERANCE)
199 {
200 if (!aligned)
201 mooseError("Lines are parallel! Infinitely many closest points exist.");
202 else
203 return std::make_pair((point_1 + point_2) / 2., (point_1 + point_2) / 2.);
204 }
205
206 const auto n1_vec = direction_1.cross(n_vec);
207 const auto n2_vec = direction_2.cross(n_vec);
208
209 // calculate closest points
210 // take absolute value to ensure specified directions are honored
211 libMesh::Real point_a_dir_scalar =
212 std::abs((point_2 - point_1) * n2_vec / (direction_1 * n2_vec));
213 libMesh::Real point_b_dir_scalar =
214 std::abs((point_1 - point_2) * n1_vec / (direction_2 * n1_vec));
215 libMesh::Point point_a = point_1 + point_a_dir_scalar * direction_1;
216 libMesh::Point point_b = point_2 + point_b_dir_scalar * direction_2;
217
218 // points will be returned as a vector containing two points
219 const auto closest_points = std::make_pair(point_a, point_b);
220
221 return closest_points;
222}
223}
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
LIBMESH_DEVICE_INLINE TypeVector< T > unit() const
auto norm_sq() const
Utilities useful for splines in general Reference for B-Spline implementation: https://mat....
Definition SplineUtils.h:19
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.
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, const std::string &caller_name)
Creates control points for an open uniform BSpline.
Definition SplineUtils.C:67
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.
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.
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
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
const Real radius
Real distance(const Point &p)