CSGObjectTest.h 64.3 KB
Newer Older
1
2
3
4
5
6
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
//     NScD Oak Ridge National Laboratory, European Spallation Source
//     & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
Karl Palmen's avatar
Karl Palmen committed
7
8
#ifndef MANTID_TESTCSGOBJECT__
#define MANTID_TESTCSGOBJECT__
Russell Taylor's avatar
Russell Taylor committed
9

10
#include "MantidGeometry/Objects/CSGObject.h"
11

12
#include "MantidGeometry/Math/Algebra.h"
13
#include "MantidGeometry/Objects/Rules.h"
LamarMoore's avatar
LamarMoore committed
14
#include "MantidGeometry/Objects/ShapeFactory.h"
15
#include "MantidGeometry/Objects/Track.h"
16
#include "MantidGeometry/Rendering/GeometryHandler.h"
LamarMoore's avatar
LamarMoore committed
17
18
19
20
#include "MantidGeometry/Surfaces/Cylinder.h"
#include "MantidGeometry/Surfaces/Plane.h"
#include "MantidGeometry/Surfaces/Sphere.h"
#include "MantidGeometry/Surfaces/SurfaceFactory.h"
21
#include "MantidKernel/Material.h"
22
#include "MantidKernel/MersenneTwister.h"
23
#include "MantidTestHelpers/ComponentCreationHelper.h"
24
#include "MockRNG.h"
25

LamarMoore's avatar
LamarMoore committed
26
#include <cxxtest/TestSuite.h>
27
28

#include "boost/make_shared.hpp"
LamarMoore's avatar
LamarMoore committed
29
#include "boost/shared_ptr.hpp"
30
31
32

#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/Document.h>
33

Russell Taylor's avatar
Russell Taylor committed
34
35
using namespace Mantid;
using namespace Geometry;
36
using Mantid::Kernel::V3D;
37
using detail::ShapeInfo;
Russell Taylor's avatar
Russell Taylor committed
38

39
class CSGObjectTest : public CxxTest::TestSuite {
Russell Taylor's avatar
Russell Taylor committed
40
41

public:
42
  void testDefaultObjectHasEmptyMaterial() {
43
    CSGObject obj;
44

45
46
    TSM_ASSERT_DELTA("Expected a zero number density", 0.0,
                     obj.material().numberDensity(), 1e-12);
47
48
  }

49
  void testObjectSetMaterialReplacesExisting() {
50
    using Mantid::Kernel::Material;
51
    CSGObject obj;
52
53
54

    TSM_ASSERT_DELTA("Expected a zero number density", 0.0,
                     obj.material().numberDensity(), 1e-12);
55
56
    obj.setMaterial(
        Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0));
57
58
59
60
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0,
                     obj.material().numberDensity(), 1e-12);
  }

61
  void testCopyConstructorGivesObjectWithSameAttributes() {
62
    auto original_ptr = ComponentCreationHelper::createSphere(1.0);
63
    auto &original = dynamic_cast<CSGObject &>(*original_ptr);
64
    original.setID("sp-1");
65
    ShapeInfo::GeometryShape objType;
66
67
    double radius(-1.0), height(-1.0);
    std::vector<V3D> pts;
Lamar Moore's avatar
Lamar Moore committed
68
    auto handler = original.getGeometryHandler();
69
    TS_ASSERT(handler->hasShapeInfo());
70
    original.GetObjectGeom(objType, pts, radius, height);
71
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
72

73
    CSGObject copy(original);
74
    // The copy should be a primitive object with a GeometryHandler
75
76
    copy.GetObjectGeom(objType, pts, radius, height);

Martyn Gigg's avatar
Martyn Gigg committed
77
    TS_ASSERT_EQUALS("sp-1", copy.id());
78
79
80
    auto handlerCopy = copy.getGeometryHandler();
    TS_ASSERT(handlerCopy->hasShapeInfo());
    TS_ASSERT_EQUALS(handler->shapeInfo(), handlerCopy->shapeInfo());
81
    TS_ASSERT_EQUALS(copy.getName(), original.getName());
82
    // Check the string representation is the same
83
84
    TS_ASSERT_EQUALS(copy.str(), original.str());
    TS_ASSERT_EQUALS(copy.getSurfaceIndex(), original.getSurfaceIndex());
85
86
  }

87
  void testAssignmentOperatorGivesObjectWithSameAttributes() {
88
    auto original_ptr = ComponentCreationHelper::createSphere(1.0);
89
    auto &original = dynamic_cast<CSGObject &>(*original_ptr);
90
    original.setID("sp-1");
91
    ShapeInfo::GeometryShape objType;
92
93
    double radius(-1.0), height(-1.0);
    std::vector<V3D> pts;
Lamar Moore's avatar
Lamar Moore committed
94
    auto handler = original.getGeometryHandler();
95
    TS_ASSERT(handler->hasShapeInfo());
96
    original.GetObjectGeom(objType, pts, radius, height);
97
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
98

99
    CSGObject lhs;  // initialize
100
    lhs = original; // assign
101
102
103
    // The copy should be a primitive object with a GluGeometryHandler
    lhs.GetObjectGeom(objType, pts, radius, height);

Martyn Gigg's avatar
Martyn Gigg committed
104
    TS_ASSERT_EQUALS("sp-1", lhs.id());
105
106
107
108
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
    auto handlerCopy = lhs.getGeometryHandler();
    TS_ASSERT(handlerCopy->hasShapeInfo());
    TS_ASSERT_EQUALS(handlerCopy->shapeInfo(), handler->shapeInfo());
109
  }
