CSGObjectTest.h 64.4 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"
17
#include "MantidGeometry/Rendering/ShapeInfo.h"
LamarMoore's avatar
LamarMoore committed
18
19
20
21
#include "MantidGeometry/Surfaces/Cylinder.h"
#include "MantidGeometry/Surfaces/Plane.h"
#include "MantidGeometry/Surfaces/Sphere.h"
#include "MantidGeometry/Surfaces/SurfaceFactory.h"
22
#include "MantidKernel/Material.h"
23
24
#include "MantidKernel/MersenneTwister.h"
#include "MantidKernel/WarningSuppressions.h"
LamarMoore's avatar
LamarMoore committed
25
#include "MantidKernel/make_unique.h"
26
#include "MantidTestHelpers/ComponentCreationHelper.h"
27

LamarMoore's avatar
LamarMoore committed
28
#include <algorithm>
29
#include <cmath>
LamarMoore's avatar
LamarMoore committed
30
31
#include <ctime>
#include <cxxtest/TestSuite.h>
32
33
34
35
#include <ostream>
#include <vector>

#include "boost/make_shared.hpp"
LamarMoore's avatar
LamarMoore committed
36
#include "boost/shared_ptr.hpp"
37
38
39

#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/Document.h>
LamarMoore's avatar
LamarMoore committed
40
#include <gmock/gmock.h>
41

Russell Taylor's avatar
Russell Taylor committed
42
43
using namespace Mantid;
using namespace Geometry;
44
using Mantid::Kernel::V3D;
45
using detail::ShapeInfo;
Russell Taylor's avatar
Russell Taylor committed
46

47
48
49
50
51
52
namespace {
// -----------------------------------------------------------------------------
// Mock Random Number Generator
// -----------------------------------------------------------------------------
class MockRNG final : public Mantid::Kernel::PseudoRandomNumberGenerator {
public:
Samuel Jackson's avatar
Samuel Jackson committed
53
  GNU_DIAG_OFF_SUGGEST_OVERRIDE
54
55
56
57
58
59
60
61
  MOCK_METHOD0(nextValue, double());
  MOCK_METHOD2(nextValue, double(double, double));
  MOCK_METHOD2(nextInt, int(int, int));
  MOCK_METHOD0(restart, void());
  MOCK_METHOD0(save, void());
  MOCK_METHOD0(restore, void());
  MOCK_METHOD1(setSeed, void(size_t));
  MOCK_METHOD2(setRange, void(const double, const double));
62
63
  MOCK_CONST_METHOD0(min, double());
  MOCK_CONST_METHOD0(max, double());
Samuel Jackson's avatar
Samuel Jackson committed
64
  GNU_DIAG_ON_SUGGEST_OVERRIDE
65
};
LamarMoore's avatar
LamarMoore committed
66
} // namespace
67

68
class CSGObjectTest : public CxxTest::TestSuite {
Russell Taylor's avatar
Russell Taylor committed
69
70

public:
71
  void testDefaultObjectHasEmptyMaterial() {
72
    CSGObject obj;
73

74
75
    TSM_ASSERT_DELTA("Expected a zero number density", 0.0,
                     obj.material().numberDensity(), 1e-12);
76
77
  }

78
  void testObjectSetMaterialReplacesExisting() {
79
    using Mantid::Kernel::Material;
80
    CSGObject obj;
81
82
83

    TSM_ASSERT_DELTA("Expected a zero number density", 0.0,
                     obj.material().numberDensity(), 1e-12);
84
85
    obj.setMaterial(
        Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0));
86
87
88
89
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0,
                     obj.material().numberDensity(), 1e-12);
  }

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

102
    CSGObject copy(original);
103
    // The copy should be a primitive object with a GeometryHandler
104
105
    copy.GetObjectGeom(objType, pts, radius, height);

Martyn Gigg's avatar
Martyn Gigg committed
106
    TS_ASSERT_EQUALS("sp-1", copy.id());
107
108
109
    auto handlerCopy = copy.getGeometryHandler();
    TS_ASSERT(handlerCopy->hasShapeInfo());
    TS_ASSERT_EQUALS(handler->shapeInfo(), handlerCopy->shapeInfo());
110
    TS_ASSERT_EQUALS(copy.getName(), original.getName());
111
    // Check the string representation is the same
112
113
    TS_ASSERT_EQUALS(copy.str(), original.str());
    TS_ASSERT_EQUALS(copy.getSurfaceIndex(), original.getSurfaceIndex());
114
115
  }

