/** * createOneDetectorInstrument, creates the most simple possible definition of an instrument in which we can extract a valid L1 and L2 distance for unit calculations. * * Beam direction is along X, * Up direction is Y * * @param sourcePos : V3D position * @param samplePos : V3D sample position * @param detectorPos : V3D detector position * @return Instrument generated. */ Geometry::Instrument_sptr createMinimalInstrument(const Mantid::Kernel::V3D& sourcePos, const Mantid::Kernel::V3D& samplePos, const Mantid::Kernel::V3D& detectorPos ) { Instrument_sptr instrument = boost::make_shared<Instrument>(); instrument->setReferenceFrame( boost::make_shared<ReferenceFrame>(Mantid::Geometry::Y /*up*/, Mantid::Geometry::X /*along*/, Left, "0,0,0")); // A source ObjComponent *source = new ObjComponent("source"); source->setPos(sourcePos); source->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); instrument->add(source); instrument->markAsSource(source); // A sample ObjComponent *sample = new ObjComponent("some-surface-holder"); sample->setPos(samplePos); sample->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); instrument->add(sample); instrument->markAsSamplePos(sample); // A detector Detector *det = new Detector("point-detector", 1 /*detector id*/, NULL); det->setPos(detectorPos); det->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); instrument->add(det); instrument->markAsDetector(det); return instrument; }
/** Returns the position of the center of the pixel at x,y, relative to the * center * of the RectangularDetector, in the plain X,Y coordinates of the * pixels (i.e. unrotated). * @param x :: x pixel integer * @param y :: y pixel integer * @return a V3D vector of the relative position */ V3D RectangularDetector::getRelativePosAtXY(int x, int y) const { if (m_map) { double scalex = 1.0; if (m_map->contains(m_rectBase, "scalex")) scalex = m_map->get(m_rectBase, "scalex")->value<double>(); double scaley = 1.0; if (m_map->contains(m_rectBase, "scaley")) scaley = m_map->get(m_rectBase, "scaley")->value<double>(); return m_rectBase->getRelativePosAtXY(x, y) * V3D(scalex, scaley, 1.0); } else return V3D(m_xstart + m_xstep * x, m_ystart + m_ystep * y, 0); }
/** Set the position of the Component * The position is with respect to the parent Component * @param x :: x position * @param y :: y position * @param z :: z position */ void Component::setPos(double x, double y, double z) { if (!m_map) m_pos = V3D(x, y, z); else throw Kernel::Exception::NotImplementedError( "Component::setPos (for Parametrized Component)"); }
/** Get ScaleFactor of detector. Looks at the "sca" parameter in the parameter *map. * * @returns A vector of the scale factors (1,1,1) if not set */ V3D Component::getScaleFactor() const { if (m_map) { Parameter_sptr par = m_map->get(m_base, "sca"); if (par) { return par->value<V3D>(); } } return V3D(1, 1, 1); }
/** Get ScaleFactor of detector. Looks at the "sca" parameter in the parameter *map. * * @returns A vector of the scale factors (1,1,1) if not set */ V3D Component::getScaleFactor() const { if (m_map) { if (hasComponentInfo()) { return m_map->componentInfo().scaleFactor(index()); } else { Parameter_sptr par = m_map->get(m_base, ParameterMap::scale()); if (par) { return par->value<V3D>(); } } } return V3D(1, 1, 1); }
Geometry::Instrument_sptr createVirtualInstrument(Kernel::V3D sourcePos, Kernel::V3D samplePos, const std::vector<Kernel::V3D> &vecdetpos, const std::vector<detid_t> &vecdetid) { Instrument_sptr instrument = boost::make_shared<Instrument>(); instrument->setReferenceFrame( boost::make_shared<ReferenceFrame>(Mantid::Geometry::Y /*up*/, Mantid::Geometry::Z /*along*/, Right, "0,0,0")); // A source ObjComponent *source = new ObjComponent("source"); source->setPos(sourcePos); source->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); instrument->add(source); instrument->markAsSource(source); // A sample ObjComponent *sample = new ObjComponent("some-surface-holder"); sample->setPos(samplePos); sample->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); instrument->add(sample); instrument->markAsSamplePos(sample); // A detector size_t numdets = vecdetpos.size(); for (size_t i = 0; i < numdets; ++i) { Detector *det = new Detector("point-detector", vecdetid[i] /*detector id*/, NULL); det->setPos(vecdetpos[i]); // FIXME - should be cubi... pixel det->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); instrument->add(det); instrument->markAsDetector(det); } return instrument; }
/** * Enlarges this bounding box so that it encompasses that given. * @param other :: The bounding box that should be encompassed */ void BoundingBox::grow(const BoundingBox &other) { m_null = false; // If the current box is empty then we definitely need to grow if (minPoint() == V3D() && maxPoint() == V3D()) { m_minPoint = other.minPoint(); m_maxPoint = other.maxPoint(); return; } // Simply checks if an of the points in the given box are outside this one and // changes the coordinate appropriately V3D otherPoint = other.minPoint(); for (size_t i = 0; i < 3; ++i) { if (otherPoint[i] < m_minPoint[i]) { m_minPoint[i] = otherPoint[i]; } } otherPoint = other.maxPoint(); for (size_t i = 0; i < 3; ++i) { if (otherPoint[i] > m_maxPoint[i]) { m_maxPoint[i] = otherPoint[i]; } } }
/**Add an additional axis to the goniometer, closer to the sample @param name :: GoniometerAxis name @param axisx :: the x component of the rotation axis @param axisy :: the y component of the rotation axis @param axisz :: the z component of the rotation axis @param angle :: rotation angle, 0 by default @param sense :: rotation sense (CW or CCW), CCW by default @param angUnit :: units for angle of type#AngleUnit, angDegrees by default */ void Goniometer::pushAxis(std::string name, double axisx, double axisy, double axisz, double angle, int sense, int angUnit) { if (initFromR == true) { throw std::runtime_error( "Initialized from a rotation matrix, so no axes can be pushed."); } else { std::vector<GoniometerAxis>::iterator it; // check if such axis is already defined for (it = motors.begin(); it < motors.end(); ++it) { if (name.compare((*it).name) == 0) throw std::invalid_argument("Motor name already defined"); } GoniometerAxis a(name, V3D(axisx, axisy, axisz), angle, sense, angUnit); motors.push_back(a); } recalculateR(); }
/** Finds the approximate solid angle covered by the component when viewed from * the point given * @param observer :: The position from which the component is being viewed * @returns The solid angle in steradians * @throw NullPointerException if the underlying geometrical Object has not been * set */ double ObjComponent::solidAngle(const V3D &observer) const { // If the form of this component is not defined, throw NullPointerException if (!shape()) throw Kernel::Exception::NullPointerException("ObjComponent::solidAngle", "shape"); // Otherwise pass through the shifted point to the Object::solidAngle method V3D scaleFactor = this->getScaleFactor(); if ((scaleFactor - V3D(1.0, 1.0, 1.0)).norm() < 1e-12) return shape()->solidAngle(factorOutComponentPosition(observer)); else { // This is the observer position in the shape's coordinate system. V3D relativeObserver = factorOutComponentPosition(observer); // This function will scale the object shape when calculating the solid // angle. return shape()->solidAngle(relativeObserver, scaleFactor); } }
//---------------------------------------------------------------------------------------------- ///< Render Object or ObjComponent void BitmapGeometryHandler::Render() { // std::cout << "BitmapGeometryHandler::Render() called\n"; V3D pos; // Wait for no error while (glGetError() != GL_NO_ERROR) ; // Because texture colours are combined with the geometry colour // make sure the current colour is white glColor3f(1.0f, 1.0f, 1.0f); // Nearest-neighbor scaling GLint texParam = GL_NEAREST; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texParam); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texParam); glEnable(GL_TEXTURE_2D); // enable texture mapping int texx, texy; m_rectDet->getTextureSize(texx, texy); double tex_frac_x = (1.0 * m_rectDet->xpixels()) / (texx); double tex_frac_y = (1.0 * m_rectDet->ypixels()) / (texy); // Point to the ID of the texture that was created before - in // RectangularDetectorActor. // int texture_id = m_rectDet->getTextureID(); // glBindTexture (GL_TEXTURE_2D, texture_id); // if (glGetError()>0) std::cout << "OpenGL error in glBindTexture \n"; glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); pos = m_rectDet->getRelativePosAtXY(0, 0); pos += V3D(m_rectDet->xstep() * (-0.5), m_rectDet->ystep() * (-0.5), 0.0); // Adjust to account for the size of a pixel glVertex3f((GLfloat)pos.X(), (GLfloat)pos.Y(), (GLfloat)pos.Z()); glTexCoord2f((GLfloat)tex_frac_x, 0.0); pos = m_rectDet->getRelativePosAtXY(m_rectDet->xpixels() - 1, 0); pos += V3D(m_rectDet->xstep() * (+0.5), m_rectDet->ystep() * (-0.5), 0.0); // Adjust to account for the size of a pixel glVertex3f((GLfloat)pos.X(), (GLfloat)pos.Y(), (GLfloat)pos.Z()); glTexCoord2f((GLfloat)tex_frac_x, (GLfloat)tex_frac_y); pos = m_rectDet->getRelativePosAtXY(m_rectDet->xpixels() - 1, m_rectDet->ypixels() - 1); pos += V3D(m_rectDet->xstep() * (+0.5), m_rectDet->ystep() * (+0.5), 0.0); // Adjust to account for the size of a pixel glVertex3f((GLfloat)pos.X(), (GLfloat)pos.Y(), (GLfloat)pos.Z()); glTexCoord2f(0.0, (GLfloat)tex_frac_y); pos = m_rectDet->getRelativePosAtXY(0, m_rectDet->ypixels() - 1); pos += V3D(m_rectDet->xstep() * (-0.5), m_rectDet->ystep() * (+0.5), 0.0); // Adjust to account for the size of a pixel glVertex3f((GLfloat)pos.X(), (GLfloat)pos.Y(), (GLfloat)pos.Z()); glEnd(); if (glGetError() > 0) std::cout << "OpenGL error in BitmapGeometryHandler::Render \n"; glDisable( GL_TEXTURE_2D); // stop texture mapping - not sure if this is necessary. }
/** Set the outline of the assembly. Creates an Object and sets m_shape point to * it. * All child components must be detectors and positioned along a straight line * and have the same shape. * The shape can be either a box or a cylinder. * @return The shape of the outline: "cylinder", "box", ... */ boost::shared_ptr<Object> ObjCompAssembly::createOutline() { if (group.empty()) { throw Kernel::Exception::InstrumentDefinitionError("Empty ObjCompAssembly"); } if (nelements() < 2) { g_log.warning("Creating outline with fewer than 2 elements. The outline displayed may be inaccurate."); } // Get information about the shape and size of a detector std::string type; int otype; std::vector<Kernel::V3D> vectors; double radius, height; boost::shared_ptr<const Object> obj = group.front()->shape(); if (!obj) { throw Kernel::Exception::InstrumentDefinitionError( "Found ObjComponent without shape"); } obj->GetObjectGeom(otype, vectors, radius, height); if (otype == 1) { type = "box"; } else if (otype == 3) { type = "cylinder"; } else { throw std::runtime_error( "IDF \"outline\" option is only allowed for assemblies containing " "components of types \"box\" or \"cylinder\"."); } // Calculate the dimensions of the outline object // find the 'moments of inertia' of the assembly double Ixx = 0, Iyy = 0, Izz = 0, Ixy = 0, Ixz = 0, Iyz = 0; V3D Cmass; // 'center of mass' of the assembly for (const_comp_it it = group.begin(); it != group.end(); it++) { V3D p = (**it).getRelativePos(); Cmass += p; } Cmass /= nelements(); for (const_comp_it it = group.begin(); it != group.end(); it++) { V3D p = (**it).getRelativePos(); double x = p.X() - Cmass.X(), x2 = x * x; double y = p.Y() - Cmass.Y(), y2 = y * y; double z = p.Z() - Cmass.Z(), z2 = z * z; Ixx += y2 + z2; Iyy += x2 + z2; Izz += y2 + x2; Ixy -= x * y; Ixz -= x * z; Iyz -= y * z; } // principal axes of the outline shape // vz defines the line through all pixel centres V3D vx, vy, vz; if (Ixx == 0) // pixels along x axis { vx = V3D(0, 1, 0); vy = V3D(0, 0, 1); vz = V3D(1, 0, 0); } else if (Iyy == 0) // pixels along y axis { vx = V3D(0, 0, 1); vy = V3D(1, 0, 0); vz = V3D(0, 1, 0); } else if (Izz == 0) // pixels along z axis { vx = V3D(1, 0, 0); vy = V3D(0, 1, 0); vz = V3D(0, 0, 1); } else { // Either the detectors are not perfectrly aligned or // vz is parallel to neither of the 3 axis x,y,z // This code is unfinished DblMatrix II(3, 3), Vec(3, 3), D(3, 3); II[0][0] = Ixx; II[0][1] = Ixy; II[0][2] = Ixz; II[1][0] = Ixy; II[1][1] = Iyy; II[1][2] = Iyz; II[2][0] = Ixz; II[2][1] = Iyz; II[2][2] = Izz; std::cerr << "Inertia matrix: " << II << II.determinant() << '\n'; II.Diagonalise(Vec, D); std::cerr << "Vectors: " << Vec << '\n'; std::cerr << "Moments: " << D << '\n'; vx = V3D(Vec[0][0], Vec[1][0], Vec[2][0]); vy = V3D(Vec[0][1], Vec[1][1], Vec[2][1]); vz = V3D(Vec[0][2], Vec[1][2], Vec[2][2]); } // find assembly sizes along the principal axes double hx, hy, hz; // sizes along x,y, and z axis // maximum displacements from the mass centre along axis vx,vy, and vz // in positive (p) and negative (n) directions. // positive displacements are positive numbers and negative ones are negative double hxn = 0, hyn = 0, hzn = 0; double hxp = 0, hyp = 0, hzp = 0; for (const_comp_it it = group.begin(); it != group.end(); it++) { // displacement vector of a detector V3D p = (**it).getRelativePos() - Cmass; // its projection on the vx axis double h = p.scalar_prod(vx); if (h > hxp) hxp = h; if (h < hxn) hxn = h; // its projection on the vy axis h = p.scalar_prod(vy); if (h > hyp) hyp = h; if (h < hyn) hyn = h; // its projection on the vz axis h = p.scalar_prod(vz); if (h > hzp) hzp = h; if (h < hzn) hzn = h; } // calc the assembly sizes hx = hxp - hxn; hy = hyp - hyn; hz = hzp - hzn; // hx and hy must be practically zero if (hx > 1e-3 || hy > 1e-3) // arbitrary numbers { throw Kernel::Exception::InstrumentDefinitionError( "Detectors of a ObjCompAssembly do not lie on a staraight line"); } // determine the order of the detectors to make sure that the texture // coordinates are correct bool firstAtBottom; // first detector is at the bottom of the outline shape // the bottom end is the one with the negative displacement from the centre firstAtBottom = ((**group.begin()).getRelativePos() - Cmass).scalar_prod(vz) < 0; // form the input string for the ShapeFactory std::ostringstream obj_str; if (type == "box") { if (hz == 0) hz = 0.1; hx = hy = 0; height = 0; V3D p0 = vectors[0]; for (size_t i = 1; i < vectors.size(); ++i) { V3D p = vectors[i] - p0; double h = fabs(p.scalar_prod(vx)); if (h > hx) hx = h; h = fabs(p.scalar_prod(vy)); if (h > hy) hy = h; height = fabs(p.scalar_prod(vz)); if (h > height) height = h; } vx *= hx / 2; vy *= hy / 2; vz *= hzp + height / 2; if (!firstAtBottom) { vz = vz * (-1); } // define the outline shape as cuboid V3D p_lfb = Cmass - vx - vy - vz; V3D p_lft = Cmass - vx - vy + vz; V3D p_lbb = Cmass - vx + vy - vz; V3D p_rfb = Cmass + vx - vy - vz; obj_str << "<cuboid id=\"shape\">"; obj_str << "<left-front-bottom-point "; obj_str << "x=\"" << p_lfb.X(); obj_str << "\" y=\"" << p_lfb.Y(); obj_str << "\" z=\"" << p_lfb.Z(); obj_str << "\" />"; obj_str << "<left-front-top-point "; obj_str << "x=\"" << p_lft.X(); obj_str << "\" y=\"" << p_lft.Y(); obj_str << "\" z=\"" << p_lft.Z(); obj_str << "\" />"; obj_str << "<left-back-bottom-point "; obj_str << "x=\"" << p_lbb.X(); obj_str << "\" y=\"" << p_lbb.Y(); obj_str << "\" z=\"" << p_lbb.Z(); obj_str << "\" />"; obj_str << "<right-front-bottom-point "; obj_str << "x=\"" << p_rfb.X(); obj_str << "\" y=\"" << p_rfb.Y(); obj_str << "\" z=\"" << p_rfb.Z(); obj_str << "\" />"; obj_str << "</cuboid>"; } else if (type == "cylinder") { // the outline is one detector height short hz += height; // shift Cmass to the end of the cylinder where the first detector is if (firstAtBottom) { Cmass += vz * hzn; } else { hzp += height; Cmass += vz * hzp; // inverse the vz axis vz = vz * (-1); } obj_str << "<segmented-cylinder id=\"stick\">"; obj_str << "<centre-of-bottom-base "; obj_str << "x=\"" << Cmass.X(); obj_str << "\" y=\"" << Cmass.Y(); obj_str << "\" z=\"" << Cmass.Z(); obj_str << "\" />"; obj_str << "<axis x=\"" << vz.X() << "\" y=\"" << vz.Y() << "\" z=\"" << vz.Z() << "\" /> "; obj_str << "<radius val=\"" << radius << "\" />"; obj_str << "<height val=\"" << hz << "\" />"; obj_str << "</segmented-cylinder>"; } if (!obj_str.str().empty()) { boost::shared_ptr<Object> s = ShapeFactory().createShape(obj_str.str()); setOutline(s); return s; } return boost::shared_ptr<Object>(); }
/** * Builds a map based on the given number of neighbours * @param noNeighbours :: The number of nearest neighbours to use to build * the graph */ void NearestNeighbours::build(const int noNeighbours) { std::map<specid_t, IDetector_const_sptr> spectraDets = getSpectraDetectors(m_instrument, m_spectraMap); if (spectraDets.empty()) { throw std::runtime_error( "NearestNeighbours::build - Cannot find any spectra"); } const int nspectra = static_cast<int>(spectraDets.size()); // ANN only deals with integers if (noNeighbours >= nspectra) { throw std::invalid_argument( "NearestNeighbours::build - Invalid number of neighbours"); } // Clear current m_graph.clear(); m_specToVertex.clear(); m_noNeighbours = noNeighbours; BoundingBox bbox; // Base the scaling on the first detector, should be adequate but we can look // at this IDetector_const_sptr firstDet = (*spectraDets.begin()).second; firstDet->getBoundingBox(bbox); m_scale.reset(new V3D(bbox.width())); ANNpointArray dataPoints = annAllocPts(nspectra, 3); MapIV pointNoToVertex; std::map<specid_t, IDetector_const_sptr>::const_iterator detIt; int pointNo = 0; for (detIt = spectraDets.begin(); detIt != spectraDets.end(); ++detIt) { IDetector_const_sptr detector = detIt->second; const specid_t spectrum = detIt->first; V3D pos = detector->getPos() / (*m_scale); dataPoints[pointNo][0] = pos.X(); dataPoints[pointNo][1] = pos.Y(); dataPoints[pointNo][2] = pos.Z(); Vertex vertex = boost::add_vertex(spectrum, m_graph); pointNoToVertex[pointNo] = vertex; m_specToVertex[spectrum] = vertex; ++pointNo; } ANNkd_tree *annTree = new ANNkd_tree(dataPoints, nspectra, 3); pointNo = 0; // Run the nearest neighbour search on each detector, reusing the arrays ANNidxArray nnIndexList = new ANNidx[m_noNeighbours]; ANNdistArray nnDistList = new ANNdist[m_noNeighbours]; for (detIt = spectraDets.begin(); detIt != spectraDets.end(); ++detIt) { ANNpoint scaledPos = dataPoints[pointNo]; annTree->annkSearch(scaledPos, // Point to search nearest neighbours of m_noNeighbours, // Number of neighbours to find (8) nnIndexList, // Index list of results nnDistList, // List of distances to each of these 0.0 // Error bound (?) is this the radius to search in? ); // The distances that are returned are in our scaled coordinate // system. We store the real space ones. V3D realPos = V3D(scaledPos[0], scaledPos[1], scaledPos[2]) * (*m_scale); for (int i = 0; i < m_noNeighbours; i++) { ANNidx index = nnIndexList[i]; V3D neighbour = V3D(dataPoints[index][0], dataPoints[index][1], dataPoints[index][2]) * (*m_scale); V3D distance = neighbour - realPos; double separation = distance.norm(); boost::add_edge(m_specToVertex[detIt->first], // from pointNoToVertex[index], // to distance, m_graph); if (separation > m_cutoff) { m_cutoff = separation; } } pointNo++; } delete[] nnIndexList; delete[] nnDistList; delete annTree; annDeallocPts(dataPoints); annClose(); pointNoToVertex.clear(); m_vertexID = get(boost::vertex_name, m_graph); m_edgeLength = get(boost::edge_name, m_graph); }