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

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

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

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

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

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

Russell Taylor's avatar
Russell Taylor committed
34
35
using namespace Mantid;
using namespace Geometry;
36
using detail::ShapeInfo;
37
using Mantid::Kernel::V3D;
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
    expectedResults.emplace_back(Link(V3D(-1, 1.5, 1),
Nick Draper's avatar
Nick Draper committed
339
340
                                      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
    // format = startPoint, endPoint, total distance so far
351
    expectedResults.emplace_back(
352
        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
    // format = startPoint, endPoint, total distance so far
363
    expectedResults.emplace_back(
364
365
        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
    // format = startPoint, endPoint, total distance so far
Nick Draper's avatar
Nick Draper committed
372
373
    expectedResults.emplace_back(
        Link(V3D(0, -3, 0), V3D(0, 3, 0), 13, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
374

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

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

384
    // format = startPoint, endPoint, total distance so far
385
    expectedResults.emplace_back(
386
387
        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
388
389
  }

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

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

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

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

Martyn Gigg's avatar
Martyn Gigg committed
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
  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);

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

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

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

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

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

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

469
    std::vector<Link> expectedResults;
470
471
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.emplace_back(
472
473
        Link(V3D(4.5, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
474
475
476
  }

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

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

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

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

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

500
    std::vector<Link> expectedResults;
501
502
    expectedResults.emplace_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.emplace_back(
503
        Link(V3D(1, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
Russell Taylor's avatar
Russell Taylor committed
504

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

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

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

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

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

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

532
    std::vector<Link> expectedResults;
533
    expectedResults.emplace_back(
534
        Link(V3D(-1, 0, 0), V3D(-0.8, 0, 0), 4.2, object1));
535
    expectedResults.emplace_back(
536
        Link(V3D(-0.8, 0, 0), V3D(0.8, 0, 0), 5.8, object1));
Nick Draper's avatar
Nick Draper committed
537
538
    expectedResults.emplace_back(
        Link(V3D(0.8, 0, 0), V3D(1, 0, 0), 6, object2));
539
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
540
541
542
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

736
    constexpr double tolerance{1e-10};
Danny Hindson's avatar
Danny Hindson committed
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
  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
761
762
763
    boost::optional<V3D> point =
        cuboid->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
764
765

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

  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
795
796
797
    boost::optional<V3D> point =
        cylinder->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
798
    // Global->cylinder local coordinates
Danny Hindson's avatar
Danny Hindson committed
799
    *point -= bottomCentre;
800
801
802
803
    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
804
805
806
    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
  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
834
835
836
    boost::optional<V3D> point;
    point = hollowCylinder->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
837
    // Global->cylinder local coordinates
Danny Hindson's avatar
Danny Hindson committed
838
    *point -= bottomCentre;
839
840
841
842
843
844
    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
845
846
847
    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
  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
866
867
868
    boost::optional<V3D> point;
    point = sphere->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
869
870
871
872
873
874
    // 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),
Danny Hindson's avatar
Danny Hindson committed
875
                    point->X(), tolerance);
876
    TS_ASSERT_DELTA(r * std::sin(azimuthalAngle) * std::sin(polarAngle),
Danny Hindson's avatar
Danny Hindson committed
877
878
                    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
892
893
  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);
894
    constexpr size_t maxAttempts{1};
Danny Hindson's avatar
Danny Hindson committed
895
896
    boost::optional<V3D> point = shell->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, true);
897
898
899
900
901
902
903
904
  }

  void testGeneratePointInsideRespectsActiveRegion() {
    using namespace ::testing;

    // Generate "random" sequence.