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{
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 ||
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
121std::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
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
183std::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}
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
auto norm_sq() const
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
TypeVector< T > unit() const
Utilities useful for splines in general Reference for B-Spline implementation: https://mat....
Definition SplineUtils.h:19
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
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 > 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)