diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 5407a8689..c028c123b 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1,4 +1,4 @@ -package Slic3r::GUI::3DScene; +package Slic3r::GUI::3DScene::Base; use strict; use warnings; @@ -7,7 +7,7 @@ use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); use Math::Trig qw(asin); -use List::Util qw(reduce min max first); +use List::Util qw(reduce min max); use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale scaled_epsilon); use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl); use Wx::GLCanvas qw(:all); @@ -41,12 +41,12 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init _zoom ) ); -use constant TRACKBALLSIZE => 0.8; +use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; use constant GROUND_Z => -0.02; +use constant DEFAULT_COLOR => [1,1,0]; use constant SELECTED_COLOR => [0,1,0,1]; use constant HOVER_COLOR => [0.4,0.9,0,1]; -use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; # make OpenGL::Array thread-safe { @@ -164,8 +164,13 @@ sub mouse_event { # get volume being dragged my $volume = $self->volumes->[$self->_drag_volume_idx]; - # get all volumes belonging to the same group but only having the same instance_idx - my @volumes = grep $_->group_id == $volume->group_id && $_->instance_idx == $volume->instance_idx, @{$self->volumes}; + # get all volumes belonging to the same group, if any + my @volumes; + if ($volume->drag_group_id == -1) { + @volumes = ($volume); + } else { + @volumes = grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes}; + } # apply new temporary volume origin and ignore Z $_->origin->translate($vector->x, $vector->y, 0) for @volumes; #,, @@ -349,57 +354,6 @@ sub set_bed_shape { $self->origin(Slic3r::Pointf->new(0,0)); } -sub load_object { - my ($self, $object, $all_instances) = @_; - - # color mesh(es) by material - my @materials = (); - - # sort volumes: non-modifiers first - my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } @{$object->volumes}; - my @volumes_idx = (); - my $group_id = $#{$self->volumes} + 1; - foreach my $volume (@volumes) { - my @instance_idxs = $all_instances ? (0..$#{$object->instances}) : (0); - foreach my $instance_idx (@instance_idxs) { - my $instance = $object->instances->[$instance_idx]; - my $mesh = $volume->mesh->clone; - $instance->transform_mesh($mesh); - - my $material_id = $volume->material_id // '_'; - my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials; - if (!defined $color_idx) { - push @materials, $material_id; - $color_idx = $#materials; - } - - my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; - push @$color, $volume->modifier ? 0.5 : 1; - push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( - bounding_box => $mesh->bounding_box, - group_id => $group_id, - instance_idx => $instance_idx, - color => $color, - ); - $v->mesh($mesh) if $self->enable_cutting; - push @volumes_idx, $#{$self->volumes}; - - { - my $vertices = $mesh->vertices; - my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets}; - $v->verts(OpenGL::Array->new_list(GL_FLOAT, @verts)); - } - - { - my @norms = map { @$_, @$_, @$_ } @{$mesh->normals}; - $v->norms(OpenGL::Array->new_list(GL_FLOAT, @norms)); - } - } - } - - return @volumes_idx; -} - sub deselect_volumes { my ($self) = @_; $_->selected(0) for @{$self->volumes}; @@ -853,7 +807,7 @@ sub draw_volumes { glTranslatef(map unscale($_), @$copy, 0); foreach my $slice (@{$layer->slices}) { - glColor3f(@{COLORS->[0]}); + glColor3f(@{&DEFAULT_COLOR}); gluTessBeginPolygon($tess); glNormal3f(0,0,1); foreach my $polygon (@$slice) { @@ -875,7 +829,7 @@ sub draw_volumes { } glLineWidth(0); - glColor3f(@{COLORS->[0]}); + glColor3f(@{&DEFAULT_COLOR}); glBegin(GL_QUADS); # We'll use this for the middle normal when using 4 quads: #my $xy_normal = $line->normal; @@ -943,16 +897,16 @@ sub draw_volumes { package Slic3r::GUI::3DScene::Volume; use Moo; -has 'mesh' => (is => 'rw', required => 0); # only required for cut contours -has 'bounding_box' => (is => 'ro', required => 1); -has 'color' => (is => 'ro', required => 1); -has 'group_id' => (is => 'ro', required => 1); -has 'instance_idx' => (is => 'ro', default => sub { 0 }); -has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); -has 'verts' => (is => 'rw'); -has 'norms' => (is => 'rw'); -has 'selected' => (is => 'rw', default => sub { 0 }); -has 'hover' => (is => 'rw', default => sub { 0 }); +has 'mesh' => (is => 'rw', required => 0); # only required for cut contours +has 'bounding_box' => (is => 'ro', required => 1); +has 'color' => (is => 'ro', required => 1); +has 'hover_group_id' => (is => 'ro', default => sub { -1 }); +has 'drag_group_id' => (is => 'ro', default => sub { -1 }); +has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); +has 'verts' => (is => 'rw'); +has 'norms' => (is => 'rw'); +has 'selected' => (is => 'rw', default => sub { 0 }); +has 'hover' => (is => 'rw', default => sub { 0 }); sub transformed_bounding_box { my ($self) = @_; @@ -962,4 +916,100 @@ sub transformed_bounding_box { return $bb; } +package Slic3r::GUI::3DScene; +use base qw(Slic3r::GUI::3DScene::Base); + +use OpenGL qw(:glconstants); +use List::Util qw(first); + +use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; + +__PACKAGE__->mk_accessors(qw( + volumes_by_object + _objects_by_volumes +)); + +sub new { + my $class = shift; + + my $self = $class->SUPER::new(@_); + $self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ] + $self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ] + + return $self; +} + +sub load_object { + my ($self, $model, $obj_idx, $instance_idxs) = @_; + + my $model_object; + if ($model->isa('Slic3r::Model::Object')) { + $model_object = $model; + $model = $model_object->model; + $obj_idx = 0; + } else { + $model_object = $model->get_object($obj_idx); + } + + $instance_idxs ||= [0..$#{$model_object->instances}]; + + # color mesh(es) by material + my @materials = (); + + # sort volumes: non-modifiers first + my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } + @{$model_object->volumes}; + my @volumes_idx = (); + my $group_id = $#{$self->volumes} + 1; + foreach my $volume (@volumes) { + foreach my $instance_idx (@$instance_idxs) { + my $instance = $model_object->instances->[$instance_idx]; + my $mesh = $volume->mesh->clone; + $instance->transform_mesh($mesh); + + my $material_id = $volume->material_id // '_'; + my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials; + if (!defined $color_idx) { + push @materials, $material_id; + $color_idx = $#materials; + } + + my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; + push @$color, $volume->modifier ? 0.5 : 1; + push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( + bounding_box => $mesh->bounding_box, + drag_group_id => $group_id * 1000 + $instance_idx, + color => $color, + ); + $v->mesh($mesh) if $self->enable_cutting; + push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; + $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $instance_idx ]; + + { + my $vertices = $mesh->vertices; + my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets}; + $v->verts(OpenGL::Array->new_list(GL_FLOAT, @verts)); + } + + { + my @norms = map { @$_, @$_, @$_ } @{$mesh->normals}; + $v->norms(OpenGL::Array->new_list(GL_FLOAT, @norms)); + } + } + } + + $self->volumes_by_object->{$obj_idx} = [@volumes_idx]; + return @volumes_idx; +} + +sub object_idx { + my ($self, $volume_idx) = @_; + return $self->_objects_by_volumes->{$volume_idx}[0]; +} + +sub instance_idx { + my ($self, $volume_idx) = @_; + return $self->_objects_by_volumes->{$volume_idx}[1]; +} + 1; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 0e1fca4e0..6e4c305cf 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -10,8 +10,6 @@ use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); use Wx::Event qw(); use base qw(Slic3r::GUI::3DScene Class::Accessor); -__PACKAGE__->mk_accessors(qw(_volumes _volumes_inv)); - sub new { my $class = shift; my ($parent, $objects, $model, $config) = @_; @@ -31,8 +29,8 @@ sub new { my $obj_idx = undef; if ($volume_idx != -1) { - $obj_idx = $self->_volumes_inv->{$volume_idx}; - $self->volumes->[$_]->selected(1) for @{$self->_volumes->{$obj_idx}}; + $obj_idx = $self->object_idx($volume_idx); + $self->volumes->[$_]->selected(1) for @{$self->volumes_by_object->{$obj_idx}}; $self->Refresh; } $self->{on_select_object}->($obj_idx) @@ -41,22 +39,23 @@ sub new { $self->on_hover(sub { my ($volume_idx) = @_; - my $obj_idx = $self->_volumes_inv->{$volume_idx}; - $self->volumes->[$_]->hover(1) for @{$self->_volumes->{$obj_idx}}; + my $obj_idx = $self->object_idx($volume_idx); + $self->volumes->[$_]->hover(1) for @{$self->volumes_by_object->{$obj_idx}}; }); $self->on_move(sub { my ($volume_idx) = @_; my $volume = $self->volumes->[$volume_idx]; - my $obj_idx = $self->_volumes_inv->{$volume_idx}; + my $obj_idx = $self->object_idx($volume_idx); + my $instance_idx = $self->instance_idx($volume_idx); my $model_object = $self->{model}->get_object($obj_idx); $model_object - ->instances->[$volume->instance_idx] + ->instances->[$instance_idx] ->offset ->translate($volume->origin->x, $volume->origin->y); #)) $model_object->invalidate_bounding_box; - $self->{on_instance_moved}->($obj_idx, $volume->instance_idx) + $self->{on_instance_moved}->($obj_idx, $instance_idx) if $self->{on_instance_moved}; }); @@ -86,19 +85,11 @@ sub set_on_instance_moved { sub update { my ($self) = @_; - $self->_volumes({}); # obj_idx => [ volume_idx, volume_idx ] - $self->_volumes_inv({}); # volume_idx => obj_idx $self->reset_objects; - $self->update_bed_size; foreach my $obj_idx (0..$#{$self->{model}->objects}) { - my $model_object = $self->{model}->get_object($obj_idx); - my @volume_idxs = $self->load_object($model_object, 1); - - # store mapping between canvas volumes and model objects - $self->_volumes->{$obj_idx} = [ @volume_idxs ]; - $self->_volumes_inv->{$_} = $obj_idx for @volume_idxs; + my @volume_idxs = $self->load_object($self->{model}, $obj_idx); if ($self->{objects}[$obj_idx]->selected) { $self->select_volume($_) for @volume_idxs; diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index 67fb15a12..687a56224 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -31,11 +31,12 @@ my %opt = (); # make sure all objects have at least one defined instance $model->add_default_instances; + $_->center_around_origin for @{$model->objects}; # and align to Z = 0 my $app = Slic3r::ViewMesh->new; $app->{canvas}->enable_picking(1); $app->{canvas}->enable_moving($opt{enable_moving}); - $app->{canvas}->load_object($model->objects->[0]); + $app->{canvas}->load_object($model, 0); $app->{canvas}->set_auto_bed_shape; $app->{canvas}->zoom_to_volumes; $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut};