CSGObjectTest.h 68 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
    double radius(-1.0), height(-1.0), innerRadius(0.0);
67
    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, innerRadius, 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
    copy.GetObjectGeom(objType, pts, innerRadius, radius, height);
76

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
    double radius(-1.0), height(-1.0), innerRadius(0.0);
93
    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, innerRadius, radius, height);
97
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
98

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

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
  }

Martyn Gigg's avatar
Martyn Gigg committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  void testDistanceWithIntersectionReturnsResult() {
    auto geom_obj = createCappedCylinder();
    V3D dir(0., 1., 0.);
    dir.normalize();
    Track track(V3D(0, 0, 0), dir);

    TS_ASSERT_DELTA(3.0, geom_obj->distance(track), 1e-08)
  }

  void testDistanceWithoutIntersectionThrows() {
    auto geom_obj = createCappedCylinder();
    V3D dir(-1., 0., 0.);
    dir.normalize();
    Track track(V3D(-10, 0, 0), dir);

    TS_ASSERT_THROWS(geom_obj->distance(track), std::runtime_error)
  }

444
  void testTrackTwoIsolatedCubes()
445
446
447
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
448
  {
449
450
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006";
    std::string ObjB = "80001 -80002 60003 -60004 60005 -60006";
451

452
    createSurfaces(ObjA);
453
    CSGObject object1 = CSGObject();
454
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
455
456
    object1.populate(SMap);

457
    createSurfaces(ObjB);
458
    CSGObject object2 = CSGObject();
459
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
460
461
    object2.populate(SMap);

462
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
463
464

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

468
    std::vector<Link> expectedResults;
469
470
471
472
    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
473
474
475
  }

  void testTrackTwoTouchingCubes()
476
477
478
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
479
  {
480
481
    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
482

483
    createSurfaces(ObjA);
484
    CSGObject object1 = CSGObject();
485
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
486
487
    object1.populate(SMap);

488
    createSurfaces(ObjB);
489
    CSGObject object2 = CSGObject();
490
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
491
492
    object2.populate(SMap);

493
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
494
495

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

499
    std::vector<Link> expectedResults;
500
501
502
    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
503

504
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
505
506
507
  }

  void testTrackCubeWithInternalSphere()
508
509
510
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
511
  {
512
513
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 71";
    std::string ObjB = "-71";
Russell Taylor's avatar
Russell Taylor committed
514

515
    createSurfaces(ObjA);
516
    CSGObject object1 = CSGObject();
517
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
518
519
    object1.populate(SMap);

520
    createSurfaces(ObjB);
521
    CSGObject object2 = CSGObject();
522
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
523
524
    object2.populate(SMap);

525
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
526
527

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

531
    std::vector<Link> expectedResults;
532
533
534
535
536
537
    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
538
539
540
  }

  void testTrack_CubePlusInternalEdgeTouchSpheres()
541
542
543
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
544
  {
545
546
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
547

548
    createSurfaces(ObjA);
549
    CSGObject object1 = CSGObject();
550
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
551
552
    object1.populate(SMap);

553
    createSurfaces(ObjB);
554
    CSGObject object2 = CSGObject();
555
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
556
557
    object2.populate(SMap);

558
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
559
560

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

564
    std::vector<Link> expectedResults;
565
566
567
568
569
570
    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
571
572
573
  }

  void testTrack_CubePlusInternalEdgeTouchSpheresMiss()
574
575
576
  /**
  Test a track missing an object
  */
Russell Taylor's avatar
Russell Taylor committed
577
  {
578
579
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
580

581
    createSurfaces(ObjA);
582
    CSGObject object1 = CSGObject();
583
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
584
585
    object1.populate(SMap);

586
    createSurfaces(ObjB);
587
    CSGObject object2 = CSGObject();
588
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
589
590
    object2.populate(SMap);

591
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
592
593

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

597
598
    std::vector<Link> expectedResults; // left empty as this should miss
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
599
600
  }

