void computeBoundarySurface(const Vector4i *pTet, const int ntet, const int nvert, Vector3i *& pTri, int &nTri) { int i; CMemoryMgr* pm = new CMemoryMgr; //malloc the pointer buffer; CTriangleListItem** plist = new CTriangleListItem*[nvert]; assert(plist!=NULL); for (i=0; i<nvert; i++) plist[i]=NULL; //insert triangles into the buffer; for (i=0; i<ntet; i++){ Vector4i t = pTet[i]; const int v0=t.x; const int v1=t.y; const int v2=t.z; const int v3=t.w; Vector3i tri0(v0, v2, v1); Vector3i tri1(v0, v1, v3); Vector3i tri2(v1, v2, v3); Vector3i tri3(v0, v3, v2); _insertTriangle(tri0, plist, *pm); _insertTriangle(tri1, plist, *pm); _insertTriangle(tri2, plist, *pm); _insertTriangle(tri3, plist, *pm); } nTri = _countValidTriangle(plist, nvert); pTri = _collectBoundarySurface(plist, nvert, nTri); delete pm; delete [] plist; }
bool svlQuad::IsWithin(int x, int y) const { svlTriangle tri1(x1, y1, x2, y2, x3, y3); svlTriangle tri2(x3, y3, x4, y4, x1, y1); // Test convex case if (tri1.IsWithin(x, y) || tri2.IsWithin(x, y)) return true; // TO DO: concave implementation return false; }
void renderer::poly(vector<vec2> *p,XColor c,int z) { tri2 t; for(int i=2;i<p->size();i++){ t=tri2((*p)[i-2],(*p)[i-1],(*p)[i],c,z); t.p[0].rotate(theta); t.p[1].rotate(theta); t.p[2].rotate(theta); t.p[0]=t.p[0]+origin; t.p[1]=t.p[1]+origin; t.p[2]=t.p[2]+origin; tris.push_back(t); } }
TEST_F(ShapeTest, ConstructOverLoad) { Rectangle rec; shape = &rec; ASSERT_EQ(0, shape->get_dimension1()); Rectangle rec2(10); shape = &rec2; ASSERT_EQ(10, shape->get_dimension1()); Triangle tri; shape = &tri; ASSERT_EQ(1, shape->get_dimension1()); Triangle tri2(30); shape = &tri2; ASSERT_EQ(30, shape->get_dimension1()); }
void D3D11RendererMesh::Pick(DirectX::SimpleMath::Ray ray) { m_bPick = false; mPickedTriangle = -1; XMFLOAT3 Center; // Center of the box. XMFLOAT3 Extents; // Distance from the center to each side. XMStoreFloat3(&Center, 0.5f*(vMin + vMax)); XMStoreFloat3(&Extents, 0.5f*(vMax - vMin)); float tmin = 0.0f; DirectX::BoundingBox box(Center,Extents); if (ray.Intersects(box, tmin)) { tmin = FLT_MAX; for (UINT i = 0; i < indices.size() / 3; ++i) { // Indices for this triangle. UINT i0 = indices[i * 3 + 0]; UINT i1 = indices[i * 3 + 1]; UINT i2 = indices[i * 3 + 2]; // Vertices for this triangle. XMVECTOR v0 = XMLoadFloat3(&vertices[i0].position); XMVECTOR v1 = XMLoadFloat3(&vertices[i1].position); XMVECTOR v2 = XMLoadFloat3(&vertices[i2].position); DirectX::SimpleMath::Vector3 tri0(v0); DirectX::SimpleMath::Vector3 tri1(v1); DirectX::SimpleMath::Vector3 tri2(v2); float t = 0.0f; if (ray.Intersects(tri0, tri1, tri2, t)) { if (t < tmin) { tmin = t; mPickedTriangle = i; m_bPick = true; } } } } }
void computeBoundarySurface(const Vector8i *pTet, const int ntet, const int nvert, Vector4i *& pTri, int &nTri) { int i; CMemoryMgr* pm = new CMemoryMgr; //malloc the pointer buffer; CQuadListItem** plist = new CQuadListItem*[nvert]; assert(plist!=NULL); for (i=0; i<nvert; i++) plist[i]=NULL; //insert triangles into the buffer; for (i=0; i<ntet; i++){ Vector8i t = pTet[i]; const int v0=t.x; const int v1=t.y; const int v2=t.z; const int v3=t.w; const int v4=t.x1; const int v5=t.y1; const int v6=t.z1; const int v7=t.w1; Vector4i tri0(v0, v3, v2, v1); Vector4i tri1(v4, v5, v6, v7); Vector4i tri2(v0, v4, v7, v3); Vector4i tri3(v1, v2, v6, v5); Vector4i tri4(v0, v1, v5, v4); Vector4i tri5(v2, v3, v7, v6); _insertQuad(tri0, plist, *pm); _insertQuad(tri1, plist, *pm); _insertQuad(tri2, plist, *pm); _insertQuad(tri3, plist, *pm); _insertQuad(tri4, plist, *pm); _insertQuad(tri5, plist, *pm); } nTri = _countValidQuad(plist, nvert); pTri = _collectBoundarySurface(plist, nvert, nTri); delete pm; delete [] plist; }
void body2::load_hexagon(int size){ vec2 hex[6]={ vec2(-size,0), vec2(size*-0.5,size*0.866), vec2(size*0.5,size*0.866), vec2(size,0), vec2(size*0.5,size*-0.866), vec2(size*-0.5,size*-0.866) }; vec2 o(0,0); tris.push_back(tri2(hex[0],hex[1],o,color(200,00,00),1)); tris.push_back(tri2(hex[1],hex[2],o,color(100,100,00),1)); tris.push_back(tri2(hex[2],hex[3],o,color(00,200,00),1)); tris.push_back(tri2(hex[4],hex[5],o,color(00,100,100),1)); tris.push_back(tri2(hex[5],hex[0],o,color(00,00,200),1)); tris.push_back(tri2(hex[3],hex[4],o,color(100,00,100),1)); }
// // must be called with pa's dimension larger than pb's void intersection( const PrimitiveHandle<2>& pa, const PrimitiveHandle<2>& pb, GeometrySet<2>& output, dim_t<2> ) { // everything vs a point if ( pb.handle.which() == PrimitivePoint ) { if ( algorithm::intersects( pa, pb ) ) { output.addPrimitive( *pb.as<CGAL::Point_2<Kernel> >() ); } } else if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitiveSurface ) { const CGAL::Polygon_with_holes_2<Kernel>* poly1 = pa.as<CGAL::Polygon_with_holes_2<Kernel> >(); const CGAL::Polygon_with_holes_2<Kernel>* poly2 = pb.as<CGAL::Polygon_with_holes_2<Kernel> >(); // shortcut for triangles if ( poly1->holes_begin() == poly1->holes_end() && poly1->outer_boundary().size() == 3 && poly2->holes_begin() == poly2->holes_end() && poly2->outer_boundary().size() == 3 ) { CGAL::Polygon_2<Kernel>::Vertex_iterator vit1 = poly1->outer_boundary().vertices_begin(); CGAL::Point_2<Kernel> pa1 = *vit1++; CGAL::Point_2<Kernel> pa2 = *vit1++; CGAL::Point_2<Kernel> pa3 = *vit1; CGAL::Triangle_2<Kernel> tri1( pa1, pa2, pa3 ); CGAL::Polygon_2<Kernel>::Vertex_iterator vit2 = poly2->outer_boundary().vertices_begin(); CGAL::Point_2<Kernel> pb1 = *vit2++; CGAL::Point_2<Kernel> pb2 = *vit2++; CGAL::Point_2<Kernel> pb3 = *vit2; CGAL::Triangle_2<Kernel> tri2( pb1, pb2, pb3 ); CGAL::Object interObj = CGAL::intersection( tri1, tri2 ); output.addPrimitive( interObj, /* pointsAsRing */ true ); return; } // CGAL::intersection does not work when the intersection is a point or a segment // We have to call intersection on boundaries first GeometrySet<2> gpoly1, gpoly2; gpoly1.addBoundary( *poly1 ); gpoly2.addBoundary( *poly2 ); algorithm::intersection( gpoly1, gpoly2, output ); // CGAL::intersection does not work when rings intersect themselves // However, touching by a single point is valid for OGC // FIXME: not implemented yet if ( numIntersectionPoints( *poly1 ) > 0 ) { BOOST_THROW_EXCEPTION( NotImplementedException( "Intersection does not support polygon with connected rings" ) ); } if ( numIntersectionPoints( *poly2 ) > 0 ) { BOOST_THROW_EXCEPTION( NotImplementedException( "Intersection does not support polygon with connected rings" ) ); } // now call on polygon's interiors CGAL::intersection( *poly1, *poly2, std::back_inserter( output.surfaces() ) ); } else if ( pa.handle.which() == PrimitiveSegment && pb.handle.which() == PrimitiveSegment ) { const CGAL::Segment_2<Kernel> *seg1 = pa.as<CGAL::Segment_2<Kernel> >(); const CGAL::Segment_2<Kernel> *seg2 = pb.as<CGAL::Segment_2<Kernel> >(); CGAL::Object interObj = CGAL::intersection( *seg1, *seg2 ); output.addPrimitive( interObj ); } else if ( pa.handle.which() == PrimitiveSurface && pb.handle.which() == PrimitiveSegment ) { const CGAL::Polygon_with_holes_2<Kernel> *poly = pa.as<CGAL::Polygon_with_holes_2<Kernel> >(); const CGAL::Segment_2<Kernel> *seg = pb.as<CGAL::Segment_2<Kernel> >(); // shortcut for triangle if ( poly->holes_begin() == poly->holes_end() && poly->outer_boundary().size() == 3 ) { // no holes and 3 vertices => it is a triangle CGAL::Polygon_2<Kernel>::Vertex_iterator vit = poly->outer_boundary().vertices_begin(); CGAL::Point_2<Kernel> p1( *vit++ ); CGAL::Point_2<Kernel> p2( *vit++ ); CGAL::Point_2<Kernel> p3( *vit++ ); CGAL::Triangle_2<Kernel> tri( p1, p2, p3 ); CGAL::Object interObj = CGAL::intersection( tri, *seg ); output.addPrimitive( interObj ); return; } // if it s a regulat polygon, triangulate it and recurse call GeometrySet<2> triangles, g; triangulate::triangulate( *poly, triangles ); g.addPrimitive( pb ); // recurse call algorithm::intersection( triangles, g, output ); } }
void Delaunay :: triangulate(const std :: vector< FloatArray > &iVertices, std :: vector< Triangle > &oTriangles) const { // 4th order algorithm - four loops, only for testing purposes if( iVertices.size() == 4 ) { // Check if the points approximately form a rectangle // and treat that case separately . // The purpose is to avoid annyoing round-off effects for // this quite common case. /ES const double relTol2 = 1.0e-6; std::vector<double> dist_square = {iVertices[0].distance_square(iVertices[1]), iVertices[0].distance_square(iVertices[2]), iVertices[0].distance_square(iVertices[3])}; std::sort(dist_square.begin(), dist_square.end()); // This expression is zero for a rectangle according to the Pythagorean theorem if( fabs(dist_square[2] - dist_square[1] - dist_square[0]) < relTol2*dist_square[2] ) { // We found a rectangle double maxDist_square = iVertices[0].distance_square(iVertices[1]); int maxDistInd = 1; if( iVertices[0].distance_square(iVertices[2]) > maxDist_square ) { maxDist_square = iVertices[0].distance_square(iVertices[2]); maxDistInd = 2; } if( iVertices[0].distance_square(iVertices[3]) > maxDist_square ) { maxDist_square = iVertices[0].distance_square(iVertices[3]); maxDistInd = 3; } int remainingInd1 = -1, remainingInd2 = -1; switch(maxDistInd) { case 1: remainingInd1 = 2; remainingInd2 = 3; break; case 2: remainingInd1 = 1; remainingInd2 = 3; break; case 3: remainingInd1 = 1; remainingInd2 = 2; break; default: OOFEM_ERROR("Case not handled in switch.") break; } Triangle tri1(iVertices [ 0 ], iVertices [ remainingInd1 ], iVertices [ maxDistInd ]); if ( !tri1.isOrientedAnticlockwise() ) { tri1.changeToAnticlockwise(); } oTriangles.push_back(tri1); Triangle tri2(iVertices [ 0 ], iVertices [ remainingInd2 ], iVertices [ maxDistInd ]); if ( !tri2.isOrientedAnticlockwise() ) { tri2.changeToAnticlockwise(); } oTriangles.push_back(tri2); return; } }
int spheroidsToSTL(const string& out, const shared_ptr<DemField>& dem, Real tol, const string& solid, int mask, bool append, bool clipCell, bool merge){ if(tol==0 || isnan(tol)) throw std::runtime_error("tol must be non-zero."); #ifndef WOO_GTS if(merge) throw std::runtime_error("woo.triangulated.spheroidsToSTL: merge=True only possible in builds with the 'gts' feature."); #endif // first traversal to find reference radius auto particleOk=[&](const shared_ptr<Particle>&p){ return (mask==0 || (p->mask & mask)) && (p->shape->isA<Sphere>() || p->shape->isA<Ellipsoid>() || p->shape->isA<Capsule>()); }; int numTri=0; if(tol<0){ LOG_DEBUG("tolerance is negative, taken as relative to minimum radius."); Real minRad=Inf; for(const auto& p: *dem->particles){ if(particleOk(p)) minRad=min(minRad,p->shape->equivRadius()); } if(isinf(minRad) || isnan(minRad)) throw std::runtime_error("Minimum radius not found (relative tolerance specified); no matching particles?"); tol=-minRad*tol; LOG_DEBUG("Minimum radius "<<minRad<<"."); } LOG_DEBUG("Triangulation tolerance is "<<tol); std::ofstream stl(out,append?(std::ofstream::app|std::ofstream::binary):std::ofstream::binary); // binary better, anyway if(!stl.good()) throw std::runtime_error("Failed to open output file "+out+" for writing."); Scene* scene=dem->scene; if(!scene) throw std::logic_error("DEM field has not associated scene?"); // periodicity, cache that for later use AlignedBox3r cell; /* wasteful memory-wise, but we need to store the whole triangulation in case *merge* is in effect, when it is only an intermediary result and will not be output as-is */ vector<vector<Vector3r>> ppts; vector<vector<Vector3i>> ttri; vector<Particle::id_t> iid; for(const auto& p: *dem->particles){ if(!particleOk(p)) continue; const auto sphere=dynamic_cast<Sphere*>(p->shape.get()); const auto ellipsoid=dynamic_cast<Ellipsoid*>(p->shape.get()); const auto capsule=dynamic_cast<Capsule*>(p->shape.get()); vector<Vector3r> pts; vector<Vector3i> tri; if(sphere || ellipsoid){ Real r=sphere?sphere->radius:ellipsoid->semiAxes.minCoeff(); // 1 is for icosahedron int tess=ceil(M_PI/(5*acos(1-tol/r))); LOG_DEBUG("Tesselation level for #"<<p->id<<": "<<tess); tess=max(tess,0); auto uSphTri(CompUtils::unitSphereTri20(/*0 for icosahedron*/max(tess-1,0))); const auto& uPts=std::get<0>(uSphTri); // unit sphere point coords pts.resize(uPts.size()); const auto& node=(p->shape->nodes[0]); Vector3r scale=(sphere?sphere->radius*Vector3r::Ones():ellipsoid->semiAxes); for(size_t i=0; i<uPts.size(); i++){ pts[i]=node->loc2glob(uPts[i].cwiseProduct(scale)); } tri=std::get<1>(uSphTri); // this makes a copy, but we need out own for capsules } if(capsule){ #ifdef WOO_VTK int subdiv=max(4.,ceil(M_PI/(acos(1-tol/capsule->radius)))); std::tie(pts,tri)=VtkExport::triangulateCapsule(static_pointer_cast<Capsule>(p->shape),subdiv); #else throw std::runtime_error("Triangulation of capsules is (for internal and entirely fixable reasons) only available when compiled with the 'vtk' features."); #endif } // do not write out directly, store first for later ppts.push_back(pts); ttri.push_back(tri); LOG_TRACE("#"<<p->id<<" triangulated: "<<tri.size()<<","<<pts.size()<<" faces,vertices."); if(scene->isPeriodic){ // make sure we have aabb, in skewed coords and such if(!p->shape->bound){ // this is a bit ugly, but should do the trick; otherwise we would recompute all that ourselves here if(sphere) Bo1_Sphere_Aabb().go(p->shape); else if(ellipsoid) Bo1_Ellipsoid_Aabb().go(p->shape); else if(capsule) Bo1_Capsule_Aabb().go(p->shape); } assert(p->shape->bound); const AlignedBox3r& box(p->shape->bound->box); AlignedBox3r cell(Vector3r::Zero(),scene->cell->getSize()); // possibly in skewed coords // central offset Vector3i off0; scene->cell->canonicalizePt(p->shape->nodes[0]->pos,off0); // computes off0 Vector3i off; // offset from the original cell //cerr<<"#"<<p->id<<" at "<<p->shape->nodes[0]->pos.transpose()<<", off0="<<off0<<endl; for(off[0]=off0[0]-1; off[0]<=off0[0]+1; off[0]++) for(off[1]=off0[1]-1; off[1]<=off0[1]+1; off[1]++) for(off[2]=off0[2]-1; off[2]<=off0[2]+1; off[2]++){ Vector3r dx=scene->cell->intrShiftPos(off); //cerr<<" off="<<off.transpose()<<", dx="<<dx.transpose()<<endl; AlignedBox3r boxOff(box); boxOff.translate(dx); //cerr<<" boxOff="<<boxOff.min()<<";"<<boxOff.max()<<" | cell="<<cell.min()<<";"<<cell.max()<<endl; if(boxOff.intersection(cell).isEmpty()) continue; // copy the entire triangulation, offset by dx vector<Vector3r> pts2(pts); for(auto& p: pts2) p+=dx; vector<Vector3i> tri2(tri); // same topology ppts.push_back(pts2); ttri.push_back(tri2); LOG_TRACE(" offset "<<off.transpose()<<": #"<<p->id<<": "<<tri2.size()<<","<<pts2.size()<<" faces,vertices."); } } } if(!merge){ LOG_DEBUG("Will export (unmerged) "<<ppts.size()<<" particles to STL."); stl<<"solid "<<solid<<"\n"; for(size_t i=0; i<ppts.size(); i++){ const auto& pts(ppts[i]); const auto& tri(ttri[i]); LOG_TRACE("Exporting "<<i<<" with "<<tri.size()<<" faces."); for(const Vector3i& t: tri){ Vector3r pp[]={pts[t[0]],pts[t[1]],pts[t[2]]}; // skip triangles which are entirely out of the canonical periodic cell if(scene->isPeriodic && clipCell && (!scene->cell->isCanonical(pp[0]) && !scene->cell->isCanonical(pp[1]) && !scene->cell->isCanonical(pp[2]))) continue; numTri++; Vector3r n=(pp[1]-pp[0]).cross(pp[2]-pp[1]).normalized(); stl<<" facet normal "<<n.x()<<" "<<n.y()<<" "<<n.z()<<"\n"; stl<<" outer loop\n"; for(auto p: {pp[0],pp[1],pp[2]}){ stl<<" vertex "<<p[0]<<" "<<p[1]<<" "<<p[2]<<"\n"; } stl<<" endloop\n"; stl<<" endfacet\n"; } } stl<<"endsolid "<<solid<<"\n"; stl.close(); return numTri; } #if WOO_GTS /***** Convert all triangulation to GTS surfaces, find their distances, isolate connected components, merge these components incrementally and write to STL *****/ // total number of points const size_t N(ppts.size()); // bounds for collision detection struct Bound{ Bound(Real _coord, int _id, bool _isMin): coord(_coord), id(_id), isMin(_isMin){}; Bound(): coord(NaN), id(-1), isMin(false){}; // just for allocation Real coord; int id; bool isMin; bool operator<(const Bound& b) const { return coord<b.coord; } }; vector<Bound> bounds[3]={vector<Bound>(2*N),vector<Bound>(2*N),vector<Bound>(2*N)}; /* construct GTS surface objects; all objects must be deleted explicitly! */ vector<GtsSurface*> ssurf(N); vector<vector<GtsVertex*>> vvert(N); vector<vector<GtsEdge*>> eedge(N); vector<AlignedBox3r> boxes(N); for(size_t i=0; i<N; i++){ LOG_TRACE("** Creating GTS surface for #"<<i<<", with "<<ttri[i].size()<<" faces, "<<ppts[i].size()<<" vertices."); AlignedBox3r box; // new surface object ssurf[i]=gts_surface_new(gts_surface_class(),gts_face_class(),gts_edge_class(),gts_vertex_class()); // copy over all vertices vvert[i].reserve(ppts[i].size()); eedge[i].reserve(size_t(1.5*ttri[i].size())); // each triangle consumes 1.5 edges, for closed surfs for(size_t v=0; v<ppts[i].size(); v++){ vvert[i].push_back(gts_vertex_new(gts_vertex_class(),ppts[i][v][0],ppts[i][v][1],ppts[i][v][2])); box.extend(ppts[i][v]); } // create faces, and create edges on the fly as needed std::map<std::pair<int,int>,int> edgeIndices; for(size_t t=0; t<ttri[i].size(); t++){ //const Vector3i& t(ttri[i][t]); //LOG_TRACE("Face with vertices "<<ttri[i][t][0]<<","<<ttri[i][t][1]<<","<<ttri[i][t][2]); Vector3i eIxs; for(int a:{0,1,2}){ int A(ttri[i][t][a]), B(ttri[i][t][(a+1)%3]); auto AB=std::make_pair(min(A,B),max(A,B)); auto ABI=edgeIndices.find(AB); if(ABI==edgeIndices.end()){ // this edge not created yet edgeIndices[AB]=eedge[i].size(); // last index eIxs[a]=eedge[i].size(); //LOG_TRACE(" New edge #"<<eIxs[a]<<": "<<A<<"--"<<B<<" (length "<<(ppts[i][A]-ppts[i][B]).norm()<<")"); eedge[i].push_back(gts_edge_new(gts_edge_class(),vvert[i][A],vvert[i][B])); } else { eIxs[a]=ABI->second; //LOG_TRACE(" Found edge #"<<ABI->second<<" for "<<A<<"--"<<B); } } //LOG_TRACE(" New face: edges "<<eIxs[0]<<"--"<<eIxs[1]<<"--"<<eIxs[2]); GtsFace* face=gts_face_new(gts_face_class(),eedge[i][eIxs[0]],eedge[i][eIxs[1]],eedge[i][eIxs[2]]); gts_surface_add_face(ssurf[i],face); } // make sure the surface is OK if(!gts_surface_is_orientable(ssurf[i])) LOG_ERROR("Surface of #"+to_string(iid[i])+" is not orientable (expect troubles)."); if(!gts_surface_is_closed(ssurf[i])) LOG_ERROR("Surface of #"+to_string(iid[i])+" is not closed (expect troubles)."); assert(!gts_surface_is_self_intersecting(ssurf[i])); // copy bounds LOG_TRACE("Setting bounds of surf #"<<i); boxes[i]=box; for(int ax:{0,1,2}){ bounds[ax][2*i+0]=Bound(box.min()[ax],/*id*/i,/*isMin*/true); bounds[ax][2*i+1]=Bound(box.max()[ax],/*id*/i,/*isMin*/false); } } /* broad-phase collision detection between GTS surfaces only those will be probed with exact algorithms below and merged if needed */ for(int ax:{0,1,2}) std::sort(bounds[ax].begin(),bounds[ax].end()); vector<Bound>& bb(bounds[0]); // run the search along x-axis, does not matter really std::list<std::pair<int,int>> int0; // broad-phase intersections for(size_t i=0; i<2*N; i++){ if(!bb[i].isMin) continue; // only start with lower bound // go up to the upper bound, but handle overflow safely (no idea why it would happen here) as well for(size_t j=i+1; j<2*N && bb[j].id!=bb[i].id; j++){ if(bb[j].isMin) continue; // this is handled by symmetry #if EIGEN_VERSION_AT_LEAST(3,2,5) if(!boxes[bb[i].id].intersects(boxes[bb[j].id])) continue; // no intersection along all axes #else // old, less elegant if(boxes[bb[i].id].intersection(boxes[bb[j].id]).isEmpty()) continue; #endif int0.push_back(std::make_pair(min(bb[i].id,bb[j].id),max(bb[i].id,bb[j].id))); LOG_TRACE("Broad-phase collision "<<int0.back().first<<"+"<<int0.back().second); } } /* narrow-phase collision detection between GTS surface this must be done via gts_surface_inter_new, since gts_surface_distance always succeeds */ std::list<std::pair<int,int>> int1; for(const std::pair<int,int> ij: int0){ LOG_TRACE("Testing narrow-phase collision "<<ij.first<<"+"<<ij.second); #if 0 GtsRange gr1, gr2; gts_surface_distance(ssurf[ij.first],ssurf[ij.second],/*delta ??*/(gfloat).2,&gr1,&gr2); if(gr1.min>0 && gr2.min>0) continue; LOG_TRACE(" GTS reports collision "<<ij.first<<"+"<<ij.second<<" (min. distances "<<gr1.min<<", "<<gr2.min); #else GtsSurface *s1(ssurf[ij.first]), *s2(ssurf[ij.second]); GNode* t1=gts_bb_tree_surface(s1); GNode* t2=gts_bb_tree_surface(s2); GtsSurfaceInter* I=gts_surface_inter_new(gts_surface_inter_class(),s1,s2,t1,t2,/*is_open_1*/false,/*is_open_2*/false); GSList* l=gts_surface_intersection(s1,s2,t1,t2); // list of edges describing intersection int n1=g_slist_length(l); // extra check by looking at number of faces of the intersected surface #if 1 GtsSurface* s12=gts_surface_new(gts_surface_class(),gts_face_class(),gts_edge_class(),gts_vertex_class()); gts_surface_inter_boolean(I,s12,GTS_1_OUT_2); gts_surface_inter_boolean(I,s12,GTS_2_OUT_1); int n2=gts_surface_face_number(s12); gts_object_destroy(GTS_OBJECT(s12)); #endif gts_bb_tree_destroy(t1,TRUE); gts_bb_tree_destroy(t2,TRUE); gts_object_destroy(GTS_OBJECT(I)); g_slist_free(l); if(n1==0) continue; #if 1 if(n2==0){ LOG_ERROR("n1==0 but n2=="<<n2<<" (no narrow-phase collision)"); continue; } #endif LOG_TRACE(" GTS reports collision "<<ij.first<<"+"<<ij.second<<" ("<<n<<" edges describe the intersection)"); #endif int1.push_back(ij); } /* connected components on the graph: graph nodes are 0…(N-1), graph edges are in int1 see http://stackoverflow.com/a/37195784/761090 */ typedef boost::subgraph<boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS,boost::property<boost::vertex_index_t,int>,boost::property<boost::edge_index_t,int>>> Graph; Graph graph(N); for(const auto& ij: int1) boost::add_edge(ij.first,ij.second,graph); vector<size_t> clusters(boost::num_vertices(graph)); size_t numClusters=boost::connected_components(graph,clusters.data()); for(size_t n=0; n<numClusters; n++){ // beginning cluster #n // first, count how many surfaces are in this cluster; if 1, things are easier int numThisCluster=0; int cluster1st=-1; for(size_t i=0; i<N; i++){ if(clusters[i]!=n) continue; numThisCluster++; if(cluster1st<0) cluster1st=(int)i; } GtsSurface* clusterSurf=NULL; LOG_DEBUG("Cluster "<<n<<" has "<<numThisCluster<<" surfaces."); if(numThisCluster==1){ clusterSurf=ssurf[cluster1st]; } else { clusterSurf=ssurf[cluster1st]; // surface of the cluster itself LOG_TRACE(" Initial cluster surface from "<<cluster1st<<"."); /* composed surface */ for(size_t i=0; i<N; i++){ if(clusters[i]!=n || ((int)i)==cluster1st) continue; LOG_TRACE(" Adding "<<i<<" to the cluster"); // ssurf[i] now belongs to cluster #n // trees need to be rebuild every time anyway, since the merged surface keeps changing in every cycle //if(gts_surface_face_number(clusterSurf)==0) LOG_ERROR("clusterSurf has 0 faces."); //if(gts_surface_face_number(ssurf[i])==0) LOG_ERROR("Surface #"<<i<<" has 0 faces."); GNode* t1=gts_bb_tree_surface(clusterSurf); GNode* t2=gts_bb_tree_surface(ssurf[i]); GtsSurfaceInter* I=gts_surface_inter_new(gts_surface_inter_class(),clusterSurf,ssurf[i],t1,t2,/*is_open_1*/false,/*is_open_2*/false); GtsSurface* merged=gts_surface_new(gts_surface_class(),gts_face_class(),gts_edge_class(),gts_vertex_class()); gts_surface_inter_boolean(I,merged,GTS_1_OUT_2); gts_surface_inter_boolean(I,merged,GTS_2_OUT_1); gts_object_destroy(GTS_OBJECT(I)); gts_bb_tree_destroy(t1,TRUE); gts_bb_tree_destroy(t2,TRUE); if(gts_surface_face_number(merged)==0){ LOG_ERROR("Cluster #"<<n<<": 0 faces after fusing #"<<i<<" (why?), adding #"<<i<<" separately!"); // this will cause an extra 1-particle cluster to be created clusters[i]=numClusters; numClusters+=1; } else { // not from global vectors (cleanup at the end), explicit delete! if(clusterSurf!=ssurf[cluster1st]) gts_object_destroy(GTS_OBJECT(clusterSurf)); clusterSurf=merged; } } } #if 0 LOG_TRACE(" GTS surface cleanups..."); pygts_vertex_cleanup(clusterSurf,.1*tol); // cleanup 10× smaller than tolerance pygts_edge_cleanup(clusterSurf); pygts_face_cleanup(clusterSurf); #endif LOG_TRACE(" STL: cluster "<<n<<" output"); stl<<"solid "<<solid<<"_"<<n<<"\n"; /* output cluster to STL here */ _gts_face_to_stl_data data(stl,scene,clipCell,numTri); gts_surface_foreach_face(clusterSurf,(GtsFunc)_gts_face_to_stl,(gpointer)&data); stl<<"endsolid\n"; if(clusterSurf!=ssurf[cluster1st]) gts_object_destroy(GTS_OBJECT(clusterSurf)); } // this deallocates also edges and vertices for(size_t i=0; i<ssurf.size(); i++) gts_object_destroy(GTS_OBJECT(ssurf[i])); return numTri; #endif /* WOO_GTS */ }
int main(int argc, char *argv[]) { // We need all parameters, otherwise the mesh cannot be created correctly if (argc != 14) { std::cout << "usage: " << argv[0] << " type nx ny min_x min_y max_x max_y bcids factor loading ul_lr dead-axis filename\n"; std::cout << "type: Q|q for Quad-4, T|t for Tri-3\n" << "nx: no. elements on primary axis\n" << "ny: no. elements on secondary axis\n" << "min_x: minimum position on the primary axis\n" << "min_y: minimum position on the secondary axis\n" << "max_x: maximum position on the primary axis\n" << "max_y: maximum position on the secondary axis\n" << "bcids: comma-separated list of boundary condition IDs in the ordering top,bottom,left,right border (e.g. 2,0,20,21). If border has no BC, type -1\n" << "factor: global factor multiplied on all force entries in the force file\n" << "loading: 0 for no loading at all, 1 for concentrated load on central node, 2 for uniform load on entire mesh\n" << "ul_lr: 1 if triangle hypotenuse should face the lower right corner of the divided square, 0 for 90° rotated orientation. If type is Quad-4, the value doesn't matter.\n" << "dead-axis: 'x','y' or 'z' to specify which axis should be considered dead. The resulting mesh will lie in the 'yz','xz' or 'xy'-plane, respectively.\n" << "filename: the name of the mesh file to create\n"; return -1; } char type = argv[1][0]; // edit here, for more types in the future: if (type != 'Q' && type != 'q' && type != 'T' && type != 't') { std::cout << "Invalid element type specified: \"" << type << "\". " << "Only 'Q'|'q' and 'T'|'t' are allowed.\n"; return -1; } // bring the type into lower case for better management // in the rest of the program: if (type == 'Q') type = 'q'; else if (type == 'T') type = 't'; int nx = atoi(argv[2]); if (nx <= 0) { std::cout << "Invalid number of elements on primary axis! Only positive integer values allowed.\n"; return -1; } int ny = atoi(argv[3]); if (ny <= 0) { std::cout << "Invalid number of elements on secondary axis! Only positive integer values allowed.\n"; return -1; } double min_x = atof(argv[4]); double min_y = atof(argv[5]); double max_x = atof(argv[6]); double max_y = atof(argv[7]); // get comma-separated list of boundary condition IDs // expected format: int,int,int,int std::string bcids = argv[8]; std::size_t first = bcids.find_first_of(","); int curPos = 0; int t_bcid = -1; if (first != std::string::npos) { std::string str = bcids.substr(0, first); t_bcid = std::stoi(str); } curPos = first+1; first = bcids.find_first_of(",", first+1); int b_bcid = -1; if (first != std::string::npos) { std::string str = bcids.substr(curPos, first-curPos); b_bcid = std::stoi(str); } curPos = first+1; first = bcids.find_first_of(",", first+1); int l_bcid = -1; if (first != std::string::npos) { std::string str = bcids.substr(curPos, first-curPos); l_bcid = std::stoi(str); } curPos = first+1; std::string str = bcids.substr(curPos, bcids.size()); int r_bcid = std::stoi(str); double factor = atof(argv[9]); int loading = atoi(argv[10]); bool ul_lr = atoi(argv[11])==1? true : false; // upper left to lower right diagonal, or ur_ll? char deadAxis = argv[12][0]; char* fname = argv[13]; bool bleft = false; if (l_bcid >= 0) bleft = true; bool bright = false; if (r_bcid >= 0) bright = true; bool btop = false; if (t_bcid >= 0) btop = true; bool bbottom = false; if (b_bcid >= 0) bbottom = true; if (deadAxis != 'x' && deadAxis != 'y' && deadAxis != 'z') { std::cout << "Invalid parameter for dead axis: \"" << deadAxis << "\". Only 'x','y' and 'z' are allowed.\n"; return -1; } int n_elem = nx*ny; if (type == 't') // we split every rectangle into two triangles n_elem *= 2; int n_nodes = (nx+1)*(ny+1); std::vector<std::vector<double> > nodes; std::vector<std::vector<int> > elem; double fracx = (max_x-min_x)/(double)nx; double fracy = (max_y-min_y)/(double)ny; // creates nodes for (int y = 0; y <= ny; y++) { std::vector<double> node(3, 0.0); if (deadAxis == 'z') // secondary axis is y node[1] = min_y + y*fracy; else // deadAxis = y|x -> secondary axis is z node[2] = min_y + y*fracy; for (int x = 0; x <= nx; x++) { if (deadAxis == 'x') // primary axis is y node[1] = (min_x + x*fracx); else // deadAxis = y|z -> primary axis is x node[0] = (min_x + x*fracx); nodes.push_back(node); } } // create elements for (int y = 0; y < ny; y++) { if (type == 'q') { std::vector<int> quad(4); for (int x = 0; x < nx; x++) { /* 3-----2 | | | | 0-----1 */ int n_id = x + y*(nx+1); quad[0] = n_id; quad[1] = n_id + 1; quad[2] = n_id + (nx+1) + 1; quad[3] = n_id + (nx+1); elem.push_back(quad); } } else if (type == 't') { std::vector<int> tri1(3); std::vector<int> tri2(3); for (int x = 0; x < nx; x++) { int n_id = x + y*(nx+1); if (ul_lr) { /* 2|2---1 |\\ | | \\ | | \\ | 0---1|0 */ tri1[0] = n_id; tri1[1] = n_id + 1; tri1[2] = n_id + (nx+1); tri2[0] = n_id + 1; tri2[1] = n_id + (nx+1) + 1; tri2[2] = n_id + (nx+1); } else { /* 2---0|1 | //| | // | | // | 1|0---2 */ tri1[0] = n_id; tri1[1] = n_id + (nx+1) + 1; tri1[2] = n_id + 1; tri2[0] = n_id + (nx+1) + 1; tri2[1] = n_id; tri2[2] = n_id + (nx+1); } elem.push_back(tri1); elem.push_back(tri2); } } } std::string meshname = fname; meshname += ".xda"; std::filebuf fb; fb.open (meshname.c_str(), std::ios::out); std::ostream os(&fb); // The header of the XDA mesh file: os << "libMesh-0.7.0+\n"; os << n_elem << " # number of elements\n"; os << n_nodes << " # number of nodes\n"; os << ". # boundary condition specification file\n"; os << "n/a # subdomain id specification file\n"; os << "n/a # processor id specification file\n"; os << "n/a # p-level specification file\n"; os << n_elem << " # n_elem at level 0, [ type (n0 ... nN-1) ]\n"; // write the elements: char elType = 't'; if (type == 't') elType = '3'; else if (type == 'q') elType = '5'; for (unsigned int i = 0; i < elem.size(); i++) { std::vector<int> cur_elem = elem[i]; os << elType; for (unsigned int k = 0; k < cur_elem.size(); k++) os << " " << cur_elem[k]; os << "\n"; } // write the node coordinates: for (unsigned int i = 0; i < nodes.size(); i++) { std::vector<double> cur_node = nodes[i]; os << cur_node[0] << " " << cur_node[1] << " " << cur_node[2] << "\n"; } // calculate the number of edges with boundary conditions: int noBC = 0; if (bleft) noBC += ny; if (bright) noBC += ny; if (btop) noBC += nx; if (bbottom) noBC += nx; os << noBC << " # number of boundary conditions\n"; // boundary condition format: // x y z // x: ID of element // y: number of edge of element. // edge 0 from vertex 0 to 1, // edge 1 from vertex 1 to 2, etc. // z: BC ID // top and bottom borders: for (int i = 0; i < nx; i++) { if (type == 't') { if (ul_lr) { if (bbottom) os << 2*i << " 0 " << b_bcid << "\n"; // bottom border if (btop) os << 2*nx*ny-2*i-1 << " 1 " << t_bcid << "\n"; // top border } else { if (bbottom) os << 2*i << " 2 " << b_bcid << "\n"; // bottom border if (btop) os << 2*nx*ny-2*i-1 << " 2 " << t_bcid << "\n"; // top border } } else if (type == 'q') { if (bbottom) os << i << " 0 " << b_bcid << "\n"; // bottom border if (btop) os << nx*ny-1-i << " 2 " << t_bcid << "\n"; // top border } } // left and right borders: for (int i = 0; i < ny; i++) { if (type == 't') { if (ul_lr) { if (bleft) os << 2*nx*i << " 2 " << l_bcid << "\n"; // left border if (bright) os << 2*nx*(i+1)-1 << " 0 " << r_bcid << "\n"; // right border } else { if (bleft) os << 2*nx*i+1 << " 1 " << l_bcid << "\n"; // left border if (bright) os << 2*nx*(i+1)-2 << " 1 " << r_bcid << "\n"; // right border } } else if (type == 'q') { if (bleft) os << nx*i << " 3 " << l_bcid << "\n"; // left border if (bright) os << nx*(i+1)-1 << " 1 " << r_bcid << "\n"; // right border } } fb.close(); if (loading <= 0) // if no loading is desired, we are done. return 0; std::string forcename = fname; forcename += "_f"; // convention: force file is named like mesh file with "_f" at the end fb.open (forcename.c_str(), std::ios::out); std::ostream os2(&fb); os2 << n_nodes << "\n"; if (loading == 1) // concentrated loading at central node { os2 << factor << "\n"; for (unsigned int i = 0; i < nodes.size()-1; i++) { // force is applied perpendicular to mesh plane (for plate testing) // direction of force could be also command-line argument (for the future) if (i == (unsigned)n_nodes/2) if (deadAxis == 'x') os2 << "1 0 0 0 0 0\n"; else if (deadAxis == 'y') os2 << "0 1 0 0 0 0\n"; else os2 << "0 0 1 0 0 0\n"; else os2 << "0 0 0 0 0 0\n"; } } else if (loading == 2) // uniformly distributed loading { // convert area force to nodal force: // factor * elem_x_len * elem_y_len / #nodes_of_elem * #neighboring_elements_sharing_this_node // for quad's: f*xlen*ylen / 4 * 4 -> f*xlen*ylen // for tri's: f*xlen*ylen/2 / 3 * 6 -> f*xlen*ylen os2 << (factor*((max_x-min_x)/(double)nx)*((max_y-min_y)/(double)ny)) << "\n"; // force is applied perpendicular to mesh plane (for plate testing) // direction of force could be also command-line argument (for the future) if (deadAxis == 'x') for (unsigned int i = 0; i < nodes.size()-1; i++) os2 << "1 0 0 0 0 0\n"; else if (deadAxis == 'y') for (unsigned int i = 0; i < nodes.size()-1; i++) os2 << "0 1 0 0 0 0\n"; else for (unsigned int i = 0; i < nodes.size()-1; i++) os2 << "0 0 1 0 0 0\n"; } fb.close(); return 0; }