116
  void testAssignmentOperatorGivesObjectWithSameAttributes() {
117
    auto original_ptr = ComponentCreationHelper::createSphere(1.0);
118
    auto &original = dynamic_cast<CSGObject &>(*original_ptr);
119
    original.setID("sp-1");
120
    ShapeInfo::GeometryShape objType;
121
122
    double radius(-1.0), height(-1.0);
    std::vector<V3D> pts;
Lamar Moore's avatar
Lamar Moore committed
123
    auto handler = original.getGeometryHandler();
124
    TS_ASSERT(handler->hasShapeInfo());
125
    original.GetObjectGeom(objType, pts, radius, height);
126
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
127

128
    CSGObject lhs;  // initialize
129
    lhs = original; // assign
130
131
132
    // The copy should be a primitive object with a GluGeometryHandler
    lhs.GetObjectGeom(objType, pts, radius, height);

Martyn Gigg's avatar
Martyn Gigg committed
133
    TS_ASSERT_EQUALS("sp-1", lhs.id());
134
135
136
137
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
    auto handlerCopy = lhs.getGeometryHandler();
    TS_ASSERT(handlerCopy->hasShapeInfo());
    TS_ASSERT_EQUALS(handlerCopy->shapeInfo(), handler->shapeInfo());
138
  }
Russell Taylor's avatar
Russell Taylor committed
139

140
  void testCreateUnitCube() {
141
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
142

143
    TS_ASSERT_EQUALS(geom_obj->str(), "68 1 -2 3 -4 5 -6");
144
145

    double xmin(0.0), xmax(0.0), ymin(0.0), ymax(0.0), zmin(0.0), zmax(0.0);
146
    geom_obj->getBoundingBox(xmax, ymax, zmax, xmin, ymin, zmin);
Russell Taylor's avatar
Russell Taylor committed
147
148
  }

149
150
  void testCloneWithMaterial() {
    using Mantid::Kernel::Material;
mantid-builder's avatar
mantid-builder committed
151
152
    auto testMaterial =
        Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0);
153
    auto geom_obj = createUnitCube();
154
155
156
    std::unique_ptr<IObject> cloned_obj;
    TS_ASSERT_THROWS_NOTHING(
        cloned_obj.reset(geom_obj->cloneWithMaterial(testMaterial)));
157
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0,
mantid-builder's avatar
mantid-builder committed
158
                     cloned_obj->material().numberDensity(), 1e-12);
159
160
  }

161
  void testIsOnSideCappedCylinder() {
162
    auto geom_obj = createCappedCylinder();
163
164
165
166
167
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
    // 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
193
194
  }

195
  void testIsValidCappedCylinder() {
196
    auto geom_obj = createCappedCylinder();
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
    // 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
227
228
  }

229
  void testIsOnSideSphere() {
230
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
    // 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
248
249
  }

250
  void testIsValidSphere() {
251
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
    // 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
269
270
  }

271
  void testCalcValidTypeSphere() {
272
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
    // entry on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(-1, 0, 0)),
                     -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(1, 0, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(-1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -4.1, 0), V3D(0, 1, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -4.1, 0), V3D(0, -1, 0)),
                     -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 4.1, 0), V3D(0, 1, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 4.1, 0), V3D(0, -1, 0)), 1);

    // a glancing blow
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0, 1, 0)), 0);
    // not quite on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0.5, 0.5, 0)),
                     1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(0.5, 0.5, 0)),
                     -1);
Russell Taylor's avatar
Russell Taylor committed
292
293
  }

294
  void testGetBoundingBoxForSphere() {
295
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
296
297
    const double tolerance(1e-10);

298
299
300
301
302
303
304
305
    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);
306
307
  }

308
  void testCalcValidTypeCappedCylinder() {
309
    auto geom_obj = createCappedCylinder();
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
    // entry on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(-1, 0, 0)),
                     -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(1, 0, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(-1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -3, 0), V3D(0, 1, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -3, 0), V3D(0, -1, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 3, 0), V3D(0, 1, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 3, 0), V3D(0, -1, 0)), 1);

    // a glancing blow
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0, 1, 0)), 0);
    // not quite on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0.5, 0.5, 0)),
                     1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(0.5, 0.5, 0)),
                     -1);
Russell Taylor's avatar
Russell Taylor committed
328
329
  }

