CSGObjectTest.h 58.8 KB
Newer Older
Karl Palmen's avatar
Karl Palmen committed
1
2
#ifndef MANTID_TESTCSGOBJECT__
#define MANTID_TESTCSGOBJECT__
Russell Taylor's avatar
Russell Taylor committed
3

4
#include "MantidGeometry/Objects/CSGObject.h"
5

6
7
8
9
10
#include "MantidGeometry/Surfaces/Cylinder.h"
#include "MantidGeometry/Surfaces/Sphere.h"
#include "MantidGeometry/Surfaces/Plane.h"
#include "MantidGeometry/Math/Algebra.h"
#include "MantidGeometry/Surfaces/SurfaceFactory.h"
11
#include "MantidGeometry/Objects/Rules.h"
12
#include "MantidGeometry/Objects/Track.h"
13
#include "MantidGeometry/Rendering/GeometryHandler.h"
14
#include "MantidGeometry/Rendering/ShapeInfo.h"
15
#include "MantidGeometry/Objects/ShapeFactory.h"
16
#include "MantidKernel/make_unique.h"
17
#include "MantidKernel/Material.h"
18
19
#include "MantidKernel/MersenneTwister.h"
#include "MantidKernel/WarningSuppressions.h"
20
#include "MantidTestHelpers/ComponentCreationHelper.h"
21

22
23
24
25
26
27
28
29
30
31
#include <cxxtest/TestSuite.h>
#include <cmath>
#include <ostream>
#include <vector>
#include <algorithm>
#include <ctime>

#include "boost/shared_ptr.hpp"
#include "boost/make_shared.hpp"

32
#include <gmock/gmock.h>
33
34
#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/Document.h>
35

Russell Taylor's avatar
Russell Taylor committed
36
37
using namespace Mantid;
using namespace Geometry;
38
using Mantid::Kernel::V3D;
39
using detail::ShapeInfo;
Russell Taylor's avatar
Russell Taylor committed
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
namespace {
// -----------------------------------------------------------------------------
// Mock Random Number Generator
// -----------------------------------------------------------------------------
class MockRNG final : public Mantid::Kernel::PseudoRandomNumberGenerator {
public:
  GCC_DIAG_OFF_SUGGEST_OVERRIDE
  MOCK_METHOD0(nextValue, double());
  MOCK_METHOD2(nextValue, double(double, double));
  MOCK_METHOD2(nextInt, int(int, int));
  MOCK_METHOD0(restart, void());
  MOCK_METHOD0(save, void());
  MOCK_METHOD0(restore, void());
  MOCK_METHOD1(setSeed, void(size_t));
  MOCK_METHOD2(setRange, void(const double, const double));
56
57
  MOCK_CONST_METHOD0(min, double());
  MOCK_CONST_METHOD0(max, double());
58
59
60
61
  GCC_DIAG_ON_SUGGEST_OVERRIDE
};
}

62
class CSGObjectTest : public CxxTest::TestSuite {
Russell Taylor's avatar
Russell Taylor committed
63
64

public:
65
  void testDefaultObjectHasEmptyMaterial() {
66
    CSGObject obj;
67

68
69
    TSM_ASSERT_DELTA("Expected a zero number density", 0.0,
                     obj.material().numberDensity(), 1e-12);
70
71
  }

72
  void testObjectSetMaterialReplacesExisting() {
73
    using Mantid::Kernel::Material;
74
    CSGObject obj;
75
76
77

    TSM_ASSERT_DELTA("Expected a zero number density", 0.0,
                     obj.material().numberDensity(), 1e-12);
78
79
    obj.setMaterial(
        Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0));
80
81
82
83
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0,
                     obj.material().numberDensity(), 1e-12);
  }

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

96
    CSGObject copy(original);
97
    // The copy should be a primitive object with a GeometryHandler
98
99
    copy.GetObjectGeom(objType, pts, radius, height);

Martyn Gigg's avatar
Martyn Gigg committed
100
    TS_ASSERT_EQUALS("sp-1", copy.id());
101
102
103
    auto handlerCopy = copy.getGeometryHandler();
    TS_ASSERT(handlerCopy->hasShapeInfo());
    TS_ASSERT_EQUALS(handler->shapeInfo(), handlerCopy->shapeInfo());
104
    TS_ASSERT_EQUALS(copy.getName(), original.getName());
