RelationDetector.cpp
#include "RelationDetector.h"
#include <algorithm>
#include <numeric>
Q_DECLARE_METATYPE(Vector3)
bool isTaged(const PairRelation &pr)
{
return pr.tag;
}
bool isTagedGr(const GroupRelation &gr)
{
return gr.tag;
}
void saveToFile(QString filename, QVector<PairRelation>& prs)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
QTextStream out(&file);
int i(0);
out << "pair relations number: " << prs.size() << "\n";
out << " TRANS: " << countByType(prs,TRANS) << "\n";
out << " REF: " << countByType(prs,REF) << "\n\n";
foreach(PairRelation pr, prs)
{
out << i << " " << pr.type << ": " << pr.n1->id << ", " << pr.n2->id << ", ";
if ( pr.type == TRANS)
out << "Trans: ["<<pr.trans_vec[0]<<", "<<pr.trans_vec[1]<<", "<<pr.trans_vec[2]<< "]\n";
else
out << "\n";
out << ", diameter: " << pr.diameter << "\n\n";
++i;
}
file.close();
}
void saveToFile(QString filename, QVector<GroupRelation>& grs)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
QTextStream out(&file);
// output groups info
int i(0);
foreach(GroupRelation gr, grs)
{
out << i << " <group-" << gr.type << ">: ";
if ( gr.type == REF_SYMMETRY)
{
out << "\n Point: " << gr.point.x() << "," << gr.point.y() << "," << gr.point.z() << "\n";
out << "normal: " << gr.normal.x() << "," << gr.normal.y() << "," << gr.normal.z() << "\n";
}
else if ( gr.type == AXIS_SYMMETRY)
{
out << "\n Center: " << gr.center.x() << "," << gr.center.y() << "," << gr.center.z() << "\n";
out << "direction: " << gr.direction.x() << "," << gr.direction.y() << "," << gr.direction.z() << "\n";
}
else
out << "\n";
foreach(QString id, gr.ids){
out << id << ", ";
}
out << "</group>\n";
out << "diameter: " << gr.diameter << "\n";
out << "deviation: " << gr.deviation << "\n\n";
++i;
}
file.close();
}
QTextStream& operator << (QTextStream& os, const PairRelation& pr)
{
os << pr.type << " Pair <" << pr.n1->id << ", " << pr.n2->id << "> size <" << pr.n1->bbox().diagonal().norm() << ", " << pr.n2->bbox().diagonal().norm() <<
"> deviation: " << pr.deviation << "\n";
return os;
}
QTextStream& operator << (QTextStream& os, const GroupRelation& gr)
{
os << "Group <";
for (int i = 0; i < gr.ids.size(); ++i)
{
os << gr.ids[i] << ", ";
}
os << gr.type << ">\n";
if ( gr.type == AXIS_SYMMETRY)
{
os << "Center: " << gr.center.x() << ", " << gr.center.y() << ", " << gr.center.z() << "\n";
os << "Direction: " << gr.direction.x() << ", " << gr.direction.y() << ", " << gr.direction.z() << "\n";
}
else if ( gr.type == REF_SYMMETRY)
{
os << "normal of plane: " << gr.normal.x() << ", " << gr.normal.y() << ", " << gr.normal.z() << "\n";
os << "point of plane: " << gr.point.x() << ", " << gr.point.y() << ", " << gr.point.z() << "\n";
}
os << "Diameter: " << gr.diameter << "\n";
os << "Deviation: " << gr.deviation << "\n\n";
return os;
}
bool GroupRelation::equal(GroupRelation& gr)
{
if (type.compare(gr.type))
return false;
if (ids.size() != gr.ids.size())
return false;
QVector<QString>::iterator it2 = gr.ids.begin();
for ( QVector<QString>::iterator it1=ids.begin(); it1 != ids.end(); ++it1, ++it2)
{
if ( *it1 != *it2)
return false;
}
return true;
}
Structure::Link* RelationDetector::findLink(Structure::Node *n1, Structure::Node * n2, Structure::Graph * graph)
{
Structure::Link* link(0);
int tmp1 = graph->edges.size();
for ( int i = 0; i < tmp1; ++i)
{
Structure::Link* tmpLink = graph->edges[i];
if (tmpLink->n1 == n1 && tmpLink->n2 == n2 || tmpLink->n1 == n2 && tmpLink->n2 == n1)
{
link = tmpLink;
break;
}
}
return link;
}
double RelationDetector::computeDeviationByLink(Structure::Link* link)
{
double deviation(0.0);
if (link)
{
SurfaceMesh::Vector3 p1 = link->position(link->n1->id);
SurfaceMesh::Vector3 p2 = link->position(link->n2->id);
deviation = (p1-p2).norm();
//deviation = link->property["blendedDelta"].value<Vector3>().norm();
}
return deviation;
}
RelationDetector::RelationDetector(Structure::Graph* g, const QString& logprefix, int ith, double normalizeCoef, int pointLevel, int logLevel)
:graph_(g),logLevel_(logLevel), normalizeCoef_(normalizeCoef), pointLevel_(pointLevel)
{
thRadiusRadio_ = 1.1;
thTransRadio_ = 0.01; //0.3
thRefRadio_ = 0.03;
thAxisDeviationRadio_ = 0.9;
thCopla_ = 0.002;//0.1
thParal_ = 0.001;
thOthog_ = 0.001;//0.1
thAxisGroup_ = 0.01;
thRefGroup_ = 0.01;
thCoplaGroup_ = 0.003;
if ( logLevel_ > 0)
{
logFile_.setFileName(logprefix + QString::number(ith) + ".log");
if (!logFile_.open(QIODevice::WriteOnly | QIODevice::Text)) return;
logStream_.setDevice(&logFile_);
}
}
double RelationDetector::computePairDiameter(PairRelation& pr)
{
Eigen::AlignedBox3d bbox1 = pr.n1->bbox();
Eigen::AlignedBox3d bbox2 = pr.n2->bbox();
Eigen::AlignedBox3d bbox = bbox2.merged(bbox1);
return bbox.diagonal().norm();
}
double RelationDetector::computeGroupDiameter(GroupRelation& gr)
{
Eigen::AlignedBox3d bbox;
for ( QVector<QString>::iterator it = gr.ids.begin(); it!=gr.ids.end(); ++it)
{
Eigen::AlignedBox3d bbox1 = graph_->getNode(*it)->bbox();
bbox = bbox.merged(bbox1);
}
//return bbox.volume();
return bbox.diagonal().norm();
}
void RelationDetector::computeGroupCenter(GroupRelation& gr)
{
Eigen::Vector3d center(0,0,0);
for ( QVector<QString>::iterator it = gr.ids.begin(); it != gr.ids.end(); ++it)
{
Structure::Node* n1 = graph_->getNode(*it);
center += computeCptsCenter(n1);
//center += n1->center();
}
center = center / gr.ids.size();
gr.center = Point_3(center.x(), center.y(), center.z());
}
double RelationDetector::fixDeviationByPartName(const QString& s1, const QString& s2, double deviation, double times)
{
double ndeviation = deviation;
int idx = s1.indexOf(QRegExp("\\d"), 0);
QString str1 = s1.left(idx);
QString str2 = s2.left(idx);
if ( str1 == str2)
ndeviation *= times;
return ndeviation;
}
Eigen::MatrixXd RelationDetector::node2matrix(Structure::Node* node, int pointLevel)
{
std::vector<Eigen::Vector3d> nodeCptsV;
extractCpts( node, nodeCptsV, pointLevel);
Eigen::MatrixXd nodeCptsM;
vectorPts2MatrixPts(nodeCptsV, nodeCptsM);
return nodeCptsM;
}
int RelationDetector::extractCpts( Structure::Node * n, std::vector<Eigen::Vector3d>& mcpts, int pointsLevel)
{
if ( pointsLevel == 2)
{
SurfaceMesh::Model * m1 = n->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data();
SurfaceMesh::Vector3VertexProperty pts1 = m1->vertex_coordinates();
double* tmp;
foreach(Vertex v1, m1->vertices())
{
tmp = pts1[v1].data();
mcpts.push_back( Eigen::Vector3d(tmp[0], tmp[1], tmp[2]) );
}
}
else
{
if ( Structure::CURVE == n->type() )
{
Structure::Curve* c = dynamic_cast<Structure::Curve *>(n);
if ( pointsLevel == 1)
{
for (int i = 0; i < (int) c->numCtrlPnts(); ++i)
{
mcpts.push_back( c->controlPoint(i));
}
}
else
{
mcpts.push_back( c->controlPoint(0) );
mcpts.push_back( c->controlPoint( c->numCtrlPnts()-1 ) );
}
}
else
{
Structure::Sheet* s = dynamic_cast<Structure::Sheet *>(n);
if ( pointsLevel == 1)
{
for (int i = 0; i < (int) s->numCtrlPnts(); ++i)
{
mcpts.push_back( s->controlPoint(i));
}
}
else
{
int nu = s->numUCtrlPnts(), nv = s->numVCtrlPnts();
mcpts.push_back( s->surface.GetControlPoint(0,0) );
mcpts.push_back( s->surface.GetControlPoint(nu-1,0) );
mcpts.push_back( s->surface.GetControlPoint(nu-1,nv-1) );
mcpts.push_back( s->surface.GetControlPoint(0,nv-1) );
}
}
}
return mcpts.size();
}
void RelationDetector::vectorPts2MatrixPts(const std::vector<Eigen::Vector3d>& ptsin, Eigen::MatrixXd& ptsout)
{
ptsout.resize(ptsin.size(), 3);
for ( int i = 0; i < (int)ptsin.size(); ++i)
{
ptsout.row(i) = ptsin[i];
}
}
std::vector<Eigen::Vector3d> RelationDetector::matrixPts2VectorPts(Eigen::MatrixXd& ptsin)
{
std::vector<Eigen::Vector3d> ptsout;
for ( int i = 0; i < ptsin.rows(); ++i)
{
ptsout.push_back( ptsin.row(i));
}
return ptsout;
}
Eigen::Vector3d RelationDetector::curve2vectorNormalized(Structure::Node * n)
{
const Vector3& bpn = n->controlPoint(0);
const Vector3& epn = n->controlPoint(n->numCtrlPnts()-1);
Eigen::Vector3d vec = bpn - epn;
vec.normalize();
return vec;
}
std::vector<Point_3> RelationDetector::sheet2rect(Structure::Node * n)
{
Structure::Sheet* s = dynamic_cast<Structure::Sheet *>(n);
std::vector<Point_3> result;
Vector3 v = s->surface.GetControlPoint(0,0);
result.push_back( Point_3(v.x(), v.y(), v.z()) );
v = s->surface.GetControlPoint(s->surface.GetNumCtrlPoints(0)-1,0);
result.push_back( Point_3(v.x(), v.y(), v.z()) );
v = s->surface.GetControlPoint(0,s->surface.GetNumCtrlPoints(1)-1);
result.push_back( Point_3(v.x(), v.y(), v.z()) );
v = s->surface.GetControlPoint(s->surface.GetNumCtrlPoints(0)-1,s->surface.GetNumCtrlPoints(1)-1);
result.push_back( Point_3(v.x(), v.y(), v.z()) );
return result;
}
Segment_3 RelationDetector::curve2segment(Structure::Node * n)
{
const Vector3& bpn = n->controlPoint(0);
const Vector3& epn = n->controlPoint(n->numCtrlPnts()-1);
Segment_3 seg(Point_3(bpn.x(),bpn.y(),bpn.z()), Point_3(epn.x(),epn.y(),epn.z()));
return seg;
}
Line_3 RelationDetector::curve2line(Structure::Node * n)
{
return Line_3(curve2segment(n));
}
void RelationDetector::sheet2plane(Structure::Sheet * s, Vector3& pos, Vector3& normal)
{
std::vector<Vector3> nf = noFrame();
s->get(Vector4d(0.5,0.5,0,0), pos, nf);
normal = nf[2];
}
bool RelationDetector::node2direction(Structure::Node * n, Vector_3& result)
{
bool iscurve(true);
if ( 0 == Structure::CURVE.compare( n->type()) )
{
Line_3 line = curve2line(n);
result = line.direction;
}
else
{
Vector3 point, normal;
sheet2plane(dynamic_cast<Structure::Sheet *>(n), point, normal);
result = Vector_3(normal.x(), normal.y(), normal.z());
iscurve = false;
}
result.normalize();
return iscurve;
}
// bSource == true means that the id is from target shape, & we want to find its corresponded node in source shape, then graph should be source
std::vector<Structure::Node*> RelationDetector::findNodesInST(QString id, Structure::Graph *graph, QVector<PART_LANDMARK> &corres, bool bSource)
{
QVector<QString> ids;
if ( bSource)
{
for ( int i = 0; i < (int) corres.size(); ++i)
{
QVector<QString> ids2 = corres[i].second;
for ( int j = 0; j < ids2.size(); ++j)
{
if ( ids2[j] == id)
{
ids = corres[i].first;
break;
}
}
if ( !ids.isEmpty() )
break;
}
}
else
{
for ( int i = 0; i < (int) corres.size(); ++i)
{
QVector<QString> ids1 = corres[i].first;
for ( int j = 0; j < ids1.size(); ++j)
{
if ( ids1[j] == id)
{
ids = corres[i].second;
break;
}
}
if ( !ids.isEmpty() )
break;
}
}
std::vector<Structure::Node*> result;
for ( int i = 0; i < (int) ids.size(); ++i)
{
result.push_back( graph->getNode(ids[i]) );
}
return result;
}
std::vector<Structure::Node*> RelationDetector::findNodesInB(QString id, Structure::Graph *graph, QVector<PART_LANDMARK> &corres, bool bSource)
{
std::vector<Structure::Node*> result;
if ( bSource)
{
for ( int i = 0; i < (int) corres.size(); ++i)
{
QVector<QString> ids1 = corres[i].first;
for ( int j = 0; j < ids1.size(); ++j)
{
if ( id == ids1[j])
{
QVector<QString> ids2 = corres[i].second;
if ( ids1.size() >= ids2.size()) // 1-1 or *-1
{
Structure::Node* tmpNode = graph->getNode(id);
if ( tmpNode != NULL)
result.push_back( tmpNode );
}
else // 1-*
{
for ( int k = 0; k < ids2.size(); ++k)
{
QString str; str.setNum(k);
Structure::Node* tmpNode = graph->getNode(id + "_" + str );
if ( tmpNode != NULL)
result.push_back( tmpNode);
}
}
return result;
}
}
}
// 1-0
Structure::Node* tmpNode = graph->getNode(id);
if ( tmpNode != NULL)
result.push_back( tmpNode );
}
else // is target
{
for ( int i = 0; i < (int) corres.size(); ++i)
{
QVector<QString> ids2 = corres[i].second;
for ( int j = 0; j < ids2.size(); ++j)
{
if ( id == ids2[j])
{
QVector<QString> ids1 = corres[i].first;
if ( ids1.size() >= ids2.size()) // 1-1 or *-1
{
for ( int k = 0; k < ids1.size(); ++k)
{
Structure::Node* tmpNode = graph->getNode(ids1[k]);
if ( tmpNode != NULL)
result.push_back( tmpNode );
}
}
else // 1-*
{
QString str; str.setNum(j);
Structure::Node* tmpNode = graph->getNode(ids1[0] + "_" + str );
if ( tmpNode != NULL)
result.push_back( tmpNode);
}
return result;
}
}
}
// 0-1
Structure::Node* tmpNode = graph->getNode(id + "_null");
if ( tmpNode != NULL)
result.push_back( tmpNode );
}
//result.push_back( graph->getNode(id) );
return result;
}
void RelationDetector::createPlane(Structure::Node *n1,Structure::Node *n2, Eigen::Vector3d& point, Eigen::Vector3d& normal)
{
Segment_3 s0 = curve2segment(n1);
Segment_3 s2 = curve2segment(n2);
// use middle point of 2 segment to generate a new segment, avoiding that s0 & s2 are parallel
Point_3 p0( 0.5*(s0.point(0).x() + s0.point(1).x()),
0.5*(s0.point(0).y() + s0.point(1).y()),
0.5*(s0.point(0).z() + s0.point(1).z()) );
Point_3 p2(0.5*(s2.point(0).x() + s2.point(1).x()),
0.5*(s2.point(0).y() + s2.point(1).y()),
0.5*(s2.point(0).z() + s2.point(1).z()) );
Segment_3 s1 = Segment_3(p0,p2);
///////
Point_3 cp( 0.25*(s0.point(0).x() + s0.point(1).x() + s2.point(0).x() + s2.point(1).x()),
0.25*(s0.point(0).y() + s0.point(1).y() + s2.point(0).y() + s2.point(1).y()),
0.25*(s0.point(0).z() + s0.point(1).z() + s2.point(0).z() + s2.point(1).z()) );
point[0] = cp.x(); point[1] = cp.y(); point[2] = cp.z();
Vector_3 v0 = cross_product(s1,s0);
Vector_3 v2 = cross_product(s1,s2);
if ( v0.squaredNorm() > v2.squaredNorm())
{
normal[0] = v0.x(); normal[1] = v0.y(); normal[2] = v0.z();
}
else
{
normal[0] = v2.x(); normal[1] = v2.y(); normal[2] = v2.z();
}
normal.normalize();
}
double RelationDetector::computeRefSymmetryGroupDeviation(GroupRelation& gr, int pointLevel)
{
std::vector<Structure::Node*> nodes;
for ( int i = 0; i < (int) gr.ids.size(); ++i)
{
nodes.push_back( graph_->getNode(gr.ids[i]) );
}
double minErr = std::numeric_limits<double>::max();
std::pair<int, int> minNodes;
Eigen::Vector3d point, normal;
double err;
for ( int i = 0; i < (int) gr.ids.size(); ++i)
{
for ( int j = i+1; j < (int) gr.ids.size(); ++j)
{
findRefPlane(nodes[i], nodes[j], point,normal);
if ( normal.squaredNorm() == 0)
continue;
err = errorOfRefSymmGroup(nodes, point, normal, pointLevel);
if ( err < minErr)
{
minErr = err;
minNodes = std::make_pair(i,j);
}
}
}
findRefPlane(nodes[minNodes.first], nodes[minNodes.second], gr.point, gr.normal);
if ( gr.normal.squaredNorm() == 0)
minErr = 0;
else
minErr = errorOfRefSymmGroup(nodes, gr.point, gr.normal, pointLevel);
return minErr/gr.ids.size();
}
double RelationDetector::computeAxisSymmetryGroupDeviationSortParts(GroupRelation& gr, int pointLevel)
{
double result(0.0);
int n = gr.ids.size();
double angle = 2*3.1415926/n;
QVector<QString> ids;
ids.push_back(gr.ids.last());
gr.ids.pop_back();
double min_dist, mean_dist, max_dist;
while(gr.ids.size())
{
Structure::Node* n1 = graph_->getNode(ids.last());
Eigen::MatrixXd verts1 = node2matrix(n1, pointLevel);
double err(std::numeric_limits<double>::max());
int k(0);
for ( int i = 0; i < (int) gr.ids.size(); ++i)
{
Structure::Node* n2 = graph_->getNode(gr.ids[i]);
Eigen::MatrixXd verts2 = node2matrix(n2, pointLevel);
Eigen::MatrixXd newverts2;
rotate_points3d(verts2, gr.center, gr.direction, angle, newverts2);
distanceBetween(verts1, newverts2, min_dist, mean_dist, max_dist);
if ( mean_dist < err)
{
err = mean_dist;
k = i;
}
}
result += err;
ids.push_back(gr.ids[k]);
gr.ids.remove(k);
}
for ( QVector<QString>::iterator it = ids.begin(); it != ids.end(); ++it)
gr.ids.push_back(*it);
return result/(ids.size()-1);
}
double RelationDetector::computeAxisSymmetryGroupDeviation(const GroupRelation& gr, int pointLevel)
{
double result(0.0);
int n = gr.ids.size();
double angle = 2*3.1415926/n;
double min_dist, mean_dist, max_dist;
for ( int i = 0; i < n; ++i)
{
Structure::Node* n1 = graph_->getNode(gr.ids[i]);
Eigen::MatrixXd verts1 = node2matrix(n1, pointLevel);
Structure::Node* n2 = graph_->getNode(gr.ids[(i+1)%n]);
Eigen::MatrixXd verts2 = node2matrix(n2, pointLevel);
Eigen::MatrixXd newverts2;
rotate_points3d(verts2, gr.center, gr.direction, angle, newverts2);
distanceBetween(verts1, newverts2, min_dist, mean_dist, max_dist);
result += mean_dist;
}
return result/n;
}
void RelationDetector::findRefPlane(Structure::Node* n1, Structure::Node* n2, Eigen::Vector3d& center,Eigen::Vector3d& normal)
{
Eigen::Vector3d c1 = computeCptsCenter(n1);
Eigen::Vector3d c2 = computeCptsCenter(n2);
normal = c1 - c2;
if ( normal.squaredNorm() != 0)
normal.normalize();
center = (c1+c2)*0.5;
}
Eigen::Vector3d RelationDetector::computeCptsCenter(Structure::Node* nn)
{
Eigen::MatrixXd verts;
vectorPts2MatrixPts(nn->controlPoints(), verts);
Eigen::Vector3d center( verts.col(0).mean(), verts.col(1).mean(),verts.col(2).mean() );
return center;
}
double RelationDetector::errorOfRefSymmGroup(std::vector<Structure::Node*> &nodes, Eigen::Vector3d& center, Eigen::Vector3d& normal, int pointLevel)
{
std::vector<double> error;
std::vector<bool> bDone(nodes.size(),false);
double min_dist, mean_dist, max_dist;
for ( int i = 0; i < (int) nodes.size(); ++i)
{
if ( bDone[i] ) continue;
Eigen::MatrixXd newverts1;
reflect_points3d(node2matrix(nodes[i], pointLevel), center, normal, newverts1);
double err = std::numeric_limits<double>::max();
int minIdx(0);
for ( int j = i; j < (int) nodes.size(); ++j)
{
distanceBetween( node2matrix(nodes[j], pointLevel), newverts1, min_dist, mean_dist, max_dist);
if ( mean_dist < err)
{
err = mean_dist;
minIdx = j;
}
}
error.push_back(err);
bDone[i] = true; bDone[minIdx] = true;
}
return std::accumulate( error.begin(), error.end(), 0.0);
}
void RelationDetector::computeTransGroupInfo(GroupRelation &gr, QSet<QString>& ids)
{
QSet<QString>::iterator it = ids.begin();
Structure::Node* n = graph_->getNode(*it);
Vector_3 direction(0,0,0);
node2direction(n, direction);
gr.ids.push_back(*it);
++it;
Vector_3 dc;
int k(1);
for (; it!=ids.end(); ++it,++k )
{
Structure::Node* nn = graph_->getNode(*it);
node2direction(nn, dc);
if ( dot(direction, dc) > 0)
direction = direction + dc;
else
direction = direction - dc;
direction = direction/sqrt(direction.squaredNorm());
gr.ids.push_back(*it);
}
gr.direction = direction;
gr.diameter = computeGroupDiameter(gr);
computeGroupCenter(gr);
}