330
  void testInterceptSurfaceSphereZ() {
331
    std::vector<Link> expectedResults;
332
    std::string S41 = "s 1 1 1 4"; // Sphere at (1,1,1) radius 4
Russell Taylor's avatar
Russell Taylor committed
333
334

    // First create some surfaces
335
336
    std::map<int, boost::shared_ptr<Surface>> SphSurMap;
    SphSurMap[41] = boost::make_shared<Sphere>();
Russell Taylor's avatar
Russell Taylor committed
337
338
339
    SphSurMap[41]->setSurface(S41);
    SphSurMap[41]->setName(41);

340
    // A sphere
341
    std::string ObjSphere = "-41";
Russell Taylor's avatar
Russell Taylor committed
342

mantid-builder's avatar
mantid-builder committed
343
344
    boost::shared_ptr<CSGObject> geom_obj =
        boost::shared_ptr<CSGObject>(new CSGObject);
345
    geom_obj->setObject(41, ObjSphere);
346
    geom_obj->populate(SphSurMap);
Russell Taylor's avatar
Russell Taylor committed
347

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

350
    // format = startPoint, endPoint, total distance so far
Russell Taylor's avatar
Russell Taylor committed
351
    // forward only intercepts means that start point should be track origin
352
353
354
    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
355

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

359
  void testInterceptSurfaceSphereY() {
360
    std::vector<Link> expectedResults;
361
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
362
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
363

364
365
366
    // 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
367

368
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
369
370
  }

371
  void testInterceptSurfaceSphereX() {
372
    std::vector<Link> expectedResults;
373
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
374
    Track track(V3D(-10, 0, 0), V3D(1, 0, 0));
375

376
377
378
379
    // 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
380
381
  }

382
  void testInterceptSurfaceCappedCylinderY() {
383
    std::vector<Link> expectedResults;
384
    auto geom_obj = createCappedCylinder();
385
386
    // 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
387

388
389
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
390
391
  }

392
  void testInterceptSurfaceCappedCylinderX() {
393
    std::vector<Link> expectedResults;
394
    auto geom_obj = createCappedCylinder();
395
    Track track(V3D(-10, 0, 0), V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
396

397
398
399
400
    // 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
401
402
  }

403
404
405
  void testInterceptSurfaceCappedCylinderMiss() {
    std::vector<Link>
        expectedResults; // left empty as there are no expected results
406
    auto geom_obj = createCappedCylinder();
407
    Track track(V3D(-10, 0, 0), V3D(1, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
408

409
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
410
411
  }

412
413
  void checkTrackIntercept(Track &track,
                           const std::vector<Link> &expectedResults) {
414
    size_t index = 0;
mantid-builder's avatar
mantid-builder committed
415
416
    for (Track::LType::const_iterator it = track.cbegin(); it != track.cend();
         ++it) {
417
418
      if (index < expectedResults.size()) {
        TS_ASSERT_DELTA(it->distFromStart, expectedResults[index].distFromStart,
mantid-builder's avatar
mantid-builder committed
419
                        1e-6);
420
        TS_ASSERT_DELTA(it->distInsideObject,
mantid-builder's avatar
mantid-builder committed
421
                        expectedResults[index].distInsideObject, 1e-6);
422
423
424
425
        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
426
427
      ++index;
    }
428
    TS_ASSERT_EQUALS(index, expectedResults.size());
Russell Taylor's avatar
Russell Taylor committed
429
430
  }

431
  void checkTrackIntercept(IObject_sptr obj, Track &track,
432
                           const std::vector<Link> &expectedResults) {
433
    int unitCount = obj->interceptSurface(track);
434
435
    TS_ASSERT_EQUALS(unitCount, expectedResults.size());
    checkTrackIntercept(track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
436
437
  }

438
  void testTrackTwoIsolatedCubes()
439
440
441
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
442
  {
443
444
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006";
    std::string ObjB = "80001 -80002 60003 -60004 60005 -60006";
445

446
    createSurfaces(ObjA);
447
    CSGObject object1 = CSGObject();
448
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
449
450
    object1.populate(SMap);

451
    createSurfaces(ObjB);
452
    CSGObject object2 = CSGObject();
453
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
454
455
    object2.populate(SMap);

456
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
457
458

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

462
    std::vector<Link> expectedResults;
463
464
465
466
    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
467
468
469
  }

  void testTrackTwoTouchingCubes()
470
471
472
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
473
  {
474
475
    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
476

477
    createSurfaces(ObjA);
478
    CSGObject object1 = CSGObject();
479
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
480
481
    object1.populate(SMap);

482
    createSurfaces(ObjB);
483
    CSGObject object2 = CSGObject();
484
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
485
486
    object2.populate(SMap);

487
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
488
489

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

493
    std::vector<Link> expectedResults;
494
495
496
    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
497

498
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
499
500
501
  }

  void testTrackCubeWithInternalSphere()
502
503
504
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
505
  {
506
507
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 71";
    std::string ObjB = "-71";
Russell Taylor's avatar
Russell Taylor committed
508

509
    createSurfaces(ObjA);
510
    CSGObject object1 = CSGObject();
511
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
512
513
    object1.populate(SMap);

514
    createSurfaces(ObjB);
515
    CSGObject object2 = CSGObject();
516
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
517
518
    object2.populate(SMap);

519
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
520
521

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

525
    std::vector<Link> expectedResults;
526
527
528
529
530
531
    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
532
533
534
  }

  void testTrack_CubePlusInternalEdgeTouchSpheres()
535
536
537
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
538
  {
539
540
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
541

542
    createSurfaces(ObjA);
543
    CSGObject object1 = CSGObject();
544
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
545
546
    object1.populate(SMap);

547
    createSurfaces(ObjB);
548
    CSGObject object2 = CSGObject();
549
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
550
551
    object2.populate(SMap);

552
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
553
554

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

558
    std::vector<Link> expectedResults;
559
560
561
562
563
564
    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
565
566
567
  }

  void testTrack_CubePlusInternalEdgeTouchSpheresMiss()
568
569
570
  /**
  Test a track missing an object
  */
Russell Taylor's avatar
Russell Taylor committed
571
  {
572
573
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
574

575
    createSurfaces(ObjA);
576
    CSGObject object1 = CSGObject();
577
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
578
579
    object1.populate(SMap);

580
    createSurfaces(ObjB);
581
    CSGObject object2 = CSGObject();
582
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
583
584
    object2.populate(SMap);

585
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
586
587

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

591
592
    std::vector<Link> expectedResults; // left empty as this should miss
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
593
594
  }

595
  void testComplementWithTwoPrimitives() {
596
    auto shell_ptr = ComponentCreationHelper::createHollowShell(0.5, 1.0);
597
    auto shell = dynamic_cast<CSGObject *>(shell_ptr.get());
598
599
600
601
602
603
604
605
606
607

    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);
608
    TS_ASSERT_EQUALS(2, surfPt1->getKeyN());
609
610
611
612
613
614
615
616
617
618
    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));
619
    TS_ASSERT_EQUALS(1, surfPt2->getKeyN());
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
    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
638
  void testFindPointInCube()
639
640
641
  /**
  Test find point in cube
  */
Russell Taylor's avatar
Russell Taylor committed
642
  {
643
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
Russell Taylor's avatar
Russell Taylor committed
644
    // initial guess in object
645
    Kernel::V3D pt;
646
647
    TS_ASSERT_EQUALS(geom_obj->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
648
    // initial guess not in object, but on x-axis
649
650
    std::vector<std::string> planes{"px 10",  "px 11",   "py -0.5",
                                    "py 0.5", "pz -0.5", "pz 0.5"};
651
    boost::shared_ptr<CSGObject> B = createCuboid(planes);
652
653
    TS_ASSERT_EQUALS(B->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(10, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
654
    // on y axis
655
    planes = {"px -0.5", "px 0.5", "py -22", "py -21", "pz -0.5", "pz 0.5"};
656
    boost::shared_ptr<CSGObject> C = createCuboid(planes);
657
658
    TS_ASSERT_EQUALS(C->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0, -21, 0));
Russell Taylor's avatar
Russell Taylor committed
659
    // not on principle axis, now works using getBoundingBox
660
    planes = {"px 0.5", "px 1.5", "py -22", "py -21", "pz -0.5", "pz 0.5"};
661
    boost::shared_ptr<CSGObject> D = createCuboid(planes);
662
663
664
665
666
667
668
669
670
671
672
673
    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
674
    // common
LamarMoore's avatar
LamarMoore committed
675
676
    planes = {"p 1 0 0 -0.5",
              "p 1 0 0 0.5",
677
678
679
680
              "p 0 .70710678118 .70710678118 -1.1",
              "p 0 .70710678118 .70710678118 -0.1",
              "p 0 -.70710678118 .70710678118 -0.5",
              "p 0 -.70710678118 .70710678118 0.5"};
681
    boost::shared_ptr<CSGObject> E = createCuboid(planes);
682
683
684
685
    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);
686
687
688
    // 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.
689
    // Object is unit cube located at +-0.5 in x but centred on z=y=-1.606.. and
690
    // rotated 45deg to these two axes
LamarMoore's avatar
LamarMoore committed
691
692
    planes = {"p 1 0 0 -0.5",
              "p 1 0 0 0.5",
693
694
695
696
              "p 0  .70710678118 .70710678118 -2",
              "p 0  .70710678118 .70710678118 -1",
              "p 0 -.70710678118 .70710678118 -0.5",
              "p 0 -.70710678118 .70710678118 0.5"};
697
    boost::shared_ptr<CSGObject> F = createCuboid(planes);
698
    TS_ASSERT_EQUALS(F->getPointInObject(pt), 1); // This now succeeds
699
700
    // Test use of defineBoundingBox to explictly set the bounding box, when the
    // automatic method fails
701
702
703
    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);
704
    TS_ASSERT_EQUALS(F->getPointInObject(pt), 1);
705
    auto S = ComponentCreationHelper::createSphere(4.1);
706
707
    TS_ASSERT_EQUALS(S->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0.0, 0.0, 0));
Russell Taylor's avatar
Russell Taylor committed
708
709
  }

710
711
712
713
714
715
716
717
718
719
720
721
722
  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);
723
    constexpr size_t maxAttempts{1};
724
725
726
727
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = shell->generatePointInObject(rng, maxAttempts));

728
    constexpr double tolerance{1e-10};
729
730
731
732
733
    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);
  }

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
820
821
822
823
824
825
826
827
828
829
830
831
  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);
  }