Russell Taylor's avatar
Russell Taylor committed
110

111
  void testCreateUnitCube() {
112
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
113

114
    TS_ASSERT_EQUALS(geom_obj->str(), "68 1 -2 3 -4 5 -6");
115
116

    double xmin(0.0), xmax(0.0), ymin(0.0), ymax(0.0), zmin(0.0), zmax(0.0);
117
    geom_obj->getBoundingBox(xmax, ymax, zmax, xmin, ymin, zmin);
Russell Taylor's avatar
Russell Taylor committed
118
119
  }

120
121
  void testCloneWithMaterial() {
    using Mantid::Kernel::Material;
mantid-builder's avatar
mantid-builder committed
122
123
    auto testMaterial =
        Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0);
124
    auto geom_obj = createUnitCube();
125
126
127
    std::unique_ptr<IObject> cloned_obj;
    TS_ASSERT_THROWS_NOTHING(
        cloned_obj.reset(geom_obj->cloneWithMaterial(testMaterial)));
128
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0,
mantid-builder's avatar
mantid-builder committed
129
                     cloned_obj->material().numberDensity(), 1e-12);
130
131
  }

132
  void testIsOnSideCappedCylinder() {
133
    auto geom_obj = createCappedCylinder();
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
    // inside
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 0)), false); // origin
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 2.9, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, -2.9, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, -2.9)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 2.9)), false);
    // on the side
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, -3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, -3)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 3)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(1.2, 0, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(-3.2, 0, 0)), true);

    // on the edges
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(1.2, 3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(1.2, -3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(1.2, 0, -3)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(1.2, 0, 3)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(-3.2, 3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(-3.2, -3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(-3.2, 0, -3)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(-3.2, 0, 3)), true);
    // out side
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 3.1, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, -3.1, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, -3.1)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 3.1)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(1.3, 0, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(-3.3, 0, 0)), false);
Russell Taylor's avatar
Russell Taylor committed
164
165
  }

166
  void testIsValidCappedCylinder() {
167
    auto geom_obj = createCappedCylinder();
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
    // inside
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 0)), true); // origin
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 2.9, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, -2.9, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, -2.9)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 2.9)), true);
    // on the side
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, -3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, -3)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 3)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(1.2, 0, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(-3.2, 0, 0)), true);

    // on the edges
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(1.2, 3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(1.2, -3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(1.2, 0, -3)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(1.2, 0, 3)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(-3.2, 3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(-3.2, -3, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(-3.2, 0, -3)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(-3.2, 0, 3)), true);
    // out side
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 3.1, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, -3.1, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, -3.1)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 3.1)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(1.3, 0, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(-3.3, 0, 0)), false);
Russell Taylor's avatar
Russell Taylor committed
198
199
  }

200
  void testIsOnSideSphere() {
201
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
    // inside
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 0)), false); // origin
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 4.0, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, -4.0, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, -4.0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 4.0)), false);
    // on the side
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 4.1, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, -4.1, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, -4.1)), true);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 4.1)), true);

    // out side
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 4.2, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, -4.2, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, -4.2)), false);
    TS_ASSERT_EQUALS(geom_obj->isOnSide(V3D(0, 0, 4.2)), false);
Russell Taylor's avatar
Russell Taylor committed
219
220
  }

221
  void testIsValidSphere() {
222
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
    // inside
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 0)), true); // origin
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 4.0, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, -4.0, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, -4.0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 4.0)), true);
    // on the side
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 4.1, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, -4.1, 0)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, -4.1)), true);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 4.1)), true);

    // out side
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 4.2, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, -4.2, 0)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, -4.2)), false);
    TS_ASSERT_EQUALS(geom_obj->isValid(V3D(0, 0, 4.2)), false);
Russell Taylor's avatar
Russell Taylor committed
240
241
  }

242
  void testCalcValidTypeSphere() {
243
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
244
    // entry on the normal
Peterson, Peter's avatar
Peterson, Peter committed
245
246
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(1, 0, 0)),
                     TrackDirection::ENTERING);
247
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(-1, 0, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
248
249
250
251
252
253
254
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(1, 0, 0)),
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(-1, 0, 0)),
                     TrackDirection::ENTERING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -4.1, 0), V3D(0, 1, 0)),
                     TrackDirection::ENTERING);