105
    // Check the string representation is the same
106
107
    TS_ASSERT_EQUALS(copy.str(), original.str());
    TS_ASSERT_EQUALS(copy.getSurfaceIndex(), original.getSurfaceIndex());
108
109
  }

110
  void testAssignmentOperatorGivesObjectWithSameAttributes() {
111
    auto original_ptr = ComponentCreationHelper::createSphere(1.0);
112
    auto &original = dynamic_cast<CSGObject &>(*original_ptr);
113
    original.setID("sp-1");
114
    ShapeInfo::GeometryShape objType;
115
116
    double radius(-1.0), height(-1.0);
    std::vector<V3D> pts;
Lamar Moore's avatar
Lamar Moore committed
117
    auto handler = original.getGeometryHandler();
118
    TS_ASSERT(handler->hasShapeInfo());
119
    original.GetObjectGeom(objType, pts, radius, height);
120
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
121

122
    CSGObject lhs;  // initialize
123
    lhs = original; // assign
124
125
126
    // The copy should be a primitive object with a GluGeometryHandler
    lhs.GetObjectGeom(objType, pts, radius, height);

Martyn Gigg's avatar
Martyn Gigg committed
127
    TS_ASSERT_EQUALS("sp-1", lhs.id());
128
129
130
131
    TS_ASSERT_EQUALS(ShapeInfo::GeometryShape::SPHERE, objType);
    auto handlerCopy = lhs.getGeometryHandler();
    TS_ASSERT(handlerCopy->hasShapeInfo());
    TS_ASSERT_EQUALS(handlerCopy->shapeInfo(), handler->shapeInfo());
132
  }
Russell Taylor's avatar
Russell Taylor committed
133

134
  void testCreateUnitCube() {
135
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
136

137
    TS_ASSERT_EQUALS(geom_obj->str(), "68 1 -2 3 -4 5 -6");
138
139

    double xmin(0.0), xmax(0.0), ymin(0.0), ymax(0.0), zmin(0.0), zmax(0.0);
140
    geom_obj->getBoundingBox(xmax, ymax, zmax, xmin, ymin, zmin);
Russell Taylor's avatar
Russell Taylor committed
141
142
  }

143
144
  void testCloneWithMaterial() {
    using Mantid::Kernel::Material;
mantid-builder's avatar
mantid-builder committed
145
146
    auto testMaterial =
        Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0);
147
148
149
150
    auto geom_obj = createUnitCube();
    TS_ASSERT_THROWS_NOTHING(geom_obj->cloneWithMaterial(testMaterial));
    auto cloned_obj = geom_obj->cloneWithMaterial(testMaterial);
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0,
mantid-builder's avatar
mantid-builder committed
151
                     cloned_obj->material().numberDensity(), 1e-12);
152
153
  }

154
  void testIsOnSideCappedCylinder() {
155
    auto geom_obj = createCappedCylinder();
156
157
158
159
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
    // 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
186
187
  }

188
  void testIsValidCappedCylinder() {
189
    auto geom_obj = createCappedCylinder();
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    // 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
220
221
  }

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

243
  void testIsValidSphere() {
244
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
    // 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
262
263
  }

264
  void testCalcValidTypeSphere() {
265
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
    // entry on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(-1, 0, 0)),
                     -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(1, 0, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(-1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -4.1, 0), V3D(0, 1, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -4.1, 0), V3D(0, -1, 0)),
                     -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 4.1, 0), V3D(0, 1, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 4.1, 0), V3D(0, -1, 0)), 1);

    // a glancing blow
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0, 1, 0)), 0);
    // not quite on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-4.1, 0, 0), V3D(0.5, 0.5, 0)),
                     1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(4.1, 0, 0), V3D(0.5, 0.5, 0)),
                     -1);
Russell Taylor's avatar
Russell Taylor committed
285
286
  }

287
  void testGetBoundingBoxForSphere() {
288
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
289
290
    const double tolerance(1e-10);

291
292
293
294
295
296
297
298
    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);
299
300
  }

301
  void testCalcValidTypeCappedCylinder() {
302
    auto geom_obj = createCappedCylinder();
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
    // entry on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(-1, 0, 0)),
                     -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(1, 0, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(-1, 0, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -3, 0), V3D(0, 1, 0)), 1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, -3, 0), V3D(0, -1, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 3, 0), V3D(0, 1, 0)), -1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(0, 3, 0), V3D(0, -1, 0)), 1);

    // a glancing blow
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0, 1, 0)), 0);
    // not quite on the normal
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(-3.2, 0, 0), V3D(0.5, 0.5, 0)),
                     1);
    TS_ASSERT_EQUALS(geom_obj->calcValidType(V3D(1.2, 0, 0), V3D(0.5, 0.5, 0)),
                     -1);
Russell Taylor's avatar
Russell Taylor committed
321
322
  }

323
  void testInterceptSurfaceSphereZ() {
324
    std::vector<Link> expectedResults;
325
    std::string S41 = "s 1 1 1 4"; // Sphere at (1,1,1) radius 4
Russell Taylor's avatar
Russell Taylor committed
326
327

    // First create some surfaces
328
329
    std::map<int, boost::shared_ptr<Surface>> SphSurMap;
    SphSurMap[41] = boost::make_shared<Sphere>();
Russell Taylor's avatar
Russell Taylor committed
330
331
332
    SphSurMap[41]->setSurface(S41);
    SphSurMap[41]->setName(41);

333
    // A sphere
334
    std::string ObjSphere = "-41";
Russell Taylor's avatar
Russell Taylor committed
335

mantid-builder's avatar
mantid-builder committed
336
337
    boost::shared_ptr<CSGObject> geom_obj =
        boost::shared_ptr<CSGObject>(new CSGObject);
338
    geom_obj->setObject(41, ObjSphere);
339
    geom_obj->populate(SphSurMap);
Russell Taylor's avatar
Russell Taylor committed
340

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

343
    // format = startPoint, endPoint, total distance so far
Russell Taylor's avatar
Russell Taylor committed
344
    // forward only intercepts means that start point should be track origin
345
346
347
    expectedResults.push_back(Link(V3D(-1, 1.5, 1),
                                   V3D(sqrt(16 - 0.25) + 1, 1.5, 1.0),
                                   sqrt(15.75) + 2, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
348

349
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
350
351
  }

352
  void testInterceptSurfaceSphereY() {
353
    std::vector<Link> expectedResults;
354
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
355
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
356

357
358
359
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(
        Link(V3D(0, -4.1, 0), V3D(0, 4.1, 0), 14.1, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
360

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

364
  void testInterceptSurfaceSphereX() {
365
    std::vector<Link> expectedResults;
366
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
367
    Track track(V3D(-10, 0, 0), V3D(1, 0, 0));
368

369
370
371
372
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(
        Link(V3D(-4.1, 0, 0), V3D(4.1, 0, 0), 14.1, *geom_obj));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
373
374
  }

375
  void testInterceptSurfaceCappedCylinderY() {
376
    std::vector<Link> expectedResults;
377
    auto geom_obj = createCappedCylinder();
378
379
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(Link(V3D(0, -3, 0), V3D(0, 3, 0), 13, *geom_obj));
Russell Taylor's avatar
Russell Taylor committed
380

381
382
    Track track(V3D(0, -10, 0), V3D(0, 1, 0));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
383
384
  }

385
  void testInterceptSurfaceCappedCylinderX() {
386
    std::vector<Link> expectedResults;
387
    auto geom_obj = createCappedCylinder();
388
    Track track(V3D(-10, 0, 0), V3D(1, 0, 0));
Russell Taylor's avatar
Russell Taylor committed
389

390
391
392
393
    // format = startPoint, endPoint, total distance so far
    expectedResults.push_back(
        Link(V3D(-3.2, 0, 0), V3D(1.2, 0, 0), 11.2, *geom_obj));
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
394
395
  }

396
397
398
  void testInterceptSurfaceCappedCylinderMiss() {
    std::vector<Link>
        expectedResults; // left empty as there are no expected results
399
    auto geom_obj = createCappedCylinder();
400
    Track track(V3D(-10, 0, 0), V3D(1, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
401

402
    checkTrackIntercept(geom_obj, track, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
403
404
  }

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

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

431
  void testTrackTwoIsolatedCubes()
432
433
434
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
435
  {
436
437
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006";
    std::string ObjB = "80001 -80002 60003 -60004 60005 -60006";
438

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

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

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

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

455
    std::vector<Link> expectedResults;
456
457
458
459
    expectedResults.push_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.push_back(
        Link(V3D(4.5, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
460
461
462
  }

  void testTrackTwoTouchingCubes()
463
464
465
  /**
  Test a track going through an object
  */
Russell Taylor's avatar
Russell Taylor committed
466
  {
467
468
    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
469

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

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

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

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

486
    std::vector<Link> expectedResults;
487
488
489
    expectedResults.push_back(Link(V3D(-1, 0, 0), V3D(1, 0, 0), 6, object1));
    expectedResults.push_back(
        Link(V3D(1, 0, 0), V3D(6.5, 0, 0), 11.5, object2));
Russell Taylor's avatar
Russell Taylor committed
490

491
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
492
493
494
  }

  void testTrackCubeWithInternalSphere()
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 71";
    std::string ObjB = "-71";
Russell Taylor's avatar
Russell Taylor committed
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;
519
520
521
522
523
524
    expectedResults.push_back(
        Link(V3D(-1, 0, 0), V3D(-0.8, 0, 0), 4.2, object1));
    expectedResults.push_back(
        Link(V3D(-0.8, 0, 0), V3D(0.8, 0, 0), 5.8, object1));
    expectedResults.push_back(Link(V3D(0.8, 0, 0), V3D(1, 0, 0), 6, object2));
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
525
526
527
  }

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

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

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

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

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

551
    std::vector<Link> expectedResults;
552
553
554
555
556
557
    expectedResults.push_back(
        Link(V3D(-1, 0, 0), V3D(-0.4, 0, 0), 4.6, object1));
    expectedResults.push_back(
        Link(V3D(-0.4, 0, 0), V3D(0.2, 0, 0), 5.2, object1));
    expectedResults.push_back(Link(V3D(0.2, 0, 0), V3D(1, 0, 0), 6, object2));
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
558
559
560
  }

  void testTrack_CubePlusInternalEdgeTouchSpheresMiss()
561
562
563
  /**
  Test a track missing an object
  */
Russell Taylor's avatar
Russell Taylor committed
564
  {
565
566
    std::string ObjA = "60001 -60002 60003 -60004 60005 -60006 72 73";
    std::string ObjB = "(-72 : -73)";
567

568
    createSurfaces(ObjA);
569
    CSGObject object1 = CSGObject();
570
    object1.setObject(3, ObjA);
Russell Taylor's avatar
Russell Taylor committed
571
572
    object1.populate(SMap);

573
    createSurfaces(ObjB);
574
    CSGObject object2 = CSGObject();
575
    object2.setObject(4, ObjB);
Russell Taylor's avatar
Russell Taylor committed
576
577
    object2.populate(SMap);

578
    Track TL(Kernel::V3D(-5, 0, 0), Kernel::V3D(0, 1, 0));
Russell Taylor's avatar
Russell Taylor committed
579
580

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

584
585
    std::vector<Link> expectedResults; // left empty as this should miss
    checkTrackIntercept(TL, expectedResults);
Russell Taylor's avatar
Russell Taylor committed
586
587
  }

588
  void testComplementWithTwoPrimitives() {
589
    auto shell_ptr = ComponentCreationHelper::createHollowShell(0.5, 1.0);
590
    auto shell = dynamic_cast<CSGObject *>(shell_ptr.get());
591
592
593
594
595
596
597
598
599
600

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

701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
  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);
    size_t maxAttempts(1);
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = shell->generatePointInObject(rng, maxAttempts));

    const double tolerance(1e-10);
    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);
  }

  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);
    size_t maxAttempts(1);
    TS_ASSERT_THROWS(shell->generatePointInObject(rng, maxAttempts),
                     std::runtime_error);
  }

  void testGeneratePointInsideRespectsActiveRegion() {
    using namespace ::testing;

    // Generate "random" sequence.
    MockRNG rng;
    Sequence rand;
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.01));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.02));
    EXPECT_CALL(rng, nextValue()).InSequence(rand).WillOnce(Return(0.03));

    // Radius=0.5
    auto ball = ComponentCreationHelper::createSphere(0.5);
    // Create a thin infinite rectangular region to restrict point generation
    BoundingBox activeRegion(0.1, 0.1, 0.1, -0.1, -0.1, -0.1);
    size_t maxAttempts(1);
    V3D point;
    TS_ASSERT_THROWS_NOTHING(
        point = ball->generatePointInObject(rng, activeRegion, maxAttempts));
    const double tolerance(1e-10);
Martyn Gigg's avatar
Martyn Gigg committed
762
763
764
    TS_ASSERT_DELTA(-0.1 + 0.01 * 0.2, point.X(), tolerance);
    TS_ASSERT_DELTA(-0.1 + 0.02 * 0.2, point.Y(), tolerance);
    TS_ASSERT_DELTA(-0.1 + 0.03 * 0.2, point.Z(), tolerance);
765
766
  }

