PLaSK library

Loading...
Searching...
No Matches
stack.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 "stack.hpp"
15#include "separator.hpp"
16
17#define PLASK_STACK2D_NAME ("stack" PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D)
18#define PLASK_STACK3D_NAME ("stack" PLASK_GEOMETRY_TYPE_NAME_SUFFIX_3D)
19#define PLASK_SHELF_NAME "shelf"
20
21#define BASEH_ATTR "shift"
22#define REPEAT_ATTR "repeat"
23#define REQUIRE_EQUAL_HEIGHTS_ATTR "flat"
24#define ZERO_ATTR "zero"
25
26 namespace plask {
27
28 template <int dim, typename Primitive<dim>::Direction growingDirection>
29 void StackContainerBaseImpl<dim, growingDirection>::setBaseHeight(double newBaseHeight) {
30 if (getBaseHeight() == newBaseHeight) return;
31 double diff = newBaseHeight - getBaseHeight();
32 stackHeights.front() = newBaseHeight;
33 for (std::size_t i = 1; i < stackHeights.size(); ++i) {
34 stackHeights[i] += diff;
35 children[i - 1]->translation[growingDirection] += diff;
36 // children[i-1]->fireChanged(GeometryObject::Event::EVENT_RESIZE);
37 }
38 this->fireChildrenChanged(); // TODO should this be called? children was moved but not removed/inserted
39 // this->fireChanged(GeometryObject::Event::EVENT_RESIZE);
40 // TODO: block connection to not react on children changed, call
41 // children[i]->fireChanged(GeometryObject::Event::EVENT_RESIZE); for each child, call
42 // this->fireChanged(GeometryObject::Event::EVENT_RESIZE delegate;
43}
44
45 template <int dim, typename Primitive<dim>::Direction growingDirection>
46 void StackContainerBaseImpl<dim, growingDirection>::setZeroBefore(std::size_t index) {
47 std::size_t h_count = stackHeights.size();
48 if (index >= h_count) throw OutOfBoundsException("setZeroBefore", "index", index, 0, h_count - 1);
49 setBaseHeight(stackHeights[0] - stackHeights[index]);
50}
51
52 template <int dim, typename Primitive<dim>::Direction growingDirection>
53 void StackContainerBaseImpl<dim, growingDirection>::alignZeroOn(std::size_t index, double pos) {
54 std::size_t h_count = children.size();
55 if (index >= h_count) throw OutOfBoundsException("alignZeroOn", "index", index, 0, h_count - 1);
56 auto child = children[index]->getChild();
57 double shift = child ? child->getBoundingBox().lower[growingDirection] : 0.;
58 setBaseHeight(stackHeights[0] - stackHeights[index] + shift - pos);
59}
60
61 template <int dim, typename Primitive<dim>::Direction growingDirection>
62std::size_t StackContainerBaseImpl<dim, growingDirection>::getInsertionIndexForHeight(double height) const {
63 return std::lower_bound(stackHeights.begin(), stackHeights.end(), height) - stackHeights.begin();
64}
65
66 template <int dim, typename Primitive<dim>::Direction growingDirection>
67 const shared_ptr<typename StackContainerBaseImpl<dim, growingDirection>::TranslationT>
68 StackContainerBaseImpl<dim, growingDirection>::getChildForHeight(
69 double height,
70 shared_ptr<typename StackContainerBaseImpl<dim, growingDirection>::TranslationT>& sec_candidate) const {
71 auto it = std::lower_bound(stackHeights.begin(), stackHeights.end(), height);
72 // we have: *it >= height > *(it-1)
73 if (it == stackHeights.end()) { // height > stackHeights.back()
74 if (is_zero(height - stackHeights.back(), 16 * SMALL) && !children.empty()) return children.back();
75 return shared_ptr<TranslationT>();
76 }
77 if (it == stackHeights.begin()) { // stackHeights.front() >= height
78 if (is_zero(stackHeights.front() - height,
79 16 * SMALL)) // children.empty() now impossible - then it == stackHeights.end()
80 return children[0];
81 else
82 return shared_ptr<TranslationT>();
83 }
84 std::ptrdiff_t sh_index = it - stackHeights.begin();
85 if (sh_index > 1 && is_zero(height - stackHeights[sh_index-1], 16 * SMALL))
86 sec_candidate = children[sh_index-2];
87 else if (sh_index + 1 < stackHeights.size() && is_zero(stackHeights[sh_index] - height, 16 * SMALL))
88 sec_candidate = children[sh_index];
89 return children[sh_index-1];
90}
91
92 template <int dim, typename Primitive<dim>::Direction growingDirection>
93 bool StackContainerBaseImpl<dim, growingDirection>::contains(
94 const typename StackContainerBaseImpl<dim, growingDirection>::DVec& p) const {
95 shared_ptr<TranslationT> sec_candidate;
96 const shared_ptr<TranslationT> c = getChildForHeight(p[growingDirection], sec_candidate);
97 if (c) {
98 if (c->contains(p)) return true;
99 if (sec_candidate) return sec_candidate->contains(p);
100 }
101 return false;
102}
103
104 template <int dim, typename Primitive<dim>::Direction growingDirection>
105 shared_ptr<Material> StackContainerBaseImpl<dim, growingDirection>::getMaterial(
106 const typename StackContainerBaseImpl<dim, growingDirection>::DVec& p) const {
107 shared_ptr<TranslationT> sec_candidate;
108 const shared_ptr<TranslationT> c = getChildForHeight(p[growingDirection], sec_candidate);
109 if (c) {
110 if (auto material = c->getMaterial(p)) return material;
111 if (sec_candidate) return sec_candidate->getMaterial(p);
112 }
113 return shared_ptr<Material>();
114}
115
116 template <int dim, typename Primitive<dim>::Direction growingDirection>
117 GeometryObject::Subtree StackContainerBaseImpl<dim, growingDirection>::getPathsAt(
118 const typename StackContainerBaseImpl<dim, growingDirection>::DVec& point,
119 bool all) const {
120 shared_ptr<TranslationT> sec_candidate;
121 const shared_ptr<TranslationT> c = getChildForHeight(point[growingDirection], sec_candidate);
122 GeometryObject::Subtree result;
123 if (c) {
124 GeometryObject::Subtree child_path = c->getPathsAt(point, all);
125 if (!child_path.empty()) {
126 result.children.push_back(std::move(child_path));
127 if (!all) { // one is enough
128 result.object = this->shared_from_this();
129 return result;
130 }
131 }
132 if (sec_candidate) {
133 child_path = sec_candidate->getPathsAt(point, all);
134 if (!child_path.empty()) result.children.push_back(std::move(child_path));
135 }
136 if (!result.children.empty()) // c or sec_candidate or both have given us some children
137 result.object = this->shared_from_this();
138 }
139 return result;
140}
141
142 template <int dim, typename Primitive<dim>::Direction growingDirection>
143 void StackContainerBaseImpl<dim, growingDirection>::removeAtUnsafe(std::size_t index) {
144 GeometryObjectContainer<dim>::removeAtUnsafe(index);
145 stackHeights.pop_back();
146 updateAllHeights(index);
147}
148
149 template <int dim, typename Primitive<dim>::Direction growingDirection>
150 void StackContainerBaseImpl<dim, growingDirection>::onChildChanged(const GeometryObject::Event& evt) {
151 if (evt.isResize())
152 updateAllHeights(); // TODO optimization: find evt source index and update size from this index to back
153 this->fireChanged(evt.originalSource(), evt.flagsForParent());
154}
155
156 template <int dim, typename Primitive<dim>::Direction growingDirection>
157 void StackContainerBaseImpl<dim, growingDirection>::updateAllHeights(std::size_t first_child_index) {
158 for (; first_child_index < children.size(); ++first_child_index) updateHeight(first_child_index);
159
160 updateAllHeights();
161}
162
163 template <int dim, typename Primitive<dim>::Direction growingDirection>
164 void StackContainerBaseImpl<dim, growingDirection>::updateAllHeights() {
165 AccurateSum sum = stackHeights[0];
166 for (std::size_t child_index = 0; child_index < children.size(); ++child_index) {
167 auto child = children[child_index]->getChild();
168 auto elBoudingBox = child ? child->getBoundingBox() : Box(Primitive<dim>::ZERO_VEC, Primitive<dim>::ZERO_VEC);
169 sum -= elBoudingBox.lower[growingDirection];
170 children[child_index]->translation[growingDirection] = sum;
171 sum += elBoudingBox.upper[growingDirection];
172 stackHeights[child_index + 1] = sum;
173 }
174}
175
176 template <int dim, typename Primitive<dim>::Direction growingDirection>
177 void StackContainerBaseImpl<dim, growingDirection>::rebuildStackHeights(std::size_t first_child_index) {
178 stackHeights.resize(children.size() + 1);
179 updateAllHeights(first_child_index);
180}
181
182 template <int dim, typename Primitive<dim>::Direction growingDirection>
183 void StackContainerBaseImpl<dim, growingDirection>::writeXMLAttr(XMLWriter::Element& dest_xml_object,
184 const AxisNames& axes) const {
185 BaseClass::writeXMLAttr(dest_xml_object, axes);
186 dest_xml_object.attr(BASEH_ATTR, getBaseHeight());
187}
188
189 template <int dim, typename Primitive<dim>::Direction growingDirection>
190 bool StackContainerBaseImpl<dim, growingDirection>::removeIfTUnsafe(
191 const std::function<bool(const shared_ptr<TranslationT>& c)>& predicate) {
192 if (GeometryObjectContainer<dim>::removeIfTUnsafe(predicate)) {
193 this->rebuildStackHeights();
194 return true;
195 } else
196 return false;
197}
198
199 template struct PLASK_API StackContainerBaseImpl<2, Primitive<2>::DIRECTION_VERT>;
200 template struct PLASK_API StackContainerBaseImpl<3, Primitive<3>::DIRECTION_VERT>;
201 template struct PLASK_API StackContainerBaseImpl<2, Primitive<2>::DIRECTION_TRAN>;
202
203/*template <int dim> //this is fine but GeometryObjects doesn't have copy constructors at all, because signal doesn't
204 have copy constructor StackContainer<dim>::StackContainer(const StackContainer& to_copy) :
205 StackContainerBaseImpl<dim>(to_copy) //copy all but aligners
206{
207 std::vector<Aligner*> aligners_copy;
208 aligners_copy.reserve(to_copy.size());
209 for (auto a: to_copy.aligners) aligners_copy.push_back(a->clone());
210 this->aligners = aligners_copy;
211}*/
212
213 template <> const StackContainer<2>::ChildAligner& StackContainer<2>::DefaultAligner() {
214 static auto leftZeroAl = align::left(0);
215 return leftZeroAl;
216}
217
218 template <> const StackContainer<3>::ChildAligner& StackContainer<3>::DefaultAligner() {
219 static auto leftBackAl = align::left(0) & align::back(0);
220 return leftBackAl;
221}
222
223 template <int dim>
224 PathHints::Hint StackContainer<dim>::insertUnsafe(const shared_ptr<ChildType>& el,
225 const std::size_t pos,
226 const ChildAligner& aligner) {
227 const auto bb = el ? el->getBoundingBox() : Box(Primitive<dim>::ZERO_VEC, Primitive<dim>::ZERO_VEC);
228 shared_ptr<TranslationT> trans_geom = newTranslation(el, aligner, stackHeights[pos] - bb.lower.vert(), bb);
229 this->connectOnChildChanged(*trans_geom);
230 children.insert(children.begin() + pos, trans_geom);
231 this->aligners.insert(this->aligners.begin() + pos, aligner);
232 stackHeights.insert(stackHeights.begin() + pos, stackHeights[pos]);
233 const double delta = bb.upper.vert() - bb.lower.vert();
234 for (std::size_t i = pos + 1; i < children.size(); ++i) {
235 stackHeights[i] += delta;
236 children[i]->translation.vert() += delta;
237 }
238 stackHeights.back() += delta;
239
240 this->updateAllHeights();
241
242 this->fireChildrenInserted(pos, pos + 1);
243 return PathHints::Hint(shared_from_this(), trans_geom);
244}
245
246 template <int dim>
247 shared_ptr<typename StackContainer<dim>::TranslationT> StackContainer<dim>::newTranslation(
248 const shared_ptr<typename StackContainer<dim>::ChildType>& el,
249 const typename StackContainer<dim>::ChildAligner& aligner,
250 double up_trans,
251 const typename StackContainer<dim>::Box& elBB) const {
252 shared_ptr<TranslationT> result(new TranslationT(el, Primitive<dim>::ZERO_VEC));
253 result->translation.vert() = up_trans;
254 aligner.align(*result, elBB);
255 // el->fireChanged(); //??
256 return result;
257}
258
259 template <int dim>
260 shared_ptr<typename StackContainer<dim>::TranslationT> StackContainer<dim>::newTranslation(
261 const shared_ptr<typename StackContainer<dim>::ChildType>& el,
262 const typename StackContainer<dim>::ChildAligner& aligner,
263 double up_trans) const {
264 shared_ptr<TranslationT> result(new TranslationT(el, Primitive<dim>::ZERO_VEC));
265 result->translation.vert() = up_trans;
266 aligner.align(*result);
267 return result;
268}
269
270 template <int dim> void StackContainer<dim>::onChildChanged(const GeometryObject::Event& evt) {
271 if (evt.isResize()) {
272 ParentClass::align(const_cast<TranslationT*>(evt.source<TranslationT>()));
273 this->updateAllHeights(); // TODO optimization: find evt source index and update size from this index to back
274 }
275 this->fireChanged(evt.originalSource(), evt.flagsForParent());
276}
277
278 template <int dim>
279 PathHints::Hint StackContainer<dim>::addUnsafe(const shared_ptr<typename StackContainer<dim>::ChildType>& el,
280 const typename StackContainer<dim>::ChildAligner& aligner) {
281 double el_translation, next_height;
282 auto elBB = el ? el->getBoundingBox() : Box(Primitive<dim>::ZERO_VEC, Primitive<dim>::ZERO_VEC);
283 this->calcHeight(elBB, stackHeights.back(), el_translation, next_height);
284 shared_ptr<TranslationT> trans_geom = newTranslation(el, aligner, el_translation, elBB);
285 this->connectOnChildChanged(*trans_geom);
286 children.push_back(trans_geom);
287 stackHeights.push_back(next_height);
288 this->aligners.push_back(aligner);
289
290 this->updateAllHeights();
291
292 this->fireChildrenInserted(children.size() - 1, children.size());
293 return PathHints::Hint(shared_from_this(), trans_geom);
294}
295
296 template <int dim>
297 bool StackContainer<dim>::removeIfTUnsafe(const std::function<bool(const shared_ptr<TranslationT>& c)>& predicate) {
298 if (ParentClass::removeIfTUnsafe(predicate)) {
299 this->rebuildStackHeights();
300 return true;
301 } else
302 return false;
303 /* auto dst = children.begin();
304 auto al_dst = this->aligners.begin();
305 auto al_src = this->aligners.begin();
306 for (auto i: children) {
307 if (predicate(i))
308 this->disconnectOnChildChanged(*i);
309 else {
310 *dst++ = i;
311 *al_dst++ = std::move(*al_src);
312 }
313 ++al_src;
314 }
315 if (dst != children.end()) {
316 children.erase(dst, children.end());
317 this->aligners.erase(al_dst, this->aligners.end());
318 this->rebuildStackHeights();
319 return true;
320 } else
321 return false;*/
322}
323
324 template <int dim> void StackContainer<dim>::removeAtUnsafe(std::size_t index) {
325 GeometryObjectContainer<dim>::removeAtUnsafe(index);
326 this->aligners.erase(this->aligners.begin() + index);
327 stackHeights.pop_back();
328 this->updateAllHeights(index);
329}
330
331 template <int dim>
332 void StackContainer<dim>::writeXML(XMLWriter::Element& parent_xml_object,
333 GeometryObject::WriteXMLCallback& write_cb,
334 AxisNames axes) const {
335 XMLWriter::Element container_tag = write_cb.makeTag(parent_xml_object, *this, axes);
336 if (GeometryObject::WriteXMLCallback::isRef(container_tag)) return;
337 this->writeXMLAttr(container_tag, axes);
338 for (int i = int(children.size()) - 1; i >= 0; --i) { // children are written in reverse order
339 XMLWriter::Element child_tag = write_cb.makeChildTag(container_tag, *this, i);
340 writeXMLChildAttr(child_tag, i, axes);
341 if (auto child = children[i]->getChild()) child->writeXML(child_tag, write_cb, axes);
342 }
343}
344
345 template <int dim>
346 void StackContainer<dim>::writeXMLChildAttr(XMLWriter::Element& dest_xml_child_tag,
347 std::size_t child_index,
348 const AxisNames& axes) const {
349 this->aligners[child_index].writeToXML(dest_xml_child_tag, axes);
350}
351
352 template <int dim> shared_ptr<GeometryObject> StackContainer<dim>::shallowCopy() const {
353 shared_ptr<StackContainer<dim>> result = plask::make_shared<StackContainer<dim>>(this->getBaseHeight());
354 result->default_aligner = default_aligner;
355 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
356 result->addUnsafe(this->children[child_no]->getChild(), this->aligners[child_no]);
357 return result;
358}
359
360 template <int dim>
361 shared_ptr<GeometryObject> StackContainer<dim>::deepCopy(
362 std::map<const GeometryObject*, shared_ptr<GeometryObject>>& copied) const {
363 auto found = copied.find(this);
364 if (found != copied.end()) return found->second;
365 shared_ptr<StackContainer<dim>> result = plask::make_shared<StackContainer<dim>>(this->getBaseHeight());
366 result->default_aligner = default_aligner;
367 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
368 if (this->children[child_no]->getChild())
369 result->addUnsafe(static_pointer_cast<ChildType>(this->children[child_no]->getChild()->deepCopy(copied)),
370 this->aligners[child_no]);
371 copied[this] = result;
372 return result;
373}
374
375 template <int dim>
376 shared_ptr<GeometryObject> StackContainer<dim>::changedVersionForChildren(
377 std::vector<std::pair<shared_ptr<ChildType>, Vec<3, double>>>& children_after_change,
378 Vec<3, double>* /*recomended_translation*/) const {
379 shared_ptr<StackContainer<dim>> result = plask::make_shared<StackContainer<dim>>(this->getBaseHeight());
380 result->default_aligner = default_aligner;
381 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
382 if (children_after_change[child_no].first)
383 result->addUnsafe(children_after_change[child_no].first, this->aligners[child_no]);
384 return result;
385}
386
387 template <int dim> const char* StackContainer<dim>::NAME = dim == 2 ? PLASK_STACK2D_NAME : PLASK_STACK3D_NAME;
388
389 template struct PLASK_API StackContainer<2>;
390 template struct PLASK_API StackContainer<3>;
391
392 const char* ShelfContainer2D::NAME = PLASK_SHELF_NAME;
393
394 PathHints::Hint ShelfContainer2D::addGap(double size) {
395 return addUnsafe(plask::make_shared<Gap1D<2, Primitive<2>::DIRECTION_TRAN>>(size));
396}
397
398 bool ShelfContainer2D::isFlat() const {
399 std::size_t first = 0;
400 while (first < children.size() && children[first]->childHasType(GeometryObject::TYPE_SEPARATOR)) ++first;
401 if (first + 2 >= children.size()) // will we outside when we go 2 steps forward?
402 return true; //(almost) same separators
403 const double height = children[first]->getBoundingBoxSize().vert();
404 for (std::size_t i = first + 1; i < children.size(); ++i)
405 if (!children[i]->childHasType(GeometryObject::TYPE_SEPARATOR) &&
406 !is_zero(height - children[i]->getBoundingBoxSize().vert()))
407 return false;
408 return true;
409}
410
411 PathHints::Hint ShelfContainer2D::addUnsafe(const shared_ptr<ChildType>& el) {
412 if (!el) return PathHints::Hint(shared_from_this(), shared_ptr<GeometryObject>());
413
414 double el_translation, next_height;
415 auto elBB = el->getBoundingBox();
416 calcHeight(elBB, stackHeights.back(), el_translation, next_height);
417 shared_ptr<TranslationT> trans_geom = plask::make_shared<TranslationT>(el, vec(el_translation, -elBB.lower.vert()));
418 connectOnChildChanged(*trans_geom);
419 children.push_back(trans_geom);
420 stackHeights.push_back(next_height);
421
422 this->updateAllHeights();
423
424 this->fireChildrenInserted(children.size() - 1, children.size());
425 return PathHints::Hint(shared_from_this(), trans_geom);
426}
427
428 PathHints::Hint ShelfContainer2D::insertUnsafe(const shared_ptr<ChildType>& el, const std::size_t pos) {
429 if (!el) return PathHints::Hint(shared_from_this(), shared_ptr<GeometryObject>());
430
431 const auto bb = el->getBoundingBox();
432 shared_ptr<TranslationT> trans_geom =
433 plask::make_shared<TranslationT>(el, vec(stackHeights[pos] - bb.lower.tran(), -bb.lower.vert()));
434 connectOnChildChanged(*trans_geom);
435 children.insert(children.begin() + pos, trans_geom);
436 stackHeights.insert(stackHeights.begin() + pos, stackHeights[pos]);
437 const double delta = bb.upper.tran() - bb.lower.tran();
438 for (std::size_t i = pos + 1; i < children.size(); ++i) {
439 stackHeights[i] += delta;
440 children[i]->translation.tran() += delta;
441 }
442 stackHeights.back() += delta;
443
444 this->updateAllHeights();
445
446 this->fireChildrenInserted(pos, pos + 1);
447 return PathHints::Hint(shared_from_this(), trans_geom);
448}
449
450 shared_ptr<GeometryObject> ShelfContainer2D::shallowCopy() const {
451 shared_ptr<ShelfContainer2D> result = plask::make_shared<ShelfContainer2D>(this->getBaseHeight());
452 result->resizableGap = resizableGap;
453 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
454 result->addUnsafe(this->children[child_no]->getChild());
455 return result;
456}
457
458 shared_ptr<GeometryObject> ShelfContainer2D::deepCopy(
459 std::map<const GeometryObject*, shared_ptr<GeometryObject>>& copied) const {
460 auto found = copied.find(this);
461 if (found != copied.end()) return found->second;
462 shared_ptr<ShelfContainer2D> result = plask::make_shared<ShelfContainer2D>(this->getBaseHeight());
463 result->resizableGap = resizableGap;
464 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
465 if (this->children[child_no]->getChild())
466 result->addUnsafe(static_pointer_cast<ChildType>(this->children[child_no]->getChild()->deepCopy(copied)));
467 copied[this] = result;
468 return result;
469}
470
471 shared_ptr<GeometryObject> ShelfContainer2D::changedVersionForChildren(
472 std::vector<std::pair<shared_ptr<ChildType>, Vec<3, double>>>& children_after_change,
473 Vec<3, double>* /*recomended_translation*/) const {
474 shared_ptr<ShelfContainer2D> result = plask::make_shared<ShelfContainer2D>(this->getBaseHeight());
475 result->resizableGap = resizableGap;
476 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
477 if (children_after_change[child_no].first) result->addUnsafe(children_after_change[child_no].first);
478 return result;
479}
480
481 void ShelfContainer2D::writeXMLAttr(XMLWriter::Element& dest_xml_object, const AxisNames& axes) const {
482 BaseClass::writeXMLAttr(dest_xml_object, axes);
483 dest_xml_object.attr(REQUIRE_EQUAL_HEIGHTS_ATTR, false);
484}
485
486 template <typename UpperClass> bool MultiStackContainer<UpperClass>::reduceHeight(double& height) const {
487 const double zeroBasedStackHeight = stackHeights.back() - stackHeights.front();
488 const double zeroBasedRequestHeight = height - stackHeights.front();
489 if (zeroBasedRequestHeight < 0.0 || zeroBasedRequestHeight > zeroBasedStackHeight * repeat_count) return false;
490 height = std::fmod(zeroBasedRequestHeight, zeroBasedStackHeight) + stackHeights.front();
491 return true;
492}
493
494 template <typename UpperClass>
495 typename MultiStackContainer<UpperClass>::Box MultiStackContainer<UpperClass>::getBoundingBox() const {
496 Box result = UpperClass::getBoundingBox();
497 result.upper[UpperClass::GROWING_DIR] =
498 result.lower[UpperClass::GROWING_DIR] +
499 (result.upper[UpperClass::GROWING_DIR] - result.lower[UpperClass::GROWING_DIR]) * repeat_count;
500 return result;
501}
502
503 template <typename UpperClass>
504 typename MultiStackContainer<UpperClass>::Box MultiStackContainer<UpperClass>::getRealBoundingBox() const {
505 return UpperClass::getBoundingBox();
506}
507
508// TODO good but unused
509/*template <typename UpperClass>
510 bool MultiStackContainer<UpperClass>::intersects(const Box& area) const {
511 const double minusZeroBasedStackHeight = stackHeights.front() - stackHeights.back();
512 for (unsigned r = 0; r < repeat_count; ++r)
513 if (UpperClass::intersects(area.translatedUp(minusZeroBasedStackHeight*r)))
514 return true;
515 return false;
516}*/
517
518 template <typename UpperClass>
519 void MultiStackContainer<UpperClass>::getBoundingBoxesToVec(const GeometryObject::Predicate& predicate,
520 std::vector<Box>& dest,
521 const PathHints* path) const {
522 if (predicate(*this)) {
523 dest.push_back(getBoundingBox());
524 return;
525 }
526 if (repeat_count == 0) return;
527 std::size_t old_size = dest.size();
528 UpperClass::getBoundingBoxesToVec(predicate, dest, path);
529 std::size_t new_size = dest.size();
530 const double stackHeight = stackHeights.back() - stackHeights.front();
531 for (unsigned r = 1; r < repeat_count; ++r) {
532 for (std::size_t i = old_size; i < new_size; ++i) dest.push_back(dest[i]);
533 for (auto i = dest.end() - (new_size - old_size); i != dest.end(); ++i)
534 i->translateDir(UpperClass::GROWING_DIR, stackHeight * r);
535 }
536}
537
538 template <typename UpperClass>
539 void MultiStackContainer<UpperClass>::getObjectsToVec(const GeometryObject::Predicate& predicate,
540 std::vector<shared_ptr<const GeometryObject>>& dest,
541 const PathHints* path) const {
542 if (predicate(*this)) {
543 dest.push_back(this->shared_from_this());
544 return;
545 }
546 if (repeat_count == 0) return;
547 std::size_t old_size = dest.size();
548 UpperClass::getObjectsToVec(predicate, dest, path);
549 std::size_t new_size = dest.size();
550 for (unsigned r = 1; r < repeat_count; ++r)
551 for (std::size_t i = old_size; i < new_size; ++i) dest.push_back(dest[i]);
552}
553
554 template <typename UpperClass>
555 void MultiStackContainer<UpperClass>::getPositionsToVec(const GeometryObject::Predicate& predicate,
556 std::vector<DVec>& dest,
557 const PathHints* path) const {
558 if (predicate(*this)) {
559 dest.push_back(Primitive<MultiStackContainer<UpperClass>::DIM>::ZERO_VEC);
560 return;
561 }
562 if (repeat_count == 0) return;
563 std::size_t old_size = dest.size();
564 UpperClass::getPositionsToVec(predicate, dest, path);
565 std::size_t new_size = dest.size();
566 const double stackHeight = stackHeights.back() - stackHeights.front();
567 for (unsigned r = 1; r < repeat_count; ++r)
568 for (std::size_t i = old_size; i < new_size; ++i) {
569 dest.push_back(dest[i]);
570 dest.back()[UpperClass::GROWING_DIR] += stackHeight * r;
571 }
572}
573
574// template <typename UpperClass>
575// void MultiStackContainer<UpperClass>::extractToVec(const GeometryObject::Predicate &predicate, std::vector<
576// shared_ptr<const GeometryObjectD<dim> > >& dest, const PathHints *path) const {
577// if (predicate(*this)) {
578// dest.push_back(static_pointer_cast< const GeometryObjectD<dim> >(this->shared_from_this()));
579// return;
580// }
581// std::size_t old_size = dest.size();
582// UpperClass::extractToVec(predicate, dest, path);
583// std::size_t new_size = dest.size();
584// const double stackHeight = stackHeights.back() - stackHeights.front();
585// for (unsigned r = 1; r < repeat_count; ++r) {
586// Vec<dim, double> v = Primitive<dim>::ZERO_VEC;
587// v.vert() += stackHeight * r;
588// for (std::size_t i = old_size; i < new_size; ++i) {
589// dest.push_back(Translation<dim>::compress(const_pointer_cast< GeometryObjectD<dim> >(dest[i]), v));
590// }
591// }
592// }
593
594 template <typename UpperClass>
595 GeometryObject::Subtree MultiStackContainer<UpperClass>::getPathsTo(const GeometryObject& el,
596 const PathHints* path) const {
597 if (repeat_count == 0) return GeometryObject::Subtree();
598 GeometryObject::Subtree result = UpperClass::getPathsTo(el, path);
599 if (!result.empty()) {
600 const std::size_t size = result.children.size(); // original size
601 const double stackHeight = stackHeights.back() - stackHeights.front();
602 for (unsigned r = 1; r < repeat_count; ++r)
603 for (std::size_t org_child_no = 0; org_child_no < size; ++org_child_no) {
604 auto& org_child = const_cast<Translation<UpperClass::DIM>&>(
605 static_cast<const Translation<UpperClass::DIM>&>(*(result.children[org_child_no].object)));
606 shared_ptr<Translation<UpperClass::DIM>> new_child = org_child.copyShallow();
607 new_child->translation[UpperClass::GROWING_DIR] += stackHeight;
608 result.children.push_back(GeometryObject::Subtree(new_child, result.children[org_child_no].children));
609 }
610 }
611 return result;
612}
613
614 template <typename UpperClass>
615 GeometryObject::Subtree MultiStackContainer<UpperClass>::getPathsAt(
616 const typename MultiStackContainer<UpperClass>::DVec& point,
617 bool all) const {
618 if (repeat_count == 0) return GeometryObject::Subtree();
619 MultiStackContainer::DVec new_point = point;
620 if (!reduceHeight(new_point[UpperClass::GROWING_DIR])) return GeometryObject::Subtree();
621 return UpperClass::getPathsAt(new_point, all);
622}
623
624 template <typename UpperClass>
625 bool MultiStackContainer<UpperClass>::contains(const typename MultiStackContainer<UpperClass>::DVec& p) const {
626 if (repeat_count == 0) return false;
627 DVec p_reduced = p;
628 if (!reduceHeight(p_reduced[UpperClass::GROWING_DIR])) return false;
629 return UpperClass::contains(p_reduced);
630}
631
632 template <typename UpperClass>
633 shared_ptr<Material> MultiStackContainer<UpperClass>::getMaterial(
634 const typename MultiStackContainer<UpperClass>::DVec& p) const {
635 if (repeat_count == 0) return shared_ptr<Material>();
636 DVec p_reduced = p;
637 if (!reduceHeight(p_reduced[UpperClass::GROWING_DIR])) return shared_ptr<Material>();
638 return UpperClass::getMaterial(p_reduced);
639}
640
641 template <typename UpperClass>
642 shared_ptr<GeometryObject> MultiStackContainer<UpperClass>::getChildNo(std::size_t child_no) const {
643 if (child_no >= getChildrenCount()) {
644 auto children_count = getChildrenCount();
645 if (children_count == 0) throw OutOfBoundsException("getChildNo", "child_no", child_no, "nothing", "nothing");
646 throw OutOfBoundsException("getChildNo", "child_no", child_no, 0, children_count - 1);
647 }
648 if (child_no < children.size()) return children[child_no];
649 auto result = children[child_no % children.size()]->copyShallow();
650 result->translation[UpperClass::GROWING_DIR] +=
651 double(child_no / children.size()) * (stackHeights.back() - stackHeights.front());
652 return result;
653}
654
655 template <typename UpperClass> std::size_t MultiStackContainer<UpperClass>::getRealChildrenCount() const {
656 return UpperClass::getChildrenCount();
657}
658
659 template <typename UpperClass>
660 shared_ptr<GeometryObject> MultiStackContainer<UpperClass>::getRealChildNo(std::size_t child_no) const {
661 return UpperClass::getChildNo(child_no);
662}
663
664 template <typename UpperClass>
665 void MultiStackContainer<UpperClass>::writeXMLAttr(XMLWriter::Element& dest_xml_object, const AxisNames& axes) const {
666 UpperClass::writeXMLAttr(dest_xml_object, axes);
667 dest_xml_object.attr(REPEAT_ATTR, repeat_count);
668}
669
670 template <typename StackContainerT>
671 static inline void addChild(
672 StackContainerT& result,
673 const StackContainerT& src,
674 std::size_t child_no,
675 typename std::vector<std::pair<shared_ptr<typename StackContainerT::ChildType>, Vec<3, double>>>&
676 children_after_change) {
677 result.addUnsafe(children_after_change[child_no].first, src.getAlignerAt(child_no));
678}
679
680// it has to overload generic version above, but it is for shelf and so doesn't use aligners
681 static inline void addChild(
682 MultiStackContainer<ShelfContainer2D>& result,
683 const MultiStackContainer<ShelfContainer2D>& /*src*/,
684 std::size_t child_no,
685 typename std::vector<std::pair<shared_ptr<typename ShelfContainer2D::ChildType>, Vec<3, double>>>&
686 children_after_change) {
687 result.addUnsafe(children_after_change[child_no].first);
688}
689
690 template <typename StackContainerT>
691 static inline void addChild(StackContainerT& result,
692 const StackContainerT& src,
693 const shared_ptr<GeometryObject>& child,
694 std::size_t child_no) {
695 result.addUnsafe(static_pointer_cast<typename StackContainerT::ChildType>(child), src.getAlignerAt(child_no));
696}
697
698// it has to overload generic version above, but it is for shelf and so doesn't use aligners
699 static inline void addChild(MultiStackContainer<ShelfContainer2D>& result,
700 const MultiStackContainer<ShelfContainer2D>& /*src*/,
701 const shared_ptr<GeometryObject>& child,
702 std::size_t /*child_no*/) {
703 result.addUnsafe(static_pointer_cast<MultiStackContainer<ShelfContainer2D>::ChildType>(child));
704}
705
706 template <typename UpperClass> shared_ptr<GeometryObject> MultiStackContainer<UpperClass>::shallowCopy() const {
707 shared_ptr<MultiStackContainer<UpperClass>> result =
708 plask::make_shared<MultiStackContainer<UpperClass>>(this->repeat_count, this->getBaseHeight());
709 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
710 addChild(*result, *this, children[child_no]->getChild(), child_no);
711 return result;
712}
713
714 template <typename UpperClass>
715 shared_ptr<GeometryObject> MultiStackContainer<UpperClass>::deepCopy(
716 std::map<const GeometryObject*, shared_ptr<GeometryObject>>& copied) const {
717 auto found = copied.find(this);
718 if (found != copied.end()) return found->second;
719 shared_ptr<MultiStackContainer<UpperClass>> result =
720 plask::make_shared<MultiStackContainer<UpperClass>>(this->repeat_count, this->getBaseHeight());
721 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
722 if (children[child_no]->getChild())
723 addChild(*result, *this, children[child_no]->getChild()->deepCopy(copied), child_no);
724 copied[this] = result;
725 return result;
726}
727
728 template <typename UpperClass>
729 shared_ptr<GeometryObject> MultiStackContainer<UpperClass>::changedVersionForChildren(
730 std::vector<std::pair<shared_ptr<ChildType>, Vec<3, double>>>& children_after_change,
731 Vec<3, double>* /*recomended_translation*/) const {
732 shared_ptr<MultiStackContainer<UpperClass>> result =
733 plask::make_shared<MultiStackContainer<UpperClass>>(this->repeat_count, this->getBaseHeight());
734 for (std::size_t child_no = 0; child_no < children.size(); ++child_no)
735 if (children_after_change[child_no].first) addChild(*result, *this, child_no, children_after_change);
736 return result;
737}
738
739 template <typename UpperClass>
740 void MultiStackContainer<UpperClass>::addPointsAlongToSet(std::set<double>& points,
741 Primitive<3>::Direction direction,
742 unsigned max_steps,
743 double min_step_size) const {
744 if (repeat_count == 0) return;
745 if (repeat_count == 1 || direction != UpperClass::GROWING_DIR + (3 - DIM)) {
746 UpperClass::addPointsAlongToSet(points, direction, max_steps, min_step_size);
747 return;
748 }
749 double shift = stackHeights.back() - stackHeights.front();
750 std::set<double> points0;
751 UpperClass::addPointsAlongToSet(points0, direction, max_steps, min_step_size);
752 for (size_t i = 0; i < repeat_count; ++i) {
753 double trans = i * shift;
754 for (double p : points0) points.insert(p + trans);
755 }
756}
757
758 template <typename UpperClass>
759 void MultiStackContainer<UpperClass>::addLineSegmentsToSet(
760 std::set<typename GeometryObjectD<UpperClass::DIM>::LineSegment>& segments,
761 unsigned max_steps,
762 double min_step_size) const {
763 if (repeat_count == 0) return;
764 if (repeat_count == 1) {
765 UpperClass::addLineSegmentsToSet(segments, max_steps, min_step_size);
766 return;
767 }
768 typedef typename GeometryObjectD<MultiStackContainer<UpperClass>::DIM>::LineSegment LineSegment;
769 std::set<LineSegment> segments0;
770 UpperClass::addLineSegmentsToSet(segments0, max_steps, min_step_size);
771 DVec shift = Primitive<DIM>::ZERO_VEC;
772 shift[size_t(UpperClass::GROWING_DIR)] = stackHeights.back() - stackHeights.front();
773 for (size_t i = 0; i < repeat_count; ++i) {
774 DVec trans = i * shift;
775 for (const auto& s : segments0) segments.insert(LineSegment(s.p0() + trans, s.p1() + trans));
776 }
777}
778
779 template class PLASK_API MultiStackContainer<StackContainer<2>>;
780 template class PLASK_API MultiStackContainer<StackContainer<3>>;
781 template class PLASK_API MultiStackContainer<ShelfContainer2D>;
782
784 struct HeightReader {
785 XMLReader& reader;
786 const char* what;
787 int whereWasZero;
788 double alignZero;
789 bool align;
790
791 HeightReader(XMLReader& reader, const char* what)
792 : reader(reader),
793 what(what),
794 whereWasZero(reader.hasAttribute(BASEH_ATTR) ? -2 : -1),
795 alignZero(0.),
796 align(false) {}
797
798 inline void setZero(shared_ptr<plask::GeometryObject> stack) {
799 if (whereWasZero != -1) throw XMLException(reader, format("{} shift has already been specified.", what));
800 whereWasZero = int(stack->getRealChildrenCount());
801 }
802
803 bool tryReadZeroTag(shared_ptr<plask::GeometryObject> stack) {
804 if (reader.getNodeName() != "zero") return false;
805 setZero(stack);
806 reader.requireTagEnd();
807 return true;
808 }
809
810 void tryReadZeroAttr(shared_ptr<plask::GeometryObject> stack) {
811 auto zeroAttr = reader.getAttribute<double>(ZERO_ATTR);
812 if (!zeroAttr) return;
813 setZero(stack);
814 alignZero = *zeroAttr;
815 align = true;
816 }
817
818 template <typename StackPtrT> void setBaseHeight(StackPtrT stack, bool reverse) {
819 if (whereWasZero >= 0) {
820 if (align)
821 stack->alignZeroOn(reverse ? stack->getRealChildrenCount() - whereWasZero - 1 : whereWasZero,
822 alignZero);
823 else
824 stack->setZeroBefore(reverse ? stack->getRealChildrenCount() - whereWasZero : whereWasZero);
825 }
826 }
827};
828
829 template <int dim> static shared_ptr<GeometryObject> read_StackContainer(GeometryReader& reader) {
830 HeightReader height_reader(reader.source, "Stack's vertical");
831 const double baseH = reader.source.getAttribute(BASEH_ATTR, 0.0);
832
833 shared_ptr<StackContainer<dim>> result(
834 reader.source.hasAttribute(REPEAT_ATTR)
835 ? new MultiStackContainer<StackContainer<dim>>(reader.source.getAttribute(REPEAT_ATTR, 1u), baseH)
836 : new StackContainer<dim>(baseH));
837
838 result->default_aligner =
839 align::fromXML(reader.source, reader.getAxisNames(), StackContainer<dim>::DefaultAligner());
840
841 GeometryReader::SetExpectedSuffix suffixSetter(
842 reader, dim == 2 ? PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D : PLASK_GEOMETRY_TYPE_NAME_SUFFIX_3D);
843 read_children(
844 reader,
845 [&]() -> PathHints::Hint {
846 height_reader.tryReadZeroAttr(result);
847 auto aligner = align::fromXML(reader.source, reader.getAxisNames(), result->default_aligner);
848 return result->push_front(reader.readExactlyOneChild<typename StackContainer<dim>::ChildType>(), aligner);
849 },
850 [&]() {
851 if (height_reader.tryReadZeroTag(result)) return;
852 result->push_front(reader.readObject<typename StackContainer<dim>::ChildType>());
853 });
854 height_reader.setBaseHeight(result, true);
855 return result;
856}
857
858 static GeometryReader::RegisterObjectReader stack2D_reader(PLASK_STACK2D_NAME, read_StackContainer<2>);
859 static GeometryReader::RegisterObjectReader stack3D_reader(PLASK_STACK3D_NAME, read_StackContainer<3>);
860
861 static shared_ptr<GeometryObject> read_ShelfContainer2D(GeometryReader& reader) {
862 HeightReader height_reader(reader.source, "Shelf's horizontal");
863 // TODO migrate to gap which can update self
864 shared_ptr<Gap1D<2, Primitive<2>::DIRECTION_TRAN>> total_size_gap; // gap which can change total size
865 double required_total_size; // required total size, valid only if total_size_gap is not nullptr
866 const double baseH = reader.source.getAttribute(BASEH_ATTR, 0.0);
867 shared_ptr<ShelfContainer2D> result(
868 reader.source.hasAttribute(REPEAT_ATTR)
869 ? new MultiStackContainer<ShelfContainer2D>(reader.source.getAttribute(REPEAT_ATTR, 1u), baseH)
870 : new ShelfContainer2D(baseH));
871 bool requireEqHeights = reader.source.getAttribute(REQUIRE_EQUAL_HEIGHTS_ATTR, true);
872 GeometryReader::SetExpectedSuffix suffixSetter(reader, PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D);
873 read_children(
874 reader,
875 [&]() {
876 height_reader.tryReadZeroAttr(result);
877 return result->push_back(reader.readExactlyOneChild<typename ShelfContainer2D::ChildType>());
878 },
879 [&]() {
880 if (height_reader.tryReadZeroTag(result)) return;
881 shared_ptr<Gap1D<2, Primitive<2>::DIRECTION_TRAN>> this_gap;
882 if (reader.source.getNodeName() == Gap1D<2, Primitive<2>::DIRECTION_TRAN>::NAME) {
883 plask::optional<double> total_size_attr = reader.source.getAttribute<double>("total");
884 if (total_size_attr) { // total size provided?
885 if (total_size_gap) throw XMLException(reader.source, "total size has been already chosen.");
886 required_total_size = *total_size_attr;
887 total_size_gap = this_gap = static_pointer_cast<Gap1D<2, Primitive<2>::DIRECTION_TRAN>>(
888 static_pointer_cast<Translation<2>>(result->addGap(0.0).second)->getChild());
889 } else {
890 this_gap = static_pointer_cast<Gap1D<2, Primitive<2>::DIRECTION_TRAN>>(
891 static_pointer_cast<Translation<2>>(
892 result
893 ->addGap(reader.source.requireAttribute<double>(
894 Gap1D<2, Primitive<2>::DIRECTION_TRAN>::XML_SIZE_ATTR))
895 .second)
896 ->getChild());
897 }
898 reader.registerObjectNameFromCurrentNode(this_gap);
899 reader.source.requireTagEnd();
900 return;
901 }
902 result->push_back(reader.readObject<ShelfContainer2D::ChildType>());
903 });
904 if (total_size_gap) {
905 if (required_total_size < result->getHeight()) {
906 reader.manager.throwErrorIfNotDraft(
907 XMLException(reader.source, "required total width of shelf is lower than sum of children widths"));
908 total_size_gap->setSize(0);
909 } else
910 total_size_gap->setSize(required_total_size - result->getHeight());
911 }
912 height_reader.setBaseHeight(result, false);
913 if (requireEqHeights) {
914 try {
915 result->ensureFlat();
916 } catch (const Exception& e) {
917 reader.manager.throwErrorIfNotDraft(XMLException(reader.source, e.what()));
918 }
919 }
920 return result;
921}
922
923 static GeometryReader::RegisterObjectReader horizontalstack_reader(PLASK_SHELF_NAME, read_ShelfContainer2D);
924 static GeometryReader::RegisterObjectReader horizontalstack2D_reader(
925 PLASK_SHELF_NAME PLASK_GEOMETRY_TYPE_NAME_SUFFIX_2D,
926 read_ShelfContainer2D);
927
928} // namespace plask