CSGObjectTest.h 70 KB
Newer Older
1
2
3
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4
5
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6
// SPDX - License - Identifier: GPL - 3.0 +
7
#pragma once
Russell Taylor's avatar
Russell Taylor committed
8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

131
  void testIsOnSideCappedCylinder() {
132
    auto geom_obj = createCappedCylinder();
133
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
    // 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
163
164
  }

165
  void testIsValidCappedCylinder() {
166
    auto geom_obj = createCappedCylinder();
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
193
194
195
196
    // 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
197
198
  }

199
  void testIsOnSideSphere() {
200
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
    // 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
218
219
  }

220
  void testIsValidSphere() {
221
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
    // 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
239
240
  }

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

    // a glancing blow
Peterson, Peter's avatar
Peterson, Peter committed
262
263
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0, 1, 0)),
                     TrackDirection::INVALID);
264
265
    // 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
266
                     TrackDirection::ENTERING);
267
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(0.5, 0.5, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
268
                     TrackDirection::LEAVING);
Russell Taylor's avatar
Russell Taylor committed
269
270
  }

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

275
276
277
278
279
280
281
282
    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);
283
284
  }

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

    // a glancing blow
Peterson, Peter's avatar
Peterson, Peter committed
306
307
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0, 1, 0)),
                     TrackDirection::INVALID);
308
309
    // 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
310
                     TrackDirection::ENTERING);
311
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(0.5, 0.5, 0)),
Peterson, Peter's avatar
Peterson, Peter committed
312
                     TrackDirection::LEAVING);
Russell Taylor's avatar
Russell Taylor committed
313
314
  }

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

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

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

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

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

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

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

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

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

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

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

361
    // format = startPoint, endPoint, total distance so far
362
    expectedResults.emplace_back(
363
364
        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
365
366
  }

367
  void testInterceptSurfaceCappedCylinderY() {
368
    std::vector<Link> expectedResults;
369
    auto geom_obj = createCappedCylinder();
370
    // format = startPoint, endPoint, total distance so far
Nick Draper's avatar
Nick Draper committed
371
372
    expectedResults.emplace_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
    // format = startPoint, endPoint, total distance so far
384
    expectedResults.emplace_back(
385
386
        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
  }

David Fairbrother's avatar
David Fairbrother committed
419
  void checkTrackIntercept(const 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
  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);

441
    TS_ASSERT_THROWS(geom_obj->distance(track), const std::runtime_error &)
Martyn Gigg's avatar
Martyn Gigg committed
442
443
  }

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
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.emplace_back(
471
472
        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
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.emplace_back(
502
        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
    expectedResults.emplace_back(
533
        Link(V3D(-1, 0, 0), V3D(-0.8, 0, 0), 4.2, object1));
534
    expectedResults.emplace_back(
535
        Link(V3D(-0.8, 0, 0), V3D(0.8, 0, 0), 5.8, object1));
Nick Draper's avatar
Nick Draper committed
536
537
    expectedResults.emplace_back(
        Link(V3D(0.8, 0, 0), V3D(1, 0, 0), 6, object2));
538
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
539
540
541
  }

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

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

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

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

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

565
    std::vector<Link> expectedResults;
566
    expectedResults.emplace_back(
567
        Link(V3D(-1, 0, 0), V3D(-0.4, 0, 0), 4.6, object1));
568
    expectedResults.emplace_back(
569
        Link(V3D(-0.4, 0, 0), V3D(0.2, 0, 0), 5.2, object1));
Nick Draper's avatar
Nick Draper committed
570
571
    expectedResults.emplace_back(
        Link(V3D(0.2, 0, 0), V3D(1, 0, 0), 6, object2));
572
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
573
574
575
  }

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

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

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

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

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

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

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

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

718
719
720
721
722
723
724
725
726
727
728
729
730
  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);
731
    constexpr size_t maxAttempts{1};
Danny Hindson's avatar
Danny Hindson committed
732
733
    boost::optional<V3D> point = shell->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
734

735
    constexpr double tolerance{1e-10};
Danny Hindson's avatar
Danny Hindson committed
736
737
738
    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);
739
740
  }

741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
  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};
Danny Hindson's avatar
Danny Hindson committed
760
761
762
    boost::optional<V3D> point =
        cuboid->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
763
764

    constexpr double tolerance{1e-10};
Danny Hindson's avatar
Danny Hindson committed
765
766
767
    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);
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
  }

  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};
Danny Hindson's avatar
Danny Hindson committed
794
795
796
    boost::optional<V3D> point =
        cylinder->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
797
    // Global->cylinder local coordinates
Danny Hindson's avatar
Danny Hindson committed
798
    *point -= bottomCentre;
799
800
801
802
    constexpr double tolerance{1e-10};
    const double polarAngle{2. * M_PI * randT};
    const double radialLength{radius * std::sqrt(randR)};
    const double axisLength{height * randZ};
Danny Hindson's avatar
Danny Hindson committed
803
804
805
    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);
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
832
  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};
Danny Hindson's avatar
Danny Hindson committed
833
834
835
    boost::optional<V3D> point;
    point = hollowCylinder->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
836
    // Global->cylinder local coordinates
Danny Hindson's avatar
Danny Hindson committed
837
    *point -= bottomCentre;
838
839
840
841
842
843
    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};
Danny Hindson's avatar
Danny Hindson committed
844
845
846
    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);
847
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
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
  void testTracksForSphere() {
    // Sphere centered at origin, 3 mm diameter
    constexpr double RADIUS{0.0015};

    const V3D BEAM_X{1.0, 0.0, 0.0};
    const V3D BEAM_Y{0.0, 1.0, 0.0};
    const V3D BEAM_Z{0.0, 0.0, 1.0};

    auto sphere = ComponentCreationHelper::createSphere(RADIUS);

    // Test center of sphere
    Track origin(V3D{0.0, 0.0, 0.0}, BEAM_X);
    sphere->interceptSurface(origin);
    TS_ASSERT_EQUALS(origin.front().distInsideObject, RADIUS);

    Track front_midpoint(V3D{0.5*RADIUS, 0.0, 0.0}, BEAM_X);
    sphere->interceptSurface(front_midpoint);
    TS_ASSERT_EQUALS(front_midpoint.front().distInsideObject, 0.5*RADIUS);

    Track back_midpoint(V3D{-0.5*RADIUS, 0.0, 0.0}, BEAM_X);
    sphere->interceptSurface(back_midpoint);
    TS_ASSERT_EQUALS(back_midpoint.front().distInsideObject, 1.5*RADIUS);

    // Y axis tests
    origin = Track(V3D{0.0, 0.0, 0.0}, BEAM_Y);
    sphere->interceptSurface(origin);
    TS_ASSERT_EQUALS(origin.front().distInsideObject, RADIUS);

    front_midpoint = Track(V3D{0.0, 0.5*RADIUS, 0.0}, BEAM_Y);
    sphere->interceptSurface(front_midpoint);
    TS_ASSERT_EQUALS(front_midpoint.front().distInsideObject, 0.5*RADIUS);

    back_midpoint = Track(V3D{0.0, -0.5*RADIUS, 0.0}, BEAM_Y);
    sphere->interceptSurface(back_midpoint);
    TS_ASSERT_EQUALS(back_midpoint.front().distInsideObject, 1.5*RADIUS);

    // Z axis tests
    origin = Track(V3D{0.0, 0.0, 0.0}, BEAM_Z);
    sphere->interceptSurface(origin);
    TS_ASSERT_EQUALS(origin.front().distInsideObject, RADIUS);

    front_midpoint = Track(V3D{0.0, 0.0, 0.5*RADIUS}, BEAM_Z);
    sphere->interceptSurface(front_midpoint);
    TS_ASSERT_EQUALS(front_midpoint.front().distInsideObject, 0.5*RADIUS);

    back_midpoint = Track(V3D{0.0, 0.0, -0.5*RADIUS}, BEAM_Z);
    sphere->interceptSurface(back_midpoint);
    TS_ASSERT_EQUALS(back_midpoint.front().distInsideObject, 1.5*RADIUS);
  }

899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
  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};
Danny Hindson's avatar
Danny Hindson committed
915
916
917
    boost::optional<V3D> point;
    point = sphere->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
918
919
920
921
922
923