CSGObjectTest.h 87.1 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

Samuel Jones's avatar
Samuel Jones committed
44
    TSM_ASSERT_DELTA("Expected a zero number density", 0.0, obj.material().numberDensity(), 1e-12);
45
46
  }

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

Samuel Jones's avatar
Samuel Jones committed
51
52
53
    TSM_ASSERT_DELTA("Expected a zero number density", 0.0, obj.material().numberDensity(), 1e-12);
    obj.setMaterial(Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0));
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0, obj.material().numberDensity(), 1e-12);
54
55
  }

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

68
    CSGObject copy(original);
69
    // The copy should be a primitive object with a GeometryHandler
70
    copy.GetObjectGeom(objType, pts, innerRadius, radius, height);
71

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

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

94
    CSGObject lhs;  // initialize
95
    lhs = original; // assign
96
    // The copy should be a primitive object with a GluGeometryHandler
97
    lhs.GetObjectGeom(objType, pts, innerRadius, radius, height);
98

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

106
  void testCreateUnitCube() {
107
    std::shared_ptr<CSGObject> geom_obj = createUnitCube();
108

109
    TS_ASSERT_EQUALS(geom_obj->str(), "68 1 -2 3 -4 5 -6");
110
111

    double xmin(0.0), xmax(0.0), ymin(0.0), ymax(0.0), zmin(0.0), zmax(0.0);
112
    geom_obj->getBoundingBox(xmax, ymax, zmax, xmin, ymin, zmin);
Russell Taylor's avatar
Russell Taylor committed
113
114
  }

115
116
  void testCloneWithMaterial() {
    using Mantid::Kernel::Material;
Samuel Jones's avatar
Samuel Jones committed
117
    auto testMaterial = Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0);
118
    auto geom_obj = createUnitCube();
119
    std::unique_ptr<IObject> cloned_obj;
Samuel Jones's avatar
Samuel Jones committed
120
121
    TS_ASSERT_THROWS_NOTHING(cloned_obj.reset(geom_obj->cloneWithMaterial(testMaterial)));
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0, cloned_obj->material().numberDensity(), 1e-12);
122
123
  }

124
  void testIsOnSideCappedCylinder() {
125
    auto geom_obj = createCappedCylinder();
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    // 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
156
157
  }

158
  void testIsValidCappedCylinder() {
159
    auto geom_obj = createCappedCylinder();
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    // 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
190
191
  }

192
  void testIsOnSideSphere() {
193
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
    // 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
211
212
  }

213
  void testIsValidSphere() {
214
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    // 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
232
233
  }

234
  void testCalcValidTypeSphere() {
235
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
236
    // entry on the normal
Samuel Jones's avatar
Samuel Jones committed
237
238
239
240
241
242
243
244
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(1, 0, 0)), TrackDirection::ENTERING);
    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::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);
    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::LEAVING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 4.1, 0), V3D(0, -1, 0)), TrackDirection::ENTERING);
245
246

    // a glancing blow
Samuel Jones's avatar
Samuel Jones committed
247
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0, 1, 0)), TrackDirection::INVALID);
248
    // not quite on the normal
Samuel Jones's avatar
Samuel Jones committed
249
250
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0.5, 0.5, 0)), TrackDirection::ENTERING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(0.5, 0.5, 0)), TrackDirection::LEAVING);
Russell Taylor's avatar
Russell Taylor committed
251
252
  }

253
  void testGetBoundingBoxForSphere() {
254
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
255
256
    const double tolerance(1e-10);

257
258
259
260
261
262
263
264
    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);
265
266
  }

267
  void testCalcValidTypeCappedCylinder() {
268
    auto geom_obj = createCappedCylinder();
269
    // entry on the normal
Samuel Jones's avatar
Samuel Jones committed
270
271
272
273
274
275
276
277
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(1, 0, 0)), TrackDirection::ENTERING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.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::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);
278
279

    // a glancing blow
Samuel Jones's avatar
Samuel Jones committed
280
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0, 1, 0)), TrackDirection::INVALID);
281
    // not quite on the normal
Samuel Jones's avatar
Samuel Jones committed
282
283
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0.5, 0.5, 0)), TrackDirection::ENTERING);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(0.5, 0.5, 0)), TrackDirection::LEAVING);
Russell Taylor's avatar
Russell Taylor committed
284
285
  }

