PLaSK library
Loading...
Searching...
No Matches
prism.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 */
14#include "prism.hpp"
15#include "reader.hpp"
16
17#include "../manager.hpp"
18
19#define PLASK_TRIANGULAR_PRISM_NAME "triangular-prism"
20#define PLASK_PRISM_NAME "prism"
21
22namespace plask {
23
25
26std::string TriangularPrism::getTypeName() const { return NAME; }
27
29 : BaseClass(material), p0(p0), p1(p1), height(height) {}
30
36
38 return Box3D(std::min(std::min(p0.c0, p1.c0), 0.0), std::min(std::min(p0.c1, p1.c1), 0.0), 0.,
39 std::max(std::max(p0.c0, p1.c0), 0.0), std::max(std::max(p0.c1, p1.c1), 0.0), height);
40}
41
42inline static double sign(const Vec<3, double>& p1, const LateralVec<double>& p2, const LateralVec<double>& p3) {
43 return (p1.c0 - p3.c0) * (p2.c1 - p3.c1) - (p2.c0 - p3.c0) * (p1.c1 - p3.c1);
44}
45
46// Like sign, but with p3 = (0, 0)
47inline static double sign0(const Vec<3, double>& p1, const LateralVec<double>& p2) {
48 return (p1.c0) * (p2.c1) - (p2.c0) * (p1.c1);
49}
50
52 if (p.c2 < 0 || p.c2 > height) return false;
53 // algorithm comes from:
54 // http://stackoverflow.com/questions/2049582/how-to-determine-a-point-in-a-triangle
55 // with: v1 -> p0, v2 -> p1, v3 -> (0, 0)
56 // maybe barycentric method would be better?
57 bool b1 = sign(p, p0, p1) < 0.0;
58 bool b2 = sign0(p, p1) < 0.0;
59 return (b1 == b2) && (b2 == (sign(p, Primitive<2>::ZERO_VEC, p0) < 0.0));
60}
61
64 materialProvider->writeXML(dest_xml_object, axes)
65 .attr("a" + axes.getNameForLong(), p0.tran())
66 .attr("a" + axes.getNameForTran(), p0.vert())
67 .attr("b" + axes.getNameForLong(), p1.tran())
68 .attr("b" + axes.getNameForTran(), p1.vert())
69 .attr("height", height);
70}
71
72void TriangularPrism::addPointsAlongToSet(std::set<double>& points,
74 unsigned max_steps,
75 double min_step_size) const {
76 if (direction == Primitive<3>::DIRECTION_VERT) {
78 points.insert(0);
79 points.insert(height);
80 } else {
81 if (this->max_steps) max_steps = this->max_steps;
82 if (this->min_step_size) min_step_size = this->min_step_size;
83 unsigned steps = min(unsigned(height / min_step_size), max_steps);
84 double step = height / steps;
85 for (unsigned i = 0; i <= steps; ++i) points.insert(i * step);
86 }
87 } else {
88 if (this->max_steps) max_steps = this->max_steps;
89 if (this->min_step_size) min_step_size = this->min_step_size;
90
91 double x[3] = {0., p0[int(direction)], p1[int(direction)]};
92
93 // Sort x
94 if (x[2] < x[0]) std::swap(x[0], x[2]);
95 if (x[1] > x[2])
96 std::swap(x[1], x[2]);
97 else if (x[1] < x[0])
98 std::swap(x[1], x[0]);
99
100 for (int i = 0; i < 3; ++i) points.insert(x[i]);
101 double dx02 = x[2] - x[0];
102 if (dx02 == 0) return;
103
104 for (int i = 0; i < 2; ++i) {
105 double dx = x[i + 1] - x[i];
106 unsigned maxsteps = max_steps * (dx / dx02);
107 unsigned steps = min(unsigned(dx / min_step_size), maxsteps);
108 double step = dx / steps;
109 for (unsigned j = 1; j < steps; ++j) points.insert(x[i] + j * step);
110 }
111 }
112}
113
115 unsigned max_steps,
116 double min_step_size) const {
118 throw NotImplemented("prismatic mesh for prisms non-uniform in longitudinal direction");
120 throw NotImplemented("prismatic mesh for prisms non-uniform in transverse direction");
121 std::set<double> vert;
123 typedef typename GeometryObjectD<3>::LineSegment Segment;
124 double pv = 0.;
125 for (double v : vert) {
126 segments.insert(Segment(DVec(0., 0., v), DVec(p0[0], p0[1], v)));
127 segments.insert(Segment(DVec(0., 0., v), DVec(p1[0], p1[1], v)));
128 segments.insert(Segment(DVec(p0[0], p0[1], v), DVec(p1[0], p1[1], v)));
129 if (v != 0.) {
130 segments.insert(Segment(DVec(0., 0., pv), DVec(0., 0., v)));
131 segments.insert(Segment(DVec(p0[0], p0[1], pv), DVec(p0[0], p0[1], v)));
132 segments.insert(Segment(DVec(p1[0], p1[1], pv), DVec(p1[0], p1[1], v)));
133 }
134 pv = v;
135 }
136}
137
140 if (reader.manager.draft) {
141 prism->p0.c0 = reader.source.getAttribute("a" + reader.getAxisLongName(), 0.0);
142 prism->p0.c1 = reader.source.getAttribute("a" + reader.getAxisTranName(), 0.0);
143 prism->p1.c0 = reader.source.getAttribute("b" + reader.getAxisLongName(), 0.0);
144 prism->p1.c1 = reader.source.getAttribute("b" + reader.getAxisTranName(), 0.0);
145 prism->height = reader.source.getAttribute("height", 0.0);
146 } else {
147 prism->p0.c0 = reader.source.requireAttribute<double>("a" + reader.getAxisLongName());
148 prism->p0.c1 = reader.source.requireAttribute<double>("a" + reader.getAxisTranName());
149 prism->p1.c0 = reader.source.requireAttribute<double>("b" + reader.getAxisLongName());
150 prism->p1.c1 = reader.source.requireAttribute<double>("b" + reader.getAxisTranName());
151 prism->height = reader.source.requireAttribute<double>("height");
152 }
153 prism->readMaterial(reader);
154 reader.source.requireTagEnd();
155 return prism;
156}
157
158static GeometryReader::RegisterObjectReader prism_reader(PLASK_TRIANGULAR_PRISM_NAME, read_triangular_prism);
159
161
162const char* Prism::NAME = PLASK_PRISM_NAME;
163
164std::string Prism::getTypeName() const { return NAME; }
165
166Prism::Prism(double height, const std::vector<LateralVec<double>>& vertices, const shared_ptr<Material>& material)
167 : BaseClass(material), height(height), vertices(vertices) {}
168
169Prism::Prism(double height, std::vector<LateralVec<double>>&& vertices, const shared_ptr<Material>&& material)
170 : BaseClass(material), height(height), vertices(std::move(vertices)) {}
171
172Prism::Prism(double height, std::initializer_list<LateralVec<double>> vertices, const shared_ptr<Material>& material)
173 : BaseClass(material), height(height), vertices(vertices) {}
174
175Prism::Prism(double height,
176 const std::vector<LateralVec<double>>& vertices,
178 : BaseClass(materialTopBottom), height(height), vertices(vertices) {}
179
180Prism::Prism(double height,
181 std::vector<LateralVec<double>>&& vertices,
183 : BaseClass(materialTopBottom), height(height), vertices(std::move(vertices)) {}
184
185Prism::Prism(double height,
186 std::initializer_list<LateralVec<double>> vertices,
188 : BaseClass(materialTopBottom), height(height), vertices(vertices) {}
189
190void Prism::validate() const {
191 if (vertices.size() < 3) {
192 throw GeometryException("polygon has less than 3 vertices");
193 }
194 // if (!checkSegments()) {
195 // throw GeometryException("polygon has intersecting segments");
196 // }
197}
198
199// bool Prism::checkSegments() const {
200// for (size_t i = 0; i < vertices.size(); ++i) {
201// LateralVec<double> a1 = vertices[i];
202// LateralVec<double> b1 = vertices[(i + 1) % vertices.size()];
203// double min1x = std::min(a1.c0, b1.c0), max1x = std::max(a1.c0, b1.c0);
204// double min1y = std::min(a1.c1, b1.c1), max1y = std::max(a1.c1, b1.c1);
205// double d1x = b1.c0 - a1.c0;
206// double d1y = b1.c1 - a1.c1;
207// double det1 = a1.c0 * b1.c1 - a1.c1 * b1.c0;
208// for (size_t j = i + 2; j < vertices.size() - (i ? 0 : 1); ++j) {
209// LateralVec<double> a2 = vertices[j];
210// LateralVec<double> b2 = vertices[(j + 1) % vertices.size()];
211// double min2x = std::min(a2.c0, b2.c0), max2x = std::max(a2.c0, b2.c0);
212// double min2y = std::min(a2.c1, b2.c1), max2y = std::max(a2.c1, b2.c1);
213// if (max2x < min1x || max1x < min2x || max2y < min1y || max1y < min2y) continue;
214// double d2x = b2.c0 - a2.c0;
215// double d2y = b2.c1 - a2.c1;
216// double det = d1x * d2y - d2x * d1y;
217// double det2 = a2.c0 * b2.c1 - a2.c1 * b2.c0;
218// if (det == 0) continue;
219// double x = (d1x * det2 - d2x * det1) / det;
220// double y = (d1y * det2 - d2y * det1) / det;
221// if (x >= min1x && x <= max1x && x >= min2x && x <= max2x && y >= min1y && y <= max1y && y >= min2y && y <= max2y)
222// return false;
223// }
224// }
225// return true;
226// }
227
229 if (vertices.empty()) return Box(DVec(0, 0, 0), DVec(0, 0, 0));
230 double min_x = vertices[0].c0;
231 double max_x = vertices[0].c0;
232 double min_y = vertices[0].c1;
233 double max_y = vertices[0].c1;
234 for (const LateralVec<double>& v : vertices) {
235 min_x = std::min(min_x, v.c0);
236 max_x = std::max(max_x, v.c0);
237 min_y = std::min(min_y, v.c1);
238 max_y = std::max(max_y, v.c1);
239 }
240 return Box(DVec(min_x, min_y, 0), DVec(max_x, max_y, height));
241}
242
243bool Prism::contains(const DVec& p) const {
244 if (vertices.size() < 3) return false;
245 if (p.c2 < 0 || p.c2 > height) return false;
246 int n = vertices.size();
247 int i, j;
248 int c = 0;
249 for (i = 0, j = n - 1; i < n; j = i++) {
250 if (((vertices[i].c1 > p.c1) != (vertices[j].c1 > p.c1)) &&
251 (p.c0 <
252 (vertices[j].c0 - vertices[i].c0) * (p.c1 - vertices[i].c1) / (vertices[j].c1 - vertices[i].c1) + vertices[i].c0))
253 c += (vertices[i].c1 > vertices[j].c1) ? 1 : -1;
254 }
255 return c;
256}
257
258void Prism::addPointsAlongToSet(std::set<double>& points,
259 Primitive<3>::Direction direction,
260 unsigned max_steps,
261 double min_step_size) const {
264 points.insert(0);
265 points.insert(height);
266 } else {
267 if (this->max_steps) max_steps = this->max_steps;
268 if (this->min_step_size) min_step_size = this->min_step_size;
269 unsigned steps = min(unsigned(height / min_step_size), max_steps);
270 double step = height / steps;
271 for (unsigned i = 0; i <= steps; ++i) points.insert(i * step);
272 }
273 return;
274 }
275
276 if (vertices.size() < 3) return;
277 std::set<double> vert;
278 for (const LateralVec<double>& v : vertices) {
279 vert.insert(v[int(direction)]);
280 }
281 for (std::set<double>::const_iterator b = vert.begin(), a = b++; b != vert.end(); ++a, ++b) {
282 double d = *b - *a;
283 unsigned steps = std::max(1u, static_cast<unsigned>(d / min_step_size));
284 steps = std::min(steps, max_steps);
285 double step = d / steps;
286 for (unsigned i = 0; i <= steps; ++i) points.insert(*a + i * step);
287 }
288}
289
291 unsigned max_steps,
292 double min_step_size) const {
293 if (vertices.size() < 3) return;
294 typedef typename GeometryObjectD<3>::LineSegment Segment;
295 for (size_t i = 0; i < vertices.size(); ++i) {
297 LateralVec<double> b = vertices[(i + 1) % vertices.size()];
299 double d = std::sqrt(dot(ab, ab));
300 unsigned steps = std::max(1u, static_cast<unsigned>(d / min_step_size));
301 steps = std::min(steps, max_steps);
303 for (unsigned j = 1; j <= steps; ++j) {
304 segments.insert(Segment(DVec(p0.c0, p0.c1, 0), DVec(p0.c0, p0.c1, height)));
305 double t = static_cast<double>(j) / steps;
306 LateralVec<double> p = a * (1 - t) + b * t;
307 segments.insert(Segment(DVec(p0.c0, p0.c1, 0), DVec(p.c0, p.c1, 0)));
308 segments.insert(Segment(DVec(p0.c0, p0.c1, height), DVec(p.c0, p.c1, height)));
309 }
310 }
311}
312
315 materialProvider->writeXML(dest_xml_object, axes).attr("height", height);
316 if (vertices.empty()) return;
317 std::string vertices_str;
318 const char* sep = "";
319 for (const LateralVec<double>& v : vertices) {
320 vertices_str += sep;
321 vertices_str += str(v.c0) + " " + str(v.c1);
322 sep = "; ";
323 }
324 dest_xml_object.writeText(vertices_str);
325}
326
328
329 // DEPRECATED: use TriangularPrism instead
330 if (reader.source.hasAttribute("a" + reader.getAxisLongName()) ||
331 reader.source.hasAttribute("a" + reader.getAxisTranName()) ||
332 reader.source.hasAttribute("b" + reader.getAxisLongName()) ||
333 reader.source.hasAttribute("b" + reader.getAxisTranName())) {
334 writelog(LOG_WARNING, "<prism> with vertices a and b is deprecated, use <triangular-prism> instead");
335 return read_triangular_prism(reader);
336 }
337
339 prism->readMaterial(reader);
340 if (reader.manager.draft)
341 prism->height = reader.source.getAttribute("height", 0.0);
342 else
343 prism->height = reader.source.requireAttribute<double>("height");
344
345 std::string vertex_spec = reader.source.requireTextInCurrentTag();
347 std::vector<LateralVec<double>> vertices;
348 boost::tokenizer<boost::char_separator<char>> tokens(vertex_spec, boost::char_separator<char>(" \t\n\r", ";"));
349 int vi = 0;
350 for (const std::string& t : tokens) {
351 if (t == ";") { // end of point or segment
352 if (vi != 2) throw Exception("each vertex must have two coordinates");
353 vi = 0;
354 } else { // end of point coordinate
355 if (vi == 2) throw Exception("end of vertex (\";\") was expected, but got \"{0}\"", t);
356 if (vi == 0) vertices.emplace_back();
357 try {
358 vertices.back()[vi++] = boost::lexical_cast<double>(t);
359 } catch (const boost::bad_lexical_cast&) {
360 throw Exception("bad vertex coordinate: {0}", t);
361 }
362 }
363 }
364 prism->vertices = std::move(vertices);
365 if (!reader.manager.draft) prism->validate();
366 return prism;
367}
368
370
371} // namespace plask