255
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -4.1, 0), V3D(0, -1, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
256
257
258
259
260
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 4.1, 0), V3D(0, 1, 0)),
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 4.1, 0), V3D(0, -1, 0)),
                     TrackDirection::ENTERING);
261
262

    // a glancing blow
Peterson, Peter's avatar
Peterson, Peter committed
263
264
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0, 1, 0)),
                     TrackDirection::INVALID);
265
266
    // not quite on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0.5, 0.5, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
267
                     TrackDirection::ENTERING);
268
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(0.5, 0.5, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
269
                     TrackDirection::LEAVING);
Russell Taylor's avatar
Russell Taylor committed
270
271
  }

272
  void testGetBoundingBoxForSphere() {
273
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
274
275
    const double tolerance(1e-10);

276
277
278
279
280
281
282
283
    const BoundingBox &bbox = geom_obj->getBoundingBox();

    TS_ASSERT_DELTA(bbox.xMax(), 4.1, tolerance);
    TS_ASSERT_DELTA(bbox.yMax(), 4.1, tolerance);
    TS_ASSERT_DELTA(bbox.zMax(), 4.1, tolerance);
    TS_ASSERT_DELTA(bbox.xMin(), -4.1, tolerance);
    TS_ASSERT_DELTA(bbox.yMin(), -4.1, tolerance);
    TS_ASSERT_DELTA(bbox.zMin(), -4.1, tolerance);
284
285
  }

286
  void testCalcValidTypeCappedCylinder() {
287
    auto geom_obj = createCappedCylinder();
288
    // entry on the normal
Peterson, Peter's avatar
Peterson, Peter committed
289
290
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(1, 0, 0)),
                     TrackDirection::ENTERING);
291
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(-1, 0, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
292
293
294
295
296
297
298
299
300
301
302
303
304
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(1, 0, 0)),
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(-1, 0, 0)),
                     TrackDirection::ENTERING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -3, 0), V3D(0, 1, 0)),
                     TrackDirection::ENTERING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -3, 0), V3D(0, -1, 0)),
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 3, 0), V3D(0, 1, 0)),
                     TrackDirection::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 3, 0), V3D(0, -1, 0)),
                     TrackDirection::ENTERING);
305
306

    // a glancing blow
Peterson, Peter's avatar
Peterson, Peter committed
307
308
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0, 1, 0)),
                     TrackDirection::INVALID);
309
310
    // not quite on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0.5, 0.5, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
311
                     TrackDirection::ENTERING);
312
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(0.5, 0.5, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
313
                     TrackDirection::LEAVING);
Russell Taylor's avatar
Russell Taylor committed
314
315
  }