286
  void testInterceptSurfaceSphereZ() {
287
    std::vector<Link> expectedResults;
288
    std::string S41 = "s 1 1 1 4"; // Sphere at (1,1,1) radius 4
Russell Taylor's avatar
Russell Taylor committed
289
290

    // First create some surfaces
291
292
    std::map<int, std::shared_ptr<Surface>> SphSurMap;
    SphSurMap[41] = std::make_shared<Sphere>();
Russell Taylor's avatar
Russell Taylor committed
293
294
295
    SphSurMap[41]->setSurface(S41);
    SphSurMap[41]->setName(41);

296
    // A sphere
297
    std::string ObjSphere = "-41";
Russell Taylor's avatar
Russell Taylor committed
298

Samuel Jones's avatar
Samuel Jones committed
299
    std::shared_ptr<CSGObject> geom_obj = std::shared_ptr<CSGObject>(new CSGObject);
300
    geom_obj->setObject(41, ObjSphere);
301
    geom_obj->populate(SphSurMap);
Russell Taylor's avatar
Russell Taylor committed
302

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

305
    // format = startPoint, endPoint, total distance so far
Russell Taylor's avatar
Russell Taylor committed
306
    // forward only intercepts means that start point should be track origin
Samuel Jones's avatar
Samuel Jones committed
307
    expectedResults.emplace_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
308

309
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
310
311
  }

