19#include "Poco/DOM/AutoPtr.h"
20#include "Poco/DOM/DOMParser.h"
21#include "Poco/DOM/DOMWriter.h"
22#include "Poco/DOM/Document.h"
23#include "Poco/DOM/NamedNodeMap.h"
24#include "Poco/DOM/NodeFilter.h"
25#include "Poco/DOM/NodeIterator.h"
28#include "Poco/SAX/InputSource.h"
29#include "Poco/SAX/SAXException.h"
31#include <boost/algorithm/string.hpp>
41std::string MATERIALS_TAG =
"materials";
42std::string COMPONENTS_TAG =
"components";
43std::string FULL_SPEC_TAG =
"fullspecification";
44std::string COMPONENT_TAG =
"component";
45std::string CONTAINERS_TAG =
"containers";
46std::string CONTAINER_TAG =
"container";
47std::string COMPONENTGEOMETRY_TAG =
"geometry";
48std::string SAMPLEGEOMETRY_TAG =
"samplegeometry";
49std::string COMPONENTSTLFILE_TAG =
"stlfile";
50std::string SAMPLESTLFILE_TAG =
"samplestlfile";
57std::string MATERIALS_TAG =
"materials";
58std::string COMPONENTS_TAG =
"components";
59std::string COMPONENT_TAG =
"component";
60std::string GLOBAL_OFFSET_TAG =
"globaloffset";
61std::string TRANSLATION_VECTOR_TAG =
"translationvector";
62std::string STL_FILENAME_TAG =
"stlfilename";
63std::string SCALE_TAG =
"scale";
64std::string XDEGREES_TAG =
"xdegrees";
65std::string YDEGREES_TAG =
"ydegrees";
66std::string ZDEGREES_TAG =
"zdegrees";
70double DegreesToRadians(
double angle) {
return angle * M_PI / 180; }
86 using DocumentPtr = AutoPtr<Document>;
88 InputSource src(istr);
94 doc = parser.parse(&src);
95 }
catch (SAXParseException &exc) {
96 std::ostringstream msg;
97 msg <<
"SampleEnvironmentSpecParser::parse() - Error parsing content "
100 throw std::runtime_error(msg.str());
103 return parse(name, doc->documentElement());
118 NodeIterator nodeIter(element, NodeFilter::SHOW_ELEMENT);
119 Node *node = nodeIter.nextNode();
120 auto spec = std::make_unique<SampleEnvironmentSpec>(name);
123 auto *childElement =
static_cast<Element *
>(node);
124 if (node->nodeName() == MATERIALS_TAG) {
126 }
else if (node->nodeName() == COMPONENTS_TAG) {
128 }
else if (node->nodeName() == FULL_SPEC_TAG) {
131 node = nodeIter.nextNode();
144 if (element->nodeName() !=
ROOT_TAG) {
145 std::ostringstream msg;
146 msg <<
"SampleEnvironmentSpecParser::validateRootElement() - Element tag "
148 <<
ROOT_TAG <<
"'. Found " << element->nodeName() <<
"\n";
149 throw std::invalid_argument(msg.str());
161 NodeIterator nodeIter(element, NodeFilter::SHOW_ELEMENT);
165 Node *node = nodeIter.nextNode();
168 auto material = parser.
parse(
static_cast<Poco::XML::Element *
>(node),
m_filepath);
170 node = nodeIter.nextNode();
182 throw std::runtime_error(
"SampleEnvironmentSpecParser::parseComponents() - "
183 "Trying to parse list of components but no "
184 "materials have been found. Please ensure the "
185 "materials are defined first.");
187 NodeIterator nodeIter(element, NodeFilter::SHOW_ELEMENT);
191 Node *node = nodeIter.nextNode();
193 auto *childElement =
static_cast<Element *
>(node);
194 const auto &nodeName = childElement->nodeName();
195 if (nodeName == CONTAINERS_TAG) {
197 }
else if (nodeName == COMPONENT_TAG) {
200 node = nodeIter.nextNode();
206 auto filename = element->getAttribute(
"filename");
207 if (!filename.empty()) {
209 std::string stlFileName =
findFile(filename);
211 Poco::Path suppliedFileName(stlFileName);
212 std::string fileExt = suppliedFileName.getExtension();
213 std::transform(fileExt.begin(), fileExt.end(), fileExt.begin(), toupper);
215 std::vector<std::shared_ptr<Geometry::MeshObject>> environmentMeshes;
216 std::shared_ptr<Geometry::MeshObject> sampleMesh;
218 if (fileExt ==
"3MF") {
225 throw std::runtime_error(
"3MF format not supported on this platform");
228 for (
auto cpt : environmentMeshes) {
229 if (spec->
ncans() == 0) {
232 cpt->setID(
"default");
233 auto can = std::make_shared<Container>(cpt);
234 can->setSampleShape(sampleMesh);
242 throw std::runtime_error(
"Full specification must be a .3mf file");
245 throw std::runtime_error(
"fullspecification element supplied without a filename");
256 NodeIterator nodeIter(element, NodeFilter::SHOW_ELEMENT);
258 Node *node = nodeIter.nextNode();
260 auto *childElement =
static_cast<Element *
>(node);
261 if (childElement->nodeName() == CONTAINER_TAG) {
264 node = nodeIter.nextNode();
276 auto sampleGeometry = element->getChildElement(SAMPLEGEOMETRY_TAG);
277 auto sampleSTLFile = element->getChildElement(SAMPLESTLFILE_TAG);
279 if ((sampleGeometry) && (sampleSTLFile)) {
280 throw std::runtime_error(
"SampleEnvironmentSpecParser::parseComponent() - "
281 "Cannot define sample using both a" +
282 SAMPLEGEOMETRY_TAG +
" and a " + SAMPLESTLFILE_TAG +
" child tag.");
285 if (sampleGeometry) {
287 std::stringstream sampleShapeXML;
288 writer.writeNode(sampleShapeXML, sampleGeometry);
289 can->setSampleShape(sampleShapeXML.str());
304 const std::string &attributeName,
305 double &targetVariable)
const {
307 auto attributeText = componentElement->getAttribute(attributeName);
308 if (!attributeText.empty()) {
310 targetVariable = std::stod(attributeText);
311 }
catch (std::invalid_argument &ex) {
312 throw std::invalid_argument(std::string(
"Invalid string supplied for " + attributeName +
" ") + ex.what());
324 std::vector<double> translationVector;
330 translationVector.clear();
331 translationVector.reserve(values.
count());
333 std::transform(values.
cbegin(), values.
cend(), std::back_inserter(translationVector),
334 [](
const std::string &str) { return boost::lexical_cast<double>(str); });
335 return translationVector;
339 Poco::Path suppliedStlFileName(filename);
340 Poco::Path stlFileName;
341 if (suppliedStlFileName.isRelative()) {
342 bool useSearchDirectories =
true;
346 stlFileName = Poco::Path(Poco::Path(
m_filepath).parent(), filename);
347 if (Poco::File(stlFileName).
exists()) {
348 useSearchDirectories =
false;
352 if (useSearchDirectories) {
355 if (!foundFile.empty()) {
356 stlFileName = Poco::Path(foundFile);
358 stlFileName = suppliedStlFileName;
362 stlFileName = suppliedStlFileName;
364 return stlFileName.toString();
374 std::string filename = stlFileElement->getAttribute(
"filename");
375 if (!filename.empty()) {
377 std::string stlFileName =
findFile(filename);
379 if (Poco::File(stlFileName).
exists()) {
381 std::string scaleStr = stlFileElement->getAttribute(
"scale");
382 if (scaleStr.empty()) {
383 throw std::runtime_error(
"Scale must be supplied for stl file:" + filename);
389 std::shared_ptr<Geometry::MeshObject> comp = reader->readShape();
391 Element *
rotation = stlFileElement->getChildElement(
"rotation");
394 double xDegrees = 0, yDegrees = 0, zDegrees = 0;
399 const double xRotation = DegreesToRadians(xDegrees);
400 const double yRotation = DegreesToRadians(yDegrees);
401 const double zRotation = DegreesToRadians(zDegrees);
402 comp = reader->rotate(comp, xRotation, yRotation, zRotation);
404 Element *translation = stlFileElement->getChildElement(
"translation");
406 std::string translationVectorStr = translation->getAttribute(
"vector");
408 comp = reader->translate(comp, translationVector);
412 throw std::runtime_error(
"Unable to find STLFile " + filename);
415 throw std::runtime_error(
"STLFile element supplied without a filename");
426 Element *geometry = element->getChildElement(COMPONENTGEOMETRY_TAG);
427 Element *stlfile = element->getChildElement(COMPONENTSTLFILE_TAG);
428 if ((!geometry) && (!stlfile)) {
429 throw std::runtime_error(
"SampleEnvironmentSpecParser::parseComponent() - Expected a " + COMPONENTGEOMETRY_TAG +
430 " or " + COMPONENTSTLFILE_TAG +
" child tag. None found.");
432 if ((geometry) && (stlfile)) {
433 throw std::runtime_error(
"SampleEnvironmentSpecParser::parseComponent() - "
434 "Cannot define container using both a" +
435 COMPONENTGEOMETRY_TAG +
" and a " + COMPONENTSTLFILE_TAG +
" child tag.");
438 std::shared_ptr<Geometry::IObject> comp;
445 auto materialID = element->getAttribute(
"material");
451 throw std::runtime_error(
"SampleEnvironmentSpecParser::parseComponent() - "
452 "Unable to find material with id=" +
455 comp->setID(element->getAttribute(
"id"));
456 comp->setMaterial(mat);
Mantid::Kernel::Quat(ComponentInfo::* rotation)(const size_t) const
static std::unique_ptr< LoadStl > createReader(const std::string &filename, ScaleUnits scaleType)
Class to load and save .3mf files .3mf format is a 3D manufacturing format for storing mesh descripti...
void readMeshObjects(std::vector< MeshObject_sptr > &meshObjects, MeshObject_sptr &sample)
Read a set of mesh objects from the in memory lib3mf model object.
void LoadFile(std::string filename)
Load 3MF format file.
std::shared_ptr< Geometry::IObject > parseComponent(Poco::XML::Element *element) const
Parse a single definition of a component.
Geometry::Container_const_sptr parseContainer(Poco::XML::Element *element) const
Parse a single definition of a Can.
std::string findFile(const std::string &filename) const
void parseAndAddComponents(SampleEnvironmentSpec *spec, Poco::XML::Element *element) const
Take a <components> tag, parse the definitions and add them to the spec.
void parseMaterials(Poco::XML::Element *element)
Parse the set of materials in the document.
std::shared_ptr< Geometry::MeshObject > loadMeshFromSTL(Poco::XML::Element *stlFileElement) const
Create a mesh shape from an STL input file.
void parseAndAddContainers(SampleEnvironmentSpec *spec, Poco::XML::Element *element) const
Take a <containers> tag, parse the definitions and add them to the spec.
static constexpr const char * ROOT_TAG
SampleEnvironmentSpec_uptr parse(const std::string &name, const std::string &filename, std::istream &istr)
Takes a stream that is assumed to contain a single complete SampleEnvironmentSpec definition,...
void loadFullSpecification(SampleEnvironmentSpec *spec, Poco::XML::Element *element)
void validateRootElement(Poco::XML::Element *element) const
Validate that the element points to the expected root element.
std::vector< double > parseTranslationVector(const std::string &translationVectorStr) const
Take a comma separated translation vector and return it as a std::vector.
void LoadOptionalDoubleFromXML(Poco::XML::Element *componentElement, const std::string &elementName, double &targetVariable) const
Load a double from an optional XML element.
MaterialsIndex m_materials
Defines the properties of a named SampleEnvironment setup.
void addContainer(const Geometry::Container_const_sptr &can)
Adds a can definition to the known list.
void addComponent(const Geometry::IObject_const_sptr &component)
Add a non-can component to the specification.
Models a Container is used to hold a sample in the beam.
Class originally intended to be used with the DataHandling 'LoadInstrument' algorithm.
std::shared_ptr< CSGObject > createShape(Poco::XML::Element *pElem)
Creates a geometric object from a DOM-element-node pointing to an element whose child nodes contain t...
Read an XML definition of a Material and produce a new Material object.
Material parse(std::istream &istr) const
Takes a stream that is assumed to contain a single complete material definition, reads the definition...
A material is defined as being composed of a given element, defined as a PhysicalConstants::NeutronAt...
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
@ TOK_IGNORE_EMPTY
ignore empty tokens
@ TOK_TRIM
remove leading and trailing whitespace from tokens
ConstIterator cend() const
Const iterator referring to the past-the-end element in the container.
ConstIterator cbegin() const
Const iterator referring to first element in the container.
std::size_t count() const
Get the total number of tokens.
std::unique_ptr< SampleEnvironmentSpec > SampleEnvironmentSpec_uptr
unique_ptr to a SampleEnvironmentSpec
bool exists(::NeXus::File &file, const std::string &name)
Based on the current group in the file, does the named sub-entry exist?
ScaleUnits getScaleTypeFromStr(const std::string &scaleProperty)
std::shared_ptr< const Container > Container_const_sptr
Typdef for a shared pointer to a const object.