601
  void testComplementWithTwoPrimitives() {
602
    auto shell_ptr = ComponentCreationHelper::createHollowShell(0.5, 1.0);
603
    auto shell = dynamic_cast<CSGObject *>(shell_ptr.get());
604
605
606
607
608
609
610
611
612
613

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

716
717
718
719
720
721
722
723
724
725
726
727
728
  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);
729
    constexpr size_t maxAttempts{1};
730
731
732
733
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = shell->generatePointInObject(rng, maxAttempts));

734
    constexpr double tolerance{1e-10};
735
736
737
738
739
    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);
  }

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
  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);
  }

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
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
  void testGeneratePointInsideHollowCylinder() {
    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 innerRadius{0.29};
    constexpr double radius{0.3};
    constexpr double height{0.5};
    const V3D axis{0., 0., 1.};
    const V3D bottomCentre{
        -1.,
        2.,
        -3.,
    };
    auto hollowCylinder = ComponentCreationHelper::createHollowCylinder(
        innerRadius, radius, height, bottomCentre, axis, "hol-cyl");
    constexpr size_t maxAttempts{0};
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = hollowCylinder->generatePointInObject(rng, maxAttempts));
    // Global->cylinder local coordinates
    point -= bottomCentre;
    constexpr double tolerance{1e-10};
    const double polarAngle{2. * M_PI * randT};
    const double c1 = std::pow(innerRadius, 2);
    const double c2 = std::pow(radius, 2);
    const double radialLength{std::sqrt(c1 + (c2 - c1) * 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);
  }

848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
  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);
  }

879
880
881
882
883
884
885
886
887
888
889
890
891
  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);
892
    constexpr size_t maxAttempts{1};
893
    TS_ASSERT_THROWS(shell->generatePointInObject(rng, maxAttempts),
894
                     const std::runtime_error &);
895
896
897
898
899
900
901
902
  }

  void testGeneratePointInsideRespectsActiveRegion() {
    using namespace ::testing;

    // Generate "random" sequence.
    MockRNG rng;
    Sequence rand;
903
904
905
906
907
908
909
910
911
    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);
912
913
    // Create a thin infinite rectangular region to restrict point generation
    BoundingBox activeRegion(0.1, 0.1, 0.1, -0.1, -0.1, -0.1);
914
    constexpr size_t maxAttempts{1};
915
916
917
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = ball->generatePointInObject(rng, activeRegion, maxAttempts));
918
919
920
921
922
    // 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)
923
924
  }

Russell Taylor's avatar
Russell Taylor committed
925
  void testSolidAngleSphere()
926
927
928
  /**
  Test solid angle calculation for a sphere
  */
Russell Taylor's avatar
Russell Taylor committed
929
  {
930
    auto geom_obj_ptr = ComponentCreationHelper::createSphere(4.1);
931
    auto geom_obj = dynamic_cast<CSGObject *>(geom_obj_ptr.get());
932
    double satol = 2e-2; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
933
934
935
936
937

    // 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
938
939
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(8.1, 0, 0)), 0.864364,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
940
    // internal point (should be 4pi)
941
942
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(0, 0, 0)), 4 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
943
    // surface point
944
945
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(4.1, 0, 0)), 2 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
946
947
948
  }

  void testSolidAngleCappedCylinder()
949
950
951
  /**
  Test solid angle calculation for a capped cylinder
  */
Russell Taylor's avatar
Russell Taylor committed
952
  {
953
    boost::shared_ptr<CSGObject> geom_obj = createSmallCappedCylinder();
954
    // Want to test triangulation so setup a geometry handler
955
    auto h = boost::make_shared<GeometryHandler>(geom_obj);
956
957
958
959
    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));
960
    geom_obj->setGeometryHandler(h);
961

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

964
965
966
967
    // 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
968
    TS_ASSERT_DELTA