CSGObjectTest.h 58.5 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"
Nick Draper's avatar
re #843    
Nick Draper committed
13
#include "MantidGeometry/Rendering/GluGeometryHandler.h"
14
#include "MantidGeometry/Objects/ShapeFactory.h"
15
#include "MantidKernel/make_unique.h"
16
#include "MantidKernel/Material.h"
17
18
#include "MantidKernel/MersenneTwister.h"
#include "MantidKernel/WarningSuppressions.h"
19
#include "MantidTestHelpers/ComponentCreationHelper.h"
20

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

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

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

Russell Taylor's avatar
Russell Taylor committed
35
36
using namespace Mantid;
using namespace Geometry;
37
using Mantid::Kernel::V3D;
Russell Taylor's avatar
Russell Taylor committed
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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));
54
55
  MOCK_CONST_METHOD0(min, double());
  MOCK_CONST_METHOD0(max, double());
56
57
58
59
  GCC_DIAG_ON_SUGGEST_OVERRIDE
};
}

60
class CSGObjectTest : public CxxTest::TestSuite {
Russell Taylor's avatar
Russell Taylor committed
61
62

public:
63
  void testDefaultObjectHasEmptyMaterial() {
64
    CSGObject obj;
65

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

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

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

82
  void testCopyConstructorGivesObjectWithSameAttributes() {
83
    auto original_ptr = ComponentCreationHelper::createSphere(1.0);
84
    auto &original = dynamic_cast<CSGObject &>(*original_ptr);
85
    original.setID("sp-1");
86
87
88
    int objType(-1);
    double radius(-1.0), height(-1.0);
    std::vector<V3D> pts;
89
    original.GetObjectGeom(objType, pts, radius, height);
Moore's avatar
Moore committed
90
    TS_ASSERT_EQUALS(3, objType);
91
    TS_ASSERT(boost::dynamic_pointer_cast<GluGeometryHandler>(
92
        original.getGeometryHandler()));
93

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

Martyn Gigg's avatar
Martyn Gigg committed
99
    TS_ASSERT_EQUALS("sp-1", copy.id());
Moore's avatar
Moore committed
100
    TS_ASSERT_EQUALS(3, objType);
101
102
    TS_ASSERT(boost::dynamic_pointer_cast<GluGeometryHandler>(
        copy.getGeometryHandler()));
103
    TS_ASSERT_EQUALS(copy.getName(), original.getName());
104
    // Check the string representation is the same
105
106
    TS_ASSERT_EQUALS(copy.str(), original.str());
    TS_ASSERT_EQUALS(copy.getSurfaceIndex(), original.getSurfaceIndex());
107
108
  }

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

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

Martyn Gigg's avatar
Martyn Gigg committed
127
    TS_ASSERT_EQUALS("sp-1", lhs.id());
Moore's avatar
Moore committed
128
    TS_ASSERT_EQUALS(3, objType);
129
130
    TS_ASSERT(boost::dynamic_pointer_cast<GluGeometryHandler>(
        lhs.getGeometryHandler()));
131
  }
Russell Taylor's avatar
Russell Taylor committed
132

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

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

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

142
143
144
145
146
147
148
149
150
151
152
  void testCloneWithMaterial() {
    using Mantid::Kernel::Material;
    auto testMaterial = Material("arm", PhysicalConstants::getNeutronAtom(13), 45.0);
    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,
      cloned_obj->material().numberDensity(), 1e-12);
  }


153
  void testIsOnSideCappedCylinder() {
154
    auto geom_obj = createCappedCylinder();
155
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
    // 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
185
186
  }

187
  void testIsValidCappedCylinder() {
188
    auto geom_obj = createCappedCylinder();
189
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
    // 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
219
220
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

356
357
358
    // 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
359

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

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

368
369
370
371
    // 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
372
373
  }

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

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

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

389
390
391
392
    // 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
393
394
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

804
805
806
807
808
809
    // 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);
810
    // Other end
811
812
    TS_ASSERT_DELTA(geom_obj->triangleSolidAngle(V3D(-1.497, 0.0, 0.0)), 0.0,
                    satol);
813

814
    // Side values
815
816
817
818
819
820
821
822
    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);
823

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

    // surface points
829
830
831
832
    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
833
834
835
  }

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

    // solid angle at distance 0.5 should be 4pi/6 by symmetry
    //
    // tests for Triangulated cube
    //
848
849
850
851
852
853
854
855
856
857
858
859
    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
860
861
  }

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

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

913
914
915
916
917
918
919
  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");
920
921
    AutoPtr<Element> shapeElement = createCylinderTypeElement(
        "cylinder-shape", height, radius, shapeDescription);
922
    typeElement->appendChild(shapeElement);
923
924
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
    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");
942
943
    AutoPtr<Element> shapeElement = createCuboidTypeElement(
        "solid-cuboid", width, height, thickness, shapeDescription);
944
945
    typeElement->appendChild(shapeElement);
    const double radius = 0.47 * std::min(std::min(width, height), thickness);
946
947
    shapeElement =
        createSphereTypeElement("void-sphere", radius, shapeDescription);
948
    typeElement->appendChild(shapeElement);
949
950
    AutoPtr<Element> algebraElement =
        shapeDescription->createElement("algebra");
951
952
953
954
955
956
957
958
    algebraElement->setAttribute("val", "solid-cuboid (# void-sphere)");
    typeElement->appendChild(algebraElement);
    ShapeFactory shapeFactory;
    auto cuboid = shapeFactory.createShape(typeElement);
    const double cuboidVolume = width * height * thickness;
    const double sphereVolume = 4.0 / 3.0 * M_PI * radius * radius * radius;
    const double correctVolume = cuboidVolume - sphereVolume;
    TS_ASSERT_DELTA(cuboid->volume(), correctVolume, 1e-3 * correctVolume)
959
960
  }

961
  void testVolumeThrowsWhenBoundingBoxIsInvalid() {
962
    CSGObject shape("This text gives an invalid Object.");
963
964
965
    TS_ASSERT_THROWS(shape.volume(), std::runtime_error);
  }

966
  void testGetBoundingBoxForCylinder()