PLaSK library
Loading...
Searching...
No Matches
generator_triangular.cpp
Go to the documentation of this file.
1/*
2 * This file is part of PLaSK (https://plask.app) by Photonics Group at TUL
3 * Copyright (c) 2022 Lodz University of Technology
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
15#include "triangular2d.hpp"
16
17#include <triangle.h>
18using namespace triangle;
19
20namespace plask {
21
23 void operator()(void* ptr) const { trifree(ptr); }
24};
25
27 bool operator()(const typename Primitive<2>::DVec& a, const typename Primitive<2>::DVec& b) const {
29 }
30};
31
33 typedef typename GeometryObjectD<2>::LineSegment LineSegment;
34 typedef typename Primitive<2>::DVec DVec;
35 typedef std::map<typename Primitive<2>::DVec, int, VecFuzzyCompare> PointMap;
36
37 std::set<LineSegment> lineSegments = geometry->getLineSegments();
38
39 // Make sets of points and segments removing duplicates
40 Box2D bb = geometry->getBoundingBox();
41
43 std::set<std::pair<int, int>> segmentsset;
44 if (full) {
45 pointmap[DVec(bb.left(), bb.bottom())] = 0;
46 pointmap[DVec(bb.right(), bb.bottom())] = 1;
47 pointmap[DVec(bb.right(), bb.top())] = 2;
48 pointmap[DVec(bb.left(), bb.top())] = 3;
49
50 if (pointmap.size() < 4)
51 throw BadMesh("TriangularGenerator", "geometry object is too small to create triangular mesh");
52
53 segmentsset.insert(std::make_pair(0, 1));
54 segmentsset.insert(std::make_pair(1, 2));
55 segmentsset.insert(std::make_pair(2, 3));
56 segmentsset.insert(std::make_pair(0, 3));
57 }
58
59 size_t n = pointmap.size();
60 for (const LineSegment& segment : lineSegments) {
61 int iseg[2];
62 for (int i = 0; i < 2; ++i) {
63 bool ok;
64 PointMap::iterator it;
65 std::tie(it, ok) = pointmap.insert(std::make_pair(segment[i], n));
66 if (ok) ++n;
67 iseg[i] = it->second;
68 }
69 if (iseg[0] != iseg[1]) {
70 if (iseg[0] > iseg[1]) std::swap(iseg[0], iseg[1]);
71 segmentsset.insert(std::make_pair(iseg[0], iseg[1]));
72 }
73 }
74
75 triangulateio in = {}, out = {}; // are fields are nulled, so we will only fill fields we need
76
77 in.numberofpoints = pointmap.size();
78 std::unique_ptr<double[]> in_points(new double[2 * in.numberofpoints]);
79 in.pointlist = in_points.get();
80 for (auto pi : pointmap) {
81 in_points[2 * pi.second] = pi.first.c0;
82 in_points[2 * pi.second + 1] = pi.first.c1;
83 // std::cerr << format("{}: {:5.3f}, {:5.3f}\n", pi.second, pi.first.c0, pi.first.c1);
84 }
85
86 in.numberofsegments = segmentsset.size();
87 std::unique_ptr<int[]> in_segments(new int[2 * in.numberofsegments]);
88 in.segmentlist = in_segments.get();
89 n = 0;
90 for (auto s : segmentsset) {
91 in_segments[n] = s.first;
92 in_segments[n + 1] = s.second;
93 n += 2;
94 // std::cerr << s.first << "-" << s.second << "\n";
95 }
96
97 triangulate(const_cast<char*>(getSwitches().c_str()), &in, &out, nullptr);
98
99 // just for case we free memory which could be allocated by triangulate but we do not need (some of this can be
100 // nullptr):
101 trifree(out.pointattributelist);
102 trifree(out.pointmarkerlist);
103 trifree(out.triangleattributelist);
104 trifree(out.trianglearealist);
105 trifree(out.neighborlist);
106 trifree(out.segmentlist);
107 trifree(out.segmentmarkerlist);
108 trifree(out.holelist);
109 trifree(out.regionlist);
110 trifree(out.edgelist);
111 trifree(out.edgemarkerlist);
112 trifree(out.normlist);
113
114 // this will free rest of memory allocated by triangulate (even if an exception will be thrown):
115 std::unique_ptr<REAL[], TrifreeCaller> out_points(out.pointlist);
116 std::unique_ptr<int[], TrifreeCaller> out_triangles(out.trianglelist);
117
119 result->nodes.reserve(out.numberofpoints);
120 for (std::size_t i = 0; i < std::size_t(out.numberofpoints) * 2; i += 2)
121 result->nodes.emplace_back(out.pointlist[i], out.pointlist[i + 1]);
122 result->elementNodes.reserve(out.numberoftriangles);
123 for (std::size_t i = 0; i < std::size_t(out.numberoftriangles) * 3; i += 3)
124 result->elementNodes.push_back({std::size_t(out.trianglelist[i]), std::size_t(out.trianglelist[i + 1]),
125 std::size_t(out.trianglelist[i + 2])});
126 // it is also possible by triangle, to use 6 numbers per triangle, but we do not support such things at the moment
127 return result;
128}
129
130std::string TriangleGenerator::getSwitches() const {
131 std::ostringstream result;
132
133 // p - specifies vertices, segments, holes, regional attributes, and regional area constraints
134 // z - points (and other items) are numbered from zero
135 // Q - quiet
136 // B - suppresses boundary markers in the output
137 // P - suppresses the output .poly file
138 result << "pzQqBP";
139
140 // D - Conforming Delaunay triangulation
141 if (delaunay) result << 'D';
142
143 // a - imposes a maximum triangle area
144 if (maxTriangleArea) result << 'a' << std::fixed << *maxTriangleArea;
145
146 // q - adds vertices to the mesh to ensure that all angles are between given and 140 degrees.
147 if (minTriangleAngle) {
148 result << 'q';
149 if (!isnan(*minTriangleAngle)) result << std::fixed << *minTriangleAngle;
150 }
151
152 // TODO more configuration
153
154 return result.str();
155}
156
157shared_ptr<MeshGenerator> readTriangleGenerator(XMLReader& reader, const Manager&) {
158 shared_ptr<TriangleGenerator> result = make_shared<TriangleGenerator>();
159 if (reader.requireTagOrEnd("options")) {
160 result->maxTriangleArea = reader.getAttribute<double>("maxarea");
161 result->minTriangleAngle = reader.getAttribute<double>("minangle");
162 result->delaunay = reader.getAttribute<bool>("delaunay", false);
163 result->full = reader.getAttribute<bool>("full", false);
164 reader.requireTagEnd(); // end of options
165 reader.requireTagEnd(); // end of generator
166 }
167 return result;
168}
169
170static RegisterMeshGeneratorReader trianglegenerator_reader("triangular2d.triangle", readTriangleGenerator);
171
172} // namespace plask