312
  void testInterceptSurfaceSphereY() {
313
    std::vector<Link> expectedResults;
314
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
315
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
316

317
    // format = startPoint, endPoint, total distance so far
Samuel Jones's avatar
Samuel Jones committed
318
    expectedResults.emplace_back(Link(V3D(0, -4.1, 0), V3D(0, 4.1, 0), 14.1, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
319

320
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
321
322
  }

323
  void testInterceptSurfaceSphereX() {
324
    std::vector<Link> expectedResults;
325
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
326
    Track track(V3D(-10, 0, 0), V3D(1, 0, 0));
327

328
    // format = startPoint, endPoint, total distance so far
Samuel Jones's avatar
Samuel Jones committed
329
    expectedResults.emplace_back(Link(V3D(-4.1, 0, 0), V3D(4.1, 0, 0), 14.1, *geom_obj));
330
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
331
332
  }

333
  void testInterceptSurfaceCappedCylinderY() {
334
    std::vector<Link> expectedResults;
335
    auto geom_obj = createCappedCylinder();
336
    // format = startPoint, endPoint, total distance so far
Samuel Jones's avatar
Samuel Jones committed
337
    expectedResults.emplace_back(Link(V3D(0, -3, 0), V3D(0, 3, 0), 13, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
338

339
340
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
341
342
  }

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

348
    // format = startPoint, endPoint, total distance so far
Samuel Jones's avatar
Samuel Jones committed
349
    expectedResults.emplace_back(Link(V3D(-3.2, 0, 0), V3D(1.2, 0, 0), 11.2, *geom_obj));
350
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
351
352
  }

353
  void testInterceptSurfaceCappedCylinderMiss() {
Samuel Jones's avatar
Samuel Jones committed
354
    std::vector<Link> expectedResults; // left empty as there are no expected results
355
    auto geom_obj = createCappedCylinder();
356
357
358
    V3D dir(1., 1., 0.);
    dir.normalize();
    Track track(V3D(-10, 0, 0), dir);
Russell Taylor's avatar
Russell Taylor committed
359

360
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
361
362
  }

Samuel Jones's avatar
Samuel Jones committed
363
  void checkTrackIntercept(Track &track, const std::vector<Link> &expectedResults) {
364
    size_t index = 0;
Samuel Jones's avatar
Samuel Jones committed
365
    for (Track::LType::const_iterator it = track.cbegin(); it != track.cend(); ++it) {
366
      if (index < expectedResults.size()) {
Samuel Jones's avatar
Samuel Jones committed
367
368
        TS_ASSERT_DELTA(it->distFromStart, expectedResults[index].distFromStart, 1e-6);
        TS_ASSERT_DELTA(it->distInsideObject, expectedResults[index].distInsideObject, 1e-6);
369
370
371
372
        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
373
374
      ++index;
    }
375
    TS_ASSERT_EQUALS(index, expectedResults.size());
Russell Taylor's avatar
Russell Taylor committed
376
377
  }

Samuel Jones's avatar
Samuel Jones committed
378
  void checkTrackIntercept(const IObject_sptr &obj, Track &track, const std::vector<Link> &expectedResults) {
379
    int unitCount = obj->interceptSurface(track);
380
381
    TS_ASSERT_EQUALS(unitCount, expectedResults.size());
    checkTrackIntercept(track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
382
383
  }

Martyn Gigg's avatar
Martyn Gigg committed
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
  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);

399
    TS_ASSERT_THROWS(geom_obj->distance(track), const std::runtime_error &)
Martyn Gigg's avatar
Martyn Gigg committed
400
401
  }

402
  void testTrackTwoIsolatedCubes()
403
404
405
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
406
  {
407
408
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006";
    std::string ObjB = "80001 -80002 60003 -60004 60005 -60006";
409

410
    createSurfaces(ObjA);
411
    CSGObject object1 = CSGObject();
412
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
413
414
    object1.populate(SMap);

415
    createSurfaces(ObjB);
416
    CSGObject object2 = CSGObject();
417
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
418
419
    object2.populate(SMap);

420
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
421
422

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

426
    std::vector<Link> expectedResults;
427
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
Samuel Jones's avatar
Samuel Jones committed
428
    expectedResults.emplace_back(Link(V3D(4.5, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
429
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
430
431
432
  }

  void testTrackTwoTouchingCubes()
433
434
435
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
436
  {
437
438
    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
439

440
    createSurfaces(ObjA);
441
    CSGObject object1 = CSGObject();
442
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
443
444
    object1.populate(SMap);

445
    createSurfaces(ObjB);
446
    CSGObject object2 = CSGObject();
447
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
448
449
    object2.populate(SMap);

450
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
451
452

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

456
    std::vector<Link> expectedResults;
457
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
Samuel Jones's avatar
Samuel Jones committed
458
    expectedResults.emplace_back(Link(V3D(1, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
Russell Taylor's avatar
Russell Taylor committed
459

460
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
461
462
463
  }

  void testTrackCubeWithInternalSphere()
464
465
466
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
467
  {
468
469
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 71";
    std::string ObjB = "-71";
Russell Taylor's avatar
Russell Taylor committed
470

471
    createSurfaces(ObjA);
472
    CSGObject object1 = CSGObject();
473
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
474
475
    object1.populate(SMap);

476
    createSurfaces(ObjB);
477
    CSGObject object2 = CSGObject();
478
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
479
480
    object2.populate(SMap);

481
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
482
483

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

487
    std::vector<Link> expectedResults;
Samuel Jones's avatar
Samuel Jones committed
488
489
490
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(-0.8, 0, 0), 4.2, object1));
    expectedResults.emplace_back(Link(V3D(-0.8, 0, 0), V3D(0.8, 0, 0), 5.8, object1));
    expectedResults.emplace_back(Link(V3D(0.8, 0, 0), V3D(1, 0, 0), 6, object2));
491
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
492
493
494
  }

  void testTrack_CubePlusInternalEdgeTouchSpheres()
495
496
497
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
498
  {
499
500
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
501

502
    createSurfaces(ObjA);
503
    CSGObject object1 = CSGObject();
504
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
505
506
    object1.populate(SMap);

507
    createSurfaces(ObjB);
508
    CSGObject object2 = CSGObject();
509
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
510
511
    object2.populate(SMap);

512
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
513
514

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

518
    std::vector<Link> expectedResults;
Samuel Jones's avatar
Samuel Jones committed
519
520
521
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(-0.4, 0, 0), 4.6, object1));
    expectedResults.emplace_back(Link(V3D(-0.4, 0, 0), V3D(0.2, 0, 0), 5.2, object1));
    expectedResults.emplace_back(Link(V3D(0.2, 0, 0), V3D(1, 0, 0), 6, object2));
522
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
523
524
525
  }

  void testTrack_CubePlusInternalEdgeTouchSpheresMiss()
526
527
528
  /**
  Test a track missing an object
  */
Russell Taylor's avatar
Russell Taylor committed
529
  {
530
531
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
532

533
    createSurfaces(ObjA);
534
    CSGObject object1 = CSGObject();
535
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
536
537
    object1.populate(SMap);

538
    createSurfaces(ObjB);
539
    CSGObject object2 = CSGObject();
540
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
541
542
    object2.populate(SMap);

543
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
544
545

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

549
550
    std::vector<Link> expectedResults; // left empty as this should miss
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
551
552
  }

553
  void testComplementWithTwoPrimitives() {
554
    auto shell_ptr = ComponentCreationHelper::createHollowShell(0.5, 1.0);
555
    auto shell = dynamic_cast<CSGObject *>(shell_ptr.get());
556
557
558
559
560
561
562
563
564
565

    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);
566
    TS_ASSERT_EQUALS(2, surfPt1->getKeyN());
567
568
569
570
571
572
573
574
575
576
    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));
577
    TS_ASSERT_EQUALS(1, surfPt2->getKeyN());
578
579
580
581
582
583
584
585
586
587
588
589
    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(),
Samuel Jones's avatar
Samuel Jones committed
590
                  [&distanceInside](const Link &segment) { distanceInside += segment.distInsideObject; });
591
592
593
    TS_ASSERT_DELTA(1.0, distanceInside, 1e-10);
  }

Russell Taylor's avatar
Russell Taylor committed
594
  void testFindPointInCube()
595
596
597
  /**
  Test find point in cube
  */
Russell Taylor's avatar
Russell Taylor committed
598
  {
599
    std::shared_ptr<CSGObject> geom_obj = createUnitCube();
Russell Taylor's avatar
Russell Taylor committed
600
    // initial guess in object
601
    Kernel::V3D pt;
602
603
    TS_ASSERT_EQUALS(geom_obj->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
604
    // initial guess not in object, but on x-axis
Samuel Jones's avatar
Samuel Jones committed
605
    std::vector<std::string> planes{"px 10", "px 11", "py -0.5", "py 0.5", "pz -0.5", "pz 0.5"};
606
    std::shared_ptr<CSGObject> B = createCuboid(planes);
607
608
    TS_ASSERT_EQUALS(B->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(10, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
609
    // on y axis
610
    planes = {"px -0.5", "px 0.5", "py -22", "py -21", "pz -0.5", "pz 0.5"};
611
    std::shared_ptr<CSGObject> C = createCuboid(planes);
612
613
    TS_ASSERT_EQUALS(C->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0, -21, 0));
Russell Taylor's avatar
Russell Taylor committed
614
    // not on principle axis, now works using getBoundingBox
615
    planes = {"px 0.5", "px 1.5", "py -22", "py -21", "pz -0.5", "pz 0.5"};
616
    std::shared_ptr<CSGObject> D = createCuboid(planes);
617
618
619
620
621
622
623
624
625
626
627
628
    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
629
    // common
LamarMoore's avatar
LamarMoore committed
630
631
    planes = {"p 1 0 0 -0.5",
              "p 1 0 0 0.5",
632
633
634
635
              "p 0 .70710678118 .70710678118 -1.1",
              "p 0 .70710678118 .70710678118 -0.1",
              "p 0 -.70710678118 .70710678118 -0.5",
              "p 0 -.70710678118 .70710678118 0.5"};
636
    std::shared_ptr<CSGObject> E = createCuboid(planes);
637
638
639
640
    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);
641
642
643
    // 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.
644
    // Object is unit cube located at +-0.5 in x but centred on z=y=-1.606.. and
645
    // rotated 45deg to these two axes
LamarMoore's avatar
LamarMoore committed
646
647
    planes = {"p 1 0 0 -0.5",
              "p 1 0 0 0.5",
648
649
650
651
              "p 0  .70710678118 .70710678118 -2",
              "p 0  .70710678118 .70710678118 -1",
              "p 0 -.70710678118 .70710678118 -0.5",
              "p 0 -.70710678118 .70710678118 0.5"};
652
    std::shared_ptr<CSGObject> F = createCuboid(planes);
653
    TS_ASSERT_EQUALS(F->getPointInObject(pt), 1); // This now succeeds
654
655
    // Test use of defineBoundingBox to explictly set the bounding box, when the
    // automatic method fails
Samuel Jones's avatar
Samuel Jones committed
656
    F->defineBoundingBox(0.5, -0.5 * M_SQRT1_2, -0.5 * M_SQRT1_2, -0.5, -M_SQRT2 - 0.5 * M_SQRT1_2,
657
                         -M_SQRT2 - 0.5 * M_SQRT1_2);
658
    TS_ASSERT_EQUALS(F->getPointInObject(pt), 1);
659
    auto S = ComponentCreationHelper::createSphere(4.1);
660
661
    TS_ASSERT_EQUALS(S->getPointInObject(pt), 1);
    TS_ASSERT_EQUALS(pt, V3D(0.0, 0.0, 0));
Russell Taylor's avatar
Russell Taylor committed
662
663
  }

664
665
666
667
668
669
670
671
672
673
674
675
676
  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);
677
    constexpr size_t maxAttempts{1};
Danny Hindson's avatar
Danny Hindson committed
678
679
    boost::optional<V3D> point = shell->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
680

681
    constexpr double tolerance{1e-10};
Danny Hindson's avatar
Danny Hindson committed
682
683
684
    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);
