From f29d4553191d96ea10ec39cefbb36190e7e0b4ce Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Wed, 29 Aug 2012 18:23:34 +0200 Subject: [PATCH] Refactor the AMF parser so that it popolates a Model object directly. Also read constellations --- lib/Slic3r/Format/AMF.pm | 24 +++---------- lib/Slic3r/Format/AMF/Parser.pm | 63 ++++++++++++++++++++++++++------- lib/Slic3r/Model.pm | 2 +- utils/stl-to-amf.pl | 5 +-- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index ce3fbb474..552f1fdbd 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -12,28 +12,12 @@ sub read_file { open my $fh, '<', $file or die "Failed to open $file\n"; - my $vertices = []; - my $materials = {}; - my $meshes_by_material = {}; + my $model = Slic3r::Model->new; XML::SAX::PurePerl - ->new(Handler => Slic3r::Format::AMF::Parser->new( - _vertices => $vertices, - _materials => $materials, - _meshes_by_material => $meshes_by_material, - )) + ->new(Handler => Slic3r::Format::AMF::Parser->new(_model => $model)) ->parse_file($fh); - close $fh; - my $model = Slic3r::Model->new; - my $object = $model->add_object(vertices => $vertices); - foreach my $material (keys %$meshes_by_material) { - push @{$model->materials}, $material; # TODO: we should not add duplicate materials - $object->add_volume( - material_id => $#{$model->materials}, - facets => $meshes_by_material->{$material}, - ); - } return $model; } @@ -48,8 +32,8 @@ sub write_file { printf $fh qq{<?xml version="1.0" encoding="UTF-8"?>\n}; printf $fh qq{<amf unit="millimeter">\n}; printf $fh qq{ <metadata type="cad">Slic3r %s</metadata>\n}, $Slic3r::VERSION; - for my $material_id (0 .. $#{ $model->materials }) { - my $material = $model->materials->[$material_id]; + for my $material_id (sort keys %{ $model->materials }) { + my $material = $model->materials->{$material_id}; printf $fh qq{ <material id="%d">\n}, $material_id; for (keys %$material) { printf $fh qq{ <metadata type=\"%s\">%s</metadata>\n}, $_, $material->{$_}; diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index 7c100b777..93b35765e 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -11,6 +11,8 @@ my %xyz_index = (x => 0, y => 1, z => 2); #= sub new { my $self = shift->SUPER::new(@_); $self->{_tree} = []; + $self->{_objects_map} = {}; # this hash maps AMF object IDs to object indexes in $model->objects + $self->{_instances} = {}; # apply these lazily to make sure all objects have been parsed $self; } @@ -18,23 +20,35 @@ sub start_element { my $self = shift; my $data = shift; - if ($data->{LocalName} eq 'vertex') { + if ($data->{LocalName} eq 'object') { + $self->{_object} = $self->{_model}->add_object; + $self->{_objects_map}{ $self->_get_attribute($data, 'id') } = $#{ $self->{_model}->objects }; + } elsif ($data->{LocalName} eq 'vertex') { $self->{_vertex} = ["", "", ""]; } elsif ($self->{_vertex} && $data->{LocalName} =~ /^[xyz]$/ && $self->{_tree}[-1] eq 'coordinates') { $self->{_coordinate} = $data->{LocalName}; } elsif ($data->{LocalName} eq 'volume') { - $self->{_volume_materialid} = $self->_get_attribute($data, 'materialid') || '_'; - $self->{_volume} = []; + $self->{_volume} = $self->{_object}->add_volume( + material_id => $self->_get_attribute($data, 'materialid') || undef, + ); } elsif ($data->{LocalName} eq 'triangle') { $self->{_triangle} = ["", "", ""]; } elsif ($self->{_triangle} && $data->{LocalName} =~ /^v([123])$/ && $self->{_tree}[-1] eq 'triangle') { $self->{_vertex_idx} = $1-1; } elsif ($data->{LocalName} eq 'material') { - $self->{_material_id} = $self->_get_attribute($data, 'id') || '_'; - $self->{_material} = {}; + my $material_id = $self->_get_attribute($data, 'id') || '_'; + $self->{_material} = $self->{_model}->materials->{ $material_id } = {}; } elsif ($data->{LocalName} eq 'metadata' && $self->{_tree}[-1] eq 'material') { $self->{_material_metadata_type} = $self->_get_attribute($data, 'type'); $self->{_material}{ $self->{_material_metadata_type} } = ""; + } elsif ($data->{LocalName} eq 'constellation') { + $self->{_constellation} = 1; # we merge all constellations as we don't support more than one + } elsif ($data->{LocalName} eq 'instance' && $self->{_constellation}) { + my $object_id = $self->_get_attribute($data, 'objectid'); + $self->{_instances}{$object_id} ||= []; + push @{ $self->{_instances}{$object_id} }, $self->{_instance} = {}; + } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) { + $self->{_instance_property} = $data->{LocalName}; } push @{$self->{_tree}}, $data->{LocalName}; @@ -50,6 +64,8 @@ sub characters { $self->{_triangle}[ $self->{_vertex_idx} ] .= $data->{Data}; } elsif ($self->{_material_metadata_type}) { $self->{_material}{ $self->{_material_metadata_type} } .= $data->{Data}; + } elsif ($self->{_instance_property}) { + $self->{_instance}{ $self->{_instance_property} } .= $data->{Data}; } } @@ -59,26 +75,49 @@ sub end_element { pop @{$self->{_tree}}; - if ($data->{LocalName} eq 'vertex') { - push @{$self->{_vertices}}, $self->{_vertex}; + if ($data->{LocalName} eq 'object') { + $self->{_object} = undef; + } elsif ($data->{LocalName} eq 'vertex') { + push @{$self->{_object}->vertices}, $self->{_vertex}; $self->{_vertex} = undef; } elsif ($self->{_coordinate} && $data->{LocalName} =~ /^[xyz]$/) { $self->{_coordinate} = undef; } elsif ($data->{LocalName} eq 'volume') { - $self->{_meshes_by_material}{ $self->{_volume_materialid} } ||= []; - push @{ $self->{_meshes_by_material}{ $self->{_volume_materialid} } }, @{$self->{_volume}}; $self->{_volume} = undef; } elsif ($data->{LocalName} eq 'triangle') { - push @{$self->{_volume}}, $self->{_triangle}; + push @{$self->{_volume}->facets}, $self->{_triangle}; $self->{_triangle} = undef; } elsif (defined $self->{_vertex_idx} && $data->{LocalName} =~ /^v[123]$/) { $self->{_vertex_idx} = undef; } elsif ($data->{LocalName} eq 'material') { - $self->{_materials}{ $self->{_material_id} } = $self->{_material}; - $self->{_material_id} = undef; $self->{_material} = undef; } elsif ($data->{LocalName} eq 'metadata' && $self->{_material}) { $self->{_material_metadata_type} = undef; + } elsif ($data->{LocalName} eq 'constellation') { + $self->{_constellation} = undef; + } elsif ($data->{LocalName} eq 'instance') { + $self->{_instance} = undef; + } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) { + $self->{_instance_property} = undef; + } +} + +sub end_document { + my $self = shift; + + foreach my $object_id (keys %{ $self->{_instances} }) { + my $new_object_id = $self->{_objects_map}{$object_id}; + if (!$new_object_id) { + warn "Undefined object $object_id referenced in constellation\n"; + next; + } + + foreach my $instance (@{ $self->{_instances}{$object_id} }) { + $self->{_model}->objects->[$new_object_id]->add_instance( + rotation => $instance->{rz} || 0, + offset => [ $instance->{deltax} || 0, $instance->{deltay} ], + ); + } } } diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 48979da31..b648d4b34 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -3,7 +3,7 @@ use Moo; use Slic3r::Geometry qw(X Y Z); -has 'materials' => (is => 'ro', default => sub { [] }); +has 'materials' => (is => 'ro', default => sub { {} }); has 'objects' => (is => 'ro', default => sub { [] }); sub add_object { diff --git a/utils/stl-to-amf.pl b/utils/stl-to-amf.pl index 90aacc535..78e5989b4 100755 --- a/utils/stl-to-amf.pl +++ b/utils/stl-to-amf.pl @@ -43,9 +43,10 @@ my %opt = (); $f; } @{ $model->objects->[0]->volumes->[0]->facets }; - push @{$new_model->materials}, { Name => basename($ARGV[$m]) }; + my $material_id = scalar keys %{$new_model->materials}; + $new_model->materials->{$material_id} = { Name => basename($ARGV[$m]) }; $new_object->add_volume( - material_id => $#{$new_model->materials}, + material_id => $material_id, facets => [@new_facets], ); }