Russell Taylor's avatar
Russell Taylor committed
767
  void testSolidAngleSphere()
768
769
770
  /**
  Test solid angle calculation for a sphere
  */
Russell Taylor's avatar
Russell Taylor committed
771
  {
772
    auto geom_obj_ptr = ComponentCreationHelper::createSphere(4.1);
773
    auto geom_obj = dynamic_cast<CSGObject *>(geom_obj_ptr.get());
774
    double satol = 2e-2; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
775
776
777
778
779

    // Solid angle at distance 8.1 from centre of sphere radius 4.1 x/y/z
    // Expected solid angle calculated values from sa=2pi(1-cos(arcsin(R/r))
    // where R is sphere radius and r is distance of observer from sphere centre
    // Intercept for track in reverse direction now worked round
780
781
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(8.1, 0, 0)), 0.864364,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
782
    // internal point (should be 4pi)
783
784
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(0, 0, 0)), 4 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
785
    // surface point
786
787
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(4.1, 0, 0)), 2 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
788
789
790
  }

  void testSolidAngleCappedCylinder()
791
792
793
  /**
  Test solid angle calculation for a capped cylinder
  */
Russell Taylor's avatar
Russell Taylor committed
794
  {
795
    boost::shared_ptr<CSGObject> geom_obj = createSmallCappedCylinder();
796
    // Want to test triangulation so setup a geometry handler
797
    auto h = boost::make_shared<GeometryHandler>(geom_obj);
798
799
800
801
    detail::ShapeInfo shapeInfo;
    shapeInfo.setCylinder(V3D(-0.0015, 0.0, 0.0), V3D(1., 0.0, 0.0), 0.005,
                          0.003);
    h->setShapeInfo(std::move(shapeInfo));
802
    geom_obj->setGeometryHandler(h);
803

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

806
807
808
809
810
811
    // solid angle at point -0.5 from capped cyl -1.0 -0.997 in x, rad 0.005 -
    // approx WISH cylinder
    // We intentionally exclude the cylinder end caps so they this should
    // produce 0
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-0.5, 0.0, 0.0)), 0.0,
                    satol);
