Merge remote-tracking branch 'remotes/origin/master' into support_improvements
1
Build.PL
|
@ -38,7 +38,6 @@ if ($gui) {
|
|||
%prereqs = qw(
|
||||
Class::Accessor 0
|
||||
Wx 0.9918
|
||||
Socket 2.016
|
||||
);
|
||||
%recommends = qw(
|
||||
Wx::GLCanvas 0
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Boost 1.63 requires CMake 3.7 or newer
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
project(Slic3r)
|
||||
|
||||
|
@ -23,6 +22,7 @@ option(SLIC3R_GUI "Compile Slic3r with GUI components (OpenGL, wxWidgets)"
|
|||
option(SLIC3R_PRUSACONTROL "Compile Slic3r with the PrusaControl prject file format (requires wxWidgets base library)" 1)
|
||||
option(SLIC3R_PROFILE "Compile Slic3r with an invasive Shiny profiler" 0)
|
||||
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
|
||||
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
|
||||
|
||||
if (MSVC AND SLIC3R_MSVC_COMPILE_PARALLEL)
|
||||
add_compile_options(/MP)
|
||||
|
@ -36,10 +36,18 @@ else()
|
|||
set(ENV_PATH_SEPARATOR ":")
|
||||
endif()
|
||||
set(ENV{PATH} "${PROJECT_SOURCE_DIR}/local-lib/bin${ENV_PATH_SEPARATOR}$ENV{PATH}")
|
||||
set(ENV{PERL5LIB} "${PROJECT_SOURCE_DIR}/local-lib/lib/perl${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}")
|
||||
set(PERL_INCLUDE "${PROJECT_SOURCE_DIR}/local-lib/lib/perl5${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}")
|
||||
message("PATH: $ENV{PATH}")
|
||||
message("PERL5LIB: $ENV{PERL5LIB}")
|
||||
message("PERL_INCLUDE: ${PERL_INCLUDE}")
|
||||
find_package(Perl REQUIRED)
|
||||
if (WIN32)
|
||||
# On Windows passing the PERL5LIB variable causes various problems (such as with MAX_PATH and others),
|
||||
# basically I've found no good way to do it on Windows.
|
||||
set(PERL5LIB_ENV_CMD "")
|
||||
else()
|
||||
set(PERL5LIB_ENV_CMD ${CMAKE_COMMAND} -E env PERL5LIB=${PERL_INCLUDE})
|
||||
endif()
|
||||
|
||||
|
||||
# CMAKE_PREFIX_PATH is used to point CMake to the remaining dependencies (Boost, TBB, ...)
|
||||
# We pick it from environment if it is not defined in another way
|
||||
|
|
|
@ -167,6 +167,7 @@ sub thread_cleanup {
|
|||
*Slic3r::GUI::PresetHints::DESTROY = sub {};
|
||||
*Slic3r::GUI::TabIface::DESTROY = sub {};
|
||||
*Slic3r::OctoPrint::DESTROY = sub {};
|
||||
*Slic3r::Duet::DESTROY = sub {};
|
||||
*Slic3r::PresetUpdater::DESTROY = sub {};
|
||||
return undef; # this prevents a "Scalars leaked" warning
|
||||
}
|
||||
|
|
|
@ -141,12 +141,12 @@ sub OnInit {
|
|||
$self->CallAfter(sub {
|
||||
eval {
|
||||
if (! $self->{preset_updater}->config_update()) {
|
||||
exit 0;
|
||||
$self->{mainframe}->Close;
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
warn $@ . "\n";
|
||||
fatal_error(undef, $@);
|
||||
show_error(undef, $@);
|
||||
$self->{mainframe}->Close;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ sub new {
|
|||
wxTheApp->{app_config}->save;
|
||||
$self->{plater}->{print} = undef if($self->{plater});
|
||||
Slic3r::GUI::_3DScene::remove_all_canvases();
|
||||
Slic3r::GUI::deregister_on_request_update_callback();
|
||||
# propagate event
|
||||
$event->Skip;
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ sub new {
|
|||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
|
||||
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
|
||||
serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
|
||||
serial_port serial_speed host_type print_host printhost_apikey printhost_cafile
|
||||
nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
|
||||
wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model
|
||||
)]);
|
||||
|
@ -127,7 +127,9 @@ sub new {
|
|||
$range->[1] *= $variation;
|
||||
}
|
||||
$_->set_scaling_factor($scale) for @{ $model_object->instances };
|
||||
$object->transform_thumbnail($self->{model}, $obj_idx);
|
||||
|
||||
$self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
|
||||
# $object->transform_thumbnail($self->{model}, $obj_idx);
|
||||
|
||||
#update print and start background processing
|
||||
$self->{print}->add_model_object($model_object, $obj_idx);
|
||||
|
@ -138,11 +140,18 @@ sub new {
|
|||
};
|
||||
|
||||
# callback to react to gizmo rotate
|
||||
# omitting last three parameters means rotation around Z
|
||||
# otherwise they are the components of the rotation axis vector
|
||||
my $on_gizmo_rotate = sub {
|
||||
my ($angle_z) = @_;
|
||||
$self->rotate(rad2deg($angle_z), Z, 'absolute');
|
||||
my ($angle, $axis_x, $axis_y, $axis_z) = @_;
|
||||
if (!defined $axis_x) {
|
||||
$self->rotate(rad2deg($angle), Z, 'absolute');
|
||||
}
|
||||
else {
|
||||
$self->rotate(rad2deg($angle), undef, 'absolute', $axis_x, $axis_y, $axis_z) if $angle != 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
# callback to update object's geometry info while using gizmos
|
||||
my $on_update_geometry_info = sub {
|
||||
my ($size_x, $size_y, $size_z, $scale_factor) = @_;
|
||||
|
@ -200,19 +209,22 @@ sub new {
|
|||
|
||||
Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{canvas3D}, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{preview3D}->canvas, $self->{canvas3D}); });
|
||||
}
|
||||
|
||||
Slic3r::GUI::register_on_request_update_callback(sub { $self->schedule_background_process; });
|
||||
|
||||
# Initialize 2D preview canvas
|
||||
$self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
|
||||
$self->{preview_notebook}->AddPage($self->{canvas}, L('2D'));
|
||||
$self->{canvas}->on_select_object($on_select_object);
|
||||
$self->{canvas}->on_double_click($on_double_click);
|
||||
$self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
|
||||
$self->{canvas}->on_instances_moved($on_instances_moved);
|
||||
# # Initialize 2D preview canvas
|
||||
# $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
|
||||
# $self->{preview_notebook}->AddPage($self->{canvas}, L('2D'));
|
||||
# $self->{canvas}->on_select_object($on_select_object);
|
||||
# $self->{canvas}->on_double_click($on_double_click);
|
||||
# $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
|
||||
# $self->{canvas}->on_instances_moved($on_instances_moved);
|
||||
|
||||
# Initialize 3D toolpaths preview
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config});
|
||||
Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1);
|
||||
Slic3r::GUI::_3DScene::enable_dynamic_background($self->{preview3D}->canvas, 1);
|
||||
Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); });
|
||||
$self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview'));
|
||||
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
|
||||
|
@ -310,6 +322,9 @@ sub new {
|
|||
my ($list, $event) = @_;
|
||||
if ($event->GetKeyCode == WXK_TAB) {
|
||||
$list->Navigate($event->ShiftDown ? &Wx::wxNavigateBackward : &Wx::wxNavigateForward);
|
||||
} elsif ($event->GetKeyCode == WXK_DELETE ||
|
||||
($event->GetKeyCode == WXK_BACK && &Wx::wxMAC) ) {
|
||||
$self->remove;
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
|
@ -399,7 +414,8 @@ sub new {
|
|||
|
||||
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
|
||||
for grep defined($_),
|
||||
$self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list};
|
||||
$self, $self->{canvas3D}, $self->{preview3D}, $self->{list};
|
||||
# $self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list};
|
||||
|
||||
EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
|
@ -430,7 +446,7 @@ sub new {
|
|||
});
|
||||
}
|
||||
|
||||
$self->{canvas}->update_bed_size;
|
||||
# $self->{canvas}->update_bed_size;
|
||||
if ($self->{canvas3D}) {
|
||||
Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape);
|
||||
Slic3r::GUI::_3DScene::zoom_to_bed($self->{canvas3D});
|
||||
|
@ -845,8 +861,8 @@ sub load_model_objects {
|
|||
|
||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
$self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
|
||||
|
||||
$self->reset_thumbnail($obj_idx);
|
||||
|
||||
# $self->reset_thumbnail($obj_idx);
|
||||
}
|
||||
$self->arrange if $need_arrange;
|
||||
$self->update;
|
||||
|
@ -986,9 +1002,10 @@ sub set_number_of_copies {
|
|||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
|
||||
# prompt user
|
||||
my $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
|
||||
my $copies = -1;
|
||||
$copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
|
||||
my $diff = $copies - $model_object->instances_count;
|
||||
if ($diff == 0) {
|
||||
if ($diff == 0 || $copies == -1) {
|
||||
# no variation
|
||||
$self->resume_background_process;
|
||||
} elsif ($diff > 0) {
|
||||
|
@ -1021,28 +1038,40 @@ sub _get_number_from_user {
|
|||
}
|
||||
|
||||
sub rotate {
|
||||
my ($self, $angle, $axis, $relative_key) = @_;
|
||||
my ($self, $angle, $axis, $relative_key, $axis_x, $axis_y, $axis_z) = @_;
|
||||
$relative_key //= 'absolute'; # relative or absolute coordinates
|
||||
$axis //= Z; # angle is in degrees
|
||||
|
||||
$axis_x //= 0;
|
||||
$axis_y //= 0;
|
||||
$axis_z //= 0;
|
||||
my $relative = $relative_key eq 'relative';
|
||||
|
||||
|
||||
my ($obj_idx, $object) = $self->selected_object;
|
||||
return if !defined $obj_idx;
|
||||
|
||||
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
my $model_instance = $model_object->instances->[0];
|
||||
|
||||
|
||||
if (!defined $angle) {
|
||||
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
|
||||
my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0;
|
||||
$angle = $self->_get_number_from_user(L("Enter the rotation angle:"), L("Rotate around ").$axis_name.(" axis"), L("Invalid rotation angle entered"), $default);
|
||||
return if $angle eq '';
|
||||
}
|
||||
|
||||
# Let's calculate vector of rotation axis (if we don't have it already)
|
||||
# The minus is there so that the direction is the same as was established
|
||||
if (defined $axis) {
|
||||
if ($axis == X) {
|
||||
$axis_x = -1;
|
||||
}
|
||||
if ($axis == Y) {
|
||||
$axis_y = -1;
|
||||
}
|
||||
}
|
||||
|
||||
$self->stop_background_process;
|
||||
|
||||
if ($axis == Z) {
|
||||
if (defined $axis && $axis == Z) {
|
||||
my $new_angle = deg2rad($angle);
|
||||
foreach my $inst (@{ $model_object->instances }) {
|
||||
my $rotation = ($relative ? $inst->rotation : 0.) + $new_angle;
|
||||
|
@ -1055,19 +1084,21 @@ sub rotate {
|
|||
$inst->set_rotation($rotation);
|
||||
Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D});
|
||||
}
|
||||
$object->transform_thumbnail($self->{model}, $obj_idx);
|
||||
# $object->transform_thumbnail($self->{model}, $obj_idx);
|
||||
} else {
|
||||
# rotation around X and Y needs to be performed on mesh
|
||||
# so we first apply any Z rotation
|
||||
if ($model_instance->rotation != 0) {
|
||||
$model_object->rotate($model_instance->rotation, Z);
|
||||
$_->set_rotation(0) for @{ $model_object->instances };
|
||||
if (defined $axis) {
|
||||
# rotation around X and Y needs to be performed on mesh
|
||||
# so we first apply any Z rotation
|
||||
if ($model_instance->rotation != 0) {
|
||||
$model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, -1));
|
||||
$_->set_rotation(0) for @{ $model_object->instances };
|
||||
}
|
||||
}
|
||||
$model_object->rotate(deg2rad($angle), $axis);
|
||||
$model_object->rotate(deg2rad($angle), Slic3r::Pointf3->new($axis_x, $axis_y, $axis_z));
|
||||
|
||||
# realign object to Z = 0
|
||||
$model_object->center_around_origin;
|
||||
$self->reset_thumbnail($obj_idx);
|
||||
# # realign object to Z = 0
|
||||
# $model_object->center_around_origin;
|
||||
# $self->reset_thumbnail($obj_idx);
|
||||
}
|
||||
|
||||
# update print and start background processing
|
||||
|
@ -1089,15 +1120,15 @@ sub mirror {
|
|||
|
||||
# apply Z rotation before mirroring
|
||||
if ($model_instance->rotation != 0) {
|
||||
$model_object->rotate($model_instance->rotation, Z);
|
||||
$model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
|
||||
$_->set_rotation(0) for @{ $model_object->instances };
|
||||
}
|
||||
|
||||
$model_object->mirror($axis);
|
||||
|
||||
# realign object to Z = 0
|
||||
$model_object->center_around_origin;
|
||||
$self->reset_thumbnail($obj_idx);
|
||||
# # realign object to Z = 0
|
||||
# $model_object->center_around_origin;
|
||||
# $self->reset_thumbnail($obj_idx);
|
||||
|
||||
# update print and start background processing
|
||||
$self->stop_background_process;
|
||||
|
@ -1117,8 +1148,7 @@ sub changescale {
|
|||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
my $model_instance = $model_object->instances->[0];
|
||||
|
||||
my $object_size = $model_object->bounding_box->size;
|
||||
my $bed_size = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape})->bounding_box->size;
|
||||
my $object_size = $model_object->instance_bounding_box(0)->size;
|
||||
|
||||
if (defined $axis) {
|
||||
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
|
||||
|
@ -1126,7 +1156,7 @@ sub changescale {
|
|||
if ($tosize) {
|
||||
my $cursize = $object_size->[$axis];
|
||||
my $newsize = $self->_get_number_from_user(
|
||||
sprintf(L('Enter the new size for the selected object (print bed: %smm):'), unscale($bed_size->[$axis])),
|
||||
L('Enter the new size for the selected object:'),
|
||||
L("Scale along ").$axis_name, L('Invalid scaling value entered'), $cursize, 1);
|
||||
return if $newsize eq '';
|
||||
$scale = $newsize / $cursize * 100;
|
||||
|
@ -1137,7 +1167,7 @@ sub changescale {
|
|||
|
||||
# apply Z rotation before scaling
|
||||
if ($model_instance->rotation != 0) {
|
||||
$model_object->rotate($model_instance->rotation, Z);
|
||||
$model_object->rotate($model_instance->rotation, Slic3r::Pointf3->new(0, 0, 1));
|
||||
$_->set_rotation(0) for @{ $model_object->instances };
|
||||
}
|
||||
|
||||
|
@ -1147,7 +1177,7 @@ sub changescale {
|
|||
#FIXME Scale the layer height profile when $axis == Z?
|
||||
#FIXME Scale the layer height ranges $axis == Z?
|
||||
# object was already aligned to Z = 0, so no need to realign it
|
||||
$self->reset_thumbnail($obj_idx);
|
||||
# $self->reset_thumbnail($obj_idx);
|
||||
} else {
|
||||
my $scale;
|
||||
if ($tosize) {
|
||||
|
@ -1171,7 +1201,7 @@ sub changescale {
|
|||
$range->[1] *= $variation;
|
||||
}
|
||||
$_->set_scaling_factor($scale) for @{ $model_object->instances };
|
||||
$object->transform_thumbnail($self->{model}, $obj_idx);
|
||||
# $object->transform_thumbnail($self->{model}, $obj_idx);
|
||||
}
|
||||
|
||||
# update print and start background processing
|
||||
|
@ -1279,6 +1309,13 @@ sub async_apply_config {
|
|||
$self->{gcode_preview_data}->reset;
|
||||
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||
|
||||
# We also need to reload 3D scene because of the wipe tower preview box
|
||||
if ($self->{config}->wipe_tower) {
|
||||
my $selections = $self->collect_selections;
|
||||
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
|
||||
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1) if $self->{canvas3D}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1491,6 +1528,11 @@ sub on_process_completed {
|
|||
return if $error;
|
||||
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||
|
||||
# in case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
|
||||
my $selections = $self->collect_selections;
|
||||
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
|
||||
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
|
||||
|
||||
# if we have an export filename, start a new thread for exporting G-code
|
||||
if ($self->{export_gcode_output_file}) {
|
||||
|
@ -1552,7 +1594,7 @@ sub on_export_completed {
|
|||
$message = L("File added to print queue");
|
||||
$do_print = 1;
|
||||
} elsif ($self->{send_gcode_file}) {
|
||||
$message = L("Sending G-code file to the OctoPrint server...");
|
||||
$message = L("Sending G-code file to the Printer Host ...");
|
||||
$send_gcode = 1;
|
||||
} else {
|
||||
$message = L("G-code file exported to ") . $self->{export_gcode_output_file};
|
||||
|
@ -1568,9 +1610,10 @@ sub on_export_completed {
|
|||
|
||||
# Send $self->{send_gcode_file} to OctoPrint.
|
||||
if ($send_gcode) {
|
||||
my $op = Slic3r::OctoPrint->new($self->{config});
|
||||
if ($op->send_gcode($self->{send_gcode_file})) {
|
||||
$self->statusbar->SetStatusText(L("OctoPrint upload finished."));
|
||||
my $host = Slic3r::PrintHost::get_print_host($self->{config});
|
||||
|
||||
if ($host->send_gcode($self->{send_gcode_file})) {
|
||||
$self->statusbar->SetStatusText(L("Upload to host finished."));
|
||||
} else {
|
||||
$self->statusbar->SetStatusText("");
|
||||
}
|
||||
|
@ -1593,7 +1636,7 @@ sub print_info_box_show {
|
|||
my ($self, $show) = @_;
|
||||
my $scrolled_window_panel = $self->{scrolled_window_panel};
|
||||
my $scrolled_window_sizer = $self->{scrolled_window_sizer};
|
||||
return if $scrolled_window_sizer->IsShown(2) == $show;
|
||||
return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show));
|
||||
|
||||
if ($show) {
|
||||
my $print_info_sizer = $self->{print_info_sizer};
|
||||
|
@ -1802,10 +1845,10 @@ sub _get_export_file {
|
|||
return $output_file;
|
||||
}
|
||||
|
||||
sub reset_thumbnail {
|
||||
my ($self, $obj_idx) = @_;
|
||||
$self->{objects}[$obj_idx]->thumbnail(undef);
|
||||
}
|
||||
#sub reset_thumbnail {
|
||||
# my ($self, $obj_idx) = @_;
|
||||
# $self->{objects}[$obj_idx]->thumbnail(undef);
|
||||
#}
|
||||
|
||||
# this method gets called whenever print center is changed or the objects' bounding box changes
|
||||
# (i.e. when an object is added/removed/moved/rotated/scaled)
|
||||
|
@ -1829,7 +1872,9 @@ sub update {
|
|||
$self->resume_background_process;
|
||||
}
|
||||
|
||||
$self->{canvas}->reload_scene if $self->{canvas};
|
||||
$self->print_info_box_show(0);
|
||||
|
||||
# $self->{canvas}->reload_scene if $self->{canvas};
|
||||
my $selections = $self->collect_selections;
|
||||
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
|
||||
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0);
|
||||
|
@ -1886,7 +1931,7 @@ sub on_config_change {
|
|||
foreach my $opt_key (@{$self->{config}->diff($config)}) {
|
||||
$self->{config}->set($opt_key, $config->get($opt_key));
|
||||
if ($opt_key eq 'bed_shape') {
|
||||
$self->{canvas}->update_bed_size;
|
||||
# $self->{canvas}->update_bed_size;
|
||||
Slic3r::GUI::_3DScene::set_bed_shape($self->{canvas3D}, $self->{config}->bed_shape) if $self->{canvas3D};
|
||||
Slic3r::GUI::_3DScene::set_bed_shape($self->{preview3D}->canvas, $self->{config}->bed_shape) if $self->{preview3D};
|
||||
$update_scheduled = 1;
|
||||
|
@ -1895,8 +1940,8 @@ sub on_config_change {
|
|||
} elsif ($opt_key eq 'serial_port') {
|
||||
$self->{btn_print}->Show($config->get('serial_port'));
|
||||
$self->Layout;
|
||||
} elsif ($opt_key eq 'octoprint_host') {
|
||||
$self->{btn_send_gcode}->Show($config->get('octoprint_host'));
|
||||
} elsif ($opt_key eq 'print_host') {
|
||||
$self->{btn_send_gcode}->Show($config->get('print_host'));
|
||||
$self->Layout;
|
||||
} elsif ($opt_key eq 'variable_layer_height') {
|
||||
if ($config->get('variable_layer_height') != 1) {
|
||||
|
@ -1946,7 +1991,7 @@ sub list_item_deselected {
|
|||
$self->{_lecursor} = Wx::BusyCursor->new();
|
||||
if ($self->{list}->GetFirstSelected == -1) {
|
||||
$self->select_object(undef);
|
||||
$self->{canvas}->Refresh;
|
||||
# $self->{canvas}->Refresh;
|
||||
Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D}) if $self->{canvas3D};
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas3D}) if $self->{canvas3D};
|
||||
}
|
||||
|
@ -1959,7 +2004,7 @@ sub list_item_selected {
|
|||
$self->{_lecursor} = Wx::BusyCursor->new();
|
||||
my $obj_idx = $event->GetIndex;
|
||||
$self->select_object($obj_idx);
|
||||
$self->{canvas}->Refresh;
|
||||
# $self->{canvas}->Refresh;
|
||||
if ($self->{canvas3D}) {
|
||||
my $selections = $self->collect_selections;
|
||||
Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections);
|
||||
|
@ -2056,19 +2101,19 @@ sub object_settings_dialog {
|
|||
$self->pause_background_process;
|
||||
$dlg->ShowModal;
|
||||
|
||||
# update thumbnail since parts may have changed
|
||||
if ($dlg->PartsChanged) {
|
||||
# recenter and re-align to Z = 0
|
||||
$model_object->center_around_origin;
|
||||
$self->reset_thumbnail($obj_idx);
|
||||
}
|
||||
# # update thumbnail since parts may have changed
|
||||
# if ($dlg->PartsChanged) {
|
||||
# # recenter and re-align to Z = 0
|
||||
# $model_object->center_around_origin;
|
||||
# $self->reset_thumbnail($obj_idx);
|
||||
# }
|
||||
|
||||
# update print
|
||||
if ($dlg->PartsChanged || $dlg->PartSettingsChanged) {
|
||||
$self->stop_background_process;
|
||||
$self->{print}->reload_object($obj_idx);
|
||||
$self->schedule_background_process;
|
||||
$self->{canvas}->reload_scene if $self->{canvas};
|
||||
# $self->{canvas}->reload_scene if $self->{canvas};
|
||||
my $selections = $self->collect_selections;
|
||||
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
|
||||
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 0);
|
||||
|
@ -2100,7 +2145,8 @@ sub object_list_changed {
|
|||
|
||||
my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file};
|
||||
my $model_fits = $self->{canvas3D} ? Slic3r::GUI::_3DScene::check_volumes_outside_state($self->{canvas3D}, $self->{config}) : 1;
|
||||
my $method = ($have_objects && ! $export_in_progress && $model_fits) ? 'Enable' : 'Disable';
|
||||
# $model_fits == 1 -> ModelInstance::PVS_Partly_Outside
|
||||
my $method = ($have_objects && ! $export_in_progress && ($model_fits != 1)) ? 'Enable' : 'Disable';
|
||||
$self->{"btn_$_"}->$method
|
||||
for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode);
|
||||
}
|
||||
|
@ -2353,48 +2399,48 @@ package Slic3r::GUI::Plater::Object;
|
|||
use Moo;
|
||||
|
||||
has 'name' => (is => 'rw', required => 1);
|
||||
has 'thumbnail' => (is => 'rw'); # ExPolygon::Collection in scaled model units with no transforms
|
||||
has 'transformed_thumbnail' => (is => 'rw');
|
||||
has 'instance_thumbnails' => (is => 'ro', default => sub { [] }); # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units
|
||||
#has 'thumbnail' => (is => 'rw'); # ExPolygon::Collection in scaled model units with no transforms
|
||||
#has 'transformed_thumbnail' => (is => 'rw');
|
||||
#has 'instance_thumbnails' => (is => 'ro', default => sub { [] }); # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units
|
||||
has 'selected' => (is => 'rw', default => sub { 0 });
|
||||
|
||||
sub make_thumbnail {
|
||||
my ($self, $model, $obj_idx) = @_;
|
||||
# make method idempotent
|
||||
$self->thumbnail->clear;
|
||||
# raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
|
||||
my $mesh = $model->objects->[$obj_idx]->raw_mesh;
|
||||
#FIXME The "correct" variant could be extremely slow.
|
||||
# if ($mesh->facets_count <= 5000) {
|
||||
# # remove polygons with area <= 1mm
|
||||
# my $area_threshold = Slic3r::Geometry::scale 1;
|
||||
# $self->thumbnail->append(
|
||||
# grep $_->area >= $area_threshold,
|
||||
# @{ $mesh->horizontal_projection }, # horizontal_projection returns scaled expolygons
|
||||
# );
|
||||
# $self->thumbnail->simplify(0.5);
|
||||
# } else {
|
||||
my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull);
|
||||
$self->thumbnail->append($convex_hull);
|
||||
# }
|
||||
return $self->thumbnail;
|
||||
}
|
||||
|
||||
sub transform_thumbnail {
|
||||
my ($self, $model, $obj_idx) = @_;
|
||||
|
||||
return unless defined $self->thumbnail;
|
||||
|
||||
my $model_object = $model->objects->[$obj_idx];
|
||||
my $model_instance = $model_object->instances->[0];
|
||||
|
||||
# the order of these transformations MUST be the same everywhere, including
|
||||
# in Slic3r::Print->add_model_object()
|
||||
my $t = $self->thumbnail->clone;
|
||||
$t->rotate($model_instance->rotation, Slic3r::Point->new(0,0));
|
||||
$t->scale($model_instance->scaling_factor);
|
||||
|
||||
$self->transformed_thumbnail($t);
|
||||
}
|
||||
#sub make_thumbnail {
|
||||
# my ($self, $model, $obj_idx) = @_;
|
||||
# # make method idempotent
|
||||
# $self->thumbnail->clear;
|
||||
# # raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
|
||||
# my $mesh = $model->objects->[$obj_idx]->raw_mesh;
|
||||
##FIXME The "correct" variant could be extremely slow.
|
||||
## if ($mesh->facets_count <= 5000) {
|
||||
## # remove polygons with area <= 1mm
|
||||
## my $area_threshold = Slic3r::Geometry::scale 1;
|
||||
## $self->thumbnail->append(
|
||||
## grep $_->area >= $area_threshold,
|
||||
## @{ $mesh->horizontal_projection }, # horizontal_projection returns scaled expolygons
|
||||
## );
|
||||
## $self->thumbnail->simplify(0.5);
|
||||
## } else {
|
||||
# my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull);
|
||||
# $self->thumbnail->append($convex_hull);
|
||||
## }
|
||||
# return $self->thumbnail;
|
||||
#}
|
||||
#
|
||||
#sub transform_thumbnail {
|
||||
# my ($self, $model, $obj_idx) = @_;
|
||||
#
|
||||
# return unless defined $self->thumbnail;
|
||||
#
|
||||
# my $model_object = $model->objects->[$obj_idx];
|
||||
# my $model_instance = $model_object->instances->[0];
|
||||
#
|
||||
# # the order of these transformations MUST be the same everywhere, including
|
||||
# # in Slic3r::Print->add_model_object()
|
||||
# my $t = $self->thumbnail->clone;
|
||||
# $t->rotate($model_instance->rotation, Slic3r::Point->new(0,0));
|
||||
# $t->scale($model_instance->scaling_factor);
|
||||
#
|
||||
# $self->transformed_thumbnail($t);
|
||||
#}
|
||||
|
||||
1;
|
||||
|
|
|
@ -25,6 +25,7 @@ sub new {
|
|||
# init GUI elements
|
||||
my $canvas = Slic3r::GUI::3DScene->new($self);
|
||||
Slic3r::GUI::_3DScene::enable_shader($canvas, 1);
|
||||
Slic3r::GUI::_3DScene::set_config($canvas, $config);
|
||||
$self->canvas($canvas);
|
||||
my $slider_low = Wx::Slider->new(
|
||||
$self, -1,
|
||||
|
@ -365,16 +366,8 @@ sub load_print {
|
|||
if ($self->gcode_preview_data->empty) {
|
||||
# load skirt and brim
|
||||
Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);
|
||||
Slic3r::GUI::_3DScene::load_print_toolpaths($self->canvas);
|
||||
Slic3r::GUI::_3DScene::load_wipe_tower_toolpaths($self->canvas, \@colors);
|
||||
foreach my $object (@{$self->print->objects}) {
|
||||
Slic3r::GUI::_3DScene::load_print_object_toolpaths($self->canvas, $object, \@colors);
|
||||
# Show the objects in very transparent color.
|
||||
#my @volume_ids = $self->canvas->load_object($object->model_object);
|
||||
#$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids;
|
||||
}
|
||||
Slic3r::GUI::_3DScene::load_preview($self->canvas, \@colors);
|
||||
$self->show_hide_ui_elements('simple');
|
||||
Slic3r::GUI::_3DScene::reset_legend_texture();
|
||||
} else {
|
||||
$self->{force_sliders_full_range} = (Slic3r::GUI::_3DScene::get_volumes_count($self->canvas) == 0);
|
||||
Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);
|
||||
|
|
|
@ -137,7 +137,7 @@ sub new {
|
|||
# Adjust position / orientation of the split object halves.
|
||||
if ($self->{new_model_objects}{lower}) {
|
||||
if ($self->{cut_options}{rotate_lower}) {
|
||||
$self->{new_model_objects}{lower}->rotate(PI, X);
|
||||
$self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0));
|
||||
$self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ sub new {
|
|||
my ($volume_idx) = @_;
|
||||
$self->reload_tree($volume_idx);
|
||||
});
|
||||
Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
|
||||
Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
|
||||
$canvas->SetSize([500,700]);
|
||||
|
@ -377,7 +377,8 @@ sub on_btn_load {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$self->{model_object}->center_around_origin if $self->{parts_changed};
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
|
@ -479,7 +480,8 @@ sub on_btn_delete {
|
|||
$self->{model_object}->delete_volume($itemData->{volume_id});
|
||||
$self->{parts_changed} = 1;
|
||||
}
|
||||
|
||||
|
||||
$self->{model_object}->center_around_origin if $self->{parts_changed};
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 30 KiB |
BIN
resources/icons/overlay/layflat_hover.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/overlay/layflat_off.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
resources/icons/overlay/layflat_on.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 41 KiB |
BIN
resources/icons/printers/PrusaResearch_MK3MMU2.png
Normal file
After Width: | Height: | Size: 70 KiB |
|
@ -1,8 +1,20 @@
|
|||
min_slic3r_version = 1.41.0-alpha
|
||||
0.2.0-alpha2
|
||||
0.2.0-alpha1
|
||||
0.2.0-alpha
|
||||
0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles
|
||||
0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets.
|
||||
0.2.0-alpha7 Fixed the *MK3* references
|
||||
0.2.0-alpha6
|
||||
0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2
|
||||
0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers.
|
||||
0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost
|
||||
0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material
|
||||
0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0
|
||||
0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters
|
||||
min_slic3r_version = 1.40.0
|
||||
0.1.11 fw version changed to 3.3.1
|
||||
0.1.10 MK3 jerk and acceleration update
|
||||
0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles
|
||||
0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes
|
||||
0.1.7 Fixed errors in 0.25mm and 0.6mm profiles
|
||||
0.1.6 Split the MK2.5 profile from the MK2S
|
||||
min_slic3r_version = 1.40.0-beta
|
||||
0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the Slic3r configuration to be downgraded.
|
||||
config_version = 0.2.0-alpha2
|
||||
config_version = 0.2.0-beta
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
|
||||
|
||||
|
@ -14,26 +14,27 @@ config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/ma
|
|||
#TODO: One day we may differentiate variants of the nozzles / hot ends,
|
||||
#for example by the melt zone size, or whether the nozzle is hardened.
|
||||
# Printer model name will be shown by the installation wizard.
|
||||
|
||||
[printer_model:MK3]
|
||||
name = Original Prusa i3 MK3
|
||||
variants = 0.4; 0.25; 0.6
|
||||
|
||||
[printer_model:MK2S]
|
||||
name = Original Prusa i3 MK2S
|
||||
variants = 0.4; 0.25; 0.6
|
||||
|
||||
[printer_model:MK2.5]
|
||||
name = Original Prusa i3 MK2.5
|
||||
variants = 0.4; 0.25; 0.6
|
||||
|
||||
[printer_model:MK2SMM]
|
||||
name = Original Prusa i3 MK2S Multi Material Upgrade
|
||||
variants = 0.4; 0.6
|
||||
[printer_model:MK2S]
|
||||
name = Original Prusa i3 MK2/S
|
||||
variants = 0.4; 0.25; 0.6
|
||||
|
||||
[printer_model:MK3MM2]
|
||||
name = Original Prusa i3 MK3 Multi Material Upgrade 2.0
|
||||
[printer_model:MK3MMU2]
|
||||
name = Original Prusa i3 MK3 MMU 2.0
|
||||
variants = 0.4
|
||||
|
||||
[printer_model:MK2SMM]
|
||||
name = Original Prusa i3 MK2/S MMU 1.0
|
||||
variants = 0.4; 0.6
|
||||
|
||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||
# not make it into the user interface.
|
||||
|
||||
|
@ -85,7 +86,7 @@ notes =
|
|||
overhangs = 0
|
||||
only_retract_when_crossing_perimeters = 0
|
||||
ooze_prevention = 0
|
||||
output_filename_format = [input_filename_base].gcode
|
||||
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}.gcode
|
||||
perimeters = 2
|
||||
perimeter_extruder = 1
|
||||
perimeter_extrusion_width = 0.45
|
||||
|
@ -94,6 +95,7 @@ print_settings_id =
|
|||
raft_layers = 0
|
||||
resolution = 0
|
||||
seam_position = nearest
|
||||
single_extruder_multi_material_priming = 1
|
||||
skirts = 1
|
||||
skirt_distance = 2
|
||||
skirt_height = 3
|
||||
|
@ -132,9 +134,13 @@ wipe_tower_bridging = 10
|
|||
wipe_tower_rotation_angle = 0
|
||||
wipe_tower_width = 60
|
||||
wipe_tower_x = 180
|
||||
wipe_tower_y = 140
|
||||
wipe_tower_y = 135
|
||||
xy_size_compensation = 0
|
||||
|
||||
[print:*MK3*]
|
||||
fill_pattern = grid
|
||||
single_extruder_multi_material_priming = 0
|
||||
|
||||
# Print parameters common to a 0.25mm diameter nozzle.
|
||||
[print:*0.25nozzle*]
|
||||
external_perimeter_extrusion_width = 0.25
|
||||
|
@ -210,9 +216,8 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
|
|||
infill_extrusion_width = 0.5
|
||||
|
||||
[print:0.05mm ULTRADETAIL MK3]
|
||||
inherits = *0.05mm*
|
||||
inherits = *0.05mm*; *MK3*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
|
||||
fill_pattern = grid
|
||||
top_infill_extrusion_width = 0.4
|
||||
|
||||
[print:0.05mm ULTRADETAIL 0.25 nozzle]
|
||||
|
@ -227,9 +232,8 @@ solid_infill_speed = 20
|
|||
support_material_speed = 20
|
||||
|
||||
[print:0.05mm ULTRADETAIL 0.25 nozzle MK3]
|
||||
inherits = *0.05mm*; *0.25nozzle*
|
||||
inherits = *0.05mm*; *0.25nozzle*; *MK3*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1
|
||||
fill_pattern = grid
|
||||
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
# XXX--- 0.10mm ---XXX
|
||||
|
@ -254,12 +258,11 @@ perimeter_speed = 50
|
|||
solid_infill_speed = 50
|
||||
|
||||
[print:0.10mm DETAIL MK3]
|
||||
inherits = *0.10mm*
|
||||
inherits = *0.10mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -281,12 +284,11 @@ solid_infill_speed = 40
|
|||
top_solid_infill_speed = 30
|
||||
|
||||
[print:0.10mm DETAIL 0.25 nozzle MK3]
|
||||
inherits = *0.10mm*; *0.25nozzle*
|
||||
inherits = *0.10mm*; *0.25nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -294,12 +296,11 @@ solid_infill_speed = 200
|
|||
top_solid_infill_speed = 50
|
||||
|
||||
[print:0.10mm DETAIL 0.6 nozzle MK3]
|
||||
inherits = *0.10mm*; *0.6nozzle*
|
||||
inherits = *0.10mm*; *0.6nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -360,12 +361,11 @@ inherits = *0.15mm*; *0.6nozzle*
|
|||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6
|
||||
|
||||
[print:0.15mm OPTIMAL MK3]
|
||||
inherits = *0.15mm*
|
||||
inherits = *0.15mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -392,12 +392,11 @@ support_material_with_sheath = 0
|
|||
support_material_xy_spacing = 80%
|
||||
|
||||
[print:0.15mm OPTIMAL 0.25 nozzle MK3]
|
||||
inherits = *0.15mm*; *0.25nozzle*
|
||||
inherits = *0.15mm*; *0.25nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -408,7 +407,7 @@ inherits = *common*
|
|||
bottom_solid_layers = 4
|
||||
bridge_flow_ratio = 0.95
|
||||
external_perimeter_speed = 40
|
||||
infill_acceleration = 2000
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 60
|
||||
layer_height = 0.2
|
||||
perimeter_acceleration = 800
|
||||
|
@ -418,44 +417,17 @@ top_infill_extrusion_width = 0.4
|
|||
top_solid_layers = 5
|
||||
|
||||
[print:0.15mm OPTIMAL 0.6 nozzle MK3]
|
||||
inherits = *0.15mm*; *0.6nozzle*
|
||||
inherits = *0.15mm*; *0.6nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
solid_infill_speed = 200
|
||||
top_solid_infill_speed = 50
|
||||
|
||||
[print:0.15mm OPTIMAL MK3 MMU2]
|
||||
inherits = 0.15mm OPTIMAL MK3
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
|
||||
bottom_solid_layers = 4
|
||||
external_perimeter_speed = 40
|
||||
fill_density = 10%
|
||||
infill_overlap = 15%
|
||||
perimeter_speed = 60
|
||||
small_perimeter_speed = 20
|
||||
support_material_threshold = 20
|
||||
top_solid_layers = 5
|
||||
|
||||
[print:0.20mm FAST MK3 MMU2]
|
||||
inherits = 0.20mm FAST MK3
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
|
||||
bridge_flow_ratio = 0.8
|
||||
external_perimeter_speed = 40
|
||||
fill_density = 15%
|
||||
infill_overlap = 35%
|
||||
infill_speed = 150
|
||||
perimeter_speed = 50
|
||||
small_perimeter_speed = 20
|
||||
solid_infill_speed = 150
|
||||
wipe_tower_x = 169
|
||||
wipe_tower_y = 137
|
||||
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
# XXX--- 0.20mm ---XXX
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
|
@ -473,12 +445,11 @@ support_material_speed = 60
|
|||
top_solid_infill_speed = 70
|
||||
|
||||
[print:0.20mm FAST MK3]
|
||||
inherits = *0.20mm*
|
||||
inherits = *0.20mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -511,12 +482,11 @@ support_material_with_sheath = 0
|
|||
support_material_xy_spacing = 80%
|
||||
|
||||
[print:0.20mm FAST 0.6 nozzle MK3]
|
||||
inherits = *0.20mm*; *0.6nozzle*
|
||||
inherits = *0.20mm*; *0.6nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6
|
||||
external_perimeter_speed = 35
|
||||
fill_pattern = grid
|
||||
infill_acceleration = 1500
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
|
@ -593,6 +563,7 @@ filament_cooling_moves = 4
|
|||
filament_cooling_initial_speed = 2.2
|
||||
filament_cooling_final_speed = 3.4
|
||||
filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
|
||||
filament_minimal_purge_on_wipe_tower = 5
|
||||
filament_cost = 0
|
||||
filament_density = 0
|
||||
filament_diameter = 1.75
|
||||
|
@ -680,6 +651,8 @@ inherits = *PLA*
|
|||
# For now, all but selected filaments are disabled for the MMU 2.0
|
||||
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material)
|
||||
extrusion_multiplier = 1.2
|
||||
filament_cost = 80.65
|
||||
filament_density = 4
|
||||
filament_colour = #804040
|
||||
filament_max_volumetric_speed = 10
|
||||
|
||||
|
@ -700,12 +673,16 @@ temperature = 270
|
|||
|
||||
[filament:ColorFabb PLA-PHA]
|
||||
inherits = *PLA*
|
||||
filament_cost = 55.5
|
||||
filament_density = 1.24
|
||||
|
||||
[filament:ColorFabb Woodfil]
|
||||
inherits = *PLA*
|
||||
# For now, all but selected filaments are disabled for the MMU 2.0
|
||||
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material)
|
||||
extrusion_multiplier = 1.2
|
||||
filament_cost = 62.9
|
||||
filament_density = 1.15
|
||||
filament_colour = #804040
|
||||
filament_max_volumetric_speed = 10
|
||||
first_layer_temperature = 200
|
||||
|
@ -714,7 +691,9 @@ temperature = 200
|
|||
|
||||
[filament:ColorFabb XT]
|
||||
inherits = *PET*
|
||||
filament_type = PLA
|
||||
filament_type = PET
|
||||
filament_cost = 62.9
|
||||
filament_density = 1.27
|
||||
first_layer_bed_temperature = 90
|
||||
first_layer_temperature = 260
|
||||
temperature = 270
|
||||
|
@ -722,6 +701,8 @@ temperature = 270
|
|||
[filament:ColorFabb XT-CF20]
|
||||
inherits = *PET*
|
||||
extrusion_multiplier = 1.2
|
||||
filament_cost = 80.65
|
||||
filament_density = 1.35
|
||||
filament_colour = #804040
|
||||
filament_max_volumetric_speed = 1
|
||||
first_layer_bed_temperature = 90
|
||||
|
@ -731,6 +712,8 @@ temperature = 260
|
|||
|
||||
[filament:ColorFabb nGen]
|
||||
inherits = *PET*
|
||||
filament_cost = 21.2
|
||||
filament_density = 1.2
|
||||
bridge_fan_speed = 40
|
||||
fan_always_on = 0
|
||||
fan_below_layer_time = 10
|
||||
|
@ -741,6 +724,8 @@ min_fan_speed = 20
|
|||
|
||||
[filament:ColorFabb nGen flex]
|
||||
inherits = *FLEX*
|
||||
filament_cost = 0
|
||||
filament_density = 1
|
||||
bed_temperature = 85
|
||||
bridge_fan_speed = 40
|
||||
cooling = 1
|
||||
|
@ -756,26 +741,36 @@ temperature = 260
|
|||
|
||||
[filament:E3D Edge]
|
||||
inherits = *PET*
|
||||
filament_cost = 0
|
||||
filament_density = 1.26
|
||||
filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG"
|
||||
|
||||
[filament:E3D PC-ABS]
|
||||
inherits = *ABS*
|
||||
filament_cost = 0
|
||||
filament_density = 1.05
|
||||
first_layer_temperature = 270
|
||||
temperature = 270
|
||||
|
||||
[filament:Fillamentum ABS]
|
||||
inherits = *ABS*
|
||||
filament_cost = 0
|
||||
filament_density = 1.04
|
||||
first_layer_temperature = 240
|
||||
temperature = 240
|
||||
|
||||
[filament:Fillamentum ASA]
|
||||
inherits = *ABS*
|
||||
filament_cost = 0
|
||||
filament_density = 1.04
|
||||
fan_always_on = 1
|
||||
first_layer_temperature = 265
|
||||
temperature = 265
|
||||
|
||||
[filament:Fillamentum CPE HG100 HM100]
|
||||
inherits = *PET*
|
||||
filament_cost = 0
|
||||
filament_density = 1.25
|
||||
filament_notes = "CPE HG100 , CPE HM100"
|
||||
first_layer_bed_temperature = 90
|
||||
first_layer_temperature = 275
|
||||
|
@ -788,6 +783,8 @@ inherits = *PLA*
|
|||
# For now, all but selected filaments are disabled for the MMU 2.0
|
||||
compatible_printers_condition = nozzle_diameter[0]>0.35 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material)
|
||||
extrusion_multiplier = 1.2
|
||||
filament_cost = 0
|
||||
filament_density = 1.15
|
||||
filament_colour = #804040
|
||||
filament_max_volumetric_speed = 10
|
||||
first_layer_temperature = 190
|
||||
|
@ -796,14 +793,20 @@ temperature = 190
|
|||
|
||||
[filament:Generic ABS]
|
||||
inherits = *ABS*
|
||||
filament_cost = 0
|
||||
filament_density = 1.04
|
||||
filament_notes = "List of materials tested with standart ABS print settings for MK2:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladeč ABS"
|
||||
|
||||
[filament:Generic PET]
|
||||
inherits = *PET*
|
||||
filament_cost = 0
|
||||
filament_density = 1.24
|
||||
filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG"
|
||||
|
||||
[filament:Generic PLA]
|
||||
inherits = *PLA*
|
||||
filament_cost = 0
|
||||
filament_density = 1.27
|
||||
filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
|
||||
|
||||
[filament:Polymaker PC-Max]
|
||||
|
@ -830,8 +833,27 @@ temperature = 195
|
|||
|
||||
[filament:Prusa ABS]
|
||||
inherits = *ABS*
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.08
|
||||
filament_notes = "List of materials tested with standart ABS print settings for MK2:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladeč ABS"
|
||||
|
||||
[filament:*ABS MMU2*]
|
||||
inherits = Prusa ABS
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
|
||||
filament_cooling_final_speed = 50
|
||||
filament_cooling_initial_speed = 10
|
||||
filament_cooling_moves = 5
|
||||
filament_loading_speed = 14
|
||||
filament_ramming_parameters = "120 110 5.32258 5.45161 5.67742 6 6.48387 7.12903 7.90323 8.70968 9.3871 9.83871 10.0968 10.2258| 0.05 5.30967 0.45 5.50967 0.95 6.1871 1.45 7.39677 1.95 9.05484 2.45 10 2.95 10.3098 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6";
|
||||
filament_load_time = 12
|
||||
filament_unload_time = 11
|
||||
|
||||
[filament:Generic ABS MMU2]
|
||||
inherits = *ABS MMU2*
|
||||
|
||||
[filament:Prusa ABS MMU2]
|
||||
inherits = *ABS MMU2*
|
||||
|
||||
[filament:Prusa HIPS]
|
||||
inherits = *ABS*
|
||||
bridge_fan_speed = 50
|
||||
|
@ -850,10 +872,14 @@ temperature = 220
|
|||
|
||||
[filament:Prusa PET]
|
||||
inherits = *PET*
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.27
|
||||
filament_notes = "List of manufacturers tested with standart PET print settings for MK2:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladeč PETG"
|
||||
|
||||
[filament:Prusa PLA]
|
||||
inherits = *PLA*
|
||||
filament_cost = 25.4
|
||||
filament_density = 1.24
|
||||
filament_notes = "List of materials tested with standart PLA print settings for MK2:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladeč PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH"
|
||||
|
||||
[filament:*PLA MMU2*]
|
||||
|
@ -864,6 +890,8 @@ filament_cooling_initial_speed = 10
|
|||
filament_cooling_moves = 7
|
||||
filament_loading_speed = 14
|
||||
filament_ramming_parameters = "120 110 4.03226 4.12903 4.25806 4.41935 4.58065 4.80645 5.35484 6.29032 7.58065 9.09677 10.5806 11.8387 12.6452 12.9677| 0.05 4.01935 0.45 4.15483 0.95 4.50968 1.45 4.94516 1.95 6.79677 2.45 9.87102 2.95 12.4388 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6"
|
||||
filament_load_time = 12
|
||||
filament_unload_time = 11
|
||||
|
||||
[filament:Generic PLA MMU2]
|
||||
inherits = *PLA MMU2*
|
||||
|
@ -873,6 +901,8 @@ inherits = *PLA MMU2*
|
|||
|
||||
[filament:SemiFlex or Flexfill 98A]
|
||||
inherits = *FLEX*
|
||||
filament_cost = 0
|
||||
filament_density = 1.22
|
||||
|
||||
[filament:Taulman Bridge]
|
||||
inherits = *common*
|
||||
|
@ -955,8 +985,9 @@ extruder_colour = #FFFF00
|
|||
extruder_offset = 0x0
|
||||
gcode_flavor = marlin
|
||||
silent_mode = 0
|
||||
remaining_times = 0
|
||||
machine_max_acceleration_e = 10000
|
||||
machine_max_acceleration_extruding = 1500
|
||||
machine_max_acceleration_extruding = 2000
|
||||
machine_max_acceleration_retracting = 1500
|
||||
machine_max_acceleration_x = 9000
|
||||
machine_max_acceleration_y = 9000
|
||||
|
@ -994,7 +1025,7 @@ retract_speed = 35
|
|||
serial_port =
|
||||
serial_speed = 250000
|
||||
single_extruder_multi_material = 0
|
||||
start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
toolchange_gcode =
|
||||
use_firmware_retraction = 0
|
||||
use_relative_e_distances = 1
|
||||
|
@ -1030,7 +1061,7 @@ printer_model = MK2SMM
|
|||
inherits = *multimaterial*
|
||||
end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM84 ; disable motors\n\n
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN
|
||||
start_gcode = M115 U3.1.0 ; tell printer latest fw version\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\nG92 E0.0
|
||||
start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\nG92 E0.0
|
||||
default_print_profile = 0.15mm OPTIMAL
|
||||
|
||||
[printer:*mm-multi*]
|
||||
|
@ -1039,7 +1070,7 @@ end_gcode = {if not has_wipe_tower}\n; Pull the filament into the cooling tubes.
|
|||
extruder_colour = #FFAA55;#5182DB;#4ECDD3;#FB7259
|
||||
nozzle_diameter = 0.4,0.4,0.4,0.4
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN
|
||||
start_gcode = M115 U3.1.0 ; tell printer latest fw version\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_wipe_tower}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\n{endif}\nG92 E0.0
|
||||
start_gcode = M115 U3.1.0 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_single_extruder_multi_material_priming}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG1 E-4 F1000.0\n{endif}\nG92 E0.0
|
||||
variable_layer_height = 0
|
||||
default_print_profile = 0.15mm OPTIMAL
|
||||
|
||||
|
@ -1099,17 +1130,20 @@ default_print_profile = 0.20mm NORMAL 0.6 nozzle
|
|||
[printer:Original Prusa i3 MK2.5]
|
||||
inherits = Original Prusa i3 MK2
|
||||
printer_model = MK2.5
|
||||
start_gcode = M115 U3.2.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.25 nozzle]
|
||||
inherits = Original Prusa i3 MK2 0.25 nozzle
|
||||
printer_model = MK2.5
|
||||
start_gcode = M115 U3.2.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.6 nozzle]
|
||||
inherits = Original Prusa i3 MK2 0.6 nozzle
|
||||
printer_model = MK2.5
|
||||
start_gcode = M115 U3.2.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
|
||||
# XXXXXXXXXXXXXXXXX
|
||||
# XXX--- MK3 ---XXX
|
||||
|
@ -1118,11 +1152,11 @@ start_gcode = M115 U3.2.1 ; tell printer latest fw version\nM83 ; extruder rela
|
|||
[printer:Original Prusa i3 MK3]
|
||||
inherits = *common*
|
||||
end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors
|
||||
machine_max_acceleration_e = 9000,9000
|
||||
machine_max_acceleration_extruding = 1250,960
|
||||
machine_max_acceleration_e = 5000,5000
|
||||
machine_max_acceleration_extruding = 1250,1250
|
||||
machine_max_acceleration_retracting = 1250,1250
|
||||
machine_max_acceleration_x = 1000,1000
|
||||
machine_max_acceleration_y = 1000,1000
|
||||
machine_max_acceleration_x = 1000,960
|
||||
machine_max_acceleration_y = 1000,960
|
||||
machine_max_acceleration_z = 1000,1000
|
||||
machine_max_feedrate_e = 120,120
|
||||
machine_max_feedrate_x = 200,172
|
||||
|
@ -1135,10 +1169,11 @@ machine_max_jerk_z = 0.4,0.4
|
|||
machine_min_extruding_rate = 0,0
|
||||
machine_min_travel_rate = 0,0
|
||||
silent_mode = 1
|
||||
remaining_times = 1
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
|
||||
retract_lift_below = 209
|
||||
max_print_height = 210
|
||||
start_gcode = M115 U3.3.0 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
|
||||
start_gcode = M115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
|
||||
printer_model = MK3
|
||||
default_print_profile = 0.15mm OPTIMAL MK3
|
||||
|
||||
|
@ -1161,19 +1196,18 @@ default_print_profile = 0.15mm OPTIMAL 0.6 nozzle MK3
|
|||
[printer:*mm2*]
|
||||
inherits = Original Prusa i3 MK3
|
||||
single_extruder_multi_material = 1
|
||||
max_print_height = 200
|
||||
cooling_tube_length = 10
|
||||
cooling_tube_retraction = 30
|
||||
parking_pos_retraction = 85
|
||||
retract_length_toolchange = 3
|
||||
extra_loading_move = -13
|
||||
printer_model = MK3MM2
|
||||
default_print_profile = 0.15mm OPTIMAL MK3 MMU2
|
||||
printer_model = MK3MMU2
|
||||
default_print_profile = 0.15mm OPTIMAL MK3
|
||||
default_filament_profile = Prusa PLA MMU2
|
||||
|
||||
[printer:Original Prusa i3 MK3 MMU2 Single]
|
||||
inherits = *mm2*
|
||||
start_gcode = M107\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n ; go outside print area\nG1 Y-3.0 F1000.0 \nG1 Z0.4 F1000\n; select extruder\nT?\n; initial load\nG1 X50 E15 F1073\nG1 X100 E10 F2000\nG1 Z0.3 F1000\n\nG92 E0.0\nG1 X240.0 E15.0 F2400.0 \nG1 Y-2.0 F1000.0\nG1 X100.0 E10 F1400.0 \nG1 Z0.20 F1000\nG1 X0.0 E4 F1000.0\n\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
start_gcode = M107\nM115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT?\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
|
||||
|
||||
[printer:Original Prusa i3 MK3 MMU2]
|
||||
|
@ -1182,11 +1216,11 @@ inherits = *mm2*
|
|||
# (for example the retract values) are duplicaed from the first value, so they do not need
|
||||
# to be defined explicitely.
|
||||
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
|
||||
extruder_colour = #FFFF00;#FFFFFF;#804040;#0000FF;#C0C0C0
|
||||
start_gcode = M107\n\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = G1 E-15.0000 F3000\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
|
||||
extruder_colour = #FF8000;#0080FF;#00FFFF;#FF4F4F;#9FFF9F
|
||||
start_gcode = M107\nM115 U3.3.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\n;M221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
|
||||
|
||||
# The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer.
|
||||
[obsolete_presets]
|
||||
print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3"
|
||||
print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2"
|
||||
filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm"
|
||||
|
|
|
@ -26,7 +26,7 @@ include_directories(${LIBDIR}/libslic3r)
|
|||
|
||||
if(WIN32)
|
||||
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
|
||||
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB)
|
||||
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601)
|
||||
# -D_ITERATOR_DEBUG_LEVEL)
|
||||
if(WIN10SDK_PATH)
|
||||
message("Building with Win10 Netfabb STL fixing service support")
|
||||
|
@ -136,6 +136,7 @@ add_library(libslic3r STATIC
|
|||
${LIBDIR}/libslic3r/Line.hpp
|
||||
${LIBDIR}/libslic3r/Model.cpp
|
||||
${LIBDIR}/libslic3r/Model.hpp
|
||||
${LIBDIR}/libslic3r/ModelArrange.hpp
|
||||
${LIBDIR}/libslic3r/MotionPlanner.cpp
|
||||
${LIBDIR}/libslic3r/MotionPlanner.hpp
|
||||
${LIBDIR}/libslic3r/MultiPoint.cpp
|
||||
|
@ -250,14 +251,22 @@ add_library(libslic3r_gui STATIC
|
|||
${LIBDIR}/slic3r/Utils/Http.hpp
|
||||
${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp
|
||||
${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp
|
||||
${LIBDIR}/slic3r/Utils/PrintHostSendDialog.cpp
|
||||
${LIBDIR}/slic3r/Utils/PrintHostSendDialog.hpp
|
||||
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
|
||||
${LIBDIR}/slic3r/Utils/OctoPrint.hpp
|
||||
${LIBDIR}/slic3r/Utils/Duet.cpp
|
||||
${LIBDIR}/slic3r/Utils/Duet.hpp
|
||||
${LIBDIR}/slic3r/Utils/PrintHost.cpp
|
||||
${LIBDIR}/slic3r/Utils/PrintHost.hpp
|
||||
${LIBDIR}/slic3r/Utils/Bonjour.cpp
|
||||
${LIBDIR}/slic3r/Utils/Bonjour.hpp
|
||||
${LIBDIR}/slic3r/Utils/PresetUpdater.cpp
|
||||
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
|
||||
${LIBDIR}/slic3r/Utils/Time.cpp
|
||||
${LIBDIR}/slic3r/Utils/Time.hpp
|
||||
${LIBDIR}/slic3r/Utils/HexFile.cpp
|
||||
${LIBDIR}/slic3r/Utils/HexFile.hpp
|
||||
${LIBDIR}/slic3r/IProgressIndicator.hpp
|
||||
${LIBDIR}/slic3r/AppController.hpp
|
||||
${LIBDIR}/slic3r/AppController.cpp
|
||||
|
@ -364,7 +373,7 @@ set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap)
|
|||
add_custom_command(
|
||||
OUTPUT ${MyTypemap}
|
||||
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/xsp/my.map
|
||||
COMMAND ${PERL_EXECUTABLE} -MExtUtils::Typemaps -MExtUtils::Typemaps::Basic -e "$typemap = ExtUtils::Typemaps->new(file => \"${CMAKE_CURRENT_LIST_DIR}/xsp/my.map\"); $typemap->merge(typemap => ExtUtils::Typemaps::Basic->new); $typemap->write(file => \"${MyTypemap}\")"
|
||||
COMMAND ${PERL5LIB_ENV_CMD} ${PERL_EXECUTABLE} -MExtUtils::Typemaps -MExtUtils::Typemaps::Basic -e "$typemap = ExtUtils::Typemaps->new(file => \"${CMAKE_CURRENT_LIST_DIR}/xsp/my.map\"); $typemap->merge(typemap => ExtUtils::Typemaps::Basic->new); $typemap->write(file => \"${MyTypemap}\")"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
|
@ -408,7 +417,7 @@ set(XS_XSP_FILES
|
|||
${XSP_DIR}/Surface.xsp
|
||||
${XSP_DIR}/SurfaceCollection.xsp
|
||||
${XSP_DIR}/TriangleMesh.xsp
|
||||
${XSP_DIR}/Utils_OctoPrint.xsp
|
||||
${XSP_DIR}/Utils_PrintHost.xsp
|
||||
${XSP_DIR}/Utils_PresetUpdater.xsp
|
||||
${XSP_DIR}/AppController.xsp
|
||||
${XSP_DIR}/XS.xsp
|
||||
|
@ -429,7 +438,8 @@ set(XS_MAIN_CPP ${CMAKE_CURRENT_BINARY_DIR}/XS.cpp)
|
|||
add_custom_command(
|
||||
OUTPUT ${XS_MAIN_CPP}
|
||||
DEPENDS ${MyTypemap} ${XS_XSP_FILES} ${CMAKE_CURRENT_LIST_DIR}/xsp/typemap.xspt
|
||||
COMMAND COMMAND xsubpp -typemap typemap -output ${XS_MAIN_CPP} -hiertype ${XS_MAIN_XS}
|
||||
COMMAND ${PERL5LIB_ENV_CMD} xsubpp -typemap typemap -output ${XS_MAIN_CPP} -hiertype ${XS_MAIN_XS}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# Define the Perl XS shared library.
|
||||
|
@ -456,7 +466,7 @@ if(APPLE)
|
|||
# Ignore undefined symbols of the perl interpreter, they will be found in the caller image.
|
||||
target_link_libraries(XS "-undefined dynamic_lookup")
|
||||
endif()
|
||||
target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude)
|
||||
target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver avrdude qhull)
|
||||
if(SLIC3R_PROFILE)
|
||||
target_link_libraries(XS Shiny)
|
||||
endif()
|
||||
|
@ -493,6 +503,14 @@ if (WIN32)
|
|||
target_compile_definitions(XS PRIVATE -DNOGDI -DNOMINMAX -DHAS_BOOL)
|
||||
endif ()
|
||||
|
||||
# SLIC3R_MSVC_PDB
|
||||
if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
||||
set_target_properties(XS PROPERTIES
|
||||
COMPILE_FLAGS "/Zi"
|
||||
LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"
|
||||
)
|
||||
endif()
|
||||
|
||||
## Configuration flags
|
||||
if (SLIC3R_GUI)
|
||||
message("Slic3r will be built with GUI support")
|
||||
|
@ -537,6 +555,10 @@ endif()
|
|||
|
||||
add_subdirectory(src/avrdude)
|
||||
|
||||
add_subdirectory(src/qhull)
|
||||
include_directories(${LIBDIR}/qhull/src)
|
||||
message(STATUS ${LIBDIR}/qhull/src)
|
||||
|
||||
## REQUIRED packages
|
||||
|
||||
# Find and configure boost
|
||||
|
@ -727,6 +749,7 @@ set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d"
|
|||
|
||||
add_subdirectory(${LIBDIR}/libnest2d)
|
||||
target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
|
||||
target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
|
||||
|
||||
message(STATUS "Libnest2D Libraries: ${LIBNEST2D_LIBRARIES}")
|
||||
target_link_libraries(libslic3r ${LIBNEST2D_LIBRARIES})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
|
||||
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
|
||||
|
@ -13,67 +14,74 @@ endif()
|
|||
|
||||
|
||||
set(AVRDUDE_SOURCES
|
||||
${LIBDIR}/avrdude/arduino.c
|
||||
${LIBDIR}/avrdude/avr.c
|
||||
# ${LIBDIR}/avrdude/avrftdi.c
|
||||
# ${LIBDIR}/avrdude/avrftdi_tpi.c
|
||||
${LIBDIR}/avrdude/avrpart.c
|
||||
${LIBDIR}/avrdude/avr910.c
|
||||
${LIBDIR}/avrdude/bitbang.c
|
||||
${LIBDIR}/avrdude/buspirate.c
|
||||
${LIBDIR}/avrdude/butterfly.c
|
||||
${LIBDIR}/avrdude/config.c
|
||||
${LIBDIR}/avrdude/config_gram.c
|
||||
# ${LIBDIR}/avrdude/confwin.c
|
||||
${LIBDIR}/avrdude/crc16.c
|
||||
# ${LIBDIR}/avrdude/dfu.c
|
||||
${LIBDIR}/avrdude/fileio.c
|
||||
# ${LIBDIR}/avrdude/flip1.c
|
||||
# ${LIBDIR}/avrdude/flip2.c
|
||||
# ${LIBDIR}/avrdude/ft245r.c
|
||||
# ${LIBDIR}/avrdude/jtagmkI.c
|
||||
# ${LIBDIR}/avrdude/jtagmkII.c
|
||||
# ${LIBDIR}/avrdude/jtag3.c
|
||||
${LIBDIR}/avrdude/lexer.c
|
||||
${LIBDIR}/avrdude/linuxgpio.c
|
||||
${LIBDIR}/avrdude/lists.c
|
||||
# ${LIBDIR}/avrdude/par.c
|
||||
${LIBDIR}/avrdude/pgm.c
|
||||
${LIBDIR}/avrdude/pgm_type.c
|
||||
${LIBDIR}/avrdude/pickit2.c
|
||||
${LIBDIR}/avrdude/pindefs.c
|
||||
# ${LIBDIR}/avrdude/ppi.c
|
||||
# ${LIBDIR}/avrdude/ppiwin.c
|
||||
${LIBDIR}/avrdude/safemode.c
|
||||
${LIBDIR}/avrdude/ser_avrdoper.c
|
||||
${LIBDIR}/avrdude/serbb_posix.c
|
||||
${LIBDIR}/avrdude/serbb_win32.c
|
||||
${LIBDIR}/avrdude/ser_posix.c
|
||||
${LIBDIR}/avrdude/ser_win32.c
|
||||
${LIBDIR}/avrdude/stk500.c
|
||||
${LIBDIR}/avrdude/stk500generic.c
|
||||
${LIBDIR}/avrdude/stk500v2.c
|
||||
${LIBDIR}/avrdude/term.c
|
||||
${LIBDIR}/avrdude/update.c
|
||||
# ${LIBDIR}/avrdude/usbasp.c
|
||||
# ${LIBDIR}/avrdude/usb_hidapi.c
|
||||
# ${LIBDIR}/avrdude/usb_libusb.c
|
||||
# ${LIBDIR}/avrdude/usbtiny.c
|
||||
${LIBDIR}/avrdude/wiring.c
|
||||
arduino.c
|
||||
avr.c
|
||||
# avrftdi.c
|
||||
# avrftdi_tpi.c
|
||||
avrpart.c
|
||||
avr910.c
|
||||
bitbang.c
|
||||
buspirate.c
|
||||
butterfly.c
|
||||
config.c
|
||||
config_gram.c
|
||||
# confwin.c
|
||||
crc16.c
|
||||
# dfu.c
|
||||
fileio.c
|
||||
# flip1.c
|
||||
# flip2.c
|
||||
# ft245r.c
|
||||
# jtagmkI.c
|
||||
# jtagmkII.c
|
||||
# jtag3.c
|
||||
lexer.c
|
||||
linuxgpio.c
|
||||
lists.c
|
||||
# par.c
|
||||
pgm.c
|
||||
pgm_type.c
|
||||
pickit2.c
|
||||
pindefs.c
|
||||
# ppi.c
|
||||
# ppiwin.c
|
||||
safemode.c
|
||||
ser_avrdoper.c
|
||||
serbb_posix.c
|
||||
serbb_win32.c
|
||||
ser_posix.c
|
||||
ser_win32.c
|
||||
stk500.c
|
||||
stk500generic.c
|
||||
stk500v2.c
|
||||
term.c
|
||||
update.c
|
||||
# usbasp.c
|
||||
# usb_hidapi.c
|
||||
# usb_libusb.c
|
||||
# usbtiny.c
|
||||
wiring.c
|
||||
|
||||
${LIBDIR}/avrdude/main.c
|
||||
${LIBDIR}/avrdude/avrdude-slic3r.hpp
|
||||
${LIBDIR}/avrdude/avrdude-slic3r.cpp
|
||||
main.c
|
||||
avrdude-slic3r.hpp
|
||||
avrdude-slic3r.cpp
|
||||
)
|
||||
if (WIN32)
|
||||
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
|
||||
${LIBDIR}/avrdude/windows/unistd.cpp
|
||||
${LIBDIR}/avrdude/windows/getopt.c
|
||||
windows/unistd.cpp
|
||||
windows/getopt.c
|
||||
)
|
||||
endif()
|
||||
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
|
||||
|
||||
set(STANDALONE_SOURCES
|
||||
main-standalone.c
|
||||
)
|
||||
add_executable(avrdude-slic3r ${STANDALONE_SOURCES})
|
||||
target_link_libraries(avrdude-slic3r avrdude)
|
||||
set_target_properties(avrdude-slic3r PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
|
||||
target_include_directories(avrdude SYSTEM PRIVATE ${LIBDIR}/avrdude/windows) # So that sources find the getopt.h windows drop-in
|
||||
target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in
|
||||
endif()
|
||||
|
|
54
xs/src/avrdude/Makefile.standalone
Normal file
|
@ -0,0 +1,54 @@
|
|||
|
||||
TARGET = avrdude-slic3r
|
||||
|
||||
SOURCES = \
|
||||
arduino.c \
|
||||
avr.c \
|
||||
avrpart.c \
|
||||
avr910.c \
|
||||
bitbang.c \
|
||||
buspirate.c \
|
||||
butterfly.c \
|
||||
config.c \
|
||||
config_gram.c \
|
||||
crc16.c \
|
||||
fileio.c \
|
||||
lexer.c \
|
||||
linuxgpio.c \
|
||||
lists.c \
|
||||
pgm.c \
|
||||
pgm_type.c \
|
||||
pickit2.c \
|
||||
pindefs.c \
|
||||
safemode.c \
|
||||
ser_avrdoper.c \
|
||||
serbb_posix.c \
|
||||
serbb_win32.c \
|
||||
ser_posix.c \
|
||||
ser_win32.c \
|
||||
stk500.c \
|
||||
stk500generic.c \
|
||||
stk500v2.c \
|
||||
term.c \
|
||||
update.c \
|
||||
wiring.c \
|
||||
main.c \
|
||||
main-standalone.c
|
||||
|
||||
OBJECTS = $(SOURCES:.c=.o)
|
||||
CFLAGS = -std=c99 -Wall -D_BSD_SOURCE -D_DEFAULT_SOURCE -O3 -DNDEBUG -fPIC
|
||||
LDFLAGS = -lm
|
||||
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) -o ./$@ $(OBJECTS) $(LDFLAGS)
|
||||
|
||||
$(OBJECTS): %.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
clean:
|
||||
$(RM) -f $(OBJECTS) $(TARGET)
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
#include <exception>
|
||||
|
||||
extern "C" {
|
||||
#include "ac_cfg.h"
|
||||
|
@ -28,6 +32,11 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress
|
|||
(*progress_fn)(task, progress);
|
||||
}
|
||||
|
||||
static void avrdude_oom_handler(const char *context, void *user_p)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
|
||||
// Private
|
||||
|
||||
|
@ -35,6 +44,8 @@ struct AvrDude::priv
|
|||
{
|
||||
std::string sys_config;
|
||||
std::deque<std::vector<std::string>> args;
|
||||
bool cancelled = false;
|
||||
int exit_code = 0;
|
||||
size_t current_args_set = 0;
|
||||
RunFn run_fn;
|
||||
MessageFn message_fn;
|
||||
|
@ -45,16 +56,22 @@ struct AvrDude::priv
|
|||
|
||||
priv(std::string &&sys_config) : sys_config(sys_config) {}
|
||||
|
||||
void set_handlers();
|
||||
void unset_handlers();
|
||||
int run_one(const std::vector<std::string> &args);
|
||||
int run();
|
||||
|
||||
struct HandlerGuard
|
||||
{
|
||||
priv &p;
|
||||
|
||||
HandlerGuard(priv &p) : p(p) { p.set_handlers(); }
|
||||
~HandlerGuard() { p.unset_handlers(); }
|
||||
};
|
||||
};
|
||||
|
||||
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
||||
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
|
||||
for (const auto &arg : args) {
|
||||
c_args.push_back(const_cast<char*>(arg.data()));
|
||||
}
|
||||
|
||||
void AvrDude::priv::set_handlers()
|
||||
{
|
||||
if (message_fn) {
|
||||
::avrdude_message_handler_set(avrdude_message_handler_closure, reinterpret_cast<void*>(&message_fn));
|
||||
} else {
|
||||
|
@ -67,10 +84,27 @@ int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
|||
::avrdude_progress_handler_set(nullptr, nullptr);
|
||||
}
|
||||
|
||||
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
|
||||
::avrdude_oom_handler_set(avrdude_oom_handler, nullptr);
|
||||
}
|
||||
|
||||
void AvrDude::priv::unset_handlers()
|
||||
{
|
||||
::avrdude_message_handler_set(nullptr, nullptr);
|
||||
::avrdude_progress_handler_set(nullptr, nullptr);
|
||||
::avrdude_oom_handler_set(nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
||||
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
||||
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
|
||||
for (const auto &arg : args) {
|
||||
c_args.push_back(const_cast<char*>(arg.data()));
|
||||
}
|
||||
|
||||
HandlerGuard guard(*this);
|
||||
|
||||
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -132,7 +166,7 @@ AvrDude& AvrDude::on_complete(CompleteFn fn)
|
|||
|
||||
int AvrDude::run_sync()
|
||||
{
|
||||
return p->run();
|
||||
return p ? p->run() : -1;
|
||||
}
|
||||
|
||||
AvrDude::Ptr AvrDude::run()
|
||||
|
@ -141,14 +175,46 @@ AvrDude::Ptr AvrDude::run()
|
|||
|
||||
if (self->p) {
|
||||
auto avrdude_thread = std::thread([self]() {
|
||||
if (self->p->run_fn) {
|
||||
self->p->run_fn();
|
||||
}
|
||||
try {
|
||||
if (self->p->run_fn) {
|
||||
self->p->run_fn(self);
|
||||
}
|
||||
|
||||
auto res = self->p->run();
|
||||
if (! self->p->cancelled) {
|
||||
self->p->exit_code = self->p->run();
|
||||
}
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn(res, self->p->current_args_set);
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn();
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
self->p->exit_code = EXIT_EXCEPTION;
|
||||
|
||||
static const char *msg = "An exception was thrown in the background thread:\n";
|
||||
|
||||
const char *what = ex.what();
|
||||
auto &message_fn = self->p->message_fn;
|
||||
if (message_fn) {
|
||||
message_fn(msg, sizeof(msg));
|
||||
message_fn(what, std::strlen(what));
|
||||
message_fn("\n", 1);
|
||||
}
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn();
|
||||
}
|
||||
} catch (...) {
|
||||
self->p->exit_code = EXIT_EXCEPTION;
|
||||
|
||||
static const char *msg = "An unkown exception was thrown in the background thread.\n";
|
||||
|
||||
if (self->p->message_fn) {
|
||||
self->p->message_fn(msg, sizeof(msg));
|
||||
}
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -160,7 +226,10 @@ AvrDude::Ptr AvrDude::run()
|
|||
|
||||
void AvrDude::cancel()
|
||||
{
|
||||
::avrdude_cancel();
|
||||
if (p) {
|
||||
p->cancelled = true;
|
||||
::avrdude_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void AvrDude::join()
|
||||
|
@ -170,5 +239,20 @@ void AvrDude::join()
|
|||
}
|
||||
}
|
||||
|
||||
bool AvrDude::cancelled()
|
||||
{
|
||||
return p ? p->cancelled : false;
|
||||
}
|
||||
|
||||
int AvrDude::exit_code()
|
||||
{
|
||||
return p ? p->exit_code : 0;
|
||||
}
|
||||
|
||||
size_t AvrDude::last_args_set()
|
||||
{
|
||||
return p ? p->current_args_set : 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -11,11 +11,16 @@ namespace Slic3r {
|
|||
class AvrDude
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
EXIT_SUCCEESS = 0,
|
||||
EXIT_EXCEPTION = -1000,
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<AvrDude> Ptr;
|
||||
typedef std::function<void()> RunFn;
|
||||
typedef std::function<void(Ptr /* avrdude */)> RunFn;
|
||||
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
|
||||
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
|
||||
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
|
||||
typedef std::function<void()> CompleteFn;
|
||||
|
||||
// Main c-tor, sys_config is the location of avrdude's main configuration file
|
||||
AvrDude(std::string sys_config);
|
||||
|
@ -31,7 +36,8 @@ public:
|
|||
AvrDude& push_args(std::vector<std::string> args);
|
||||
|
||||
// Set a callback to be called just after run() before avrdude is ran
|
||||
// This can be used to perform any needed setup tasks from the background thread.
|
||||
// This can be used to perform any needed setup tasks from the background thread,
|
||||
// and, optionally, to cancel by writing true to the `cancel` argument.
|
||||
// This has no effect when using run_sync().
|
||||
AvrDude& on_run(RunFn fn);
|
||||
|
||||
|
@ -48,11 +54,23 @@ public:
|
|||
// This has no effect when using run_sync().
|
||||
AvrDude& on_complete(CompleteFn fn);
|
||||
|
||||
// Perform AvrDude invocation(s) synchronously on the current thread
|
||||
int run_sync();
|
||||
|
||||
// Perform AvrDude invocation(s) on a background thread.
|
||||
// Current instance is moved into a shared_ptr which is returned (and also passed in on_run, if any).
|
||||
Ptr run();
|
||||
|
||||
// Cancel current operation
|
||||
void cancel();
|
||||
|
||||
// If there is a background thread and it is joinable, join() it,
|
||||
// that is, wait for it to finish.
|
||||
void join();
|
||||
|
||||
bool cancelled(); // Whether avrdude run was cancelled
|
||||
int exit_code(); // The exit code of the last invocation
|
||||
size_t last_args_set(); // Index of the last argument set that was processsed
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
|
|
@ -39,6 +39,12 @@ typedef void (*avrdude_progress_handler_t)(const char *task, unsigned progress,
|
|||
void avrdude_progress_handler_set(avrdude_progress_handler_t newhandler, void *user_p);
|
||||
void avrdude_progress_external(const char *task, unsigned progress);
|
||||
|
||||
// OOM handler
|
||||
typedef void (*avrdude_oom_handler_t)(const char *context, void *user_p);
|
||||
void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p);
|
||||
void avrdude_oom(const char *context);
|
||||
|
||||
|
||||
// Cancellation
|
||||
void avrdude_cancel();
|
||||
|
||||
|
|
|
@ -36,8 +36,9 @@ OPCODE * avr_new_opcode(void)
|
|||
|
||||
m = (OPCODE *)malloc(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_new_opcode(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("avr_new_opcode(): out of memory\n");
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
|
@ -56,8 +57,9 @@ static OPCODE * avr_dup_opcode(OPCODE * op)
|
|||
|
||||
m = (OPCODE *)malloc(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_dup_opcode(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("avr_dup_opcode(): out of memory\n");
|
||||
}
|
||||
|
||||
memcpy(m, op, sizeof(*m));
|
||||
|
@ -249,8 +251,9 @@ AVRMEM * avr_new_memtype(void)
|
|||
|
||||
m = (AVRMEM *)malloc(sizeof(*m));
|
||||
if (m == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_new_memtype(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("avr_new_memtype(): out of memory\n");
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
|
@ -300,9 +303,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
|
|||
if (m->buf != NULL) {
|
||||
n->buf = (unsigned char *)malloc(n->size);
|
||||
if (n->buf == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
n->size);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
// n->size);
|
||||
// exit(1);
|
||||
avrdude_oom("avr_dup_mem(): out of memory");
|
||||
}
|
||||
memcpy(n->buf, m->buf, n->size);
|
||||
}
|
||||
|
@ -310,9 +314,10 @@ AVRMEM * avr_dup_mem(AVRMEM * m)
|
|||
if (m->tags != NULL) {
|
||||
n->tags = (unsigned char *)malloc(n->size);
|
||||
if (n->tags == NULL) {
|
||||
avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
n->size);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "avr_dup_mem(): out of memory (memsize=%d)\n",
|
||||
// n->size);
|
||||
// exit(1);
|
||||
avrdude_oom("avr_dup_mem(): out of memory");
|
||||
}
|
||||
memcpy(n->tags, m->tags, n->size);
|
||||
}
|
||||
|
@ -441,8 +446,9 @@ AVRPART * avr_new_part(void)
|
|||
|
||||
p = (AVRPART *)malloc(sizeof(AVRPART));
|
||||
if (p == NULL) {
|
||||
avrdude_message(MSG_INFO, "new_part(): out of memory\n");
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "new_part(): out of memory\n");
|
||||
// exit(1);
|
||||
avrdude_oom("new_part(): out of memory\n");
|
||||
}
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
|
|
@ -1135,9 +1135,10 @@ static void buspirate_setup(struct programmer_t *pgm)
|
|||
{
|
||||
/* Allocate private data */
|
||||
if ((pgm->cookie = calloc(1, sizeof(struct pdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: buspirate_initpgm(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("buspirate_initpgm(): Out of memory allocating private data\n");
|
||||
}
|
||||
PDATA(pgm)->serial_recv_timeout = 100;
|
||||
}
|
||||
|
|
|
@ -63,9 +63,10 @@ struct pdata
|
|||
static void butterfly_setup(PROGRAMMER * pgm)
|
||||
{
|
||||
if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: butterfly_setup(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("butterfly_setup(): Out of memory allocating private data\n");
|
||||
}
|
||||
memset(pgm->cookie, 0, sizeof(struct pdata));
|
||||
}
|
||||
|
|
|
@ -98,11 +98,11 @@ static int fileio_num(struct fioparms * fio,
|
|||
char * filename, FILE * f, AVRMEM * mem, int size,
|
||||
FILEFMT fmt);
|
||||
|
||||
static int fmt_autodetect(char * fname, size_t offset);
|
||||
static int fmt_autodetect(char * fname, unsigned section);
|
||||
|
||||
|
||||
|
||||
static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset)
|
||||
static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
|
||||
{
|
||||
FILE *file;
|
||||
// On Windows we need to convert the filename to UTF-16
|
||||
|
@ -118,16 +118,38 @@ static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offse
|
|||
file = fopen(filename, mode);
|
||||
#endif
|
||||
|
||||
if (file != NULL) {
|
||||
// Some systems allow seeking past the end of file, so we need check for that first and disallow
|
||||
if (fseek(file, 0, SEEK_END) != 0
|
||||
|| offset >= ftell(file)
|
||||
|| fseek(file, offset, SEEK_SET) != 0
|
||||
) {
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
errno = EINVAL;
|
||||
if (file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Seek to the specified 'section'
|
||||
static const char *hex_terminator = ":00000001FF\r";
|
||||
unsigned terms_seen = 0;
|
||||
char buffer[MAX_LINE_LEN + 1];
|
||||
|
||||
while (terms_seen < section && fgets(buffer, MAX_LINE_LEN, file) != NULL) {
|
||||
size_t len = strlen(buffer);
|
||||
|
||||
if (buffer[len - 1] == '\n') {
|
||||
len--;
|
||||
buffer[len] = 0;
|
||||
}
|
||||
if (buffer[len - 1] != '\r') {
|
||||
buffer[len] = '\r';
|
||||
len++;
|
||||
buffer[len] = 0;
|
||||
}
|
||||
|
||||
if (strcmp(buffer, hex_terminator) == 0) {
|
||||
// Found a section terminator
|
||||
terms_seen++;
|
||||
}
|
||||
}
|
||||
|
||||
if (feof(file)) {
|
||||
// Section not found
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return file;
|
||||
|
@ -1392,7 +1414,7 @@ int fileio_setparms(int op, struct fioparms * fp,
|
|||
|
||||
|
||||
|
||||
static int fmt_autodetect(char * fname, size_t offset)
|
||||
static int fmt_autodetect(char * fname, unsigned section)
|
||||
{
|
||||
FILE * f;
|
||||
unsigned char buf[MAX_LINE_LEN];
|
||||
|
@ -1402,9 +1424,9 @@ static int fmt_autodetect(char * fname, size_t offset)
|
|||
int first = 1;
|
||||
|
||||
#if defined(WIN32NATIVE)
|
||||
f = fopen_and_seek(fname, "r", offset);
|
||||
f = fopen_and_seek(fname, "r", section);
|
||||
#else
|
||||
f = fopen_and_seek(fname, "rb", offset);
|
||||
f = fopen_and_seek(fname, "rb", section);
|
||||
#endif
|
||||
|
||||
if (f == NULL) {
|
||||
|
@ -1480,7 +1502,7 @@ static int fmt_autodetect(char * fname, size_t offset)
|
|||
|
||||
|
||||
int fileio(int op, char * filename, FILEFMT format,
|
||||
struct avrpart * p, char * memtype, int size, size_t offset)
|
||||
struct avrpart * p, char * memtype, int size, unsigned section)
|
||||
{
|
||||
int rc;
|
||||
FILE * f;
|
||||
|
@ -1539,7 +1561,7 @@ int fileio(int op, char * filename, FILEFMT format,
|
|||
return -1;
|
||||
}
|
||||
|
||||
format_detect = fmt_autodetect(fname, offset);
|
||||
format_detect = fmt_autodetect(fname, section);
|
||||
if (format_detect < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
|
||||
progname, fname);
|
||||
|
@ -1570,7 +1592,7 @@ int fileio(int op, char * filename, FILEFMT format,
|
|||
|
||||
if (format != FMT_IMM) {
|
||||
if (!using_stdio) {
|
||||
f = fopen_and_seek(fname, fio.mode, offset);
|
||||
f = fopen_and_seek(fname, fio.mode, section);
|
||||
if (f == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
|
||||
progname, fio.iodesc, fname, strerror(errno));
|
||||
|
|
|
@ -2834,7 +2834,8 @@ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
|
|||
n = (yy_size_t) (_yybytes_len + 2);
|
||||
buf = (char *) yyalloc( n );
|
||||
if ( ! buf )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
|
||||
// YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
|
||||
avrdude_oom("out of dynamic memory in yy_scan_bytes()");
|
||||
|
||||
for ( i = 0; i < _yybytes_len; ++i )
|
||||
buf[i] = yybytes[i];
|
||||
|
@ -2859,8 +2860,9 @@ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
|
|||
|
||||
static void yynoreturn yy_fatal_error (const char* msg )
|
||||
{
|
||||
fprintf( stderr, "%s\n", msg );
|
||||
exit( YY_EXIT_FAILURE );
|
||||
fprintf( stderr, "%s\n", msg );
|
||||
// exit( YY_EXIT_FAILURE );
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Redefine yyless() so it works in section 3 code. */
|
||||
|
|
|
@ -821,7 +821,7 @@ extern "C" {
|
|||
char * fmtstr(FILEFMT format);
|
||||
|
||||
int fileio(int op, char * filename, FILEFMT format,
|
||||
struct avrpart * p, char * memtype, int size, size_t offset);
|
||||
struct avrpart * p, char * memtype, int size, unsigned section);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -870,7 +870,7 @@ enum updateflags {
|
|||
typedef struct update_t {
|
||||
char * memtype;
|
||||
int op;
|
||||
size_t offset;
|
||||
unsigned section;
|
||||
char * filename;
|
||||
int format;
|
||||
} UPDATE;
|
||||
|
@ -882,7 +882,7 @@ extern "C" {
|
|||
extern UPDATE * parse_op(char * s);
|
||||
extern UPDATE * dup_update(UPDATE * upd);
|
||||
extern UPDATE * new_update(int op, char * memtype, int filefmt,
|
||||
char * filename, size_t offset);
|
||||
char * filename, unsigned section);
|
||||
extern void free_update(UPDATE * upd);
|
||||
extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
|
||||
enum updateflags flags);
|
||||
|
|
9
xs/src/avrdude/main-standalone.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "avrdude.h"
|
||||
|
||||
|
||||
static const char* SYS_CONFIG = "/etc/avrdude-slic3r.conf";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return avrdude_main(argc, argv, SYS_CONFIG);
|
||||
}
|
|
@ -144,6 +144,33 @@ void avrdude_progress_external(const char *task, unsigned progress)
|
|||
avrdude_progress_handler(task, progress, avrdude_progress_handler_user_p);
|
||||
}
|
||||
|
||||
static void avrdude_oom_handler_null(const char *context, void *user_p)
|
||||
{
|
||||
// Output a message and just exit
|
||||
fputs("avrdude: Out of memory: ", stderr);
|
||||
fputs(context, stderr);
|
||||
exit(99);
|
||||
}
|
||||
|
||||
static void *avrdude_oom_handler_user_p = NULL;
|
||||
static avrdude_oom_handler_t avrdude_oom_handler = avrdude_oom_handler_null;
|
||||
|
||||
void avrdude_oom_handler_set(avrdude_oom_handler_t newhandler, void *user_p)
|
||||
{
|
||||
if (newhandler != NULL) {
|
||||
avrdude_oom_handler = newhandler;
|
||||
avrdude_oom_handler_user_p = user_p;
|
||||
} else {
|
||||
avrdude_oom_handler = avrdude_oom_handler_null;
|
||||
avrdude_oom_handler_user_p = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void avrdude_oom(const char *context)
|
||||
{
|
||||
avrdude_oom_handler(context, avrdude_oom_handler_user_p);
|
||||
}
|
||||
|
||||
void avrdude_cancel()
|
||||
{
|
||||
cancel_flag = true;
|
||||
|
@ -194,7 +221,7 @@ static void usage(void)
|
|||
" -F Override invalid signature check.\n"
|
||||
" -e Perform a chip erase.\n"
|
||||
" -O Perform RC oscillator calibration (see AVR053). \n"
|
||||
" -U <memtype>:r|w|v:<offset>:<filename>[:format]\n"
|
||||
" -U <memtype>:r|w|v:<section>:<filename>[:format]\n"
|
||||
" Memory operation specification.\n"
|
||||
" Multiple -U options are allowed, each request\n"
|
||||
" is performed in the order specified.\n"
|
||||
|
|
|
@ -172,9 +172,10 @@ PROGRAMMER * pgm_dup(const PROGRAMMER * const src)
|
|||
for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
|
||||
int *ip = malloc(sizeof(int));
|
||||
if (ip == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory allocating programmer structure\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("out of memory allocating programmer structure\n");
|
||||
}
|
||||
*ip = *(int *) ldata(ln);
|
||||
ladd(pgm->usbpid, ip);
|
||||
|
|
|
@ -246,10 +246,11 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
|||
newname = malloc(strlen("\\\\.\\") + strlen(port) + 1);
|
||||
|
||||
if (newname == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
// avrdude_message(MSG_INFO, "%s: ser_open(): out of memory\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("ser_open(): out of memory\n");
|
||||
}
|
||||
strcpy(newname, "\\\\.\\");
|
||||
strcat(newname, port);
|
||||
|
||||
|
@ -311,8 +312,10 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
|||
static void ser_close(union filedescriptor *fd)
|
||||
{
|
||||
if (serial_over_ethernet) {
|
||||
#ifdef HAVE_LIBWS2_32
|
||||
closesocket(fd->ifd);
|
||||
WSACleanup();
|
||||
#endif
|
||||
} else {
|
||||
HANDLE hComPort=(HANDLE)fd->pfd;
|
||||
if (hComPort != INVALID_HANDLE_VALUE)
|
||||
|
|
|
@ -295,9 +295,10 @@ static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p);
|
|||
void stk500v2_setup(PROGRAMMER * pgm)
|
||||
{
|
||||
if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: stk500v2_setup(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("stk500v2_setup(): Out of memory allocating private data\n");
|
||||
}
|
||||
memset(pgm->cookie, 0, sizeof(struct pdata));
|
||||
PDATA(pgm)->command_sequence = 1;
|
||||
|
|
|
@ -38,8 +38,9 @@ UPDATE * parse_op(char * s)
|
|||
|
||||
upd = (UPDATE *)malloc(sizeof(UPDATE));
|
||||
if (upd == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("parse_op: out of memory\n");
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
@ -53,8 +54,9 @@ UPDATE * parse_op(char * s)
|
|||
upd->op = DEVICE_WRITE;
|
||||
upd->filename = (char *)malloc(strlen(buf) + 1);
|
||||
if (upd->filename == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("parse_op: out of memory\n");
|
||||
}
|
||||
strcpy(upd->filename, buf);
|
||||
upd->format = FMT_AUTO;
|
||||
|
@ -63,8 +65,9 @@ UPDATE * parse_op(char * s)
|
|||
|
||||
upd->memtype = (char *)malloc(strlen(buf)+1);
|
||||
if (upd->memtype == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("parse_op: out of memory\n");
|
||||
}
|
||||
strcpy(upd->memtype, buf);
|
||||
|
||||
|
@ -101,22 +104,22 @@ UPDATE * parse_op(char * s)
|
|||
|
||||
p++;
|
||||
|
||||
// Extension: Parse file contents offset
|
||||
size_t offset = 0;
|
||||
// Extension: Parse file section number
|
||||
unsigned section = 0;
|
||||
|
||||
for (; *p != ':'; p++) {
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
offset *= 10;
|
||||
offset += *p - 0x30;
|
||||
section *= 10;
|
||||
section += *p - 0x30;
|
||||
} else {
|
||||
avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname);
|
||||
avrdude_message(MSG_INFO, "%s: invalid update specification: <section> is not a number\n", progname);
|
||||
free(upd->memtype);
|
||||
free(upd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
upd->offset = offset;
|
||||
upd->section = section;
|
||||
p++;
|
||||
|
||||
/*
|
||||
|
@ -179,8 +182,9 @@ UPDATE * dup_update(UPDATE * upd)
|
|||
|
||||
u = (UPDATE *)malloc(sizeof(UPDATE));
|
||||
if (u == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("dup_update: out of memory\n");
|
||||
}
|
||||
|
||||
memcpy(u, upd, sizeof(UPDATE));
|
||||
|
@ -194,21 +198,22 @@ UPDATE * dup_update(UPDATE * upd)
|
|||
return u;
|
||||
}
|
||||
|
||||
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset)
|
||||
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, unsigned section)
|
||||
{
|
||||
UPDATE * u;
|
||||
|
||||
u = (UPDATE *)malloc(sizeof(UPDATE));
|
||||
if (u == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: out of memory\n", progname);
|
||||
// exit(1);
|
||||
avrdude_oom("new_update: out of memory\n");
|
||||
}
|
||||
|
||||
u->memtype = strdup(memtype);
|
||||
u->filename = strdup(filename);
|
||||
u->op = op;
|
||||
u->format = filefmt;
|
||||
u->offset = offset;
|
||||
u->section = section;
|
||||
|
||||
return u;
|
||||
}
|
||||
|
@ -286,7 +291,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
|
|||
progname,
|
||||
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
|
||||
}
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
|
||||
if (rc < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
||||
progname, upd->filename);
|
||||
|
@ -351,7 +356,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
|
|||
progname, mem->desc, upd->filename);
|
||||
}
|
||||
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
|
||||
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
|
||||
if (rc < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
|
||||
progname, upd->filename);
|
||||
|
|
|
@ -85,9 +85,10 @@ static void wiring_setup(PROGRAMMER * pgm)
|
|||
* Now prepare our data
|
||||
*/
|
||||
if ((mycookie = malloc(sizeof(struct wiringpdata))) == 0) {
|
||||
avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
|
||||
progname);
|
||||
exit(1);
|
||||
// avrdude_message(MSG_INFO, "%s: wiring_setup(): Out of memory allocating private data\n",
|
||||
// progname);
|
||||
// exit(1);
|
||||
avrdude_oom("wiring_setup(): Out of memory allocating private data\n");
|
||||
}
|
||||
memset(mycookie, 0, sizeof(struct wiringpdata));
|
||||
WIRINGPDATA(mycookie)->snoozetime = 0;
|
||||
|
|
|
@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 2.8)
|
|||
|
||||
project(Libnest2D)
|
||||
|
||||
enable_testing()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
# Update if necessary
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long ")
|
||||
|
@ -32,6 +30,7 @@ set(LIBNEST2D_SRCFILES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/geometry_traits.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp
|
||||
|
@ -60,8 +59,7 @@ if(LIBNEST2D_GEOMETRIES_BACKEND STREQUAL "clipper")
|
|||
include_directories(BEFORE ${CLIPPER_INCLUDE_DIRS})
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.hpp
|
||||
list(APPEND LIBNEST2D_SRCFILES ${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/clipper_backend/clipper_backend.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/boost_alg.hpp)
|
||||
list(APPEND LIBNEST2D_LIBRARIES ${CLIPPER_LIBRARIES})
|
||||
list(APPEND LIBNEST2D_HEADERS ${CLIPPER_INCLUDE_DIRS}
|
||||
|
@ -81,22 +79,12 @@ if(LIBNEST2D_OPTIMIZER_BACKEND STREQUAL "nlopt")
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/subplex.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/genetic.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizers/nlopt_boilerplate.hpp)
|
||||
list(APPEND LIBNEST2D_LIBRARIES ${NLopt_LIBS}
|
||||
# Threads::Threads
|
||||
)
|
||||
list(APPEND LIBNEST2D_LIBRARIES ${NLopt_LIBS})
|
||||
list(APPEND LIBNEST2D_HEADERS ${NLopt_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
# Currently we are outsourcing the non-convex NFP implementation from
|
||||
# libnfporb and it needs libgmp to work
|
||||
#find_package(GMP)
|
||||
#if(GMP_FOUND)
|
||||
# list(APPEND LIBNEST2D_LIBRARIES ${GMP_LIBRARIES})
|
||||
# list(APPEND LIBNEST2D_HEADERS ${GMP_INCLUDE_DIR})
|
||||
# add_definitions(-DLIBNFP_USE_RATIONAL)
|
||||
#endif()
|
||||
|
||||
if(LIBNEST2D_UNITTESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -27,5 +27,6 @@ set(NLOPT_LINK_PYTHON OFF CACHE BOOL "" FORCE)
|
|||
add_subdirectory(${nlopt_SOURCE_DIR} ${nlopt_BINARY_DIR})
|
||||
|
||||
set(NLopt_LIBS nlopt)
|
||||
set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR})
|
||||
set(NLopt_INCLUDE_DIR ${nlopt_BINARY_DIR}
|
||||
${nlopt_BINARY_DIR}/src/api)
|
||||
set(SHARED_LIBS_STATE ${SHARED_STATE})
|
|
@ -1,35 +0,0 @@
|
|||
# Try to find the GMP libraries:
|
||||
# GMP_FOUND - System has GMP lib
|
||||
# GMP_INCLUDE_DIR - The GMP include directory
|
||||
# GMP_LIBRARIES - Libraries needed to use GMP
|
||||
|
||||
if (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
# Force search at every time, in case configuration changes
|
||||
unset(GMP_INCLUDE_DIR CACHE)
|
||||
unset(GMP_LIBRARIES CACHE)
|
||||
endif (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
|
||||
find_path(GMP_INCLUDE_DIR NAMES gmp.h)
|
||||
|
||||
if(WIN32)
|
||||
find_library(GMP_LIBRARIES NAMES libgmp.a gmp gmp.lib mpir mpir.lib)
|
||||
else(WIN32)
|
||||
if(STBIN)
|
||||
message(STATUS "STBIN: ${STBIN}")
|
||||
find_library(GMP_LIBRARIES NAMES libgmp.a gmp)
|
||||
else(STBIN)
|
||||
find_library(GMP_LIBRARIES NAMES libgmp.so gmp)
|
||||
endif(STBIN)
|
||||
endif(WIN32)
|
||||
|
||||
if(GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
set(GMP_FOUND TRUE)
|
||||
endif(GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
|
||||
if(GMP_FOUND)
|
||||
message(STATUS "Configured GMP: ${GMP_LIBRARIES}")
|
||||
else(GMP_FOUND)
|
||||
message(STATUS "Could NOT find GMP")
|
||||
endif(GMP_FOUND)
|
||||
|
||||
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES)
|
|
@ -535,19 +535,34 @@ void arrangeRectangles() {
|
|||
proba[0].rotate(Pi/3);
|
||||
proba[1].rotate(Pi-Pi/3);
|
||||
|
||||
// std::vector<Item> input(25, Rectangle(70*SCALE, 10*SCALE));
|
||||
std::vector<Item> input;
|
||||
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
|
||||
// input.insert(input.end(), prusaExParts().begin(), prusaExParts().end());
|
||||
input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
||||
// input.insert(input.end(), stegoParts().begin(), stegoParts().end());
|
||||
// input.insert(input.end(), rects.begin(), rects.end());
|
||||
input.insert(input.end(), proba.begin(), proba.end());
|
||||
// input.insert(input.end(), proba.begin(), proba.end());
|
||||
// input.insert(input.end(), crasher.begin(), crasher.end());
|
||||
|
||||
Box bin(250*SCALE, 210*SCALE);
|
||||
// PolygonImpl bin = {
|
||||
// {
|
||||
// {25*SCALE, 0},
|
||||
// {0, 25*SCALE},
|
||||
// {0, 225*SCALE},
|
||||
// {25*SCALE, 250*SCALE},
|
||||
// {225*SCALE, 250*SCALE},
|
||||
// {250*SCALE, 225*SCALE},
|
||||
// {250*SCALE, 25*SCALE},
|
||||
// {225*SCALE, 0},
|
||||
// {25*SCALE, 0}
|
||||
// },
|
||||
// {}
|
||||
// };
|
||||
|
||||
Coord min_obj_distance = 6*SCALE;
|
||||
auto min_obj_distance = static_cast<Coord>(0*SCALE);
|
||||
|
||||
using Placer = NfpPlacer;
|
||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, Box>;
|
||||
using Packer = Arranger<Placer, FirstFitSelection>;
|
||||
|
||||
Packer arrange(bin, min_obj_distance);
|
||||
|
@ -556,28 +571,107 @@ void arrangeRectangles() {
|
|||
pconf.alignment = Placer::Config::Alignment::CENTER;
|
||||
pconf.starting_point = Placer::Config::Alignment::CENTER;
|
||||
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
|
||||
pconf.object_function = [&bin](Placer::Pile pile, double area,
|
||||
double norm, double penality) {
|
||||
pconf.accuracy = 0.5f;
|
||||
|
||||
auto bb = ShapeLike::boundingBox(pile);
|
||||
// auto bincenter = ShapeLike::boundingBox(bin).center();
|
||||
// pconf.object_function = [&bin, bincenter](
|
||||
// Placer::Pile pile, const Item& item,
|
||||
// double /*area*/, double norm, double penality) {
|
||||
|
||||
auto& sh = pile.back();
|
||||
auto rv = Nfp::referenceVertex(sh);
|
||||
auto c = bin.center();
|
||||
auto d = PointLike::distance(rv, c);
|
||||
double score = double(d)/norm;
|
||||
// using pl = PointLike;
|
||||
|
||||
// If it does not fit into the print bed we will beat it
|
||||
// with a large penality
|
||||
if(!NfpPlacer::wouldFit(bb, bin)) score = 2*penality - score;
|
||||
// static const double BIG_ITEM_TRESHOLD = 0.2;
|
||||
// static const double GRAVITY_RATIO = 0.5;
|
||||
// static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO;
|
||||
|
||||
return score;
|
||||
};
|
||||
// // We will treat big items (compared to the print bed) differently
|
||||
// NfpPlacer::Pile bigs;
|
||||
// bigs.reserve(pile.size());
|
||||
// for(auto& p : pile) {
|
||||
// auto pbb = ShapeLike::boundingBox(p);
|
||||
// auto na = std::sqrt(pbb.width()*pbb.height())/norm;
|
||||
// if(na > BIG_ITEM_TRESHOLD) bigs.emplace_back(p);
|
||||
// }
|
||||
|
||||
// // Candidate item bounding box
|
||||
// auto ibb = item.boundingBox();
|
||||
|
||||
// // Calculate the full bounding box of the pile with the candidate item
|
||||
// pile.emplace_back(item.transformedShape());
|
||||
// auto fullbb = ShapeLike::boundingBox(pile);
|
||||
// pile.pop_back();
|
||||
|
||||
// // The bounding box of the big items (they will accumulate in the center
|
||||
// // of the pile
|
||||
// auto bigbb = bigs.empty()? fullbb : ShapeLike::boundingBox(bigs);
|
||||
|
||||
// // The size indicator of the candidate item. This is not the area,
|
||||
// // but almost...
|
||||
// auto itemnormarea = std::sqrt(ibb.width()*ibb.height())/norm;
|
||||
|
||||
// // Will hold the resulting score
|
||||
// double score = 0;
|
||||
|
||||
// if(itemnormarea > BIG_ITEM_TRESHOLD) {
|
||||
// // This branch is for the bigger items..
|
||||
// // Here we will use the closest point of the item bounding box to
|
||||
// // the already arranged pile. So not the bb center nor the a choosen
|
||||
// // corner but whichever is the closest to the center. This will
|
||||
// // prevent unwanted strange arrangements.
|
||||
|
||||
// auto minc = ibb.minCorner(); // bottom left corner
|
||||
// auto maxc = ibb.maxCorner(); // top right corner
|
||||
|
||||
// // top left and bottom right corners
|
||||
// auto top_left = PointImpl{getX(minc), getY(maxc)};
|
||||
// auto bottom_right = PointImpl{getX(maxc), getY(minc)};
|
||||
|
||||
// auto cc = fullbb.center(); // The gravity center
|
||||
|
||||
// // Now the distnce of the gravity center will be calculated to the
|
||||
// // five anchor points and the smallest will be chosen.
|
||||
// std::array<double, 5> dists;
|
||||
// dists[0] = pl::distance(minc, cc);
|
||||
// dists[1] = pl::distance(maxc, cc);
|
||||
// dists[2] = pl::distance(ibb.center(), cc);
|
||||
// dists[3] = pl::distance(top_left, cc);
|
||||
// dists[4] = pl::distance(bottom_right, cc);
|
||||
|
||||
// auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
||||
|
||||
// // Density is the pack density: how big is the arranged pile
|
||||
// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
|
||||
// // The score is a weighted sum of the distance from pile center
|
||||
// // and the pile size
|
||||
// score = GRAVITY_RATIO * dist + DENSITY_RATIO * density;
|
||||
|
||||
// } else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) {
|
||||
// // If there are no big items, only small, we should consider the
|
||||
// // density here as well to not get silly results
|
||||
// auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||
// auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
// score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density;
|
||||
// } else {
|
||||
// // Here there are the small items that should be placed around the
|
||||
// // already processed bigger items.
|
||||
// // No need to play around with the anchor points, the center will be
|
||||
// // just fine for small items
|
||||
// score = pl::distance(ibb.center(), bigbb.center()) / norm;
|
||||
// }
|
||||
|
||||
// // If it does not fit into the print bed we will beat it
|
||||
// // with a large penality. If we would not do this, there would be only
|
||||
// // one big pile that doesn't care whether it fits onto the print bed.
|
||||
// if(!NfpPlacer::wouldFit(fullbb, bin)) score = 2*penality - score;
|
||||
|
||||
// return score;
|
||||
// };
|
||||
|
||||
Packer::SelectionConfig sconf;
|
||||
// sconf.allow_parallel = false;
|
||||
// sconf.force_parallel = false;
|
||||
// sconf.try_triplets = false;
|
||||
// sconf.try_triplets = true;
|
||||
// sconf.try_reverse_order = true;
|
||||
// sconf.waste_increment = 0.005;
|
||||
|
||||
|
@ -613,7 +707,7 @@ void arrangeRectangles() {
|
|||
std::vector<double> eff;
|
||||
eff.reserve(result.size());
|
||||
|
||||
auto bin_area = double(bin.height()*bin.width());
|
||||
auto bin_area = ShapeLike::area<PolygonImpl>(bin);
|
||||
for(auto& r : result) {
|
||||
double a = 0;
|
||||
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); });
|
||||
|
@ -630,7 +724,7 @@ void arrangeRectangles() {
|
|||
<< " %" << std::endl;
|
||||
|
||||
std::cout << "Bin usage: (";
|
||||
unsigned total = 0;
|
||||
size_t total = 0;
|
||||
for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
|
||||
std::cout << ") Total: " << total << std::endl;
|
||||
|
||||
|
@ -643,10 +737,12 @@ void arrangeRectangles() {
|
|||
<< input.size() - total << " elements!"
|
||||
<< std::endl;
|
||||
|
||||
svg::SVGWriter::Config conf;
|
||||
using SVGWriter = svg::SVGWriter<PolygonImpl>;
|
||||
|
||||
SVGWriter::Config conf;
|
||||
conf.mm_in_coord_units = SCALE;
|
||||
svg::SVGWriter svgw(conf);
|
||||
svgw.setSize(bin);
|
||||
SVGWriter svgw(conf);
|
||||
svgw.setSize(Box(250*SCALE, 210*SCALE));
|
||||
svgw.writePackGroup(result);
|
||||
// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
|
||||
svgw.save("out");
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <libnest2d/clipper_backend/clipper_backend.hpp>
|
||||
|
||||
// We include the stock optimizers for local and global optimization
|
||||
#include <libnest2d/optimizers/simplex.hpp> // Local subplex for NfpPlacer
|
||||
#include <libnest2d/optimizers/subplex.hpp> // Local subplex for NfpPlacer
|
||||
#include <libnest2d/optimizers/genetic.hpp> // Genetic for min. bounding box
|
||||
|
||||
#include <libnest2d/libnest2d.hpp>
|
||||
|
|
|
@ -8,8 +8,16 @@
|
|||
#ifdef __clang__
|
||||
#undef _MSC_EXTENSIONS
|
||||
#endif
|
||||
#include <boost/geometry.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4267)
|
||||
#endif
|
||||
#include <boost/geometry.hpp>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
// this should be removed to not confuse the compiler
|
||||
// #include <libnest2d.h>
|
||||
|
||||
|
@ -350,7 +358,7 @@ inline double ShapeLike::area(const PolygonImpl& shape)
|
|||
#endif
|
||||
|
||||
template<>
|
||||
inline bool ShapeLike::isInside(const PointImpl& point,
|
||||
inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point,
|
||||
const PolygonImpl& shape)
|
||||
{
|
||||
return boost::geometry::within(point, shape);
|
||||
|
@ -461,15 +469,6 @@ inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
|
|||
}
|
||||
#endif
|
||||
|
||||
//#ifndef DISABLE_BOOST_MINKOWSKI_ADD
|
||||
//template<>
|
||||
//inline PolygonImpl& Nfp::minkowskiAdd(PolygonImpl& sh,
|
||||
// const PolygonImpl& /*other*/)
|
||||
//{
|
||||
// return sh;
|
||||
//}
|
||||
//#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_SERIALIZE
|
||||
template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
||||
const PolygonImpl& sh, double scale)
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
//#include "clipper_backend.hpp"
|
||||
//#include <atomic>
|
||||
|
||||
//namespace libnest2d {
|
||||
|
||||
//namespace {
|
||||
|
||||
//class SpinLock {
|
||||
// std::atomic_flag& lck_;
|
||||
//public:
|
||||
|
||||
// inline SpinLock(std::atomic_flag& flg): lck_(flg) {}
|
||||
|
||||
// inline void lock() {
|
||||
// while(lck_.test_and_set(std::memory_order_acquire)) {}
|
||||
// }
|
||||
|
||||
// inline void unlock() { lck_.clear(std::memory_order_release); }
|
||||
//};
|
||||
|
||||
//class HoleCache {
|
||||
// friend struct libnest2d::ShapeLike;
|
||||
|
||||
// std::unordered_map< const PolygonImpl*, ClipperLib::Paths> map;
|
||||
|
||||
// ClipperLib::Paths& _getHoles(const PolygonImpl* p) {
|
||||
// static std::atomic_flag flg = ATOMIC_FLAG_INIT;
|
||||
// SpinLock lock(flg);
|
||||
|
||||
// lock.lock();
|
||||
// ClipperLib::Paths& paths = map[p];
|
||||
// lock.unlock();
|
||||
|
||||
// if(paths.size() != p->Childs.size()) {
|
||||
// paths.reserve(p->Childs.size());
|
||||
|
||||
// for(auto np : p->Childs) {
|
||||
// paths.emplace_back(np->Contour);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return paths;
|
||||
// }
|
||||
|
||||
// ClipperLib::Paths& getHoles(PolygonImpl& p) {
|
||||
// return _getHoles(&p);
|
||||
// }
|
||||
|
||||
// const ClipperLib::Paths& getHoles(const PolygonImpl& p) {
|
||||
// return _getHoles(&p);
|
||||
// }
|
||||
//};
|
||||
//}
|
||||
|
||||
//HoleCache holeCache;
|
||||
|
||||
//}
|
||||
|
|
@ -21,7 +21,7 @@ struct PolygonImpl {
|
|||
PathImpl Contour;
|
||||
HoleStore Holes;
|
||||
|
||||
inline PolygonImpl() {}
|
||||
inline PolygonImpl() = default;
|
||||
|
||||
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
|
||||
inline explicit PolygonImpl(const HoleStore& holes):
|
||||
|
@ -66,6 +66,19 @@ inline PointImpl operator-(const PointImpl& p1, const PointImpl& p2) {
|
|||
ret -= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline PointImpl& operator *=(PointImpl& p, const PointImpl& pa ) {
|
||||
p.X *= pa.X;
|
||||
p.Y *= pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline PointImpl operator*(const PointImpl& p1, const PointImpl& p2) {
|
||||
PointImpl ret = p1;
|
||||
ret *= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace libnest2d {
|
||||
|
@ -135,7 +148,7 @@ inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity)
|
|||
|
||||
namespace _smartarea {
|
||||
template<Orientation o>
|
||||
inline double area(const PolygonImpl& sh) {
|
||||
inline double area(const PolygonImpl& /*sh*/) {
|
||||
return std::nan("");
|
||||
}
|
||||
|
||||
|
@ -220,22 +233,6 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
|
|||
}
|
||||
}
|
||||
|
||||
//template<> // TODO make it support holes if this method will ever be needed.
|
||||
//inline PolygonImpl Nfp::minkowskiDiff(const PolygonImpl& sh,
|
||||
// const PolygonImpl& other)
|
||||
//{
|
||||
// #define DISABLE_BOOST_MINKOWSKI_ADD
|
||||
|
||||
// ClipperLib::Paths solution;
|
||||
|
||||
// ClipperLib::MinkowskiDiff(sh.Contour, other.Contour, solution);
|
||||
|
||||
// PolygonImpl ret;
|
||||
// ret.Contour = solution.front();
|
||||
|
||||
// return sh;
|
||||
//}
|
||||
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
|
||||
std::stringstream ss;
|
||||
|
@ -406,35 +403,12 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
|||
}
|
||||
|
||||
#define DISABLE_BOOST_NFP_MERGE
|
||||
template<> inline Nfp::Shapes<PolygonImpl>
|
||||
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes, const PolygonImpl& sh)
|
||||
{
|
||||
inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
Nfp::Shapes<PolygonImpl> retv;
|
||||
|
||||
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
||||
|
||||
bool closed = true;
|
||||
bool valid = false;
|
||||
|
||||
valid = clipper.AddPath(sh.Contour, ClipperLib::ptSubject, closed);
|
||||
|
||||
for(auto& hole : sh.Holes) {
|
||||
valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
|
||||
for(auto& path : shapes) {
|
||||
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
|
||||
for(auto& hole : path.Holes) {
|
||||
valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
}
|
||||
|
||||
if(!valid) throw GeometryException(GeomErr::MERGE);
|
||||
|
||||
ClipperLib::PolyTree result;
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);
|
||||
retv.reserve(result.Total());
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
|
||||
retv.reserve(static_cast<size_t>(result.Total()));
|
||||
|
||||
std::function<void(ClipperLib::PolyNode*, PolygonImpl&)> processHole;
|
||||
|
||||
|
@ -445,7 +419,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes, const PolygonImpl& sh)
|
|||
retv.push_back(poly);
|
||||
};
|
||||
|
||||
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly) {
|
||||
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly)
|
||||
{
|
||||
poly.Holes.push_back(pptr->Contour);
|
||||
poly.Holes.back().push_back(poly.Holes.back().front());
|
||||
for(auto c : pptr->Childs) processPoly(c);
|
||||
|
@ -463,6 +438,27 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes, const PolygonImpl& sh)
|
|||
return retv;
|
||||
}
|
||||
|
||||
template<> inline Nfp::Shapes<PolygonImpl>
|
||||
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
|
||||
{
|
||||
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
||||
|
||||
bool closed = true;
|
||||
bool valid = true;
|
||||
|
||||
for(auto& path : shapes) {
|
||||
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
|
||||
for(auto& hole : path.Holes) {
|
||||
valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
}
|
||||
|
||||
if(!valid) throw GeometryException(GeomErr::MERGE);
|
||||
|
||||
return _merge(clipper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//#define DISABLE_BOOST_SERIALIZE
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L
|
||||
#define BP2D_NOEXCEPT
|
||||
#define BP2D_CONSTEXPR
|
||||
#define BP2D_COMPILER_MSVC12
|
||||
#elif __cplusplus >= 201103L
|
||||
#define BP2D_NOEXCEPT noexcept
|
||||
#define BP2D_CONSTEXPR constexpr
|
||||
|
@ -84,44 +85,6 @@ struct invoke_result {
|
|||
template<class F, class...Args>
|
||||
using invoke_result_t = typename invoke_result<F, Args...>::type;
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* C++14 std::index_sequence implementation: */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* \brief C++11 conformant implementation of the index_sequence type from C++14
|
||||
*/
|
||||
template<size_t...Ints> struct index_sequence {
|
||||
using value_type = size_t;
|
||||
BP2D_CONSTEXPR value_type size() const { return sizeof...(Ints); }
|
||||
};
|
||||
|
||||
// A Help structure to generate the integer list
|
||||
template<size_t...Nseq> struct genSeq;
|
||||
|
||||
// Recursive template to generate the list
|
||||
template<size_t I, size_t...Nseq> struct genSeq<I, Nseq...> {
|
||||
// Type will contain a genSeq with Nseq appended by one element
|
||||
using Type = typename genSeq< I - 1, I - 1, Nseq...>::Type;
|
||||
};
|
||||
|
||||
// Terminating recursion
|
||||
template <size_t ... Nseq> struct genSeq<0, Nseq...> {
|
||||
// If I is zero, Type will contain index_sequence with the fuly generated
|
||||
// integer list.
|
||||
using Type = index_sequence<Nseq...>;
|
||||
};
|
||||
|
||||
/// Helper alias to make an index sequence from 0 to N
|
||||
template<size_t N> using make_index_sequence = typename genSeq<N>::Type;
|
||||
|
||||
/// Helper alias to make an index sequence for a parameter pack
|
||||
template<class...Args>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Args)>;
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* A useful little tool for triggering static_assert error messages e.g. when
|
||||
* a mandatory template specialization (implementation) is missing.
|
||||
|
@ -229,7 +192,7 @@ public:
|
|||
|
||||
GeomErr errcode() const { return errcode_; }
|
||||
|
||||
virtual const char * what() const BP2D_NOEXCEPT override {
|
||||
const char * what() const BP2D_NOEXCEPT override {
|
||||
return errorstr(errcode_).c_str();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
@ -68,7 +69,7 @@ class _Box: PointPair<RawPoint> {
|
|||
using PointPair<RawPoint>::p2;
|
||||
public:
|
||||
|
||||
inline _Box() {}
|
||||
inline _Box() = default;
|
||||
inline _Box(const RawPoint& p, const RawPoint& pp):
|
||||
PointPair<RawPoint>({p, pp}) {}
|
||||
|
||||
|
@ -85,6 +86,31 @@ public:
|
|||
inline TCoord<RawPoint> height() const BP2D_NOEXCEPT;
|
||||
|
||||
inline RawPoint center() const BP2D_NOEXCEPT;
|
||||
|
||||
inline double area() const BP2D_NOEXCEPT {
|
||||
return double(width()*height());
|
||||
}
|
||||
};
|
||||
|
||||
template<class RawPoint>
|
||||
class _Circle {
|
||||
RawPoint center_;
|
||||
double radius_ = 0;
|
||||
public:
|
||||
|
||||
_Circle() = default;
|
||||
|
||||
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
|
||||
|
||||
inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; }
|
||||
inline const void center(const RawPoint& c) { center_ = c; }
|
||||
|
||||
inline double radius() const BP2D_NOEXCEPT { return radius_; }
|
||||
inline void radius(double r) { radius_ = r; }
|
||||
|
||||
inline double area() const BP2D_NOEXCEPT {
|
||||
return 2.0*Pi*radius_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -97,7 +123,7 @@ class _Segment: PointPair<RawPoint> {
|
|||
mutable Radians angletox_ = std::nan("");
|
||||
public:
|
||||
|
||||
inline _Segment() {}
|
||||
inline _Segment() = default;
|
||||
|
||||
inline _Segment(const RawPoint& p, const RawPoint& pp):
|
||||
PointPair<RawPoint>({p, pp}) {}
|
||||
|
@ -188,7 +214,7 @@ struct PointLike {
|
|||
|
||||
if( (y < y1 && y < y2) || (y > y1 && y > y2) )
|
||||
return {0, false};
|
||||
else if ((y == y1 && y == y2) && (x > x1 && x > x2))
|
||||
if ((y == y1 && y == y2) && (x > x1 && x > x2))
|
||||
ret = std::min( x-x1, x -x2);
|
||||
else if( (y == y1 && y == y2) && (x < x1 && x < x2))
|
||||
ret = -std::min(x1 - x, x2 - x);
|
||||
|
@ -214,7 +240,7 @@ struct PointLike {
|
|||
|
||||
if( (x < x1 && x < x2) || (x > x1 && x > x2) )
|
||||
return {0, false};
|
||||
else if ((x == x1 && x == x2) && (y > y1 && y > y2))
|
||||
if ((x == x1 && x == x2) && (y > y1 && y > y2))
|
||||
ret = std::min( y-y1, y -y2);
|
||||
else if( (x == x1 && x == x2) && (y < y1 && y < y2))
|
||||
ret = -std::min(y1 - y, y2 - y);
|
||||
|
@ -329,7 +355,7 @@ enum class Formats {
|
|||
};
|
||||
|
||||
// This struct serves as a namespace. The only difference is that it can be
|
||||
// used in friend declarations.
|
||||
// used in friend declarations and can be aliased at class scope.
|
||||
struct ShapeLike {
|
||||
|
||||
template<class RawShape>
|
||||
|
@ -361,6 +387,51 @@ struct ShapeLike {
|
|||
return create<RawShape>(contour, {});
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getHole(const RawShape& sh,
|
||||
unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static size_t holeCount(const RawShape& sh)
|
||||
{
|
||||
return holes(sh).size();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getContour(RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getContour(const RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
// Optional, does nothing by default
|
||||
template<class RawShape>
|
||||
static void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
|
||||
|
@ -402,7 +473,7 @@ struct ShapeLike {
|
|||
}
|
||||
|
||||
template<Formats, class RawShape>
|
||||
static std::string serialize(const RawShape& /*sh*/, double scale=1)
|
||||
static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::serialize() unimplemented!");
|
||||
|
@ -498,51 +569,6 @@ struct ShapeLike {
|
|||
return RawShape();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
||||
{
|
||||
static THolesContainer<RawShape> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getHole(const RawShape& sh,
|
||||
unsigned long idx)
|
||||
{
|
||||
return holes(sh)[idx];
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static size_t holeCount(const RawShape& sh)
|
||||
{
|
||||
return holes(sh).size();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static TContour<RawShape>& getContour(RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static const TContour<RawShape>& getContour(const RawShape& sh)
|
||||
{
|
||||
return sh;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
|
||||
{
|
||||
|
@ -614,6 +640,22 @@ struct ShapeLike {
|
|||
return box;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline _Box<TPoint<RawShape>> boundingBox(
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
TPoint<RawShape> pmin = {
|
||||
static_cast<Coord>(getX(circ.center()) - circ.radius()),
|
||||
static_cast<Coord>(getY(circ.center()) - circ.radius()) };
|
||||
|
||||
TPoint<RawShape> pmax = {
|
||||
static_cast<Coord>(getX(circ.center()) + circ.radius()),
|
||||
static_cast<Coord>(getY(circ.center()) + circ.radius()) };
|
||||
|
||||
return {pmin, pmax};
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline double area(const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
|
@ -621,14 +663,74 @@ struct ShapeLike {
|
|||
}
|
||||
|
||||
template<class RawShape>
|
||||
static double area(const Shapes<RawShape>& shapes)
|
||||
static inline double area(const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
double ret = 0;
|
||||
std::accumulate(shapes.first(), shapes.end(),
|
||||
[](const RawShape& a, const RawShape& b) {
|
||||
return area(a) + area(b);
|
||||
return circ.area();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static inline double area(const Shapes<RawShape>& shapes)
|
||||
{
|
||||
return std::accumulate(shapes.begin(), shapes.end(), 0.0,
|
||||
[](double a, const RawShape& b) {
|
||||
return a += area(b);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const TPoint<RawShape>& point,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return PointLike::distance(point, circ.center()) < circ.radius();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const TPoint<RawShape>& point,
|
||||
const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
auto px = getX(point);
|
||||
auto py = getY(point);
|
||||
auto minx = getX(box.minCorner());
|
||||
auto miny = getY(box.minCorner());
|
||||
auto maxx = getX(box.maxCorner());
|
||||
auto maxy = getY(box.maxCorner());
|
||||
|
||||
return px > minx && px < maxx && py > miny && py < maxy;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const RawShape& sh,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return std::all_of(cbegin(sh), cend(sh),
|
||||
[&circ](const TPoint<RawShape>& p){
|
||||
return isInside<RawShape>(p, circ);
|
||||
});
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const _Box<TPoint<RawShape>>& box,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
{
|
||||
return isInside<RawShape>(box.minCorner(), circ) &&
|
||||
isInside<RawShape>(box.maxCorner(), circ);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
static bool isInside(const _Box<TPoint<RawShape>>& ibb,
|
||||
const _Box<TPoint<RawShape>>& box)
|
||||
{
|
||||
auto iminX = getX(ibb.minCorner());
|
||||
auto imaxX = getX(ibb.maxCorner());
|
||||
auto iminY = getY(ibb.minCorner());
|
||||
auto imaxY = getY(ibb.maxCorner());
|
||||
|
||||
auto minX = getX(box.minCorner());
|
||||
auto maxX = getX(box.maxCorner());
|
||||
auto minY = getY(box.minCorner());
|
||||
auto maxY = getY(box.maxCorner());
|
||||
|
||||
return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY;
|
||||
}
|
||||
|
||||
template<class RawShape> // Potential O(1) implementation may exist
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#include "geometry_traits.hpp"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
|
@ -23,64 +25,22 @@ struct Nfp {
|
|||
template<class RawShape>
|
||||
using Shapes = typename ShapeLike::Shapes<RawShape>;
|
||||
|
||||
/// Minkowski addition (not used yet)
|
||||
/**
|
||||
* Merge a bunch of polygons with the specified additional polygon.
|
||||
*
|
||||
* \tparam RawShape the Polygon data type.
|
||||
* \param shc The pile of polygons that will be unified with sh.
|
||||
* \param sh A single polygon to unify with shc.
|
||||
*
|
||||
* \return A set of polygons that is the union of the input polygons. Note that
|
||||
* mostly it will be a set containing only one big polygon but if the input
|
||||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
static RawShape minkowskiDiff(const RawShape& sh, const RawShape& cother)
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||
{
|
||||
using Vertex = TPoint<RawShape>;
|
||||
//using Coord = TCoord<Vertex>;
|
||||
using Edge = _Segment<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
using std::signbit;
|
||||
|
||||
// Copy the orbiter (controur only), we will have to work on it
|
||||
RawShape orbiter = sl::create(sl::getContour(cother));
|
||||
|
||||
// Make the orbiter reverse oriented
|
||||
for(auto &v : sl::getContour(orbiter)) v = -v;
|
||||
|
||||
// An egde with additional data for marking it
|
||||
struct MarkedEdge { Edge e; Radians turn_angle; bool is_turning_point; };
|
||||
|
||||
// Container for marked edges
|
||||
using EdgeList = std::vector<MarkedEdge>;
|
||||
|
||||
EdgeList A, B;
|
||||
|
||||
auto fillEdgeList = [](EdgeList& L, const RawShape& poly) {
|
||||
L.reserve(sl::contourVertexCount(poly));
|
||||
|
||||
auto it = sl::cbegin(poly);
|
||||
auto nextit = std::next(it);
|
||||
|
||||
L.emplace_back({Edge(*it, *nextit), 0, false});
|
||||
it++; nextit++;
|
||||
|
||||
while(nextit != sl::cend(poly)) {
|
||||
Edge e(*it, *nextit);
|
||||
auto& L_prev = L.back();
|
||||
auto phi = L_prev.e.angleToXaxis();
|
||||
auto phi_prev = e.angleToXaxis();
|
||||
auto turn_angle = phi-phi_prev;
|
||||
if(turn_angle > Pi) turn_angle -= 2*Pi;
|
||||
L.emplace_back({
|
||||
e,
|
||||
turn_angle,
|
||||
signbit(turn_angle) != signbit(L_prev.turn_angle)
|
||||
});
|
||||
it++; nextit++;
|
||||
}
|
||||
|
||||
L.front().turn_angle = L.front().e.angleToXaxis() -
|
||||
L.back().e.angleToXaxis();
|
||||
|
||||
if(L.front().turn_angle > Pi) L.front().turn_angle -= 2*Pi;
|
||||
};
|
||||
|
||||
fillEdgeList(A, sh);
|
||||
fillEdgeList(B, orbiter);
|
||||
|
||||
return sh;
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"Nfp::merge(shapes, shape) unimplemented!");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,10 +55,12 @@ static RawShape minkowskiDiff(const RawShape& sh, const RawShape& cother)
|
|||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& shc, const RawShape& sh)
|
||||
static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
||||
const RawShape& sh)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"Nfp::merge(shapes, shape) unimplemented!");
|
||||
auto m = merge(shc);
|
||||
m.push_back(sh);
|
||||
return merge(m);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,16 +101,20 @@ template<class RawShape>
|
|||
static TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
|
||||
{
|
||||
|
||||
// find min x and min y vertex
|
||||
// find max x and max y vertex
|
||||
auto it = std::max_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
|
||||
_vsort<RawShape>);
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
|
||||
|
||||
/// Helper function to get the NFP
|
||||
template<NfpLevel nfptype, class RawShape>
|
||||
static RawShape noFitPolygon(const RawShape& sh, const RawShape& other)
|
||||
static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
||||
const RawShape& other)
|
||||
{
|
||||
NfpImpl<RawShape, nfptype> nfp;
|
||||
return nfp(sh, other);
|
||||
|
@ -167,44 +133,46 @@ static RawShape noFitPolygon(const RawShape& sh, const RawShape& other)
|
|||
* \tparam RawShape the Polygon data type.
|
||||
* \param sh The stationary polygon
|
||||
* \param cother The orbiting polygon
|
||||
* \return Returns the NFP of the two input polygons which have to be strictly
|
||||
* convex. The resulting NFP is proven to be convex as well in this case.
|
||||
* \return Returns a pair of the NFP and its reference vertex of the two input
|
||||
* polygons which have to be strictly convex. The resulting NFP is proven to be
|
||||
* convex as well in this case.
|
||||
*
|
||||
*/
|
||||
template<class RawShape>
|
||||
static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
|
||||
static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||
const RawShape& other)
|
||||
{
|
||||
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
|
||||
|
||||
RawShape other = cother;
|
||||
|
||||
// Make the other polygon counter-clockwise
|
||||
std::reverse(ShapeLike::begin(other), ShapeLike::end(other));
|
||||
using sl = ShapeLike;
|
||||
|
||||
RawShape rsh; // Final nfp placeholder
|
||||
Vertex top_nfp;
|
||||
std::vector<Edge> edgelist;
|
||||
|
||||
auto cap = ShapeLike::contourVertexCount(sh) +
|
||||
ShapeLike::contourVertexCount(other);
|
||||
auto cap = sl::contourVertexCount(sh) + sl::contourVertexCount(other);
|
||||
|
||||
// Reserve the needed memory
|
||||
edgelist.reserve(cap);
|
||||
ShapeLike::reserve(rsh, static_cast<unsigned long>(cap));
|
||||
sl::reserve(rsh, static_cast<unsigned long>(cap));
|
||||
|
||||
{ // place all edges from sh into edgelist
|
||||
auto first = ShapeLike::cbegin(sh);
|
||||
auto next = first + 1;
|
||||
auto endit = ShapeLike::cend(sh);
|
||||
auto first = sl::cbegin(sh);
|
||||
auto next = std::next(first);
|
||||
|
||||
while(next != endit) edgelist.emplace_back(*(first++), *(next++));
|
||||
while(next != sl::cend(sh)) {
|
||||
edgelist.emplace_back(*(first), *(next));
|
||||
++first; ++next;
|
||||
}
|
||||
}
|
||||
|
||||
{ // place all edges from other into edgelist
|
||||
auto first = ShapeLike::cbegin(other);
|
||||
auto next = first + 1;
|
||||
auto endit = ShapeLike::cend(other);
|
||||
auto first = sl::cbegin(other);
|
||||
auto next = std::next(first);
|
||||
|
||||
while(next != endit) edgelist.emplace_back(*(first++), *(next++));
|
||||
while(next != sl::cend(other)) {
|
||||
edgelist.emplace_back(*(next), *(first));
|
||||
++first; ++next;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the edges by angle to X axis.
|
||||
|
@ -215,10 +183,16 @@ static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
|
|||
});
|
||||
|
||||
// Add the two vertices from the first edge into the final polygon.
|
||||
ShapeLike::addVertex(rsh, edgelist.front().first());
|
||||
ShapeLike::addVertex(rsh, edgelist.front().second());
|
||||
sl::addVertex(rsh, edgelist.front().first());
|
||||
sl::addVertex(rsh, edgelist.front().second());
|
||||
|
||||
auto tmp = std::next(ShapeLike::begin(rsh));
|
||||
// Sorting function for the nfp reference vertex search
|
||||
auto& cmp = _vsort<RawShape>;
|
||||
|
||||
// the reference (rightmost top) vertex so far
|
||||
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
|
||||
|
||||
auto tmp = std::next(sl::begin(rsh));
|
||||
|
||||
// Construct final nfp by placing each edge to the end of the previous
|
||||
for(auto eit = std::next(edgelist.begin());
|
||||
|
@ -226,56 +200,325 @@ static RawShape nfpConvexOnly(const RawShape& sh, const RawShape& cother)
|
|||
++eit)
|
||||
{
|
||||
auto d = *tmp - eit->first();
|
||||
auto p = eit->second() + d;
|
||||
Vertex p = eit->second() + d;
|
||||
|
||||
ShapeLike::addVertex(rsh, p);
|
||||
sl::addVertex(rsh, p);
|
||||
|
||||
// Set the new reference vertex
|
||||
if(cmp(top_nfp, p)) top_nfp = p;
|
||||
|
||||
tmp = std::next(tmp);
|
||||
}
|
||||
|
||||
// Now we have an nfp somewhere in the dark. We need to get it
|
||||
// to the right position around the stationary shape.
|
||||
// This is done by choosing the leftmost lowest vertex of the
|
||||
// orbiting polygon to be touched with the rightmost upper
|
||||
// vertex of the stationary polygon. In this configuration, the
|
||||
// reference vertex of the orbiting polygon (which can be dragged around
|
||||
// the nfp) will be its rightmost upper vertex that coincides with the
|
||||
// rightmost upper vertex of the nfp. No proof provided other than Jonas
|
||||
// Lindmark's reasoning about the reference vertex of nfp in his thesis
|
||||
// ("No fit polygon problem" - section 2.1.9)
|
||||
return {rsh, top_nfp};
|
||||
}
|
||||
|
||||
// TODO: dont do this here. Cache the rmu and lmd in Item and get translate
|
||||
// the nfp after this call
|
||||
template<class RawShape>
|
||||
static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||
const RawShape& cother)
|
||||
{
|
||||
|
||||
auto csh = sh; // Copy sh, we will sort the verices in the copy
|
||||
auto& cmp = _vsort<RawShape>;
|
||||
std::sort(ShapeLike::begin(csh), ShapeLike::end(csh), cmp);
|
||||
std::sort(ShapeLike::begin(other), ShapeLike::end(other), cmp);
|
||||
// Algorithms are from the original algorithm proposed in paper:
|
||||
// https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
|
||||
|
||||
// leftmost lower vertex of the stationary polygon
|
||||
auto& touch_sh = *(std::prev(ShapeLike::end(csh)));
|
||||
// rightmost upper vertex of the orbiting polygon
|
||||
auto& touch_other = *(ShapeLike::begin(other));
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Algorithm 1: Obtaining the minkowski sum
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculate the difference and move the orbiter to the touch position.
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto top_other = *(std::prev(ShapeLike::end(other))) + dtouch;
|
||||
// I guess this is not a full minkowski sum of the two input polygons by
|
||||
// definition. This yields a subset that is compatible with the next 2
|
||||
// algorithms.
|
||||
|
||||
// Get the righmost upper vertex of the nfp and move it to the RMU of
|
||||
// the orbiter because they should coincide.
|
||||
auto&& top_nfp = rightmostUpVertex(rsh);
|
||||
auto dnfp = top_other - top_nfp;
|
||||
std::for_each(ShapeLike::begin(rsh), ShapeLike::end(rsh),
|
||||
[&dnfp](Vertex& v) { v+= dnfp; } );
|
||||
using Result = NfpResult<RawShape>;
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Coord = TCoord<Vertex>;
|
||||
using Edge = _Segment<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
using std::signbit;
|
||||
using std::sort;
|
||||
using std::vector;
|
||||
using std::ref;
|
||||
using std::reference_wrapper;
|
||||
|
||||
return rsh;
|
||||
// TODO The original algorithms expects the stationary polygon in
|
||||
// counter clockwise and the orbiter in clockwise order.
|
||||
// So for preventing any further complication, I will make the input
|
||||
// the way it should be, than make my way around the orientations.
|
||||
|
||||
// Reverse the stationary contour to counter clockwise
|
||||
auto stcont = sl::getContour(cstationary);
|
||||
std::reverse(stcont.begin(), stcont.end());
|
||||
RawShape stationary;
|
||||
sl::getContour(stationary) = stcont;
|
||||
|
||||
// Reverse the orbiter contour to counter clockwise
|
||||
auto orbcont = sl::getContour(cother);
|
||||
|
||||
std::reverse(orbcont.begin(), orbcont.end());
|
||||
|
||||
// Copy the orbiter (contour only), we will have to work on it
|
||||
RawShape orbiter;
|
||||
sl::getContour(orbiter) = orbcont;
|
||||
|
||||
// Step 1: Make the orbiter reverse oriented
|
||||
for(auto &v : sl::getContour(orbiter)) v = -v;
|
||||
|
||||
// An egde with additional data for marking it
|
||||
struct MarkedEdge {
|
||||
Edge e; Radians turn_angle = 0; bool is_turning_point = false;
|
||||
MarkedEdge() = default;
|
||||
MarkedEdge(const Edge& ed, Radians ta, bool tp):
|
||||
e(ed), turn_angle(ta), is_turning_point(tp) {}
|
||||
};
|
||||
|
||||
// Container for marked edges
|
||||
using EdgeList = vector<MarkedEdge>;
|
||||
|
||||
EdgeList A, B;
|
||||
|
||||
// This is how an edge list is created from the polygons
|
||||
auto fillEdgeList = [](EdgeList& L, const RawShape& poly, int dir) {
|
||||
L.reserve(sl::contourVertexCount(poly));
|
||||
|
||||
auto it = sl::cbegin(poly);
|
||||
auto nextit = std::next(it);
|
||||
|
||||
double turn_angle = 0;
|
||||
bool is_turn_point = false;
|
||||
|
||||
while(nextit != sl::cend(poly)) {
|
||||
L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
|
||||
it++; nextit++;
|
||||
}
|
||||
|
||||
auto getTurnAngle = [](const Edge& e1, const Edge& e2) {
|
||||
auto phi = e1.angleToXaxis();
|
||||
auto phi_prev = e2.angleToXaxis();
|
||||
auto TwoPi = 2.0*Pi;
|
||||
if(phi > Pi) phi -= TwoPi;
|
||||
if(phi_prev > Pi) phi_prev -= TwoPi;
|
||||
auto turn_angle = phi-phi_prev;
|
||||
if(turn_angle > Pi) turn_angle -= TwoPi;
|
||||
return phi-phi_prev;
|
||||
};
|
||||
|
||||
if(dir > 0) {
|
||||
auto eit = L.begin();
|
||||
auto enext = std::next(eit);
|
||||
|
||||
eit->turn_angle = getTurnAngle(L.front().e, L.back().e);
|
||||
|
||||
while(enext != L.end()) {
|
||||
enext->turn_angle = getTurnAngle( enext->e, eit->e);
|
||||
enext->is_turning_point =
|
||||
signbit(enext->turn_angle) != signbit(eit->turn_angle);
|
||||
++eit; ++enext;
|
||||
}
|
||||
|
||||
L.front().is_turning_point = signbit(L.front().turn_angle) !=
|
||||
signbit(L.back().turn_angle);
|
||||
} else {
|
||||
std::cout << L.size() << std::endl;
|
||||
|
||||
auto eit = L.rbegin();
|
||||
auto enext = std::next(eit);
|
||||
|
||||
eit->turn_angle = getTurnAngle(L.back().e, L.front().e);
|
||||
|
||||
while(enext != L.rend()) {
|
||||
enext->turn_angle = getTurnAngle(enext->e, eit->e);
|
||||
enext->is_turning_point =
|
||||
signbit(enext->turn_angle) != signbit(eit->turn_angle);
|
||||
std::cout << enext->is_turning_point << " " << enext->turn_angle << std::endl;
|
||||
|
||||
++eit; ++enext;
|
||||
}
|
||||
|
||||
L.back().is_turning_point = signbit(L.back().turn_angle) !=
|
||||
signbit(L.front().turn_angle);
|
||||
}
|
||||
};
|
||||
|
||||
// Step 2: Fill the edgelists
|
||||
fillEdgeList(A, stationary, 1);
|
||||
fillEdgeList(B, orbiter, -1);
|
||||
|
||||
// A reference to a marked edge that also knows its container
|
||||
struct MarkedEdgeRef {
|
||||
reference_wrapper<MarkedEdge> eref;
|
||||
reference_wrapper<vector<MarkedEdgeRef>> container;
|
||||
Coord dir = 1; // Direction modifier
|
||||
|
||||
inline Radians angleX() const { return eref.get().e.angleToXaxis(); }
|
||||
inline const Edge& edge() const { return eref.get().e; }
|
||||
inline Edge& edge() { return eref.get().e; }
|
||||
inline bool isTurningPoint() const {
|
||||
return eref.get().is_turning_point;
|
||||
}
|
||||
inline bool isFrom(const vector<MarkedEdgeRef>& cont ) {
|
||||
return &(container.get()) == &cont;
|
||||
}
|
||||
inline bool eq(const MarkedEdgeRef& mr) {
|
||||
return &(eref.get()) == &(mr.eref.get());
|
||||
}
|
||||
|
||||
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
|
||||
reference_wrapper<vector<MarkedEdgeRef>> ec):
|
||||
eref(er), container(ec), dir(1) {}
|
||||
|
||||
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
|
||||
reference_wrapper<vector<MarkedEdgeRef>> ec,
|
||||
Coord d):
|
||||
eref(er), container(ec), dir(d) {}
|
||||
};
|
||||
|
||||
using EdgeRefList = vector<MarkedEdgeRef>;
|
||||
|
||||
// Comparing two marked edges
|
||||
auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) {
|
||||
return e1.angleX() < e2.angleX();
|
||||
};
|
||||
|
||||
EdgeRefList Aref, Bref; // We create containers for the references
|
||||
Aref.reserve(A.size()); Bref.reserve(B.size());
|
||||
|
||||
// Fill reference container for the stationary polygon
|
||||
std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) {
|
||||
Aref.emplace_back( ref(me), ref(Aref) );
|
||||
});
|
||||
|
||||
// Fill reference container for the orbiting polygon
|
||||
std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) {
|
||||
Bref.emplace_back( ref(me), ref(Bref) );
|
||||
});
|
||||
|
||||
struct EdgeGroup { typename EdgeRefList::const_iterator first, last; };
|
||||
|
||||
auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure
|
||||
(const EdgeGroup& Q, const EdgeGroup& R, bool positive)
|
||||
{
|
||||
|
||||
// Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)"
|
||||
// Sort the containers of edge references and merge them.
|
||||
// Q could be sorted only once and be reused here but we would still
|
||||
// need to merge it with sorted(R).
|
||||
|
||||
EdgeRefList merged;
|
||||
EdgeRefList S, seq;
|
||||
merged.reserve((Q.last - Q.first) + (R.last - R.first));
|
||||
|
||||
merged.insert(merged.end(), Q.first, Q.last);
|
||||
merged.insert(merged.end(), R.first, R.last);
|
||||
sort(merged.begin(), merged.end(), sortfn);
|
||||
|
||||
// Step 2 "set i = 1, k = 1, direction = 1, s1 = q1"
|
||||
// we dont use i, instead, q is an iterator into Q. k would be an index
|
||||
// into the merged sequence but we use "it" as an iterator for that
|
||||
|
||||
// here we obtain references for the containers for later comparisons
|
||||
const auto& Rcont = R.first->container.get();
|
||||
const auto& Qcont = Q.first->container.get();
|
||||
|
||||
// Set the intial direction
|
||||
Coord dir = positive? 1 : -1;
|
||||
|
||||
// roughly i = 1 (so q = Q.first) and s1 = q1 so S[0] = q;
|
||||
auto q = Q.first;
|
||||
S.push_back(*q++);
|
||||
|
||||
// Roughly step 3
|
||||
while(q != Q.last) {
|
||||
auto it = merged.begin();
|
||||
while(it != merged.end() && !(it->eq(*(Q.first))) ) {
|
||||
if(it->isFrom(Rcont)) {
|
||||
auto s = *it;
|
||||
s.dir = dir;
|
||||
S.push_back(s);
|
||||
}
|
||||
if(it->eq(*q)) {
|
||||
S.push_back(*q);
|
||||
if(it->isTurningPoint()) dir = -dir;
|
||||
if(q != Q.first) it += dir;
|
||||
}
|
||||
else it += dir;
|
||||
}
|
||||
++q; // "Set i = i + 1"
|
||||
}
|
||||
|
||||
// Step 4:
|
||||
|
||||
// "Let starting edge r1 be in position si in sequence"
|
||||
// whaaat? I guess this means the following:
|
||||
S[0] = *R.first;
|
||||
auto it = S.begin();
|
||||
|
||||
// "Set j = 1, next = 2, direction = 1, seq1 = si"
|
||||
// we dont use j, seq is expanded dynamically.
|
||||
dir = 1; auto next = std::next(R.first);
|
||||
|
||||
// Step 5:
|
||||
// "If all si edges have been allocated to seqj" should mean that
|
||||
// we loop until seq has equal size with S
|
||||
while(seq.size() < S.size()) {
|
||||
++it; if(it == S.end()) it = S.begin();
|
||||
|
||||
if(it->isFrom(Qcont)) {
|
||||
seq.push_back(*it); // "If si is from Q, j = j + 1, seqj = si"
|
||||
|
||||
// "If si is a turning point in Q,
|
||||
// direction = - direction, next = next + direction"
|
||||
if(it->isTurningPoint()) { dir = -dir; next += dir; }
|
||||
}
|
||||
|
||||
if(it->eq(*next) && dir == next->dir) { // "If si = direction.rnext"
|
||||
// "j = j + 1, seqj = si, next = next + direction"
|
||||
seq.push_back(*it); next += dir;
|
||||
}
|
||||
}
|
||||
|
||||
return seq;
|
||||
};
|
||||
|
||||
EdgeGroup R{ Bref.begin(), Bref.begin() }, Q{ Aref.begin(), Aref.end() };
|
||||
auto it = Bref.begin();
|
||||
bool orientation = true;
|
||||
EdgeRefList seqlist;
|
||||
seqlist.reserve(3*(Aref.size() + Bref.size()));
|
||||
|
||||
while(it != Bref.end()) // This is step 3 and step 4 in one loop
|
||||
if(it->isTurningPoint()) {
|
||||
R = {R.last, it++};
|
||||
auto seq = mink(Q, R, orientation);
|
||||
|
||||
// TODO step 6 (should be 5 shouldn't it?): linking edges from A
|
||||
// I don't get this step
|
||||
|
||||
seqlist.insert(seqlist.end(), seq.begin(), seq.end());
|
||||
orientation = !orientation;
|
||||
} else ++it;
|
||||
|
||||
if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true);
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Algorithm 2: breaking Minkowski sums into track line trips
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Algorithm 3: finding the boundary of the NFP from track line trips
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
return Result(stationary, Vertex());
|
||||
}
|
||||
|
||||
// Specializable NFP implementation class. Specialize it if you have a faster
|
||||
// or better NFP implementation
|
||||
template<class RawShape, NfpLevel nfptype>
|
||||
struct NfpImpl {
|
||||
RawShape operator()(const RawShape& sh, const RawShape& other) {
|
||||
NfpResult<RawShape> operator()(const RawShape& sh, const RawShape& other)
|
||||
{
|
||||
static_assert(nfptype == NfpLevel::CONVEX_ONLY,
|
||||
"Nfp::noFitPolygon() unimplemented!");
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <functional>
|
||||
|
||||
#include "geometry_traits.hpp"
|
||||
#include "optimizer.hpp"
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
|
@ -27,6 +28,7 @@ class _Item {
|
|||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Box = _Box<Vertex>;
|
||||
using sl = ShapeLike;
|
||||
|
||||
// The original shape that gets encapsulated.
|
||||
RawShape sh_;
|
||||
|
@ -51,11 +53,18 @@ class _Item {
|
|||
|
||||
enum class Convexity: char {
|
||||
UNCHECKED,
|
||||
TRUE,
|
||||
FALSE
|
||||
C_TRUE,
|
||||
C_FALSE
|
||||
};
|
||||
|
||||
mutable Convexity convexity_ = Convexity::UNCHECKED;
|
||||
mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex
|
||||
mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex
|
||||
mutable bool rmt_valid_ = false, lmb_valid_ = false;
|
||||
mutable struct BBCache {
|
||||
Box bb; bool valid; Vertex tr;
|
||||
BBCache(): valid(false), tr(0, 0) {}
|
||||
} bb_cache_;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -104,15 +113,15 @@ public:
|
|||
* @param il The initializer list of vertices.
|
||||
*/
|
||||
inline _Item(const std::initializer_list< Vertex >& il):
|
||||
sh_(ShapeLike::create<RawShape>(il)) {}
|
||||
sh_(sl::create<RawShape>(il)) {}
|
||||
|
||||
inline _Item(const TContour<RawShape>& contour,
|
||||
const THolesContainer<RawShape>& holes = {}):
|
||||
sh_(ShapeLike::create<RawShape>(contour, holes)) {}
|
||||
sh_(sl::create<RawShape>(contour, holes)) {}
|
||||
|
||||
inline _Item(TContour<RawShape>&& contour,
|
||||
THolesContainer<RawShape>&& holes):
|
||||
sh_(ShapeLike::create<RawShape>(std::move(contour),
|
||||
sh_(sl::create<RawShape>(std::move(contour),
|
||||
std::move(holes))) {}
|
||||
|
||||
/**
|
||||
|
@ -122,31 +131,31 @@ public:
|
|||
*/
|
||||
inline std::string toString() const
|
||||
{
|
||||
return ShapeLike::toString(sh_);
|
||||
return sl::toString(sh_);
|
||||
}
|
||||
|
||||
/// Iterator tho the first contour vertex in the polygon.
|
||||
inline Iterator begin() const
|
||||
{
|
||||
return ShapeLike::cbegin(sh_);
|
||||
return sl::cbegin(sh_);
|
||||
}
|
||||
|
||||
/// Alias to begin()
|
||||
inline Iterator cbegin() const
|
||||
{
|
||||
return ShapeLike::cbegin(sh_);
|
||||
return sl::cbegin(sh_);
|
||||
}
|
||||
|
||||
/// Iterator to the last contour vertex.
|
||||
inline Iterator end() const
|
||||
{
|
||||
return ShapeLike::cend(sh_);
|
||||
return sl::cend(sh_);
|
||||
}
|
||||
|
||||
/// Alias to end()
|
||||
inline Iterator cend() const
|
||||
{
|
||||
return ShapeLike::cend(sh_);
|
||||
return sl::cend(sh_);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +170,7 @@ public:
|
|||
*/
|
||||
inline Vertex vertex(unsigned long idx) const
|
||||
{
|
||||
return ShapeLike::vertex(sh_, idx);
|
||||
return sl::vertex(sh_, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,7 +185,7 @@ public:
|
|||
inline void setVertex(unsigned long idx, const Vertex& v )
|
||||
{
|
||||
invalidateCache();
|
||||
ShapeLike::vertex(sh_, idx) = v;
|
||||
sl::vertex(sh_, idx) = v;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,7 +200,7 @@ public:
|
|||
double ret ;
|
||||
if(area_cache_valid_) ret = area_cache_;
|
||||
else {
|
||||
ret = ShapeLike::area(offsettedShape());
|
||||
ret = sl::area(offsettedShape());
|
||||
area_cache_ = ret;
|
||||
area_cache_valid_ = true;
|
||||
}
|
||||
|
@ -203,17 +212,17 @@ public:
|
|||
|
||||
switch(convexity_) {
|
||||
case Convexity::UNCHECKED:
|
||||
ret = ShapeLike::isConvex<RawShape>(ShapeLike::getContour(transformedShape()));
|
||||
convexity_ = ret? Convexity::TRUE : Convexity::FALSE;
|
||||
ret = sl::isConvex<RawShape>(sl::getContour(transformedShape()));
|
||||
convexity_ = ret? Convexity::C_TRUE : Convexity::C_FALSE;
|
||||
break;
|
||||
case Convexity::TRUE: ret = true; break;
|
||||
case Convexity::FALSE:;
|
||||
case Convexity::C_TRUE: ret = true; break;
|
||||
case Convexity::C_FALSE:;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool isHoleConvex(unsigned holeidx) const {
|
||||
inline bool isHoleConvex(unsigned /*holeidx*/) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -223,11 +232,11 @@ public:
|
|||
|
||||
/// The number of the outer ring vertices.
|
||||
inline size_t vertexCount() const {
|
||||
return ShapeLike::contourVertexCount(sh_);
|
||||
return sl::contourVertexCount(sh_);
|
||||
}
|
||||
|
||||
inline size_t holeCount() const {
|
||||
return ShapeLike::holeCount(sh_);
|
||||
return sl::holeCount(sh_);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,36 +244,39 @@ public:
|
|||
* @param p
|
||||
* @return
|
||||
*/
|
||||
inline bool isPointInside(const Vertex& p)
|
||||
inline bool isPointInside(const Vertex& p) const
|
||||
{
|
||||
return ShapeLike::isInside(p, sh_);
|
||||
return sl::isInside(p, transformedShape());
|
||||
}
|
||||
|
||||
inline bool isInside(const _Item& sh) const
|
||||
{
|
||||
return ShapeLike::isInside(transformedShape(), sh.transformedShape());
|
||||
return sl::isInside(transformedShape(), sh.transformedShape());
|
||||
}
|
||||
|
||||
inline bool isInside(const _Box<TPoint<RawShape>>& box);
|
||||
inline bool isInside(const RawShape& sh) const
|
||||
{
|
||||
return sl::isInside(transformedShape(), sh);
|
||||
}
|
||||
|
||||
inline bool isInside(const _Box<TPoint<RawShape>>& box) const;
|
||||
inline bool isInside(const _Circle<TPoint<RawShape>>& box) const;
|
||||
|
||||
inline void translate(const Vertex& d) BP2D_NOEXCEPT
|
||||
{
|
||||
translation_ += d; has_translation_ = true;
|
||||
tr_cache_valid_ = false;
|
||||
translation(translation() + d);
|
||||
}
|
||||
|
||||
inline void rotate(const Radians& rads) BP2D_NOEXCEPT
|
||||
{
|
||||
rotation_ += rads;
|
||||
has_rotation_ = true;
|
||||
tr_cache_valid_ = false;
|
||||
rotation(rotation() + rads);
|
||||
}
|
||||
|
||||
inline void addOffset(Coord distance) BP2D_NOEXCEPT
|
||||
{
|
||||
offset_distance_ = distance;
|
||||
has_offset_ = true;
|
||||
offset_cache_valid_ = false;
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
inline void removeOffset() BP2D_NOEXCEPT {
|
||||
|
@ -286,6 +298,8 @@ public:
|
|||
{
|
||||
if(rotation_ != rot) {
|
||||
rotation_ = rot; has_rotation_ = true; tr_cache_valid_ = false;
|
||||
rmt_valid_ = false; lmb_valid_ = false;
|
||||
bb_cache_.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,6 +307,7 @@ public:
|
|||
{
|
||||
if(translation_ != tr) {
|
||||
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
|
||||
bb_cache_.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -301,9 +316,10 @@ public:
|
|||
if(tr_cache_valid_) return tr_cache_;
|
||||
|
||||
RawShape cpy = offsettedShape();
|
||||
if(has_rotation_) ShapeLike::rotate(cpy, rotation_);
|
||||
if(has_translation_) ShapeLike::translate(cpy, translation_);
|
||||
if(has_rotation_) sl::rotate(cpy, rotation_);
|
||||
if(has_translation_) sl::translate(cpy, translation_);
|
||||
tr_cache_ = cpy; tr_cache_valid_ = true;
|
||||
rmt_valid_ = false; lmb_valid_ = false;
|
||||
|
||||
return tr_cache_;
|
||||
}
|
||||
|
@ -321,23 +337,53 @@ public:
|
|||
inline void resetTransformation() BP2D_NOEXCEPT
|
||||
{
|
||||
has_translation_ = false; has_rotation_ = false; has_offset_ = false;
|
||||
invalidateCache();
|
||||
}
|
||||
|
||||
inline Box boundingBox() const {
|
||||
return ShapeLike::boundingBox(transformedShape());
|
||||
if(!bb_cache_.valid) {
|
||||
bb_cache_.bb = sl::boundingBox(transformedShape());
|
||||
bb_cache_.tr = {0, 0};
|
||||
bb_cache_.valid = true;
|
||||
}
|
||||
|
||||
auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr;
|
||||
return {bb.minCorner() + tr, bb.maxCorner() + tr};
|
||||
}
|
||||
|
||||
inline Vertex referenceVertex() const {
|
||||
return rightmostTopVertex();
|
||||
}
|
||||
|
||||
inline Vertex rightmostTopVertex() const {
|
||||
if(!rmt_valid_ || !tr_cache_valid_) { // find max x and max y vertex
|
||||
auto& tsh = transformedShape();
|
||||
rmt_ = std::max_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
||||
rmt_valid_ = true;
|
||||
}
|
||||
return *rmt_;
|
||||
}
|
||||
|
||||
inline Vertex leftmostBottomVertex() const {
|
||||
if(!lmb_valid_ || !tr_cache_valid_) { // find min x and min y vertex
|
||||
auto& tsh = transformedShape();
|
||||
lmb_ = std::min_element(sl::cbegin(tsh), sl::cend(tsh), vsort);
|
||||
lmb_valid_ = true;
|
||||
}
|
||||
return *lmb_;
|
||||
}
|
||||
|
||||
//Static methods:
|
||||
|
||||
inline static bool intersects(const _Item& sh1, const _Item& sh2)
|
||||
{
|
||||
return ShapeLike::intersects(sh1.transformedShape(),
|
||||
return sl::intersects(sh1.transformedShape(),
|
||||
sh2.transformedShape());
|
||||
}
|
||||
|
||||
inline static bool touches(const _Item& sh1, const _Item& sh2)
|
||||
{
|
||||
return ShapeLike::touches(sh1.transformedShape(),
|
||||
return sl::touches(sh1.transformedShape(),
|
||||
sh2.transformedShape());
|
||||
}
|
||||
|
||||
|
@ -346,12 +392,11 @@ private:
|
|||
inline const RawShape& offsettedShape() const {
|
||||
if(has_offset_ ) {
|
||||
if(offset_cache_valid_) return offset_cache_;
|
||||
else {
|
||||
offset_cache_ = sh_;
|
||||
ShapeLike::offset(offset_cache_, offset_distance_);
|
||||
offset_cache_valid_ = true;
|
||||
return offset_cache_;
|
||||
}
|
||||
|
||||
offset_cache_ = sh_;
|
||||
sl::offset(offset_cache_, offset_distance_);
|
||||
offset_cache_valid_ = true;
|
||||
return offset_cache_;
|
||||
}
|
||||
return sh_;
|
||||
}
|
||||
|
@ -359,10 +404,23 @@ private:
|
|||
inline void invalidateCache() const BP2D_NOEXCEPT
|
||||
{
|
||||
tr_cache_valid_ = false;
|
||||
lmb_valid_ = false; rmt_valid_ = false;
|
||||
area_cache_valid_ = false;
|
||||
offset_cache_valid_ = false;
|
||||
bb_cache_.valid = false;
|
||||
convexity_ = Convexity::UNCHECKED;
|
||||
}
|
||||
|
||||
static inline bool vsort(const Vertex& v1, const Vertex& v2)
|
||||
{
|
||||
Coord &&x1 = getX(v1), &&x2 = getX(v2);
|
||||
Coord &&y1 = getY(v1), &&y2 = getY(v2);
|
||||
auto diff = y1 - y2;
|
||||
if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
|
||||
return x1 < x2;
|
||||
|
||||
return diff < 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -370,7 +428,6 @@ private:
|
|||
*/
|
||||
template<class RawShape>
|
||||
class _Rectangle: public _Item<RawShape> {
|
||||
RawShape sh_;
|
||||
using _Item<RawShape>::vertex;
|
||||
using TO = Orientation;
|
||||
public:
|
||||
|
@ -415,9 +472,13 @@ public:
|
|||
};
|
||||
|
||||
template<class RawShape>
|
||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) {
|
||||
_Rectangle<RawShape> rect(box.width(), box.height());
|
||||
return _Item<RawShape>::isInside(rect);
|
||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
||||
return ShapeLike::isInside<RawShape>(boundingBox(), box);
|
||||
}
|
||||
|
||||
template<class RawShape> inline bool
|
||||
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
||||
return ShapeLike::isInside<RawShape>(transformedShape(), circ);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -874,9 +935,8 @@ private:
|
|||
|
||||
Radians findBestRotation(Item& item) {
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.stoplimit = 0.01;
|
||||
stopcr.absolute_score_difference = 0.01;
|
||||
stopcr.max_iterations = 10000;
|
||||
stopcr.type = opt::StopLimitType::RELATIVE;
|
||||
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
|
||||
|
||||
auto orig_rot = item.rotation();
|
||||
|
@ -910,7 +970,6 @@ private:
|
|||
if(min_obj_distance_ > 0) std::for_each(from, to, [](Item& item) {
|
||||
item.removeOffset();
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
227
xs/src/libnest2d/libnest2d/metaloop.hpp
Normal file
|
@ -0,0 +1,227 @@
|
|||
#ifndef METALOOP_HPP
|
||||
#define METALOOP_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* C++14 std::index_sequence implementation: */
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* \brief C++11 conformant implementation of the index_sequence type from C++14
|
||||
*/
|
||||
template<size_t...Ints> struct index_sequence {
|
||||
using value_type = size_t;
|
||||
BP2D_CONSTEXPR value_type size() const { return sizeof...(Ints); }
|
||||
};
|
||||
|
||||
// A Help structure to generate the integer list
|
||||
template<size_t...Nseq> struct genSeq;
|
||||
|
||||
// Recursive template to generate the list
|
||||
template<size_t I, size_t...Nseq> struct genSeq<I, Nseq...> {
|
||||
// Type will contain a genSeq with Nseq appended by one element
|
||||
using Type = typename genSeq< I - 1, I - 1, Nseq...>::Type;
|
||||
};
|
||||
|
||||
// Terminating recursion
|
||||
template <size_t ... Nseq> struct genSeq<0, Nseq...> {
|
||||
// If I is zero, Type will contain index_sequence with the fuly generated
|
||||
// integer list.
|
||||
using Type = index_sequence<Nseq...>;
|
||||
};
|
||||
|
||||
/// Helper alias to make an index sequence from 0 to N
|
||||
template<size_t N> using make_index_sequence = typename genSeq<N>::Type;
|
||||
|
||||
/// Helper alias to make an index sequence for a parameter pack
|
||||
template<class...Args>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Args)>;
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
namespace opt {
|
||||
|
||||
using std::forward;
|
||||
using std::tuple;
|
||||
using std::get;
|
||||
using std::tuple_element;
|
||||
|
||||
/**
|
||||
* @brief Helper class to be able to loop over a parameter pack's elements.
|
||||
*/
|
||||
class metaloop {
|
||||
|
||||
// The implementation is based on partial struct template specializations.
|
||||
// Basically we need a template type that is callable and takes an integer
|
||||
// non-type template parameter which can be used to implement recursive calls.
|
||||
//
|
||||
// C++11 will not allow the usage of a plain template function that is why we
|
||||
// use struct with overloaded call operator. At the same time C++11 prohibits
|
||||
// partial template specialization with a non type parameter such as int. We
|
||||
// need to wrap that in a type (see metaloop::Int).
|
||||
|
||||
/*
|
||||
* A helper alias to create integer values wrapped as a type. It is nessecary
|
||||
* because a non type template parameter (such as int) would be prohibited in
|
||||
* a partial specialization. Also for the same reason we have to use a class
|
||||
* _Metaloop instead of a simple function as a functor. A function cannot be
|
||||
* partially specialized in a way that is neccesary for this trick.
|
||||
*/
|
||||
template<int N> using Int = std::integral_constant<int, N>;
|
||||
|
||||
/*
|
||||
* Helper class to implement in-place functors.
|
||||
*
|
||||
* We want to be able to use inline functors like a lambda to keep the code
|
||||
* as clear as possible.
|
||||
*/
|
||||
template<int N, class Fn> class MapFn {
|
||||
Fn&& fn_;
|
||||
public:
|
||||
|
||||
// It takes the real functor that can be specified in-place but only
|
||||
// with C++14 because the second parameter's type will depend on the
|
||||
// type of the parameter pack element that is processed. In C++14 we can
|
||||
// specify this second parameter type as auto in the lamda parameter list.
|
||||
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
|
||||
|
||||
template<class T> void operator ()(T&& pack_element) {
|
||||
// We provide the index as the first parameter and the pack (or tuple)
|
||||
// element as the second parameter to the functor.
|
||||
fn_(N, forward<T>(pack_element));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation of the template loop trick.
|
||||
* We create a mechanism for looping over a parameter pack in compile time.
|
||||
* \tparam Idx is the loop index which will be decremented at each recursion.
|
||||
* \tparam Args The parameter pack that will be processed.
|
||||
*
|
||||
*/
|
||||
template <typename Idx, class...Args>
|
||||
class _MetaLoop {};
|
||||
|
||||
// Implementation for the first element of Args...
|
||||
template <class...Args>
|
||||
class _MetaLoop<Int<0>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int N = 0;
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run( Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (get<ARGNUM-N>(valtup));
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation for the N-th element of Args...
|
||||
template <int N, class...Args>
|
||||
class _MetaLoop<Int<N>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run(Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (std::get<ARGNUM-N>(valtup));
|
||||
|
||||
// Recursive call to process the next element of Args
|
||||
_MetaLoop<Int<N-1>, Args...> ().run(forward<Tup>(valtup),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Instantiation: We must instantiate the template with the last index because
|
||||
* the generalized version calls the decremented instantiations recursively.
|
||||
* Once the instantiation with the first index is called, the terminating
|
||||
* version of run is called which does not call itself anymore.
|
||||
*
|
||||
* If you are utterly annoyed, at least you have learned a super crazy
|
||||
* functional metaprogramming pattern.
|
||||
*/
|
||||
template<class...Args>
|
||||
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief The final usable function template.
|
||||
*
|
||||
* This is similar to what varags was on C but in compile time C++11.
|
||||
* You can call:
|
||||
* apply(<the mapping function>, <arbitrary number of arguments of any type>);
|
||||
* For example:
|
||||
*
|
||||
* struct mapfunc {
|
||||
* template<class T> void operator()(int N, T&& element) {
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* apply(mapfunc(), 'a', 10, 151.545);
|
||||
*
|
||||
* C++14:
|
||||
* apply([](int N, auto&& element){
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }, 'a', 10, 151.545);
|
||||
*
|
||||
* This yields the output:
|
||||
* The value of the parameter 0: a
|
||||
* The value of the parameter 1: 10
|
||||
* The value of the parameter 2: 151.545
|
||||
*
|
||||
* As an addition, the function can be called with a tuple as the second
|
||||
* parameter holding the arguments instead of a parameter pack.
|
||||
*
|
||||
*/
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, Args&&...args) {
|
||||
MetaLoop<Args...>().run(tuple<Args&&...>(forward<Args>(args)...),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple rvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>&& tup) {
|
||||
MetaLoop<Args...>().run(std::move(tup), forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple lvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple const reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, const tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function with its arguments encapsualted in a tuple.
|
||||
*/
|
||||
template<class Fn, class Tup, std::size_t...Is>
|
||||
inline static auto
|
||||
callFunWithTuple(Fn&& fn, Tup&& tup, index_sequence<Is...>) ->
|
||||
decltype(fn(std::get<Is>(tup)...))
|
||||
{
|
||||
return fn(std::get<Is>(tup)...);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // METALOOP_HPP
|
|
@ -10,8 +10,7 @@ namespace libnest2d { namespace opt {
|
|||
|
||||
using std::forward;
|
||||
using std::tuple;
|
||||
using std::get;
|
||||
using std::tuple_element;
|
||||
using std::make_tuple;
|
||||
|
||||
/// A Type trait for upper and lower limit of a numeric type.
|
||||
template<class T, class B = void >
|
||||
|
@ -51,176 +50,7 @@ inline Bound<T> bound(const T& min, const T& max) { return Bound<T>(min, max); }
|
|||
template<class...Args> using Input = tuple<Args...>;
|
||||
|
||||
template<class...Args>
|
||||
inline tuple<Args...> initvals(Args...args) { return std::make_tuple(args...); }
|
||||
|
||||
/**
|
||||
* @brief Helper class to be able to loop over a parameter pack's elements.
|
||||
*/
|
||||
class metaloop {
|
||||
// The implementation is based on partial struct template specializations.
|
||||
// Basically we need a template type that is callable and takes an integer
|
||||
// non-type template parameter which can be used to implement recursive calls.
|
||||
//
|
||||
// C++11 will not allow the usage of a plain template function that is why we
|
||||
// use struct with overloaded call operator. At the same time C++11 prohibits
|
||||
// partial template specialization with a non type parameter such as int. We
|
||||
// need to wrap that in a type (see metaloop::Int).
|
||||
|
||||
/*
|
||||
* A helper alias to create integer values wrapped as a type. It is nessecary
|
||||
* because a non type template parameter (such as int) would be prohibited in
|
||||
* a partial specialization. Also for the same reason we have to use a class
|
||||
* _Metaloop instead of a simple function as a functor. A function cannot be
|
||||
* partially specialized in a way that is neccesary for this trick.
|
||||
*/
|
||||
template<int N> using Int = std::integral_constant<int, N>;
|
||||
|
||||
/*
|
||||
* Helper class to implement in-place functors.
|
||||
*
|
||||
* We want to be able to use inline functors like a lambda to keep the code
|
||||
* as clear as possible.
|
||||
*/
|
||||
template<int N, class Fn> class MapFn {
|
||||
Fn&& fn_;
|
||||
public:
|
||||
|
||||
// It takes the real functor that can be specified in-place but only
|
||||
// with C++14 because the second parameter's type will depend on the
|
||||
// type of the parameter pack element that is processed. In C++14 we can
|
||||
// specify this second parameter type as auto in the lamda parameter list.
|
||||
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
|
||||
|
||||
template<class T> void operator ()(T&& pack_element) {
|
||||
// We provide the index as the first parameter and the pack (or tuple)
|
||||
// element as the second parameter to the functor.
|
||||
fn_(N, forward<T>(pack_element));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation of the template loop trick.
|
||||
* We create a mechanism for looping over a parameter pack in compile time.
|
||||
* \tparam Idx is the loop index which will be decremented at each recursion.
|
||||
* \tparam Args The parameter pack that will be processed.
|
||||
*
|
||||
*/
|
||||
template <typename Idx, class...Args>
|
||||
class _MetaLoop {};
|
||||
|
||||
// Implementation for the first element of Args...
|
||||
template <class...Args>
|
||||
class _MetaLoop<Int<0>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int N = 0;
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run( Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (get<ARGNUM-N>(valtup));
|
||||
}
|
||||
};
|
||||
|
||||
// Implementation for the N-th element of Args...
|
||||
template <int N, class...Args>
|
||||
class _MetaLoop<Int<N>, Args...> {
|
||||
public:
|
||||
|
||||
const static BP2D_CONSTEXPR int ARGNUM = sizeof...(Args)-1;
|
||||
|
||||
template<class Tup, class Fn>
|
||||
void run(Tup&& valtup, Fn&& fn) {
|
||||
MapFn<ARGNUM-N, Fn> {forward<Fn>(fn)} (std::get<ARGNUM-N>(valtup));
|
||||
|
||||
// Recursive call to process the next element of Args
|
||||
_MetaLoop<Int<N-1>, Args...> ().run(forward<Tup>(valtup),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Instantiation: We must instantiate the template with the last index because
|
||||
* the generalized version calls the decremented instantiations recursively.
|
||||
* Once the instantiation with the first index is called, the terminating
|
||||
* version of run is called which does not call itself anymore.
|
||||
*
|
||||
* If you are utterly annoyed, at least you have learned a super crazy
|
||||
* functional metaprogramming pattern.
|
||||
*/
|
||||
template<class...Args>
|
||||
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief The final usable function template.
|
||||
*
|
||||
* This is similar to what varags was on C but in compile time C++11.
|
||||
* You can call:
|
||||
* apply(<the mapping function>, <arbitrary number of arguments of any type>);
|
||||
* For example:
|
||||
*
|
||||
* struct mapfunc {
|
||||
* template<class T> void operator()(int N, T&& element) {
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* apply(mapfunc(), 'a', 10, 151.545);
|
||||
*
|
||||
* C++14:
|
||||
* apply([](int N, auto&& element){
|
||||
* std::cout << "The value of the parameter "<< N <<": "
|
||||
* << element << std::endl;
|
||||
* }, 'a', 10, 151.545);
|
||||
*
|
||||
* This yields the output:
|
||||
* The value of the parameter 0: a
|
||||
* The value of the parameter 1: 10
|
||||
* The value of the parameter 2: 151.545
|
||||
*
|
||||
* As an addition, the function can be called with a tuple as the second
|
||||
* parameter holding the arguments instead of a parameter pack.
|
||||
*
|
||||
*/
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, Args&&...args) {
|
||||
MetaLoop<Args...>().run(tuple<Args&&...>(forward<Args>(args)...),
|
||||
forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple rvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>&& tup) {
|
||||
MetaLoop<Args...>().run(std::move(tup), forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple lvalue reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/// The version of apply with a tuple const reference.
|
||||
template<class...Args, class Fn>
|
||||
inline static void apply(Fn&& fn, const tuple<Args...>& tup) {
|
||||
MetaLoop<Args...>().run(tup, forward<Fn>(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a function with its arguments encapsualted in a tuple.
|
||||
*/
|
||||
template<class Fn, class Tup, std::size_t...Is>
|
||||
inline static auto
|
||||
callFunWithTuple(Fn&& fn, Tup&& tup, index_sequence<Is...>) ->
|
||||
decltype(fn(std::get<Is>(tup)...))
|
||||
{
|
||||
return fn(std::get<Is>(tup)...);
|
||||
}
|
||||
|
||||
};
|
||||
inline tuple<Args...> initvals(Args...args) { return make_tuple(args...); }
|
||||
|
||||
/**
|
||||
* @brief Specific optimization methods for which a default optimizer
|
||||
|
@ -257,29 +87,20 @@ enum ResultCodes {
|
|||
template<class...Args>
|
||||
struct Result {
|
||||
ResultCodes resultcode;
|
||||
std::tuple<Args...> optimum;
|
||||
tuple<Args...> optimum;
|
||||
double score;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The stop limit can be specified as the absolute error or as the
|
||||
* relative error, just like in nlopt.
|
||||
*/
|
||||
enum class StopLimitType {
|
||||
ABSOLUTE,
|
||||
RELATIVE
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A type for specifying the stop criteria.
|
||||
*/
|
||||
struct StopCriteria {
|
||||
|
||||
/// Relative or absolute termination error
|
||||
StopLimitType type = StopLimitType::RELATIVE;
|
||||
/// If the absolute value difference between two scores.
|
||||
double absolute_score_difference = std::nan("");
|
||||
|
||||
/// The error value that is interpredted depending on the type property.
|
||||
double stoplimit = 0.0001;
|
||||
/// If the relative value difference between two scores.
|
||||
double relative_score_difference = std::nan("");
|
||||
|
||||
unsigned max_iterations = 0;
|
||||
};
|
||||
|
@ -310,11 +131,11 @@ public:
|
|||
* \return Returns a Result<Args...> structure.
|
||||
* An example call would be:
|
||||
* auto result = opt.optimize_min(
|
||||
* [](std::tuple<double> x) // object function
|
||||
* [](tuple<double> x) // object function
|
||||
* {
|
||||
* return std::pow(std::get<0>(x), 2);
|
||||
* },
|
||||
* std::make_tuple(-0.5), // initial value
|
||||
* make_tuple(-0.5), // initial value
|
||||
* {-1.0, 1.0} // search space bounds
|
||||
* );
|
||||
*/
|
||||
|
@ -390,10 +211,14 @@ public:
|
|||
static_assert(always_false<T>::value, "Optimizer unimplemented!");
|
||||
}
|
||||
|
||||
DummyOptimizer(const StopCriteria&) {
|
||||
static_assert(always_false<T>::value, "Optimizer unimplemented!");
|
||||
}
|
||||
|
||||
template<class Func, class...Args>
|
||||
Result<Args...> optimize(Func&& func,
|
||||
std::tuple<Args...> initvals,
|
||||
Bound<Args>... args)
|
||||
Result<Args...> optimize(Func&& /*func*/,
|
||||
tuple<Args...> /*initvals*/,
|
||||
Bound<Args>... /*args*/)
|
||||
{
|
||||
return Result<Args...>();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
#ifndef NLOPT_BOILERPLATE_HPP
|
||||
#define NLOPT_BOILERPLATE_HPP
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4267)
|
||||
#endif
|
||||
#include <nlopt.hpp>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <libnest2d/optimizer.hpp>
|
||||
#include <cassert>
|
||||
#include "libnest2d/metaloop.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace libnest2d { namespace opt {
|
||||
|
||||
nlopt::algorithm method2nloptAlg(Method m) {
|
||||
inline nlopt::algorithm method2nloptAlg(Method m) {
|
||||
|
||||
switch(m) {
|
||||
case Method::L_SIMPLEX: return nlopt::LN_NELDERMEAD;
|
||||
|
@ -87,7 +97,7 @@ protected:
|
|||
|
||||
template<class Fn, class...Args>
|
||||
static double optfunc(const std::vector<double>& params,
|
||||
std::vector<double>& grad,
|
||||
std::vector<double>& /*grad*/,
|
||||
void *data)
|
||||
{
|
||||
auto fnptr = static_cast<remove_ref_t<Fn>*>(data);
|
||||
|
@ -132,12 +142,10 @@ protected:
|
|||
default: ;
|
||||
}
|
||||
|
||||
switch(this->stopcr_.type) {
|
||||
case StopLimitType::ABSOLUTE:
|
||||
opt_.set_ftol_abs(stopcr_.stoplimit); break;
|
||||
case StopLimitType::RELATIVE:
|
||||
opt_.set_ftol_rel(stopcr_.stoplimit); break;
|
||||
}
|
||||
auto abs_diff = stopcr_.absolute_score_difference;
|
||||
auto rel_diff = stopcr_.relative_score_difference;
|
||||
if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff);
|
||||
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff);
|
||||
|
||||
if(this->stopcr_.max_iterations > 0)
|
||||
opt_.set_maxeval(this->stopcr_.max_iterations );
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
#endif
|
||||
#include "placer_boilerplate.hpp"
|
||||
#include "../geometry_traits_nfp.hpp"
|
||||
#include "libnest2d/optimizer.hpp"
|
||||
#include <cassert>
|
||||
|
||||
#include "tools/svgtools.hpp"
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
|
||||
|
@ -20,15 +24,60 @@ struct NfpPConfig {
|
|||
TOP_RIGHT,
|
||||
};
|
||||
|
||||
/// Which angles to try out for better results
|
||||
/// Which angles to try out for better results.
|
||||
std::vector<Radians> rotations;
|
||||
|
||||
/// Where to align the resulting packed pile
|
||||
/// Where to align the resulting packed pile.
|
||||
Alignment alignment;
|
||||
|
||||
/// Where to start putting objects in the bin.
|
||||
Alignment starting_point;
|
||||
|
||||
std::function<double(const Nfp::Shapes<RawShape>&, double, double, double)>
|
||||
/**
|
||||
* @brief A function object representing the fitting function in the
|
||||
* placement optimization process. (Optional)
|
||||
*
|
||||
* This is the most versatile tool to configure the placer. The fitting
|
||||
* function is evaluated many times when a new item is being placed into the
|
||||
* bin. The output should be a rated score of the new item's position.
|
||||
*
|
||||
* This is not a mandatory option as there is a default fitting function
|
||||
* that will optimize for the best pack efficiency. With a custom fitting
|
||||
* function you can e.g. influence the shape of the arranged pile.
|
||||
*
|
||||
* \param shapes The first parameter is a container with all the placed
|
||||
* polygons excluding the current candidate. You can calculate a bounding
|
||||
* box or convex hull on this pile of polygons without the candidate item
|
||||
* or push back the candidate item into the container and then calculate
|
||||
* some features.
|
||||
*
|
||||
* \param item The second parameter is the candidate item.
|
||||
*
|
||||
* \param occupied_area The third parameter is the sum of areas of the
|
||||
* items in the first parameter so you don't have to iterate through them
|
||||
* if you only need their area.
|
||||
*
|
||||
* \param norm A norming factor for physical dimensions. E.g. if your score
|
||||
* is the distance between the item and the bin center, you should divide
|
||||
* that distance with the norming factor. If the score is an area than
|
||||
* divide it with the square of the norming factor. Imagine it as a unit of
|
||||
* distance.
|
||||
*
|
||||
* \param penality The fifth parameter is the amount of minimum penality if
|
||||
* the arranged pile would't fit into the bin. You can use the wouldFit()
|
||||
* function to check this. Note that the pile can be outside the bin's
|
||||
* boundaries while the placement algorithm is running. Your job is only to
|
||||
* check if the pile could be translated into a position in the bin where
|
||||
* all the items would be inside. For a box shaped bin you can use the
|
||||
* pile's bounding box to check whether it's width and height is small
|
||||
* enough. If the pile would not fit, you have to make sure that the
|
||||
* resulting score will be higher then the penality value. A good solution
|
||||
* would be to set score = 2*penality-score in case the pile wouldn't fit
|
||||
* into the bin.
|
||||
*
|
||||
*/
|
||||
std::function<double(Nfp::Shapes<RawShape>&, const _Item<RawShape>&,
|
||||
double, double, double)>
|
||||
object_function;
|
||||
|
||||
/**
|
||||
|
@ -38,11 +87,30 @@ struct NfpPConfig {
|
|||
*/
|
||||
float accuracy = 1.0;
|
||||
|
||||
/**
|
||||
* @brief If you want to see items inside other item's holes, you have to
|
||||
* turn this switch on.
|
||||
*
|
||||
* This will only work if a suitable nfp implementation is provided.
|
||||
* The library has no such implementation right now.
|
||||
*/
|
||||
bool explore_holes = false;
|
||||
|
||||
NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
|
||||
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
|
||||
};
|
||||
|
||||
// A class for getting a point on the circumference of the polygon (in log time)
|
||||
/**
|
||||
* A class for getting a point on the circumference of the polygon (in log time)
|
||||
*
|
||||
* This is a transformation of the provided polygon to be able to pinpoint
|
||||
* locations on the circumference. The optimizer will pass a floating point
|
||||
* value e.g. within <0,1> and we have to transform this value quickly into a
|
||||
* coordinate on the circumference. By definition 0 should yield the first
|
||||
* vertex and 1.0 would be the last (which should coincide with first).
|
||||
*
|
||||
* We also have to make this work for the holes of the captured polygon.
|
||||
*/
|
||||
template<class RawShape> class EdgeCache {
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Coord = TCoord<Vertex>;
|
||||
|
@ -57,6 +125,8 @@ template<class RawShape> class EdgeCache {
|
|||
|
||||
std::vector<ContourCache> holes_;
|
||||
|
||||
double accuracy_ = 1.0;
|
||||
|
||||
void createCache(const RawShape& sh) {
|
||||
{ // For the contour
|
||||
auto first = ShapeLike::cbegin(sh);
|
||||
|
@ -90,21 +160,44 @@ template<class RawShape> class EdgeCache {
|
|||
}
|
||||
}
|
||||
|
||||
size_t stride(const size_t N) const {
|
||||
using std::ceil;
|
||||
using std::round;
|
||||
using std::pow;
|
||||
|
||||
return static_cast<Coord>(
|
||||
std::round(N/std::pow(N, std::pow(accuracy_, 1.0/3.0)))
|
||||
);
|
||||
}
|
||||
|
||||
void fetchCorners() const {
|
||||
if(!contour_.corners.empty()) return;
|
||||
|
||||
// TODO Accuracy
|
||||
contour_.corners = contour_.distances;
|
||||
for(auto& d : contour_.corners) d /= contour_.full_distance;
|
||||
const auto N = contour_.distances.size();
|
||||
const auto S = stride(N);
|
||||
|
||||
contour_.corners.reserve(N / S + 1);
|
||||
auto N_1 = N-1;
|
||||
contour_.corners.emplace_back(0.0);
|
||||
for(size_t i = 0; i < N_1; i += S) {
|
||||
contour_.corners.emplace_back(
|
||||
contour_.distances.at(i) / contour_.full_distance);
|
||||
}
|
||||
}
|
||||
|
||||
void fetchHoleCorners(unsigned hidx) const {
|
||||
auto& hc = holes_[hidx];
|
||||
if(!hc.corners.empty()) return;
|
||||
|
||||
// TODO Accuracy
|
||||
hc.corners = hc.distances;
|
||||
for(auto& d : hc.corners) d /= hc.full_distance;
|
||||
const auto N = hc.distances.size();
|
||||
const auto S = stride(N);
|
||||
auto N_1 = N-1;
|
||||
hc.corners.reserve(N / S + 1);
|
||||
hc.corners.emplace_back(0.0);
|
||||
for(size_t i = 0; i < N_1; i += S) {
|
||||
hc.corners.emplace_back(
|
||||
hc.distances.at(i) / hc.full_distance);
|
||||
}
|
||||
}
|
||||
|
||||
inline Vertex coords(const ContourCache& cache, double distance) const {
|
||||
|
@ -150,6 +243,9 @@ public:
|
|||
createCache(sh);
|
||||
}
|
||||
|
||||
/// Resolution of returned corners. The stride is derived from this value.
|
||||
void accuracy(double a /* within <0.0, 1.0>*/) { accuracy_ = a; }
|
||||
|
||||
/**
|
||||
* @brief Get a point on the circumference of a polygon.
|
||||
* @param distance A relative distance from the starting point to the end.
|
||||
|
@ -176,24 +272,64 @@ public:
|
|||
return holes_[hidx].full_distance;
|
||||
}
|
||||
|
||||
/// Get the normalized distance values for each vertex
|
||||
inline const std::vector<double>& corners() const BP2D_NOEXCEPT {
|
||||
fetchCorners();
|
||||
return contour_.corners;
|
||||
}
|
||||
|
||||
/// corners for a specific hole
|
||||
inline const std::vector<double>&
|
||||
corners(unsigned holeidx) const BP2D_NOEXCEPT {
|
||||
fetchHoleCorners(holeidx);
|
||||
return holes_[holeidx].corners;
|
||||
}
|
||||
|
||||
inline unsigned holeCount() const BP2D_NOEXCEPT { return holes_.size(); }
|
||||
/// The number of holes in the abstracted polygon
|
||||
inline size_t holeCount() const BP2D_NOEXCEPT { return holes_.size(); }
|
||||
|
||||
};
|
||||
|
||||
template<NfpLevel lvl>
|
||||
struct Lvl { static const NfpLevel value = lvl; };
|
||||
|
||||
template<class RawShape>
|
||||
inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
|
||||
const _Item<RawShape>& stationary,
|
||||
const _Item<RawShape>& orbiter)
|
||||
{
|
||||
// The provided nfp is somewhere in the dark. We need to get it
|
||||
// to the right position around the stationary shape.
|
||||
// This is done by choosing the leftmost lowest vertex of the
|
||||
// orbiting polygon to be touched with the rightmost upper
|
||||
// vertex of the stationary polygon. In this configuration, the
|
||||
// reference vertex of the orbiting polygon (which can be dragged around
|
||||
// the nfp) will be its rightmost upper vertex that coincides with the
|
||||
// rightmost upper vertex of the nfp. No proof provided other than Jonas
|
||||
// Lindmark's reasoning about the reference vertex of nfp in his thesis
|
||||
// ("No fit polygon problem" - section 2.1.9)
|
||||
|
||||
auto touch_sh = stationary.rightmostTopVertex();
|
||||
auto touch_other = orbiter.leftmostBottomVertex();
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
||||
auto dnfp = top_other - nfp.second; // nfp.second is the nfp reference point
|
||||
ShapeLike::translate(nfp.first, dnfp);
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
|
||||
const RawShape& stationary,
|
||||
const _Item<RawShape>& orbiter)
|
||||
{
|
||||
auto touch_sh = Nfp::rightmostUpVertex(stationary);
|
||||
auto touch_other = orbiter.leftmostBottomVertex();
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
||||
auto dnfp = top_other - nfp.second;
|
||||
ShapeLike::translate(nfp.first, dnfp);
|
||||
}
|
||||
|
||||
template<class RawShape, class Container>
|
||||
Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
||||
const _Item<RawShape>& trsh,
|
||||
|
@ -203,18 +339,35 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
|||
|
||||
Nfp::Shapes<RawShape> nfps;
|
||||
|
||||
//int pi = 0;
|
||||
for(Item& sh : polygons) {
|
||||
auto subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
sh.transformedShape(), trsh.transformedShape());
|
||||
auto subnfp_r = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
sh.transformedShape(), trsh.transformedShape());
|
||||
#ifndef NDEBUG
|
||||
auto vv = ShapeLike::isValid(sh.transformedShape());
|
||||
assert(vv.first);
|
||||
|
||||
auto vnfp = ShapeLike::isValid(subnfp);
|
||||
auto vnfp = ShapeLike::isValid(subnfp_r.first);
|
||||
assert(vnfp.first);
|
||||
#endif
|
||||
|
||||
nfps = Nfp::merge(nfps, subnfp);
|
||||
correctNfpPosition(subnfp_r, sh, trsh);
|
||||
|
||||
nfps = Nfp::merge(nfps, subnfp_r.first);
|
||||
|
||||
// double SCALE = 1000000;
|
||||
// using SVGWriter = svg::SVGWriter<RawShape>;
|
||||
// SVGWriter::Config conf;
|
||||
// conf.mm_in_coord_units = SCALE;
|
||||
// SVGWriter svgw(conf);
|
||||
// Box bin(250*SCALE, 210*SCALE);
|
||||
// svgw.setSize(bin);
|
||||
// for(int i = 0; i <= pi; i++) svgw.writeItem(polygons[i]);
|
||||
// svgw.writeItem(trsh);
|
||||
//// svgw.writeItem(Item(subnfp_r.first));
|
||||
// for(auto& n : nfps) svgw.writeItem(Item(n));
|
||||
// svgw.save("nfpout");
|
||||
// pi++;
|
||||
}
|
||||
|
||||
return nfps;
|
||||
|
@ -227,50 +380,73 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
|||
{
|
||||
using Item = _Item<RawShape>;
|
||||
|
||||
Nfp::Shapes<RawShape> nfps, stationary;
|
||||
Nfp::Shapes<RawShape> nfps;
|
||||
|
||||
auto& orb = trsh.transformedShape();
|
||||
bool orbconvex = trsh.isContourConvex();
|
||||
|
||||
for(Item& sh : polygons) {
|
||||
stationary = Nfp::merge(stationary, sh.transformedShape());
|
||||
}
|
||||
Nfp::NfpResult<RawShape> subnfp;
|
||||
auto& stat = sh.transformedShape();
|
||||
|
||||
std::cout << "pile size: " << stationary.size() << std::endl;
|
||||
for(RawShape& sh : stationary) {
|
||||
if(sh.isContourConvex() && orbconvex)
|
||||
subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
|
||||
else if(orbconvex)
|
||||
subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
|
||||
else
|
||||
subnfp = Nfp::noFitPolygon<Level::value>(stat, orb);
|
||||
|
||||
RawShape subnfp;
|
||||
// if(sh.isContourConvex() && trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
// sh.transformedShape(), trsh.transformedShape());
|
||||
// } else {
|
||||
subnfp = Nfp::noFitPolygon<Level::value>( sh/*.transformedShape()*/,
|
||||
trsh.transformedShape());
|
||||
// }
|
||||
correctNfpPosition(subnfp, sh, trsh);
|
||||
|
||||
// #ifndef NDEBUG
|
||||
// auto vv = ShapeLike::isValid(sh.transformedShape());
|
||||
// assert(vv.first);
|
||||
|
||||
// auto vnfp = ShapeLike::isValid(subnfp);
|
||||
// assert(vnfp.first);
|
||||
// #endif
|
||||
|
||||
// auto vnfp = ShapeLike::isValid(subnfp);
|
||||
// if(!vnfp.first) {
|
||||
// std::cout << vnfp.second << std::endl;
|
||||
// std::cout << ShapeLike::toString(subnfp) << std::endl;
|
||||
// }
|
||||
|
||||
nfps = Nfp::merge(nfps, subnfp);
|
||||
nfps = Nfp::merge(nfps, subnfp.first);
|
||||
}
|
||||
|
||||
return nfps;
|
||||
|
||||
|
||||
// using Item = _Item<RawShape>;
|
||||
// using sl = ShapeLike;
|
||||
|
||||
// Nfp::Shapes<RawShape> nfps, stationary;
|
||||
|
||||
// for(Item& sh : polygons) {
|
||||
// stationary = Nfp::merge(stationary, sh.transformedShape());
|
||||
// }
|
||||
|
||||
// for(RawShape& sh : stationary) {
|
||||
|
||||
//// auto vv = sl::isValid(sh);
|
||||
//// std::cout << vv.second << std::endl;
|
||||
|
||||
|
||||
// Nfp::NfpResult<RawShape> subnfp;
|
||||
// bool shconvex = sl::isConvex<RawShape>(sl::getContour(sh));
|
||||
// if(shconvex && trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
// sh, trsh.transformedShape());
|
||||
// } else if(trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(
|
||||
// sh, trsh.transformedShape());
|
||||
// }
|
||||
// else {
|
||||
// subnfp = Nfp::noFitPolygon<Level::value>( sh,
|
||||
// trsh.transformedShape());
|
||||
// }
|
||||
|
||||
// correctNfpPosition(subnfp, sh, trsh);
|
||||
|
||||
// nfps = Nfp::merge(nfps, subnfp.first);
|
||||
// }
|
||||
|
||||
// return nfps;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape>,
|
||||
RawShape, _Box<TPoint<RawShape>>, NfpPConfig<RawShape>> {
|
||||
template<class RawShape, class TBin = _Box<TPoint<RawShape>>>
|
||||
class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>,
|
||||
RawShape, TBin, NfpPConfig<RawShape>> {
|
||||
|
||||
using Base = PlacerBoilerplate<_NofitPolyPlacer<RawShape>,
|
||||
RawShape, _Box<TPoint<RawShape>>, NfpPConfig<RawShape>>;
|
||||
using Base = PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin>,
|
||||
RawShape, TBin, NfpPConfig<RawShape>>;
|
||||
|
||||
DECLARE_PLACER(Base)
|
||||
|
||||
|
@ -280,28 +456,45 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape>,
|
|||
const double penality_;
|
||||
|
||||
using MaxNfpLevel = Nfp::MaxNfpLevel<RawShape>;
|
||||
using sl = ShapeLike;
|
||||
|
||||
public:
|
||||
|
||||
using Pile = const Nfp::Shapes<RawShape>&;
|
||||
using Pile = Nfp::Shapes<RawShape>;
|
||||
|
||||
inline explicit _NofitPolyPlacer(const BinType& bin):
|
||||
Base(bin),
|
||||
norm_(std::sqrt(ShapeLike::area<RawShape>(bin))),
|
||||
norm_(std::sqrt(sl::area<RawShape>(bin))),
|
||||
penality_(1e6*norm_) {}
|
||||
|
||||
_NofitPolyPlacer(const _NofitPolyPlacer&) = default;
|
||||
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
|
||||
|
||||
#ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors
|
||||
_NofitPolyPlacer(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
|
||||
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
|
||||
#endif
|
||||
|
||||
bool static inline wouldFit(const Box& bb, const RawShape& bin) {
|
||||
auto bbin = sl::boundingBox<RawShape>(bin);
|
||||
auto d = bbin.center() - bb.center();
|
||||
_Rectangle<RawShape> rect(bb.width(), bb.height());
|
||||
rect.translate(bb.minCorner() + d);
|
||||
return sl::isInside<RawShape>(rect.transformedShape(), bin);
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull, const RawShape& bin) {
|
||||
auto bbch = ShapeLike::boundingBox<RawShape>(chull);
|
||||
auto bbin = ShapeLike::boundingBox<RawShape>(bin);
|
||||
auto d = bbin.minCorner() - bbch.minCorner();
|
||||
auto bbch = sl::boundingBox<RawShape>(chull);
|
||||
auto bbin = sl::boundingBox<RawShape>(bin);
|
||||
auto d = bbch.center() - bbin.center();
|
||||
auto chullcpy = chull;
|
||||
ShapeLike::translate(chullcpy, d);
|
||||
return ShapeLike::isInside<RawShape>(chullcpy, bbin);
|
||||
sl::translate(chullcpy, d);
|
||||
return sl::isInside<RawShape>(chullcpy, bin);
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull, const Box& bin)
|
||||
{
|
||||
auto bbch = ShapeLike::boundingBox<RawShape>(chull);
|
||||
auto bbch = sl::boundingBox<RawShape>(chull);
|
||||
return wouldFit(bbch, bin);
|
||||
}
|
||||
|
||||
|
@ -310,6 +503,17 @@ public:
|
|||
return bb.width() <= bin.width() && bb.height() <= bin.height();
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const Box& bb, const _Circle<Vertex>& bin)
|
||||
{
|
||||
return sl::isInside<RawShape>(bb, bin);
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull,
|
||||
const _Circle<Vertex>& bin)
|
||||
{
|
||||
return sl::isInside<RawShape>(chull, bin);
|
||||
}
|
||||
|
||||
PackResult trypack(Item& item) {
|
||||
|
||||
PackResult ret;
|
||||
|
@ -348,7 +552,10 @@ public:
|
|||
std::vector<EdgeCache<RawShape>> ecache;
|
||||
ecache.reserve(nfps.size());
|
||||
|
||||
for(auto& nfp : nfps ) ecache.emplace_back(nfp);
|
||||
for(auto& nfp : nfps ) {
|
||||
ecache.emplace_back(nfp);
|
||||
ecache.back().accuracy(config_.accuracy);
|
||||
}
|
||||
|
||||
struct Optimum {
|
||||
double relpos;
|
||||
|
@ -363,7 +570,7 @@ public:
|
|||
auto getNfpPoint = [&ecache](const Optimum& opt)
|
||||
{
|
||||
return opt.hidx < 0? ecache[opt.nfpidx].coords(opt.relpos) :
|
||||
ecache[opt.nfpidx].coords(opt.nfpidx, opt.relpos);
|
||||
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
|
||||
};
|
||||
|
||||
Nfp::Shapes<RawShape> pile;
|
||||
|
@ -374,17 +581,25 @@ public:
|
|||
pile_area += mitem.area();
|
||||
}
|
||||
|
||||
auto merged_pile = Nfp::merge(pile);
|
||||
|
||||
// This is the kernel part of the object function that is
|
||||
// customizable by the library client
|
||||
auto _objfunc = config_.object_function?
|
||||
config_.object_function :
|
||||
[this](const Nfp::Shapes<RawShape>& pile, double occupied_area,
|
||||
double /*norm*/, double penality)
|
||||
[this, &merged_pile](
|
||||
Nfp::Shapes<RawShape>& /*pile*/,
|
||||
const Item& item,
|
||||
double occupied_area,
|
||||
double norm,
|
||||
double /*penality*/)
|
||||
{
|
||||
auto ch = ShapeLike::convexHull(pile);
|
||||
merged_pile.emplace_back(item.transformedShape());
|
||||
auto ch = sl::convexHull(merged_pile);
|
||||
merged_pile.pop_back();
|
||||
|
||||
// The pack ratio -- how much is the convex hull occupied
|
||||
double pack_rate = occupied_area/ShapeLike::area(ch);
|
||||
double pack_rate = occupied_area/sl::area(ch);
|
||||
|
||||
// ratio of waste
|
||||
double waste = 1.0 - pack_rate;
|
||||
|
@ -394,7 +609,7 @@ public:
|
|||
// (larger) values.
|
||||
auto score = std::sqrt(waste);
|
||||
|
||||
if(!wouldFit(ch, bin_)) score = 2*penality - score;
|
||||
if(!wouldFit(ch, bin_)) score += norm;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
@ -406,23 +621,31 @@ public:
|
|||
d += startpos;
|
||||
item.translation(d);
|
||||
|
||||
pile.emplace_back(item.transformedShape());
|
||||
|
||||
double occupied_area = pile_area + item.area();
|
||||
|
||||
double score = _objfunc(pile, occupied_area,
|
||||
double score = _objfunc(pile, item, occupied_area,
|
||||
norm_, penality_);
|
||||
|
||||
pile.pop_back();
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
auto boundaryCheck = [&](const Optimum& o) {
|
||||
auto v = getNfpPoint(o);
|
||||
auto d = v - iv;
|
||||
d += startpos;
|
||||
item.translation(d);
|
||||
|
||||
merged_pile.emplace_back(item.transformedShape());
|
||||
auto chull = sl::convexHull(merged_pile);
|
||||
merged_pile.pop_back();
|
||||
|
||||
return wouldFit(chull, bin_);
|
||||
};
|
||||
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.max_iterations = 1000;
|
||||
stopcr.stoplimit = 0.001;
|
||||
stopcr.type = opt::StopLimitType::RELATIVE;
|
||||
opt::TOptimizer<opt::Method::L_SIMPLEX> solver(stopcr);
|
||||
stopcr.max_iterations = 100;
|
||||
stopcr.relative_score_difference = 1e-6;
|
||||
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
|
||||
|
||||
Optimum optimum(0, 0);
|
||||
double best_score = penality_;
|
||||
|
@ -441,7 +664,7 @@ public:
|
|||
std::for_each(cache.corners().begin(),
|
||||
cache.corners().end(),
|
||||
[ch, &contour_ofn, &solver, &best_score,
|
||||
&optimum] (double pos)
|
||||
&optimum, &boundaryCheck] (double pos)
|
||||
{
|
||||
try {
|
||||
auto result = solver.optimize_min(contour_ofn,
|
||||
|
@ -450,10 +673,11 @@ public:
|
|||
);
|
||||
|
||||
if(result.score < best_score) {
|
||||
best_score = result.score;
|
||||
optimum.relpos = std::get<0>(result.optimum);
|
||||
optimum.nfpidx = ch;
|
||||
optimum.hidx = -1;
|
||||
Optimum o(std::get<0>(result.optimum), ch, -1);
|
||||
if(boundaryCheck(o)) {
|
||||
best_score = result.score;
|
||||
optimum = o;
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
derr() << "ERROR: " << e.what() << "\n";
|
||||
|
@ -472,7 +696,7 @@ public:
|
|||
std::for_each(cache.corners(hidx).begin(),
|
||||
cache.corners(hidx).end(),
|
||||
[&hole_ofn, &solver, &best_score,
|
||||
&optimum, ch, hidx]
|
||||
&optimum, ch, hidx, &boundaryCheck]
|
||||
(double pos)
|
||||
{
|
||||
try {
|
||||
|
@ -482,10 +706,12 @@ public:
|
|||
);
|
||||
|
||||
if(result.score < best_score) {
|
||||
best_score = result.score;
|
||||
Optimum o(std::get<0>(result.optimum),
|
||||
ch, hidx);
|
||||
optimum = o;
|
||||
if(boundaryCheck(o)) {
|
||||
best_score = result.score;
|
||||
optimum = o;
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
derr() << "ERROR: " << e.what() << "\n";
|
||||
|
@ -524,34 +750,35 @@ public:
|
|||
m.reserve(items_.size());
|
||||
|
||||
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
||||
auto&& bb = ShapeLike::boundingBox<RawShape>(m);
|
||||
auto&& bb = sl::boundingBox<RawShape>(m);
|
||||
|
||||
Vertex ci, cb;
|
||||
auto bbin = sl::boundingBox<RawShape>(bin_);
|
||||
|
||||
switch(config_.alignment) {
|
||||
case Config::Alignment::CENTER: {
|
||||
ci = bb.center();
|
||||
cb = bin_.center();
|
||||
cb = bbin.center();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_LEFT: {
|
||||
ci = bb.minCorner();
|
||||
cb = bin_.minCorner();
|
||||
cb = bbin.minCorner();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_RIGHT: {
|
||||
ci = {getX(bb.maxCorner()), getY(bb.minCorner())};
|
||||
cb = {getX(bin_.maxCorner()), getY(bin_.minCorner())};
|
||||
cb = {getX(bbin.maxCorner()), getY(bbin.minCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_LEFT: {
|
||||
ci = {getX(bb.minCorner()), getY(bb.maxCorner())};
|
||||
cb = {getX(bin_.minCorner()), getY(bin_.maxCorner())};
|
||||
cb = {getX(bbin.minCorner()), getY(bbin.maxCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_RIGHT: {
|
||||
ci = bb.maxCorner();
|
||||
cb = bin_.maxCorner();
|
||||
cb = bbin.maxCorner();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -567,31 +794,32 @@ private:
|
|||
void setInitialPosition(Item& item) {
|
||||
Box&& bb = item.boundingBox();
|
||||
Vertex ci, cb;
|
||||
auto bbin = sl::boundingBox<RawShape>(bin_);
|
||||
|
||||
switch(config_.starting_point) {
|
||||
case Config::Alignment::CENTER: {
|
||||
ci = bb.center();
|
||||
cb = bin_.center();
|
||||
cb = bbin.center();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_LEFT: {
|
||||
ci = bb.minCorner();
|
||||
cb = bin_.minCorner();
|
||||
cb = bbin.minCorner();
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::BOTTOM_RIGHT: {
|
||||
ci = {getX(bb.maxCorner()), getY(bb.minCorner())};
|
||||
cb = {getX(bin_.maxCorner()), getY(bin_.minCorner())};
|
||||
cb = {getX(bbin.maxCorner()), getY(bbin.minCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_LEFT: {
|
||||
ci = {getX(bb.minCorner()), getY(bb.maxCorner())};
|
||||
cb = {getX(bin_.minCorner()), getY(bin_.maxCorner())};
|
||||
cb = {getX(bbin.minCorner()), getY(bbin.maxCorner())};
|
||||
break;
|
||||
}
|
||||
case Config::Alignment::TOP_RIGHT: {
|
||||
ci = bb.maxCorner();
|
||||
cb = bin_.maxCorner();
|
||||
cb = bbin.maxCorner();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -602,7 +830,7 @@ private:
|
|||
|
||||
void placeOutsideOfBin(Item& item) {
|
||||
auto&& bb = item.boundingBox();
|
||||
Box binbb = ShapeLike::boundingBox<RawShape>(bin_);
|
||||
Box binbb = sl::boundingBox<RawShape>(bin_);
|
||||
|
||||
Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
|
||||
|
||||
|
|
|
@ -256,14 +256,14 @@ public:
|
|||
|
||||
if(not_packed.size() < 2)
|
||||
return false; // No group of two items
|
||||
else {
|
||||
double largest_area = not_packed.front().get().area();
|
||||
auto itmp = not_packed.begin(); itmp++;
|
||||
double second_largest = itmp->get().area();
|
||||
if( free_area - second_largest - largest_area > waste)
|
||||
return false; // If even the largest two items do not fill
|
||||
// the bin to the desired waste than we can end here.
|
||||
}
|
||||
|
||||
double largest_area = not_packed.front().get().area();
|
||||
auto itmp = not_packed.begin(); itmp++;
|
||||
double second_largest = itmp->get().area();
|
||||
if( free_area - second_largest - largest_area > waste)
|
||||
return false; // If even the largest two items do not fill
|
||||
// the bin to the desired waste than we can end here.
|
||||
|
||||
|
||||
bool ret = false;
|
||||
auto it = not_packed.begin();
|
||||
|
@ -481,7 +481,7 @@ public:
|
|||
{
|
||||
std::array<bool, 3> packed = {false};
|
||||
|
||||
for(auto id : idx) packed[id] =
|
||||
for(auto id : idx) packed.at(id) =
|
||||
placer.pack(candidates[id]);
|
||||
|
||||
bool check =
|
||||
|
@ -535,10 +535,9 @@ public:
|
|||
// then it should be removed from the not_packed list
|
||||
{ auto it = store_.begin();
|
||||
while (it != store_.end()) {
|
||||
Placer p(bin);
|
||||
Placer p(bin); p.configure(pconfig);
|
||||
if(!p.pack(*it)) {
|
||||
auto itmp = it++;
|
||||
store_.erase(itmp);
|
||||
it = store_.erase(it);
|
||||
} else it++;
|
||||
}
|
||||
}
|
||||
|
@ -605,8 +604,7 @@ public:
|
|||
if(placer.pack(*it)) {
|
||||
filled_area += it->get().area();
|
||||
free_area = bin_area - filled_area;
|
||||
auto itmp = it++;
|
||||
not_packed.erase(itmp);
|
||||
it = not_packed.erase(it);
|
||||
makeProgress(placer, idx, 1);
|
||||
} else it++;
|
||||
}
|
||||
|
|
|
@ -52,17 +52,16 @@ public:
|
|||
auto total = last-first;
|
||||
auto makeProgress = [this, &total](Placer& placer, size_t idx) {
|
||||
packed_bins_[idx] = placer.getItems();
|
||||
this->progress_(--total);
|
||||
this->progress_(static_cast<unsigned>(--total));
|
||||
};
|
||||
|
||||
// Safety test: try to pack each item into an empty bin. If it fails
|
||||
// then it should be removed from the list
|
||||
{ auto it = store_.begin();
|
||||
while (it != store_.end()) {
|
||||
Placer p(bin);
|
||||
Placer p(bin); p.configure(pconfig);
|
||||
if(!p.pack(*it)) {
|
||||
auto itmp = it++;
|
||||
store_.erase(itmp);
|
||||
it = store_.erase(it);
|
||||
} else it++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -682,7 +682,9 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||
auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(),
|
||||
orbiter.transformedShape());
|
||||
|
||||
auto v = ShapeLike::isValid(nfp);
|
||||
strategies::correctNfpPosition(nfp, stationary, orbiter);
|
||||
|
||||
auto v = ShapeLike::isValid(nfp.first);
|
||||
|
||||
if(!v.first) {
|
||||
std::cout << v.second << std::endl;
|
||||
|
@ -690,7 +692,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
|||
|
||||
ASSERT_TRUE(v.first);
|
||||
|
||||
Item infp(nfp);
|
||||
Item infp(nfp.first);
|
||||
|
||||
int i = 0;
|
||||
auto rorbiter = orbiter.transformedShape();
|
||||
|
@ -742,6 +744,15 @@ TEST(GeometryAlgorithms, nfpConvexConvex) {
|
|||
// testNfp<NfpLevel::BOTH_CONCAVE, 1000>(nfp_concave_testdata);
|
||||
//}
|
||||
|
||||
TEST(GeometryAlgorithms, nfpConcaveConcave) {
|
||||
using namespace libnest2d;
|
||||
|
||||
// Rectangle r1(10, 10);
|
||||
// Rectangle r2(20, 20);
|
||||
// auto result = Nfp::nfpSimpleSimple(r1.transformedShape(),
|
||||
// r2.transformedShape());
|
||||
}
|
||||
|
||||
TEST(GeometryAlgorithms, pointOnPolygonContour) {
|
||||
using namespace libnest2d;
|
||||
|
||||
|
|
|
@ -49,18 +49,18 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
|
|||
long double px = p.x_.val();
|
||||
long double py = p.y_.val();
|
||||
#endif
|
||||
return libnfporb::point_t(px*factor, py*factor);
|
||||
return {px*factor, py*factor};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
{
|
||||
using Vertex = PointImpl;
|
||||
|
||||
PolygonImpl ret;
|
||||
NfpR ret;
|
||||
|
||||
// try {
|
||||
try {
|
||||
libnfporb::polygon_t pstat, porb;
|
||||
|
||||
boost::geometry::convert(sh, pstat);
|
||||
|
@ -85,7 +85,7 @@ PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
|||
// this can throw
|
||||
auto nfp = libnfporb::generateNFP(pstat, porb, true);
|
||||
|
||||
auto &ct = ShapeLike::getContour(ret);
|
||||
auto &ct = ShapeLike::getContour(ret.first);
|
||||
ct.reserve(nfp.front().size()+1);
|
||||
for(auto v : nfp.front()) {
|
||||
v = scale(v, refactor);
|
||||
|
@ -94,10 +94,10 @@ PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
|||
ct.push_back(ct.front());
|
||||
std::reverse(ct.begin(), ct.end());
|
||||
|
||||
auto &rholes = ShapeLike::holes(ret);
|
||||
auto &rholes = ShapeLike::holes(ret.first);
|
||||
for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
|
||||
if(nfp[hidx].size() >= 3) {
|
||||
rholes.push_back({});
|
||||
rholes.emplace_back();
|
||||
auto& h = rholes.back();
|
||||
h.reserve(nfp[hidx].size()+1);
|
||||
|
||||
|
@ -110,73 +110,48 @@ PolygonImpl _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
|||
}
|
||||
}
|
||||
|
||||
auto& cmp = vsort;
|
||||
std::sort(pstat.outer().begin(), pstat.outer().end(), cmp);
|
||||
std::sort(porb.outer().begin(), porb.outer().end(), cmp);
|
||||
ret.second = Nfp::referenceVertex(ret.first);
|
||||
|
||||
// leftmost lower vertex of the stationary polygon
|
||||
auto& touch_sh = scale(pstat.outer().back(), refactor);
|
||||
// rightmost upper vertex of the orbiting polygon
|
||||
auto& touch_other = scale(porb.outer().front(), refactor);
|
||||
|
||||
// Calculate the difference and move the orbiter to the touch position.
|
||||
auto dtouch = touch_sh - touch_other;
|
||||
auto _top_other = scale(porb.outer().back(), refactor) + dtouch;
|
||||
|
||||
Vertex top_other(getX(_top_other), getY(_top_other));
|
||||
|
||||
// Get the righmost upper vertex of the nfp and move it to the RMU of
|
||||
// the orbiter because they should coincide.
|
||||
auto&& top_nfp = Nfp::rightmostUpVertex(ret);
|
||||
auto dnfp = top_other - top_nfp;
|
||||
|
||||
std::for_each(ShapeLike::begin(ret), ShapeLike::end(ret),
|
||||
[&dnfp](Vertex& v) { v+= dnfp; } );
|
||||
|
||||
for(auto& h : ShapeLike::holes(ret))
|
||||
std::for_each( h.begin(), h.end(),
|
||||
[&dnfp](Vertex& v) { v += dnfp; } );
|
||||
|
||||
// } catch(std::exception& e) {
|
||||
// std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
|
||||
} catch(std::exception& e) {
|
||||
std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
|
||||
// auto ch_stat = ShapeLike::convexHull(sh);
|
||||
// auto ch_orb = ShapeLike::convexHull(cother);
|
||||
// ret = Nfp::nfpConvexOnly(ch_stat, ch_orb);
|
||||
// }
|
||||
ret = Nfp::nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PolygonImpl Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);//nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
PolygonImpl Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
|
||||
PolygonImpl Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
|
||||
PolygonImpl
|
||||
Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
//PolygonImpl
|
||||
//Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES>::operator()(
|
||||
// const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
//{
|
||||
// return _nfp(sh, cother);
|
||||
//}
|
||||
|
||||
PolygonImpl
|
||||
Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
//PolygonImpl
|
||||
//Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES>::operator()(
|
||||
// const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
//{
|
||||
// return _nfp(sh, cother);
|
||||
//}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,37 +5,39 @@
|
|||
|
||||
namespace libnest2d {
|
||||
|
||||
PolygonImpl _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
using NfpR = Nfp::NfpResult<PolygonImpl>;
|
||||
|
||||
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
//template<>
|
||||
//struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX_WITH_HOLES> {
|
||||
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
//};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES> {
|
||||
PolygonImpl operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
//template<>
|
||||
//struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE_WITH_HOLES> {
|
||||
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
//};
|
||||
|
||||
template<> struct Nfp::MaxNfpLevel<PolygonImpl> {
|
||||
static const BP2D_CONSTEXPR NfpLevel value =
|
||||
// NfpLevel::CONVEX_ONLY;
|
||||
NfpLevel::BOTH_CONCAVE_WITH_HOLES;
|
||||
NfpLevel::BOTH_CONCAVE;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -5,11 +5,17 @@
|
|||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include <libnest2d.h>
|
||||
#include <libnest2d/libnest2d.hpp>
|
||||
|
||||
namespace libnest2d { namespace svg {
|
||||
|
||||
template<class RawShape>
|
||||
class SVGWriter {
|
||||
using Item = _Item<RawShape>;
|
||||
using Coord = TCoord<TPoint<RawShape>>;
|
||||
using Box = _Box<TPoint<RawShape>>;
|
||||
using PackGroup = _PackGroup<RawShape>;
|
||||
|
||||
public:
|
||||
|
||||
enum OrigoLocation {
|
||||
|
|
|
@ -603,6 +603,8 @@ namespace Slic3r {
|
|||
|
||||
if (!_generate_volumes(*object.second, obj_geometry->second, *volumes_ptr))
|
||||
return false;
|
||||
|
||||
object.second->center_around_origin();
|
||||
}
|
||||
|
||||
// fixes the min z of the model if negative
|
||||
|
@ -1491,6 +1493,7 @@ namespace Slic3r {
|
|||
|
||||
stl_get_size(&stl);
|
||||
volume->mesh.repair();
|
||||
volume->calculate_convex_hull();
|
||||
|
||||
// apply volume's name and config data
|
||||
for (const Metadata& metadata : volume_data.metadata)
|
||||
|
@ -1989,7 +1992,7 @@ namespace Slic3r {
|
|||
|
||||
// stores object's name
|
||||
if (!obj->name.empty())
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << obj->name << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
|
||||
|
||||
// stores object's config data
|
||||
for (const std::string& key : obj->config.keys())
|
||||
|
@ -2012,7 +2015,7 @@ namespace Slic3r {
|
|||
|
||||
// stores volume's name
|
||||
if (!volume->name.empty())
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << volume->name << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
|
||||
|
||||
// stores volume's modifier field
|
||||
if (volume->modifier)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../slic3r/GUI/PresetBundle.hpp"
|
||||
#include "AMF.hpp"
|
||||
|
||||
|
@ -405,6 +406,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
}
|
||||
stl_get_size(&stl);
|
||||
m_volume->mesh.repair();
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_volume = nullptr;
|
||||
break;
|
||||
|
@ -686,33 +688,6 @@ bool load_amf(const char *path, PresetBundle* bundle, Model *model)
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string xml_escape(std::string text)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
for (;;)
|
||||
{
|
||||
pos = text.find_first_of("\"\'&<>", pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
std::string replacement;
|
||||
switch (text[pos])
|
||||
{
|
||||
case '\"': replacement = """; break;
|
||||
case '\'': replacement = "'"; break;
|
||||
case '&': replacement = "&"; break;
|
||||
case '<': replacement = "<"; break;
|
||||
case '>': replacement = ">"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
text.replace(pos, 1, replacement);
|
||||
pos += replacement.size();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
|
||||
{
|
||||
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
|
||||
|
@ -761,7 +736,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
|
|||
for (const std::string &key : object->config.keys())
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
|
||||
if (!object->name.empty())
|
||||
stream << " <metadata type=\"name\">" << object->name << "</metadata>\n";
|
||||
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
|
||||
// Store the layer height profile as a single semicolon separated list.
|
||||
|
@ -805,7 +780,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
|
|||
for (const std::string &key : volume->config.keys())
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
|
||||
if (!volume->name.empty())
|
||||
stream << " <metadata type=\"name\">" << volume->name << "</metadata>\n";
|
||||
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
|
||||
if (volume->modifier)
|
||||
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
|
||||
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
|
||||
|
|
|
@ -167,6 +167,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
{
|
||||
std::string gcode;
|
||||
|
||||
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin
|
||||
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
|
||||
float alpha = m_wipe_tower_rotation/180.f * M_PI;
|
||||
WipeTower::xy start_pos = tcr.start_pos;
|
||||
WipeTower::xy end_pos = tcr.end_pos;
|
||||
start_pos.rotate(alpha);
|
||||
start_pos.translate(m_wipe_tower_pos);
|
||||
end_pos.rotate(alpha);
|
||||
end_pos.translate(m_wipe_tower_pos);
|
||||
std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
|
||||
|
||||
|
||||
// Disable linear advance for the wipe tower operations.
|
||||
gcode += "M900 K0\n";
|
||||
// Move over the wipe tower.
|
||||
|
@ -174,14 +186,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
gcode += gcodegen.retract(true);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
gcode += gcodegen.travel_to(
|
||||
wipe_tower_point_to_object_point(gcodegen, tcr.start_pos),
|
||||
wipe_tower_point_to_object_point(gcodegen, start_pos),
|
||||
erMixed,
|
||||
"Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += tcr.gcode;
|
||||
gcode += tcr_rotated_gcode;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
|
||||
gcodegen.writer().toolchange(new_extruder_id);
|
||||
|
@ -195,18 +207,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
check_add_eol(gcode);
|
||||
}
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
|
||||
gcodegen.writer().travel_to_xy(Pointf(end_pos.x, end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
|
||||
|
||||
// Prepare a future wipe.
|
||||
gcodegen.m_wipe.path.points.clear();
|
||||
if (new_extruder_id >= 0) {
|
||||
// Start the wipe at the current position.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
|
||||
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
WipeTower::xy((std::abs(m_left - tcr.end_pos.x) < std::abs(m_right - tcr.end_pos.x)) ? m_right : m_left,
|
||||
tcr.end_pos.y)));
|
||||
WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
|
||||
end_pos.y)));
|
||||
}
|
||||
|
||||
// Let the planner know we are traveling between objects.
|
||||
|
@ -214,6 +226,57 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
|
|||
return gcode;
|
||||
}
|
||||
|
||||
// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
|
||||
// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
|
||||
std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
|
||||
{
|
||||
std::istringstream gcode_str(gcode_original);
|
||||
std::string gcode_out;
|
||||
std::string line;
|
||||
WipeTower::xy pos = start_pos;
|
||||
WipeTower::xy transformed_pos;
|
||||
WipeTower::xy old_pos(-1000.1f, -1000.1f);
|
||||
|
||||
while (gcode_str) {
|
||||
std::getline(gcode_str, line); // we read the gcode line by line
|
||||
if (line.find("G1 ") == 0) {
|
||||
std::ostringstream line_out;
|
||||
std::istringstream line_str(line);
|
||||
line_str >> std::noskipws; // don't skip whitespace
|
||||
char ch = 0;
|
||||
while (line_str >> ch) {
|
||||
if (ch == 'X')
|
||||
line_str >> pos.x;
|
||||
else
|
||||
if (ch == 'Y')
|
||||
line_str >> pos.y;
|
||||
else
|
||||
line_out << ch;
|
||||
}
|
||||
|
||||
transformed_pos = pos;
|
||||
transformed_pos.rotate(angle);
|
||||
transformed_pos.translate(translation);
|
||||
|
||||
if (transformed_pos != old_pos) {
|
||||
line = line_out.str();
|
||||
char buf[2048] = "G1";
|
||||
if (transformed_pos.x != old_pos.x)
|
||||
sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
|
||||
if (transformed_pos.y != old_pos.y)
|
||||
sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
|
||||
|
||||
line.replace(line.find("G1 "), 3, buf);
|
||||
old_pos = transformed_pos;
|
||||
}
|
||||
}
|
||||
gcode_out += line + "\n";
|
||||
}
|
||||
return gcode_out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string WipeTowerIntegration::prime(GCode &gcodegen)
|
||||
{
|
||||
assert(m_layer_idx == 0);
|
||||
|
@ -309,10 +372,12 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
|||
size_t object_idx;
|
||||
size_t layer_idx;
|
||||
};
|
||||
std::vector<std::vector<LayerToPrint>> per_object(print.objects.size(), std::vector<LayerToPrint>());
|
||||
|
||||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
std::vector<std::vector<LayerToPrint>> per_object(printable_objects.size(), std::vector<LayerToPrint>());
|
||||
std::vector<OrderingItem> ordering;
|
||||
for (size_t i = 0; i < print.objects.size(); ++ i) {
|
||||
per_object[i] = collect_layers_to_print(*print.objects[i]);
|
||||
for (size_t i = 0; i < printable_objects.size(); ++i) {
|
||||
per_object[i] = collect_layers_to_print(*printable_objects[i]);
|
||||
OrderingItem ordering_item;
|
||||
ordering_item.object_idx = i;
|
||||
ordering.reserve(ordering.size() + per_object[i].size());
|
||||
|
@ -337,8 +402,8 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
|||
std::pair<coordf_t, std::vector<LayerToPrint>> merged;
|
||||
// Assign an average print_z to the set of layers with nearly equal print_z.
|
||||
merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z);
|
||||
merged.second.assign(print.objects.size(), LayerToPrint());
|
||||
for (; i < j; ++ i) {
|
||||
merged.second.assign(printable_objects.size(), LayerToPrint());
|
||||
for (; i < j; ++i) {
|
||||
const OrderingItem &oi = ordering[i];
|
||||
assert(merged.second[oi.object_idx].layer() == nullptr);
|
||||
merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]);
|
||||
|
@ -375,10 +440,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
}
|
||||
fclose(file);
|
||||
|
||||
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
|
||||
if (m_silent_time_estimator_enabled)
|
||||
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
if (print->config.remaining_times.value)
|
||||
{
|
||||
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
if (m_silent_time_estimator_enabled)
|
||||
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
}
|
||||
|
||||
if (! this->m_placeholder_parser_failed_templates.empty()) {
|
||||
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
|
||||
|
@ -457,8 +524,21 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
|
||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
||||
// are considered to be active for the single extruder multi-material printers only.
|
||||
m_silent_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
|
||||
m_silent_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
||||
// are considered to be active for the single extruder multi-material printers only.
|
||||
m_normal_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
|
||||
m_normal_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
|
||||
}
|
||||
|
||||
// resets analyzer
|
||||
m_analyzer.reset();
|
||||
|
@ -472,9 +552,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
// How many times will be change_layer() called?
|
||||
// change_layer() in turn increments the progress bar status.
|
||||
m_layer_count = 0;
|
||||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
if (print.config.complete_objects.value) {
|
||||
// Add each of the object's layers separately.
|
||||
for (auto object : print.objects) {
|
||||
for (auto object : printable_objects) {
|
||||
std::vector<coordf_t> zs;
|
||||
zs.reserve(object->layers.size() + object->support_layers.size());
|
||||
for (auto layer : object->layers)
|
||||
|
@ -487,7 +568,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
} else {
|
||||
// Print all objects with the same print_z together.
|
||||
std::vector<coordf_t> zs;
|
||||
for (auto object : print.objects) {
|
||||
for (auto object : printable_objects) {
|
||||
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
|
||||
for (auto layer : object->layers)
|
||||
zs.push_back(layer->print_z);
|
||||
|
@ -506,8 +587,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
{
|
||||
// get the minimum cross-section used in the print
|
||||
std::vector<double> mm3_per_mm;
|
||||
for (auto object : print.objects) {
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
for (auto object : printable_objects) {
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) {
|
||||
auto region = print.regions[region_id];
|
||||
for (auto layer : object->layers) {
|
||||
auto layerm = layer->regions[region_id];
|
||||
|
@ -567,7 +648,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
_write(file, "\n");
|
||||
}
|
||||
// Write some terse information on the slicing parameters.
|
||||
const PrintObject *first_object = print.objects.front();
|
||||
const PrintObject *first_object = printable_objects.front();
|
||||
const double layer_height = first_object->config.layer_height.value;
|
||||
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
|
@ -596,20 +677,24 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
size_t initial_print_object_id = 0;
|
||||
bool has_wipe_tower = false;
|
||||
if (print.config.complete_objects.value) {
|
||||
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
|
||||
for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) {
|
||||
tool_ordering = ToolOrdering(*print.objects[initial_print_object_id], initial_extruder_id);
|
||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
|
||||
for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
|
||||
tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
|
||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Find tool ordering for all the objects at once, and the initial extruder ID.
|
||||
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
|
||||
tool_ordering = print.m_tool_ordering.empty() ?
|
||||
ToolOrdering(print, initial_extruder_id) :
|
||||
print.m_tool_ordering;
|
||||
initial_extruder_id = tool_ordering.first_extruder();
|
||||
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
|
||||
initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ?
|
||||
// The priming towers will be skipped.
|
||||
tool_ordering.all_extruders().back() :
|
||||
// Don't skip the priming towers.
|
||||
tool_ordering.first_extruder();
|
||||
}
|
||||
if (initial_extruder_id == (unsigned int)-1) {
|
||||
// Nothing to print!
|
||||
|
@ -637,6 +722,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
m_placeholder_parser.set("current_object_idx", 0);
|
||||
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
|
||||
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
|
||||
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming);
|
||||
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
|
||||
|
||||
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
|
||||
|
@ -676,7 +762,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
// Collect outer contours of all objects over all layers.
|
||||
// Discard objects only containing thin walls (offset would fail on an empty polygon).
|
||||
Polygons islands;
|
||||
for (const PrintObject *object : print.objects)
|
||||
for (const PrintObject *object : printable_objects)
|
||||
for (const Layer *layer : object->layers)
|
||||
for (const ExPolygon &expoly : layer->slices.expolygons)
|
||||
for (const Point © : object->_shifted_copies) {
|
||||
|
@ -717,14 +803,17 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
}
|
||||
}
|
||||
|
||||
// Set initial extruder only after custom start G-code.
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) {
|
||||
// Set initial extruder only after custom start G-code.
|
||||
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
}
|
||||
|
||||
// Do all objects for each layer.
|
||||
if (print.config.complete_objects.value) {
|
||||
// Print objects from the smallest to the tallest to avoid collisions
|
||||
// when moving onto next object starting point.
|
||||
std::vector<PrintObject*> objects(print.objects);
|
||||
std::vector<PrintObject*> objects(printable_objects);
|
||||
std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; });
|
||||
size_t finished_objects = 0;
|
||||
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
|
||||
|
@ -788,7 +877,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
for (PrintObject *object : printable_objects)
|
||||
object_reference_points.push_back(object->_shifted_copies.front());
|
||||
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
|
||||
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
|
||||
// Sort layers by Z.
|
||||
// All extrusion moves with the same top layer height are extruded uninterrupted.
|
||||
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
|
||||
|
@ -796,27 +885,29 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : printable_objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
if (print.config.single_extruder_multi_material_priming) {
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : printable_objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Extrude the layers.
|
||||
|
@ -996,9 +1087,10 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
|
|||
int(print.config.machine_max_feedrate_y.values.front() + 0.5),
|
||||
int(print.config.machine_max_feedrate_z.values.front() + 0.5),
|
||||
int(print.config.machine_max_feedrate_e.values.front() + 0.5));
|
||||
fprintf(file, "M204 S%d T%d ; sets acceleration (S) and retract acceleration (T), mm/sec^2\n",
|
||||
fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
|
||||
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_retracting.values.front() + 0.5));
|
||||
int(print.config.machine_max_acceleration_retracting.values.front() + 0.5),
|
||||
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5));
|
||||
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
|
||||
print.config.machine_max_jerk_x.values.front(),
|
||||
print.config.machine_max_jerk_y.values.front(),
|
||||
|
|
|
@ -83,8 +83,10 @@ public:
|
|||
const WipeTower::ToolChangeResult &priming,
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
|
||||
const WipeTower::ToolChangeResult &final_purge) :
|
||||
m_left(float(print_config.wipe_tower_x.value)),
|
||||
m_right(float(print_config.wipe_tower_x.value + print_config.wipe_tower_width.value)),
|
||||
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
|
||||
m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)),
|
||||
m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)),
|
||||
m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)),
|
||||
m_priming(priming),
|
||||
m_tool_changes(tool_changes),
|
||||
m_final_purge(final_purge),
|
||||
|
@ -101,9 +103,14 @@ private:
|
|||
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
||||
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
|
||||
|
||||
// Postprocesses gcode: rotates and moves all G1 extrusions and returns result
|
||||
std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
|
||||
|
||||
// Left / right edges of the wipe tower, for the planning of wipe moves.
|
||||
const float m_left;
|
||||
const float m_right;
|
||||
const WipeTower::xy m_wipe_tower_pos;
|
||||
const float m_wipe_tower_rotation;
|
||||
// Reference to cached values at the Printer class.
|
||||
const WipeTower::ToolChangeResult &m_priming;
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
|
||||
|
@ -112,6 +119,7 @@ private:
|
|||
int m_layer_idx;
|
||||
int m_tool_change_idx;
|
||||
bool m_brim_done;
|
||||
bool i_have_brim = false;
|
||||
};
|
||||
|
||||
class GCode {
|
||||
|
|
|
@ -134,6 +134,11 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
|
|||
// The projection does not contain the priming regions.
|
||||
BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_t max_print_z)
|
||||
{
|
||||
// Wipe tower extrusions are saved as if the tower was at the origin with no rotation
|
||||
// We need to get position and angle of the wipe tower to transform them to actual position.
|
||||
Pointf wipe_tower_pos(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value);
|
||||
float wipe_tower_angle = print.config.wipe_tower_rotation_angle.value;
|
||||
|
||||
BoundingBoxf bbox;
|
||||
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) {
|
||||
if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z)
|
||||
|
@ -144,6 +149,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
|
|||
if (e.width > 0) {
|
||||
Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y);
|
||||
Pointf p2(e.pos.x, e.pos.y);
|
||||
p1.rotate(wipe_tower_angle);
|
||||
p1.translate(wipe_tower_pos);
|
||||
p2.rotate(wipe_tower_angle);
|
||||
p2.translate(wipe_tower_pos);
|
||||
|
||||
bbox.merge(p1);
|
||||
coordf_t radius = 0.5 * e.width;
|
||||
bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius);
|
||||
|
|
|
@ -451,10 +451,9 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
|
||||
|
||||
// we will sort objects so that dedicated for wiping are at the beginning:
|
||||
PrintObjectPtrs object_list = print.objects;
|
||||
PrintObjectPtrs object_list = print.get_printable_objects();
|
||||
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
|
||||
|
||||
|
||||
// We will now iterate through
|
||||
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
|
||||
// - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
|
||||
|
@ -548,7 +547,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
|
|||
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
|
||||
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
|
||||
|
||||
for (const PrintObject* object : print.objects) {
|
||||
PrintObjectPtrs printable_objects = print.get_printable_objects();
|
||||
for (const PrintObject* object : printable_objects) {
|
||||
// Finds this layer:
|
||||
auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
|
||||
if (this_layer_it == object->layers.end())
|
||||
|
|
|
@ -66,8 +66,10 @@ public:
|
|||
wipe_tower_partitions(0),
|
||||
wipe_tower_layer_height(0.) {}
|
||||
|
||||
bool operator< (const LayerTools &rhs) const { return print_z - EPSILON < rhs.print_z; }
|
||||
bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
|
||||
// Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
|
||||
// In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
|
||||
bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
|
||||
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
|
||||
|
||||
bool is_extruder_order(unsigned int a, unsigned int b) const;
|
||||
|
||||
|
|
|
@ -25,18 +25,30 @@ public:
|
|||
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
|
||||
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
|
||||
|
||||
// Rotate the point around given point about given angle (in degrees)
|
||||
// shifts the result so that point of rotation is in the middle of the tower
|
||||
xy rotate(const xy& origin, float width, float depth, float angle) const {
|
||||
// Rotate the point around center of the wipe tower about given angle (in degrees)
|
||||
xy rotate(float width, float depth, float angle) const {
|
||||
xy out(0,0);
|
||||
float temp_x = x - width / 2.f;
|
||||
float temp_y = y - depth / 2.f;
|
||||
angle *= M_PI/180.;
|
||||
out.x += (temp_x - origin.x) * cos(angle) - (temp_y - origin.y) * sin(angle);
|
||||
out.y += (temp_x - origin.x) * sin(angle) + (temp_y - origin.y) * cos(angle);
|
||||
return out + origin;
|
||||
out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
|
||||
out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Rotate the point around origin about given angle in degrees
|
||||
void rotate(float angle) {
|
||||
float temp_x = x * cos(angle) - y * sin(angle);
|
||||
y = x * sin(angle) + y * cos(angle);
|
||||
x = temp_x;
|
||||
}
|
||||
|
||||
void translate(const xy& vect) {
|
||||
x += vect.x;
|
||||
y += vect.y;
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
@ -104,6 +116,9 @@ public:
|
|||
// This is useful not only for the print time estimation, but also for the control of layer cooling.
|
||||
float elapsed_time;
|
||||
|
||||
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
|
||||
bool priming;
|
||||
|
||||
// Sum the total length of the extrusion.
|
||||
float total_extrusion_length_in_plane() {
|
||||
float e_length = 0.f;
|
||||
|
|
|
@ -5,7 +5,7 @@ TODO LIST
|
|||
|
||||
1. cooling moves - DONE
|
||||
2. account for perimeter and finish_layer extrusions and subtract it from last wipe - DONE
|
||||
3. priming extrusions (last wipe must clear the color)
|
||||
3. priming extrusions (last wipe must clear the color) - DONE
|
||||
4. Peter's wipe tower - layer's are not exactly square
|
||||
5. Peter's wipe tower - variable width for higher levels
|
||||
6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
|
||||
|
@ -17,7 +17,6 @@ TODO LIST
|
|||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
@ -68,8 +67,11 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
Writer& set_initial_position(const WipeTower::xy &pos) {
|
||||
m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg);
|
||||
Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
|
||||
m_wipe_tower_width = width;
|
||||
m_wipe_tower_depth = depth;
|
||||
m_internal_angle = internal_angle;
|
||||
m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle);
|
||||
m_current_pos = pos;
|
||||
return *this;
|
||||
}
|
||||
|
@ -81,9 +83,6 @@ public:
|
|||
|
||||
Writer& set_extrusion_flow(float flow)
|
||||
{ m_extrusion_flow = flow; return *this; }
|
||||
|
||||
Writer& set_rotation(WipeTower::xy& pos, float width, float depth, float angle)
|
||||
{ m_wipe_tower_pos = pos; m_wipe_tower_width = width; m_wipe_tower_depth=depth; m_angle_deg = angle; return (*this); }
|
||||
|
||||
Writer& set_y_shift(float shift) {
|
||||
m_current_pos.y -= shift-m_y_shift;
|
||||
|
@ -110,7 +109,7 @@ public:
|
|||
float y() const { return m_current_pos.y; }
|
||||
const WipeTower::xy& pos() const { return m_current_pos; }
|
||||
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
|
||||
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg); }
|
||||
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
|
||||
float elapsed_time() const { return m_elapsed_time; }
|
||||
|
||||
// Extrude with an explicitely provided amount of extrusion.
|
||||
|
@ -125,9 +124,9 @@ public:
|
|||
double len = sqrt(dx*dx+dy*dy);
|
||||
|
||||
|
||||
// For rotated wipe tower, transform position to printer coordinates
|
||||
WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we are
|
||||
WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_angle_deg)); // this is where we want to go
|
||||
// Now do the "internal rotation" with respect to the wipe tower center
|
||||
WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
|
||||
WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go
|
||||
|
||||
if (! m_preview_suppressed && e > 0.f && len > 0.) {
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
|
@ -147,6 +146,7 @@ public:
|
|||
if (std::abs(rot.y - rotated_current_pos.y) > EPSILON)
|
||||
m_gcode += set_format_Y(rot.y);
|
||||
|
||||
|
||||
if (e != 0.f)
|
||||
m_gcode += set_format_E(e);
|
||||
|
||||
|
@ -397,9 +397,8 @@ private:
|
|||
std::string m_gcode;
|
||||
std::vector<WipeTower::Extrusion> m_extrusions;
|
||||
float m_elapsed_time;
|
||||
float m_angle_deg = 0.f;
|
||||
float m_internal_angle = 0.f;
|
||||
float m_y_shift = 0.f;
|
||||
WipeTower::xy m_wipe_tower_pos;
|
||||
float m_wipe_tower_width = 0.f;
|
||||
float m_wipe_tower_depth = 0.f;
|
||||
float m_last_fan_speed = 0.f;
|
||||
|
@ -490,7 +489,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||
// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
|
||||
|
||||
const float prime_section_width = std::min(240.f / tools.size(), 60.f);
|
||||
box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 100.f);
|
||||
box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
|
||||
|
||||
PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
|
@ -539,6 +538,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||
m_print_brim = true;
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = true;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
|
@ -575,7 +575,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
}
|
||||
|
||||
box_coordinates cleaning_box(
|
||||
m_wipe_tower_pos + xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
|
||||
xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
|
||||
m_wipe_tower_width - m_perimeter_width,
|
||||
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
|
||||
: m_wipe_tower_depth-m_perimeter_width));
|
||||
|
@ -584,7 +584,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
|
||||
.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
|
||||
.append(";--------------------\n"
|
||||
"; CP TOOLCHANGE START\n")
|
||||
|
@ -594,7 +593,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
.speed_override(100);
|
||||
|
||||
xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
|
||||
writer.set_initial_position(initial_position);
|
||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
// Increase the extruder driver current to allow fast ramming.
|
||||
writer.set_extruder_trimpot(750);
|
||||
|
@ -616,11 +615,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
if (last_change_in_layer) {// draw perimeter line
|
||||
writer.set_y_shift(m_y_shift);
|
||||
if (m_peters_wipe_tower)
|
||||
writer.rectangle(m_wipe_tower_pos,m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
|
||||
writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
|
||||
else {
|
||||
writer.rectangle(m_wipe_tower_pos,m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
|
||||
writer.travel(m_wipe_tower_pos.x + (writer.x()> (m_wipe_tower_pos.x + m_wipe_tower_width) / 2.f ? 0.f : m_wipe_tower_width), writer.y());
|
||||
writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -634,6 +633,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
"\n\n");
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
|
@ -647,7 +647,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset)
|
||||
{
|
||||
const box_coordinates wipeTower_box(
|
||||
m_wipe_tower_pos,
|
||||
WipeTower::xy(0.f, 0.f),
|
||||
m_wipe_tower_width,
|
||||
m_wipe_tower_depth);
|
||||
|
||||
|
@ -655,12 +655,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
|||
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
|
||||
.set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
|
||||
.append(";-------------------------------------\n"
|
||||
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
|
||||
|
||||
xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
|
||||
writer.set_initial_position(initial_position);
|
||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
|
||||
1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
|
||||
|
@ -685,6 +684,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
|||
m_print_brim = false; // Mark the brim as extruded
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
|
@ -724,7 +724,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
|||
if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
|
||||
|
||||
// this is y of the center of previous sparse infill border
|
||||
float sparse_beginning_y = m_wipe_tower_pos.y;
|
||||
float sparse_beginning_y = 0.f;
|
||||
if (m_current_shape == SHAPE_REVERSED)
|
||||
sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
|
||||
- ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
|
||||
|
@ -742,7 +742,7 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
|||
for (const auto& tch : m_layer_info->tool_changes) { // let's find this toolchange
|
||||
if (tch.old_tool == m_current_tool) {
|
||||
sum_of_depths += tch.ramming_depth;
|
||||
float ramming_end_y = m_wipe_tower_pos.y + sum_of_depths;
|
||||
float ramming_end_y = sum_of_depths;
|
||||
ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
|
||||
|
||||
// debugging:
|
||||
|
@ -793,11 +793,16 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
|||
float turning_point = (!m_left_to_right ? xl : xr );
|
||||
float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
|
||||
writer.suppress_preview()
|
||||
.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
|
||||
.retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
|
||||
.retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
|
||||
.retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
|
||||
.retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
|
||||
|
||||
/*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
|
||||
.load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
|
||||
.load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
|
||||
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
|
||||
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
|
||||
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
|
||||
.resume_preview();
|
||||
|
||||
if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
|
||||
|
@ -874,10 +879,15 @@ void WipeTowerPrusaMM::toolchange_Load(
|
|||
|
||||
writer.append("; CP TOOLCHANGE LOAD\n")
|
||||
.suppress_preview()
|
||||
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
|
||||
/*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
|
||||
.load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
|
||||
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
|
||||
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow
|
||||
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
|
||||
|
||||
.load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
|
||||
.load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
|
||||
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
|
||||
|
||||
.travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
|
||||
.resume_preview();
|
||||
|
||||
|
@ -950,7 +960,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
|
|||
if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
|
||||
m_left_to_right = !m_left_to_right;
|
||||
writer.travel(writer.x(), writer.y() - dy)
|
||||
.travel(m_wipe_tower_pos.x + (m_left_to_right ? m_wipe_tower_width : 0.f), writer.y());
|
||||
.travel(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y());
|
||||
}
|
||||
|
||||
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
|
||||
|
@ -969,7 +979,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
|||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_rotation(m_wipe_tower_pos, m_wipe_tower_width, m_wipe_tower_depth, m_wipe_tower_rotation_angle)
|
||||
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
|
||||
.append(";--------------------\n"
|
||||
"; CP EMPTY GRID START\n")
|
||||
|
@ -978,14 +987,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
|||
// Slow down on the 1st layer.
|
||||
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
|
||||
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
|
||||
box_coordinates fill_box(m_wipe_tower_pos + xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
|
||||
box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
|
||||
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
|
||||
|
||||
if (m_left_to_right) // so there is never a diagonal travel
|
||||
writer.set_initial_position(fill_box.ru);
|
||||
else
|
||||
writer.set_initial_position(fill_box.lu);
|
||||
|
||||
writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
|
||||
m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
box_coordinates box = fill_box;
|
||||
for (int i=0;i<2;++i) {
|
||||
|
@ -1044,6 +1051,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
|||
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
|
||||
|
||||
ToolChangeResult result;
|
||||
result.priming = false;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.layer_height = this->m_layer_height;
|
||||
result.gcode = writer.gcode();
|
||||
|
@ -1165,9 +1173,9 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
|
|||
{
|
||||
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
|
||||
if (m_peters_wipe_tower)
|
||||
m_wipe_tower_rotation_angle += 90.f;
|
||||
m_internal_rotation += 90.f;
|
||||
else
|
||||
m_wipe_tower_rotation_angle += 180.f;
|
||||
m_internal_rotation += 180.f;
|
||||
|
||||
if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
|
||||
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
|
||||
|
@ -1188,7 +1196,7 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
|
|||
last_toolchange.gcode += buf;
|
||||
}
|
||||
last_toolchange.gcode += finish_layer_toolchange.gcode;
|
||||
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(),finish_layer_toolchange.extrusions.begin(),finish_layer_toolchange.extrusions.end());
|
||||
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(), finish_layer_toolchange.extrusions.begin(), finish_layer_toolchange.extrusions.end());
|
||||
last_toolchange.end_pos = finish_layer_toolchange.end_pos;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -65,9 +65,9 @@ public:
|
|||
|
||||
|
||||
// Set the extruder properties.
|
||||
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
|
||||
float unloading_speed, float delay, int cooling_moves, float cooling_initial_speed,
|
||||
float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
|
||||
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
|
||||
float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
|
||||
float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
|
||||
{
|
||||
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
@ -76,7 +76,9 @@ public:
|
|||
m_filpar[idx].temperature = temp;
|
||||
m_filpar[idx].first_layer_temperature = first_layer_temp;
|
||||
m_filpar[idx].loading_speed = loading_speed;
|
||||
m_filpar[idx].loading_speed_start = loading_speed_start;
|
||||
m_filpar[idx].unloading_speed = unloading_speed;
|
||||
m_filpar[idx].unloading_speed_start = unloading_speed_start;
|
||||
m_filpar[idx].delay = delay;
|
||||
m_filpar[idx].cooling_moves = cooling_moves;
|
||||
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
|
||||
|
@ -102,6 +104,8 @@ public:
|
|||
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
||||
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
|
||||
|
||||
float get_depth() const { return m_wipe_tower_depth; }
|
||||
|
||||
|
||||
|
||||
// Switch to a next layer.
|
||||
|
@ -189,6 +193,7 @@ private:
|
|||
float m_wipe_tower_width; // Width of the wipe tower.
|
||||
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
|
||||
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
|
||||
float m_internal_rotation = 0.f;
|
||||
float m_y_shift = 0.f; // y shift passed to writer
|
||||
float m_z_pos = 0.f; // Current Z position.
|
||||
float m_layer_height = 0.f; // Current layer height.
|
||||
|
@ -213,7 +218,9 @@ private:
|
|||
int temperature = 0;
|
||||
int first_layer_temperature = 0;
|
||||
float loading_speed = 0.f;
|
||||
float loading_speed_start = 0.f;
|
||||
float unloading_speed = 0.f;
|
||||
float unloading_speed_start = 0.f;
|
||||
float delay = 0.f ;
|
||||
int cooling_moves = 0;
|
||||
float cooling_initial_speed = 0.f;
|
||||
|
|
|
@ -114,6 +114,28 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
|||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has(char axis) const
|
||||
{
|
||||
const char *c = m_raw.c_str();
|
||||
// Skip the whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
// Skip the command.
|
||||
c = skip_word(c);
|
||||
// Up to the end of line or comment.
|
||||
while (! is_end_of_gcode_line(*c)) {
|
||||
// Skip whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
if (is_end_of_gcode_line(*c))
|
||||
break;
|
||||
// Check the name of the axis.
|
||||
if (*c == axis)
|
||||
return true;
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
|
||||
{
|
||||
const char *c = m_raw.c_str();
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
|
||||
bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; }
|
||||
float value(Axis axis) const { return m_axis[axis]; }
|
||||
bool has(char axis) const;
|
||||
bool has_value(char axis, float &value) const;
|
||||
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
|
||||
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
@ -568,16 +569,12 @@ GCodeSender::set_DTR(bool on)
|
|||
void
|
||||
GCodeSender::reset()
|
||||
{
|
||||
this->set_DTR(false);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
||||
this->set_DTR(true);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(200));
|
||||
this->set_DTR(false);
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->can_send = true;
|
||||
}
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(true);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -469,6 +469,40 @@ namespace Slic3r {
|
|||
return _state.minimum_travel_feedrate;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times)
|
||||
{
|
||||
_state.filament_load_times.clear();
|
||||
for (double t : filament_load_times)
|
||||
_state.filament_load_times.push_back(t);
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times)
|
||||
{
|
||||
_state.filament_unload_times.clear();
|
||||
for (double t : filament_unload_times)
|
||||
_state.filament_unload_times.push_back(t);
|
||||
}
|
||||
|
||||
float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder)
|
||||
{
|
||||
return
|
||||
(_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
|
||||
0 :
|
||||
(_state.filament_load_times.size() <= id_extruder) ?
|
||||
_state.filament_load_times.front() :
|
||||
_state.filament_load_times[id_extruder];
|
||||
}
|
||||
|
||||
float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder)
|
||||
{
|
||||
return
|
||||
(_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
|
||||
0 :
|
||||
(_state.filament_unload_times.size() <= id_extruder) ?
|
||||
_state.filament_unload_times.front() :
|
||||
_state.filament_unload_times[id_extruder];
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage)
|
||||
{
|
||||
_state.extrude_factor_override_percentage = percentage;
|
||||
|
@ -535,6 +569,23 @@ namespace Slic3r {
|
|||
_state.g1_line_id = 0;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::set_extruder_id(unsigned int id)
|
||||
{
|
||||
_state.extruder_id = id;
|
||||
}
|
||||
|
||||
unsigned int GCodeTimeEstimator::get_extruder_id() const
|
||||
{
|
||||
return _state.extruder_id;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::reset_extruder_id()
|
||||
{
|
||||
// Set the initial extruder ID to unknown. For the multi-material setup it means
|
||||
// that all the filaments are parked in the MMU and no filament is loaded yet.
|
||||
_state.extruder_id = _state.extruder_id_unloaded;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::add_additional_time(float timeSec)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
@ -575,6 +626,9 @@ namespace Slic3r {
|
|||
set_axis_max_acceleration(axis, DEFAULT_AXIS_MAX_ACCELERATION[a]);
|
||||
set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]);
|
||||
}
|
||||
|
||||
_state.filament_load_times.clear();
|
||||
_state.filament_unload_times.clear();
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::reset()
|
||||
|
@ -613,6 +667,7 @@ namespace Slic3r {
|
|||
|
||||
set_additional_time(0.0f);
|
||||
|
||||
reset_extruder_id();
|
||||
reset_g1_line_id();
|
||||
_g1_line_ids.clear();
|
||||
|
||||
|
@ -666,6 +721,8 @@ namespace Slic3r {
|
|||
}
|
||||
|
||||
_last_st_synchronized_block_id = _blocks.size() - 1;
|
||||
// The additional time has been consumed (added to the total time), reset it to zero.
|
||||
set_additional_time(0.);
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line)
|
||||
|
@ -778,8 +835,18 @@ namespace Slic3r {
|
|||
_processM566(line);
|
||||
break;
|
||||
}
|
||||
case 702: // MK3 MMU2: Process the final filament unload.
|
||||
{
|
||||
_processM702(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'T': // Select Tools
|
||||
{
|
||||
_processT(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1164,11 +1231,25 @@ namespace Slic3r {
|
|||
{
|
||||
PROFILE_FUNC();
|
||||
float value;
|
||||
if (line.has_value('S', value))
|
||||
if (line.has_value('S', value)) {
|
||||
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
|
||||
// and it is also generated by Slic3r to control acceleration per extrusion type
|
||||
// (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
|
||||
set_acceleration(value);
|
||||
|
||||
if (line.has_value('T', value))
|
||||
set_retract_acceleration(value);
|
||||
if (line.has_value('T', value))
|
||||
set_retract_acceleration(value);
|
||||
} else {
|
||||
// New acceleration format, compatible with the upstream Marlin.
|
||||
if (line.has_value('P', value))
|
||||
set_acceleration(value);
|
||||
if (line.has_value('R', value))
|
||||
set_retract_acceleration(value);
|
||||
if (line.has_value('T', value)) {
|
||||
// Interpret the T value as the travel acceleration in the new Marlin format.
|
||||
//FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
|
||||
// set_travel_acceleration(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line)
|
||||
|
@ -1223,6 +1304,37 @@ namespace Slic3r {
|
|||
set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC);
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
if (line.has('C')) {
|
||||
// MK3 MMU2 specific M code:
|
||||
// M702 C is expected to be sent by the custom end G-code when finalizing a print.
|
||||
// The MK3 unit shall unload and park the active filament into the MMU2 unit.
|
||||
add_additional_time(get_filament_unload_time(get_extruder_id()));
|
||||
reset_extruder_id();
|
||||
_simulate_st_synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processT(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
std::string cmd = line.cmd();
|
||||
if (cmd.length() > 1)
|
||||
{
|
||||
unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10);
|
||||
if (get_extruder_id() != id)
|
||||
{
|
||||
// Specific to the MK3 MMU2: The initial extruder ID is set to -1 indicating
|
||||
// that the filament is parked in the MMU2 unit and there is nothing to be unloaded yet.
|
||||
add_additional_time(get_filament_unload_time(get_extruder_id()));
|
||||
set_extruder_id(id);
|
||||
add_additional_time(get_filament_load_time(get_extruder_id()));
|
||||
_simulate_st_synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_simulate_st_synchronize()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
|
|
@ -79,7 +79,15 @@ namespace Slic3r {
|
|||
float minimum_feedrate; // mm/s
|
||||
float minimum_travel_feedrate; // mm/s
|
||||
float extrude_factor_override_percentage;
|
||||
// Additional load / unload times for a filament exchange sequence.
|
||||
std::vector<float> filament_load_times;
|
||||
std::vector<float> filament_unload_times;
|
||||
unsigned int g1_line_id;
|
||||
// extruder_id is currently used to correctly calculate filament load / unload times
|
||||
// into the total print time. This is currently only really used by the MK3 MMU2:
|
||||
// Extruder id (-1) means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
|
||||
static const unsigned int extruder_id_unloaded = (unsigned int)-1;
|
||||
unsigned int extruder_id;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -281,6 +289,11 @@ namespace Slic3r {
|
|||
void set_minimum_travel_feedrate(float feedrate_mm_sec);
|
||||
float get_minimum_travel_feedrate() const;
|
||||
|
||||
void set_filament_load_times(const std::vector<double> &filament_load_times);
|
||||
void set_filament_unload_times(const std::vector<double> &filament_unload_times);
|
||||
float get_filament_load_time(unsigned int id_extruder);
|
||||
float get_filament_unload_time(unsigned int id_extruder);
|
||||
|
||||
void set_extrude_factor_override_percentage(float percentage);
|
||||
float get_extrude_factor_override_percentage() const;
|
||||
|
||||
|
@ -300,6 +313,10 @@ namespace Slic3r {
|
|||
void increment_g1_line_id();
|
||||
void reset_g1_line_id();
|
||||
|
||||
void set_extruder_id(unsigned int id);
|
||||
unsigned int get_extruder_id() const;
|
||||
void reset_extruder_id();
|
||||
|
||||
void add_additional_time(float timeSec);
|
||||
void set_additional_time(float timeSec);
|
||||
float get_additional_time() const;
|
||||
|
@ -383,6 +400,12 @@ namespace Slic3r {
|
|||
// Set allowable instantaneous speed change
|
||||
void _processM566(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Unload the current filament into the MK3 MMU2 unit at the end of print.
|
||||
void _processM702(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes T line (Select Tool)
|
||||
void _processT(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void _simulate_st_synchronize();
|
||||
|
||||
|
|
|
@ -195,47 +195,62 @@ using namespace boost::polygon; // provides also high() and low()
|
|||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
static bool
|
||||
sort_points (Point a, Point b)
|
||||
{
|
||||
return (a.x < b.x) || (a.x == b.x && a.y < b.y);
|
||||
}
|
||||
struct SortPoints {
|
||||
template <class T>
|
||||
bool operator()(const T& a, const T& b) const {
|
||||
return (b.x > a.x) || (a.x == b.x && b.y > a.y);
|
||||
}
|
||||
};
|
||||
|
||||
/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */
|
||||
Polygon
|
||||
convex_hull(Points points)
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
template<class T>
|
||||
static T raw_convex_hull(T& points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
std::sort(points.begin(), points.end(), sort_points);
|
||||
std::sort(points.begin(), points.end(), SortPoints());
|
||||
|
||||
int n = points.size(), k = 0;
|
||||
Polygon hull;
|
||||
T hull;
|
||||
|
||||
if (n >= 3) {
|
||||
hull.points.resize(2*n);
|
||||
hull.resize(2*n);
|
||||
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; i++) {
|
||||
while (k >= 2 && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
|
||||
hull.points[k++] = points[i];
|
||||
while (k >= 2 && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
|
||||
hull.points[k++] = points[i];
|
||||
while (k >= t && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
hull.points.resize(k);
|
||||
hull.resize(k);
|
||||
|
||||
assert( hull.points.front().coincides_with(hull.points.back()) );
|
||||
hull.points.pop_back();
|
||||
assert( hull.front().coincides_with(hull.back()) );
|
||||
hull.pop_back();
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
Pointf3s
|
||||
convex_hull(Pointf3s points)
|
||||
{
|
||||
return raw_convex_hull(points);
|
||||
}
|
||||
|
||||
Polygon
|
||||
convex_hull(Points points)
|
||||
{
|
||||
Polygon hull;
|
||||
hull.points = raw_convex_hull(points);
|
||||
return hull;
|
||||
}
|
||||
|
||||
Polygon
|
||||
convex_hull(const Polygons &polygons)
|
||||
{
|
||||
|
@ -243,7 +258,7 @@ convex_hull(const Polygons &polygons)
|
|||
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
||||
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
||||
}
|
||||
return convex_hull(pp);
|
||||
return convex_hull(std::move(pp));
|
||||
}
|
||||
|
||||
/* accepts an arrayref of points and returns a list of indices
|
||||
|
|
|
@ -108,8 +108,10 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co
|
|||
return true;
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
||||
void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
|
||||
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
|
||||
template<class T> void chained_path_items(Points &points, T &items, T &retval);
|
||||
|
|
|
@ -7,11 +7,6 @@
|
|||
#include "Format/STL.hpp"
|
||||
#include "Format/3mf.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <libnest2d.h>
|
||||
#include <ClipperUtils.hpp>
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
@ -22,6 +17,11 @@
|
|||
#include "SVG.hpp"
|
||||
#include <Eigen/Dense>
|
||||
|
||||
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
unsigned int Model::s_auto_extruder_id = 1;
|
||||
|
@ -240,14 +240,6 @@ BoundingBoxf3 Model::bounding_box() const
|
|||
return bb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 Model::transformed_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
for (const ModelObject* obj : this->objects)
|
||||
bb.merge(obj->tight_bounding_box(false));
|
||||
return bb;
|
||||
}
|
||||
|
||||
void Model::center_instances_around_point(const Pointf &point)
|
||||
{
|
||||
// BoundingBoxf3 bb = this->bounding_box();
|
||||
|
@ -304,369 +296,36 @@ static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb
|
|||
return result;
|
||||
}
|
||||
|
||||
namespace arr {
|
||||
|
||||
using namespace libnest2d;
|
||||
|
||||
std::string toString(const Model& model, bool holes = true) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "{\n";
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
for(auto& expoly_complex : expolys) {
|
||||
|
||||
auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
|
||||
if(tmp.empty()) continue;
|
||||
auto expoly = tmp.front();
|
||||
expoly.contour.make_clockwise();
|
||||
for(auto& h : expoly.holes) h.make_counter_clockwise();
|
||||
|
||||
ss << "\t{\n";
|
||||
ss << "\t\t{\n";
|
||||
|
||||
for(auto v : expoly.contour.points) ss << "\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = expoly.contour.points.front();
|
||||
ss << "\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
// Holes:
|
||||
ss << "\t\t{\n";
|
||||
if(holes) for(auto h : expoly.holes) {
|
||||
ss << "\t\t\t{\n";
|
||||
for(auto v : h.points) ss << "\t\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = h.points.front();
|
||||
ss << "\t\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t\t},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
ss << "\t},\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ss << "}\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void toSVG(SVG& svg, const Model& model) {
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
svg.draw(expolys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A container which stores a pointer to the 3D object and its projected
|
||||
// 2D shape from top view.
|
||||
using ShapeData2D =
|
||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
|
||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
ShapeData2D ret;
|
||||
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0,
|
||||
[](size_t s, ModelObject* o){
|
||||
return s + o->instances.size();
|
||||
});
|
||||
|
||||
ret.reserve(s);
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(objptr) {
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
ClipperLib::PolygonImpl pn;
|
||||
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
|
||||
// TODO export the exact 2D projection
|
||||
auto p = tmpmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
pn.Contour = Slic3rMultiPoint_to_ClipperPath( p );
|
||||
|
||||
// Efficient conversion to item.
|
||||
Item item(std::move(pn));
|
||||
|
||||
// Invalid geometries would throw exceptions when arranging
|
||||
if(item.vertexCount() > 3) {
|
||||
item.rotation(objinst->rotation);
|
||||
item.translation( {
|
||||
ClipperLib::cInt(objinst->offset.x/SCALING_FACTOR),
|
||||
ClipperLib::cInt(objinst->offset.y/SCALING_FACTOR)
|
||||
});
|
||||
ret.emplace_back(objinst, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Arranges the model objects on the screen.
|
||||
*
|
||||
* The arrangement considers multiple bins (aka. print beds) for placing all
|
||||
* the items provided in the model argument. If the items don't fit on one
|
||||
* print bed, the remaining will be placed onto newly created print beds.
|
||||
* The first_bin_only parameter, if set to true, disables this behaviour and
|
||||
* makes sure that only one print bed is filled and the remaining items will be
|
||||
* untouched. When set to false, the items which could not fit onto the
|
||||
* print bed will be placed next to the print bed so the user should see a
|
||||
* pile of items on the print bed and some other piles outside the print
|
||||
* area that can be dragged later onto the print bed as a group.
|
||||
*
|
||||
* \param model The model object with the 3D content.
|
||||
* \param dist The minimum distance which is allowed for any pair of items
|
||||
* on the print bed in any direction.
|
||||
* \param bb The bounding box of the print bed. It corresponds to the 'bin'
|
||||
* for bin packing.
|
||||
* \param first_bin_only This parameter controls whether to place the
|
||||
* remaining items which do not fit onto the print area next to the print
|
||||
* bed or leave them untouched (let the user arrange them by hand or remove
|
||||
* them).
|
||||
*/
|
||||
bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
|
||||
bool first_bin_only,
|
||||
std::function<void(unsigned)> progressind)
|
||||
{
|
||||
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
||||
|
||||
bool ret = true;
|
||||
|
||||
// Create the arranger config
|
||||
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
|
||||
|
||||
// Get the 2D projected shapes with their 3D model instance pointers
|
||||
auto shapemap = arr::projectModelFromTop(model);
|
||||
|
||||
bool hasbin = bb != nullptr && bb->defined;
|
||||
double area_max = 0;
|
||||
|
||||
// Copy the references for the shapes only as the arranger expects a
|
||||
// sequence of objects convertible to Item or ClipperPolygon
|
||||
std::vector<std::reference_wrapper<Item>> shapes;
|
||||
shapes.reserve(shapemap.size());
|
||||
std::for_each(shapemap.begin(), shapemap.end(),
|
||||
[&shapes, min_obj_distance, &area_max, hasbin]
|
||||
(ShapeData2D::value_type& it)
|
||||
{
|
||||
shapes.push_back(std::ref(it.second));
|
||||
});
|
||||
|
||||
Box bin;
|
||||
|
||||
if(hasbin) {
|
||||
// Scale up the bounding box to clipper scale.
|
||||
BoundingBoxf bbb = *bb;
|
||||
bbb.scale(1.0/SCALING_FACTOR);
|
||||
|
||||
bin = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min.x),
|
||||
static_cast<libnest2d::Coord>(bbb.min.y)
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max.x),
|
||||
static_cast<libnest2d::Coord>(bbb.max.y)
|
||||
});
|
||||
}
|
||||
|
||||
// Will use the DJD selection heuristic with the BottomLeft placement
|
||||
// strategy
|
||||
using Arranger = Arranger<NfpPlacer, FirstFitSelection>;
|
||||
using PConf = Arranger::PlacementConfig;
|
||||
using SConf = Arranger::SelectionConfig;
|
||||
|
||||
PConf pcfg; // Placement configuration
|
||||
SConf scfg; // Selection configuration
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
|
||||
// Start placing the items from the center of the print bed
|
||||
pcfg.starting_point = PConf::Alignment::CENTER;
|
||||
|
||||
// TODO cannot use rotations until multiple objects of same geometry can
|
||||
// handle different rotations
|
||||
// arranger.useMinimumBoundigBoxRotation();
|
||||
pcfg.rotations = { 0.0 };
|
||||
|
||||
// Magic: we will specify what is the goal of arrangement... In this case
|
||||
// we override the default object function to make the larger items go into
|
||||
// the center of the pile and smaller items orbit it so the resulting pile
|
||||
// has a circle-like shape. This is good for the print bed's heat profile.
|
||||
// We alse sacrafice a bit of pack efficiency for this to work. As a side
|
||||
// effect, the arrange procedure is a lot faster (we do not need to
|
||||
// calculate the convex hulls)
|
||||
pcfg.object_function = [bin, hasbin](
|
||||
NfpPlacer::Pile pile, // The currently arranged pile
|
||||
double /*area*/, // Sum area of items (not needed)
|
||||
double norm, // A norming factor for physical dimensions
|
||||
double penality) // Min penality in case of bad arrangement
|
||||
{
|
||||
auto bb = ShapeLike::boundingBox(pile);
|
||||
|
||||
// We get the current item that's being evaluated.
|
||||
auto& sh = pile.back();
|
||||
|
||||
// We retrieve the reference point of this item
|
||||
auto rv = Nfp::referenceVertex(sh);
|
||||
|
||||
// We get the distance of the reference point from the center of the
|
||||
// heat bed
|
||||
auto c = bin.center();
|
||||
auto d = PointLike::distance(rv, c);
|
||||
|
||||
// The score will be the normalized distance which will be minimized,
|
||||
// effectively creating a circle shaped pile of items
|
||||
double score = double(d)/norm;
|
||||
|
||||
// If it does not fit into the print bed we will beat it
|
||||
// with a large penality. If we would not do this, there would be only
|
||||
// one big pile that doesn't care whether it fits onto the print bed.
|
||||
if(hasbin && !NfpPlacer::wouldFit(bb, bin)) score = 2*penality - score;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
// Create the arranger object
|
||||
Arranger arranger(bin, min_obj_distance, pcfg, scfg);
|
||||
|
||||
// Set the progress indicator for the arranger.
|
||||
arranger.progressIndicator(progressind);
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
auto result = arranger.arrangeIndexed(shapes.begin(), shapes.end());
|
||||
|
||||
auto applyResult = [&shapemap](ArrangeResult::value_type& group,
|
||||
Coord batch_offset)
|
||||
{
|
||||
for(auto& r : group) {
|
||||
auto idx = r.first; // get the original item index
|
||||
Item& item = r.second; // get the item itself
|
||||
|
||||
// Get the model instance from the shapemap using the index
|
||||
ModelInstance *inst_ptr = shapemap[idx].first;
|
||||
|
||||
// Get the tranformation data from the item object and scale it
|
||||
// appropriately
|
||||
auto off = item.translation();
|
||||
Radians rot = item.rotation();
|
||||
Pointf foff(off.X*SCALING_FACTOR + batch_offset,
|
||||
off.Y*SCALING_FACTOR);
|
||||
|
||||
// write the tranformation data into the model instance
|
||||
inst_ptr->rotation = rot;
|
||||
inst_ptr->offset = foff;
|
||||
}
|
||||
};
|
||||
|
||||
if(first_bin_only) {
|
||||
applyResult(result.front(), 0);
|
||||
} else {
|
||||
|
||||
const auto STRIDE_PADDING = 1.2;
|
||||
|
||||
Coord stride = static_cast<Coord>(STRIDE_PADDING*
|
||||
bin.width()*SCALING_FACTOR);
|
||||
Coord batch_offset = 0;
|
||||
|
||||
for(auto& group : result) {
|
||||
applyResult(group, batch_offset);
|
||||
|
||||
// Only the first pack group can be placed onto the print bed. The
|
||||
// other objects which could not fit will be placed next to the
|
||||
// print bed
|
||||
batch_offset += stride;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto objptr : model.objects) objptr->invalidate_bounding_box();
|
||||
|
||||
return ret && result.size() == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* arrange objects preserving their instance count
|
||||
but altering their instance positions */
|
||||
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb,
|
||||
std::function<void(unsigned)> progressind)
|
||||
bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
||||
{
|
||||
bool ret = false;
|
||||
if(bb != nullptr && bb->defined) {
|
||||
// Despite the new arrange is able to run without a specified bin,
|
||||
// the perl testsuit still fails for this case. For now the safest
|
||||
// thing to do is to use the new arrange only when a proper bin is
|
||||
// specified.
|
||||
ret = arr::arrange(*this, dist, bb, false, progressind);
|
||||
} else {
|
||||
// get the (transformed) size of each instance so that we take
|
||||
// into account their different transformations when packing
|
||||
Pointfs instance_sizes;
|
||||
Pointfs instance_centers;
|
||||
for (const ModelObject *o : this->objects)
|
||||
for (size_t i = 0; i < o->instances.size(); ++ i) {
|
||||
// an accurate snug bounding box around the transformed mesh.
|
||||
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
|
||||
instance_sizes.push_back(bbox.size());
|
||||
instance_centers.push_back(bbox.center());
|
||||
}
|
||||
|
||||
Pointfs positions;
|
||||
if (! _arrange(instance_sizes, dist, bb, positions))
|
||||
return false;
|
||||
|
||||
size_t idx = 0;
|
||||
for (ModelObject *o : this->objects) {
|
||||
for (ModelInstance *i : o->instances) {
|
||||
i->offset = positions[idx] - instance_centers[idx];
|
||||
++ idx;
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
// get the (transformed) size of each instance so that we take
|
||||
// into account their different transformations when packing
|
||||
Pointfs instance_sizes;
|
||||
Pointfs instance_centers;
|
||||
for (const ModelObject *o : this->objects)
|
||||
for (size_t i = 0; i < o->instances.size(); ++ i) {
|
||||
// an accurate snug bounding box around the transformed mesh.
|
||||
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
|
||||
instance_sizes.push_back(bbox.size());
|
||||
instance_centers.push_back(bbox.center());
|
||||
}
|
||||
|
||||
Pointfs positions;
|
||||
if (! _arrange(instance_sizes, dist, bb, positions))
|
||||
return false;
|
||||
|
||||
size_t idx = 0;
|
||||
for (ModelObject *o : this->objects) {
|
||||
for (ModelInstance *i : o->instances) {
|
||||
i->offset = positions[idx] - instance_centers[idx];
|
||||
++ idx;
|
||||
}
|
||||
o->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Duplicate the entire model preserving instance relative positions.
|
||||
|
@ -961,54 +620,6 @@ const BoundingBoxf3& ModelObject::bounding_box() const
|
|||
return m_bounding_box;
|
||||
}
|
||||
|
||||
BoundingBoxf3 ModelObject::tight_bounding_box(bool include_modifiers) const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
|
||||
for (const ModelVolume* vol : this->volumes)
|
||||
{
|
||||
if (include_modifiers || !vol->modifier)
|
||||
{
|
||||
for (const ModelInstance* inst : this->instances)
|
||||
{
|
||||
double c = cos(inst->rotation);
|
||||
double s = sin(inst->rotation);
|
||||
|
||||
for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
|
||||
{
|
||||
const stl_facet& facet = vol->mesh.stl.facet_start[f];
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
// original point
|
||||
const stl_vertex& v = facet.vertex[i];
|
||||
Pointf3 p((double)v.x, (double)v.y, (double)v.z);
|
||||
|
||||
// scale
|
||||
p.x *= inst->scaling_factor;
|
||||
p.y *= inst->scaling_factor;
|
||||
p.z *= inst->scaling_factor;
|
||||
|
||||
// rotate Z
|
||||
double x = p.x;
|
||||
double y = p.y;
|
||||
p.x = c * x - s * y;
|
||||
p.y = s * x + c * y;
|
||||
|
||||
// translate
|
||||
p.x += inst->offset.x;
|
||||
p.y += inst->offset.y;
|
||||
|
||||
bb.merge(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
// A mesh containing all transformed instances of this object.
|
||||
TriangleMesh ModelObject::mesh() const
|
||||
{
|
||||
|
@ -1093,25 +704,38 @@ void ModelObject::center_around_origin()
|
|||
void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.translate(float(x), float(y), float(z));
|
||||
if (m_bounding_box_valid)
|
||||
v->m_convex_hull.translate(float(x), float(y), float(z));
|
||||
}
|
||||
|
||||
if (m_bounding_box_valid)
|
||||
m_bounding_box.translate(x, y, z);
|
||||
}
|
||||
|
||||
void ModelObject::scale(const Pointf3 &versor)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.scale(versor);
|
||||
v->m_convex_hull.scale(versor);
|
||||
}
|
||||
// reset origin translation since it doesn't make sense anymore
|
||||
this->origin_translation = Pointf3(0,0,0);
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
void ModelObject::rotate(float angle, const Axis &axis)
|
||||
void ModelObject::rotate(float angle, const Pointf3& axis)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.rotate(angle, axis);
|
||||
this->origin_translation = Pointf3(0,0,0);
|
||||
v->m_convex_hull.rotate(angle, axis);
|
||||
}
|
||||
|
||||
center_around_origin();
|
||||
|
||||
this->origin_translation = Pointf3(0, 0, 0);
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
|
@ -1123,6 +747,7 @@ void ModelObject::transform(const float* matrix3x4)
|
|||
for (ModelVolume* v : volumes)
|
||||
{
|
||||
v->mesh.transform(matrix3x4);
|
||||
v->m_convex_hull.transform(matrix3x4);
|
||||
}
|
||||
|
||||
origin_translation = Pointf3(0.0, 0.0, 0.0);
|
||||
|
@ -1132,8 +757,12 @@ void ModelObject::transform(const float* matrix3x4)
|
|||
void ModelObject::mirror(const Axis &axis)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.mirror(axis);
|
||||
this->origin_translation = Pointf3(0,0,0);
|
||||
v->m_convex_hull.mirror(axis);
|
||||
}
|
||||
|
||||
this->origin_translation = Pointf3(0, 0, 0);
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
|
@ -1237,45 +866,20 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
|
||||
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
|
||||
{
|
||||
for (ModelVolume* vol : this->volumes)
|
||||
for (const ModelVolume* vol : this->volumes)
|
||||
{
|
||||
if (!vol->modifier)
|
||||
{
|
||||
for (ModelInstance* inst : this->instances)
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
std::vector<float> world_mat(UNIT_MATRIX, std::end(UNIT_MATRIX));
|
||||
Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
|
||||
m.translate(Eigen::Vector3f((float)inst->offset.x, (float)inst->offset.y, 0.0f));
|
||||
m.rotate(Eigen::AngleAxisf(inst->rotation, Eigen::Vector3f::UnitZ()));
|
||||
m.scale(inst->scaling_factor);
|
||||
::memcpy((void*)world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
|
||||
|
||||
double c = cos(inst->rotation);
|
||||
double s = sin(inst->rotation);
|
||||
|
||||
for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
|
||||
{
|
||||
const stl_facet& facet = vol->mesh.stl.facet_start[f];
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
// original point
|
||||
const stl_vertex& v = facet.vertex[i];
|
||||
Pointf3 p((double)v.x, (double)v.y, (double)v.z);
|
||||
|
||||
// scale
|
||||
p.x *= inst->scaling_factor;
|
||||
p.y *= inst->scaling_factor;
|
||||
p.z *= inst->scaling_factor;
|
||||
|
||||
// rotate Z
|
||||
double x = p.x;
|
||||
double y = p.y;
|
||||
p.x = c * x - s * y;
|
||||
p.y = s * x + c * y;
|
||||
|
||||
// translate
|
||||
p.x += inst->offset.x;
|
||||
p.y += inst->offset.y;
|
||||
|
||||
bb.merge(p);
|
||||
}
|
||||
}
|
||||
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(world_mat);
|
||||
|
||||
if (print_volume.contains(bb))
|
||||
inst->print_volume_state = ModelInstance::PVS_Inside;
|
||||
|
@ -1358,6 +962,16 @@ ModelMaterial* ModelVolume::assign_unique_material()
|
|||
return model->add_material(this->_material_id);
|
||||
}
|
||||
|
||||
void ModelVolume::calculate_convex_hull()
|
||||
{
|
||||
m_convex_hull = mesh.convex_hull_3d();
|
||||
}
|
||||
|
||||
const TriangleMesh& ModelVolume::get_convex_hull() const
|
||||
{
|
||||
return m_convex_hull;
|
||||
}
|
||||
|
||||
// Split this volume, append the result to the object owning this volume.
|
||||
// Return the number of volumes created from this one.
|
||||
// This is useful to assign different materials to different volumes of an object.
|
||||
|
|
|
@ -105,9 +105,6 @@ public:
|
|||
// This bounding box is being cached.
|
||||
const BoundingBoxf3& bounding_box() const;
|
||||
void invalidate_bounding_box() { m_bounding_box_valid = false; }
|
||||
// Returns a snug bounding box of the transformed instances.
|
||||
// This bounding box is not being cached.
|
||||
BoundingBoxf3 tight_bounding_box(bool include_modifiers) const;
|
||||
|
||||
// A mesh containing all transformed instances of this object.
|
||||
TriangleMesh mesh() const;
|
||||
|
@ -123,7 +120,7 @@ public:
|
|||
void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); }
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||
void scale(const Pointf3 &versor);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
void rotate(float angle, const Pointf3& axis);
|
||||
void transform(const float* matrix3x4);
|
||||
void mirror(const Axis &axis);
|
||||
size_t materials_count() const;
|
||||
|
@ -157,6 +154,10 @@ private:
|
|||
class ModelVolume
|
||||
{
|
||||
friend class ModelObject;
|
||||
|
||||
// The convex hull of this model's mesh.
|
||||
TriangleMesh m_convex_hull;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
// The triangular model.
|
||||
|
@ -180,19 +181,32 @@ public:
|
|||
|
||||
ModelMaterial* assign_unique_material();
|
||||
|
||||
void calculate_convex_hull();
|
||||
const TriangleMesh& get_convex_hull() const;
|
||||
|
||||
private:
|
||||
// Parent object owning this ModelVolume.
|
||||
ModelObject* object;
|
||||
t_model_material_id _material_id;
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object) {}
|
||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh) : mesh(std::move(mesh)), modifier(false), object(object) {}
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
||||
name(other.name), mesh(other.mesh), config(other.config), modifier(other.modifier), object(object)
|
||||
{ this->material_id(other.material_id()); }
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
|
||||
{
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
calculate_convex_hull();
|
||||
}
|
||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
||||
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
|
||||
{
|
||||
this->material_id(other.material_id());
|
||||
}
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
||||
name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
|
||||
{ this->material_id(other.material_id()); }
|
||||
{
|
||||
this->material_id(other.material_id());
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
calculate_convex_hull();
|
||||
}
|
||||
};
|
||||
|
||||
// A single instance of a ModelObject.
|
||||
|
@ -285,13 +299,10 @@ public:
|
|||
bool add_default_instances();
|
||||
// Returns approximate axis aligned bounding box of this model
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns tight axis aligned bounding box of this model
|
||||
BoundingBoxf3 transformed_bounding_box() const;
|
||||
void center_instances_around_point(const Pointf &point);
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
|
||||
TriangleMesh mesh() const;
|
||||
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL,
|
||||
std::function<void(unsigned)> progressind = [](unsigned){});
|
||||
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
// Croaks if the duplicated objects do not fit the print bed.
|
||||
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
|
||||
|
|
597
xs/src/libslic3r/ModelArrange.hpp
Normal file
|
@ -0,0 +1,597 @@
|
|||
#ifndef MODELARRANGE_HPP
|
||||
#define MODELARRANGE_HPP
|
||||
|
||||
#include "Model.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include <libnest2d.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <ClipperUtils.hpp>
|
||||
|
||||
#include <boost/geometry/index/rtree.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace arr {
|
||||
|
||||
using namespace libnest2d;
|
||||
|
||||
std::string toString(const Model& model, bool holes = true) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "{\n";
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
for(auto& expoly_complex : expolys) {
|
||||
|
||||
auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
|
||||
if(tmp.empty()) continue;
|
||||
auto expoly = tmp.front();
|
||||
expoly.contour.make_clockwise();
|
||||
for(auto& h : expoly.holes) h.make_counter_clockwise();
|
||||
|
||||
ss << "\t{\n";
|
||||
ss << "\t\t{\n";
|
||||
|
||||
for(auto v : expoly.contour.points) ss << "\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = expoly.contour.points.front();
|
||||
ss << "\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
// Holes:
|
||||
ss << "\t\t{\n";
|
||||
if(holes) for(auto h : expoly.holes) {
|
||||
ss << "\t\t\t{\n";
|
||||
for(auto v : h.points) ss << "\t\t\t\t{"
|
||||
<< v.x << ", "
|
||||
<< v.y << "},\n";
|
||||
{
|
||||
auto v = h.points.front();
|
||||
ss << "\t\t\t\t{" << v.x << ", " << v.y << "},\n";
|
||||
}
|
||||
ss << "\t\t\t},\n";
|
||||
}
|
||||
ss << "\t\t},\n";
|
||||
|
||||
ss << "\t},\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ss << "}\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void toSVG(SVG& svg, const Model& model) {
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(!objinst) continue;
|
||||
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
svg.draw(expolys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
||||
using SpatElement = std::pair<Box, unsigned>;
|
||||
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
|
||||
|
||||
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||
objfunc(const PointImpl& bincenter,
|
||||
double /*bin_area*/,
|
||||
ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile
|
||||
double /*pile_area*/,
|
||||
const Item &item,
|
||||
double norm, // A norming factor for physical dimensions
|
||||
std::vector<double>& areacache, // pile item areas will be cached
|
||||
// a spatial index to quickly get neighbors of the candidate item
|
||||
SpatIndex& spatindex
|
||||
)
|
||||
{
|
||||
using pl = PointLike;
|
||||
using sl = ShapeLike;
|
||||
|
||||
static const double BIG_ITEM_TRESHOLD = 0.2;
|
||||
static const double ROUNDNESS_RATIO = 0.5;
|
||||
static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
|
||||
|
||||
// We will treat big items (compared to the print bed) differently
|
||||
auto normarea = [norm](double area) { return std::sqrt(area)/norm; };
|
||||
|
||||
// If a new bin has been created:
|
||||
if(pile.size() < areacache.size()) {
|
||||
areacache.clear();
|
||||
spatindex.clear();
|
||||
}
|
||||
|
||||
// We must fill the caches:
|
||||
int idx = 0;
|
||||
for(auto& p : pile) {
|
||||
if(idx == areacache.size()) {
|
||||
areacache.emplace_back(sl::area(p));
|
||||
if(normarea(areacache[idx]) > BIG_ITEM_TRESHOLD)
|
||||
spatindex.insert({sl::boundingBox(p), idx});
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Candidate item bounding box
|
||||
auto ibb = item.boundingBox();
|
||||
|
||||
// Calculate the full bounding box of the pile with the candidate item
|
||||
pile.emplace_back(item.transformedShape());
|
||||
auto fullbb = ShapeLike::boundingBox(pile);
|
||||
pile.pop_back();
|
||||
|
||||
// The bounding box of the big items (they will accumulate in the center
|
||||
// of the pile
|
||||
Box bigbb;
|
||||
if(spatindex.empty()) bigbb = fullbb;
|
||||
else {
|
||||
auto boostbb = spatindex.bounds();
|
||||
boost::geometry::convert(boostbb, bigbb);
|
||||
}
|
||||
|
||||
// The size indicator of the candidate item. This is not the area,
|
||||
// but almost...
|
||||
double item_normarea = normarea(item.area());
|
||||
|
||||
// Will hold the resulting score
|
||||
double score = 0;
|
||||
|
||||
if(item_normarea > BIG_ITEM_TRESHOLD) {
|
||||
// This branch is for the bigger items..
|
||||
// Here we will use the closest point of the item bounding box to
|
||||
// the already arranged pile. So not the bb center nor the a choosen
|
||||
// corner but whichever is the closest to the center. This will
|
||||
// prevent some unwanted strange arrangements.
|
||||
|
||||
auto minc = ibb.minCorner(); // bottom left corner
|
||||
auto maxc = ibb.maxCorner(); // top right corner
|
||||
|
||||
// top left and bottom right corners
|
||||
auto top_left = PointImpl{getX(minc), getY(maxc)};
|
||||
auto bottom_right = PointImpl{getX(maxc), getY(minc)};
|
||||
|
||||
// Now the distance of the gravity center will be calculated to the
|
||||
// five anchor points and the smallest will be chosen.
|
||||
std::array<double, 5> dists;
|
||||
auto cc = fullbb.center(); // The gravity center
|
||||
dists[0] = pl::distance(minc, cc);
|
||||
dists[1] = pl::distance(maxc, cc);
|
||||
dists[2] = pl::distance(ibb.center(), cc);
|
||||
dists[3] = pl::distance(top_left, cc);
|
||||
dists[4] = pl::distance(bottom_right, cc);
|
||||
|
||||
// The smalles distance from the arranged pile center:
|
||||
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
||||
|
||||
// Density is the pack density: how big is the arranged pile
|
||||
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
|
||||
// Prepare a variable for the alignment score.
|
||||
// This will indicate: how well is the candidate item aligned with
|
||||
// its neighbors. We will check the aligment with all neighbors and
|
||||
// return the score for the best alignment. So it is enough for the
|
||||
// candidate to be aligned with only one item.
|
||||
auto alignment_score = std::numeric_limits<double>::max();
|
||||
|
||||
auto& trsh = item.transformedShape();
|
||||
|
||||
auto querybb = item.boundingBox();
|
||||
|
||||
// Query the spatial index for the neigbours
|
||||
std::vector<SpatElement> result;
|
||||
spatindex.query(bgi::intersects(querybb), std::back_inserter(result));
|
||||
|
||||
for(auto& e : result) { // now get the score for the best alignment
|
||||
auto idx = e.second;
|
||||
auto& p = pile[idx];
|
||||
auto parea = areacache[idx];
|
||||
auto bb = sl::boundingBox(sl::Shapes<PolygonImpl>{p, trsh});
|
||||
auto bbarea = bb.area();
|
||||
auto ascore = 1.0 - (item.area() + parea)/bbarea;
|
||||
|
||||
if(ascore < alignment_score) alignment_score = ascore;
|
||||
}
|
||||
|
||||
// The final mix of the score is the balance between the distance
|
||||
// from the full pile center, the pack density and the
|
||||
// alignment with the neigbours
|
||||
auto C = 0.33;
|
||||
score = C * dist + C * density + C * alignment_score;
|
||||
|
||||
} else if( item_normarea < BIG_ITEM_TRESHOLD && spatindex.empty()) {
|
||||
// If there are no big items, only small, we should consider the
|
||||
// density here as well to not get silly results
|
||||
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
|
||||
score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density;
|
||||
} else {
|
||||
// Here there are the small items that should be placed around the
|
||||
// already processed bigger items.
|
||||
// No need to play around with the anchor points, the center will be
|
||||
// just fine for small items
|
||||
score = pl::distance(ibb.center(), bigbb.center()) / norm;
|
||||
}
|
||||
|
||||
return std::make_tuple(score, fullbb);
|
||||
}
|
||||
|
||||
template<class PConf>
|
||||
void fillConfig(PConf& pcfg) {
|
||||
|
||||
// Align the arranged pile into the center of the bin
|
||||
pcfg.alignment = PConf::Alignment::CENTER;
|
||||
|
||||
// Start placing the items from the center of the print bed
|
||||
pcfg.starting_point = PConf::Alignment::CENTER;
|
||||
|
||||
// TODO cannot use rotations until multiple objects of same geometry can
|
||||
// handle different rotations
|
||||
// arranger.useMinimumBoundigBoxRotation();
|
||||
pcfg.rotations = { 0.0 };
|
||||
|
||||
// The accuracy of optimization.
|
||||
// Goes from 0.0 to 1.0 and scales performance as well
|
||||
pcfg.accuracy = 0.6f;
|
||||
}
|
||||
|
||||
template<class TBin>
|
||||
class AutoArranger {};
|
||||
|
||||
template<class TBin>
|
||||
class _ArrBase {
|
||||
protected:
|
||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
|
||||
using Selector = FirstFitSelection;
|
||||
using Packer = Arranger<Placer, Selector>;
|
||||
using PConfig = typename Packer::PlacementConfig;
|
||||
using Distance = TCoord<PointImpl>;
|
||||
using Pile = ShapeLike::Shapes<PolygonImpl>;
|
||||
|
||||
Packer pck_;
|
||||
PConfig pconf_; // Placement configuration
|
||||
double bin_area_;
|
||||
std::vector<double> areacache_;
|
||||
SpatIndex rtree_;
|
||||
public:
|
||||
|
||||
_ArrBase(const TBin& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
pck_(bin, dist), bin_area_(ShapeLike::area<PolygonImpl>(bin))
|
||||
{
|
||||
fillConfig(pconf_);
|
||||
pck_.progressIndicator(progressind);
|
||||
}
|
||||
|
||||
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
||||
areacache_.clear();
|
||||
return pck_.arrangeIndexed(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<Box>: public _ArrBase<Box> {
|
||||
public:
|
||||
|
||||
AutoArranger(const Box& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<Box>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, bin] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
|
||||
auto result = objfunc(bin.center(), bin_area_, pile,
|
||||
pile_area, item, norm, areacache_, rtree_);
|
||||
double score = std::get<0>(result);
|
||||
auto& fullbb = std::get<1>(result);
|
||||
|
||||
auto wdiff = fullbb.width() - bin.width();
|
||||
auto hdiff = fullbb.height() - bin.height();
|
||||
if(wdiff > 0) score += std::pow(wdiff, 2) / norm;
|
||||
if(hdiff > 0) score += std::pow(hdiff, 2) / norm;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
pck_.configure(pconf_);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
||||
public:
|
||||
AutoArranger(const PolygonImpl& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<PolygonImpl>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, &bin] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
|
||||
auto binbb = ShapeLike::boundingBox(bin);
|
||||
auto result = objfunc(binbb.center(), bin_area_, pile,
|
||||
pile_area, item, norm, areacache_, rtree_);
|
||||
double score = std::get<0>(result);
|
||||
|
||||
pile.emplace_back(item.transformedShape());
|
||||
auto chull = ShapeLike::convexHull(pile);
|
||||
pile.pop_back();
|
||||
|
||||
// If it does not fit into the print bed we will beat it with a
|
||||
// large penality. If we would not do this, there would be only one
|
||||
// big pile that doesn't care whether it fits onto the print bed.
|
||||
if(!Placer::wouldFit(chull, bin)) score += norm;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
pck_.configure(pconf_);
|
||||
}
|
||||
};
|
||||
|
||||
template<> // Specialization with no bin
|
||||
class AutoArranger<bool>: public _ArrBase<Box> {
|
||||
public:
|
||||
|
||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
|
||||
_ArrBase<Box>(Box(0, 0), dist, progressind)
|
||||
{
|
||||
this->pconf_.object_function = [this] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
double pile_area,
|
||||
double norm,
|
||||
double /*penality*/) {
|
||||
|
||||
auto result = objfunc({0, 0}, 0, pile, pile_area,
|
||||
item, norm, areacache_, rtree_);
|
||||
return std::get<0>(result);
|
||||
};
|
||||
|
||||
this->pck_.configure(pconf_);
|
||||
}
|
||||
};
|
||||
|
||||
// A container which stores a pointer to the 3D object and its projected
|
||||
// 2D shape from top view.
|
||||
using ShapeData2D =
|
||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
|
||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
ShapeData2D ret;
|
||||
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0,
|
||||
[](size_t s, ModelObject* o){
|
||||
return s + o->instances.size();
|
||||
});
|
||||
|
||||
ret.reserve(s);
|
||||
|
||||
for(auto objptr : model.objects) {
|
||||
if(objptr) {
|
||||
|
||||
auto rmesh = objptr->raw_mesh();
|
||||
|
||||
for(auto objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
Slic3r::TriangleMesh tmpmesh = rmesh;
|
||||
ClipperLib::PolygonImpl pn;
|
||||
|
||||
tmpmesh.scale(objinst->scaling_factor);
|
||||
|
||||
// TODO export the exact 2D projection
|
||||
auto p = tmpmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
pn.Contour = Slic3rMultiPoint_to_ClipperPath( p );
|
||||
|
||||
// Efficient conversion to item.
|
||||
Item item(std::move(pn));
|
||||
|
||||
// Invalid geometries would throw exceptions when arranging
|
||||
if(item.vertexCount() > 3) {
|
||||
item.rotation(objinst->rotation);
|
||||
item.translation( {
|
||||
ClipperLib::cInt(objinst->offset.x/SCALING_FACTOR),
|
||||
ClipperLib::cInt(objinst->offset.y/SCALING_FACTOR)
|
||||
});
|
||||
ret.emplace_back(objinst, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum BedShapeHint {
|
||||
BOX,
|
||||
CIRCLE,
|
||||
IRREGULAR,
|
||||
WHO_KNOWS
|
||||
};
|
||||
|
||||
BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) {
|
||||
// Determine the bed shape by hand
|
||||
return BOX;
|
||||
}
|
||||
|
||||
void applyResult(
|
||||
IndexedPackGroup::value_type& group,
|
||||
Coord batch_offset,
|
||||
ShapeData2D& shapemap)
|
||||
{
|
||||
for(auto& r : group) {
|
||||
auto idx = r.first; // get the original item index
|
||||
Item& item = r.second; // get the item itself
|
||||
|
||||
// Get the model instance from the shapemap using the index
|
||||
ModelInstance *inst_ptr = shapemap[idx].first;
|
||||
|
||||
// Get the tranformation data from the item object and scale it
|
||||
// appropriately
|
||||
auto off = item.translation();
|
||||
Radians rot = item.rotation();
|
||||
Pointf foff(off.X*SCALING_FACTOR + batch_offset,
|
||||
off.Y*SCALING_FACTOR);
|
||||
|
||||
// write the tranformation data into the model instance
|
||||
inst_ptr->rotation = rot;
|
||||
inst_ptr->offset = foff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Arranges the model objects on the screen.
|
||||
*
|
||||
* The arrangement considers multiple bins (aka. print beds) for placing all
|
||||
* the items provided in the model argument. If the items don't fit on one
|
||||
* print bed, the remaining will be placed onto newly created print beds.
|
||||
* The first_bin_only parameter, if set to true, disables this behaviour and
|
||||
* makes sure that only one print bed is filled and the remaining items will be
|
||||
* untouched. When set to false, the items which could not fit onto the
|
||||
* print bed will be placed next to the print bed so the user should see a
|
||||
* pile of items on the print bed and some other piles outside the print
|
||||
* area that can be dragged later onto the print bed as a group.
|
||||
*
|
||||
* \param model The model object with the 3D content.
|
||||
* \param dist The minimum distance which is allowed for any pair of items
|
||||
* on the print bed in any direction.
|
||||
* \param bb The bounding box of the print bed. It corresponds to the 'bin'
|
||||
* for bin packing.
|
||||
* \param first_bin_only This parameter controls whether to place the
|
||||
* remaining items which do not fit onto the print area next to the print
|
||||
* bed or leave them untouched (let the user arrange them by hand or remove
|
||||
* them).
|
||||
*/
|
||||
bool arrange(Model &model, coordf_t min_obj_distance,
|
||||
const Slic3r::Polyline& bed,
|
||||
BedShapeHint bedhint,
|
||||
bool first_bin_only,
|
||||
std::function<void(unsigned)> progressind)
|
||||
{
|
||||
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
||||
|
||||
bool ret = true;
|
||||
|
||||
// Get the 2D projected shapes with their 3D model instance pointers
|
||||
auto shapemap = arr::projectModelFromTop(model);
|
||||
|
||||
// Copy the references for the shapes only as the arranger expects a
|
||||
// sequence of objects convertible to Item or ClipperPolygon
|
||||
std::vector<std::reference_wrapper<Item>> shapes;
|
||||
shapes.reserve(shapemap.size());
|
||||
std::for_each(shapemap.begin(), shapemap.end(),
|
||||
[&shapes] (ShapeData2D::value_type& it)
|
||||
{
|
||||
shapes.push_back(std::ref(it.second));
|
||||
});
|
||||
|
||||
IndexedPackGroup result;
|
||||
BoundingBox bbb(bed.points);
|
||||
|
||||
auto binbb = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min.x),
|
||||
static_cast<libnest2d::Coord>(bbb.min.y)
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max.x),
|
||||
static_cast<libnest2d::Coord>(bbb.max.y)
|
||||
});
|
||||
|
||||
switch(bedhint) {
|
||||
case BOX: {
|
||||
|
||||
// Create the arranger for the box shaped bed
|
||||
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
case CIRCLE:
|
||||
break;
|
||||
case IRREGULAR:
|
||||
case WHO_KNOWS: {
|
||||
using P = libnest2d::PolygonImpl;
|
||||
|
||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||
P irrbed = ShapeLike::create<PolygonImpl>(std::move(ctour));
|
||||
|
||||
// std::cout << ShapeLike::toString(irrbed) << std::endl;
|
||||
|
||||
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if(first_bin_only) {
|
||||
applyResult(result.front(), 0, shapemap);
|
||||
} else {
|
||||
|
||||
const auto STRIDE_PADDING = 1.2;
|
||||
|
||||
Coord stride = static_cast<Coord>(STRIDE_PADDING*
|
||||
binbb.width()*SCALING_FACTOR);
|
||||
Coord batch_offset = 0;
|
||||
|
||||
for(auto& group : result) {
|
||||
applyResult(group, batch_offset, shapemap);
|
||||
|
||||
// Only the first pack group can be placed onto the print bed. The
|
||||
// other objects which could not fit will be placed next to the
|
||||
// print bed
|
||||
batch_offset += stride;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto objptr : model.objects) objptr->invalidate_bounding_box();
|
||||
|
||||
return ret && result.size() == 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif // MODELARRANGE_HPP
|
|
@ -263,6 +263,12 @@ operator<<(std::ostream &stm, const Pointf &pointf)
|
|||
return stm << pointf.x << "," << pointf.y;
|
||||
}
|
||||
|
||||
double
|
||||
Pointf::ccw(const Pointf &p1, const Pointf &p2) const
|
||||
{
|
||||
return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x);
|
||||
}
|
||||
|
||||
std::string
|
||||
Pointf::wkt() const
|
||||
{
|
||||
|
|
|
@ -221,6 +221,7 @@ public:
|
|||
static Pointf new_unscale(const Point &p) {
|
||||
return Pointf(unscale(p.x), unscale(p.y));
|
||||
};
|
||||
double ccw(const Pointf &p1, const Pointf &p2) const;
|
||||
std::string wkt() const;
|
||||
std::string dump_perl() const;
|
||||
void scale(double factor);
|
||||
|
|
|
@ -128,7 +128,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"gcode_comments",
|
||||
"gcode_flavor",
|
||||
"infill_acceleration",
|
||||
"infill_first",
|
||||
"layer_gcode",
|
||||
"min_fan_speed",
|
||||
"max_fan_speed",
|
||||
|
@ -155,6 +154,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"retract_restart_extra",
|
||||
"retract_restart_extra_toolchange",
|
||||
"retract_speed",
|
||||
"single_extruder_multi_material_priming",
|
||||
"slowdown_below_layer_time",
|
||||
"standby_temperature_delta",
|
||||
"start_gcode",
|
||||
|
@ -166,17 +166,16 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"use_relative_e_distances",
|
||||
"use_volumetric_e",
|
||||
"variable_layer_height",
|
||||
"wipe"
|
||||
"wipe",
|
||||
"wipe_tower_x",
|
||||
"wipe_tower_y",
|
||||
"wipe_tower_rotation_angle"
|
||||
};
|
||||
|
||||
std::vector<PrintStep> steps;
|
||||
std::vector<PrintObjectStep> osteps;
|
||||
bool invalidated = false;
|
||||
|
||||
// Always invalidate the wipe tower. This is probably necessary because of the wipe_into_infill / wipe_into_objects
|
||||
// features - nearly anything can influence what should (and could) be wiped into.
|
||||
steps.emplace_back(psWipeTower);
|
||||
|
||||
for (const t_config_option_key &opt_key : opt_keys) {
|
||||
if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||||
// These options only affect G-code export or they are just notes without influence on the generated G-code,
|
||||
|
@ -201,21 +200,22 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
|| opt_key == "filament_soluble"
|
||||
|| opt_key == "first_layer_temperature"
|
||||
|| opt_key == "filament_loading_speed"
|
||||
|| opt_key == "filament_loading_speed_start"
|
||||
|| opt_key == "filament_unloading_speed"
|
||||
|| opt_key == "filament_unloading_speed_start"
|
||||
|| opt_key == "filament_toolchange_delay"
|
||||
|| opt_key == "filament_cooling_moves"
|
||||
|| opt_key == "filament_minimal_purge_on_wipe_tower"
|
||||
|| opt_key == "filament_cooling_initial_speed"
|
||||
|| opt_key == "filament_cooling_final_speed"
|
||||
|| opt_key == "filament_ramming_parameters"
|
||||
|| opt_key == "gcode_flavor"
|
||||
|| opt_key == "infill_first"
|
||||
|| opt_key == "single_extruder_multi_material"
|
||||
|| opt_key == "spiral_vase"
|
||||
|| opt_key == "temperature"
|
||||
|| opt_key == "wipe_tower"
|
||||
|| opt_key == "wipe_tower_x"
|
||||
|| opt_key == "wipe_tower_y"
|
||||
|| opt_key == "wipe_tower_width"
|
||||
|| opt_key == "wipe_tower_rotation_angle"
|
||||
|| opt_key == "wipe_tower_bridging"
|
||||
|| opt_key == "wiping_volumes_matrix"
|
||||
|| opt_key == "parking_pos_retraction"
|
||||
|
@ -1051,6 +1051,8 @@ void Print::_make_wipe_tower()
|
|||
if (! this->has_wipe_tower())
|
||||
return;
|
||||
|
||||
m_wipe_tower_depth = 0.f;
|
||||
|
||||
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
|
||||
std::vector<float> wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
|
||||
// Extract purging volumes for each extruder pair:
|
||||
|
@ -1123,7 +1125,9 @@ void Print::_make_wipe_tower()
|
|||
this->config.temperature.get_at(i),
|
||||
this->config.first_layer_temperature.get_at(i),
|
||||
this->config.filament_loading_speed.get_at(i),
|
||||
this->config.filament_loading_speed_start.get_at(i),
|
||||
this->config.filament_unloading_speed.get_at(i),
|
||||
this->config.filament_unloading_speed_start.get_at(i),
|
||||
this->config.filament_toolchange_delay.get_at(i),
|
||||
this->config.filament_cooling_moves.get_at(i),
|
||||
this->config.filament_cooling_initial_speed.get_at(i),
|
||||
|
@ -1144,12 +1148,19 @@ void Print::_make_wipe_tower()
|
|||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
|
||||
for (const auto extruder_id : layer_tools.extruders) {
|
||||
if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
|
||||
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||
// Not all of that can be used for infill purging:
|
||||
volume_to_wipe -= config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||
|
||||
// try to assign some infills/objects for the wiping:
|
||||
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
|
||||
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
|
||||
|
||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
|
||||
// add back the minimal amount toforce on the wipe tower:
|
||||
volume_to_wipe += config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||
|
||||
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
|
||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
|
||||
first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
|
||||
current_extruder_id = extruder_id;
|
||||
}
|
||||
}
|
||||
|
@ -1162,7 +1173,8 @@ void Print::_make_wipe_tower()
|
|||
// Generate the wipe tower layers.
|
||||
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
|
||||
wipe_tower.generate(m_wipe_tower_tool_changes);
|
||||
|
||||
m_wipe_tower_depth = wipe_tower.get_depth();
|
||||
|
||||
// Unload the current filament over the purge tower.
|
||||
coordf_t layer_height = this->objects.front()->config.layer_height.value;
|
||||
if (m_tool_ordering.back().wipe_tower_partitions > 0) {
|
||||
|
@ -1183,10 +1195,6 @@ void Print::_make_wipe_tower()
|
|||
wipe_tower.tool_change((unsigned int)-1, false));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Print::output_filename()
|
||||
{
|
||||
this->placeholder_parser.update_timestamp();
|
||||
|
@ -1225,7 +1233,6 @@ void Print::set_status(int percent, const std::string &message)
|
|||
printf("Print::status %d => %s\n", percent, message.c_str());
|
||||
}
|
||||
|
||||
|
||||
// Returns extruder this eec should be printed with, according to PrintRegion config
|
||||
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion)
|
||||
{
|
||||
|
@ -1233,5 +1240,4 @@ int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion
|
|||
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -276,6 +276,7 @@ public:
|
|||
|
||||
void add_model_object(ModelObject* model_object, int idx = -1);
|
||||
bool apply_config(DynamicPrintConfig config);
|
||||
float get_wipe_tower_depth() const { return m_wipe_tower_depth; }
|
||||
bool has_infinite_skirt() const;
|
||||
bool has_skirt() const;
|
||||
// Returns an empty string if valid, otherwise returns an error message.
|
||||
|
@ -329,6 +330,9 @@ private:
|
|||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
|
||||
|
||||
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
||||
float m_wipe_tower_depth = 0.f;
|
||||
|
||||
// Has the calculation been canceled?
|
||||
tbb::atomic<bool> m_canceled;
|
||||
};
|
||||
|
|
|
@ -473,6 +473,14 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 28. };
|
||||
|
||||
def = this->add("filament_loading_speed_start", coFloats);
|
||||
def->label = L("Loading speed at the start");
|
||||
def->tooltip = L("Speed used at the very beginning of loading phase. ");
|
||||
def->sidetext = L("mm/s");
|
||||
def->cli = "filament-loading-speed-start=f@";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 3. };
|
||||
|
||||
def = this->add("filament_unloading_speed", coFloats);
|
||||
def->label = L("Unloading speed");
|
||||
def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect "
|
||||
|
@ -482,6 +490,14 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 90. };
|
||||
|
||||
def = this->add("filament_unloading_speed_start", coFloats);
|
||||
def->label = L("Unloading speed at the start");
|
||||
def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming. ");
|
||||
def->sidetext = L("mm/s");
|
||||
def->cli = "filament-unloading-speed-start=f@";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 100. };
|
||||
|
||||
def = this->add("filament_toolchange_delay", coFloats);
|
||||
def->label = L("Delay after unloading");
|
||||
def->tooltip = L("Time to wait after the filament is unloaded. "
|
||||
|
@ -504,19 +520,38 @@ PrintConfigDef::PrintConfigDef()
|
|||
def = this->add("filament_cooling_initial_speed", coFloats);
|
||||
def->label = L("Speed of the first cooling move");
|
||||
def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed. ");
|
||||
def->cli = "filament-cooling-initial-speed=i@";
|
||||
def->cli = "filament-cooling-initial-speed=f@";
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 2.2f };
|
||||
|
||||
def = this->add("filament_minimal_purge_on_wipe_tower", coFloats);
|
||||
def->label = L("Minimal purge on wipe tower");
|
||||
def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside "
|
||||
"the nozzle may not be known, and the filament pressure is likely not yet stable. "
|
||||
"Before purging the print head into an infill or a sacrificial object, Slic3r will always prime "
|
||||
"this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably.");
|
||||
def->cli = "filament-minimal-purge-on-wipe-tower=f@";
|
||||
def->sidetext = L("mm³");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 15.f };
|
||||
|
||||
def = this->add("filament_cooling_final_speed", coFloats);
|
||||
def->label = L("Speed of the last cooling move");
|
||||
def->tooltip = L("Cooling moves are gradually accelerating towards this speed. ");
|
||||
def->cli = "filament-cooling-final-speed=i@";
|
||||
def->cli = "filament-cooling-final-speed=f@";
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 3.4f };
|
||||
|
||||
def = this->add("filament_load_time", coFloats);
|
||||
def->label = L("Filament load time");
|
||||
def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to load a new filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
|
||||
def->cli = "filament-load-time=i@";
|
||||
def->sidetext = L("s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 0.0f };
|
||||
|
||||
def = this->add("filament_ramming_parameters", coStrings);
|
||||
def->label = L("Ramming parameters");
|
||||
def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
|
||||
|
@ -524,6 +559,14 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|"
|
||||
" 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" };
|
||||
|
||||
def = this->add("filament_unload_time", coFloats);
|
||||
def->label = L("Filament unload time");
|
||||
def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to unload a filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
|
||||
def->cli = "filament-unload-time=i@";
|
||||
def->sidetext = L("s");
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloats { 0.0f };
|
||||
|
||||
def = this->add("filament_diameter", coFloats);
|
||||
def->label = L("Diameter");
|
||||
def->tooltip = L("Enter your filament diameter here. Good precision is required, so use a caliper "
|
||||
|
@ -545,10 +588,7 @@ PrintConfigDef::PrintConfigDef()
|
|||
|
||||
def = this->add("filament_type", coStrings);
|
||||
def->label = L("Filament type");
|
||||
def->tooltip = L("If you want to process the output G-code through custom scripts, just list their "
|
||||
"absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed "
|
||||
"the absolute path to the G-code file as the first argument, and they can access "
|
||||
"the Slic3r config settings by reading environment variables.");
|
||||
def->tooltip = L("The filament material type for use in custom G-codes.");
|
||||
def->cli = "filament_type=s@";
|
||||
def->gui_type = "f_enum_open";
|
||||
def->gui_flags = "show_value";
|
||||
|
@ -892,8 +932,16 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(0.3);
|
||||
|
||||
def = this->add("remaining_times", coBool);
|
||||
def->label = L("Supports remaining times");
|
||||
def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute"
|
||||
" intervals into the G-code to let the firmware show accurate remaining time."
|
||||
" As of now only the Prusa i3 MK3 firmware recognizes M73."
|
||||
" Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode.");
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("silent_mode", coBool);
|
||||
def->label = L("Support silent mode");
|
||||
def->label = L("Supports silent mode");
|
||||
def->tooltip = L("Set silent mode for the G-code flavor");
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
|
@ -1105,25 +1153,37 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->cli = "nozzle-diameter=f@";
|
||||
def->default_value = new ConfigOptionFloats { 0.5 };
|
||||
|
||||
def = this->add("octoprint_apikey", coString);
|
||||
def->label = L("API Key");
|
||||
def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
|
||||
"the API Key required for authentication.");
|
||||
def->cli = "octoprint-apikey=s";
|
||||
def = this->add("host_type", coEnum);
|
||||
def->label = L("Host Type");
|
||||
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
|
||||
"the kind of the host.");
|
||||
def->cli = "host-type=s";
|
||||
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
|
||||
def->enum_values.push_back("octoprint");
|
||||
def->enum_values.push_back("duet");
|
||||
def->enum_labels.push_back("OctoPrint");
|
||||
def->enum_labels.push_back("Duet");
|
||||
def->default_value = new ConfigOptionEnum<PrintHostType>(htOctoPrint);
|
||||
|
||||
def = this->add("printhost_apikey", coString);
|
||||
def->label = L("API Key / Password");
|
||||
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
|
||||
"the API Key or the password required for authentication.");
|
||||
def->cli = "printhost-apikey=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
def = this->add("octoprint_cafile", coString);
|
||||
def = this->add("printhost_cafile", coString);
|
||||
def->label = "HTTPS CA file";
|
||||
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
|
||||
"If left blank, the default OS CA certificate repository is used.";
|
||||
def->cli = "octoprint-cafile=s";
|
||||
def->cli = "printhost-cafile=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
def = this->add("octoprint_host", coString);
|
||||
def = this->add("print_host", coString);
|
||||
def->label = L("Hostname, IP or URL");
|
||||
def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
|
||||
"the hostname, IP address or URL of the OctoPrint instance.");
|
||||
def->cli = "octoprint-host=s";
|
||||
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
|
||||
"the hostname, IP address or URL of the printer host instance.");
|
||||
def->cli = "print-host=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
def = this->add("only_retract_when_crossing_perimeters", coBool);
|
||||
|
@ -1623,6 +1683,12 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->cli = "single-extruder-multi-material!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("single_extruder_multi_material_priming", coBool);
|
||||
def->label = L("Prime all printing extruders");
|
||||
def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print.");
|
||||
def->cli = "single-extruder-multi-material-priming!";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("support_material", coBool);
|
||||
def->label = L("Generate support material");
|
||||
def->category = L("Support material");
|
||||
|
@ -1993,8 +2059,8 @@ PrintConfigDef::PrintConfigDef()
|
|||
|
||||
def = this->add("wipe_into_infill", coBool);
|
||||
def->category = L("Extruders");
|
||||
def->label = L("Purging into infill");
|
||||
def->tooltip = L("Wiping after toolchange will be preferentially done inside infills. "
|
||||
def->label = L("Wipe into this object's infill");
|
||||
def->tooltip = L("Purging after toolchange will done inside this object's infills. "
|
||||
"This lowers the amount of waste but may result in longer print time "
|
||||
" due to additional travel moves.");
|
||||
def->cli = "wipe-into-infill!";
|
||||
|
@ -2002,8 +2068,8 @@ PrintConfigDef::PrintConfigDef()
|
|||
|
||||
def = this->add("wipe_into_objects", coBool);
|
||||
def->category = L("Extruders");
|
||||
def->label = L("Purging into objects");
|
||||
def->tooltip = L("Objects will be used to wipe the nozzle after a toolchange to save material "
|
||||
def->label = L("Wipe into this object");
|
||||
def->tooltip = L("Object will be used to purge the nozzle after a toolchange to save material "
|
||||
"that would otherwise end up in the wipe tower and decrease print time. "
|
||||
"Colours of the objects will be mixed as a result.");
|
||||
def->cli = "wipe-into-objects!";
|
||||
|
@ -2069,10 +2135,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
std::ostringstream oss;
|
||||
oss << "0x0," << p.value.x << "x0," << p.value.x << "x" << p.value.y << ",0x" << p.value.y;
|
||||
value = oss.str();
|
||||
// Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r.
|
||||
// Commenting this out fixes github issue #869 for now.
|
||||
// } else if (opt_key == "octoprint_host" && !value.empty()) {
|
||||
// opt_key = "print_host";
|
||||
} else if ((opt_key == "perimeter_acceleration" && value == "25")
|
||||
|| (opt_key == "infill_acceleration" && value == "50")) {
|
||||
/* For historical reasons, the world's full of configs having these very low values;
|
||||
|
@ -2083,6 +2145,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
} else if (opt_key == "support_material_pattern" && value == "pillars") {
|
||||
// Slic3r PE does not support the pillars. They never worked well.
|
||||
value = "rectilinear";
|
||||
} else if (opt_key == "octoprint_host") {
|
||||
opt_key = "print_host";
|
||||
} else if (opt_key == "octoprint_cafile") {
|
||||
opt_key = "printhost_cafile";
|
||||
} else if (opt_key == "octoprint_apikey") {
|
||||
opt_key = "printhost_apikey";
|
||||
}
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
|
@ -2092,9 +2160,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||
"seal_position", "vibration_limit", "bed_size",
|
||||
// Maybe one day we will rename octoprint_host to print_host as it has been done in the upstream Slic3r.
|
||||
// Commenting this out fixes github issue #869 for now.
|
||||
// "octoprint_host",
|
||||
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe"
|
||||
};
|
||||
|
||||
|
@ -2104,7 +2169,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
}
|
||||
|
||||
if (! print_config_def.has(opt_key)) {
|
||||
//printf("Unknown option %s\n", opt_key.c_str());
|
||||
opt_key = "";
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,10 @@ enum GCodeFlavor {
|
|||
gcfSmoothie, gcfNoExtrusion,
|
||||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htOctoPrint, htDuet,
|
||||
};
|
||||
|
||||
enum InfillPattern {
|
||||
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
||||
|
@ -61,6 +65,15 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
|
|||
return keys_map;
|
||||
}
|
||||
|
||||
template<> inline t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
|
||||
static t_config_enum_values keys_map;
|
||||
if (keys_map.empty()) {
|
||||
keys_map["octoprint"] = htOctoPrint;
|
||||
keys_map["duet"] = htDuet;
|
||||
}
|
||||
return keys_map;
|
||||
}
|
||||
|
||||
template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
|
||||
static t_config_enum_values keys_map;
|
||||
if (keys_map.empty()) {
|
||||
|
@ -536,10 +549,15 @@ public:
|
|||
ConfigOptionFloats filament_cost;
|
||||
ConfigOptionFloats filament_max_volumetric_speed;
|
||||
ConfigOptionFloats filament_loading_speed;
|
||||
ConfigOptionFloats filament_loading_speed_start;
|
||||
ConfigOptionFloats filament_load_time;
|
||||
ConfigOptionFloats filament_unloading_speed;
|
||||
ConfigOptionFloats filament_unloading_speed_start;
|
||||
ConfigOptionFloats filament_toolchange_delay;
|
||||
ConfigOptionFloats filament_unload_time;
|
||||
ConfigOptionInts filament_cooling_moves;
|
||||
ConfigOptionFloats filament_cooling_initial_speed;
|
||||
ConfigOptionFloats filament_minimal_purge_on_wipe_tower;
|
||||
ConfigOptionFloats filament_cooling_final_speed;
|
||||
ConfigOptionStrings filament_ramming_parameters;
|
||||
ConfigOptionBool gcode_comments;
|
||||
|
@ -561,6 +579,7 @@ public:
|
|||
ConfigOptionString start_gcode;
|
||||
ConfigOptionStrings start_filament_gcode;
|
||||
ConfigOptionBool single_extruder_multi_material;
|
||||
ConfigOptionBool single_extruder_multi_material_priming;
|
||||
ConfigOptionString toolchange_gcode;
|
||||
ConfigOptionFloat travel_speed;
|
||||
ConfigOptionBool use_firmware_retraction;
|
||||
|
@ -570,6 +589,7 @@ public:
|
|||
ConfigOptionFloat cooling_tube_retraction;
|
||||
ConfigOptionFloat cooling_tube_length;
|
||||
ConfigOptionFloat parking_pos_retraction;
|
||||
ConfigOptionBool remaining_times;
|
||||
ConfigOptionBool silent_mode;
|
||||
ConfigOptionFloat extra_loading_move;
|
||||
|
||||
|
@ -597,10 +617,15 @@ protected:
|
|||
OPT_PTR(filament_cost);
|
||||
OPT_PTR(filament_max_volumetric_speed);
|
||||
OPT_PTR(filament_loading_speed);
|
||||
OPT_PTR(filament_loading_speed_start);
|
||||
OPT_PTR(filament_load_time);
|
||||
OPT_PTR(filament_unloading_speed);
|
||||
OPT_PTR(filament_unloading_speed_start);
|
||||
OPT_PTR(filament_unload_time);
|
||||
OPT_PTR(filament_toolchange_delay);
|
||||
OPT_PTR(filament_cooling_moves);
|
||||
OPT_PTR(filament_cooling_initial_speed);
|
||||
OPT_PTR(filament_minimal_purge_on_wipe_tower);
|
||||
OPT_PTR(filament_cooling_final_speed);
|
||||
OPT_PTR(filament_ramming_parameters);
|
||||
OPT_PTR(gcode_comments);
|
||||
|
@ -620,6 +645,7 @@ protected:
|
|||
OPT_PTR(retract_restart_extra_toolchange);
|
||||
OPT_PTR(retract_speed);
|
||||
OPT_PTR(single_extruder_multi_material);
|
||||
OPT_PTR(single_extruder_multi_material_priming);
|
||||
OPT_PTR(start_gcode);
|
||||
OPT_PTR(start_filament_gcode);
|
||||
OPT_PTR(toolchange_gcode);
|
||||
|
@ -631,6 +657,7 @@ protected:
|
|||
OPT_PTR(cooling_tube_retraction);
|
||||
OPT_PTR(cooling_tube_length);
|
||||
OPT_PTR(parking_pos_retraction);
|
||||
OPT_PTR(remaining_times);
|
||||
OPT_PTR(silent_mode);
|
||||
OPT_PTR(extra_loading_move);
|
||||
}
|
||||
|
@ -787,18 +814,20 @@ class HostConfig : public StaticPrintConfig
|
|||
{
|
||||
STATIC_PRINT_CONFIG_CACHE(HostConfig)
|
||||
public:
|
||||
ConfigOptionString octoprint_host;
|
||||
ConfigOptionString octoprint_apikey;
|
||||
ConfigOptionString octoprint_cafile;
|
||||
ConfigOptionEnum<PrintHostType> host_type;
|
||||
ConfigOptionString print_host;
|
||||
ConfigOptionString printhost_apikey;
|
||||
ConfigOptionString printhost_cafile;
|
||||
ConfigOptionString serial_port;
|
||||
ConfigOptionInt serial_speed;
|
||||
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
OPT_PTR(octoprint_host);
|
||||
OPT_PTR(octoprint_apikey);
|
||||
OPT_PTR(octoprint_cafile);
|
||||
OPT_PTR(host_type);
|
||||
OPT_PTR(print_host);
|
||||
OPT_PTR(printhost_apikey);
|
||||
OPT_PTR(printhost_cafile);
|
||||
OPT_PTR(serial_port);
|
||||
OPT_PTR(serial_speed);
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ bool PrintObject::delete_last_copy()
|
|||
|
||||
bool PrintObject::set_copies(const Points &points)
|
||||
{
|
||||
bool copies_num_changed = this->_copies.size() != points.size();
|
||||
this->_copies = points;
|
||||
|
||||
// order copies with a nearest neighbor search and translate them by _copies_shift
|
||||
|
@ -93,7 +94,8 @@ bool PrintObject::set_copies(const Points &points)
|
|||
|
||||
bool invalidated = this->_print->invalidate_step(psSkirt);
|
||||
invalidated |= this->_print->invalidate_step(psBrim);
|
||||
invalidated |= this->_print->invalidate_step(psWipeTower);
|
||||
if (copies_num_changed)
|
||||
invalidated |= this->_print->invalidate_step(psWipeTower);
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include "TriangleMesh.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "qhull/src/libqhullcpp/Qhull.h"
|
||||
#include "qhull/src/libqhullcpp/QhullFacetList.h"
|
||||
#include "qhull/src/libqhullcpp/QhullVertexSet.h"
|
||||
#include <cmath>
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
|
@ -10,11 +13,14 @@
|
|||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#if 0
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
|
@ -318,6 +324,17 @@ void TriangleMesh::translate(float x, float y, float z)
|
|||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate(float angle, Pointf3 axis)
|
||||
{
|
||||
if (angle == 0.f)
|
||||
return;
|
||||
|
||||
axis = normalize(axis);
|
||||
Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
|
||||
m.rotate(Eigen::AngleAxisf(angle, Eigen::Vector3f(axis.x, axis.y, axis.z)));
|
||||
stl_transform(&stl, (float*)m.data());
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate(float angle, const Axis &axis)
|
||||
{
|
||||
if (angle == 0.f)
|
||||
|
@ -597,6 +614,140 @@ TriangleMesh::bounding_box() const
|
|||
return bb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const std::vector<float>& matrix) const
|
||||
{
|
||||
bool has_shared = (stl.v_shared != nullptr);
|
||||
if (!has_shared)
|
||||
stl_generate_shared_vertices(&stl);
|
||||
|
||||
unsigned int vertices_count = (stl.stats.shared_vertices > 0) ? (unsigned int)stl.stats.shared_vertices : 3 * (unsigned int)stl.stats.number_of_facets;
|
||||
|
||||
if (vertices_count == 0)
|
||||
return BoundingBoxf3();
|
||||
|
||||
Eigen::MatrixXf src_vertices(3, vertices_count);
|
||||
|
||||
if (stl.stats.shared_vertices > 0)
|
||||
{
|
||||
stl_vertex* vertex_ptr = stl.v_shared;
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++i)
|
||||
{
|
||||
src_vertices(0, i) = vertex_ptr->x;
|
||||
src_vertices(1, i) = vertex_ptr->y;
|
||||
src_vertices(2, i) = vertex_ptr->z;
|
||||
vertex_ptr += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stl_facet* facet_ptr = stl.facet_start;
|
||||
unsigned int v_id = 0;
|
||||
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
src_vertices(0, v_id) = facet_ptr->vertex[i].x;
|
||||
src_vertices(1, v_id) = facet_ptr->vertex[i].y;
|
||||
src_vertices(2, v_id) = facet_ptr->vertex[i].z;
|
||||
}
|
||||
facet_ptr += 1;
|
||||
++v_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_shared && (stl.stats.shared_vertices > 0))
|
||||
stl_invalidate_shared_vertices(&stl);
|
||||
|
||||
Eigen::Transform<float, 3, Eigen::Affine> m;
|
||||
::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
|
||||
|
||||
Eigen::MatrixXf dst_vertices(3, vertices_count);
|
||||
dst_vertices = m * src_vertices.colwise().homogeneous();
|
||||
|
||||
float min_x = dst_vertices(0, 0);
|
||||
float max_x = dst_vertices(0, 0);
|
||||
float min_y = dst_vertices(1, 0);
|
||||
float max_y = dst_vertices(1, 0);
|
||||
float min_z = dst_vertices(2, 0);
|
||||
float max_z = dst_vertices(2, 0);
|
||||
|
||||
for (int i = 1; i < vertices_count; ++i)
|
||||
{
|
||||
min_x = std::min(min_x, dst_vertices(0, i));
|
||||
max_x = std::max(max_x, dst_vertices(0, i));
|
||||
min_y = std::min(min_y, dst_vertices(1, i));
|
||||
max_y = std::max(max_y, dst_vertices(1, i));
|
||||
min_z = std::min(min_z, dst_vertices(2, i));
|
||||
max_z = std::max(max_z, dst_vertices(2, i));
|
||||
}
|
||||
|
||||
return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z));
|
||||
}
|
||||
|
||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
{
|
||||
// Helper struct for qhull:
|
||||
struct PointForQHull{
|
||||
PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {}
|
||||
realT x, y, z;
|
||||
};
|
||||
std::vector<PointForQHull> src_vertices;
|
||||
|
||||
// We will now fill the vector with input points for computation:
|
||||
stl_facet* facet_ptr = stl.facet_start;
|
||||
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
const stl_vertex& v = facet_ptr->vertex[i];
|
||||
src_vertices.emplace_back(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
facet_ptr += 1;
|
||||
}
|
||||
|
||||
// The qhull call:
|
||||
orgQhull::Qhull qhull;
|
||||
qhull.disableOutputStream(); // we want qhull to be quiet
|
||||
try
|
||||
{
|
||||
qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << "Unable to create convex hull" << std::endl;
|
||||
return TriangleMesh();
|
||||
}
|
||||
|
||||
// Let's collect results:
|
||||
Pointf3s det_vertices;
|
||||
std::vector<Point3> facets;
|
||||
auto facet_list = qhull.facetList().toStdVector();
|
||||
for (const orgQhull::QhullFacet& facet : facet_list)
|
||||
{ // iterate through facets
|
||||
orgQhull::QhullVertexSet vertices = facet.vertices();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{ // iterate through facet's vertices
|
||||
|
||||
orgQhull::QhullPoint p = vertices[i].point();
|
||||
const float* coords = p.coordinates();
|
||||
det_vertices.emplace_back(coords[0], coords[1], coords[2]);
|
||||
}
|
||||
unsigned int size = (unsigned int)det_vertices.size();
|
||||
facets.emplace_back(size - 3, size - 2, size - 1);
|
||||
}
|
||||
|
||||
TriangleMesh output_mesh(det_vertices, facets);
|
||||
output_mesh.repair();
|
||||
output_mesh.require_shared_vertices();
|
||||
return output_mesh;
|
||||
}
|
||||
|
||||
const float* TriangleMesh::first_vertex() const
|
||||
{
|
||||
return stl.facet_start ? &stl.facet_start->vertex[0].x : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::require_shared_vertices()
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
void scale(const Pointf3 &versor);
|
||||
void translate(float x, float y, float z);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
void rotate(float angle, Pointf3 axis);
|
||||
void rotate_x(float angle);
|
||||
void rotate_y(float angle);
|
||||
void rotate_z(float angle);
|
||||
|
@ -53,8 +54,13 @@ public:
|
|||
TriangleMeshPtrs split() const;
|
||||
void merge(const TriangleMesh &mesh);
|
||||
ExPolygons horizontal_projection() const;
|
||||
const float* first_vertex() const;
|
||||
Polygon convex_hull();
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns the bbox of this TriangleMesh transformed by the given matrix
|
||||
BoundingBoxf3 transformed_bounding_box(const std::vector<float>& matrix) const;
|
||||
// Returns the convex hull of this TriangleMesh
|
||||
TriangleMesh convex_hull_3d() const;
|
||||
void reset_repair_stats();
|
||||
bool needed_repair() const;
|
||||
size_t facets_count() const;
|
||||
|
@ -66,7 +72,7 @@ public:
|
|||
// Count disconnected triangle patches.
|
||||
size_t number_of_patches() const;
|
||||
|
||||
stl_file stl;
|
||||
mutable stl_file stl;
|
||||
bool repaired;
|
||||
|
||||
private:
|
||||
|
|
|
@ -84,6 +84,8 @@ inline T next_highest_power_of_2(T v)
|
|||
return ++ v;
|
||||
}
|
||||
|
||||
extern std::string xml_escape(std::string text);
|
||||
|
||||
class PerlCallback {
|
||||
public:
|
||||
PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||
#define SLIC3R_VERSION "1.41.0-alpha2"
|
||||
#define SLIC3R_VERSION "1.41.0-beta2"
|
||||
#define SLIC3R_BUILD "UNKNOWN"
|
||||
|
||||
typedef int32_t coord_t;
|
||||
|
|
|
@ -387,4 +387,31 @@ unsigned get_current_pid()
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string xml_escape(std::string text)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
for (;;)
|
||||
{
|
||||
pos = text.find_first_of("\"\'&<>", pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
std::string replacement;
|
||||
switch (text[pos])
|
||||
{
|
||||
case '\"': replacement = """; break;
|
||||
case '\'': replacement = "'"; break;
|
||||
case '&': replacement = "&"; break;
|
||||
case '<': replacement = "<"; break;
|
||||
case '>': replacement = ">"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
text.replace(pos, 1, replacement);
|
||||
pos += replacement.size();
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -64,9 +64,9 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
|
|||
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
||||
REGISTER_CLASS(TabIface, "GUI::Tab");
|
||||
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
|
||||
REGISTER_CLASS(OctoPrint, "OctoPrint");
|
||||
REGISTER_CLASS(AppController, "AppController");
|
||||
REGISTER_CLASS(PrintController, "PrintController");
|
||||
REGISTER_CLASS(PrintHost, "PrintHost");
|
||||
|
||||
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
||||
{
|
||||
|
|
47
xs/src/qhull/Announce.txt
Normal file
|
@ -0,0 +1,47 @@
|
|||
|
||||
Qhull 2015.2 2016/01/18
|
||||
|
||||
http://www.qhull.org
|
||||
git@github.com:qhull/qhull.git
|
||||
http://www.geomview.org
|
||||
|
||||
Qhull computes convex hulls, Delaunay triangulations, Voronoi diagrams,
|
||||
furthest-site Voronoi diagrams, and halfspace intersections about a point.
|
||||
It runs in 2-d, 3-d, 4-d, or higher. It implements the Quickhull algorithm
|
||||
for computing convex hulls. Qhull handles round-off errors from floating
|
||||
point arithmetic. It can approximate a convex hull.
|
||||
|
||||
The program includes options for hull volume, facet area, partial hulls,
|
||||
input transformations, randomization, tracing, multiple output formats, and
|
||||
execution statistics. The program can be called from within your application.
|
||||
You can view the results in 2-d, 3-d and 4-d with Geomview.
|
||||
|
||||
To download Qhull:
|
||||
http://www.qhull.org/download
|
||||
git@github.com:qhull/qhull.git
|
||||
|
||||
Download qhull-96.ps for:
|
||||
|
||||
Barber, C. B., D.P. Dobkin, and H.T. Huhdanpaa, "The
|
||||
Quickhull Algorithm for Convex Hulls," ACM Trans. on
|
||||
Mathematical Software, 22(4):469-483, Dec. 1996.
|
||||
http://www.acm.org/pubs/citations/journals/toms/1996-22-4/p469-barber/
|
||||
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405
|
||||
|
||||
Abstract:
|
||||
|
||||
The convex hull of a set of points is the smallest convex set that contains
|
||||
the points. This article presents a practical convex hull algorithm that
|
||||
combines the two-dimensional Quickhull Algorithm with the general dimension
|
||||
Beneath-Beyond Algorithm. It is similar to the randomized, incremental
|
||||
algorithms for convex hull and Delaunay triangulation. We provide empirical
|
||||
evidence that the algorithm runs faster when the input contains non-extreme
|
||||
points, and that it uses less memory.
|
||||
|
||||
Computational geometry algorithms have traditionally assumed that input sets
|
||||
are well behaved. When an algorithm is implemented with floating point
|
||||
arithmetic, this assumption can lead to serious errors. We briefly describe
|
||||
a solution to this problem when computing the convex hull in two, three, or
|
||||
four dimensions. The output is a set of "thick" facets that contain all
|
||||
possible exact convex hulls of the input. A variation is effective in five
|
||||
or more dimensions.
|
128
xs/src/qhull/CMakeLists.txt
Normal file
|
@ -0,0 +1,128 @@
|
|||
|
||||
# This CMake file is written specifically to integrate qhull library with Slic3rPE
|
||||
# (see https://github.com/prusa3d/Slic3r for more information about the project)
|
||||
#
|
||||
# Only original libraries qhullstatic_r and qhullcpp are included.
|
||||
# They are built as a single statically linked library.
|
||||
#
|
||||
# Created by modification of the original qhull CMakeLists.
|
||||
# Lukas Matena (25.7.2018), lukasmatena@seznam.cz
|
||||
|
||||
|
||||
project(qhull)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri
|
||||
set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c
|
||||
set(qhull_VERSION "7.2.0") # Advance every release
|
||||
|
||||
#include(CMakeModules/CheckLFS.cmake)
|
||||
#option(WITH_LFS "Enable Large File Support" ON)
|
||||
#check_lfs(WITH_LFS)
|
||||
|
||||
|
||||
message(STATUS "qhull Version: ${qhull_VERSION} (static linking)")
|
||||
|
||||
|
||||
set(libqhull_HEADERS
|
||||
# reentrant qhull HEADERS:
|
||||
src/libqhull_r/libqhull_r.h
|
||||
src/libqhull_r/geom_r.h
|
||||
src/libqhull_r/io_r.h
|
||||
src/libqhull_r/mem_r.h
|
||||
src/libqhull_r/merge_r.h
|
||||
src/libqhull_r/poly_r.h
|
||||
src/libqhull_r/qhull_ra.h
|
||||
src/libqhull_r/qset_r.h
|
||||
src/libqhull_r/random_r.h
|
||||
src/libqhull_r/stat_r.h
|
||||
src/libqhull_r/user_r.h
|
||||
|
||||
# C++ interface to reentrant Qhull HEADERS:
|
||||
src/libqhullcpp/Coordinates.h
|
||||
src/libqhullcpp/functionObjects.h
|
||||
src/libqhullcpp/PointCoordinates.h
|
||||
src/libqhullcpp/Qhull.h
|
||||
src/libqhullcpp/QhullError.h
|
||||
src/libqhullcpp/QhullFacet.h
|
||||
src/libqhullcpp/QhullFacetList.h
|
||||
src/libqhullcpp/QhullFacetSet.h
|
||||
src/libqhullcpp/QhullHyperplane.h
|
||||
src/libqhullcpp/QhullIterator.h
|
||||
src/libqhullcpp/QhullLinkedList.h
|
||||
src/libqhullcpp/QhullPoint.h
|
||||
src/libqhullcpp/QhullPoints.h
|
||||
src/libqhullcpp/QhullPointSet.h
|
||||
src/libqhullcpp/QhullQh.h
|
||||
src/libqhullcpp/QhullRidge.h
|
||||
src/libqhullcpp/QhullSet.h
|
||||
src/libqhullcpp/QhullSets.h
|
||||
src/libqhullcpp/QhullStat.h
|
||||
src/libqhullcpp/QhullVertex.h
|
||||
src/libqhullcpp/QhullVertexSet.h
|
||||
src/libqhullcpp/RboxPoints.h
|
||||
src/libqhullcpp/RoadError.h
|
||||
src/libqhullcpp/RoadLogEvent.h
|
||||
src/qhulltest/RoadTest.h
|
||||
)
|
||||
|
||||
set(libqhull_SOURCES
|
||||
# reentrant qhull SOURCES:
|
||||
src/libqhull_r/global_r.c
|
||||
src/libqhull_r/stat_r.c
|
||||
src/libqhull_r/geom2_r.c
|
||||
src/libqhull_r/poly2_r.c
|
||||
src/libqhull_r/merge_r.c
|
||||
src/libqhull_r/libqhull_r.c
|
||||
src/libqhull_r/geom_r.c
|
||||
src/libqhull_r/poly_r.c
|
||||
src/libqhull_r/qset_r.c
|
||||
src/libqhull_r/mem_r.c
|
||||
src/libqhull_r/random_r.c
|
||||
src/libqhull_r/usermem_r.c
|
||||
src/libqhull_r/userprintf_r.c
|
||||
src/libqhull_r/io_r.c
|
||||
src/libqhull_r/user_r.c
|
||||
src/libqhull_r/rboxlib_r.c
|
||||
src/libqhull_r/userprintf_rbox_r.c
|
||||
|
||||
# C++ interface to reentrant Qhull SOURCES:
|
||||
src/libqhullcpp/Coordinates.cpp
|
||||
src/libqhullcpp/PointCoordinates.cpp
|
||||
src/libqhullcpp/Qhull.cpp
|
||||
src/libqhullcpp/QhullFacet.cpp
|
||||
src/libqhullcpp/QhullFacetList.cpp
|
||||
src/libqhullcpp/QhullFacetSet.cpp
|
||||
src/libqhullcpp/QhullHyperplane.cpp
|
||||
src/libqhullcpp/QhullPoint.cpp
|
||||
src/libqhullcpp/QhullPointSet.cpp
|
||||
src/libqhullcpp/QhullPoints.cpp
|
||||
src/libqhullcpp/QhullQh.cpp
|
||||
src/libqhullcpp/QhullRidge.cpp
|
||||
src/libqhullcpp/QhullSet.cpp
|
||||
src/libqhullcpp/QhullStat.cpp
|
||||
src/libqhullcpp/QhullVertex.cpp
|
||||
src/libqhullcpp/QhullVertexSet.cpp
|
||||
src/libqhullcpp/RboxPoints.cpp
|
||||
src/libqhullcpp/RoadError.cpp
|
||||
src/libqhullcpp/RoadLogEvent.cpp
|
||||
|
||||
# headers for both (libqhullr and libqhullcpp:
|
||||
${libqhull_HEADERS}
|
||||
)
|
||||
|
||||
|
||||
##################################################
|
||||
# combined library (reentrant qhull and qhullcpp) for Slic3r:
|
||||
set(qhull_STATIC qhull)
|
||||
add_library(${qhull_STATIC} STATIC ${libqhull_SOURCES})
|
||||
set_target_properties(${qhull_STATIC} PROPERTIES
|
||||
VERSION ${qhull_VERSION})
|
||||
|
||||
if(UNIX)
|
||||
target_link_libraries(${qhull_STATIC} m)
|
||||
endif(UNIX)
|
||||
##################################################
|
||||
|
||||
# LIBDIR is defined in the main xs CMake file:
|
||||
target_include_directories(${qhull_STATIC} PRIVATE ${LIBDIR}/qhull/src)
|
38
xs/src/qhull/COPYING.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
Qhull, Copyright (c) 1993-2015
|
||||
|
||||
C.B. Barber
|
||||
Arlington, MA
|
||||
|
||||
and
|
||||
|
||||
The National Science and Technology Research Center for
|
||||
Computation and Visualization of Geometric Structures
|
||||
(The Geometry Center)
|
||||
University of Minnesota
|
||||
|
||||
email: qhull@qhull.org
|
||||
|
||||
This software includes Qhull from C.B. Barber and The Geometry Center.
|
||||
Qhull is copyrighted as noted above. Qhull is free software and may
|
||||
be obtained via http from www.qhull.org. It may be freely copied, modified,
|
||||
and redistributed under the following conditions:
|
||||
|
||||
1. All copyright notices must remain intact in all files.
|
||||
|
||||
2. A copy of this text file must be distributed along with any copies
|
||||
of Qhull that you redistribute; this includes copies that you have
|
||||
modified, or copies of programs or other software products that
|
||||
include Qhull.
|
||||
|
||||
3. If you modify Qhull, you must include a notice giving the
|
||||
name of the person performing the modification, the date of
|
||||
modification, and the reason for such modification.
|
||||
|
||||
4. When distributing modified versions of Qhull, or other software
|
||||
products that include Qhull, you must provide notice that the original
|
||||
source code may be obtained as noted above.
|
||||
|
||||
5. There is no warranty or other guarantee of fitness for Qhull, it is
|
||||
provided solely "as is". Bug reports or fixes may be sent to
|
||||
qhull_bug@qhull.org; the authors may or may not act on them as
|
||||
they desire.
|
623
xs/src/qhull/README.txt
Normal file
|
@ -0,0 +1,623 @@
|
|||
This distribution of qhull library is only meant for interfacing qhull with Slic3rPE
|
||||
(https://github.com/prusa3d/Slic3r).
|
||||
|
||||
The qhull source file was acquired from https://github.com/qhull/qhull at revision
|
||||
f0bd8ceeb84b554d7cdde9bbfae7d3351270478c.
|
||||
|
||||
No changes to the qhull library were made, except for
|
||||
- setting REALfloat=1 in user_r.h to enforce calculations in floats
|
||||
- modifying CMakeLists.txt (the original was renamed to origCMakeLists.txt)
|
||||
|
||||
Many thanks to C. Bradford Barber and all contributors.
|
||||
|
||||
Lukas Matena (lukasmatena@seznam.cz)
|
||||
25.7.2018
|
||||
|
||||
|
||||
See original contents of the README file below.
|
||||
|
||||
======================================================================================
|
||||
======================================================================================
|
||||
======================================================================================
|
||||
|
||||
|
||||
Name
|
||||
|
||||
qhull, rbox 2015.2 2016/01/18
|
||||
|
||||
Convex hull, Delaunay triangulation, Voronoi diagrams, Halfspace intersection
|
||||
|
||||
Documentation:
|
||||
html/index.htm
|
||||
<http://www.qhull.org/html>
|
||||
|
||||
Available from:
|
||||
<http://www.qhull.org>
|
||||
<http://www.qhull.org/download>
|
||||
<http://github.com/qhull/qhull> (git@github.com:qhull/qhull.git)
|
||||
|
||||
News and a paper:
|
||||
<http://www.qhull.org/news>
|
||||
<http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.405>
|
||||
|
||||
Version 1 (simplicial only):
|
||||
<http://www.qhull.org/download/qhull-1.0.tar.gz>
|
||||
|
||||
Purpose
|
||||
|
||||
Qhull is a general dimension convex hull program that reads a set
|
||||
of points from stdin, and outputs the smallest convex set that contains
|
||||
the points to stdout. It also generates Delaunay triangulations, Voronoi
|
||||
diagrams, furthest-site Voronoi diagrams, and halfspace intersections
|
||||
about a point.
|
||||
|
||||
Rbox is a useful tool in generating input for Qhull; it generates
|
||||
hypercubes, diamonds, cones, circles, simplices, spirals,
|
||||
lattices, and random points.
|
||||
|
||||
Qhull produces graphical output for Geomview. This helps with
|
||||
understanding the output. <http://www.geomview.org>
|
||||
|
||||
Environment requirements
|
||||
|
||||
Qhull and rbox should run on all 32-bit and 64-bit computers. Use
|
||||
an ANSI C or C++ compiler to compile the program. The software is
|
||||
self-contained. It comes with examples and test scripts.
|
||||
|
||||
Qhull's C++ interface uses the STL. The C++ test program uses QTestLib
|
||||
from the Qt Framework. Qhull's C++ interface may change without
|
||||
notice. Eventually, it will move into the qhull shared library.
|
||||
|
||||
Qhull is copyrighted software. Please read COPYING.txt and REGISTER.txt
|
||||
before using or distributing Qhull.
|
||||
|
||||
To cite Qhull, please use
|
||||
|
||||
Barber, C.B., Dobkin, D.P., and Huhdanpaa, H.T., "The Quickhull
|
||||
algorithm for convex hulls," ACM Trans. on Mathematical Software,
|
||||
22(4):469-483, Dec 1996, http://www.qhull.org.
|
||||
|
||||
To modify Qhull, particularly the C++ interface
|
||||
|
||||
Qhull is on GitHub
|
||||
(http://github.com/qhull/qhull, git@github.com:qhull/qhull.git)
|
||||
|
||||
For internal documentation, see html/qh-code.htm
|
||||
|
||||
To install Qhull
|
||||
|
||||
Qhull is precompiled for Windows 32-bit, otherwise it needs compilation.
|
||||
|
||||
Qhull includes Makefiles for gcc and other targets, CMakeLists.txt for CMake,
|
||||
.sln/.vcproj/.vcxproj files for Microsoft Visual Studio, and .pro files
|
||||
for Qt Creator. It compiles under Windows with mingw.
|
||||
|
||||
Install and build instructions follow.
|
||||
|
||||
See the end of this document for a list of distributed files.
|
||||
|
||||
-----------------
|
||||
Installing Qhull on Windows 10, 8, 7 (32- or 64-bit), Windows XP, and Windows NT
|
||||
|
||||
The zip file contains rbox.exe, qhull.exe, qconvex.exe, qdelaunay.exe,
|
||||
qhalf.exe, qvoronoi.exe, testqset.exe, user_eg*.exe, documentation files,
|
||||
and source files. Qhull.exe and user-eg3.exe are compiled with the reentrant
|
||||
library while the other executables use the non-reentrant library.
|
||||
|
||||
To install Qhull:
|
||||
- Unzip the files into a directory (e.g., named 'qhull')
|
||||
- Click on QHULL-GO or open a command window into Qhull's bin directory.
|
||||
- Test with 'rbox D4 | qhull'
|
||||
|
||||
To uninstall Qhull
|
||||
- Delete the qhull directory
|
||||
|
||||
To learn about Qhull:
|
||||
- Execute 'qconvex' for a synopsis and examples.
|
||||
- Execute 'rbox 10 | qconvex' to compute the convex hull of 10 random points.
|
||||
- Execute 'rbox 10 | qconvex i TO file' to write results to 'file'.
|
||||
- Browse the documentation: qhull\html\index.htm
|
||||
- If an error occurs, Windows sends the error to stdout instead of stderr.
|
||||
Use 'TO xxx' to send normal output to xxx
|
||||
|
||||
To improve the command window
|
||||
- Double-click the window bar to increase the size of the window
|
||||
- Right-click the window bar
|
||||
- Select Properties
|
||||
- Check QuickEdit Mode
|
||||
Select text with right-click or Enter
|
||||
Paste text with right-click
|
||||
- Change Font to Lucinda Console
|
||||
- Change Layout to Screen Buffer Height 999, Window Size Height 55
|
||||
- Change Colors to Screen Background White, Screen Text Black
|
||||
- Click OK
|
||||
- Select 'Modify shortcut that started this window', then OK
|
||||
|
||||
If you use qhull a lot, install a bash shell such as
|
||||
MSYS (www.mingw.org/wiki/msys), Road Bash (www.qhull.org/bash),
|
||||
or Cygwin (www.cygwin.com).
|
||||
|
||||
-----------------
|
||||
Installing Qhull on Unix with gcc
|
||||
|
||||
To build Qhull, static libraries, shared library, and C++ interface
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- make
|
||||
- export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
|
||||
|
||||
The Makefiles may be edited for other compilers.
|
||||
If 'testqset' exits with an error, qhull is broken
|
||||
|
||||
A simple Makefile for Qhull is in src/libqhull and src/libqhull_r.
|
||||
To build the Qhull executables and libqhullstatic
|
||||
- Extract Qhull from qhull...tgz or qhull...zip
|
||||
- cd src/libqhull_r # cd src/libqhull
|
||||
- make
|
||||
|
||||
|
||||
-----------------
|
||||
Installing Qhull with CMake 2.6 or later
|
||||
|
||||
See CMakeLists.txt for examples and further build instructions
|
||||
|
||||
To build Qhull, static libraries, shared library, and C++ interface
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- cd build
|
||||
- cmake --help # List build generators
|
||||
- make -G "<generator>" .. && cmake ..
|
||||
- cmake ..
|
||||
- make
|
||||
- make install
|
||||
|
||||
The ".." is important. It refers to the parent directory (i.e., qhull/)
|
||||
|
||||
On Windows, CMake installs to C:/Program Files/qhull. 64-bit generators
|
||||
have a "Win64" tag.
|
||||
|
||||
If creating a qhull package, please include a pkg-config file based on build/qhull*.pc.in
|
||||
|
||||
If cmake fails with "No CMAKE_C_COMPILER could be found"
|
||||
- cmake was not able to find the build environment specified by -G "..."
|
||||
|
||||
-----------------
|
||||
Installing Qhull with Qt
|
||||
|
||||
To build Qhull, including its C++ test (qhulltest)
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- Load src/qhull-all.pro into QtCreator
|
||||
- Build
|
||||
|
||||
-------------------
|
||||
Working with Qhull's C++ interface
|
||||
|
||||
See html/qh-code.htm#cpp for calling Qhull from C++ programs
|
||||
|
||||
See html/qh-code.htm#reentrant for converting from Qhull-2012
|
||||
|
||||
Examples of using the C++ interface
|
||||
user_eg3_r.cpp
|
||||
qhulltest/*_test.cpp
|
||||
|
||||
Qhull's C++ interface is likely to change. Stay current with GitHub.
|
||||
|
||||
To clone Qhull's next branch from http://github.com/qhull/qhull
|
||||
git init
|
||||
git clone git@github.com:qhull/qhull.git
|
||||
cd qhull
|
||||
git checkout next
|
||||
...
|
||||
git pull origin next
|
||||
|
||||
Compile qhullcpp and libqhullstatic_r with the same compiler. Both libraries
|
||||
use the C routines setjmp() and longjmp() for error handling. They must
|
||||
be compiled with the same compiler.
|
||||
|
||||
-------------------
|
||||
Calling Qhull from C programs
|
||||
|
||||
See html/qh-code.htm#library for calling Qhull from C programs
|
||||
|
||||
See html/qh-code.htm#reentrant for converting from Qhull-2012
|
||||
|
||||
Warning: You will need to understand Qhull's data structures and read the
|
||||
code. Most users will find it easier to call Qhull as an external command.
|
||||
|
||||
The new, reentrant 'C' code (src/libqhull_r), passes a pointer to qhT
|
||||
to most Qhull routines. This allows multiple instances of Qhull to run
|
||||
at the same time. It simplifies the C++ interface.
|
||||
|
||||
The non-reentrant 'C' code (src/libqhull) looks unusual. It refers to
|
||||
Qhull's global data structure, qhT, through a 'qh' macro (e.g., 'qh ferr').
|
||||
This allows the same code to use static memory or heap memory.
|
||||
If qh_QHpointer is defined, qh_qh is a pointer to an allocated qhT;
|
||||
otherwise qh_qh is a global static data structure of type qhT.
|
||||
|
||||
------------------
|
||||
Compiling Qhull with Microsoft Visual C++
|
||||
|
||||
To compile 32-bit Qhull with Microsoft Visual C++ 2010 and later
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- Load solution build/qhull-32.sln
|
||||
- Build target 'Win32'
|
||||
- Project qhulltest requires Qt for DevStudio (http://www.qt.io)
|
||||
Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012)
|
||||
If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
|
||||
|
||||
To compile 64-bit Qhull with Microsoft Visual C++ 2010 and later
|
||||
- 64-bit Qhull has larger data structures due to 64-bit pointers
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- Load solution build/qhull-64.sln
|
||||
- Build target 'Win32'
|
||||
- Project qhulltest requires Qt for DevStudio (http://www.qt.io)
|
||||
Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/5.2.0/5.2.0/msvc2012_64)
|
||||
If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
|
||||
|
||||
To compile Qhull with Microsoft Visual C++ 2005 (vcproj files)
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- Load solution build/qhull.sln
|
||||
- Build target 'win32' (not 'x64')
|
||||
- Project qhulltest requires Qt for DevStudio (http://www.qt.io)
|
||||
Set the QTDIR environment variable to your Qt directory (e.g., c:/qt/4.7.4)
|
||||
If QTDIR is incorrect, precompile will fail with 'Can not locate the file specified'
|
||||
|
||||
-----------------
|
||||
Compiling Qhull with Qt Creator
|
||||
|
||||
Qt (http://www.qt.io) is a C++ framework for Windows, Linux, and Macintosh
|
||||
|
||||
Qhull uses QTestLib to test qhull's C++ interface (see src/qhulltest/)
|
||||
|
||||
To compile Qhull with Qt Creator
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- Download the Qt SDK
|
||||
- Start Qt Creator
|
||||
- Load src/qhull-all.pro
|
||||
- Build
|
||||
|
||||
-----------------
|
||||
Compiling Qhull with mingw on Windows
|
||||
|
||||
To compile Qhull with MINGW
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- Install Road Bash (http://www.qhull.org/bash)
|
||||
or install MSYS (http://www.mingw.org/wiki/msys)
|
||||
- Install MINGW-w64 (http://sourceforge.net/projects/mingw-w64).
|
||||
Mingw is included with Qt SDK.
|
||||
- make
|
||||
|
||||
-----------------
|
||||
Compiling Qhull with cygwin on Windows
|
||||
|
||||
To compile Qhull with cygwin
|
||||
- Download and extract Qhull (either GitHub, .tgz file, or .zip file)
|
||||
- Install cygwin (http://www.cygwin.com)
|
||||
- Include packages for gcc, make, ar, and ln
|
||||
- make
|
||||
|
||||
-----------------
|
||||
Compiling from Makfile without gcc
|
||||
|
||||
The file, qhull-src.tgz, contains documentation and source files for
|
||||
qhull and rbox.
|
||||
|
||||
To unpack the tgz file
|
||||
- tar zxf qhull-src.tgz
|
||||
- cd qhull
|
||||
- Use qhull/Makefile
|
||||
Simpler Makefiles are qhull/src/libqhull/Makefile and qhull/src/libqhull_r/Makefile
|
||||
|
||||
Compiling qhull and rbox with Makefile
|
||||
- in Makefile, check the CC, CCOPTS1, PRINTMAN, and PRINTC defines
|
||||
- the defaults are gcc and enscript
|
||||
- CCOPTS1 should include the ANSI flag. It defines __STDC__
|
||||
- in user.h, check the definitions of qh_SECticks and qh_CPUclock.
|
||||
- use '#define qh_CLOCKtype 2' for timing runs longer than 1 hour
|
||||
- type: make
|
||||
- this builds: qhull qconvex qdelaunay qhalf qvoronoi rbox libqhull.a libqhull_r.a
|
||||
- type: make doc
|
||||
- this prints the man page
|
||||
- See also qhull/html/index.htm
|
||||
- if your compiler reports many errors, it is probably not a ANSI C compiler
|
||||
- you will need to set the -ansi switch or find another compiler
|
||||
- if your compiler warns about missing prototypes for fprintf() etc.
|
||||
- this is ok, your compiler should have these in stdio.h
|
||||
- if your compiler warns about missing prototypes for memset() etc.
|
||||
- include memory.h in qhull_a.h
|
||||
- if your compiler reports "global.c: storage size of 'qh_qh' isn't known"
|
||||
- delete the initializer "={0}" in global.c, stat.c and mem.c
|
||||
- if your compiler warns about "stat.c: improper initializer"
|
||||
- this is ok, the initializer is not used
|
||||
- if you have trouble building libqhull.a with 'ar'
|
||||
- try 'make -f Makefile.txt qhullx'
|
||||
- if the code compiles, the qhull test case will automatically execute
|
||||
- if an error occurs, there's an incompatibility between machines
|
||||
- If you can, try a different compiler
|
||||
- You can turn off the Qhull memory manager with qh_NOmem in mem.h
|
||||
- You can turn off compiler optimization (-O2 in Makefile)
|
||||
- If you find the source of the problem, please let us know
|
||||
- to install the programs and their man pages:
|
||||
- define MANDIR and BINDIR
|
||||
- type 'make install'
|
||||
|
||||
- if you have Geomview (www.geomview.org)
|
||||
- try 'rbox 100 | qconvex G >a' and load 'a' into Geomview
|
||||
- run 'q_eg' for Geomview examples of Qhull output (see qh-eg.htm)
|
||||
|
||||
------------------
|
||||
Compiling on other machines and compilers
|
||||
|
||||
Qhull may compile with Borland C++ 5.0 bcc32. A Makefile is included.
|
||||
Execute 'cd src/libqhull; make -f Mborland'. If you use the Borland IDE, set
|
||||
the ANSI option in Options:Project:Compiler:Source:Language-compliance.
|
||||
|
||||
Qhull may compile with Borland C++ 4.02 for Win32 and DOS Power Pack.
|
||||
Use 'cd src/libqhull; make -f Mborland -D_DPMI'. Qhull 1.0 compiles with
|
||||
Borland C++ 4.02. For rbox 1.0, use "bcc32 -WX -w- -O2-e -erbox -lc rbox.c".
|
||||
Use the same options for Qhull 1.0. [D. Zwick]
|
||||
|
||||
If you have troubles with the memory manager, you can turn it off by
|
||||
defining qh_NOmem in mem.h.
|
||||
|
||||
-----------------
|
||||
Distributed files
|
||||
|
||||
README.txt // Instructions for installing Qhull
|
||||
REGISTER.txt // Qhull registration
|
||||
COPYING.txt // Copyright notice
|
||||
QHULL-GO.lnk // Windows icon for eg/qhull-go.bat
|
||||
Announce.txt // Announcement
|
||||
CMakeLists.txt // CMake build file (2.6 or later)
|
||||
CMakeModules/CheckLFS.cmake // enables Large File Support in cmake
|
||||
File_id.diz // Package descriptor
|
||||
index.htm // Home page
|
||||
Makefile // Makefile for gcc and other compilers
|
||||
qhull*.md5sum // md5sum for all files
|
||||
|
||||
bin/* // Qhull executables and dll (.zip only)
|
||||
build/qhull*.pc.in // pkg-config templates for qhull_r, qhull, and qhull_p
|
||||
build/qhull-32.sln // 32-bit DevStudio solution and project files (2010 and later)
|
||||
build/*-32.vcxproj
|
||||
build/qhull-64.sln // 64-bit DevStudio solution and project files (2010 and later)
|
||||
build/*-64.vcxproj
|
||||
build/qhull.sln // DevStudio solution and project files (2005 and 2009)
|
||||
build/*.vcproj
|
||||
eg/* // Test scripts and geomview files from q_eg
|
||||
html/index.htm // Manual
|
||||
html/qh-faq.htm // Frequently asked questions
|
||||
html/qh-get.htm // Download page
|
||||
html/qhull-cpp.xml // C++ style notes as a Road FAQ (www.qhull.org/road)
|
||||
src/Changes.txt // Change history for Qhull and rbox
|
||||
src/qhull-all.pro // Qt project
|
||||
|
||||
eg/
|
||||
q_eg // shell script for Geomview examples (eg.01.cube)
|
||||
q_egtest // shell script for Geomview test examples
|
||||
q_test // shell script to test qhull
|
||||
q_test-ok.txt // output from q_test
|
||||
qhulltest-ok.txt // output from qhulltest (Qt only)
|
||||
make-vcproj.sh // bash shell script to create vcproj and vcxprog files
|
||||
qhull-zip.sh // bash shell script for distribution files
|
||||
|
||||
rbox consists of (bin, html):
|
||||
rbox.exe // Win32 executable (.zip only)
|
||||
rbox.htm // html manual
|
||||
rbox.man // Unix man page
|
||||
rbox.txt
|
||||
|
||||
qhull consists of (bin, html):
|
||||
qconvex.exe // Win32 executables and dlls (.zip download only)
|
||||
qhull.exe // Built with the reentrant library (about 2% slower)
|
||||
qdelaunay.exe
|
||||
qhalf.exe
|
||||
qvoronoi.exe
|
||||
qhull_r.dll
|
||||
qhull-go.bat // command window
|
||||
qconvex.htm // html manual
|
||||
qdelaun.htm
|
||||
qdelau_f.htm
|
||||
qhalf.htm
|
||||
qvoronoi.htm
|
||||
qvoron_f.htm
|
||||
qh-eg.htm
|
||||
qh-code.htm
|
||||
qh-impre.htm
|
||||
index.htm
|
||||
qh-opt*.htm
|
||||
qh-quick.htm
|
||||
qh--*.gif // images for manual
|
||||
normal_voronoi_knauss_oesterle.jpg
|
||||
qhull.man // Unix man page
|
||||
qhull.txt
|
||||
|
||||
bin/
|
||||
msvcr80.dll // Visual C++ redistributable file (.zip download only)
|
||||
|
||||
src/
|
||||
qhull/unix.c // Qhull and rbox applications using non-reentrant libqhullstatic.a
|
||||
rbox/rbox.c
|
||||
qconvex/qconvex.c
|
||||
qhalf/qhalf.c
|
||||
qdelaunay/qdelaunay.c
|
||||
qvoronoi/qvoronoi.c
|
||||
|
||||
qhull/unix_r.c // Qhull and rbox applications using reentrant libqhullstatic_r.a
|
||||
rbox/rbox_r.c
|
||||
qconvex/qconvex_r.c // Qhull applications built with reentrant libqhull_r/Makefile
|
||||
qhalf/qhalf_r.c
|
||||
qdelaunay/qdelaun_r.c
|
||||
qvoronoi/qvoronoi_r.c
|
||||
|
||||
user_eg/user_eg_r.c // example of using qhull_r.dll from a user program
|
||||
user_eg2/user_eg2_r.c // example of using libqhullstatic_r.a from a user program
|
||||
user_eg3/user_eg3_r.cpp // example of Qhull's C++ interface libqhullcpp with libqhullstatic_r.a
|
||||
qhulltest/qhulltest.cpp // Test of Qhull's C++ interface using Qt's QTestLib
|
||||
qhull-*.pri // Include files for Qt projects
|
||||
testqset_r/testqset_r.c // Test of reentrant qset_r.c and mem_r.c
|
||||
testqset/testqset.c // Test of non-rentrant qset.c and mem.c
|
||||
|
||||
|
||||
src/libqhull
|
||||
libqhull.pro // Qt project for non-rentrant, shared library (qhull.dll)
|
||||
index.htm // design documentation for libqhull
|
||||
qh-*.htm
|
||||
qhull-exports.def // Export Definition file for Visual C++
|
||||
Makefile // Simple gcc Makefile for qhull and libqhullstatic.a
|
||||
Mborland // Makefile for Borland C++ 5.0
|
||||
|
||||
libqhull.h // header file for qhull
|
||||
user.h // header file of user definable constants
|
||||
libqhull.c // Quickhull algorithm with partitioning
|
||||
user.c // user re-definable functions
|
||||
usermem.c
|
||||
userprintf.c
|
||||
userprintf_rbox.c
|
||||
|
||||
qhull_a.h // include files for libqhull/*.c
|
||||
geom.c // geometric routines
|
||||
geom2.c
|
||||
geom.h
|
||||
global.c // global variables
|
||||
io.c // input-output routines
|
||||
io.h
|
||||
mem.c // memory routines, this is stand-alone code
|
||||
mem.h
|
||||
merge.c // merging of non-convex facets
|
||||
merge.h
|
||||
poly.c // polyhedron routines
|
||||
poly2.c
|
||||
poly.h
|
||||
qset.c // set routines, this only depends on mem.c
|
||||
qset.h
|
||||
random.c // utilities w/ Park & Miller's random number generator
|
||||
random.h
|
||||
rboxlib.c // point set generator for rbox
|
||||
stat.c // statistics
|
||||
stat.h
|
||||
|
||||
src/libqhull_r
|
||||
libqhull_r.pro // Qt project for rentrant, shared library (qhull_r.dll)
|
||||
index.htm // design documentation for libqhull_r
|
||||
qh-*_r.htm
|
||||
qhull-exports_r.def // Export Definition file for Visual C++
|
||||
Makefile // Simple gcc Makefile for qhull and libqhullstatic.a
|
||||
|
||||
libqhull_r.h // header file for qhull
|
||||
user_r.h // header file of user definable constants
|
||||
libqhull_r.c // Quickhull algorithm wi_r.hpartitioning
|
||||
user_r.c // user re-definable functions
|
||||
usermem.c
|
||||
userprintf.c
|
||||
userprintf_rbox.c
|
||||
qhull_ra.h // include files for libqhull/*_r.c
|
||||
geom_r.c // geometric routines
|
||||
geom2.c
|
||||
geom_r.h
|
||||
global_r.c // global variables
|
||||
io_r.c // input-output routines
|
||||
io_r.h
|
||||
mem_r.c // memory routines, this is stand-alone code
|
||||
mem.h
|
||||
merge_r.c // merging of non-convex facets
|
||||
merge.h
|
||||
poly_r.c // polyhedron routines
|
||||
poly2.c
|
||||
poly_r.h
|
||||
qset_r.c // set routines, this only depends on mem_r.c
|
||||
qset.h
|
||||
random_r.c // utilities w/ Park & Miller's random number generator
|
||||
random.h
|
||||
rboxlib_r.c // point set generator for rbox
|
||||
stat_r.c // statistics
|
||||
stat.h
|
||||
|
||||
src/libqhullcpp/
|
||||
libqhullcpp.pro // Qt project for renentrant, static C++ library
|
||||
Qhull.cpp // Calls libqhull_r.c from C++
|
||||
Qhull.h
|
||||
qt-qhull.cpp // Supporting methods for Qt
|
||||
|
||||
Coordinates.cpp // input classes
|
||||
Coordinates.h
|
||||
|
||||
PointCoordinates.cpp
|
||||
PointCoordinates.h
|
||||
RboxPoints.cpp // call rboxlib.c from C++
|
||||
RboxPoints.h
|
||||
|
||||
QhullFacet.cpp // data structure classes
|
||||
QhullFacet.h
|
||||
QhullHyperplane.cpp
|
||||
QhullHyperplane.h
|
||||
QhullPoint.cpp
|
||||
QhullPoint.h
|
||||
QhullQh.cpp
|
||||
QhullRidge.cpp
|
||||
QhullRidge.h
|
||||
QhullVertex.cpp
|
||||
QhullVertex.h
|
||||
|
||||
QhullFacetList.cpp // collection classes
|
||||
QhullFacetList.h
|
||||
QhullFacetSet.cpp
|
||||
QhullFacetSet.h
|
||||
QhullIterator.h
|
||||
QhullLinkedList.h
|
||||
QhullPoints.cpp
|
||||
QhullPoints.h
|
||||
QhullPointSet.cpp
|
||||
QhullPointSet.h
|
||||
QhullSet.cpp
|
||||
QhullSet.h
|
||||
QhullSets.h
|
||||
QhullVertexSet.cpp
|
||||
QhullVertexSet.h
|
||||
|
||||
functionObjects.h // supporting classes
|
||||
QhullError.cpp
|
||||
QhullError.h
|
||||
QhullQh.cpp
|
||||
QhullQh.h
|
||||
QhullStat.cpp
|
||||
QhullStat.h
|
||||
RoadError.cpp // Supporting base classes
|
||||
RoadError.h
|
||||
RoadLogEvent.cpp
|
||||
RoadLogEvent.h
|
||||
usermem_r-cpp.cpp // Optional override for qh_exit() to throw an error
|
||||
|
||||
src/libqhullstatic/
|
||||
libqhullstatic.pro // Qt project for non-reentrant, static library
|
||||
|
||||
src/libqhullstatic_r/
|
||||
libqhullstatic_r.pro // Qt project for reentrant, static library
|
||||
|
||||
src/qhulltest/
|
||||
qhulltest.pro // Qt project for test of C++ interface
|
||||
Coordinates_test.cpp // Test of each class
|
||||
PointCoordinates_test.cpp
|
||||
Qhull_test.cpp
|
||||
QhullFacet_test.cpp
|
||||
QhullFacetList_test.cpp
|
||||
QhullFacetSet_test.cpp
|
||||
QhullHyperplane_test.cpp
|
||||
QhullLinkedList_test.cpp
|
||||
QhullPoint_test.cpp
|
||||
QhullPoints_test.cpp
|
||||
QhullPointSet_test.cpp
|
||||
QhullRidge_test.cpp
|
||||
QhullSet_test.cpp
|
||||
QhullVertex_test.cpp
|
||||
QhullVertexSet_test.cpp
|
||||
RboxPoints_test.cpp
|
||||
RoadTest.cpp // Run multiple test files with QTestLib
|
||||
RoadTest.h
|
||||
|
||||
-----------------
|
||||
Authors:
|
||||
|
||||
C. Bradford Barber Hannu Huhdanpaa (Version 1.0)
|
||||
bradb@shore.net hannu@qhull.org
|
||||
|
||||
Qhull 1.0 and 2.0 were developed under NSF grants NSF/DMS-8920161
|
||||
and NSF-CCR-91-15793 750-7504 at the Geometry Center and Harvard
|
||||
University. If you find Qhull useful, please let us know.
|