/* openDCM, dimensional constraint manager Copyright (C) 2012 Stefan Troeger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef GCM_CLUSTERMATH_H #define GCM_CLUSTERMATH_H #include #include #include #include #include #include "defines.hpp" #define MAXFAKTOR 1.2 //the maximal distance allowd by a point normed to the cluster size #define MINFAKTOR 0.8 //the minimal distance allowd by a point normed to the cluster size #define SKALEFAKTOR 1. //the faktor by which the biggest size is multiplied to get the scale value #define NQFAKTOR 0.5 //the faktor by which the norm quaternion is multiplied with to get the RealScalar //norm quaternion to generate the unit quaternion namespace dcm { namespace details { enum Scalemode { one, two, three, multiple_inrange, multiple_outrange }; template struct ClusterMath { public: typedef typename system_traits::Kernel Kernel; typedef typename system_traits::Cluster Cluster; typedef typename system_traits::template getModule::type module3d; typedef typename module3d::Geometry3D Geometry3D; typedef boost::shared_ptr Geom; typedef typename module3d::math_prop math_prop; typedef typename module3d::fix_prop fix_prop; typedef typename Kernel::number_type Scalar; typename Kernel::Transform3D m_transform, m_ssrTransform, m_resetTransform; typename Kernel::DiffTransform3D m_diffTrans; typename Kernel::Vector3Map m_normQ; typename Kernel::Quaternion m_resetQuaternion; int m_offset; bool init, fix; std::vector m_geometry; typename Kernel::Vector3Map m_translation; //shift scale stuff typename Kernel::Vector3 midpoint, m_shift, scale_dir, maxm, minm, max, fixtrans; Scalemode mode; Scalar m_scale; typedef std::vector > Vec; Vec m_points, m_pseudo; #ifdef USE_LOGGING src::logger log; #endif public: ClusterMath(); void setParameterOffset(int offset); int getParameterOffset(); typename Kernel::Vector3Map& getNormQuaternionMap(); typename Kernel::Vector3Map& getTranslationMap(); void initMaps(); void initFixMaps(); typename Kernel::Transform3D& getTransform(); void mapsToTransform(typename Kernel::Transform3D& trans); void transformToMaps(typename Kernel::Transform3D& trans); void finishCalculation(); void finishFixCalculation(); void resetClusterRotation(typename Kernel::Transform3D& trans); void calcDiffTransform(typename Kernel::DiffTransform3D& trans); void recalculate(); void addGeometry(Geom g); void clearGeometry(); std::vector& getGeometry(); struct map_downstream { details::ClusterMath& m_clusterMath; typename Kernel::Transform3D m_transform; bool m_isFixed; map_downstream(details::ClusterMath& cm, bool fix); void operator()(Geom g); void operator()(boost::shared_ptr c); }; void mapClusterDownstreamGeometry(boost::shared_ptr cluster); //Calculate the scale of the cluster. Therefore the midpoint is calculated and the scale is // defined as the max distance between the midpoint and the points. Scalar calculateClusterScale(); void applyClusterScale(Scalar scale, bool isFixed); private: Scalar calcOnePoint(const typename Kernel::Vector3& p); Scalar calcTwoPoints(const typename Kernel::Vector3& p1, const typename Kernel::Vector3& p2); Scalar calcThreePoints(const typename Kernel::Vector3& p1, const typename Kernel::Vector3& p2, const typename Kernel::Vector3& p3); }; /*****************************************************************************************************************/ /*****************************************************************************************************************/ /*****************************************************************************************************************/ /*****************************************************************************************************************/ template ClusterMath::ClusterMath() : m_normQ(NULL), m_translation(NULL), init(false) { m_resetTransform = typename Kernel::Quaternion(1,1,1,1); m_shift.setZero(); #ifdef USE_LOGGING log.add_attribute("Tag", attrs::constant< std::string >("Clustermath3D")); #endif }; template void ClusterMath::setParameterOffset(int offset) { m_offset = offset; }; template int ClusterMath::getParameterOffset() { return m_offset; }; template typename ClusterMath::Kernel::Vector3Map& ClusterMath::getNormQuaternionMap() { return m_normQ; }; template typename ClusterMath::Kernel::Vector3Map& ClusterMath::getTranslationMap() { return m_translation; }; template void ClusterMath::initMaps() { transformToMaps(m_transform); init = true; midpoint.setZero(); m_shift.setZero(); m_ssrTransform.setIdentity(); m_diffTrans = m_transform; fix=false; #ifdef USE_LOGGING BOOST_LOG(log) << "Init transform: "< void ClusterMath::initFixMaps() { //when fixed no maps exist new(&m_translation) typename Kernel::Vector3Map(&fixtrans(0)); m_translation = m_transform.translation().vector(); init = true; midpoint.setZero(); m_shift.setZero(); m_ssrTransform.setIdentity(); m_diffTrans = m_transform; fix=true; #ifdef USE_LOGGING BOOST_LOG(log) << "Init fix transform: "< typename ClusterMath::Kernel::Transform3D& ClusterMath::getTransform() { return m_transform; }; template void ClusterMath::mapsToTransform(typename ClusterMath::Kernel::Transform3D& trans) { //add scale only after possible reset typename Kernel::Transform3D::Scaling scale(m_transform.scaling()); trans = m_diffTrans; trans *= scale; }; template void ClusterMath::transformToMaps(typename ClusterMath::Kernel::Transform3D& trans) { const typename Kernel::Quaternion& m_quaternion = trans.rotation(); if(m_quaternion.w() < 1.) { Scalar s = std::acos(m_quaternion.w())/std::sin(std::acos(m_quaternion.w())); m_normQ = m_quaternion.vec()*s; m_normQ /= NQFAKTOR; } else { m_normQ.setZero(); } m_translation = trans.translation().vector(); }; template void ClusterMath::finishCalculation() { mapsToTransform(m_transform); init=false; #ifdef USE_LOGGING BOOST_LOG(log) << "Finish calculation"; #endif m_transform = m_ssrTransform*m_transform; //scale all geometries back to the original size m_diffTrans *= typename Kernel::Transform3D::Scaling(1./m_ssrTransform.scaling().factor()); typedef typename std::vector::iterator iter; for(iter it = m_geometry.begin(); it != m_geometry.end(); it++) (*it)->recalculate(m_diffTrans); #ifdef USE_LOGGING BOOST_LOG(log) << "Finish transform:"< void ClusterMath::finishFixCalculation() { #ifdef USE_LOGGING BOOST_LOG(log) << "Finish fix calculation"; #endif typedef typename std::vector::iterator iter; m_transform *= m_ssrTransform.inverse(); typename Kernel::DiffTransform3D diff(m_transform); for(iter it = m_geometry.begin(); it != m_geometry.end(); it++) (*it)->recalculate(diff); #ifdef USE_LOGGING BOOST_LOG(log) << "Finish fix transform:"< void ClusterMath::resetClusterRotation(typename ClusterMath::Kernel::Transform3D& trans) { #ifdef USE_LOGGING BOOST_LOG(log) << "Reset cluster rotation:"<::iterator iter; for(iter it = m_geometry.begin(); it != m_geometry.end(); it++) { (*it)->transform(m_resetTransform); }; }; template void ClusterMath::calcDiffTransform(typename ClusterMath::Kernel::DiffTransform3D& trans) { Scalar norm = m_normQ.norm(); trans.setIdentity(); if(norm < 0.1) { if(norm == 0) { trans *= typename Kernel::Transform3D::Translation(m_translation); resetClusterRotation(trans); } else { const Scalar fac = std::sin(NQFAKTOR*norm)/norm; trans = typename Kernel::Quaternion(std::cos(NQFAKTOR*norm), m_normQ(0)*fac, m_normQ(1)*fac,m_normQ(2)*fac); trans *= typename Kernel::Transform3D::Translation(m_translation); resetClusterRotation(trans); } transformToMaps(trans); return; } const Scalar fac = std::sin(NQFAKTOR*norm)/norm; trans = typename Kernel::Quaternion(std::cos(NQFAKTOR*norm), m_normQ(0)*fac, m_normQ(1)*fac, m_normQ(2)*fac); trans *= typename Kernel::Transform3D::Translation(m_translation); }; template void ClusterMath::recalculate() { calcDiffTransform(m_diffTrans); const typename Kernel::Quaternion Q = m_diffTrans.rotation(); // now calculate the gradient quaternions and calculate the diff rotation matrices // m_normQ = (a,b,c) // n = ||m_normQ|| // // Q = (a/n sin(n), b/n sin(n), c/n sin(n), cos(n)) // //n=||m_normQ||, sn = sin(n)/n, sn3 = sin(n)/n^3, cn = cos(n)/n, divn = 1/n; const Scalar n = m_normQ.norm(); const Scalar sn = std::sin(NQFAKTOR*n)/n; const Scalar mul = (NQFAKTOR*std::cos(NQFAKTOR*n)-sn)/std::pow(n,2); //dxa = dQx/da const Scalar dxa = sn + std::pow(m_normQ(0),2)*mul; const Scalar dxb = m_normQ(0)*m_normQ(1)*mul; const Scalar dxc = m_normQ(0)*m_normQ(2)*mul; const Scalar dya = m_normQ(1)*m_normQ(0)*mul; const Scalar dyb = sn + std::pow(m_normQ(1),2)*mul; const Scalar dyc = m_normQ(1)*m_normQ(2)*mul; const Scalar dza = m_normQ(2)*m_normQ(0)*mul; const Scalar dzb = m_normQ(2)*m_normQ(1)*mul; const Scalar dzc = sn + std::pow(m_normQ(2),2)*mul; const Scalar dwa = -sn*NQFAKTOR*m_normQ(0); const Scalar dwb = -sn*NQFAKTOR*m_normQ(1); const Scalar dwc = -sn*NQFAKTOR*m_normQ(2); //write in the diffrot matrix, starting with dQ/dx m_diffTrans.at(0,0) = -4.0*(Q.y()*dya+Q.z()*dza); m_diffTrans.at(0,1) = -2.0*(Q.w()*dza+dwa*Q.z())+2.0*(Q.x()*dya+dxa*Q.y()); m_diffTrans.at(0,2) = 2.0*(dwa*Q.y()+Q.w()*dya)+2.0*(dxa*Q.z()+Q.x()*dza); m_diffTrans.at(1,0) = 2.0*(Q.w()*dza+dwa*Q.z())+2.0*(Q.x()*dya+dxa*Q.y()); m_diffTrans.at(1,1) = -4.0*(Q.x()*dxa+Q.z()*dza); m_diffTrans.at(1,2) = -2.0*(dwa*Q.x()+Q.w()*dxa)+2.0*(dya*Q.z()+Q.y()*dza); m_diffTrans.at(2,0) = -2.0*(dwa*Q.y()+Q.w()*dya)+2.0*(dxa*Q.z()+Q.x()*dza); m_diffTrans.at(2,1) = 2.0*(dwa*Q.x()+Q.w()*dxa)+2.0*(dya*Q.z()+Q.y()*dza); m_diffTrans.at(2,2) = -4.0*(Q.x()*dxa+Q.y()*dya); //dQ/dy m_diffTrans.at(0,3) = -4.0*(Q.y()*dyb+Q.z()*dzb); m_diffTrans.at(0,4) = -2.0*(Q.w()*dzb+dwb*Q.z())+2.0*(Q.x()*dyb+dxb*Q.y()); m_diffTrans.at(0,5) = 2.0*(dwb*Q.y()+Q.w()*dyb)+2.0*(dxb*Q.z()+Q.x()*dzb); m_diffTrans.at(1,3) = 2.0*(Q.w()*dzb+dwb*Q.z())+2.0*(Q.x()*dyb+dxb*Q.y()); m_diffTrans.at(1,4) = -4.0*(Q.x()*dxb+Q.z()*dzb); m_diffTrans.at(1,5) = -2.0*(dwb*Q.x()+Q.w()*dxb)+2.0*(dyb*Q.z()+Q.y()*dzb); m_diffTrans.at(2,3) = -2.0*(dwb*Q.y()+Q.w()*dyb)+2.0*(dxb*Q.z()+Q.x()*dzb); m_diffTrans.at(2,4) = 2.0*(dwb*Q.x()+Q.w()*dxb)+2.0*(dyb*Q.z()+Q.y()*dzb); m_diffTrans.at(2,5) = -4.0*(Q.x()*dxb+Q.y()*dyb); //dQ/dz m_diffTrans.at(0,6) = -4.0*(Q.y()*dyc+Q.z()*dzc); m_diffTrans.at(0,7) = -2.0*(Q.w()*dzc+dwc*Q.z())+2.0*(Q.x()*dyc+dxc*Q.y()); m_diffTrans.at(0,8) = 2.0*(dwc*Q.y()+Q.w()*dyc)+2.0*(dxc*Q.z()+Q.x()*dzc); m_diffTrans.at(1,6) = 2.0*(Q.w()*dzc+dwc*Q.z())+2.0*(Q.x()*dyc+dxc*Q.y()); m_diffTrans.at(1,7) = -4.0*(Q.x()*dxc+Q.z()*dzc); m_diffTrans.at(1,8) = -2.0*(dwc*Q.x()+Q.w()*dxc)+2.0*(dyc*Q.z()+Q.y()*dzc); m_diffTrans.at(2,6) = -2.0*(dwc*Q.y()+Q.w()*dyc)+2.0*(dxc*Q.z()+Q.x()*dzc); m_diffTrans.at(2,7) = 2.0*(dwc*Q.x()+Q.w()*dxc)+2.0*(dyc*Q.z()+Q.y()*dzc); m_diffTrans.at(2,8) = -4.0*(Q.x()*dxc+Q.y()*dyc); //recalculate all geometries typedef typename std::vector::iterator iter; for(iter it = m_geometry.begin(); it != m_geometry.end(); it++) (*it)->recalculate(m_diffTrans); }; template void ClusterMath::addGeometry(Geom g) { m_geometry.push_back(g); }; template void ClusterMath::clearGeometry() { m_geometry.clear(); }; template std::vector::Geom>& ClusterMath::getGeometry() { return m_geometry; }; template ClusterMath::map_downstream::map_downstream(details::ClusterMath& cm, bool fix) : m_clusterMath(cm), m_isFixed(fix) { m_transform = m_clusterMath.getTransform(); }; template void ClusterMath::map_downstream::operator()(Geom g) { //allow iteration over all maped geometries m_clusterMath.addGeometry(g); //set the offsets so that geometry knows where it is in the parameter map g->m_offset = m_clusterMath.getParameterOffset(); //position and offset of the parameters must be set to the clusters values g->setClusterMode(true, m_isFixed); //calculate the appropriate local values typename Kernel::Transform3D trans = m_transform.inverse(); g->transform(trans); }; template void ClusterMath::map_downstream::operator()(boost::shared_ptr c) { m_transform *= c->template getClusterProperty().getTransform(); }; template void ClusterMath::mapClusterDownstreamGeometry(boost::shared_ptr cluster) { #ifdef USE_LOGGING BOOST_LOG(log) << "Map downstream geometry"; #endif map_downstream down(cluster->template getClusterProperty(), cluster->template getClusterProperty()); cluster->template for_each(down, true); //TODO: if one subcluster is fixed the hole cluster should be too, as there are no // dof's remaining between parts and so nothing can be moved when one part is fixed. }; template typename ClusterMath::Scalar ClusterMath::calculateClusterScale() { #ifdef USE_LOGGING BOOST_LOG(log) << "Calculate cluster scale with transform scale: "<::iterator g_iter; for(g_iter it = m_geometry.begin(); it != m_geometry.end(); it++) m_points.push_back((*it)->getPoint()); //start scale calculation if(m_points.empty()) assert(false); //TODO: Throw else if(m_points.size() == 1) { const typename Kernel::Vector3 p = m_points[0]; return calcOnePoint(p); } else if(m_points.size() == 2) { const typename Kernel::Vector3 p1 = m_points[0]; const typename Kernel::Vector3 p2 = m_points[1]; if(Kernel::isSame((p1-p2).norm(), 0.)) return calcOnePoint(p1); return calcTwoPoints(p1, p2); } else if(m_points.size() == 3) { const typename Kernel::Vector3 p1 = m_points[0]; const typename Kernel::Vector3 p2 = m_points[1]; const typename Kernel::Vector3 p3 = m_points[2]; const typename Kernel::Vector3 d = p2-p1; const typename Kernel::Vector3 e = p3-p1; if(Kernel::isSame(d.norm(), 0.)) { if(Kernel::isSame(e.norm(), 0.)) return calcOnePoint(p1); return calcTwoPoints(p1, p3); } else if(Kernel::isSame(e.norm(), 0.)) { return calcTwoPoints(p1, p2); } else if(!Kernel::isSame((d/d.norm() - e/e.norm()).norm(), 0.) && !Kernel::isSame((d/d.norm() + e/e.norm()).norm(), 0.)) { return calcThreePoints(p1, p2, p3); } //three points on a line need to be treaded as multiple points } //more than 3 points dont have a exakt solution. we search for a midpoint from which all points //are at least MAXFAKTOR*scale away, but not closer than MINFAKTOR*scale //get the bonding box to get the center of points Scalar xmin=1e10, xmax=1e-10, ymin=1e10, ymax=1e-10, zmin=1e10, zmax=1e-10; for(iter it = m_points.begin(); it != m_points.end(); it++) { typename Kernel::Vector3 v = (*it); xmin = (v(0) void ClusterMath::applyClusterScale(Scalar scale, bool isFixed) { #ifdef USE_LOGGING BOOST_LOG(log) << "Apply cluster scale: "<::iterator iter; for(iter it = m_geometry.begin(); it != m_geometry.end(); it++) { (*it)->recalculate(diff); #ifdef USE_LOGGING BOOST_LOG(log) << "Fixed cluster geometry value:" << (*it)->m_rotated.transpose(); #endif }; return; } //if this is our scale then just applie the midpoint as shift if(Kernel::isSame(scale, m_scale)) { } //if only one point exists we extend the origin-point-line to match the scale else if(mode==details::one) { if(Kernel::isSame(midpoint.norm(),0)) midpoint << scale, 0, 0; else midpoint += scale*scale_dir; } //two and three points form a rectangular triangle, so same procedure else if(mode==details::two || mode==details::three) { midpoint+= scale_dir*std::sqrt(std::pow(scale,2) - std::pow(m_scale,2)); } //multiple points else if(mode==details::multiple_outrange) { if(scale_dir(0)) { Scalar d = std::pow(maxm(1),2) + std::pow(maxm(2),2); Scalar h = std::sqrt(std::pow(MAXFAKTOR*scale,2)-d); midpoint(0) += maxm(0) + h; } else if(scale_dir(1)) { Scalar d = std::pow(maxm(0),2) + std::pow(maxm(2),2); Scalar h = std::sqrt(std::pow(MAXFAKTOR*scale,2)-d); midpoint(1) += maxm(1) + h; } else { Scalar d = std::pow(maxm(0),2) + std::pow(maxm(1),2); Scalar h = std::sqrt(std::pow(MAXFAKTOR*scale,2)-d); midpoint(2) += maxm(2) + h; } } else { //TODO: it's possible that for this case we get too far away from the outer points. // The m_scale for "midpoint outside the bounding box" may be bigger than the // scale to applie, so it results in an error. //get the closest point typedef typename Vec::iterator iter; for(iter it = m_points.begin(); it != m_points.end(); it++) { const Eigen::Vector3d point = (*it)-midpoint; if(point.norm()::iterator iter; for(iter it = m_geometry.begin(); it != m_geometry.end(); it++) (*it)->transform(ssTrans); //set the new rotation and translation m_transform = ssTrans.inverse()*m_transform; m_ssrTransform *= ssTrans; transformToMaps(m_transform); #ifdef USE_LOGGING BOOST_LOG(log) << "sstrans scale: "< typename ClusterMath::Scalar ClusterMath::calcOnePoint(const typename ClusterMath::Kernel::Vector3& p) { //one point can have every scale when moving the midpoint on the origin - point vector midpoint = p; scale_dir = -midpoint; scale_dir.normalize(); mode = details::one; m_scale = 0.; return 0.; }; template typename ClusterMath::Scalar ClusterMath::calcTwoPoints(const typename ClusterMath::Kernel::Vector3& p1, const typename ClusterMath::Kernel::Vector3& p2) { //two points have their minimal scale at the mid position. Scaling perpendicular to this //line allows arbitrary scale values. Best is to have the scale dir move towards the origin //as good as possible. midpoint = p1+(p2-p1)/2.; scale_dir = (p2-p1).cross(midpoint); scale_dir = scale_dir.cross(p2-p1); if(!Kernel::isSame(scale_dir.norm(),0)) scale_dir.normalize(); else scale_dir(0) = 1; mode = details::two; m_scale = (p2-p1).norm()/2.; return m_scale; }; template typename ClusterMath::Scalar ClusterMath::calcThreePoints(const typename ClusterMath::Kernel::Vector3& p1, const typename ClusterMath::Kernel::Vector3& p2, const typename ClusterMath::Kernel::Vector3& p3) { //Three points form a triangle with it's minimal scale at the center of it's outer circle. //Arbitrary scale values can be achieved by moving perpendicular to the triangle plane. typename Kernel::Vector3 d = p2-p1; typename Kernel::Vector3 e = p3-p1; typename Kernel::Vector3 f = p1+0.5*d; typename Kernel::Vector3 g = p1+0.5*e; scale_dir = d.cross(e); typename Kernel::Matrix3 m; m.row(0) = d.transpose(); m.row(1) = e.transpose(); m.row(2) = scale_dir.transpose(); typename Kernel::Vector3 res(d.transpose()*f, e.transpose()*g, scale_dir.transpose()*p1); midpoint = m.colPivHouseholderQr().solve(res); scale_dir.normalize(); mode = details::three; m_scale = (midpoint-p1).norm(); return m_scale; }; }//details }//dcm #endif //GCM_CLUSTERMATH_H