CSGObjectTest.h 68.2 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
    boost::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, boost::shared_ptr<Surface>> SphSurMap;
    SphSurMap[41] = boost::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

mantid-builder's avatar
mantid-builder committed
328
329
    boost::shared_ptr<CSGObject> geom_obj =
        boost::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
    boost::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
    boost::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
    boost::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
    boost::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
    boost::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
    boost::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
  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
865
866
867
    boost::optional<V3D> point;
    point = sphere->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, false);
868
869
870
871
872
873
    // 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
874
                    point->X(), tolerance);
875
    TS_ASSERT_DELTA(r * std::sin(azimuthalAngle) * std::sin(polarAngle),
Danny Hindson's avatar
Danny Hindson committed
876
877
                    point->Y(), tolerance);
    TS_ASSERT_DELTA(r * std::cos(polarAngle), point->Z(), tolerance);
878
879
  }

880
881
882
883
884
885
886
887
888
889
890
891
892
  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);
893
    constexpr size_t maxAttempts{1};
Danny Hindson's avatar
Danny Hindson committed
894
895
    boost::optional<V3D> point = shell->generatePointInObject(rng, maxAttempts);
    TS_ASSERT_EQUALS(!point, true);
Martyn Gigg's avatar