316
  void testInterceptSurfaceSphereZ() {
317
    std::vector<Link> expectedResults;
318
    std::string S41 = "s 1 1 1 4"; // Sphere at (1,1,1) radius 4
Russell Taylor's avatar
Russell Taylor committed
319
320

    // First create some surfaces
321
322
    std::map<int, boost::shared_ptr<Surface>> SphSurMap;
    SphSurMap[41] = boost::make_shared<Sphere>();
Russell Taylor's avatar
Russell Taylor committed
323
324
325
    SphSurMap[41]->setSurface(S41);
    SphSurMap[41]->setName(41);

326
    // A sphere
327
    std::string ObjSphere = "-41";
Russell Taylor's avatar
Russell Taylor committed
328

mantid-builder's avatar
mantid-builder committed
329
330
    boost::shared_ptr<CSGObject> geom_obj =
        boost::shared_ptr<CSGObject>(new CSGObject);
331
    geom_obj->setObject(41, ObjSphere);
332
    geom_obj->populate(SphSurMap);
Russell Taylor's avatar
Russell Taylor committed
333

334
    Track track(V3D(-1, 1.5, 1), V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
335

336
    // format = startPoint, endPoint, total distance so far
Russell Taylor's avatar
Russell Taylor committed
337
    // forward only intercepts means that start point should be track origin
338
339
340
    expectedResults.push_back(Link(V3D(-1, 1.5, 1),
                                   V3D(sqrt(16 - 0.25) + 1, 1.5, 1.0),
                                   sqrt(15.75) + 2, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
341

342
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
343
344
  }

345
  void testInterceptSurfaceSphereY() {
346
    std::vector<Link> expectedResults;
347
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
348
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
349

350
351
352
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(
        Link(V3D(0, -4.1, 0), V3D(0, 4.1, 0), 14.1, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
353

354
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
355
356
  }

357
  void testInterceptSurfaceSphereX() {
358
    std::vector<Link> expectedResults;
359
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
360
    Track track(V3D(-10, 0, 0), V3D(1, 0, 0));
361

362
363
364
365
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(
        Link(V3D(-4.1, 0, 0), V3D(4.1, 0, 0), 14.1, *geom_obj));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
366
367
  }

368
  void testInterceptSurfaceCappedCylinderY() {
369
    std::vector<Link> expectedResults;
370
    auto geom_obj = createCappedCylinder();
371
372
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(Link(V3D(0, -3, 0), V3D(0, 3, 0), 13, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
373

374
375
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
376
377
  }

378
  void testInterceptSurfaceCappedCylinderX() {
379
    std::vector<Link> expectedResults;
380
    auto geom_obj = createCappedCylinder();
381
    Track track(V3D(-10, 0, 0), V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
382

383
384
385
386
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(
        Link(V3D(-3.2, 0, 0), V3D(1.2, 0, 0), 11.2, *geom_obj));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
387
388
  }

389
390
391
  void testInterceptSurfaceCappedCylinderMiss() {
    std::vector<Link>
        expectedResults; // left empty as there are no expected results
392
    auto geom_obj = createCappedCylinder();
393
394
395
    V3D dir(1., 1., 0.);
    dir.normalize();
    Track track(V3D(-10, 0, 0), dir);
Russell Taylor's avatar
Russell Taylor committed
396

397
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
398
399
  }

400
401
  void checkTrackIntercept(Track &track,
                           const std::vector<Link> &expectedResults) {
402
    size_t index = 0;
mantid-builder's avatar
mantid-builder committed
403
404
    for (Track::LType::const_iterator it = track.cbegin(); it != track.cend();
         ++it) {
405
406
      if (index < expectedResults.size()) {
        TS_ASSERT_DELTA(it->distFromStart, expectedResults[index].distFromStart,
mantid-builder's avatar
mantid-builder committed
407
                        1e-6);
408
        TS_ASSERT_DELTA(it->distInsideObject,
mantid-builder's avatar
mantid-builder committed
409
                        expectedResults[index].distInsideObject, 1e-6);
410
411
412
413
        TS_ASSERT_EQUALS(it->componentID, expectedResults[index].componentID);
        TS_ASSERT_EQUALS(it->entryPoint, expectedResults[index].entryPoint);
        TS_ASSERT_EQUALS(it->exitPoint, expectedResults[index].exitPoint);
      }
Russell Taylor's avatar
Russell Taylor committed
414
415
      ++index;
    }
416
    TS_ASSERT_EQUALS(index, expectedResults.size());
Russell Taylor's avatar
Russell Taylor committed
417
418
  }

419
  void checkTrackIntercept(IObject_sptr obj, Track &track,
420
                           const std::vector<Link> &expectedResults) {
421
    int unitCount = obj->interceptSurface(track);
422
423
    TS_ASSERT_EQUALS(unitCount, expectedResults.size());
    checkTrackIntercept(track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
424
425
  }

426
  void testTrackTwoIsolatedCubes()
427
428
429
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
430
  {
431
432
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006";
    std::string ObjB = "80001 -80002 60003 -60004 60005 -60006";
433

434
    createSurfaces(ObjA);
435
    CSGObject object1 = CSGObject();
436
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
437
438
    object1.populate(SMap);

439
    createSurfaces(ObjB);
440
    CSGObject object2 = CSGObject();
441
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
442
443
    object2.populate(SMap);

444
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
445
446

    // CARE: This CANNOT be called twice
447
448
    TS_ASSERT(object1.interceptSurface(TL) != 0);
    TS_ASSERT(object2.interceptSurface(TL) != 0);
Russell Taylor's avatar
Russell Taylor committed
449

450
    std::vector<Link> expectedResults;
451
452
453
454
    expectedResults.push_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.push_back(
        Link(V3D(4.5, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
455
456
457
  }

  void testTrackTwoTouchingCubes()
458
459
460
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
461
  {
462
463
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006";
    std::string ObjB = "60002 -80002 60003 -60004 60005 -60006";
Russell Taylor's avatar
Russell Taylor committed
464

465
    createSurfaces(ObjA);
466
    CSGObject object1 = CSGObject();
467
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
468
469
    object1.populate(SMap);

470
    createSurfaces(ObjB);
471
    CSGObject object2 = CSGObject();
472
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
473
474
    object2.populate(SMap);

475
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
476
477

    // CARE: This CANNOT be called twice
478
479
    TS_ASSERT(object1.interceptSurface(TL) != 0);
    TS_ASSERT(object2.interceptSurface(TL) != 0);
Russell Taylor's avatar
Russell Taylor committed
480

481
    std::vector<Link> expectedResults;
482
483
484
    expectedResults.push_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.push_back(
        Link(V3D(1, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
Russell Taylor's avatar
Russell Taylor committed
485

486
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
487
488
489
  }

  void testTrackCubeWithInternalSphere()
490
491
492
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
493
  {
494
495
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 71";
    std::string ObjB = "-71";
Russell Taylor's avatar
Russell Taylor committed
496

497
    createSurfaces(ObjA);
498
    CSGObject object1 = CSGObject();
499
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
500
501
    object1.populate(SMap);

502
    createSurfaces(ObjB);
503
    CSGObject object2 = CSGObject();
504
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
505
506
    object2.populate(SMap);

507
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
508
509

    // CARE: This CANNOT be called twice
510
511
    TS_ASSERT(object1.interceptSurface(TL) != 0);
    TS_ASSERT(object2.interceptSurface(TL) != 0);
Russell Taylor's avatar
Russell Taylor committed
512

513
    std::vector<Link> expectedResults;
514
515
516
517
518
519
    expectedResults.push_back(
        Link(V3D(-1, 0, 0), V3D(-0.8, 0, 0), 4.2, object1));
    expectedResults.push_back(
        Link(V3D(-0.8, 0, 0), V3D(0.8, 0, 0), 5.8, object1));
    expectedResults.push_back(Link(V3D(0.8, 0, 0), V3D(1, 0, 0), 6, object2));
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
520
521
522
  }

  void testTrack_CubePlusInternalEdgeTouchSpheres()
523
524
525
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
526
  {
527
528
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
529

530
    createSurfaces(ObjA);
531
    CSGObject object1 = CSGObject();
532
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
533
534
    object1.populate(SMap);

535
    createSurfaces(ObjB);
536
    CSGObject object2 = CSGObject();
537
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
538
539
    object2.populate(SMap);

540
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
541
542

    // CARE: This CANNOT be called twice
543
544
    TS_ASSERT(object1.interceptSurface(TL) != 0);
    TS_ASSERT(object2.interceptSurface(TL) != 0);
Russell Taylor's avatar
Russell Taylor committed
545

546
    std::vector<Link> expectedResults;
547
548
549
550
551
552
    expectedResults.push_back(
        Link(V3D(-1, 0, 0), V3D(-0.4, 0, 0), 4.6, object1));
    expectedResults.push_back(
        Link(V3D(-0.4, 0, 0), V3D(0.2, 0, 0), 5.2, object1));
    expectedResults.push_back(Link(V3D(0.2, 0, 0), V3D(1, 0, 0), 6, object2));
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
553
554
555
  }

  void testTrack_CubePlusInternalEdgeTouchSpheresMiss()
556
557
558
  /**
  Test a track missing an object
  */
Russell Taylor's avatar
Russell Taylor committed
559
  {
560
561
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
562

563
    createSurfaces(ObjA);
564
    CSGObject object1 = CSGObject();
565
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
566
567
    object1.populate(SMap);

568
    createSurfaces(ObjB);
569
    CSGObject object2 = CSGObject();
570
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
571
572
    object2.populate(SMap);

573
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
574
575

    // CARE: This CANNOT be called twice
576
577
    TS_ASSERT_EQUALS(object1.interceptSurface(TL), 0);
    TS_ASSERT_EQUALS(object2.interceptSurface(TL), 0);
Russell Taylor's avatar
Russell Taylor committed
578

579
580
    std::vector<Link> expectedResults; // left empty as this should miss
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
581
582
  }

583
  void testComplementWithTwoPrimitives() {
584
    auto shell_ptr = ComponentCreationHelper::createHollowShell(0.5, 1.0);
585
    auto shell = dynamic_cast<CSGObject *>(shell_ptr.get());
586
587
588
589
590
591
592
593
594
595

    TS_ASSERT_EQUALS(2, shell->getSurfaceIndex().size());

    // Are the rules correct?
    const Rule *headRule = shell->topRule();
    TS_ASSERT_EQUALS("Intersection", headRule->className());
    const Rule *leaf1 = headRule->leaf(0);
    TS_ASSERT_EQUALS("SurfPoint", leaf1->className());
    auto surfPt1 = dynamic_cast<const SurfPoint *>(leaf1);
    TS_ASSERT(surfPt1);
596
    TS_ASSERT_EQUALS(2, surfPt1->getKeyN());
597
598
599
600
601
602
603
604
605
606
    auto outer = dynamic_cast<const Sphere *>(surfPt1->getKey());
    TS_ASSERT(outer);
    TS_ASSERT_DELTA(1.0, outer->getRadius(), 1e-10);

    const Rule *leaf2 = headRule->leaf(1);
    TS_ASSERT_EQUALS("CompGrp", leaf2->className());
    auto compRule = dynamic_cast<const CompGrp *>(leaf2);
    TS_ASSERT(compRule);
    TS_ASSERT_EQUALS("SurfPoint", compRule->leaf(0)->className());
    auto surfPt2 = dynamic_cast<const SurfPoint *>(compRule->leaf(0));
607
    TS_ASSERT_EQUALS(1, surfPt2->getKeyN());
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
    auto inner = dynamic_cast<const Sphere *>(surfPt2->getKey());
    TS_ASSERT(inner);
    TS_ASSERT_DELTA(0.5, inner->getRadius(), 1e-10);

    TS_ASSERT_EQUALS(false, shell->isValid(V3D(0, 0, 0)));

    Track p1(V3D(-2, 0, 0), V3D(1, 0, 0));
    int nsegments = shell->interceptSurface(p1);
    TS_ASSERT_EQUALS(2, nsegments);
    // total traversed distance -> 2*(r2-r1)
    double distanceInside(0.0);
    std::for_each(p1.cbegin(), p1.cend(),
                  [&distanceInside](const Link &segment) {
                    distanceInside += segment.distInsideObject;
                  });
    TS_ASSERT_DELTA(1.0, distanceInside, 1e-10);
  }

Russell Taylor's avatar
Russell Taylor committed
626
  void testFindPointInCube()
627
628
629
  /**
  Test find point in cube
  */
Russell Taylor's avatar
Russell Taylor committed
630
  {
631
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
Russell Taylor's avatar
Russell Taylor committed
632
    // initial guess in object
633
    Kernel::V3D pt;
634
635
    TS_ASSERT_EQUALS(geom_obj->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
636
    // initial guess not in object, but on x-axis
637
638
    std::vector<std::string> planes{"px 10",  "px 11",   "py -0.5",
                                    "py 0.5", "pz -0.5", "pz 0.5"};
639
    boost::shared_ptr<CSGObject> B = createCuboid(planes);
640
641
    TS_ASSERT_EQUALS(B->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(10, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
642
    // on y axis
643
    planes = {"px -0.5", "px 0.5", "py -22", "py -21", "pz -0.5", "pz 0.5"};
644
    boost::shared_ptr<CSGObject> C = createCuboid(planes);
645
646
    TS_ASSERT_EQUALS(C->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0, -21, 0));
Russell Taylor's avatar
Russell Taylor committed
647
    // not on principle axis, now works using getBoundingBox
648
    planes = {"px 0.5", "px 1.5", "py -22", "py -21", "pz -0.5", "pz 0.5"};
649
    boost::shared_ptr<CSGObject> D = createCuboid(planes);
650
651
652
653
654
655
656
657
658
659
660
661
    TS_ASSERT_EQUALS(D->getPointInObject(pt), 1);
    TS_ASSERT_DELTA(pt.X(), 1.0, 1e-6);
    TS_ASSERT_DELTA(pt.Y(), -21.5, 1e-6);
    TS_ASSERT_DELTA(pt.Z(), 0.0, 1e-6);
    // Test non axis aligned (AA) case - getPointInObject works because the
    // object is on a principle axis
    // However, if not on a principle axis then the getBoundingBox fails to find
    // correct minima (maxima are OK)
    // This is related to use of the complement for -ve surfaces and might be
    // avoided by only using +ve surfaces
    // for defining non-AA objects. However, BoundingBox is poor for non-AA and
    // needs improvement if these are
Russell Taylor's avatar
Russell Taylor committed
662
    // common
LamarMoore's avatar
LamarMoore committed
663
664
    planes = {"p 1 0 0 -0.5",
              "p 1 0 0 0.5",
665
666
667
668
              "p 0 .70710678118 .70710678118 -1.1",
              "p 0 .70710678118 .70710678118 -0.1",
              "p 0 -.70710678118 .70710678118 -0.5",
              "p 0 -.70710678118 .70710678118 0.5"};
669
    boost::shared_ptr<CSGObject> E = createCuboid(planes);
670
671
672
673
    TS_ASSERT_EQUALS(E->getPointInObject(pt), 1);
    TS_ASSERT_DELTA(pt.X(), 0.0, 1e-6);
    TS_ASSERT_DELTA(pt.Y(), -0.1414213562373, 1e-6);
    TS_ASSERT_DELTA(pt.Z(), 0.0, 1e-6);
674
675
676
    // This test used to fail to find a point in object, as object not on a
    // principle axis and getBoundingBox did not give a useful result in this
    // case. Framework has now been updated to support this automatically.
677
    // Object is unit cube located at +-0.5 in x but centred on z=y=-1.606.. and
678
    // rotated 45deg to these two axes
LamarMoore's avatar
LamarMoore committed
679
680
    planes = {"p 1 0 0 -0.5",
              "p 1 0 0 0.5",
681
682
683
684
              "p 0  .70710678118 .70710678118 -2",
              "p 0  .70710678118 .70710678118 -1",
              "p 0 -.70710678118 .70710678118 -0.5",
              "p 0 -.70710678118 .70710678118 0.5"};
685
    boost::shared_ptr<CSGObject> F = createCuboid(planes);
686
    TS_ASSERT_EQUALS(F->getPointInObject(pt), 1); // This now succeeds
687
688
    // Test use of defineBoundingBox to explictly set the bounding box, when the
    // automatic method fails
689
690
691
    F->defineBoundingBox(0.5, -0.5 * M_SQRT1_2, -0.5 * M_SQRT1_2, -0.5,
                         -M_SQRT2 - 0.5 * M_SQRT1_2,
                         -M_SQRT2 - 0.5 * M_SQRT1_2);
692
    TS_ASSERT_EQUALS(F->getPointInObject(pt), 1);
693
    auto S = ComponentCreationHelper::createSphere(4.1);
694
695
    TS_ASSERT_EQUALS(S->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0.0, 0.0, 0));
Russell Taylor's avatar
Russell Taylor committed
696
697
  }

698
699
700
701
702
703
704
705
706
707
708
709
710
  void testGeneratePointInside() {
    using namespace ::testing;

    // Generate "random" sequence
    MockRNG rng;
    Sequence rand;
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.55));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.65));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.70));

    // inner radius=0.5, outer=1. Random sequence set up so as to give point
    // inside hole
    auto shell = ComponentCreationHelper::createHollowShell(0.5, 1.0);
711
    constexpr size_t maxAttempts{1};
712
713
714
715
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = shell->generatePointInObject(rng, maxAttempts));

716
    constexpr double tolerance{1e-10};
717
718
719
720
721
    TS_ASSERT_DELTA(-1. + 2. * 0.55, point.X(), tolerance);
    TS_ASSERT_DELTA(-1. + 2. * 0.65, point.Y(), tolerance);
    TS_ASSERT_DELTA(-1. + 2. * 0.70, point.Z(), tolerance);
  }

722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
  void testGeneratePointInsideCuboid() {
    using namespace ::testing;

    // Generate "random" sequence
    MockRNG rng;
    Sequence rand;
    constexpr double randX{0.55};
    constexpr double randY{0.65};
    constexpr double randZ{0.70};
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randZ));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randX));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randY));

    constexpr double xLength{0.3};
    constexpr double yLength{0.5};
    constexpr double zLength{0.2};
    auto cuboid =
        ComponentCreationHelper::createCuboid(xLength, yLength, zLength);
    constexpr size_t maxAttempts{0};
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = cuboid->generatePointInObject(rng, maxAttempts));

    constexpr double tolerance{1e-10};
    TS_ASSERT_DELTA(xLength - randX * 2. * xLength, point.X(), tolerance);
    TS_ASSERT_DELTA(-yLength + randY * 2. * yLength, point.Y(), tolerance);
    TS_ASSERT_DELTA(-zLength + randZ * 2. * zLength, point.Z(), tolerance);
  }

  void testGeneratePointInsideCylinder() {
    using namespace ::testing;

    // Generate "random" sequence
    MockRNG rng;
    Sequence rand;
    constexpr double randT{0.65};
    constexpr double randR{0.55};
    constexpr double randZ{0.70};
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randT));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randR));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randZ));

    constexpr double radius{0.3};
    constexpr double height{0.5};
    const V3D axis{0., 0., 1.};
    const V3D bottomCentre{
        -1.,
        2.,
        -3.,
    };
    auto cylinder = ComponentCreationHelper::createCappedCylinder(
        radius, height, bottomCentre, axis, "cyl");
    constexpr size_t maxAttempts{0};
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = cylinder->generatePointInObject(rng, maxAttempts));
    // Global->cylinder local coordinates
    point -= bottomCentre;
    constexpr double tolerance{1e-10};
    const double polarAngle{2. * M_PI * randT};
    const double radialLength{radius * std::sqrt(randR)};
    const double axisLength{height * randZ};
    TS_ASSERT_DELTA(radialLength * std::cos(polarAngle), point.X(), tolerance);
    TS_ASSERT_DELTA(radialLength * std::sin(polarAngle), point.Y(), tolerance);
    TS_ASSERT_DELTA(axisLength, point.Z(), tolerance);
  }

  void testGeneratePointInsideSphere() {
    using namespace ::testing;

    // Generate "random" sequence
    MockRNG rng;
    Sequence rand;
    constexpr double randT{0.65};
    constexpr double randF{0.55};
    constexpr double randR{0.70};
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randT));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randF));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randR));

    constexpr double radius{0.23};
    auto sphere = ComponentCreationHelper::createSphere(radius);
    constexpr size_t maxAttempts{0};
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = sphere->generatePointInObject(rng, maxAttempts));
    // Global->cylinder local coordinates
    constexpr double tolerance{1e-10};
    const double azimuthalAngle{2. * M_PI * randT};
    const double polarAngle{std::acos(2. * randF - 1.)};
    const double r{radius * randR};
    TS_ASSERT_DELTA(r * std::cos(azimuthalAngle) * std::sin(polarAngle),
                    point.X(), tolerance);
    TS_ASSERT_DELTA(r * std::sin(azimuthalAngle) * std::sin(polarAngle),
                    point.Y(), tolerance);
    TS_ASSERT_DELTA(r * std::cos(polarAngle), point.Z(), tolerance);
  }

