mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-14 05:11:49 +08:00
Merge branch 'lordofhyphens-lambda-modifier-mesh'
This commit is contained in:
commit
b5940e542a
@ -24,6 +24,7 @@ use Slic3r::GUI::Plater::3DPreview;
|
||||
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
||||
use Slic3r::GUI::Plater::ObjectCutDialog;
|
||||
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
||||
use Slic3r::GUI::Plater::LambdaObjectDialog;
|
||||
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||
use Slic3r::GUI::Preferences;
|
||||
use Slic3r::GUI::ProgressStatusBar;
|
||||
|
222
lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm
Normal file
222
lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm
Normal file
@ -0,0 +1,222 @@
|
||||
# Generate an anonymous or "lambda" 3D object. This gets used with the Add Generic option in Settings.
|
||||
#
|
||||
|
||||
package Slic3r::GUI::Plater::LambdaObjectDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Slic3r::Geometry qw(PI X);
|
||||
use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL wxCB_READONLY wxTE_PROCESS_TAB);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_COMBOBOX EVT_TEXT);
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Lambda Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
# Note whether the window was already closed, so a pending update is not executed.
|
||||
$self->{already_closed} = 0;
|
||||
$self->{object_parameters} = {
|
||||
type => "box",
|
||||
dim => [1, 1, 1],
|
||||
cyl_r => 1,
|
||||
cyl_h => 1,
|
||||
sph_rho => 1.0,
|
||||
slab_h => 1.0,
|
||||
slab_z => 0.0,
|
||||
};
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||
my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
my $button_ok = $self->CreateStdDialogButtonSizer(wxOK);
|
||||
my $button_cancel = $self->CreateStdDialogButtonSizer(wxCANCEL);
|
||||
$button_sizer->Add($button_ok);
|
||||
$button_sizer->Add($button_cancel);
|
||||
EVT_BUTTON($self, wxID_OK, sub {
|
||||
# validate user input
|
||||
return if !$self->CanClose;
|
||||
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->Destroy;
|
||||
});
|
||||
EVT_BUTTON($self, wxID_CANCEL, sub {
|
||||
# validate user input
|
||||
return if !$self->CanClose;
|
||||
|
||||
$self->EndModal(wxID_CANCEL);
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
my $optgroup_box;
|
||||
$optgroup_box = $self->{optgroup_box} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Cube...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) {
|
||||
if (!looks_like_number($optgroup_box->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{dim}[$opt_id] = $optgroup_box->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
my @options = ("box", "slab", "cylinder", "sphere");
|
||||
$self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY);
|
||||
|
||||
$optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 0,
|
||||
label => 'L',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 1,
|
||||
label => 'W',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 2,
|
||||
label => 'H',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
|
||||
my $optgroup_cylinder;
|
||||
$optgroup_cylinder = $self->{optgroup_cylinder} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Cylinder...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id eq 'cyl_r' || $opt_id eq 'cyl_h') {
|
||||
if (!looks_like_number($optgroup_cylinder->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{$opt_id} = $optgroup_cylinder->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
|
||||
$optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "cyl_r",
|
||||
label => 'Radius',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "cyl_h",
|
||||
label => 'Height',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
|
||||
my $optgroup_sphere;
|
||||
$optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Sphere...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id eq 'sph_rho') {
|
||||
if (!looks_like_number($optgroup_sphere->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{$opt_id} = $optgroup_sphere->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
|
||||
$optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "sph_rho",
|
||||
label => 'Rho',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
|
||||
my $optgroup_slab;
|
||||
$optgroup_slab = $self->{optgroup_slab} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Slab...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id eq 'slab_z' || $opt_id eq 'slab_h') {
|
||||
if (!looks_like_number($optgroup_slab->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{$opt_id} = $optgroup_slab->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
$optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "slab_h",
|
||||
label => 'H',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "slab_z",
|
||||
label => 'Initial Z',
|
||||
type => 'f',
|
||||
default => '0',
|
||||
));
|
||||
|
||||
|
||||
EVT_COMBOBOX($self, 1, sub{
|
||||
$self->{object_parameters}->{type} = $self->{type}->GetValue();
|
||||
$self->_update_ui;
|
||||
});
|
||||
|
||||
|
||||
$self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_slab->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->_update_ui;
|
||||
|
||||
$self->SetSizer($self->{sizer});
|
||||
$self->{sizer}->Fit($self);
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
|
||||
return $self;
|
||||
}
|
||||
sub CanClose {
|
||||
return 1;
|
||||
}
|
||||
sub ObjectParameter {
|
||||
my ($self) = @_;
|
||||
return $self->{object_parameters};
|
||||
}
|
||||
|
||||
sub _update_ui {
|
||||
my ($self) = @_;
|
||||
$self->{sizer}->Hide($self->{optgroup_cylinder}->sizer);
|
||||
$self->{sizer}->Hide($self->{optgroup_slab}->sizer);
|
||||
$self->{sizer}->Hide($self->{optgroup_box}->sizer);
|
||||
$self->{sizer}->Hide($self->{optgroup_sphere}->sizer);
|
||||
if ($self->{type}->GetValue eq "box") {
|
||||
$self->{sizer}->Show($self->{optgroup_box}->sizer);
|
||||
} elsif ($self->{type}->GetValue eq "cylinder") {
|
||||
$self->{sizer}->Show($self->{optgroup_cylinder}->sizer);
|
||||
} elsif ($self->{type}->GetValue eq "slab") {
|
||||
$self->{sizer}->Show($self->{optgroup_slab}->sizer);
|
||||
} elsif ($self->{type}->GetValue eq "sphere") {
|
||||
$self->{sizer}->Show($self->{optgroup_sphere}->sizer);
|
||||
}
|
||||
$self->{sizer}->Fit($self);
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
}
|
||||
1;
|
@ -7,7 +7,7 @@ use warnings;
|
||||
use utf8;
|
||||
|
||||
use File::Basename qw(basename);
|
||||
use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG
|
||||
use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL
|
||||
wxTheApp);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED);
|
||||
use base 'Wx::Panel';
|
||||
@ -23,6 +23,18 @@ sub new {
|
||||
|
||||
my $object = $self->{model_object} = $params{model_object};
|
||||
|
||||
# Save state for sliders.
|
||||
$self->{move_options} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
$self->{last_coords} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
|
||||
# create TreeCtrl
|
||||
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
|
||||
wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT
|
||||
@ -41,10 +53,12 @@ sub new {
|
||||
# buttons
|
||||
$self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Load generic…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
if ($Slic3r::GUI::have_button_icons) {
|
||||
$self->{btn_load_part}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_delete}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG));
|
||||
}
|
||||
|
||||
@ -52,9 +66,11 @@ sub new {
|
||||
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$buttons_sizer->Add($self->{btn_load_part}, 0);
|
||||
$buttons_sizer->Add($self->{btn_load_modifier}, 0);
|
||||
$buttons_sizer->Add($self->{btn_load_lambda_modifier}, 0);
|
||||
$buttons_sizer->Add($self->{btn_delete}, 0);
|
||||
$self->{btn_load_part}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_load_modifier}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_load_lambda_modifier}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_delete}->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
# part settings panel
|
||||
@ -62,11 +78,59 @@ sub new {
|
||||
my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
|
||||
$settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
my $optgroup_movers;
|
||||
$optgroup_movers = $self->{optgroup_movers} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Move',
|
||||
on_change => sub {
|
||||
my ($opt_id) = @_;
|
||||
# There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider
|
||||
# genates tens of events for a single value change.
|
||||
# Only trigger the recalculation if the value changes
|
||||
# or a live preview was activated and the mesh cut is not valid yet.
|
||||
if ($self->{move_options}{$opt_id} != $optgroup_movers->get_value($opt_id)) {
|
||||
$self->{move_options}{$opt_id} = $optgroup_movers->get_value($opt_id);
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_update;
|
||||
});
|
||||
}
|
||||
},
|
||||
label_width => 20,
|
||||
);
|
||||
$optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'x',
|
||||
type => 'slider',
|
||||
label => 'X',
|
||||
default => 0,
|
||||
min => -($self->{model_object}->bounding_box->size->x)*4,
|
||||
max => $self->{model_object}->bounding_box->size->x*4,
|
||||
full_width => 1,
|
||||
));
|
||||
$optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'y',
|
||||
type => 'slider',
|
||||
label => 'Y',
|
||||
default => 0,
|
||||
min => -($self->{model_object}->bounding_box->size->y)*4,
|
||||
max => $self->{model_object}->bounding_box->size->y*4,
|
||||
full_width => 1,
|
||||
));
|
||||
$optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'z',
|
||||
type => 'slider',
|
||||
label => 'Z',
|
||||
default => 0,
|
||||
min => -($self->{model_object}->bounding_box->size->z)*4,
|
||||
max => $self->{model_object}->bounding_box->size->z*4,
|
||||
full_width => 1,
|
||||
));
|
||||
|
||||
# left pane with tree
|
||||
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$left_sizer->Add($tree, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
$left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
$left_sizer->Add($settings_sizer, 1, wxEXPAND | wxALL, 0);
|
||||
$left_sizer->Add($optgroup_movers->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
# right pane with preview canvas
|
||||
my $canvas;
|
||||
@ -84,7 +148,7 @@ sub new {
|
||||
|
||||
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->SetSize([500,700]);
|
||||
$canvas->zoom_to_volumes;
|
||||
}
|
||||
|
||||
@ -107,6 +171,7 @@ sub new {
|
||||
});
|
||||
EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) });
|
||||
EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) });
|
||||
EVT_BUTTON($self, $self->{btn_load_lambda_modifier}, sub { $self->on_btn_lambda(1) });
|
||||
EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete);
|
||||
|
||||
$self->reload_tree;
|
||||
@ -180,6 +245,21 @@ sub selection_changed {
|
||||
$self->{settings_panel}->disable;
|
||||
$self->{settings_panel}->set_config(undef);
|
||||
|
||||
# reset move sliders
|
||||
$self->{optgroup_movers}->set_value("x", 0);
|
||||
$self->{optgroup_movers}->set_value("y", 0);
|
||||
$self->{optgroup_movers}->set_value("z", 0);
|
||||
$self->{move_options} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
$self->{last_coords} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
|
||||
if (my $itemData = $self->get_selection) {
|
||||
my ($config, @opt_keys);
|
||||
if ($itemData->{type} eq 'volume') {
|
||||
@ -191,6 +271,12 @@ sub selection_changed {
|
||||
|
||||
# attach volume config to settings panel
|
||||
my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
|
||||
|
||||
if ($volume->modifier) {
|
||||
$self->{optgroup_movers}->enable;
|
||||
} else {
|
||||
$self->{optgroup_movers}->disable;
|
||||
}
|
||||
$config = $volume->config;
|
||||
$self->{staticbox}->SetLabel('Part Settings');
|
||||
|
||||
@ -200,6 +286,7 @@ sub selection_changed {
|
||||
# select nothing in 3D preview
|
||||
|
||||
# attach object config to settings panel
|
||||
$self->{optgroup_movers}->disable;
|
||||
$self->{staticbox}->SetLabel('Object Settings');
|
||||
@opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new);
|
||||
$config = $self->{model_object}->config;
|
||||
@ -252,6 +339,43 @@ sub on_btn_load {
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
sub on_btn_lambda {
|
||||
my ($self, $is_modifier) = @_;
|
||||
|
||||
my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self);
|
||||
if ($dlg->ShowModal() == wxID_CANCEL) {
|
||||
return;
|
||||
}
|
||||
my $params = $dlg->ObjectParameter;
|
||||
my $type = "".$params->{"type"};
|
||||
my $name = "lambda-".$params->{"type"};
|
||||
my $mesh;
|
||||
|
||||
if ($type eq "box") {
|
||||
$mesh = Slic3r::TriangleMesh::make_cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]);
|
||||
} elsif ($type eq "cylinder") {
|
||||
$mesh = Slic3r::TriangleMesh::make_cylinder($params->{"cyl_r"}, $params->{"cyl_h"});
|
||||
} elsif ($type eq "sphere") {
|
||||
$mesh = Slic3r::TriangleMesh::make_sphere($params->{"sph_rho"});
|
||||
} elsif ($type eq "slab") {
|
||||
$mesh = Slic3r::TriangleMesh::make_cube($self->{model_object}->bounding_box->size->x*1.5, $self->{model_object}->bounding_box->size->y*1.5, $params->{"slab_h"}); #**
|
||||
# box sets the base coordinate at 0,0, move to center of plate and move it up to initial_z
|
||||
$mesh->translate(-$self->{model_object}->bounding_box->size->x*1.5/2.0, -$self->{model_object}->bounding_box->size->y*1.5/2.0, $params->{"slab_z"}); #**
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
$mesh->repair;
|
||||
my $new_volume = $self->{model_object}->add_volume(mesh => $mesh);
|
||||
$new_volume->set_modifier($is_modifier);
|
||||
$new_volume->set_name($name);
|
||||
|
||||
# set a default extruder value, since user can't add it manually
|
||||
$new_volume->config->set_ifndef('extruder', 0);
|
||||
|
||||
$self->{parts_changed} = 1;
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
sub on_btn_delete {
|
||||
my ($self) = @_;
|
||||
|
||||
@ -310,4 +434,27 @@ sub PartSettingsChanged {
|
||||
return $self->{part_settings_changed};
|
||||
}
|
||||
|
||||
sub _update {
|
||||
my ($self) = @_;
|
||||
my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z});
|
||||
my ($l_x, $l_y, $l_z) = ($self->{last_coords}{x}, $self->{last_coords}{y}, $self->{last_coords}{z});
|
||||
|
||||
my $itemData = $self->get_selection;
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $d = Slic3r::Pointf3->new($m_x - $l_x, $m_y - $l_y, $m_z - $l_z);
|
||||
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
|
||||
$volume->mesh->translate(@{$d});
|
||||
$self->{last_coords}{x} = $m_x;
|
||||
$self->{last_coords}{y} = $m_y;
|
||||
$self->{last_coords}{z} = $m_z;
|
||||
}
|
||||
|
||||
$self->{parts_changed} = 1;
|
||||
my @objects = ();
|
||||
push @objects, $self->{model_object};
|
||||
$self->{canvas}->reset_objects;
|
||||
$self->{canvas}->load_object($_, undef, [0]) for @objects;
|
||||
$self->{canvas}->Render;
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -27,6 +27,14 @@ sub mesh {
|
||||
$facets = [
|
||||
[0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5],
|
||||
],
|
||||
} elsif ($name eq 'box') {
|
||||
my ($x, $y, $z) = @{ $params{"dim"} };
|
||||
$vertices = [
|
||||
[$x,$y,0], [$x,0,0], [0,0,0], [0,$y,0], [$x,$y,$z], [0,$y,$z], [0,0,$z], [$x,0,$z],
|
||||
];
|
||||
$facets = [
|
||||
[0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5],
|
||||
],
|
||||
} elsif ($name eq 'cube_with_hole') {
|
||||
$vertices = [
|
||||
[0,0,0],[0,0,10],[0,20,0],[0,20,10],[20,0,0],[20,0,10],[5,5,0],[15,5,0],[5,15,0],[20,20,0],[15,15,0],[20,20,10],[5,5,10],[5,15,10],[15,5,10],[15,15,10]
|
||||
|
@ -25,6 +25,48 @@ TriangleMesh::TriangleMesh()
|
||||
stl_initialize(&this->stl);
|
||||
}
|
||||
|
||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Point3>& facets )
|
||||
: repaired(false)
|
||||
{
|
||||
stl_initialize(&this->stl);
|
||||
stl_file &stl = this->stl;
|
||||
stl.error = 0;
|
||||
stl.stats.type = inmemory;
|
||||
|
||||
// count facets and allocate memory
|
||||
stl.stats.number_of_facets = facets.size();
|
||||
stl.stats.original_num_facets = stl.stats.number_of_facets;
|
||||
stl_allocate(&stl);
|
||||
|
||||
for (int i = 0; i < stl.stats.number_of_facets; i++) {
|
||||
stl_facet facet;
|
||||
facet.normal.x = 0;
|
||||
facet.normal.y = 0;
|
||||
facet.normal.z = 0;
|
||||
|
||||
const Pointf3& ref_f1 = points[facets[i].x];
|
||||
facet.vertex[0].x = ref_f1.x;
|
||||
facet.vertex[0].y = ref_f1.y;
|
||||
facet.vertex[0].z = ref_f1.z;
|
||||
|
||||
const Pointf3& ref_f2 = points[facets[i].y];
|
||||
facet.vertex[1].x = ref_f2.x;
|
||||
facet.vertex[1].y = ref_f2.y;
|
||||
facet.vertex[1].z = ref_f2.z;
|
||||
|
||||
const Pointf3& ref_f3 = points[facets[i].z];
|
||||
facet.vertex[2].x = ref_f3.x;
|
||||
facet.vertex[2].y = ref_f3.y;
|
||||
facet.vertex[2].z = ref_f3.z;
|
||||
|
||||
facet.extra[0] = 0;
|
||||
facet.extra[1] = 0;
|
||||
|
||||
stl.facet_start[i] = facet;
|
||||
}
|
||||
stl_get_size(&stl);
|
||||
}
|
||||
|
||||
TriangleMesh::TriangleMesh(const TriangleMesh &other)
|
||||
: stl(other.stl), repaired(other.repaired)
|
||||
{
|
||||
@ -477,6 +519,154 @@ TriangleMesh::extrude_tin(float offset)
|
||||
this->repair();
|
||||
}
|
||||
|
||||
// Generate the vertex list for a cube solid of arbitrary size in X/Y/Z.
|
||||
TriangleMesh
|
||||
TriangleMesh::make_cube(double x, double y, double z) {
|
||||
Pointf3 pv[8] = {
|
||||
Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0),
|
||||
Pointf3(0, y, 0), Pointf3(x, y, z), Pointf3(0, y, z),
|
||||
Pointf3(0, 0, z), Pointf3(x, 0, z)
|
||||
};
|
||||
Point3 fv[12] = {
|
||||
Point3(0, 1, 2), Point3(0, 2, 3), Point3(4, 5, 6),
|
||||
Point3(4, 6, 7), Point3(0, 4, 7), Point3(0, 7, 1),
|
||||
Point3(1, 7, 6), Point3(1, 6, 2), Point3(2, 6, 5),
|
||||
Point3(2, 5, 3), Point3(4, 0, 3), Point3(4, 3, 5)
|
||||
};
|
||||
|
||||
std::vector<Point3> facets(&fv[0], &fv[0]+12);
|
||||
Pointf3s vertices(&pv[0], &pv[0]+8);
|
||||
|
||||
TriangleMesh mesh(vertices ,facets);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// Generate the mesh for a cylinder and return it, using
|
||||
// the generated angle to calculate the top mesh triangles.
|
||||
// Default is 360 sides, angle fa is in radians.
|
||||
TriangleMesh
|
||||
TriangleMesh::make_cylinder(double r, double h, double fa) {
|
||||
Pointf3s vertices;
|
||||
std::vector<Point3> facets;
|
||||
|
||||
// 2 special vertices, top and bottom center, rest are relative to this
|
||||
vertices.push_back(Pointf3(0.0, 0.0, 0.0));
|
||||
vertices.push_back(Pointf3(0.0, 0.0, h));
|
||||
|
||||
// adjust via rounding to get an even multiple for any provided angle.
|
||||
double angle = (2*PI / floor(2*PI / fa));
|
||||
|
||||
// for each line along the polygon approximating the top/bottom of the
|
||||
// circle, generate four points and four facets (2 for the wall, 2 for the
|
||||
// top and bottom.
|
||||
// Special case: Last line shares 2 vertices with the first line.
|
||||
unsigned id = vertices.size() - 1;
|
||||
vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, 0));
|
||||
vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, h));
|
||||
for (double i = 0; i < 2*PI; i+=angle) {
|
||||
Pointf3 b(0, r, 0);
|
||||
Pointf3 t(0, r, h);
|
||||
b.rotate(i, Pointf3(0,0,0));
|
||||
t.rotate(i, Pointf3(0,0,h));
|
||||
vertices.push_back(b);
|
||||
vertices.push_back(t);
|
||||
id = vertices.size() - 1;
|
||||
facets.push_back(Point3( 0, id - 1, id - 3)); // top
|
||||
facets.push_back(Point3(id, 1, id - 2)); // bottom
|
||||
facets.push_back(Point3(id, id - 2, id - 3)); // upper-right of side
|
||||
facets.push_back(Point3(id, id - 3, id - 1)); // bottom-left of side
|
||||
}
|
||||
// Connect the last set of vertices with the first.
|
||||
facets.push_back(Point3( 2, 0, id - 1));
|
||||
facets.push_back(Point3( 1, 3, id));
|
||||
facets.push_back(Point3(id, 3, 2));
|
||||
facets.push_back(Point3(id, 2, id - 1));
|
||||
|
||||
TriangleMesh mesh(vertices, facets);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// Generates mesh for a sphere centered about the origin, using the generated angle
|
||||
// to determine the granularity.
|
||||
// Default angle is 1 degree.
|
||||
TriangleMesh
|
||||
TriangleMesh::make_sphere(double rho, double fa) {
|
||||
Pointf3s vertices;
|
||||
std::vector<Point3> facets;
|
||||
|
||||
// Algorithm:
|
||||
// Add points one-by-one to the sphere grid and form facets using relative coordinates.
|
||||
// Sphere is composed effectively of a mesh of stacked circles.
|
||||
|
||||
// adjust via rounding to get an even multiple for any provided angle.
|
||||
double angle = (2*PI / floor(2*PI / fa));
|
||||
|
||||
// Ring to be scaled to generate the steps of the sphere
|
||||
std::vector<double> ring;
|
||||
for (double i = 0; i < 2*PI; i+=angle) {
|
||||
ring.push_back(i);
|
||||
}
|
||||
const size_t steps = ring.size();
|
||||
const double increment = (double)(1.0 / (double)steps);
|
||||
|
||||
// special case: first ring connects to 0,0,0
|
||||
// insert and form facets.
|
||||
vertices.push_back(Pointf3(0.0, 0.0, -rho));
|
||||
size_t id = vertices.size();
|
||||
for (size_t i = 0; i < ring.size(); i++) {
|
||||
// Fixed scaling
|
||||
const double z = -rho + increment*rho*2.0;
|
||||
// radius of the circle for this step.
|
||||
const double r = sqrt(abs(rho*rho - z*z));
|
||||
Pointf3 b(0, r, z);
|
||||
b.rotate(ring[i], Pointf3(0,0,z));
|
||||
vertices.push_back(b);
|
||||
if (i == 0) {
|
||||
facets.push_back(Point3(1, 0, ring.size()));
|
||||
} else {
|
||||
facets.push_back(Point3(id, 0, id - 1));
|
||||
}
|
||||
id++;
|
||||
}
|
||||
|
||||
// General case: insert and form facets for each step, joining it to the ring below it.
|
||||
for (size_t s = 2; s < steps - 1; s++) {
|
||||
const double z = -rho + increment*(double)s*2.0*rho;
|
||||
const double r = sqrt(abs(rho*rho - z*z));
|
||||
|
||||
for (size_t i = 0; i < ring.size(); i++) {
|
||||
Pointf3 b(0, r, z);
|
||||
b.rotate(ring[i], Pointf3(0,0,z));
|
||||
vertices.push_back(b);
|
||||
if (i == 0) {
|
||||
// wrap around
|
||||
facets.push_back(Point3(id + ring.size() - 1 , id, id - 1));
|
||||
facets.push_back(Point3(id, id - ring.size(), id - 1));
|
||||
} else {
|
||||
facets.push_back(Point3(id , id - ring.size(), (id - 1) - ring.size()));
|
||||
facets.push_back(Point3(id, id - 1 - ring.size() , id - 1));
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// special case: last ring connects to 0,0,rho*2.0
|
||||
// only form facets.
|
||||
vertices.push_back(Pointf3(0.0, 0.0, rho));
|
||||
for (size_t i = 0; i < ring.size(); i++) {
|
||||
if (i == 0) {
|
||||
// third vertex is on the other side of the ring.
|
||||
facets.push_back(Point3(id, id - ring.size(), id - 1));
|
||||
} else {
|
||||
facets.push_back(Point3(id, id - ring.size() + i, id - ring.size() + (i - 1)));
|
||||
}
|
||||
}
|
||||
id++;
|
||||
TriangleMesh mesh(vertices, facets);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
template <Axis A>
|
||||
void
|
||||
TriangleMeshSlicer<A>::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
|
||||
|
@ -21,6 +21,7 @@ class TriangleMesh
|
||||
{
|
||||
public:
|
||||
TriangleMesh();
|
||||
TriangleMesh(const Pointf3s &points, const std::vector<Point3> &facets);
|
||||
TriangleMesh(const TriangleMesh &other);
|
||||
TriangleMesh& operator= (TriangleMesh other);
|
||||
void swap(TriangleMesh &other);
|
||||
@ -56,6 +57,11 @@ class TriangleMesh
|
||||
bool needed_repair() const;
|
||||
size_t facets_count() const;
|
||||
void extrude_tin(float offset);
|
||||
|
||||
static TriangleMesh make_cube(double x, double y, double z);
|
||||
static TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360));
|
||||
static TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
|
||||
|
||||
stl_file stl;
|
||||
bool repaired;
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
%code{% RETVAL = THIS->bounding_box().center(); %};
|
||||
int facets_count();
|
||||
void reset_repair_stats();
|
||||
|
||||
%{
|
||||
|
||||
void
|
||||
@ -252,6 +253,13 @@ TriangleMesh::bb3()
|
||||
|
||||
%package{Slic3r::TriangleMesh};
|
||||
|
||||
Clone<TriangleMesh> make_cube(double x, double y, double z)
|
||||
%code{% RETVAL = TriangleMesh::make_cube(x, y, z); %};
|
||||
Clone<TriangleMesh> make_cylinder(double r, double h)
|
||||
%code{% RETVAL = TriangleMesh::make_cylinder(r, h); %};
|
||||
Clone<TriangleMesh> make_sphere(double rho)
|
||||
%code{% RETVAL = TriangleMesh::make_sphere(rho); %};
|
||||
|
||||
%{
|
||||
PROTOTYPES: DISABLE
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user