/** *find the tangential points from a given point, i.e., the tangent lines should pass * the given point and tangential points * *Author: Dongxu Li */ RS_VectorSolutions RS_Circle::getTangentPoint(const RS_Vector& point) const { RS_VectorSolutions ret; double r2(getRadius()*getRadius()); if(r2<RS_TOLERANCE*RS_TOLERANCE) return ret; //circle too small RS_Vector vp(point-getCenter()); double c2(vp.squared()); if(c2<r2-getRadius()*2.*RS_TOLERANCE) { //inside point, no tangential point return ret; } if(c2>r2+getRadius()*2.*RS_TOLERANCE) { //external point RS_Vector vp1(-vp.y,vp.x); vp1*=getRadius()*sqrt(c2-r2)/c2; vp *= r2/c2; vp += getCenter(); if(vp1.squared()>RS_TOLERANCE*RS_TOLERANCE) { ret.push_back(vp+vp1); ret.push_back(vp-vp1); return ret; } } ret.push_back(point); return ret; }
RS_Vector RS_Spline::getNearestEndpoint(const RS_Vector& coord, double* dist)const { double minDist = RS_MAXDOUBLE; RS_Vector ret(false); if(! data.closed) { // no endpoint for closed spline RS_Vector vp1(getStartpoint()); RS_Vector vp2(getEndpoint()); double d1( (coord-vp1).squared()); double d2( (coord-vp2).squared()); if( d1<d2){ ret=vp1; minDist=sqrt(d1); }else{ ret=vp2; minDist=sqrt(d2); } // for (int i=0; i<data.controlPoints.count(); i++) { // d = (data.controlPoints.at(i)).distanceTo(coord); // if (d<minDist) { // minDist = d; // ret = data.controlPoints.at(i); // } // } } if (dist!=nullptr) { *dist = minDist; } return ret; }
double Cara::getAnguloMax(Malla *malla) { assert(malla != 0); //obtenemos los indices de los arcos de la cara Vect normal_cara = this->getNormal(malla); Punto p1, p2, p3; Vect v1, v2, prod_cruz; double angulo; double angulo_max = 0; for(int i=0; i<num_elem; i++) { p1 = malla->getNodo(ind_nodos[i])->getPunto(); p2 = malla->getNodo(ind_nodos[int(fmod(i+1,num_elem))])->getPunto(); p3 = malla->getNodo(ind_nodos[int(fmod(i+2,num_elem))])->getPunto(); Vect vp1(p1); Vect vp2(p2); Vect vp3(p3); v1 = vp1 - vp2; v2 = vp3 - vp2; prod_cruz = v1.prodCruz(v2); angulo = v1.getAngulo(v2); if(normal_cara.prodPunto(prod_cruz) > 0) { angulo = 2*PI - angulo; } if(angulo > angulo_max) { angulo_max = angulo; } } return angulo_max; }
void RS_Image::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) { data.insertionPoint.mirror(axisPoint1, axisPoint2); RS_Vector vp0(0.,0.); RS_Vector vp1( axisPoint2-axisPoint1 ); data.uVector.mirror(vp0,vp1); data.vVector.mirror(vp0,vp1); calculateBorders(); }
/** * find the closest grid point *@return the closest grid to given point *@coord: the given point */ RS_Vector RS_Grid::snapGrid(const RS_Vector& coord) const { if( cellV.x<RS_TOLERANCE || cellV.y<RS_TOLERANCE) return coord; RS_Vector vp(coord-baseGrid); if(isometric){ //use remainder instead of fmod to locate the left-bottom corner for both positive and negative displacement RS_Vector vp1( vp-RS_Vector( remainder(vp.x-0.5*cellV.x,cellV.x)+0.5*cellV.x, remainder(vp.y-0.5*cellV.y,cellV.y)+0.5*cellV.y)); RS_VectorSolutions sol({vp1,vp1+cellV,vp1+cellV*0.5, vp1+RS_Vector(cellV.x,0.), vp1+RS_Vector(0.,cellV.y)}); vp1=sol.getClosest(vp); return baseGrid+vp1; }else{ return baseGrid+vp-RS_Vector(remainder(vp.x,cellV.x),remainder(vp.y,cellV.y)); } }
RS_Vector RS_Circle::getNearestOrthTan(const RS_Vector& coord, const RS_Line& normal, bool /*onEntity = false*/) { if ( !coord.valid) { return RS_Vector(false); } RS_Vector vp0(coord-getCenter()); RS_Vector vp1(normal.getAngle1()); double d=RS_Vector::dotP(vp0,vp1); if(d >= 0. ) { return getCenter() + vp1*getRadius(); }else{ return getCenter() - vp1*getRadius(); } }
/** * this function creates offset *@coord, position indicates the direction of offset *@distance, distance of offset * return true, if success, otherwise, false * *Author: Dongxu Li */ bool RS_Line::offset(const RS_Vector& coord, const double& distance) { RS_Vector direction(getEndpoint()-getStartpoint()); double ds(direction.magnitude()); if(ds< RS_TOLERANCE) return false; direction /= ds; RS_Vector vp(coord-getStartpoint()); RS_Vector vp1(getStartpoint() + direction*(RS_Vector::dotP(direction,vp))); //projection direction.set(-direction.y,direction.x); //rotate pi/2 if(RS_Vector::dotP(direction,vp)<0.) { direction *= -1.; } direction*=distance; move(direction); moveBorders(direction); return true; }
Vect Cara::getNormal(Malla *malla) { assert(malla != 0); Punto p1,p2,p3; p1 = malla->getNodo(ind_nodos[0])->getPunto(); p2 = malla->getNodo(ind_nodos[1])->getPunto(); p3 = malla->getNodo(ind_nodos[2])->getPunto(); Vect vp1(p1); Vect vp2(p2); Vect vp3(p3); Vect v12 = vp2 - vp1; Vect v13 = vp3 - vp2; Vect N = v12.prodCruz(v13); N.normalizar(); return N; }
/** * finds out which angles this dimension actually measures. * * @param ang1 Reference will return the start angle * @param ang2 Reference will return the end angle * @param reversed Reference will return the reversed flag. * * @return true: on success */ bool RS_DimAngular::getAngles(double& ang1, double& ang2, bool& reversed, RS_Vector& p1, RS_Vector& p2) { RS_Vector vp0(edata.definitionPoint4 - getCenter()); RS_Vector vp1(edata.definitionPoint2 - edata.definitionPoint1); RS_Vector vp2(data.definitionPoint - edata.definitionPoint3); // project p0 to the basis of p1 and p2 // p0 = a1 p1 + a2 p2 // <p0.p1>= a1 |p1|^2 + a2 <p1.p2> // <p0,p2>= a1 <p1.p2> + a2 |p2|^2 // a1 = ( |p2|^2 <p0.p1> - <p1.p2><p0.p2>) /( |p1|^2 |p2|^2 - <p1.p2>^2) // a2 = ( |p1|^2 <p0.p2> - <p1.p2><p0.p1>) /( |p1|^2 |p2|^2 - <p1.p2>^2) double rp1=vp1.squared(); double rp2=vp2.squared(); double p0p1=RS_Vector::dotP(vp0,vp1); double p0p2=RS_Vector::dotP(vp0,vp2); double p1p2=RS_Vector::dotP(vp1,vp2); double d= rp1*rp2 - p1p2*p1p2; double a1=d*(rp2*p0p1-p1p2*p0p2); // we only need the sign, so, use multiply instead of division to avoid divid by zero; if ( a1 >= 0. ) { p1 = edata.definitionPoint2; } else { vp1 *= -1; p1 = edata.definitionPoint1; } a1 = d*(rp1*p0p2-p1p2*p0p1); if ( a1 >= 0. ) { p2 = data.definitionPoint; } else { vp2 *= -1; p2 = edata.definitionPoint3; } RS_Vector center = getCenter(); double ang = center.angleTo(edata.definitionPoint4); ang1=vp1.angle(); ang2=vp2.angle(); if ( ! RS_Math::isAngleBetween(ang, ang1,ang2,false) ) { reversed = true; } return true; }
void DrawTContact(QPainter *p,pigalePaint *paint) {GeometricGraph G(paint->GCP); Prop<Tpoint> hp1(G.Set(tvertex()),PROP_DRAW_POINT_1); Prop<Tpoint> hp2(G.Set(tvertex()),PROP_DRAW_POINT_2); Prop<Tpoint> vp1(G.Set(tvertex()),PROP_DRAW_POINT_3); Prop<Tpoint> vp2(G.Set(tvertex()),PROP_DRAW_POINT_4); Prop<Tpoint> postxt(G.Set(tvertex()),PROP_DRAW_POINT_5); Prop1<double> sizetext(G.Set(),PROP_DRAW_DBLE_1); tvertex v; // Draw horizontals and verticals for(v = 1;v <= G.nv();v++) {if(hp1[v].x() > .0)paint->DrawSeg(p,hp1[v],hp2[v],Black); if(vp1[v].x() > .0)paint->DrawSeg(p,vp1[v],vp2[v],Black); } // Draw text p->setFont(QFont("sans",Min((int)(sizetext() * Min(paint->xscale,paint->yscale) + .5),13))); for(v=1; v <= G.nv();v++) paint->DrawText(p,postxt[v],v,G.vcolor[v],0); }
/** *create circle inscribled in a triangle * *Author: Dongxu Li */ bool RS_Circle::createInscribe(const RS_Vector& coord, const std::vector<RS_Line*>& lines){ if(lines.size()<3) return false; std::vector<RS_Line*> tri(lines); RS_VectorSolutions sol=RS_Information::getIntersectionLineLine(tri[0],tri[1]); if(sol.getNumber() == 0 ) {//move parallel to opposite std::swap(tri[1],tri[2]); sol=RS_Information::getIntersectionLineLine(tri[0],tri[1]); } if(sol.getNumber() == 0 ) return false; RS_Vector vp0(sol.get(0)); sol=RS_Information::getIntersectionLineLine(tri[2],tri[1]); if(sol.getNumber() == 0 ) return false; RS_Vector vp1(sol.get(0)); RS_Vector dvp(vp1-vp0); double a(dvp.squared()); if( a< RS_TOLERANCE2) return false; //three lines share a common intersecting point RS_Vector vp(coord - vp0); vp -= dvp*(RS_Vector::dotP(dvp,vp)/a); //normal component RS_Vector vl0(tri[0]->getEndpoint() - tri[0]->getStartpoint()); a=dvp.angle(); double angle0(0.5*(vl0.angle() + a)); if( RS_Vector::dotP(vp,vl0) <0.) { angle0 += 0.5*M_PI; } RS_Line line0(vp0, vp0+RS_Vector(angle0));//first bisecting line vl0=(tri[2]->getEndpoint() - tri[2]->getStartpoint()); angle0=0.5*(vl0.angle() + a+M_PI); if( RS_Vector::dotP(vp,vl0) <0.) { angle0 += 0.5*M_PI; } RS_Line line1(vp1, vp1+RS_Vector(angle0));//second bisection line sol=RS_Information::getIntersectionLineLine(&line0,&line1); if(sol.getNumber() == 0 ) return false; bool ret=createFromCR(sol.get(0),tri[1]->getDistanceToPoint(sol.get(0))); if(!ret) return false; for(auto p: lines){ if(! p->isTangent(data)) return false; } return true; }
double Cara::getAnguloVertice(int ind_nodo, Malla *malla) { assert(malla != 0 && ind_nodo >= 0 && ind_nodo <= malla->getMaxIndiceNodos()); Punto p1, p2, p3; Vect v1, v2; double angulo; for(int i=0; i<(int)ind_nodos.size(); i++) { // Buscamos que el nodo ind_nodo esté al medio if(ind_nodo != ind_nodos[int(fmod(i+1,ind_nodos.size()))]) continue; p1 = malla->getNodo(ind_nodos[i])->getPunto(); p2 = malla->getNodo(ind_nodos[int(fmod(i+1,num_elem))])->getPunto(); p3 = malla->getNodo(ind_nodos[int(fmod(i+2,num_elem))])->getPunto(); Vect vp1(p1); Vect vp2(p2); Vect vp3(p3); v1 = vp1 - vp2; v2 = vp3 - vp2; angulo = v1.getAngulo(v2); return angulo; } // el indice del nodo no pertenece a la cara assert(false); return 0; }
/** * Draws the meta grid. * * @see drawIt() */ void RS_GraphicView::drawMetaGrid(RS_Painter *painter) { if (!(grid && isGridOn()) /*|| grid->getMetaSpacing()<0.0*/) { return; } //draw grid after metaGrid to avoid overwriting grid points by metaGrid lines //bug# 3430258 grid->updatePointArray(); RS_Pen pen(metaGridColor, RS2::Width00, RS2::DotLine); painter->setPen(pen); RS_Vector dv=grid->getMetaGridWidth().scale(factor); double dx=fabs(dv.x); double dy=fabs(dv.y); //potential bug, need to recover metaGrid.width // draw meta grid: auto mx = grid->getMetaX(); for(auto const& x: mx){ painter->drawLine(RS_Vector(toGuiX(x), 0), RS_Vector(toGuiX(x), getHeight())); if(grid->isIsometric()){ painter->drawLine(RS_Vector(toGuiX(x)+0.5*dx, 0), RS_Vector(toGuiX(x)+0.5*dx, getHeight())); } } auto my = grid->getMetaY(); if(grid->isIsometric()){//isometric metaGrid dx=fabs(dx); dy=fabs(dy); if(!my.size()|| dx<1||dy<1) return; RS_Vector baseMeta(toGui(RS_Vector(mx[0],my[0]))); // x-x0=k*dx, x-remainder(x-x0,dx) RS_Vector vp0(-remainder(-baseMeta.x,dx)-dx,getHeight()-remainder(getHeight()-baseMeta.y,dy)+dy); RS_Vector vp1(vp0); RS_Vector vp2(getWidth()-remainder(getWidth()-baseMeta.x,dx)+dx,vp0.y); RS_Vector vp3(vp2); int cmx = round((vp2.x - vp0.x)/dx); int cmy = round((vp0.y +remainder(-baseMeta.y,dy)+dy)/dy); for(int i=cmx+cmy+2;i>=0;i--){ if ( i <= cmx ) { vp0.x += dx; vp2.y -= dy; }else{ vp0.y -= dy; vp2.x -= dx; } if ( i <= cmy ) { vp1.y -= dy; vp3.x -= dx; }else{ vp1.x += dx; vp3.y -= dy; } painter->drawLine(vp0,vp1); painter->drawLine(vp2,vp3); } }else{//orthogonal for(auto const& y: my){ painter->drawLine(RS_Vector(0, toGuiY(y)), RS_Vector(getWidth(), toGuiY(y))); } } }
void World::update(float timeStep) { for(BodyList::iterator it = m_bodies.begin(); it != m_bodies.end(); ++it){ if(!(*it)->getBodyDef().isStatic()){ (*it)->acceleration(m_gravitation*timeStep); (*it)->update(timeStep); } } for(BodyList::iterator it = m_bodies.begin(); it != m_bodies.end(); ++it){ BodyList::iterator ith = it; ith++; for(BodyList::iterator it2 = ith; it2 != m_bodies.end(); ++it2){ if(!((*it)->getBodyDef().isStatic()) or !((*it2)->getBodyDef().isStatic())){ if((*it)->getShape()->collide((*it2)->getShape().get())){ Polygon *p1 = static_cast<Polygon *>((*it)->getShape().get()); Polygon *p2 = static_cast<Polygon *>((*it2)->getShape().get()); bool b = true; Line cl; Vector2f point1 = p1->getTransformedPoint(p1->getNumberOfPoints()-1); Vector2f point2 = p2->getTransformedPoint(p2->getNumberOfPoints()-1); for(size_t i = 0; i < p1->getNumberOfPoints(); ++i){ Line line1(p1->getTransformedPoint(i), point1-p1->getTransformedPoint(i)); point1 = p1->getTransformedPoint(i); for(size_t j = 0; j < p2->getNumberOfPoints(); ++j){ Line line2(p2->getTransformedPoint(j), point2-p2->getTransformedPoint(j)); point2 = p2->getTransformedPoint(j); float t1 = line1.intersects(line2); float t2 = line2.intersects(line1); if(0.f < t1 and t1 < 1.f and 0.f < t2 and t2 < 1.f){ Vector2f vec = line1.getPoint() + t1 * line1.getDirectionVector(); if(b){ cl.setPoint(vec); b = false; } else { cl.setDirectionVector(vec - cl.getPoint()); } } } } Vector2f n = cl.getDirectionVector().normal(); //n.normalize(); Vector2f p = cl.getPoint()+(cl.getDirectionVector()/2.f); Body::Ptr b1 = (*it); Body::Ptr b2 = (*it2); mx::Vector2f r1(p - b1->getShape()->getPosition()); mx::Vector2f r2(p - b2->getShape()->getPosition()); mx::Vector2f rap = r1.normal(); mx::Vector2f rbp = r2.normal(); float rapn = dot(rap, n); float rbpn = dot(rbp, n); mx::Vector2f vp1(b1->getVelocity()+b1->getAngularVelocity()*rap); mx::Vector2f vp2(b2->getVelocity()+b2->getAngularVelocity()*rbp); mx::Vector2f vab(vp2 - vp1); float M1Inv = 0; float M2Inv = 0; float I1Inv = 0; float I2Inv = 0; if(!b1->getBodyDef().isStatic()){ M1Inv = 1.f / b1->getBodyDef().getMass(); I1Inv = 1.f / b1->getBodyDef().getMomentOfInertia(); } if(!b2->getBodyDef().isStatic()){ M2Inv = 1.f / b2->getBodyDef().getMass(); I2Inv = 1.f / b2->getBodyDef().getMomentOfInertia(); } float j = -(1+(b1->getBodyDef().getElasticity()+b2->getBodyDef().getElasticity())/2.f) * dot(vab, n); j /= dot(n, n)*(M1Inv+M2Inv) + rapn*rapn*I1Inv + rbpn*rbpn*I2Inv; b1->acceleration(-(j*M1Inv) * n); b2->acceleration( (j*M2Inv) * n); b1->angularAcceleration(-j*I1Inv * rapn); b2->angularAcceleration( j*I2Inv * rbpn); Shape::Ptr s1 = b1->getShape(); Shape::Ptr s2 = b2->getShape(); mx::Vector2f MTD = s1->MTD(s2.get()); float MInv = M1Inv + M2Inv; s1->setPosition(s1->getPosition()+MTD*(M1Inv/MInv)); s2->setPosition(s2->getPosition()-MTD*(M2Inv/MInv)); } } } } }
bool fill_hole(std::vector<std::size_t> const & hole, UniGraph const & graph, mve::TriangleMesh::ConstPtr mesh, mve::MeshInfo const & mesh_info, std::vector<std::vector<VertexProjectionInfo> > * vertex_projection_infos, std::vector<TexturePatch::Ptr> * texture_patches) { mve::TriangleMesh::FaceList const & mesh_faces = mesh->get_faces(); mve::TriangleMesh::VertexList const & vertices = mesh->get_vertices(); std::map<std::size_t, std::set<std::size_t> > tmp; for (std::size_t const face_id : hole) { std::size_t const v0 = mesh_faces[face_id * 3]; std::size_t const v1 = mesh_faces[face_id * 3 + 1]; std::size_t const v2 = mesh_faces[face_id * 3 + 2]; tmp[v0].insert(face_id); tmp[v1].insert(face_id); tmp[v2].insert(face_id); } std::size_t const num_vertices = tmp.size(); /* Only fill small holes. */ if (num_vertices > MAX_HOLE_NUM_FACES) return false; /* Calculate 2D parameterization using the technique from libremesh/patch2d, * which was published as sourcecode accompanying the following paper: * * Isotropic Surface Remeshing * Simon Fuhrmann, Jens Ackermann, Thomas Kalbe, Michael Goesele */ std::size_t seed = -1; std::vector<bool> is_border(num_vertices, false); std::vector<std::vector<std::size_t> > adj_verts_via_border(num_vertices); /* Index structures to map from local <-> global vertex id. */ std::map<std::size_t, std::size_t> g2l; std::vector<std::size_t> l2g(num_vertices); /* Index structure to determine column in matrix/vector. */ std::vector<std::size_t> idx(num_vertices); std::size_t num_border_vertices = 0; bool disk_topology = true; std::map<std::size_t, std::set<std::size_t> >::iterator it = tmp.begin(); for (std::size_t j = 0; j < num_vertices; ++j, ++it) { std::size_t vertex_id = it->first; g2l[vertex_id] = j; l2g[j] = vertex_id; /* Check topology in original mesh. */ if (mesh_info[vertex_id].vclass != mve::MeshInfo::VERTEX_CLASS_SIMPLE) { /* Complex/Border vertex in original mesh */ disk_topology = false; break; } /* Check new topology and determine if vertex is now at the border. */ std::vector<std::size_t> const & adj_faces = mesh_info[vertex_id].faces; std::set<std::size_t> const & adj_hole_faces = it->second; std::vector<std::pair<std::size_t, std::size_t> > fan; for (std::size_t k = 0; k < adj_faces.size(); ++k) { std::size_t adj_face = adj_faces[k]; if (graph.get_label(adj_faces[k]) == 0 && adj_hole_faces.find(adj_face) != adj_hole_faces.end()) { std::size_t curr = adj_faces[k]; std::size_t next = adj_faces[(k + 1) % adj_faces.size()]; std::pair<std::size_t, std::size_t> pair(curr, next); fan.push_back(pair); } } std::size_t gaps = 0; for (std::size_t k = 0; k < fan.size(); k++) { std::size_t curr = fan[k].first; std::size_t next = fan[(k + 1) % fan.size()].first; if (fan[k].second != next) { ++gaps; for (std::size_t l = 0; l < 3; ++l) { if(mesh_faces[curr * 3 + l] == vertex_id) { std::size_t second = mesh_faces[curr * 3 + (l + 2) % 3]; adj_verts_via_border[j].push_back(second); } if(mesh_faces[next * 3 + l] == vertex_id) { std::size_t first = mesh_faces[next * 3 + (l + 1) % 3]; adj_verts_via_border[j].push_back(first); } } } } is_border[j] = gaps == 1; /* Check if vertex is now complex. */ if (gaps > 1) { /* Complex vertex in hole */ disk_topology = false; break; } if (is_border[j]) { idx[j] = num_border_vertices++; seed = vertex_id; } else { idx[j] = j - num_border_vertices; } } tmp.clear(); /* No disk or genus zero topology */ if (!disk_topology || num_border_vertices == 0) return false; std::vector<std::size_t> border; border.reserve(num_border_vertices); std::size_t prev = seed; std::size_t curr = seed; while (prev == seed || curr != seed) { std::size_t next = std::numeric_limits<std::size_t>::max(); std::vector<std::size_t> const & adj_verts = adj_verts_via_border[g2l[curr]]; for (std::size_t adj_vert : adj_verts) { assert(is_border[g2l[adj_vert]]); if (adj_vert != prev && adj_vert != curr) { next = adj_vert; break; } } if (next != std::numeric_limits<std::size_t>::max()) { prev = curr; curr = next; border.push_back(next); } else { /* No new border vertex */ border.clear(); break; } /* Loop within border */ if (border.size() > num_border_vertices) break; } if (border.size() != num_border_vertices) return false; float total_length = 0.0f; float total_projection_length = 0.0f; for (std::size_t j = 0; j < border.size(); ++j) { std::size_t vi0 = border[j]; std::size_t vi1 = border[(j + 1) % border.size()]; std::vector<VertexProjectionInfo> const & vpi0 = vertex_projection_infos->at(vi0); std::vector<VertexProjectionInfo> const & vpi1 = vertex_projection_infos->at(vi0); /* According to the previous checks (vertex class within the origial * mesh and boundary) there already has to be at least one projection * of each border vertex. */ assert(!vpi0.empty() && !vpi1.empty()); math::Vec2f vp0(0.0f), vp1(0.0f); for (VertexProjectionInfo const & info0 : vpi0) { for (VertexProjectionInfo const & info1 : vpi1) { if (info0.texture_patch_id == info1.texture_patch_id) { vp0 = info0.projection; vp1 = info1.projection; break; } } } total_projection_length += (vp0 - vp1).norm(); math::Vec3f const & v0 = vertices[vi0]; math::Vec3f const & v1 = vertices[vi1]; total_length += (v0 - v1).norm(); } float radius = total_projection_length / (2.0f * MATH_PI); if (total_length < std::numeric_limits<float>::epsilon()) return false; float length = 0.0f; std::vector<math::Vec2f> projections(num_vertices); for (std::size_t j = 0; j < border.size(); ++j) { float angle = 2.0f * MATH_PI * (length / total_length); projections[g2l[border[j]]] = math::Vec2f(std::cos(angle), std::sin(angle)); math::Vec3f const & v0 = vertices[border[j]]; math::Vec3f const & v1 = vertices[border[(j + 1) % border.size()]]; length += (v0 - v1).norm(); } typedef Eigen::Triplet<float, int> Triplet; std::vector<Triplet> coeff; std::size_t matrix_size = num_vertices - border.size(); Eigen::VectorXf xx(matrix_size), xy(matrix_size); if (matrix_size != 0) { Eigen::VectorXf bx(matrix_size); Eigen::VectorXf by(matrix_size); for (std::size_t j = 0; j < num_vertices; ++j) { if (is_border[j]) continue; std::size_t const vertex_id = l2g[j]; /* Calculate "Mean Value Coordinates" as proposed by Michael S. Floater */ std::map<std::size_t, float> weights; std::vector<std::size_t> const & adj_faces = mesh_info[vertex_id].faces; for (std::size_t adj_face : adj_faces) { std::size_t v0 = mesh_faces[adj_face * 3]; std::size_t v1 = mesh_faces[adj_face * 3 + 1]; std::size_t v2 = mesh_faces[adj_face * 3 + 2]; if (v1 == vertex_id) std::swap(v1, v0); if (v2 == vertex_id) std::swap(v2, v0); math::Vec3f v01 = vertices[v1] - vertices[v0]; float v01n = v01.norm(); math::Vec3f v02 = vertices[v2] - vertices[v0]; float v02n = v02.norm(); /* Ensure numerical stability */ if (v01n * v02n < std::numeric_limits<float>::epsilon()) return false; float alpha = std::acos(v01.dot(v02) / (v01n * v02n)); weights[g2l[v1]] += std::tan(alpha / 2.0f) / v01n; weights[g2l[v2]] += std::tan(alpha / 2.0f) / v02n; } std::map<std::size_t, float>::iterator it; float sum = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) sum += it->second; assert(sum > 0.0f); for (it = weights.begin(); it != weights.end(); ++it) it->second /= sum; bx[idx[j]] = 0.0f; by[idx[j]] = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) { if (is_border[it->first]) { std::size_t border_vertex_id = border[idx[it->first]]; bx[idx[j]] += projections[g2l[border_vertex_id]][0] * it->second; by[idx[j]] += projections[g2l[border_vertex_id]][1] * it->second; } else { coeff.push_back(Triplet(idx[j], idx[it->first], -it->second)); } } } for (std::size_t j = 0; j < matrix_size; ++j) { coeff.push_back(Triplet(j, j, 1.0f)); } typedef Eigen::SparseMatrix<float> SpMat; SpMat A(matrix_size, matrix_size); A.setFromTriplets(coeff.begin(), coeff.end()); Eigen::SparseLU<SpMat> solver; solver.analyzePattern(A); solver.factorize(A); xx = solver.solve(bx); xy = solver.solve(by); } float const max_hole_patch_size = MAX_HOLE_PATCH_SIZE; int image_size = std::min(std::floor(radius * 1.1f) * 2.0f, max_hole_patch_size); /* Ensure a minimum scale of one */ image_size += 2 * (1 + texture_patch_border); int scale = image_size / 2 - texture_patch_border; for (std::size_t j = 0, k = 0; j < num_vertices; ++j) { if (is_border[j]) { projections[j] = projections[j] * scale + image_size / 2; } else { projections[j] = math::Vec2f(xx[k], xy[k]) * scale + image_size / 2; ++k; } } mve::ByteImage::Ptr image = mve::ByteImage::create(image_size, image_size, 3); //DEBUG image->fill_color(*math::Vec4uc(0, 255, 0, 255)); std::vector<math::Vec2f> texcoords; texcoords.reserve(hole.size()); for (std::size_t const face_id : hole) { for (std::size_t j = 0; j < 3; ++j) { std::size_t const vertex_id = mesh_faces[face_id * 3 + j]; math::Vec2f const & projection = projections[g2l[vertex_id]]; texcoords.push_back(projection); } } TexturePatch::Ptr texture_patch = TexturePatch::create(0, hole, texcoords, image); std::size_t texture_patch_id; #pragma omp critical { texture_patches->push_back(texture_patch); texture_patch_id = texture_patches->size() - 1; } for (std::size_t j = 0; j < num_vertices; ++j) { std::size_t const vertex_id = l2g[j]; std::vector<std::size_t> const & adj_faces = mesh_info[vertex_id].faces; std::vector<std::size_t> faces; faces.reserve(adj_faces.size()); for (std::size_t adj_face : adj_faces) { if (graph.get_label(adj_face) == 0) { faces.push_back(adj_face); } } VertexProjectionInfo info = {texture_patch_id, projections[j], faces}; #pragma omp critical vertex_projection_infos->at(vertex_id).push_back(info); } return true; }
void generate_texture_patches(UniGraph const & graph, std::vector<TextureView> const & texture_views, mve::TriangleMesh::ConstPtr mesh, mve::VertexInfoList::ConstPtr vertex_infos, std::vector<std::vector<VertexProjectionInfo> > * vertex_projection_infos, std::vector<TexturePatch> * texture_patches) { util::WallTimer timer; mve::TriangleMesh::FaceList const & mesh_faces = mesh->get_faces(); mve::TriangleMesh::VertexList const & vertices = mesh->get_vertices(); vertex_projection_infos->resize(vertices.size()); std::size_t num_patches = 0; std::cout << "\tRunning... " << std::flush; #pragma omp parallel for schedule(dynamic) for (std::size_t i = 0; i < texture_views.size(); ++i) { std::vector<std::vector<std::size_t> > subgraphs; int const label = i + 1; graph.get_subgraphs(label, &subgraphs); std::list<TexturePatchCandidate> candidates; for (std::size_t j = 0; j < subgraphs.size(); ++j) { candidates.push_back(generate_candidate(label, texture_views[i], subgraphs[j], mesh)); } /* Merge candidates which contain the same image content. */ std::list<TexturePatchCandidate>::iterator it, sit; for (it = candidates.begin(); it != candidates.end(); ++it) { for (sit = candidates.begin(); sit != candidates.end();) { Rect<int> bounding_box = sit->bounding_box; if (it != sit && bounding_box.is_inside(&it->bounding_box)) { TexturePatch::Faces & faces = it->texture_patch.get_faces(); TexturePatch::Faces & ofaces = sit->texture_patch.get_faces(); faces.insert(faces.end(), ofaces.begin(), ofaces.end()); TexturePatch::Texcoords & texcoords = it->texture_patch.get_texcoords(); TexturePatch::Texcoords & otexcoords = sit->texture_patch.get_texcoords(); math::Vec2f offset; offset[0] = sit->bounding_box.min_x - it->bounding_box.min_x; offset[1] = sit->bounding_box.min_y - it->bounding_box.min_y; for (std::size_t i = 0; i < otexcoords.size(); ++i) { texcoords.push_back(otexcoords[i] + offset); } sit = candidates.erase(sit); } else { ++sit; } } } it = candidates.begin(); for (; it != candidates.end(); ++it) { std::size_t texture_patch_id; #pragma omp critical { texture_patches->push_back(it->texture_patch); texture_patch_id = num_patches++; } std::vector<std::size_t> const & faces = it->texture_patch.get_faces(); std::vector<math::Vec2f> const & texcoords = it->texture_patch.get_texcoords(); for (std::size_t i = 0; i < faces.size(); ++i) { std::size_t const face_id = faces[i]; std::size_t const face_pos = face_id * 3; for (std::size_t j = 0; j < 3; ++j) { std::size_t const vertex_id = mesh_faces[face_pos + j]; math::Vec2f const projection = texcoords[i * 3 + j]; VertexProjectionInfo info = {texture_patch_id, projection, {face_id}}; #pragma omp critical vertex_projection_infos->at(vertex_id).push_back(info); } } } } merge_vertex_projection_infos(vertex_projection_infos); std::size_t num_holes = 0; std::size_t num_hole_faces = 0; //if (!settings.skip_hole_filling) { { std::vector<std::vector<std::size_t> > subgraphs; graph.get_subgraphs(0, &subgraphs); #pragma omp parallel for schedule(dynamic) for (std::size_t i = 0; i < subgraphs.size(); ++i) { std::vector<std::size_t> const & subgraph = subgraphs[i]; std::map<std::size_t, std::set<std::size_t> > tmp; for (std::size_t const face_id : subgraph) { std::size_t const v0 = mesh_faces[face_id * 3]; std::size_t const v1 = mesh_faces[face_id * 3 + 1]; std::size_t const v2 = mesh_faces[face_id * 3 + 2]; tmp[v0].insert(face_id); tmp[v1].insert(face_id); tmp[v2].insert(face_id); } std::size_t const num_vertices = tmp.size(); /* Only fill small holes. */ if (num_vertices > 100) { //std::cerr << "Hole to large" << std::endl; continue; } /* Calculate 2D parameterization using the technique from libremesh/patch2d, * which was published as sourcecode accompanying the following paper: * * Isotropic Surface Remeshing * Simon Fuhrmann, Jens Ackermann, Thomas Kalbe, Michael Goesele */ std::size_t seed = -1; std::vector<bool> is_border(num_vertices, false); std::vector<std::vector<std::size_t> > adj_verts_via_border(num_vertices); /* Index structures to map from local <-> global vertex id. */ std::map<std::size_t, std::size_t> g2l; std::vector<std::size_t> l2g(num_vertices); /* Index structure to determine column in matrix/vector. */ std::vector<std::size_t> idx(num_vertices); std::size_t num_border_vertices = 0; bool disk_topology = true; std::map<std::size_t, std::set<std::size_t> >::iterator it = tmp.begin(); for (std::size_t j = 0; j < num_vertices; ++j, ++it) { std::size_t vertex_id = it->first; g2l[vertex_id] = j; l2g[j] = vertex_id; /* Check topology in original mesh. */ if (vertex_infos->at(vertex_id).vclass != mve::VERTEX_CLASS_SIMPLE) { //std::cerr << "Complex/Border vertex in original mesh" << std::endl; disk_topology = false; break; } /* Check new topology and determine if vertex is now at the border. */ std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces; std::set<std::size_t> const & adj_hole_faces = it->second; std::vector<std::pair<std::size_t, std::size_t> > fan; for (std::size_t k = 0; k < adj_faces.size(); ++k) { std::size_t adj_face = adj_faces[k]; if (graph.get_label(adj_faces[k]) == 0 && adj_hole_faces.find(adj_face) != adj_hole_faces.end()) { std::size_t curr = adj_faces[k]; std::size_t next = adj_faces[(k + 1) % adj_faces.size()]; std::pair<std::size_t, std::size_t> pair(curr, next); fan.push_back(pair); } } std::size_t gaps = 0; for (std::size_t k = 0; k < fan.size(); k++) { std::size_t curr = fan[k].first; std::size_t next = fan[(k + 1) % fan.size()].first; if (fan[k].second != next) { ++gaps; for (std::size_t l = 0; l < 3; ++l) { if(mesh_faces[curr * 3 + l] == vertex_id) { std::size_t second = mesh_faces[curr * 3 + (l + 2) % 3]; adj_verts_via_border[j].push_back(second); } if(mesh_faces[next * 3 + l] == vertex_id) { std::size_t first = mesh_faces[next * 3 + (l + 1) % 3]; adj_verts_via_border[j].push_back(first); } } } } is_border[j] = gaps == 1; /* Check if vertex is now complex. */ if (gaps > 1) { //std::cerr << "Complex vertex in hole" << std::endl; disk_topology = false; break; } if (is_border[j]) { idx[j] = num_border_vertices++; seed = vertex_id; } else { idx[j] = j - num_border_vertices; } } tmp.clear(); if (!disk_topology) continue; if (num_border_vertices == 0) { //std::cerr << "Genus zero topology" << std::endl; continue; } std::vector<std::size_t> border; border.reserve(num_border_vertices); std::size_t prev = seed; std::size_t curr = seed; while (prev == seed || curr != seed) { std::size_t next = std::numeric_limits<std::size_t>::max(); std::vector<std::size_t> const & adj_verts = adj_verts_via_border[g2l[curr]]; for (std::size_t adj_vert : adj_verts) { assert(is_border[g2l[adj_vert]]); if (adj_vert != prev && adj_vert != curr) { next = adj_vert; break; } } if (next != std::numeric_limits<std::size_t>::max()) { prev = curr; curr = next; border.push_back(next); } else { //std::cerr << "No new border vertex" << std::endl; border.clear(); break; } if (border.size() > num_border_vertices) { //std::cerr << "Loop within border" << std::endl; break; } } if (border.size() != num_border_vertices) { continue; } float total_length = 0.0f; float total_projection_length = 0.0f; for (std::size_t j = 0; j < border.size(); ++j) { std::size_t vi0 = border[j]; std::size_t vi1 = border[(j + 1) % border.size()]; std::vector<VertexProjectionInfo> const & vpi0 = vertex_projection_infos->at(vi0); std::vector<VertexProjectionInfo> const & vpi1 = vertex_projection_infos->at(vi0); /* According to the previous checks (vertex class within the origial * mesh and boundary) there already has to be at least one projection * of each border vertex. */ assert(!vpi0.empty() && !vpi1.empty()); math::Vec2f vp0(0.0f), vp1(0.0f); for (VertexProjectionInfo const & info0 : vpi0) { for (VertexProjectionInfo const & info1 : vpi1) { if (info0.texture_patch_id == info1.texture_patch_id) { vp0 = info0.projection; vp1 = info1.projection; break; } } } total_projection_length += (vp0 - vp1).norm(); math::Vec3f const & v0 = vertices[vi0]; math::Vec3f const & v1 = vertices[vi1]; total_length += (v0 - v1).norm(); } float radius = total_projection_length / (2.0f * MATH_PI); float length = 0.0f; std::vector<math::Vec2f> projections(num_vertices); for (std::size_t j = 0; j < border.size(); ++j) { float angle = 2.0f * MATH_PI * (length / total_length); projections[g2l[border[j]]] = math::Vec2f(std::cos(angle), std::sin(angle)); math::Vec3f const & v0 = vertices[border[j]]; math::Vec3f const & v1 = vertices[border[(j + 1) % border.size()]]; length += (v0 - v1).norm(); } typedef Eigen::Triplet<float, int> Triplet; std::vector<Triplet> coeff; std::size_t matrix_size = num_vertices - border.size(); Eigen::VectorXf xx(matrix_size), xy(matrix_size); if (matrix_size != 0) { Eigen::VectorXf bx(matrix_size); Eigen::VectorXf by(matrix_size); for (std::size_t j = 0; j < num_vertices; ++j) { if (is_border[j]) continue; std::size_t const vertex_id = l2g[j]; /* Calculate "Mean Value Coordinates" as proposed by Michael S. Floater */ std::map<std::size_t, float> weights; std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces; for (std::size_t adj_face : adj_faces) { std::size_t v0 = mesh_faces[adj_face * 3]; std::size_t v1 = mesh_faces[adj_face * 3 + 1]; std::size_t v2 = mesh_faces[adj_face * 3 + 2]; if (v1 == vertex_id) std::swap(v1, v0); if (v2 == vertex_id) std::swap(v2, v0); math::Vec3f v01 = vertices[v1] - vertices[v0]; float v01n = v01.norm(); math::Vec3f v02 = vertices[v2] - vertices[v0]; float v02n = v02.norm(); float alpha = std::acos(v01.dot(v02) / (v01n * v02n)); weights[g2l[v1]] += std::tan(alpha / 2.0f) / v01n; weights[g2l[v2]] += std::tan(alpha / 2.0f) / v02n; } std::map<std::size_t, float>::iterator it; float sum = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) sum += it->second; for (it = weights.begin(); it != weights.end(); ++it) it->second /= sum; bx[idx[j]] = 0.0f; by[idx[j]] = 0.0f; for (it = weights.begin(); it != weights.end(); ++it) { if (is_border[it->first]) { std::size_t border_vertex_id = border[idx[it->first]]; bx[idx[j]] += projections[g2l[border_vertex_id]][0] * it->second; by[idx[j]] += projections[g2l[border_vertex_id]][1] * it->second; } else { coeff.push_back(Triplet(idx[j], idx[it->first], -it->second)); } } } for (std::size_t j = 0; j < matrix_size; ++j) { coeff.push_back(Triplet(j, j, 1.0f)); } typedef Eigen::SparseMatrix<float> SpMat; SpMat A(matrix_size, matrix_size); A.setFromTriplets(coeff.begin(), coeff.end()); Eigen::SparseLU<SpMat> solver; solver.analyzePattern(A); solver.factorize(A); xx = solver.solve(bx); xy = solver.solve(by); } int image_size = std::floor(radius * 1.1f) * 2 + 4; int scale = image_size / 2 - texture_patch_border; for (std::size_t j = 0, k = 0; j < num_vertices; ++j) { if (!is_border[j]) { projections[j] = math::Vec2f(xx[k], xy[k]) * scale + image_size / 2; ++k; } else { projections[j] = projections[j] * scale + image_size / 2; } } mve::ByteImage::Ptr image = mve::ByteImage::create(image_size, image_size, 3); //DEBUG image->fill_color(*math::Vec4uc(0, 255, 0, 255)); std::vector<math::Vec2f> texcoords; texcoords.reserve(subgraph.size()); for (std::size_t const face_id : subgraph) { for (std::size_t j = 0; j < 3; ++j) { std::size_t const vertex_id = mesh_faces[face_id * 3 + j]; math::Vec2f const & projection = projections[g2l[vertex_id]]; texcoords.push_back(projection); } } TexturePatch texture_patch(0, subgraph, texcoords, image); std::size_t texture_patch_id; #pragma omp critical { texture_patches->push_back(texture_patch); texture_patch_id = num_patches++; num_hole_faces += subgraph.size(); ++num_holes; } for (std::size_t j = 0; j < num_vertices; ++j) { std::size_t const vertex_id = l2g[j]; std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces; std::vector<std::size_t> faces; faces.reserve(adj_faces.size()); for (std::size_t adj_face : adj_faces) { if (graph.get_label(adj_face) == 0) { faces.push_back(adj_face); } } VertexProjectionInfo info = {texture_patch_id, projections[j], faces}; #pragma omp critical vertex_projection_infos->at(vertex_id).push_back(info); } } } merge_vertex_projection_infos(vertex_projection_infos); std::cout << "done. (Took " << timer.get_elapsed_sec() << "s)" << std::endl; std::cout << "\t" << num_patches << " texture patches." << std::endl; std::cout << "\t" << num_holes << " holes (" << num_hole_faces << " faces)." << std::endl; }