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 // rebind the cloned handler to this object: it renders the object it points at, and A may be
433 // destroyed while this copy is still alive (e.g. cloneWithMaterial replacing a sample shape)
434 m_handler = A.m_handler->clone(this);
439 m_id = A.m_id;
440 m_material = std::make_unique<Material>(A.material());
441
442 if (m_topRule)
444 }
445 return *this;
446}
447
449CSGObject::~CSGObject() = default;
450
454void CSGObject::setMaterial(const Kernel::Material &material) { m_material = std::make_unique<Material>(material); }
455
460
467 // Assume invalid shape if object has no 'm_topRule' or surfaces
468 return (m_topRule != nullptr && !m_surList.empty());
469}
470
477int CSGObject::setObject(const int objName, const std::string &lineStr) {
478 // Split line
479 // Does the string now contain junk...
480 static const boost::regex letters("[a-zA-Z]");
481 if (Mantid::Kernel::Strings::StrLook(lineStr, letters))
482 return 0;
483
484 procString(lineStr);
485 m_surList.clear();
486 m_objNum = objName;
487 return 1;
488}
489
495void CSGObject::convertComplement(const std::map<int, CSGObject> &MList)
496
497{
498 this->procString(this->cellStr(MList));
499}
500
507std::string CSGObject::cellStr(const std::map<int, CSGObject> &MList) const {
508 std::string TopStr = this->topRule()->display();
509 std::string::size_type pos = TopStr.find('#');
510 std::ostringstream cx;
511 while (pos != std::string::npos) {
512 pos++;
513 cx << TopStr.substr(0, pos); // Everything including the #
514 int cN(0);
515 const int nLen = Mantid::Kernel::Strings::convPartNum(TopStr.substr(pos), cN);
516 if (nLen > 0) {
517 cx << "(";
518 auto vc = MList.find(cN);
519 if (vc == MList.end())
520 throw Kernel::Exception::NotFoundError("Not found in the list of indexable hulls (Object::cellStr)", cN);
521 // Not the recursion :: This will cause no end of problems
522 // if there is an infinite loop.
523 cx << vc->second.cellStr(MList);
524 cx << ") ";
525 pos += nLen;
526 }
527 TopStr.erase(0, pos);
528 pos = TopStr.find('#');
529 }
530 cx << TopStr;
531 return cx.str();
532}
533
540
541 if (m_topRule)
542 return m_topRule->isComplementary();
543 return 0;
544}
545
553int CSGObject::populate(const std::map<int, std::shared_ptr<Surface>> &surfMap) {
554 std::deque<Rule *> rules;
555 rules.emplace_back(m_topRule.get());
556 while (!rules.empty()) {
557 Rule *T1 = rules.front();
558 rules.pop_front();
559 if (T1) {
560 // if an actual surface process :
561 auto *surface = dynamic_cast<SurfPoint *>(T1);
562 if (surface) {
563 // Ensure that we have a it in the surface list:
564 auto mapFound = surfMap.find(surface->getKeyN());
565 if (mapFound != surfMap.end()) {
566 surface->setKey(mapFound->second);
567 } else {
568 throw Kernel::Exception::NotFoundError("Object::populate", surface->getKeyN());
569 }
570 }
571 // Not a surface : Determine leaves etc and add to stack:
572 else {
573 Rule *TA = T1->leaf(0);
574 Rule *TB = T1->leaf(1);
575 if (TA)
576 rules.emplace_back(TA);
577 if (TB)
578 rules.emplace_back(TB);
579 }
580 }
581 }
583 return 0;
584}
585
597int CSGObject::procPair(std::string &lineStr, std::map<int, std::unique_ptr<Rule>> &ruleMap, int &compUnit) const
598
599{
600 unsigned int Rstart;
601 unsigned int Rend;
602 int Ra, Rb;
603
604 for (Rstart = 0; Rstart < lineStr.size() && lineStr[Rstart] != 'R'; Rstart++)
605 ;
606
607 int type = 0; // intersection
608
609 // plus 1 to skip 'R'
610 if (Rstart == lineStr.size() || !Mantid::Kernel::Strings::convert(lineStr.c_str() + Rstart + 1, Ra) ||
611 ruleMap.find(Ra) == ruleMap.end())
612 return 0;
613
614 for (Rend = Rstart + 1; Rend < lineStr.size() && lineStr[Rend] != 'R'; Rend++) {
615 if (lineStr[Rend] == ':')
616 type = 1; // make union
617 }
618 if (Rend == lineStr.size() || !Mantid::Kernel::Strings::convert(lineStr.c_str() + Rend + 1, Rb) ||
619 ruleMap.find(Rb) == ruleMap.end()) {
620 // No second rule but we did find the first one
621 compUnit = Ra;
622 return 0;
623 }
624 // Get end of number (digital)
625 for (Rend++; Rend < lineStr.size() && lineStr[Rend] >= '0' && lineStr[Rend] <= '9'; Rend++)
626 ;
627
628 // Get rules
629 auto Join =
630 (type) ? std::unique_ptr<Rule>(std::make_unique<Union>(std::move(ruleMap[Ra]), std::move(ruleMap[Rb])))
631 : std::unique_ptr<Rule>(std::make_unique<Intersection>(std::move(ruleMap[Ra]), std::move(ruleMap[Rb])));
632 ruleMap[Ra] = std::move(Join);
633 ruleMap.erase(ruleMap.find(Rb));
634
635 // Remove space round pair
636 int strPos;
637 for (strPos = Rstart - 1; strPos >= 0 && lineStr[strPos] == ' '; strPos--)
638 ;
639 Rstart = (strPos < 0) ? 0 : strPos;
640 for (strPos = Rend; strPos < static_cast<int>(lineStr.size()) && lineStr[strPos] == ' '; strPos++)
641 ;
642 Rend = strPos;
643
644 std::stringstream newRuleStr;
645 newRuleStr << " R" << Ra << " ";
646 lineStr.replace(Rstart, Rend, newRuleStr.str());
647 compUnit = Ra;
648 return 1;
649}
650
656std::unique_ptr<CompGrp> CSGObject::procComp(std::unique_ptr<Rule> ruleItem) const {
657 if (!ruleItem)
658 return std::make_unique<CompGrp>();
659
660 Rule *Pptr = ruleItem->getParent();
661 const Rule *RItemptr = ruleItem.get();
662 auto CG = std::make_unique<CompGrp>(Pptr, std::move(ruleItem));
663 if (Pptr) {
664 const int Ln = Pptr->findLeaf(RItemptr);
665 Pptr->setLeaf(std::move(CG), Ln);
666 // CG already in tree. Return empty object.
667 return std::make_unique<CompGrp>();
668 }
669 return CG;
670}
671
690bool CSGObject::isOnSide(const Kernel::V3D &point) const {
691 std::vector<Kernel::V3D> Snorms; // Normals from the contact surface.
692 Snorms.reserve(m_surList.size());
693
694 for (auto vc = m_surList.begin(); vc != m_surList.end(); ++vc) {
695 if ((*vc)->onSurface(point)) {
696 Snorms.emplace_back((*vc)->surfaceNormal(point));
697 // can check direct normal here since one success
698 // means that we can return true and finish
699 if (!checkSurfaceValid(point, Snorms.back()))
700 return true;
701 }
702 }
703 Kernel::V3D NormPair;
704 for (auto xs = Snorms.begin(); xs != Snorms.end(); ++xs)
705 for (auto ys = std::next(xs); ys != Snorms.end(); ++ys) {
706 NormPair = (*ys) + (*xs);
707 try {
708 NormPair.normalize();
709 if (!checkSurfaceValid(point, NormPair))
710 return true;
711 } catch (std::runtime_error &) {
712 }
713 }
714 // Ok everthing failed
715 return false;
716}
717
728int CSGObject::checkSurfaceValid(const Kernel::V3D &point, const Kernel::V3D &direction) const {
729 int status(0);
730 Kernel::V3D tmp = point + direction * (Kernel::Tolerance * 5.0);
731 status = (!isValid(tmp)) ? 1 : -1;
732 tmp -= direction * (Kernel::Tolerance * 10.0);
733 status += (!isValid(tmp)) ? 1 : -1;
734 return status / 2;
735}
736
742bool CSGObject::isValid(const Kernel::V3D &point) const {
743 if (!m_topRule)
744 return false;
745 return m_topRule->isValid(point);
746}
747
753bool CSGObject::isValid(const std::map<int, int> &SMap) const {
754 if (!m_topRule)
755 return false;
756 return m_topRule->isValid(SMap);
757}
758
765int CSGObject::createSurfaceList(const int outFlag) {
766 m_surList.clear();
767 std::stack<const Rule *> TreeLine;
768 TreeLine.push(m_topRule.get());
769 while (!TreeLine.empty()) {
770 const Rule *tmpA = TreeLine.top();
771 TreeLine.pop();
772 const Rule *tmpB = tmpA->leaf(0);
773 const Rule *tmpC = tmpA->leaf(1);
774 if (tmpB || tmpC) {
775 if (tmpB)
776 TreeLine.push(tmpB);
777 if (tmpC)
778 TreeLine.push(tmpC);
779 } else {
780 const auto *SurX = dynamic_cast<const SurfPoint *>(tmpA);
781 if (SurX) {
782 m_surList.emplace_back(SurX->getKey());
783 }
784 }
785 }
786 // Remove duplicates without reordering
787 std::unordered_set<const Surface *> uniqueSurfacePtrs;
788
789 auto newEnd = std::remove_if(m_surList.begin(), m_surList.end(), [&uniqueSurfacePtrs](const Surface *sPtr) {
790 if (uniqueSurfacePtrs.find(sPtr) != std::end(uniqueSurfacePtrs)) {
791 return true;
792 } else {
793 uniqueSurfacePtrs.insert(sPtr);
794 return false;
795 };
796 });
797 m_surList.erase(newEnd, m_surList.end());
798
799 if (outFlag) {
800
801 std::vector<const Surface *>::const_iterator vc;
802 for (vc = m_surList.begin(); vc != m_surList.end(); ++vc) {
803 logger.debug() << "Point == " << *vc << '\n';
804 logger.debug() << (*vc)->getName() << '\n';
805 }
806 }
807 return 1;
808}
809
814std::vector<int> CSGObject::getSurfaceIndex() const {
815 std::vector<int> out;
816 transform(m_surList.begin(), m_surList.end(), std::insert_iterator<std::vector<int>>(out, out.begin()),
817 std::mem_fn(&Surface::getName));
818 return out;
819}
820
828int CSGObject::removeSurface(const int surfNum) {
829 if (!m_topRule)
830 return -1;
831 const int nRemoved = Rule::removeItem(m_topRule, surfNum);
832 if (nRemoved)
834 return nRemoved;
835}
836
844int CSGObject::substituteSurf(const int surfNum, const int newSurfNum, const std::shared_ptr<Surface> &surfPtr) {
845 if (!m_topRule)
846 return 0;
847 const int out = m_topRule->substituteSurf(surfNum, newSurfNum, surfPtr);
848 if (out)
850 return out;
851}
852
856void CSGObject::print() const {
857 std::deque<Rule *> rst;
858 std::vector<int> Cells;
859 int Rcount(0);
860 rst.emplace_back(m_topRule.get());
861 Rule *TA, *TB; // Temp. for storage
862
863 while (!rst.empty()) {
864 const Rule *T1 = rst.front();
865 rst.pop_front();
866 if (T1) {
867 Rcount++;
868 const auto *surface = dynamic_cast<const SurfPoint *>(T1);
869 if (surface)
870 Cells.emplace_back(surface->getKeyN());
871 else {
872 TA = T1->leaf(0);
873 TB = T1->leaf(1);
874 if (TA)
875 rst.emplace_back(TA);
876 if (TB)
877 rst.emplace_back(TB);
878 }
879 }
880 }
881
882 logger.debug() << "Name == " << m_objNum << '\n';
883 logger.debug() << "Rules == " << Rcount << '\n';
884 std::vector<int>::const_iterator mc;
885 logger.debug() << "Surface included == ";
886 for (mc = Cells.begin(); mc < Cells.end(); ++mc) {
887 logger.debug() << (*mc) << " ";
888 }
889 logger.debug() << '\n';
890}
891
896 std::unique_ptr<Rule> NCG = procComp(std::move(m_topRule));
897 m_topRule = std::move(NCG);
898}
899
904 logger.debug() << "Name == " << m_objNum << '\n';
905 logger.debug() << m_topRule->display() << '\n';
906}
907
913std::string CSGObject::cellCompStr() const {
914 std::ostringstream objStr;
915 if (m_topRule)
916 objStr << m_topRule->display();
917 return objStr.str();
918}
919
925std::string CSGObject::str() const {
926 std::ostringstream objStr;
927 if (m_topRule) {
928 objStr << m_objNum << " ";
929 objStr << m_topRule->display();
930 }
931 return objStr.str();
932}
933
939void CSGObject::write(std::ostream &outStream) const {
940 std::ostringstream objStr;
941 objStr.precision(10);
942 objStr << str();
943 Mantid::Kernel::Strings::writeMCNPX(objStr.str(), outStream);
944}
945
951void CSGObject::procString(const std::string &lineStr) {
952 m_topRule = nullptr;
953 std::map<int, std::unique_ptr<Rule>> RuleList; // List for the rules
954 int Ridx = 0; // Current index (not necessary size of RuleList
955 // SURFACE REPLACEMENT
956 // Now replace all free planes/Surfaces with appropiate Rxxx
957 std::unique_ptr<SurfPoint> TmpR; // Tempory Rule storage position
958 std::unique_ptr<CompObj> TmpO; // Tempory Rule storage position
959
960 std::string Ln = lineStr;
961 // Remove all surfaces :
962 std::ostringstream cx;
963 const std::string::size_type length = Ln.length();
964 for (size_t i = 0; i < length; i++) {
965 if (isdigit(Ln[i]) || Ln[i] == '-') {
966 int SN;
967 int nLen = Mantid::Kernel::Strings::convPartNum(Ln.substr(i), SN);
968 if (!nLen)
969 throw std::invalid_argument("Invalid surface string in Object::ProcString : " + lineStr);
970 // Process #Number
971 if (i != 0 && Ln[i - 1] == '#') {
972 TmpO = std::make_unique<CompObj>();
973 TmpO->setObjN(SN);
974 RuleList[Ridx] = std::move(TmpO);
975 } else // Normal rule
976 {
977 TmpR = std::make_unique<SurfPoint>();
978 TmpR->setKeyN(SN);
979 RuleList[Ridx] = std::move(TmpR);
980 }
981 cx << " R" << Ridx << " ";
982 Ridx++;
983 i += nLen;
984 }
985 if (i < length)
986 cx << Ln[i];
987 }
988 Ln = cx.str();
989 // PROCESS BRACKETS
990
991 int brack_exists = 1;
992 while (brack_exists) {
993 std::string::size_type rbrack = Ln.find(')');
994 std::string::size_type lbrack = Ln.rfind('(', rbrack);
995 if (rbrack != std::string::npos && lbrack != std::string::npos) {
996 std::string Lx = Ln.substr(lbrack + 1, rbrack - lbrack - 1);
997 // Check to see if a #( unit
998 int compUnit(0);
999 while (procPair(Lx, RuleList, compUnit))
1000 ;
1001 Ln.replace(lbrack, 1 + rbrack - lbrack, Lx);
1002 // Search back and find if # ( exists.
1003 int hCnt;
1004 for (hCnt = static_cast<int>(lbrack) - 1; hCnt >= 0 && isspace(Ln[hCnt]); hCnt--)
1005 ;
1006 if (hCnt >= 0 && Ln[hCnt] == '#') {
1007 RuleList[compUnit] = procComp(std::move(RuleList[compUnit]));
1008 Ln.erase(hCnt, lbrack - hCnt);
1009 }
1010 } else
1011 brack_exists = 0;
1012 }
1013 // Do outside loop...
1014 int nullInt;
1015 while (procPair(Ln, RuleList, nullInt)) {
1016 }
1017
1018 if (RuleList.size() == 1) {
1019 m_topRule = std::move((RuleList.begin())->second);
1020 } else {
1021 throw std::logic_error("Object::procString() - Unexpected number of "
1022 "surface rules found. Expected=1, found=" +
1023 std::to_string(RuleList.size()));
1024 }
1025}
1026
1033 // Number of intersections original track
1034 int originalCount = track.count();
1035
1036 // Loop over all the surfaces to get the intercepts, i.e. populating
1037 // points into LI
1038 LineIntersectVisit LI(track.startPoint(), track.direction());
1039
1040 for (auto &surface : m_surList) {
1041 surface->acceptVisitor(LI);
1042 }
1043
1044 // Call the pruner so that we don't have to worry about the duplicates and
1045 // the order
1047
1048 // IPoints: std::vector<Geometry::V3D>
1049 const auto &IPoints(LI.getPoints());
1050 // dPoints: std::vector<double>, distance to the start point for each point
1051 const auto &dPoints(LI.getDistance());
1052 // nPoints: size_t, total number of points, for most shape, this number should
1053 // be a single digit number
1054 const size_t nPoints(IPoints.size());
1055
1056 // Loop over all the points and add them to the track
1057 for (size_t i = 0; i < nPoints; i++) {
1058 // skip over the points that are before the starting points
1059 if (dPoints[i] < 0)
1060 continue;
1061
1062 //
1063 const auto &currentPt(IPoints[i]);
1064 const auto &prePt((i == 0 || dPoints[i - 1] <= 0) ? track.startPoint() : IPoints[i - 1]);
1065 const auto &nextPt(i + 1 < nPoints ? IPoints[i + 1] : currentPt + currentPt - prePt);
1066
1067 // get the intercept type
1068 const TrackDirection trackType = calcValidTypeBy3Points(prePt, currentPt, nextPt);
1069 // only record the intercepts that is interacting with the shape directly
1070 if (trackType != TrackDirection::INVALID) {
1071 track.addPoint(trackType, currentPt, *this);
1072 }
1073 }
1074
1075 track.buildLink();
1076 // Return number of track segments added
1077 return (track.count() - originalCount);
1078}
1079
1086double CSGObject::distance(const Geometry::Track &track) const {
1087 LineIntersectVisit LI(track.startPoint(), track.direction());
1088 for (auto &surface : m_surList) {
1089 surface->acceptVisitor(LI);
1090 }
1092 const auto &distances(LI.getDistance());
1093 if (!distances.empty()) {
1094 return std::abs(*std::min_element(std::begin(distances), std::end(distances)));
1095 } else {
1096 std::ostringstream os;
1097 os << "Unable to find intersection with object with track starting at " << track.startPoint() << " in direction "
1098 << track.direction() << "\n";
1099 throw std::runtime_error(os.str());
1100 }
1101}
1102
1112 // NOTE: This method is sensitive to the geometry dimension, and will lead to
1113 // incorrect identification due to the value of VALID_INTERCEPT_POINT_SHIFT
1114 // being either too large or too small.
1115 const Kernel::V3D shift(uVec * VALID_INTERCEPT_POINT_SHIFT);
1116 const int flagA = isValid(point - shift);
1117 const int flagB = isValid(point + shift);
1118 if (!(flagA ^ flagB))
1121}
1122
1133 const Kernel::V3D &nxtPt) const {
1134 // upstream point
1135 const auto upstreamPt = (prePt + curPt) * 0.5;
1136 const auto upstreamPtInsideShape = isValid(upstreamPt);
1137 // downstream point
1138 const auto downstreamPt = (curPt + nxtPt) * 0.5;
1139 const auto downstreamPtInsideShape = isValid(downstreamPt);
1140 // NOTE:
1141 // When the track is parallel to the shape, it can still intersect with its
1142 // component (infinite) surface.
1143 // __Legends__
1144 // o-->: track
1145 // o: track starting point
1146 // m: upstreamPt
1147 // x: currentPt
1148 // d: downstreamPt
1149 // | | o
1150 // | | |
1151 // ---------|--------------------|---------------x------
1152 // | SHAPE | |
1153 // o--m---x-d----> o---m---x-d----> v Invalid
1154 // | Entering | Leaving
1155 // ---------|--------------------|----------------------
1156 // | |
1157 if (!(upstreamPtInsideShape ^ downstreamPtInsideShape))
1159 else
1161}
1162
1172double CSGObject::solidAngle(const SolidAngleParams &params) const {
1173 if (this->numberOfTriangles() > 30000)
1174 return rayTraceSolidAngle(params.observer());
1175 return triangulatedSolidAngle(params);
1176}
1177
1186double CSGObject::solidAngle(const SolidAngleParams &params, const Kernel::V3D &scaleFactor) const {
1187 return triangulatedSolidAngle(params, scaleFactor);
1188}
1189
1195double CSGObject::rayTraceSolidAngle(const Kernel::V3D &observer) const {
1196 // Calculation of solid angle as numerical double integral over all angles.
1197 // This could be optimized further e.g. by using a light weight version of
1198 // the interceptSurface method - this does more work than is necessary in this
1199 // application.
1200 // Accuracy is of the order of 1% for objects with an accurate bounding box,
1201 // though less in the case of high aspect ratios.
1202 //
1203 // resBB controls accuracy and cost - linear accuracy improvement with
1204 // increasing res, but quadratic increase in run time. If no bounding box
1205 // found, resNoBB used instead.
1206 const int resNoBB = 200, resPhiMin = 10;
1207 int res = resNoBB, itheta, jphi, resPhi;
1208 double theta, phi, sum, dphi, dtheta;
1209 if (this->isValid(observer) && !this->isOnSide(observer))
1210 return 4 * M_PI; // internal point
1211 if (this->isOnSide(observer))
1212 return 2 * M_PI; // this is wrong if on an edge
1213 // Use BB if available, and if observer not within it
1214 const BoundingBox &boundingBox = getBoundingBox();
1215 double thetaMax = M_PI;
1216 bool useBB = false, usePt = false;
1217 Kernel::V3D ptInObject, axis;
1218 Quat zToPt;
1219
1220 // Is the bounding box a reasonable one?
1221 if (boundingBox.isNonNull() && !boundingBox.isPointInside(observer)) {
1222 useBB = usePt = true;
1223 thetaMax = boundingBox.angularWidth(observer);
1224 ptInObject = boundingBox.centrePoint();
1225 const int resBB = 100;
1226 res = resBB;
1227 }
1228 // Try and find a point in the object if useful bounding box not found
1229 if (!useBB) {
1230 usePt = getPointInObject(ptInObject) == 1;
1231 }
1232 if (usePt) {
1233 // found point in object, now get rotation that maps z axis to this
1234 // direction from observer
1235 ptInObject -= observer;
1236 double theta0 = -180.0 / M_PI * acos(ptInObject.Z() / ptInObject.norm());
1237 Kernel::V3D zDir(0.0, 0.0, 1.0);
1238 axis = ptInObject.cross_prod(zDir);
1239 if (axis.nullVector())
1240 axis = Kernel::V3D(1.0, 0.0, 0.0);
1241 zToPt(theta0, axis);
1242 }
1243 dtheta = thetaMax / res;
1244 int count = 0, countPhi;
1245 sum = 0.;
1246 for (itheta = 1; itheta <= res; itheta++) {
1247 // itegrate theta from 0 to maximum from bounding box, or PI otherwise
1248 theta = thetaMax * (itheta - 0.5) / res;
1249 resPhi = static_cast<int>(res * sin(theta));
1250 if (resPhi < resPhiMin)
1251 resPhi = resPhiMin;
1252 dphi = 2 * M_PI / resPhi;
1253 countPhi = 0;
1254 for (jphi = 1; jphi <= resPhi; jphi++) {
1255 // integrate phi from 0 to 2*PI
1256 phi = 2.0 * M_PI * (jphi - 0.5) / resPhi;
1257 Kernel::V3D dir(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
1258 if (usePt)
1259 zToPt.rotate(dir);
1260 if (!useBB || boundingBox.doesLineIntersect(observer, dir)) {
1261 Track tr(observer, dir);
1262 if (this->interceptSurface(tr) > 0) {
1263 sum += dtheta * dphi * sin(theta);
1264 countPhi++;
1265 }
1266 }
1267 }
1268 // this break (only used if no BB defined) may be wrong if object has hole
1269 // in middle
1270 if (!useBB && countPhi == 0)
1271 break;
1272 count += countPhi;
1273 }
1274 if (!useBB && count < resPhiMin + 1) {
1275 // case of no bound box defined and object has few if any points in sum
1276 // redo integration on finer scale
1277 thetaMax = thetaMax * (itheta - 0.5) / res;
1278 dtheta = thetaMax / res;
1279 sum = 0;
1280 for (itheta = 1; itheta <= res; itheta++) {
1281 theta = thetaMax * (itheta - 0.5) / res;
1282 resPhi = static_cast<int>(res * sin(theta));
1283 if (resPhi < resPhiMin)
1284 resPhi = resPhiMin;
1285 dphi = 2 * M_PI / resPhi;
1286 countPhi = 0;
1287 for (jphi = 1; jphi <= resPhi; jphi++) {
1288 phi = 2.0 * M_PI * (jphi - 0.5) / resPhi;
1289 Kernel::V3D dir(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
1290 if (usePt)
1291 zToPt.rotate(dir);
1292 Track tr(observer, dir);
1293 if (this->interceptSurface(tr) > 0) {
1294 sum += dtheta * dphi * sin(theta);
1295 countPhi++;
1296 }
1297 }
1298 if (countPhi == 0)
1299 break;
1300 }
1301 }
1302
1303 return sum;
1304}
1305
1314 //
1315 // Because the triangles from OC are not consistently ordered wrt their
1316 // outward normal internal points give incorrect solid angle. Surface
1317 // points are difficult to get right with the triangle based method.
1318 // Hence catch these two (unlikely) cases.
1319 const auto &observer = params.observer();
1320 const BoundingBox &boundingBox = this->getBoundingBox();
1321 if (boundingBox.isNonNull() && boundingBox.isPointInside(observer)) {
1322 if (isValid(observer)) {
1323 if (isOnSide(observer))
1324 return (2.0 * M_PI);
1325 else
1326 return (4.0 * M_PI);
1327 }
1328 }
1329
1330 // If the object is a simple shape use the special methods
1331 double height(0.0), radius(0.0), innerRadius(0.0);
1333 std::vector<Mantid::Kernel::V3D> geometry_vectors;
1334 // Maximum of 4 vectors depending on the type
1335 geometry_vectors.reserve(4);
1336 this->GetObjectGeom(type, geometry_vectors, innerRadius, radius, height);
1337 auto nTri = this->numberOfTriangles();
1338
1339 // Cylinders are by far the most frequently used
1340 switch (type) {
1342 return cuboidSolidAngle(observer, geometry_vectors);
1343 break;
1345 return sphereSolidAngle(observer, geometry_vectors, radius);
1346 break;
1348 return cylinderSolidAngle(observer, geometry_vectors[0], geometry_vectors[1], radius, height,
1349 params.cylinderSlices());
1350 break;
1352 return coneSolidAngle(observer, geometry_vectors[0], geometry_vectors[1], radius, height);
1353 break;
1354 default:
1355 if (nTri == 0) // Fall back to raytracing if there are no triangles
1356 {
1357 return rayTraceSolidAngle(observer);
1358 } else { // Compute a generic shape that has been triangulated
1359 const auto &vertices = this->getTriangleVertices();
1360 const auto &faces = this->getTriangleFaces();
1361 double sangle(0.0), sneg(0.0);
1362 for (size_t i = 0; i < nTri; i++) {
1363 int p1 = faces[i * 3], p2 = faces[i * 3 + 1], p3 = faces[i * 3 + 2];
1364 V3D vp1 = V3D(vertices[3 * p1], vertices[3 * p1 + 1], vertices[3 * p1 + 2]);
1365 V3D vp2 = V3D(vertices[3 * p2], vertices[3 * p2 + 1], vertices[3 * p2 + 2]);
1366 V3D vp3 = V3D(vertices[3 * p3], vertices[3 * p3 + 1], vertices[3 * p3 + 2]);
1367 double sa = triangleSolidAngle(vp1, vp2, vp3, observer);
1368 if (sa > 0.0) {
1369 sangle += sa;
1370 } else {
1371 sneg += sa;
1372 }
1373 }
1374 /* We assume that objects are opaque to neutrons and that objects define
1375 * closed surfaces which are convex. For such objects negative solid angle
1376 * equals positive solid angle. This is true providing that the winding
1377 * order is defined properly such that the contribution from each triangle
1378 * w.r.t the observer gets counted to either the negative or positive
1379 * contribution correctly. If that is done correctly then it would only be
1380 * necessary to consider the positive contribution to the solid angle.
1381 *
1382 * The following provides a fix to situations where the winding order is
1383 * incorrectly defined. It does not matter if the contribution is positive
1384 * or negative since we take the average.
1385 */
1386 return 0.5 * (sangle - sneg);
1387 }
1388 }
1389}
1400double CSGObject::triangulatedSolidAngle(const SolidAngleParams &params, const V3D &scaleFactor) const {
1401 //
1402 // Because the triangles from OC are not consistently ordered wrt their
1403 // outward normal internal points give incorrect solid angle. Surface
1404 // points are difficult to get right with the triangle based method.
1405 // Hence catch these two (unlikely) cases.
1406 const auto &observer = params.observer();
1407 const BoundingBox &boundingBox = this->getBoundingBox();
1408 double sx = scaleFactor[0], sy = scaleFactor[1], sz = scaleFactor[2];
1409 const V3D sObserver = observer;
1410 if (boundingBox.isNonNull() && boundingBox.isPointInside(sObserver)) {
1411 if (isValid(sObserver)) {
1412 if (isOnSide(sObserver))
1413 return (2.0 * M_PI);
1414 else
1415 return (4.0 * M_PI);
1416 }
1417 }
1418
1419 auto nTri = this->numberOfTriangles();
1420 //
1421 // If triangulation is not available fall back to ray tracing method, unless
1422 // object is a standard shape, currently Cuboid or Sphere. Should add Cylinder
1423 // and Cone cases as well.
1424 //
1425 if (nTri == 0) {
1426 double height = 0.0, radius(0.0), innerRadius;
1428 std::vector<Kernel::V3D> vectors;
1429 this->GetObjectGeom(type, vectors, innerRadius, radius, height);
1430 switch (type) {
1432 std::transform(vectors.begin(), vectors.end(), vectors.begin(),
1433 [scaleFactor](const V3D &v) { return v * scaleFactor; });
1434 return cuboidSolidAngle(observer, vectors);
1435 break;
1437 return sphereSolidAngle(observer, vectors, radius);
1438 break;
1439 default:
1440 break;
1441 }
1442
1443 //
1444 // No special case, do the ray trace.
1445 //
1446 return rayTraceSolidAngle(observer);
1447 }
1448 const auto &vertices = this->getTriangleVertices();
1449 const auto &faces = this->getTriangleFaces();
1450 double sangle(0.0), sneg(0.0);
1451 for (size_t i = 0; i < nTri; i++) {
1452 int p1 = faces[i * 3], p2 = faces[i * 3 + 1], p3 = faces[i * 3 + 2];
1453 // would be more efficient to pre-multiply the vertices (copy of) by these
1454 // factors beforehand
1455 V3D vp1 = V3D(sx * vertices[3 * p1], sy * vertices[3 * p1 + 1], sz * vertices[3 * p1 + 2]);
1456 V3D vp2 = V3D(sx * vertices[3 * p2], sy * vertices[3 * p2 + 1], sz * vertices[3 * p2 + 2]);
1457 V3D vp3 = V3D(sx * vertices[3 * p3], sy * vertices[3 * p3 + 1], sz * vertices[3 * p3 + 2]);
1458 double sa = triangleSolidAngle(vp1, vp2, vp3, observer);
1459 if (sa > 0.0)
1460 sangle += sa;
1461 else
1462 sneg += sa;
1463 }
1464 return (0.5 * (sangle - sneg));
1465}
1466
1472double CSGObject::volume() const {
1474 double height;
1475 double radius;
1476 double innerRadius;
1477 std::vector<Kernel::V3D> vectors;
1478 this->GetObjectGeom(type, vectors, innerRadius, radius, height);
1479 switch (type) {
1481 // Here, the volume is calculated by the triangular method.
1482 // We use one of the vertices (vectors[0]) as the reference
1483 // point.
1484 double volumeTri = 0.0;
1485 // Vertices. Name codes follow flb = front-left-bottom etc.
1486 const Kernel::V3D &flb = vectors[0];
1487 const Kernel::V3D &flt = vectors[1];
1488 const Kernel::V3D &frb = vectors[3];
1489 const Kernel::V3D frt = frb + flt - flb;
1490 const Kernel::V3D &blb = vectors[2];
1491 const Kernel::V3D blt = blb + flt - flb;
1492 const Kernel::V3D brb = blb + frb - flb;
1493 const Kernel::V3D brt = frt + blb - flb;
1494 // Normals point out, follow right-handed rule when
1495 // defining the triangle faces.
1496 volumeTri += flb.scalar_prod(flt.cross_prod(blb));
1497 volumeTri += blb.scalar_prod(flt.cross_prod(blt));
1498 volumeTri += flb.scalar_prod(frb.cross_prod(flt));
1499 volumeTri += frb.scalar_prod(frt.cross_prod(flt));
1500 volumeTri += flb.scalar_prod(blb.cross_prod(frb));
1501 volumeTri += blb.scalar_prod(brb.cross_prod(frb));
1502 volumeTri += frb.scalar_prod(brb.cross_prod(frt));
1503 volumeTri += brb.scalar_prod(brt.cross_prod(frt));
1504 volumeTri += flt.scalar_prod(frt.cross_prod(blt));
1505 volumeTri += frt.scalar_prod(brt.cross_prod(blt));
1506 volumeTri += blt.scalar_prod(brt.cross_prod(blb));
1507 volumeTri += brt.scalar_prod(brb.cross_prod(blb));
1508 return volumeTri / 6;
1509 }
1511 return 4.0 / 3.0 * M_PI * radius * radius * radius;
1513 return M_PI * radius * radius * height;
1515 return M_PI * height * (radius * radius - innerRadius * innerRadius);
1516 default:
1517 // Fall back to Monte Carlo method.
1518 return monteCarloVolume();
1519 }
1520}
1521
1529 using namespace boost::accumulators;
1530 const int singleShotIterations = 10000;
1531 accumulator_set<double, features<tag::mean, tag::error_of<tag::mean>>> accumulate;
1532 // For seeding the single shot runs.
1533 std::ranlux48 rnEngine;
1534 // Warm up statistics.
1535 for (int i = 0; i < 10; ++i) {
1536 const auto seed = rnEngine();
1537 const double volumeMc = singleShotMonteCarloVolume(singleShotIterations, seed);
1538 accumulate(volumeMc);
1539 }
1540 const double relativeErrorTolerance = 1e-3;
1541 double currentMean;
1542 double currentError;
1543 do {
1544 const auto seed = rnEngine();
1545 const double volumeMc = singleShotMonteCarloVolume(singleShotIterations, seed);
1546 accumulate(volumeMc);
1547 currentMean = mean(accumulate);
1548 currentError = error_of<tag::mean>(accumulate);
1549 if (std::isnan(currentError)) {
1550 currentError = 0;
1551 }
1552 } while (currentError / currentMean > relativeErrorTolerance);
1553 return currentMean;
1554}
1555
1562double CSGObject::singleShotMonteCarloVolume(const int shotSize, const size_t seed) const {
1563 const auto &boundingBox = getBoundingBox();
1564 if (boundingBox.isNull()) {
1565 throw std::runtime_error("Cannot calculate volume: invalid bounding box.");
1566 }
1567 int totalHits = 0;
1568 const double boundingDx = boundingBox.xMax() - boundingBox.xMin();
1569 const double boundingDy = boundingBox.yMax() - boundingBox.yMin();
1570 const double boundingDz = boundingBox.zMax() - boundingBox.zMin();
1571 PARALLEL {
1572 const auto threadCount = PARALLEL_NUMBER_OF_THREADS;
1573 const auto currentThreadNum = PARALLEL_THREAD_NUMBER;
1574 size_t blocksize = shotSize / threadCount;
1575 // cppcheck-suppress knownConditionTrueFalse
1576 if (currentThreadNum == threadCount - 1) {
1577 // Last thread may have to do threadCount extra iterations in
1578 // the worst case.
1579 blocksize = shotSize - (threadCount - 1) * blocksize;
1580 }
1581 std::mt19937 rnEngine(static_cast<std::mt19937::result_type>(seed));
1582 // All threads init their engine with the same seed.
1583 // We discard the random numbers used by the other threads.
1584 // This ensures reproducible results independent of the number
1585 // of threads.
1586 // We need three random numbers for each iteration.
1587 rnEngine.discard(currentThreadNum * 3 * blocksize);
1588 std::uniform_real_distribution<double> rnDistribution(0.0, 1.0);
1589 int hits = 0;
1590 for (int i = 0; i < static_cast<int>(blocksize); ++i) {
1591 double rnd = rnDistribution(rnEngine);
1592 const double x = boundingBox.xMin() + rnd * boundingDx;
1593 rnd = rnDistribution(rnEngine);
1594 const double y = boundingBox.yMin() + rnd * boundingDy;
1595 rnd = rnDistribution(rnEngine);
1596 const double z = boundingBox.zMin() + rnd * boundingDz;
1597 if (isValid(V3D(x, y, z))) {
1598 ++hits;
1599 }
1600 }
1601 // Collect results.
1603 totalHits += hits;
1604 }
1605 const double ratio = static_cast<double>(totalHits) / static_cast<double>(shotSize);
1606 const double boundingVolume = boundingDx * boundingDy * boundingDz;
1607 return ratio * boundingVolume;
1608}
1609
1615 // This member function is const given that from a user's perspective it is
1616 // perfectly reasonable to call it on a const object. We need to call a
1617 // non-const function in places to update the cache, which is where the
1618 // const_cast comes into play.
1619
1620 // If we don't know the extent of the object, the bounding box doesn't mean
1621 // anything
1622 if (!m_topRule) {
1623 const_cast<CSGObject *>(this)->setNullBoundingBox();
1624 return m_boundingBox;
1625 }
1626
1627 // We have a bounding box already, so just return it
1629 return m_boundingBox;
1630
1631 // Try to calculate using Rule method first
1632 const_cast<CSGObject *>(this)->calcBoundingBoxByRule();
1634 return m_boundingBox;
1635
1636 // Rule method failed; Try geometric method
1637 const_cast<CSGObject *>(this)->calcBoundingBoxByGeometry();
1639 return m_boundingBox;
1640
1641 // Geometric method failed; try to calculate by vertices
1642 const_cast<CSGObject *>(this)->calcBoundingBoxByVertices();
1644 return m_boundingBox;
1645
1646 // All options failed; give up
1647 // Set to a large box so that a) we don't keep trying to calculate a box
1648 // every time this is called and b) to serve as a visual indicator that
1649 // something went wrong.
1650 const_cast<CSGObject *>(this)->defineBoundingBox(100, 100, 100, -100, -100, -100);
1651 return m_boundingBox;
1652}
1653
1662 // Must have a top rule for this to work
1663 if (!m_topRule)
1664 return;
1665
1666 // Set up some unreasonable values that will be refined
1667 const double huge(1e10);
1668 const double big(1e4);
1669 double minX(-huge), minY(-huge), minZ(-huge);
1670 double maxX(huge), maxY(huge), maxZ(huge);
1671
1672 // Try to use the Rule system to derive the box
1673 m_topRule->getBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1674
1675 // Check whether values are reasonable now. Rule system will fail to produce
1676 // a reasonable box if the shape is not axis-aligned.
1677 if (minX > -big && maxX < big && minY > -big && maxY < big && minZ > -big && maxZ < big && minX <= maxX &&
1678 minY <= maxY && minZ <= maxZ) {
1679 // Values make sense, cache and return bounding box
1680 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1681 }
1682}
1683
1693 // Grab vertex information
1694 auto vertCount = this->numberOfVertices();
1695
1696 if (vertCount > 0) {
1697 const auto &vertArray = this->getTriangleVertices();
1698 // Unreasonable extents to be overwritten by loop
1699 constexpr double huge = 1e10;
1700 double minX, maxX, minY, maxY, minZ, maxZ;
1701 minX = minY = minZ = huge;
1702 maxX = maxY = maxZ = -huge;
1703
1704 // Loop over all vertices and determine minima and maxima on each axis
1705 for (size_t i = 0; i < vertCount; ++i) {
1706 auto vx = vertArray[3 * i + 0];
1707 auto vy = vertArray[3 * i + 1];
1708 auto vz = vertArray[3 * i + 2];
1709
1710 minX = std::min(minX, vx);
1711 maxX = std::max(maxX, vx);
1712 minY = std::min(minY, vy);
1713 maxY = std::max(maxY, vy);
1714 minZ = std::min(minZ, vz);
1715 maxZ = std::max(maxZ, vz);
1716 }
1717
1718 // Store bounding box in cache
1719 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1720 }
1721}
1722
1730 // Must have a GeometryHandler for this to work
1731 if (!m_handler)
1732 return;
1733
1734 // Extent of bounding box
1735 double minX, maxX, minY, maxY, minZ, maxZ;
1736
1737 // Shape geometry data
1739 std::vector<Kernel::V3D> vectors;
1740 double radius;
1741 double height;
1742 double innerRadius;
1743
1744 // Will only work for shapes with ShapeInfo
1745 m_handler->GetObjectGeom(type, vectors, innerRadius, radius, height);
1746 // Type of shape is given as a simple integer
1747 switch (type) {
1749 // Points as defined in IDF XML
1750 const auto &lfb = vectors[0]; // Left-Front-Bottom
1751 const auto &lft = vectors[1]; // Left-Front-Top
1752 const auto &lbb = vectors[2]; // Left-Back-Bottom
1753 const auto &rfb = vectors[3]; // Right-Front-Bottom
1754
1755 // Calculate and add missing corner points to vectors
1756 auto lbt = lft + (lbb - lfb); // Left-Back-Top
1757 auto rft = rfb + (lft - lfb); // Right-Front-Top
1758 auto rbb = lbb + (rfb - lfb); // Right-Back-Bottom
1759 auto rbt = rbb + (rft - rfb); // Right-Back-Top
1760
1761 vectors.emplace_back(lbt);
1762 vectors.emplace_back(rft);
1763 vectors.emplace_back(rbb);
1764 vectors.emplace_back(rbt);
1765
1766 // Unreasonable extents to be replaced by first loop cycle
1767 constexpr double huge = 1e10;
1768 minX = minY = minZ = huge;
1769 maxX = maxY = maxZ = -huge;
1770
1771 // Loop over all corner points to find minima and maxima on each axis
1772 for (const auto &vector : vectors) {
1773 minX = std::min(minX, vector.X());
1774 maxX = std::max(maxX, vector.X());
1775 minY = std::min(minY, vector.Y());
1776 maxY = std::max(maxY, vector.Y());
1777 minZ = std::min(minZ, vector.Z());
1778 maxZ = std::max(maxZ, vector.Z());
1779 }
1780 } break;
1782 // These will be replaced by more realistic values in the loop below
1783 minX = minY = minZ = std::numeric_limits<decltype(minZ)>::max();
1784 maxX = maxY = maxZ = -std::numeric_limits<decltype(maxZ)>::max();
1785
1786 // Loop over all corner points to find minima and maxima on each axis
1787 for (const auto &vector : vectors) {
1788 minX = std::min(minX, vector.X());
1789 maxX = std::max(maxX, vector.X());
1790 minY = std::min(minY, vector.Y());
1791 maxY = std::max(maxY, vector.Y());
1792 minZ = std::min(minZ, vector.Z());
1793 maxZ = std::max(maxZ, vector.Z());
1794 }
1795 } break;
1797 // Center-point of base and normalized axis based on IDF XML
1798 const auto &base = vectors[0];
1799 const auto &axis = vectors[1];
1800 auto top = base + (axis * height); // Center-point of other end
1801
1802 // How much of the radius must be considered for each axis
1803 // (If this ever becomes a performance issue, you could use just the radius
1804 // for a quick approx that is still guaranteed to fully contain the shape)
1805 volatile auto rx = radius * sqrt(pow(axis.Y(), 2) + pow(axis.Z(), 2));
1806 volatile auto ry = radius * sqrt(pow(axis.X(), 2) + pow(axis.Z(), 2));
1807 volatile auto rz = radius * sqrt(pow(axis.X(), 2) + pow(axis.Y(), 2));
1808
1809 // The bounding box is drawn around the base and top center-points,
1810 // then expanded in order to account for the radius
1811 minX = std::min(base.X(), top.X()) - rx;
1812 maxX = std::max(base.X(), top.X()) + rx;
1813 minY = std::min(base.Y(), top.Y()) - ry;
1814 maxY = std::max(base.Y(), top.Y()) + ry;
1815 minZ = std::min(base.Z(), top.Z()) - rz;
1816 maxZ = std::max(base.Z(), top.Z()) + rz;
1817 } break;
1818
1820 const auto &tip = vectors[0]; // Tip-point of cone
1821 const auto &axis = vectors[1]; // Normalized axis
1822 auto base = tip + (axis * height); // Center of base
1823
1824 // How much of the radius must be considered for each axis
1825 // (If this ever becomes a performance issue, you could use just the radius
1826 // for a quick approx that is still guaranteed to fully contain the shape)
1827 auto rx = radius * sqrt(pow(axis.Y(), 2) + pow(axis.Z(), 2));
1828 auto ry = radius * sqrt(pow(axis.X(), 2) + pow(axis.Z(), 2));
1829 auto rz = radius * sqrt(pow(axis.X(), 2) + pow(axis.Y(), 2));
1830
1831 // For a cone, the adjustment is only applied to the base
1832 minX = std::min(tip.X(), base.X() - rx);
1833 maxX = std::max(tip.X(), base.X() + rx);
1834 minY = std::min(tip.Y(), base.Y() - ry);
1835 maxY = std::max(tip.Y(), base.Y() + ry);
1836 minZ = std::min(tip.Z(), base.Z() - rz);
1837 maxZ = std::max(tip.Z(), base.Z() + rz);
1838 } break;
1839
1840 default: // Invalid (0, -1) or SPHERE (2) which should be handled by Rules
1841 return; // Don't store bounding box
1842 }
1843
1844 // Store bounding box in cache
1845 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1846}
1847
1860void CSGObject::getBoundingBox(double &xmax, double &ymax, double &zmax, double &xmin, double &ymin,
1861 double &zmin) const {
1862 if (!m_topRule) { // If no rule defined then return zero boundbing box
1863 xmax = ymax = zmax = xmin = ymin = zmin = 0.0;
1864 return;
1865 }
1866 if (!boolBounded) {
1867 AABBxMax = xmax;
1868 AABByMax = ymax;
1869 AABBzMax = zmax;
1870 AABBxMin = xmin;
1871 AABByMin = ymin;
1872 AABBzMin = zmin;
1874 if (AABBxMax >= xmax || AABBxMin <= xmin || AABByMax >= ymax || AABByMin <= ymin || AABBzMax >= zmax ||
1875 AABBzMin <= zmin)
1876 boolBounded = false;
1877 else
1878 boolBounded = true;
1879 }
1880 xmax = AABBxMax;
1881 ymax = AABByMax;
1882 zmax = AABBzMax;
1883 xmin = AABBxMin;
1884 ymin = AABByMin;
1885 zmin = AABBzMin;
1886}
1887
1902void CSGObject::defineBoundingBox(const double &xMax, const double &yMax, const double &zMax, const double &xMin,
1903 const double &yMin, const double &zMin) {
1904 BoundingBox::checkValid(xMax, yMax, zMax, xMin, yMin, zMin);
1905
1906 AABBxMax = xMax;
1907 AABByMax = yMax;
1908 AABBzMax = zMax;
1909 AABBxMin = xMin;
1910 AABByMin = yMin;
1911 AABBzMin = zMin;
1912 boolBounded = true;
1913
1914 PARALLEL_CRITICAL(defineBoundingBox) { m_boundingBox = BoundingBox(xMax, yMax, zMax, xMin, yMin, zMin); }
1915}
1916
1921
1928 //
1929 // Simple method - check if origin in object, if not search directions along
1930 // axes. If that fails, try centre of boundingBox, and paths about there
1931 //
1932 Kernel::V3D testPt(0, 0, 0);
1933 if (searchForObject(testPt)) {
1934 point = testPt;
1935 return 1;
1936 }
1937 // Try centre of bounding box as initial guess, if we have one.
1938 const BoundingBox &boundingBox = getBoundingBox();
1939 if (boundingBox.isNonNull()) {
1940 testPt = boundingBox.centrePoint();
1941 if (searchForObject(testPt) > 0) {
1942 point = testPt;
1943 return 1;
1944 }
1945 }
1946
1947 return 0;
1948}
1949
1962 const size_t maxAttempts) const {
1963 std::optional<V3D> point{std::nullopt};
1964 // If the shape fills its bounding box well enough then the most efficient
1965 // way to get the point is just brute force. We'll try that first with
1966 // just a few attempts.
1967 // Increasing the brute force attemps speeds up the shapes that fill
1968 // the bounding box but slows down shapes that leave lots of void
1969 // within the box. So there is a sweet spot which depends on the actual
1970 // shape, its dimension and orientation.
1971 const size_t bruteForceAttempts{std::min(static_cast<size_t>(5), maxAttempts)};
1972 std::optional<V3D> maybePoint{RandomPoint::inGenericShape(*this, rng, bruteForceAttempts)};
1973 if (maybePoint) {
1974 point = maybePoint;
1975 } else {
1976 switch (shape()) {
1978 point = RandomPoint::inCuboid(m_handler->shapeInfo(), rng);
1979 break;
1981 point = RandomPoint::inCylinder(m_handler->shapeInfo(), rng);
1982 break;
1984 point = RandomPoint::inHollowCylinder(m_handler->shapeInfo(), rng);
1985 break;
1987 point = RandomPoint::inSphere(m_handler->shapeInfo(), rng);
1988 break;
1989 default:
1990 maybePoint = RandomPoint::inGenericShape(*this, rng, maxAttempts - bruteForceAttempts);
1991 point = maybePoint;
1992 }
1993 }
1994 return point;
1995}
1996
2008 const BoundingBox &activeRegion,
2009 const size_t maxAttempts) const {
2010 std::optional<V3D> point{std::nullopt};
2011 // We'll first try brute force. If the shape fills its bounding box
2012 // well enough, this should be the fastest method.
2013 // Increasing the brute force attemps speeds up the shapes that fill
2014 // the bounding box well but slows down shapes that leave lots of void
2015 // within the box. So there is a sweet spot which depends on the actual
2016 // shape, its dimension and orientation.
2017 const size_t bruteForceAttempts{std::min(static_cast<size_t>(5), maxAttempts)};
2018 point = RandomPoint::bounded(*this, rng, activeRegion, bruteForceAttempts);
2019 if (!point) {
2021 std::vector<Kernel::V3D> shapeVectors;
2022 double radius;
2023 double height;
2024 double innerRadius;
2025 GetObjectGeom(shapeGeometry, shapeVectors, innerRadius, radius, height);
2026 switch (shapeGeometry) {
2028 point = RandomPoint::bounded<RandomPoint::inCuboid>(m_handler->shapeInfo(), rng, activeRegion,
2029 maxAttempts - bruteForceAttempts);
2030 break;
2032 point = RandomPoint::bounded<RandomPoint::inCylinder>(m_handler->shapeInfo(), rng, activeRegion,
2033 maxAttempts - bruteForceAttempts);
2034 break;
2036 point = RandomPoint::bounded<RandomPoint::inHollowCylinder>(m_handler->shapeInfo(), rng, activeRegion,
2037 maxAttempts - bruteForceAttempts);
2038 break;
2040 point = RandomPoint::bounded<RandomPoint::inSphere>(m_handler->shapeInfo(), rng, activeRegion,
2041 maxAttempts - bruteForceAttempts);
2042 break;
2043 default:
2044 point = RandomPoint::bounded(*this, rng, activeRegion, maxAttempts - bruteForceAttempts);
2045 break;
2046 }
2047 }
2048 return point;
2049}
2050
2057 //
2058 // Method - check if point in object, if not search directions along
2059 // principle axes using interceptSurface
2060 //
2061 if (isValid(point))
2062 return 1;
2063 for (const auto &dir :
2064 {V3D(1., 0., 0.), V3D(-1., 0., 0.), V3D(0., 1., 0.), V3D(0., -1., 0.), V3D(0., 0., 1.), V3D(0., 0., -1.)}) {
2065 Geometry::Track tr(point, dir);
2066 if (this->interceptSurface(tr) > 0) {
2067 point = tr.cbegin()->entryPoint;
2068 return 1;
2069 }
2070 }
2071 return 0;
2072}
2073
2078void CSGObject::setGeometryHandler(const std::shared_ptr<GeometryHandler> &h) {
2079 if (h)
2080 m_handler = h;
2081}
2082
2087void CSGObject::draw() const {
2088 if (m_handler == nullptr)
2089 return;
2090 // Render the Object
2091 m_handler->render();
2092}
2093
2100 if (m_handler == nullptr)
2101 return;
2102 // Render the Object
2103 m_handler->initialize();
2104}
2108void CSGObject::setVtkGeometryCacheWriter(std::shared_ptr<vtkGeometryCacheWriter> writer) {
2109 vtkCacheWriter = std::move(writer);
2111}
2112
2116void CSGObject::setVtkGeometryCacheReader(std::shared_ptr<vtkGeometryCacheReader> reader) {
2117 vtkCacheReader = std::move(reader);
2119}
2120
2124std::shared_ptr<GeometryHandler> CSGObject::getGeometryHandler() const {
2125 // Check if the geometry handler is upto dated with the cache, if not then
2126 // cache it now.
2127 return m_handler;
2128}
2129
2134 if (bGeometryCaching)
2135 return;
2136 bGeometryCaching = true;
2137 // Check if the Geometry handler can be handled for cache
2138 if (m_handler == nullptr)
2139 return;
2140 if (!m_handler->canTriangulate())
2141 return;
2142 // Check if the reader exist then read the cache
2143 if (vtkCacheReader.get() != nullptr) {
2144 vtkCacheReader->readCacheForObject(this);
2145 }
2146 // Check if the writer exist then write the cache
2147 if (vtkCacheWriter.get() != nullptr) {
2148 vtkCacheWriter->addObject(this);
2149 }
2150}
2151
2152// Initialize Draw Object
2153
2155 if (m_handler == nullptr)
2156 return 0;
2157 return m_handler->numberOfTriangles();
2158}
2160 if (m_handler == nullptr)
2161 return 0;
2162 return m_handler->numberOfPoints();
2163}
2167const std::vector<double> &CSGObject::getTriangleVertices() const {
2168 static const std::vector<double> empty;
2169 if (m_handler == nullptr)
2170 return empty;
2171 return m_handler->getTriangleVertices();
2172}
2173
2177const std::vector<uint32_t> &CSGObject::getTriangleFaces() const {
2178 static const std::vector<uint32_t> empty;
2179 if (m_handler == nullptr)
2180 return empty;
2181 return m_handler->getTriangleFaces();
2182}
2183
2185 if (m_handler && m_handler->hasShapeInfo()) {
2186 return m_handler->shapeInfo().shape();
2187 } else {
2189 }
2190}
2191
2193 if (m_handler && m_handler->hasShapeInfo()) {
2194 return m_handler->shapeInfo();
2195 } else {
2196 throw std::logic_error("CSGObject has no ShapeInfo to return");
2197 }
2198}
2199
2203void CSGObject::GetObjectGeom(detail::ShapeInfo::GeometryShape &type, std::vector<Kernel::V3D> &vectors,
2204 double &innerRadius, double &radius, double &height) const {
2206 if (m_handler == nullptr)
2207 return;
2208 m_handler->GetObjectGeom(type, vectors, innerRadius, radius, height);
2209}
2210
2214std::string CSGObject::getShapeXML() const { return this->m_shapeXML; }
2215
2216} // namespace Mantid::Geometry
gsl_vector * tmp
double centre
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:217
std::shared_ptr< vtkGeometryCacheReader > vtkCacheReader
a pointer to a class for reading from the geometry cache
Definition CSGObject.h:225
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:212
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:213
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
get faces
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:210
CSGObject & operator=(const CSGObject &)
Assignment operator.
double AABBxMin
xmin of Axis aligned bounding box cache
Definition CSGObject.h:211
std::shared_ptr< GeometryHandler > m_handler
Geometry Handle for rendering.
Definition CSGObject.h:219
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:209
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:223
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:227
int hasComplement() const
Determine if the object has a complementary object.
double AABBxMax
xmax of Axis aligned bounding box cache
Definition CSGObject.h:208
BoundingBox m_boundingBox
Object's bounding box.
Definition CSGObject.h:206
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:214
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:204
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)