// grid sampling Matrix3r woo::Volumetric::tetraInertia_grid(const Vector3r v[4], int div){ AlignedBox3r b; for(int i:{0,1,2,3}) b.extend(v[i]); std::cerr<<"bbox "<<b.min()<<", "<<b.max()<<std::endl; Real dd=b.sizes().minCoeff()/div; Vector3r xyz; // point inside test: http://steve.hollasch.net/cgindex/geometry/ptintet.html typedef Eigen::Matrix<Real,4,4> Matrix4r; Matrix4r M0; M0<<v[0].transpose(),1,v[1].transpose(),1,v[2].transpose(),1,v[3].transpose(),1; Real D0=M0.determinant(); // Matrix3r I(Matrix3r::Zero()); Matrix3r C(Matrix3r::Zero()); Real dV=pow(dd,3); // std::ofstream dbg("/tmp/tetra.txt"); for(xyz.x()=b.min().x()+dd/2.; xyz.x()<b.max().x(); xyz.x()+=dd){ for(xyz.y()=b.min().y()+dd/2.; xyz.y()<b.max().y(); xyz.y()+=dd){ for(xyz.z()=b.min().z()+dd/2.; xyz.z()<b.max().z(); xyz.z()+=dd){ bool inside=true; for(int i:{0,1,2,3}){ Matrix4r D=M0; D.row(i).head<3>()=xyz; if(std::signbit(D.determinant())!=std::signbit(D0)){ inside=false; break; } } if(inside){ C+=dV*(xyz*xyz.transpose()); // dbg<<xyz[0]<<" "<<xyz[1]<<" "<<xyz[2]<<" "<<dd/2.<<endl; } } } } return Matrix3r::Identity()*C.trace()-C; }
string GenerateCloud_water(vector<BasicSphere>& sphere_list, Vector3r lowerCorner, Vector3r upperCorner, long number, Real rad_std_dev, Real porosity) { typedef boost::minstd_rand StdGenerator; static StdGenerator generator; static boost::variate_generator<StdGenerator&, boost::uniform_real<> > random1(generator, boost::uniform_real<>(0,1)); // static boost::variate_generator<StdGenerator&, boost::normal_distribution<> > // randomN(generator, boost::normal_distribution<>(aggregateMeanRadius,aggregateSigmaRadius)); sphere_list.clear(); long tries = 1000; //nb of tries for positionning the next sphere Vector3r dimensions = upperCorner - lowerCorner; Real mean_radius = std::pow(dimensions.x()*dimensions.y()*dimensions.z()*(1-porosity)/(3.1416*1.3333*number),0.333333); //cerr << mean_radius; Real Rmin=mean_radius, Rmax=mean_radius; std::cerr << "generating aggregates ... "; long t, i; for (i=0; i<number; ++i) { BasicSphere s; s.second = (random1()-0.5)*rad_std_dev*mean_radius+mean_radius; for (t=0; t<tries; ++t) { s.first.x() = lowerCorner.x()+s.second+(dimensions.x()-2*s.second)*random1(); s.first.y() = lowerCorner.y()+s.second+(dimensions.y()-2*s.second)*random1(); s.first.z() = lowerCorner.z()+s.second+(dimensions.z()-2*s.second)*random1(); bool overlap=false; for (long j=0; (j<i && !overlap); j++) if ( pow(sphere_list[j].second+s.second, 2) > (sphere_list[j].first-s.first).squaredNorm()) overlap=true; if (!overlap){ sphere_list.push_back(s); Rmin = std::min(Rmin,s.second); Rmax = std::max(Rmax,s.second); break;} } if (t==tries) return "More than " + lexical_cast<string>(tries) + " tries while generating sphere number " + lexical_cast<string>(i+1) + "/" + lexical_cast<string>(number) + "."; } return "Generated a sample with " + lexical_cast<string>(number) + "spheres inside box of dimensions: (" + lexical_cast<string>(dimensions[0]) + "," + lexical_cast<string>(dimensions[1]) + "," + lexical_cast<string>(dimensions[2]) + ")." + " mean radius=" + lexical_cast<string>(mean_radius) + + " Rmin =" + lexical_cast<string>(Rmin) + + " Rmax =" + lexical_cast<string>(Rmax) + "."; }
void SimpleShear::createSphere(shared_ptr<Body>& body, Vector3r position, Real radius) { body = shared_ptr<Body>(new Body); body->groupMask=1; shared_ptr<NormalInelasticMat> mat(new NormalInelasticMat); shared_ptr<Aabb> aabb(new Aabb); shared_ptr<Sphere> iSphere(new Sphere); body->state->pos =position; body->state->ori =Quaternionr::Identity(); body->state->vel =Vector3r(0,0,0); body->state->angVel =Vector3r(0,0,0); Real masse =4.0/3.0*Mathr::PI*radius*radius*radius*density; body->state->mass =masse; body->state->inertia = Vector3r(2.0/5.0*masse*radius*radius,2.0/5.0*masse*radius*radius,2.0/5.0*masse*radius*radius); mat->young = sphereYoungModulus; mat->poisson = spherePoissonRatio; mat->frictionAngle = sphereFrictionDeg * Mathr::PI/180.0; body->material = mat; aabb->color = Vector3r(0,1,0); iSphere->radius = radius; iSphere->color = ((int)(floor(8*position.x()/length)))%2?Vector3r(0.7,0.7,0.7):Vector3r(0.45,0.45,0.45);// so that we have eight different colour bands body->shape = iSphere; body->bound = aabb; }
/* The following code is based on GPL-licensed K-3d importer module from https://github.com/K-3D/k3d/blob/master/modules/stl_io/mesh_reader.cpp TODO: read color/material, convert to scalar color in Woo */ vector<shared_ptr<Particle>> DemFuncs::importSTL(const string& filename, const shared_ptr<Material>& mat, int mask, Real color, Real scale, const Vector3r& shift, const Quaternionr& ori, Real threshold, Real maxBox, bool readColors, bool flex, Real thickness){ vector<shared_ptr<Particle>> ret; std::ifstream in(filename,std::ios::in|std::ios::binary); if(!in) throw std::runtime_error("Error opening "+filename+" for reading (STL import)."); char buffer[80]; in.read(buffer, 80); in.seekg(0, std::ios::beg); bool isAscii=boost::algorithm::starts_with(buffer,"solid"); // linear array of vertices, each triplet is one face // this is filled from ASCII and binary formats as intermediary representation // coordinate system change using (ori, scale, shift) is done already when reading vertices from the file vector<Vector3r> vertices; if(isAscii){ LOG_TRACE("STL: ascii format detected"); string lineBuf; long lineNo=-1; int fVertsNum=0; // number of vertices in this facet (for checking) for(std::getline(in,lineBuf); in; getline(in,lineBuf)){ lineNo++; string tok; std::istringstream line(lineBuf); line>>tok; if(tok=="facet"){ string tok2; line>>tok2; if(tok2!="normal") LOG_WARN("STL: 'normal' expected after 'facet' (line "+to_string(lineNo)+")"); // we ignore normal values: // Vector3r normal; line>>normal.x(); line>>normal.y(); line>>normal.z(); } else if(tok=="vertex"){ Vector3r p; line>>p.x(); line>>p.y(); line>>p.z(); vertices.push_back(ori*(p*scale+shift)); fVertsNum++; } else if(tok=="endfacet"){
string SimpleShear::GenerateCloud(vector<BasicSphere>& sphere_list,Vector3r lowerCorner,Vector3r upperCorner,long number,Real rad_std_dev, Real porosity) { sphere_list.clear(); long tries = 1000; //nb max of tries for positionning the next sphere Vector3r dimensions = upperCorner - lowerCorner; Real mean_radius = pow(dimensions.x()*dimensions.y()*dimensions.z()*(1-porosity)/(4.0/3.0*Mathr::PI*number),1.0/3.0); cerr << " mean radius " << mean_radius << endl;; // std::cerr << "generating aggregates ... "; long t, i; for (i=0; i<number; ++i) { BasicSphere s; for (t=0; t<tries; ++t) { s.second = (Mathr::UnitRandom()-0.5)*rad_std_dev*mean_radius+mean_radius; s.first.x() = lowerCorner.x()+s.second+(dimensions.x()-2*s.second)*Mathr::UnitRandom(); s.first.y() = lowerCorner.y()+s.second+(dimensions.y()-2*s.second)*Mathr::UnitRandom(); s.first.z() = lowerCorner.z()+s.second+(dimensions.z()-2*s.second)*Mathr::UnitRandom(); bool overlap=false; for (long j=0; (j<i && !overlap); j++) if ( pow(sphere_list[j].second+s.second, 2) > (sphere_list[j].first-s.first).squaredNorm()) overlap=true; if (!overlap) { sphere_list.push_back(s); // cout << "j'ai bien rajoute une sphere dans la liste" << endl; break; } } if (t==tries) { string str1="Generated a sample with " + boost::lexical_cast<string>(i) + " spheres inside box of dimensions: (" + boost::lexical_cast<string>(dimensions[0]) + "," + boost::lexical_cast<string>(dimensions[1]) + "," + boost::lexical_cast<string>(dimensions[2]) + ").\n"; return str1 + "More than " + boost::lexical_cast<string>(tries) + " tries while generating sphere number " + boost::lexical_cast<string>(i+1) + "/" + boost::lexical_cast<string>(number) + "."; } } return "Generated a sample with " + boost::lexical_cast<string>(number) + " spheres inside box of dimensions: (" + boost::lexical_cast<string>(dimensions[0]) + "," + boost::lexical_cast<string>(dimensions[1]) + "," + boost::lexical_cast<string>(dimensions[2]) + ")."; }
void SphereClumpGeom::recompute(int _div, bool failOk, bool fastOnly){ if((centers.empty() && radii.empty()) || centers.size()!=radii.size()){ if(failOk) { makeInvalid(); return;} throw std::runtime_error("SphereClumpGeom.recompute: centers and radii must have the same length (len(centers)="+to_string(centers.size())+", len(radii)="+to_string(radii.size())+"), and may not be empty."); } div=_div; // one single sphere: simple if(centers.size()==1){ pos=centers[0]; ori=Quaternionr::Identity(); volume=(4/3.)*M_PI*pow(radii[0],3); inertia=Vector3r::Constant((2/5.)*volume*pow(radii[0],2)); equivRad=radii[0]; return; } volume=0; Vector3r Sg=Vector3r::Zero(); Matrix3r Ig=Matrix3r::Zero(); if(_div<=0){ // non-intersecting: Steiner's theorem for(size_t i=0; i<centers.size(); i++){ const Real& r(radii[i]); const Vector3r& x(centers[i]); Real v=(4/3.)*M_PI*pow(r,3); volume+=v; Sg+=v*x; Ig+=woo::Volumetric::inertiaTensorTranslate(Vector3r::Constant((2/5.)*v*pow(r,2)).asDiagonal(),v,-1.*x); } } else { // intersecting: grid sampling Real rMin=Inf; AlignedBox3r aabb; for(size_t i=0; i<centers.size(); i++){ aabb.extend(centers[i]+Vector3r::Constant(radii[i])); aabb.extend(centers[i]-Vector3r::Constant(radii[i])); rMin=min(rMin,radii[i]); } if(rMin<=0){ if(failOk){ makeInvalid(); return; } throw std::runtime_error("SphereClumpGeom.recompute: minimum radius must be positive (not "+to_string(rMin)+")"); } Real dx=rMin/_div; Real dv=pow(dx,3); long nCellsApprox=(aabb.sizes()/dx).prod(); // don't compute anything, it would take too long if(fastOnly && nCellsApprox>1e5){ makeInvalid(); return; } if(nCellsApprox>1e8) LOG_WARN("SphereClumpGeom: space grid has "<<nCellsApprox<<" cells, computing inertia can take a long time."); Vector3r x; for(x.x()=aabb.min().x()+dx/2.; x.x()<aabb.max().x(); x.x()+=dx){ for(x.y()=aabb.min().y()+dx/2.; x.y()<aabb.max().y(); x.y()+=dx){ for(x.z()=aabb.min().z()+dx/2.; x.z()<aabb.max().z(); x.z()+=dx){ for(size_t i=0; i<centers.size(); i++){ if((x-centers[i]).squaredNorm()<pow(radii[i],2)){ volume+=dv; Sg+=dv*x; Ig+=dv*(x.dot(x)*Matrix3r::Identity()-x*x.transpose())+/*along princial axes of dv; perhaps negligible?*/Matrix3r(Vector3r::Constant(dv*pow(dx,2)/6.).asDiagonal()); break; } } } } } } woo::Volumetric::computePrincipalAxes(volume,Sg,Ig,pos,ori,inertia); equivRad=(inertia.array()/volume).sqrt().mean(); // mean of radii of gyration }