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