/* * This file is part of the GreasePad distribution (https://github.com/FraunhoferIOSB/GreasePad). * Copyright (c) 2022-2025 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 #include #include #include "qassert.h" #include "qtdeprecationdefinitions.h" #include #include #include #include namespace Constraint { using Eigen::Vector3d; using Eigen::Vector2cd; using Eigen::Matrix; MatrixXd ConstraintBase::Rot_ab( const VectorXd &a, const VectorXd &b) { Q_ASSERT( a.size()==b.size() ); #ifdef QT_DEBUG Q_ASSERT( std::fabs( a.norm()-1.) < FLT_EPSILON ); Q_ASSERT( std::fabs( b.norm()-1.) < FLT_EPSILON ); #endif 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 QT_DEBUG QString const what = QStringLiteral("norm(x) = %1").arg( QString::number(xs.norm()) ); Q_ASSERT_X(fabs(xs.norm() - 1.) <= FLT_EPSILON, "null(x)", what.toStdString().data()); #endif Eigen::Index const 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 const check = JJ.adjoint() * xs; #ifdef QT_DEBUG Q_ASSERT_X( check.norm() <= FLT_EPSILON, Q_FUNC_INFO, "not a zero vector"); #endif return JJ; } ConstraintBase::ConstraintBase() : m_status(UNEVAL) , m_enforced(false) { // qDebug().noquote() << Q_FUNC_INFO; } /*std::shared_ptr ConstraintBase::clone() const { // qDebug() << Q_FUNC_INFO; std::shared_ptr ptr = doClone(); auto & r = *ptr; // .get(); assert( typeid(r) == typeid(*this) && "ConstraintBase: doClone() incorrectly overridden" ); return ptr; }*/ MatrixXd Orthogonal::Jacobian( const VectorXidx & idxx, const VectorXd &l0, const VectorXd &l) const { Vector3d const a0 = l0.segment(3 * idxx(0), 3); Vector3d const b0 = l0.segment(3 * idxx(1), 3); Vector3d const a = l.segment(3 * idxx(0), 3); Vector3d const b = l.segment(3 * idxx(1), 3); Matrix JJa; Matrix JJb; JJa = b0.adjoint()*CC()*Rot_ab(a0,a)*null(a0); JJb = a0.adjoint()*CC()*Rot_ab(b0,b)*null(b0); MatrixXd Tmp(1,4); Tmp << JJa, JJb; return Tmp; } VectorXd Orthogonal::contradict( const VectorXidx &idx, const VectorXd &l0) const { Vector3d const a = l0.segment(3 * idx(0), 3); Vector3d const b = l0.segment(3 * idx(1), 3); VectorXd tmp(1); tmp << a.dot( CC()*b ); return tmp; } //! Diag([1,1,0]) Matrix3d Orthogonal::CC() { const static Matrix3d tmp = (Matrix3d() << 1,0,0, 0,1,0, 0,0,0).finished(); return tmp; } /*std::shared_ptr Orthogonal::doClone() const { auto T = std::make_shared(); T->setStatus( this->status() ); T->setEnforced( this->enforced()); return T; }*/ MatrixXd Copunctual::Jacobian(const VectorXidx &idx, const VectorXd &l0, const VectorXd &l) const { Vector3d const a0 = l0.segment(3 * idx(0), 3); Vector3d const b0 = l0.segment(3 * idx(1), 3); Vector3d const c0 = l0.segment(3 * idx(2), 3); Vector3d const a = l.segment(3 * idx(0), 3); Vector3d const b = l.segment(3 * idx(1), 3); Vector3d const c = l.segment(3 * idx(2), 3); Matrix3d const MM = (Matrix3d() << a0,b0,c0 ).finished(); // [a0,b0,c0] Matrix3d Adju = cof3(MM).adjoint(); // adjugate(MM) constexpr int SIX = 6; MatrixXd Tmp(1,SIX); Tmp << Adju.row(0)*Rot_ab(a0,a)*null(a0), Adju.row(1)*Rot_ab(b0,b)*null(b0), Adju.row(2)*Rot_ab(c0,c)*null(c0); return Tmp; } VectorXd Copunctual::contradict(const VectorXidx &idx, const VectorXd &l0) const { Vector3d const a = l0.segment(3 * idx(0), 3); Vector3d const b = l0.segment(3 * idx(1), 3); Vector3d const c = l0.segment(3 * idx(2), 3); Matrix3d const MM = (Matrix3d() << a,b,c).finished(); // [a0,b0,c0] VectorXd tmp(1); tmp << MM.determinant(); return tmp; } //! 3x3 cofactor matrix, i.e., transposed adjugate Matrix3d Copunctual::cof3(const Matrix3d &MM) { 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; } MatrixXd Identical::Jacobian( const VectorXidx & idx, const VectorXd & l0, const VectorXd & l) const { Vector3d a0 = l0.segment( 3*idx(0),3 ); Vector3d b0 = l0.segment(3 * idx(1), 3); Vector3d const a = l.segment(3 * idx(0), 3); Vector3d const b = l.segment( 3*idx(1),3 ); Eigen::FullPivLU LU; // identical Matrix JJ; // identical int idx1 = 0; int idx2 = 0; 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) Matrix const JJa = null(a0).adjoint() * Rot_ab(a, a0); Matrix const JJb = null(b0).adjoint() * Rot_ab(b, b0); MatrixXd Tmp(2,4); Tmp << JJ.adjoint()*JJa.adjoint(), -JJ.adjoint()*JJb.adjoint(); return Tmp; } VectorXd Identical::contradict( const VectorXidx & idx, const VectorXd & l0) const { Vector3d a0 = l0.segment( 3*idx(0),3 ); Vector3d b0 = l0.segment( 3*idx(1),3 ); Eigen::FullPivLU const LU; // check sign ............................................ int idx1 = 0; int idx2 = 0; a0.head(2).cwiseAbs().maxCoeff( &idx1 ); b0.head(2).cwiseAbs().maxCoeff( &idx2 ); Q_ASSERT( sameSign(a0(idx1), b0(idx2)) ); Matrix const JJ = null(a0); Eigen::Vector2d const d2 = JJ.adjoint() * (a0 - b0); // (10.141) return d2; } MatrixXd Parallel::Jacobian(const VectorXidx &idx, const VectorXd &l0, const VectorXd &l) const { Vector3d const a0 = l0.segment(3 * idx(0), 3); Vector3d const b0 = l0.segment(3 * idx(1), 3); Vector3d const a = l.segment(3 * idx(0), 3); Vector3d const b = l.segment( 3*idx(1),3 ); const Matrix JJa = -b0.adjoint() * S3() * Rot_ab(a0, a) * null(a0); const Matrix JJb = a0.adjoint() * S3() * Rot_ab(b0, b) * null(b0); MatrixXd Tmp(1,4); Tmp << JJa, JJb; return Tmp; } VectorXd Parallel::contradict(const VectorXidx &idx, const VectorXd &l0) const { Vector3d const a = l0.segment( 3*idx(0), 3); Vector3d const b = l0.segment( 3*idx(1), 3); VectorXd tmp(1,1); tmp << a.dot( S3()*b ); return tmp; } //! skew(e_3), e_3=[0,0,1]' Matrix3d Parallel::S3() { const static Matrix3d tmp = (Matrix3d() << 0,-1,0, +1,0,0, 0,0,0).finished(); return tmp; } //! e_2 = [0,1,0]' Vector3d Vertical::e2() { const static Vector3d tmp = (Vector3d() << 0,1,0).finished(); return tmp; } MatrixXd Vertical::Jacobian(const VectorXidx &idx, const VectorXd &l0, const VectorXd &l) const { Vector3d const a0 = l0.segment(3 * idx(0), 3); Vector3d const a = l.segment( 3*idx(0), 3); // returns a scalar, not a 1-vector: // return e2().dot( Rot_ab(a0,a)*null(a0) ); return e2().adjoint()* Rot_ab(a0,a)*null(a0); } VectorXd Vertical::contradict( const VectorXidx &idx, const VectorXd &l0) const { VectorXd tmp(1); tmp << l0.segment( (3*idx(0))+1, 1); // 2nd element return tmp; } VectorXd Diagonal::contradict( const VectorXidx &idx, const VectorXd &l0) const { Vector3d l = l0.segment( 3*idx(0), 3); VectorXd tmp(1); // scalar as vector tmp << abs(l(0)) - abs( l(1)); // abs(a)-abs(b) return tmp; } MatrixXd Diagonal::Jacobian( const VectorXidx &idx, const VectorXd &l0, const VectorXd &l) const { // abs(a)-abs(b) = 0, l=[a,b,c]', J = [ sign(a), -sign(b), 0] Vector3d a0 = l0.segment( 3*idx(0), 3); Vector3d const a = l.segment(3 * idx(0), 3); Eigen::RowVector3d const JJ = (Eigen::RowVector3d() << sign(a0(0)), -sign(a0(1)), 0).finished(); return JJ * Rot_ab(a0,a) * null(a0); } VectorXd Horizontal::contradict( const VectorXidx &idx, const VectorXd &l0) const { VectorXd tmp(1); // scalar as vector tmp << l0.segment( 3*idx(0), 1); // 1st element of 3-vector return tmp; } //! e_1 = [1,0,0]' Vector3d Horizontal::e1() { const static Vector3d tmp = (Vector3d() << 1,0,0).finished(); return tmp; } MatrixXd Horizontal::Jacobian( const VectorXidx &idx, const VectorXd &l0, const VectorXd &l) const { Vector3d const a0 = l0.segment(3 * idx(0), 3); Vector3d const a = l.segment(3 * idx(0), 3); return e1().adjoint()*Rot_ab(a0,a)*null(a0); } } // namespace Constraint