685
686
  }

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
  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};
Samuel Jones's avatar
Samuel Jones committed
703
    auto cuboid = ComponentCreationHelper::createCuboid(xLength, yLength, zLength);
704
    constexpr size_t maxAttempts{0};
Samuel Jones's avatar
Samuel Jones committed
705
    boost::optional<V3D> point = cuboid->generatePointInObject(rng, maxAttempts);
Danny Hindson's avatar
Danny Hindson committed
706
    TS_ASSERT_EQUALS(!point, false);
707
708

    constexpr double tolerance{1e-10};
Danny Hindson's avatar
Danny Hindson committed
709
710
711
    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);
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
  }

  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.,
    };
Samuel Jones's avatar
Samuel Jones committed
735
    auto cylinder = ComponentCreationHelper::createCappedCylinder(radius, height, bottomCentre, axis, "cyl");
736
    constexpr size_t maxAttempts{0};
Samuel Jones's avatar
Samuel Jones committed
737
    boost::optional<V3D> point = cylinder->generatePointInObject(rng, maxAttempts);
Danny Hindson's avatar
Danny Hindson committed
738
    TS_ASSERT_EQUALS(!point, false);
739
    // Global->cylinder local coordinates
Danny Hindson's avatar
Danny Hindson committed
740
    *point -= bottomCentre;
741
742
743
744
    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
745
746
747
    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);
748
749
  }

750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
  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.,
    };
Samuel Jones's avatar
Samuel Jones committed
772
773
    auto hollowCylinder =
        ComponentCreationHelper::createHollowCylinder(innerRadius, radius, height, bottomCentre, axis, "hol-cyl");
774
    constexpr size_t maxAttempts{0};
Danny Hindson's avatar
Danny Hindson committed
775
776
777
    boost::optional<V3D> point;
    point = hollowCylinder->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
778
    // Global->cylinder local coordinates
Danny Hindson's avatar
Danny Hindson committed
779
    *point -= bottomCentre;
780
781
782
783
784
785
    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
786
787
788
    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);
789
790
  }

791
792
793
794
795
796
797
798
799
800
801
802
803
  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);
804
    TS_ASSERT_EQUALS(origin.totalDistInsideObject(), RADIUS);
805

Kendrick, Coleman's avatar
Kendrick, Coleman committed
806
    Track front_midpoint(V3D{0.5 * RADIUS, 0.0, 0.0}, BEAM_X);
807
    sphere->interceptSurface(front_midpoint);
Kendrick, Coleman's avatar
Kendrick, Coleman committed
808
    TS_ASSERT_EQUALS(front_midpoint.totalDistInsideObject(), 0.5 * RADIUS);
809

Kendrick, Coleman's avatar
Kendrick, Coleman committed
810
    Track back_midpoint(V3D{-0.5 * RADIUS, 0.0, 0.0}, BEAM_X);
811
    sphere->interceptSurface(back_midpoint);
Kendrick, Coleman's avatar
Kendrick, Coleman committed
812
    TS_ASSERT_EQUALS(back_midpoint.totalDistInsideObject(), 1.5 * RADIUS);
813
814
815
816

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

Kendrick, Coleman's avatar
Kendrick, Coleman committed
819
    front_midpoint = Track(V3D{0.0, 0.5 * RADIUS, 0.0}, BEAM_Y);
820
    sphere->interceptSurface(front_midpoint);
Kendrick, Coleman's avatar
Kendrick, Coleman committed
821
    TS_ASSERT_EQUALS(front_midpoint.totalDistInsideObject(), 0.5 * RADIUS);
822

Kendrick, Coleman's avatar
Kendrick, Coleman committed
823
    back_midpoint = Track(V3D{0.0, -0.5 * RADIUS, 0.0}, BEAM_Y);
824
    sphere->interceptSurface(back_midpoint);