diff --git a/MANIFEST.in b/MANIFEST.in index d4c380a667..ca33b9479d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,5 +3,4 @@ include LICENSE recursive-include genesis/assets * recursive-include genesis/ext/pyrender/fonts * recursive-include genesis/ext/pyrender/shaders * -include genesis/ext/VolumeSampling -include genesis/ext/fast_simplification/*.h \ No newline at end of file +include genesis/ext/VolumeSampling \ No newline at end of file diff --git a/genesis/engine/mesh.py b/genesis/engine/mesh.py index 14803e1a3b..2afe406e1c 100644 --- a/genesis/engine/mesh.py +++ b/genesis/engine/mesh.py @@ -1,6 +1,7 @@ import os import pickle as pkl +import fast_simplification import numpy as np import numpy.typing as npt import pyvista as pv @@ -14,7 +15,6 @@ import genesis.utils.gltf as gltf_utils import genesis.utils.usda as usda_utils import genesis.utils.particle as pu -from genesis.ext import fast_simplification from genesis.repr_base import RBC @@ -93,15 +93,23 @@ def decimate(self, decimate_face_num, decimate_aggressiveness, convexify): """ if self._mesh.vertices.shape[0] > 3 and self._mesh.faces.shape[0] > decimate_face_num: self._mesh.process(validate=True) - self._mesh = trimesh.Trimesh( - *fast_simplification.simplify( - self._mesh.vertices, - self._mesh.faces, - target_count=decimate_face_num, - agg=decimate_aggressiveness, - lossless=(decimate_aggressiveness == 0), + # TODO: lossless option support is pending on fast_simplification package. + # NOTE: https://github.com/pyvista/fast-simplification/pull/71 + if decimate_aggressiveness == 0: + gs.logger.debug("Lossless simplification is not supported yet. Not applying simplification.") + self._mesh = trimesh.Trimesh( + vertices=self._mesh.vertices, + faces=self._mesh.faces, + ) + else: + self._mesh = trimesh.Trimesh( + *fast_simplification.simplify( + self._mesh.vertices, + self._mesh.faces, + target_count=decimate_face_num, + agg=decimate_aggressiveness, + ) ) - ) # need to run convexify again after decimation, because sometimes decimating a convex-mesh can make it non-convex... if convexify: diff --git a/genesis/ext/fast_simplification/LICENSE b/genesis/ext/fast_simplification/LICENSE deleted file mode 100644 index 43f988fb07..0000000000 --- a/genesis/ext/fast_simplification/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2017-2021 The PyVista Developers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/genesis/ext/fast_simplification/Replay.h b/genesis/ext/fast_simplification/Replay.h deleted file mode 100644 index 356a1142f9..0000000000 --- a/genesis/ext/fast_simplification/Replay.h +++ /dev/null @@ -1,440 +0,0 @@ -#include "Simplify.h" - -namespace Replay{ - - // Global Variables & Structures (same as for Simplify) - enum Attributes { - NONE, - NORMAL = 2, - TEXCOORD = 4, - COLOR = 8 - }; - struct Triangle { int v[3];double err[4];int deleted,dirty,attr;vec3f n;vec3f uvs[3];int material; }; - struct Vertex { vec3f p;int tstart,tcount;SymetricMatrix q;int border;}; - struct Ref { int tid,tvertex; }; - std::vector triangles; - std::vector vertices; - std::vector refs; - std::string mtllib; - std::vector materials; - std::vector> collapses; - - // Helper functions - double vertex_error(SymetricMatrix q, double x, double y, double z); - double calculate_error(int id_v1, int id_v2, vec3f &p_result); - void initialize_quadrics(); - - - void replay_simplification() - { - // init - for(int i=0; i vcount,vids; - - loopi(0,vertices.size()) - vertices[i].border=0; - - loopi(0,vertices.size()) - { - Vertex &v=vertices[i]; - vcount.clear(); - vids.clear(); - loopj(0,v.tcount) - { - int k=refs[v.tstart+j].tid; - Triangle &t=triangles[k]; - loopk(0,3) - { - int ofs=0,id=t.v[k]; - while(ofs try to find best result - vec3f p1=vertices[id_v1].p; - vec3f p2=vertices[id_v2].p; - vec3f p3=(p1+p2)/2; - double error1 = vertex_error(q, p1.x,p1.y,p1.z); - double error2 = vertex_error(q, p2.x,p2.y,p2.z); - double error3 = vertex_error(q, p3.x,p3.y,p3.z); - error = min(error1, min(error2, error3)); - if (error1 == error) p_result=p1; - if (error2 == error) p_result=p2; - if (error3 == error) p_result=p3; - } - return error; - } - - char *trimwhitespace(char *str) - { - char *end; - - // Trim leading space - while(isspace((unsigned char)*str)) str++; - - if(*str == 0) // All spaces? - return str; - - // Trim trailing space - end = str + strlen(str) - 1; - while(end > str && isspace((unsigned char)*end)) end--; - - // Write new null terminator - *(end+1) = 0; - - return str; - } - - //Option : Load OBJ - void load_obj(const char* filename, bool process_uv=false){ - vertices.clear(); - triangles.clear(); - // printf ( "Loading Objects %s ... \n",filename); - FILE* fn; - if(filename==NULL) return ; - if((char)filename[0]==0) return ; - if ((fn = fopen(filename, "rb")) == NULL) - { - printf ( "File %s not found!\n" ,filename ); - return; - } - char line[1000]; - memset ( line,0,1000 ); - int vertex_cnt = 0; - int material = -1; - std::map material_map; - std::vector uvs; - std::vector > uvMap; - - while(fgets( line, 1000, fn ) != NULL) - { - Vertex v; - vec3f uv; - - if (strncmp(line, "mtllib", 6) == 0) - { - mtllib = trimwhitespace(&line[7]); - } - if (strncmp(line, "usemtl", 6) == 0) - { - std::string usemtl = trimwhitespace(&line[7]); - if (material_map.find(usemtl) == material_map.end()) - { - material_map[usemtl] = materials.size(); - materials.push_back(usemtl); - } - material = material_map[usemtl]; - } - - if ( line[0] == 'v' && line[1] == 't' ) - { - if ( line[2] == ' ' ) - if(sscanf(line,"vt %lf %lf", - &uv.x,&uv.y)==2) - { - uv.z = 0; - uvs.push_back(uv); - } else - if(sscanf(line,"vt %lf %lf %lf", - &uv.x,&uv.y,&uv.z)==3) - { - uvs.push_back(uv); - } - } - else if ( line[0] == 'v' ) - { - if ( line[1] == ' ' ) - if(sscanf(line,"v %lf %lf %lf", - &v.p.x, &v.p.y, &v.p.z)==3) - { - vertices.push_back(v); - } - } - int integers[9]; - if ( line[0] == 'f' ) - { - Triangle t; - bool tri_ok = false; - bool has_uv = false; - - if(sscanf(line,"f %d %d %d", - &integers[0],&integers[1],&integers[2])==3) - { - tri_ok = true; - }else - if(sscanf(line,"f %d// %d// %d//", - &integers[0],&integers[1],&integers[2])==3) - { - tri_ok = true; - }else - if(sscanf(line,"f %d//%d %d//%d %d//%d", - &integers[0],&integers[3], - &integers[1],&integers[4], - &integers[2],&integers[5])==6) - { - tri_ok = true; - }else - if(sscanf(line,"f %d/%d/%d %d/%d/%d %d/%d/%d", - &integers[0],&integers[6],&integers[3], - &integers[1],&integers[7],&integers[4], - &integers[2],&integers[8],&integers[5])==9) - { - tri_ok = true; - has_uv = true; - }else // Add Support for v/vt only meshes - if (sscanf(line, "f %d/%d %d/%d %d/%d", - &integers[0], &integers[6], - &integers[1], &integers[7], - &integers[2], &integers[8]) == 6) - { - tri_ok = true; - has_uv = true; - } - else - { - printf("unrecognized sequence\n"); - printf("%s\n",line); - while(1); - } - if ( tri_ok ) - { - t.v[0] = integers[0]-1-vertex_cnt; - t.v[1] = integers[1]-1-vertex_cnt; - t.v[2] = integers[2]-1-vertex_cnt; - t.attr = 0; - - if ( process_uv && has_uv ) - { - std::vector indices; - indices.push_back(integers[6]-1-vertex_cnt); - indices.push_back(integers[7]-1-vertex_cnt); - indices.push_back(integers[8]-1-vertex_cnt); - uvMap.push_back(indices); - t.attr |= TEXCOORD; - } - - t.material = material; - //geo.triangles.push_back ( tri ); - triangles.push_back(t); - //state_before = state; - //state ='f'; - } - } - } - - if ( process_uv && uvs.size() ) - { - loopi(0,triangles.size()) - { - loopj(0,3) - triangles[i].uvs[j] = uvs[uvMap[i][j]]; - } - } - - fclose(fn); - - //printf("load_obj: vertices = %lu, triangles = %lu, uvs = %lu\n", vertices.size(), triangles.size(), uvs.size() ); - } // load_obj() - - // Optional : Store as OBJ - - void write_obj(const char* filename) - { - FILE *file=fopen(filename, "w"); - int cur_material = -1; - bool has_uv = (triangles.size() && (triangles[0].attr & TEXCOORD) == TEXCOORD); - - if (!file) - { - printf("write_obj: can't write data file \"%s\".\n", filename); - exit(0); - } - if (!mtllib.empty()) - { - fprintf(file, "mtllib %s\n", mtllib.c_str()); - } - loopi(0,vertices.size()) - { - //fprintf(file, "v %lf %lf %lf\n", vertices[i].p.x,vertices[i].p.y,vertices[i].p.z); - fprintf(file, "v %g %g %g\n", vertices[i].p.x,vertices[i].p.y,vertices[i].p.z); //more compact: remove trailing zeros - } - if (has_uv) - { - loopi(0,triangles.size()) if(!triangles[i].deleted) - { - fprintf(file, "vt %g %g\n", triangles[i].uvs[0].x, triangles[i].uvs[0].y); - fprintf(file, "vt %g %g\n", triangles[i].uvs[1].x, triangles[i].uvs[1].y); - fprintf(file, "vt %g %g\n", triangles[i].uvs[2].x, triangles[i].uvs[2].y); - } - } - int uv = 1; - loopi(0,triangles.size()) if(!triangles[i].deleted) - { - if (triangles[i].material != cur_material) - { - cur_material = triangles[i].material; - fprintf(file, "usemtl %s\n", materials[triangles[i].material].c_str()); - } - if (has_uv) - { - fprintf(file, "f %d/%d %d/%d %d/%d\n", triangles[i].v[0]+1, uv, triangles[i].v[1]+1, uv+1, triangles[i].v[2]+1, uv+2); - uv += 3; - } - else - { - fprintf(file, "f %d %d %d\n", triangles[i].v[0]+1, triangles[i].v[1]+1, triangles[i].v[2]+1); - } - //fprintf(file, "f %d// %d// %d//\n", triangles[i].v[0]+1, triangles[i].v[1]+1, triangles[i].v[2]+1); //more compact: remove trailing zeros - } - fclose(file); - } - -} \ No newline at end of file diff --git a/genesis/ext/fast_simplification/Simplify.h b/genesis/ext/fast_simplification/Simplify.h deleted file mode 100644 index 06e6c9fc49..0000000000 --- a/genesis/ext/fast_simplification/Simplify.h +++ /dev/null @@ -1,1057 +0,0 @@ -///////////////////////////////////////////// -// -// Mesh Simplification Tutorial -// -// (C) by Sven Forstmann in 2014 -// -// License : MIT -// http://opensource.org/licenses/MIT -// -//https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification -// -// 5/2016: Chris Rorden created minimal version for OSX/Linux/Windows compile - -#include -//#include -//#include -//#include -//#include -#include -//#include -//#include -#include -#include -#include -#include -#include // std::pair -#include -#include -#include -#include //FLT_EPSILON, DBL_EPSILON - -#define loopi(start_l,end_l) for ( int i=start_l;i1) input=1; - return (double) acos ( input ); - } - - inline double angle2( const vec3f& v , const vec3f& w ) - { - vec3f a = v , b= *this; - double dot = a.x*b.x + a.y*b.y + a.z*b.z; - double len = a.length() * b.length(); - if(len==0)len=1; - - vec3f plane; plane.cross( b,w ); - - if ( plane.x * a.x + plane.y * a.y + plane.z * a.z > 0 ) - return (double) -acos ( dot / len ); - - return (double) acos ( dot / len ); - } - - inline vec3f rot_x( double a ) - { - double yy = cos ( a ) * y + sin ( a ) * z; - double zz = cos ( a ) * z - sin ( a ) * y; - y = yy; z = zz; - return *this; - } - inline vec3f rot_y( double a ) - { - double xx = cos ( -a ) * x + sin ( -a ) * z; - double zz = cos ( -a ) * z - sin ( -a ) * x; - x = xx; z = zz; - return *this; - } - inline void clamp( double min, double max ) - { - if (xmax) x=max; - if (y>max) y=max; - if (z>max) z=max; - } - inline vec3f rot_z( double a ) - { - double yy = cos ( a ) * y + sin ( a ) * x; - double xx = cos ( a ) * x - sin ( a ) * y; - y = yy; x = xx; - return *this; - } - inline vec3f invert() - { - x=-x;y=-y;z=-z;return *this; - } - inline vec3f frac() - { - return vec3f( - x-double(int(x)), - y-double(int(y)), - z-double(int(z)) - ); - } - - inline vec3f integer() - { - return vec3f( - double(int(x)), - double(int(y)), - double(int(z)) - ); - } - - inline double length() const - { - return (double)sqrt(x*x + y*y + z*z); - } - - inline vec3f normalize( double desired_length = 1 ) - { - double square = sqrt(x*x + y*y + z*z); - /* - if (square <= 0.00001f ) - { - x=1;y=0;z=0; - return *this; - }*/ - //double len = desired_length / square; - x/=square;y/=square;z/=square; - - return *this; - } - static vec3f normalize( vec3f a ); - - static void random_init(); - static double random_double(); - static vec3f random(); - - static int random_number; - - double random_double_01(double a){ - double rnf=a*14.434252+a*364.2343+a*4213.45352+a*2341.43255+a*254341.43535+a*223454341.3523534245+23453.423412; - int rni=((int)rnf)%100000; - return double(rni)/(100000.0f-1.0f); - } - - vec3f random01_fxyz(){ - x=(double)random_double_01(x); - y=(double)random_double_01(y); - z=(double)random_double_01(z); - return *this; - } - -}; - -vec3f barycentric(const vec3f &p, const vec3f &a, const vec3f &b, const vec3f &c){ - vec3f v0 = b-a; - vec3f v1 = c-a; - vec3f v2 = p-a; - double d00 = v0.dot(v0); - double d01 = v0.dot(v1); - double d11 = v1.dot(v1); - double d20 = v2.dot(v0); - double d21 = v2.dot(v1); - double denom = d00*d11-d01*d01; - double v = (d11 * d20 - d01 * d21) / denom; - double w = (d00 * d21 - d01 * d20) / denom; - double u = 1.0 - v - w; - return vec3f(u,v,w); -} - -vec3f interpolate(const vec3f &p, const vec3f &a, const vec3f &b, const vec3f &c, const vec3f attrs[3]) -{ - vec3f bary = barycentric(p,a,b,c); - vec3f out = vec3f(0,0,0); - out = out + attrs[0] * bary.x; - out = out + attrs[1] * bary.y; - out = out + attrs[2] * bary.z; - return out; -} - -double min(double v1, double v2) { - return fmin(v1,v2); -} - - -class SymetricMatrix { - - public: - - // Constructor - - SymetricMatrix(double c=0) { loopi(0,10) m[i] = c; } - - SymetricMatrix( double m11, double m12, double m13, double m14, - double m22, double m23, double m24, - double m33, double m34, - double m44) { - m[0] = m11; m[1] = m12; m[2] = m13; m[3] = m14; - m[4] = m22; m[5] = m23; m[6] = m24; - m[7] = m33; m[8] = m34; - m[9] = m44; - } - - // Make plane - - SymetricMatrix(double a,double b,double c,double d) - { - m[0] = a*a; m[1] = a*b; m[2] = a*c; m[3] = a*d; - m[4] = b*b; m[5] = b*c; m[6] = b*d; - m[7 ] =c*c; m[8 ] = c*d; - m[9 ] = d*d; - } - - double operator[](int c) const { return m[c]; } - - // Determinant - - double det( int a11, int a12, int a13, - int a21, int a22, int a23, - int a31, int a32, int a33) - { - double det = m[a11]*m[a22]*m[a33] + m[a13]*m[a21]*m[a32] + m[a12]*m[a23]*m[a31] - - m[a13]*m[a22]*m[a31] - m[a11]*m[a23]*m[a32]- m[a12]*m[a21]*m[a33]; - return det; - } - - const SymetricMatrix operator+(const SymetricMatrix& n) const - { - return SymetricMatrix( m[0]+n[0], m[1]+n[1], m[2]+n[2], m[3]+n[3], - m[4]+n[4], m[5]+n[5], m[6]+n[6], - m[ 7]+n[ 7], m[ 8]+n[8 ], - m[ 9]+n[9 ]); - } - - SymetricMatrix& operator+=(const SymetricMatrix& n) - { - m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3]; - m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6]; m[7]+=n[7]; - m[8]+=n[8]; m[9]+=n[9]; - return *this; - } - - double m[10]; -}; -/////////////////////////////////////////// - -namespace Simplify -{ - // Global Variables & Strctures - enum Attributes { - NONE, - NORMAL = 2, - TEXCOORD = 4, - COLOR = 8 - }; - struct Triangle { int v[3];double err[4];int deleted,dirty,attr;vec3f n;vec3f uvs[3];int material; }; - struct Vertex { vec3f p;int tstart,tcount;SymetricMatrix q;int border;}; - struct Ref { int tid,tvertex; }; - std::vector triangles; - std::vector vertices; - std::vector refs; - std::string mtllib; - std::vector materials; - - std::vector> collapses; - - // Helper functions - - double vertex_error(SymetricMatrix q, double x, double y, double z); - double calculate_error(int id_v1, int id_v2, vec3f &p_result); - bool flipped(vec3f p,int i0,int i1,Vertex &v0,Vertex &v1,std::vector &deleted); - void update_uvs(int i0,const Vertex &v,const vec3f &p,std::vector &deleted); - void update_triangles(int i0,Vertex &v,std::vector &deleted,int &deleted_triangles); - void update_mesh(int iteration); - void compact_mesh(); - // - // Main simplification function - // - // target_count : target nr. of triangles - // agressiveness : sharpness to increase the threshold. - // 5..8 are good numbers - // more iterations yield higher quality - // - - void simplify_mesh(int target_count, double agressiveness=7, bool verbose=false) - { - - // init - loopi(0,triangles.size()) - { - triangles[i].deleted=0; - } - - // main iteration loop - int deleted_triangles=0; - std::vector deleted0,deleted1; - int triangle_count=triangles.size(); - //int iteration = 0; - //loop(iteration,0,100) - collapses.clear(); - for (int iteration = 0; iteration < 100; iteration ++) - { - - if(triangle_count-deleted_triangles<=target_count)break; - - // update mesh once in a while - if(iteration%5==0) - { - update_mesh(iteration); - } - - // clear dirty flag - loopi(0,triangles.size()) triangles[i].dirty=0; - - // - // All triangles with edges below the threshold will be removed - // - // The following numbers works well for most models. - // If it does not, try to adjust the 3 parameters - // - double threshold = 0.000000001*pow(double(iteration+3),agressiveness); - - // target number of triangles reached ? Then break - if ((verbose) && (iteration%5==0)) { - printf("iteration %d - triangles %d threshold %g\n",iteration,triangle_count-deleted_triangles, threshold); - } - - // remove vertices & mark deleted triangles - loopi(0,triangles.size()) - { - Triangle &t=triangles[i]; - if(t.err[3]>threshold) continue; - if(t.deleted) continue; - if(t.dirty) continue; - - loopj(0,3)if(t.err[j]({i0,i1})); - - int tcount=refs.size()-tstart; - - if(tcount<=v0.tcount) - { - // save ram - if(tcount)memcpy(&refs[v0.tstart],&refs[tstart],tcount*sizeof(Ref)); - - } - else - // append - v0.tstart=tstart; - - v0.tcount=tcount; - break; - } - // done? - if(triangle_count-deleted_triangles<=target_count)break; - } - } - // clean up mesh - compact_mesh(); - - } //simplify_mesh() - - void simplify_mesh_lossless(bool verbose=false) - { - // init - loopi(0,triangles.size()) triangles[i].deleted=0; - - // main iteration loop - int deleted_triangles=0; - std::vector deleted0,deleted1; - int triangle_count=triangles.size(); - //int iteration = 0; - //loop(iteration,0,100) - collapses.clear(); - for (int iteration = 0; iteration < 9999; iteration ++) - { - // update mesh constantly - update_mesh(iteration); - // clear dirty flag - loopi(0,triangles.size()) triangles[i].dirty=0; - // - // All triangles with edges below the threshold will be removed - // - // The following numbers works well for most models. - // If it does not, try to adjust the 3 parameters - // - double threshold = DBL_EPSILON; //1.0E-3 EPS; - if (verbose) { - printf("lossless iteration %d\n", iteration); - } - - // remove vertices & mark deleted triangles - loopi(0,triangles.size()) - { - Triangle &t=triangles[i]; - if(t.err[3]>threshold) continue; - if(t.deleted) continue; - if(t.dirty) continue; - - loopj(0,3)if(t.err[j]({i0,i1})); - - int tcount=refs.size()-tstart; - - if(tcount<=v0.tcount) - { - // save ram - if(tcount)memcpy(&refs[v0.tstart],&refs[tstart],tcount*sizeof(Ref)); - } - else - // append - v0.tstart=tstart; - - v0.tcount=tcount; - break; - } - } - if(deleted_triangles<=0)break; - deleted_triangles=0; - } //for each iteration - // clean up mesh - compact_mesh(); - } //simplify_mesh_lossless() - - - // Check if a triangle flips when this edge is removed - - bool flipped(vec3f p,int i0,int i1,Vertex &v0,Vertex &v1,std::vector &deleted) - { - - loopk(0,v0.tcount) - { - Triangle &t=triangles[refs[v0.tstart+k].tid]; - if(t.deleted)continue; - - int s=refs[v0.tstart+k].tvertex; - int id1=t.v[(s+1)%3]; - int id2=t.v[(s+2)%3]; - - if(id1==i1 || id2==i1) // delete ? - { - - deleted[k]=1; - continue; - } - vec3f d1 = vertices[id1].p-p; d1.normalize(); - vec3f d2 = vertices[id2].p-p; d2.normalize(); - if(fabs(d1.dot(d2))>0.999) return true; - vec3f n; - n.cross(d1,d2); - n.normalize(); - deleted[k]=0; - if(n.dot(t.n)<0.2) return true; - } - return false; - } - - // update_uvs - - void update_uvs(int i0,const Vertex &v,const vec3f &p,std::vector &deleted) - { - loopk(0,v.tcount) - { - Ref &r=refs[v.tstart+k]; - Triangle &t=triangles[r.tid]; - if(t.deleted)continue; - if(deleted[k])continue; - vec3f p1=vertices[t.v[0]].p; - vec3f p2=vertices[t.v[1]].p; - vec3f p3=vertices[t.v[2]].p; - t.uvs[r.tvertex] = interpolate(p,p1,p2,p3,t.uvs); - } - } - - // Update triangle connections and edge error after a edge is collapsed - - void update_triangles(int i0,Vertex &v,std::vector &deleted,int &deleted_triangles) - { - vec3f p; - loopk(0,v.tcount) - { - Ref &r=refs[v.tstart+k]; - Triangle &t=triangles[r.tid]; - if(t.deleted)continue; - if(deleted[k]) - { - t.deleted=1; - deleted_triangles++; - continue; - } - t.v[r.tvertex]=i0; - t.dirty=1; - t.err[0]=calculate_error(t.v[0],t.v[1],p); - t.err[1]=calculate_error(t.v[1],t.v[2],p); - t.err[2]=calculate_error(t.v[2],t.v[0],p); - t.err[3]=min(t.err[0],min(t.err[1],t.err[2])); - refs.push_back(r); - } - } - - // compact triangles, compute edge error and build reference list - - void update_mesh(int iteration) - { - if(iteration>0) // compact triangles - { - int dst=0; - loopi(0,triangles.size()) - if(!triangles[i].deleted) - { - triangles[dst++]=triangles[i]; - } - triangles.resize(dst); - } - // - // Init Quadrics by Plane & Edge Errors - // - // required at the beginning ( iteration == 0 ) - // recomputing during the simplification is not required, - // but mostly improves the result for closed meshes - // - if( iteration == 0 ) - { - loopi(0,vertices.size()) - vertices[i].q=SymetricMatrix(0.0); - loopi(0,vertices.size()) - vertices[i].border=0; - - loopi(0,triangles.size()) - { - Triangle &t=triangles[i]; - vec3f n,p[3]; - loopj(0,3) p[j]=vertices[t.v[j]].p; - n.cross(p[1]-p[0],p[2]-p[0]); - n.normalize(); - t.n=n; - loopj(0,3) vertices[t.v[j]].q = - vertices[t.v[j]].q+SymetricMatrix(n.x,n.y,n.z,-n.dot(p[0])); - } - loopi(0,triangles.size()) - { - // Calc Edge Error - Triangle &t=triangles[i];vec3f p; - loopj(0,3) t.err[j]=calculate_error(t.v[j],t.v[(j+1)%3],p); - t.err[3]=min(t.err[0],min(t.err[1],t.err[2])); - } - } - - // Init Reference ID list - loopi(0,vertices.size()) - { - vertices[i].tstart=0; - vertices[i].tcount=0; - } - loopi(0,triangles.size()) - { - Triangle &t=triangles[i]; - loopj(0,3) vertices[t.v[j]].tcount++; - } - int tstart=0; - loopi(0,vertices.size()) - { - Vertex &v=vertices[i]; - v.tstart=tstart; - tstart+=v.tcount; - v.tcount=0; - } - - // Write References - refs.resize(triangles.size()*3); - loopi(0,triangles.size()) - { - Triangle &t=triangles[i]; - loopj(0,3) - { - Vertex &v=vertices[t.v[j]]; - refs[v.tstart+v.tcount].tid=i; - refs[v.tstart+v.tcount].tvertex=j; - v.tcount++; - } - } - - // Identify boundary : vertices[].border=0,1 - if( iteration == 0 ) - { - std::vector vcount,vids; - - loopi(0,vertices.size()) - vertices[i].border=0; - - loopi(0,vertices.size()) - { - Vertex &v=vertices[i]; - vcount.clear(); - vids.clear(); - loopj(0,v.tcount) - { - int k=refs[v.tstart+j].tid; - Triangle &t=triangles[k]; - loopk(0,3) - { - int ofs=0,id=t.v[k]; - while(ofs try to find best result - vec3f p1=vertices[id_v1].p; - vec3f p2=vertices[id_v2].p; - vec3f p3=(p1+p2)/2; - double error1 = vertex_error(q, p1.x,p1.y,p1.z); - double error2 = vertex_error(q, p2.x,p2.y,p2.z); - double error3 = vertex_error(q, p3.x,p3.y,p3.z); - error = min(error1, min(error2, error3)); - if (error1 == error) p_result=p1; - if (error2 == error) p_result=p2; - if (error3 == error) p_result=p3; - } - return error; - } - - char *trimwhitespace(char *str) - { - char *end; - - // Trim leading space - while(isspace((unsigned char)*str)) str++; - - if(*str == 0) // All spaces? - return str; - - // Trim trailing space - end = str + strlen(str) - 1; - while(end > str && isspace((unsigned char)*end)) end--; - - // Write new null terminator - *(end+1) = 0; - - return str; - } - - //Option : Load OBJ - void load_obj(const char* filename, bool process_uv=false){ - vertices.clear(); - triangles.clear(); - // printf ( "Loading Objects %s ... \n",filename); - FILE* fn; - if(filename==NULL) return ; - if((char)filename[0]==0) return ; - if ((fn = fopen(filename, "rb")) == NULL) - { - printf ( "File %s not found!\n" ,filename ); - return; - } - char line[1000]; - memset ( line,0,1000 ); - int vertex_cnt = 0; - int material = -1; - std::map material_map; - std::vector uvs; - std::vector > uvMap; - - while(fgets( line, 1000, fn ) != NULL) - { - Vertex v; - vec3f uv; - - if (strncmp(line, "mtllib", 6) == 0) - { - mtllib = trimwhitespace(&line[7]); - } - if (strncmp(line, "usemtl", 6) == 0) - { - std::string usemtl = trimwhitespace(&line[7]); - if (material_map.find(usemtl) == material_map.end()) - { - material_map[usemtl] = materials.size(); - materials.push_back(usemtl); - } - material = material_map[usemtl]; - } - - if ( line[0] == 'v' && line[1] == 't' ) - { - if ( line[2] == ' ' ) - if(sscanf(line,"vt %lf %lf", - &uv.x,&uv.y)==2) - { - uv.z = 0; - uvs.push_back(uv); - } else - if(sscanf(line,"vt %lf %lf %lf", - &uv.x,&uv.y,&uv.z)==3) - { - uvs.push_back(uv); - } - } - else if ( line[0] == 'v' ) - { - if ( line[1] == ' ' ) - if(sscanf(line,"v %lf %lf %lf", - &v.p.x, &v.p.y, &v.p.z)==3) - { - vertices.push_back(v); - } - } - int integers[9]; - if ( line[0] == 'f' ) - { - Triangle t; - bool tri_ok = false; - bool has_uv = false; - - if(sscanf(line,"f %d %d %d", - &integers[0],&integers[1],&integers[2])==3) - { - tri_ok = true; - }else - if(sscanf(line,"f %d// %d// %d//", - &integers[0],&integers[1],&integers[2])==3) - { - tri_ok = true; - }else - if(sscanf(line,"f %d//%d %d//%d %d//%d", - &integers[0],&integers[3], - &integers[1],&integers[4], - &integers[2],&integers[5])==6) - { - tri_ok = true; - }else - if(sscanf(line,"f %d/%d/%d %d/%d/%d %d/%d/%d", - &integers[0],&integers[6],&integers[3], - &integers[1],&integers[7],&integers[4], - &integers[2],&integers[8],&integers[5])==9) - { - tri_ok = true; - has_uv = true; - }else // Add Support for v/vt only meshes - if (sscanf(line, "f %d/%d %d/%d %d/%d", - &integers[0], &integers[6], - &integers[1], &integers[7], - &integers[2], &integers[8]) == 6) - { - tri_ok = true; - has_uv = true; - } - else - { - printf("unrecognized sequence\n"); - printf("%s\n",line); - while(1); - } - if ( tri_ok ) - { - t.v[0] = integers[0]-1-vertex_cnt; - t.v[1] = integers[1]-1-vertex_cnt; - t.v[2] = integers[2]-1-vertex_cnt; - t.attr = 0; - - if ( process_uv && has_uv ) - { - std::vector indices; - indices.push_back(integers[6]-1-vertex_cnt); - indices.push_back(integers[7]-1-vertex_cnt); - indices.push_back(integers[8]-1-vertex_cnt); - uvMap.push_back(indices); - t.attr |= TEXCOORD; - } - - t.material = material; - //geo.triangles.push_back ( tri ); - triangles.push_back(t); - //state_before = state; - //state ='f'; - } - } - } - - if ( process_uv && uvs.size() ) - { - loopi(0,triangles.size()) - { - loopj(0,3) - triangles[i].uvs[j] = uvs[uvMap[i][j]]; - } - } - - fclose(fn); - - //printf("load_obj: vertices = %lu, triangles = %lu, uvs = %lu\n", vertices.size(), triangles.size(), uvs.size() ); - } // load_obj() - - // Optional : Store as OBJ - - void write_obj(const char* filename) - { - FILE *file=fopen(filename, "w"); - int cur_material = -1; - bool has_uv = (triangles.size() && (triangles[0].attr & TEXCOORD) == TEXCOORD); - - if (!file) - { - printf("write_obj: can't write data file \"%s\".\n", filename); - exit(0); - } - if (!mtllib.empty()) - { - fprintf(file, "mtllib %s\n", mtllib.c_str()); - } - loopi(0,vertices.size()) - { - //fprintf(file, "v %lf %lf %lf\n", vertices[i].p.x,vertices[i].p.y,vertices[i].p.z); - fprintf(file, "v %g %g %g\n", vertices[i].p.x,vertices[i].p.y,vertices[i].p.z); //more compact: remove trailing zeros - } - if (has_uv) - { - loopi(0,triangles.size()) if(!triangles[i].deleted) - { - fprintf(file, "vt %g %g\n", triangles[i].uvs[0].x, triangles[i].uvs[0].y); - fprintf(file, "vt %g %g\n", triangles[i].uvs[1].x, triangles[i].uvs[1].y); - fprintf(file, "vt %g %g\n", triangles[i].uvs[2].x, triangles[i].uvs[2].y); - } - } - int uv = 1; - loopi(0,triangles.size()) if(!triangles[i].deleted) - { - if (triangles[i].material != cur_material) - { - cur_material = triangles[i].material; - fprintf(file, "usemtl %s\n", materials[triangles[i].material].c_str()); - } - if (has_uv) - { - fprintf(file, "f %d/%d %d/%d %d/%d\n", triangles[i].v[0]+1, uv, triangles[i].v[1]+1, uv+1, triangles[i].v[2]+1, uv+2); - uv += 3; - } - else - { - fprintf(file, "f %d %d %d\n", triangles[i].v[0]+1, triangles[i].v[1]+1, triangles[i].v[2]+1); - } - //fprintf(file, "f %d// %d// %d//\n", triangles[i].v[0]+1, triangles[i].v[1]+1, triangles[i].v[2]+1); //more compact: remove trailing zeros - } - fclose(file); - } -}; -/////////////////////////////////////////// diff --git a/genesis/ext/fast_simplification/__init__.py b/genesis/ext/fast_simplification/__init__.py deleted file mode 100644 index 1691177113..0000000000 --- a/genesis/ext/fast_simplification/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from ._version import __version__ # noqa: F401 -from .replay import _map_isolated_points, replay_simplification # noqa: F401 -from .simplify import simplify, simplify_mesh # noqa: F401 diff --git a/genesis/ext/fast_simplification/_replay.pyx b/genesis/ext/fast_simplification/_replay.pyx deleted file mode 100644 index 9385b84d90..0000000000 --- a/genesis/ext/fast_simplification/_replay.pyx +++ /dev/null @@ -1,219 +0,0 @@ -# cython: language_level=3 -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True - - -import numpy as np - -cimport numpy as np -from libc.stdint cimport int64_t -from libcpp cimport bool - - -cdef extern from "wrapper_replay.h" namespace "Replay": - void load_arrays_int32(const int, const int, const int, float*, int*, int*) - void load_arrays_int64(const int, const int, const int, float*, int64_t*, int*) - void replay_simplification() - void get_points(float*) - void get_triangles(int*) - void get_collapses(int*) - int get_faces_int32(int*) - int get_faces_int32_no_padding(int*) - int get_faces_int64(int64_t*) - void write_obj(const char*) - void load_obj(const char*, bool) - int n_points() - int n_triangles() - int n_collapses() - int load_triangles_from_vtk(const int, int*) - void load_points(const int, float*) - void load_collapses(const int, int*) - -def load_int32(int n_points, int n_faces, int n_collapses, float [:, ::1] points, int [:, ::1] faces, int [:, ::1] collapses): - load_arrays_int32(n_points, n_faces, n_collapses, &points[0, 0], &faces[0, 0], &collapses[0, 0]) - - -def load_int64( - int n_points, int n_faces, int n_collapses, float [:, ::1] points, int64_t [:, ::1] faces, int [:, ::1] collapses -): - load_arrays_int64(n_points, n_faces, n_collapses, &points[0, 0], &faces[0, 0], &collapses[0, 0]) - - -# def simplify(int target_count, double aggressiveness=7, bool verbose=False): -# simplify_mesh(target_count, aggressiveness, verbose) - -def replay(): - replay_simplification() - - -def save_obj(filename): - py_byte_string = filename.encode('UTF-8') - cdef char* c_filename = py_byte_string - write_obj(c_filename) - - -def read(filename): - py_byte_string = filename.encode('UTF-8') - cdef char* c_filename = py_byte_string - load_obj(c_filename, False) - - -def return_points(): - cdef float [:, ::1] points = np.empty((n_points(), 3), np.float32) - get_points(&points[0, 0]) - return np.array(points) - - -def return_triangles(): - cdef int [:, ::1] triangles = np.empty((n_triangles(), 3), np.int32) - get_triangles(&triangles[0, 0]) - return np.array(triangles) - -def return_collapses(): - cdef int [:, ::1] collapses = np.empty((n_collapses(), 2), np.int32) - get_collapses(&collapses[0, 0]) - return np.array(collapses) - - -def return_faces_int32_no_padding(): - """VTK formatted faces""" - cdef int [::1] faces = np.empty(n_triangles()*3, np.int32) - n_tri = get_faces_int32_no_padding(&faces[0]) - return np.array(faces[:n_tri*3]) - - -def return_faces_int32(): - """VTK formatted faces""" - cdef int [::1] faces = np.empty(n_triangles()*4, np.int32) - n_tri = get_faces_int32(&faces[0]) - return np.array(faces[:n_tri*4]) - - -def return_faces_int64(): - """VTK formatted faces""" - cdef int64_t [::1] faces = np.empty(n_triangles()*4, np.int64) - n_tri = get_faces_int64(&faces[0]) - return np.array(faces[:n_tri*4]) - - -def load_from_vtk(int n_points, float [:, ::1] points, int [::1] faces, int n_faces): - result = load_triangles_from_vtk(n_faces, &faces[0]) - if result: - raise ValueError( - "Input mesh ``mesh`` must consist of only triangles.\n" - "Run ``.triangulate()`` to convert to an all triangle mesh." - ) - load_points(n_points, &points[0, 0]) - - -def compute_indice_mapping(int[:, :] collapses, int n_points): - - ''' Compute the mapping from original indices to new indices after collapsing - edges - - (pure python implementation with numpy) - ''' - - # start with identity mapping - indice_mapping = np.arange(n_points, dtype=int) - - # First round of mapping - origin_indices = collapses[:, 1] - indice_mapping[origin_indices] = collapses[:, 0] - previous = np.zeros(len(indice_mapping)) - while not np.array_equal(previous, indice_mapping): - previous = indice_mapping.copy() - indice_mapping[origin_indices] = indice_mapping[ - indice_mapping[origin_indices] - ] - - keep = np.setdiff1d( - np.arange(n_points), collapses[:, 1] - ) # Indices of the points that must be kept after decimation - - cdef int i = 0 - cdef int j = 0 - - cdef int[:] application = np.zeros(n_points, dtype=np.int32) - for i in range(n_points): - if j == len(keep): - break - if i == keep[j]: - application[i] = j - j += 1 - - indice_mapping = np.array(application)[indice_mapping] - - return indice_mapping - - -def clean_triangles_and_edges(int[:, :] mapped_triangles, bool clean_edges=False): - """Return the edges and triangles of a mesh from mapped triangles - - Args: - mapped_triangles (np.ndarray): Mapped triangles - clean_edges (bool, optional): If True, remove duplicated edges. - - Returns: - np.ndarray: Edges - np.ndarray: Triangles - """ - - cdef int i, j, k, l - cdef int n_edges = 0 - cdef int n_triangles = 0 - cdef int N = len(mapped_triangles) - cdef int[:, :] edges_with_rep = np.zeros((N, 2), dtype=np.int32) - cdef int[:, :] triangles = np.zeros((N, 3), dtype=np.int32) - - for i in range(N): - j = mapped_triangles[i, 0] - k = mapped_triangles[i, 1] - l = mapped_triangles[i, 2] - - if j != k and j != l and k != l: - triangles[n_triangles, 0] = j - triangles[n_triangles, 1] = k - triangles[n_triangles, 2] = l - n_triangles += 1 - - elif j != k: - # j, k = np.sort([j, k]) - edges_with_rep[n_edges, 0] = j - edges_with_rep[n_edges, 1] = k - n_edges += 1 - - elif j != l: - # j, l = np.sort([j, l]) - edges_with_rep[n_edges, 0] = j - edges_with_rep[n_edges, 1] = l - n_edges += 1 - - elif l != k: - # l, k = np.sort([j, k]) - edges_with_rep[n_edges, 0] = l - edges_with_rep[n_edges, 1] = k - n_edges += 1 - - if not clean_edges: - - return np.asarray(edges_with_rep)[:n_edges, :], np.asarray(triangles)[:n_triangles, :] - - - cdef int[:, :] edges = np.zeros((n_edges, 2), dtype=np.int32) - - - # Lexicographic sort - cdef int[:] order = np.lexsort((np.asarray(edges_with_rep[:n_edges, 1]), np.asarray(edges_with_rep[:n_edges, 0]))) - # Remove duplicates - cdef int n_keep_edges = 1 - edges[0, :] = edges_with_rep[order[0], :] - print(f"n_edges : {n_edges}") - for i in range(1, n_edges): - if (edges_with_rep[order[i], 0] != edges_with_rep[order[i - 1], 0]) or (edges_with_rep[order[i], 1] != edges_with_rep[order[i - 1], 1]): - edges[n_keep_edges, :] = edges_with_rep[order[i], :] - n_keep_edges += 1 - - - return np.asarray(edges)[:n_keep_edges, :], np.asarray(triangles)[:n_triangles, :] diff --git a/genesis/ext/fast_simplification/_simplify.pyx b/genesis/ext/fast_simplification/_simplify.pyx deleted file mode 100644 index d608e35fbe..0000000000 --- a/genesis/ext/fast_simplification/_simplify.pyx +++ /dev/null @@ -1,109 +0,0 @@ -# cython: language_level=3 -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True - - -import numpy as np - -cimport numpy as np -from libc.stdint cimport int64_t -from libcpp cimport bool - - -cdef extern from "wrapper.h" namespace "Simplify": - void load_arrays_int32(const int, const int, double*, int*) - void load_arrays_int64(const int, const int, double*, int64_t*) - void simplify_mesh(int, double aggressiveness, bool verbose) - void simplify_mesh_lossless(bool) - void get_points(double*) - void get_triangles(int*) - void get_collapses(int*) - int get_faces_int32(int*) - int get_faces_int32_no_padding(int*) - int get_faces_int64(int64_t*) - void write_obj(const char*) - void load_obj(const char*, bool) - int n_points() - int n_triangles() - int n_collapses() - int load_triangles_from_vtk(const int, int*) - void load_points(const int, double*) - - - -def load_int32(int n_points, int n_faces, double [:, ::1] points, int [:, ::1] faces): - load_arrays_int32(n_points, n_faces, &points[0, 0], &faces[0, 0]) - - -def load_int64( - int n_points, int n_faces, double [:, ::1] points, int64_t [:, ::1] faces -): - load_arrays_int64(n_points, n_faces, &points[0, 0], &faces[0, 0]) - - -def simplify(int target_count, double aggressiveness=7, bool verbose=False): - simplify_mesh(target_count, aggressiveness, verbose) - -def simplify_lossless(bool verbose=False): - simplify_mesh_lossless(verbose) - - -def save_obj(filename): - py_byte_string = filename.encode('UTF-8') - cdef char* c_filename = py_byte_string - write_obj(c_filename) - - -def read(filename): - py_byte_string = filename.encode('UTF-8') - cdef char* c_filename = py_byte_string - load_obj(c_filename, False) - - -def return_points(): - cdef double [:, ::1] points = np.empty((n_points(), 3), np.float64) - get_points(&points[0, 0]) - return np.array(points) - - -def return_triangles(): - cdef int [:, ::1] triangles = np.empty((n_triangles(), 3), np.int32) - get_triangles(&triangles[0, 0]) - return np.array(triangles) - -def return_collapses(): - cdef int [:, ::1] collapses = np.empty((n_collapses(), 2), np.int32) - get_collapses(&collapses[0, 0]) - return np.array(collapses) - - -def return_faces_int32_no_padding(): - """VTK formatted faces""" - cdef int [::1] faces = np.empty(n_triangles()*3, np.int32) - n_tri = get_faces_int32_no_padding(&faces[0]) - return np.array(faces[:n_tri*3]) - - -def return_faces_int32(): - """VTK formatted faces""" - cdef int [::1] faces = np.empty(n_triangles()*4, np.int32) - n_tri = get_faces_int32(&faces[0]) - return np.array(faces[:n_tri*4]) - - -def return_faces_int64(): - """VTK formatted faces""" - cdef int64_t [::1] faces = np.empty(n_triangles()*4, np.int64) - n_tri = get_faces_int64(&faces[0]) - return np.array(faces[:n_tri*4]) - - -def load_from_vtk(int n_points, double [:, ::1] points, int [::1] faces, int n_faces): - result = load_triangles_from_vtk(n_faces, &faces[0]) - if result: - raise ValueError( - "Input mesh ``mesh`` must consist of only triangles.\n" - "Run ``.triangulate()`` to convert to an all triangle mesh." - ) - load_points(n_points, &points[0, 0]) diff --git a/genesis/ext/fast_simplification/_version.py b/genesis/ext/fast_simplification/_version.py deleted file mode 100644 index 53f3170d1f..0000000000 --- a/genesis/ext/fast_simplification/_version.py +++ /dev/null @@ -1,11 +0,0 @@ -"""fast_simplification version - -On the ``main`` branch, use 'dev0' to denote a development version. -For example: - -version_info = 0, 27, 'dev0' - -""" - -version_info = 0, 2, "dev0" -__version__ = ".".join(map(str, version_info)) diff --git a/genesis/ext/fast_simplification/replay.py b/genesis/ext/fast_simplification/replay.py deleted file mode 100644 index 64199e5b6a..0000000000 --- a/genesis/ext/fast_simplification/replay.py +++ /dev/null @@ -1,214 +0,0 @@ -import numpy as np - -from . import _replay -from .utils import ascontiguous - - -def _map_isolated_points(points, edges, triangles, return_outliers=False): - r"""Map the isolated points to the triangles. - - (points, edges, triangles) represents a structure. The goal of this function - is to compute a mapping array such that the points that are not in the triangles - but are in the edges are merged into the points that are in the triangles, with - respect to the edges. An example is given below. - - (1) - / | \\ - (0) | (2)-3 - \ | / \\ - (4) 6-9 - | - 5 8-7 - - In this example, the points 5, 3, 4, 7, 8, 9 are not connected to any triangle. - The expected mapping is: - - 0 -> 0 - 1 -> 1 - 2 -> 2 - 3 -> 2 - 4 -> 4 - 5 -> 4 - 6 -> 2 - 7 -> 7 (7 cannot be merged into any point in the triangles) - 8 -> 8 (8 cannot be merged into any point in the triangles) - 9 -> 2 - - The output will be the mapping array and the merged points array. In this example, - the mapping array is [0, 1, 2, 2, 4, 4, 2, 7, 8, 2] and the merged points array is - [3, 5, 6, 9]. The points 7 and 8 are outliers. If return_outliers is True, - the function will return the mapping array, the merged points array and the - isolated points array. Else, the function will return the mapping array and the - merged points array. - - Parameters - ---------- - points : sequence - array of points - edges : sequence - array of edges - triangles : sequence - array of triangles - return_outsider : bool - if True, return the outliers - - Returns - ------- - np.ndarray - mapping array - np.ndarray - merged points array - """ - n_points = points.shape[0] - - # The points to connect are the points that are not in the triangles - # but are in the edges - points_to_connect = np.intersect1d(np.setdiff1d(np.arange(n_points), np.unique(triangles)), np.unique(edges)) - # Start with the identity mapping - mapping = np.arange(n_points, dtype=np.int64) - - # Remove edges that do not contains points to connect - edges = edges[np.isin(edges, points_to_connect).any(axis=1)] - - n_edges = edges.shape[0] - n_edges_old = 0 - - # Iterate until there is no more edges to collapse - # or until a statiionary state is reached - while n_edges > 0 and n_edges != n_edges_old: - n_edges_old = n_edges - - # Edges that connect two points to connect - # are kept for the next iteration - keep = np.isin(edges, points_to_connect).all(axis=1) - - # Edges that connect a point to connect to a point - # that is not to connect are merged - connexions = edges[~keep] - - a = np.isin(connexions, points_to_connect) - merged = connexions[np.where(a)] - target = connexions[np.where(~a)] - - # Update the mapping array and the points to connect - mapping[merged] = mapping[target] - points_to_connect = np.setdiff1d(points_to_connect, merged) - - # Remove the edges that are merged - edges = edges[keep] - # Remove edges that do not contains points to connect - edges = edges[np.isin(edges, points_to_connect).any(axis=1)] - n_edges = edges.shape[0] - - # The points that have been merged are the ones - # such that mapping[i] != i - merged_points = np.where(mapping != np.arange(len(mapping)))[0] - - if return_outliers: - isolated_points = points_to_connect - return mapping, merged_points, isolated_points - return mapping, merged_points - - -@ascontiguous -def replay_simplification(points, triangles, collapses): - """Replay the decimation of a triangular mesh. - - Parameters - ---------- - points : sequence - A ``(n, 3)`` array of points. May be a ``numpy.ndarray`` or a - list of points. For efficiency, provide points as a float32 - array. - triangles : sequence - A ``(n, 3)`` array of triangle indices. May be a - ``numpy.ndarray`` or a list of triangle indices. For - efficiency, provide points as a float32 array. - collapses : sequence - The collapses to replay. - A ``(n, 2)`` numpy.ndarray of collapses. - ``collapses[i] = [i0, i1]`` means that during the i-th - collapse, the vertex ``i1`` was collapsed into the vertex - ``i0``. - - Returns - ------- - np.ndarray - Points array. - np.ndarray - Triangles array. - np.ndarray - indice_mapping array. - A ``(n,)`` array of indices. - ``indice_mapping[i] = j`` means that the vertex ``i`` of - the original mesh was mapped to the vertex ``j`` of the - decimated mesh. - - """ - import numpy as np - - if not isinstance(points, np.ndarray): - points = np.array(points, dtype=np.float32) - if not isinstance(triangles, np.ndarray): - triangles = np.array(triangles, dtype=np.int32) - - if points.ndim != 2: - raise ValueError("``points`` array must be 2 dimensional") - if points.shape[1] != 3: - raise ValueError(f"Expected ``points`` array to be (n, 3), not {points.shape}") - - if triangles.ndim != 2: - raise ValueError("``triangles`` array must be 2 dimensional") - if triangles.shape[1] != 3: - raise ValueError(f"Expected ``triangles`` array to be (n, 3), not {triangles.shape}") - - if not triangles.flags.c_contiguous: - triangles = np.ascontiguousarray(triangles) - - if triangles.dtype == np.int32: - load = _replay.load_int32 - elif triangles.dtype == np.int64: - load = _replay.load_int64 - else: - load = _replay.load_int32 - triangles = triangles.astype(np.int32) - - # Collapse the points - n_faces = triangles.shape[0] - n_points = points.shape[0] - load(n_points, n_faces, collapses.shape[0], points, triangles, collapses) - _replay.replay() - dec_points = _replay.return_points() - - # Compute the indice mapping - indice_mapping = _replay.compute_indice_mapping(collapses, len(points)) - - # compute the new triangles - # Apply the indice mapping to the triangles - mapped_triangles = indice_mapping[triangles.copy()] - - # Extract the edges and the triangles - # Edges can be repeated, but this is not a problem - # and it is faster to do so - dec_edges, dec_triangles = _replay.clean_triangles_and_edges(mapped_triangles) - - # Map the isolated points to the triangles - mapping, points_to_merge, outliers = _map_isolated_points( - dec_points, dec_edges, dec_triangles, return_outliers=True - ) - - dec_triangles = mapping[dec_triangles] - indice_mapping = mapping[indice_mapping] - - points_to_merge = np.union1d(points_to_merge, outliers) - # Remove the isolated points - # isolated_points = new_collapses[:, 1] - points_to_merge = np.sort(points_to_merge)[::-1] - mapping = np.arange(dec_points.shape[0]) - for ip in points_to_merge: - dec_points = np.delete(dec_points, ip, axis=0) - mapping[ip:] -= 1 - indice_mapping = mapping[indice_mapping] - dec_triangles = mapping[dec_triangles] - - return dec_points, dec_triangles, indice_mapping diff --git a/genesis/ext/fast_simplification/simplify.py b/genesis/ext/fast_simplification/simplify.py deleted file mode 100644 index d926991a4f..0000000000 --- a/genesis/ext/fast_simplification/simplify.py +++ /dev/null @@ -1,221 +0,0 @@ -"""Simplification library.""" - -import numpy as np - -from . import _simplify -from .utils import ascontiguous - - -def _check_args(target_reduction, target_count, n_faces): - """Check arguments.""" - if target_reduction and target_count: - raise ValueError("You may specify ``target_reduction`` or ``target_count``, but not" " both") - if target_reduction is None and target_count is None: - raise ValueError("You must specify ``target_reduction`` or ``target_count``") - - if target_reduction is not None: - if target_reduction > 1 or target_reduction < 0: - raise ValueError("``target_reduction`` must be between 0 and 1") - target_count = (1 - target_reduction) * n_faces - - if target_count < 0: - raise ValueError("``target_count`` must be greater than 0") - if target_count > n_faces: - raise ValueError(f"``target_count`` must be less than the number of faces {n_faces}") - return int(target_count) - - -@ascontiguous -def simplify( - points, - triangles, - target_reduction=None, - target_count=None, - agg=7, - verbose=False, - return_collapses=False, - lossless=False, -): - """Simplify a triangular mesh. - - Parameters - ---------- - points : sequence[float | double] - A ``(n, 3)`` array of points. May be a ``numpy.ndarray`` or a - sequence of points. Internally converted to double precision. - triangles : sequence - A ``(n, 3)`` array of triangle indices. May be a - ``numpy.ndarray`` or a list of triangle indices. - target_reduction : float, optional - Fraction of the original mesh to remove. If set to ``0.9``, - this function will try to reduce the data set to 10% of its - original size and will remove 90% of the input triangles. Use - this parameter or ``target_count``. - target_count : int, optional - Target number of triangles to reduce mesh to. This may be - used in place of ``target_reduction``, but both cannot be set. - agg : int, optional - Controls how aggressively to decimate the mesh. A value of 10 - will result in a fast decimation at the expense of mesh - quality and shape. A value of 0 will attempt to preserve the - original mesh geometry at the expense of time. Setting a low - value may result in being unable to reach the - ``target_reduction`` or ``target_count``. - verbose : bool, optional - Enable verbose output when simplifying the mesh. - return_collapses : bool, optional - If True, return the history of collapses as a - ``(n_collapses, 2)`` array of indices. - ``collapses[i] = [i0, i1]`` means that durint the i-th - collapse, the vertex ``i1`` was collapsed into the vertex - ``i0``. - - Returns - ------- - np.ndarray - Points array. - np.ndarray - Triangles array. - np.ndarray (optional) - Collapses array. - - Examples - -------- - This basic example demonstrates how to decimate a simple planar - mesh composed by 8 triangles. - - >>> import fast_simplification - >>> points = [ - ... [0.5, -0.5, 0.0], - ... [0.0, -0.5, 0.0], - ... [-0.5, -0.5, 0.0], - ... [0.5, 0.0, 0.0], - ... [0.0, 0.0, 0.0], - ... [-0.5, 0.0, 0.0], - ... [0.5, 0.5, 0.0], - ... [0.0, 0.5, 0.0], - ... [-0.5, 0.5, 0.0], - ... ] - >>> faces = [ - ... [0, 1, 3], - ... [4, 3, 1], - ... [1, 2, 4], - ... [5, 4, 2], - ... [3, 4, 6], - ... [7, 6, 4], - ... [4, 5, 7], - ... [8, 7, 5], - ... ] - >>> points_out, faces_out = fast_simplification.simplify(points, faces, 0.5) - - """ - - points = np.asarray(points, dtype=np.float64) - if not isinstance(triangles, np.ndarray): - triangles = np.array(triangles, dtype=np.int32) - - if points.ndim != 2: - raise ValueError("``points`` array must be 2 dimensional") - if points.shape[1] != 3: - raise ValueError(f"Expected ``points`` array to be (n, 3), not {points.shape}") - - if triangles.ndim != 2: - raise ValueError("``triangles`` array must be 2 dimensional") - if triangles.shape[1] != 3: - raise ValueError(f"Expected ``triangles`` array to be (n, 3), not {triangles.shape}") - - n_faces = triangles.shape[0] - - triangles = np.ascontiguousarray(triangles) - - if triangles.dtype == np.int32: - load = _simplify.load_int32 - elif triangles.dtype == np.int64: - load = _simplify.load_int64 - else: - load = _simplify.load_int32 - triangles = triangles.astype(np.int32) - - load( - points.shape[0], - n_faces, - points, - triangles, - ) - - if lossless: - _simplify.simplify_lossless(verbose) - else: - target_count = _check_args(target_reduction, target_count, n_faces) - _simplify.simplify(target_count, agg, verbose) - points = _simplify.return_points() - faces = _simplify.return_faces_int32_no_padding().reshape(-1, 3) - - if return_collapses: - return points, faces, _simplify.return_collapses() - return points, faces - - -def simplify_mesh(mesh, target_reduction=None, target_count=None, agg=7, verbose=False): - """Simplify a pyvista mesh. - - Parameters - ---------- - mesh : pyvista.PolyData - PyVista mesh. - target_reduction : float - Fraction of the original mesh to remove. If set to ``0.9``, - this function will try to reduce the data set to 10% of its - original size and will remove 90% of the input triangles. Use - this parameter or ``target_count``. - target_count : int, optional - Target number of triangles to reduce mesh to. This may be - used in place of ``target_reduction``, but both cannot be set. - agg : int, optional - Controls how aggressively to decimate the mesh. A value of 10 - will result in a fast decimation at the expense of mesh - quality and shape. A value of 0 will attempt to preserve the - original mesh geometry at the expense of time. Setting a low - value may result in being unable to reach the - ``target_reduction`` or ``target_count``. - verbose : bool, optional - Enable verbose output when simplifying the mesh. - - Returns - ------- - pyvista.PolyData - Simplified mesh. The field data of the mesh will contain a - field named ``fast_simplification_collapses`` that contains - the history of collapses as a ``(n_collapses, 2)`` array of - indices. ``collapses[i] = [i0, i1]`` means that during the - i-th collapse, the vertex ``i1`` was collapsed into the vertex - ``i0``. - - """ - try: - import pyvista as pv - except ImportError: - raise ImportError("Please install pyvista to use this feature with:\n" "pip install pyvista") - - n_faces = mesh.n_cells - _simplify.load_from_vtk( - mesh.n_points, - mesh.points.astype(np.float64, order="C", copy=False), - mesh.faces.astype(np.int32, order="C", copy=False), - n_faces, - ) - - target_count = _check_args(target_reduction, target_count, n_faces) - _simplify.simplify(target_count, agg, verbose) - - # return the correct datatype of the faces - if pv._get_vtk_id_type() == np.int32: - faces = _simplify.return_faces_int32() - else: - faces = _simplify.return_faces_int64() - - # construct mesh - mesh = pv.PolyData(_simplify.return_points(), faces, deep=False) - mesh.field_data["fast_simplification_collapses"] = _simplify.return_collapses() - - return mesh diff --git a/genesis/ext/fast_simplification/tests/test_map_isolated_points.py b/genesis/ext/fast_simplification/tests/test_map_isolated_points.py deleted file mode 100644 index 04c8d697a8..0000000000 --- a/genesis/ext/fast_simplification/tests/test_map_isolated_points.py +++ /dev/null @@ -1,206 +0,0 @@ -import numpy as np - -from fast_simplification import _map_isolated_points as map_isolated_points - - -def test_map_isolated_points(): - # Example 1 - # - # (1) - # / | \ - # (0) | (2)-3 - # \ | / \ - # (4) 6-9 - # | - # 5 8-7 - - points = np.random.rand(10, 3) - - edges = np.array( - [ - [0, 1], - [0, 4], - [1, 4], - [1, 2], - [2, 4], - [2, 3], - [2, 6], - [6, 9], - [4, 5], - [8, 7], - ], - dtype=np.int64, - ) - - triangles = np.array( - [ - [0, 1, 4], - [1, 2, 4], - ], - dtype=np.int64, - ) - - target_mapping = np.array([0, 1, 2, 2, 4, 4, 2, 7, 8, 2], dtype=np.int64) - - target_merged_points = np.array([3, 5, 6, 9], dtype=np.int64) - - mapping, merged_points = map_isolated_points(points, edges, triangles) - assert np.allclose(mapping, target_mapping) - assert np.allclose(merged_points, target_merged_points) - - # Example 2 - # - # (7)-(8) 3 - # \ | - # \ | - # (0)-1-2-9-4-5-6 - - points = np.random.rand(10, 3) - - edges = np.array( - [ - [0, 1], - [1, 2], - [2, 9], - [9, 4], - [4, 5], - [5, 6], - ], - dtype=np.int64, - ) - - triangles = np.array( - [ - [0, 7, 8], - ] - ) - - target_mapping = np.array([0, 0, 0, 3, 0, 0, 0, 7, 8, 0], dtype=np.int64) - - target_merged_points = np.array([1, 2, 4, 5, 6, 9], dtype=np.int64) - - mapping, merged_points = map_isolated_points(points, edges, triangles) - assert np.allclose(mapping, target_mapping) - assert np.allclose(merged_points, target_merged_points) - - # Example 3 - # - # (1) - # | \ - # | \ - # (2)-(0)-4-6 - # | | - # 3-5 - - points = np.random.rand(7, 3) - - edges = np.array([[0, 1], [1, 2], [2, 0], [0, 4], [4, 6], [5, 6], [3, 5]], dtype=np.int64) - - triangles = np.array( - [ - [0, 1, 2], - ], - dtype=np.int64, - ) - - target_mapping = np.array([0, 1, 2, 0, 0, 0, 0], dtype=np.int64) - - target_merged_points = np.array([3, 4, 5, 6], dtype=np.int64) - - mapping, merged_points = map_isolated_points(points, edges, triangles) - assert np.allclose(mapping, target_mapping) - assert np.allclose(merged_points, target_merged_points) - - ## Example 4 - # - # (6) (7)-(8) - # | \ | / - # | \ | / - # (5)-(0)-1-2-3-(4) - # - # Here the situation is ambiguous. Does 2 merge into 0 or 4 ? - # We consider 2 -> 4 and 2 -> 0 as valid solutions. - - points = np.random.rand(9, 3) - - edges = np.array( - [ - [0, 1], - [1, 2], - [2, 3], - [3, 4], - ], - dtype=np.int64, - ) - - triangles = np.array([[0, 5, 6], [4, 7, 8]], dtype=np.int64) - - target_mapping1 = np.array([0, 0, 0, 4, 4, 5, 6, 7, 8], dtype=np.int64) - - target_mapping2 = np.array([0, 0, 4, 4, 4, 5, 6, 7, 8], dtype=np.int64) - - target_merged_points = np.array([1, 2, 3], dtype=np.int64) - - mapping, merged_points = map_isolated_points(points, edges, triangles) - assert np.allclose(mapping, target_mapping1) or np.allclose(mapping, target_mapping2) - assert np.allclose(merged_points, target_merged_points) - - ## Example 5 - # - # (1) (7)-(8)-9 - # | \ | / - # | \ | / - # (2)-(3)-0 4-5-(6) - - points = np.random.rand(10, 3) - edges = np.array( - [ - [0, 3], - [1, 3], - [2, 3], - [1, 2], - [4, 5], - [5, 6], - [8, 9], - ], - dtype=np.int64, - ) - triangles = np.array( - [ - [1, 2, 3], - [6, 7, 8], - ], - dtype=np.int64, - ) - - target_mapping = np.array([3, 1, 2, 3, 6, 6, 6, 7, 8, 8], dtype=np.int64) - - target_merged_points = np.array([0, 4, 5, 9], dtype=np.int64) - - mapping, merged_points = map_isolated_points(points, edges, triangles) - assert np.allclose(mapping, target_mapping) - assert np.allclose(merged_points, target_merged_points) - - ## Example 6 - # - # 0-1-2 - - points = np.random.rand(3, 3) - - edges = np.array( - [ - [0, 1], - [1, 2], - ], - dtype=np.int64, - ) - - triangles = np.array([[]], dtype=np.int64) - - target_mapping = np.array([0, 1, 2], dtype=np.int64) - - target_merged_points = np.array([], dtype=np.int64) - - mapping, merged_points = map_isolated_points(points, edges, triangles) - assert np.allclose(mapping, target_mapping) - assert np.allclose(merged_points, target_merged_points) diff --git a/genesis/ext/fast_simplification/tests/test_replay.py b/genesis/ext/fast_simplification/tests/test_replay.py deleted file mode 100644 index c41afb0249..0000000000 --- a/genesis/ext/fast_simplification/tests/test_replay.py +++ /dev/null @@ -1,127 +0,0 @@ -import numpy as np -import pytest - -import fast_simplification - -try: - import pyvista as pv - - has_vtk = True -except ModuleNotFoundError: - has_vtk = False -skip_no_vtk = pytest.mark.skipif(not has_vtk, reason="Requires VTK") - - -@pytest.fixture -def mesh(): - return pv.Sphere() - - -def test_collapses_trivial(): - # arrays from: - # mesh = pv.Plane(i_resolution=2, j_resolution=2).triangulate() - points = [ - [0.5, -0.5, 0.0], - [0.0, -0.5, 0.0], - [-0.5, -0.5, 0.0], - [0.5, 0.0, 0.0], - [0.0, 0.0, 0.0], - [-0.5, 0.0, 0.0], - [0.5, 0.5, 0.0], - [0.0, 0.5, 0.0], - [-0.5, 0.5, 0.0], - ] - - faces = [ - [0, 1, 3], - [4, 3, 1], - [1, 2, 4], - [5, 4, 2], - [3, 4, 6], - [7, 6, 4], - [4, 5, 7], - [8, 7, 5], - ] - - with pytest.raises(ValueError, match="You must specify"): - fast_simplification.simplify(points, faces) - - points_out, faces_out, collapses = fast_simplification.simplify(points, faces, 0.5, return_collapses=True) - - ( - replay_points, - replay_faces, - indice_mapping, - ) = fast_simplification.replay_simplification(points, faces, collapses) - assert np.allclose(points_out, replay_points) - assert np.allclose(faces_out, replay_faces) - - -@skip_no_vtk -def test_collapses_sphere(mesh): - points = mesh.points - faces = mesh.faces.reshape(-1, 4)[:, 1:] - reduction = 0.5 - - points_out, faces_out, collapses = fast_simplification.simplify(points, faces, reduction, return_collapses=True) - - ( - replay_points, - replay_faces, - indice_mapping, - ) = fast_simplification.replay_simplification(points, faces, collapses) - assert np.allclose(points_out, replay_points) - assert np.allclose(faces_out, replay_faces) - - -try: - from pyvista import examples - - @pytest.fixture - def louis(): - return examples.download_louis_louvre() - - @pytest.fixture - def human(): - return examples.download_human() - - has_examples = True -except: - has_examples = False -skip_no_examples = pytest.mark.skipif(not has_examples, reason="Requires pyvista.examples") - - -@skip_no_examples -@skip_no_vtk -def test_collapses_louis(louis): - points = louis.points - faces = louis.faces.reshape(-1, 4)[:, 1:] - reduction = 0.9 - - points_out, faces_out, collapses = fast_simplification.simplify(points, faces, reduction, return_collapses=True) - - ( - replay_points, - replay_faces, - indice_mapping, - ) = fast_simplification.replay_simplification(points, faces, collapses) - assert np.allclose(points_out, replay_points) - assert np.allclose(faces_out, replay_faces) - - -@skip_no_examples -@skip_no_vtk -def test_human(human): - points = human.points - faces = human.faces.reshape(-1, 4)[:, 1:] - reduction = 0.9 - - points_out, faces_out, collapses = fast_simplification.simplify(points, faces, reduction, return_collapses=True) - - ( - replay_points, - replay_faces, - indice_mapping, - ) = fast_simplification.replay_simplification(points, faces, collapses) - assert np.allclose(points_out, replay_points) - assert np.allclose(faces_out, replay_faces) diff --git a/genesis/ext/fast_simplification/tests/test_simplify.py b/genesis/ext/fast_simplification/tests/test_simplify.py deleted file mode 100644 index bd3a3b6839..0000000000 --- a/genesis/ext/fast_simplification/tests/test_simplify.py +++ /dev/null @@ -1,118 +0,0 @@ -import numpy as np -import pytest - -import fast_simplification - -try: - import pyvista as pv - - has_vtk = True -except ModuleNotFoundError: - has_vtk = False - -skip_no_vtk = pytest.mark.skipif(not has_vtk, reason="Requires VTK") - - -@pytest.fixture -def mesh(): - return pv.Sphere() - - -def test_simplify_trivial(): - # arrays from: - # mesh = pv.Plane(i_resolution=2, j_resolution=2).triangulate() - points = [ - [0.5, -0.5, 0.0], - [0.0, -0.5, 0.0], - [-0.5, -0.5, 0.0], - [0.5, 0.0, 0.0], - [0.0, 0.0, 0.0], - [-0.5, 0.0, 0.0], - [0.5, 0.5, 0.0], - [0.0, 0.5, 0.0], - [-0.5, 0.5, 0.0], - ] - - faces = [ - [0, 1, 3], - [4, 3, 1], - [1, 2, 4], - [5, 4, 2], - [3, 4, 6], - [7, 6, 4], - [4, 5, 7], - [8, 7, 5], - ] - - with pytest.raises(ValueError, match="You must specify"): - fast_simplification.simplify(points, faces) - - points_out, faces_out = fast_simplification.simplify(points, faces, 0.5) - assert points_out.shape[0] == 5 - assert faces_out.shape[0] == 4 - - # Test with return_collapses=True - # We check that the number of points after simplification is equal to the number of - # points before simplification minus the number of collapses - points_out, faces_out, collapses = fast_simplification.simplify(points, faces, 0.5, return_collapses=True) - n_points_before_simplification = len(points) - n_points_after_simplification = len(points_out) - n_collapses = len(collapses) - assert n_points_after_simplification == n_points_before_simplification - n_collapses - - -@skip_no_vtk -def test_simplify_none(mesh): - triangles = mesh.faces.reshape(-1, 4)[:, 1:] - - reduction = 0 - points, faces = fast_simplification.simplify(mesh.points, triangles, reduction) - assert np.allclose(triangles, faces) - assert np.allclose(mesh.points, points) - - -@skip_no_vtk -def test_simplify(mesh): - triangles = mesh.faces.reshape(-1, 4)[:, 1:] - reduction = 0.5 - points, faces, collapses = fast_simplification.simplify(mesh.points, triangles, reduction, return_collapses=True) - assert triangles.shape[0] * reduction == faces.shape[0] - # We check that the number of points after simplification is equal to the number of - # points before simplification minus the number of collapses - n_points_before_simplification = mesh.points.shape[0] - n_points_after_simplification = points.shape[0] - n_collapses = collapses.shape[0] - assert n_points_after_simplification == n_points_before_simplification - n_collapses - - assert points.dtype == np.float64 - - -@skip_no_vtk -def test_simplify_agg(mesh): - triangles = mesh.faces.reshape(-1, 4)[:, 1:] - - reduction = 0.5 - points, faces = fast_simplification.simplify( - mesh.points, - triangles, - reduction, - agg=0, - ) - assert triangles.shape[0] == faces.shape[0] - - reduction = 0.5 - points, faces = fast_simplification.simplify( - mesh.points, - triangles, - reduction, - agg=1, - ) - # somewhere between the requested reduction and the original number of triangles - assert triangles.shape[0] * reduction < faces.shape[0] < triangles.shape[0] - - -@skip_no_vtk -def test_simplify_mesh(mesh): - reduction = 0.5 - mesh_out = fast_simplification.simplify_mesh(mesh, reduction) - assert mesh_out.n_cells == mesh.n_cells * reduction diff --git a/genesis/ext/fast_simplification/utils.py b/genesis/ext/fast_simplification/utils.py deleted file mode 100644 index 88254b27bc..0000000000 --- a/genesis/ext/fast_simplification/utils.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Utility functions for the fast_simplification package.""" - -import numpy as np - - -def ascontiguous(func): - """A decorator that ensure that all the numpy arrays passed to the function - are contiguous in memory and if not, apply np.ascontinguous arrays. - """ - - def wrapper(*args, **kwargs): - args = list(args) - for i, arg in enumerate(args): - if isinstance(arg, np.ndarray): - args[i] = np.ascontiguousarray(arg) - - for key, value in kwargs.items(): - if isinstance(value, np.ndarray): - kwargs[key] = np.ascontiguousarray(value) - - return func(*args, **kwargs) - - # Copy annotations - wrapper.__annotations__ = func.__annotations__ - return wrapper diff --git a/genesis/ext/fast_simplification/wrapper.h b/genesis/ext/fast_simplification/wrapper.h deleted file mode 100644 index 1cff537ac0..0000000000 --- a/genesis/ext/fast_simplification/wrapper.h +++ /dev/null @@ -1,190 +0,0 @@ -// wrap simplify header file for integration with cython -#include "Simplify.h" - -namespace Simplify{ - - // load triangles - void load_points(const int n_points, double* points){ - vertices.clear(); - // load vertices - for (int ii = 0; ii < n_points; ii ++){ - Vertex v; - v.p.x = points[0 + 3*ii]; - v.p.y = points[1 + 3*ii]; - v.p.z = points[2 + 3*ii]; - vertices.push_back(v); - } - } - - // load triangles - void load_triangles(const int n_tri, int* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - t.v[0] = faces[0 + 3*ii]; - t.v[1] = faces[1 + 3*ii]; - t.v[2] = faces[2 + 3*ii]; - triangles.push_back(t); - } - } - - // load triangles - void load_triangles_int64(const int n_tri, int64_t* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - t.v[0] = faces[0 + 3*ii]; - t.v[1] = faces[1 + 3*ii]; - t.v[2] = faces[2 + 3*ii]; - triangles.push_back(t); - } - } - - // load triangles from vtk and deal with padding - int load_triangles_from_vtk(const int n_tri, int* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - if (faces[4*ii] != 3){ - return 1; - } - t.v[0] = faces[1 + 4*ii]; - t.v[1] = faces[2 + 4*ii]; - t.v[2] = faces[3 + 4*ii]; - triangles.push_back(t); - } - return 0; - } - - void load_arrays_int32(const int n_points, const int n_tri, - double* points, int* faces){ - load_points(n_points, points); - load_triangles(n_tri, faces); - } - - void load_arrays_int64(const int n_points, const int n_tri, - double* points, int64_t* faces){ - load_points(n_points, points); - load_triangles_int64(n_tri, faces); - } - - int n_points(){ - return vertices.size(); - } - - int n_triangles(){ - return triangles.size(); - } - - int n_collapses(){ - return collapses.size(); - } - - // load triangles - void load_triangles(const int n_tri, int64_t* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - t.v[0] = faces[0 + 3*ii]; - t.v[1] = faces[1 + 3*ii]; - t.v[2] = faces[2 + 3*ii]; - triangles.push_back(t); - } - } - - // populate a contiguous array with the points in the vertices vector - void get_points(double* points){ - - // load vertices - int n_points = vertices.size(); - for (int ii = 0; ii < n_points; ii ++){ - points[0 + 3*ii] = vertices[ii].p.x; - points[1 + 3*ii] = vertices[ii].p.y; - points[2 + 3*ii] = vertices[ii].p.z; - } - } - - // populate a contiguous array with the points in the vertices vector - void get_triangles(int* tri){ - - // load vertices - int n_tri = triangles.size(); - for (int ii = 0; ii < n_tri; ii ++){ - tri[0 + 3*ii] = triangles[ii].v[0]; - tri[1 + 3*ii] = triangles[ii].v[1]; - tri[2 + 3*ii] = triangles[ii].v[2]; - } - } - - void get_collapses(int* coll){ - - // load vertices - int n_collapse = collapses.size(); - for (int ii = 0; ii < n_collapse; ii ++){ - coll[0 + 2*ii] = collapses.at(ii).at(0); - coll[1 + 2*ii] = collapses.at(ii).at(1); - } - } - - // populate a contiguous array with the points in the vertices vector - int get_faces_int32(int32_t* tri){ - - // load vertices - int n_tri = triangles.size(); - int jj = 0; - for (int ii = 0; ii < n_tri; ii ++){ - if (!triangles[ii].deleted){ - tri[0 + 3*jj] = 3; - tri[1 + 3*jj] = triangles[ii].v[0]; - tri[2 + 3*jj] = triangles[ii].v[1]; - tri[3 + 3*jj] = triangles[ii].v[2]; - jj += 1; - } - } - return jj; - } - - // populate a contiguous array with the points in the vertices - // vector without the vtk padding - int get_faces_int32_no_padding(int32_t* tri){ - - // load vertices - int n_tri = triangles.size(); - int jj = 0; - for (int ii = 0; ii < n_tri; ii ++){ - if (!triangles[ii].deleted){ - tri[0 + 3*jj] = triangles[ii].v[0]; - tri[1 + 3*jj] = triangles[ii].v[1]; - tri[2 + 3*jj] = triangles[ii].v[2]; - jj += 1; - } - } - return jj; - } - - // populate a contiguous array with the points in the vertices vector - int get_faces_int64(int64_t* tri){ - - // load vertices - int n_tri = triangles.size(); - int jj = 0; - for (int ii = 0; ii < n_tri; ii ++){ - if (!triangles[ii].deleted){ - tri[0 + 4*jj] = 3; - tri[1 + 4*jj] = triangles[ii].v[0]; - tri[2 + 4*jj] = triangles[ii].v[1]; - tri[3 + 4*jj] = triangles[ii].v[2]; - jj += 1; - } - } - return jj; - } -} diff --git a/genesis/ext/fast_simplification/wrapper_replay.h b/genesis/ext/fast_simplification/wrapper_replay.h deleted file mode 100644 index d684bb5680..0000000000 --- a/genesis/ext/fast_simplification/wrapper_replay.h +++ /dev/null @@ -1,203 +0,0 @@ -// wrap simplify header file for integration with cython -#include "Replay.h" - -namespace Replay{ - - // load collapses - void load_collapses(const int n_coll, int* coll){ - collapses.clear(); - for (int ii = 0; ii < n_coll; ii ++){ - std::vector c; - c.push_back(coll[0 + 2*ii]); - c.push_back(coll[1 + 2*ii]); - collapses.push_back(c); - } - } - - // load points - void load_points(const int n_points, float* points){ - vertices.clear(); - // load vertices - for (int ii = 0; ii < n_points; ii ++){ - Vertex v; - v.p.x = points[0 + 3*ii]; - v.p.y = points[1 + 3*ii]; - v.p.z = points[2 + 3*ii]; - vertices.push_back(v); - } - } - - // load triangles - void load_triangles(const int n_tri, int* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - t.v[0] = faces[0 + 3*ii]; - t.v[1] = faces[1 + 3*ii]; - t.v[2] = faces[2 + 3*ii]; - triangles.push_back(t); - } - } - - // load triangles - void load_triangles_int64(const int n_tri, int64_t* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - t.v[0] = faces[0 + 3*ii]; - t.v[1] = faces[1 + 3*ii]; - t.v[2] = faces[2 + 3*ii]; - triangles.push_back(t); - } - } - - // load triangles from vtk and deal with padding - int load_triangles_from_vtk(const int n_tri, int* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - if (faces[4*ii] != 3){ - return 1; - } - t.v[0] = faces[1 + 4*ii]; - t.v[1] = faces[2 + 4*ii]; - t.v[2] = faces[3 + 4*ii]; - triangles.push_back(t); - } - return 0; - } - - void load_arrays_int32(const int n_points, const int n_tri, const int n_coll, - float* points, int* faces, int* collapses){ - load_points(n_points, points); - load_triangles(n_tri, faces); - load_collapses(n_coll, collapses); - } - - void load_arrays_int64(const int n_points, const int n_tri, const int n_coll, - float* points, int64_t* faces, int* collapses){ - load_points(n_points, points); - load_triangles_int64(n_tri, faces); - load_collapses(n_coll, collapses); - } - - int n_points(){ - return vertices.size(); - } - - int n_triangles(){ - return triangles.size(); - } - - int n_collapses(){ - return collapses.size(); - } - - // load triangles - void load_triangles(const int n_tri, int64_t* faces){ - triangles.clear(); - for (int ii = 0; ii < n_tri; ii ++){ - Triangle t; - t.attr = 0; - t.material = -1; - t.v[0] = faces[0 + 3*ii]; - t.v[1] = faces[1 + 3*ii]; - t.v[2] = faces[2 + 3*ii]; - triangles.push_back(t); - } - } - - // populate a contiguous array with the points in the vertices vector - void get_points(float* points){ - - // load vertices - int n_points = vertices.size(); - for (int ii = 0; ii < n_points; ii ++){ - points[0 + 3*ii] = vertices[ii].p.x; - points[1 + 3*ii] = vertices[ii].p.y; - points[2 + 3*ii] = vertices[ii].p.z; - } - } - - // populate a contiguous array with the points in the vertices vector - void get_triangles(int* tri){ - - // load vertices - int n_tri = triangles.size(); - for (int ii = 0; ii < n_tri; ii ++){ - tri[0 + 3*ii] = triangles[ii].v[0]; - tri[1 + 3*ii] = triangles[ii].v[1]; - tri[2 + 3*ii] = triangles[ii].v[2]; - } - } - - void get_collapses(int* coll){ - - // load vertices - int n_collapse = collapses.size(); - for (int ii = 0; ii < n_collapse; ii ++){ - coll[0 + 2*ii] = collapses.at(ii).at(0); - coll[1 + 2*ii] = collapses.at(ii).at(1); - } - } - - // populate a contiguous array with the points in the vertices vector - int get_faces_int32(int32_t* tri){ - - // load vertices - int n_tri = triangles.size(); - int jj = 0; - for (int ii = 0; ii < n_tri; ii ++){ - if (!triangles[ii].deleted){ - tri[0 + 3*jj] = 3; - tri[1 + 3*jj] = triangles[ii].v[0]; - tri[2 + 3*jj] = triangles[ii].v[1]; - tri[3 + 3*jj] = triangles[ii].v[2]; - jj += 1; - } - } - return jj; - } - - // populate a contiguous array with the points in the vertices - // vector without the vtk padding - int get_faces_int32_no_padding(int32_t* tri){ - - // load vertices - int n_tri = triangles.size(); - int jj = 0; - for (int ii = 0; ii < n_tri; ii ++){ - if (!triangles[ii].deleted){ - tri[0 + 3*jj] = triangles[ii].v[0]; - tri[1 + 3*jj] = triangles[ii].v[1]; - tri[2 + 3*jj] = triangles[ii].v[2]; - jj += 1; - } - } - return jj; - } - - // populate a contiguous array with the points in the vertices vector - int get_faces_int64(int64_t* tri){ - - // load vertices - int n_tri = triangles.size(); - int jj = 0; - for (int ii = 0; ii < n_tri; ii ++){ - if (!triangles[ii].deleted){ - tri[0 + 4*jj] = 3; - tri[1 + 4*jj] = triangles[ii].v[0]; - tri[2 + 4*jj] = triangles[ii].v[1]; - tri[3 + 4*jj] = triangles[ii].v[2]; - jj += 1; - } - } - return jj; - } -} diff --git a/genesis/utils/terrain.py b/genesis/utils/terrain.py index 8938396fcd..f790c667e4 100644 --- a/genesis/utils/terrain.py +++ b/genesis/utils/terrain.py @@ -2,11 +2,11 @@ import math import pickle +import fast_simplification import numpy as np import trimesh import genesis as gs -from genesis.ext import fast_simplification from genesis.ext.isaacgym import terrain_utils as isaacgym_terrain_utils from genesis.options.morphs import Terrain @@ -386,13 +386,14 @@ def convert_heightfield_to_watertight_trimesh( # This is the mesh used for non-sdf purposes. # It's losslessly simplified from the full mesh, to save memory cost for storing verts and faces. - - v_simp, f_simp = fast_simplification.simplify( - sdf_mesh.vertices, - sdf_mesh.faces, - target_count=0, - lossless=True, - ) + # TODO: lossless option support is pending on fast_simplification package. + # v_simp, f_simp = fast_simplification.simplify( + # sdf_mesh.vertices, + # sdf_mesh.faces, + # target_count=0, + # lossless=True, + # ) + v_simp, f_simp = sdf_mesh.vertices, sdf_mesh.faces if uvs is not None: idx_map = np.empty(len(v_simp), dtype=np.int64) diff --git a/pyproject.toml b/pyproject.toml index 1cee065517..81f75a4893 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,6 +59,8 @@ dependencies = [ "OpenEXR", # Native batch renderer specifically designed for Genesis "gs-madrona>=0.0.2; platform_system == 'Linux' and (platform_machine == 'x86_64' or platform_machine == 'AMD64')", + # Used for mesh simplification + "fast_simplification", ] [project.optional-dependencies] diff --git a/setup.py b/setup.py deleted file mode 100644 index 9d8df66d06..0000000000 --- a/setup.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Setup for fast-simplification.""" - -import builtins -from io import open as io_open -import os -import sys -import numpy as np -from setuptools import Extension, setup -from setuptools.command.build_ext import build_ext as _build_ext - - -# Define macros for cython -macros = [] -if os.name == "nt": # windows - extra_compile_args = ["/openmp", "/O2", "/w", "/GS"] -elif os.name == "posix": # linux org mac os - if sys.platform == "linux": - extra_compile_args = ["-std=gnu++11", "-O3", "-w"] - else: # probably mac os - extra_compile_args = ["-std=c++11", "-O3", "-w"] -else: - raise OSError("Unsupported OS %s" % os.name) - -# Check if 64-bit -if sys.maxsize > 2**32: - macros.append(("IS64BITPLATFORM", None)) - - -# for: the cc1plus: warning: command line option '-Wstrict-prototypes' -class build_ext(_build_ext): - def finalize_options(self): - _build_ext.finalize_options(self) - # prevent numpy from thinking it is still in its setup process: - try: - del builtins.__NUMPY_SETUP__ - except AttributeError: - pass - import numpy - - self.include_dirs.append(numpy.get_include()) - - def build_extensions(self): - try: - self.compiler.compiler_so.remove("-Wstrict-prototypes") - except (AttributeError, ValueError): - pass - _build_ext.build_extensions(self) - - -setup( - # Build cython modules - cmdclass={"build_ext": build_ext}, - ext_modules=[ - Extension( - "genesis.ext.fast_simplification._simplify", - ["genesis/ext/fast_simplification/_simplify.pyx"], - include_dirs=[np.get_include()], - language="c++", - extra_compile_args=extra_compile_args, - define_macros=macros, - ), - Extension( - "genesis.ext.fast_simplification._replay", - ["genesis/ext/fast_simplification/_replay.pyx"], - include_dirs=[np.get_include()], - language="c++", - extra_compile_args=extra_compile_args, - define_macros=macros, - ), - ], -)