820
821
822
823
824
825
826
827
828
829
830
831
832
  void testGeneratePointInsideRespectsMaxAttempts() {
    using namespace ::testing;

    // Generate "random" sequence
    MockRNG rng;
    Sequence rand;
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.1));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.2));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.3));

    // inner radius=0.5, outer=1. Random sequence set up so as to give point
    // inside hole
    auto shell = ComponentCreationHelper::createHollowShell(0.5, 1.0);
833
    constexpr size_t maxAttempts{1};
834
835
836
837
838
839
840
841
842
843
    TS_ASSERT_THROWS(shell->generatePointInObject(rng, maxAttempts),
                     std::runtime_error);
  }

  void testGeneratePointInsideRespectsActiveRegion() {
    using namespace ::testing;

    // Generate "random" sequence.
    MockRNG rng;
    Sequence rand;
844
845
846
847
848
849
850
851
852
    constexpr double randX{0.92};
    constexpr double randY{0.14};
    constexpr double randZ{0.83};
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randX));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randY));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(randZ));

    constexpr double halfWidth{0.75};
    auto ball = ComponentCreationHelper::createCuboid(halfWidth);
853
854
    // Create a thin infinite rectangular region to restrict point generation
    BoundingBox activeRegion(0.1, 0.1, 0.1, -0.1, -0.1, -0.1);