812
    // Other end
813
814
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.497, 0.0, 0.0)), 0.0,
                    satol);
815

816
    // Side values
817
818
819
820
821
822
823
824
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, 0.1)), 0.00301186,
                    satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, -0.1)), 0.00301186,
                    satol);
    // Sweep in the axis of the cylinder angle to see if the solid angle
    // decreases (as we are excluding the end caps)
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0.1, 0.0, 0.1)),
                    0.00100267, satol);
825

Russell Taylor's avatar
Russell Taylor committed
826
    // internal point (should be 4pi)
827
828
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-0.999, 0.0, 0.0)),
                    4 * M_PI, satol);
829
830

    // surface points
831
832
833
834
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.0, 0.0, 0.0)), 2 * M_PI,
                    satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-0.997, 0.0, 0.0)),
                    2 * M_PI, satol);
Russell Taylor's avatar
Russell Taylor committed
835
836
837
  }

  void testSolidAngleCubeTriangles()
838
839
840
841
  /**
  Test solid angle calculation for a cube using triangles
  - test for using Open Cascade surface triangulation for all solid angles.
  */
Russell Taylor's avatar
Russell Taylor committed
842
  {
843
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
844
    double satol = 1e-3; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
845
846
847
848
849

    // solid angle at distance 0.5 should be 4pi/6 by symmetry
    //
    // tests for Triangulated cube
    //
850
851
852
853
854
855
856
857
858
859
860
861
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(1.0, 0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.0, 0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 1.0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, -1.0, 0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, 1.0)),
                    M_PI * 2.0 / 3.0, satol);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(0, 0, -1.0)),
                    M_PI * 2.0 / 3.0, satol);
