Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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);
	}
}
Ejemplo n.º 4
0
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());

}
Ejemplo n.º 5
0
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;
				}
			}
		}
	}
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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));
}
Ejemplo n.º 8
0
//
// 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 );
    }
}
Ejemplo n.º 9
0
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;
        }


    }
Ejemplo n.º 10
0
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 */
}
Ejemplo n.º 11
0
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;
}