832
833
834
835
836
837
838
839
840
841
842
843
844
  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);
845
    constexpr size_t maxAttempts{1};
846
847
848
849
850
851
852
853
854
855
    TS_ASSERT_THROWS(shell->generatePointInObject(rng, maxAttempts),
                     std::runtime_error);
  }

  void testGeneratePointInsideRespectsActiveRegion() {
    using namespace ::testing;

    // Generate "random" sequence.
    MockRNG rng;
    Sequence rand;
856
857
858
859
860
861
862
863
864
    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);
865
866
    // Create a thin infinite rectangular region to restrict point generation
    BoundingBox activeRegion(0.1, 0.1, 0.1, -0.1, -0.1, -0.1);
867
    constexpr size_t maxAttempts{1};
868
869
870
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = ball->generatePointInObject(rng, activeRegion, maxAttempts));
871
872
873
874
875
    // 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)
876
877
  }

Russell Taylor's avatar
Russell Taylor committed
878
  void testSolidAngleSphere()
879
880
881
  /**
  Test solid angle calculation for a sphere
  */
Russell Taylor's avatar
Russell Taylor committed
882
  {
883
    auto geom_obj_ptr = ComponentCreationHelper::createSphere(4.1);
884
    auto geom_obj = dynamic_cast<CSGObject *>(geom_obj_ptr.get());
885
    double satol = 2e-2; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
886
887
888
889
890

    // 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
891
892
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(8.1, 0, 0)), 0.864364,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
893
    // internal point (should be 4pi)