Russell Taylor's avatar
Russell Taylor committed
862
863
  }

864
  /** Add a scale factor */
865
  void testSolidAngleCubeTriangles_WithScaleFactor() {
866
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
867
    double satol = 1e-3; // tolerance for solid angle
868
    // solid angle at distance 0.5 should be 4pi/6 by symmetry
869
870
871
872
    double expected = M_PI * 2.0 / 3.0;
    V3D scaleFactor(2.0, 2.0, 2.0);
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(2.0, 0, 0), scaleFactor),
                    expected, satol);
873
874
  }

875
876
877
878
879
880
881
882
  void testExactVolumeCuboid() {
    using namespace Poco::XML;
    const double width = 1.23;
    const double height = 4.98;
    const double thickness = 8.14;
    AutoPtr<Document> shapeDescription = new Document;
    AutoPtr<Element> typeElement = shapeDescription->createElement("type");
    typeElement->setAttribute("name", "testCuboid");
883
884
    AutoPtr<Element> shapeElement = createCuboidTypeElement(
        "cuboid-shape", width, height, thickness, shapeDescription);
885
    typeElement->appendChild(shapeElement);
886
887
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
888
889
890
891
892
893
894
895
896
897
898
899
900
901
    algebraElement->setAttribute("val", "cuboid-shape");
    typeElement->appendChild(algebraElement);
    ShapeFactory shapeFactory;
    auto cuboid = shapeFactory.createShape(typeElement);
    const double cuboidVolume = width * height * thickness;
    TS_ASSERT_DELTA(cuboid->volume(), cuboidVolume, 1e-6)
  }

  void testExactVolumeSphere() {
    using namespace Poco::XML;
    const double radius = 99.9;
    AutoPtr<Document> shapeDescription = new Document;
    AutoPtr<Element> typeElement = shapeDescription->createElement("type");
    typeElement->setAttribute("name", "testSphere");
902
903
    AutoPtr<Element> shapeElement =
        createSphereTypeElement("sphere-shape", radius, shapeDescription);
904
    typeElement->appendChild(shapeElement);
905
906
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
907
908
909
910
911
912
    algebraElement->setAttribute("val", "sphere-shape");
    typeElement->appendChild(algebraElement);
    ShapeFactory shapeFactory;
    auto cuboid = shapeFactory.createShape(typeElement);
    const double sphereVolume = 4.0 / 3.0 * M_PI * radius * radius * radius;
    TS_ASSERT_DELTA(cuboid->volume(), sphereVolume, 1e-6)
913
914
  }

915
916
917
918
919
920
921
  void testExactVolumeCylinder() {
    using namespace Poco::XML;
    const double radius = 0.99;
    const double height = 88;
    AutoPtr<Document> shapeDescription = new Document;
    AutoPtr<Element> typeElement = shapeDescription->createElement("type");
    typeElement->setAttribute("name", "testCylinder");
922
923
    AutoPtr<Element> shapeElement = createCylinderTypeElement(
        "cylinder-shape", height, radius, shapeDescription);
924
    typeElement->appendChild(shapeElement);
925
926
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
    algebraElement->setAttribute("val", "cylinder-shape");
    typeElement->appendChild(algebraElement);
    ShapeFactory shapeFactory;
    auto cuboid = shapeFactory.createShape(typeElement);
    const double cylinderVolume = height * M_PI * radius * radius;
    TS_ASSERT_DELTA(cuboid->volume(), cylinderVolume, 1e-6)
  }

  void testMonteCarloVolume() {
    // We use a cuboid with spherical void here.
    using namespace Poco::XML;
    const double width = 71.99;
    const double height = 11.87;
    const double thickness = 74.1;
    AutoPtr<Document> shapeDescription = new Document;
    AutoPtr<Element> typeElement = shapeDescription->createElement("type");
    typeElement->setAttribute("name", "testShape");
944
945
    AutoPtr<Element> shapeElement = createCuboidTypeElement(
        "solid-cuboid", width, height, thickness, shapeDescription);
946
947
    typeElement->appendChild(shapeElement);
    const double radius = 0.47 * std::min(std::min(width, height), thickness);
948
949
    shapeElement =
        createSphereTypeElement("void-sphere", radius, shapeDescription);
950
    typeElement->appendChild(shapeElement);
951
952
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
953
954
955
956
957
958
959