Newer
Older
#include "MantidDataHandling/LoadSampleShape.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/InstrumentValidator.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/MatrixWorkspace_fwd.h"
#include "MantidGeometry/Instrument.h"
#include "MantidKernel/CompositeValidator.h"
#include "MantidKernel/EnabledWhenProperty.h"
#include "MantidKernel/Exception.h"
#include <Poco/File.h>
namespace Mantid {
namespace DataHandling {
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Register the algorithm into the algorithm factory
DECLARE_ALGORITHM(LoadSampleShape)
using namespace Kernel;
using namespace API;
using namespace Geometry;
namespace {
bool areEqualVertices(Kernel::V3D const &v1, Kernel::V3D const &v2) {
Kernel::V3D diff = v1 - v2;
return diff.norm() < 1e-9;
}
// Read, check and ignore line in STL file. Return true if line is read
bool readSTLLine(std::ifstream &file, std::string const &type) {
std::string line;
if (getline(file, line)) {
boost::trim(line);
if (line.size() < type.size() || line.substr(0, type.size()) != type) {
// Before throwing, check for endsolid statment
std::string type2 = "endsolid";
if (line.size() < type2.size() || line.substr(0, type2.size()) != type2) {
throw std::runtime_error("Expected STL line begining with " + type +
" or " + type2);
}
else {
return false; // ends reading at endsolid
}
}
return true; // expected line read, then ignored
}
else {
return false; // end of file
}
}
/* Reads vertex from STL file and returns true if vertex is found */
bool readSTLVertex(std::ifstream &file, V3D &vertex) {
std::string line;
if (getline(file, line)) {
boost::trim(line);
std::vector<std::string> tokens;
boost::split(tokens, line, boost::is_any_of(" "), boost::token_compress_on);
if (tokens.size() == 4 && tokens[0] == "vertex") {
vertex.setX(boost::lexical_cast<double>(tokens[1]));
vertex.setY(boost::lexical_cast<double>(tokens[2]));
vertex.setZ(boost::lexical_cast<double>(tokens[3]));
return true;
}
else {
throw std::runtime_error("Error on reading STL vertex");
}
}
return false;
}
/* Reads triangle for STL file and returns true if triangle is found */
bool readSTLTriangle(std::ifstream &file, V3D &v1, V3D &v2,
V3D &v3) {
if (readSTLLine(file, "facet") && readSTLLine(file, "outer loop")) {
bool ok = (readSTLVertex(file, v1) && readSTLVertex(file, v2) &&
readSTLVertex(file, v3));
if (!ok) {
throw std::runtime_error("Error on reading STL triangle");
}
}
else {
return false; // End of file
}
return readSTLLine(file, "endloop") && readSTLLine(file, "endfacet");
}
// Adds vertex to list if distinct and returns index to vertex added or equal
uint16_t addSTLVertex(V3D &vertex, std::vector<V3D> &vertices) {
for (uint16_t i = 0; i < vertices.size(); ++i) {
if (areEqualVertices(vertex, vertices[i])) {
return i;
}
}
vertices.push_back(vertex);
uint16_t index = static_cast<uint16_t>(vertices.size() - 1);
if (index != vertices.size() - 1) {
throw std::runtime_error("Too many vertices in solid");
}
return index;
}
std::unique_ptr<MeshObject> readSTLMeshObject(std::ifstream &file) {
std::vector<uint16_t> triangleIndices;
std::vector<V3D> vertices;
V3D t1, t2, t3;
while (readSTLTriangle(file, t1, t2, t3)) {
// Add triangle if all 3 vertices are distinct
if (!areEqualVertices(t1, t2) && !areEqualVertices(t1, t3) &&
!areEqualVertices(t2, t3)) {
triangleIndices.push_back(addSTLVertex(t1, vertices));
triangleIndices.push_back(addSTLVertex(t2, vertices));
triangleIndices.push_back(addSTLVertex(t3, vertices));
}
}
// Use efficient constructor of MeshObject
std::unique_ptr<MeshObject> retVal = std::unique_ptr<MeshObject>(
new MeshObject(std::move(triangleIndices), std::move(vertices),
Mantid::Kernel::Material()));
return retVal;
}
std::unique_ptr<Geometry::MeshObject> readSTLSolid(std::ifstream &file, std::string &name) {
// Read Solid name
// We expect line after trimming to be "solid "+name.
std::string line;
if (getline(file, line)) {
boost::trim(line);
if (line.size() < 5 || line.substr(0, 5) != "solid") {
throw std::runtime_error("Expected start of solid");
}
else {
name = line.substr(6, std::string::npos);
}
// Read Solid shape
return readSTLMeshObject(file);
}
return nullptr;
}
} // end anonymous namespace
void LoadSampleShape::init() {
auto wsValidator = boost::make_shared<API::InstrumentValidator>();
;
// input workspace
declareProperty(
make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input,
wsValidator),
"The name of the workspace containing the instrument to add the shape");
// shape file
const std::vector<std::string> extensions{".stl"};
make_unique<FileProperty>("Filename", "", FileProperty::Load, extensions),
"The path name of the file containing the shape.");
declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"The name of the workspace that provides the"
"sample whose shape is being loaded");
/**
* Return the confidence with with this algorithm can load the file
* @param descriptor A descriptor for the file
* @returns An integer specifying the confidence level. 0 indicates it will not
* be used
*/
int LoadSampleShape::confidence(Kernel::FileDescriptor &descriptor) const {
const std::string &filePath = descriptor.filename();
const size_t filenameLength = filePath.size();
// Avoid some known file types that have different loaders
int confidence(0);
if (filePath.compare(filenameLength - 4, 4, ".stl") == 0) {
confidence = 90;
}
return confidence;
}
void LoadSampleShape::exec() {
MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
if (inputWS != outputWS) {
outputWS = inputWS->clone();
}
std::string filename = getProperty("Filename");
std::ifstream file(filename.c_str());
if (!file) {
g_log.error("Unable to open file: " + filename);
throw Exception::FileError("Unable to open file: ", filename);
}
std::string solidName = "";
boost::shared_ptr<MeshObject> shape = nullptr;
} catch (std::exception &) {
throw Exception::FileError(
"Failed to recognize this file as a valid STL file: ", filename);
Sample &sample = outputWS->mutableSample();
// Set output workspace
setProperty("OutputWorkspace", outputWS);
} // end DataHandling namespace
} // end MantidNamespace