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 <unordered_set>
42#include <utility>
43
44using namespace Mantid::Geometry;
45using namespace Mantid::Kernel;
46
47namespace {
48
50constexpr double VALID_INTERCEPT_POINT_SHIFT{2.5e-05};
51
64double triangleSolidAngle(const V3D &a, const V3D &b, const V3D &c, const V3D &observer) {
65 const V3D ao = a - observer;
66 const V3D bo = b - observer;
67 const V3D co = c - observer;
68 const double modao = ao.norm();
69 const double modbo = bo.norm();
70 const double modco = co.norm();
71 const double aobo = ao.scalar_prod(bo);
72 const double aoco = ao.scalar_prod(co);
73 const double boco = bo.scalar_prod(co);
74 const double scalTripProd = ao.scalar_prod(bo.cross_prod(co));
75 const double denom = modao * modbo * modco + modco * aobo + modbo * aoco + modao * boco;
76 if (denom != 0.0)
77 return 2.0 * atan2(scalTripProd, denom);
78 else
79 return 0.0; // not certain this is correct
80}
81
91double coneSolidAngle(const V3D &observer, const Mantid::Kernel::V3D &centre, const Mantid::Kernel::V3D &axis,
92 const double radius, const double height) {
93 // The cone is broken down into three pieces and then in turn broken down into
94 // triangles. Any triangle that has a normal facing away from the observer
95 // gives a negative solid angle and is excluded
96 // For simplicity the triangulation points are constructed such that the cone
97 // axis points up the +Z axis and then rotated into their final position
98
99 const V3D axis_direction = normalize(axis);
100 // Required rotation
101 constexpr V3D initial_axis(0., 0., 1.0);
102 const Quat transform(initial_axis, axis_direction);
103
104 // Do the base cap which is a point at the centre and nslices points around it
105 constexpr double angle_step = 2 * M_PI / Mantid::Geometry::Cone::g_NSLICES;
106 // Store the (x,y) points as they are used quite frequently
107 std::array<double, Mantid::Geometry::Cone::g_NSLICES> cos_table;
108 std::array<double, Mantid::Geometry::Cone::g_NSLICES> sin_table;
109
110 double solid_angle(0.0);
111 for (int sl = 0; sl < Mantid::Geometry::Cone::g_NSLICES; ++sl) {
112 int vertex = sl;
113 cos_table[vertex] = std::cos(angle_step * vertex);
114 sin_table[vertex] = std::sin(angle_step * vertex);
115 V3D pt2 = V3D(radius * cos_table[vertex], radius * sin_table[vertex], 0.0);
116
118 vertex = sl + 1;
119 cos_table[vertex] = std::cos(angle_step * vertex);
120 sin_table[vertex] = std::sin(angle_step * vertex);
121 } else
122 vertex = 0;
123
124 V3D pt3 = V3D(radius * cos_table[vertex], radius * sin_table[vertex], 0.0);
125
126 transform.rotate(pt2);
127 transform.rotate(pt3);
128 pt2 += centre;
129 pt3 += centre;
130
131 double sa = triangleSolidAngle(centre, pt2, pt3, observer);
132 if (sa > 0.0) {
133 solid_angle += sa;
134 }
135 }
136
137 // Now the main section
138 const double z_step = height / Cone::g_NSTACKS;
139 const double r_step = height / Cone::g_NSTACKS;
140 double z0(0.0), z1(z_step);
141 double r0(radius), r1(r0 - r_step);
142
143 for (int st = 1; st < Cone::g_NSTACKS; ++st) {
144 for (int sl = 0; sl < Cone::g_NSLICES; ++sl) {
145 int vertex = sl;
146 V3D pt1 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], z0);
148 vertex = sl + 1;
149 else
150 vertex = 0;
151 V3D pt3 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], z0);
152
153 vertex = sl;
154 V3D pt2 = V3D(r1 * cos_table[vertex], r1 * sin_table[vertex], z1);
156 vertex = sl + 1;
157 else
158 vertex = 0;
159 V3D pt4 = V3D(r1 * cos_table[vertex], r1 * sin_table[vertex], z1);
160 // Rotations
161 transform.rotate(pt1);
162 transform.rotate(pt3);
163 transform.rotate(pt2);
164 transform.rotate(pt4);
165
166 pt1 += centre;
167 pt2 += centre;
168 pt3 += centre;
169 pt4 += centre;
170 double sa = triangleSolidAngle(pt1, pt4, pt3, observer);
171 if (sa > 0.0) {
172 solid_angle += sa;
173 }
174 sa = triangleSolidAngle(pt1, pt2, pt4, observer);
175 if (sa > 0.0) {
176 solid_angle += sa;
177 }
178 }
179
180 z0 = z1;
181 r0 = r1;
182 z1 += z_step;
183 r1 -= r_step;
184 }
185
186 // Top section
187 V3D top_centre = V3D(0.0, 0.0, height) + centre;
188 transform.rotate(top_centre);
189 top_centre += centre;
190
191 for (int sl = 0; sl < Cone::g_NSLICES; ++sl) {
192 int vertex = sl;
193 V3D pt2 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], height);
194
196 vertex = sl + 1;
197 else
198 vertex = 0;
199 V3D pt3 = V3D(r0 * cos_table[vertex], r0 * sin_table[vertex], height);
200
201 // Rotate them to the correct axis orientation
202 transform.rotate(pt2);
203 transform.rotate(pt3);
204
205 pt2 += centre;
206 pt3 += centre;
207
208 double sa = triangleSolidAngle(top_centre, pt3, pt2, observer);
209 if (sa > 0.0) {
210 solid_angle += sa;
211 }
212 }
213 return solid_angle;
214}
215
225double cuboidSolidAngle(const V3D &observer, const std::vector<V3D> &vectors) {
226 // Build bounding points, then set up map of 12 bounding
227 // triangles defining the 6 surfaces of the bounding box. Using a consistent
228 // ordering of points the "away facing" triangles give -ve contributions to
229 // the solid angle and hence are ignored.
230 std::vector<V3D> pts;
231 pts.reserve(8);
232 const V3D dx = vectors[1] - vectors[0];
233 const V3D dz = vectors[3] - vectors[0];
234 pts.emplace_back(vectors[2]);
235 pts.emplace_back(vectors[2] + dx);
236 pts.emplace_back(vectors[1]);
237 pts.emplace_back(vectors[0]);
238 pts.emplace_back(vectors[2] + dz);
239 pts.emplace_back(vectors[2] + dz + dx);
240 pts.emplace_back(vectors[1] + dz);
241 pts.emplace_back(vectors[0] + dz);
242
243 constexpr unsigned int ntriangles(12);
244 std::vector<std::vector<int>> triMap(ntriangles, std::vector<int>(3, 0));
245 triMap[0][0] = 1;
246 triMap[0][1] = 4;
247 triMap[0][2] = 3;
248 triMap[1][0] = 3;
249 triMap[1][1] = 2;
250 triMap[1][2] = 1;
251 triMap[2][0] = 5;
252 triMap[2][1] = 6;
253 triMap[2][2] = 7;
254 triMap[3][0] = 7;
255 triMap[3][1] = 8;
256 triMap[3][2] = 5;
257 triMap[4][0] = 1;
258 triMap[4][1] = 2;
259 triMap[4][2] = 6;
260 triMap[5][0] = 6;
261 triMap[5][1] = 5;
262 triMap[5][2] = 1;
263 triMap[6][0] = 2;
264 triMap[6][1] = 3;
265 triMap[6][2] = 7;
266 triMap[7][0] = 7;
267 triMap[7][1] = 6;
268 triMap[7][2] = 2;
269 triMap[8][0] = 3;
270 triMap[8][1] = 4;
271 triMap[8][2] = 8;
272 triMap[9][0] = 8;
273 triMap[9][1] = 7;
274 triMap[9][2] = 3;
275 triMap[10][0] = 1;
276 triMap[10][1] = 5;
277 triMap[10][2] = 8;
278 triMap[11][0] = 8;
279 triMap[11][1] = 4;
280 triMap[11][2] = 1;
281 double sangle = 0.0;
282 for (unsigned int i = 0; i < ntriangles; i++) {
283 const double sa = triangleSolidAngle(pts[triMap[i][0] - 1], pts[triMap[i][1] - 1], pts[triMap[i][2] - 1], observer);
284 if (sa > 0)
285 sangle += sa;
286 }
287 return sangle;
288}
289
300double cylinderSolidAngle(const V3D &observer, const V3D &centre, const V3D &axis, const double radius,
301 const double height) {
302 // The cylinder is triangulated along its axis EXCLUDING the end caps so that
303 // stacked cylinders give the correct value of solid angle (i.e shadowing is
304 // loosely taken into account by this method) Any triangle that has a normal
305 // facing away from the observer gives a negative solid angle and is excluded
306 // For simplicity the triangulation points are constructed such that the cone
307 // axis points up the +Z axis and then rotated into their final position
308
309 // Required rotation
310 constexpr V3D initial_axis(0., 0., 1.0);
311 const Quat transform(initial_axis, axis);
312
313 // Do the base cap which is a point at the centre and nslices points around it
314 constexpr double angle_step = 2 * M_PI / static_cast<double>(Cylinder::g_NSLICES);
315
316 const double z_step = height / Cylinder::g_NSTACKS;
317 double z0(0.0), z1(z_step);
318 double solid_angle(0.0);
319 for (int st = 1; st <= Cylinder::g_NSTACKS; ++st) {
320 if (st == Cylinder::g_NSTACKS)
321 z1 = height;
322
323 for (int sl = 0; sl < Cylinder::g_NSLICES; ++sl) {
324 double x = radius * std::cos(angle_step * sl);
325 double y = radius * std::sin(angle_step * sl);
326 V3D pt1 = V3D(x, y, z0);
327 V3D pt2 = V3D(x, y, z1);
328 int vertex = (sl + 1) % Cylinder::g_NSLICES;
329 x = radius * std::cos(angle_step * vertex);
330 y = radius * std::sin(angle_step * vertex);
331 V3D pt3 = V3D(x, y, z0);
332 V3D pt4 = V3D(x, y, z1);
333 // Rotations
334 transform.rotate(pt1);
335 transform.rotate(pt3);
336 transform.rotate(pt2);
337 transform.rotate(pt4);
338
339 pt1 += centre;
340 pt2 += centre;
341 pt3 += centre;
342 pt4 += centre;
343
344 double sa = triangleSolidAngle(pt1, pt4, pt3, observer);
345 if (sa > 0.0) {
346 solid_angle += sa;
347 }
348 sa = triangleSolidAngle(pt1, pt2, pt4, observer);
349 if (sa > 0.0) {
350 solid_angle += sa;
351 }
352 }
353 z0 = z1;
354 z1 += z_step;
355 }
356
357 return solid_angle;
358}
359
368double sphereSolidAngle(const V3D &observer, const std::vector<V3D> &vectors, const double radius) {
369 const double distance = (observer - vectors[0]).norm();
370 if (distance > radius + Tolerance) {
371 const double sa = 2.0 * M_PI * (1.0 - cos(asin(radius / distance)));
372 return sa;
373 } else if (distance < radius - Tolerance)
374 return 4.0 * M_PI; // internal point
375 else
376 return 2.0 * M_PI; // surface point
377}
378} // namespace
379
380namespace Mantid::Geometry {
381
382namespace {
383Kernel::Logger logger("CSGObject");
384}
389
394CSGObject::CSGObject(std::string shapeXML)
395 : m_topRule(nullptr), m_boundingBox(), AABBxMax(0), AABByMax(0), AABBzMax(0), AABBxMin(0), AABByMin(0), AABBzMin(0),
396 boolBounded(false), m_objNum(0), m_handler(std::make_shared<GeometryHandler>(this)), bGeometryCaching(false),
397 vtkCacheReader(std::shared_ptr<vtkGeometryCacheReader>()),
398 vtkCacheWriter(std::shared_ptr<vtkGeometryCacheWriter>()), m_shapeXML(std::move(shapeXML)), m_id(),
399 m_material(std::make_unique<Material>()) {}
400
405CSGObject::CSGObject(const CSGObject &A) : CSGObject() { *this = A; }
406
413 if (this != &A) {
414 m_topRule = (A.m_topRule) ? A.m_topRule->clone() : nullptr;
415 AABBxMax = A.AABBxMax;
416 AABByMax = A.AABByMax;
417 AABBzMax = A.AABBzMax;
418 AABBxMin = A.AABBxMin;
419 AABByMin = A.AABByMin;
420 AABBzMin = A.AABBzMin;
422 m_objNum = A.m_objNum;
423 m_handler = A.m_handler->clone();
428 m_id = A.m_id;
429 m_material = std::make_unique<Material>(A.material());
430
431 if (m_topRule)
433 }
434 return *this;
435}
436
438CSGObject::~CSGObject() = default;
439
443void CSGObject::setMaterial(const Kernel::Material &material) { m_material = std::make_unique<Material>(material); }
444
449
456 // Assume invalid shape if object has no 'm_topRule' or surfaces
457 return (m_topRule != nullptr && !m_surList.empty());
458}
459
466int CSGObject::setObject(const int objName, const std::string &lineStr) {
467 // Split line
468 // Does the string now contain junk...
469 static const boost::regex letters("[a-zA-Z]");
470 if (Mantid::Kernel::Strings::StrLook(lineStr, letters))
471 return 0;
472
473 if (procString(lineStr)) // this currently does not fail:
474 {
475 m_surList.clear();
476 m_objNum = objName;
477 return 1;
478 }
479
480 // failure
481 return 0;
482}
483
490void CSGObject::convertComplement(const std::map<int, CSGObject> &MList)
491
492{
493 this->procString(this->cellStr(MList));
494}
495
502std::string CSGObject::cellStr(const std::map<int, CSGObject> &MList) const {
503 std::string TopStr = this->topRule()->display();
504 std::string::size_type pos = TopStr.find('#');
505 std::ostringstream cx;
506 while (pos != std::string::npos) {
507 pos++;
508 cx << TopStr.substr(0, pos); // Everything including the #
509 int cN(0);
510 const int nLen = Mantid::Kernel::Strings::convPartNum(TopStr.substr(pos), cN);
511 if (nLen > 0) {
512 cx << "(";
513 auto vc = MList.find(cN);
514 if (vc == MList.end())
515 throw Kernel::Exception::NotFoundError("Not found in the list of indexable hulls (Object::cellStr)", cN);
516 // Not the recursion :: This will cause no end of problems
517 // if there is an infinite loop.
518 cx << vc->second.cellStr(MList);
519 cx << ") ";
520 pos += nLen;
521 }
522 TopStr.erase(0, pos);
523 pos = TopStr.find('#');
524 }
525 cx << TopStr;
526 return cx.str();
527}
528
529/*
530 * Calculate if there are any complementary components in
531 * the object. That is lines with #(....)
532 * @throw ColErr::ExBase :: Error with processing
533 * @param lineStr :: Input string must: ID Mat {Density} {rules}
534 * @param cellNum :: Number for cell since we don't have one
535 * @retval 0 on no work to do
536 * @retval 1 :: A (maybe there are many) #(...) object found
537 */
538int CSGObject::complementaryObject(const int cellNum, std::string &lineStr) {
539 std::string::size_type posA = lineStr.find("#(");
540 // No work to do ?
541 if (posA == std::string::npos)
542 return 0;
543 posA += 2;
544
545 // First get the area to be removed
546 int numBrackets;
547 std::string::size_type posB;
548 posB = lineStr.find_first_of("()", posA);
549 if (posB == std::string::npos)
550 throw std::runtime_error("Object::complement :: " + lineStr);
551
552 numBrackets = (lineStr[posB] == '(') ? 1 : 0;
553 while (posB != std::string::npos && numBrackets) {
554 posB = lineStr.find_first_of("()", posB);
555 if (posB == std::string::npos)
556 break;
557 numBrackets += (lineStr[posB] == '(') ? 1 : -1;
558 posB++;
559 }
560
561 std::string Part = lineStr.substr(posA, posB - (posA + 1));
562
563 m_objNum = cellNum;
564 if (procString(Part)) {
565 m_surList.clear();
566 lineStr.erase(posA - 1, posB + 1); // Delete brackets ( Part ) .
567 std::ostringstream CompCell;
568 CompCell << cellNum << " ";
569 lineStr.insert(posA - 1, CompCell.str());
570 return 1;
571 }
572
573 throw std::runtime_error("Object::complement :: " + Part);
574}
575
582
583 if (m_topRule)
584 return m_topRule->isComplementary();
585 return 0;
586}
587
595int CSGObject::populate(const std::map<int, std::shared_ptr<Surface>> &surfMap) {
596 std::deque<Rule *> rules;
597 rules.emplace_back(m_topRule.get());
598 while (!rules.empty()) {
599 Rule *T1 = rules.front();
600 rules.pop_front();
601 if (T1) {
602 // if an actual surface process :
603 auto *surface = dynamic_cast<SurfPoint *>(T1);
604 if (surface) {
605 // Ensure that we have a it in the surface list:
606 auto mapFound = surfMap.find(surface->getKeyN());
607 if (mapFound != surfMap.end()) {
608 surface->setKey(mapFound->second);
609 } else {
610 throw Kernel::Exception::NotFoundError("Object::populate", surface->getKeyN());
611 }
612 }
613 // Not a surface : Determine leaves etc and add to stack:
614 else {
615 Rule *TA = T1->leaf(0);
616 Rule *TB = T1->leaf(1);
617 if (TA)
618 rules.emplace_back(TA);
619 if (TB)
620 rules.emplace_back(TB);
621 }
622 }
623 }
625 return 0;
626}
627
639int CSGObject::procPair(std::string &lineStr, std::map<int, std::unique_ptr<Rule>> &ruleMap, int &compUnit) const
640
641{
642 unsigned int Rstart;
643 unsigned int Rend;
644 int Ra, Rb;
645
646 for (Rstart = 0; Rstart < lineStr.size() && lineStr[Rstart] != 'R'; Rstart++)
647 ;
648
649 int type = 0; // intersection
650
651 // plus 1 to skip 'R'
652 if (Rstart == lineStr.size() || !Mantid::Kernel::Strings::convert(lineStr.c_str() + Rstart + 1, Ra) ||
653 ruleMap.find(Ra) == ruleMap.end())
654 return 0;
655
656 for (Rend = Rstart + 1; Rend < lineStr.size() && lineStr[Rend] != 'R'; Rend++) {
657 if (lineStr[Rend] == ':')
658 type = 1; // make union
659 }
660 if (Rend == lineStr.size() || !Mantid::Kernel::Strings::convert(lineStr.c_str() + Rend + 1, Rb) ||
661 ruleMap.find(Rb) == ruleMap.end()) {
662 // No second rule but we did find the first one
663 compUnit = Ra;
664 return 0;
665 }
666 // Get end of number (digital)
667 for (Rend++; Rend < lineStr.size() && lineStr[Rend] >= '0' && lineStr[Rend] <= '9'; Rend++)
668 ;
669
670 // Get rules
671 auto Join =
672 (type) ? std::unique_ptr<Rule>(std::make_unique<Union>(std::move(ruleMap[Ra]), std::move(ruleMap[Rb])))
673 : std::unique_ptr<Rule>(std::make_unique<Intersection>(std::move(ruleMap[Ra]), std::move(ruleMap[Rb])));
674 ruleMap[Ra] = std::move(Join);
675 ruleMap.erase(ruleMap.find(Rb));
676
677 // Remove space round pair
678 int strPos;
679 for (strPos = Rstart - 1; strPos >= 0 && lineStr[strPos] == ' '; strPos--)
680 ;
681 Rstart = (strPos < 0) ? 0 : strPos;
682 for (strPos = Rend; strPos < static_cast<int>(lineStr.size()) && lineStr[strPos] == ' '; strPos++)
683 ;
684 Rend = strPos;
685
686 std::stringstream newRuleStr;
687 newRuleStr << " R" << Ra << " ";
688 lineStr.replace(Rstart, Rend, newRuleStr.str());
689 compUnit = Ra;
690 return 1;
691}
692
698std::unique_ptr<CompGrp> CSGObject::procComp(std::unique_ptr<Rule> ruleItem) const {
699 if (!ruleItem)
700 return std::make_unique<CompGrp>();
701
702 Rule *Pptr = ruleItem->getParent();
703 Rule *RItemptr = ruleItem.get();
704 auto CG = std::make_unique<CompGrp>(Pptr, std::move(ruleItem));
705 if (Pptr) {
706 const int Ln = Pptr->findLeaf(RItemptr);
707 Pptr->setLeaf(std::move(CG), Ln);
708 // CG already in tree. Return empty object.
709 return std::make_unique<CompGrp>();
710 }
711 return CG;
712}
713
732bool CSGObject::isOnSide(const Kernel::V3D &point) const {
733 std::vector<Kernel::V3D> Snorms; // Normals from the contact surface.
734 Snorms.reserve(m_surList.size());
735
736 for (auto vc = m_surList.begin(); vc != m_surList.end(); ++vc) {
737 if ((*vc)->onSurface(point)) {
738 Snorms.emplace_back((*vc)->surfaceNormal(point));
739 // can check direct normal here since one success
740 // means that we can return true and finish
741 if (!checkSurfaceValid(point, Snorms.back()))
742 return true;
743 }
744 }
745 Kernel::V3D NormPair;
746 for (auto xs = Snorms.begin(); xs != Snorms.end(); ++xs)
747 for (auto ys = std::next(xs); ys != Snorms.end(); ++ys) {
748 NormPair = (*ys) + (*xs);
749 try {
750 NormPair.normalize();
751 if (!checkSurfaceValid(point, NormPair))
752 return true;
753 } catch (std::runtime_error &) {
754 }
755 }
756 // Ok everthing failed
757 return false;
758}
759
770int CSGObject::checkSurfaceValid(const Kernel::V3D &point, const Kernel::V3D &direction) const {
771 int status(0);
772 Kernel::V3D tmp = point + direction * (Kernel::Tolerance * 5.0);
773 status = (!isValid(tmp)) ? 1 : -1;
774 tmp -= direction * (Kernel::Tolerance * 10.0);
775 status += (!isValid(tmp)) ? 1 : -1;
776 return status / 2;
777}
778
784bool CSGObject::isValid(const Kernel::V3D &point) const {
785 if (!m_topRule)
786 return false;
787 return m_topRule->isValid(point);
788}
789
795bool CSGObject::isValid(const std::map<int, int> &SMap) const {
796 if (!m_topRule)
797 return false;
798 return m_topRule->isValid(SMap);
799}
800
807int CSGObject::createSurfaceList(const int outFlag) {
808 m_surList.clear();
809 std::stack<const Rule *> TreeLine;
810 TreeLine.push(m_topRule.get());
811 while (!TreeLine.empty()) {
812 const Rule *tmpA = TreeLine.top();
813 TreeLine.pop();
814 const Rule *tmpB = tmpA->leaf(0);
815 const Rule *tmpC = tmpA->leaf(1);
816 if (tmpB || tmpC) {
817 if (tmpB)
818 TreeLine.push(tmpB);
819 if (tmpC)
820 TreeLine.push(tmpC);
821 } else {
822 const auto *SurX = dynamic_cast<const SurfPoint *>(tmpA);
823 if (SurX) {
824 m_surList.emplace_back(SurX->getKey());
825 }
826 }
827 }
828 // Remove duplicates without reordering
829 std::unordered_set<const Surface *> uniqueSurfacePtrs;
830
831 auto newEnd = std::remove_if(m_surList.begin(), m_surList.end(), [&uniqueSurfacePtrs](const Surface *sPtr) {
832 if (uniqueSurfacePtrs.find(sPtr) != std::end(uniqueSurfacePtrs)) {
833 return true;
834 } else {
835 uniqueSurfacePtrs.insert(sPtr);
836 return false;
837 };
838 });
839 m_surList.erase(newEnd, m_surList.end());
840
841 if (outFlag) {
842
843 std::vector<const Surface *>::const_iterator vc;
844 for (vc = m_surList.begin(); vc != m_surList.end(); ++vc) {
845 logger.debug() << "Point == " << *vc << '\n';
846 logger.debug() << (*vc)->getName() << '\n';
847 }
848 }
849 return 1;
850}
851
856std::vector<int> CSGObject::getSurfaceIndex() const {
857 std::vector<int> out;
858 transform(m_surList.begin(), m_surList.end(), std::insert_iterator<std::vector<int>>(out, out.begin()),
859 std::mem_fn(&Surface::getName));
860 return out;
861}
862
870int CSGObject::removeSurface(const int surfNum) {
871 if (!m_topRule)
872 return -1;
873 const int nRemoved = Rule::removeItem(m_topRule, surfNum);
874 if (nRemoved)
876 return nRemoved;
877}
878
886int CSGObject::substituteSurf(const int surfNum, const int newSurfNum, const std::shared_ptr<Surface> &surfPtr) {
887 if (!m_topRule)
888 return 0;
889 const int out = m_topRule->substituteSurf(surfNum, newSurfNum, surfPtr);
890 if (out)
892 return out;
893}
894
898void CSGObject::print() const {
899 std::deque<Rule *> rst;
900 std::vector<int> Cells;
901 int Rcount(0);
902 rst.emplace_back(m_topRule.get());
903 Rule *TA, *TB; // Temp. for storage
904
905 while (!rst.empty()) {
906 Rule *T1 = rst.front();
907 rst.pop_front();
908 if (T1) {
909 Rcount++;
910 auto *surface = dynamic_cast<SurfPoint *>(T1);
911 if (surface)
912 Cells.emplace_back(surface->getKeyN());
913 else {
914 TA = T1->leaf(0);
915 TB = T1->leaf(1);
916 if (TA)
917 rst.emplace_back(TA);
918 if (TB)
919 rst.emplace_back(TB);
920 }
921 }
922 }
923
924 logger.debug() << "Name == " << m_objNum << '\n';
925 logger.debug() << "Rules == " << Rcount << '\n';
926 std::vector<int>::const_iterator mc;
927 logger.debug() << "Surface included == ";
928 for (mc = Cells.begin(); mc < Cells.end(); ++mc) {
929 logger.debug() << (*mc) << " ";
930 }
931 logger.debug() << '\n';
932}
933
938 std::unique_ptr<Rule> NCG = procComp(std::move(m_topRule));
939 m_topRule = std::move(NCG);
940}
941
946 logger.debug() << "Name == " << m_objNum << '\n';
947 logger.debug() << m_topRule->display() << '\n';
948}
949
955std::string CSGObject::cellCompStr() const {
956 std::ostringstream objStr;
957 if (m_topRule)
958 objStr << m_topRule->display();
959 return objStr.str();
960}
961
967std::string CSGObject::str() const {
968 std::ostringstream objStr;
969 if (m_topRule) {
970 objStr << m_objNum << " ";
971 objStr << m_topRule->display();
972 }
973 return objStr.str();
974}
975
981void CSGObject::write(std::ostream &outStream) const {
982 std::ostringstream objStr;
983 objStr.precision(10);
984 objStr << str();
985 Mantid::Kernel::Strings::writeMCNPX(objStr.str(), outStream);
986}
987
994int CSGObject::procString(const std::string &lineStr) {
995 m_topRule = nullptr;
996 std::map<int, std::unique_ptr<Rule>> RuleList; // List for the rules
997 int Ridx = 0; // Current index (not necessary size of RuleList
998 // SURFACE REPLACEMENT
999 // Now replace all free planes/Surfaces with appropiate Rxxx
1000 std::unique_ptr<SurfPoint> TmpR; // Tempory Rule storage position
1001 std::unique_ptr<CompObj> TmpO; // Tempory Rule storage position
1002
1003 std::string Ln = lineStr;
1004 // Remove all surfaces :
1005 std::ostringstream cx;
1006 const std::string::size_type length = Ln.length();
1007 for (size_t i = 0; i < length; i++) {
1008 if (isdigit(Ln[i]) || Ln[i] == '-') {
1009 int SN;
1010 int nLen = Mantid::Kernel::Strings::convPartNum(Ln.substr(i), SN);
1011 if (!nLen)
1012 throw std::invalid_argument("Invalid surface string in Object::ProcString : " + lineStr);
1013 // Process #Number
1014 if (i != 0 && Ln[i - 1] == '#') {
1015 TmpO = std::make_unique<CompObj>();
1016 TmpO->setObjN(SN);
1017 RuleList[Ridx] = std::move(TmpO);
1018 } else // Normal rule
1019 {
1020 TmpR = std::make_unique<SurfPoint>();
1021 TmpR->setKeyN(SN);
1022 RuleList[Ridx] = std::move(TmpR);
1023 }
1024 cx << " R" << Ridx << " ";
1025 Ridx++;
1026 i += nLen;
1027 }
1028 if (i < length)
1029 cx << Ln[i];
1030 }
1031 Ln = cx.str();
1032 // PROCESS BRACKETS
1033
1034 int brack_exists = 1;
1035 while (brack_exists) {
1036 std::string::size_type rbrack = Ln.find(')');
1037 std::string::size_type lbrack = Ln.rfind('(', rbrack);
1038 if (rbrack != std::string::npos && lbrack != std::string::npos) {
1039 std::string Lx = Ln.substr(lbrack + 1, rbrack - lbrack - 1);
1040 // Check to see if a #( unit
1041 int compUnit(0);
1042 while (procPair(Lx, RuleList, compUnit))
1043 ;
1044 Ln.replace(lbrack, 1 + rbrack - lbrack, Lx);
1045 // Search back and find if # ( exists.
1046 int hCnt;
1047 for (hCnt = static_cast<int>(lbrack) - 1; hCnt >= 0 && isspace(Ln[hCnt]); hCnt--)
1048 ;
1049 if (hCnt >= 0 && Ln[hCnt] == '#') {
1050 RuleList[compUnit] = procComp(std::move(RuleList[compUnit]));
1051 Ln.erase(hCnt, lbrack - hCnt);
1052 }
1053 } else
1054 brack_exists = 0;
1055 }
1056 // Do outside loop...
1057 int nullInt;
1058 while (procPair(Ln, RuleList, nullInt)) {
1059 }
1060
1061 if (RuleList.size() == 1) {
1062 m_topRule = std::move((RuleList.begin())->second);
1063 } else {
1064 throw std::logic_error("Object::procString() - Unexpected number of "
1065 "surface rules found. Expected=1, found=" +
1066 std::to_string(RuleList.size()));
1067 }
1068 return 1;
1069}
1070
1077 // Number of intersections original track
1078 int originalCount = track.count();
1079
1080 // Loop over all the surfaces to get the intercepts, i.e. populating
1081 // points into LI
1082 LineIntersectVisit LI(track.startPoint(), track.direction());
1083
1084 for (auto &surface : m_surList) {
1085 surface->acceptVisitor(LI);
1086 }
1087
1088 // Call the pruner so that we don't have to worry about the duplicates and
1089 // the order
1091
1092 // IPoints: std::vector<Geometry::V3D>
1093 const auto &IPoints(LI.getPoints());
1094 // dPoints: std::vector<double>, distance to the start point for each point
1095 const auto &dPoints(LI.getDistance());
1096 // nPoints: size_t, total number of points, for most shape, this number should
1097 // be a single digit number
1098 const size_t nPoints(IPoints.size());
1099
1100 // Loop over all the points and add them to the track
1101 for (size_t i = 0; i < nPoints; i++) {
1102 // skip over the points that are before the starting points
1103 if (dPoints[i] < 0)
1104 continue;
1105
1106 //
1107 const auto &currentPt(IPoints[i]);
1108 const auto &prePt((i == 0 || dPoints[i - 1] <= 0) ? track.startPoint() : IPoints[i - 1]);
1109 const auto &nextPt(i + 1 < nPoints ? IPoints[i + 1] : currentPt + currentPt - prePt);
1110
1111 // get the intercept type
1112 const TrackDirection trackType = calcValidTypeBy3Points(prePt, currentPt, nextPt);
1113 // only record the intercepts that is interacting with the shape directly
1114 if (trackType != TrackDirection::INVALID) {
1115 track.addPoint(trackType, currentPt, *this);
1116 }
1117 }
1118
1119 track.buildLink();
1120 // Return number of track segments added
1121 return (track.count() - originalCount);
1122}
1123
1130double CSGObject::distance(const Geometry::Track &track) const {
1131 LineIntersectVisit LI(track.startPoint(), track.direction());
1132 for (auto &surface : m_surList) {
1133 surface->acceptVisitor(LI);
1134 }
1136 const auto &distances(LI.getDistance());
1137 if (!distances.empty()) {
1138 return std::abs(*std::min_element(std::begin(distances), std::end(distances)));
1139 } else {
1140 std::ostringstream os;
1141 os << "Unable to find intersection with object with track starting at " << track.startPoint() << " in direction "
1142 << track.direction() << "\n";
1143 throw std::runtime_error(os.str());
1144 }
1145}
1146
1156 // NOTE: This method is sensitive to the geometry dimension, and will lead to
1157 // incorrect identification due to the value of VALID_INTERCEPT_POINT_SHIFT
1158 // being either too large or too small.
1159 const Kernel::V3D shift(uVec * VALID_INTERCEPT_POINT_SHIFT);
1160 const int flagA = isValid(point - shift);
1161 const int flagB = isValid(point + shift);
1162 if (!(flagA ^ flagB))
1165}
1166
1177 const Kernel::V3D &nxtPt) const {
1178 // upstream point
1179 const auto upstreamPt = (prePt + curPt) * 0.5;
1180 const auto upstreamPtInsideShape = isValid(upstreamPt);
1181 // downstream point
1182 const auto downstreamPt = (curPt + nxtPt) * 0.5;
1183 const auto downstreamPtInsideShape = isValid(downstreamPt);
1184 // NOTE:
1185 // When the track is parallel to the shape, it can still intersect with its
1186 // component (infinite) surface.
1187 // __Legends__
1188 // o-->: track
1189 // o: track starting point
1190 // m: upstreamPt
1191 // x: currentPt
1192 // d: downstreamPt
1193 // | | o
1194 // | | |
1195 // ---------|--------------------|---------------x------
1196 // | SHAPE | |
1197 // o--m---x-d----> o---m---x-d----> v Invalid
1198 // | Entering | Leaving
1199 // ---------|--------------------|----------------------
1200 // | |
1201 if (!(upstreamPtInsideShape ^ downstreamPtInsideShape))
1203 else
1205}
1206
1216double CSGObject::solidAngle(const Kernel::V3D &observer) const {
1217 if (this->numberOfTriangles() > 30000)
1218 return rayTraceSolidAngle(observer);
1219 return triangulatedSolidAngle(observer);
1220}
1221
1230double CSGObject::solidAngle(const Kernel::V3D &observer, const Kernel::V3D &scaleFactor) const {
1231 return triangulatedSolidAngle(observer, scaleFactor);
1232}
1233
1239double CSGObject::rayTraceSolidAngle(const Kernel::V3D &observer) const {
1240 // Calculation of solid angle as numerical double integral over all angles.
1241 // This could be optimized further e.g. by using a light weight version of
1242 // the interceptSurface method - this does more work than is necessary in this
1243 // application.
1244 // Accuracy is of the order of 1% for objects with an accurate bounding box,
1245 // though less in the case of high aspect ratios.
1246 //
1247 // resBB controls accuracy and cost - linear accuracy improvement with
1248 // increasing res, but quadratic increase in run time. If no bounding box
1249 // found, resNoBB used instead.
1250 const int resNoBB = 200, resPhiMin = 10;
1251 int res = resNoBB, itheta, jphi, resPhi;
1252 double theta, phi, sum, dphi, dtheta;
1253 if (this->isValid(observer) && !this->isOnSide(observer))
1254 return 4 * M_PI; // internal point
1255 if (this->isOnSide(observer))
1256 return 2 * M_PI; // this is wrong if on an edge
1257 // Use BB if available, and if observer not within it
1258 const BoundingBox &boundingBox = getBoundingBox();
1259 double thetaMax = M_PI;
1260 bool useBB = false, usePt = false;
1261 Kernel::V3D ptInObject, axis;
1262 Quat zToPt;
1263
1264 // Is the bounding box a reasonable one?
1265 if (boundingBox.isNonNull() && !boundingBox.isPointInside(observer)) {
1266 useBB = usePt = true;
1267 thetaMax = boundingBox.angularWidth(observer);
1268 ptInObject = boundingBox.centrePoint();
1269 const int resBB = 100;
1270 res = resBB;
1271 }
1272 // Try and find a point in the object if useful bounding box not found
1273 if (!useBB) {
1274 usePt = getPointInObject(ptInObject) == 1;
1275 }
1276 if (usePt) {
1277 // found point in object, now get rotation that maps z axis to this
1278 // direction from observer
1279 ptInObject -= observer;
1280 double theta0 = -180.0 / M_PI * acos(ptInObject.Z() / ptInObject.norm());
1281 Kernel::V3D zDir(0.0, 0.0, 1.0);
1282 axis = ptInObject.cross_prod(zDir);
1283 if (axis.nullVector())
1284 axis = Kernel::V3D(1.0, 0.0, 0.0);
1285 zToPt(theta0, axis);
1286 }
1287 dtheta = thetaMax / res;
1288 int count = 0, countPhi;
1289 sum = 0.;
1290 for (itheta = 1; itheta <= res; itheta++) {
1291 // itegrate theta from 0 to maximum from bounding box, or PI otherwise
1292 theta = thetaMax * (itheta - 0.5) / res;
1293 resPhi = static_cast<int>(res * sin(theta));
1294 if (resPhi < resPhiMin)
1295 resPhi = resPhiMin;
1296 dphi = 2 * M_PI / resPhi;
1297 countPhi = 0;
1298 for (jphi = 1; jphi <= resPhi; jphi++) {
1299 // integrate phi from 0 to 2*PI
1300 phi = 2.0 * M_PI * (jphi - 0.5) / resPhi;
1301 Kernel::V3D dir(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
1302 if (usePt)
1303 zToPt.rotate(dir);
1304 if (!useBB || boundingBox.doesLineIntersect(observer, dir)) {
1305 Track tr(observer, dir);
1306 if (this->interceptSurface(tr) > 0) {
1307 sum += dtheta * dphi * sin(theta);
1308 countPhi++;
1309 }
1310 }
1311 }
1312 // this break (only used if no BB defined) may be wrong if object has hole
1313 // in middle
1314 if (!useBB && countPhi == 0)
1315 break;
1316 count += countPhi;
1317 }
1318 if (!useBB && count < resPhiMin + 1) {
1319 // case of no bound box defined and object has few if any points in sum
1320 // redo integration on finer scale
1321 thetaMax = thetaMax * (itheta - 0.5) / res;
1322 dtheta = thetaMax / res;
1323 sum = 0;
1324 for (itheta = 1; itheta <= res; itheta++) {
1325 theta = thetaMax * (itheta - 0.5) / res;
1326 resPhi = static_cast<int>(res * sin(theta));
1327 if (resPhi < resPhiMin)
1328 resPhi = resPhiMin;
1329 dphi = 2 * M_PI / resPhi;
1330 countPhi = 0;
1331 for (jphi = 1; jphi <= resPhi; jphi++) {
1332 phi = 2.0 * M_PI * (jphi - 0.5) / resPhi;
1333 Kernel::V3D dir(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
1334 if (usePt)
1335 zToPt.rotate(dir);
1336 Track tr(observer, dir);
1337 if (this->interceptSurface(tr) > 0) {
1338 sum += dtheta * dphi * sin(theta);
1339 countPhi++;
1340 }
1341 }
1342 if (countPhi == 0)
1343 break;
1344 }
1345 }
1346
1347 return sum;
1348}
1349
1357double CSGObject::triangulatedSolidAngle(const V3D &observer) const {
1358 //
1359 // Because the triangles from OC are not consistently ordered wrt their
1360 // outward normal internal points give incorrect solid angle. Surface
1361 // points are difficult to get right with the triangle based method.
1362 // Hence catch these two (unlikely) cases.
1363 const BoundingBox &boundingBox = this->getBoundingBox();
1364 if (boundingBox.isNonNull() && boundingBox.isPointInside(observer)) {
1365 if (isValid(observer)) {
1366 if (isOnSide(observer))
1367 return (2.0 * M_PI);
1368 else
1369 return (4.0 * M_PI);
1370 }
1371 }
1372
1373 // If the object is a simple shape use the special methods
1374 double height(0.0), radius(0.0), innerRadius(0.0);
1376 std::vector<Mantid::Kernel::V3D> geometry_vectors;
1377 // Maximum of 4 vectors depending on the type
1378 geometry_vectors.reserve(4);
1379 this->GetObjectGeom(type, geometry_vectors, innerRadius, radius, height);
1380 auto nTri = this->numberOfTriangles();
1381 // Cylinders are by far the most frequently used
1382 switch (type) {
1384 return cuboidSolidAngle(observer, geometry_vectors);
1385 break;
1387 return sphereSolidAngle(observer, geometry_vectors, radius);
1388 break;
1390 return cylinderSolidAngle(observer, geometry_vectors[0], geometry_vectors[1], radius, height);
1391 break;
1393 return coneSolidAngle(observer, geometry_vectors[0], geometry_vectors[1], radius, height);
1394 break;
1395 default:
1396 if (nTri == 0) // Fall back to raytracing if there are no triangles
1397 {
1398 return rayTraceSolidAngle(observer);
1399 } else { // Compute a generic shape that has been triangulated
1400 const auto &vertices = this->getTriangleVertices();
1401 const auto &faces = this->getTriangleFaces();
1402 double sangle(0.0), sneg(0.0);
1403 for (size_t i = 0; i < nTri; i++) {
1404 int p1 = faces[i * 3], p2 = faces[i * 3 + 1], p3 = faces[i * 3 + 2];
1405 V3D vp1 = V3D(vertices[3 * p1], vertices[3 * p1 + 1], vertices[3 * p1 + 2]);
1406 V3D vp2 = V3D(vertices[3 * p2], vertices[3 * p2 + 1], vertices[3 * p2 + 2]);
1407 V3D vp3 = V3D(vertices[3 * p3], vertices[3 * p3 + 1], vertices[3 * p3 + 2]);
1408 double sa = triangleSolidAngle(vp1, vp2, vp3, observer);
1409 if (sa > 0.0) {
1410 sangle += sa;
1411 } else {
1412 sneg += sa;
1413 }
1414 }
1415 /* We assume that objects are opaque to neutrons and that objects define
1416 * closed surfaces which are convex. For such objects negative solid angle
1417 * equals positive solid angle. This is true providing that the winding
1418 * order is defined properly such that the contribution from each triangle
1419 * w.r.t the observer gets counted to either the negative or positive
1420 * contribution correctly. If that is done correctly then it would only be
1421 * necessary to consider the positive contribution to the solid angle.
1422 *
1423 * The following provides a fix to situations where the winding order is
1424 * incorrectly defined. It does not matter if the contribution is positive
1425 * or negative since we take the average.
1426 */
1427 return 0.5 * (sangle - sneg);
1428 }
1429 }
1430}
1441double CSGObject::triangulatedSolidAngle(const V3D &observer, const V3D &scaleFactor) const {
1442 //
1443 // Because the triangles from OC are not consistently ordered wrt their
1444 // outward normal internal points give incorrect solid angle. Surface
1445 // points are difficult to get right with the triangle based method.
1446 // Hence catch these two (unlikely) cases.
1447 const BoundingBox &boundingBox = this->getBoundingBox();
1448 double sx = scaleFactor[0], sy = scaleFactor[1], sz = scaleFactor[2];
1449 const V3D sObserver = observer;
1450 if (boundingBox.isNonNull() && boundingBox.isPointInside(sObserver)) {
1451 if (isValid(sObserver)) {
1452 if (isOnSide(sObserver))
1453 return (2.0 * M_PI);
1454 else
1455 return (4.0 * M_PI);
1456 }
1457 }
1458
1459 auto nTri = this->numberOfTriangles();
1460 //
1461 // If triangulation is not available fall back to ray tracing method, unless
1462 // object is a standard shape, currently Cuboid or Sphere. Should add Cylinder
1463 // and Cone cases as well.
1464 //
1465 if (nTri == 0) {
1466 double height = 0.0, radius(0.0), innerRadius;
1468 std::vector<Kernel::V3D> vectors;
1469 this->GetObjectGeom(type, vectors, innerRadius, radius, height);
1470 switch (type) {
1472 std::transform(vectors.begin(), vectors.end(), vectors.begin(),
1473 [scaleFactor](const V3D &v) { return v * scaleFactor; });
1474 return cuboidSolidAngle(observer, vectors);
1475 break;
1477 return sphereSolidAngle(observer, vectors, radius);
1478 break;
1479 default:
1480 break;
1481 }
1482
1483 //
1484 // No special case, do the ray trace.
1485 //
1486 return rayTraceSolidAngle(observer);
1487 }
1488 const auto &vertices = this->getTriangleVertices();
1489 const auto &faces = this->getTriangleFaces();
1490 double sangle(0.0), sneg(0.0);
1491 for (size_t i = 0; i < nTri; i++) {
1492 int p1 = faces[i * 3], p2 = faces[i * 3 + 1], p3 = faces[i * 3 + 2];
1493 // would be more efficient to pre-multiply the vertices (copy of) by these
1494 // factors beforehand
1495 V3D vp1 = V3D(sx * vertices[3 * p1], sy * vertices[3 * p1 + 1], sz * vertices[3 * p1 + 2]);
1496 V3D vp2 = V3D(sx * vertices[3 * p2], sy * vertices[3 * p2 + 1], sz * vertices[3 * p2 + 2]);
1497 V3D vp3 = V3D(sx * vertices[3 * p3], sy * vertices[3 * p3 + 1], sz * vertices[3 * p3 + 2]);
1498 double sa = triangleSolidAngle(vp1, vp2, vp3, observer);
1499 if (sa > 0.0)
1500 sangle += sa;
1501 else
1502 sneg += sa;
1503 }
1504 return (0.5 * (sangle - sneg));
1505}
1506
1512double CSGObject::volume() const {
1514 double height;
1515 double radius;
1516 double innerRadius;
1517 std::vector<Kernel::V3D> vectors;
1518 this->GetObjectGeom(type, vectors, innerRadius, radius, height);
1519 switch (type) {
1521 // Here, the volume is calculated by the triangular method.
1522 // We use one of the vertices (vectors[0]) as the reference
1523 // point.
1524 double volume = 0.0;
1525 // Vertices. Name codes follow flb = front-left-bottom etc.
1526 const Kernel::V3D &flb = vectors[0];
1527 const Kernel::V3D &flt = vectors[1];
1528 const Kernel::V3D &frb = vectors[3];
1529 const Kernel::V3D frt = frb + flt - flb;
1530 const Kernel::V3D &blb = vectors[2];
1531 const Kernel::V3D blt = blb + flt - flb;
1532 const Kernel::V3D brb = blb + frb - flb;
1533 const Kernel::V3D brt = frt + blb - flb;
1534 // Normals point out, follow right-handed rule when
1535 // defining the triangle faces.
1536 volume += flb.scalar_prod(flt.cross_prod(blb));
1537 volume += blb.scalar_prod(flt.cross_prod(blt));
1538 volume += flb.scalar_prod(frb.cross_prod(flt));
1539 volume += frb.scalar_prod(frt.cross_prod(flt));
1540 volume += flb.scalar_prod(blb.cross_prod(frb));
1541 volume += blb.scalar_prod(brb.cross_prod(frb));
1542 volume += frb.scalar_prod(brb.cross_prod(frt));
1543 volume += brb.scalar_prod(brt.cross_prod(frt));
1544 volume += flt.scalar_prod(frt.cross_prod(blt));
1545 volume += frt.scalar_prod(brt.cross_prod(blt));
1546 volume += blt.scalar_prod(brt.cross_prod(blb));
1547 volume += brt.scalar_prod(brb.cross_prod(blb));
1548 return volume / 6;
1549 }
1551 return 4.0 / 3.0 * M_PI * radius * radius * radius;
1553 return M_PI * radius * radius * height;
1555 return M_PI * height * (radius * radius - innerRadius * innerRadius);
1556 default:
1557 // Fall back to Monte Carlo method.
1558 return monteCarloVolume();
1559 }
1560}
1561
1569 using namespace boost::accumulators;
1570 const int singleShotIterations = 10000;
1571 accumulator_set<double, features<tag::mean, tag::error_of<tag::mean>>> accumulate;
1572 // For seeding the single shot runs.
1573 std::ranlux48 rnEngine;
1574 // Warm up statistics.
1575 for (int i = 0; i < 10; ++i) {
1576 const auto seed = rnEngine();
1577 const double volume = singleShotMonteCarloVolume(singleShotIterations, seed);
1578 accumulate(volume);
1579 }
1580 const double relativeErrorTolerance = 1e-3;
1581 double currentMean;
1582 double currentError;
1583 do {
1584 const auto seed = rnEngine();
1585 const double volume = singleShotMonteCarloVolume(singleShotIterations, seed);
1586 accumulate(volume);
1587 currentMean = mean(accumulate);
1588 currentError = error_of<tag::mean>(accumulate);
1589 if (std::isnan(currentError)) {
1590 currentError = 0;
1591 }
1592 } while (currentError / currentMean > relativeErrorTolerance);
1593 return currentMean;
1594}
1595
1602double CSGObject::singleShotMonteCarloVolume(const int shotSize, const size_t seed) const {
1603 const auto &boundingBox = getBoundingBox();
1604 if (boundingBox.isNull()) {
1605 throw std::runtime_error("Cannot calculate volume: invalid bounding box.");
1606 }
1607 int totalHits = 0;
1608 const double boundingDx = boundingBox.xMax() - boundingBox.xMin();
1609 const double boundingDy = boundingBox.yMax() - boundingBox.yMin();
1610 const double boundingDz = boundingBox.zMax() - boundingBox.zMin();
1611 PARALLEL {
1612 const auto threadCount = PARALLEL_NUMBER_OF_THREADS;
1613 const auto currentThreadNum = PARALLEL_THREAD_NUMBER;
1614 size_t blocksize = shotSize / threadCount;
1615 // cppcheck-suppress knownConditionTrueFalse
1616 if (currentThreadNum == threadCount - 1) {
1617 // Last thread may have to do threadCount extra iterations in
1618 // the worst case.
1619 blocksize = shotSize - (threadCount - 1) * blocksize;
1620 }
1621 std::mt19937 rnEngine(static_cast<std::mt19937::result_type>(seed));
1622 // All threads init their engine with the same seed.
1623 // We discard the random numbers used by the other threads.
1624 // This ensures reproducible results independent of the number
1625 // of threads.
1626 // We need three random numbers for each iteration.
1627 rnEngine.discard(currentThreadNum * 3 * blocksize);
1628 std::uniform_real_distribution<double> rnDistribution(0.0, 1.0);
1629 int hits = 0;
1630 for (int i = 0; i < static_cast<int>(blocksize); ++i) {
1631 double rnd = rnDistribution(rnEngine);
1632 const double x = boundingBox.xMin() + rnd * boundingDx;
1633 rnd = rnDistribution(rnEngine);
1634 const double y = boundingBox.yMin() + rnd * boundingDy;
1635 rnd = rnDistribution(rnEngine);
1636 const double z = boundingBox.zMin() + rnd * boundingDz;
1637 if (isValid(V3D(x, y, z))) {
1638 ++hits;
1639 }
1640 }
1641 // Collect results.
1643 totalHits += hits;
1644 }
1645 const double ratio = static_cast<double>(totalHits) / static_cast<double>(shotSize);
1646 const double boundingVolume = boundingDx * boundingDy * boundingDz;
1647 return ratio * boundingVolume;
1648}
1649
1655 // This member function is const given that from a user's perspective it is
1656 // perfectly reasonable to call it on a const object. We need to call a
1657 // non-const function in places to update the cache, which is where the
1658 // const_cast comes into play.
1659
1660 // If we don't know the extent of the object, the bounding box doesn't mean
1661 // anything
1662 if (!m_topRule) {
1663 const_cast<CSGObject *>(this)->setNullBoundingBox();
1664 return m_boundingBox;
1665 }
1666
1667 // We have a bounding box already, so just return it
1669 return m_boundingBox;
1670
1671 // Try to calculate using Rule method first
1672 const_cast<CSGObject *>(this)->calcBoundingBoxByRule();
1674 return m_boundingBox;
1675
1676 // Rule method failed; Try geometric method
1677 const_cast<CSGObject *>(this)->calcBoundingBoxByGeometry();
1679 return m_boundingBox;
1680
1681 // Geometric method failed; try to calculate by vertices
1682 const_cast<CSGObject *>(this)->calcBoundingBoxByVertices();
1684 return m_boundingBox;
1685
1686 // All options failed; give up
1687 // Set to a large box so that a) we don't keep trying to calculate a box
1688 // every time this is called and b) to serve as a visual indicator that
1689 // something went wrong.
1690 const_cast<CSGObject *>(this)->defineBoundingBox(100, 100, 100, -100, -100, -100);
1691 return m_boundingBox;
1692}
1693
1702 // Must have a top rule for this to work
1703 if (!m_topRule)
1704 return;
1705
1706 // Set up some unreasonable values that will be refined
1707 const double huge(1e10);
1708 const double big(1e4);
1709 double minX(-huge), minY(-huge), minZ(-huge);
1710 double maxX(huge), maxY(huge), maxZ(huge);
1711
1712 // Try to use the Rule system to derive the box
1713 m_topRule->getBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1714
1715 // Check whether values are reasonable now. Rule system will fail to produce
1716 // a reasonable box if the shape is not axis-aligned.
1717 if (minX > -big && maxX < big && minY > -big && maxY < big && minZ > -big && maxZ < big && minX <= maxX &&
1718 minY <= maxY && minZ <= maxZ) {
1719 // Values make sense, cache and return bounding box
1720 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1721 }
1722}
1723
1733 // Grab vertex information
1734 auto vertCount = this->numberOfVertices();
1735
1736 if (vertCount > 0) {
1737 const auto &vertArray = this->getTriangleVertices();
1738 // Unreasonable extents to be overwritten by loop
1739 constexpr double huge = 1e10;
1740 double minX, maxX, minY, maxY, minZ, maxZ;
1741 minX = minY = minZ = huge;
1742 maxX = maxY = maxZ = -huge;
1743
1744 // Loop over all vertices and determine minima and maxima on each axis
1745 for (size_t i = 0; i < vertCount; ++i) {
1746 auto vx = vertArray[3 * i + 0];
1747 auto vy = vertArray[3 * i + 1];
1748 auto vz = vertArray[3 * i + 2];
1749
1750 minX = std::min(minX, vx);
1751 maxX = std::max(maxX, vx);
1752 minY = std::min(minY, vy);
1753 maxY = std::max(maxY, vy);
1754 minZ = std::min(minZ, vz);
1755 maxZ = std::max(maxZ, vz);
1756 }
1757
1758 // Store bounding box in cache
1759 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1760 }
1761}
1762
1770 // Must have a GeometryHandler for this to work
1771 if (!m_handler)
1772 return;
1773
1774 // Extent of bounding box
1775 double minX, maxX, minY, maxY, minZ, maxZ;
1776
1777 // Shape geometry data
1779 std::vector<Kernel::V3D> vectors;
1780 double radius;
1781 double height;
1782 double innerRadius;
1783
1784 // Will only work for shapes with ShapeInfo
1785 m_handler->GetObjectGeom(type, vectors, innerRadius, radius, height);
1786 // Type of shape is given as a simple integer
1787 switch (type) {
1789 // Points as defined in IDF XML
1790 auto &lfb = vectors[0]; // Left-Front-Bottom
1791 auto &lft = vectors[1]; // Left-Front-Top
1792 auto &lbb = vectors[2]; // Left-Back-Bottom
1793 auto &rfb = vectors[3]; // Right-Front-Bottom
1794
1795 // Calculate and add missing corner points to vectors
1796 auto lbt = lft + (lbb - lfb); // Left-Back-Top
1797 auto rft = rfb + (lft - lfb); // Right-Front-Top
1798 auto rbb = lbb + (rfb - lfb); // Right-Back-Bottom
1799 auto rbt = rbb + (rft - rfb); // Right-Back-Top
1800
1801 vectors.emplace_back(lbt);
1802 vectors.emplace_back(rft);
1803 vectors.emplace_back(rbb);
1804 vectors.emplace_back(rbt);
1805
1806 // Unreasonable extents to be replaced by first loop cycle
1807 constexpr double huge = 1e10;
1808 minX = minY = minZ = huge;
1809 maxX = maxY = maxZ = -huge;
1810
1811 // Loop over all corner points to find minima and maxima on each axis
1812 for (const auto &vector : vectors) {
1813 minX = std::min(minX, vector.X());
1814 maxX = std::max(maxX, vector.X());
1815 minY = std::min(minY, vector.Y());
1816 maxY = std::max(maxY, vector.Y());
1817 minZ = std::min(minZ, vector.Z());
1818 maxZ = std::max(maxZ, vector.Z());
1819 }
1820 } break;
1822 // These will be replaced by more realistic values in the loop below
1823 minX = minY = minZ = std::numeric_limits<decltype(minZ)>::max();
1824 maxX = maxY = maxZ = -std::numeric_limits<decltype(maxZ)>::max();
1825
1826 // Loop over all corner points to find minima and maxima on each axis
1827 for (const auto &vector : vectors) {
1828 minX = std::min(minX, vector.X());
1829 maxX = std::max(maxX, vector.X());
1830 minY = std::min(minY, vector.Y());
1831 maxY = std::max(maxY, vector.Y());
1832 minZ = std::min(minZ, vector.Z());
1833 maxZ = std::max(maxZ, vector.Z());
1834 }
1835 } break;
1837 // Center-point of base and normalized axis based on IDF XML
1838 auto &base = vectors[0];
1839 auto &axis = vectors[1];
1840 auto top = base + (axis * height); // Center-point of other end
1841
1842 // How much of the radius must be considered for each axis
1843 // (If this ever becomes a performance issue, you could use just the radius
1844 // for a quick approx that is still guaranteed to fully contain the shape)
1845 volatile auto rx = radius * sqrt(pow(axis.Y(), 2) + pow(axis.Z(), 2));
1846 volatile auto ry = radius * sqrt(pow(axis.X(), 2) + pow(axis.Z(), 2));
1847 volatile auto rz = radius * sqrt(pow(axis.X(), 2) + pow(axis.Y(), 2));
1848
1849 // The bounding box is drawn around the base and top center-points,
1850 // then expanded in order to account for the radius
1851 minX = std::min(base.X(), top.X()) - rx;
1852 maxX = std::max(base.X(), top.X()) + rx;
1853 minY = std::min(base.Y(), top.Y()) - ry;
1854 maxY = std::max(base.Y(), top.Y()) + ry;
1855 minZ = std::min(base.Z(), top.Z()) - rz;
1856 maxZ = std::max(base.Z(), top.Z()) + rz;
1857 } break;
1858
1860 auto &tip = vectors[0]; // Tip-point of cone
1861 auto &axis = vectors[1]; // Normalized axis
1862 auto base = tip + (axis * height); // Center of base
1863
1864 // How much of the radius must be considered for each axis
1865 // (If this ever becomes a performance issue, you could use just the radius
1866 // for a quick approx that is still guaranteed to fully contain the shape)
1867 auto rx = radius * sqrt(pow(axis.Y(), 2) + pow(axis.Z(), 2));
1868 auto ry = radius * sqrt(pow(axis.X(), 2) + pow(axis.Z(), 2));
1869 auto rz = radius * sqrt(pow(axis.X(), 2) + pow(axis.Y(), 2));
1870
1871 // For a cone, the adjustment is only applied to the base
1872 minX = std::min(tip.X(), base.X() - rx);
1873 maxX = std::max(tip.X(), base.X() + rx);
1874 minY = std::min(tip.Y(), base.Y() - ry);
1875 maxY = std::max(tip.Y(), base.Y() + ry);
1876 minZ = std::min(tip.Z(), base.Z() - rz);
1877 maxZ = std::max(tip.Z(), base.Z() + rz);
1878 } break;
1879
1880 default: // Invalid (0, -1) or SPHERE (2) which should be handled by Rules
1881 return; // Don't store bounding box
1882 }
1883
1884 // Store bounding box in cache
1885 defineBoundingBox(maxX, maxY, maxZ, minX, minY, minZ);
1886}
1887
1900void CSGObject::getBoundingBox(double &xmax, double &ymax, double &zmax, double &xmin, double &ymin,
1901 double &zmin) const {
1902 if (!m_topRule) { // If no rule defined then return zero boundbing box
1903 xmax = ymax = zmax = xmin = ymin = zmin = 0.0;
1904 return;
1905 }
1906 if (!boolBounded) {
1907 AABBxMax = xmax;
1908 AABByMax = ymax;
1909 AABBzMax = zmax;
1910 AABBxMin = xmin;
1911 AABByMin = ymin;
1912 AABBzMin = zmin;
1914 if (AABBxMax >= xmax || AABBxMin <= xmin || AABByMax >= ymax || AABByMin <= ymin || AABBzMax >= zmax ||
1915 AABBzMin <= zmin)
1916 boolBounded = false;
1917 else
1918 boolBounded = true;
1919 }
1920 xmax = AABBxMax;
1921 ymax = AABByMax;
1922 zmax = AABBzMax;
1923 xmin = AABBxMin;
1924 ymin = AABByMin;
1925 zmin = AABBzMin;
1926}
1927
1942void CSGObject::defineBoundingBox(const double &xMax, const double &yMax, const double &zMax, const double &xMin,
1943 const double &yMin, const double &zMin) {
1944 BoundingBox::checkValid(xMax, yMax, zMax, xMin, yMin, zMin);
1945
1946 AABBxMax = xMax;
1947 AABByMax = yMax;
1948 AABBzMax = zMax;
1949 AABBxMin = xMin;
1950 AABByMin = yMin;
1951 AABBzMin = zMin;
1952 boolBounded = true;
1953
1954 PARALLEL_CRITICAL(defineBoundingBox) { m_boundingBox = BoundingBox(xMax, yMax, zMax, xMin, yMin, zMin); }
1955}
1956
1961
1968 //
1969 // Simple method - check if origin in object, if not search directions along
1970 // axes. If that fails, try centre of boundingBox, and paths about there
1971 //
1972 Kernel::V3D testPt(0, 0, 0);
1973 if (searchForObject(testPt)) {
1974 point = testPt;
1975 return 1;
1976 }
1977 // Try centre of bounding box as initial guess, if we have one.
1978 const BoundingBox &boundingBox = getBoundingBox();
1979 if (boundingBox.isNonNull()) {
1980 testPt = boundingBox.centrePoint();
1981 if (searchForObject(testPt) > 0) {
1982 point = testPt;
1983 return 1;
1984 }
1985 }
1986
1987 return 0;
1988}
1989
2002 const size_t maxAttempts) const {
2003 boost::optional<V3D> point{boost::none};
2004 // If the shape fills its bounding box well enough then the most efficient
2005 // way to get the point is just brute force. We'll try that first with
2006 // just a few attempts.
2007 // Increasing the brute force attemps speeds up the shapes that fill
2008 // the bounding box but slows down shapes that leave lots of void
2009 // within the box. So there is a sweet spot which depends on the actual
2010 // shape, its dimension and orientation.
2011 const size_t bruteForceAttempts{std::min(static_cast<size_t>(5), maxAttempts)};
2012 boost::optional<V3D> maybePoint{RandomPoint::inGenericShape(*this, rng, bruteForceAttempts)};
2013 if (maybePoint) {
2014 point = maybePoint;
2015 } else {
2016 switch (shape()) {
2018 point = RandomPoint::inCuboid(m_handler->shapeInfo(), rng);
2019 break;
2021 point = RandomPoint::inCylinder(m_handler->shapeInfo(), rng);
2022 break;
2024 point = RandomPoint::inHollowCylinder(m_handler->shapeInfo(), rng);
2025 break;
2027 point = RandomPoint::inSphere(m_handler->shapeInfo(), rng);
2028 break;
2029 default:
2030 maybePoint = RandomPoint::inGenericShape(*this, rng, maxAttempts - bruteForceAttempts);
2031 point = maybePoint;
2032 }
2033 }
2034 return point;
2035}
2036
2048 const BoundingBox &activeRegion,
2049 const size_t maxAttempts) const {
2050 boost::optional<V3D> point{boost::none};
2051 // We'll first try brute force. If the shape fills its bounding box
2052 // well enough, this should be the fastest method.
2053 // Increasing the brute force attemps speeds up the shapes that fill
2054 // the bounding box well but slows down shapes that leave lots of void
2055 // within the box. So there is a sweet spot which depends on the actual
2056 // shape, its dimension and orientation.
2057 const size_t bruteForceAttempts{std::min(static_cast<size_t>(5), maxAttempts)};
2058 point = RandomPoint::bounded(*this, rng, activeRegion, bruteForceAttempts);
2059 if (!point) {
2061 std::vector<Kernel::V3D> shapeVectors;
2062 double radius;
2063 double height;
2064 double innerRadius;
2065 GetObjectGeom(shape, shapeVectors, innerRadius, radius, height);
2066 switch (shape) {
2068 point = RandomPoint::bounded<RandomPoint::inCuboid>(m_handler->shapeInfo(), rng, activeRegion,
2069 maxAttempts - bruteForceAttempts);
2070 break;
2072 point = RandomPoint::bounded<RandomPoint::inCylinder>(m_handler->shapeInfo(), rng, activeRegion,
2073 maxAttempts - bruteForceAttempts);
2074 break;
2076 point = RandomPoint::bounded<RandomPoint::inHollowCylinder>(m_handler->shapeInfo(), rng, activeRegion,
2077 maxAttempts - bruteForceAttempts);
2078 break;
2080 point = RandomPoint::bounded<RandomPoint::inSphere>(m_handler->shapeInfo(), rng, activeRegion,
2081 maxAttempts - bruteForceAttempts);
2082 break;
2083 default:
2084 point = RandomPoint::bounded(*this, rng, activeRegion, maxAttempts - bruteForceAttempts);
2085 break;
2086 }
2087 }
2088 return point;
2089}
2090
2097 //
2098 // Method - check if point in object, if not search directions along
2099 // principle axes using interceptSurface
2100 //
2101 if (isValid(point))
2102 return 1;
2103 for (const auto &dir :
2104 {V3D(1., 0., 0.), V3D(-1., 0., 0.), V3D(0., 1., 0.), V3D(0., -1., 0.), V3D(0., 0., 1.), V3D(0., 0., -1.)}) {
2105 Geometry::Track tr(point, dir);
2106 if (this->interceptSurface(tr) > 0) {
2107 point = tr.cbegin()->entryPoint;
2108 return 1;
2109 }
2110 }
2111 return 0;
2112}
2113
2118void CSGObject::setGeometryHandler(const std::shared_ptr<GeometryHandler> &h) {
2119 if (h)
2120 m_handler = h;
2121}
2122
2127void CSGObject::draw() const {
2128 if (m_handler == nullptr)
2129 return;
2130 // Render the Object
2131 m_handler->render();
2132}
2133
2140 if (m_handler == nullptr)
2141 return;
2142 // Render the Object
2143 m_handler->initialize();
2144}
2148void CSGObject::setVtkGeometryCacheWriter(std::shared_ptr<vtkGeometryCacheWriter> writer) {
2149 vtkCacheWriter = std::move(writer);
2151}
2152
2156void CSGObject::setVtkGeometryCacheReader(std::shared_ptr<vtkGeometryCacheReader> reader) {
2157 vtkCacheReader = std::move(reader);
2159}
2160
2164std::shared_ptr<GeometryHandler> CSGObject::getGeometryHandler() const {
2165 // Check if the geometry handler is upto dated with the cache, if not then
2166 // cache it now.
2167 return m_handler;
2168}
2169
2174 if (bGeometryCaching)
2175 return;
2176 bGeometryCaching = true;
2177 // Check if the Geometry handler can be handled for cache
2178 if (m_handler == nullptr)
2179 return;
2180 if (!m_handler->canTriangulate())
2181 return;
2182 // Check if the reader exist then read the cache
2183 if (vtkCacheReader.get() != nullptr) {
2184 vtkCacheReader->readCacheForObject(this);
2185 }
2186 // Check if the writer exist then write the cache
2187 if (vtkCacheWriter.get() != nullptr) {
2188 vtkCacheWriter->addObject(this);
2189 }
2190}
2191
2192// Initialize Draw Object
2193
2195 if (m_handler == nullptr)
2196 return 0;
2197 return m_handler->numberOfTriangles();
2198}
2200 if (m_handler == nullptr)
2201 return 0;
2202 return m_handler->numberOfPoints();
2203}
2207const std::vector<double> &CSGObject::getTriangleVertices() const {
2208 static const std::vector<double> empty;
2209 if (m_handler == nullptr)
2210 return empty;
2211 return m_handler->getTriangleVertices();
2212}
2213
2217const std::vector<uint32_t> &CSGObject::getTriangleFaces() const {
2218 static const std::vector<uint32_t> empty;
2219 if (m_handler == nullptr)
2220 return empty;
2221 return m_handler->getTriangleFaces();
2222}
2223
2225 if (m_handler && m_handler->hasShapeInfo()) {
2226 return m_handler->shapeInfo().shape();
2227 } else {
2229 }
2230}
2231
2233 if (m_handler && m_handler->hasShapeInfo()) {
2234 return m_handler->shapeInfo();
2235 } else {
2236 throw std::logic_error("CSGObject has no ShapeInfo to return");
2237 }
2238}
2239
2243void CSGObject::GetObjectGeom(detail::ShapeInfo::GeometryShape &type, std::vector<Kernel::V3D> &vectors,
2244 double &innerRadius, double &radius, double &height) const {
2246 if (m_handler == nullptr)
2247 return;
2248 m_handler->GetObjectGeom(type, vectors, innerRadius, radius, height);
2249}
2250
2254std::string CSGObject::getShapeXML() const { return this->m_shapeXML; }
2255
2256} // namespace Mantid::Geometry
gsl_vector * tmp
double height
Definition: GetAllEi.cpp:155
double top
Definition: LineProfile.cpp:78
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
double radius
Definition: Rasterize.cpp:31
double innerRadius
Definition: Rasterize.cpp:39
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
Definition: BoundingBox.h:34
bool isPointInside(const Kernel::V3D &point) const
Is the given point within the bounding box?
Definition: BoundingBox.cpp:28
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()
Definition: BoundingBox.h:106
bool doesLineIntersect(const Track &track) const
Does a specified track intersect the bounding box.
Definition: BoundingBox.cpp:44
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:64
Kernel::V3D centrePoint() const
Returns the centre of the bounding box.
Definition: BoundingBox.h:94
Constructive Solid Geometry object.
Definition: CSGObject.h:51
void setVtkGeometryCacheWriter(std::shared_ptr< vtkGeometryCacheWriter >)
set vtkGeometryCache writer
Definition: CSGObject.cpp:2148
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.
Definition: CSGObject.cpp:955
std::string m_shapeXML
original shape xml used to generate this object.
Definition: CSGObject.h:235
bool isValid(const Kernel::V3D &) const override
Check if a point is valid.
Definition: CSGObject.cpp:784
void makeComplement()
Takes the complement of a group.
Definition: CSGObject.cpp:937
void updateGeometryHandler()
Updates the geometry handler if needed.
Definition: CSGObject.cpp:2173
void GetObjectGeom(detail::ShapeInfo::GeometryShape &type, std::vector< Kernel::V3D > &vectors, double &innerRadius, double &radius, double &height) const override
get info on standard shapes
Definition: CSGObject.cpp:2243
int createSurfaceList(const int outFlag=0)
create Surface list
Definition: CSGObject.cpp:807
int setObject(const int objName, const std::string &lineStr)
Object line == cell.
Definition: CSGObject.cpp:466
void setGeometryHandler(const std::shared_ptr< GeometryHandler > &h)
Set Geometry Handler.
Definition: CSGObject.cpp:2118
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...
Definition: CSGObject.cpp:770
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.
Definition: CSGObject.cpp:1654
const Kernel::Material & material() const override
Definition: CSGObject.cpp:448
std::string cellStr(const std::map< int, CSGObject > &) const
Returns just the cell string object.
Definition: CSGObject.cpp:502
std::vector< const Surface * > m_surList
Full surfaces (make a map.
Definition: CSGObject.h:244
int substituteSurf(const int surfNum, const int newSurfNum, const std::shared_ptr< Surface > &surfPtr)
Removes a surface and then re-builds the cell.
Definition: CSGObject.cpp:886
double AABBzMin
zmin of Axis Aligned Bounding Box Cache
Definition: CSGObject.h:213
double volume() const override
Calculates the volume of this object.
Definition: CSGObject.cpp:1512
int getPointInObject(Kernel::V3D &point) const override
Try to find a point that lies within (or on) the object.
Definition: CSGObject.cpp:1967
const std::vector< uint32_t > & getTriangleFaces() const
for solid angle from triangulation
Definition: CSGObject.cpp:2217
int removeSurface(const int surfNum)
Removes a surface and then re-builds the cell.
Definition: CSGObject.cpp:870
~CSGObject() override
Destructor.
std::unique_ptr< CompGrp > procComp(std::unique_ptr< Rule >) const
Takes a Rule item and makes it a complementary group.
Definition: CSGObject.cpp:698
void write(std::ostream &) const
MCNPX output.
Definition: CSGObject.cpp:981
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...
Definition: CSGObject.cpp:639
double singleShotMonteCarloVolume(const int shotSize, const size_t seed) const
Returns the volume.
Definition: CSGObject.cpp:1602
void convertComplement(const std::map< int, CSGObject > &)
Returns just the cell string object.
Definition: CSGObject.cpp:490
const std::vector< double > & getTriangleVertices() const
get vertices
Definition: CSGObject.cpp:2207
std::string m_id
Optional string identifier.
Definition: CSGObject.h:237
std::unique_ptr< Kernel::Material > m_material
material composition
Definition: CSGObject.h:239
size_t numberOfTriangles() const
Definition: CSGObject.cpp:2194
void setNullBoundingBox()
Set a null bounding box for this object.
Definition: CSGObject.cpp:1960
std::string getShapeXML() const
Getter for the shape xml.
Definition: CSGObject.cpp:2254
void setMaterial(const Kernel::Material &material) override
Definition: CSGObject.cpp:443
int searchForObject(Kernel::V3D &) const
Try to find a point that lies within (or on) the object, given a seed point.
Definition: CSGObject.cpp:2096
double solidAngle(const Kernel::V3D &observer) const override
Find solid angle of object wrt the observer.
Definition: CSGObject.cpp:1216
double rayTraceSolidAngle(const Kernel::V3D &observer) const
Given an observer position find the approximate solid angle of the object.
Definition: CSGObject.cpp:1239
std::shared_ptr< GeometryHandler > getGeometryHandler() const override
Returns the geometry handler.
Definition: CSGObject.cpp:2164
void draw() const override
Draws the Object using geometry handler, If the handler is not set then this function does nothing.
Definition: CSGObject.cpp:2127
double AABBzMax
zmax of Axis aligned bounding box cache
Definition: CSGObject.h:210
int complementaryObject(const int cellNum, std::string &lineStr)
Process a complementary object.
Definition: CSGObject.cpp:538
CSGObject & operator=(const CSGObject &)
Assignment operator.
Definition: CSGObject.cpp:412
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.
Definition: CSGObject.cpp:898
double distance(const Track &track) const override
Compute the distance to the first point of intersection with the surface.
Definition: CSGObject.cpp:1130
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.
Definition: CSGObject.cpp:1701
bool bGeometryCaching
Is geometry caching enabled?
Definition: CSGObject.h:223
CSGObject()
Default constructor.
Definition: CSGObject.cpp:388
std::string str() const
Write the object to a string.
Definition: CSGObject.cpp:967
double monteCarloVolume() const
Returns the volume.
Definition: CSGObject.cpp:1568
std::vector< int > getSurfaceIndex() const
Returns all of the numbers of surfaces.
Definition: CSGObject.cpp:856
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.
Definition: CSGObject.cpp:1176
void initDraw() const override
Initializes/prepares the object to be rendered, this will generate geometry for object,...
Definition: CSGObject.cpp:2139
bool hasValidShape() const override
Return whether this object has a valid shape.
Definition: CSGObject.cpp:455
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.
Definition: CSGObject.cpp:581
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
Definition: CSGObject.cpp:2232
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.
Definition: CSGObject.cpp:1155
bool boolBounded
flag true if a bounding box exists, either by
Definition: CSGObject.h:214
bool isOnSide(const Kernel::V3D &) const override
Definition: CSGObject.cpp:732
void calcBoundingBoxByVertices()
Calculate bounding box using object's vertices.
Definition: CSGObject.cpp:1732
int procString(const std::string &lineStr)
Processes the cell string.
Definition: CSGObject.cpp:994
detail::ShapeInfo::GeometryShape shape() const override
Definition: CSGObject.cpp:2224
size_t numberOfVertices() const
Definition: CSGObject.cpp:2199
void printTree() const
Displays the rule tree.
Definition: CSGObject.cpp:945
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)
Definition: CSGObject.cpp:595
int interceptSurface(Geometry::Track &track) const override
Given a track, fill the track with valid section.
Definition: CSGObject.cpp:1076
void setVtkGeometryCacheReader(std::shared_ptr< vtkGeometryCacheReader >)
set vtkGeometryCache reader
Definition: CSGObject.cpp:2156
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.
Definition: CSGObject.cpp:1942
boost::optional< Kernel::V3D > generatePointInObject(Kernel::PseudoRandomNumberGenerator &rng, const size_t) const override
Select a random point within the object.
Definition: CSGObject.cpp:2001
double triangulatedSolidAngle(const Kernel::V3D &observer) const
Find solid angle of object from point "observer" using the OC triangulation of the object,...
Definition: CSGObject.cpp:1357
void calcBoundingBoxByGeometry()
Calculate bounding box using object's geometric data.
Definition: CSGObject.cpp:1769
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
static constexpr int g_NSLICES
The number of slices to approximate a cylinder.
Definition: Cylinder.h:89
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:466
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.
Surface leaf node.
Definition: Rules.h:213
void setKey(const std::shared_ptr< Surface > &Spoint)
Sets the key pointer.
Definition: RuleItems.cpp:752
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:52
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:274
constexpr double X() const noexcept
Get x.
Definition: V3D.h:232
double normalize()
Make a normalized vector (return norm value)
Definition: V3D.cpp:130
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition: V3D.h:278
constexpr double Y() const noexcept
Get y.
Definition: V3D.h:233
void rotate(const Matrix< double > &) noexcept
Rotate a point by a matrix.
Definition: V3D.cpp:217
double norm() const noexcept
Definition: V3D.h:263
constexpr double Z() const noexcept
Get z.
Definition: V3D.h:234
bool nullVector(const double tolerance=1e-3) const noexcept
Determine if the point is null.
Definition: V3D.cpp:241
MANTID_GEOMETRY_DLL Kernel::V3D inCylinder(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in cylinder.
Definition: RandomPoint.cpp:67
MANTID_GEOMETRY_DLL Kernel::V3D inCuboid(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in a cuboid shape.
Definition: RandomPoint.cpp:50
MANTID_GEOMETRY_DLL Kernel::V3D inSphere(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in sphere.
MANTID_GEOMETRY_DLL Kernel::V3D inHollowCylinder(const detail::ShapeInfo &shapeInfo, Kernel::PseudoRandomNumberGenerator &rng)
Return a random point in a hollow cylinder.
Definition: RandomPoint.cpp:87
MANTID_GEOMETRY_DLL boost::optional< Kernel::V3D > inGenericShape(const IObject &object, Kernel::PseudoRandomNumberGenerator &rng, size_t maxAttempts)
Return a random point in a generic shape.
boost::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
int convPartNum(const std::string &A, T &out)
Takes a character string and evaluates the first [typename T] object.
Definition: Strings.cpp:639
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:421
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition: Strings.cpp:665
constexpr double Tolerance
Standard tolerance value.
Definition: Tolerance.h:12
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition: V3D.h:341
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)