/* * This file is part of the GreasePad distribution (https://github.com/FraunhoferIOSB/GreasePad). * Copyright (c) 2022 Jochen Meidow, Fraunhofer IOSB * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "constraints.h" #include "matrix.h" #include namespace Constraint { using Eigen::Vector3d; using Eigen::Vector2cd; using Eigen::Matrix; static const double T_ZERO = 1e-5; MatrixXd ConstraintBase::Rot_ab( const VectorXd &a, const VectorXd &b) { Q_ASSERT( a.size()==b.size()); Q_ASSERT( std::fabs( a.norm()-1.) < T_ZERO ); Q_ASSERT( std::fabs( b.norm()-1.) < T_ZERO ); return MatrixXd::Identity( a.size(),a.size()) +2*b*a.adjoint() -(a+b)*(a+b).adjoint()/(1.+a.dot(b)); } MatrixXd ConstraintBase::null( const VectorXd & xs ) { // cf. PCV, eq. (A.120) //if ( fabs(xs.norm()-1.) > T_ZERO ) // qDebug() << xs; #ifdef Q_DEBUG QString what = QStringLiteral("norm(x) = %1").arg( QString::number(xs.norm()) ); Q_ASSERT_X( fabs(xs.norm()-1.) <= T_ZERO, "null(x)", what.toStdString().data() ) ; #endif Eigen::Index N = xs.size(); VectorXd x0 = xs.head(N-1); double xN = xs(N-1); if ( xN < 0.0 ) { x0 = -x0; xN = -xN; } MatrixXd JJ( N, N-1); JJ.topRows(N-1) = MatrixXd::Identity(N-1,N-1) -x0*x0.adjoint()/(1.+xN); JJ.bottomRows(1) = -x0.adjoint(); VectorXd check = JJ.adjoint()*xs; Q_ASSERT_X( check.norm() <= T_ZERO, Q_FUNC_INFO, "not a zero vector"); return JJ; } // Diag([1,1,0]) Matrix3d Orthogonal::CC() { const static Matrix3d tmp = (Matrix3d() << 1,0,0, 0,1,0, 0,0,0).finished(); return tmp; } // S(e3) Matrix3d Parallel::S3() { const static Matrix3d tmp = (Matrix3d() << 0,-1,0, +1,0,0, 0,0,0).finished(); return tmp; } ConstraintBase::ConstraintBase() { m_status = UNEVAL; m_enforced = false; } MatrixXd Orthogonal::Jacobian( const VectorXi & idxx, const VectorXd &l0, const VectorXd &l) const { Vector3d a0 = l0.segment( 3*idxx(0), 3); Vector3d b0 = l0.segment( 3*idxx(1), 3); Vector3d a = l.segment( 3*idxx(0), 3); Vector3d b = l.segment( 3*idxx(1), 3); Matrix JJa; Matrix JJb; MatrixXd JJl; JJl = null(a0).adjoint() * Rot_ab(a,a0); JJa = b.adjoint()*CC()* JJl.adjoint(); JJl = null(b0).adjoint() * Rot_ab(b,b0); JJb = a0.adjoint()*CC()*JJl.adjoint(); MatrixXd Tmp(1,4); Tmp << JJa, JJb; return Tmp; } VectorXd Orthogonal::contradict( const VectorXi &idxx, const VectorXd &l0) const { Vector3d a = l0.segment (3*idxx(0), 3); Vector3d b = l0.segment (3*idxx(1), 3); VectorXd tmp(1); tmp << a.dot( CC()*b ); return tmp; } MatrixXd Copunctual::Jacobian( const VectorXi & idxx, const VectorXd & l0, const VectorXd & l) const { Vector3d a0 = l0.segment(3*idxx(0),3); Vector3d b0 = l0.segment(3*idxx(1),3); Vector3d c0 = l0.segment(3*idxx(2),3); Vector3d a = l.segment(3*idxx(0),3); Vector3d b = l.segment(3*idxx(1),3); Vector3d c = l.segment(3*idxx(2),3); Matrix3d MM = (Matrix3d() << a0,b0,c0).finished(); Matrix3d Adju = cof3(MM).adjoint(); // adjugate(MM) Matrix JJa = null(a0).adjoint() * Rot_ab(a,a0); Matrix JJb = null(b0).adjoint() * Rot_ab(b,b0); Matrix JJc = null(c0).adjoint() * Rot_ab(c,c0); constexpr int six = 6; MatrixXd Tmp(1,six); Tmp << Adju.row(0)*JJa.adjoint(), Adju.row(1)*JJb.adjoint(), Adju.row(2)*JJc.adjoint(); return Tmp; } VectorXd Copunctual::contradict( const VectorXi & idxx, const VectorXd & l0) const { Vector3d a = l0.segment( 3*idxx(0), 3 ); Vector3d b = l0.segment( 3*idxx(1), 3 ); Vector3d c = l0.segment( 3*idxx(2), 3 ); Matrix3d MM = (Matrix3d() << a,b,c).finished(); VectorXd tmp(1); tmp << MM.determinant(); return tmp; } MatrixXd Identical::Jacobian( const VectorXi & idxx, const VectorXd & l0, const VectorXd & l) const { Vector3d a0 = l0.segment( 3*idxx(0),3 ); Vector3d b0 = l0.segment( 3*idxx(1),3 ); Vector3d a = l.segment( 3*idxx(0),3 ); Vector3d b = l.segment( 3*idxx(1),3 ); Matrix JJa = null(a0).adjoint() * Rot_ab(a,a0); Matrix JJb = null(b0).adjoint() * Rot_ab(b,b0); Eigen::FullPivLU LU; // identical Matrix JJ; // identical int idx1; int idx2; a0.cwiseAbs().maxCoeff( &idx1 ); b0.cwiseAbs().maxCoeff( &idx2 ); Q_ASSERT( sign( a0(idx1) )==sign( b0(idx2) ) ); Q_ASSERT( sameSign( a0(idx1), b0(idx2) ) ); LU.compute(a0.adjoint()); JJ = LU.kernel(); // JJ = null( a'); // d2 = JJ.adjoint()*(a -b); // (10.141) MatrixXd Tmp(2,4); Tmp << JJ.adjoint()*JJa.adjoint(), -JJ.adjoint()*JJb.adjoint(); return Tmp; } VectorXd Identical::contradict( const VectorXi & idxx, const VectorXd & l0) const { Vector3d a = l0.segment( 3*idxx(0),3 ); Vector3d b = l0.segment( 3*idxx(1),3 ); Eigen::FullPivLU LU; // check sign ............................................ int idx1; int idx2; a.head(2).cwiseAbs().maxCoeff( &idx1 ); b.head(2).cwiseAbs().maxCoeff( &idx2 ); Q_ASSERT( sameSign(a(idx1), b(idx2)) ); LU.compute( a.adjoint() ); Matrix JJ = LU.kernel(); // JJ = null( a'); Eigen::Vector2d d2 = JJ.adjoint()*(a -b); // (10.141) return d2; } MatrixXd Parallel::Jacobian( const VectorXi & idxx, const VectorXd & l0, const VectorXd & l) const { Vector3d a0 = l0.segment( 3*idxx(0),3 ); Vector3d b0 = l0.segment( 3*idxx(1),3 ); Vector3d a = l.segment( 3*idxx(0),3 ); Vector3d b = l.segment( 3*idxx(1),3 ); Matrix JJa; Matrix JJb; MatrixXd JJaa = null(a0).adjoint() * Rot_ab(a,a0); MatrixXd JJbb = null(b0).adjoint() * Rot_ab(b,b0); JJa = -b0.adjoint()*S3()*JJaa.adjoint(); JJb = a0.adjoint()*S3()*JJbb.adjoint(); MatrixXd Tmp(1,4); Tmp << JJa, JJb; return Tmp; } VectorXd Parallel::contradict( const VectorXi & idxx, const VectorXd & l0) const { Vector3d a = l0.segment( 3*idxx(0),3 ); Vector3d b = l0.segment( 3*idxx(1),3 ); VectorXd tmp(1,1); tmp << a.dot( S3()*b ); return tmp; } Matrix3d Copunctual::cof3( const Matrix3d & MM ) const { Matrix3d Cof; Cof(0,0) = +MM(1,1)*MM(2,2) -MM(2,1)*MM(1,2); Cof(0,1) = -MM(1,0)*MM(2,2) +MM(2,0)*MM(1,2); Cof(0,2) = +MM(1,0)*MM(2,1) -MM(2,0)*MM(1,1); Cof(1,0) = -MM(0,1)*MM(2,2) +MM(2,1)*MM(0,2); Cof(1,1) = +MM(0,0)*MM(2,2) -MM(2,0)*MM(0,2); Cof(1,2) = -MM(0,0)*MM(2,1) +MM(2,0)*MM(0,1); Cof(2,0) = +MM(0,1)*MM(1,2) -MM(1,1)*MM(0,2); Cof(2,1) = -MM(0,0)*MM(1,2) +MM(1,0)*MM(0,2); Cof(2,2) = +MM(0,0)*MM(1,1) -MM(1,0)*MM(0,1); return Cof; } void ConstraintBase::serialize( QDataStream & out) const { // qDebug() << Q_FUNC_INFO << type_name(); out << type_name(); out << status(); // { UNEVAL=0 | REQUIRED | OBSOLETE }; out << enforced(); } std::shared_ptr ConstraintBase::deserialize( QDataStream &in ) { char* type_name; in >> type_name; // qDebug() << Q_FUNC_INFO << type_name; if ( in.status()!=0) { return nullptr; } std::shared_ptr c; if ( std::strcmp( type_name, "orthogonal")==0 ){ c = Orthogonal::create(); } if ( std::strcmp( type_name, "parallel")==0 ){ c = Parallel::create(); } if ( std::strcmp( type_name, "copunctual")==0 ){ c = Copunctual::create(); } int status; in >> status; bool enforced; in >> enforced; if ( in.status()!=0) { return nullptr; } c->setStatus( static_cast(status) ); c->setEnforced( enforced ); return c; } std::shared_ptr Parallel::clone() const { auto T = std::make_shared(); T->setStatus( this->status() ); T->setEnforced( this->enforced()); return std::move(T); } std::shared_ptr Identical::clone() const { auto T = std::make_shared(); T->setStatus( this->status() ); T->setEnforced( this->enforced()); return std::move(T); } std::shared_ptr Copunctual::clone() const { auto T = std::make_shared(); T->setStatus( this->status() ); T->setEnforced( this->enforced() ); return std::move(T); } std::shared_ptr Orthogonal::clone() const { auto T = std::make_shared(); T->setStatus( this->status() ); T->setEnforced( this->enforced()); return std::move(T); } } // namespace Constraint