855
    constexpr size_t maxAttempts{1};
856
857
858
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = ball->generatePointInObject(rng, activeRegion, maxAttempts));
859
860
861
862
863
    // We should get the point generated from the second 'random' triplet.
    constexpr double tolerance{1e-10};
    TS_ASSERT_DELTA(-0.1 + randX * 0.2, point.X(), tolerance)
    TS_ASSERT_DELTA(-0.1 + randY * 0.2, point.Y(), tolerance)
    TS_ASSERT_DELTA(-0.1 + randZ * 0.2, point.Z(), tolerance)
864
865
  }

Russell Taylor's avatar
Russell Taylor committed
866
  void testSolidAngleSphere()
867
868
869
  /**
  Test solid angle calculation for a sphere
  */
Russell Taylor's avatar
Russell Taylor committed
870
  {
871
    auto geom_obj_ptr = ComponentCreationHelper::createSphere(4.1);
872
    auto geom_obj = dynamic_cast<CSGObject *>(geom_obj_ptr.get());
873
    double satol = 2e-2; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
874
875
876
877
878

    // Solid angle at distance 8.1 from centre of sphere radius 4.1 x/y/z
    // Expected solid angle calculated values from sa=2pi(1-cos(arcsin(R/r))
    // where R is sphere radius and r is distance of observer from sphere centre
    // Intercept for track in reverse direction now worked round
879
880
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(8.1, 0, 0)), 0.864364,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
881
    // internal point (should be 4pi)
