diff options
author | Vojtech Bubnik <bubnikv@gmail.com> | 2021-09-20 18:12:22 +0300 |
---|---|---|
committer | Vojtech Bubnik <bubnikv@gmail.com> | 2021-09-20 18:12:22 +0300 |
commit | 8a2a9dba2f8f94da0106b60df613cd04ada4d595 (patch) | |
tree | 6997d6e42ad455cfcca787a514245a4fbfe0eee7 /xs | |
parent | f484953a5a1fecd878242ca8d2f5175151b81678 (diff) |
Eradicated admesh from TriangleMesh:
TriangleMesh newly only holds indexed_triangle_set and
TriangleMeshStats. TriangleMeshStats contains an excerpt of stl_stats.
TriangleMeshStats are updated when initializing with indexed_triangle_set.
Admesh triangle mesh fixing is newly only used when loading an STL.
AMF / 3MF / OBJ file formats are already indexed triangle sets, thus
they are no more converted to admesh stl_file format, nor fixed
through admesh repair machinery. When importing AMF / 3MF / OBJ files,
volume is calculated and if negative, all faces are flipped. Also
a bounding box and number of open edges is calculated.
Implemented its_number_of_patches(), its_num_open_edges()
Optimized its_split(), its_is_splittable() using a visitor pattern.
Reworked QHull integration into TriangleMesh:
1) Face normals were not right.
2) Indexed triangle set is newly emitted instead of duplicating
vertices for each face.
Fixed cut_mesh(): Orient the triangulated faces correctly.
Diffstat (limited to 'xs')
-rw-r--r-- | xs/t/01_trianglemesh.t | 110 | ||||
-rw-r--r-- | xs/xsp/Model.xsp | 10 | ||||
-rw-r--r-- | xs/xsp/TriangleMesh.xsp | 138 | ||||
-rw-r--r-- | xs/xsp/my.map | 1 | ||||
-rw-r--r-- | xs/xsp/typemap.xspt | 1 |
5 files changed, 40 insertions, 220 deletions
diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index 4013a1f83..453cc9218 100644 --- a/xs/t/01_trianglemesh.t +++ b/xs/t/01_trianglemesh.t @@ -4,10 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 49; - -is Slic3r::TriangleMesh::hello_world(), 'Hello world!', - 'hello world'; +use Test::More tests => 5; my $cube = { vertices => [ [20,20,0], [20,0,0], [0,0,0], [0,20,0], [20,20,20], [0,20,20], [0,0,20], [20,0,20] ], @@ -17,12 +14,10 @@ my $cube = { { my $m = Slic3r::TriangleMesh->new; $m->ReadFromPerl($cube->{vertices}, $cube->{facets}); - $m->repair; my ($vertices, $facets) = ($m->vertices, $m->facets); is_deeply $vertices, $cube->{vertices}, 'vertices arrayref roundtrip'; is_deeply $facets, $cube->{facets}, 'facets arrayref roundtrip'; - is scalar(@{$m->normals}), scalar(@$facets), 'normals returns the right number of items'; { my $m2 = $m->clone; @@ -34,109 +29,6 @@ my $cube = { { my $stats = $m->stats; is $stats->{number_of_facets}, scalar(@{ $cube->{facets} }), 'stats.number_of_facets'; - ok abs($stats->{volume} - 20*20*20) < 1E-2, 'stats.volume'; - } - - $m->scale(2); - ok abs($m->stats->{volume} - 40*40*40) < 1E-2, 'scale'; - - $m->scale_xyz(Slic3r::Pointf3->new(2,1,1)); - ok abs($m->stats->{volume} - 2*40*40*40) < 1E-2, 'scale_xyz'; - - $m->translate(5,10,0); - is_deeply $m->vertices->[0], [85,50,0], 'translate'; - - $m->align_to_origin; - is_deeply $m->vertices->[2], [0,0,0], 'align_to_origin'; - - is_deeply $m->size, [80,40,40], 'size'; - - $m->scale_xyz(Slic3r::Pointf3->new(0.5,1,1)); - $m->rotate(45, Slic3r::Point->new(20,20)); - ok abs($m->size->[0] - sqrt(2)*40) < 1E-4, 'rotate'; - - { - my $meshes = $m->split; - is scalar(@$meshes), 1, 'split'; - isa_ok $meshes->[0], 'Slic3r::TriangleMesh', 'split'; - is_deeply $m->bb3, $meshes->[0]->bb3, 'split populates stats'; - } - - my $m2 = Slic3r::TriangleMesh->new; - $m2->ReadFromPerl($cube->{vertices}, $cube->{facets}); - $m2->repair; - $m->merge($m2); - $m->repair; - is $m->stats->{number_of_facets}, 2 * $m2->stats->{number_of_facets}, 'merge'; - - { - my $meshes = $m->split; - is scalar(@$meshes), 2, 'split'; - } -} - -{ - my $m = Slic3r::TriangleMesh->new; - $m->ReadFromPerl($cube->{vertices}, $cube->{facets}); - $m->repair; - # The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be - # open intervals at the bottom end, closed at the top end. - my @z = (0.0001,2,4,8,6,8,10,12,14,16,18,20); - my $result = $m->slice(\@z); - my $SCALING_FACTOR = 0.000001; - for my $i (0..$#z) { - is scalar(@{$result->[$i]}), 1, "number of returned polygons per layer (z = " . $z[$i] . ")"; - is $result->[$i][0]->area, 20*20/($SCALING_FACTOR**2), 'size of returned polygon'; - } -} - -{ - my $m = Slic3r::TriangleMesh->new; - $m->ReadFromPerl( - [ [0,0,0],[0,0,20],[0,5,0],[0,5,20],[50,0,0],[50,0,20],[15,5,0],[35,5,0],[15,20,0],[50,5,0],[35,20,0],[15,5,10],[50,5,20],[35,5,10],[35,20,10],[15,20,10] ], - [ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ], - ); - $m->repair; - { - # at Z = 10 we have a top horizontal surface - my $slices = $m->slice([ 5, 10 ]); - is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a top tangent plane includes its area'; - } - $m->mirror_z; - { - # this second test also checks that performing a second slice on a mesh after - # a transformation works properly (shared_vertices is correctly invalidated); - # at Z = -10 we have a bottom horizontal surface - # (The slice at zero height does not belong to the mesh, the slicing considers the vertical structures to be - # open intervals at the bottom end, closed at the top end, so the Z = -10 is shifted a bit up to get a valid slice). - my $slices = $m->slice([ -5, -10+0.00001 ]); - is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area'; - } -} - -{ - my $m = Slic3r::TriangleMesh->new; - $m->ReadFromPerl($cube->{vertices}, $cube->{facets}); - $m->repair; - { - my $upper = Slic3r::TriangleMesh->new; - my $lower = Slic3r::TriangleMesh->new; - $m->cut(0, $upper, $lower); - $upper->repair; $lower->repair; - is $upper->facets_count, 12, 'upper mesh has all facets except those belonging to the slicing plane'; - is $lower->facets_count, 0, 'lower mesh has no facets'; - } - { - my $upper = Slic3r::TriangleMesh->new; - my $lower = Slic3r::TriangleMesh->new; - $m->cut(10, $upper, $lower); - #$upper->repair; $lower->repair; - # we expect: - # 2 facets on external horizontal surfaces - # 3 facets on each side = 12 facets - # 6 facets on the triangulated side (8 vertices) - is $upper->facets_count, 2+12+6, 'upper mesh has the expected number of facets'; - is $lower->facets_count, 2+12+6, 'lower mesh has the expected number of facets'; } } diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 35fbb48ee..34795681e 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -88,8 +88,6 @@ bool looks_like_multipart_object() const; void convert_multipart_object(unsigned int max_extruders); - void print_info() const; - bool store_stl(char *path, bool binary) %code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %}; @@ -212,7 +210,6 @@ ModelMaterial::attributes() %code%{ THIS->origin_translation = *point; %}; void ensure_on_bed(); - bool needed_repair() const; int materials_count() const; int facets_count(); void center_around_origin(); @@ -223,13 +220,6 @@ ModelMaterial::attributes() %code{% THIS->rotate(angle, *axis); %}; void mirror(Axis axis); - ModelObjectPtrs* split_object() - %code%{ - RETVAL = new ModelObjectPtrs(); // leak? - THIS->split(RETVAL); - %}; - - void print_info() const; }; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 2b07c78ee..105fe0f05 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -11,14 +11,11 @@ ~TriangleMesh(); Clone<TriangleMesh> clone() %code{% RETVAL = THIS; %}; - void ReadSTLFile(char* input_file); void write_ascii(char* output_file); void write_binary(char* output_file); - void repair(); - void WriteOBJFile(char* output_file); void scale(float factor); void scale_xyz(Vec3d* versor) - %code{% THIS->scale(*versor); %}; + %code{% THIS->scale(versor->cast<float>()); %}; void translate(float x, float y, float z); void rotate_x(float angle); void rotate_y(float angle); @@ -28,16 +25,13 @@ void mirror_z(); void align_to_origin(); void rotate(double angle, Point* center); - TriangleMeshPtrs split(); void merge(TriangleMesh* mesh) %code{% THIS->merge(*mesh); %}; - ExPolygons horizontal_projection(); Clone<Polygon> convex_hull(); Clone<BoundingBoxf3> bounding_box(); Clone<Vec3d> center() %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); - void reset_repair_stats(); %{ @@ -46,51 +40,40 @@ TriangleMesh::ReadFromPerl(vertices, facets) SV* vertices SV* facets CODE: - stl_file &stl = THIS->stl; - stl.stats.type = inmemory; - - // count facets and allocate memory - AV* facets_av = (AV*)SvRV(facets); - stl.stats.number_of_facets = av_len(facets_av)+1; - stl.stats.original_num_facets = stl.stats.number_of_facets; - stl_allocate(&stl); - - // read geometry - AV* vertices_av = (AV*)SvRV(vertices); - for (int i = 0; i < stl.stats.number_of_facets; i++) { - AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0)); - stl_facet facet; - facet.normal(0) = 0; - facet.normal(1) = 0; - facet.normal(2) = 0; - for (unsigned int v = 0; v <= 2; v++) { - AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, SvIV(*av_fetch(facet_av, v, 0)), 0)); - facet.vertex[v](0) = SvNV(*av_fetch(vertex_av, 0, 0)); - facet.vertex[v](1) = SvNV(*av_fetch(vertex_av, 1, 0)); - facet.vertex[v](2) = SvNV(*av_fetch(vertex_av, 2, 0)); + std::vector<Slic3r::Vec3f> out_vertices; + { + AV* vertices_av = (AV*)SvRV(vertices); + int number_of_vertices = av_len(vertices_av) + 1; + out_vertices.reserve(number_of_vertices); + for (int i = 0; i < number_of_vertices; ++ i) { + AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, i, 0)); + out_vertices.push_back(Slic3r::Vec3f(SvNV(*av_fetch(vertex_av, 0, 0)), SvNV(*av_fetch(vertex_av, 1, 0)), SvNV(*av_fetch(vertex_av, 2, 0)))); + } + } + std::vector<Slic3r::Vec3i> out_indices; + { + AV* facets_av = (AV*)SvRV(facets); + int number_of_facets = av_len(facets_av) + 1; + out_indices.reserve(number_of_facets); + for (int i = 0; i < number_of_facets; ++ i) { + AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0)); + out_indices.push_back(Slic3r::Vec3i(SvIV(*av_fetch(facet_av, 0, 0)), SvIV(*av_fetch(facet_av, 1, 0)), SvIV(*av_fetch(facet_av, 2, 0)))); } - facet.extra[0] = 0; - facet.extra[1] = 0; - - stl.facet_start[i] = facet; } - - stl_get_size(&stl); + *THIS = TriangleMesh(std::move(out_vertices), std::move(out_indices)); SV* TriangleMesh::stats() CODE: HV* hv = newHV(); - (void)hv_stores( hv, "number_of_facets", newSViv(THIS->stl.stats.number_of_facets) ); - (void)hv_stores( hv, "number_of_parts", newSViv(THIS->stl.stats.number_of_parts) ); - (void)hv_stores( hv, "volume", newSVnv(THIS->stl.stats.volume) ); - (void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stl.stats.degenerate_facets) ); - (void)hv_stores( hv, "edges_fixed", newSViv(THIS->stl.stats.edges_fixed) ); - (void)hv_stores( hv, "facets_removed", newSViv(THIS->stl.stats.facets_removed) ); - (void)hv_stores( hv, "facets_added", newSViv(THIS->stl.stats.facets_added) ); - (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stl.stats.facets_reversed) ); - (void)hv_stores( hv, "backwards_edges", newSViv(THIS->stl.stats.backwards_edges) ); - (void)hv_stores( hv, "normals_fixed", newSViv(THIS->stl.stats.normals_fixed) ); + (void)hv_stores( hv, "number_of_facets", newSViv(THIS->facets_count()) ); + (void)hv_stores( hv, "number_of_parts", newSViv(THIS->stats().number_of_parts) ); + (void)hv_stores( hv, "volume", newSVnv(THIS->stats().volume) ); + (void)hv_stores( hv, "degenerate_facets", newSViv(THIS->stats().degenerate_facets) ); + (void)hv_stores( hv, "edges_fixed", newSViv(THIS->stats().edges_fixed) ); + (void)hv_stores( hv, "facets_removed", newSViv(THIS->stats().facets_removed) ); + (void)hv_stores( hv, "facets_reversed", newSViv(THIS->stats().facets_reversed) ); + (void)hv_stores( hv, "backwards_edges", newSViv(THIS->stats().backwards_edges) ); RETVAL = (SV*)newRV_noinc((SV*)hv); OUTPUT: RETVAL @@ -98,9 +81,6 @@ TriangleMesh::stats() SV* TriangleMesh::vertices() CODE: - if (!THIS->repaired) CONFESS("vertices() requires repair()"); - THIS->require_shared_vertices(); - // vertices AV* vertices = newAV(); av_extend(vertices, THIS->its.vertices.size()); @@ -120,13 +100,10 @@ TriangleMesh::vertices() SV* TriangleMesh::facets() CODE: - if (!THIS->repaired) CONFESS("facets() requires repair()"); - THIS->require_shared_vertices(); - // facets AV* facets = newAV(); - av_extend(facets, THIS->stl.stats.number_of_facets); - for (int i = 0; i < THIS->stl.stats.number_of_facets; i++) { + av_extend(facets, THIS->facets_count()); + for (int i = 0; i < THIS->facets_count(); i++) { AV* facet = newAV(); av_store(facets, i, newRV_noinc((SV*)facet)); av_extend(facet, 2); @@ -140,34 +117,13 @@ TriangleMesh::facets() RETVAL SV* -TriangleMesh::normals() - CODE: - if (!THIS->repaired) CONFESS("normals() requires repair()"); - - // normals - AV* normals = newAV(); - av_extend(normals, THIS->stl.stats.number_of_facets); - for (int i = 0; i < THIS->stl.stats.number_of_facets; i++) { - AV* facet = newAV(); - av_store(normals, i, newRV_noinc((SV*)facet)); - av_extend(facet, 2); - av_store(facet, 0, newSVnv(THIS->stl.facet_start[i].normal(0))); - av_store(facet, 1, newSVnv(THIS->stl.facet_start[i].normal(1))); - av_store(facet, 2, newSVnv(THIS->stl.facet_start[i].normal(2))); - } - - RETVAL = newRV_noinc((SV*)normals); - OUTPUT: - RETVAL - -SV* TriangleMesh::size() CODE: AV* size = newAV(); av_extend(size, 2); - av_store(size, 0, newSVnv(THIS->stl.stats.size(0))); - av_store(size, 1, newSVnv(THIS->stl.stats.size(1))); - av_store(size, 2, newSVnv(THIS->stl.stats.size(2))); + av_store(size, 0, newSVnv(THIS->stats().size(0))); + av_store(size, 1, newSVnv(THIS->stats().size(1))); + av_store(size, 2, newSVnv(THIS->stats().size(2))); RETVAL = newRV_noinc((SV*)size); OUTPUT: RETVAL @@ -176,8 +132,6 @@ SV* TriangleMesh::slice(z) std::vector<double> z CODE: - THIS->require_shared_vertices(); // TriangleMeshSlicer needs this - // convert doubles to floats std::vector<float> z_f = cast<float>(z); @@ -206,7 +160,6 @@ TriangleMesh::cut(z, upper_mesh, lower_mesh) TriangleMesh* upper_mesh; TriangleMesh* lower_mesh; CODE: - THIS->require_shared_vertices(); // TriangleMeshSlicer needs this indexed_triangle_set upper, lower; cut_mesh(THIS->its, z, upper_mesh ? &upper : nullptr, lower_mesh ? &lower : nullptr); if (upper_mesh) @@ -217,12 +170,12 @@ TriangleMesh::cut(z, upper_mesh, lower_mesh) std::vector<double> TriangleMesh::bb3() CODE: - RETVAL.push_back(THIS->stl.stats.min(0)); - RETVAL.push_back(THIS->stl.stats.min(1)); - RETVAL.push_back(THIS->stl.stats.max(0)); - RETVAL.push_back(THIS->stl.stats.max(1)); - RETVAL.push_back(THIS->stl.stats.min(2)); - RETVAL.push_back(THIS->stl.stats.max(2)); + RETVAL.push_back(THIS->stats().min(0)); + RETVAL.push_back(THIS->stats().min(1)); + RETVAL.push_back(THIS->stats().max(0)); + RETVAL.push_back(THIS->stats().max(1)); + RETVAL.push_back(THIS->stats().min(2)); + RETVAL.push_back(THIS->stats().max(2)); OUTPUT: RETVAL @@ -250,16 +203,3 @@ sphere(double rho) %} }; - -%package{Slic3r::TriangleMesh}; - -%{ -PROTOTYPES: DISABLE - -std::string -hello_world() - CODE: - RETVAL = "Hello world!"; - OUTPUT: - RETVAL -%} diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 54e686ae3..ca26750dc 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -239,7 +239,6 @@ SupportLayerPtrs* T_PTR_ARRAYREF_PTR # we return these types whenever we want the items to be returned # by reference and not marked ::Ref because they're newly allocated # and not referenced by any Perl object -TriangleMeshPtrs T_PTR_ARRAYREF INPUT diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 2d364628e..f9e61c6a0 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -163,7 +163,6 @@ %typemap{Surfaces}; %typemap{Polygons*}; %typemap{TriangleMesh*}; -%typemap{TriangleMeshPtrs}; %typemap{Model*}; %typemap{Ref<Model>}{simple}; %typemap{Clone<Model>}{simple}; |