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
#include "MantidGeometry/Math/Algebra.h"
7
#include "MantidGeometry/Objects/Rules.h"
LamarMoore's avatar
LamarMoore committed
8
#include "MantidGeometry/Objects/ShapeFactory.h"
9
#include "MantidGeometry/Objects/Track.h"
10
#include "MantidGeometry/Rendering/GeometryHandler.h"
11
#include "MantidGeometry/Rendering/ShapeInfo.h"
LamarMoore's avatar
LamarMoore committed
12
13
14
15
#include "MantidGeometry/Surfaces/Cylinder.h"
#include "MantidGeometry/Surfaces/Plane.h"
#include "MantidGeometry/Surfaces/Sphere.h"
#include "MantidGeometry/Surfaces/SurfaceFactory.h"
16
#include "MantidKernel/Material.h"
17
18
#include "MantidKernel/MersenneTwister.h"
#include "MantidKernel/WarningSuppressions.h"
LamarMoore's avatar
LamarMoore committed
19
#include "MantidKernel/make_unique.h"
20
#include "MantidTestHelpers/ComponentCreationHelper.h"
21

LamarMoore's avatar
LamarMoore committed
22
#include <algorithm>
23
#include <cmath>
LamarMoore's avatar
LamarMoore committed
24
25
#include <ctime>
#include <cxxtest/TestSuite.h>
26
27
28
29
#include <ostream>
#include <vector>

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

#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/Document.h>
LamarMoore's avatar
LamarMoore committed
34
#include <gmock/gmock.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
namespace {
// -----------------------------------------------------------------------------
// Mock Random Number Generator
// -----------------------------------------------------------------------------
class MockRNG final : public Mantid::Kernel::PseudoRandomNumberGenerator {
public:
Samuel Jackson's avatar
Samuel Jackson committed
47
  GNU_DIAG_OFF_SUGGEST_OVERRIDE
48
49
50
51
52
53
54
55
  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());
Samuel Jackson's avatar
Samuel Jackson committed
58
  GNU_DIAG_ON_SUGGEST_OVERRIDE
59
};
LamarMoore's avatar
LamarMoore committed
60
} // namespace
61

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
    auto geom_obj = createUnitCube();
148
149
150
    std::unique_ptr<IObject> cloned_obj;
    TS_ASSERT_THROWS_NOTHING(
        cloned_obj.reset(geom_obj->cloneWithMaterial(testMaterial)));
151
    TSM_ASSERT_DELTA("Expected a number density of 45", 45.0,
mantid-builder's avatar
mantid-builder committed
152
                     cloned_obj->material().numberDensity(), 1e-12);
153
154
  }

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

189
  void testIsValidCappedCylinder() {
190
    auto geom_obj = createCappedCylinder();
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
220
    // 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
221
222
  }

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

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

265
  void testCalcValidTypeSphere() {
266
    auto geom_obj = ComponentCreationHelper::createSphere(4.1);
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
    // 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
286
287
  }

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

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

302
  void testCalcValidTypeCappedCylinder() {
303
    auto geom_obj = createCappedCylinder();
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
    // 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
322
323
  }

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

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

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

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

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

344
    // format = startPoint, endPoint, total distance so far
Russell Taylor's avatar
Russell Taylor committed
345
    // forward only intercepts means that start point should be track origin
346
347
348
    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
349

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

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

358
359
360
    // 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
361

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

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

370
371
372
373
    // 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
374
375
  }

376
  void testInterceptSurfaceCappedCylinderY() {
377
    std::vector<Link> expectedResults;
378
    auto geom_obj = createCappedCylinder();
379
380
    // 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
381

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

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

391
392
393
394
    // 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
395
396
  }

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

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

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

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

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

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

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

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

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

456
    std::vector<Link> expectedResults;
457
458
459
460
    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
461
462
463
  }

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

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

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

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

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

487
    std::vector<Link> expectedResults;
488
489
490
    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
491

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

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

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

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

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

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

519
    std::vector<Link> expectedResults;
520
521
522
523
524
525
    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
526
527
528
  }

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

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

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

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

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

552
    std::vector<Link> expectedResults;
553
554
555
556
557
558
    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
559
560
561
  }

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

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

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

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

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

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

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

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

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

    // 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
783
784
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(8.1, 0, 0)), 0.864364,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
785
    // internal point (should be 4pi)
786
787
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(0, 0, 0)), 4 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
788
    // surface point
789
790
    TS_ASSERT_DELTA(geom_obj->rayTraceSolidAngle(V3D(4.1, 0, 0)), 2 * M_PI,
                    satol);
Russell Taylor's avatar
Russell Taylor committed
791
792
793
  }

  void testSolidAngleCappedCylinder()
794
795
796
  /**
  Test solid angle calculation for a capped cylinder
  */
Russell Taylor's avatar
Russell Taylor committed
797
  {
798
    boost::shared_ptr<CSGObject> geom_obj = createSmallCappedCylinder();
799
    // Want to test triangulation so setup a geometry handler
800
    auto h = boost::make_shared<GeometryHandler>(geom_obj);
801
802
803
804
    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));
805
    geom_obj->setGeometryHandler(h);
806

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

809
810
811
812
813
814
    // 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);
815
    // Other end
816
817
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.497, 0.0, 0.0)), 0.0,
                    satol);
818

819
    // Side values
820
821
822
823
824
825
826
827
    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);
828

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

    // surface points
834
835
836
837
    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
838
839
840
  }

  void testSolidAngleCubeTriangles()
841
842
843
844
  /**
  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
845
  {
846
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
847
    double satol = 1e-3; // tolerance for solid angle
Russell Taylor's avatar
Russell Taylor committed
848
849
850
851
852

    // solid angle at distance 0.5 should be 4pi/6 by symmetry
    //
    // tests for Triangulated cube
    //
853
854
855
856
857
858
859
860
861
862
863
864
    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
865
866
  }

867
  /** Add a scale factor */
868
  void testSolidAngleCubeTriangles_WithScaleFactor() {
869
    boost::shared_ptr<CSGObject> geom_obj = createUnitCube();
870
    double satol = 1e-3; // tolerance for solid angle
871
    // solid angle at distance 0.5 should be 4pi/6 by symmetry
872
873
874
875
    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);
876
877
  }

878
879
880
881
882
883
884
885
  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");
886
887
    AutoPtr<Element> shapeElement = createCuboidTypeElement(
        "cuboid-shape", width, height, thickness, shapeDescription);
888
    typeElement->appendChild(shapeElement);
889
890
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
891
892
893
894
895
896
897
898
899
900
901
902
903
904
    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");
905
906
    AutoPtr<Element> shapeElement =
        createSphereTypeElement("sphere-shape", radius, shapeDescription);
907
    typeElement->appendChild(shapeElement);
908
909
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
910
911
912
913
914
915
    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)
916
917
  }

918
919
920
921
922
923
924
  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");
925
926
    AutoPtr<Element> shapeElement = createCylinderTypeElement(
        "cylinder-shape", height, radius, shapeDescription);
927
    typeElement->appendChild(shapeElement);
928
929
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
930
931
932
933
934
935
936
937
    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)
  }