Mantid
Loading...
Searching...
No Matches
CSGObject.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
21#include "MantidKernel/Logger.h"
26#include "MantidKernel/Quat.h"
30
31#include <boost/accumulators/accumulators.hpp>
32#include <boost/accumulators/statistics/error_of_mean.hpp>
33#include <boost/accumulators/statistics/mean.hpp>
34#include <boost/accumulators/statistics/stats.hpp>
35#include <memory>
36
37#include <array>
38#include <deque>
39#include <random>
40#include <stack>
41#include <stdexcept>
42#include <unordered_set>
43#include <utility>
44
45using namespace Mantid::Geometry;
46using namespace Mantid::Kernel;
47
48namespace {
49
51constexpr double VALID_INTERCEPT_POINT_SHIFT{2.5e-05};
52
65double triangleSolidAngle(const V3D &a, const V3D &b, const V3D &c, const V3D &observer) {
66 const V3D ao = a - observer;
67 const V3D bo = b - observer;
68 const V3D co = c - observer;
69 const double modao = ao.norm();
70 const double modbo = bo.norm();
71 const double modco = co.norm();
72 const double aobo = ao.scalar_prod(bo);
73 const double aoco = ao.scalar_prod(co);
74 const double boco = bo.scalar_prod(co);
75 const double scalTripProd = ao.scalar_prod(bo.cross_prod(co));
76 const double denom = modao * modbo * modco + modco * aobo + modbo * aoco + modao * boco;
77 if (denom != 0.0)
78 return 2.0 * atan2(scalTripProd, denom);
79 else
80 return 0.0; // not certain this is correct
81}
82
92double coneSolidAngle(const V3D &observer, const Mantid::Kernel::V3D &centre, const Mantid::Kernel::V3D &axis,
93 const double radius, const double height) {
94 // The cone is broken down into three pieces and then in turn broken down into
95 // triangles. Any triangle that has a normal facing away from the observer
96 // gives a negative solid angle and is excluded
97 // For simplicity the triangulation points are constructed such that the cone
98 // axis points up the +Z axis and then rotated into their final position
99
100 const V3D axis_direction = normalize(axis);
101 // Required rotation
102 constexpr V3D initial_axis(0., 0., 1.0);
103 const Quat transform(initial_axis, axis_direction);
104
105 // Do the base cap which is a point at the centre and nslices points around it
106 constexpr double angle_step = 2 * M_PI / Mantid::Geometry::Cone::g_NSLICES;
107 // Store the (x,y) points as they are used quite frequently
108 std::array<double, Mantid::Geometry::Cone::g_NSLICES> cos_table;
109 std::array<double, Mantid::Geometry::Cone::g_NSLICES> sin_table;
110
111 double solid_angle(0.0);
112 for (int sl = 0; sl < Mantid::Geometry::Cone::g_NSLICES; ++sl) {
113 int vertex = sl;
114 cos_table[vertex] = std::cos(angle_step * vertex);
115 sin_table[vertex] = std::sin(angle_step * vertex);
116 V3D pt2 = V3D(radius * cos_table[vertex], radius * sin_table[vertex], 0.0);
117
119 vertex = sl + 1;
120 cos_table[vertex] = std::cos(angle_step * vertex);
121 sin_table[vertex] = std::sin(angle_step * vertex);
122 } else
123 vertex = 0;
124
125 V3D pt3 = V3D(radius * cos_table[vertex], radius * sin_table[vertex], 0.0);
126
127 transform.rotate(pt2);
128 transform.rotate(pt3);
129 pt2 += centre;
130 pt3 += centre;
131
132 double sa = triangleSolidAngle(centre, pt2, pt3, observer);
133 if (sa > 0.0) {
134 solid_angle += sa;
135 }
136 }
137
138 // Now the main section
139 const double z_step = height / Cone::g_NSTACKS;
140 const double r_step = height / Cone::g_NSTACKS;
141 double z0(0.0), z1(z_step);
142 double r0(radius), r1(r0 - r_step);
143
144 // cppcheck-suppress knownConditionTrueFalse as although Cone::g_NSTACKS is currently set at 1 if this changes this
145 // code block would stop working
146 for (int st = 1; st < Cone::g_NSTACKS; ++st) {
147 for (int sl = 0; sl < Cone::g_NSLICES; ++sl) {
148 int vertex = sl;
149 V3D pt1 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], z0);
151 vertex = sl + 1;
152 else
153 vertex = 0;
154 V3D pt3 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], z0);
155
156 vertex = sl;
157 V3D pt2 = V3D(r1 * cos_table[vertex], r1 * sin_table[vertex], z1);
159 vertex = sl + 1;
160 else
161 vertex = 0;
162 V3D pt4 = V3D(r1 * cos_table[vertex], r1 * sin_table[vertex], z1);
163 // Rotations
164 transform.rotate(pt1);
165 transform.rotate(pt3);
166 transform.rotate(pt2);
167 transform.rotate(pt4);
168
169 pt1 += centre;
170 pt2 += centre;
171 pt3 += centre;
172 pt4 += centre;
173 double sa = triangleSolidAngle(pt1, pt4, pt3, observer);
174 if (sa > 0.0) {
175 solid_angle += sa;
176 }
177 sa = triangleSolidAngle(pt1, pt2, pt4, observer);
178 if (sa > 0.0) {
179 solid_angle += sa;
180 }
181 }
182
183 z0 = z1;
184 r0 = r1;
185 z1 += z_step;
186 r1 -= r_step;
187 }
188
189 // Top section
190 V3D top_centre = V3D(0.0, 0.0, height) + centre;
191 transform.rotate(top_centre);
192 top_centre += centre;
193
194 for (int sl = 0; sl < Cone::g_NSLICES; ++sl) {
195 int vertex = sl;
196 V3D pt2 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], height);
197
199 vertex = sl + 1;
200 else
201 vertex = 0;
202 V3D pt3 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], height);
203
204 // Rotate them to the correct axis orientation
205 transform.rotate(pt2);
206 transform.rotate(pt3);
207
208 pt2 += centre;
209 pt3 += centre;
210
211 double sa = triangleSolidAngle(top_centre, pt3, pt2, observer);
212 if (sa > 0.0) {
213 solid_angle += sa;
214 }
215 }
216 return solid_angle;
217}
218
228double cuboidSolidAngle(const V3D &observer, const std::vector<V3D> &vectors) {
229 // Build bounding points, then set up map of 12 bounding
230 // triangles defining the 6 surfaces of the bounding box. Using a consistent
231 // ordering of points the "away facing" triangles give -ve contributions to
232 // the solid angle and hence are ignored.
233 std::vector<V3D> pts;
234 pts.reserve(8);
235 const V3D dx = vectors[1] - vectors[0];
236 const V3D dz = vectors[3] - vectors[0];
237 pts.emplace_back(vectors[2]);
238 pts.emplace_back(vectors[2] + dx);
239 pts.emplace_back(vectors[1]);
240 pts.emplace_back(vectors[0]);
241 pts.emplace_back(vectors[2] + dz);
242 pts.emplace_back(vectors[2] + dz + dx);
243 pts.emplace_back(vectors[1] + dz);
244 pts.emplace_back(vectors[0] + dz);
245
246 constexpr unsigned int ntriangles(12);
247 std::vector<std::vector<int>> triMap(ntriangles, std::vector<int>(3, 0));
248 triMap[0][0] = 1;
249 triMap[0][1] = 4;
250 triMap[0][2] = 3;
251 triMap[1][0] = 3;
252 triMap[1][1] = 2;
253 triMap[1][2] = 1;
254 triMap[2][0] = 5;
255 triMap[2][1] = 6;
256 triMap[2][2] = 7;
257 triMap[3][0] = 7;
258 triMap[3][1] = 8;
259 triMap[3][2] = 5;
260 triMap[4][0] = 1;
261 triMap[4][1] = 2;
262 triMap[4][2] = 6;
263 triMap[5][0] = 6;
264 triMap[5][1] = 5;
265 triMap[5][2] = 1;
266 triMap[6][0] = 2;
267 triMap[6][1] = 3;
268 triMap[6][2] = 7;
269 triMap[7][0] = 7;
270 triMap[7][1] = 6;
271 triMap[7][2] = 2;
272 triMap[8][0] = 3;
273 triMap[8][1] = 4;
274 triMap[8][2] = 8;
275 triMap[9][0] = 8;
276 triMap[9][1] = 7;
277 triMap[9][2] = 3;
278 triMap[10][0] = 1;
279 triMap[10][1] = 5;
280 triMap[10][2] = 8;
281 triMap[11][0] = 8;
282 triMap[11][1] = 4;
283 triMap[11][2] = 1;
284 double sangle = 0.0;
285 for (unsigned int i = 0; i < ntriangles; i++) {
286 const double sa = triangleSolidAngle(pts[triMap[i][0] - 1], pts[triMap[i][1] - 1], pts[triMap[i][2] - 1], observer);
287 if (sa > 0)
288 sangle += sa;
289 }
290 return sangle;
291}
292
303double cylinderSolidAngle(const V3D &observer, const V3D &centre, const V3D &axis, const double radius,
304 const double height, const int numberOfSlices) {
305 // The cylinder is triangulated along its axis EXCLUDING the end caps so that
306 // stacked cylinders give the correct value of solid angle (i.e shadowing is
307 // loosely taken into account by this method) Any triangle that has a normal
308 // facing away from the observer gives a negative solid angle and is excluded
309 // For simplicity the triangulation points are constructed such that the cone
310 // axis points up the +Z axis and then rotated into their final position
311
312 // Required rotation
313 constexpr V3D initial_axis(0., 0., 1.0);
314 const Quat transform(initial_axis, axis);
315
316 // Do the base cap which is a point at the centre and nslices points around it
317 const double angle_step = 2 * M_PI / static_cast<double>(numberOfSlices);
318
319 const double z_step = height / Cylinder::g_NSTACKS;
320 double z0(0.0), z1(z_step);
321 double solid_angle(0.0);
322 for (int st = 1; st <= Cylinder::g_NSTACKS; ++st) {
323 // cppcheck-suppress knownConditionTrueFalse as although Cylinder::g_NSTACKS is currently set at 1 if this changes
324 // this code block is necessary
325 if (st == Cylinder::g_NSTACKS)
326 z1 = height;
327
328 for (int sl = 0; sl < numberOfSlices; ++sl) {
329 double x = radius * std::cos(angle_step * sl);
330 double y = radius * std::sin(angle_step * sl);
331 V3D pt1 = V3D(x, y, z0);
332 V3D pt2 = V3D(x, y, z1);
333 int vertex = (sl + 1) % numberOfSlices;
334 x = radius * std::cos(angle_step * vertex);
335 y = radius * std::sin(angle_step * vertex);
336 V3D pt3 = V3D(x, y, z0);
337 V3D pt4 = V3D(x, y, z1);
338 // Rotations
339 transform.rotate(pt1);
340 transform.rotate(pt3);
341 transform.rotate(pt2);
342 transform.rotate(pt4);
343
344 pt1 += centre;
345 pt2 += centre;
346 pt3 += centre;
347 pt4 += centre;
348
349 double sa = triangleSolidAngle(pt1, pt4, pt3, observer);
350 if (sa > 0.0) {
351 solid_angle += sa;
352 }
353 sa = triangleSolidAngle(pt1, pt2, pt4, observer);
354 if (sa > 0.0) {
355 solid_angle += sa;
356 }
357 }
358 z0 = z1;
359 z1 += z_step;
360 }
361
362 return solid_angle;
363}
364
373double sphereSolidAngle(const V3D &observer, const std::vector<V3D> &vectors, const double radius) {
374 const double distance = (observer - vectors[0]).norm();
375 if (distance > radius + Tolerance) {
376 const double sa = 2.0 * M_PI * (1.0 - cos(asin(radius / distance)));
377 return sa;
378 } else if (distance < radius - Tolerance)
379 return 4.0 * M_PI; // internal point
380 else
381 return 2.0 * M_PI; // surface point
382}
383} // namespace
384
385namespace Mantid::Geometry {
386
387namespace {
388Kernel::Logger logger("CSGObject");
389}
394
399CSGObject::CSGObject(std::string shapeXML)
400 : m_topRule(nullptr), m_boundingBox(), AABBxMax(0), AABByMax(0), AABBzMax(0), AABBxMin(0), AABByMin(0), AABBzMin(0),
401 boolBounded(false), m_objNum(0), m_handler(std::make_shared<GeometryHandler>(this)), bGeometryCaching(false),
402 vtkCacheReader(std::shared_ptr<vtkGeometryCacheReader>()),
403 vtkCacheWriter(std::shared_ptr<vtkGeometryCacheWriter>()), m_shapeXML(std::move(shapeXML)), m_id(),
404 m_material(std::make_unique<Material>()) {}
405
410CSGObject::CSGObject(const CSGObject &A) : CSGObject() { *this = A; }
411
418 if (this != &A) {
419 m_topRule = (A.m_topRule) ? A.m_topRule->clone() : nullptr;
420 if (m_topRule) {
421 m_topRule->setParent(nullptr); // Top rule has no parent
422 m_topRule->makeParents();
423 }
424 AABBxMax = A.AABBxMax;
425 AABByMax = A.AABByMax;
426 AABBzMax = A.AABBzMax;
427 AABBxMin = A.AABBxMin;
428 AABByMin = A.AABByMin;
429 AABBzMin = A.AABBzMin;
431 m_objNum = A.m_objNum;
432 m_handler = A.m_handler->clone();
437 m_id = A.m_id;
438 m_material = std::make_unique<Material>(A.material());
439
440 if (m_topRule)
442 }
443 return *this;
444}
445
447CSGObject::~CSGObject() = default;
448
452void CSGObject::setMaterial(const Kernel::Material &material) { m_material = std::make_unique<Material>(material); }
453
458
465 // Assume invalid shape if object has no 'm_topRule' or surfaces
466 return (m_topRule != nullptr && !m_surList.empty());
467}
468
475int CSGObject::setObject(const int objName, const std::string &lineStr) {
476 // Split line
477 // Does the string now contain junk...
478 static const boost::regex letters("[a-zA-Z]");
479 if (Mantid::Kernel::Strings::StrLook(lineStr, letters))
480 return 0;
481
482 procString(lineStr);
483 m_surList.clear();
484 m_objNum = objName;
485 return 1;
486}
487
493void CSGObject::convertComplement(const std::map<int, CSGObject> &MList)
494
495{
496 this->procString(this->cellStr(MList));
497}
498
505std::string CSGObject::cellStr(const std::map<int, CSGObject> &MList) const {
506 std::string TopStr = this->topRule()->display();
507 std::string::size_type pos = TopStr.find('#');
508 std::ostringstream cx;
509 while (pos != std::string::npos) {
510 pos++;
511 cx << TopStr.substr(0, pos); // Everything including the #
512 int cN(0);
513 const int nLen = Mantid::Kernel::Strings::convPartNum(TopStr.substr(pos), cN);
514 if (nLen > 0) {
515 cx << "(";
516 auto vc = MList.find(cN);
517 if (vc == MList.end())
518 throw Kernel::Exception::NotFoundError("Not found in the list of indexable hulls (Object::cellStr)", cN);
519 // Not the recursion :: This will cause no end of problems
520 // if there is an infinite loop.
521 cx << vc->second.cellStr(MList);
522 cx << ") ";
523 pos += nLen;
524 }
525 TopStr.erase(0, pos);
526 pos = TopStr.find('#');
527 }
528 cx << TopStr;
529 return cx.str();
530}
531
538
539 if (m_topRule)
540 return m_topRule->isComplementary();
541 return 0;
542}
543
551int CSGObject::populate(const std::map<int, std::shared_ptr<Surface>> &surfMap) {
552 std::deque<Rule *> rules;
553 rules.emplace_back(m_topRule.get());
554 while (!rules.empty()) {
555 Rule *T1 = rules.front();
556 rules.pop_front();
557 if (T1) {
558 // if an actual surface process :
559 auto *surface = dynamic_cast<SurfPoint *>(T1);
560 if (surface) {
561 // Ensure that we have a it in the surface list:
562 auto mapFound = surfMap.find(surface->getKeyN());
563 if (mapFound != surfMap.end()) {
564 surface->setKey(mapFound->second);
565 } else {
566 throw Kernel::Exception::NotFoundError("Object::populate", surface->getKeyN());
567 }
568 }
569 // Not a surface : Determine leaves etc and add to stack:
570 else {
571 Rule *TA = T1->leaf(0);
572 Rule *TB = T1->leaf(1);
573 if (TA)
574 rules.emplace_back(TA);
575 if (TB)
576 rules.emplace_back(TB);
577 }
578 }
579 }
581 return 0;
582}
583
595int CSGObject::procPair(std::string &lineStr, std::map<int, std::unique_ptr<Rule>> &ruleMap, int &compUnit) const
596
597{
598 unsigned int Rstart;
599 unsigned int Rend;
600 int Ra, Rb;
601
602 for (Rstart = 0; Rstart < lineStr.size() && lineStr[Rstart] != 'R'; Rstart++)
603 ;
604
605 int type = 0; // intersection
606
607 // plus 1 to skip 'R'
608 if (Rstart == lineStr.size() || !Mantid::Kernel::Strings::convert(lineStr.c_str() + Rstart + 1, Ra) ||
609 ruleMap.find(Ra) == ruleMap.end())
610 return 0;
611
612 for (Rend = Rstart + 1; Rend < lineStr.size() && lineStr[Rend] != 'R'; Rend++) {
613 if (lineStr[Rend] == ':')
614 type = 1; // make union
615 }
616 if (Rend == lineStr.size() || !Mantid::Kernel::Strings::convert(lineStr.c_str() + Rend + 1, Rb) ||
617 ruleMap.find(Rb) == ruleMap.end()) {
618 // No second rule but we did find the first one
619 compUnit = Ra;
620 return 0;
621 }
622 // Get end of number (digital)
623 for (Rend++; Rend < lineStr.size() && lineStr[Rend] >= '0' && lineStr[Rend] <= '9'; Rend++)
624 ;
625
626 // Get rules
627 auto Join =
628 (type) ? std::unique_ptr<Rule>(std::make_unique<Union>(std::move(ruleMap[Ra]), std::move(ruleMap[Rb])))
629 : std::unique_ptr<Rule>(std::make_unique<Intersection>(std::move(ruleMap[Ra]), std::move(ruleMap[Rb])));
630 ruleMap[Ra] = std::move(Join);
631 ruleMap.erase(ruleMap.find(Rb));
632
633 // Remove space round pair
634 int strPos;
635 for (strPos = Rstart - 1; strPos >= 0 && lineStr[strPos] == ' '; strPos--)
636 ;
637 Rstart = (strPos < 0) ? 0 : strPos;
638 for (strPos = Rend; strPos < static_cast<int>(lineStr.size()) && lineStr[strPos] == ' '; strPos++)
639 ;
640 Rend = strPos;
641
642 std::stringstream newRuleStr;
643 newRuleStr << " R" << Ra << " ";
644 lineStr.replace(Rstart, Rend, newRuleStr.str());
645 compUnit = Ra;
646 return 1;
647}
648
654std::unique_ptr<CompGrp> CSGObject::procComp(std::unique_ptr<Rule> ruleItem) const {
655 if (!ruleItem)
656 return std::make_unique<CompGrp>();
657
658 Rule *Pptr = ruleItem->getParent();
659 const Rule *RItemptr = ruleItem.get();
660 auto CG = std::make_unique<CompGrp>(Pptr, std::move(ruleItem));
661 if (Pptr) {
662 const int Ln = Pptr->findLeaf(RItemptr);
663 Pptr->setLeaf(std::move(CG), Ln);
664 // CG already in tree. Return empty object.
665 return std::make_unique<CompGrp>();
666 }
667 return CG;
668}
669
688bool CSGObject::isOnSide(const Kernel::V3D &point) const {
689 std::vector<Kernel::V3D> Snorms; // Normals from the contact surface.
690 Snorms.reserve(m_surList.size());
691
692 for (auto vc = m_surList.begin(); vc != m_surList.end(); ++vc) {
693 if ((*vc)->onSurface(point)) {
694 Snorms.emplace_back((*vc)->surfaceNormal(point));
695 // can check direct normal here since one success
696 // means that we can return true and finish
697 if (!checkSurfaceValid(point, Snorms.back()))
698 return true;
699 }
700 }
701 Kernel::V3D NormPair;
702 for (auto xs = Snorms.begin(); xs != Snorms.end(); ++xs)
703 for (auto ys = std::next(xs); ys != Snorms.end(); ++ys) {
704 NormPair = (*ys) + (*xs);
705 try {
706 NormPair.normalize();
707 if (!checkSurfaceValid(point, NormPair))
708 return true;
709 } catch (std::runtime_error &) {
710 }
711 }
712 // Ok everthing failed
713 return false;
714}
715
726int CSGObject::checkSurfaceValid(const Kernel::V3D &point, const Kernel::V3D &direction) const {
727 int status(0);
728 Kernel::V3D tmp = point + direction * (Kernel::Tolerance * 5.0);
729 status = (!isValid(tmp)) ? 1 : -1;
730 tmp -= direction * (Kernel::Tolerance * 10.0);
731 status += (!isValid(tmp)) ? 1 : -1;
732 return status / 2;
733}
734
740bool CSGObject::isValid(const Kernel::V3D &point) const {
741 if (!m_topRule)
742 return false;
743 return m_topRule->isValid(point);
744}
745
751bool CSGObject::isValid(const std::map<int, int> &SMap) const {
752 if (!m_topRule)
753 return false;
754 return m_topRule->isValid(SMap);
755}
756
763int CSGObject::createSurfaceList(const int outFlag) {
764 m_surList.clear();
765 std::stack<const Rule *> TreeLine;
766 TreeLine.push(m_topRule.get());
767 while (!TreeLine.empty()) {
768 const Rule *tmpA = TreeLine.top();
769 TreeLine.pop();
770 const Rule *tmpB = tmpA->leaf(0);
771 const Rule *tmpC = tmpA->leaf(1);
772 if (tmpB || tmpC) {
773 if (tmpB)
774 TreeLine.push(tmpB);
775 if (tmpC)
776 TreeLine.push(tmpC);
777 } else {
778 const auto *SurX = dynamic_cast<const SurfPoint *>(tmpA);
779 if (SurX) {
780 m_surList.emplace_back(SurX->getKey());
781 }
782 }
783 }
784 // Remove duplicates without reordering
785 std::unordered_set<const Surface *> uniqueSurfacePtrs;
786
787 auto newEnd = std::remove_if(m_surList.begin(), m_surList.end(), [&uniqueSurfacePtrs](const Surface *sPtr) {
788 if (uniqueSurfacePtrs.find(sPtr) != std::end(uniqueSurfacePtrs)) {
789 return true;
790 } else {
791 uniqueSurfacePtrs.insert(sPtr);
792 return false;
793 };
794 });
795 m_surList.erase(newEnd, m_surList.end());
796
797 if (outFlag) {
798
799 std::vector<const Surface *>::const_iterator vc;
800 for (vc = m_surList.begin(); vc != m_surList.end(); ++vc) {
801 logger.debug() << "Point == " << *vc << '\n';
802 logger.debug() << (*vc)->getName() << '\n';
803 }
804 }
805 return 1;
806}
807
812std::vector<int> CSGObject::getSurfaceIndex() const {
813 std::vector<int> out;
814 transform(m_surList.begin(), m_surList.end(), std::insert_iterator<std::vector<int>>(out, out.begin()),
815 std::mem_fn(&Surface::getName));
816 return out;
817}
818
826int CSGObject::removeSurface(const int surfNum) {
827 if (!m_topRule)
828 return -1;
829 const int nRemoved = Rule::removeItem(m_topRule, surfNum);
830 if (nRemoved)
832 return nRemoved;
833}
834
842int CSGObject::substituteSurf(const int surfNum, const int newSurfNum, const std::shared_ptr<Surface> &surfPtr) {
843 if (!m_topRule)
844 return 0;
845 const int out = m_topRule->substituteSurf(surfNum, newSurfNum, surfPtr);
846 if (out)
848 return out;
849}
850
854void CSGObject::print() const {
855 std::deque<Rule *> rst;
856 std::vector<int> Cells;
857 int Rcount(0);
858 rst.emplace_back(m_topRule.get());
859 Rule *TA, *TB; // Temp. for storage
860
861 while (!rst.empty()) {
862 const Rule *T1 = rst.front();
863 rst.pop_front();
864 if (T1) {
865 Rcount++;
866 const auto *surface = dynamic_cast<const SurfPoint *>(T1);
867 if (surface)
868 Cells.emplace_back(surface->getKeyN());
869 else {
870 TA = T1->leaf(0);
871 TB = T1->leaf(1);
872 if (TA)
873 rst.emplace_back(TA);
874 if (TB)
875 rst.emplace_back(TB);
876 }
877 }
878 }
879
880 logger.debug() << "Name == " << m_objNum << '\n';
881 logger.debug() << "Rules == " << Rcount << '\n';
882 std::vector<int>::const_iterator mc;
883 logger.debug() << "Surface included == ";
884 for (mc = Cells.begin(); mc < Cells.end(); ++mc) {
885 logger.debug() << (*mc) << " ";
886 }
887 logger.debug() << '\n';
888}
889
894 std::unique_ptr<Rule> NCG = procComp(std::move(m_topRule));
895 m_topRule = std::move(NCG);
896}
897
902 logger.debug() << "Name == " << m_objNum << '\n';
903 logger.debug() << m_topRule->display() << '\n';
904}
905
911std::string CSGObject::cellCompStr() const {
912 std::ostringstream objStr;
913 if (m_topRule)
914 objStr << m_topRule->display();
915 return objStr.str();
916}
917
923std::string CSGObject::str() const {
924 std::ostringstream objStr;
925 if (m_topRule) {
926 objStr << m_objNum << " ";
927 objStr << m_topRule->display();
928 }
929 return objStr.str();
930}
931
937void CSGObject::write(std::ostream &outStream) const {
938 std::ostringstream objStr;
939 objStr.precision(10);
940 objStr << str();
941 Mantid::Kernel::Strings::writeMCNPX(objStr.str(), outStream);
942}
943
949void CSGObject::procString(const std::string &lineStr) {
950 m_topRule = nullptr;
951 std::map<int, std::unique_ptr<Rule>> RuleList; // List for the rules
952 int Ridx = 0; // Current index (not necessary size of RuleList
953 // SURFACE REPLACEMENT
954 // Now replace all free planes/Surfaces with appropiate Rxxx
955 std::unique_ptr<SurfPoint> TmpR; // Tempory Rule storage position
956 std::unique_ptr<CompObj> TmpO; // Tempory Rule storage position
957
958 std::string Ln = lineStr;
959 // Remove all surfaces :
960 std::ostringstream cx;
961 const std::string::size_type length = Ln.length();
962 for (size_t i = 0; i < length; i++) {
963 if (isdigit(Ln[i]) || Ln[i] == '-') {
964 int SN;
965 int nLen = Mantid::Kernel::Strings::convPartNum(Ln.substr(i), SN);
966 if (!nLen)
967 throw std::invalid_argument("Invalid surface string in Object::ProcString : " + lineStr);
968 // Process #Number
969 if (i != 0 && Ln[i - 1] == '#') {
970 TmpO = std::make_unique<CompObj>();
971 TmpO->setObjN(SN);
972 RuleList[Ridx] = std::move(TmpO);
973 } else // Normal rule
974 {
975 TmpR = std::make_unique<SurfPoint>();
976 TmpR->setKeyN(SN);
977 RuleList[Ridx] = std::move(TmpR);
978 }
979 cx << " R" << Ridx << " ";
980 Ridx++;
981 i += nLen;
982 }
983 if (i < length)
984 cx << Ln[i];
985 }
986 Ln = cx.str();
987 // PROCESS BRACKETS
988
989 int brack_exists = 1;
990 while (brack_exists) {
991 std::string::size_type rbrack = Ln.find(')');
992 std::string::size_type lbrack = Ln.rfind('(', rbrack);
993 if (rbrack != std::string::npos && lbrack != std::string::npos) {
994 std::string Lx = Ln.substr(lbrack + 1, rbrack - lbrack - 1);
995 // Check to see if a #( unit
996 int compUnit(0);
997 while (procPair(Lx, RuleList, compUnit))
998 ;
999 Ln.replace(lbrack, 1 + rbrack - lbrack, Lx);
1000 // Search back and find if # ( exists.
1001 int hCnt;
1002 for (hCnt = static_cast<int>(lbrack) - 1; hCnt >= 0 && isspace(Ln[hCnt]); hCnt--)
1003 ;
1004 if (hCnt >= 0 && Ln[hCnt] == '#') {
1005 RuleList[compUnit] = procComp(std::move(RuleList[compUnit]));
1006 Ln.erase(hCnt, lbrack - hCnt);
1007 }
1008 } else
1009 brack_exists = 0;
1010 }
1011 // Do outside loop...
1012 int nullInt;
1013 while (procPair(Ln, RuleList, nullInt)) {
1014 }
1015
1016 if (RuleList.size() == 1) {
1017 m_topRule = std::move((RuleList.begin())->second);
1018 } else {
1019 throw std::logic_error("Object::procString() - Unexpected number of "
1020 "surface rules found. Expected=1, found=" +
1021 std::to_string(RuleList.size()));
1022 }
1023}
1024
1031 // Number of intersections original track
1032 int originalCount = track.count();
1033
1034 // Loop over all the surfaces to get the intercepts, i.e. populating
1035 // points into LI
1036 LineIntersectVisit LI(track.startPoint(), track.direction());
1037
1038 for (auto &surface : m_surList) {
1039 surface->acceptVisitor(LI);
1040 }
1041
1042 // Call the pruner so that we don't have to worry about the duplicates and
1043 // the order
1045
1046 // IPoints: std::vector<Geometry::V3D>
1047 const auto &IPoints(LI.getPoints());
1048 // dPoints: std::vector<double>, distance to the start point for each point
1049 const auto &dPoints(LI.getDistance());
1050 // nPoints: size_t, total number of points, for most shape, this number should
1051 // be a single digit number
1052 const size_t nPoints(IPoints.size());
1053
1054 // Loop over all the points and add them to the track
1055 for (size_t i = 0; i < nPoints; i++) {
1056 // skip over the points that are before the starting points
1057 if (dPoints[i] < 0)
1058 continue;
1059
1060 //
1061 const auto &currentPt(IPoints[i]);
1062 const auto &prePt((i == 0 || dPoints[i - 1] <= 0) ? track.startPoint() : IPoints[i - 1]);
1063 const auto &nextPt(i + 1 < nPoints ? IPoints[i + 1] : currentPt + currentPt - prePt);
1064
1065 // get the intercept type
1066 const TrackDirection trackType = calcValidTypeBy3Points(prePt, currentPt, nextPt);
1067 // only record the intercepts that is interacting with the shape directly
1068 if (trackType != TrackDirection::INVALID) {
1069 track.addPoint(trackType, currentPt, *this);
1070 }
1071 }
1072
1073 track.buildLink();
1074 // Return number of track segments added
1075 return (track.count() - originalCount);
1076}
1077
1084double CSGObject::distance(const Geometry::Track &track) const {
1085 LineIntersectVisit LI(track.startPoint(), track.direction());
1086 for (auto &surface : m_surList) {
1087 surface->acceptVisitor(LI);
1088 }
1090 const auto &distances(LI.getDistance());
1091 if (!distances.empty()) {
1092 return std::abs(*std::min_element(std::begin(distances), std::end(distances)));
1093 } else {
1094 std::ostringstream os;
1095 os << "Unable to find intersection with object with track starting at " << track.startPoint() << " in direction "
1096 << track.direction() << "\n";
1097 throw std::runtime_error(os.str());
1098 }
1099}
1100
1110 // NOTE: This method is sensitive to the geometry dimension, and will lead to
1111 // incorrect identification due to the value of VALID_INTERCEPT_POINT_SHIFT
1112 // being either too large or too small.
1113 const Kernel::V3D shift(uVec * VALID_INTERCEPT_POINT_SHIFT);
1114 const int flagA = isValid(point - shift);
1115 const int flagB = isValid(point + shift);
1116 if (!(flagA ^ flagB))
1119}
1120
1131 const Kernel::V3D &nxtPt) const {
1132 // upstream point
1133 const auto upstreamPt = (prePt + curPt) * 0.5;
1134 const auto upstreamPtInsideShape = isValid(upstreamPt);
1135 // downstream point
1136 const auto downstreamPt = (curPt + nxtPt) * 0.5;
1137 const auto downstreamPtInsideShape = isValid(downstreamPt);
1138 // NOTE:
1139 // When the track is parallel to the shape, it can still intersect with its
1140 // component (infinite) surface.
1141 // __Legends__
1142 // o-->: track
1143 // o: track starting point
1144 // m: upstreamPt
1145 // x: currentPt
1146 // d: downstreamPt
1147 // | | o
1148 // | | |
1149 // ---------|--------------------|---------------x------
1150 // | SHAPE | |
1151 // o--m---x-d----> o---m---x-d----> v Invalid
1152 // | Entering | Leaving
1153 // ---------|--------------------|----------------------
1154 // | |
1155 if (!(upstreamPtInsideShape ^ downstreamPtInsideShape))
1157 else
1159}
1160
1170double CSGObject::solidAngle(const SolidAngleParams &params) const {
1171 if (this->numberOfTriangles() > 30000)
1172 return rayTraceSolidAngle(params.observer());
1173 return triangulatedSolidAngle(params);
1174}
1175
1184double CSGObject::solidAngle(const SolidAngleParams &params, const Kernel::V3D &scaleFactor) const {
1185 return triangulatedSolidAngle(params, scaleFactor);
1186}
1187
1193double CSGObject::rayTraceSolidAngle(const Kernel::V3D &observer) const {
1194 // Calculation of solid angle as numerical double integral over all angles.
1195 // This could be optimized further e.g. by using a light weight version of
1196 // the interceptSurface method - this does more work than is necessary in this
1197 // application.
1198 // Accuracy is of the order of 1% for objects with an accurate bounding box,
1199 // though less in the case of high aspect ratios.
1200 //
1201 // resBB controls accuracy and cost - linear accuracy improvement with
1202 // increasing res, but quadratic increase in run time. If no bounding box
1203 // found, resNoBB used instead.
1204 const int resNoBB = 200, resPhiMin = 10;
1205 int res = resNoBB, itheta, jphi, resPhi;
1206 double theta, phi, sum, dphi, dtheta;
1207 if (this->isValid(observer) && !this->isOnSide(observer))
1208 return 4 * M_PI; // internal point
1209 if (this->isOnSide(observer))
1210 return 2 * M_PI; // this is wrong if on an edge
1211 // Use BB if available, and if observer not within it
1212 const BoundingBox &boundingBox = getBoundingBox();
1213 double thetaMax = M_PI;
1214 bool useBB = false, usePt = false;
1215 Kernel::V3D ptInObject, axis;
1216 Quat zToPt;
1217
1218 // Is the bounding box a reasonable one?
1219 if (boundingBox.isNonNull() && !boundingBox.isPointInside(observer)) {
1220 useBB = usePt = true;
1221 thetaMax = boundingBox.angularWidth(observer);
1222 ptInObject = boundingBox.centrePoint();
1223 const int resBB = 100;
1224 res = resBB;
1225 }
1226 // Try and find a point in the object if useful bounding box not found
1227 if (!useBB) {
1228 usePt = getPointInObject(ptInObject) == 1;
1229 }
1230 if (usePt) {
1231 // found point in object, now get rotation that maps z axis to this
1232 // direction from observer
1233 ptInObject -= observer;
1234 double theta0 = -180.0 / M_PI * acos(ptInObject.Z() / ptInObject.norm());
1235 Kernel::V3D zDir(0.0, 0.0, 1.0);
1236 axis = ptInObject.cross_prod(zDir);
1237 if (axis.nullVector())
1238 axis = Kernel::V3D(1.0, 0.0, 0.0);
1239 zToPt(theta0, axis);
1240 }
1241 dtheta = thetaMax / res;
1242 int count = 0, countPhi;
1243 sum = 0.;
1244 for (itheta = 1; itheta <= res; itheta++) {
1245 // itegrate theta from 0 to maximum from bounding box, or PI otherwise
1246 theta = thetaMax * (itheta - 0.5) / res;
1247 resPhi = static_cast<int>(res * sin(theta));
1248 if (resPhi < resPhiMin)
1249 resPhi = resPhiMin;
1250 dphi = 2 * M_PI / resPhi;
1251 countPhi = 0;
1252 for (jphi = 1; jphi <= resPhi; jphi++) {
1253 // integrate phi from 0 to 2*PI
1254 phi = 2.0 * M_PI * (jphi - 0.5) / resPhi;
1255 Kernel::V3D dir(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
1256 if (usePt)
1257 zToPt.rotate(dir);
1258 if (!useBB || boundingBox.doesLineIntersect(observer, dir)) {
1259 Track tr(observer, dir);
1260 if (this->interceptSurface(tr) > 0) {
1261 sum += dtheta * dphi * sin(theta);
1262 countPhi++;
1263 }
1264 }
1265 }
1266 // this break (only used if no BB defined) may be wrong if object has hole
1267 // in middle
1268 if (!useBB && countPhi == 0)
1269 break;
1270 count += countPhi;
1271 }
1272 if (!useBB && count < resPhiMin + 1) {
1273 // case of no bound box defined and object has few if any points in sum
1274 // redo integration on finer scale
1275 thetaMax = thetaMax * (itheta - 0.5) / res;
1276 dtheta = thetaMax / res;
1277 sum = 0;
1278 for (itheta = 1; itheta <= res; itheta++) {
1279 theta = thetaMax * (itheta - 0.5) / res;
1280 resPhi = static_cast<int>(res * sin(theta));
1281 if (resPhi < resPhiMin)
1282 resPhi = resPhiMin;
1283 dphi = 2 * M_PI / resPhi;
1284 countPhi = 0;
1285 for (jphi = 1; jphi <= resPhi; jphi++) {
1286 phi = 2.0 * M_PI * (jphi - 0.5) / resPhi;
1287 Kernel::V3D dir(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
1288 if (usePt)
1289 zToPt.rotate(dir);
1290 Track tr(observer, dir);
1291 if (this->interceptSurface(tr) > 0) {
1292 sum += dtheta * dphi * sin(theta);
1293 countPhi++;
1294 }
1295 }
1296 if (countPhi == 0)
1297 break;
1298 }
1299 }
1300
1301 return sum;
1302}
1303
1312 //
1313 // Because the triangles from OC are not consistently ordered wrt their
1314 // outward normal internal points give incorrect solid angle. Surface
1315 // points are difficult to get right with the triangle based method.
1316 // Hence catch these two (unlikely) cases.
1317 const auto &observer = params.observer();
1318 const BoundingBox &boundingBox = this->getBoundingBox();
1319 if (boundingBox.isNonNull() && boundingBox.isPointInside(observer)) {
1320 if (isValid(observer)) {
1321 if (isOnSide(observer))
1322 return (2.0 * M_PI);
1323 else
1324 return (4.0 * M_PI);
1325 }
1326 }
1327
1328 // If the object is a simple shape use the special methods
1329 double height(0.0), radius(0.0), innerRadius(0.0);
1331 std::vector<Mantid::Kernel::V3D> geometry_vectors;
1332 // Maximum of 4 vectors depending on the type
1333 geometry_vectors.reserve(4);
1334 this->GetObjectGeom(type, geometry_vectors, innerRadius, radius, height);
1335 auto nTri = this->numberOfTriangles();
1336
1337 // Cylinders are by far the most frequently used
1338 switch (type) {
1340 return cuboidSolidAngle(observer, geometry_vectors);
1341 break;
1343 return sphereSolidAngle(observer, geometry_vectors, radius);
1344 break;
1346 return cylinderSolidAngle(observer, geometry_vectors[0], geometry_vectors[1], radius, height,
1347 params.cylinderSlices());
1348 break;
1350 return coneSolidAngle(observer, geometry_vectors[0], geometry_vectors[1], radius, height);
1351 break;
1352 default:
1353 if (nTri == 0) // Fall back to raytracing if there are no triangles
1354 {
1355 return rayTraceSolidAngle(observer);
1356 } else { // Compute a generic shape that has been triangulated
1357 const auto &vertices = this->getTriangleVertices();
1358 const auto &faces = this->getTriangleFaces();
1359 double sangle(0.0), sneg(0.0);
1360 for (size_t i = 0; i < nTri; i++) {
1361 int p1 = faces[i * 3], p2 = faces[i * 3 + 1], p3 = faces[i * 3 + 2];
1362 V3D vp1 = V3D(vertices[3 * p1], vertices[3 * p1 + 1], vertices[3 * p1 + 2]);
1363 V3D vp2 = V3D(vertices[3 * p2], vertices[3 * p2 + 1], vertices[3 * p2 + 2]);
1364 V3D vp3 = V3D(vertices[3 * p3], vertices[3 * p3 + 1], vertices[3 * p3 + 2]);
1365 double sa = triangleSolidAngle(vp1, vp2, vp3, observer);
1366 if (sa > 0.0) {
1367 sangle += sa;
1368 } else {
1369 sneg += sa;
1370 }
1371 }
1372 /* We assume that objects are opaque to neutrons and that objects define
1373 * closed surfaces which are convex. For such objects negative solid angle
1374 * equals positive solid angle. This is true providing that the winding
1375 * order is defined properly such that the contribution from each triangle
1376 * w.r.t the observer gets counted to either the negative or positive
1377 * contribution correctly. If that is done correctly then it would only be
1378 * necessary to consider the positive contribution to the solid angle.
1379 *
1380 * The following provides a fix to situations where the winding order is
1381 * incorrectly defined. It does not matter if the contribution is positive
1382 * or negative since we take the average.
1383 */
1384 return 0.5 * (sangle - sneg);
1385 }
1386 }
1387}
1398double CSGObject::triangulatedSolidAngle(const SolidAngleParams &params, const V3D &scaleFactor) const {
1399 //
1400 // Because the triangles from OC are not consistently ordered wrt their
1401 // outward normal internal points give incorrect solid angle. Surface
1402 // points are difficult to get right with the triangle based method.
1403 // Hence catch these two (unlikely) cases.
1404 const auto &observer = params.observer();
1405 const BoundingBox &boundingBox = this->getBoundingBox();
1406 double sx = scaleFactor[0], sy = scaleFactor[1], sz = scaleFactor[2];
1407 const V3D sObserver = observer;
1408 if (boundingBox.isNonNull() && boundingBox.isPointInside(sObserver)) {
1409 if (isValid(sObserver)) {
1410 if (isOnSide(sObserver))
1411 return (2.0 * M_PI);
1412 else
1413 return (4.0 * M_PI);
1414 }
1415 }
1416
1417 auto nTri = this->numberOfTriangles();
1418 //
1419 // If triangulation is not available fall back to ray tracing method, unless
1420 // object is a standard shape, currently Cuboid or Sphere. Should add Cylinder
1421 // and Cone cases as well.
1422 //
1423 if (nTri == 0) {
1424 double height = 0.0, radius(0.0), innerRadius;
1426 std::vector<Kernel::V3D> vectors;
1427 this->GetObjectGeom(type, vectors, innerRadius, radius, height);
1428 switch (type) {
1430 std::transform(vectors.begin(), vectors.end(), vectors.begin(),
1431 [scaleFactor](const V3D &v) { return v * scaleFactor; });
1432 return cuboidSolidAngle(observer, vectors);
1433 break;
1435 return sphereSolidAngle(observer, vectors, radius);
1436 break;
1437 default:
1438 break;
1439 }
1440
1441 //
1442 // No special case, do the ray trace.
1443 //
1444 return rayTraceSolidAngle(observer);
1445 }
1446 const auto &vertices = this->getTriangleVertices();
1447 const auto &faces = this->getTriangleFaces();
1448 double sangle(0.0), sneg(0.0);
1449 for (size_t i = 0; i < nTri; i++) {
1450 int p1 = faces[i * 3], p2 = faces[i * 3 + 1], p3 = faces[i * 3 + 2];
1451 // would be more efficient to pre-multiply the vertices (copy of) by these
1452 // factors beforehand
1453 V3D vp1 = V3D(sx * vertices[3 * p1], sy * vertices[3 * p1 + 1], sz * vertices[3 * p1 + 2]);
1454 V3D vp2 = V3D(sx * vertices[3 * p2], sy * vertices[3 * p2 + 1], sz * vertices[3 * p2 + 2]);
1455 V3D vp3 = V3D(sx * vertices[3 * p3], sy * vertices[3 * p3 + 1], sz * vertices[3 * p3 + 2]);
1456 double sa = triangleSolidAngle(vp1, vp2, vp3, observer);
1457 if (sa > 0.0)
1458 sangle += sa;
1459 else
1460 sneg += sa;
1461 }
1462 return (0.5 * (sangle - sneg));
1463}
1464
1470double CSGObject::volume() const {
1472 double height;
1473 double radius;
1474 double innerRadius;
1475 std::vector<Kernel::V3D> vectors;
1476 this->GetObjectGeom(type, vectors, innerRadius, radius, height);
1477 switch (type) {
1479 // Here, the volume is calculated by the triangular method.
1480 // We use one of the vertices (vectors[0]) as the reference
1481 // point.
1482 double volumeTri = 0.0;
1483 // Vertices. Name codes follow flb = front-left-bottom etc.
1484 const Kernel::V3D &flb = vectors[0];
1485 const Kernel::V3D &flt = vectors[1];
1486 const Kernel::V3D &frb = vectors[3];
1487 const Kernel::V3D frt = frb + flt - flb;
1488 const Kernel::V3D &blb = vectors[2];
1489 const Kernel::V3D blt = blb + flt - flb;
1490 const Kernel::V3D brb = blb + frb - flb;
1491 const Kernel::V3D brt = frt + blb - flb;
1492 // Normals point out, follow right-handed rule when
1493 // defining the triangle faces.
1494 volumeTri += flb.scalar_prod(flt.cross_prod(blb));
1495 volumeTri += blb.scalar_prod(flt.cross_prod(blt));
1496 volumeTri += flb.scalar_prod(frb.cross_prod(flt));
1497 volumeTri += frb.scalar_prod(frt.cross_prod(flt));
1498 volumeTri += flb.scalar_prod(blb.cross_prod(frb));
1499 volumeTri += blb.scalar_prod(brb.cross_prod(frb));
1500 volumeTri += frb.scalar_prod(brb.cross_prod(frt));
1501 volumeTri += brb.scalar_prod(brt.cross_prod(frt));
1502 volumeTri += flt.scalar_prod(frt.cross_prod(blt));
1503 volumeTri += frt.scalar_prod(brt.cross_prod(blt));
1504 volumeTri += blt.scalar_prod(brt.cross_prod(blb));
1505 volumeTri += brt.scalar_prod(brb.cross_prod(blb));
1506 return volumeTri / 6;
1507 }
1509 return 4.0 / 3.0 * M_PI * radius * radius * radius;
1511 return M_PI * radius * radius * height;
1513 return M_PI * height * (radius * radius - innerRadius * innerRadius);
1514 default:
1515 // Fall back to Monte Carlo method.
1516 return monteCarloVolume();
1517 }
1518}
1519
1527 using namespace boost::accumulators;
1528 const int singleShotIterations = 10000;
1529 accumulator_set<double, features<tag::mean, tag::error_of<tag::mean>>> accumulate;
1530 // For seeding the single shot runs.
1531 std::ranlux48 rnEngine;
1532 // Warm up statistics.
1533 for (int i = 0; i < 10; ++i) {
1534 const auto seed = rnEngine();
1535 const double volumeMc = singleShotMonteCarloVolume(singleShotIterations, seed);
1536 accumulate(volumeMc);
1537 }
1538 const double relativeErrorTolerance = 1e-3;
1539 double currentMean;
1540 double currentError;
1541 do {
1542 const auto seed = rnEngine();
1543 const double volumeMc = singleShotMonteCarloVolume(singleShotIterations, seed);
1544 accumulate(volumeMc);
1545 currentMean = mean(accumulate);
1546 currentError = error_of<tag::mean>(accumulate);
1547 if (std::isnan(currentError)) {
1548 currentError = 0;
1549 }
1550 } while (currentError / currentMean > relativeErrorTolerance);
1551 return currentMean;
1552}
1553
1560double CSGObject::singleShotMonteCarloVolume(const int shotSize, const size_t seed) const {
1561 const auto &boundingBox = getBoundingBox();
1562 if (boundingBox.isNull()) {
1563 throw std::runtime_error("Cannot calculate volume: invalid bounding box.");
1564 }
1565 int totalHits = 0;
1566 const double boundingDx = boundingBox.xMax() - boundingBox.xMin();
1567 const double boundingDy = boundingBox.yMax() - boundingBox.yMin();
1568 const double boundingDz = boundingBox.zMax() - boundingBox.zMin();
1569 PARALLEL {
1570 const auto threadCount = PARALLEL_NUMBER_OF_THREADS;
1571 const auto currentThreadNum = PARALLEL_THREAD_NUMBER;
1572 size_t blocksize = shotSize / threadCount;
1573 // cppcheck-suppress knownConditionTrueFalse
1574 if (currentThreadNum == threadCount - 1) {
1575 // Last thread may have to do threadCount extra iterations in
1576 // the worst case.
1577 blocksize = shotSize - (threadCount - 1) * blocksize;
1578 }
1579 std::mt19937 rnEngine(static_cast<std::mt19937::result_type>(seed));
1580 // All threads init their engine with the same seed.
1581 // We discard the random numbers used by the other threads.
1582 // This ensures reproducible results independent of the number
1583 // of threads.
1584 // We need three random numbers for each iteration.
1585 rnEngine.discard(currentThreadNum * 3 * blocksize);
1586 std::uniform_real_distribution<double> rnDistribution(0.0, 1.0);
1587 int hits = 0;
1588 for (int i = 0; i < static_cast<int>(blocksize); ++i) {
1589 double rnd = rnDistribution(rnEngine);
1590 const double x = boundingBox.xMin() + rnd * boundingDx;
1591 rnd = rnDistribution(rnEngine);
1592 const double y = boundingBox.yMin() + rnd * boundingDy;
1593 rnd = rnDistribution(rnEngine);
1594 const double z = boundingBox.zMin() + rnd * boundingDz;
1595 if (isValid(V3D(x, y, z))) {
1596 ++hits;
1597 }
1598 }
1599 // Collect results.
1601 totalHits += hits;
1602 }
1603 const double ratio = static_cast<double>(totalHits) / static_cast<double>(shotSize);
1604 const double boundingVolume = boundingDx * boundingDy * boundingDz;
1605 return ratio * boundingVolume;
1606}
1607
1613 // This member function is const given that from a user's perspective it is
1614 // perfectly reasonable to call it on a const object. We need to call a
1615 // non-const function in places to update the cache, which is where the
1616 // const_cast comes into play.
1617
1618 // If we don't know the extent of the object, the bounding box doesn't mean
1619 // anything
1620 if (!m_topRule) {
1621 const_cast<CSGObject *>(this)->setNullBoundingBox();
1622 return m_boundingBox;
1623 }
1624
1625 // We have a bounding box already, so just return it
1627 return m_boundingBox;
1628
1629 // Try to calculate using Rule method first
1630 const_cast<CSGObject *>(this)->calcBoundingBoxByRule();
1632 return m_boundingBox;
1633
1634 // Rule method failed; Try geometric method
1635 const_cast<CSGObject *>(this)->calcBoundingBoxByGeometry();
1637 return m_boundingBox;
1638
1639 // Geometric method failed; try to calculate by vertices
1640 const_cast<CSGObject *>(this)->calcBoundingBoxByVertices();
1642 return m_boundingBox;
1643
1644 // All options failed; give up
1645 // Set to a large box so that a) we don't keep trying to calculate a box
1646 // every time this is called and b) to serve as a visual indicator that
1647 // something went wrong.
1648 const_cast<CSGObject *>(this)->defineBoundingBox(100, 100, 100, -100, -100, -100);
1649 return m_boundingBox;
1650}
1651
1660 // Must have a top rule for this to work
1661 if (!m_topRule)
1662 return;
1663
1664 // Set up some unreasonable values that will be refined
1665 const double huge(1e10);
1666 const double big(1e4);
1667 double minX(-huge), minY(-huge), minZ(-huge);
1668 double maxX(huge), maxY(huge), maxZ(huge);
1669
1670 // Try to use the Rule system to derive the box
1671 m_topRule->getBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1672
1673 // Check whether values are reasonable now. Rule system will fail to produce
1674 // a reasonable box if the shape is not axis-aligned.
1675 if (minX > -big && maxX < big && minY > -big && maxY < big && minZ > -big && maxZ < big && minX <= maxX &&
1676 minY <= maxY && minZ <= maxZ) {
1677 // Values make sense, cache and return bounding box
1678 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1679 }
1680}
1681
1691 // Grab vertex information
1692 auto vertCount = this->numberOfVertices();
1693
1694 if (vertCount > 0) {
1695 const auto &vertArray = this->getTriangleVertices();
1696 // Unreasonable extents to be overwritten by loop
1697 constexpr double huge = 1e10;
1698 double minX, maxX, minY, maxY, minZ, maxZ;
1699 minX = minY = minZ = huge;
1700 maxX = maxY = maxZ = -huge;
1701
1702 // Loop over all vertices and determine minima and maxima on each axis
1703 for (size_t i = 0; i < vertCount; ++i) {
1704 auto vx = vertArray[3 * i + 0];
1705 auto vy = vertArray[3 * i + 1];
1706 auto vz = vertArray[3 * i + 2];
1707
1708 minX = std::min(minX, vx);
1709 maxX = std::max(maxX, vx);
1710 minY = std::min(minY, vy);
1711 maxY = std::max(maxY, vy);
1712 minZ = std::min(minZ, vz);
1713 maxZ = std::max(maxZ, vz);
1714 }
1715
1716 // Store bounding box in cache
1717 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1718 }
1719}
1720
1728 // Must have a GeometryHandler for this to work
1729 if (!m_handler)
1730 return;
1731
1732 // Extent of bounding box
1733 double minX, maxX, minY, maxY, minZ, maxZ;
1734
1735 // Shape geometry data
1737 std::vector<Kernel::V3D> vectors;
1738 double radius;
1739 double height;
1740 double innerRadius;
1741
1742 // Will only work for shapes with ShapeInfo
1743 m_handler->GetObjectGeom(type, vectors, innerRadius, radius, height);
1744 // Type of shape is given as a simple integer
1745 switch (type) {
1747 // Points as defined in IDF XML
1748 const auto &lfb = vectors[0]; // Left-Front-Bottom
1749 const auto &lft = vectors[1]; // Left-Front-Top
1750 const auto &lbb = vectors[2]; // Left-Back-Bottom
1751 const auto &rfb = vectors[3]; // Right-Front-Bottom
1752
1753 // Calculate and add missing corner points to vectors
1754 auto lbt = lft + (lbb - lfb); // Left-Back-Top
1755 auto rft = rfb + (lft - lfb); // Right-Front-Top
1756 auto rbb = lbb + (rfb - lfb); // Right-Back-Bottom
1757 auto rbt = rbb + (rft - rfb); // Right-Back-Top
1758
1759 vectors.emplace_back(lbt);
1760 vectors.emplace_back(rft);
1761 vectors.emplace_back(rbb);
1762 vectors.emplace_back(rbt);
1763
1764 // Unreasonable extents to be replaced by first loop cycle
1765 constexpr double huge = 1e10;
1766 minX = minY = minZ = huge;
1767 maxX = maxY = maxZ = -huge;
1768
1769 // Loop over all corner points to find minima and maxima on each axis
1770 for (const auto &vector : vectors) {
1771 minX = std::min(minX, vector.X());
1772 maxX = std::max(maxX, vector.X());
1773 minY = std::min(minY, vector.Y());
1774 maxY = std::max(maxY, vector.Y());
1775 minZ = std::min(minZ, vector.Z());
1776 maxZ = std::max(maxZ, vector.Z());
1777 }
1778 } break;
1780 // These will be replaced by more realistic values in the loop below
1781 minX = minY = minZ = std::numeric_limits<decltype(minZ)>::max();
1782 maxX = maxY = maxZ = -std::numeric_limits<decltype(maxZ)>::max();
1783
1784 // Loop over all corner points to find minima and maxima on each axis
1785 for (const auto &vector : vectors) {
1786 minX = std::min(minX, vector.X());
1787 maxX = std::max(maxX, vector.X());
1788 minY = std::min(minY, vector.Y());
1789 maxY = std::max(maxY, vector.Y());
1790 minZ = std::min(minZ, vector.Z());
1791 maxZ = std::max(maxZ, vector.Z());
1792 }
1793 } break;
1795 // Center-point of base and normalized axis based on IDF XML
1796 const auto &base = vectors[0];
1797 const auto &axis = vectors[1];
1798 auto top = base + (axis * height); // Center-point of other end
1799
1800 // How much of the radius must be considered for each axis
1801 // (If this ever becomes a performance issue, you could use just the radius
1802 // for a quick approx that is still guaranteed to fully contain the shape)
1803 volatile auto rx = radius * sqrt(pow(axis.Y(), 2) + pow(axis.Z(), 2));
1804 volatile auto ry = radius * sqrt(pow(axis.X(), 2) + pow(axis.Z(), 2));
1805 volatile auto rz = radius * sqrt(pow(axis.X(), 2) + pow(axis.Y(), 2));
1806
1807 // The bounding box is drawn around the base and top center-points,
1808 // then expanded in order to account for the radius
1809 minX = std::min(base.X(), top.X()) - rx;
1810 maxX = std::max(base.X(), top.X()) + rx;
1811 minY = std::min(base.Y(), top.Y()) - ry;
1812 maxY = std::max(base.Y(), top.Y()) + ry;
1813 minZ = std::min(base.Z(), top.Z()) - rz;
1814 maxZ = std::max(base.Z(), top.Z()) + rz;
1815 } break;
1816
1818 const auto &tip = vectors[0]; // Tip-point of cone
1819 const auto &axis = vectors[1]; // Normalized axis
1820 auto base = tip + (axis * height); // Center of base
1821
1822 // How much of the radius must be considered for each axis
1823 // (If this ever becomes a performance issue, you could use just the radius
1824 // for a quick approx that is still guaranteed to fully contain the shape)
1825 auto rx = radius * sqrt(pow(axis.Y(), 2) + pow(axis.Z(), 2));
1826 auto ry = radius * sqrt(pow(axis.X(), 2) + pow(axis.Z(), 2));
1827 auto rz = radius * sqrt(pow(axis.X(), 2) + pow(axis.Y(), 2));
1828
1829 // For a cone, the adjustment is only applied to the base
1830 minX = std::min(tip.X(), base.X() - rx);
1831 maxX = std::max(tip.X(), base.X() + rx);
1832 minY = std::min(tip.Y(), base.Y() - ry);
1833 maxY = std::max(tip.Y(), base.Y() + ry);
1834 minZ = std::min(tip.Z(), base.Z() - rz);
1835 maxZ = std::max(tip.Z(), base.Z() + rz);
1836 } break;
1837
1838 default: // Invalid (0, -1) or SPHERE (2) which should be handled by Rules
1839 return; // Don't store bounding box
1840 }
1841
1842 // Store bounding box in cache
1843 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1844}
1845
1858void CSGObject::getBoundingBox(double &xmax, double &ymax, double &zmax, double &xmin, double &ymin,
1859 double &zmin) const {
1860 if (!m_topRule) { // If no rule defined then return zero boundbing box
1861 xmax = ymax = zmax = xmin = ymin = zmin = 0.0;
1862 return;
1863 }
1864 if (!boolBounded) {
1865 AABBxMax = xmax;
1866 AABByMax = ymax;
1867 AABBzMax = zmax;
1868 AABBxMin = xmin;
1869 AABByMin = ymin;
1870 AABBzMin = zmin;
1872 if (AABBxMax >= xmax || AABBxMin <= xmin || AABByMax >= ymax || AABByMin <= ymin || AABBzMax >= zmax ||
1873 AABBzMin <= zmin)
1874 boolBounded = false;
1875 else
1876 boolBounded = true;
1877 }
1878 xmax = AABBxMax;
1879 ymax = AABByMax;
1880 zmax = AABBzMax;
1881 xmin = AABBxMin;
1882 ymin = AABByMin;
1883 zmin = AABBzMin;
1884}
1885
1900void CSGObject::defineBoundingBox(const double &xMax, const double &yMax, const double &zMax, const double &xMin,
1901 const double &yMin, const double &zMin) {
1902 BoundingBox::checkValid(xMax, yMax, zMax, xMin, yMin, zMin);
1903
1904 AABBxMax = xMax;
1905 AABByMax = yMax;
1906 AABBzMax = zMax;
1907 AABBxMin = xMin;
1908 AABByMin = yMin;
1909 AABBzMin = zMin;
1910 boolBounded = true;
1911
1912 PARALLEL_CRITICAL(defineBoundingBox) { m_boundingBox = BoundingBox(xMax, yMax, zMax, xMin, yMin, zMin); }
1913}
1914
1919
1926 //
1927 // Simple method - check if origin in object, if not search directions along
1928 // axes. If that fails, try centre of boundingBox, and paths about there
1929 //
1930 Kernel::V3D testPt(0, 0, 0);
1931 if (searchForObject(testPt)) {
1932 point = testPt;
1933 return 1;
1934 }
1935 // Try centre of bounding box as initial guess, if we have one.
1936 const BoundingBox &boundingBox = getBoundingBox();
1937 if (boundingBox.isNonNull()) {
1938 testPt = boundingBox.centrePoint();
1939 if (searchForObject(testPt) > 0) {
1940 point = testPt;
1941 return 1;
1942 }
1943 }
1944
1945 return 0;
1946}
1947
1960 const size_t maxAttempts) const {
1961 std::optional<V3D> point{std::nullopt};
1962 // If the shape fills its bounding box well enough then the most efficient
1963 // way to get the point is just brute force. We'll try that first with
1964 // just a few attempts.
1965 // Increasing the brute force attemps speeds up the shapes that fill
1966 // the bounding box but slows down shapes that leave lots of void
1967 // within the box. So there is a sweet spot which depends on the actual
1968 // shape, its dimension and orientation.
1969 const size_t bruteForceAttempts{std::min(static_cast<size_t>(5), maxAttempts)};
1970 std::optional<V3D> maybePoint{RandomPoint::inGenericShape(*this, rng, bruteForceAttempts)};
1971 if (maybePoint) {
1972 point = maybePoint;
1973 } else {
1974 switch (shape()) {
1976 point = RandomPoint::inCuboid(m_handler->shapeInfo(), rng);
1977 break;
1979 point = RandomPoint::inCylinder(m_handler->shapeInfo(), rng);
1980 break;
1982 point = RandomPoint::inHollowCylinder(m_handler->shapeInfo(), rng);
1983 break;
1985 point = RandomPoint::inSphere(m_handler->shapeInfo(), rng);
1986 break;
1987 default:
1988 maybePoint = RandomPoint::inGenericShape(*this, rng, maxAttempts - bruteForceAttempts);
1989 point = maybePoint;
1990 }
1991 }
1992 return point;
1993}
1994
2006 const BoundingBox &activeRegion,
2007 const size_t maxAttempts) const {
2008 std::optional<V3D> point{std::nullopt};
2009 // We'll first try brute force. If the shape fills its bounding box
2010 // well enough, this should be the fastest method.
2011 // Increasing the brute force attemps speeds up the shapes that fill
2012 // the bounding box well but slows down shapes that leave lots of void
2013 // within the box. So there is a sweet spot which depends on the actual
2014 // shape, its dimension and orientation.
2015 const size_t bruteForceAttempts{std::min(static_cast<size_t>(5), maxAttempts)};
2016 point = RandomPoint::bounded(*this, rng, activeRegion, bruteForceAttempts);
2017 if (!point) {
2019 std::vector<Kernel::V3D> shapeVectors;
2020 double radius;
2021 double height;
2022 double innerRadius;
2023 GetObjectGeom(shapeGeometry, shapeVectors, innerRadius, radius, height);
2024 switch (shapeGeometry) {
2026 point = RandomPoint::bounded<RandomPoint::inCuboid>(m_handler->shapeInfo(), rng, activeRegion,
2027 maxAttempts - bruteForceAttempts);
2028 break;
2030 point = RandomPoint::bounded<RandomPoint::inCylinder>(m_handler->shapeInfo(), rng, activeRegion,
2031 maxAttempts - bruteForceAttempts);
2032 break;
2034 point = RandomPoint::bounded<RandomPoint::inHollowCylinder>(m_handler->shapeInfo(), rng, activeRegion,
2035 maxAttempts - bruteForceAttempts);
2036 break;
2038 point = RandomPoint::bounded<RandomPoint::inSphere>(m_handler->shapeInfo(), rng, activeRegion,
2039 maxAttempts - bruteForceAttempts);
2040 break;
2041 default:
2042 point = RandomPoint::bounded(*this, rng, activeRegion, maxAttempts - bruteForceAttempts);
2043 break;
2044 }
2045 }
2046 return point;
2047}
2048
2055 //
2056 // Method - check if point in object, if not search directions along
2057 // principle axes using interceptSurface
2058 //
2059 if (isValid(point))
2060 return 1;
2061 for (const auto &dir :
2062 {V3D(1., 0., 0.), V3D(-1., 0., 0.), V3D(0., 1., 0.), V3D(0., -1., 0.), V3D(0., 0., 1.), V3D(0., 0., -1.)}) {
2063 Geometry::Track tr(point, dir);
2064 if (this->interceptSurface(tr) > 0) {
2065 point = tr.cbegin()->entryPoint;
2066 return 1;
2067 }
2068 }
2069 return 0;
2070}
2071
2076void CSGObject::setGeometryHandler(const std::shared_ptr<GeometryHandler> &h) {
2077 if (h)
2078 m_handler = h;
2079}
2080
2085void CSGObject::draw() const {
2086 if (m_handler == nullptr)
2087 return;
2088 // Render the Object
2089 m_handler->render();
2090}
2091
2098 if (m_handler == nullptr)
2099 return;
2100 // Render the Object
2101 m_handler->initialize();
2102}
2106void CSGObject::setVtkGeometryCacheWriter(std::shared_ptr<vtkGeometryCacheWriter> writer) {
2107 vtkCacheWriter = std::move(writer);
2109}
2110
2114void CSGObject::setVtkGeometryCacheReader(std::shared_ptr<vtkGeometryCacheReader> reader) {
2115 vtkCacheReader = std::move(reader);
2117}
2118
2122std::shared_ptr<GeometryHandler> CSGObject::getGeometryHandler() const {
2123 // Check if the geometry handler is upto dated with the cache, if not then
2124 // cache it now.
2125 return m_handler;
2126}
2127
2132 if (bGeometryCaching)
2133 return;
2134 bGeometryCaching = true;
2135 // Check if the Geometry handler can be handled for cache
2136 if (m_handler == nullptr)
2137 return;
2138 if (!m_handler->canTriangulate())
2139 return;
2140 // Check if the reader exist then read the cache
2141 if (vtkCacheReader.get() != nullptr) {
2142 vtkCacheReader->readCacheForObject(this);
2143 }
2144 // Check if the writer exist then write the cache
2145 if (vtkCacheWriter.get() != nullptr) {
2146 vtkCacheWriter->addObject(this);
2147 }
2148}
2149
2150// Initialize Draw Object
2151
2153 if (m_handler == nullptr)
2154 return 0;
2155 return m_handler->numberOfTriangles();
2156}
2158 if (m_handler == nullptr)
2159 return 0;
2160 return m_handler->numberOfPoints();
2161}
2165const std::vector<double> &CSGObject::getTriangleVertices() const {
2166 static const std::vector<double> empty;
2167 if (m_handler == nullptr)
2168 return empty;
2169 return m_handler->getTriangleVertices();
2170}
2171
2175const std::vector<uint32_t> &CSGObject::getTriangleFaces() const {
2176 static const std::vector<uint32_t> empty;
2177 if (m_handler == nullptr)
2178 return empty;
2179 return m_handler->getTriangleFaces();
2180}
2181
2183 if (m_handler && m_handler->hasShapeInfo()) {
2184 return m_handler->shapeInfo().shape();
2185 } else {
2187 }
2188}
2189
2191 if (m_handler && m_handler->hasShapeInfo()) {
2192 return m_handler->shapeInfo();
2193 } else {
2194 throw std::logic_error("CSGObject has no ShapeInfo to return");
2195 }
2196}
2197
2201void CSGObject::GetObjectGeom(detail::ShapeInfo::GeometryShape &type, std::vector<Kernel::V3D> &vectors,
2202 double &innerRadius, double &radius, double &height) const {
2204 if (m_handler == nullptr)
2205 return;
2206 m_handler->GetObjectGeom(type, vectors, innerRadius, radius, height);
2207}
2208
2212std::string CSGObject::getShapeXML() const { return this->m_shapeXML; }
2213
2214} // namespace Mantid::Geometry
gsl_vector * tmp
double height
Definition GetAllEi.cpp:155
double top
int count
counter
Definition Matrix.cpp:37
#define PARALLEL_THREAD_NUMBER
#define PARALLEL_CRITICAL(name)
#define PARALLEL_ATOMIC
#define PARALLEL_NUMBER_OF_THREADS
#define PARALLEL
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
Definition BoundingBox.h:33
bool isPointInside(const Kernel::V3D &point) const
Is the given point within the bounding box?
double angularWidth(const Kernel::V3D &observer) const
Calculate the angular half width from the given point.
bool isNonNull() const
Is the box considered valid. Convenience for !isNull()
bool doesLineIntersect(const Track &track) const
Does a specified track intersect the bounding box.
static void checkValid(double xmax, double ymax, double zmax, double xmin, double ymin, double zmin)
Do the given arguments form a valid bounding box, throws std::invalid argument if not.
Definition BoundingBox.h:63
Kernel::V3D centrePoint() const
Returns the centre of the bounding box.
Definition BoundingBox.h:93
Constructive Solid Geometry object.
Definition CSGObject.h:51
void setVtkGeometryCacheWriter(std::shared_ptr< vtkGeometryCacheWriter >)
set vtkGeometryCache writer
int m_objNum
Creation number.
Definition CSGObject.h:214
std::shared_ptr< vtkGeometryCacheReader > vtkCacheReader
a pointer to a class for reading from the geometry cache
Definition CSGObject.h:222
std::string cellCompStr() const
Write the object to a string.
std::string m_shapeXML
original shape xml used to generate this object.
Definition CSGObject.h:232
bool isValid(const Kernel::V3D &) const override
Check if a point is valid.
void makeComplement()
Takes the complement of a group.
void updateGeometryHandler()
Updates the geometry handler if needed.
void GetObjectGeom(detail::ShapeInfo::GeometryShape &type, std::vector< Kernel::V3D > &vectors, double &innerRadius, double &radius, double &height) const override
get info on standard shapes
int createSurfaceList(const int outFlag=0)
create Surface list
int setObject(const int objName, const std::string &lineStr)
Object line == cell.
void setGeometryHandler(const std::shared_ptr< GeometryHandler > &h)
Set Geometry Handler.
int checkSurfaceValid(const Kernel::V3D &, const Kernel::V3D &) const
Determine if a point is valid by checking both directions of the normal away from the line A good poi...
double solidAngle(const SolidAngleParams &params) const override
Find solid angle of object wrt the observer.
double AABByMin
xmin of Axis aligned bounding box cache
Definition CSGObject.h:209
const BoundingBox & getBoundingBox() const override
Return cached value of axis-aligned bounding box.
const Kernel::Material & material() const override
std::string cellStr(const std::map< int, CSGObject > &) const
Returns just the cell string object.
std::vector< const Surface * > m_surList
Full surfaces (make a map.
Definition CSGObject.h:241
int substituteSurf(const int surfNum, const int newSurfNum, const std::shared_ptr< Surface > &surfPtr)
Removes a surface and then re-builds the cell.
double AABBzMin
zmin of Axis Aligned Bounding Box Cache
Definition CSGObject.h:210
double volume() const override
Calculates the volume of this object.
int getPointInObject(Kernel::V3D &point) const override
Try to find a point that lies within (or on) the object.
const std::vector< uint32_t > & getTriangleFaces() const
for solid angle from triangulation
int removeSurface(const int surfNum)
Removes a surface and then re-builds the cell.
~CSGObject() override
Destructor.
std::unique_ptr< CompGrp > procComp(std::unique_ptr< Rule >) const
Takes a Rule item and makes it a complementary group.
void write(std::ostream &) const
MCNPX output.
int procPair(std::string &lineStr, std::map< int, std::unique_ptr< Rule > > &ruleMap, int &compUnit) const
This takes a string lineStr, finds the first two Rxxx function, determines their join type make the r...
double singleShotMonteCarloVolume(const int shotSize, const size_t seed) const
Returns the volume.
void convertComplement(const std::map< int, CSGObject > &)
Returns just the cell string object.
const std::vector< double > & getTriangleVertices() const
get vertices
std::string m_id
Optional string identifier.
Definition CSGObject.h:234
std::unique_ptr< Kernel::Material > m_material
material composition
Definition CSGObject.h:236
size_t numberOfTriangles() const
void setNullBoundingBox()
Set a null bounding box for this object.
std::string getShapeXML() const
Getter for the shape xml.
void setMaterial(const Kernel::Material &material) override
int searchForObject(Kernel::V3D &) const
Try to find a point that lies within (or on) the object, given a seed point.
double rayTraceSolidAngle(const Kernel::V3D &observer) const
Given an observer position find the approximate solid angle of the object.
std::shared_ptr< GeometryHandler > getGeometryHandler() const override
Returns the geometry handler.
void draw() const override
Draws the Object using geometry handler, If the handler is not set then this function does nothing.
double AABBzMax
zmax of Axis aligned bounding box cache
Definition CSGObject.h:207
CSGObject & operator=(const CSGObject &)
Assignment operator.
double AABBxMin
xmin of Axis aligned bounding box cache
Definition CSGObject.h:208
std::shared_ptr< GeometryHandler > m_handler
Geometry Handle for rendering.
Definition CSGObject.h:216
virtual void print() const
Prints almost everything.
double distance(const Track &track) const override
Compute the distance to the first point of intersection with the surface.
const Rule * topRule() const
Return the top rule.
Definition CSGObject.h:76
double AABByMax
ymax of Axis aligned bounding box cache
Definition CSGObject.h:206
void calcBoundingBoxByRule()
Calculate bounding box using Rule system.
double triangulatedSolidAngle(const SolidAngleParams &params) const
Find solid angle of object from point "observer" using the OC triangulation of the object,...
bool bGeometryCaching
Is geometry caching enabled?
Definition CSGObject.h:220
CSGObject()
Default constructor.
std::string str() const
Write the object to a string.
double monteCarloVolume() const
Returns the volume.
std::vector< int > getSurfaceIndex() const
Returns all of the numbers of surfaces.
Mantid::Geometry::TrackDirection calcValidTypeBy3Points(const Kernel::V3D &prePt, const Kernel::V3D &curPt, const Kernel::V3D &nxtPt) const
Check if an intercept is guiding the ray into the shape or leaving the shape.
void initDraw() const override
Initializes/prepares the object to be rendered, this will generate geometry for object,...
void procString(const std::string &lineStr)
Processes the cell string.
bool hasValidShape() const override
Return whether this object has a valid shape.
std::shared_ptr< vtkGeometryCacheWriter > vtkCacheWriter
a pointer to a class for writing to the geometry cache
Definition CSGObject.h:224
int hasComplement() const
Determine if the object has a complementary object.
double AABBxMax
xmax of Axis aligned bounding box cache
Definition CSGObject.h:205
BoundingBox m_boundingBox
Object's bounding box.
Definition CSGObject.h:203
const detail::ShapeInfo & shapeInfo() const override
Mantid::Geometry::TrackDirection calcValidType(const Kernel::V3D &Pt, const Kernel::V3D &uVec) const
Calculate if a point PT is a valid point on the track.
bool boolBounded
flag true if a bounding box exists, either by
Definition CSGObject.h:211
bool isOnSide(const Kernel::V3D &) const override
void calcBoundingBoxByVertices()
Calculate bounding box using object's vertices.
detail::ShapeInfo::GeometryShape shape() const override
std::optional< Kernel::V3D > generatePointInObject(Kernel::PseudoRandomNumberGenerator &rng, const size_t) const override
Select a random point within the object.
size_t numberOfVertices() const
void printTree() const
Displays the rule tree.
std::unique_ptr< Rule > m_topRule
Top rule [ Geometric scope of object].
Definition CSGObject.h:201
int populate(const std::map< int, std::shared_ptr< Surface > > &)
Goes through the cell objects and adds the pointers to the SurfPoint keys (using their keyN)
int interceptSurface(Geometry::Track &track) const override
Given a track, fill the track with valid section.
void setVtkGeometryCacheReader(std::shared_ptr< vtkGeometryCacheReader >)
set vtkGeometryCache reader
void defineBoundingBox(const double &xMax, const double &yMax, const double &zMax, const double &xMin, const double &yMin, const double &zMin)
Define axis-aligned bounding box.
void calcBoundingBoxByGeometry()
Calculate bounding box using object's geometric data.
static constexpr int g_NSLICES
The number of slices to approximate a cone.
Definition Cone.h:88
static constexpr int g_NSTACKS
The number of stacks to approximate a cone.
Definition Cone.h:90
static constexpr int g_NSTACKS
The number of stacks to approximate a cylinder.
Definition Cylinder.h:91
Handles rendering of all object Geometry.
Interset of Line with a surface.
const Line::PType & getPoints() const
Get the intersection points.
const DistancesType & getDistance() const
Get the distance.
void sortAndRemoveDuplicates()
Prune out duplicated points and sort by distance to starting point.
Object generation rule tree.
Definition Rules.h:33
Rule * getParent() const
Returns the parent object.
Definition Rules.cpp:467
virtual Rule * leaf(const int=0) const
No leaf for a base rule.
Definition Rules.h:59
virtual int findLeaf(const Rule *) const =0
Abstract find.
virtual void setLeaf(std::unique_ptr< Rule >, const int=0)=0
Abstract set.
static int removeItem(std::unique_ptr< Rule > &TRule, const int SurfN)
Given an item as a surface name, remove the surface from the Rule tree.
Definition Rules.cpp:376
virtual std::string display() const =0
Abstract Display.
const Kernel::V3D & observer() const
Surface leaf node.
Definition Rules.h:213
void setKey(const std::shared_ptr< Surface > &Spoint)
Sets the key pointer.
Holds a basic quadratic surface.
Definition Surface.h:33
int getName() const
Get Name.
Definition Surface.h:56
Defines a track as a start point and a direction.
Definition Track.h:165
const Kernel::V3D & startPoint() const
Returns the starting point.
Definition Track.h:191
void addPoint(const TrackDirection direction, const Kernel::V3D &endPoint, const IObject &obj, const ComponentID compID=nullptr)
Adds a point of intersection to the track.
Definition Track.cpp:124
void buildLink()
Construct links between added points.
Definition Track.cpp:184
int count() const
Returns the number of links.
Definition Track.h:219
const Kernel::V3D & direction() const
Returns the direction as a unit vector.
Definition Track.h:193
LType::const_iterator cbegin() const
Returns an interator to the start of the set of links (const version)
Definition Track.h:206
Reads the Geometry Cache from the file to the Object.
Writes the Geometry from Object to Cache.
Exception for when an item is not found in a collection.
Definition Exception.h:145
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
A material is defined as being composed of a given element, defined as a PhysicalConstants::NeutronAt...
Definition Material.h:50
Defines a 1D pseudo-random number generator, i.e.
Class for quaternions.
Definition Quat.h:39
void rotate(V3D &) const
Rotate a vector.
Definition Quat.cpp:397
Class for 3D vectors.
Definition V3D.h:34
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition V3D.h:280
constexpr double X() const noexcept
Get x.
Definition V3D.h:238
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition V3D.h:284
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
void rotate(const Matrix< double > &) noexcept
Rotate a point by a matrix.
Definition V3D.cpp:214
double norm() const noexcept
Definition V3D.h:269
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
bool nullVector(const double tolerance=1e-3) const noexcept
Determine if the point is null.
Definition V3D.cpp:238
std::optional< Kernel::V3D > bounded(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng, const BoundingBox &box, size_t maxAttempts)
Return a random point in a known shape restricted by a bounding box.
Definition RandomPoint.h:62
MANTID_GEOMETRY_DLL Kernel::V3D inCylinder(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in cylinder.
MANTID_GEOMETRY_DLL Kernel::V3D inCuboid(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in a cuboid shape.
MANTID_GEOMETRY_DLL Kernel::V3D inSphere(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in sphere.
MANTID_GEOMETRY_DLL std::optional< Kernel::V3D > inGenericShape(const IObject &object, Kernel::PseudoRandomNumberGenerator &rng, size_t maxAttempts)
Return a random point in a generic shape.
MANTID_GEOMETRY_DLL Kernel::V3D inHollowCylinder(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in a hollow cylinder.
int convPartNum(const std::string &A, T &out)
Takes a character string and evaluates the first [typename T] object.
Definition Strings.cpp:670
MANTID_KERNEL_DLL int StrLook(const std::string &, const boost::regex &)
Find is a pattern matches.
MANTID_KERNEL_DLL void writeMCNPX(const std::string &Line, std::ostream &OX)
Write file in standard MCNPX input form.
Definition Strings.cpp:452
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition Strings.cpp:696
constexpr double Tolerance
Standard tolerance value.
Definition Tolerance.h:12
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition V3D.h:352
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)