yade-dev team mailing list archive
-
yade-dev team
-
Mailing list archive
-
Message #10138
[Branch ~yade-pkg/yade/git-trunk] Rev 3715: Polyhedra implementation + examples (Contributed by Jan Elias).
------------------------------------------------------------
revno: 3715
committer: Jan Stransky <jan.stransky@xxxxxxxxxxx>
timestamp: Tue 2013-10-15 18:14:46 +0200
message:
Polyhedra implementation + examples (Contributed by Jan Elias).
added:
examples/polyhedra/
examples/polyhedra/ball.py
examples/polyhedra/free-fall.py
examples/polyhedra/irregular.py
examples/polyhedra/wall.py
pkg/dem/Polyhedra.cpp
pkg/dem/Polyhedra.hpp
pkg/dem/Polyhedra_Ig2.cpp
pkg/dem/Polyhedra_Ig2.hpp
pkg/dem/Polyhedra_splitter.cpp
pkg/dem/Polyhedra_splitter.hpp
pkg/dem/Polyhedra_support.cpp
py/_polyhedra_utils.cpp
py/polyhedra_utils.py
modified:
.gitignore
doc/sphinx/yadeSphinx.py
py/CMakeLists.txt
--
lp:yade
https://code.launchpad.net/~yade-pkg/yade/git-trunk
Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-pkg/yade/git-trunk/+edit-subscription
=== modified file '.gitignore'
--- .gitignore 2013-10-01 07:37:13 +0000
+++ .gitignore 2013-10-15 16:14:46 +0000
@@ -25,6 +25,7 @@
doc/sphinx/yade.utils.rst
doc/sphinx/yade.wrapper.rst
doc/sphinx/yade.ymport.rst
+doc/sphinx/yade.polyhedra_utils.rst
.kdev4/
*.kdev4
*.pyc
=== modified file 'doc/sphinx/yadeSphinx.py'
--- doc/sphinx/yadeSphinx.py 2013-09-11 10:55:59 +0000
+++ doc/sphinx/yadeSphinx.py 2013-10-15 16:14:46 +0000
@@ -35,7 +35,7 @@
#
# don't forget to put the module in index.rst as well!
#
-mods={'export':[],'post2d':[],'pack':['_packSpheres','_packPredicates','_packObb'],'plot':[],'timing':[],'utils':['_utils'],'ymport':[],'geom':[],'bodiesHandling':[],'qt':['qt._GLViewer'],'linterpolation':[]}
+mods={'export':[],'post2d':[],'pack':['_packSpheres','_packPredicates','_packObb'],'plot':[],'timing':[],'utils':['_utils'],'polyhedra_utils':['_polyhedra_utils'],'ymport':[],'geom':[],'bodiesHandling':[],'qt':['qt._GLViewer'],'linterpolation':[]}
#
# generate documentation, in alphabetical order
mm=mods.keys(); mm.sort()
=== added directory 'examples/polyhedra'
=== added file 'examples/polyhedra/ball.py'
--- examples/polyhedra/ball.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/ball.py 2013-10-15 16:14:46 +0000
@@ -0,0 +1,47 @@
+from yade import plot, polyhedra_utils
+from yade import qt
+
+m = PolyhedraMat()
+m.density = 2600 #kg/m^3
+m.Ks = 20000
+m.Kn = 1E6 #Pa
+m.frictionAngle = 0.6 #rad
+
+maxLoad = 3E6
+minLoad = 1E3
+
+O.bodies.append(utils.wall(0,axis=2,sense=1, material = m))
+
+t = polyhedra_utils.polyhedralBall(0.025, 100, m, (0,0,0))
+t.state.pos = (0,0,0.5)
+O.bodies.append(t)
+
+def checkUnbalanced():
+ print "unbalanced forces = %.5f, position %f, %f, %f"%(utils.unbalancedForce(), t.state.pos[0], t.state.pos[1], t.state.pos[2])
+
+
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb(),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.5,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalanced()',realPeriod=3,label='checker')
+]
+
+
+#O.dt=0.025*polyhedra_utils.PWaveTimeStep()
+O.dt=0.00025
+
+
+qt.Controller()
+V = qt.View()
+
+
+O.saveTmp()
+#O.run()
=== added file 'examples/polyhedra/free-fall.py'
--- examples/polyhedra/free-fall.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/free-fall.py 2013-10-15 16:14:46 +0000
@@ -0,0 +1,58 @@
+from yade import plot, polyhedra_utils
+
+
+gravel = PolyhedraMat()
+gravel.density = 2600 #kg/m^3
+gravel.Ks = 20000
+gravel.Kn = 1E7 #Pa
+gravel.frictionAngle = 0.5 #rad
+
+steel = PolyhedraMat()
+steel.density = 7850 #kg/m^3
+steel.Ks = 10*gravel.Ks
+steel.Kn = 10*gravel.Kn
+steel.frictionAngle = 0.4 #rad
+
+rubber = PolyhedraMat()
+rubber.density = 1000 #kg/m^3
+rubber.Ks = gravel.Ks/10
+rubber.Kn = gravel.Kn/10
+rubber.frictionAngle = 0.7 #rad
+
+O.bodies.append(polyhedra_utils.polyhedra(gravel,v=((0,0,-0.05),(0.3,0,-0.05),(0.3,0.3,-0.05),(0,0.3,-0.05),(0,0,0),(0.3,0,0),(0.3,0.3,0),(0,0.3,0)),fixed=True, color=(0.35,0.35,0.35)))
+#O.bodies.append(utils.wall(0,axis=1,sense=1, material = gravel))
+#O.bodies.append(utils.wall(0,axis=0,sense=1, material = gravel))
+#O.bodies.append(utils.wall(0.3,axis=1,sense=-1, material = gravel))
+#O.bodies.append(utils.wall(0.3,axis=0,sense=-1, material = gravel))
+
+polyhedra_utils.fillBox((0,0,0), (0.3,0.3,0.3),gravel,sizemin=[0.025,0.025,0.025],sizemax=[0.05,0.05,0.05],seed=4)
+
+def checkUnbalancedI():
+ print "iter %d, time elapsed %f, time step %.5e, unbalanced forces = %.5f"%(O.iter, O.realtime, O.dt, utils.unbalancedForce())
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb(),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.3,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalancedI()',realPeriod=5,label='checker')
+]
+
+
+#O.dt=0.25*polyhedra_utils.PWaveTimeStep()
+O.dt=0.0025*polyhedra_utils.PWaveTimeStep()
+
+
+from yade import qt
+qt.Controller()
+V = qt.View()
+V.screenSize = (550,450)
+V.sceneRadius = 1
+V.eyePosition = (0.7,0.5,0.1)
+V.upVector = (0,0,1)
+V.lookAt = (0.15,0.15,0.1)
=== added file 'examples/polyhedra/irregular.py'
--- examples/polyhedra/irregular.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/irregular.py 2013-10-15 16:14:46 +0000
@@ -0,0 +1,63 @@
+# gravity deposition, continuing with oedometric test after stabilization
+# shows also how to run parametric studies with yade-batch
+
+# The components of the batch are:
+# 1. table with parameters, one set of parameters per line (ccc.table)
+# 2. utils.readParamsFromTable which reads respective line from the parameter file
+# 3. the simulation muse be run using yade-batch, not yade
+#
+# $ yade-batch --job-threads=1 03-oedometric-test.table 03-oedometric-test.py
+#
+
+
+# create box with free top, and ceate loose packing inside the box
+from yade import plot, polyhedra_utils
+from yade import qt
+
+m = PolyhedraMat()
+m.density = 2600 #kg/m^3
+m.Ks = 20000
+m.Kn = 1E6 #Pa
+m.frictionAngle = 0.6 #rad
+
+O.bodies.append(utils.wall(0,axis=2,sense=1, material = m))
+
+t = polyhedra_utils.polyhedra(m,size = (0.06,0.06,0.06),seed = 5)
+t.state.pos = (0.,0.,0.5)
+O.bodies.append(t)
+
+def checkUnbalanced():
+ # at the very start, unbalanced force can be low as there is only few contacts, but it does not mean the packing is stable
+ print "unbalanced forces = %.5f, position %f, %f, %f"%(utils.unbalancedForce(), t.state.pos[0], t.state.pos[1], t.state.pos[2])
+
+
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb(),Bo1_Facet_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.5,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalanced()',realPeriod=3,label='checker')
+
+]
+
+
+#O.dt=0.025*polyhedra_utils.PWaveTimeStep()
+O.dt=0.00025
+
+
+
+
+qt.Controller()
+V = qt.View()
+
+
+O.saveTmp()
+#O.run()
+#O.save('./done')
+utils.waitIfBatch()
=== added file 'examples/polyhedra/wall.py'
--- examples/polyhedra/wall.py 1970-01-01 00:00:00 +0000
+++ examples/polyhedra/wall.py 2013-10-15 16:14:46 +0000
@@ -0,0 +1,72 @@
+from yade import qt
+import numpy as np
+import random
+from yade import polyhedra_utils
+
+m = PolyhedraMat()
+m.density = 1000
+m.Ks = 5E6
+m.Kn = 5E8
+m.frictionAngle = 0.7
+
+size = 0.1;
+vertices = [[0,0,0],[size,0,0],[size,size,0],[size,size,size],[0,size,0],[0,size,size],[0,0,size],[size,0,size]]
+
+for i in range(0,10):
+ for j in range(0,10):
+ t = polyhedra_utils.polyhedra(m,v=vertices)
+ t.state.pos = (0,(i+0.5)*size-5*size,(j+0.5)*size*1)
+ O.bodies.append(t)
+
+
+
+qt.Controller()
+V = qt.View()
+V.screenSize = (750,550)
+V.sceneRadius = 1
+V.eyePosition = (-1.5,1.5,0.5)
+V.lookAt = (0,-0.5,0)
+V.upVector = (0,0,1)
+
+
+O.bodies.append(utils.wall(0,axis=2,sense=1,material=m))
+
+def checkUnbalanced():
+ print ('iter %d, time elapsed %f, unbalanced forces = %f'%(O.iter, O.realtime, utils.unbalancedForce()))
+ if O.iter<200: return
+ if utils.unbalancedForce()>0.01: return
+ AddBall()
+
+def AddBall():
+ ball = polyhedra_utils.polyhedralBall(size*1, 40, m, (-size*2.5,0,size*7),mask=1)
+ ball.shape.color = (0,0,0.9)
+ ball.state.vel = (15,0,0)
+ O.bodies.append(ball)
+ checker.dead = True
+
+O.engines=[
+ ForceResetter(),
+ InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Polyhedra_Aabb(),Bo1_Wall_Aabb()]),
+ InteractionLoop(
+ [Ig2_Wall_Polyhedra_PolyhedraGeom(), Ig2_Polyhedra_Polyhedra_PolyhedraGeom(), Ig2_Facet_Polyhedra_PolyhedraGeom()],
+ [Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys()], # collision "physics"
+ [PolyhedraVolumetricLaw()] # contact law -- apply forces
+ ),
+ #GravityEngine(gravity=(0,0,-9.81)),
+ NewtonIntegrator(damping=0.5,gravity=(0,0,-9.81)),
+ PyRunner(command='checkUnbalanced()',realPeriod=3,label='checker')#,
+ # wideo_recording
+ #qt.SnapshotEngine(fileBase='W',iterPeriod=50,label='snapshot')
+]
+
+#O.dt=.5*polyhedra_utils.PWaveTimeStep()
+O.dt = 0.000025
+O.saveTmp()
+
+#O.run()
+
+
+#comment ball to see stability
+
+
+
=== added file 'pkg/dem/Polyhedra.cpp'
--- pkg/dem/Polyhedra.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra.cpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,427 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include<yade/core/Interaction.hpp>
+#include<yade/core/Omega.hpp>
+#include<yade/core/Scene.hpp>
+#include<yade/core/State.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+#include<yade/pkg/common/Aabb.hpp>
+#include"Polyhedra.hpp"
+
+#define _USE_MATH_DEFINES
+
+YADE_PLUGIN(/* self-contained in hpp: */ (Polyhedra) (PolyhedraGeom) (Bo1_Polyhedra_Aabb) (PolyhedraPhys) (PolyhedraMat) (Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys) (PolyhedraVolumetricLaw)
+ /* some code in cpp (this file): */
+ #ifdef YADE_OPENGL
+ (Gl1_Polyhedra)
+ #endif
+ );
+
+//*********************************************************************************
+/* Polyhedra Constructor */
+
+void Polyhedra::Initialize(){
+
+ if (init) return;
+
+ bool isRandom = false;
+
+ //get vertices
+ int N = (int) v.size();
+ if (N==0) {
+ //generate randomly
+ while ((int) v.size()<4) GenerateRandomGeometry();
+ N = (int) v.size();
+ isRandom = true;
+ }
+
+ //compute convex hull of vertices
+ std::vector<CGALpoint> points;
+ points.resize(v.size());
+ for(int i=0;i<N;i++) {
+ points[i] = CGALpoint(v[i][0],v[i][1],v[i][2]);
+ }
+
+ CGAL::convex_hull_3(points.begin(), points.end(), P);
+
+ //connect triagular facets if possible
+ std::transform(P.facets_begin(), P.facets_end(), P.planes_begin(),Plane_equation());
+ P = Simplify(P, 1E-9);
+
+ //modify order of v according to CGAl polyhedron
+ int i = 0;
+ v.clear();
+ for (Polyhedron::Vertex_iterator vIter = P.vertices_begin(); vIter != P.vertices_end(); ++vIter, i++){
+ v.push_back(Vector3r(vIter->point().x(),vIter->point().y(),vIter->point().z()));
+ }
+
+ //list surface triangles for plotting
+ faceTri.clear();
+ std::transform(P.facets_begin(), P.facets_end(), P.planes_begin(),Plane_equation());
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter != P.facets_end(); fIter++){
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ int n = fIter->facet_degree();
+ hfc0 = fIter->facet_begin();
+ int a = std::distance(P.vertices_begin(), hfc0->vertex());
+ for (int i=2; i<n; i++){
+ ++hfc0;
+ faceTri.push_back(a);
+ faceTri.push_back(std::distance(P.vertices_begin(), hfc0->vertex()));
+ faceTri.push_back(std::distance(P.vertices_begin(), hfc0->next()->vertex()));
+ }
+ }
+
+ //compute centroid and volume
+ P_volume_centroid(P, &volume, ¢roid);
+ //check vierd behavior of CGAL in tessalation
+ if(isRandom && volume*1.75<4./3.*3.14*size[0]/2.*size[1]/2.*size[2]/2.) {
+ v.clear();
+ seed = rand();
+ Initialize();
+ }
+ Vector3r translation((-1)*centroid);
+
+ //set centroid to be [0,0,0]
+ for(int i=0;i<N;i++) {
+ v[i] = v[i]-centroid;
+ }
+ if(isRandom) centroid = Vector3r::Zero();
+
+ Vector3r origin(0,0,0);
+
+ //move and rotate also the CGAL structure Polyhedron
+ Transformation t_trans(1.,0.,0.,translation[0],0.,1.,0.,translation[1],0.,0.,1.,translation[2],1.);
+ std::transform( P.points_begin(), P.points_end(), P.points_begin(), t_trans);
+
+ //compute inertia
+ double vtet;
+ Vector3r ctet;
+ Matrix3r Itet1, Itet2;
+ Matrix3r inertia_tensor(Matrix3r::Zero());
+ for(int i=0; i<(int) faceTri.size(); i+=3){
+ vtet = std::abs((origin-v[faceTri[i+2]]).dot((v[faceTri[i]]-v[faceTri[i+2]]).cross(v[faceTri[i+1]]-v[faceTri[i+2]]))/6.);
+ ctet = (origin+v[faceTri[i]]+v[faceTri[i+1]]+v[faceTri[i+2]]) / 4.;
+ Itet1 = TetraInertiaTensor(origin-ctet, v[faceTri[i]]-ctet, v[faceTri[i+1]]-ctet, v[faceTri[i+2]]-ctet);
+ ctet = ctet-origin;
+ Itet2<<
+ ctet[1]*ctet[1]+ctet[2]*ctet[2], -ctet[0]*ctet[1], -ctet[0]*ctet[2],
+ -ctet[0]*ctet[1], ctet[0]*ctet[0]+ctet[2]*ctet[2], -ctet[2]*ctet[1],
+ -ctet[0]*ctet[2], -ctet[2]*ctet[1], ctet[1]*ctet[1]+ctet[0]*ctet[0];
+ inertia_tensor = inertia_tensor + Itet1 + Itet2*vtet;
+ }
+
+ if(abs(inertia_tensor(0,1))+abs(inertia_tensor(0,2))+abs(inertia_tensor(1,2)) < 1E-13){
+ // no need to rotate, inertia already diagonal
+ orientation = Quaternionr::Identity();
+ inertia = Vector3r(inertia_tensor(0,0),inertia_tensor(1,1),inertia_tensor(2,2));
+ }else{
+ // calculate eigenvectors of I
+ Vector3r rot;
+ Matrix3r I_rot(Matrix3r::Zero()), I_new(Matrix3r::Zero());
+ matrixEigenDecomposition(inertia_tensor,I_rot,I_new);
+ // I_rot = eigenvectors of inertia_tensors in columns
+ // I_new = eigenvalues on diagonal
+ // set positove direction of vectors - otherwise reloading does not work
+ Matrix3r sign(Matrix3r::Zero());
+ double max_v_signed = I_rot(0,0);
+ double max_v = abs(I_rot(0,0));
+ if (max_v < abs(I_rot(1,0))) {max_v_signed = I_rot(1,0); max_v = abs(I_rot(1,0));}
+ if (max_v < abs(I_rot(2,0))) {max_v_signed = I_rot(2,0); max_v = abs(I_rot(2,0));}
+ sign(0,0) = max_v_signed/max_v;
+ max_v_signed = I_rot(0,1);
+ max_v = abs(I_rot(0,1));
+ if (max_v < abs(I_rot(1,1))) {max_v_signed = I_rot(1,1); max_v = abs(I_rot(1,1));}
+ if (max_v < abs(I_rot(2,1))) {max_v_signed = I_rot(2,1); max_v = abs(I_rot(2,1));}
+ sign(1,1) = max_v_signed/max_v;
+ sign(2,2) = 1.;
+ I_rot = I_rot*sign;
+ // force the eigenvectors to be right-hand oriented
+ Vector3r third = (I_rot.col(0)).cross(I_rot.col(1));
+ I_rot(0,2) = third[0];
+ I_rot(1,2) = third[1];
+ I_rot(2,2) = third[2];
+
+
+ inertia = Vector3r(I_new(0,0),I_new(1,1),I_new(2,2));
+ orientation = Quaternionr(I_rot);
+ //rotate the voronoi cell so that x - is maximal inertia axis and z - is minimal inertia axis
+ //orientation.normalize(); //not needed
+ for(int i=0; i< (int) v.size();i++) {
+ v[i] = orientation.conjugate()*v[i];
+ }
+
+ //rotate also the CGAL structure Polyhedron
+ Matrix3r rot_mat = (orientation.conjugate()).toRotationMatrix();
+ Transformation t_rot(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2),rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),1.);
+ std::transform( P.points_begin(), P.points_end(), P.points_begin(), t_rot);
+
+ }
+ //initialization done
+ init = 1;
+}
+
+//**************************************************************************
+/* Generator of randomly shaped polyhedron based on Voronoi tessellation*/
+
+void Polyhedra::GenerateRandomGeometry(){
+ srand(seed);
+
+ vector<CGALpoint> nuclei;
+ nuclei.push_back(CGALpoint(5.,5.,5.));
+ CGALpoint trial;
+ int iter = 0;
+ bool isOK;
+ //fill box 5x5x5 with randomly located nuclei with restricted minimal mutual distance 0.75 which gives approximate mean mutual distance 1;
+ double dist_min2 = 0.75*0.75;
+ while(iter<500){
+ isOK = true;
+ iter++;
+ trial = CGALpoint(double(rand())/RAND_MAX*5.+2.5,double(rand())/RAND_MAX*5.+2.5,double(rand())/RAND_MAX*5.+2.5);
+ for(int i=0;i< (int) nuclei.size();i++) {
+ isOK = pow(nuclei[i].x()-trial.x(),2)+pow(nuclei[i].y()-trial.y(),2)+pow(nuclei[i].z()-trial.z(),2) > dist_min2;
+ if (!isOK) break;
+ }
+
+ if(isOK){
+ iter = 0;
+ nuclei.push_back(trial);
+ }
+ }
+
+
+ //perform Voronoi tessellation
+ nuclei.erase(nuclei.begin());
+ Triangulation dt(nuclei.begin(), nuclei.end());
+ Triangulation::Vertex_handle zero_point = dt.insert(CGALpoint(5.,5.,5.));
+ v.clear();
+ std::vector<typename Triangulation::Cell_handle> ch_cells;
+ dt.incident_cells(zero_point,std::back_inserter(ch_cells));
+ for(std::vector<typename Triangulation::Cell_handle>::iterator ci = ch_cells.begin(); ci !=ch_cells.end(); ++ci){
+ v.push_back(FromCGALPoint(dt.dual(*ci))-Vector3r(5.,5.,5.));
+ }
+
+
+ //resize and rotate the voronoi cell
+ Quaternionr Rot(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+ Rot.normalize();
+ for(int i=0; i< (int) v.size();i++) {
+ v[i] = Rot*(Vector3r(v[i][0]*size[0],v[i][1]*size[1],v[i][2]*size[2]));
+ }
+
+ //to avoid patological cases (that should not be present, but CGAL works somehow unpredicable)
+ if (v.size() < 8) {
+ cout << "wrong " << v.size() << endl;
+ v.clear();
+ seed = rand();
+ GenerateRandomGeometry();
+ }
+}
+
+//****************************************************************************************
+/* Destructor */
+
+Polyhedra::~Polyhedra(){}
+
+//****************************************************************************************
+/* Destructor */
+
+PolyhedraGeom::~PolyhedraGeom(){}
+
+//****************************************************************************************
+/* AaBb overlap checker */
+
+void Bo1_Polyhedra_Aabb::go(const shared_ptr<Shape>& ig, shared_ptr<Bound>& bv, const Se3r& se3, const Body*){
+
+ Polyhedra* t=static_cast<Polyhedra*>(ig.get());
+ if (!t->IsInitialized()) t->Initialize();
+ if(!bv){ bv=shared_ptr<Bound>(new Aabb); }
+ Aabb* aabb=static_cast<Aabb*>(bv.get());
+ //Quaternionr invRot=se3.orientation.conjugate();
+ int N = (int) t->v.size();
+ Vector3r v_g, mincoords(0.,0.,0.), maxcoords(0.,0.,0.);
+ for(int i=0; i<N; i++) {
+ v_g=se3.orientation*t->v[i]; // vertices in global coordinates
+ mincoords = Vector3r(min(mincoords[0],v_g[0]),min(mincoords[1],v_g[1]),min(mincoords[2],v_g[2]));
+ maxcoords = Vector3r(max(maxcoords[0],v_g[0]),max(maxcoords[1],v_g[1]),max(maxcoords[2],v_g[2]));
+ }
+ aabb->min=se3.position+mincoords;
+ aabb->max=se3.position+maxcoords;
+}
+
+//**********************************************************************************
+/* Plotting */
+
+#ifdef YADE_OPENGL
+ #include<yade/lib/opengl/OpenGLWrapper.hpp>
+ void Gl1_Polyhedra::go(const shared_ptr<Shape>& cm, const shared_ptr<State>&,bool,const GLViewInfo&)
+ {
+ glMaterialv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,Vector3r(cm->color[0],cm->color[1],cm->color[2]));
+ glColor3v(cm->color);
+ Polyhedra* t=static_cast<Polyhedra*>(cm.get());
+ vector<int> faceTri = t->GetSurfaceTriangulation();
+
+ if (0) {
+ glDisable(GL_LIGHTING);
+ glBegin(GL_LINES);
+ #define __ONEWIRE(a,b) glVertex3v(t->v[a]);glVertex3v(t->v[b])
+ for(int tri=0; tri < (int) faceTri.size(); tri+=3) {__ONEWIRE(faceTri[tri],faceTri[tri+1]); __ONEWIRE(faceTri[tri],faceTri[tri+2]); __ONEWIRE(faceTri[tri+1],faceTri[tri+2]);}
+ #undef __ONEWIRE
+ glEnd();
+ }
+ else
+ {
+ Vector3r centroid = t->GetCentroid();
+ Vector3r faceCenter, n;
+ glDisable(GL_CULL_FACE); glEnable(GL_LIGHTING);
+ glBegin(GL_TRIANGLES);
+ #define __ONEFACE(a,b,c) n=(t->v[b]-t->v[a]).cross(t->v[c]-t->v[a]); n.normalize(); faceCenter=(t->v[a]+t->v[b]+t->v[c])/3.; if((faceCenter-centroid).dot(n)<0)n=-n; glNormal3v(n); glVertex3v(t->v[a]); glVertex3v(t->v[b]); glVertex3v(t->v[c]);
+ for(int tri=0; tri < (int) faceTri.size(); tri+=3) {__ONEFACE(faceTri[tri],faceTri[tri+1],faceTri[tri+2]);}
+ #undef __ONEFACE
+ glEnd();
+ }
+ }
+#endif
+
+//**********************************************************************************
+//!Precompute data needed for rotating tangent vectors attached to the interaction
+
+void PolyhedraGeom::precompute(const State& rbp1, const State& rbp2, const Scene* scene, const shared_ptr<Interaction>& c, const Vector3r&
+currentNormal, bool isNew, const Vector3r& shift2){
+
+ if(!isNew) {
+ orthonormal_axis = normal.cross(currentNormal);
+ Real angle = scene->dt*0.5*normal.dot(rbp1.angVel + rbp2.angVel);
+ twist_axis = angle*normal;}
+ else twist_axis=orthonormal_axis=Vector3r::Zero();
+ //Update contact normal
+ normal=currentNormal;
+ //Precompute shear increment
+ Vector3r c1x = (contactPoint - rbp1.pos);
+ Vector3r c2x = (contactPoint - rbp2.pos + shift2);
+ Vector3r relativeVelocity = (rbp2.vel+rbp2.angVel.cross(c2x)) - (rbp1.vel+rbp1.angVel.cross(c1x));
+ //keep the shear part only
+ relativeVelocity = relativeVelocity-normal.dot(relativeVelocity)*normal;
+ shearInc = relativeVelocity*scene->dt;
+}
+
+//**********************************************************************************
+Vector3r& PolyhedraGeom::rotate(Vector3r& shearForce) const {
+ // approximated rotations
+ shearForce -= shearForce.cross(orthonormal_axis);
+ shearForce -= shearForce.cross(twist_axis);
+ //NOTE : make sure it is in the tangent plane? It's never been done before. Is it not adding rounding errors at the same time in fact?...
+ shearForce -= normal.dot(shearForce)*normal;
+ return shearForce;
+}
+
+
+//**********************************************************************************
+/* Material law, physics */
+
+void Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys::go( const shared_ptr<Material>& b1
+ , const shared_ptr<Material>& b2
+ , const shared_ptr<Interaction>& interaction)
+{
+ if(interaction->phys) return;
+ const shared_ptr<PolyhedraMat>& mat1 = YADE_PTR_CAST<PolyhedraMat>(b1);
+ const shared_ptr<PolyhedraMat>& mat2 = YADE_PTR_CAST<PolyhedraMat>(b2);
+ interaction->phys = shared_ptr<PolyhedraPhys>(new PolyhedraPhys());
+ const shared_ptr<PolyhedraPhys>& contactPhysics = YADE_PTR_CAST<PolyhedraPhys>(interaction->phys);
+ Real Kna = mat1->Kn;
+ Real Knb = mat2->Kn;
+ Real Ksa = mat1->Ks;
+ Real Ksb = mat2->Ks;
+ Real frictionAngle = std::min(mat1->frictionAngle,mat2->frictionAngle);
+ contactPhysics->tangensOfFrictionAngle = std::tan(frictionAngle);
+ contactPhysics->kn = Kna*Knb/(Kna+Knb);
+ contactPhysics->ks = Ksa*Ksb/(Ksa+Ksb);
+};
+
+//**************************************************************************************
+#if 1
+Real PolyhedraVolumetricLaw::getPlasticDissipation() {return (Real) plasticDissipation;}
+void PolyhedraVolumetricLaw::initPlasticDissipation(Real initVal) {plasticDissipation.reset(); plasticDissipation+=initVal;}
+Real PolyhedraVolumetricLaw::elasticEnergy()
+{
+ Real energy=0;
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ FrictPhys* phys = dynamic_cast<FrictPhys*>(I->phys.get());
+ if(phys) {
+ energy += 0.5*(phys->normalForce.squaredNorm()/phys->kn + phys->shearForce.squaredNorm()/phys->ks);}
+ }
+ return energy;
+}
+#endif
+
+
+//**************************************************************************************
+// Apply forces on polyhedrons in collision based on geometric configuration
+void PolyhedraVolumetricLaw::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* I){
+
+ if (!I->geom) {I->phys=shared_ptr<IPhys>(); return;}
+ const shared_ptr<PolyhedraGeom>& contactGeom(dynamic_pointer_cast<PolyhedraGeom>(I->geom));
+ if(!contactGeom) {I->phys=shared_ptr<IPhys>(); return;}
+ const Body::id_t idA=I->getId1(), idB=I->getId2();
+ const shared_ptr<Body>& A=Body::byId(idA), B=Body::byId(idB);
+
+ PolyhedraPhys* phys = dynamic_cast<PolyhedraPhys*>(I->phys.get());
+
+ //erase the interaction when aAbB shows separation, otherwise keep it to be able to store previous separating plane for fast detection of separation
+ if (A->bound->min[0] >= B->bound->max[0] || B->bound->min[0] >= A->bound->max[0] || A->bound->min[1] >= B->bound->max[1] || B->bound->min[1] >= A->bound->max[1] || A->bound->min[2] >= B->bound->max[2] || B->bound->min[2] >= A->bound->max[2]) {
+ scene->interactions->requestErase(I);
+ return;
+ }
+
+ //zero penetration depth means no interaction force
+ if(!(contactGeom->penetrationVolume > 0) ) {I->phys=shared_ptr<IPhys>(); return;}
+ Vector3r normalForce=contactGeom->normal*contactGeom->penetrationVolume*phys->kn;
+
+ //shear force: in case the polyhdras are separated and come to contact again, one should not use the previous shear force
+ if (contactGeom->isShearNew)
+ shearForce = Vector3r::Zero();
+ else
+ shearForce = contactGeom->rotate(shearForce);
+
+ const Vector3r& shearDisp = contactGeom->shearInc;
+ shearForce -= phys->ks*shearDisp;
+
+ Real maxFs = normalForce.squaredNorm()*std::pow(phys->tangensOfFrictionAngle,2);
+
+ if (likely(!scene->trackEnergy && !traceEnergy)){//Update force but don't compute energy terms (see below))
+ // PFC3d SlipModel, is using friction angle. CoulombCriterion
+ if( shearForce.squaredNorm() > maxFs ){
+ Real ratio = sqrt(maxFs) / shearForce.norm();
+ shearForce *= ratio;}
+ } else {
+ //almost the same with additional Vector3r instatinated for energy tracing,
+ //duplicated block to make sure there is no cost for the instanciation of the vector when traceEnergy==false
+ if(shearForce.squaredNorm() > maxFs){
+ Real ratio = sqrt(maxFs) / shearForce.norm();
+ Vector3r trialForce=shearForce;//store prev force for definition of plastic slip
+ //define the plastic work input and increment the total plastic energy dissipated
+ shearForce *= ratio;
+ Real dissip=((1/phys->ks)*(trialForce-shearForce)).dot(shearForce);
+ if (traceEnergy) plasticDissipation += dissip;
+ else if(dissip>0) scene->energy->add(dissip,"plastDissip",plastDissipIx,false);
+ }
+ // compute elastic energy as well
+ scene->energy->add(0.5*(normalForce.squaredNorm()/phys->kn+shearForce.squaredNorm()/phys->ks),"elastPotential",elastPotentialIx,true);
+ }
+
+ Vector3r F = -normalForce-shearForce;
+ if (contactGeom->equivalentPenetrationDepth != contactGeom->equivalentPenetrationDepth) exit(1);
+ scene->forces.addForce (idA,F);
+ scene->forces.addForce (idB, -F);
+ scene->forces.addTorque(idA, -(A->state->pos-contactGeom->contactPoint).cross(F));
+ scene->forces.addTorque(idB, (B->state->pos-contactGeom->contactPoint).cross(F));
+
+ //needed to be able to acces interaction forces in other parts of yade
+ phys->normalForce = normalForce;
+ phys->shearForce = shearForce;
+}
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra.hpp'
--- pkg/dem/Polyhedra.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra.hpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,274 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+
+#pragma once
+
+#ifdef YADE_CGAL
+
+#include<vector>
+#include<yade/core/Shape.hpp>
+#include<yade/core/IGeom.hpp>
+#include<yade/core/GlobalEngine.hpp>
+#include<yade/core/Material.hpp>
+#include<yade/pkg/common/Aabb.hpp>
+#include<yade/pkg/common/Dispatching.hpp>
+#include<yade/pkg/dem/FrictPhys.hpp>
+#include<yade/pkg/common/Wall.hpp>
+#include<yade/pkg/common/Facet.hpp>
+#include<yade/lib/base/openmp-accu.hpp>
+
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Delaunay_triangulation_3.h>
+#include <CGAL/Triangulation_data_structure_3.h>
+#include <CGAL/Polyhedron_3.h>
+#include <CGAL/Polyhedron_items_with_id_3.h>
+#include <CGAL/convex_hull_3.h>
+#include <CGAL/Tetrahedron_3.h>
+#include <CGAL/linear_least_squares_fitting_3.h>
+
+#include<time.h>
+
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
+
+//CGAL definitions - does not work with another kernel!! Why???
+typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
+typedef CGAL::Polyhedron_3<K> Polyhedron;
+typedef CGAL::Delaunay_triangulation_3<K> Triangulation;
+typedef K::Point_3 CGALpoint;
+typedef K::Vector_3 CGALvector;
+typedef CGAL::Aff_transformation_3<K> Transformation;
+typedef K::Segment_3 Segment;
+typedef CGAL::Triangle_3<K> Triangle;
+typedef CGAL::Plane_3<K> Plane;
+typedef CGAL::Line_3<K> Line;
+typedef CGAL::Origin CGAL_ORIGIN;
+
+//**********************************************************************************
+class Polyhedra: public Shape{
+ public:
+ //constructor from Vertices
+ Polyhedra(std::vector<Vector3r> V) { createIndex(); v.resize(V.size()); for(int i=0;i<(int) V.size();i++) v[i]=V[i]; Initialize();} //contructor of "random" polyhedra
+ Polyhedra(Vector3r xsize, int xseed) { createIndex(); seed=xseed; size=xsize; v.clear(); Initialize();}
+ virtual ~Polyhedra();
+ Vector3r GetCentroid(){Initialize(); return centroid;}
+ Vector3r GetInertia(){Initialize(); return inertia;}
+ vector<int> GetSurfaceTriangulation(){Initialize(); return faceTri;}
+ void Initialize();
+ bool IsInitialized(){return init;}
+ std::vector<Vector3r> GetOriginalVertices();
+ double GetVolume(){Initialize(); return volume;}
+ Quaternionr GetOri(){Initialize(); return orientation;}
+ Polyhedron GetPolyhedron(){return P;};
+ void Clear(){v.clear(); P.clear(); init = 0; size = Vector3r(1.,1.,1.); faceTri.clear();};
+
+ protected:
+ //triangulation of facets for plotting
+ vector<int> faceTri;
+ //centroid = (0,0,0) for random Polyhedra
+ Vector3r centroid;
+ //CGAL structure Polyhedron
+ Polyhedron P;
+ //sign of performed initialization
+ bool init;
+ //centroid Volume
+ double volume;
+ //centroid inerta - diagonal of the tensor
+ Vector3r inertia;
+ //orientation, that provides diagonal inertia tensor
+ Quaternionr orientation;
+ void GenerateRandomGeometry();
+
+ YADE_CLASS_BASE_DOC_ATTRS_INIT_CTOR_PY(Polyhedra,Shape,"Polyhedral (convex) geometry.",
+ ((std::vector<Vector3r>,v,,,"Tetrahedron vertices in global coordinate system."))
+ ((int,seed, time(NULL),,"Seed for random generator."))
+ ((Vector3r, size, Vector3r(1.,1.,1.),,"Size of the grain in meters - x,y,z - before random rotation")),
+ /*init*/,
+ /*ctor*/
+ createIndex();
+ init = 0,
+ .def("Initialize",&Polyhedra::Initialize,"Initialization")
+ .def("GetVolume",&Polyhedra::GetVolume,"return polyhedra's volume")
+ .def("GetInertia",&Polyhedra::GetInertia,"return polyhedra's inertia tensor")
+ .def("GetOri",&Polyhedra::GetOri,"return polyhedra's orientation")
+ .def("GetCentroid",&Polyhedra::GetCentroid,"return polyhedra's centroid")
+ );
+ REGISTER_CLASS_INDEX(Polyhedra,Shape);
+};
+REGISTER_SERIALIZABLE(Polyhedra);
+
+
+//***************************************************************************
+/*! Collision configuration for Polyhedra and something.
+ * This is expressed as penetration volume properties: centroid, volume, depth ...
+ *
+ * Self-contained. */
+class PolyhedraGeom: public IGeom{
+ public:
+ virtual ~PolyhedraGeom();
+ //precompute data for shear evaluation
+ void precompute(const State& rbp1, const State& rbp2, const Scene* scene, const shared_ptr<Interaction>& c, const Vector3r& currentNormal, bool isNew, const Vector3r& shift2);
+ Vector3r& rotate(Vector3r& shearForce) const;
+ //sep_plane is a code storing plane, that previously separated two polyhedras. It is used for faster detection of non-overlap.
+ std::vector<int> sep_plane;
+ bool isShearNew;
+ protected:
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(PolyhedraGeom,IGeom,"Geometry of interaction between 2 :yref:`vector<Polyhedra>`, including volumetric characteristics",
+ ((Real,penetrationVolume,NaN,,"Volume of overlap [m³]"))
+ ((Real,equivalentCrossSection,NaN,,"Cross-section area of the overlap (perpendicular to the normal) - not used"))
+ ((Real,equivalentPenetrationDepth,NaN,,"volume / equivalentCrossSection - not used"))
+ ((Vector3r,contactPoint,Vector3r::Zero(),,"Contact point (global coords), centriod of the overlapping polyhedron"))
+ ((Vector3r,shearInc,Vector3r::Zero(),,"Shear displacement increment in the last step"))
+ ((Vector3r,normal,Vector3r::Zero(),,"Normal direction of the interaction"))
+ ((Vector3r,twist_axis,Vector3r::Zero(),,""))
+ ((Vector3r,orthonormal_axis,Vector3r::Zero(),,"")),
+ createIndex();
+ sep_plane.assign(3,0);
+ );
+ //FUNCTOR2D(Tetra,Tetra);
+ REGISTER_CLASS_INDEX(PolyhedraGeom,IGeom);
+};
+REGISTER_SERIALIZABLE(PolyhedraGeom);
+
+//***************************************************************************
+/*! Creates Aabb from Polyhedra.
+ *
+ * Self-contained. */
+class Bo1_Polyhedra_Aabb: public BoundFunctor{
+ public:
+ void go(const shared_ptr<Shape>& ig, shared_ptr<Bound>& bv, const Se3r& se3, const Body*);
+ FUNCTOR1D(Polyhedra);
+ YADE_CLASS_BASE_DOC(Bo1_Polyhedra_Aabb,BoundFunctor,"Create/update :yref:`Aabb` of a :yref:`Polyhedra`");
+};
+REGISTER_SERIALIZABLE(Bo1_Polyhedra_Aabb);
+
+//***************************************************************************
+#ifdef YADE_OPENGL
+ #include<yade/pkg/common/GLDrawFunctors.hpp>
+ /*! Draw Polyhedra using OpenGL */
+ class Gl1_Polyhedra: public GlShapeFunctor{
+ public:
+ virtual void go(const shared_ptr<Shape>&, const shared_ptr<State>&,bool,const GLViewInfo&);
+ YADE_CLASS_BASE_DOC(Gl1_Polyhedra,GlShapeFunctor,"Renders :yref:`Polyhedra` object");
+ RENDERS(Polyhedra);
+ };
+ REGISTER_SERIALIZABLE(Gl1_Polyhedra);
+#endif
+
+//***************************************************************************
+/*! Elastic material */
+class PolyhedraMat: public Material{
+ public:
+ PolyhedraMat(double N, double S, double F){Kn=N; Ks=S; frictionAngle=F;};
+ double GetStrength(){return strength;};
+ virtual ~PolyhedraMat(){};
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(PolyhedraMat,Material,"Elastic material with Coulomb friction.",
+ ((Real,Kn,1e8,,"Normal volumetric 'stiffness' (N/m3)."))
+ ((Real,Ks,1e5,,"Shear stiffness (N/m)."))
+ ((Real,frictionAngle,.5,,"Contact friction angle (in radians)."))
+ ((bool,IsSplitable,0,,"To be splitted ... or not"))
+ ((double,strength,100,,"Stress at whis polyhedra of volume 4/3*pi [mm] breaks.")),
+ /*ctor*/ createIndex();
+ );
+ REGISTER_CLASS_INDEX(PolyhedraMat,Material);
+};
+REGISTER_SERIALIZABLE(PolyhedraMat);
+
+//***************************************************************************
+class PolyhedraPhys: public IPhys{
+ public:
+ virtual ~PolyhedraPhys(){};
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR(PolyhedraPhys,IPhys,"Simple elastic material with friction for volumetric constitutive laws",
+ ((Real,kn,0,,"Normal stiffness"))
+ ((Vector3r,normalForce,Vector3r::Zero(),,"Normal force after previous step (in global coordinates)."))
+ ((Real,ks,0,,"Shear stiffness"))
+ ((Vector3r,shearForce,Vector3r::Zero(),,"Shear force after previous step (in global coordinates)."))
+ ((Real,tangensOfFrictionAngle,NaN,,"tangens of angle of internal friction")),
+ /*ctor*/ createIndex();
+ );
+ REGISTER_CLASS_INDEX(PolyhedraPhys,IPhys);
+};
+REGISTER_SERIALIZABLE(PolyhedraPhys);
+
+//***************************************************************************
+class Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys: public IPhysFunctor{
+ public:
+ virtual void go(const shared_ptr<Material>& b1,
+ const shared_ptr<Material>& b2,
+ const shared_ptr<Interaction>& interaction);
+ FUNCTOR2D(PolyhedraMat,PolyhedraMat);
+ YADE_CLASS_BASE_DOC_ATTRS(Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys,IPhysFunctor,"",
+ );
+};
+REGISTER_SERIALIZABLE(Ip2_PolyhedraMat_PolyhedraMat_PolyhedraPhys);
+
+//***************************************************************************
+/*! Calculate physical response based on penetration configuration given by TTetraGeom. */
+
+class PolyhedraVolumetricLaw: public LawFunctor{
+ OpenMPAccumulator<Real> plasticDissipation;
+ virtual void go(shared_ptr<IGeom>&, shared_ptr<IPhys>&, Interaction*);
+ Real elasticEnergy ();
+ Real getPlasticDissipation();
+ void initPlasticDissipation(Real initVal=0);
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(PolyhedraVolumetricLaw,LawFunctor,"Calculate physical response of 2 :yref:`vector<Polyhedra>` in interaction, based on penetration configuration given by :yref:`PolyhedraGeom`.",
+ ((Vector3r,shearForce,Vector3r::Zero(),,"Shear force from last step"))
+ ((bool,traceEnergy,false,,"Define the total energy dissipated in plastic slips at all contacts. This will trace only plastic energy in this law, see O.trackEnergy for a more complete energies tracing"))
+ ((int,plastDissipIx,-1,(Attr::hidden|Attr::noSave),"Index for plastic dissipation (with O.trackEnergy)"))
+ ((int,elastPotentialIx,-1,(Attr::hidden|Attr::noSave),"Index for elastic potential energy (with O.trackEnergy)"))
+ ,,
+ .def("elasticEnergy",&PolyhedraVolumetricLaw::elasticEnergy,"Compute and return the total elastic energy in all \"FrictPhys\" contacts")
+ .def("plasticDissipation",&PolyhedraVolumetricLaw::getPlasticDissipation,"Total energy dissipated in plastic slips at all FrictPhys contacts. Computed only if :yref:`Law2_ScGeom_FrictPhys_CundallStrack::traceEnergy` is true.")
+ .def("initPlasticDissipation",&PolyhedraVolumetricLaw::initPlasticDissipation,"Initialize cummulated plastic dissipation to a value (0 by default).")
+ );
+ FUNCTOR2D(PolyhedraGeom,PolyhedraPhys);
+ DECLARE_LOGGER;
+};
+REGISTER_SERIALIZABLE(PolyhedraVolumetricLaw);
+
+
+//***************************************************************************
+//compute plane equation from three points on the facet
+struct Plane_equation {
+ template <class Facet>
+ typename Facet::Plane_3 operator()( Facet& f) {
+ typename Facet::Halfedge_handle h = f.halfedge();
+ typedef typename Facet::Plane_3 Plane;
+ return Plane( h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ }
+};
+//get Tetrahedron inertia
+Matrix3r TetraInertiaTensor(Vector3r av,Vector3r bv,Vector3r cv,Vector3r dv);
+//return intersection of two polyhedrons
+Polyhedron Polyhedron_Polyhedron_intersection(Polyhedron A, Polyhedron B, CGALpoint X, CGALpoint centroidA, CGALpoint centroidB, std::vector<int> &code);
+//return intersection of plane & polyhedron
+Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Plane B, CGALpoint centroid, CGALpoint X);
+//return approximate intersection of sphere & polyhedron
+bool Sphere_Polyhedron_intersection(Polyhedron A, double r, CGALpoint C, CGALpoint centroid, double volume, CGALvector normal, double area);
+//return volume and centroid of polyhedra
+bool P_volume_centroid(Polyhedron P, double * volume, Vector3r * centroid);
+//CGAL - miniEigen communication
+Vector3r FromCGALPoint(CGALpoint A);
+Vector3r FromCGALVector(CGALvector A);
+CGALpoint ToCGALPoint(Vector3r A);
+CGALvector ToCGALVector(Vector3r A);
+//determination of intersection of two polyhedras
+bool do_intersect(Polyhedron A, Polyhedron B);
+bool do_intersect(Polyhedron A, Polyhedron B, std::vector<int> &sep_plane);
+//connect triagular facets if possible
+Polyhedron Simplify(Polyhedron P, double lim);
+//list of facets and edges
+void PrintPolyhedron(Polyhedron P);
+//normal by least square fitting of separating segments
+Vector3r FindNormal(Polyhedron Int, Polyhedron PA, Polyhedron PB);
+//calculate area of projection of polyhedron into the plane
+double CalculateProjectionArea(Polyhedron Int, CGALvector CGALnormal);
+//split polyhedron
+void SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction);
+//new polyhedra
+shared_ptr<Body> NewPolyhedra(vector<Vector3r> v, shared_ptr<Material> mat);
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_Ig2.cpp'
--- pkg/dem/Polyhedra_Ig2.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_Ig2.cpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,305 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"Polyhedra.hpp"
+#include"Polyhedra_Ig2.hpp"
+
+#define _USE_MATH_DEFINES
+
+YADE_PLUGIN(/* self-contained in hpp: */ (Ig2_Polyhedra_Polyhedra_PolyhedraGeom) (Ig2_Wall_Polyhedra_PolyhedraGeom) (Ig2_Facet_Polyhedra_PolyhedraGeom) );
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedras. */
+
+bool Ig2_Polyhedra_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+ //get polyhedras
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se31.orientation).toRotationMatrix();
+ Vector3r trans_vec = se31.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+
+ //move and rotate 2nd the CGAL structure Polyhedron
+ rot_mat = (se32.orientation).toRotationMatrix();
+ trans_vec = se32.position;
+ t_rot_trans = Transformation(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->sep_plane.assign(3,0);
+ bang->contactPoint = Vector3r(0,0,0);
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ bang->isShearNew = bang->equivalentPenetrationDepth<=0;
+ }
+
+
+ //find intersection Polyhedra
+ Polyhedron Int;
+ Int = Polyhedron_Polyhedron_intersection(PA,PB,ToCGALPoint(bang->contactPoint),ToCGALPoint(se31.position),ToCGALPoint(se32.position), bang->sep_plane);
+
+ //volume and centroid of intersection
+ double volume;
+ Vector3r centroid;
+ P_volume_centroid(Int, &volume, ¢roid);
+ if(isnan(volume) || volume<=1E-25 || volume > min(A->GetVolume(),B->GetVolume())) {bang->equivalentPenetrationDepth=0; return true;}
+
+ //find normal direction
+ Vector3r normal = FindNormal(Int, PA, PB);
+ if((se32.position-centroid).dot(normal)<0) normal*=-1;
+
+ //calculate area of projection of Intersection into the normal plane
+ //double area = CalculateProjectionArea(Int, ToCGALVector(normal));
+ //if(isnan(area) || area<=1E-20) {bang->equivalentPenetrationDepth=0; return true;}
+ double area = volume/1E-8;
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,bang->isShearNew,shift2);
+ bang->normal=normal;
+
+ return true;
+}
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedron and Wall. */
+
+bool Ig2_Wall_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+ const int& PA(cm1->cast<Wall>().axis);
+ const int& sense(cm1->cast<Wall>().sense);
+ const Se3r& se31=state1.se3; const Se3r& se32=state2.se3;
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate also the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se32.orientation).toRotationMatrix();
+ Vector3r trans_vec = se32.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+
+ //move wall
+ Vector3r normal = Vector3r(0,0,0);
+ normal[PA] = sense;
+ CGALvector CGALnormal = CGALvector(normal[0],normal[1],normal[2]);
+ Plane A = Plane(CGALpoint(se31.position[0],se31.position[1],se31.position[2]),CGALvector(normal[0],normal[1],normal[2]));
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->contactPoint = Vector3r(0,0,0);
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ bang->isShearNew = bang->equivalentPenetrationDepth<=0;
+ }
+
+ //find intersection Polyhedra
+ Polyhedron Int;
+ Int = Polyhedron_Plane_intersection(PB,A,ToCGALPoint(se32.position),ToCGALPoint(bang->contactPoint));
+
+ //volume and centroid of intersection
+ double volume;
+ Vector3r centroid;
+ P_volume_centroid(Int, &volume, ¢roid);
+ if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume()) {bang->equivalentPenetrationDepth=0; return true;}
+
+ //calculate area of projection of Intersection into the normal plane
+ double area = volume/1E-8;
+ //double area = CalculateProjectionArea(Int, CGALnormal);
+ //if(isnan(area) || area<=1E-20) {bang->equivalentPenetrationDepth=0; return true;}
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,isNew,shift2);
+ bang->normal=FromCGALVector(CGALnormal);
+
+ return true;
+}
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedron and Facet. */
+
+bool Ig2_Facet_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Facet* A = static_cast<Facet*>(cm1.get());
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se32.orientation).toRotationMatrix();
+ Vector3r trans_vec = se32.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+
+ //move and rotate facet
+ vector<CGALpoint> v;
+ v.resize(6);
+ for (int i=0; i<3; i++) v[i] = ToCGALPoint(se31.orientation*A->vertices[i] + se31.position); // vertices in global coordinates
+
+ //determine
+ CGALpoint f_center((v[0].x()+v[1].x()+v[2].x())/3.,(v[0].y()+v[1].y()+v[2].y())/3.,(v[0].z()+v[1].z()+v[2].z())/3.);
+ CGALvector f_normal = CGAL::cross_product((v[1]-v[0]),(v[2]-v[0]));
+
+ //chage normal + change order of vertices to get outwarding facet normal for initial tetrahedron
+ if ((ToCGALPoint(se32.position)-f_center)*(f_normal) < 0) {
+ f_normal = (-1)*f_normal;
+ CGALpoint help;
+ help = v[0];
+ v[0]=v[1];
+ v[1]=help;
+ }
+
+ double f_area = sqrt(f_normal.squared_length());
+ for (int i=3; i<6; i++) v[i] = v[i-3]-f_normal/f_area*0.05*sqrt(f_area); // vertices in global coordinates
+
+ Polyhedron PA;
+ //use convex hull
+ //CGAL::convex_hull_3(v.begin(), v.end(), PA);
+
+ //construct polyhedron directly
+ Polyhedron::Halfedge_iterator hei = PA.make_tetrahedron(v[4],v[1],v[0],v[2]);
+ hei = PA.split_vertex(hei, hei->next()->opposite());
+ hei->vertex()->point() = v[3];
+ hei = PA.split_facet(hei->next()->next()->next(),hei->next());
+ hei = PA.split_vertex(hei, hei->next_on_vertex()->next_on_vertex());
+ hei->vertex()->point() = v[5];
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->sep_plane.assign(3,0);
+ bang->contactPoint = Vector3r(0,0,0);
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ bang->isShearNew = bang->equivalentPenetrationDepth<=0;
+ }
+
+ //find intersection Polyhedra
+ Polyhedron Int;
+ Int = Polyhedron_Polyhedron_intersection(PA,PB,ToCGALPoint(bang->contactPoint),ToCGALPoint(se31.position),ToCGALPoint(se32.position), bang->sep_plane);
+
+ //volume and centroid of intersection
+ double volume;
+ Vector3r centroid;
+ P_volume_centroid(Int, &volume, ¢roid);
+ if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume()) {bang->equivalentPenetrationDepth=0; return true;}
+
+ //find normal direction
+ Vector3r normal = FindNormal(Int, PA, PB);
+ if((se32.position-centroid).dot(normal)<0) normal*=-1;
+
+ //calculate area of projection of Intersection into the normal plane
+ double area = volume/1E-8;
+ //double area = CalculateProjectionArea(Int, ToCGALVector(normal));
+ //if(isnan(area) || area<=1E-20) {bang->equivalentPenetrationDepth=0; return true;}
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,bang->isShearNew,shift2);
+ bang->normal=normal;
+
+ return true;
+}
+
+//**********************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedron and Wall. */
+
+/*
+bool Ig2_Sphere_Polyhedra_PolyhedraGeom::go(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& interaction){
+
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+ const Real& r=cm1->cast<Sphere>().radius;
+
+ //cout << "Sphere x Polyhedra" << endl;
+
+ bool isNew = !interaction->geom;
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se32.orientation).toRotationMatrix();
+ Vector3r trans_vec = se32.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+
+ shared_ptr<PolyhedraGeom> bang;
+ if (isNew) {
+ // new interaction
+ bang=shared_ptr<PolyhedraGeom>(new PolyhedraGeom());
+ bang->isShearNew = true;
+ interaction->geom = bang;
+ }else{
+ // use data from old interaction
+ bang=YADE_PTR_CAST<PolyhedraGeom>(interaction->geom);
+ }
+
+ //volume and centroid of intersection
+ double volume, area;
+ CGALvector normalCGAL;
+ CGALpoint centroidCGAL=ToCGALPoint(se32.position);
+ Sphere_Polyhedron_intersection(PB, r, ToCGALPoint(se31.position), centroidCGAL, volume, normalCGAL, area);
+ if(isnan(volume) || volume<=1E-25 || volume > B->GetVolume()) {bang->equivalentPenetrationDepth=0; return true;}
+ Vector3r centroid = FromCGALPoint(centroidCGAL);
+ Vector3r normal = FromCGALVector(normalCGAL);
+
+
+
+ // store calculated stuff in bang; some is redundant
+ bang->equivalentCrossSection=area;
+ bang->contactPoint=centroid;
+ bang->penetrationVolume=volume;
+ bang->equivalentPenetrationDepth=volume/area;
+ bang->precompute(state1,state2,scene,interaction,normal,bang->isShearNew,shift2);
+ bang->normal=normal;
+
+ return true;
+}
+*/
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_Ig2.hpp'
--- pkg/dem/Polyhedra_Ig2.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_Ig2.hpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,71 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"Polyhedra.hpp"
+#include<yade/pkg/common/Sphere.hpp>
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Polyhedras. */
+class Ig2_Polyhedra_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Polyhedra_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Polyhedra,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Polyhedra,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Polyhedra_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between 2 Polyhedras");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Polyhedra_Polyhedra_PolyhedraGeom);
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Wall & Polyhedra. */
+class Ig2_Wall_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Wall_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Wall,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Wall,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Wall_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between Wall and Polyhedra");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Wall_Polyhedra_PolyhedraGeom);
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Facet & Polyhedra. */
+class Ig2_Facet_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Facet_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Facet,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Facet,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Facet_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between Facet and Polyhedra");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Facet_Polyhedra_PolyhedraGeom);
+
+//***************************************************************************
+/*! Create Polyhedra (collision geometry) from colliding Sphere & Polyhedra. */
+/*
+class Ig2_Sphere_Polyhedra_PolyhedraGeom: public IGeomFunctor
+{
+ public:
+ virtual ~Ig2_Sphere_Polyhedra_PolyhedraGeom(){};
+ virtual bool go(const shared_ptr<Shape>& cm1, const shared_ptr<Shape>& cm2, const State& state1, const State& state2, const Vector3r& shift2, const bool& force, const shared_ptr<Interaction>& c);
+ FUNCTOR2D(Sphere,Polyhedra);
+ DEFINE_FUNCTOR_ORDER_2D(Sphere,Polyhedra);
+ YADE_CLASS_BASE_DOC(Ig2_Sphere_Polyhedra_PolyhedraGeom,IGeomFunctor,"Create/update geometry of collision between Sphere and Polyhedra");
+ DECLARE_LOGGER;
+ private:
+};
+REGISTER_SERIALIZABLE(Ig2_Sphere_Polyhedra_PolyhedraGeom);
+*/
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_splitter.cpp'
--- pkg/dem/Polyhedra_splitter.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_splitter.cpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,109 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include<yade/pkg/dem/Polyhedra_splitter.hpp>
+
+
+YADE_PLUGIN((PolyhedraSplitter));
+CREATE_LOGGER(PolyhedraSplitter);
+
+//*********************************************************************************
+/* Evaluate tensorial stress estimation in polyhedras */
+
+void getStressForEachBody(vector<Matrix3r>& bStresses){
+ const shared_ptr<Scene>& scene=Omega::instance().getScene();
+ bStresses.resize(scene->bodies->size());
+ for (size_t k=0;k<scene->bodies->size();k++) bStresses[k]=Matrix3r::Zero();
+ FOREACH(const shared_ptr<Interaction>& I, *scene->interactions){
+ if(!I->isReal()) continue;
+ PolyhedraGeom* geom=YADE_CAST<PolyhedraGeom*>(I->geom.get());
+ PolyhedraPhys* phys=YADE_CAST<PolyhedraPhys*>(I->phys.get());
+ if(!geom || !phys) continue;
+ Vector3r f=phys->normalForce+phys->shearForce;
+ //Sum f_i*l_j for each contact of each particle
+ bStresses[I->getId1()] -=f*((geom->contactPoint-Body::byId(I->getId1(),scene)->state->pos).transpose());
+ bStresses[I->getId2()] +=f*((geom->contactPoint-Body::byId(I->getId2(),scene)->state->pos).transpose());
+
+
+ }
+}
+
+//*********************************************************************************
+/* Size dependent strength */
+
+double PolyhedraSplitter::getStrength(double volume, double strength){
+ //equvalent radius
+ double r_eq = pow(volume*3./4./Mathr::PI,1./3.);
+ //r should be in milimeters
+ return strength/(r_eq/1000.);
+}
+
+//*********************************************************************************
+/* Symmetrization of stress tensor */
+
+void PolyhedraSplitter::Symmetrize(Matrix3r & bStress){
+ bStress(0,1) = (bStress(0,1) + bStress(1,0))/2.;
+ bStress(0,2) = (bStress(0,2) + bStress(2,0))/2.;
+ bStress(1,2) = (bStress(1,2) + bStress(2,1))/2.;
+ bStress(1,0) = bStress(0,1);
+ bStress(2,0) = bStress(0,2);
+ bStress(2,1) = bStress(1,2);
+}
+
+//*********************************************************************************
+/* Split if stress exceed strength */
+
+void PolyhedraSplitter::action()
+{
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+
+ vector<shared_ptr<Body> > bodies;
+ vector<Vector3r > directions;
+ vector<double > sigmas;
+
+
+
+ vector<Matrix3r> bStresses;
+ getStressForEachBody(bStresses);
+
+ int i = -1;
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ i++;
+ if(!b || !b->material || !b->shape) continue;
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ shared_ptr<PolyhedraMat> m=dynamic_pointer_cast<PolyhedraMat>(b->material);
+
+ if(p && m->IsSplitable){
+ //not real strees, to get real one, it has to be divided by body volume
+ Matrix3r stress = bStresses[i];
+
+ //get eigenstresses
+ Symmetrize(stress);
+ Matrix3r I_vect(Matrix3r::Zero()), I_valu(Matrix3r::Zero());
+ matrixEigenDecomposition(stress,I_vect,I_valu);
+ int min_i = 0;
+ if (I_valu(min_i,min_i) > I_valu(1,1)) min_i = 1;
+ if (I_valu(min_i,min_i) > I_valu(2,2)) min_i = 2;
+ int max_i = 0;
+ if (I_valu(max_i,max_i) < I_valu(1,1)) max_i = 1;
+ if (I_valu(max_i,max_i) < I_valu(2,2)) max_i = 2;
+
+ //division of stress by volume
+ double comp_stress = I_valu(min_i,min_i)/p->GetVolume();
+ double tens_stress = I_valu(max_i,max_i)/p->GetVolume();
+ Vector3r dirC = I_vect.col(max_i);
+ Vector3r dirT = I_vect.col(min_i);
+ Vector3r dir = dirC.normalized() + dirT.normalized();;
+ double sigma_t = -comp_stress/2.+ tens_stress;
+ if (sigma_t > getStrength(p->GetVolume(),m->GetStrength())) {bodies.push_back(b); directions.push_back(dir); sigmas.push_back(sigma_t);};
+ }
+ }
+ for(int i=0; i<int(bodies.size()); i++){
+ SplitPolyhedra(bodies[i], directions[i]);
+ }
+}
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_splitter.hpp'
--- pkg/dem/Polyhedra_splitter.hpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_splitter.hpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,33 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+
+#pragma once
+
+#ifdef YADE_CGAL
+
+#include<yade/pkg/common/PeriodicEngines.hpp>
+#include<yade/core/Interaction.hpp>
+
+#include"Polyhedra.hpp"
+
+//*********************************************************************************
+/* Polyhedra Splitter */
+class PolyhedraSplitter : public PeriodicEngine{
+
+ public:
+ virtual void action();
+ double getStrength(double volume, double strength);
+ void Symmetrize(Matrix3r & bStress);
+
+ YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(PolyhedraSplitter,PeriodicEngine,"Engine that splits polyhedras.",
+ ,
+ /*ctor*/
+ ,/*py*/
+ );
+ DECLARE_LOGGER;
+};
+
+REGISTER_SERIALIZABLE(PolyhedraSplitter);
+
+#endif // YADE_CGAL
=== added file 'pkg/dem/Polyhedra_support.cpp'
--- pkg/dem/Polyhedra_support.cpp 1970-01-01 00:00:00 +0000
+++ pkg/dem/Polyhedra_support.cpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,817 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"Polyhedra.hpp"
+
+#define _USE_MATH_DEFINES
+
+
+//EMPRIRICAL CONSTANTS - ADJUST IF SEGMENTATION FAULT OCCUR, IT IS A PROBLEM OF CGAL. THESE ARE USED TO CHECK CGAL IMPUTS
+//DISTANCE_LIMIT controls numerical issues in calculating intersection. It should be small enough to neglect only extremely small overlaps, but large enough to prevent errors during computation of convex hull
+#define DISTANCE_LIMIT 1E-11 //-11
+//MERGE_PLANES_LIMIT - if two facets of two intersecting polyhedron differ less, then they are treated ose one only
+#define MERGE_PLANES_LIMIT 1E-18 //18
+//SIMPLIFY_LIMIT - if two facets of one polyhedron differ less, then they are joint into one facet
+#define SIMPLIFY_LIMIT 1E-20 //19
+//FIND_NORMAL_LIMIT - to determine which facet of intersection belongs to which polyhedron
+#define FIND_NORMAL_LIMIT 1E-40
+
+
+//**********************************************************************************
+//return volume and centroid of polyhedron
+
+bool P_volume_centroid(Polyhedron P, double * volume, Vector3r * centroid){
+
+
+ Vector3r basepoint = FromCGALPoint(P.vertices_begin()->point());
+ Vector3r A,B,C,D;
+ (*volume) = 0;
+ double vtet;
+ (*centroid) = Vector3r(0.,0.,0.);
+
+ //compute centroid and volume
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter != P.facets_end(); fIter++){
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ hfc0 = fIter->facet_begin();
+ int n = fIter->facet_degree();
+ A = FromCGALPoint(hfc0->vertex()->point());
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ for (int i=2; i<n; i++){
+ ++hfc0;
+ B = C;
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ vtet = std::abs((basepoint-C).dot((A-C).cross(B-C)))/6.;
+ *volume += vtet;
+ *centroid = *centroid + (basepoint+A+B+C) / 4. * vtet;
+ }
+ }
+ *centroid = *centroid/(*volume);
+ return true;
+}
+
+//**********************************************************************************
+//STOLEN FROM TETRA body of Vaclav Smilauer
+
+/*! Calculates tetrahedron inertia relative to the origin (0,0,0), with unit density (scales linearly).
+
+See article F. Tonon, "Explicit Exact Formulas for the 3-D Tetrahedron Inertia Tensor in Terms of its Vertex Coordinates", http://www.scipub.org/fulltext/jms2/jms2118-11.pdf
+*/
+
+//Centroid MUST be [0,0,0]
+Matrix3r TetraInertiaTensor(Vector3r av,Vector3r bv,Vector3r cv,Vector3r dv){
+ #define x1 av[0]
+ #define y1 av[1]
+ #define z1 av[2]
+ #define x2 bv[0]
+ #define y2 bv[1]
+ #define z2 bv[2]
+ #define x3 cv[0]
+ #define y3 cv[1]
+ #define z3 cv[2]
+ #define x4 dv[0]
+ #define y4 dv[1]
+ #define z4 dv[2]
+
+ // Jacobian of transformation to the reference 4hedron
+ double detJ=(x2-x1)*(y3-y1)*(z4-z1)+(x3-x1)*(y4-y1)*(z2-z1)+(x4-x1)*(y2-y1)*(z3-z1)
+ -(x2-x1)*(y4-y1)*(z3-z1)-(x3-x1)*(y2-y1)*(z4-z1)-(x4-x1)*(y3-y1)*(z2-z1);
+ detJ=fabs(detJ);
+ double a=detJ*(y1*y1+y1*y2+y2*y2+y1*y3+y2*y3+
+ y3*y3+y1*y4+y2*y4+y3*y4+y4*y4+z1*z1+z1*z2+
+ z2*z2+z1*z3+z2*z3+z3*z3+z1*z4+z2*z4+z3*z4+z4*z4)/60.;
+ double b=detJ*(x1*x1+x1*x2+x2*x2+x1*x3+x2*x3+x3*x3+
+ x1*x4+x2*x4+x3*x4+x4*x4+z1*z1+z1*z2+z2*z2+z1*z3+
+ z2*z3+z3*z3+z1*z4+z2*z4+z3*z4+z4*z4)/60.;
+ double c=detJ*(x1*x1+x1*x2+x2*x2+x1*x3+x2*x3+x3*x3+x1*x4+
+ x2*x4+x3*x4+x4*x4+y1*y1+y1*y2+y2*y2+y1*y3+
+ y2*y3+y3*y3+y1*y4+y2*y4+y3*y4+y4*y4)/60.;
+ // a' in the article etc.
+ double a__=detJ*(2*y1*z1+y2*z1+y3*z1+y4*z1+y1*z2+
+ 2*y2*z2+y3*z2+y4*z2+y1*z3+y2*z3+2*y3*z3+
+ y4*z3+y1*z4+y2*z4+y3*z4+2*y4*z4)/120.;
+ double b__=detJ*(2*x1*z1+x2*z1+x3*z1+x4*z1+x1*z2+
+ 2*x2*z2+x3*z2+x4*z2+x1*z3+x2*z3+2*x3*z3+
+ x4*z3+x1*z4+x2*z4+x3*z4+2*x4*z4)/120.;
+ double c__=detJ*(2*x1*y1+x2*y1+x3*y1+x4*y1+x1*y2+
+ 2*x2*y2+x3*y2+x4*y2+x1*y3+x2*y3+2*x3*y3+
+ x4*y3+x1*y4+x2*y4+x3*y4+2*x4*y4)/120.;
+
+ Matrix3r ret; ret<<
+ a , -c__, -b__,
+ -c__, b , -a__,
+ -b__, -a__, c ;
+ return ret;
+
+ #undef x1
+ #undef y1
+ #undef z1
+ #undef x2
+ #undef y2
+ #undef z2
+ #undef x3
+ #undef y3
+ #undef z3
+ #undef x4
+ #undef y4
+ #undef z4
+}
+
+//**********************************************************************************
+//distace of point from a plane (squared) with sign
+double Oriented_squared_distance(Plane P, CGALpoint x){
+ double h = P.a()*x.x()+P.b()*x.y()+P.c()*x.z()+P.d();
+ return ((h>0.)-(h<0.))*pow(h,2)/(CGALvector(P.a(),P.b(),P.c())).squared_length();
+}
+
+//**********************************************************************************
+// test if point is inside polyhedra in strong sence, i.e. boundary location is not enough
+bool Is_inside_Polyhedron(Polyhedron P, CGALpoint inside){
+ Polyhedron::Plane_iterator pi;
+ for(pi=P.planes_begin(); pi!=P.planes_end(); ++pi){
+ if( ! pi->has_on_negative_side(inside)) return false;
+ }
+ return true;
+}
+
+//**********************************************************************************
+// test if point is inside polyhedra not closer than lim to its boundary
+bool Is_inside_Polyhedron(Polyhedron P, CGALpoint inside, double lim){
+ Polyhedron::Plane_iterator pi;
+ lim = pow(lim,2);
+ for(pi=P.planes_begin(); pi!=P.planes_end(); ++pi){
+ if(Oriented_squared_distance(*pi, inside)>-lim) return false;
+ }
+ return true;
+}
+
+//**********************************************************************************
+//test if two polyhedron intersect
+bool do_intersect(Polyhedron A, Polyhedron B){
+ std::vector<int> sep_plane;
+ sep_plane.assign(3,0);
+ return do_intersect(A, B, sep_plane);
+}
+
+//**********************************************************************************
+//test if two polyhedron intersect based on previous data
+bool do_intersect(Polyhedron A, Polyhedron B, std::vector<int> &sep_plane){
+
+
+ bool found;
+ //check previous separation plane
+ switch (sep_plane[0]){
+ case 1: //separation plane was previously determined as sep_plane[2]-th plane of A polyhedron
+ {
+ if(unlikely((unsigned) sep_plane[2]>=A.size_of_facets())) break;
+ Polyhedron::Facet_iterator fIter = A.facets_begin();
+ for (int i=0; i<sep_plane[2]; i++) ++fIter;
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) return false;
+ }
+ break;
+ case 2: //separation plane was previously determined as sep_plane[2]-th plane of B polyhedron
+ {
+ if(unlikely((unsigned) sep_plane[2]>=B.size_of_facets())) break;
+ Polyhedron::Facet_iterator fIter = B.facets_begin();
+ for (int i=0; i<sep_plane[2]; i++) ++fIter;
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) return false;
+ }
+ break;
+ case 3: //separation plane was previously given by sep_plane[1]-th and sep_plane[2]-th edge of A & B polyhedrons
+ {
+ if(unlikely((unsigned) sep_plane[1]>=A.size_of_halfedges()/2)) break;
+ if(unlikely((unsigned) sep_plane[2]>=B.size_of_halfedges()/2)) break;
+ Polyhedron::Edge_iterator eIter1 = A.edges_begin();
+ Polyhedron::Edge_iterator eIter2 = B.edges_begin();
+ for (int i=0; i<sep_plane[1]; i++) ++eIter1;
+ for (int i=0; i<sep_plane[2]; i++) ++eIter2;
+ found = true;
+ Plane X(eIter1->vertex()->point(),CGAL::cross_product((eIter1->vertex()->point() - eIter1->opposite()->vertex()->point()),(eIter2->vertex()->point() - eIter2->opposite()->vertex()->point())));
+ if (!X.has_on_positive_side(B.vertices_begin()->point())) X = X.opposite();
+
+ double lim = pow(DISTANCE_LIMIT,2);
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (Oriented_squared_distance(X, vIter->point())>lim) {found = false; break;};
+ }
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! X.has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) return false;
+ }
+ break;
+ }
+
+ //regular test with no previous information about separating plane
+ //test all planes from A
+ int i = 0;
+ for (Polyhedron::Facet_iterator fIter = A.facets_begin(); fIter != A.facets_end(); fIter++, i++){
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) {sep_plane[0] = 1; sep_plane[1] = 1; sep_plane[2] = i; return false;}
+ }
+ //test all planes from B
+ i = 0;
+ for (Polyhedron::Facet_iterator fIter = B.facets_begin(); fIter != B.facets_end(); fIter++, i++){
+ found = true;
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (! fIter->plane().has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) {sep_plane[0] = 2; sep_plane[1] = 2; sep_plane[2] = i; return false;}
+ }
+ //test all pairs of edges from A & B
+ Plane X;
+ CGALvector vA;
+ double lim = pow(DISTANCE_LIMIT,2);
+ i = 0;
+ for (Polyhedron::Edge_iterator eIter1 = A.edges_begin(); eIter1 != A.edges_end(); ++eIter1, i++){
+ vA = eIter1->vertex()->point() - eIter1->opposite()->vertex()->point();
+ int j = 0;
+ for (Polyhedron::Edge_iterator eIter2 = B.edges_begin(); eIter2 != B.edges_end(); ++eIter2, j++){
+ found = true;
+ X = Plane(eIter1->vertex()->point(),CGAL::cross_product(vA,(eIter2->vertex()->point() - eIter2->opposite()->vertex()->point()) ));
+ if (!X.has_on_positive_side(B.vertices_begin()->point())) X = X.opposite();
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end(); ++vIter){
+ if (Oriented_squared_distance(X, vIter->point())>lim) {found = false; break;};
+ }
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end(); ++vIter){
+ if (! X.has_on_positive_side(vIter->point())) {found = false; break;};
+ }
+ if(found) {sep_plane[0] = 3; sep_plane[1] = i; sep_plane[2] = j; return false;}
+ }
+ }
+
+ sep_plane[0] = 0;
+ return true;
+}
+
+//**********************************************************************************
+//norm of difference between two planes
+double PlaneDifference(const Plane &a, const Plane &b){
+ double la = sqrt(pow(a.a(),2) + pow(a.b(),2) + pow(a.c(),2) + pow(a.d(),2));
+ double lb = sqrt(pow(b.a(),2) + pow(b.b(),2) + pow(b.c(),2) + pow(b.d(),2));
+ return pow(a.a()/la-b.a()/lb,2) + pow(a.b()/la-b.b()/lb,2) + pow(a.c()/la-b.c()/lb,2) + pow(a.d()/la-b.d()/lb,2);
+
+ //in case we do not care of the orientation
+ //return min(pow(a.a()/la-b.a()/lb,2) + pow(a.b()/la-b.b()/lb,2) + pow(a.c()/la-b.c()/lb,2) + pow(a.d()/la-b.d()/lb,2),pow(a.a()/la+b.a()/lb,2) + pow(a.b()/la+b.b()/lb,2) + pow(a.c()/la+b.c()/lb,2) + pow(a.d()/la+b.d()/lb,2));
+}
+
+//**********************************************************************************
+//connect triagular facets if possible
+Polyhedron Simplify(Polyhedron P, double limit){
+ bool elimination = true;
+ while(elimination){
+ elimination = false;
+ for (Polyhedron::Edge_iterator hei = P.edges_begin(); hei!=P.edges_end(); ++hei){
+ if (PlaneDifference(hei->facet()->plane(),hei->opposite()->facet()->plane()) < limit){
+ if (hei->vertex()->vertex_degree() < 3) hei = P.erase_center_vertex(hei);
+ else if(hei->opposite()->vertex()->vertex_degree() < 3) hei = P.erase_center_vertex(hei->opposite());
+ else hei = P.join_facet(hei);
+ elimination = true;
+ break;
+ }
+ }
+ }
+ if (P.size_of_facets() < 4) P.clear();
+ return P;
+}
+
+//**********************************************************************************
+//list of facets + edges
+void PrintPolyhedron(Polyhedron P){
+ Vector3r A,B,C;
+ cout << "*** faces ***" << endl;
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter!=P.facets_end(); ++fIter){
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ hfc0 = fIter->facet_begin();
+ int n = fIter->facet_degree();
+ A = FromCGALPoint(hfc0->vertex()->point());
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ for (int i=2; i<n; i++){
+ ++hfc0;
+ B = C;
+ C = FromCGALPoint(hfc0->next()->vertex()->point());
+ cout << A << " "<< B << " "<< C << endl;
+ }
+ }
+ cout << "*** edges ***" << endl;
+ for (Polyhedron::Edge_iterator hei = P.edges_begin(); hei!=P.edges_end(); ++hei){
+ cout << hei->vertex()->point() << " " << hei->opposite()->vertex()->point() << endl;
+ }
+}
+
+//**********************************************************************************
+//list of facets
+void PrintPolyhedronFacets(Polyhedron P){
+ Vector3r A,B,C;
+ for (Polyhedron::Facet_iterator fIter = P.facets_begin(); fIter!=P.facets_end(); ++fIter){
+ cout << "***" << endl;
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ hfc0 = fIter->facet_begin();
+ int n = fIter->facet_degree();
+ for (int i = 0; i<n; ++hfc0, i++){
+ cout << hfc0->vertex()->point() << endl;
+ }
+ }
+}
+
+//**********************************************************************************
+//calculate intersection of three planes (exceptional cases not checked) - not in use, can be deleted
+CGALpoint PlaneIntersection( Plane a, Plane b, Plane c){
+ Matrix3r A;
+ A<<
+ a.a(),a.b(),a.c(),
+ b.a(),b.b(),b.c(),
+ c.a(),c.b(),c.c();
+ Vector3r x = A.inverse()*Vector3r(-a.d(),-b.d(),-c.d());
+ return CGALpoint(x.x(),x.y(),x.z());
+}
+
+//**********************************************************************************
+//solve system of 3x3 by Cramers rule
+Vector3r SolveLinSys3x3(Matrix3r A, Vector3r y){
+ //only system 3x3 by Cramers rule
+ double det = A(0,0)*A(1,1)*A(2,2)+A(0,1)*A(1,2)*A(2,0)+A(0,2)*A(1,0)*A(2,1)-A(0,2)*A(1,1)*A(2,0)-A(0,1)*A(1,0)*A(2,2)-A(0,0)*A(1,2)*A(2,1);
+ if (det == 0) {cerr << "error in linear solver" << endl; return Vector3r(0,0,0);}
+ return Vector3r((y(0)*A(1,1)*A(2,2)+A(0,1)*A(1,2)*y(2)+A(0,2)*y(1)*A(2,1)-A(0,2)*A(1,1)*y(2)-A(0,1)*y(1)*A(2,2)-y(0)*A(1,2)*A(2,1))/det, (A(0,0)*y(1)*A(2,2)+y(0)*A(1,2)*A(2,0)+A(0,2)*A(1,0)*y(2)-A(0,2)*y(1)*A(2,0)-y(0)*A(1,0)*A(2,2)-A(0,0)*A(1,2)*y(2))/det, (A(0,0)*A(1,1)*y(2)+A(0,1)*y(1)*A(2,0)+y(0)*A(1,0)*A(2,1)-y(0)*A(1,1)*A(2,0)-A(0,1)*A(1,0)*y(2)-A(0,0)*y(1)*A(2,1))/det);
+}
+
+//**********************************************************************************
+/*return convex hull of points
+critical point, because CGAL often returnes segmentation fault. The planes must be sufficiently "different". This is, however, checked elswhere by DISTANCE_LIMIT variable.
+*/
+Polyhedron ConvexHull(vector<CGALpoint> &planes){
+ Polyhedron Int;
+ //cout << "C"; cout.flush();
+ if (planes.size()>3) CGAL::convex_hull_3(planes.begin(), planes.end(), Int);
+ //cout << "-C"<< endl; cout.flush();
+ return Int;
+}
+
+//**********************************************************************************
+//determination of normal direction of intersection
+
+Vector3r FindNormal(Polyhedron Int, Polyhedron PA, Polyhedron PB){
+
+ //determine which plane is from which polyhedra
+ Polyhedron::Plane_iterator pi, pj;
+ std::transform( Int.facets_begin(), Int.facets_end(), Int.planes_begin(),Plane_equation());
+ std::transform( PA.facets_begin(), PA.facets_end(), PA.planes_begin(),Plane_equation());
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+ vector<bool> from_A(Int.size_of_facets());
+ vector<double> minsA(Int.size_of_facets());
+ vector<double> minsB(Int.size_of_facets());
+ int i=0;
+ double minA, minB, k;
+ for(pi = Int.planes_begin(); pi!=Int.planes_end(); ++pi,i++){
+ minA = 1.;
+ minB = 1.;
+ for(pj=PA.planes_begin(); pj!=PA.planes_end(); ++pj){
+ k = PlaneDifference(*pi, *pj);
+ if (k<minA) {
+ minA = k;
+ minsA[i] = minA;
+ if (minA<FIND_NORMAL_LIMIT) {from_A[i] = true; break;} //already satisfactory
+ }
+
+ }
+ if (minA<FIND_NORMAL_LIMIT) continue;
+ for(pj=PB.planes_begin(); pj!=PB.planes_end(); ++pj){
+ k = PlaneDifference(*pi, *pj);
+
+ if (k<minB){
+ minB = k;
+ minsB[i] = minB;
+ if (minB<FIND_NORMAL_LIMIT || minB<minA) break; //already satisfactory
+ }
+ }
+ from_A[i] = ((minA < minB) ? true : false);
+ }
+ //check that not all belongs to A and not all belongs to B
+ if (*std::min_element(from_A.begin(),from_A.end())==1){
+ int loc = std::min_element(minsB.begin(),minsB.end()) - minsB.begin();
+ from_A[loc] = false;
+ }else if (*std::max_element(from_A.begin(),from_A.end())==0){
+ int loc = std::min_element(minsA.begin(),minsA.end()) - minsA.begin();
+ from_A[loc] = true;
+ }
+
+ //find intersecting segments
+ vector<Segment> segments;
+ int a,b;
+
+ for (Polyhedron::Edge_iterator hei = Int.edges_begin(); hei!=Int.edges_end(); ++hei){
+ a = std::distance(Int.facets_begin(), hei->facet());
+ b = std::distance(Int.facets_begin(), hei->opposite()->facet());
+ if ((from_A[a] && !from_A[b]) || (from_A[b] && !from_A[a])) segments.push_back(Segment(hei->vertex()->point(),hei->opposite()->vertex()->point()));
+ }
+
+ //find normal direction
+ Plane fit;
+ linear_least_squares_fitting_3(segments.begin(),segments.end(),fit,CGAL::Dimension_tag<1>());
+ CGALvector CGALnormal=fit.orthogonal_vector();
+ CGALnormal = CGALnormal/sqrt(CGALnormal.squared_length());
+ // reverse direction if projection of the (contact_point-centroid_of_B) vector onto the normal is negative (i.e. the normal points more towards B)
+
+ return FromCGALVector(CGALnormal);
+}
+
+//**********************************************************************************
+//not used
+double CalculateProjectionArea(Polyhedron Int, CGALvector CGALnormal){
+ //calculate area of projection of Intersection into the normal plane
+ double area = 0.;
+ double abs_size;
+ Polyhedron::Halfedge_around_facet_circulator hfc0;
+ CGALvector normal2;
+ double norm2;
+ std::transform( Int.facets_begin(), Int.facets_end(), Int.planes_begin(),Plane_equation());
+ for(Polyhedron::Facet_iterator fi = Int.facets_begin(); fi!= Int.facets_end(); ++fi){
+ assert(fi->is_triangle());
+ hfc0 = fi->facet_begin();
+ normal2 = fi->plane().orthogonal_vector();
+ norm2 = normal2.squared_length();
+ if(norm2 < 1E-20) continue;
+ abs_size = 0.5*sqrt((cross_product(CGALvector(hfc0->vertex()->point(),hfc0->next()->vertex()->point()),CGALvector(hfc0->vertex()->point(),hfc0->next()->next()->vertex()->point()))).squared_length());
+ // factor 0.5 because this procedure returnes doubles projected area
+ if (abs_size>0) area += 0.5*abs_size*abs(CGALnormal*normal2/sqrt(norm2));
+ }
+ return area;
+}
+
+//**********************************************************************************
+//prepare data for CGAL convex hull
+vector<Plane> MergePlanes(vector<Plane> planes1, vector<Plane> planes2, double limit){
+ vector<Plane> P = planes1;
+ bool add;
+ for(vector<Plane>::iterator i = planes2.begin(); i!=planes2.end(); ++i){
+ add = true;
+ for(vector<Plane>::iterator j = planes1.begin(); j!=planes1.end(); ++j){
+ if (PlaneDifference(*i,*j) < limit){
+ add = false;
+ break;
+ }
+ }
+ if (add) P.push_back(*i);
+ }
+ return P;
+}
+
+//**********************************************************************************
+//returnes intersecting polyhedron of polyhedron & plane (possibly empty)
+Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Plane B, CGALpoint centroid, CGALpoint X){
+
+ Polyhedron Intersection;
+ CGALpoint inside;
+ vector<Plane> planes1, planes2;
+ vector<CGALpoint> dual_planes;
+ // test if do intersect, find some intersecting point
+ bool intersection_found = false;
+ double lim = pow(DISTANCE_LIMIT,2);
+ // test centroid of previous intersection
+ if(Is_inside_Polyhedron(A,X,DISTANCE_LIMIT) && Oriented_squared_distance(B,X)<=-lim) {
+ intersection_found = true;
+ inside = X;
+ // find new point by checking polyhedron vertices that lies on negative side of the plane
+ } else {
+ for(Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end() && !intersection_found ; vIter++){
+ if(Oriented_squared_distance(B,vIter->point())<=-lim) {
+ intersection_found = true;
+ CGAL::Object result = CGAL::intersection(Line(vIter->point(),centroid), B);
+ if (const CGALpoint *ipoint = CGAL::object_cast<CGALpoint>(&result)) {
+ inside = vIter->point() + 0.5*CGALvector(vIter->point(),*ipoint);
+ }else{
+ cerr << "Error in line-plane intersection" << endl;
+ }
+ }
+ }
+ }
+ //no intersectiong point => no intersection polyhedron
+ if(!intersection_found) return Intersection;
+
+
+ //set the intersection point to origin
+ Transformation transl_back(CGAL::TRANSLATION, inside - CGALpoint(0.,0.,0.));
+ Transformation transl(CGAL::TRANSLATION, CGALpoint(0.,0.,0.)-inside);
+
+ std::transform( A.points_begin(), A.points_end(), A.points_begin(), transl);
+ B = transl.transform(B);
+
+ //dualize plane
+ planes1.push_back(B);
+
+ //dualize polyhedron
+ std::transform( A.facets_begin(), A.facets_end(), A.planes_begin(),Plane_equation());
+ for(Polyhedron::Plane_iterator pi =A.planes_begin(); pi!=A.planes_end(); ++pi) planes2.push_back(*pi);;
+
+ //merge planes
+ planes1 = MergePlanes(planes1,planes2, MERGE_PLANES_LIMIT);//MERGE_PLANES_LIMIT);
+ for(vector<Plane>::iterator pi =planes1.begin(); pi!= planes1.end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+ if (Intersection.empty()) {cout << "0" << endl; cout.flush(); return Intersection;}
+
+ //simplify
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+ Intersection = Simplify(Intersection, SIMPLIFY_LIMIT);
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+
+ //dualize again
+ dual_planes.clear();
+ for(Polyhedron::Plane_iterator pi =Intersection.planes_begin(); pi!=Intersection.planes_end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+
+ //return to original position
+ std::transform( Intersection.points_begin(), Intersection.points_end(), Intersection.points_begin(), transl_back);
+
+ if (Intersection.size_of_facets() < 4) Intersection.clear();
+ return Intersection;
+}
+
+//**********************************************************************************
+//returnes intersecting polyhedron of polyhedron & plane defined by diriction and point
+Polyhedron Polyhedron_Plane_intersection(Polyhedron A, Vector3r direction, Vector3r plane_point){
+ Plane B(ToCGALPoint(plane_point), ToCGALVector(direction));
+ CGALpoint X = ToCGALPoint(plane_point) - 1E-8*ToCGALVector(direction);
+ return Polyhedron_Plane_intersection(A, B, ToCGALPoint(plane_point), X);
+}
+
+//**********************************************************************************
+//returnes intersecting polyhedron of two polyhedrons (possibly empty)
+Polyhedron Polyhedron_Polyhedron_intersection(Polyhedron A, Polyhedron B, CGALpoint X, CGALpoint centroidA, CGALpoint centroidB, std::vector<int> &sep_plane){
+
+ Polyhedron Intersection;
+
+ vector<Plane> planes1, planes2;
+ vector<CGALpoint> dual_planes;
+ Polyhedron::Plane_iterator pi;
+ CGALpoint inside(0,0,0);
+
+ bool intersection_found = false;
+ std::transform( A.facets_begin(), A.facets_end(), A.planes_begin(),Plane_equation());
+ std::transform( B.facets_begin(), B.facets_end(), B.planes_begin(),Plane_equation());
+ Matrix3r Amatrix;
+ Vector3r y;
+ // test that X is really inside
+ if(Is_inside_Polyhedron(A,X,DISTANCE_LIMIT) && Is_inside_Polyhedron(B,X,DISTANCE_LIMIT)) {
+ intersection_found = true;
+ inside = X;
+ } else {
+ if (!do_intersect(A, B, sep_plane)) return Intersection;
+ //some intersection point
+ double dist_S, dist_T;
+ double lim2 = pow(DISTANCE_LIMIT,2);
+ CGALvector d1;
+ double factor = sqrt(DISTANCE_LIMIT * 1.5);
+ //test vertices A - not needed, edges are enough
+ for (Polyhedron::Vertex_iterator vIter = A.vertices_begin(); vIter != A.vertices_end() && !intersection_found; vIter++){
+ d1 = centroidA-vIter->point();
+ inside = vIter->point() + d1/sqrt(d1.squared_length())*DISTANCE_LIMIT*20.;
+ intersection_found = (Is_inside_Polyhedron(A,inside,DISTANCE_LIMIT) && Is_inside_Polyhedron(B,inside,DISTANCE_LIMIT));
+ }
+ //test vertices B - necessary
+ for (Polyhedron::Vertex_iterator vIter = B.vertices_begin(); vIter != B.vertices_end() && !intersection_found; vIter++){
+ d1 = centroidB-vIter->point();
+ inside = vIter->point() + d1/sqrt(d1.squared_length())*DISTANCE_LIMIT*20.;
+ intersection_found = (Is_inside_Polyhedron(A,inside,DISTANCE_LIMIT) && Is_inside_Polyhedron(B,inside,DISTANCE_LIMIT));
+ }
+
+ //test edges
+ for (Polyhedron::Edge_iterator eIter = A.edges_begin(); eIter != A.edges_end() && !intersection_found; eIter++){
+ for (Polyhedron::Facet_iterator fIter = B.facets_begin(); fIter != B.facets_end() && !intersection_found; fIter++){
+ dist_S = Oriented_squared_distance(fIter->plane(), eIter->vertex()->point());
+ dist_T = Oriented_squared_distance(fIter->plane(), eIter->opposite()->vertex()->point());
+ if (dist_S*dist_T >= 0 || abs(dist_S)<lim2 || abs(dist_T)<lim2) continue;
+ inside = eIter->vertex()->point() + (eIter->opposite()->vertex()->point()-eIter->vertex()->point())*sqrt(abs(dist_S))/(sqrt(abs(dist_S))+sqrt(abs(dist_T)));
+ // the fact that edge intersects the facet (not only its plane) is not explicitely checked, it sufices to check that the resulting point is inside both polyhedras
+ Plane p1 = fIter->plane();
+ Plane p2 = eIter->facet()->plane();
+ Plane p3 = eIter->opposite()->facet()->plane();
+ Amatrix << p1.a(),p1.b(),p1.c(),
+ p2.a(),p2.b(),p2.c(),
+ p3.a(),p3.b(),p3.c();
+ y = Vector3r(-p1.d()-factor*sqrt(pow(p1.a(),2)+pow(p1.b(),2)+pow(p1.c(),2)),-p2.d()-factor*sqrt(pow(p2.a(),2)+pow(p2.b(),2)+pow(p2.c(),2)),-p3.d()-factor*sqrt(pow(p3.a(),2)+pow(p3.b(),2)+pow(p3.c(),2)));
+ inside = ToCGALPoint(SolveLinSys3x3(Amatrix,y));
+ intersection_found = (Is_inside_Polyhedron(A,inside,DISTANCE_LIMIT) && Is_inside_Polyhedron (B,inside,DISTANCE_LIMIT));
+ }
+ }
+ }
+
+ //Polyhedrons do not intersect
+ if (!intersection_found) return Intersection;
+
+ //set the intersection point to origin
+ Transformation transl_back(CGAL::TRANSLATION, inside - CGALpoint(0.,0.,0.));
+ Transformation transl(CGAL::TRANSLATION, CGALpoint(0.,0.,0.)-inside);
+
+ std::transform( A.points_begin(), A.points_end(), A.points_begin(), transl);
+ std::transform( B.points_begin(), B.points_end(), B.points_begin(), transl);
+
+ //dualize polyhedrons
+ std::transform( A.facets_begin(), A.facets_end(), A.planes_begin(),Plane_equation());
+ std::transform( B.facets_begin(), B.facets_end(), B.planes_begin(),Plane_equation());
+ for(Polyhedron::Plane_iterator pi =A.planes_begin(); pi!=A.planes_end(); ++pi) planes1.push_back(*pi);
+ for(Polyhedron::Plane_iterator pi =B.planes_begin(); pi!=B.planes_end(); ++pi) planes2.push_back(*pi);
+
+
+ //merge planes
+ planes1 = MergePlanes(planes1,planes2, MERGE_PLANES_LIMIT);//MERGE_PLANES_LIMIT);
+ for(vector<Plane>::iterator pi =planes1.begin(); pi!= planes1.end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+ if (Intersection.empty()) {cout << "0" << endl; cout.flush(); return Intersection;};
+
+ //simplify
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+ Intersection = Simplify(Intersection, SIMPLIFY_LIMIT);
+ std::transform( Intersection.facets_begin(), Intersection.facets_end(), Intersection.planes_begin(),Plane_equation());
+
+ //dualize again
+ dual_planes.clear();
+ for(Polyhedron::Plane_iterator pi =Intersection.planes_begin(); pi!=Intersection.planes_end(); ++pi) dual_planes.push_back(CGALpoint(-pi->a()/pi->d(),-pi->b()/pi->d(),-pi->c()/pi->d()));
+
+ //compute convex hull of it
+ Intersection = ConvexHull(dual_planes);
+ //return to original position
+ std::transform( Intersection.points_begin(), Intersection.points_end(), Intersection.points_begin(), transl_back);
+
+ if (Intersection.size_of_facets() < 4) Intersection.clear();
+ return Intersection;
+}
+
+//**********************************************************************************
+/*
+//returnes intersection of Sphere and polyhedron (possibly empty) - not finished
+bool Sphere_Polyhedron_intersection(Polyhedron A, double r, CGALpoint C, CGALpoint centroid, double volume, CGALvector normal, double area){
+ volume = 0;
+ area = 0;
+
+ double r2 = pow(r,2);
+ //check points
+ Polyhedron::Vertex_iterator closest_vert;
+ double dist2;
+ double min_dist2 = (C-centroid).squared_length();
+ //test vertices
+ for(Polyhedron::Vertex_iterator vi=A.vertices_begin(); vi!=A.vertices_end(); ++vi){
+ dist2 = (C-vi->point()).squared_length();
+ if(min_dist2 > dist2) {min_dist2 = dist2; closest_vert=vi;}
+ }
+ if(min_dist2 < r2){ //test if we are already inside the sphere
+
+ return true;
+ }
+
+ //test edges
+ CGALvector v1(C-closest_vert->point());
+ v1 = v1/sqrt(v1.squared_length());
+ Polyhedron::Halfedge_around_vertex_circulator closest_edge;
+ int i =0;
+ double cos_angle;
+ double max_cos_angle=-1;
+ CGALvector v2;
+ for(Polyhedron::Halfedge_around_vertex_circulator hfi=closest_vert->vertex_begin(); i<(int)closest_vert->degree(); ++hfi, i++){
+ v2 = hfi->opposite()->vertex()->point()-closest_vert->point();
+ v2 = v2/sqrt(v2.squared_length());
+ cos_angle = v1*v2;
+ if(cos_angle > max_cos_angle) closest_edge=hfi; max_cos_angle=cos_angle; //find the edge with minimum angle
+ }
+ if(max_cos_angle<=0) return false; //all edges goes with angle above or equal 90
+ double k = (C - closest_vert->point())*v2; //find closest point on the most descending edge (it must be on edge, otherwise opposite point would be already closer)
+ CGALpoint closest_point = closest_vert->point() + k*v2;
+ if((C-closest_point).squared_length()<r2){ //test if we are already inside the sphere
+
+ return true;
+ }
+
+
+ //check planes
+ v1 = (C-closest_point);
+ v1 = v1/sqrt(v1.squared_length());
+ v2 = closest_edge->vertex()->point()-closest_edge->opposite()->vertex()->point();
+ CGALvector v3 = closest_edge->facet()->plane().orthogonal_vector();
+ CGALvector v4 = closest_edge->opposite()->facet()->plane().orthogonal_vector();
+ v3 = CGAL::cross_product(v3,v2);
+ v4 = CGAL::cross_product(v4,v2*(-1));
+ Plane closest_plane;
+ if (v1*v3>=0 && v1*v4>=0) return false; // both planes running away
+ if (v1*v3<0) closest_plane = closest_edge->facet()->plane();
+ else closest_plane = closest_edge->opposite()->facet()->plane();
+ closest_point = closest_plane.projection(C);
+ if((C-closest_point).squared_length()<r2){ //test if we are already inside the sphere
+ double h = sqrt((C-closest_point).squared_length());
+ volume = 3.1415*pow(h,2)/3*(3*r-h);
+ return true;
+ }
+
+ return false;
+}
+*/
+
+//**********************************************************************************
+Vector3r FromCGALPoint(CGALpoint A){
+ return Vector3r(A.x(), A.y(), A.z());
+}
+
+//**********************************************************************************
+Vector3r FromCGALVector(CGALvector A){
+ return Vector3r(A.x(), A.y(), A.z());
+}
+
+//**********************************************************************************
+CGALpoint ToCGALPoint(Vector3r A){
+ return CGALpoint(A[0], A[1], A[2]);
+}
+
+//**********************************************************************************
+CGALvector ToCGALVector(Vector3r A){
+ return CGALvector(A[0], A[1], A[2]);
+}
+
+//**********************************************************************************
+//new polyhedra
+shared_ptr<Body> NewPolyhedra(vector<Vector3r> v, shared_ptr<Material> mat){
+ shared_ptr<Body> body(new Body);
+ body->material=mat;
+ body->shape=shared_ptr<Polyhedra>(new Polyhedra());
+ Polyhedra* A = static_cast<Polyhedra*>(body->shape.get());
+ A->v = v;
+ A->Initialize();
+ body->state->pos= A->GetCentroid();
+ body->state->mass=body->material->density*A->GetVolume();
+ body->state->inertia =A->GetInertia()*body->material->density;
+ body->state->ori = A->GetOri();
+ body->bound=shared_ptr<Aabb>(new Aabb);
+ body->setAspherical(true);
+ return body;
+}
+
+
+//**********************************************************************************
+//split polyhedra
+void SplitPolyhedra(const shared_ptr<Body>& body, Vector3r direction){
+
+ const Se3r& se3=body->state->se3;
+ Polyhedra* A = static_cast<Polyhedra*>(body->shape.get());
+ State* X = static_cast<State*>(body->state.get());
+
+ Vector3r OrigPos = X->pos;
+ Vector3r OrigVel = X->vel;
+ Vector3r OrigAngVel = X->angVel;
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ //calculate splitted polyhedrons
+ Polyhedron S1 = Polyhedron_Plane_intersection(PA, direction, trans_vec);
+ Polyhedron S2 = Polyhedron_Plane_intersection(PA, (-1)*direction, trans_vec);
+
+
+ //replace original polyhedron
+ A->Clear();
+ for(Polyhedron::Vertex_iterator vi = S1.vertices_begin(); vi != S1.vertices_end(); vi++){ A->v.push_back(FromCGALPoint(vi->point()));};
+ A->Initialize();
+ X->pos = A->GetCentroid();
+ X->ori = A->GetOri();
+ X->mass=body->material->density*A->GetVolume();
+ X->refPos[0] = X->refPos[0]+1.;
+ X->inertia =A->GetInertia()*body->material->density;
+
+ //second polyhedron
+ vector<Vector3r> v2;
+ for(Polyhedron::Vertex_iterator vi = S2.vertices_begin(); vi != S2.vertices_end(); vi++){ v2.push_back(FromCGALPoint(vi->point()));};
+ shared_ptr<Body> BP = NewPolyhedra(v2, body->material);
+ //BP->shape->color = body->shape->color;
+ BP->shape->color = Vector3r(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+
+ //append to the rest
+ Scene* scene=Omega::instance().getScene().get();
+ //scene->bodies->erase(body->id);
+ scene->bodies->insert(BP);
+
+ //set proper state variables
+ X->vel = OrigVel + OrigAngVel.cross(X->pos-OrigPos);
+ BP->state->vel = OrigVel + OrigAngVel.cross(BP->state->pos-OrigPos);
+ X->angVel = OrigAngVel;
+ BP->state->angVel = OrigAngVel;
+
+}
+
+#endif // YADE_CGAL
=== modified file 'py/CMakeLists.txt'
--- py/CMakeLists.txt 2013-08-29 10:30:31 +0000
+++ py/CMakeLists.txt 2013-10-15 16:14:46 +0000
@@ -36,6 +36,12 @@
INSTALL(TARGETS _utils DESTINATION "${YADE_PY_PATH}/yade/")
+ADD_LIBRARY(_polyhedra_utils SHARED "${CMAKE_CURRENT_SOURCE_DIR}/_polyhedra_utils.cpp")
+SET_TARGET_PROPERTIES(_polyhedra_utils PROPERTIES PREFIX "")
+TARGET_LINK_LIBRARIES(_polyhedra_utils)
+INSTALL(TARGETS _polyhedra_utils DESTINATION "${YADE_PY_PATH}/yade/")
+
+
ADD_LIBRARY(_packPredicates SHARED "${CMAKE_CURRENT_SOURCE_DIR}/pack/_packPredicates.cpp")
SET_TARGET_PROPERTIES(_packPredicates PROPERTIES PREFIX "")
TARGET_LINK_LIBRARIES(_packPredicates yade)
=== added file 'py/_polyhedra_utils.cpp'
--- py/_polyhedra_utils.cpp 1970-01-01 00:00:00 +0000
+++ py/_polyhedra_utils.cpp 2013-10-15 16:14:46 +0000
@@ -0,0 +1,374 @@
+// © 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+// https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+#ifdef YADE_CGAL
+
+#include"yade/pkg/dem/Polyhedra.hpp"
+
+#include<boost/python.hpp>
+#include <boost/python/module.hpp>
+#include<yade/core/Scene.hpp>
+#include<yade/core/Omega.hpp>
+#include<yade/pkg/common/Sphere.hpp>
+#include<yade/pkg/common/ElastMat.hpp>
+#include<yade/lib/pyutil/doc_opts.hpp>
+#include<cmath>
+
+#include<numpy/ndarrayobject.h>
+
+using namespace std;
+namespace py = boost::python;
+
+
+//**********************************************************************************
+//print polyhedron in basic position
+void PrintPolyhedra(const shared_ptr<Shape>& shape){
+ Polyhedra* A = static_cast<Polyhedra*>(shape.get());
+ Polyhedron PA = A->GetPolyhedron();
+ A->Initialize();
+ PrintPolyhedron(PA);
+}
+
+//**********************************************************************************
+//print polyhedron in actual position
+void PrintPolyhedraActualPos(const shared_ptr<Shape>& cm1,const State& state1){
+ const Se3r& se3=state1.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+ A->Initialize();
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ PrintPolyhedron(PA);
+}
+
+
+//**********************************************************************************
+//test of polyhedron intersection callable from python shell
+bool do_Polyhedras_Intersect(const shared_ptr<Shape>& cm1,const shared_ptr<Shape>& cm2,const State& state1,const State& state2){
+
+ const Se3r& se31=state1.se3;
+ const Se3r& se32=state2.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+ Polyhedra* B = static_cast<Polyhedra*>(cm2.get());
+
+ //move and rotate 1st the CGAL structure Polyhedron
+ Matrix3r rot_mat = (se31.orientation).toRotationMatrix();
+ Vector3r trans_vec = se31.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ //move and rotate 2st the CGAL structure Polyhedron
+ rot_mat = (se32.orientation).toRotationMatrix();
+ trans_vec = se32.position;
+ t_rot_trans = Transformation(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PB = B->GetPolyhedron();
+ std::transform( PB.points_begin(), PB.points_end(), PB.points_begin(), t_rot_trans);
+
+ //calculate plane equations
+ std::transform( PA.facets_begin(), PA.facets_end(), PA.planes_begin(),Plane_equation());
+ std::transform( PB.facets_begin(), PB.facets_end(), PB.planes_begin(),Plane_equation());
+
+
+ //call test
+ return do_intersect(PA,PB);
+}
+
+//**********************************************************************************
+//determination of critical time step for polyhedrons & spheres (just rough estimation)
+Real PWaveTimeStep(){
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ Real dt=std::numeric_limits<Real>::infinity();
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->material || !b->shape) continue;
+ shared_ptr<Sphere> s=dynamic_pointer_cast<Sphere>(b->shape);
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ if(!s && !p) continue;
+ if(!p){
+ //spheres
+ shared_ptr<ElastMat> ebp=dynamic_pointer_cast<ElastMat>(b->material);
+ if(!ebp) continue;
+ Real density=b->state->mass/((4./3.)*Mathr::PI*pow(s->radius,3));
+ dt=min(dt,s->radius/sqrt(ebp->young/density));
+ }else{
+ //polyhedrons
+ shared_ptr<PolyhedraMat> ebp=dynamic_pointer_cast<PolyhedraMat>(b->material);
+ if(!ebp) continue;
+ Real density=b->state->mass/p->GetVolume();
+ //get equivalent radius and use same equation as for sphere
+ Real equi_radius=pow(p->GetVolume()/((4./3.)*Mathr::PI),1./3.);
+ //dt=min(dt,equi_radius/sqrt(ebp->Kn*equi_radius/density));
+ dt=min(dt,equi_radius/sqrt(ebp->Kn/equi_radius/density));
+ }
+ }
+ if (dt==std::numeric_limits<Real>::infinity()) {
+ dt = 1.0;
+ LOG_WARN("PWaveTimeStep has not found any suitable spherical or polyhedral body to calculate dt. dt is set to 1.0");
+ }
+ return dt;
+}
+
+//**********************************************************************************
+//returns approximate sieve size of polyhedron
+Real SieveSize(const shared_ptr<Shape>& cm1){
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ double phi = M_PI/4.;
+ double x,y;
+ double minx = 0, maxx = 0, miny = 0, maxy = 0;
+
+ for (vector<Vector3r>::iterator i=A->v.begin(); i!=A->v.end(); ++i){
+ x = cos(phi)*(*i)[1] + sin(phi)*(*i)[2];
+ y = -sin(phi)*(*i)[1] + cos(phi)*(*i)[2];
+ minx = min(minx,x);
+ maxx = max(maxx,x);
+ miny = min(miny,y);
+ maxy = max(maxy,y);
+ }
+
+ return max(maxx-minx, maxy-miny);
+}
+
+
+//**********************************************************************************
+//returns approximate size of polyhedron
+Vector3r SizeOfPolyhedra(const shared_ptr<Shape>& cm1){
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ double minx = 0, maxx = 0, miny = 0, maxy = 0, minz = 0, maxz = 0;
+
+ for (vector<Vector3r>::iterator i=A->v.begin(); i!=A->v.end(); ++i){
+ minx = min(minx,(*i)[0]);
+ maxx = max(maxx,(*i)[0]);
+ miny = min(miny,(*i)[1]);
+ maxy = max(maxy,(*i)[1]);
+ minz = min(minz,(*i)[2]);
+ maxz = max(maxz,(*i)[2]);
+ }
+
+ return Vector3r(maxx-minx, maxy-miny, maxz-minz);
+}
+
+//**********************************************************************************
+//save sieve curve points into a file
+void SieveCurve(){
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ std::vector< std::pair<double,double> > sieve_volume;
+ double total_volume = 0;
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->shape) continue;
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ if(p){
+ sieve_volume.push_back(std::pair<double,double>(SieveSize(p),p->GetVolume()));
+ total_volume += p->GetVolume();
+ }
+ }
+
+ std::sort(sieve_volume.begin(), sieve_volume.end()) ;
+ double cumul_vol = 0;
+
+ ofstream myfile;
+ myfile.open ("sieve_curve.dat");
+ for(std::vector< std::pair<double,double> > :: iterator i = sieve_volume.begin(); i != sieve_volume.end(); ++i) {
+ cumul_vol += i->second/total_volume;
+ myfile << i->first << "\t" << cumul_vol << endl;
+ }
+ myfile.close();
+}
+
+//**********************************************************************************
+//save size of polyhedrons into a file
+void SizeRatio(){
+ const shared_ptr<Scene> _rb=shared_ptr<Scene>();
+ shared_ptr<Scene> rb=(_rb?_rb:Omega::instance().getScene());
+ ofstream myfile;
+ myfile.open ("sizes.dat");
+ FOREACH(const shared_ptr<Body>& b, *rb->bodies){
+ if(!b || !b->shape) continue;
+ shared_ptr<Polyhedra> p=dynamic_pointer_cast<Polyhedra>(b->shape);
+ if(p){
+ myfile << SizeOfPolyhedra(p) << endl;
+ }
+ }
+ myfile.close();
+}
+
+//**********************************************************************************
+//returns max coordinates
+Vector3r MaxCoord(const shared_ptr<Shape>& cm1,const State& state1){
+ const Se3r& se3=state1.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ Vector3r maxccord = trans_vec;
+ for(Polyhedron::Vertex_iterator vi = PA.vertices_begin(); vi != PA.vertices_end(); ++vi){
+ if (vi->point()[0]>maxccord[0]) maxccord[0]=vi->point()[0];
+ if (vi->point()[1]>maxccord[1]) maxccord[1]=vi->point()[1];
+ if (vi->point()[2]>maxccord[2]) maxccord[2]=vi->point()[2];
+ }
+
+ return maxccord;
+}
+
+//**********************************************************************************
+//returns min coordinates
+Vector3r MinCoord(const shared_ptr<Shape>& cm1,const State& state1){
+ const Se3r& se3=state1.se3;
+ Polyhedra* A = static_cast<Polyhedra*>(cm1.get());
+
+ //move and rotate CGAL structure Polyhedron
+ Matrix3r rot_mat = (se3.orientation).toRotationMatrix();
+ Vector3r trans_vec = se3.position;
+ Transformation t_rot_trans(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2), trans_vec[0],rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),trans_vec[1],rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),trans_vec[2],1.);
+ Polyhedron PA = A->GetPolyhedron();
+ std::transform( PA.points_begin(), PA.points_end(), PA.points_begin(), t_rot_trans);
+
+ Vector3r minccord = trans_vec;
+ for(Polyhedron::Vertex_iterator vi = PA.vertices_begin(); vi != PA.vertices_end(); ++vi){
+ if (vi->point()[0]<minccord[0]) minccord[0]=vi->point()[0];
+ if (vi->point()[1]<minccord[1]) minccord[1]=vi->point()[1];
+ if (vi->point()[2]<minccord[2]) minccord[2]=vi->point()[2];
+ }
+
+ return minccord;
+}
+
+
+//**********************************************************************************
+//generate "packing" of non-overlapping polyhedrons
+vector<Vector3r> fillBox_cpp(Vector3r minCoord, Vector3r maxCoord, Vector3r sizemin, Vector3r sizemax, Vector3r ratio, int seed, shared_ptr<Material> mat){
+ vector<Vector3r> v;
+ Polyhedra trialP;
+ Polyhedron trial, trial_moved;
+ srand(seed);
+ int it = 0;
+ vector<Polyhedron> polyhedrons;
+ vector<vector<Vector3r> > vv;
+ Vector3r position;
+ bool intersection;
+ int count = 0;
+
+ bool fixed_ratio = 0;
+ if (ratio[0] > 0 && ratio[1] > 0 && ratio[2]>0){
+ fixed_ratio = 1;
+ sizemax[0] = min(min(sizemax[0]/ratio[0], sizemax[1]/ratio[1]), sizemax[2]/ratio[2]);
+ sizemin[0] = max(max(sizemin[0]/ratio[0], sizemin[1]/ratio[1]), sizemin[2]/ratio[2]);
+ }
+
+ //it - number of trials to make packing possibly more/less dense
+ Vector3r random_size;
+ while (it<1000){
+ it = it+1;
+ if (it == 1){
+ trialP.Clear();
+ trialP.seed = rand();
+ if(fixed_ratio) trialP.size = (rand()*(sizemax[0]-sizemin[0])/RAND_MAX + sizemin[0])*ratio;
+ else trialP.size = Vector3r(rand()*(sizemax[0]-sizemin[0]),rand()*(sizemax[1]-sizemin[1]),rand()*(sizemax[2]-sizemin[2]))/RAND_MAX + sizemin;
+ trialP.Initialize();
+ trial = trialP.GetPolyhedron();
+ Matrix3r rot_mat = (trialP.GetOri()).toRotationMatrix();
+ Transformation t_rot(rot_mat(0,0),rot_mat(0,1),rot_mat(0,2),rot_mat(1,0),rot_mat(1,1),rot_mat(1,2),rot_mat(2,0),rot_mat(2,1),rot_mat(2,2),1.);
+ std::transform( trial.points_begin(), trial.points_end(), trial.points_begin(), t_rot);
+ }
+ position = Vector3r(rand()*(maxCoord[0]-minCoord[0]),rand()*(maxCoord[1]-minCoord[1]),rand()*(maxCoord[2]-minCoord[2]))/RAND_MAX + minCoord;
+
+ //move CGAL structure Polyhedron
+ Transformation transl(CGAL::TRANSLATION, ToCGALVector(position));
+ trial_moved = trial;
+ std::transform( trial_moved.points_begin(), trial_moved.points_end(), trial_moved.points_begin(), transl);
+ //calculate plane equations
+ std::transform( trial_moved.facets_begin(), trial_moved.facets_end(), trial_moved.planes_begin(),Plane_equation());
+
+ intersection = false;
+ //call test with boundary
+ for(Polyhedron::Vertex_iterator vi = trial_moved.vertices_begin(); (vi != trial_moved.vertices_end()) && (!intersection); vi++){
+ intersection = (vi->point().x()<minCoord[0]) || (vi->point().x()>maxCoord[0]) || (vi->point().y()<minCoord[1]) || (vi->point().y()>maxCoord[1]) || (vi->point().z()<minCoord[2]) || (vi->point().z()>maxCoord[2]);
+ }
+ //call test with other polyhedrons
+ for(vector<Polyhedron>::iterator a = polyhedrons.begin(); (a != polyhedrons.end()) && (!intersection); a++){
+ intersection = do_intersect(*a,trial_moved);
+ if (intersection) break;
+ }
+ if (!intersection){
+ polyhedrons.push_back(trial_moved);
+ v.clear();
+ for(Polyhedron::Vertex_iterator vi = trial_moved.vertices_begin(); vi != trial_moved.vertices_end(); vi++){
+ v.push_back(FromCGALPoint(vi->point()));
+ }
+ vv.push_back(v);
+ it = 0;
+ count ++;
+
+ }
+ }
+ cout << "generated " << count << " polyhedrons"<< endl;
+
+ //can't be used - no information about material
+ Scene* scene=Omega::instance().getScene().get();
+ for(vector<vector<Vector3r> >::iterator p=vv.begin(); p!=vv.end(); ++p){
+ shared_ptr<Body> BP = NewPolyhedra(*p, mat);
+ BP->shape->color = Vector3r(double(rand())/RAND_MAX,double(rand())/RAND_MAX,double(rand())/RAND_MAX);
+ scene->bodies->insert(BP);
+ }
+ return v;
+}
+
+//**********************************************************************************
+//split polyhedra
+void Split(const shared_ptr<Body> body, Vector3r direction){
+ SplitPolyhedra(body, direction);
+}
+
+//**********************************************************************************
+//distace of point from a plane (squared) with sign
+double Oriented_squared_distance2(Plane P, CGALpoint x){
+ double h = P.a()*x.x()+P.b()*x.y()+P.c()*x.z()+P.d();
+ return ((h>0.)-(h<0.))*pow(h,2)/(CGALvector(P.a(),P.b(),P.c())).squared_length();
+}
+
+//**********************************************************************************
+bool convexHull(vector<Vector3r> points){
+ vector<CGALpoint> pointsCGAL;
+ for(int i=0;i<(int) points.size() ;i++) {
+ pointsCGAL.push_back(ToCGALPoint(points[i]));
+ }
+ Polyhedron P;
+ CGAL::convex_hull_3(pointsCGAL.begin(), pointsCGAL.end(), P);
+ return true;
+}
+
+BOOST_PYTHON_MODULE(_polyhedra_utils){
+ // http://numpy.scipy.org/numpydoc/numpy-13.html mentions this must be done in module init, otherwise we will crash
+ import_array();
+
+ YADE_SET_DOCSTRING_OPTS;
+
+ py::def("PrintPolyhedra",PrintPolyhedra,"Print list of vertices sorted according to polyhedrons facets.");
+ py::def("PrintPolyhedraActualPos",PrintPolyhedraActualPos,"Print list of vertices sorted according to polyhedrons facets.");
+ py::def("PWaveTimeStep",PWaveTimeStep,"Get timestep accoring to the velocity of P-Wave propagation; computed from sphere radii, rigidities and masses.");
+ py::def("do_Polyhedras_Intersect",do_Polyhedras_Intersect,"check polyhedras intersection");
+ py::def("fillBox_cpp",fillBox_cpp,"Generate non-overlaping polyhedrons in box");
+ py::def("MinCoord",MinCoord,"returns min coordinates");
+ py::def("MaxCoord",MaxCoord,"returns max coordinates");
+ py::def("SieveSize",SieveSize,"returns approximate sieve size of polyhedron");
+ py::def("SieveCurve",SieveCurve,"save sieve curve coordinates into file");
+ py::def("SizeOfPolyhedra",SizeOfPolyhedra,"returns max, middle an min size in perpendicular directions");
+ py::def("SizeRatio",SizeRatio,"save sizes of polyhedra into file");
+ py::def("convexHull",convexHull,"....");
+ py::def("Split",Split,"split polyhedron perpendicularly to given direction direction");
+}
+
+#endif // YADE_CGAL
=== added file 'py/polyhedra_utils.py'
--- py/polyhedra_utils.py 1970-01-01 00:00:00 +0000
+++ py/polyhedra_utils.py 2013-10-15 16:14:46 +0000
@@ -0,0 +1,102 @@
+# 2013 Jan Elias, http://www.fce.vutbr.cz/STM/elias.j/, elias.j@xxxxxxxxxxxx
+# https://www.vutbr.cz/www_base/gigadisk.php?i=95194aa9a
+
+"""
+Auxiliary functions for polyhedra
+"""
+
+
+import math,random,doctest,geom,numpy
+from yade import Vector3
+from yade.wrapper import *
+#from miniEigen import *
+try: # use psyco if available
+ import psyco
+ psyco.full()
+except ImportError: pass
+
+
+# c++ implementations for performance reasons
+from yade._polyhedra_utils import *
+
+#**********************************************************************************
+def randomColor(seed=None):
+ random.seed(seed);
+ #Return random Vector3 with each component in interval 0...1 (uniform distribution)
+ return Vector3(random.random(),random.random(),random.random())
+
+#**********************************************************************************
+#create polyhedra, one can specify vertices directly, or leave it empty for random shape
+def polyhedra(material,size=Vector3(1,1,1),seed=None,v=[],mask=1,fixed=False, color=[-1,-1,-1]):
+ """create polyhedra, one can specify vertices directly, or leave it empty for random shape.
+
+ :param Material material: material of new body
+ :param Vector3 size: size of new body (see Polyhedra docs)
+ :param float seed: seed for random operations
+ :param [Vector3] v: list of body vertices (see Polyhedra docs)
+ """
+ b=Body()
+ random.seed(seed);
+ b.aspherical = True
+ if len(v)>0:
+ b.shape = Polyhedra(v=v)
+ else:
+ b.shape = Polyhedra(size = size, seed=random.randint(0,1E6))
+ if color[0] == -1:
+ b.shape.color = randomColor(seed=random.randint(0,1E6))
+ else:
+ b.shape.color = color
+ b.mat = material
+ b.state.mass = b.mat.density*b.shape.GetVolume()
+ b.state.inertia = b.shape.GetInertia()*b.mat.density
+ b.state.ori = b.shape.GetOri()
+ b.state.pos = b.shape.GetCentroid()
+ b.mask=mask
+ if fixed:
+ b.state.blockedDOFs = 'xyzXYZ'
+ return b
+
+#**********************************************************************************
+#creates polyhedra having N vertices and resembling sphere
+def polyhedralBall(radius, N, material, center,mask=1):
+ """creates polyhedra having N vertices and resembling sphere
+
+ :param float radius: ball radius
+ :param int N: number of vertices
+ :param Material material: material of new body
+ :param Vector3 center: center of the new body
+ """
+ pts = []
+
+ inc = math.pi * (3. - math.sqrt(5.))
+ off = 2. / float(N)
+ for k in range(0, N):
+ y = k * off - 1. + (off / 2.)
+ r = math.sqrt(1. - y*y)
+ phi = k * inc
+ pts.append([math.cos(phi)*r*radius, y*radius, math.sin(phi)*r*radius])
+
+ ball = polyhedra(material,v=pts)
+ ball.state.pos = center
+ return ball
+
+#**********************************************************************************
+#fill box [mincoord, maxcoord] by non-overlaping polyhedrons with random geometry and sizes within the range (uniformly distributed)
+def fillBox(mincoord, maxcoord,material,sizemin=[1,1,1],sizemax=[1,1,1],ratio=[0,0,0],seed=None,mask=1):
+ """fill box [mincoord, maxcoord] by non-overlaping polyhedrons with random geometry and sizes within the range (uniformly distributed)
+ :param Vector3 mincoord: first corner
+ :param Vector3 maxcoord: second corner
+ :param Vector3 sizemin: minimal size of bodies
+ :param Vector3 sizemax: maximal size of bodies
+ :param Vector3 ratio: scaling ratio
+ :param float seed: random seed
+ """
+ random.seed(seed);
+ v = fillBox_cpp(mincoord, maxcoord, sizemin,sizemax, ratio, random.randint(0,1E6), material)
+ #lastnan = -1
+ #for i in range(0,len(v)):
+ # if(math.isnan(v[i][0])):
+ # O.bodies.append(polyhedra(material,seed=random.randint(0,1E6),v=v[lastnan+1:i],mask=1,fixed=False))
+ # lastnan = i
+
+
Follow ups