LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
Loading...
Searching...
No Matches
quad_o1.cc
1#include "quad_o1.h"
2
3#include <Eigen/Eigen>
4#include <cmath>
5
6#include "point.h"
7#include "segment_o1.h"
8#include "tria_o1.h"
9
10namespace lf::geometry {
11
13 const Eigen::Matrix<double, Eigen::Dynamic, 4>& coords, double tol) {
14 // World dimension
15 const Geometry::dim_t wd = coords.rows();
16 // Length tests
17 const double e0lensq = (coords.col(1) - coords.col(0)).squaredNorm();
18 const double e1lensq = (coords.col(2) - coords.col(1)).squaredNorm();
19 const double e2lensq = (coords.col(3) - coords.col(2)).squaredNorm();
20 const double e3lensq = (coords.col(0) - coords.col(3)).squaredNorm();
21 // Test lengths of edges versus circumference.
22 const double circum = e0lensq + e1lensq + e2lensq + e3lensq;
23 LF_VERIFY_MSG(e0lensq > tol * circum, "Collapsed edge 0");
24 LF_VERIFY_MSG(e1lensq > tol * circum, "Collapsed edge 1");
25 LF_VERIFY_MSG(e2lensq > tol * circum, "Collapsed edge 2");
26 LF_VERIFY_MSG(e3lensq > tol * circum, "Collapsed edge 3");
27 // Area test
28 switch (wd) {
29 case 2: {
30 const double ar1 =
31 ((coords(0, 1) - coords(0, 0)) * (coords(1, 2) - coords(1, 0)) -
32 (coords(1, 1) - coords(1, 0)) * (coords(0, 2) - coords(0, 0)));
33 const double ar2 =
34 ((coords(0, 3) - coords(0, 0)) * (coords(1, 2) - coords(1, 0)) -
35 (coords(1, 3) - coords(1, 0)) * (coords(0, 2) - coords(0, 0)));
36 const double area = std::fabs(ar1) + std::fabs(ar2);
37 LF_VERIFY_MSG(area > tol * circum, "Degenerate 2D quad");
38 return true;
39 break;
40 }
41 case 3: {
42 const Eigen::Matrix<double, 3, 4> c3d(coords.block<3, 4>(0, 0));
43 const double ar1 =
44 ((c3d.col(1) - c3d.col(0)).cross(c3d.col(2) - c3d.col(0))).norm();
45 const double ar2 =
46 ((c3d.col(3) - c3d.col(0)).cross(c3d.col(2) - c3d.col(0))).norm();
47 const double area = ar1 + ar2;
48 LF_VERIFY_MSG(area > tol * circum, "Degenerate 3D quad");
49 return true;
50 break;
51 }
52 default: {
53 LF_ASSERT_MSG(false, "Illegal world dimension" << wd);
54 break;
55 }
56 }
57 return false;
58}
59
60QuadO1::QuadO1(Eigen::Matrix<double, Eigen::Dynamic, 4> coords)
61 : coords_(std::move(coords)) {
62 // Check validity of geometry (non-zero area)
64}
65
66/* SAM_LISTING_BEGIN_1 */
67Eigen::MatrixXd QuadO1::Global(const Eigen::MatrixXd& local) const {
68 LF_ASSERT_MSG(local.rows() == 2, "reference coords must be 2-vectors");
69 // Componentwise bilinear transformation from unit square in a
70 // vectorized fashion.
71 // Note the use of array and matrix views offered by Eigen.
72 return coords_.col(0) *
73 ((1 - local.array().row(0)) * (1 - local.array().row(1)))
74 .matrix() +
75 coords_.col(1) *
76 (local.array().row(0) * (1 - local.array().row(1))).matrix() +
77 coords_.col(2) *
78 (local.array().row(0) * local.array().row(1)).matrix() +
79 coords_.col(3) *
80 ((1 - local.array().row(0)) * local.array().row(1)).matrix();
81}
82/* SAM_LISTING_END_1 */
83
84/* SAM_LISTING_BEGIN_2 */
85Eigen::MatrixXd QuadO1::Jacobian(const Eigen::MatrixXd& local) const {
86 Eigen::MatrixXd result(DimGlobal(), local.cols() * 2);
87
88 // Note that coords\_ stores the coordinates of the vertices of
89 // the quadrilateral in its columns.
90 for (Eigen::Index i = 0; i < local.cols(); ++i) {
91 // Partial derivative of componentwise bilinear mapping
92 // w.r.t. to first reference coordinate
93 result.col(2 * i) = (coords_.col(1) - coords_.col(0)) * (1 - local(1, i)) +
94 (coords_.col(2) - coords_.col(3)) * local(1, i);
95 // Partial derivative of componentwise bilinear mapping
96 // w.r.t. to second reference coordinate
97 result.col(2 * i + 1) =
98 (coords_.col(3) - coords_.col(0)) * (1 - local(0, i)) +
99 (coords_.col(2) - coords_.col(1)) * local(0, i);
100 }
101 return result;
102}
103/* SAM_LISTING_END_2 */
104
105/* SAM_LISTING_BEGIN_3 */
107 const Eigen::MatrixXd& local) const {
108 Eigen::MatrixXd result(DimGlobal(), local.cols() * 2);
109 Eigen::MatrixXd jacobian(DimGlobal(), 2);
110
111 // Loop over all evaluatin points
112 for (Eigen::Index i = 0; i < local.cols(); ++i) {
113 // Compute Jacobian matrix in one evaluation point.
114 jacobian.col(0) = (coords_.col(1) - coords_.col(0)) * (1 - local(1, i)) +
115 (coords_.col(2) - coords_.col(3)) * local(1, i);
116 jacobian.col(1) = (coords_.col(3) - coords_.col(0)) * (1 - local(0, i)) +
117 (coords_.col(2) - coords_.col(1)) * local(0, i);
118 // Distinguish whether the cell lies in a planar mesh or a surface mesh
119 if (DimGlobal() == 2) {
120 // Standard case: 2D planar mesh
121 // Eigen has a built-in function for computing the inverse of a small
122 // matrix
123 result.block(0, 2 * i, DimGlobal(), 2) = jacobian.transpose().inverse();
124 } else {
125 result.block(0, 2 * i, DimGlobal(), 2) = (jacobian.transpose() * jacobian)
126 .colPivHouseholderQr()
127 .solve(jacobian.transpose())
128 .transpose();
129 }
130 }
131 return result;
132}
133/* SAM_LISTING_END_3 */
134
135/* SAM_LISTING_BEGIN_4 */
136Eigen::VectorXd QuadO1::IntegrationElement(const Eigen::MatrixXd& local) const {
137 Eigen::VectorXd result(local.cols());
138 Eigen::MatrixXd jacobian(DimGlobal(), 2);
139
140 // Loop over all evaluatin points
141 for (Eigen::Index i = 0; i < local.cols(); ++i) {
142 // Compute Jacobian matrix in one evaluation point.
143 jacobian.col(0) = (coords_.col(1) - coords_.col(0)) * (1 - local(1, i)) +
144 (coords_.col(2) - coords_.col(3)) * local(1, i);
145 jacobian.col(1) = (coords_.col(3) - coords_.col(0)) * (1 - local(0, i)) +
146 (coords_.col(2) - coords_.col(1)) * local(0, i);
147 // For planar cell, simply the determinant, for a cell in 3D space, the
148 // volume form
149 if (DimGlobal() == 2) {
150 result(i) = std::abs(jacobian.determinant());
151 } else {
152 result(i) = std::sqrt((jacobian.transpose() * jacobian).determinant());
153 }
154 }
155 return result;
156}
157/* SAM_LISTING_END_4 */
158
159std::unique_ptr<Geometry> QuadO1::SubGeometry(dim_t codim, dim_t i) const {
160 using std::make_unique;
161 switch (codim) {
162 case 0:
163 LF_ASSERT_MSG(i == 0, "i is out of bounds.");
164 return std::make_unique<QuadO1>(coords_);
165 case 1:
166 LF_ASSERT_MSG(i >= 0 && i < 4, "i is out of bounds.");
167 return make_unique<SegmentO1>(
168 (Eigen::Matrix<double, Eigen::Dynamic, 2>(DimGlobal(), 2)
169 << coords_.col(RefEl().SubSubEntity2SubEntity(1, i, 1, 0)),
170 coords_.col(RefEl().SubSubEntity2SubEntity(1, i, 1, 1)))
171 .finished());
172 case 2:
173 LF_ASSERT_MSG(i >= 0 && i < 4, "i is out of bounds.");
174 return make_unique<Point>(coords_.col(i));
175 default:
176 LF_VERIFY_MSG(false, "codim is out of bounds.");
177 }
178}
179
180// NOLINTNEXTLINE(readability-function-cognitive-complexity)
181std::vector<std::unique_ptr<Geometry>> QuadO1::ChildGeometry(
182 const RefinementPattern& ref_pat, base::dim_t codim) const {
183 // The refinement pattern must be for a quadrilateral
184 LF_VERIFY_MSG(ref_pat.RefEl() == lf::base::RefEl::kQuad(),
185 "Refinement pattern for " << ref_pat.RefEl().ToString());
186 // Allowed condimensions:
187 // 0 -> child cells, 1-> child edges, 2 -> child points
188 LF_VERIFY_MSG(codim < 3, "Illegal codim " << codim);
189
190 // Lattice meshwidth
191 const double h_lattice = 1.0 / static_cast<double>(ref_pat.LatticeConst());
192 // Obtain geometry of children as lattice polygons
193 std::vector<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>>
194 child_polygons(ref_pat.ChildPolygons(codim));
195 // Number of child segments
196 const base::size_type no_children = child_polygons.size();
197 // Check consistency of data
198 LF_ASSERT_MSG(
199 no_children == ref_pat.NumChildren(codim),
200 "no_children = " << no_children << " <-> " << ref_pat.NumChildren(codim));
201
202 // Variable for returning (unique pointers to) child geometries
203 std::vector<std::unique_ptr<Geometry>> child_geo_uptrs{};
204 // For each child entity create a geometry object and a unique pointer to it.
205 for (int l = 0; l < no_children; l++) {
206 // A single child entity is described by a lattice polygon with
207 // a certain number of corners
208 LF_VERIFY_MSG(
209 child_polygons[l].rows() == 2,
210 "child_polygons[" << l << "].rows() = " << child_polygons[l].rows());
211 // Obtain physical (world) coordinates of the vertices of
212 // of the child entities after normalizing lattice coordinates
213 const Eigen::MatrixXd child_geo(
214 Global(h_lattice * child_polygons[l].cast<double>()));
215 // Treat child entities of different dimension
216 switch (codim) {
217 case 0: { // Child cells
218 if (child_polygons[l].cols() == 3) {
219 // Child cell is a triangle
220 child_geo_uptrs.push_back(std::make_unique<TriaO1>(child_geo));
221 } else if (child_polygons[l].cols() == 4) {
222 // Child cell is a quadrilateral
223 child_geo_uptrs.push_back(std::make_unique<QuadO1>(child_geo));
224 } else {
225 LF_VERIFY_MSG(false, "child_polygons[" << l << "].cols() = "
226 << child_polygons[l].cols());
227 }
228 break;
229 }
230 case 1: {
231 // Child is an edge
232 LF_VERIFY_MSG(
233 child_polygons[l].cols() == 2,
234 "child_polygons[l].cols() = " << child_polygons[l].cols());
235 child_geo_uptrs.push_back(std::make_unique<SegmentO1>(child_geo));
236 break;
237 }
238 case 2: {
239 LF_VERIFY_MSG(
240 child_polygons[l].cols() == 1,
241 "child_polygons[l].cols() = " << child_polygons[l].cols());
242 child_geo_uptrs.push_back(std::make_unique<Point>(child_geo));
243 break;
244 }
245 default: {
246 LF_VERIFY_MSG(false, "Illegal co-dimension");
247 break;
248 }
249 } // end switch codim
250 } // end loop over children
251 return (child_geo_uptrs);
252} // end ChildGeometry()
253
255// Implementation class Parallelogram
257
258Parallelogram::Parallelogram(Eigen::Matrix<double, Eigen::Dynamic, 4> coords)
259 : coords_(std::move(coords)),
260 jacobian_(coords_.rows(), 2),
261 jacobian_inverse_gramian_(coords_.rows(), 2),
262 integrationElement_(0) {
263 // Check validity of geometry (non-zero area)
265 // Check parallelogram property
266 LF_ASSERT_MSG(
267 (coords_.col(3) - coords_.col(2) + coords_.col(1) - coords_.col(0))
268 .norm() < 1.0E-8 * (coords_.col(2) - coords_.col(0)).norm(),
269 "No parallelogram!");
270 init();
271}
272
273Parallelogram::Parallelogram(const Eigen::VectorXd& p0,
274 const Eigen::VectorXd& p1,
275 const Eigen::VectorXd& p2)
276 : coords_(p0.rows(), 4),
277 jacobian_(p0.rows(), 2),
278 jacobian_inverse_gramian_(p0.rows(), 2),
279 integrationElement_(0) {
280 LF_ASSERT_MSG(p0.size() == p1.size(), "Vector length mismatch p0 <-> p1");
281 LF_ASSERT_MSG(p0.size() == p2.size(), "Vector length mismatch p0 <-> p2");
282 coords_.col(0) = p0;
283 coords_.col(1) = p1;
284 coords_.col(2) = p1 + (p2 - p0);
285 coords_.col(3) = p2;
287 init();
288}
289
291 jacobian_ << coords_.col(1) - coords_.col(0), coords_.col(3) - coords_.col(0);
292 // Distinguish between different world dimensions
293 if (coords_.rows() == 2) {
294 // 2D case: Simpler formula!
295 jacobian_inverse_gramian_ = jacobian_.transpose().inverse();
296 integrationElement_ = std::abs(jacobian_.determinant());
297 } else {
298 // 3D case: complicated formula
299 jacobian_inverse_gramian_ = Eigen::MatrixXd(
300 jacobian_ * (jacobian_.transpose() * jacobian_).inverse());
302 std::sqrt((jacobian_.transpose() * jacobian_).determinant());
303 }
304} // end InitDofHandler()
305
306Eigen::MatrixXd Parallelogram::Global(const Eigen::MatrixXd& local) const {
307 return coords_.col(0) *
308 (1 - local.array().row(0) - local.array().row(1)).matrix() +
309 coords_.col(1) * local.row(0) + coords_.col(3) * local.row(1);
310}
311
312Eigen::MatrixXd Parallelogram::Jacobian(const Eigen::MatrixXd& local) const {
313 return jacobian_.replicate(1, local.cols());
314}
315
317 const Eigen::MatrixXd& local) const {
318 return jacobian_inverse_gramian_.replicate(1, local.cols());
319}
320
322 const Eigen::MatrixXd& local) const {
323 return Eigen::VectorXd::Constant(local.cols(), integrationElement_);
324}
325
326// essentially a copy of the same method for QuadO1
327std::unique_ptr<Geometry> Parallelogram::SubGeometry(dim_t codim,
328 dim_t i) const {
329 using std::make_unique;
330 switch (codim) {
331 case 0:
332 LF_ASSERT_MSG(i == 0, "i is out of bounds.");
333 return std::make_unique<Parallelogram>(coords_);
334 case 1:
335 LF_ASSERT_MSG(i >= 0 && i < 4, "i is out of bounds.");
336 return make_unique<SegmentO1>(
337 (Eigen::Matrix<double, Eigen::Dynamic, 2>(DimGlobal(), 2)
338 << coords_.col(RefEl().SubSubEntity2SubEntity(1, i, 1, 0)),
339 coords_.col(RefEl().SubSubEntity2SubEntity(1, i, 1, 1)))
340 .finished());
341 case 2:
342 LF_ASSERT_MSG(i >= 0 && i < 4, "i is out of bounds.");
343 return make_unique<Point>(coords_.col(i));
344 default:
345 LF_VERIFY_MSG(false, "codim is out of bounds.");
346 }
347} // end SubGeometry
348
349// Essentially a copy of the same code for QuadO1
350// NOLINTNEXTLINE(readability-function-cognitive-complexity)
351std::vector<std::unique_ptr<Geometry>> Parallelogram::ChildGeometry(
352 const RefinementPattern& ref_pat, lf::base::dim_t codim) const {
353 // The refinement pattern must be for a quadrilateral
354 LF_VERIFY_MSG(ref_pat.RefEl() == lf::base::RefEl::kQuad(),
355 "Refinement pattern for " << ref_pat.RefEl().ToString());
356 // Allowed condimensions:
357 // 0 -> child cells, 1-> child edges, 2 -> child points
358 LF_VERIFY_MSG(codim < 3, "Illegal codim " << codim);
359
360 // Lattice meshwidth
361 const double h_lattice = 1.0 / static_cast<double>(ref_pat.LatticeConst());
362 // Obtain geometry of children as lattice polygons
363 std::vector<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>>
364 child_polygons(ref_pat.ChildPolygons(codim));
365 // Number of child segments
366 const base::size_type no_children = child_polygons.size();
367 // Check consistency of data
368 LF_ASSERT_MSG(
369 no_children == ref_pat.NumChildren(codim),
370 "no_children = " << no_children << " <-> " << ref_pat.NumChildren(codim));
371
372 // Variable for returning (unique pointers to) child geometries
373 std::vector<std::unique_ptr<Geometry>> child_geo_uptrs{};
374 // For each child entity create a geometry object and a unique pointer to it.
375 for (int l = 0; l < no_children; l++) {
376 // A single child entity is described by a lattice polygon with
377 // a certain number of corners
378 LF_VERIFY_MSG(
379 child_polygons[l].rows() == 2,
380 "child_polygons[" << l << "].rows() = " << child_polygons[l].rows());
381 // Obtain physical (world) coordinates of the vertices of
382 // of the child entities after normalizing lattice coordinates
383 const Eigen::MatrixXd child_geo(
384 Global(h_lattice * child_polygons[l].cast<double>()));
385 // Treat child entities of different dimension
386 switch (codim) {
387 case 0: { // Child cells
388 if (child_polygons[l].cols() == 3) {
389 // Child cell is a triangle
390 child_geo_uptrs.push_back(std::make_unique<TriaO1>(child_geo));
391 } else if (child_polygons[l].cols() == 4) {
392 // Child cell is a quadrilateral
394 // Code specific for a parallleogram
395 // If the lattice polygon is a parallelogram
396 // then create another parallelogram, otherwise
397 // a general quadrilateral
398 if (isParallelogram(child_polygons[l])) {
399 // Child cell is parallelogram as well
400 child_geo_uptrs.push_back(
401 std::make_unique<Parallelogram>(child_geo));
402 } else {
403 // General quadrilateral
404 child_geo_uptrs.push_back(std::make_unique<QuadO1>(child_geo));
405 }
406 } else {
407 LF_VERIFY_MSG(false, "child_polygons[" << l << "].cols() = "
408 << child_polygons[l].cols());
409 }
410 break;
411 }
412 case 1: {
413 // Child is an edge
414 LF_VERIFY_MSG(
415 child_polygons[l].cols() == 2,
416 "child_polygons[l].cols() = " << child_polygons[l].cols());
417 child_geo_uptrs.push_back(std::make_unique<SegmentO1>(child_geo));
418 break;
419 }
420 case 2: {
421 LF_VERIFY_MSG(
422 child_polygons[l].cols() == 1,
423 "child_polygons[l].cols() = " << child_polygons[l].cols());
424 child_geo_uptrs.push_back(std::make_unique<Point>(child_geo));
425 break;
426 }
427 default: {
428 LF_VERIFY_MSG(false, "Illegal co-dimension");
429 break;
430 }
431 } // end switch codim
432 } // end loop over children
433 return (child_geo_uptrs);
434} // end ChildGeometry()
435
436} // namespace lf::geometry
static constexpr RefEl kQuad()
Returns the reference quadrilateral.
Definition ref_el.h:169
std::string ToString() const
Return a string representation of this Reference element.
Definition ref_el.h:458
base::RefEl::dim_t dim_t
std::unique_ptr< Geometry > SubGeometry(dim_t codim, dim_t i) const override
Construct a new Geometry() object that describes the geometry of the i-th sub-entity with codimension...
Definition quad_o1.cc:327
double integrationElement_
constant Gramian determinant
Definition quad_o1.h:174
Eigen::Matrix< double, Eigen::Dynamic, 2 > jacobian_inverse_gramian_
Constant matrix ( )
Definition quad_o1.h:172
Eigen::MatrixXd Global(const Eigen::MatrixXd &local) const override
Map a number of points in local coordinates into the global coordinate system.
Definition quad_o1.cc:306
Eigen::MatrixXd JacobianInverseGramian(const Eigen::MatrixXd &local) const override
Evaluate the Jacobian * Inverse Gramian ( ) simultaneously at numPoints.
Definition quad_o1.cc:316
Eigen::Matrix< double, Eigen::Dynamic, 2 > jacobian_
Matrix of affine mapping generating the parallelogram, agrees with constant Jacobian.
Definition quad_o1.h:170
dim_t DimGlobal() const override
Dimension of the image of this mapping.
Definition quad_o1.h:123
Eigen::VectorXd IntegrationElement(const Eigen::MatrixXd &local) const override
The integration element (factor appearing in integral transformation formula, see below) at number of...
Definition quad_o1.cc:321
Eigen::Matrix< double, Eigen::Dynamic, 4 > coords_
Coordinates of the a four vertices, stored in matrix columns.
Definition quad_o1.h:167
Parallelogram(Eigen::Matrix< double, Eigen::Dynamic, 4 > coords)
Constructor building a parallelogram from four vertex coordinates.
Definition quad_o1.cc:258
Eigen::MatrixXd Jacobian(const Eigen::MatrixXd &local) const override
Evaluate the jacobian of the mapping simultaneously at numPoints points.
Definition quad_o1.cc:312
base::RefEl RefEl() const override
The Reference element that defines the domain of this mapping.
Definition quad_o1.h:124
std::vector< std::unique_ptr< Geometry > > ChildGeometry(const RefinementPattern &ref_pat, lf::base::dim_t codim) const override
Generate geometry objects for child entities created in the course of refinement.
Definition quad_o1.cc:351
void init()
performs initialization of data members
Definition quad_o1.cc:290
Eigen::MatrixXd Global(const Eigen::MatrixXd &local) const override
Map a number of points in local coordinates into the global coordinate system.
Definition quad_o1.cc:67
std::unique_ptr< Geometry > SubGeometry(dim_t codim, dim_t i) const override
Construct a new Geometry() object that describes the geometry of the i-th sub-entity with codimension...
Definition quad_o1.cc:159
Eigen::VectorXd IntegrationElement(const Eigen::MatrixXd &local) const override
The integration element (factor appearing in integral transformation formula, see below) at number of...
Definition quad_o1.cc:136
QuadO1(Eigen::Matrix< double, Eigen::Dynamic, 4 > coords)
Constructor building quadrilateral from vertex coordinates.
Definition quad_o1.cc:60
Eigen::MatrixXd JacobianInverseGramian(const Eigen::MatrixXd &local) const override
Evaluate the Jacobian * Inverse Gramian ( ) simultaneously at numPoints.
Definition quad_o1.cc:106
Eigen::MatrixXd Jacobian(const Eigen::MatrixXd &local) const override
Evaluate the jacobian of the mapping simultaneously at numPoints points.
Definition quad_o1.cc:85
dim_t DimGlobal() const override
Dimension of the image of this mapping.
Definition quad_o1.h:41
std::vector< std::unique_ptr< Geometry > > ChildGeometry(const RefinementPattern &ref_pat, lf::base::dim_t codim) const override
Generate geometry objects for child entities created in the course of refinement.
Definition quad_o1.cc:181
base::RefEl RefEl() const override
The Reference element that defines the domain of this mapping.
Definition quad_o1.h:42
Eigen::Matrix< double, Eigen::Dynamic, 4 > coords_
Coordinates of the a four vertices, stored in matrix columns.
Definition quad_o1.h:89
Abstract interface class for encoding topological local refinement
lf::base::size_type LatticeConst() const
Provides information about lattice constant used.
virtual lf::base::size_type NumChildren(lf::base::dim_t codim) const =0
provide number of child entities of a given co-dimension to be created by refinement
virtual std::vector< Eigen::Matrix< int, Eigen::Dynamic, Eigen::Dynamic > > ChildPolygons(lf::base::dim_t codim) const =0
provide lattice reference coordinates of vertices of child polygons
lf::base::RefEl RefEl() const
Returns topological type of entity for which the current object is set up.
unsigned int size_type
general type for variables related to size of arrays
Definition types.h:20
unsigned int dim_t
type for dimensions and co-dimensions and numbers derived from them
Definition types.h:32
Defines the Geometry interface and provides a number of classes that implement this interface + addit...
Definition compose.h:13
bool assertNonDegenerateQuad(const Eigen::Matrix< double, Eigen::Dynamic, 4 > &coords, double tol)
Asserting a non-degenerate bilinear quadrilateral.
Definition quad_o1.cc:12
bool isParallelogram(const Eigen::Matrix< int, Eigen::Dynamic, Eigen::Dynamic > &polygon)
Test whether a lattice polygon describes a logical parallelogram.