PLaSK library

Loading...
Searching...
No Matches
polygon.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) 2024 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
15#include "polygon.hpp"
16
17 namespace plask {
18
19 const char* Polygon::NAME = "polygon";
20
21std::string Polygon::getTypeName() const { return NAME; }
22
23 Polygon::Polygon(const std::vector<Vec<2>>& vertices, const shared_ptr<Material>& material)
24 : BaseClass(material), vertices(vertices) {}
25
26 Polygon::Polygon(std::vector<Vec<2>>&& vertices, const shared_ptr<Material>&& material)
27 : BaseClass(material), vertices(std::move(vertices)) {}
28
29 Polygon::Polygon(std::initializer_list<Vec<2>> vertices, const shared_ptr<Material>& material)
30 : BaseClass(material), vertices(vertices) {}
31
32 Polygon::Polygon(const std::vector<Vec<2>>& vertices, shared_ptr<MaterialsDB::MixedCompositionFactory> materialTopBottom)
33 : BaseClass(materialTopBottom), vertices(vertices) {}
34
35 Polygon::Polygon(std::vector<Vec<2>>&& vertices, shared_ptr<MaterialsDB::MixedCompositionFactory> materialTopBottom)
36 : BaseClass(materialTopBottom), vertices(std::move(vertices)) {}
37
38 Polygon::Polygon(std::initializer_list<Vec<2>> vertices, shared_ptr<MaterialsDB::MixedCompositionFactory> materialTopBottom)
39 : BaseClass(materialTopBottom), vertices(vertices) {}
40
41 void Polygon::validate() const {
42 if (vertices.size() < 3) {
43 throw GeometryException("polygon has less than 3 vertices");
44 }
45 // if (!checkSegments()) {
46 // throw GeometryException("polygon has intersecting segments");
47 // }
48}
49
50// bool Polygon::checkSegments() const {
51// for (size_t i = 0; i < vertices.size(); ++i) {
52// Vec<2> a1 = vertices[i];
53// Vec<2> b1 = vertices[(i + 1) % vertices.size()];
54// double min1x = std::min(a1.c0, b1.c0), max1x = std::max(a1.c0, b1.c0);
55// double min1y = std::min(a1.c1, b1.c1), max1y = std::max(a1.c1, b1.c1);
56// double d1x = b1.c0 - a1.c0;
57// double d1y = b1.c1 - a1.c1;
58// double det1 = a1.c0 * b1.c1 - a1.c1 * b1.c0;
59// for (size_t j = i + 2; j < vertices.size() - (i ? 0 : 1); ++j) {
60// Vec<2> a2 = vertices[j];
61// Vec<2> b2 = vertices[(j + 1) % vertices.size()];
62// double min2x = std::min(a2.c0, b2.c0), max2x = std::max(a2.c0, b2.c0);
63// double min2y = std::min(a2.c1, b2.c1), max2y = std::max(a2.c1, b2.c1);
64// if (max2x < min1x || max1x < min2x || max2y < min1y || max1y < min2y) continue;
65// double d2x = b2.c0 - a2.c0;
66// double d2y = b2.c1 - a2.c1;
67// double det = d1x * d2y - d2x * d1y;
68// double det2 = a2.c0 * b2.c1 - a2.c1 * b2.c0;
69// if (det == 0) continue;
70// double x = (d1x * det2 - d2x * det1) / det;
71// double y = (d1y * det2 - d2y * det1) / det;
72// if (x >= min1x && x <= max1x && x >= min2x && x <= max2x && y >= min1y && y <= max1y && y >= min2y && y <= max2y)
73// return false;
74// }
75// }
76// return true;
77// }
78
79 Polygon::Box Polygon::getBoundingBox() const {
80 if (vertices.empty()) return Box(Vec<2>(0, 0), Vec<2>(0, 0));
81 double min_x = vertices[0].c0;
82 double max_x = vertices[0].c0;
83 double min_y = vertices[0].c1;
84 double max_y = vertices[0].c1;
85 for (const Vec<2>& v : vertices) {
86 min_x = std::min(min_x, v.c0);
87 max_x = std::max(max_x, v.c0);
88 min_y = std::min(min_y, v.c1);
89 max_y = std::max(max_y, v.c1);
90 }
91 return Box(Vec<2>(min_x, min_y), Vec<2>(max_x, max_y));
92}
93
94 bool Polygon::contains(const DVec& p) const {
95 if (vertices.size() < 3) return false;
96 int n = vertices.size();
97 int i, j;
98 int c = 0;
99 for (i = 0, j = n - 1; i < n; j = i++) {
100 if (((vertices[i].c1 > p.c1) != (vertices[j].c1 > p.c1)) &&
101 (p.c0 <
102 (vertices[j].c0 - vertices[i].c0) * (p.c1 - vertices[i].c1) / (vertices[j].c1 - vertices[i].c1) + vertices[i].c0))
103 c += (vertices[i].c1 > vertices[j].c1) ? 1 : -1;
104 }
105 return c;
106}
107
108 void Polygon::addPointsAlongToSet(std::set<double>& points,
109 Primitive<3>::Direction direction,
110 unsigned max_steps,
111 double min_step_size) const {
112 if (vertices.size() < 3) return;
113 std::set<double> vert;
114 for (const Vec<2>& v : vertices) {
115 vert.insert(v[int(direction) - 1]);
116 }
117 for (std::set<double>::const_iterator b = vert.begin(), a = b++; b != vert.end(); ++a, ++b) {
118 double d = *b - *a;
119 unsigned steps = std::max(1u, static_cast<unsigned>(d / min_step_size));
120 steps = std::min(steps, max_steps);
121 double step = d / steps;
122 for (unsigned i = 0; i <= steps; ++i) points.insert(*a + i * step);
123 }
124}
125
126 void Polygon::addLineSegmentsToSet(std::set<typename GeometryObjectD<2>::LineSegment>& segments,
127 unsigned max_steps,
128 double min_step_size) const {
129 if (vertices.size() < 3) return;
130 for (size_t i = 0; i < vertices.size(); ++i) {
131 Vec<2> a = vertices[i];
132 Vec<2> b = vertices[(i + 1) % vertices.size()];
133 Vec<2> ab = b - a;
134 double d = std::sqrt(dot(ab, ab));
135 unsigned steps = std::max(1u, static_cast<unsigned>(d / min_step_size));
136 steps = std::min(steps, max_steps);
137 Vec<2> p0 = a;
138 for (unsigned j = 1; j <= steps; ++j) {
139 double t = static_cast<double>(j) / steps;
140 Vec<2> p = a * (1 - t) + b * t;
141 segments.insert({p0, p});
142 p0 = p;
143 }
144 }
145}
146
147 void Polygon::writeXMLAttr(XMLWriter::Element& dest_xml_object, const AxisNames& axes) const {
148 BaseClass::writeXMLAttr(dest_xml_object, axes);
149 materialProvider->writeXML(dest_xml_object, axes);
150 if (vertices.empty()) return;
151 std::string vertices_str;
152 const char* sep = "";
153 for (const Vec<2>& v : vertices) {
154 vertices_str += sep;
155 vertices_str += str(v.c0) + " " + str(v.c1);
156 sep = "; ";
157 }
158 dest_xml_object.writeText(vertices_str);
159}
160
161 shared_ptr<GeometryObject> readPolygon(GeometryReader& reader) {
162 shared_ptr<Polygon> polygon = make_shared<Polygon>();
163 polygon->readMaterial(reader);
164 std::string vertex_spec = reader.source.requireTextInCurrentTag();
165 if (reader.source.attributeFilter) vertex_spec = reader.source.attributeFilter(vertex_spec);
166 std::vector<Vec<2>> vertices;
167 boost::tokenizer<boost::char_separator<char>> tokens(vertex_spec, boost::char_separator<char>(" \t\n\r", ";"));
168 int vi = 0;
169 for (const std::string& t : tokens) {
170 if (t == ";") { // end of point or segment
171 if (vi != 2) throw Exception("each vertex must have two coordinates");
172 vi = 0;
173 } else { // end of point coordinate
174 if (vi == 2)
175 throw Exception("end of vertex (\";\") was expected, but got \"{0}\"", t);
176 if (vi == 0) vertices.emplace_back();
177 try {
178 vertices.back()[vi++] = boost::lexical_cast<double>(t);
179 } catch (const boost::bad_lexical_cast&) {
180 throw Exception("bad vertex coordinate: {0}", t);
181 }
182 }
183 }
184 polygon->vertices = std::move(vertices);
185 if (!reader.manager.draft) polygon->validate();
186 return polygon;
187}
188
189 static GeometryReader::RegisterObjectReader polygon_reader(Polygon::NAME, readPolygon);
190
191} // namespace plask