#include "MantidGeometry/Instrument/CompAssembly.h"
#include "MantidGeometry/Instrument/RectangularDetector.h"
#include "MantidGeometry/Instrument/ParComponentFactory.h"
#include "MantidGeometry/IObjComponent.h"
#include "MantidGeometry/Objects/BoundingBox.h"
#include <algorithm>
#include <ostream>
#include <stdexcept>
namespace Mantid
{
namespace Geometry
{
using Kernel::V3D;
using Kernel::Quat;
/** Empty constructor
*/
CompAssembly::CompAssembly() : Component(), m_children(), m_cachedBoundingBox(NULL)
{
}
/** Constructor for a parametrized CompAssembly
* @param base: the base (un-parametrized) IComponent
* @param map: pointer to the ParameterMap
* */
CompAssembly::CompAssembly(const IComponent* base, const ParameterMap * map)
: Component(base,map), m_children(), m_cachedBoundingBox(NULL)
{
}
/** Valued constructor
* @param n :: name of the assembly
* @param reference :: the parent Component
*
* If the reference is an object of class Component,
* normal parenting apply. If the reference object is
* an assembly itself, then in addition to parenting
* this is registered as a children of reference.
*/
CompAssembly::CompAssembly(const std::string& n, IComponent* reference) :
Component(n, reference), m_children(), m_cachedBoundingBox(NULL)
{
if (reference)
{
ICompAssembly* test=dynamic_cast<ICompAssembly*>(reference);
if (test)
{
test->add(this);
}
}
}
/** Copy constructor
* @param assem :: assembly to copy
*/
CompAssembly::CompAssembly(const CompAssembly& assem) :
Component(assem), m_children(assem.m_children), m_cachedBoundingBox(assem.m_cachedBoundingBox)
{
// Need to do a deep copy
comp_it it;
for (it = m_children.begin(); it != m_children.end(); ++it)
{
*it = (*it)->clone() ;
// Move copied component object's parent from old to new CompAssembly
(*it)->setParent(this);
}
}
/** Destructor
*/
CompAssembly::~CompAssembly()
{
if( m_cachedBoundingBox ) delete m_cachedBoundingBox;
// Iterate over pointers in m_children, deleting them
for (comp_it it = m_children.begin(); it != m_children.end(); ++it)
{
delete *it;
}
m_children.clear();
}
/** Clone method
* Make a copy of the component assembly
* @return new(*this)
*/
IComponent* CompAssembly::clone() const
{
return new CompAssembly(*this);
}
/** Add method
* @param comp :: component to add
* @return number of components in the assembly
*
* This becomes the new parent of comp.
*/
int CompAssembly::add(IComponent* comp)
{
if (m_map)
throw std::runtime_error("CompAssembly::add() called for a parametrized CompAssembly.");
if (comp)
{
comp->setParent(this);
m_children.push_back(comp);
}
return static_cast<int>(m_children.size());
}
/** AddCopy method
* @param comp :: component to add
* @return number of components in the assembly
*
* Add a copy of a component in the assembly.
* Comp is cloned if valid, then added in the assembly
* This becomes the parent of the cloned component
*/
int CompAssembly::addCopy(IComponent* comp)
{
if (m_map)
throw std::runtime_error("CompAssembly::addCopy() called for a parametrized CompAssembly.");
if (comp)
{
IComponent* newcomp=comp->clone();
newcomp->setParent(this);
m_children.push_back(newcomp);
}
return static_cast<int>(m_children.size());
}
/** AddCopy method
* @param comp :: component to add
* @param n :: name of the copied component.
* @return number of components in the assembly
*
* Add a copy of a component in the assembly.
* Comp is cloned if valid, then added in the assembly
* This becomes the parent of the cloned component
*/
int CompAssembly::addCopy(IComponent* comp, const std::string& n)
{
if (m_map)
throw std::runtime_error("CompAssembly::addCopy() called for a parametrized CompAssembly.");
if (comp)
{
IComponent* newcomp=comp->clone();
newcomp->setParent(this);
newcomp->setName(n);
m_children.push_back(newcomp);
}
return static_cast<int>(m_children.size());
}
/** Removes a component from the assembly. The component will be deleted.
* @param comp The component to be removed
* @return The (new) number of components in the assembly
* @throws std::runtime_error if the component is not a child of this assembly
*/
int CompAssembly::remove(IComponent* comp)
{
if (m_map)
throw std::runtime_error("CompAssembly::remove() called for a parameterized CompAssembly.");
// Look for the passed in component in the list of children
std::vector<IComponent*>::iterator it = std::find(m_children.begin(),m_children.end(),comp);
if ( it != m_children.end() )
{
// If it's found, remove it from the list and then delete it
m_children.erase(it);
delete comp;
}
else
{
throw std::runtime_error("Component " + comp->getName() + " is not a child of this assembly.");
}
return static_cast<int>(m_children.size());
}
/** Return the number of components in the assembly
* @return m_children.size()
*/
int CompAssembly::nelements() const
{
if (m_map)
return dynamic_cast<const CompAssembly*>(m_base)->nelements();
else
return static_cast<int>(m_children.size());
}
/** Get a pointer to the ith component in the assembly. Note standard C/C++
* array notation used, that is, i most be an integer i = 0,1,..., N-1, where
* N is the number of component in the assembly. Easier to use than the [] operator
* when you have a pointer
*
* @param i :: The index of the component you want
* @return m_children[i]
* @throw std::runtime_error if i is not in range
*/
boost::shared_ptr<IComponent> CompAssembly::getChild(const int i) const
{
if (m_map)
{
//Get the child of the base (unparametrized) assembly
boost::shared_ptr<IComponent> child_base = dynamic_cast<const CompAssembly*>(m_base)->getChild(i);
//And build up a parametrized version of it using the factory, and return that
return ParComponentFactory::create(child_base,m_map);
}
else
{
if( i < 0 || i > static_cast<int>(m_children.size()-1) )
{
throw std::runtime_error("CompAssembly::getChild() range not valid");
}
return boost::shared_ptr<IComponent>(m_children[i],NoDeleting());
}
}
/** Overloaded index access operator. \link getChild() \endlink
* @param i :: Element i within the component assembly
* @return A shared pointer to the ith component
* @throw std:runtime_error if i is out of range
*/
boost::shared_ptr<IComponent> CompAssembly::operator[](int i) const
{
return this->getChild(i);
}
//------------------------------------------------------------------------------------------------
/** Return a vector of all contained children components
*
* @param outVector :: vector of IComponent sptr.
* @param recursive :: if a child is a CompAssembly, returns its children recursively
*/
void CompAssembly::getChildren(std::vector<IComponent_const_sptr> & outVector, bool recursive) const
{
for (int i=0; i < this->nelements(); i++)
{
boost::shared_ptr<IComponent> comp = this->getChild(i);
if (comp)
{
outVector.push_back( comp );
// Look deeper, on option.
if (recursive)
{
boost::shared_ptr<ICompAssembly> assemb = boost::dynamic_pointer_cast<ICompAssembly>(comp);
if (assemb)
assemb->getChildren(outVector, recursive);
}
}
}
}
/**
* Find a component by name.
* @param cname :: The name of the component. If there are multiple matches, the first one found is returned.
* If the name contains '/', it will search the the component whose name occurs before the '/'
* then within that component's assembly,
* search the component whose name occurs after the '/' and so on with any subsequent '/'.
* For example to find 'tube020' in 'panel07', one could use the cname 'panel07/tube020',
* given that tube020 is unique within panel07.
* @param nlevels :: Optional argument to limit number of levels searched.
* If cname has a '/', then nlevels will apply to each step delimited by the '/'s, rather than the whole search.
* In particular, nlevels=1, would force cname to be a full path name.
* @returns A shared pointer to the component
*/
boost::shared_ptr<const IComponent> CompAssembly::getComponentByName(const std::string & cname, int nlevels) const
{
boost::shared_ptr<const IComponent> node = boost::shared_ptr<const IComponent>(this, NoDeleting());
// If name has '/' in it, it is taken as part of a path name of the component.
// Steps may be skipped at a '/' from the path name,
// but if what follows the skip is not unique within what precedes it, only the first found is returned.
size_t cut = cname.find('/');
if ( cut < cname.length() ) {
node = this->getComponentByName( cname.substr(0,cut), nlevels);
if(node) {
boost::shared_ptr<const ICompAssembly> asmb = boost::dynamic_pointer_cast<const ICompAssembly>(node);
return asmb->getComponentByName( cname.substr(cut+1,std::string::npos), nlevels );
} else {
return boost::shared_ptr<const IComponent>(); // Search failed
}
}
// Check the instrument name first
if( this->getName() == cname )
{
return node;
}
// Otherwise Search the instrument tree using a breadth-first search algorithm since most likely candidates
// are higher-level components
// I found some useful info here http://www.cs.bu.edu/teaching/c/tree/breadth-first/
std::deque<boost::shared_ptr<const IComponent> > nodeQueue;
// Need to be able to enter the while loop
nodeQueue.push_back(node);
const bool limitSearch(nlevels > 0);
while( !nodeQueue.empty() )
{
// get the next node in the queue
node = nodeQueue.front();
nodeQueue.pop_front();
// determine the depth
int depth(1);
if (limitSearch)
{
auto parent = node->getParent();
while (parent && (this->getName() != parent->getName()))
{
parent = parent->getParent();
depth++;
}
}
int nchildren(0);
boost::shared_ptr<const ICompAssembly> asmb = boost::dynamic_pointer_cast<const ICompAssembly>(node);
if( asmb )
{
nchildren = asmb->nelements();
}
for( int i = 0; i < nchildren; ++i )
{
boost::shared_ptr<const IComponent> comp = (*asmb)[i];
if( comp->getName() == cname )
{
return comp;
}
else
{
// only add things if max-recursion depth hasn't been reached
if ((!limitSearch) || (depth+1 < nlevels))
{
// don't bother adding things to the queue that aren't assemblies
if (bool(boost::dynamic_pointer_cast<const ICompAssembly>(comp)))
{
// for rectangular detectors search the depth rather than siblings as there
// is a specific naming convention to speed things along
auto rectDet = boost::dynamic_pointer_cast<const RectangularDetector>(comp);
if (bool(rectDet))
{
auto child = rectDet->getComponentByName(cname, nlevels);
if (child)
return child;
}
else
{
nodeQueue.push_back(comp);
}
}
}
}
}
}// while-end
// If we have reached here then the search failed
return boost::shared_ptr<const IComponent>();
}
//------------------------------------------------------------------------------------------------
/**
* Get the bounding box for this assembly. It is simply the sum of the bounding boxes of its children
* @param assemblyBox :: [Out] The resulting bounding box is stored here.
*/
void CompAssembly::getBoundingBox(BoundingBox & assemblyBox) const
{
if (m_map)
{
// Check cache for assembly, inside the ParameterMap
if( m_map->getCachedBoundingBox(this, assemblyBox ) )
{
return;
}
// Loop over the children and define a box large enough for all of them
assemblyBox = BoundingBox(); // this makes assembly box always axis alighned
int nchildren = nelements();
for(int i = 0; i < nchildren; ++i)
{
IComponent_sptr comp = this->operator[](i);
BoundingBox compBox;
comp->getBoundingBox(compBox);
assemblyBox.grow(compBox);
}
//Set the cache
m_map->setCachedBoundingBox(this, assemblyBox);
}
else
{
//Not parametrized
if( !m_cachedBoundingBox )
{
m_cachedBoundingBox = new BoundingBox();
// Loop over the children and define a box large enough for all of them
for (const_comp_it it = m_children.begin(); it != m_children.end(); ++it)
{
BoundingBox compBox;
if (*it)
{
(*it)->getBoundingBox(compBox);
m_cachedBoundingBox->grow(compBox);
}
}
}
// Use cached box
assemblyBox = *m_cachedBoundingBox;
}
}
//------------------------------------------------------------------------------------------------
/** Test the intersection of the ray with the children of the component assembly, for InstrumentRayTracer.
*
* @param testRay :: Track under test. The results are stored here.
* @param searchQueue :: If a child is a sub-assembly then it is appended for later searching
*/
void CompAssembly::testIntersectionWithChildren(Track & testRay, std::deque<IComponent_const_sptr> & searchQueue) const
{
int nchildren = this->nelements();
for( int i = 0; i < nchildren; ++i )
{
boost::shared_ptr<Geometry::IComponent> comp = this->getChild(i);
if( ICompAssembly_sptr childAssembly = boost::dynamic_pointer_cast<ICompAssembly>(comp) )
{
searchQueue.push_back(comp);
}
// Check the physical object intersection
else if( IObjComponent *physicalObject = dynamic_cast<IObjComponent*>(comp.get()) )
{
physicalObject->interceptSurface(testRay);
}
else {}
}
}
//------------------------------------------------------------------------------------------------
/** Print information about elements in the assembly to a stream
* @param os :: output stream
*
* Loops through all components in the assembly
* and call printSelf(os).
*/
void CompAssembly::printChildren(std::ostream& os) const
{
for (int i=0;i<nelements();i++)
{
boost::shared_ptr<IComponent> it = (*this)[i];
os << "Component " << i <<" : **********" <<std::endl;
it->printSelf(os);
}
}
/** Print information about all the elements in the tree to a stream
* Loops through all components in the tree
* and call printSelf(os).
*
* @param os :: output stream
*/
void CompAssembly::printTree(std::ostream& os) const
{
for (int i=0;i<nelements();i++)
{
boost::shared_ptr<IComponent> it = (*this)[i];
const CompAssembly* test=dynamic_cast<CompAssembly*>(it.get());
os << "Element " << i << " from " << nelements() << " in the assembly : ";
if (test)
{
os << test->getName() << std::endl;
os << "Children :******** " << std::endl;
test->printTree(os);
}
else
os << it->getName() << std::endl;
}
}
/** Gets the absolute position of the Parametrized CompAssembly
* This attempts to read the cached position value from the parameter map, and creates it if it is not available.
* @returns A vector of the absolute position
*/
V3D CompAssembly::getPos() const
{
if (!m_map)
return Component::getPos();
else
{
V3D pos;
if (!m_map->getCachedLocation(m_base,pos))
{
pos = Component::getPos();
m_map->setCachedLocation(m_base,pos);
}
return pos;
}
}
/** Gets the absolute position of the Parametrized CompAssembly
* This attempts to read the cached position value from the parameter map, and creates it if it is not available.
* @returns A vector of the absolute position
*/
const Quat CompAssembly::getRotation() const
{
if (!m_map)
return Component::getRotation();
else
{
Quat rot;
if (!m_map->getCachedRotation(m_base,rot))
{
rot = Component::getRotation();
m_map->setCachedRotation(m_base,rot);
}
return rot;
}
}
/** Print information about elements in the assembly to a stream
* Overload the operator <<
* @param os :: output stream
* @param ass :: component assembly
* @return stream representation of component assembly
*
* Loops through all components in the assembly
* and call printSelf(os).
* Also output the number of children
*/
std::ostream& operator<<(std::ostream& os, const CompAssembly& ass)
{
ass.printSelf(os);
os << "************************" << std::endl;
os << "Number of children :" << ass.nelements() << std::endl;
ass.printChildren(os);
return os;
}
} // Namespace Geometry
} // Namespace Mantid
syntax highlighted by Code2HTML, v. 0.9.1