882
883
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(0, 0, 0)), 4 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
884
    // surface point
885
886
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(4.1, 0, 0)), 2 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
887
888
889
  }

  void testSolidAngleCappedCylinder()
890
891
892
  /**
  Test solid angle calculation for a capped cylinder
  */
Russell Taylor's avatar
Russell Taylor committed
893
  {
894
    boost::shared_ptr<CSGObject> geom_obj = createSmallCappedCylinder();
895
    // Want to test triangulation so setup a geometry handler
896
    auto h = boost::make_shared<GeometryHandler>(geom_obj);
897
898
899
900
    detail::ShapeInfo shapeInfo;
    shapeInfo.setCylinder(V3D(-0.0015, 0.0, 0.0), V3D(1., 0.0, 0.0), 0.005,
                          0.003);
    h->setShapeInfo(std::move(shapeInfo));
901
    geom_obj->setGeometryHandler(h);
902

903
    double satol(1e-8); // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
904

905
906
907
908
909
910
    // solid angle at point -0.5 from capped cyl -1.0 -0.997 in x, rad 0.005 -
    // approx WISH cylinder
    // We intentionally exclude the cylinder end caps so they this should
    // produce 0
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-0.5, 0.0, 0.0)), 0.0,
                    satol);
911
    // Other end
912
913
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.497, 0.0, 0.0)), 0.0,
                    satol);
914

915
    // Side values
916
917
918
919
920
921
922
923
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, 0.1)), 0.00301186,
                    satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, -0.1)), 0.00301186,
                    satol);
    // Sweep in the axis of the cylinder angle to see if the solid angle
    // decreases (as we are excluding the end caps)
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0.1, 0.0, 0.1)),
                    0.00100267, satol);
924

Russell Taylor's avatar
Russell Taylor committed
925
    // internal point (should be 4pi)
926
927
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-0.999, 0.0, 0.0)),
                    4 * M_PI, satol);
928
929

    // surface points
930
931
932
933
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.0, 0.0, 0.0)), 2 * M_PI,
                    satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-0.997, 0.0, 0.0)),
                    2 * M_PI, satol);
Russell Taylor's avatar
Russell Taylor committed
934
935
936
  }

  void testSolidAngleCubeTriangles()
937
938
939
940
  /**
  Test solid angle calculation for a cube using triangles
  - test for using Open Cascade surface triangulation for all solid angles.
  */
Russell Taylor's avatar
Russell Taylor committed
941
  {
942
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
943
    double satol = 1e-3; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
944
945
946
947
948

    // solid angle at distance 0.5 should be 4pi/6 by symmetry
    //
    // tests for Triangulated cube
    //
</