894
895
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(0, 0, 0)), 4 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
896
    // surface point
897
898
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(4.1, 0, 0)), 2 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
899
900
901
  }

  void testSolidAngleCappedCylinder()
902
903
904
  /**
  Test solid angle calculation for a capped cylinder
  */
Russell Taylor's avatar
Russell Taylor committed
905
  {
906
    boost::shared_ptr<CSGObject> geom_obj = createSmallCappedCylinder();
907
    // Want to test triangulation so setup a geometry handler
908
    auto h = boost::make_shared<GeometryHandler>(geom_obj);
909
910
911
912
    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));
913
    geom_obj->setGeometryHandler(h);
914

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

917
918
919
920
921
922
    // 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);
923
    // Other end
924
925
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.497, 0.0, 0.0)), 0.0,
                    satol);
926

927
    // Side values
928
929
930
931
932
933
934
935
    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);
936

Russell Taylor's avatar
Russell Taylor committed
937
    // internal point (should be 4pi)
938
939
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-0.999, 0.0, 0.0)),
                    4 * M_PI, satol);
940
941

    // surface points
942
943
944
945
    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
946
947
948
  }

  void testSolidAngleCubeTriangles()
949
950
951
952
  /**
  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
953
  {
954
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
955
    double satol = 1e-3; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
956
957
958
959
960

    // solid angle at distance 0.5 should be 4pi/6 by symmetry
    //
    // tests for Triangulated cube
    //
961
962
963
964
965
966
967
968
969
970
971
972
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(1.0, 0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.0, 0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 1.0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, -1.0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, 1.0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, -1.0)),
                    M_PI * 2.0 / 3.0, satol);
Russell Taylor's avatar
Russell Taylor committed
973
974
  }