Merge remote-tracking branch 'origin/dev' into new_main_page_ui

This commit is contained in:
YuSanka 2018-09-11 11:49:36 +02:00
commit 73ee3f77c3
86 changed files with 4884 additions and 2922 deletions

View File

@ -166,6 +166,7 @@ sub thread_cleanup {
*Slic3r::GUI::Tab::DESTROY = sub {};
*Slic3r::GUI::PresetHints::DESTROY = sub {};
*Slic3r::GUI::TabIface::DESTROY = sub {};
*Slic3r::GUI::ProgressStatusBar::DESTROY= sub {};
*Slic3r::OctoPrint::DESTROY = sub {};
*Slic3r::Duet::DESTROY = sub {};
*Slic3r::PresetUpdater::DESTROY = sub {};

View File

@ -70,24 +70,11 @@ sub new {
eval { Wx::ToolTip::SetAutoPop(32767) };
# initialize status bar
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, Wx::NewId);
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new();
$self->{statusbar}->Embed;
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
$self->SetStatusBar($self->{statusbar});
# Make the global status bar and its progress indicator available in C++
$appController->set_global_progress_indicator(
$self->{statusbar}->{prog}->GetId(),
$self->{statusbar}->GetId(),
);
$appController->set_model($self->{plater}->{model});
$appController->set_print($self->{plater}->{print});
# Make the global status bar and its progress indicator available in C++
$appController->set_global_progress_indicator(
$self->{statusbar}->{prog}->GetId(),
$self->{statusbar}->GetId(),
);
$appController->set_global_progress_indicator($self->{statusbar});
$appController->set_model($self->{plater}->{model});
$appController->set_print($self->{plater}->{print});

View File

@ -85,9 +85,13 @@ sub new {
# Initialize handlers for canvases
my $on_select_object = sub {
my ($obj_idx) = @_;
# Ignore the special objects (the wipe tower proxy and such).
$self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef);
my ($obj_idx, $vol_idx) = @_;
if (($obj_idx != -1) && ($vol_idx == -1)) {
# Ignore the special objects (the wipe tower proxy and such).
$self->select_object((defined($obj_idx) && $obj_idx >= 0 && $obj_idx < 1000) ? $obj_idx : undef);
$self->item_changed_selection($obj_idx) if (defined($obj_idx));
}
};
my $on_double_click = sub {
$self->object_settings_dialog if $self->selected_object;
@ -150,16 +154,15 @@ 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) = @_;
$self->rotate(rad2deg($angle), Z, 'absolute');
};
# callback to react to gizmo flatten
my $on_gizmo_flatten = sub {
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;
}
$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
@ -218,6 +221,29 @@ sub new {
my $state = Slic3r::GUI::_3DScene::is_toolbar_item_pressed($self->{canvas3D}, "layersediting");
$self->on_layer_editing_toggled($state);
};
my $on_action_selectbyparts = sub {
my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D});
if ($curr eq 'volume') {
Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object');
my $selections = $self->collect_selections;
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
}
elsif ($curr eq 'object') {
Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume');
my $selections = [];
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D});
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
my ($obj_idx, $object) = $self->selected_object;
if (defined $obj_idx) {
my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx);
Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1);
}
}
};
# Initialize 3D plater
if ($Slic3r::GUI::have_OpenGL) {
@ -237,6 +263,7 @@ sub new {
Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons);
Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly);
Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate);
Slic3r::GUI::_3DScene::register_on_gizmo_flatten_callback($self->{canvas3D}, $on_gizmo_flatten);
Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info);
Slic3r::GUI::_3DScene::register_action_add_callback($self->{canvas3D}, $on_action_add);
Slic3r::GUI::_3DScene::register_action_delete_callback($self->{canvas3D}, $on_action_delete);
@ -248,6 +275,7 @@ sub new {
Slic3r::GUI::_3DScene::register_action_cut_callback($self->{canvas3D}, $on_action_cut);
Slic3r::GUI::_3DScene::register_action_settings_callback($self->{canvas3D}, $on_action_settings);
Slic3r::GUI::_3DScene::register_action_layersediting_callback($self->{canvas3D}, $on_action_layersediting);
Slic3r::GUI::_3DScene::register_action_selectbyparts_callback($self->{canvas3D}, $on_action_selectbyparts);
Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1);
Slic3r::GUI::_3DScene::enable_toolbar($self->{canvas3D}, 1);
Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1);
@ -871,6 +899,15 @@ sub load_files {
$model->convert_multipart_object(scalar(@$nozzle_dmrs)) if $dialog->ShowModal() == wxID_YES;
}
# objects imported from 3mf require a call to center_around_origin to have gizmos working properly and this call
# need to be done after looks_like_multipart_object detection
if ($input_file =~ /.3[mM][fF]$/)
{
foreach my $model_object (@{$model->objects}) {
$model_object->center_around_origin; # also aligns object to Z = 0
}
}
if ($one_by_one) {
push @obj_idx, $self->load_model_objects(@{$model->objects});
} else {
@ -1161,13 +1198,12 @@ sub rotate {
}
# 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;
$axis_x = 1;
}
if ($axis == Y) {
$axis_y = -1;
$axis_y = 1;
}
}
@ -1202,11 +1238,7 @@ sub rotate {
# $model_object->center_around_origin;
# $self->reset_thumbnail($obj_idx);
}
if (defined $axis) {
Slic3r::GUI::update_rotation_value(deg2rad($angle), $axis == X ? "x" : ($axis == Y ? "y" : "z"));
}
# update print and start background processing
$self->{print}->add_model_object($model_object, $obj_idx);
@ -1676,7 +1708,9 @@ sub on_progress_event {
my ($self, $percent, $message) = @_;
$self->statusbar->SetProgress($percent);
$self->statusbar->SetStatusText("$message…");
# TODO: three dot character is not properly translated into C++
# $self->statusbar->SetStatusText("$message…");
$self->statusbar->SetStatusText("$message...");
}
# Called when the G-code export finishes, either successfully or with an error.
@ -2129,17 +2163,18 @@ sub on_config_change {
$self->schedule_background_process;
}
sub item_changed_selection{
sub item_changed_selection {
my ($self, $obj_idx) = @_;
# $self->{canvas}->Refresh;
if ($self->{canvas3D}) {
Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D});
if ($obj_idx >= 0){
my $selections = $self->collect_selections;
Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections);
if (($obj_idx >= 0) && ($obj_idx < 1000)) { # skip if wipe tower selected
if ($self->{canvas3D}) {
Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D});
if ($obj_idx >= 0) {
my $selections = $self->collect_selections;
Slic3r::GUI::_3DScene::update_volumes_selection($self->{canvas3D}, \@$selections);
}
# Slic3r::GUI::_3DScene::render($self->{canvas3D});
}
Slic3r::GUI::_3DScene::render($self->{canvas3D});
}
}
@ -2359,12 +2394,24 @@ sub selection_changed {
}
Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "layersediting", $layers_height_allowed);
my $can_select_by_parts = 0;
if ($have_sel) {
my $model_object = $self->{model}->objects->[$obj_idx];
$can_select_by_parts = ($obj_idx >= 0) && ($obj_idx < 1000) && ($model_object->volumes_count > 1);
Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "fewer", $model_object->instances_count > 1);
}
if ($can_select_by_parts) {
# first disable to let the item in the toolbar to switch to the unpressed state
Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0);
Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 1);
} else {
Slic3r::GUI::_3DScene::enable_toolbar_item($self->{canvas3D}, "selectbyparts", 0);
Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object');
}
if ($self->{object_info_size}) { # have we already loaded the info pane?
if ($have_sel) {
my $model_object = $self->{model}->objects->[$obj_idx];

View File

@ -1,144 +1,18 @@
# Status bar at the bottom of the main screen.
# Now it just implements cancel cb on perl side, every other functionality is
# in C++
package Slic3r::GUI::ProgressStatusBar;
package Slic3r::GUI::ProgressStatusBar;
use strict;
use warnings;
use Wx qw(:gauge :misc);
use base 'Wx::StatusBar';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{busy} = 0;
$self->{timer} = Wx::Timer->new($self);
$self->{prog} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize);
$self->{prog}->Hide;
$self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", wxDefaultPosition, wxDefaultSize);
$self->{cancelbutton}->Hide;
$self->SetFieldsCount(3);
$self->SetStatusWidths(-1, 150, 155);
Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer});
Wx::Event::EVT_SIZE($self, \&OnSize);
Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub {
$self->{cancel_cb}->();
$self->{cancelbutton}->Hide;
});
return $self;
}
sub DESTROY {
my $self = shift;
$self->{timer}->Stop if $self->{timer} && $self->{timer}->IsRunning;
}
sub OnSize {
my ($self, $event) = @_;
my %fields = (
# 0 is reserved for status text
1 => $self->{cancelbutton},
2 => $self->{prog},
);
foreach (keys %fields) {
my $rect = $self->GetFieldRect($_);
my $offset = &Wx::wxGTK ? 1 : 0; # add a cosmetic 1 pixel offset on wxGTK
my $pos = [$rect->GetX + $offset, $rect->GetY + $offset];
$fields{$_}->Move($pos);
$fields{$_}->SetSize($rect->GetWidth - $offset, $rect->GetHeight);
}
$event->Skip;
}
sub OnTimer {
my ($self, $event) = @_;
if ($self->{prog}->IsShown) {
$self->{timer}->Stop;
}
$self->{prog}->Pulse if $self->{_busy};
}
our $cancel_cb;
sub SetCancelCallback {
my $self = shift;
my ($cb) = @_;
$self->{cancel_cb} = $cb;
$cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide;
}
sub Run {
my $self = shift;
my $rate = shift || 100;
if (!$self->{timer}->IsRunning) {
$self->{timer}->Start($rate);
}
}
sub GetProgress {
my $self = shift;
return $self->{prog}->GetValue;
}
sub SetProgress {
my $self = shift;
my ($val) = @_;
if (!$self->{prog}->IsShown) {
$self->ShowProgress(1);
}
if ($val == $self->{prog}->GetRange) {
$self->{prog}->SetValue(0);
$self->ShowProgress(0);
} else {
$self->{prog}->SetValue($val);
}
}
sub SetRange {
my $self = shift;
my ($val) = @_;
if ($val != $self->{prog}->GetRange) {
$self->{prog}->SetRange($val);
}
}
sub ShowProgress {
my $self = shift;
my ($show) = @_;
$self->{prog}->Show($show);
$self->{prog}->Pulse;
}
sub StartBusy {
my $self = shift;
my $rate = shift || 100;
$self->{_busy} = 1;
$self->ShowProgress(1);
if (!$self->{timer}->IsRunning) {
$self->{timer}->Start($rate);
}
}
sub StopBusy {
my $self = shift;
$self->{timer}->Stop;
$self->ShowProgress(0);
$self->{prog}->SetValue(0);
$self->{_busy} = 0;
}
sub IsBusy {
my $self = shift;
return $self->{_busy};
$cancel_cb = $cb;
$cb ? $self->ShowCancelButton : $self->HideCancelButton;
}
1;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,8 +1,14 @@
min_slic3r_version = 1.41.0-alpha
0.2.2 Edited MMU2 Single mode purge line
0.2.1 Added PET and BVOH settings for MMU2
0.2.0-beta5 Fixed MMU1 ramming parameters
0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower
0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2
0.2.0-beta2 Edited first layer speed and wipe tower position
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-alpha7 Vojtech's fix the incorrect *MK3* references
0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version
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

View File

@ -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-beta
config_version = 0.2.2
# Where to get the updates from?
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
@ -67,7 +67,7 @@ fill_pattern = cubic
first_layer_acceleration = 1000
first_layer_extrusion_width = 0.42
first_layer_height = 0.2
first_layer_speed = 30
first_layer_speed = 20
gap_fill_speed = 40
gcode_comments = 0
infill_every_layers = 1
@ -113,7 +113,7 @@ support_material_interface_extruder = 0
support_material_angle = 0
support_material_buildplate_only = 0
support_material_enforce_layers = 0
support_material_contact_distance = 0.15
support_material_contact_distance = 0.1
support_material_interface_contact_loops = 0
support_material_interface_layers = 2
support_material_interface_spacing = 0.2
@ -122,9 +122,9 @@ support_material_pattern = rectilinear
support_material_spacing = 2
support_material_speed = 50
support_material_synchronize_layers = 0
support_material_threshold = 45
support_material_threshold = 55
support_material_with_sheath = 0
support_material_xy_spacing = 60%
support_material_xy_spacing = 50%
thin_walls = 0
top_infill_extrusion_width = 0.45
top_solid_infill_speed = 40
@ -133,13 +133,15 @@ wipe_tower = 1
wipe_tower_bridging = 10
wipe_tower_rotation_angle = 0
wipe_tower_width = 60
wipe_tower_x = 180
wipe_tower_y = 135
wipe_tower_x = 170
wipe_tower_y = 140
xy_size_compensation = 0
[print:*MK3*]
fill_pattern = grid
single_extruder_multi_material_priming = 0
wipe_tower_x = 170
wipe_tower_y = 125
# Print parameters common to a 0.25mm diameter nozzle.
[print:*0.25nozzle*]
@ -557,13 +559,17 @@ compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ a
end_filament_gcode = "; Filament-specific end gcode"
extrusion_multiplier = 1
filament_loading_speed = 28
filament_loading_speed_start = 3
filament_unloading_speed = 90
filament_unloading_speed_start = 100
filament_toolchange_delay = 0
filament_cooling_moves = 4
filament_cooling_initial_speed = 2.2
filament_cooling_final_speed = 3.4
filament_load_time = 0
filament_unload_time = 0
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_minimal_purge_on_wipe_tower = 15
filament_cost = 0
filament_density = 0
filament_diameter = 1.75
@ -664,6 +670,8 @@ cooling = 1
disable_fan_first_layers = 3
fan_always_on = 0
fan_below_layer_time = 10
filament_cost = 58.66
filament_density = 1.18
first_layer_bed_temperature = 105
first_layer_temperature = 270
max_fan_speed = 20
@ -741,7 +749,7 @@ temperature = 260
[filament:E3D Edge]
inherits = *PET*
filament_cost = 0
filament_cost = 56.9
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"
@ -754,14 +762,14 @@ temperature = 270
[filament:Fillamentum ABS]
inherits = *ABS*
filament_cost = 0
filament_cost = 32.4
filament_density = 1.04
first_layer_temperature = 240
temperature = 240
[filament:Fillamentum ASA]
inherits = *ABS*
filament_cost = 0
filament_cost = 38.7
filament_density = 1.04
fan_always_on = 1
first_layer_temperature = 265
@ -769,7 +777,7 @@ temperature = 265
[filament:Fillamentum CPE HG100 HM100]
inherits = *PET*
filament_cost = 0
filament_cost = 54.1
filament_density = 1.25
filament_notes = "CPE HG100 , CPE HM100"
first_layer_bed_temperature = 90
@ -783,7 +791,7 @@ 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_cost = 68
filament_density = 1.15
filament_colour = #804040
filament_max_volumetric_speed = 10
@ -793,24 +801,26 @@ temperature = 190
[filament:Generic ABS]
inherits = *ABS*
filament_cost = 0
filament_cost = 27.82
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_cost = 27.82
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_cost = 25.4
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]
inherits = *ABS*
filament_cost = 77.3
filament_density = 1.20
bed_temperature = 115
filament_colour = #3A80CA
first_layer_bed_temperature = 100
@ -819,6 +829,8 @@ temperature = 270
[filament:Primavalue PVA]
inherits = *PLA*
filament_cost = 108
filament_density = 1.23
cooling = 0
fan_always_on = 0
filament_colour = #FFFFD7
@ -843,10 +855,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
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*
@ -856,6 +865,8 @@ inherits = *ABS MMU2*
[filament:Prusa HIPS]
inherits = *ABS*
filament_cost = 27.3
filament_density = 1.04
bridge_fan_speed = 50
cooling = 1
extrusion_multiplier = 0.9
@ -876,6 +887,28 @@ 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:*PET MMU2*]
inherits = Prusa PET
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
temperature = 230
first_layer_temperature = 230
filament_cooling_final_speed = 1
filament_cooling_initial_speed = 2
filament_cooling_moves = 1
filament_load_time = 12
filament_loading_speed = 14
filament_notes = PET
filament_ramming_parameters = "120 140 4.70968 4.74194 4.77419 4.80645 4.83871 4.87097 4.90323 5 5.25806 5.67742 6.29032 7.06452 7.83871 8.3871| 0.05 4.72901 0.45 4.73545 0.95 4.83226 1.45 4.88067 1.95 5.05483 2.45 5.93553 2.95 7.53556 3.45 8.6323 3.95 7.6 4.45 7.6 4.95 7.6"
filament_unload_time = 11
filament_unloading_speed = 20
filament_unloading_speed_start = 120
[filament:Generic PET MMU2]
inherits = *PET MMU2*
[filament:Prusa PET MMU2]
inherits = *PET MMU2*
[filament:Prusa PLA]
inherits = *PLA*
filament_cost = 25.4
@ -885,13 +918,15 @@ filament_notes = "List of materials tested with standart PLA print settings for
[filament:*PLA MMU2*]
inherits = Prusa PLA
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 = 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"
temperature = 205
filament_cooling_final_speed = 1
filament_cooling_initial_speed = 2
filament_cooling_moves = 1
filament_load_time = 12
filament_loading_speed = 14
filament_ramming_parameters = "120 110 2.70968 2.93548 3.32258 3.83871 4.58065 5.54839 6.51613 7.35484 7.93548 8.16129| 0.05 2.66451 0.45 3.05805 0.95 4.05807 1.45 5.97742 1.95 7.69999 2.45 8.1936 2.95 11.342 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6"
filament_unload_time = 11
filament_unloading_speed = 20
[filament:Generic PLA MMU2]
inherits = *PLA MMU2*
@ -901,11 +936,13 @@ inherits = *PLA MMU2*
[filament:SemiFlex or Flexfill 98A]
inherits = *FLEX*
filament_cost = 0
filament_cost = 82
filament_density = 1.22
[filament:Taulman Bridge]
inherits = *common*
filament_cost = 40
filament_density = 1.13
bed_temperature = 90
bridge_fan_speed = 40
cooling = 0
@ -925,6 +962,8 @@ temperature = 250
[filament:Taulman T-Glase]
inherits = *PET*
filament_cost = 40
filament_density = 1.27
bridge_fan_speed = 40
cooling = 0
fan_always_on = 0
@ -936,6 +975,8 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el
[filament:Verbatim BVOH]
inherits = *common*
filament_cost = 218
filament_density = 1.23
bed_temperature = 60
bridge_fan_speed = 100
cooling = 0
@ -944,7 +985,7 @@ extrusion_multiplier = 1
fan_always_on = 0
fan_below_layer_time = 100
filament_colour = #FFFFD7
filament_max_volumetric_speed = 10
filament_max_volumetric_speed = 4
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_soluble = 1
filament_type = PLA
@ -955,8 +996,29 @@ min_fan_speed = 100
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
temperature = 210
[filament:Verbatim BVOH MMU2]
inherits = Verbatim BVOH
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material
temperature = 195
filament_notes = BVOH
fan_always_on = 1
first_layer_temperature = 200
filament_cooling_final_speed = 1
filament_cooling_initial_speed = 2
filament_max_volumetric_speed = 4
filament_type = PVA
filament_cooling_moves = 1
filament_load_time = 12
filament_loading_speed = 14
filament_ramming_parameters = "120 110 1.74194 1.90323 2.16129 2.48387 2.83871 3.25806 3.83871 4.6129 5.41935 5.96774| 0.05 1.69677 0.45 1.96128 0.95 2.63872 1.45 3.46129 1.95 4.99031 2.45 6.12908 2.95 8.30974 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6"
filament_unload_time = 11
filament_unloading_speed = 20
filament_unloading_speed_start = 100
[filament:Verbatim PP]
inherits = *common*
filament_cost = 72
filament_density = 0.89
bed_temperature = 100
bridge_fan_speed = 100
cooling = 1
@ -1207,7 +1269,7 @@ default_filament_profile = Prusa PLA MMU2
[printer:Original Prusa i3 MK3 MMU2 Single]
inherits = *mm2*
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
start_gcode = M107\nM115 U3.4.0-RC2 ; 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; purge line\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]
@ -1215,9 +1277,10 @@ inherits = *mm2*
# The 5x nozzle diameter defines the number of extruders. Other extruder parameters
# (for example the retract values) are duplicaed from the first value, so they do not need
# to be defined explicitely.
machine_max_acceleration_e = 8000,8000
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
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
start_gcode = M107\nM115 U3.4.0-RC2 ; 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\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 = {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.

View File

@ -261,6 +261,8 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp
${LIBDIR}/slic3r/GUI/FirmwareDialog.cpp
${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp
${LIBDIR}/slic3r/GUI/ProgressStatusBar.hpp
${LIBDIR}/slic3r/GUI/ProgressStatusBar.cpp
${LIBDIR}/slic3r/Utils/Http.cpp
${LIBDIR}/slic3r/Utils/Http.hpp
${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp
@ -279,11 +281,9 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
${LIBDIR}/slic3r/Utils/Time.cpp
${LIBDIR}/slic3r/Utils/Time.hpp
${LIBDIR}/slic3r/GUI/PngExportDialog.hpp
${LIBDIR}/slic3r/GUI/PngExportDialog.cpp
${LIBDIR}/slic3r/Utils/HexFile.cpp
${LIBDIR}/slic3r/Utils/HexFile.hpp
${LIBDIR}/slic3r/IProgressIndicator.hpp
${LIBDIR}/slic3r/ProgressIndicator.hpp
${LIBDIR}/slic3r/AppController.hpp
${LIBDIR}/slic3r/AppController.cpp
${LIBDIR}/slic3r/AppControllerWx.cpp
@ -479,6 +479,7 @@ set(XS_XSP_FILES
${XSP_DIR}/Utils_PrintHost.xsp
${XSP_DIR}/Utils_PresetUpdater.xsp
${XSP_DIR}/AppController.xsp
${XSP_DIR}/ProgressStatusBar.xsp
${XSP_DIR}/XS.xsp
)
foreach (file ${XS_XSP_FILES})
@ -812,6 +813,7 @@ add_custom_target(pot
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
add_subdirectory(${LIBDIR}/libnest2d)
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
target_include_directories(libslic3r PUBLIC BEFORE ${LIBNEST2D_INCLUDES})
target_include_directories(libslic3r_gui PUBLIC BEFORE ${LIBNEST2D_INCLUDES})

View File

@ -173,6 +173,7 @@ extern void stl_mirror_xy(stl_file *stl);
extern void stl_mirror_yz(stl_file *stl);
extern void stl_mirror_xz(stl_file *stl);
extern void stl_transform(stl_file *stl, float *trafo3x4);
extern void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t);
extern void stl_open_merge(stl_file *stl, char *file);
extern void stl_invalidate_shared_vertices(stl_file *stl);
extern void stl_generate_shared_vertices(stl_file *stl);

View File

@ -286,7 +286,7 @@ void stl_read(stl_file *stl, int first_facet, bool first) {
{
// skip solid/endsolid
// (in this order, otherwise it won't work when they are paired in the middle of a file)
fscanf(stl->fp, "endsolid\n");
fscanf(stl->fp, "endsolid%*[^\n]\n");
fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
// Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs.
int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);

View File

@ -155,6 +155,47 @@ void stl_transform(stl_file *stl, float *trafo3x4) {
calculate_normals(stl);
}
void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t)
{
if (stl->error)
return;
unsigned int vertices_count = 3 * (unsigned int)stl->stats.number_of_facets;
if (vertices_count == 0)
return;
Eigen::MatrixXf src_vertices(3, vertices_count);
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)
{
::memcpy((void*)src_vertices.col(v_id).data(), (const void*)&facet_ptr->vertex[i], 3 * sizeof(float));
++v_id;
}
facet_ptr += 1;
}
Eigen::MatrixXf dst_vertices(3, vertices_count);
dst_vertices = t * src_vertices.colwise().homogeneous();
facet_ptr = stl->facet_start;
v_id = 0;
while (facet_ptr < stl->facet_start + stl->stats.number_of_facets)
{
for (int i = 0; i < 3; ++i)
{
::memcpy((void*)&facet_ptr->vertex[i], (const void*)dst_vertices.col(v_id).data(), 3 * sizeof(float));
++v_id;
}
facet_ptr += 1;
}
stl_get_size(stl);
calculate_normals(stl);
}
void
stl_rotate_x(stl_file *stl, float angle) {
int i;

View File

@ -31,6 +31,7 @@ set(LIBNEST2D_SRCFILES
${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/rotfinder.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
@ -89,14 +90,39 @@ if(LIBNEST2D_UNITTESTS)
endif()
if(LIBNEST2D_BUILD_EXAMPLES)
add_executable(example examples/main.cpp
# tools/libnfpglue.hpp
# tools/libnfpglue.cpp
tools/nfp_svgnest.hpp
tools/nfp_svgnest_glue.hpp
tools/svgtools.hpp
tests/printer_parts.cpp
tests/printer_parts.h
${LIBNEST2D_SRCFILES})
${LIBNEST2D_SRCFILES}
)
set(TBB_STATIC ON)
find_package(TBB QUIET)
if(TBB_FOUND)
message(STATUS "Parallelization with Intel TBB")
target_include_directories(example PUBLIC ${TBB_INCLUDE_DIRS})
target_compile_definitions(example PUBLIC ${TBB_DEFINITIONS} -DUSE_TBB)
if(MSVC)
# Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
target_compile_definitions(example PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE)
endif()
# The Intel TBB library will use the std::exception_ptr feature of C++11.
target_compile_definitions(example PUBLIC -DTBB_USE_CAPTURED_EXCEPTION=1)
target_link_libraries(example ${TBB_LIBRARIES})
else()
find_package(OpenMP QUIET)
if(OpenMP_CXX_FOUND)
message(STATUS "Parallelization with OpenMP")
target_include_directories(example PUBLIC OpenMP::OpenMP_CXX)
target_link_libraries(example OpenMP::OpenMP_CXX)
endif()
endif()
target_link_libraries(example ${LIBNEST2D_LIBRARIES})
target_include_directories(example PUBLIC ${LIBNEST2D_HEADERS})

View File

@ -9,18 +9,28 @@ with templated geometry types. These geometries can have custom or already
existing implementation to avoid copying or having unnecessary dependencies.
A default backend is provided if the user of the library just wants to use it
out of the box without additional integration. The default backend is reasonably
out of the box without additional integration. This backend is reasonably
fast and robust, being built on top of boost geometry and the
[polyclipping](http://www.angusj.com/delphi/clipper.php) library. Usage of
this default backend implies the dependency on these packages as well as the
compilation of the backend itself (The default backend is not yet header only).
this default backend implies the dependency on these packages but its header
only as well.
This software is currently under construction and lacks a throughout
documentation and some essential algorithms as well. At this stage it works well
for rectangles and convex closed polygons without considering holes and
concavities.
Holes and non-convex polygons will be usable in the near future as well.
Holes and non-convex polygons will be usable in the near future as well. The
no fit polygon based placer module combined with the first fit selection
strategy is now used in the [Slic3r](https://github.com/prusa3d/Slic3r)
application's arrangement feature. It uses local optimization techniques to find
the best placement of each new item based on some features of the arrangement.
In the near future I would like to use machine learning to evaluate the
placements and (or) the order if items in which they are placed and see what
results can be obtained. This is a different approach than that of SVGnest which
uses genetic algorithms to find better and better selection orders. Maybe the
two approaches can be combined as well.
# References
- [SVGNest](https://github.com/Jack000/SVGnest)

View File

@ -0,0 +1,322 @@
# The MIT License (MIT)
#
# Copyright (c) 2015 Justus Calvin
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# FindTBB
# -------
#
# Find TBB include directories and libraries.
#
# Usage:
#
# find_package(TBB [major[.minor]] [EXACT]
# [QUIET] [REQUIRED]
# [[COMPONENTS] [components...]]
# [OPTIONAL_COMPONENTS components...])
#
# where the allowed components are tbbmalloc and tbb_preview. Users may modify
# the behavior of this module with the following variables:
#
# * TBB_ROOT_DIR - The base directory the of TBB installation.
# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files.
# * TBB_LIBRARY - The directory that contains the TBB library files.
# * TBB_<library>_LIBRARY - The path of the TBB the corresponding TBB library.
# These libraries, if specified, override the
# corresponding library search results, where <library>
# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug,
# tbb_preview, or tbb_preview_debug.
# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will
# be used instead of the release version.
# * TBB_STATIC - Static linking of libraries with a _static suffix.
# For example, on Windows a tbb_static.lib will be searched for
# instead of tbb.lib.
#
# Users may modify the behavior of this module with the following environment
# variables:
#
# * TBB_INSTALL_DIR
# * TBBROOT
# * LIBRARY_PATH
#
# This module will set the following variables:
#
# * TBB_FOUND - Set to false, or undefined, if we havent found, or
# dont want to use TBB.
# * TBB_<component>_FOUND - If False, optional <component> part of TBB sytem is
# not available.
# * TBB_VERSION - The full version string
# * TBB_VERSION_MAJOR - The major version
# * TBB_VERSION_MINOR - The minor version
# * TBB_INTERFACE_VERSION - The interface version number defined in
# tbb/tbb_stddef.h.
# * TBB_<library>_LIBRARY_RELEASE - The path of the TBB release version of
# <library>, where <library> may be tbb, tbb_debug,
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
# tbb_preview_debug.
# * TBB_<library>_LIBRARY_DEGUG - The path of the TBB release version of
# <library>, where <library> may be tbb, tbb_debug,
# tbbmalloc, tbbmalloc_debug, tbb_preview, or
# tbb_preview_debug.
#
# The following varibles should be used to build and link with TBB:
#
# * TBB_INCLUDE_DIRS - The include directory for TBB.
# * TBB_LIBRARIES - The libraries to link against to use TBB.
# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB.
# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB.
# * TBB_DEFINITIONS - Definitions to use when compiling code that uses
# TBB.
# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that
# uses TBB.
# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that
# uses TBB.
#
# This module will also create the "tbb" target that may be used when building
# executables and libraries.
include(FindPackageHandleStandardArgs)
if(NOT TBB_FOUND)
##################################
# Check the build type
##################################
if(NOT DEFINED TBB_USE_DEBUG_BUILD)
if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)")
set(TBB_BUILD_TYPE DEBUG)
else()
set(TBB_BUILD_TYPE RELEASE)
endif()
elseif(TBB_USE_DEBUG_BUILD)
set(TBB_BUILD_TYPE DEBUG)
else()
set(TBB_BUILD_TYPE RELEASE)
endif()
##################################
# Set the TBB search directories
##################################
# Define search paths based on user input and environment variables
set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT})
# Define the search directories based on the current platform
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB"
"C:/Program Files (x86)/Intel/TBB")
# Set the target architecture
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(TBB_ARCHITECTURE "intel64")
else()
set(TBB_ARCHITECTURE "ia32")
endif()
# Set the TBB search library path search suffix based on the version of VC
if(WINDOWS_STORE)
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui")
elseif(MSVC14)
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14")
elseif(MSVC12)
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12")
elseif(MSVC11)
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11")
elseif(MSVC10)
set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10")
endif()
# Add the library path search suffix for the VC independent version of TBB
list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# OS X
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
# TODO: Check to see which C++ library is being used by the compiler.
if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0)
# The default C++ library on OS X 10.9 and later is libc++
set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib")
else()
set(TBB_LIB_PATH_SUFFIX "lib")
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Linux
set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb")
# TODO: Check compiler version to see the suffix should be <arch>/gcc4.1 or
# <arch>/gcc4.1. For now, assume that the compiler is more recent than
# gcc 4.4.x or later.
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4")
endif()
endif()
##################################
# Find the TBB include dir
##################################
find_path(TBB_INCLUDE_DIRS tbb/tbb.h
HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR}
PATHS ${TBB_DEFAULT_SEARCH_DIR}
PATH_SUFFIXES include)
##################################
# Set version strings
##################################
if(TBB_INCLUDE_DIRS)
file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file)
string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1"
TBB_VERSION_MAJOR "${_tbb_version_file}")
string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1"
TBB_VERSION_MINOR "${_tbb_version_file}")
string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1"
TBB_INTERFACE_VERSION "${_tbb_version_file}")
set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}")
endif()
##################################
# Find TBB components
##################################
if(TBB_VERSION VERSION_LESS 4.3)
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb)
else()
set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb)
endif()
if(TBB_STATIC)
set(TBB_STATIC_SUFFIX "_static")
endif()
# Find each component
foreach(_comp ${TBB_SEARCH_COMPOMPONENTS})
if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};")
# Search for the libraries
find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX}
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug
HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR}
PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH
PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX})
if(TBB_${_comp}_LIBRARY_DEBUG)
list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}")
endif()
if(TBB_${_comp}_LIBRARY_RELEASE)
list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}")
endif()
if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY)
set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}")
endif()
if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}")
set(TBB_${_comp}_FOUND TRUE)
else()
set(TBB_${_comp}_FOUND FALSE)
endif()
# Mark internal variables as advanced
mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE)
mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG)
mark_as_advanced(TBB_${_comp}_LIBRARY)
endif()
endforeach()
unset(TBB_STATIC_SUFFIX)
##################################
# Set compile flags and libraries
##################################
set(TBB_DEFINITIONS_RELEASE "")
set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1")
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}")
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
elseif(TBB_LIBRARIES_RELEASE)
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}")
set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}")
elseif(TBB_LIBRARIES_DEBUG)
set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}")
set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}")
endif()
find_package_handle_standard_args(TBB
REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES
HANDLE_COMPONENTS
VERSION_VAR TBB_VERSION)
##################################
# Create targets
##################################
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
add_library(tbb SHARED IMPORTED)
set_target_properties(tbb PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
IMPORTED_LOCATION ${TBB_LIBRARIES})
if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG)
set_target_properties(tbb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG}
IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
)
elseif(TBB_LIBRARIES_RELEASE)
set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ${TBB_LIBRARIES_RELEASE})
else()
set_target_properties(tbb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}"
IMPORTED_LOCATION ${TBB_LIBRARIES_DEBUG}
)
endif()
endif()
mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES)
unset(TBB_ARCHITECTURE)
unset(TBB_BUILD_TYPE)
unset(TBB_LIB_PATH_SUFFIX)
unset(TBB_DEFAULT_SEARCH_DIR)
if(TBB_DEBUG)
message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}")
message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}")
message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}")
message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}")
message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}")
message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}")
message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}")
endif()
endif()

View File

@ -1,7 +1,6 @@
#include <iostream>
#include <string>
#include <fstream>
//#define DEBUG_EXPORT_NFP
#include <libnest2d.h>
@ -9,7 +8,11 @@
#include "tests/printer_parts.h"
#include "tools/benchmark.h"
#include "tools/svgtools.hpp"
#include "libnest2d/rotfinder.hpp"
//#include "tools/libnfpglue.hpp"
//#include "tools/nfp_svgnest_glue.hpp"
using namespace libnest2d;
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
@ -50,499 +53,57 @@ void arrangeRectangles() {
using namespace libnest2d;
const int SCALE = 1000000;
// const int SCALE = 1;
std::vector<Rectangle> rects = {
{80*SCALE, 80*SCALE},
{60*SCALE, 90*SCALE},
{70*SCALE, 30*SCALE},
{80*SCALE, 60*SCALE},
{60*SCALE, 60*SCALE},
{60*SCALE, 40*SCALE},
{40*SCALE, 40*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{10*SCALE, 10*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{5*SCALE, 5*SCALE},
{20*SCALE, 20*SCALE}
};
// std::vector<Rectangle> rects = {
// {20*SCALE, 10*SCALE},
// {20*SCALE, 10*SCALE},
// {20*SCALE, 20*SCALE},
// };
std::vector<Item> rects(202, {
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190019},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802209},
{6691309, -11542349},
{5877850, -12201069},
{5000000, -12771149},
{4067369, -13246350},
{3090169, -13621459},
{2079119, -13892379},
{1045279, -14056119},
{0, -14110899},
{-1045279, -14056119},
{-2079119, -13892379},
{-3090169, -13621459},
{-4067369, -13246350},
{-5000000, -12771149},
{-5877850, -12201069},
{-6691309, -11542349},
{-7431449, -10802209},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190019},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
});
// std::vector<Item> input {
// {{0, 0}, {0, 20*SCALE}, {10*SCALE, 0}, {0, 0}}
// };
std::vector<Item> crasher =
{
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-5000000, 8954050},
{5000000, 8954050},
{5000000, -45949},
{4972609, -568549},
{3500000, -8954050},
{-3500000, -8954050},
{-4972609, -568549},
{-5000000, -45949},
{-5000000, 8954050},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-9945219, -3065619},
{-9781479, -2031780},
{-9510560, -1020730},
{-9135450, -43529},
{-2099999, 14110899},
{2099999, 14110899},
{9135450, -43529},
{9510560, -1020730},
{9781479, -2031780},
{9945219, -3065619},
{10000000, -4110899},
{9945219, -5156179},
{9781479, -6190020},
{9510560, -7201069},
{9135450, -8178270},
{8660249, -9110899},
{8090169, -9988750},
{7431449, -10802200},
{6691309, -11542300},
{5877850, -12201100},
{5000000, -12771100},
{4067369, -13246399},
{3090169, -13621500},
{2079119, -13892399},
{1045279, -14056099},
{0, -14110899},
{-1045279, -14056099},
{-2079119, -13892399},
{-3090169, -13621500},
{-4067369, -13246399},
{-5000000, -12771100},
{-5877850, -12201100},
{-6691309, -11542300},
{-7431449, -10802200},
{-8090169, -9988750},
{-8660249, -9110899},
{-9135450, -8178270},
{-9510560, -7201069},
{-9781479, -6190020},
{-9945219, -5156179},
{-10000000, -4110899},
{-9945219, -3065619},
},
{
{-18000000, -1000000},
{-15000000, 22000000},
{-11000000, 26000000},
{11000000, 26000000},
{15000000, 22000000},
{18000000, -1000000},
{18000000, -26000000},
{-18000000, -26000000},
{-18000000, -1000000},
},
};
std::vector<Item> proba = {
{
Rectangle(100, 2)
},
{
Rectangle(100, 2)
},
{
Rectangle(100, 2)
},
{
Rectangle(10, 10)
},
};
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(), rects.begin(), rects.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 = {
@ -560,10 +121,12 @@ void arrangeRectangles() {
// {}
// };
auto min_obj_distance = static_cast<Coord>(0*SCALE);
// Circle bin({0, 0}, 125*SCALE);
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, Box>;
using Packer = Arranger<Placer, FirstFitSelection>;
auto min_obj_distance = static_cast<Coord>(6*SCALE);
using Placer = placers::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
using Packer = Nester<Placer, FirstFitSelection>;
Packer arrange(bin, min_obj_distance);
@ -571,121 +134,23 @@ 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.accuracy = 0.5f;
// auto bincenter = ShapeLike::boundingBox(bin).center();
// pconf.object_function = [&bin, bincenter](
// Placer::Pile pile, const Item& item,
// double /*area*/, double norm, double penality) {
// using pl = PointLike;
// static const double BIG_ITEM_TRESHOLD = 0.2;
// static const double GRAVITY_RATIO = 0.5;
// static const double DENSITY_RATIO = 1.0 - GRAVITY_RATIO;
// // 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;
// };
pconf.accuracy = 0.65f;
pconf.parallel = true;
Packer::SelectionConfig sconf;
// sconf.allow_parallel = false;
// sconf.force_parallel = false;
// sconf.try_triplets = true;
// sconf.try_reverse_order = true;
// sconf.waste_increment = 0.005;
// sconf.waste_increment = 0.01;
arrange.configure(pconf, sconf);
arrange.progressIndicator([&](unsigned r){
// svg::SVGWriter::Config conf;
// conf.mm_in_coord_units = SCALE;
// svg::SVGWriter svgw(conf);
// svgw.setSize(bin);
// svgw.writePackGroup(arrange.lastResult());
// svgw.save("debout");
std::cout << "Remaining items: " << r << std::endl;
})/*.useMinimumBoundigBoxRotation()*/;
});
// findMinimumBoundingBoxRotations(input.begin(), input.end());
Benchmark bench;
@ -693,7 +158,7 @@ void arrangeRectangles() {
Packer::ResultType result;
try {
result = arrange.arrange(input.begin(), input.end());
result = arrange.execute(input.begin(), input.end());
} catch(GeometryException& ge) {
std::cerr << "Geometry error: " << ge.what() << std::endl;
return ;
@ -707,7 +172,7 @@ void arrangeRectangles() {
std::vector<double> eff;
eff.reserve(result.size());
auto bin_area = ShapeLike::area<PolygonImpl>(bin);
auto bin_area = sl::area(bin);
for(auto& r : result) {
double a = 0;
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); });
@ -728,10 +193,10 @@ void arrangeRectangles() {
for(auto& r : result) { std::cout << r.size() << " "; total += r.size(); }
std::cout << ") Total: " << total << std::endl;
for(auto& it : input) {
auto ret = ShapeLike::isValid(it.transformedShape());
std::cout << ret.second << std::endl;
}
// for(auto& it : input) {
// auto ret = sl::isValid(it.transformedShape());
// std::cout << ret.second << std::endl;
// }
if(total != input.size()) std::cout << "ERROR " << "could not pack "
<< input.size() - total << " elements!"
@ -744,13 +209,10 @@ void arrangeRectangles() {
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");
}
int main(void /*int argc, char **argv*/) {
arrangeRectangles();
// findDegenerateCase();
return EXIT_SUCCESS;
}

View File

@ -22,6 +22,7 @@ using Point = PointImpl;
using Coord = TCoord<PointImpl>;
using Box = _Box<PointImpl>;
using Segment = _Segment<PointImpl>;
using Circle = _Circle<PointImpl>;
using Item = _Item<PolygonImpl>;
using Rectangle = _Rectangle<PolygonImpl>;
@ -29,15 +30,12 @@ using Rectangle = _Rectangle<PolygonImpl>;
using PackGroup = _PackGroup<PolygonImpl>;
using IndexedPackGroup = _IndexedPackGroup<PolygonImpl>;
using FillerSelection = strategies::_FillerSelection<PolygonImpl>;
using FirstFitSelection = strategies::_FirstFitSelection<PolygonImpl>;
using DJDHeuristic = strategies::_DJDHeuristic<PolygonImpl>;
using FillerSelection = selections::_FillerSelection<PolygonImpl>;
using FirstFitSelection = selections::_FirstFitSelection<PolygonImpl>;
using DJDHeuristic = selections::_DJDHeuristic<PolygonImpl>;
using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>;
using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>;
//template<NfpLevel lvl = NfpLevel::BOTH_CONCAVE_WITH_HOLES>
//using NofitPolyPlacer = strategies::_NofitPolyPlacer<PolygonImpl, lvl>;
using NfpPlacer = placers::_NofitPolyPlacer<PolygonImpl>;
using BottomLeftPlacer = placers::_BottomLeftPlacer<PolygonImpl>;
}

View File

@ -36,7 +36,7 @@ using libnest2d::setX;
using libnest2d::setY;
using Box = libnest2d::_Box<PointImpl>;
using Segment = libnest2d::_Segment<PointImpl>;
using Shapes = libnest2d::Nfp::Shapes<PolygonImpl>;
using Shapes = libnest2d::nfp::Shapes<PolygonImpl>;
}
@ -241,11 +241,11 @@ template<> struct tag<bp2d::PolygonImpl> {
template<> struct exterior_ring<bp2d::PolygonImpl> {
static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) {
return libnest2d::ShapeLike::getContour(p);
return libnest2d::shapelike::getContour(p);
}
static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) {
return libnest2d::ShapeLike::getContour(p);
return libnest2d::shapelike::getContour(p);
}
};
@ -271,13 +271,13 @@ struct interior_rings<bp2d::PolygonImpl> {
static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
bp2d::PolygonImpl& p)
{
return libnest2d::ShapeLike::holes(p);
return libnest2d::shapelike::holes(p);
}
static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
bp2d::PolygonImpl const& p)
{
return libnest2d::ShapeLike::holes(p);
return libnest2d::shapelike::holes(p);
}
};
@ -311,83 +311,77 @@ struct range_value<bp2d::Shapes> {
namespace libnest2d { // Now the algorithms that boost can provide...
namespace pointlike {
template<>
inline double PointLike::distance(const PointImpl& p1,
const PointImpl& p2 )
inline double distance(const PointImpl& p1, const PointImpl& p2 )
{
return boost::geometry::distance(p1, p2);
}
template<>
inline double PointLike::distance(const PointImpl& p,
const bp2d::Segment& seg )
inline double distance(const PointImpl& p, const bp2d::Segment& seg )
{
return boost::geometry::distance(p, seg);
}
}
namespace shapelike {
// Tell libnest2d how to make string out of a ClipperPolygon object
template<>
inline bool ShapeLike::intersects(const PathImpl& sh1,
const PathImpl& sh2)
inline bool intersects(const PathImpl& sh1, const PathImpl& sh2)
{
return boost::geometry::intersects(sh1, sh2);
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<>
inline bool ShapeLike::intersects(const PolygonImpl& sh1,
const PolygonImpl& sh2)
inline bool intersects(const PolygonImpl& sh1, const PolygonImpl& sh2)
{
return boost::geometry::intersects(sh1, sh2);
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<>
inline bool ShapeLike::intersects(const bp2d::Segment& s1,
const bp2d::Segment& s2)
inline bool intersects(const bp2d::Segment& s1, const bp2d::Segment& s2)
{
return boost::geometry::intersects(s1, s2);
}
#ifndef DISABLE_BOOST_AREA
template<>
inline double ShapeLike::area(const PolygonImpl& shape)
inline double area(const PolygonImpl& shape, const PolygonTag&)
{
return boost::geometry::area(shape);
}
#endif
template<>
inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point,
const PolygonImpl& shape)
inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
{
return boost::geometry::within(point, shape);
}
template<>
inline bool ShapeLike::isInside(const PolygonImpl& sh1,
const PolygonImpl& sh2)
inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2)
{
return boost::geometry::within(sh1, sh2);
}
template<>
inline bool ShapeLike::touches( const PolygonImpl& sh1,
const PolygonImpl& sh2)
inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2)
{
return boost::geometry::touches(sh1, sh2);
}
template<>
inline bool ShapeLike::touches( const PointImpl& point,
const PolygonImpl& shape)
inline bool touches( const PointImpl& point, const PolygonImpl& shape)
{
return boost::geometry::touches(point, shape);
}
#ifndef DISABLE_BOOST_BOUNDING_BOX
template<>
inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
{
bp2d::Box b;
boost::geometry::envelope(sh, b);
@ -395,7 +389,8 @@ inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
}
template<>
inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
inline bp2d::Box boundingBox<bp2d::Shapes>(const bp2d::Shapes& shapes,
const MultiPolygonTag&)
{
bp2d::Box b;
boost::geometry::envelope(shapes, b);
@ -405,7 +400,7 @@ inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
#ifndef DISABLE_BOOST_CONVEX_HULL
template<>
inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&)
{
PolygonImpl ret;
boost::geometry::convex_hull(sh, ret);
@ -413,7 +408,8 @@ inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
}
template<>
inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes,
const MultiPolygonTag&)
{
PolygonImpl ret;
boost::geometry::convex_hull(shapes, ret);
@ -421,56 +417,17 @@ inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
}
#endif
#ifndef DISABLE_BOOST_ROTATE
template<>
inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
{
namespace trans = boost::geometry::strategy::transform;
PolygonImpl cpy = sh;
trans::rotate_transformer<boost::geometry::radian, Radians, 2, 2>
rotate(rads);
boost::geometry::transform(cpy, sh, rotate);
}
#endif
#ifndef DISABLE_BOOST_TRANSLATE
template<>
inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
{
namespace trans = boost::geometry::strategy::transform;
PolygonImpl cpy = sh;
trans::translate_transformer<bp2d::Coord, 2, 2> translate(
bp2d::getX(offs), bp2d::getY(offs));
boost::geometry::transform(cpy, sh, translate);
}
#endif
#ifndef DISABLE_BOOST_OFFSET
template<>
inline void ShapeLike::offset(PolygonImpl& sh, bp2d::Coord distance)
inline void offset(PolygonImpl& sh, bp2d::Coord distance)
{
PolygonImpl cpy = sh;
boost::geometry::buffer(cpy, sh, distance);
}
#endif
#ifndef DISABLE_BOOST_NFP_MERGE
template<>
inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
const PolygonImpl& sh)
{
bp2d::Shapes retv;
boost::geometry::union_(shapes, sh, retv);
return retv;
}
#endif
#ifndef DISABLE_BOOST_SERIALIZE
template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
template<> inline std::string serialize<libnest2d::Formats::SVG>(
const PolygonImpl& sh, double scale)
{
std::stringstream ss;
@ -482,14 +439,14 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
Polygonf::ring_type ring;
Polygonf::inner_container_type holes;
ring.reserve(ShapeLike::contourVertexCount(sh));
ring.reserve(shapelike::contourVertexCount(sh));
for(auto it = ShapeLike::cbegin(sh); it != ShapeLike::cend(sh); it++) {
for(auto it = shapelike::cbegin(sh); it != shapelike::cend(sh); it++) {
auto& v = *it;
ring.emplace_back(getX(v)*scale, getY(v)*scale);
};
auto H = ShapeLike::holes(sh);
auto H = shapelike::holes(sh);
for(PathImpl& h : H ) {
Polygonf::ring_type hf;
for(auto it = h.begin(); it != h.end(); it++) {
@ -512,21 +469,47 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
#ifndef DISABLE_BOOST_UNSERIALIZE
template<>
inline void ShapeLike::unserialize<libnest2d::Formats::SVG>(
inline void unserialize<libnest2d::Formats::SVG>(
PolygonImpl& sh,
const std::string& str)
{
}
#endif
template<> inline std::pair<bool, std::string>
ShapeLike::isValid(const PolygonImpl& sh)
template<> inline std::pair<bool, std::string> isValid(const PolygonImpl& sh)
{
std::string message;
bool ret = boost::geometry::is_valid(sh, message);
return {ret, message};
}
}
namespace nfp {
#ifndef DISABLE_BOOST_NFP_MERGE
// Warning: I could not get boost union_ to work. Geometries will overlap.
template<>
inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes,
const PolygonImpl& sh)
{
bp2d::Shapes retv;
boost::geometry::union_(shapes, sh, retv);
return retv;
}
template<>
inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes)
{
bp2d::Shapes retv;
boost::geometry::union_(shapes, shapes.back(), retv);
return retv;
}
#endif
}
}

View File

@ -99,49 +99,54 @@ template<> struct PointType<PolygonImpl> {
using Type = PointImpl;
};
// Type of vertex iterator used by Clipper
template<> struct VertexIteratorType<PolygonImpl> {
using Type = ClipperLib::Path::iterator;
};
// Type of vertex iterator used by Clipper
template<> struct VertexConstIteratorType<PolygonImpl> {
using Type = ClipperLib::Path::const_iterator;
template<> struct PointType<PointImpl> {
using Type = PointImpl;
};
template<> struct CountourType<PolygonImpl> {
using Type = PathImpl;
};
// Tell binpack2d how to extract the X coord from a ClipperPoint object
template<> inline TCoord<PointImpl> PointLike::x(const PointImpl& p)
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
template<> struct ShapeTag<TMultiShape<PolygonImpl>> {
using Type = MultiPolygonTag;
};
template<> struct PointType<TMultiShape<PolygonImpl>> {
using Type = PointImpl;
};
template<> struct HolesContainer<PolygonImpl> {
using Type = ClipperLib::Paths;
};
namespace pointlike {
// Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline TCoord<PointImpl> x(const PointImpl& p)
{
return p.X;
}
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
template<> inline TCoord<PointImpl> PointLike::y(const PointImpl& p)
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline TCoord<PointImpl> y(const PointImpl& p)
{
return p.Y;
}
// Tell binpack2d how to extract the X coord from a ClipperPoint object
template<> inline TCoord<PointImpl>& PointLike::x(PointImpl& p)
// Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline TCoord<PointImpl>& x(PointImpl& p)
{
return p.X;
}
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
template<>
inline TCoord<PointImpl>& PointLike::y(PointImpl& p)
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline TCoord<PointImpl>& y(PointImpl& p)
{
return p.Y;
}
template<>
inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity)
{
return sh.Contour.reserve(vertex_capacity);
}
#define DISABLE_BOOST_AREA
@ -175,16 +180,24 @@ inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
return ClipperLib::Area(sh.Contour) + a;
}
}
// Tell binpack2d how to make string out of a ClipperPolygon object
template<>
inline double ShapeLike::area(const PolygonImpl& sh) {
namespace shapelike {
template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity)
{
return sh.Contour.reserve(vertex_capacity);
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<> inline double area(const PolygonImpl& sh, const PolygonTag&)
{
return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
}
template<>
inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
{
#define DISABLE_BOOST_OFFSET
using ClipperLib::ClipperOffset;
@ -234,7 +247,8 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
template<> inline std::string toString(const PolygonImpl& sh)
{
std::stringstream ss;
ss << "Contour {\n";
@ -257,37 +271,8 @@ template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
}
template<>
inline TVertexIterator<PolygonImpl> ShapeLike::begin(PolygonImpl& sh)
inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
{
return sh.Contour.begin();
}
template<>
inline TVertexIterator<PolygonImpl> ShapeLike::end(PolygonImpl& sh)
{
return sh.Contour.end();
}
template<>
inline TVertexConstIterator<PolygonImpl> ShapeLike::cbegin(
const PolygonImpl& sh)
{
return sh.Contour.cbegin();
}
template<>
inline TVertexConstIterator<PolygonImpl> ShapeLike::cend(
const PolygonImpl& sh)
{
return sh.Contour.cend();
}
template<> struct HolesContainer<PolygonImpl> {
using Type = ClipperLib::Paths;
};
template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
const HoleStore& holes) {
PolygonImpl p;
p.Contour = path;
@ -308,8 +293,7 @@ template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
return p;
}
template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
HoleStore&& holes) {
template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
PolygonImpl p;
p.Contour.swap(path);
@ -331,49 +315,49 @@ template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
return p;
}
template<> inline const THolesContainer<PolygonImpl>&
ShapeLike::holes(const PolygonImpl& sh)
template<>
inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
{
return sh.Holes;
}
template<> inline THolesContainer<PolygonImpl>&
ShapeLike::holes(PolygonImpl& sh)
template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
{
return sh.Holes;
}
template<> inline TContour<PolygonImpl>&
ShapeLike::getHole(PolygonImpl& sh, unsigned long idx)
template<>
inline TContour<PolygonImpl>& getHole(PolygonImpl& sh, unsigned long idx)
{
return sh.Holes[idx];
}
template<> inline const TContour<PolygonImpl>&
ShapeLike::getHole(const PolygonImpl& sh, unsigned long idx)
template<>
inline const TContour<PolygonImpl>& getHole(const PolygonImpl& sh,
unsigned long idx)
{
return sh.Holes[idx];
}
template<> inline size_t ShapeLike::holeCount(const PolygonImpl& sh)
template<> inline size_t holeCount(const PolygonImpl& sh)
{
return sh.Holes.size();
}
template<> inline PathImpl& ShapeLike::getContour(PolygonImpl& sh)
template<> inline PathImpl& getContour(PolygonImpl& sh)
{
return sh.Contour;
}
template<>
inline const PathImpl& ShapeLike::getContour(const PolygonImpl& sh)
inline const PathImpl& getContour(const PolygonImpl& sh)
{
return sh.Contour;
}
#define DISABLE_BOOST_TRANSLATE
template<>
inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
inline void translate(PolygonImpl& sh, const PointImpl& offs)
{
for(auto& p : sh.Contour) { p += offs; }
for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; }
@ -381,7 +365,7 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
#define DISABLE_BOOST_ROTATE
template<>
inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
inline void rotate(PolygonImpl& sh, const Radians& rads)
{
using Coord = TCoord<PointImpl>;
@ -402,9 +386,11 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
}
}
} // namespace shapelike
#define DISABLE_BOOST_NFP_MERGE
inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
Nfp::Shapes<PolygonImpl> retv;
inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
shapelike::Shapes<PolygonImpl> retv;
ClipperLib::PolyTree result;
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
@ -438,8 +424,10 @@ inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
return retv;
}
template<> inline Nfp::Shapes<PolygonImpl>
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
namespace nfp {
template<> inline std::vector<PolygonImpl>
merge(const std::vector<PolygonImpl>& shapes)
{
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
@ -461,6 +449,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
}
}
//#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE

View File

@ -22,34 +22,12 @@ template<class GeomType>
using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type;
/// Getting the type of point structure used by a shape.
template<class Shape> struct PointType { /*using Type = void;*/ };
template<class Sh> struct PointType { using Type = typename Sh::PointType; };
/// TPoint<ShapeClass> as shorthand for `typename PointType<ShapeClass>::Type`.
template<class Shape>
using TPoint = typename PointType<remove_cvref_t<Shape>>::Type;
/// Getting the VertexIterator type of a shape class.
template<class Shape> struct VertexIteratorType { /*using Type = void;*/ };
/// Getting the const vertex iterator for a shape class.
template<class Shape> struct VertexConstIteratorType {/* using Type = void;*/ };
/**
* TVertexIterator<Shape> as shorthand for
* `typename VertexIteratorType<Shape>::Type`
*/
template<class Shape>
using TVertexIterator =
typename VertexIteratorType<remove_cvref_t<Shape>>::Type;
/**
* \brief TVertexConstIterator<Shape> as shorthand for
* `typename VertexConstIteratorType<Shape>::Type`
*/
template<class ShapeClass>
using TVertexConstIterator =
typename VertexConstIteratorType<remove_cvref_t<ShapeClass>>::Type;
/**
* \brief A point pair base class for other point pairs (segment, box, ...).
* \tparam RawPoint The actual point type to use.
@ -60,6 +38,17 @@ struct PointPair {
RawPoint p2;
};
struct PolygonTag {};
struct MultiPolygonTag {};
struct BoxTag {};
struct CircleTag {};
template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; };
template<class S> using Tag = typename ShapeTag<S>::Type;
template<class S> struct MultiShape { using Type = std::vector<S>; };
template<class S> using TMultiShape = typename MultiShape<S>::Type;
/**
* \brief An abstraction of a box;
*/
@ -69,6 +58,9 @@ class _Box: PointPair<RawPoint> {
using PointPair<RawPoint>::p2;
public:
using Tag = BoxTag;
using PointType = RawPoint;
inline _Box() = default;
inline _Box(const RawPoint& p, const RawPoint& pp):
PointPair<RawPoint>({p, pp}) {}
@ -98,6 +90,9 @@ class _Circle {
double radius_ = 0;
public:
using Tag = CircleTag;
using PointType = RawPoint;
_Circle() = default;
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
@ -109,7 +104,7 @@ public:
inline void radius(double r) { radius_ = r; }
inline double area() const BP2D_NOEXCEPT {
return 2.0*Pi*radius_;
return 2.0*Pi*radius_*radius_;
}
};
@ -123,6 +118,8 @@ class _Segment: PointPair<RawPoint> {
mutable Radians angletox_ = std::nan("");
public:
using PointType = RawPoint;
inline _Segment() = default;
inline _Segment(const RawPoint& p, const RawPoint& pp):
@ -156,36 +153,36 @@ public:
inline double length();
};
// This struct serves as a namespace. The only difference is that is can be
// This struct serves almost as a namespace. The only difference is that is can
// used in friend declarations.
struct PointLike {
namespace pointlike {
template<class RawPoint>
static TCoord<RawPoint> x(const RawPoint& p)
inline TCoord<RawPoint> x(const RawPoint& p)
{
return p(0);
}
template<class RawPoint>
static TCoord<RawPoint> y(const RawPoint& p)
inline TCoord<RawPoint> y(const RawPoint& p)
{
return p(1);
}
template<class RawPoint>
static TCoord<RawPoint>& x(RawPoint& p)
inline TCoord<RawPoint>& x(RawPoint& p)
{
return p(0);
}
template<class RawPoint>
static TCoord<RawPoint>& y(RawPoint& p)
inline TCoord<RawPoint>& y(RawPoint& p)
{
return p(1);
}
template<class RawPoint>
static double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
{
static_assert(always_false<RawPoint>::value,
"PointLike::distance(point, point) unimplemented!");
@ -193,7 +190,7 @@ struct PointLike {
}
template<class RawPoint>
static double distance(const RawPoint& /*p1*/,
inline double distance(const RawPoint& /*p1*/,
const _Segment<RawPoint>& /*s*/)
{
static_assert(always_false<RawPoint>::value,
@ -202,13 +199,13 @@ struct PointLike {
}
template<class RawPoint>
static std::pair<TCoord<RawPoint>, bool> horizontalDistance(
inline std::pair<TCoord<RawPoint>, bool> horizontalDistance(
const RawPoint& p, const _Segment<RawPoint>& s)
{
using Unit = TCoord<RawPoint>;
auto x = PointLike::x(p), y = PointLike::y(p);
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
auto x = pointlike::x(p), y = pointlike::y(p);
auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
TCoord<RawPoint> ret;
@ -228,13 +225,13 @@ struct PointLike {
}
template<class RawPoint>
static std::pair<TCoord<RawPoint>, bool> verticalDistance(
inline std::pair<TCoord<RawPoint>, bool> verticalDistance(
const RawPoint& p, const _Segment<RawPoint>& s)
{
using Unit = TCoord<RawPoint>;
auto x = PointLike::x(p), y = PointLike::y(p);
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
auto x = pointlike::x(p), y = pointlike::y(p);
auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
TCoord<RawPoint> ret;
@ -252,36 +249,36 @@ struct PointLike {
return {ret, true};
}
};
}
template<class RawPoint>
TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT
{
return PointLike::x(maxCorner()) - PointLike::x(minCorner());
return pointlike::x(maxCorner()) - pointlike::x(minCorner());
}
template<class RawPoint>
TCoord<RawPoint> _Box<RawPoint>::height() const BP2D_NOEXCEPT
{
return PointLike::y(maxCorner()) - PointLike::y(minCorner());
return pointlike::y(maxCorner()) - pointlike::y(minCorner());
}
template<class RawPoint>
TCoord<RawPoint> getX(const RawPoint& p) { return PointLike::x<RawPoint>(p); }
TCoord<RawPoint> getX(const RawPoint& p) { return pointlike::x<RawPoint>(p); }
template<class RawPoint>
TCoord<RawPoint> getY(const RawPoint& p) { return PointLike::y<RawPoint>(p); }
TCoord<RawPoint> getY(const RawPoint& p) { return pointlike::y<RawPoint>(p); }
template<class RawPoint>
void setX(RawPoint& p, const TCoord<RawPoint>& val)
{
PointLike::x<RawPoint>(p) = val;
pointlike::x<RawPoint>(p) = val;
}
template<class RawPoint>
void setY(RawPoint& p, const TCoord<RawPoint>& val)
{
PointLike::y<RawPoint>(p) = val;
pointlike::y<RawPoint>(p) = val;
}
template<class RawPoint>
@ -303,7 +300,7 @@ inline Radians _Segment<RawPoint>::angleToXaxis() const
template<class RawPoint>
inline double _Segment<RawPoint>::length()
{
return PointLike::distance(first(), second());
return pointlike::distance(first(), second());
}
template<class RawPoint>
@ -313,9 +310,9 @@ inline RawPoint _Box<RawPoint>::center() const BP2D_NOEXCEPT {
using Coord = TCoord<RawPoint>;
RawPoint ret = {
static_cast<Coord>( std::round((getX(minc) + getX(maxc))/2.0) ),
static_cast<Coord>( std::round((getY(minc) + getY(maxc))/2.0) )
RawPoint ret = { // No rounding here, we dont know if these are int coords
static_cast<Coord>( (getX(minc) + getX(maxc))/2.0 ),
static_cast<Coord>( (getY(minc) + getY(maxc))/2.0 )
};
return ret;
@ -356,124 +353,125 @@ enum class Formats {
// This struct serves as a namespace. The only difference is that it can be
// used in friend declarations and can be aliased at class scope.
struct ShapeLike {
namespace shapelike {
template<class RawShape>
using Shapes = std::vector<RawShape>;
using Shapes = TMultiShape<RawShape>;
template<class RawShape>
static RawShape create(const TContour<RawShape>& contour,
inline RawShape create(const TContour<RawShape>& contour,
const THolesContainer<RawShape>& holes)
{
return RawShape(contour, holes);
}
template<class RawShape>
static RawShape create(TContour<RawShape>&& contour,
inline RawShape create(TContour<RawShape>&& contour,
THolesContainer<RawShape>&& holes)
{
return RawShape(contour, holes);
}
template<class RawShape>
static RawShape create(const TContour<RawShape>& contour)
inline RawShape create(const TContour<RawShape>& contour)
{
return create<RawShape>(contour, {});
}
template<class RawShape>
static RawShape create(TContour<RawShape>&& contour)
inline RawShape create(TContour<RawShape>&& contour)
{
return create<RawShape>(contour, {});
}
template<class RawShape>
static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
inline THolesContainer<RawShape>& holes(RawShape& /*sh*/)
{
static THolesContainer<RawShape> empty;
return empty;
}
template<class RawShape>
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
inline 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)
inline TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
{
return holes(sh)[idx];
}
template<class RawShape>
static const TContour<RawShape>& getHole(const RawShape& sh,
inline const TContour<RawShape>& getHole(const RawShape& sh,
unsigned long idx)
{
return holes(sh)[idx];
}
template<class RawShape>
static size_t holeCount(const RawShape& sh)
inline size_t holeCount(const RawShape& sh)
{
return holes(sh).size();
}
template<class RawShape>
static TContour<RawShape>& getContour(RawShape& sh)
inline TContour<RawShape>& getContour(RawShape& sh)
{
return sh;
}
template<class RawShape>
static const TContour<RawShape>& getContour(const RawShape& sh)
inline 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*/) {}
inline void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
template<class RawShape, class...Args>
static void addVertex(RawShape& sh, Args...args)
inline void addVertex(RawShape& sh, Args...args)
{
return getContour(sh).emplace_back(std::forward<Args>(args)...);
}
template<class RawShape>
static TVertexIterator<RawShape> begin(RawShape& sh)
inline typename TContour<RawShape>::iterator begin(RawShape& sh)
{
return sh.begin();
return getContour(sh).begin();
}
template<class RawShape>
static TVertexIterator<RawShape> end(RawShape& sh)
inline typename TContour<RawShape>::iterator end(RawShape& sh)
{
return sh.end();
return getContour(sh).end();
}
template<class RawShape>
static TVertexConstIterator<RawShape> cbegin(const RawShape& sh)
inline typename TContour<RawShape>::const_iterator
cbegin(const RawShape& sh)
{
return sh.cbegin();
return getContour(sh).cbegin();
}
template<class RawShape>
static TVertexConstIterator<RawShape> cend(const RawShape& sh)
inline typename TContour<RawShape>::const_iterator cend(const RawShape& sh)
{
return sh.cend();
return getContour(sh).cend();
}
template<class RawShape>
static std::string toString(const RawShape& /*sh*/)
inline std::string toString(const RawShape& /*sh*/)
{
return "";
}
template<Formats, class RawShape>
static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::serialize() unimplemented!");
@ -481,14 +479,14 @@ struct ShapeLike {
}
template<Formats, class RawShape>
static void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::unserialize() unimplemented!");
}
template<class RawShape>
static double area(const RawShape& /*sh*/)
inline double area(const RawShape& /*sh*/, const PolygonTag&)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::area() unimplemented!");
@ -496,7 +494,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::intersects() unimplemented!");
@ -504,7 +502,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool isInside(const TPoint<RawShape>& /*point*/,
inline bool isInside(const TPoint<RawShape>& /*point*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@ -513,7 +511,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool isInside(const RawShape& /*shape*/,
inline bool isInside(const RawShape& /*shape*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@ -522,7 +520,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool touches( const RawShape& /*shape*/,
inline bool touches( const RawShape& /*shape*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@ -531,7 +529,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool touches( const TPoint<RawShape>& /*point*/,
inline bool touches( const TPoint<RawShape>& /*point*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
@ -540,64 +538,66 @@ struct ShapeLike {
}
template<class RawShape>
static _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/)
inline _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/,
const PolygonTag&)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::boundingBox(shape) unimplemented!");
}
template<class RawShape>
static _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/)
template<class RawShapes>
inline _Box<TPoint<typename RawShapes::value_type>>
boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&)
{
static_assert(always_false<RawShape>::value,
static_assert(always_false<RawShapes>::value,
"ShapeLike::boundingBox(shapes) unimplemented!");
}
template<class RawShape>
static RawShape convexHull(const RawShape& /*sh*/)
inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::convexHull(shape) unimplemented!");
return RawShape();
}
template<class RawShape>
static RawShape convexHull(const Shapes<RawShape>& /*sh*/)
template<class RawShapes>
inline typename RawShapes::value_type
convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&)
{
static_assert(always_false<RawShape>::value,
static_assert(always_false<RawShapes>::value,
"ShapeLike::convexHull(shapes) unimplemented!");
return RawShape();
return typename RawShapes::value_type();
}
template<class RawShape>
static void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::rotate() unimplemented!");
}
template<class RawShape, class RawPoint>
static void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::translate() unimplemented!");
}
template<class RawShape>
static void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
{
static_assert(always_false<RawShape>::value,
"ShapeLike::offset() unimplemented!");
dout() << "The current geometry backend does not support offsetting!\n";
}
template<class RawShape>
static std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
inline std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
{
return {false, "ShapeLike::isValid() unimplemented!"};
}
template<class RawShape>
static inline bool isConvex(const TContour<RawShape>& sh)
inline bool isConvex(const TContour<RawShape>& sh)
{
using Vertex = TPoint<RawShape>;
auto first = sh.begin();
@ -633,43 +633,55 @@ struct ShapeLike {
// No need to implement these
// *************************************************************************
template<class RawShape>
static inline _Box<TPoint<RawShape>> boundingBox(
const _Box<TPoint<RawShape>>& box)
template<class Box>
inline Box boundingBox(const Box& box, const BoxTag& )
{
return box;
}
template<class RawShape>
static inline _Box<TPoint<RawShape>> boundingBox(
const _Circle<TPoint<RawShape>>& circ)
template<class Circle>
inline _Box<typename Circle::PointType> boundingBox(
const Circle& circ, const CircleTag&)
{
using Coord = TCoord<TPoint<RawShape>>;
TPoint<RawShape> pmin = {
using Point = typename Circle::PointType;
using Coord = TCoord<Point>;
Point pmin = {
static_cast<Coord>(getX(circ.center()) - circ.radius()),
static_cast<Coord>(getY(circ.center()) - circ.radius()) };
TPoint<RawShape> pmax = {
Point 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)
template<class S> // Dispatch function
inline _Box<TPoint<S>> boundingBox(const S& sh)
{
return static_cast<double>(box.width() * box.height());
return boundingBox(sh, Tag<S>() );
}
template<class RawShape>
static inline double area(const _Circle<TPoint<RawShape>>& circ)
template<class Box>
inline double area(const Box& box, const BoxTag& )
{
return box.area();
}
template<class Circle>
inline double area(const Circle& circ, const CircleTag& )
{
return circ.area();
}
template<class RawShape> // Dispatching function
inline double area(const RawShape& sh)
{
return area(sh, Tag<RawShape>());
}
template<class RawShape>
static inline double area(const Shapes<RawShape>& shapes)
inline double area(const Shapes<RawShape>& shapes)
{
return std::accumulate(shapes.begin(), shapes.end(), 0.0,
[](double a, const RawShape& b) {
@ -678,14 +690,21 @@ struct ShapeLike {
}
template<class RawShape>
static bool isInside(const TPoint<RawShape>& point,
const _Circle<TPoint<RawShape>>& circ)
inline auto convexHull(const RawShape& sh)
-> decltype(convexHull(sh, Tag<RawShape>())) // TODO: C++14 could deduce
{
return PointLike::distance(point, circ.center()) < circ.radius();
return convexHull(sh, Tag<RawShape>());
}
template<class RawShape>
static bool isInside(const TPoint<RawShape>& point,
inline bool isInside(const TPoint<RawShape>& point,
const _Circle<TPoint<RawShape>>& circ)
{
return pointlike::distance(point, circ.center()) < circ.radius();
}
template<class RawShape>
inline bool isInside(const TPoint<RawShape>& point,
const _Box<TPoint<RawShape>>& box)
{
auto px = getX(point);
@ -699,7 +718,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool isInside(const RawShape& sh,
inline bool isInside(const RawShape& sh,
const _Circle<TPoint<RawShape>>& circ)
{
return std::all_of(cbegin(sh), cend(sh),
@ -709,7 +728,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool isInside(const _Box<TPoint<RawShape>>& box,
inline bool isInside(const _Box<TPoint<RawShape>>& box,
const _Circle<TPoint<RawShape>>& circ)
{
return isInside<RawShape>(box.minCorner(), circ) &&
@ -717,7 +736,7 @@ struct ShapeLike {
}
template<class RawShape>
static bool isInside(const _Box<TPoint<RawShape>>& ibb,
inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
const _Box<TPoint<RawShape>>& box)
{
auto iminX = getX(ibb.minCorner());
@ -734,31 +753,31 @@ struct ShapeLike {
}
template<class RawShape> // Potential O(1) implementation may exist
static inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
{
return *(begin(sh) + idx);
}
template<class RawShape> // Potential O(1) implementation may exist
static inline const TPoint<RawShape>& vertex(const RawShape& sh,
inline const TPoint<RawShape>& vertex(const RawShape& sh,
unsigned long idx)
{
return *(cbegin(sh) + idx);
}
template<class RawShape>
static inline size_t contourVertexCount(const RawShape& sh)
inline size_t contourVertexCount(const RawShape& sh)
{
return cend(sh) - cbegin(sh);
}
template<class RawShape, class Fn>
static inline void foreachContourVertex(RawShape& sh, Fn fn) {
inline void foreachContourVertex(RawShape& sh, Fn fn) {
for(auto it = begin(sh); it != end(sh); ++it) fn(*it);
}
template<class RawShape, class Fn>
static inline void foreachHoleVertex(RawShape& sh, Fn fn) {
inline void foreachHoleVertex(RawShape& sh, Fn fn) {
for(int i = 0; i < holeCount(sh); ++i) {
auto& h = getHole(sh, i);
for(auto it = begin(h); it != end(h); ++it) fn(*it);
@ -766,12 +785,12 @@ struct ShapeLike {
}
template<class RawShape, class Fn>
static inline void foreachContourVertex(const RawShape& sh, Fn fn) {
inline void foreachContourVertex(const RawShape& sh, Fn fn) {
for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it);
}
template<class RawShape, class Fn>
static inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
for(int i = 0; i < holeCount(sh); ++i) {
auto& h = getHole(sh, i);
for(auto it = cbegin(h); it != cend(h); ++it) fn(*it);
@ -779,18 +798,27 @@ struct ShapeLike {
}
template<class RawShape, class Fn>
static inline void foreachVertex(RawShape& sh, Fn fn) {
inline void foreachVertex(RawShape& sh, Fn fn) {
foreachContourVertex(sh, fn);
foreachHoleVertex(sh, fn);
}
template<class RawShape, class Fn>
static inline void foreachVertex(const RawShape& sh, Fn fn) {
inline void foreachVertex(const RawShape& sh, Fn fn) {
foreachContourVertex(sh, fn);
foreachHoleVertex(sh, fn);
}
}
};
#define DECLARE_MAIN_TYPES(T) \
using Polygon = T; \
using Point = TPoint<T>; \
using Coord = TCoord<Point>; \
using Contour = TContour<T>; \
using Box = _Box<Point>; \
using Circle = _Circle<Point>; \
using Segment = _Segment<Point>; \
using Polygons = TMultiShape<T>
}

View File

@ -9,6 +9,27 @@
namespace libnest2d {
namespace __nfp {
// Do not specialize this...
template<class RawShape>
inline bool _vsort(const TPoint<RawShape>& v1, const TPoint<RawShape>& v2)
{
using Coord = TCoord<TPoint<RawShape>>;
Coord &&x1 = getX(v1), &&x2 = getX(v2), &&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;
}
}
/// A collection of static methods for handling the no fit polygon creation.
namespace nfp {
//namespace sl = shapelike;
//namespace pl = pointlike;
/// The complexity level of a polygon that an NFP implementation can handle.
enum class NfpLevel: unsigned {
CONVEX_ONLY,
@ -18,12 +39,17 @@ enum class NfpLevel: unsigned {
BOTH_CONCAVE_WITH_HOLES
};
/// A collection of static methods for handling the no fit polygon creation.
struct Nfp {
template<class RawShape>
using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
template<class RawShape> struct MaxNfpLevel {
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
};
// Shorthand for a pile of polygons
template<class RawShape>
using Shapes = typename ShapeLike::Shapes<RawShape>;
using Shapes = TMultiShape<RawShape>;
/**
* Merge a bunch of polygons with the specified additional polygon.
@ -36,10 +62,10 @@ using Shapes = typename ShapeLike::Shapes<RawShape>;
* 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 Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
template<class RawShapes>
inline RawShapes merge(const RawShapes& /*shc*/)
{
static_assert(always_false<RawShape>::value,
static_assert(always_false<RawShapes>::value,
"Nfp::merge(shapes, shape) unimplemented!");
}
@ -55,12 +81,44 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
* 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)
inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
const RawShape& sh)
{
auto m = merge(shc);
auto m = nfp::merge(shc);
m.push_back(sh);
return merge(m);
return nfp::merge(m);
}
/**
* Get the vertex of the polygon that is at the lowest values (bottom) in the Y
* axis and if there are more than one vertices on the same Y coordinate than
* the result will be the leftmost (with the highest X coordinate).
*/
template<class RawShape>
inline TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
{
// find min x and min y vertex
auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh),
__nfp::_vsort<RawShape>);
return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;;
}
/**
* Get the vertex of the polygon that is at the highest values (top) in the Y
* axis and if there are more than one vertices on the same Y coordinate than
* the result will be the rightmost (with the lowest X coordinate).
*/
template<class RawShape>
TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
{
// find max x and max y vertex
auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
__nfp::_vsort<RawShape>);
return it == shapelike::cend(sh) ? TPoint<RawShape>() : *it;
}
/**
@ -71,55 +129,11 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
* reference will be always the same for the same polygon.
*/
template<class RawShape>
inline static TPoint<RawShape> referenceVertex(const RawShape& sh)
inline TPoint<RawShape> referenceVertex(const RawShape& sh)
{
return rightmostUpVertex(sh);
}
/**
* Get the vertex of the polygon that is at the lowest values (bottom) in the Y
* axis and if there are more than one vertices on the same Y coordinate than
* the result will be the leftmost (with the highest X coordinate).
*/
template<class RawShape>
static TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
{
// find min x and min y vertex
auto it = std::min_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
_vsort<RawShape>);
return *it;
}
/**
* Get the vertex of the polygon that is at the highest values (top) in the Y
* axis and if there are more than one vertices on the same Y coordinate than
* the result will be the rightmost (with the lowest X coordinate).
*/
template<class RawShape>
static TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
{
// 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 NfpResult<RawShape> noFitPolygon(const RawShape& sh,
const RawShape& other)
{
NfpImpl<RawShape, nfptype> nfp;
return nfp(sh, other);
}
/**
* The "trivial" Cuninghame-Green implementation of NFP for convex polygons.
*
@ -139,11 +153,11 @@ static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
*
*/
template<class RawShape>
static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
const RawShape& other)
{
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
using sl = ShapeLike;
namespace sl = shapelike;
RawShape rsh; // Final nfp placeholder
Vertex top_nfp;
@ -187,7 +201,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
sl::addVertex(rsh, edgelist.front().second());
// Sorting function for the nfp reference vertex search
auto& cmp = _vsort<RawShape>;
auto& cmp = __nfp::_vsort<RawShape>;
// the reference (rightmost top) vertex so far
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
@ -214,7 +228,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
}
template<class RawShape>
static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
const RawShape& cother)
{
@ -233,7 +247,7 @@ static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
using Vertex = TPoint<RawShape>;
using Coord = TCoord<Vertex>;
using Edge = _Segment<Vertex>;
using sl = ShapeLike;
namespace sl = shapelike;
using std::signbit;
using std::sort;
using std::vector;
@ -528,27 +542,16 @@ struct NfpImpl {
}
};
template<class RawShape> struct MaxNfpLevel {
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
};
private:
// Do not specialize this...
template<class RawShape>
static inline bool _vsort(const TPoint<RawShape>& v1,
const TPoint<RawShape>& v2)
/// Helper function to get the NFP
template<NfpLevel nfptype, class RawShape>
inline NfpResult<RawShape> noFitPolygon(const RawShape& sh,
const RawShape& other)
{
using Coord = TCoord<TPoint<RawShape>>;
Coord &&x1 = getX(v1), &&x2 = getX(v2), &&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;
NfpImpl<RawShape, nfptype> nfps;
return nfps(sh, other);
}
};
}
}

View File

@ -9,10 +9,12 @@
#include <functional>
#include "geometry_traits.hpp"
#include "optimizer.hpp"
namespace libnest2d {
namespace sl = shapelike;
namespace pl = pointlike;
/**
* \brief An item to be placed on a bin.
*
@ -28,7 +30,8 @@ class _Item {
using Coord = TCoord<TPoint<RawShape>>;
using Vertex = TPoint<RawShape>;
using Box = _Box<Vertex>;
using sl = ShapeLike;
using VertexConstIterator = typename TContour<RawShape>::const_iterator;
// The original shape that gets encapsulated.
RawShape sh_;
@ -38,7 +41,7 @@ class _Item {
Radians rotation_;
Coord offset_distance_;
// Info about whether the tranformations will have to take place
// Info about whether the transformations will have to take place
// This is needed because if floating point is used, it is hard to say
// that a zero angle is not a rotation because of testing for equality.
bool has_rotation_ = false, has_translation_ = false, has_offset_ = false;
@ -58,12 +61,12 @@ class _Item {
};
mutable Convexity convexity_ = Convexity::UNCHECKED;
mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex
mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex
mutable VertexConstIterator rmt_; // rightmost top vertex
mutable VertexConstIterator 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) {}
Box bb; bool valid;
BBCache(): valid(false) {}
} bb_cache_;
public:
@ -80,7 +83,7 @@ public:
* supports. Giving out a non const iterator would make it impossible to
* perform correct cache invalidation.
*/
using Iterator = TVertexConstIterator<RawShape>;
using Iterator = VertexConstIterator;
/**
* @brief Get the orientation of the polygon.
@ -109,7 +112,7 @@ public:
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
/**
* @brief Create an item from an initilizer list.
* @brief Create an item from an initializer list.
* @param il The initializer list of vertices.
*/
inline _Item(const std::initializer_list< Vertex >& il):
@ -159,7 +162,7 @@ public:
}
/**
* @brief Get a copy of an outer vertex whithin the carried shape.
* @brief Get a copy of an outer vertex within the carried shape.
*
* Note that the vertex considered here is taken from the original shape
* that this item is constructed from. This means that no transformation is
@ -244,7 +247,7 @@ public:
* @param p
* @return
*/
inline bool isPointInside(const Vertex& p) const
inline bool isInside(const Vertex& p) const
{
return sl::isInside(p, transformedShape());
}
@ -307,7 +310,7 @@ public:
{
if(translation_ != tr) {
translation_ = tr; has_translation_ = true; tr_cache_valid_ = false;
bb_cache_.valid = false;
//bb_cache_.valid = false;
}
}
@ -342,13 +345,19 @@ public:
inline Box boundingBox() const {
if(!bb_cache_.valid) {
bb_cache_.bb = sl::boundingBox(transformedShape());
bb_cache_.tr = {0, 0};
if(!has_rotation_)
bb_cache_.bb = sl::boundingBox(offsettedShape());
else {
// TODO make sure this works
auto rotsh = offsettedShape();
sl::rotate(rotsh, rotation_);
bb_cache_.bb = sl::boundingBox(rotsh);
}
bb_cache_.valid = true;
}
auto &bb = bb_cache_.bb; auto &tr = bb_cache_.tr;
return {bb.minCorner() + tr, bb.maxCorner() + tr};
auto &bb = bb_cache_.bb; auto &tr = translation_;
return {bb.minCorner() + tr, bb.maxCorner() + tr };
}
inline Vertex referenceVertex() const {
@ -438,7 +447,7 @@ public:
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != CLOCKWISE
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
_Item<RawShape>( ShapeLike::create<RawShape>( {
_Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{0, height},
{width, height},
@ -452,7 +461,7 @@ public:
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != COUNTER_CLOCKWISE
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
_Item<RawShape>( ShapeLike::create<RawShape>( {
_Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{width, 0},
{width, height},
@ -473,18 +482,38 @@ public:
template<class RawShape>
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
return ShapeLike::isInside<RawShape>(boundingBox(), box);
return sl::isInside<RawShape>(boundingBox(), box);
}
template<class RawShape> inline bool
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
return ShapeLike::isInside<RawShape>(transformedShape(), circ);
return sl::isInside<RawShape>(transformedShape(), circ);
}
template<class I> using _ItemRef = std::reference_wrapper<I>;
template<class I> using _ItemGroup = std::vector<_ItemRef<I>>;
template<class Iterator>
struct ConstItemRange {
Iterator from;
Iterator to;
bool valid = false;
ConstItemRange() = default;
ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
};
template<class Container>
inline ConstItemRange<typename Container::const_iterator>
rem(typename Container::const_iterator it, const Container& cont) {
return {std::next(it), cont.end()};
}
/**
* \brief A wrapper interface (trait) class for any placement strategy provider.
*
* If a client want's to use its own placement algorithm, all it has to do is to
* If a client wants to use its own placement algorithm, all it has to do is to
* specialize this class template and define all the ten methods it has. It can
* use the strategies::PlacerBoilerplace class for creating a new placement
* strategy where only the constructor and the trypack method has to be provided
@ -515,8 +544,9 @@ public:
*/
using PackResult = typename PlacementStrategy::PackResult;
using ItemRef = std::reference_wrapper<Item>;
using ItemGroup = std::vector<ItemRef>;
using ItemRef = _ItemRef<Item>;
using ItemGroup = _ItemGroup<Item>;
using DefaultIterator = typename ItemGroup::const_iterator;
/**
* @brief Constructor taking the bin and an optional configuration.
@ -536,29 +566,32 @@ public:
* Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation.
*
* @param config The configuration object defined by the placement startegy.
* @param config The configuration object defined by the placement strategy.
*/
inline void configure(const Config& config) { impl_.configure(config); }
/**
* @brief A method that tries to pack an item and returns an object
* describing the pack result.
* Try to pack an item with a result object that contains the packing
* information for later accepting it.
*
* The result can be casted to bool and used as an argument to the accept
* method to accept a succesfully packed item. This way the next packing
* will consider the accepted item as well. The PackResult should carry the
* transformation info so that if the tried item is later modified or tried
* multiple times, the result object should set it to the originally
* determied position. An implementation can be found in the
* strategies::PlacerBoilerplate::PackResult class.
*
* @param item Ithe item to be packed.
* @return The PackResult object that can be implicitly casted to bool.
* \param item_store A container of items that are intended to be packed
* later. Can be used by the placer to switch tactics. When it's knows that
* many items will come a greedy strategy may not be the best.
* \param from The iterator to the item from which the packing should start,
* including the pointed item
* \param count How many items should be packed. If the value is 1, than
* just the item pointed to by "from" argument should be packed.
*/
inline PackResult trypack(Item& item) { return impl_.trypack(item); }
template<class Iter = DefaultIterator>
inline PackResult trypack(
Item& item,
const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
{
return impl_.trypack(item, remaining);
}
/**
* @brief A method to accept a previously tried item.
* @brief A method to accept a previously tried item (or items).
*
* If the pack result is a failure the method should ignore it.
* @param r The result of a previous trypack call.
@ -566,19 +599,25 @@ public:
inline void accept(PackResult& r) { impl_.accept(r); }
/**
* @brief pack Try to pack an item and immediately accept it on success.
* @brief pack Try to pack and immediately accept it on success.
*
* A default implementation would be to call
* { auto&& r = trypack(item); accept(r); return r; } but we should let the
* { auto&& r = trypack(...); accept(r); return r; } but we should let the
* implementor of the placement strategy to harvest any optimizations from
* the absence of an intermadiate step. The above version can still be used
* the absence of an intermediate step. The above version can still be used
* in the implementation.
*
* @param item The item to pack.
* @return Returns true if the item was packed or false if it could not be
* packed.
*/
inline bool pack(Item& item) { return impl_.pack(item); }
template<class Range = ConstItemRange<DefaultIterator>>
inline bool pack(
Item& item,
const Range& remaining = Range())
{
return impl_.pack(item, remaining);
}
/// Unpack the last element (remove it from the list of packed items).
inline void unpackLast() { impl_.unpackLast(); }
@ -597,13 +636,6 @@ public:
inline double filledArea() const { return impl_.filledArea(); }
#ifndef NDEBUG
inline auto getDebugItems() -> decltype(impl_.debug_items_)&
{
return impl_.debug_items_;
}
#endif
};
// The progress function will be called with the number of placed items
@ -628,15 +660,15 @@ public:
* Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation.
*
* @param config The configuration object defined by the selection startegy.
* @param config The configuration object defined by the selection strategy.
*/
inline void configure(const Config& config) {
impl_.configure(config);
}
/**
* @brief A function callback which should be called whenewer an item or
* a group of items where succesfully packed.
* @brief A function callback which should be called whenever an item or
* a group of items where successfully packed.
* @param fn A function callback object taking one unsigned integer as the
* number of the remaining items to pack.
*/
@ -649,7 +681,7 @@ public:
* placer compatible with the PlacementStrategyLike interface.
*
* \param first, last The first and last iterator if the input sequence. It
* can be only an iterator of a type converitible to Item.
* can be only an iterator of a type convertible to Item.
* \param bin. The shape of the bin. It has to be supported by the placement
* strategy.
* \param An optional config object for the placer.
@ -681,7 +713,7 @@ public:
/**
* @brief Get the items for a particular bin.
* @param binIndex The index of the requested bin.
* @return Returns a list of allitems packed into the requested bin.
* @return Returns a list of all items packed into the requested bin.
*/
inline ItemGroup itemsForBin(size_t binIndex) {
return impl_.itemsForBin(binIndex);
@ -723,15 +755,14 @@ using _IndexedPackGroup = std::vector<
>;
/**
* The Arranger is the frontend class for the binpack2d library. It takes the
* The Arranger is the front-end class for the libnest2d library. It takes the
* input items and outputs the items with the proper transformations to be
* inside the provided bin.
*/
template<class PlacementStrategy, class SelectionStrategy >
class Arranger {
class Nester {
using TSel = SelectionStrategyLike<SelectionStrategy>;
TSel selector_;
bool use_min_bb_rotation_ = false;
public:
using Item = typename PlacementStrategy::Item;
using ItemRef = std::reference_wrapper<Item>;
@ -769,7 +800,7 @@ public:
template<class TBinType = BinType,
class PConf = PlacementConfig,
class SConf = SelectionConfig>
Arranger( TBinType&& bin,
Nester( TBinType&& bin,
Unit min_obj_distance = 0,
PConf&& pconfig = PConf(),
SConf&& sconfig = SConf()):
@ -802,9 +833,9 @@ public:
* the selection algorithm.
*/
template<class TIterator>
inline PackGroup arrange(TIterator from, TIterator to)
inline PackGroup execute(TIterator from, TIterator to)
{
return _arrange(from, to);
return _execute(from, to);
}
/**
@ -815,20 +846,20 @@ public:
* input sequence size.
*/
template<class TIterator>
inline IndexedPackGroup arrangeIndexed(TIterator from, TIterator to)
inline IndexedPackGroup executeIndexed(TIterator from, TIterator to)
{
return _arrangeIndexed(from, to);
return _executeIndexed(from, to);
}
/// Shorthand to normal arrange method.
template<class TIterator>
inline PackGroup operator() (TIterator from, TIterator to)
{
return _arrange(from, to);
return _execute(from, to);
}
/// Set a progress indicatior function object for the selector.
inline Arranger& progressIndicator(ProgressFunction func)
/// Set a progress indicator function object for the selector.
inline Nester& progressIndicator(ProgressFunction func)
{
selector_.progressIndicator(func); return *this;
}
@ -842,24 +873,20 @@ public:
return ret;
}
inline Arranger& useMinimumBoundigBoxRotation(bool s = true) {
use_min_bb_rotation_ = s; return *this;
}
private:
template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>,
// This funtion will be used only if the iterators are pointing to
// a type compatible with the binpack2d::_Item template.
// This function will be used only if the iterators are pointing to
// a type compatible with the libnets2d::_Item template.
// This way we can use references to input elements as they will
// have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
>
inline PackGroup _arrange(TIterator from, TIterator to, bool = false)
inline PackGroup _execute(TIterator from, TIterator to, bool = false)
{
__arrange(from, to);
__execute(from, to);
return lastResult();
}
@ -867,28 +894,28 @@ private:
class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
>
inline PackGroup _arrange(TIterator from, TIterator to, int = false)
inline PackGroup _execute(TIterator from, TIterator to, int = false)
{
item_cache_ = {from, to};
__arrange(item_cache_.begin(), item_cache_.end());
__execute(item_cache_.begin(), item_cache_.end());
return lastResult();
}
template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>,
// This funtion will be used only if the iterators are pointing to
// a type compatible with the binpack2d::_Item template.
// This function will be used only if the iterators are pointing to
// a type compatible with the libnest2d::_Item template.
// This way we can use references to input elements as they will
// have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
>
inline IndexedPackGroup _arrangeIndexed(TIterator from,
inline IndexedPackGroup _executeIndexed(TIterator from,
TIterator to,
bool = false)
{
__arrange(from, to);
__execute(from, to);
return createIndexedPackGroup(from, to, selector_);
}
@ -896,12 +923,12 @@ private:
class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
>
inline IndexedPackGroup _arrangeIndexed(TIterator from,
inline IndexedPackGroup _executeIndexed(TIterator from,
TIterator to,
int = false)
{
item_cache_ = {from, to};
__arrange(item_cache_.begin(), item_cache_.end());
__execute(item_cache_.begin(), item_cache_.end());
return createIndexedPackGroup(from, to, selector_);
}
@ -933,37 +960,12 @@ private:
return pg;
}
Radians findBestRotation(Item& item) {
opt::StopCriteria stopcr;
stopcr.absolute_score_difference = 0.01;
stopcr.max_iterations = 10000;
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
auto orig_rot = item.rotation();
auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
item.rotation(orig_rot + rot);
auto bb = item.boundingBox();
return std::sqrt(bb.height()*bb.width());
}, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
item.rotation(orig_rot);
return std::get<0>(result.optimum);
}
template<class TIter> inline void __arrange(TIter from, TIter to)
template<class TIter> inline void __execute(TIter from, TIter to)
{
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
item.addOffset(static_cast<Unit>(std::ceil(min_obj_distance_/2.0)));
});
if(use_min_bb_rotation_)
std::for_each(from, to, [this](Item& item){
Radians rot = findBestRotation(item);
item.rotate(rot);
});
selector_.template packItems<PlacementStrategy>(
from, to, bin_, pconfig_);

View File

@ -67,11 +67,11 @@ class metaloop {
// need to wrap that in a type (see metaloop::Int).
/*
* A helper alias to create integer values wrapped as a type. It is nessecary
* A helper alias to create integer values wrapped as a type. It is necessary
* 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.
* partially specialized in a way that is necessary for this trick.
*/
template<int N> using Int = std::integral_constant<int, N>;
@ -88,7 +88,7 @@ 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.
// specify this second parameter type as auto in the lambda parameter list.
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
template<class T> void operator ()(T&& pack_element) {
@ -146,7 +146,7 @@ public:
* 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.
* functional meta-programming pattern.
*/
template<class...Args>
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;

View File

@ -102,6 +102,9 @@ struct StopCriteria {
/// If the relative value difference between two scores.
double relative_score_difference = std::nan("");
/// Stop if this value or better is found.
double stop_score = std::nan("");
unsigned max_iterations = 0;
};

View File

@ -142,10 +142,12 @@ protected:
default: ;
}
auto abs_diff = stopcr_.absolute_score_difference;
auto rel_diff = stopcr_.relative_score_difference;
double abs_diff = stopcr_.absolute_score_difference;
double rel_diff = stopcr_.relative_score_difference;
double stopval = stopcr_.stop_score;
if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff);
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff);
if(!std::isnan(stopval)) opt_.set_stopval(stopval);
if(this->stopcr_.max_iterations > 0)
opt_.set_maxeval(this->stopcr_.max_iterations );

View File

@ -5,11 +5,26 @@
#include "placer_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace placers {
template<class T, class = T> struct Epsilon {};
template<class T>
struct Epsilon<T, enable_if_t<std::is_integral<T>::value, T> > {
static const T Value = 1;
};
template<class T>
struct Epsilon<T, enable_if_t<std::is_floating_point<T>::value, T> > {
static const T Value = 1e-3;
};
template<class RawShape>
struct BLConfig {
TCoord<TPoint<RawShape>> min_obj_distance = 0;
DECLARE_MAIN_TYPES(RawShape);
Coord min_obj_distance = 0;
Coord epsilon = Epsilon<Coord>::Value;
bool allow_rotations = false;
};
@ -27,9 +42,13 @@ public:
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
PackResult trypack(Item& item) {
template<class Range = ConstItemRange<typename Base::DefaultIter>>
PackResult trypack(Item& item,
const Range& = Range())
{
auto r = _trypack(item);
if(!r && Base::config_.allow_rotations) {
item.rotate(Degrees(90));
r =_trypack(item);
}
@ -65,20 +84,21 @@ protected:
setInitialPosition(item);
Unit d = availableSpaceDown(item);
bool can_move = d > 1 /*std::numeric_limits<Unit>::epsilon()*/;
auto eps = config_.epsilon;
bool can_move = d > eps;
bool can_be_packed = can_move;
bool left = true;
while(can_move) {
if(left) { // write previous down move and go down
item.translate({0, -d+1});
item.translate({0, -d+eps});
d = availableSpaceLeft(item);
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
can_move = d > eps;
left = false;
} else { // write previous left move and go down
item.translate({-d+1, 0});
item.translate({-d+eps, 0});
d = availableSpaceDown(item);
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
can_move = d > eps;
left = true;
}
}
@ -112,10 +132,10 @@ protected:
const RawShape& scanpoly)
{
auto tsh = other.transformedShape();
return ( ShapeLike::intersects(tsh, scanpoly) ||
ShapeLike::isInside(tsh, scanpoly) ) &&
( !ShapeLike::intersects(tsh, item.rawShape()) &&
!ShapeLike::isInside(tsh, item.rawShape()) );
return ( sl::intersects(tsh, scanpoly) ||
sl::isInside(tsh, scanpoly) ) &&
( !sl::intersects(tsh, item.rawShape()) &&
!sl::isInside(tsh, item.rawShape()) );
}
template<class C = Coord>
@ -126,25 +146,25 @@ protected:
{
auto tsh = other.transformedShape();
bool inters_scanpoly = ShapeLike::intersects(tsh, scanpoly) &&
!ShapeLike::touches(tsh, scanpoly);
bool inters_item = ShapeLike::intersects(tsh, item.rawShape()) &&
!ShapeLike::touches(tsh, item.rawShape());
bool inters_scanpoly = sl::intersects(tsh, scanpoly) &&
!sl::touches(tsh, scanpoly);
bool inters_item = sl::intersects(tsh, item.rawShape()) &&
!sl::touches(tsh, item.rawShape());
return ( inters_scanpoly ||
ShapeLike::isInside(tsh, scanpoly)) &&
sl::isInside(tsh, scanpoly)) &&
( !inters_item &&
!ShapeLike::isInside(tsh, item.rawShape())
!sl::isInside(tsh, item.rawShape())
);
}
Container itemsInTheWayOf(const Item& item, const Dir dir) {
ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) {
// Get the left or down polygon, that has the same area as the shadow
// of input item reflected to the left or downwards
auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) :
downPoly(item);
Container ret; // packed items 'in the way' of item
ItemGroup ret; // packed items 'in the way' of item
ret.reserve(items_.size());
// Predicate to find items that are 'in the way' for left (down) move
@ -173,18 +193,18 @@ protected:
if(dir == Dir::LEFT) {
getCoord = [](const Vertex& v) { return getX(v); };
availableDistance = PointLike::horizontalDistance<Vertex>;
availableDistance = pointlike::horizontalDistance<Vertex>;
availableDistanceSV = [](const Segment& s, const Vertex& v) {
auto ret = PointLike::horizontalDistance<Vertex>(v, s);
auto ret = pointlike::horizontalDistance<Vertex>(v, s);
if(ret.second) ret.first = -ret.first;
return ret;
};
}
else {
getCoord = [](const Vertex& v) { return getY(v); };
availableDistance = PointLike::verticalDistance<Vertex>;
availableDistance = pointlike::verticalDistance<Vertex>;
availableDistanceSV = [](const Segment& s, const Vertex& v) {
auto ret = PointLike::verticalDistance<Vertex>(v, s);
auto ret = pointlike::verticalDistance<Vertex>(v, s);
if(ret.second) ret.first = -ret.first;
return ret;
};
@ -214,9 +234,9 @@ protected:
assert(pleft.vertexCount() > 0);
auto trpleft = pleft.transformedShape();
auto first = ShapeLike::begin(trpleft);
auto first = sl::begin(trpleft);
auto next = first + 1;
auto endit = ShapeLike::end(trpleft);
auto endit = sl::end(trpleft);
while(next != endit) {
Segment seg(*(first++), *(next++));
@ -340,16 +360,16 @@ protected:
// reserve for all vertices plus 2 for the left horizontal wall, 2 for
// the additional vertices for maintaning min object distance
ShapeLike::reserve(rsh, finish-start+4);
sl::reserve(rsh, finish-start+4);
/*auto addOthers = [&rsh, finish, start, &item](){
for(size_t i = start+1; i < finish; i++)
ShapeLike::addVertex(rsh, item.vertex(i));
sl::addVertex(rsh, item.vertex(i));
};*/
auto reverseAddOthers = [&rsh, finish, start, &item](){
for(auto i = finish-1; i > start; i--)
ShapeLike::addVertex(rsh, item.vertex(
sl::addVertex(rsh, item.vertex(
static_cast<unsigned long>(i)));
};
@ -361,25 +381,25 @@ protected:
// Clockwise polygon construction
ShapeLike::addVertex(rsh, topleft_vertex);
sl::addVertex(rsh, topleft_vertex);
if(dir == Dir::LEFT) reverseAddOthers();
else {
ShapeLike::addVertex(rsh, getX(topleft_vertex), 0);
ShapeLike::addVertex(rsh, getX(bottomleft_vertex), 0);
sl::addVertex(rsh, getX(topleft_vertex), 0);
sl::addVertex(rsh, getX(bottomleft_vertex), 0);
}
ShapeLike::addVertex(rsh, bottomleft_vertex);
sl::addVertex(rsh, bottomleft_vertex);
if(dir == Dir::LEFT) {
ShapeLike::addVertex(rsh, 0, getY(bottomleft_vertex));
ShapeLike::addVertex(rsh, 0, getY(topleft_vertex));
sl::addVertex(rsh, 0, getY(bottomleft_vertex));
sl::addVertex(rsh, 0, getY(topleft_vertex));
}
else reverseAddOthers();
// Close the polygon
ShapeLike::addVertex(rsh, topleft_vertex);
sl::addVertex(rsh, topleft_vertex);
return rsh;
}

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,11 @@
#include "../libnest2d.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace placers {
struct EmptyConfig {};
template<class Subclass, class RawShape, class TBin,
class Cfg = EmptyConfig,
class Store = std::vector<std::reference_wrapper<_Item<RawShape>>>
>
template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig>
class PlacerBoilerplate {
mutable bool farea_valid_ = false;
mutable double farea_ = 0.0;
@ -22,25 +19,30 @@ public:
using Coord = TCoord<Vertex>;
using Unit = Coord;
using Config = Cfg;
using Container = Store;
using ItemGroup = _ItemGroup<Item>;
using DefaultIter = typename ItemGroup::const_iterator;
class PackResult {
Item *item_ptr_;
Vertex move_;
Radians rot_;
double overfit_;
friend class PlacerBoilerplate;
friend Subclass;
PackResult(Item& item):
item_ptr_(&item),
move_(item.translation()),
rot_(item.rotation()) {}
PackResult(): item_ptr_(nullptr) {}
PackResult(double overfit = 1.0):
item_ptr_(nullptr), overfit_(overfit) {}
public:
operator bool() { return item_ptr_ != nullptr; }
double overfit() const { return overfit_; }
};
using ItemGroup = const Container&;
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
{
items_.reserve(cap);
@ -56,8 +58,10 @@ public:
config_ = config;
}
bool pack(Item& item) {
auto&& r = static_cast<Subclass*>(this)->trypack(item);
template<class Range = ConstItemRange<DefaultIter>>
bool pack(Item& item,
const Range& rem = Range()) {
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
if(r) {
items_.push_back(*(r.item_ptr_));
farea_valid_ = false;
@ -79,14 +83,11 @@ public:
farea_valid_ = false;
}
inline ItemGroup getItems() const { return items_; }
inline const ItemGroup& getItems() const { return items_; }
inline void clearItems() {
items_.clear();
farea_valid_ = false;
#ifndef NDEBUG
debug_items_.clear();
#endif
}
inline double filledArea() const {
@ -103,14 +104,10 @@ public:
return farea_;
}
#ifndef NDEBUG
std::vector<Item> debug_items_;
#endif
protected:
BinType bin_;
Container items_;
ItemGroup items_;
Cfg config_;
};
@ -121,6 +118,7 @@ using Base::items_; \
using Base::config_; \
public: \
using typename Base::Item; \
using typename Base::ItemGroup; \
using typename Base::BinType; \
using typename Base::Config; \
using typename Base::Vertex; \
@ -128,7 +126,6 @@ using typename Base::Segment; \
using typename Base::PackResult; \
using typename Base::Coord; \
using typename Base::Unit; \
using typename Base::Container; \
private:
}

View File

@ -0,0 +1,41 @@
#ifndef ROTFINDER_HPP
#define ROTFINDER_HPP
#include <libnest2d/libnest2d.hpp>
#include <libnest2d/optimizer.hpp>
#include <iterator>
namespace libnest2d {
template<class RawShape>
Radians findBestRotation(_Item<RawShape>& item) {
opt::StopCriteria stopcr;
stopcr.absolute_score_difference = 0.01;
stopcr.max_iterations = 10000;
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
auto orig_rot = item.rotation();
auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
item.rotation(orig_rot + rot);
auto bb = item.boundingBox();
return std::sqrt(bb.height()*bb.width());
}, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
item.rotation(orig_rot);
return std::get<0>(result.optimum);
}
template<class Iterator>
void findMinimumBoundingBoxRotations(Iterator from, Iterator to) {
using V = typename std::iterator_traits<Iterator>::value_type;
std::for_each(from, to, [](V& item){
Radians rot = findBestRotation(item);
item.rotate(rot);
});
}
}
#endif // ROTFINDER_HPP

View File

@ -8,7 +8,7 @@
#include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace selections {
/**
* Selection heuristic based on [López-Camacho]\
@ -118,7 +118,7 @@ public:
using Placer = PlacementStrategyLike<TPlacer>;
using ItemList = std::list<ItemRef>;
const double bin_area = ShapeLike::area<RawShape>(bin);
const double bin_area = sl::area(bin);
const double w = bin_area * config_.waste_increment;
const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion;
@ -227,10 +227,14 @@ public:
bool ret = false;
auto it = not_packed.begin();
auto pack = [&placer, &not_packed](ItemListIt it) {
return placer.pack(*it, rem(it, not_packed));
};
while(it != not_packed.end() && !ret &&
free_area - (item_area = it->get().area()) <= waste)
{
if(item_area <= free_area && placer.pack(*it) ) {
if(item_area <= free_area && pack(it) ) {
free_area -= item_area;
filled_area = bin_area - free_area;
ret = true;
@ -270,6 +274,11 @@ public:
auto it2 = it;
std::vector<TPair> wrong_pairs;
using std::placeholders::_1;
auto trypack = [&placer, &not_packed](ItemListIt it) {
return placer.trypack(*it, rem(it, not_packed));
};
while(it != endit && !ret &&
free_area - (item_area = it->get().area()) -
@ -278,7 +287,7 @@ public:
if(item_area + smallestPiece(it, not_packed)->get().area() >
free_area ) { it++; continue; }
auto pr = placer.trypack(*it);
auto pr = trypack(it);
// First would fit
it2 = not_packed.begin();
@ -294,14 +303,14 @@ public:
}
placer.accept(pr);
auto pr2 = placer.trypack(*it2);
auto pr2 = trypack(it2);
if(!pr2) {
placer.unpackLast(); // remove first
if(try_reverse) {
pr2 = placer.trypack(*it2);
pr2 = trypack(it2);
if(pr2) {
placer.accept(pr2);
auto pr12 = placer.trypack(*it);
auto pr12 = trypack(it);
if(pr12) {
placer.accept(pr12);
ret = true;
@ -365,6 +374,14 @@ public:
return it->get().area();
};
auto trypack = [&placer, &not_packed](ItemListIt it) {
return placer.trypack(*it, rem(it, not_packed));
};
auto pack = [&placer, &not_packed](ItemListIt it) {
return placer.pack(*it, rem(it, not_packed));
};
while (it != endit && !ret) { // drill down 1st level
// We need to determine in each iteration the largest, second
@ -394,7 +411,7 @@ public:
it++; continue;
}
auto pr = placer.trypack(*it);
auto pr = trypack(it);
// Check for free area and try to pack the 1st item...
if(!pr) { it++; continue; }
@ -420,15 +437,15 @@ public:
bool can_pack2 = false;
placer.accept(pr);
auto pr2 = placer.trypack(*it2);
auto pr2 = trypack(it2);
auto pr12 = pr;
if(!pr2) {
placer.unpackLast(); // remove first
if(try_reverse) {
pr2 = placer.trypack(*it2);
pr2 = trypack(it2);
if(pr2) {
placer.accept(pr2);
pr12 = placer.trypack(*it);
pr12 = trypack(it);
if(pr12) can_pack2 = true;
placer.unpackLast();
}
@ -463,7 +480,7 @@ public:
if(a3_sum > free_area) { it3++; continue; }
placer.accept(pr12); placer.accept(pr2);
bool can_pack3 = placer.pack(*it3);
bool can_pack3 = pack(it3);
if(!can_pack3) {
placer.unpackLast();
@ -473,16 +490,16 @@ public:
if(!can_pack3 && try_reverse) {
std::array<size_t, 3> indices = {0, 1, 2};
std::array<ItemRef, 3>
candidates = {*it, *it2, *it3};
std::array<typename ItemList::iterator, 3>
candidates = {it, it2, it3};
auto tryPack = [&placer, &candidates](
auto tryPack = [&placer, &candidates, &pack](
const decltype(indices)& idx)
{
std::array<bool, 3> packed = {false};
for(auto id : idx) packed.at(id) =
placer.pack(candidates[id]);
pack(candidates[id]);
bool check =
std::all_of(packed.begin(),
@ -536,7 +553,7 @@ public:
{ auto it = store_.begin();
while (it != store_.end()) {
Placer p(bin); p.configure(pconfig);
if(!p.pack(*it)) {
if(!p.pack(*it, rem(it, store_))) {
it = store_.erase(it);
} else it++;
}
@ -551,11 +568,7 @@ public:
{
packed_bins_[idx] = placer.getItems();
#ifndef NDEBUG
packed_bins_[idx].insert(packed_bins_[idx].end(),
placer.getDebugItems().begin(),
placer.getDebugItems().end());
#endif
// TODO here should be a spinlock
slock.lock();
acounter -= packednum;
@ -601,7 +614,7 @@ public:
while(it != not_packed.end() &&
filled_area < INITIAL_FILL_AREA)
{
if(placer.pack(*it)) {
if(placer.pack(*it, rem(it, not_packed))) {
filled_area += it->get().area();
free_area = bin_area - filled_area;
it = not_packed.erase(it);

View File

@ -3,7 +3,7 @@
#include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace selections {
template<class RawShape>
class _FillerSelection: public SelectionBoilerplate<RawShape> {
@ -56,18 +56,13 @@ public:
std::sort(store_.begin(), store_.end(), sortfunc);
// Container a = {store_[0], store_[1], store_[4], store_[5] };
//// a.insert(a.end(), store_.end()-10, store_.end());
// store_ = a;
PlacementStrategyLike<TPlacer> placer(bin);
placer.configure(pconfig);
auto it = store_.begin();
while(it != store_.end()) {
if(!placer.pack(*it)) {
if(!placer.pack(*it, {std::next(it), store_.end()})) {
if(packed_bins_.back().empty()) ++it;
// makeProgress(placer);
placer.clearItems();
packed_bins_.emplace_back();
} else {
@ -76,9 +71,6 @@ public:
}
}
// if(was_packed) {
// packed_bins_.push_back(placer.getItems());
// }
}
};

View File

@ -4,7 +4,7 @@
#include "../libnest2d.hpp"
#include "selection_boilerplate.hpp"
namespace libnest2d { namespace strategies {
namespace libnest2d { namespace selections {
template<class RawShape>
class _FirstFitSelection: public SelectionBoilerplate<RawShape> {
@ -40,6 +40,7 @@ public:
packed_bins_.clear();
std::vector<Placer> placers;
placers.reserve(last-first);
std::copy(first, last, std::back_inserter(store_));
@ -66,21 +67,25 @@ public:
}
}
for(auto& item : store_ ) {
bool was_packed = false;
while(!was_packed) {
auto it = store_.begin();
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
if((was_packed = placers[j].pack(item)))
makeProgress(placers[j], j);
while(it != store_.end()) {
bool was_packed = false;
size_t j = 0;
while(!was_packed) {
for(; j < placers.size() && !was_packed; j++) {
if((was_packed = placers[j].pack(*it, rem(it, store_) )))
makeProgress(placers[j], j);
}
if(!was_packed) {
placers.emplace_back(bin);
placers.back().configure(pconfig);
packed_bins_.emplace_back();
j = placers.size() - 1;
}
}
++it;
}
}

View File

@ -3,8 +3,7 @@
#include "../libnest2d.hpp"
namespace libnest2d {
namespace strategies {
namespace libnest2d { namespace selections {
template<class RawShape>
class SelectionBoilerplate {

View File

@ -5,6 +5,7 @@
#include "printer_parts.h"
#include <libnest2d/geometry_traits_nfp.hpp>
//#include "../tools/libnfpglue.hpp"
//#include "../tools/nfp_svgnest_glue.hpp"
std::vector<libnest2d::Item>& prusaParts() {
static std::vector<libnest2d::Item> ret;
@ -99,6 +100,43 @@ TEST(BasicFunctionality, creationAndDestruction)
}
TEST(GeometryAlgorithms, boundingCircle) {
using namespace libnest2d;
using placers::boundingCircle;
PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
Circle c = boundingCircle(p);
ASSERT_EQ(c.center().X, 0);
ASSERT_EQ(c.center().Y, 0);
ASSERT_DOUBLE_EQ(c.radius(), 10);
shapelike::translate(p, PointImpl{10, 10});
c = boundingCircle(p);
ASSERT_EQ(c.center().X, 10);
ASSERT_EQ(c.center().Y, 10);
ASSERT_DOUBLE_EQ(c.radius(), 10);
auto parts = prusaParts();
int i = 0;
for(auto& part : parts) {
c = boundingCircle(part.transformedShape());
if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl;
else for(auto v : shapelike::getContour(part.transformedShape()) ) {
auto d = pointlike::distance(v, c.center());
if(d > c.radius() ) {
auto e = std::abs( 1.0 - d/c.radius());
ASSERT_LE(e, 1e-3);
}
}
i++;
}
}
TEST(GeometryAlgorithms, Distance) {
using namespace libnest2d;
@ -107,14 +145,14 @@ TEST(GeometryAlgorithms, Distance) {
Point p2 = {10, 0};
Point p3 = {10, 10};
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p2), 10);
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p3), sqrt(200));
ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10);
ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200));
Segment seg(p1, p3);
ASSERT_DOUBLE_EQ(PointLike::distance(p2, seg), 7.0710678118654755);
ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755);
auto result = PointLike::horizontalDistance(p2, seg);
auto result = pointlike::horizontalDistance(p2, seg);
auto check = [](Coord val, Coord expected) {
if(std::is_floating_point<Coord>::value)
@ -127,11 +165,11 @@ TEST(GeometryAlgorithms, Distance) {
ASSERT_TRUE(result.second);
check(result.first, 10);
result = PointLike::verticalDistance(p2, seg);
result = pointlike::verticalDistance(p2, seg);
ASSERT_TRUE(result.second);
check(result.first, -10);
result = PointLike::verticalDistance(Point{10, 20}, seg);
result = pointlike::verticalDistance(Point{10, 20}, seg);
ASSERT_TRUE(result.second);
check(result.first, 10);
@ -139,12 +177,12 @@ TEST(GeometryAlgorithms, Distance) {
Point p4 = {80, 0};
Segment seg2 = { {0, 0}, {0, 40} };
result = PointLike::horizontalDistance(p4, seg2);
result = pointlike::horizontalDistance(p4, seg2);
ASSERT_TRUE(result.second);
check(result.first, 80);
result = PointLike::verticalDistance(p4, seg2);
result = pointlike::verticalDistance(p4, seg2);
// Point should not be related to the segment
ASSERT_FALSE(result.second);
@ -172,7 +210,7 @@ TEST(GeometryAlgorithms, Area) {
{61, 97}
};
ASSERT_TRUE(ShapeLike::area(item.transformedShape()) > 0 );
ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 );
}
TEST(GeometryAlgorithms, IsPointInsidePolygon) {
@ -182,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) {
Point p = {1, 1};
ASSERT_TRUE(rect.isPointInside(p));
ASSERT_TRUE(rect.isInside(p));
p = {11, 11};
ASSERT_FALSE(rect.isPointInside(p));
ASSERT_FALSE(rect.isInside(p));
p = {11, 12};
ASSERT_FALSE(rect.isPointInside(p));
ASSERT_FALSE(rect.isInside(p));
p = {3, 3};
ASSERT_TRUE(rect.isPointInside(p));
ASSERT_TRUE(rect.isInside(p));
}
@ -250,7 +288,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
Item leftp(placer.leftPoly(item));
ASSERT_TRUE(ShapeLike::isValid(leftp.rawShape()).first);
ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first);
ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
@ -260,7 +298,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
Item downp(placer.downPoly(item));
ASSERT_TRUE(ShapeLike::isValid(downp.rawShape()).first);
ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first);
ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
@ -297,7 +335,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight)
{20, 20} };
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
auto groups = arrange(rects.begin(), rects.end());
@ -350,7 +388,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
Coord min_obj_distance = 5;
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
min_obj_distance);
auto groups = arrange(rects.begin(), rects.end());
@ -401,7 +439,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
setX(v, getX(v)/SCALE);
rbin.setVertex(i, v);
}
out << ShapeLike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
for(Item& sh : r) {
Item tsh(sh.transformedShape());
for(unsigned i = 0; i < tsh.vertexCount(); i++) {
@ -410,7 +448,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
setX(v, getX(v)/SCALE);
tsh.setVertex(i, v);
}
out << ShapeLike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
}
out << "\n</svg>" << std::endl;
}
@ -664,7 +702,7 @@ std::vector<ItemPair> nfp_concave_testdata = {
}
};
template<NfpLevel lvl, Coord SCALE>
template<nfp::NfpLevel lvl, Coord SCALE>
void testNfp(const std::vector<ItemPair>& testdata) {
using namespace libnest2d;
@ -674,29 +712,33 @@ void testNfp(const std::vector<ItemPair>& testdata) {
auto& exportfun = exportSVG<SCALE, Box>;
auto onetest = [&](Item& orbiter, Item& stationary){
auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){
testcase++;
orbiter.translate({210*SCALE, 0});
auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(),
auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
orbiter.transformedShape());
strategies::correctNfpPosition(nfp, stationary, orbiter);
placers::correctNfpPosition(nfp, stationary, orbiter);
auto v = ShapeLike::isValid(nfp.first);
auto valid = shapelike::isValid(nfp.first);
if(!v.first) {
std::cout << v.second << std::endl;
}
/*Item infp(nfp.first);
if(!valid.first) {
std::cout << "test instance: " << testidx << " "
<< valid.second << std::endl;
std::vector<std::reference_wrapper<Item>> inp = {std::ref(infp)};
exportfun(inp, bin, testidx);
}*/
ASSERT_TRUE(v.first);
ASSERT_TRUE(valid.first);
Item infp(nfp.first);
int i = 0;
auto rorbiter = orbiter.transformedShape();
auto vo = Nfp::referenceVertex(rorbiter);
auto vo = nfp::referenceVertex(rorbiter);
ASSERT_TRUE(stationary.isInside(infp));
@ -710,7 +752,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
bool touching = Item::touches(tmp, stationary);
if(!touching) {
if(!touching || !valid.first) {
std::vector<std::reference_wrapper<Item>> inp = {
std::ref(stationary), std::ref(tmp), std::ref(infp)
};
@ -722,22 +764,24 @@ void testNfp(const std::vector<ItemPair>& testdata) {
}
};
unsigned tidx = 0;
for(auto& td : testdata) {
auto orbiter = td.orbiter;
auto stationary = td.stationary;
onetest(orbiter, stationary);
onetest(orbiter, stationary, tidx++);
}
tidx = 0;
for(auto& td : testdata) {
auto orbiter = td.stationary;
auto stationary = td.orbiter;
onetest(orbiter, stationary);
onetest(orbiter, stationary, tidx++);
}
}
}
TEST(GeometryAlgorithms, nfpConvexConvex) {
testNfp<NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
}
//TEST(GeometryAlgorithms, nfpConcaveConcave) {
@ -758,7 +802,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
Rectangle input(10, 10);
strategies::EdgeCache<PolygonImpl> ecache(input);
placers::EdgeCache<PolygonImpl> ecache(input);
auto first = *input.begin();
ASSERT_TRUE(getX(first) == getX(ecache.coords(0)));
@ -770,7 +814,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
for(int i = 0; i <= 100; i++) {
auto v = ecache.coords(i*(0.01));
ASSERT_TRUE(ShapeLike::touches(v, input.transformedShape()));
ASSERT_TRUE(shapelike::touches(v, input.transformedShape()));
}
}
@ -784,17 +828,17 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) {
rect2.translate({10, 0});
rect3.translate({25, 0});
ShapeLike::Shapes<PolygonImpl> pile;
shapelike::Shapes<PolygonImpl> pile;
pile.push_back(rect1.transformedShape());
pile.push_back(rect2.transformedShape());
auto result = Nfp::merge(pile, rect3.transformedShape());
auto result = nfp::merge(pile, rect3.transformedShape());
ASSERT_EQ(result.size(), 1);
Rectangle ref(45, 15);
ASSERT_EQ(ShapeLike::area(result.front()), ref.area());
ASSERT_EQ(shapelike::area(result.front()), ref.area());
}
int main(int argc, char **argv) {

View File

@ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
{
using Vertex = PointImpl;
namespace sl = shapelike;
NfpR ret;
@ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
// this can throw
auto nfp = libnfporb::generateNFP(pstat, porb, true);
auto &ct = ShapeLike::getContour(ret.first);
auto &ct = sl::getContour(ret.first);
ct.reserve(nfp.front().size()+1);
for(auto v : nfp.front()) {
v = scale(v, refactor);
@ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
ct.push_back(ct.front());
std::reverse(ct.begin(), ct.end());
auto &rholes = ShapeLike::holes(ret.first);
auto &rholes = sl::holes(ret.first);
for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
if(nfp[hidx].size() >= 3) {
rholes.emplace_back();
@ -110,31 +110,31 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
}
}
ret.second = Nfp::referenceVertex(ret.first);
ret.second = nfp::referenceVertex(ret.first);
} 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(sh, cother);
ret = nfp::nfpConvexOnly(sh, cother);
}
return ret;
}
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY>::operator()(
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{
return _nfp(sh, cother);//nfpConvexOnly(sh, cother);
}
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX>::operator()(
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{
return _nfp(sh, cother);
}
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE>::operator()(
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{
return _nfp(sh, cother);

View File

@ -5,22 +5,22 @@
namespace libnest2d {
using NfpR = Nfp::NfpResult<PolygonImpl>;
using NfpR = nfp::NfpResult<PolygonImpl>;
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
};
template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
};
template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
};
@ -34,7 +34,7 @@ struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
//};
template<> struct Nfp::MaxNfpLevel<PolygonImpl> {
template<> struct nfp::MaxNfpLevel<PolygonImpl> {
static const BP2D_CONSTEXPR NfpLevel value =
// NfpLevel::CONVEX_ONLY;
NfpLevel::BOTH_CONCAVE;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
#ifndef NFP_SVGNEST_GLUE_HPP
#define NFP_SVGNEST_GLUE_HPP
#include "nfp_svgnest.hpp"
#include <libnest2d/clipper_backend/clipper_backend.hpp>
namespace libnest2d {
namespace __svgnest {
//template<> struct _Tol<double> {
// static const BP2D_CONSTEXPR TCoord<PointImpl> Value = 1000000;
//};
}
namespace nfp {
using NfpR = NfpResult<PolygonImpl>;
template<> struct NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
// return nfpConvexOnly(sh, cother);
namespace sl = shapelike;
using alg = __svgnest::_alg<PolygonImpl>;
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
sl::getContour(cother), false, false);
PolygonImpl nfp_cntr;
if(!nfp_p.empty()) nfp_cntr.Contour = nfp_p.front();
return {nfp_cntr, referenceVertex(nfp_cntr)};
}
};
template<> struct NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
// return nfpConvexOnly(sh, cother);
namespace sl = shapelike;
using alg = __svgnest::_alg<PolygonImpl>;
std::cout << "Itt vagyok" << std::endl;
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
sl::getContour(cother), false, false);
PolygonImpl nfp_cntr;
nfp_cntr.Contour = nfp_p.front();
return {nfp_cntr, referenceVertex(nfp_cntr)};
}
};
template<>
struct NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
namespace sl = shapelike;
using alg = __svgnest::_alg<PolygonImpl>;
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
sl::getContour(cother), true, false);
PolygonImpl nfp_cntr;
nfp_cntr.Contour = nfp_p.front();
return {nfp_cntr, referenceVertex(nfp_cntr)};
}
};
template<> struct MaxNfpLevel<PolygonImpl> {
// static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::BOTH_CONCAVE;
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
};
}}
#endif // NFP_SVGNEST_GLUE_HPP

View File

@ -56,14 +56,14 @@ public:
auto d = static_cast<Coord>(
std::round(conf_.height*conf_.mm_in_coord_units) );
auto& contour = ShapeLike::getContour(tsh);
auto& contour = shapelike::getContour(tsh);
for(auto& v : contour) setY(v, -getY(v) + d);
auto& holes = ShapeLike::holes(tsh);
auto& holes = shapelike::holes(tsh);
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
}
currentLayer() += ShapeLike::serialize<Formats::SVG>(tsh,
currentLayer() += shapelike::serialize<Formats::SVG>(tsh,
1.0/conf_.mm_in_coord_units) + "\n";
}

View File

@ -1736,10 +1736,7 @@ namespace Slic3r {
stream << " </" << COMPONENTS_TAG << ">\n";
}
Transform3d t = Transform3d::Identity();
t.translate(Vec3d(instance->offset(0), instance->offset(1), 0.0));
t.rotate(Eigen::AngleAxisd(instance->rotation, Vec3d::UnitZ()));
t.scale(instance->scaling_factor);
Transform3d t = instance->world_matrix();
build_items.emplace_back(instance_id, t);
stream << " </" << OBJECT_TAG << ">\n";

View File

@ -665,6 +665,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write_format(file, "\n");
}
// adds tags for time estimators
if (print.config.remaining_times.value)
{
_writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag);
if (m_silent_time_estimator_enabled)
_writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag);
}
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser;
m_placeholder_parser.update_timestamp();
@ -724,7 +732,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
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.
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
// Set extruder(s) temperature before and after start G-code.

View File

@ -168,6 +168,9 @@ namespace Slic3r {
}
#endif // ENABLE_MOVE_STATS
const std::string GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag = "; NORMAL_FIRST_M73_OUTPUT_PLACEHOLDER";
const std::string GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag = "; SILENT_FIRST_M73_OUTPUT_PLACEHOLDER";
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
: _mode(mode)
{
@ -294,7 +297,15 @@ namespace Slic3r {
throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n"));
}
gcode_line += "\n";
// replaces placeholders for initial line M73 with the real lines
if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
{
sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str());
gcode_line = time_line;
}
else
gcode_line += "\n";
// add remaining time lines where needed
_parser.parse_line(gcode_line,

View File

@ -17,6 +17,9 @@ namespace Slic3r {
class GCodeTimeEstimator
{
public:
static const std::string Normal_First_M73_Output_Placeholder_Tag;
static const std::string Silent_First_M73_Output_Placeholder_Tag;
enum EMode : unsigned char
{
Normal,

View File

@ -237,7 +237,6 @@ BoundingBoxf3 Model::bounding_box() const
void Model::center_instances_around_point(const Vec2d &point)
{
// BoundingBoxf3 bb = this->bounding_box();
BoundingBoxf3 bb;
for (ModelObject *o : this->objects)
for (size_t i = 0; i < o->instances.size(); ++ i)
@ -670,22 +669,19 @@ void ModelObject::center_around_origin()
if (! v->modifier)
bb.merge(v->mesh.bounding_box());
// First align to origin on XYZ, then center it on XY.
Vec3d size = bb.size();
size(2) = 0.;
Vec3d shift3 = - bb.min - 0.5 * size;
// Unaligned vector, for the Rotation2D to work on Visual Studio 2013.
Eigen::Vector2d shift2 = to_2d(shift3);
this->translate(shift3);
this->origin_translation += shift3;
// Shift is the vector from the center of the bottom face of the bounding box to the origin
Vec3d shift = -bb.center();
shift(2) = -bb.min(2);
this->translate(shift);
this->origin_translation += shift;
if (!this->instances.empty()) {
for (ModelInstance *i : this->instances) {
// apply rotation and scaling to vector as well before translating instance,
// in order to leave final position unaltered
Eigen::Rotation2Dd rot(i->rotation);
i->offset -= rot * shift2 * i->scaling_factor;
Vec3d i_shift = i->world_matrix(true) * shift;
i->offset -= to_2d(i_shift);
}
this->invalidate_bounding_box();
}
@ -861,12 +857,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_
{
for (ModelInstance* inst : this->instances)
{
Transform3d m = Transform3d::Identity();
m.translate(Vec3d(inst->offset(0), inst->offset(1), 0.0));
m.rotate(Eigen::AngleAxisd(inst->rotation, Vec3d::UnitZ()));
m.scale(inst->scaling_factor);
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(m);
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(inst->world_matrix());
if (print_volume.contains(bb))
inst->print_volume_state = ModelInstance::PVS_Inside;
@ -995,26 +986,17 @@ size_t ModelVolume::split(unsigned int max_extruders)
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{
mesh->rotate_z(this->rotation); // rotate around mesh origin
mesh->scale(this->scaling_factor); // scale around mesh origin
if (!dont_translate)
mesh->translate(this->offset(0), this->offset(1), 0);
mesh->transform(world_matrix(dont_translate).cast<float>());
}
BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const
{
// Rotate around mesh origin.
double c = cos(this->rotation);
double s = sin(this->rotation);
BoundingBoxf3 bbox;
for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) {
const stl_facet &facet = mesh->stl.facet_start[i];
for (int j = 0; j < 3; ++ j) {
const stl_vertex &v = facet.vertex[j];
bbox.merge(Vec3d(c * v(0) - s * v(1), s * v(0) + c * v(1), v(2)));
}
}
if (! empty(bbox)) {
TriangleMesh copy(*mesh);
copy.transform(world_matrix(true, false, true).cast<float>());
BoundingBoxf3 bbox = copy.bounding_box();
if (!empty(bbox)) {
// Scale the bounding box uniformly.
if (std::abs(this->scaling_factor - 1.) > EPSILON) {
bbox.min *= this->scaling_factor;
@ -1031,13 +1013,12 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
{
Transform3d matrix = Transform3d::Identity();
if (!dont_translate)
matrix.translate(Vec3d(offset(0), offset(1), 0.0));
return bbox.transformed(world_matrix(dont_translate));
}
matrix.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
matrix.scale(scaling_factor);
return bbox.transformed(matrix);
Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const
{
return world_matrix(dont_translate) * v;
}
void ModelInstance::transform_polygon(Polygon* polygon) const
@ -1046,4 +1027,20 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
polygon->scale(this->scaling_factor); // scale around polygon origin
}
Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale) const
{
Transform3d m = Transform3d::Identity();
if (!dont_translate)
m.translate(Vec3d(offset(0), offset(1), 0.0));
if (!dont_rotate)
m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
if (!dont_scale)
m.scale(scaling_factor);
return m;
}
}

View File

@ -224,7 +224,6 @@ public:
friend class ModelObject;
// Transform3d transform;
double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor;
Vec2d offset; // in unscaled coordinates
@ -240,9 +239,13 @@ public:
BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const;
// Transform an external bounding box.
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
// Transform an external vector.
Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const;
// To be called on an external polygon. It does not translate the polygon, only rotates and scales.
void transform_polygon(Polygon* polygon) const;
Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const;
bool is_printable() const { return print_volume_state == PVS_Inside; }
private:

View File

@ -99,54 +99,55 @@ namespace bgi = boost::geometry::index;
using SpatElement = std::pair<Box, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
template<class TBin>
using TPacker = typename placers::_NofitPolyPlacer<PolygonImpl, TBin>;
const double BIG_ITEM_TRESHOLD = 0.02;
Box boundingBox(const Box& pilebb, const Box& ibb ) {
auto& pminc = pilebb.minCorner();
auto& pmaxc = pilebb.maxCorner();
auto& iminc = ibb.minCorner();
auto& imaxc = ibb.maxCorner();
PointImpl minc, maxc;
setX(minc, std::min(getX(pminc), getX(iminc)));
setY(minc, std::min(getY(pminc), getY(iminc)));
setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
return Box(minc, maxc);
}
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 shapelike::Shapes<PolygonImpl>& merged_pile,
const Box& pilebb,
const ItemGroup& items,
const Item &item,
double bin_area,
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
const SpatIndex& spatindex,
const ItemGroup& remaining
)
{
using pl = PointLike;
using sl = ShapeLike;
using Coord = TCoord<PointImpl>;
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++;
}
auto isBig = [bin_area](double a) {
return a/bin_area > BIG_ITEM_TRESHOLD ;
};
// Candidate item bounding box
auto ibb = item.boundingBox();
auto ibb = sl::boundingBox(item.transformedShape());
// 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();
auto fullbb = boundingBox(pilebb, ibb);
// The bounding box of the big items (they will accumulate in the center
// of the pile
@ -157,19 +158,11 @@ objfunc(const PointImpl& bincenter,
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) {
if(isBig(item.area())) {
// 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
@ -192,46 +185,62 @@ objfunc(const PointImpl& bincenter,
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;
double density = 0;
// 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();
if(remaining.empty()) {
auto& trsh = item.transformedShape();
auto mp = merged_pile;
mp.emplace_back(item.transformedShape());
auto chull = sl::convexHull(mp);
auto querybb = item.boundingBox();
placers::EdgeCache<PolygonImpl> ec(chull);
// Query the spatial index for the neigbours
std::vector<SpatElement> result;
spatindex.query(bgi::intersects(querybb), std::back_inserter(result));
double circ = ec.circumference() / norm;
double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
score = 0.5*circ + 0.5*bcirc;
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;
} else {
// Prepare a variable for the alignment score.
// This will indicate: how well is the candidate item aligned with
// its neighbors. We will check the alignment 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 = 1.0;
if(ascore < alignment_score) alignment_score = ascore;
density = (fullbb.width()*fullbb.height()) / (norm*norm);
auto querybb = item.boundingBox();
// Query the spatial index for the neighbors
std::vector<SpatElement> result;
result.reserve(spatindex.size());
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;
Item& p = items[idx];
auto parea = p.area();
if(std::abs(1.0 - parea/item.area()) < 1e-6) {
auto bb = boundingBox(p.boundingBox(), ibb);
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 neighbors
if(result.empty())
score = 0.5 * dist + 0.5 * density;
else
score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
}
// 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
} else if( !isBig(item.area()) && spatindex.empty()) {
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
score = ROUNDNESS_RATIO * bindist + DENSITY_RATIO * density;
// Bindist is surprisingly enough...
score = bindist;
} else {
// Here there are the small items that should be placed around the
// already processed bigger items.
@ -259,7 +268,9 @@ void fillConfig(PConf& pcfg) {
// The accuracy of optimization.
// Goes from 0.0 to 1.0 and scales performance as well
pcfg.accuracy = 0.6f;
pcfg.accuracy = 0.65f;
pcfg.parallel = true;
}
template<class TBin>
@ -268,31 +279,62 @@ class AutoArranger {};
template<class TBin>
class _ArrBase {
protected:
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
using Placer = TPacker<TBin>;
using Selector = FirstFitSelection;
using Packer = Arranger<Placer, Selector>;
using Packer = Nester<Placer, Selector>;
using PConfig = typename Packer::PlacementConfig;
using Distance = TCoord<PointImpl>;
using Pile = ShapeLike::Shapes<PolygonImpl>;
using Pile = sl::Shapes<PolygonImpl>;
Packer pck_;
PConfig pconf_; // Placement configuration
double bin_area_;
std::vector<double> areacache_;
SpatIndex rtree_;
double norm_;
Pile merged_pile_;
Box pilebb_;
ItemGroup remaining_;
ItemGroup items_;
public:
_ArrBase(const TBin& bin, Distance dist,
std::function<void(unsigned)> progressind):
pck_(bin, dist), bin_area_(ShapeLike::area<PolygonImpl>(bin))
pck_(bin, dist), bin_area_(sl::area(bin)),
norm_(std::sqrt(sl::area(bin)))
{
fillConfig(pconf_);
pconf_.before_packing =
[this](const Pile& merged_pile, // merged pile
const ItemGroup& items, // packed items
const ItemGroup& remaining) // future items to be packed
{
items_ = items;
merged_pile_ = merged_pile;
remaining_ = remaining;
pilebb_ = sl::boundingBox(merged_pile);
rtree_.clear();
// We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) {
return a/bin_area_ > BIG_ITEM_TRESHOLD ;
};
for(unsigned idx = 0; idx < items.size(); ++idx) {
Item& itm = items[idx];
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
}
};
pck_.progressIndicator(progressind);
}
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
areacache_.clear();
return pck_.arrangeIndexed(std::forward<Args>(args)...);
rtree_.clear();
return pck_.executeIndexed(std::forward<Args>(args)...);
}
};
@ -304,22 +346,69 @@ public:
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_);
pconf_.object_function = [this, bin] (const Item &item) {
auto result = objfunc(bin.center(),
merged_pile_,
pilebb_,
items_,
item,
bin_area_,
norm_,
rtree_,
remaining_);
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;
double miss = Placer::overfit(fullbb, bin);
miss = miss > 0? miss : 0;
score += miss*miss;
return score;
};
pck_.configure(pconf_);
}
};
using lnCircle = libnest2d::_Circle<libnest2d::PointImpl>;
template<>
class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
public:
AutoArranger(const lnCircle& bin, Distance dist,
std::function<void(unsigned)> progressind):
_ArrBase<lnCircle>(bin, dist, progressind) {
pconf_.object_function = [this, &bin] (const Item &item) {
auto result = objfunc(bin.center(),
merged_pile_,
pilebb_,
items_,
item,
bin_area_,
norm_,
rtree_,
remaining_);
double score = std::get<0>(result);
auto isBig = [this](const Item& itm) {
return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ;
};
if(isBig(item)) {
auto mp = merged_pile_;
mp.push_back(item.transformedShape());
auto chull = sl::convexHull(mp);
double miss = Placer::overfit(chull, bin);
if(miss < 0) miss = 0;
score += miss*miss;
}
return score;
};
@ -335,27 +424,20 @@ public:
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*/) {
pconf_.object_function = [this, &bin] (const Item &item) {
auto binbb = ShapeLike::boundingBox(bin);
auto result = objfunc(binbb.center(), bin_area_, pile,
pile_area, item, norm, areacache_, rtree_);
auto binbb = sl::boundingBox(bin);
auto result = objfunc(binbb.center(),
merged_pile_,
pilebb_,
items_,
item,
bin_area_,
norm_,
rtree_,
remaining_);
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;
};
@ -370,15 +452,17 @@ 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*/) {
this->pconf_.object_function = [this] (const Item &item) {
auto result = objfunc({0, 0}, 0, pile, pile_area,
item, norm, areacache_, rtree_);
auto result = objfunc({0, 0},
merged_pile_,
pilebb_,
items_,
item,
0,
norm_,
rtree_,
remaining_);
return std::get<0>(result);
};
@ -440,16 +524,113 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
return ret;
}
enum BedShapeHint {
class Circle {
Point center_;
double radius_;
public:
inline Circle(): center_(0, 0), radius_(std::nan("")) {}
inline Circle(const Point& c, double r): center_(c), radius_(r) {}
inline double radius() const { return radius_; }
inline const Point& center() const { return center_; }
inline operator bool() { return !std::isnan(radius_); }
inline operator lnCircle() {
return lnCircle({center_(0), center_(1)}, radius_);
}
};
enum class BedShapeType {
BOX,
CIRCLE,
IRREGULAR,
WHO_KNOWS
};
BedShapeHint bedShape(const Slic3r::Polyline& /*bed*/) {
struct BedShapeHint {
BedShapeType type;
/*union*/ struct { // I know but who cares...
Circle circ;
BoundingBox box;
Polyline polygon;
} shape;
};
BedShapeHint bedShape(const Polyline& bed) {
BedShapeHint ret;
auto x = [](const Point& p) { return p(0); };
auto y = [](const Point& p) { return p(1); };
auto width = [x](const BoundingBox& box) {
return x(box.max) - x(box.min);
};
auto height = [y](const BoundingBox& box) {
return y(box.max) - y(box.min);
};
auto area = [&width, &height](const BoundingBox& box) {
double w = width(box);
double h = height(box);
return w*h;
};
auto poly_area = [](Polyline p) {
Polygon pp; pp.points.reserve(p.points.size() + 1);
pp.points = std::move(p.points);
pp.points.emplace_back(pp.points.front());
return std::abs(pp.area());
};
auto distance_to = [x, y](const Point& p1, const Point& p2) {
double dx = x(p2) - x(p1);
double dy = y(p2) - y(p1);
return std::sqrt(dx*dx + dy*dy);
};
auto bb = bed.bounding_box();
auto isCircle = [bb, distance_to](const Polyline& polygon) {
auto center = bb.center();
std::vector<double> vertex_distances;
double avg_dist = 0;
for (auto pt: polygon.points)
{
double distance = distance_to(center, pt);
vertex_distances.push_back(distance);
avg_dist += distance;
}
avg_dist /= vertex_distances.size();
Circle ret(center, avg_dist);
for (auto el: vertex_distances)
{
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
ret = Circle();
break;
}
return ret;
};
auto parea = poly_area(bed);
if( (1.0 - parea/area(bb)) < 1e-3 ) {
ret.type = BedShapeType::BOX;
ret.shape.box = bb;
}
else if(auto c = isCircle(bed)) {
ret.type = BedShapeType::CIRCLE;
ret.shape.circ = c;
} else {
ret.type = BedShapeType::IRREGULAR;
ret.shape.polygon = bed;
}
// Determine the bed shape by hand
return BOX;
return ret;
}
void applyResult(
@ -468,8 +649,7 @@ void applyResult(
// appropriately
auto off = item.translation();
Radians rot = item.rotation();
Vec2d foff(off.X*SCALING_FACTOR + batch_offset,
off.Y*SCALING_FACTOR);
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
// write the tranformation data into the model instance
inst_ptr->rotation = rot;
@ -525,7 +705,10 @@ bool arrange(Model &model, coordf_t min_obj_distance,
});
IndexedPackGroup result;
BoundingBox bbb(bed.points);
if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
BoundingBox bbb(bed);
auto binbb = Box({
static_cast<libnest2d::Coord>(bbb.min(0)),
@ -536,8 +719,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
static_cast<libnest2d::Coord>(bbb.max(1))
});
switch(bedhint) {
case BOX: {
switch(bedhint.type) {
case BedShapeType::BOX: {
// Create the arranger for the box shaped bed
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
@ -547,16 +730,22 @@ bool arrange(Model &model, coordf_t min_obj_distance,
result = arrange(shapes.begin(), shapes.end());
break;
}
case CIRCLE:
case BedShapeType::CIRCLE: {
auto c = bedhint.shape.circ;
auto cc = lnCircle(c);
AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind);
result = arrange(shapes.begin(), shapes.end());
break;
case IRREGULAR:
case WHO_KNOWS: {
}
case BedShapeType::IRREGULAR:
case BedShapeType::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;
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
@ -567,6 +756,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
}
};
if(result.empty()) return false;
if(first_bin_only) {
applyResult(result.front(), 0, shapemap);
} else {

View File

@ -13,7 +13,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
#include "slic3r/IProgressIndicator.hpp"
#include "slic3r/ProgressIndicator.hpp"
#include "PrintExport.hpp"
//! macro used to mark string used at localization,

View File

@ -227,8 +227,8 @@ private:
typedef std::vector<PrintObject*> PrintObjectPtrs;
typedef std::vector<PrintRegion*> PrintRegionPtrs;
class IProgressIndicator;
using ProgressIndicatorPtr = std::shared_ptr<IProgressIndicator>;
class ProgressIndicator;
using ProgressIndicatorPtr = std::shared_ptr<ProgressIndicator>;
// The complete print tray with possibly multiple objects.
class Print

View File

@ -264,7 +264,7 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis)
Vec3f axis_norm = axis.cast<float>().normalized();
Transform3f m = Transform3f::Identity();
m.rotate(Eigen::AngleAxisf(angle, axis_norm));
stl_transform(&stl, (float*)m.data());
stl_transform(&stl, m);
}
void TriangleMesh::mirror(const Axis &axis)
@ -279,6 +279,11 @@ void TriangleMesh::mirror(const Axis &axis)
stl_invalidate_shared_vertices(&this->stl);
}
void TriangleMesh::transform(const Transform3f& t)
{
stl_transform(&stl, t);
}
void TriangleMesh::align_to_origin()
{
this->translate(
@ -523,9 +528,9 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const
src_vertices(0, v_id) = (double)facet_ptr->vertex[i](0);
src_vertices(1, v_id) = (double)facet_ptr->vertex[i](1);
src_vertices(2, v_id) = (double)facet_ptr->vertex[i](2);
++v_id;
}
facet_ptr += 1;
++v_id;
}
}

View File

@ -48,6 +48,7 @@ public:
void mirror_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
void transform(const Transform3f& t);
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split() const;

View File

@ -97,8 +97,9 @@ public:
void call(int i) const;
void call(int i, int j) const;
void call(const std::vector<int>& ints) const;
void call(double d) const;
void call(double a) const;
void call(double a, double b) const;
void call(double a, double b, double c) const;
void call(double a, double b, double c, double d) const;
void call(bool b) const;
private:

View File

@ -14,7 +14,7 @@
#include <boost/thread.hpp>
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
#define SLIC3R_VERSION "1.41.0-beta2"
#define SLIC3R_VERSION "1.41.0"
#define SLIC3R_BUILD "UNKNOWN"
typedef int32_t coord_t;

View File

@ -251,7 +251,7 @@ void PerlCallback::call(const std::vector<int>& ints) const
LEAVE;
}
void PerlCallback::call(double d) const
void PerlCallback::call(double a) const
{
if (!m_callback)
return;
@ -259,7 +259,7 @@ void PerlCallback::call(double d) const
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(d)));
XPUSHs(sv_2mortal(newSVnv(a)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
@ -282,6 +282,23 @@ void PerlCallback::call(double a, double b) const
LEAVE;
}
void PerlCallback::call(double a, double b, double c) const
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
XPUSHs(sv_2mortal(newSVnv(b)));
XPUSHs(sv_2mortal(newSVnv(c)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(double a, double b, double c, double d) const
{
if (!m_callback)

View File

@ -62,6 +62,7 @@ REGISTER_CLASS(Preset, "GUI::Preset");
REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
REGISTER_CLASS(TabIface, "GUI::Tab");
REGISTER_CLASS(ProgressStatusBar, "GUI::ProgressStatusBar");
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
REGISTER_CLASS(AppController, "AppController");
REGISTER_CLASS(PrintController, "PrintController");

View File

@ -401,7 +401,7 @@ void PrintController::slice_to_png()
// });
}
void IProgressIndicator::message_fmt(
void ProgressIndicator::message_fmt(
const string &fmtstr, ...) {
std::stringstream ss;
va_list args;
@ -433,69 +433,68 @@ const PrintConfig &PrintController::config() const
return print_->config;
}
void AppController::arrange_model()
{
auto ftr = std::async(
supports_asynch()? std::launch::async : std::launch::deferred,
[this]()
{
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
unsigned count = 0;
for(auto obj : model_->objects) count += obj->instances.size();
unsigned count = 0;
for(auto obj : model_->objects) count += obj->instances.size();
auto pind = global_progress_indicator();
auto pind = global_progress_indicator();
float pmax = 1.0;
float pmax = 1.0;
if(pind) {
pmax = pind->max();
if(pind) {
pmax = pind->max();
// Set the range of the progress to the object count
pind->max(count);
// Set the range of the progress to the object count
pind->max(count);
}
pind->on_cancel([](){
std::cout << "Cannot be cancelled!" << std::endl;
});
}
auto dist = print_ctl()->config().min_object_distance();
auto dist = print_ctl()->config().min_object_distance();
// Create the arranger config
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
// Create the arranger config
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
auto& bedpoints = print_ctl()->config().bed_shape.values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints)
bed.append(Point::new_scale(v(0), v(1)));
auto& bedpoints = print_ctl()->config().bed_shape.values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints)
bed.append(Point::new_scale(v(0), v(1)));
if(pind) pind->update(0, _(L("Arranging objects...")));
if(pind) pind->update(0, _(L("Arranging objects...")));
try {
arr::arrange(*model_,
min_obj_distance,
bed,
arr::BOX,
false, // create many piles not just one pile
[pind, count](unsigned rem) {
if(pind)
pind->update(count - rem, _(L("Arranging objects...")));
});
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
report_issue(IssueType::ERR,
_(L("Could not arrange model objects! "
"Some geometries may be invalid.")),
_(L("Exception occurred")));
}
try {
arr::BedShapeHint hint;
// TODO: from Sasha from GUI
hint.type = arr::BedShapeType::WHO_KNOWS;
// Restore previous max value
if(pind) {
pind->max(pmax);
pind->update(0, _(L("Arranging done.")));
}
});
arr::arrange(*model_,
min_obj_distance,
bed,
hint,
false, // create many piles not just one pile
[pind, count](unsigned rem) {
if(pind)
pind->update(count - rem, _(L("Arranging objects...")));
});
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
report_issue(IssueType::ERR,
_(L("Could not arrange model objects! "
"Some geometries may be invalid.")),
_(L("Exception occurred")));
}
while( ftr.wait_for(std::chrono::milliseconds(10))
!= std::future_status::ready) {
process_events();
// Restore previous max value
if(pind) {
pind->max(pmax);
pind->update(0, _(L("Arranging done.")));
pind->on_cancel(/*remove cancel function*/);
}
}

View File

@ -7,7 +7,7 @@
#include <atomic>
#include <iostream>
#include "IProgressIndicator.hpp"
#include "ProgressIndicator.hpp"
namespace Slic3r {
@ -15,6 +15,7 @@ class Model;
class Print;
class PrintObject;
class PrintConfig;
class ProgressStatusBar;
/**
* @brief A boilerplate class for creating application logic. It should provide
@ -32,7 +33,7 @@ class AppControllerBoilerplate {
public:
/// A Progress indicator object smart pointer
using ProgresIndicatorPtr = std::shared_ptr<IProgressIndicator>;
using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
private:
class PriData; // Some structure to store progress indication data
@ -278,8 +279,7 @@ public:
* @param gauge_id The ID of the gague widget of the status bar.
* @param statusbar_id The ID of the status bar.
*/
void set_global_progress_indicator(unsigned gauge_id,
unsigned statusbar_id);
void set_global_progress_indicator(ProgressStatusBar *prs);
void arrange_model();
};

View File

@ -4,7 +4,7 @@
#include <future>
#include <slic3r/GUI/GUI.hpp>
#include <slic3r/GUI/PngExportDialog.hpp>
#include <slic3r/GUI/ProgressStatusBar.hpp>
#include <wx/app.h>
#include <wx/filedlg.h>
@ -105,10 +105,10 @@ namespace {
* the main thread as well.
*/
class GuiProgressIndicator:
public IProgressIndicator, public wxEvtHandler {
public ProgressIndicator, public wxEvtHandler {
wxProgressDialog gauge_;
using Base = IProgressIndicator;
using Base = ProgressIndicator;
wxString message_;
int range_; wxString title_;
bool is_asynch_ = false;
@ -154,7 +154,7 @@ public:
virtual void cancel() override {
update(max(), "Abort");
IProgressIndicator::cancel();
ProgressIndicator::cancel();
}
virtual void state(float val) override {
@ -211,31 +211,21 @@ AppControllerBoilerplate::create_progress_indicator(unsigned statenum,
namespace {
// A wrapper progress indicator class around the statusbar created in perl.
class Wrapper: public IProgressIndicator, public wxEvtHandler {
wxGauge *gauge_;
wxStatusBar *stbar_;
using Base = IProgressIndicator;
class Wrapper: public ProgressIndicator, public wxEvtHandler {
ProgressStatusBar *sbar_;
using Base = ProgressIndicator;
std::string message_;
AppControllerBoilerplate& ctl_;
void showProgress(bool show = true) {
gauge_->Show(show);
sbar_->show_progress(show);
}
void _state(unsigned st) {
if( st <= IProgressIndicator::max() ) {
if( st <= ProgressIndicator::max() ) {
Base::state(st);
if(!gauge_->IsShown()) showProgress(true);
stbar_->SetStatusText(message_);
if(static_cast<long>(st) == gauge_->GetRange()) {
gauge_->SetValue(0);
showProgress(false);
} else {
gauge_->SetValue(static_cast<int>(st));
}
sbar_->set_status_text(message_);
sbar_->set_progress(st);
}
}
@ -248,12 +238,12 @@ class Wrapper: public IProgressIndicator, public wxEvtHandler {
public:
inline Wrapper(wxGauge *gauge, wxStatusBar *stbar,
inline Wrapper(ProgressStatusBar *sbar,
AppControllerBoilerplate& ctl):
gauge_(gauge), stbar_(stbar), ctl_(ctl)
sbar_(sbar), ctl_(ctl)
{
Base::max(static_cast<float>(gauge->GetRange()));
Base::states(static_cast<unsigned>(gauge->GetRange()));
Base::max(static_cast<float>(sbar_->get_range()));
Base::states(static_cast<unsigned>(sbar_->get_range()));
Bind(PROGRESS_STATUS_UPDATE_EVENT,
&Wrapper::_state,
@ -266,8 +256,8 @@ public:
virtual void max(float val) override {
if(val > 1.0) {
gauge_->SetRange(static_cast<int>(val));
IProgressIndicator::max(val);
sbar_->set_range(static_cast<int>(val));
ProgressIndicator::max(val);
}
}
@ -294,135 +284,19 @@ public:
virtual void title(const string & /*title*/) override {}
virtual void on_cancel(CancelFn fn) override {
sbar_->set_cancel_callback(fn);
Base::on_cancel(fn);
}
};
}
void AppController::set_global_progress_indicator(
unsigned gid,
unsigned sid)
void AppController::set_global_progress_indicator(ProgressStatusBar *prsb)
{
wxGauge* gauge = dynamic_cast<wxGauge*>(wxWindow::FindWindowById(gid));
wxStatusBar* sb = dynamic_cast<wxStatusBar*>(wxWindow::FindWindowById(sid));
if(gauge && sb) {
global_progress_indicator(std::make_shared<Wrapper>(gauge, sb, *this));
if(prsb) {
global_progress_indicator(std::make_shared<Wrapper>(prsb, *this));
}
}
//PrintController::PngExportData PrintController::collect_png_export_data()
//{
// // Implement the logic of the PngExportDialog
// class PngExportView: public PngExportDialog {
// double ratio_, bs_ratio_;
// PrintController& ctl_;
// public:
// PngExportView(PrintController& ctl):
// PngExportDialog(wxTheApp->GetTopWindow()), ctl_(ctl)
// {
// ratio_ = double(spin_reso_width_->GetValue()) /
// spin_reso_height_->GetValue();
// bs_ratio_ = bed_width_spin_->GetValue() /
// bed_height_spin_->GetValue();
// }
// PngExportData export_data() const {
// PrintController::PngExportData ret;
// ret.zippath = filepick_ctl_->GetPath();
// ret.width_px = spin_reso_width_->GetValue();
// ret.height_px = spin_reso_height_->GetValue();
// ret.width_mm = bed_width_spin_->GetValue();
// ret.height_mm = bed_height_spin_->GetValue();
// ret.exp_time_s = exptime_spin_->GetValue();
// ret.exp_time_first_s = exptime_first_spin_->GetValue();
// ret.corr_x = corr_spin_x_->GetValue();
// ret.corr_y = corr_spin_y_->GetValue();
// ret.corr_z = corr_spin_z_->GetValue();
// return ret;
// }
// void prefill(const PngExportData& data) {
// filepick_ctl_->SetPath(data.zippath);
// spin_reso_width_->SetValue(data.width_px);
// spin_reso_height_->SetValue(data.height_px);
// bed_width_spin_->SetValue(data.width_mm);
// bed_height_spin_->SetValue(data.height_mm);
// exptime_spin_->SetValue(data.exp_time_s);
// exptime_first_spin_->SetValue(data.exp_time_first_s);
// corr_spin_x_->SetValue(data.corr_x);
// corr_spin_y_->SetValue(data.corr_y);
// corr_spin_z_->SetValue(data.corr_z);
// if(data.zippath.empty()) export_btn_->Disable();
// else export_btn_->Enable();
// }
// virtual void ResoLock( wxCommandEvent& /*event*/ ) override {
// ratio_ = double(spin_reso_width_->GetValue()) /
// double(spin_reso_height_->GetValue());
// }
// virtual void BedsizeLock( wxCommandEvent& /*event*/ ) override {
// bs_ratio_ = bed_width_spin_->GetValue() /
// bed_height_spin_->GetValue();
// }
// virtual void EvalResoSpin( wxCommandEvent& event ) override {
// if(reso_lock_btn_->GetValue()) {
// if(event.GetId() == spin_reso_width_->GetId()) {
// spin_reso_height_->SetValue(
// std::round(spin_reso_width_->GetValue()/ratio_));
// spin_reso_height_->Update();
// } else {
// spin_reso_width_->SetValue(
// std::round(spin_reso_height_->GetValue()*ratio_));
// spin_reso_width_->Update();
// }
// }
// }
// virtual void EvalBedSpin( wxCommandEvent& event ) override {
// if(bedsize_lock_btn_->GetValue()) {
// if(event.GetId() == bed_width_spin_->GetId()) {
// bed_height_spin_->SetValue(
// std::round(bed_width_spin_->GetValue()/bs_ratio_));
// bed_height_spin_->Update();
// } else {
// bed_width_spin_->SetValue(
// std::round(bed_height_spin_->GetValue()*bs_ratio_));
// bed_width_spin_->Update();
// }
// }
// }
// virtual void onFileChanged( wxFileDirPickerEvent& event ) {
// if(filepick_ctl_->GetPath().IsEmpty()) export_btn_->Disable();
// else export_btn_->Enable();
// }
// virtual void Close( wxCommandEvent& /*event*/ ) {
// auto ret = wxID_OK;
// if(wxFileName(filepick_ctl_->GetPath()).Exists())
// if(!ctl_.report_issue(PrintController::IssueType::WARN_Q,
// _(L("File already exists. Overwrite?")),
// _(L("Warning")))) ret = wxID_CANCEL;
// EndModal(ret);
// }
// };
// PngExportView exdlg(*this);
// exdlg.prefill(prev_expdata_);
// auto r = exdlg.ShowModal();
// auto ret = exdlg.export_data();
// prev_expdata_ = ret;
// if(r != wxID_OK) ret.zippath.clear();
// return ret;
//}
}

View File

@ -195,9 +195,9 @@ const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
GLVolume::GLVolume(float r, float g, float b, float a)
: m_origin(0, 0, 0)
, m_angle_z(0.0f)
, m_scale_factor(1.0f)
: m_offset(Vec3d::Zero())
, m_rotation(0.0)
, m_scaling_factor(1.0)
, m_world_matrix(Transform3f::Identity())
, m_world_matrix_dirty(true)
, m_transformed_bounding_box_dirty(true)
@ -255,43 +255,43 @@ void GLVolume::set_render_color()
set_render_color(color, 4);
}
const Vec3d& GLVolume::get_origin() const
double GLVolume::get_rotation()
{
return m_origin;
return m_rotation;
}
float GLVolume::get_angle_z()
void GLVolume::set_rotation(double rotation)
{
return m_angle_z;
}
void GLVolume::set_origin(const Vec3d& origin)
{
if (m_origin != origin)
if (m_rotation != rotation)
{
m_origin = origin;
m_rotation = rotation;
m_world_matrix_dirty = true;
m_transformed_bounding_box_dirty = true;
m_transformed_convex_hull_bounding_box_dirty = true;
}
}
void GLVolume::set_angle_z(float angle_z)
const Vec3d& GLVolume::get_offset() const
{
if (m_angle_z != angle_z)
return m_offset;
}
void GLVolume::set_offset(const Vec3d& offset)
{
if (m_offset != offset)
{
m_angle_z = angle_z;
m_offset = offset;
m_world_matrix_dirty = true;
m_transformed_bounding_box_dirty = true;
m_transformed_convex_hull_bounding_box_dirty = true;
}
}
void GLVolume::set_scale_factor(float scale_factor)
void GLVolume::set_scaling_factor(double factor)
{
if (m_scale_factor != scale_factor)
if (m_scaling_factor != factor)
{
m_scale_factor = scale_factor;
m_scaling_factor = factor;
m_world_matrix_dirty = true;
m_transformed_bounding_box_dirty = true;
m_transformed_convex_hull_bounding_box_dirty = true;
@ -303,14 +303,32 @@ void GLVolume::set_convex_hull(const TriangleMesh& convex_hull)
m_convex_hull = &convex_hull;
}
void GLVolume::set_select_group_id(const std::string& select_by)
{
if (select_by == "object")
select_group_id = object_idx() * 1000000;
else if (select_by == "volume")
select_group_id = object_idx() * 1000000 + volume_idx() * 1000;
else if (select_by == "instance")
select_group_id = composite_id;
}
void GLVolume::set_drag_group_id(const std::string& drag_by)
{
if (drag_by == "object")
drag_group_id = object_idx() * 1000;
else if (drag_by == "instance")
drag_group_id = object_idx() * 1000 + instance_idx();
}
const Transform3f& GLVolume::world_matrix() const
{
if (m_world_matrix_dirty)
{
m_world_matrix = Transform3f::Identity();
m_world_matrix.translate(Vec3f((float)m_origin(0), (float)m_origin(1), (float)m_origin(2)));
m_world_matrix.rotate(Eigen::AngleAxisf(m_angle_z, Vec3f::UnitZ()));
m_world_matrix.scale(m_scale_factor);
m_world_matrix.translate(m_offset.cast<float>());
m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ()));
m_world_matrix.scale((float)m_scaling_factor);
m_world_matrix_dirty = false;
}
return m_world_matrix;
@ -384,9 +402,9 @@ void GLVolume::render() const
::glCullFace(GL_BACK);
::glPushMatrix();
::glTranslated(m_origin(0), m_origin(1), m_origin(2));
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
::glTranslated(m_offset(0), m_offset(1), m_offset(2));
::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor);
if (this->indexed_vertex_array.indexed())
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
else
@ -510,9 +528,9 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
::glPushMatrix();
::glTranslated(m_origin(0), m_origin(1), m_origin(2));
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
::glTranslated(m_offset(0), m_offset(1), m_offset(2));
::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor);
if (n_triangles > 0)
{
@ -555,9 +573,9 @@ void GLVolume::render_legacy() const
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data());
::glPushMatrix();
::glTranslated(m_origin(0), m_origin(1), m_origin(2));
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
::glTranslated(m_offset(0), m_offset(1), m_offset(2));
::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor);
if (n_triangles > 0)
::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first);
@ -655,17 +673,8 @@ std::vector<int> GLVolumeCollection::load_object(
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx;
if (select_by == "object")
v.select_group_id = obj_idx * 1000000;
else if (select_by == "volume")
v.select_group_id = obj_idx * 1000000 + volume_idx * 1000;
else if (select_by == "instance")
v.select_group_id = v.composite_id;
if (drag_by == "object")
v.drag_group_id = obj_idx * 1000;
else if (drag_by == "instance")
v.drag_group_id = obj_idx * 1000 + instance_idx;
v.set_select_group_id(select_by);
v.set_drag_group_id(drag_by);
if (!model_volume->modifier)
{
v.set_convex_hull(model_volume->get_convex_hull());
@ -675,9 +684,9 @@ std::vector<int> GLVolumeCollection::load_object(
}
v.is_modifier = model_volume->modifier;
v.shader_outside_printer_detection_enabled = !model_volume->modifier;
v.set_origin(Vec3d(instance->offset(0), instance->offset(1), 0.0));
v.set_angle_z(instance->rotation);
v.set_scale_factor(instance->scaling_factor);
v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0));
v.set_rotation(instance->rotation);
v.set_scaling_factor(instance->scaling_factor);
}
}
@ -746,7 +755,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
v.set_origin(Vec3d(pos_x, pos_y, 0.));
v.set_offset(Vec3d(pos_x, pos_y, 0.0));
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
@ -949,6 +958,24 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
}
}
void GLVolumeCollection::set_select_by(const std::string& select_by)
{
for (GLVolume *vol : this->volumes)
{
if (vol != nullptr)
vol->set_select_group_id(select_by);
}
}
void GLVolumeCollection::set_drag_by(const std::string& drag_by)
{
for (GLVolume *vol : this->volumes)
{
if (vol != nullptr)
vol->set_drag_group_id(drag_by);
}
}
std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) const
{
// Collect layer top positions of all volumes.
@ -1190,7 +1217,7 @@ static void thick_lines_to_indexed_vertex_array(
b1_prev = b1;
v_prev = v;
if (bottom_z_different)
if (bottom_z_different && (closed || (!is_first && !is_last)))
{
// Found a change of the layer thickness -> Add a cap at the beginning of this segment.
volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
@ -1198,10 +1225,10 @@ static void thick_lines_to_indexed_vertex_array(
if (! closed) {
// Terminate open paths with caps.
if (is_first && !bottom_z_different)
if (is_first)
volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
// We don't use 'else' because both cases are true if we have only one line.
if (is_last && !bottom_z_different)
if (is_last)
volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]);
}
@ -1772,12 +1799,12 @@ void _3DScene::set_model(wxGLCanvas* canvas, Model* model)
void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape)
{
return s_canvas_mgr.set_bed_shape(canvas, shape);
s_canvas_mgr.set_bed_shape(canvas, shape);
}
void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas)
{
return s_canvas_mgr.set_auto_bed_shape(canvas);
s_canvas_mgr.set_auto_bed_shape(canvas);
}
BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas)
@ -1792,22 +1819,27 @@ void _3DScene::set_axes_length(wxGLCanvas* canvas, float length)
void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons)
{
return s_canvas_mgr.set_cutting_plane(canvas, z, polygons);
s_canvas_mgr.set_cutting_plane(canvas, z, polygons);
}
void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value)
{
return s_canvas_mgr.set_color_by(canvas, value);
s_canvas_mgr.set_color_by(canvas, value);
}
void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value)
{
return s_canvas_mgr.set_select_by(canvas, value);
s_canvas_mgr.set_select_by(canvas, value);
}
void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value)
{
return s_canvas_mgr.set_drag_by(canvas, value);
s_canvas_mgr.set_drag_by(canvas, value);
}
std::string _3DScene::get_select_by(wxGLCanvas* canvas)
{
return s_canvas_mgr.get_select_by(canvas);
}
bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas)
@ -2025,6 +2057,11 @@ void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callb
s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback);
}
void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback);
}
void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback);
@ -2080,6 +2117,11 @@ void _3DScene::register_action_layersediting_callback(wxGLCanvas* canvas, void*
s_canvas_mgr.register_action_layersediting_callback(canvas, callback);
}
void _3DScene::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_action_selectbyparts_callback(canvas, callback);
}
static inline int hex_digit_to_int(const char c)
{
return
@ -2117,6 +2159,11 @@ std::vector<int> _3DScene::load_object(wxGLCanvas* canvas, const Model* model, i
return s_canvas_mgr.load_object(canvas, model, obj_idx);
}
int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx)
{
return s_canvas_mgr.get_first_volume_id(canvas, obj_idx);
}
void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
{
s_canvas_mgr.reload_scene(canvas, force);

View File

@ -255,11 +255,11 @@ public:
private:
// Offset of the volume to be rendered.
Vec3d m_origin;
Vec3d m_offset;
// Rotation around Z axis of the volume to be rendered.
float m_angle_z;
double m_rotation;
// Scale factor of the volume to be rendered.
float m_scale_factor;
double m_scaling_factor;
// World matrix of the volume to be rendered.
mutable Transform3f m_world_matrix;
// Whether or not is needed to recalculate the world matrix.
@ -327,13 +327,19 @@ public:
// Sets render color in dependence of current state
void set_render_color();
float get_angle_z();
const Vec3d& get_origin() const;
void set_origin(const Vec3d& origin);
void set_angle_z(float angle_z);
void set_scale_factor(float scale_factor);
double get_rotation();
void set_rotation(double rotation);
const Vec3d& get_offset() const;
void set_offset(const Vec3d& offset);
void set_scaling_factor(double factor);
void set_convex_hull(const TriangleMesh& convex_hull);
void set_select_group_id(const std::string& select_by);
void set_drag_group_id(const std::string& drag_by);
int object_idx() const { return this->composite_id / 1000000; }
int volume_idx() const { return (this->composite_id / 1000) % 1000; }
int instance_idx() const { return this->composite_id % 1000; }
@ -443,6 +449,9 @@ public:
void update_colors_by_extruder(const DynamicPrintConfig* config);
void set_select_by(const std::string& select_by);
void set_drag_by(const std::string& drag_by);
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
std::vector<double> get_current_print_zs(bool active_only) const;
@ -496,6 +505,8 @@ public:
static void set_select_by(wxGLCanvas* canvas, const std::string& value);
static void set_drag_by(wxGLCanvas* canvas, const std::string& value);
static std::string get_select_by(wxGLCanvas* canvas);
static bool is_layers_editing_enabled(wxGLCanvas* canvas);
static bool is_layers_editing_allowed(wxGLCanvas* canvas);
static bool is_shader_enabled(wxGLCanvas* canvas);
@ -547,6 +558,7 @@ public:
static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback);
static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
static void register_action_add_callback(wxGLCanvas* canvas, void* callback);
@ -559,10 +571,13 @@ public:
static void register_action_cut_callback(wxGLCanvas* canvas, void* callback);
static void register_action_settings_callback(wxGLCanvas* canvas, void* callback);
static void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback);
static void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback);
static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx);
static void reload_scene(wxGLCanvas* canvas, bool force);
static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);

View File

@ -228,24 +228,20 @@ namespace Slic3r { namespace GUI {
}), temp->GetId());
#endif // __WXGTK__
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent)
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
{
#ifdef __WXGTK__
bChangedValueEvent = true;
#else
on_change_field();
if (bChangedValueEvent)
#endif //__WXGTK__
on_change_field();
}), temp->GetId());
#ifdef __WXGTK__
temp->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event)
{
if (bChangedValueEvent) {
on_change_field();
bChangedValueEvent = false;
}
event.Skip();
});
// to correct value updating on GTK we should:
// call on_change_field() on wxEVT_KEY_UP instead of wxEVT_TEXT
// and prevent value updating on wxEVT_KEY_DOWN
temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this);
temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this);
#endif //__WXGTK__
// select all text using Ctrl+A
@ -271,6 +267,15 @@ namespace Slic3r { namespace GUI {
void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); }
void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(false); }
#ifdef __WXGTK__
void TextCtrl::change_field_value(wxEvent& event)
{
if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP)
on_change_field();
event.Skip();
};
#endif //__WXGTK__
void CheckBox::BUILD() {
auto size = wxSize(wxDefaultSize);
if (m_opt.height >= 0) size.SetHeight(m_opt.height);

View File

@ -235,7 +235,8 @@ inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && ob
class TextCtrl : public Field {
using Field::Field;
#ifdef __WXGTK__
bool bChangedValueEvent = false;
bool bChangedValueEvent = true;
void change_field_value(wxEvent& event);
#endif //__WXGTK__
public:
TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}

View File

@ -1123,7 +1123,6 @@ const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale;
GLCanvas3D::Gizmos::Gizmos()
: m_enabled(false)
, m_current(Undefined)
, m_dragging(false)
{
}
@ -1134,7 +1133,16 @@ GLCanvas3D::Gizmos::~Gizmos()
bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
{
GLGizmoBase* gizmo = new GLGizmoScale3D(parent);
GLGizmoBase* gizmo = new GLGizmoMove3D(parent);
if (gizmo == nullptr)
return false;
if (!gizmo->init())
return false;
m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
gizmo = new GLGizmoScale3D(parent);
if (gizmo == nullptr)
return false;
@ -1320,16 +1328,6 @@ void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray)
curr->update(mouse_ray);
}
void GLCanvas3D::Gizmos::refresh()
{
if (!m_enabled)
return;
GLGizmoBase* curr = _get_current();
if (curr != nullptr)
curr->refresh();
}
GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const
{
return m_current;
@ -1346,25 +1344,43 @@ bool GLCanvas3D::Gizmos::is_running() const
bool GLCanvas3D::Gizmos::is_dragging() const
{
return m_dragging;
GLGizmoBase* curr = _get_current();
return (curr != nullptr) ? curr->is_dragging() : false;
}
void GLCanvas3D::Gizmos::start_dragging()
void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box)
{
m_dragging = true;
GLGizmoBase* curr = _get_current();
if (curr != nullptr)
curr->start_dragging();
curr->start_dragging(box);
}
void GLCanvas3D::Gizmos::stop_dragging()
{
m_dragging = false;
GLGizmoBase* curr = _get_current();
if (curr != nullptr)
curr->stop_dragging();
}
Vec3d GLCanvas3D::Gizmos::get_position() const
{
if (!m_enabled)
return Vec3d::Zero();
GizmosMap::const_iterator it = m_gizmos.find(Move);
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoMove3D*>(it->second)->get_position() : Vec3d::Zero();
}
void GLCanvas3D::Gizmos::set_position(const Vec3d& position)
{
if (!m_enabled)
return;
GizmosMap::const_iterator it = m_gizmos.find(Move);
if (it != m_gizmos.end())
reinterpret_cast<GLGizmoMove3D*>(it->second)->set_position(position);
}
float GLCanvas3D::Gizmos::get_scale() const
{
if (!m_enabled)
@ -2018,6 +2034,9 @@ void GLCanvas3D::update_volumes_selection(const std::vector<int>& selections)
if (m_model == nullptr)
return;
if (selections.empty())
return;
for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx)
{
if ((selections[obj_idx] == 1) && (obj_idx < (unsigned int)m_objects_volumes_idxs.size()))
@ -2144,11 +2163,23 @@ void GLCanvas3D::set_color_by(const std::string& value)
void GLCanvas3D::set_select_by(const std::string& value)
{
m_select_by = value;
m_volumes.set_select_by(value);
}
void GLCanvas3D::set_drag_by(const std::string& value)
{
m_drag_by = value;
m_volumes.set_drag_by(value);
}
const std::string& GLCanvas3D::get_select_by() const
{
return m_select_by;
}
const std::string& GLCanvas3D::get_drag_by() const
{
return m_drag_by;
}
float GLCanvas3D::get_camera_zoom() const
@ -2327,6 +2358,7 @@ void GLCanvas3D::update_gizmos_data()
ModelInstance* model_instance = model_object->instances[0];
if (model_instance != nullptr)
{
m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0));
m_gizmos.set_scale(model_instance->scaling_factor);
m_gizmos.set_angle_z(model_instance->rotation);
m_gizmos.set_flattening_data(model_object);
@ -2335,6 +2367,7 @@ void GLCanvas3D::update_gizmos_data()
}
else
{
m_gizmos.set_position(Vec3d::Zero());
m_gizmos.set_scale(1.0f);
m_gizmos.set_angle_z(0.0f);
m_gizmos.set_flattening_data(nullptr);
@ -2432,6 +2465,17 @@ std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
return std::vector<int>();
}
int GLCanvas3D::get_first_volume_id(int obj_idx) const
{
for (int i = 0; i < (int)m_volumes.volumes.size(); ++i)
{
if ((m_volumes.volumes[i] != nullptr) && (m_volumes.volumes[i]->object_idx() == obj_idx))
return i;
}
return -1;
}
void GLCanvas3D::reload_scene(bool force)
{
if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr))
@ -2467,8 +2511,6 @@ void GLCanvas3D::reload_scene(bool force)
if (!m_objects_selections.empty())
update_gizmos_data();
m_gizmos.refresh();
if (m_config->has("nozzle_diameter"))
{
// Should the wipe tower be visualized ?
@ -2691,6 +2733,12 @@ void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback)
m_on_gizmo_rotate_callback.register_callback(callback);
}
void GLCanvas3D::register_on_gizmo_flatten_callback(void* callback)
{
if (callback != nullptr)
m_on_gizmo_flatten_callback.register_callback(callback);
}
void GLCanvas3D::register_on_update_geometry_info_callback(void* callback)
{
if (callback != nullptr)
@ -2757,6 +2805,12 @@ void GLCanvas3D::register_action_layersediting_callback(void* callback)
m_action_layersediting_callback.register_callback(callback);
}
void GLCanvas3D::register_action_selectbyparts_callback(void* callback)
{
if (callback != nullptr)
m_action_selectbyparts_callback.register_callback(callback);
}
void GLCanvas3D::bind_event_handlers()
{
if (m_canvas != nullptr)
@ -2952,7 +3006,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.position = Vec2d(-1.0, -1.0);
m_dirty = true;
}
else if (evt.LeftDClick() && (m_hover_volume_id != -1))
else if (evt.LeftDClick() && (m_hover_volume_id != -1) && !gizmos_overlay_contains_mouse && (toolbar_contains_mouse == -1))
m_on_double_click_callback.call();
else if (evt.LeftDClick() && (toolbar_contains_mouse != -1))
{
@ -2993,16 +3047,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse())
{
update_gizmos_data();
m_gizmos.start_dragging();
m_gizmos.start_dragging(_selected_volumes_bounding_box());
m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx);
if (m_gizmos.get_current_type() == Gizmos::Flatten) {
// Rotate the object so the normal points downward:
Vec3d normal = m_gizmos.get_flattening_normal();
if (normal != Vec3d::Zero()) {
Vec3d axis = normal(2) > 0.999f ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ());
float angle = -acos(-normal(2));
m_on_gizmo_rotate_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2));
if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) {
Vec3d axis = normal(2) > 0.999 ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ()).normalized();
float angle = acos(clamp(-1.0, 1.0, -normal(2)));
m_on_gizmo_flatten_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2));
}
}
@ -3036,14 +3090,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
update_gizmos_data();
m_gizmos.refresh();
m_dirty = true;
}
}
// propagate event through callback
if (m_picking_enabled && (volume_idx != -1))
_on_select(volume_idx);
_on_select(volume_idx, selected_object_idx);
if (volume_idx != -1)
{
@ -3126,10 +3179,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Apply new temporary volume origin and ignore Z.
for (GLVolume* v : volumes)
v->set_origin(v->get_origin() + Vec3d(vector(0), vector(1), 0.0));
{
v->set_offset(v->get_offset() + Vec3d(vector(0), vector(1), 0.0));
}
update_position_values(volume->get_offset());
m_mouse.drag.start_position_3D = cur_pos;
m_gizmos.refresh();
m_dirty = true;
}
@ -3160,14 +3215,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
switch (m_gizmos.get_current_type())
{
case Gizmos::Move:
{
// Apply new temporary offset
GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx];
Vec3d offset = m_gizmos.get_position() - volume->get_offset();
for (GLVolume* v : volumes)
{
v->set_offset(v->get_offset() + offset);
}
update_position_values(volume->get_offset());
break;
}
case Gizmos::Scale:
{
// Apply new temporary scale factor
float scale_factor = m_gizmos.get_scale();
for (GLVolume* v : volumes)
{
v->set_scale_factor(scale_factor);
v->set_scaling_factor((double)scale_factor);
}
update_scale_values((double)scale_factor);
break;
}
case Gizmos::Rotate:
@ -3176,8 +3244,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
float angle_z = m_gizmos.get_angle_z();
for (GLVolume* v : volumes)
{
v->set_angle_z(angle_z);
v->set_rotation((double)angle_z);
}
update_rotation_value((double)angle_z, Z);
break;
}
default:
@ -3193,13 +3262,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
const Vec3d& size = bb.size();
m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale());
update_scale_values(size, m_gizmos.get_scale());
update_rotation_value(volumes[0]->get_angle_z(), "z");
}
if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1))
m_gizmos.refresh();
m_dirty = true;
}
else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
@ -3284,7 +3348,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (m_picking_enabled && !m_toolbar_action_running)
{
deselect_volumes();
_on_select(-1);
_on_select(-1, -1);
update_gizmos_data();
}
}
@ -3292,10 +3356,30 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
switch (m_gizmos.get_current_type())
{
case Gizmos::Move:
{
// get all volumes belonging to the same group, if any
std::vector<int> volume_idxs;
int vol_id = m_mouse.drag.gizmo_volume_idx;
int group_id = m_volumes.volumes[vol_id]->select_group_id;
if (group_id == -1)
volume_idxs.push_back(vol_id);
else
{
for (int i = 0; i < (int)m_volumes.volumes.size(); ++i)
{
if (m_volumes.volumes[i]->select_group_id == group_id)
volume_idxs.push_back(i);
}
}
_on_move(volume_idxs);
break;
}
case Gizmos::Scale:
{
m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
Slic3r::GUI::update_settings_value();
break;
}
case Gizmos::Rotate:
@ -3307,6 +3391,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
break;
}
m_gizmos.stop_dragging();
Slic3r::GUI::update_settings_value();
}
m_mouse.drag.move_volume_idx = -1;
@ -3509,6 +3594,17 @@ bool GLCanvas3D::_init_toolbar()
if (!m_toolbar.add_item(item))
return false;
if (!m_toolbar.add_separator())
return false;
item.name = "selectbyparts";
item.tooltip = GUI::L_str("Select by parts");
item.sprite_id = 10;
item.is_toggable = true;
item.action_callback = &m_action_selectbyparts_callback;
if (!m_toolbar.add_item(item))
return false;
enable_toolbar_item("add", true);
return true;
@ -3736,6 +3832,7 @@ void GLCanvas3D::_deregister_callbacks()
m_on_enable_action_buttons_callback.deregister_callback();
m_on_gizmo_scale_uniformly_callback.deregister_callback();
m_on_gizmo_rotate_callback.deregister_callback();
m_on_gizmo_flatten_callback.deregister_callback();
m_on_update_geometry_info_callback.deregister_callback();
m_action_add_callback.deregister_callback();
@ -3748,6 +3845,7 @@ void GLCanvas3D::_deregister_callbacks()
m_action_cut_callback.deregister_callback();
m_action_settings_callback.deregister_callback();
m_action_layersediting_callback.deregister_callback();
m_action_selectbyparts_callback.deregister_callback();
}
void GLCanvas3D::_mark_volumes_for_layer_height() const
@ -5219,7 +5317,7 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
std::set<std::string> done; // prevent moving instances twice
bool object_moved = false;
Vec3d wipe_tower_origin(0.0, 0.0, 0.0);
Vec3d wipe_tower_origin = Vec3d::Zero();
for (int volume_idx : volume_idxs)
{
GLVolume* volume = m_volumes.volumes[volume_idx];
@ -5238,34 +5336,56 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
// Move a regular object.
ModelObject* model_object = m_model->objects[obj_idx];
const Vec3d& origin = volume->get_origin();
model_object->instances[instance_idx]->offset = Vec2d(origin(0), origin(1));
model_object->invalidate_bounding_box();
object_moved = true;
if (model_object != nullptr)
{
const Vec3d& offset = volume->get_offset();
model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1));
model_object->invalidate_bounding_box();
update_position_values();
object_moved = true;
}
}
else if (obj_idx == 1000)
// Move a wipe tower proxy.
wipe_tower_origin = volume->get_origin();
wipe_tower_origin = volume->get_offset();
}
if (object_moved)
m_on_instance_moved_callback.call();
if (wipe_tower_origin != Vec3d(0.0, 0.0, 0.0))
if (wipe_tower_origin != Vec3d::Zero())
m_on_wipe_tower_moved_callback.call(wipe_tower_origin(0), wipe_tower_origin(1));
}
void GLCanvas3D::_on_select(int volume_idx)
void GLCanvas3D::_on_select(int volume_idx, int object_idx)
{
int id = -1;
int vol_id = -1;
int obj_id = -1;
if ((volume_idx != -1) && (volume_idx < (int)m_volumes.volumes.size()))
{
if (m_select_by == "volume")
id = m_volumes.volumes[volume_idx]->volume_idx();
{
if (m_volumes.volumes[volume_idx]->object_idx() != object_idx)
{
set_select_by("object");
obj_id = m_volumes.volumes[volume_idx]->object_idx();
vol_id = -1;
}
else
{
obj_id = object_idx;
vol_id = m_volumes.volumes[volume_idx]->volume_idx();
}
}
else if (m_select_by == "object")
id = m_volumes.volumes[volume_idx]->object_idx();
{
obj_id = m_volumes.volumes[volume_idx]->object_idx();
vol_id = -1;
}
}
m_on_select_object_callback.call(id);
m_on_select_object_callback.call(obj_id, vol_id);
}
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)

View File

@ -336,6 +336,7 @@ public:
enum EType : unsigned char
{
Undefined,
Move,
Scale,
Rotate,
Flatten,
@ -347,7 +348,6 @@ public:
typedef std::map<EType, GLGizmoBase*> GizmosMap;
GizmosMap m_gizmos;
EType m_current;
bool m_dragging;
public:
Gizmos();
@ -367,16 +367,18 @@ public:
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const;
bool grabber_contains_mouse() const;
void update(const Linef3& mouse_ray);
void refresh();
EType get_current_type() const;
bool is_running() const;
bool is_dragging() const;
void start_dragging();
void start_dragging(const BoundingBoxf3& box);
void stop_dragging();
Vec3d get_position() const;
void set_position(const Vec3d& position);
float get_scale() const;
void set_scale(float scale);
@ -502,6 +504,7 @@ private:
PerlCallback m_on_enable_action_buttons_callback;
PerlCallback m_on_gizmo_scale_uniformly_callback;
PerlCallback m_on_gizmo_rotate_callback;
PerlCallback m_on_gizmo_flatten_callback;
PerlCallback m_on_update_geometry_info_callback;
PerlCallback m_action_add_callback;
@ -514,6 +517,7 @@ private:
PerlCallback m_action_cut_callback;
PerlCallback m_action_settings_callback;
PerlCallback m_action_layersediting_callback;
PerlCallback m_action_selectbyparts_callback;
public:
GLCanvas3D(wxGLCanvas* canvas);
@ -556,6 +560,9 @@ public:
void set_select_by(const std::string& value);
void set_drag_by(const std::string& value);
const std::string& get_select_by() const;
const std::string& get_drag_by() const;
float get_camera_zoom() const;
BoundingBoxf3 volumes_bounding_box() const;
@ -597,6 +604,8 @@ public:
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
std::vector<int> load_object(const Model& model, int obj_idx);
int get_first_volume_id(int obj_idx) const;
void reload_scene(bool force);
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
@ -619,6 +628,7 @@ public:
void register_on_enable_action_buttons_callback(void* callback);
void register_on_gizmo_scale_uniformly_callback(void* callback);
void register_on_gizmo_rotate_callback(void* callback);
void register_on_gizmo_flatten_callback(void* callback);
void register_on_update_geometry_info_callback(void* callback);
void register_action_add_callback(void* callback);
@ -631,6 +641,7 @@ public:
void register_action_cut_callback(void* callback);
void register_action_settings_callback(void* callback);
void register_action_layersediting_callback(void* callback);
void register_action_selectbyparts_callback(void* callback);
void bind_event_handlers();
void unbind_event_handlers();
@ -733,7 +744,7 @@ private:
void _show_warning_texture_if_needed();
void _on_move(const std::vector<int>& volume_idxs);
void _on_select(int volume_idx);
void _on_select(int volume_idx, int object_idx);
// generates the legend texture in dependence of the current shown view type
void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);

View File

@ -338,6 +338,12 @@ void GLCanvas3DManager::set_drag_by(wxGLCanvas* canvas, const std::string& value
it->second->set_drag_by(value);
}
std::string GLCanvas3DManager::get_select_by(wxGLCanvas* canvas) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
return (it != m_canvases.end()) ? it->second->get_select_by() : "";
}
bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
@ -536,6 +542,12 @@ std::vector<int> GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model*
return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector<int>();
}
int GLCanvas3DManager::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1;
}
void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@ -688,6 +700,13 @@ void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, vo
it->second->register_on_gizmo_rotate_callback(callback);
}
void GLCanvas3DManager::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_on_gizmo_flatten_callback(callback);
}
void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@ -765,6 +784,13 @@ void GLCanvas3DManager::register_action_layersediting_callback(wxGLCanvas* canva
it->second->register_action_layersediting_callback(callback);
}
void GLCanvas3DManager::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_action_selectbyparts_callback(callback);
}
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);

View File

@ -98,6 +98,8 @@ public:
void set_select_by(wxGLCanvas* canvas, const std::string& value);
void set_drag_by(wxGLCanvas* canvas, const std::string& value);
std::string get_select_by(wxGLCanvas* canvas) const;
bool is_layers_editing_enabled(wxGLCanvas* canvas) const;
bool is_layers_editing_allowed(wxGLCanvas* canvas) const;
bool is_shader_enabled(wxGLCanvas* canvas) const;
@ -135,6 +137,8 @@ public:
std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const;
void reload_scene(wxGLCanvas* canvas, bool force);
void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
@ -159,6 +163,7 @@ public:
void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback);
void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
void register_action_add_callback(wxGLCanvas* canvas, void* callback);
@ -171,6 +176,7 @@ public:
void register_action_cut_callback(wxGLCanvas* canvas, void* callback);
void register_action_settings_callback(wxGLCanvas* canvas, void* callback);
void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback);
void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback);
private:
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);

View File

@ -15,14 +15,95 @@ static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f };
static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f };
static const float DEFAULT_HIGHLIGHT_COLOR[3] = { 1.0f, 0.38f, 0.0f };
static const float RED[3] = { 1.0f, 0.0f, 0.0f };
static const float GREEN[3] = { 0.0f, 1.0f, 0.0f };
static const float BLUE[3] = { 0.0f, 0.0f, 1.0f };
static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } };
namespace Slic3r {
namespace GUI {
const float GLGizmoBase::Grabber::HalfSize = 2.0f;
// returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center
// coordinates are local to the plane
Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center)
{
Transform3d m = Transform3d::Identity();
m.translate(-center);
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0);
}
// returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center
// coordinates are local to the plane
Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center)
{
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
m.translate(-center);
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1));
}
// returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center
// coordinates are local to the plane
Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center)
{
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
m.translate(-center);
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0));
}
// return an index:
// 0 for plane XY
// 1 for plane XZ
// 2 for plane YZ
// which indicates which plane is best suited for intersecting the given unit vector
// giving precedence to the plane with the given index
unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane)
{
unsigned int ret = preferred_plane;
// 1st checks if the given vector is not parallel to the given preferred plane
double dot_to_normal = 0.0;
switch (ret)
{
case 0: // plane xy
{
dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ()));
break;
}
case 1: // plane xz
{
dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY()));
break;
}
case 2: // plane yz
{
dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX()));
break;
}
default:
{
break;
}
}
// if almost parallel, select the plane whose normal direction is closest to the given vector direction,
// otherwise return the given preferred plane index
if (dot_to_normal < 0.1)
{
typedef std::map<double, unsigned int> ProjsMap;
ProjsMap projs_map;
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz
ret = projs_map.rbegin()->second;
}
return ret;
}
const float GLGizmoBase::Grabber::HalfSize = 2.0f;
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
GLGizmoBase::Grabber::Grabber()
@ -131,6 +212,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent)
, m_group_id(-1)
, m_state(Off)
, m_hover_id(-1)
, m_dragging(false)
{
::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 3 * sizeof(float));
::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 3 * sizeof(float));
@ -152,18 +234,21 @@ void GLGizmoBase::set_highlight_color(const float* color)
::memcpy((void*)m_highlight_color, (const void*)color, 3 * sizeof(float));
}
void GLGizmoBase::start_dragging()
void GLGizmoBase::start_dragging(const BoundingBoxf3& box)
{
m_dragging = true;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = (m_hover_id == i);
}
on_start_dragging();
on_start_dragging(box);
}
void GLGizmoBase::stop_dragging()
{
m_dragging = false;
set_tooltip("");
for (int i = 0; i < (int)m_grabbers.size(); ++i)
@ -199,8 +284,11 @@ void GLGizmoBase::render_grabbers() const
void GLGizmoBase::render_grabbers_for_picking() const
{
for (int i = 0; i < (int)m_grabbers.size(); ++i)
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
{
m_grabbers[i].color[0] = 1.0f;
m_grabbers[i].color[1] = 1.0f;
m_grabbers[i].color[2] = picking_color_component(i);
m_grabbers[i].render_for_picking();
}
}
@ -234,7 +322,6 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
, m_angle(0.0)
, m_center(0.0, 0.0, 0.0)
, m_radius(0.0f)
, m_keep_initial_values(false)
{
}
@ -252,6 +339,12 @@ bool GLGizmoRotate::on_init()
return true;
}
void GLGizmoRotate::on_start_dragging(const BoundingBoxf3& box)
{
m_center = box.center();
m_radius = Offset + box.radius();
}
void GLGizmoRotate::on_update(const Linef3& mouse_ray)
{
Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(mouse_ray));
@ -293,18 +386,16 @@ void GLGizmoRotate::on_update(const Linef3& mouse_ray)
void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
{
if (m_grabbers[0].dragging)
if (m_dragging)
set_tooltip(format(m_angle * 180.0f / (float)PI, 4));
::glEnable(GL_DEPTH_TEST);
if (!m_keep_initial_values)
else
{
m_center = box.center();
m_radius = Offset + box.radius();
m_keep_initial_values = true;
}
::glEnable(GL_DEPTH_TEST);
::glPushMatrix();
transform_to_local();
@ -335,12 +426,8 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
::glDisable(GL_DEPTH_TEST);
::glPushMatrix();
transform_to_local();
m_grabbers[0].color[0] = 1.0f;
m_grabbers[0].color[1] = 1.0f;
m_grabbers[0].color[2] = picking_color_component(0);
render_grabbers_for_picking();
::glPopMatrix();
@ -512,23 +599,29 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray) cons
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
, m_x(parent, GLGizmoRotate::X)
, m_y(parent, GLGizmoRotate::Y)
, m_z(parent, GLGizmoRotate::Z)
{
m_x.set_group_id(0);
m_y.set_group_id(1);
m_z.set_group_id(2);
m_gizmos.emplace_back(parent, GLGizmoRotate::X);
m_gizmos.emplace_back(parent, GLGizmoRotate::Y);
m_gizmos.emplace_back(parent, GLGizmoRotate::Z);
for (unsigned int i = 0; i < 3; ++i)
{
m_gizmos[i].set_group_id(i);
}
}
bool GLGizmoRotate3D::on_init()
{
if (!m_x.init() || !m_y.init() || !m_z.init())
return false;
for (GLGizmoRotate& g : m_gizmos)
{
if (!g.init())
return false;
}
m_x.set_highlight_color(RED);
m_y.set_highlight_color(GREEN);
m_z.set_highlight_color(BLUE);
for (unsigned int i = 0; i < 3; ++i)
{
m_gizmos[i].set_highlight_color(AXES_COLOR[i]);
}
std::string path = resources_dir() + "/icons/overlay/";
@ -547,68 +640,28 @@ bool GLGizmoRotate3D::on_init()
return true;
}
void GLGizmoRotate3D::on_start_dragging()
void GLGizmoRotate3D::on_start_dragging(const BoundingBoxf3& box)
{
switch (m_hover_id)
{
case 0:
{
m_x.start_dragging();
break;
}
case 1:
{
m_y.start_dragging();
break;
}
case 2:
{
m_z.start_dragging();
break;
}
default:
{
break;
}
}
if ((0 <= m_hover_id) && (m_hover_id < 3))
m_gizmos[m_hover_id].start_dragging(box);
}
void GLGizmoRotate3D::on_stop_dragging()
{
switch (m_hover_id)
{
case 0:
{
m_x.stop_dragging();
break;
}
case 1:
{
m_y.stop_dragging();
break;
}
case 2:
{
m_z.stop_dragging();
break;
}
default:
{
break;
}
}
if ((0 <= m_hover_id) && (m_hover_id < 3))
m_gizmos[m_hover_id].stop_dragging();
}
void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const
{
if ((m_hover_id == -1) || (m_hover_id == 0))
m_x.render(box);
m_gizmos[X].render(box);
if ((m_hover_id == -1) || (m_hover_id == 1))
m_y.render(box);
m_gizmos[Y].render(box);
if ((m_hover_id == -1) || (m_hover_id == 2))
m_z.render(box);
m_gizmos[Z].render(box);
}
const float GLGizmoScale3D::Offset = 5.0f;
@ -617,7 +670,7 @@ GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
, m_scale(Vec3d::Ones())
, m_starting_scale(Vec3d::Ones())
, m_starting_center(Vec3d::Zero())
, m_show_starting_box(false)
{
}
@ -655,12 +708,13 @@ bool GLGizmoScale3D::on_init()
return true;
}
void GLGizmoScale3D::on_start_dragging()
void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box)
{
if (m_hover_id != -1)
{
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_center = m_box.center();
m_show_starting_box = true;
m_starting_box = box;
}
}
@ -702,20 +756,20 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
// x axis
m_grabbers[0].center = Vec3d(m_box.min(0), center(1), center(2));
m_grabbers[1].center = Vec3d(m_box.max(0), center(1), center(2));
::memcpy((void*)m_grabbers[0].color, (const void*)RED, 3 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (const void*)RED, 3 * sizeof(float));
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
// y axis
m_grabbers[2].center = Vec3d(center(0), m_box.min(1), center(2));
m_grabbers[3].center = Vec3d(center(0), m_box.max(1), center(2));
::memcpy((void*)m_grabbers[2].color, (const void*)GREEN, 3 * sizeof(float));
::memcpy((void*)m_grabbers[3].color, (const void*)GREEN, 3 * sizeof(float));
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
::memcpy((void*)m_grabbers[3].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
// z axis
m_grabbers[4].center = Vec3d(center(0), center(1), m_box.min(2));
m_grabbers[5].center = Vec3d(center(0), center(1), m_box.max(2));
::memcpy((void*)m_grabbers[4].color, (const void*)BLUE, 3 * sizeof(float));
::memcpy((void*)m_grabbers[5].color, (const void*)BLUE, 3 * sizeof(float));
::memcpy((void*)m_grabbers[4].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::memcpy((void*)m_grabbers[5].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
// uniform
m_grabbers[6].center = Vec3d(m_box.min(0), m_box.min(1), m_box.min(2));
@ -731,16 +785,32 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
if (m_hover_id == -1)
{
::glColor3fv(m_base_color);
// draw box
render_box();
::glColor3fv(m_base_color);
render_box(m_box);
// draw connections
::glColor3fv(m_grabbers[0].color);
render_grabbers_connection(0, 1);
::glColor3fv(m_grabbers[2].color);
render_grabbers_connection(2, 3);
::glColor3fv(m_grabbers[4].color);
render_grabbers_connection(4, 5);
// draw grabbers
render_grabbers();
}
else if ((m_hover_id == 0) || (m_hover_id == 1))
{
::glColor3fv(m_grabbers[0].color);
// draw starting box
if (m_show_starting_box)
{
::glColor3fv(m_base_color);
render_box(m_starting_box);
}
// draw current box
::glColor3fv(m_drag_color);
render_box(m_box);
// draw connection
::glColor3fv(m_grabbers[0].color);
render_grabbers_connection(0, 1);
// draw grabbers
m_grabbers[0].render(true);
@ -748,8 +818,17 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
}
else if ((m_hover_id == 2) || (m_hover_id == 3))
{
::glColor3fv(m_grabbers[2].color);
// draw starting box
if (m_show_starting_box)
{
::glColor3fv(m_base_color);
render_box(m_starting_box);
}
// draw current box
::glColor3fv(m_drag_color);
render_box(m_box);
// draw connection
::glColor3fv(m_grabbers[2].color);
render_grabbers_connection(2, 3);
// draw grabbers
m_grabbers[2].render(true);
@ -757,8 +836,17 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
}
else if ((m_hover_id == 4) || (m_hover_id == 5))
{
::glColor3fv(m_grabbers[4].color);
// draw starting box
if (m_show_starting_box)
{
::glColor3fv(m_base_color);
render_box(m_starting_box);
}
// draw current box
::glColor3fv(m_drag_color);
render_box(m_box);
// draw connection
::glColor3fv(m_grabbers[4].color);
render_grabbers_connection(4, 5);
// draw grabbers
m_grabbers[4].render(true);
@ -766,9 +854,15 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
}
else if (m_hover_id >= 6)
{
// draw starting box
if (m_show_starting_box)
{
::glColor3fv(m_base_color);
render_box(m_starting_box);
}
// draw current box
::glColor3fv(m_drag_color);
// draw box
render_box();
render_box(m_box);
// draw grabbers
for (int i = 6; i < 10; ++i)
{
@ -781,40 +875,33 @@ void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const
{
::glDisable(GL_DEPTH_TEST);
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
{
m_grabbers[i].color[0] = 1.0f;
m_grabbers[i].color[1] = 1.0f;
m_grabbers[i].color[2] = picking_color_component(i);
}
render_grabbers_for_picking();
}
void GLGizmoScale3D::render_box() const
void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const
{
// bottom face
::glBegin(GL_LINE_LOOP);
::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2));
::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2));
::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2));
::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2));
::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2));
::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2));
::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2));
::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2));
::glEnd();
// top face
::glBegin(GL_LINE_LOOP);
::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2));
::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2));
::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2));
::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2));
::glEnd();
// vertical edges
::glBegin(GL_LINES);
::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.min(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.max(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.min(2)); ::glVertex3f((GLfloat)m_box.max(0), (GLfloat)m_box.min(1), (GLfloat)m_box.max(2));
::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2));
::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2));
::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2));
::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2));
::glEnd();
}
@ -832,7 +919,7 @@ void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int
void GLGizmoScale3D::do_scale_x(const Linef3& mouse_ray)
{
double ratio = calc_ratio(1, mouse_ray, m_starting_center);
double ratio = calc_ratio(1, mouse_ray, m_starting_box.center());
if (ratio > 0.0)
m_scale(0) = m_starting_scale(0) * ratio;
@ -840,7 +927,7 @@ void GLGizmoScale3D::do_scale_x(const Linef3& mouse_ray)
void GLGizmoScale3D::do_scale_y(const Linef3& mouse_ray)
{
double ratio = calc_ratio(2, mouse_ray, m_starting_center);
double ratio = calc_ratio(2, mouse_ray, m_starting_box.center());
if (ratio > 0.0)
m_scale(0) = m_starting_scale(1) * ratio; // << this is temporary
@ -849,7 +936,7 @@ void GLGizmoScale3D::do_scale_y(const Linef3& mouse_ray)
void GLGizmoScale3D::do_scale_z(const Linef3& mouse_ray)
{
double ratio = calc_ratio(1, mouse_ray, m_starting_center);
double ratio = calc_ratio(1, mouse_ray, m_starting_box.center());
if (ratio > 0.0)
m_scale(0) = m_starting_scale(2) * ratio; // << this is temporary
@ -858,7 +945,7 @@ void GLGizmoScale3D::do_scale_z(const Linef3& mouse_ray)
void GLGizmoScale3D::do_scale_uniform(const Linef3& mouse_ray)
{
Vec3d center = m_starting_center;
Vec3d center = m_starting_box.center();
center(2) = m_box.min(2);
double ratio = calc_ratio(0, mouse_ray, center);
@ -877,77 +964,24 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3&
Vec3d starting_vec_dir = starting_vec.normalized();
Vec3d mouse_dir = mouse_ray.unit_vector();
unsigned int plane_id = preferred_plane_id;
// 1st try to see if the mouse direction is close enough to the preferred plane normal
double dot_to_normal = 0.0;
unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id);
// ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length
switch (plane_id)
{
case 0:
{
dot_to_normal = std::abs(mouse_dir.dot(Vec3d::UnitZ()));
ratio = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, center)) / len_starting_vec;
break;
}
case 1:
{
dot_to_normal = std::abs(mouse_dir.dot(-Vec3d::UnitY()));
ratio = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, center)) / len_starting_vec;
break;
}
case 2:
{
dot_to_normal = std::abs(mouse_dir.dot(Vec3d::UnitX()));
break;
}
}
if (dot_to_normal < 0.1)
{
// if not, select the plane who's normal is closest to the mouse direction
typedef std::map<double, unsigned int> ProjsMap;
ProjsMap projs_map;
projs_map.insert(ProjsMap::value_type(std::abs(mouse_dir.dot(Vec3d::UnitZ())), 0)); // plane xy
projs_map.insert(ProjsMap::value_type(std::abs(mouse_dir.dot(-Vec3d::UnitY())), 1)); // plane xz
projs_map.insert(ProjsMap::value_type(std::abs(mouse_dir.dot(Vec3d::UnitX())), 2)); // plane yz
plane_id = projs_map.rbegin()->second;
}
switch (plane_id)
{
case 0:
{
// calculates the intersection of the mouse ray with the plane parallel to plane XY and passing through the given center
Transform3d m = Transform3d::Identity();
m.translate(-center);
Vec2d mouse_pos_2d = to_2d(transform(mouse_ray, m).intersect_plane(0.0));
// ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length
ratio = starting_vec_dir.dot(Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0)) / len_starting_vec;
break;
}
case 1:
{
// calculates the intersection of the mouse ray with the plane parallel to plane XZ and passing through the given center
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
m.translate(-center);
Vec2d mouse_pos_2d = to_2d(transform(mouse_ray, m).intersect_plane(0.0));
// ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length
ratio = starting_vec_dir.dot(Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1))) / len_starting_vec;
break;
}
case 2:
{
// calculates the intersection of the mouse ray with the plane parallel to plane YZ and passing through the given center
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
m.translate(-center);
Vec2d mouse_pos_2d = to_2d(transform(mouse_ray, m).intersect_plane(0.0));
// ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length
ratio = starting_vec_dir.dot(Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0))) / len_starting_vec;
ratio = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, center)) / len_starting_vec;
break;
}
}
@ -955,10 +989,162 @@ double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3&
return ratio;
}
const double GLGizmoMove3D::Offset = 10.0;
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
, m_position(Vec3d::Zero())
, m_starting_drag_position(Vec3d::Zero())
, m_starting_box_center(Vec3d::Zero())
{
}
bool GLGizmoMove3D::on_init()
{
std::string path = resources_dir() + "/icons/overlay/";
std::string filename = path + "move_off.png";
if (!m_textures[Off].load_from_file(filename, false))
return false;
filename = path + "move_hover.png";
if (!m_textures[Hover].load_from_file(filename, false))
return false;
filename = path + "move_on.png";
if (!m_textures[On].load_from_file(filename, false))
return false;
for (int i = 0; i < 3; ++i)
{
m_grabbers.push_back(Grabber());
}
return true;
}
void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box)
{
if (m_hover_id != -1)
{
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
}
}
void GLGizmoMove3D::on_update(const Linef3& mouse_ray)
{
if (m_hover_id == 0)
m_position(0) = 2.0 * m_starting_box_center(0) + calc_displacement(1, mouse_ray) - m_starting_drag_position(0);
else if (m_hover_id == 1)
m_position(1) = 2.0 * m_starting_box_center(1) + calc_displacement(2, mouse_ray) - m_starting_drag_position(1);
else if (m_hover_id == 2)
m_position(2) = 2.0 * m_starting_box_center(2) + calc_displacement(1, mouse_ray) - m_starting_drag_position(2);
}
void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
{
if (m_grabbers[0].dragging)
set_tooltip("X: " + format(m_position(0), 2));
else if (m_grabbers[1].dragging)
set_tooltip("Y: " + format(m_position(1), 2));
else if (m_grabbers[2].dragging)
set_tooltip("Z: " + format(m_position(2), 2));
::glEnable(GL_DEPTH_TEST);
const Vec3d& center = box.center();
// x axis
m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2));
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
// y axis
m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2));
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
// z axis
m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset);
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
if (m_hover_id == -1)
{
// draw axes
for (unsigned int i = 0; i < 3; ++i)
{
::glColor3fv(AXES_COLOR[i]);
::glBegin(GL_LINES);
::glVertex3f(center(0), center(1), center(2));
::glVertex3f((GLfloat)m_grabbers[i].center(0), (GLfloat)m_grabbers[i].center(1), (GLfloat)m_grabbers[i].center(2));
::glEnd();
}
// draw grabbers
render_grabbers();
}
else
{
// draw axis
::glColor3fv(AXES_COLOR[m_hover_id]);
::glBegin(GL_LINES);
::glVertex3f(center(0), center(1), center(2));
::glVertex3f((GLfloat)m_grabbers[m_hover_id].center(0), (GLfloat)m_grabbers[m_hover_id].center(1), (GLfloat)m_grabbers[m_hover_id].center(2));
::glEnd();
// draw grabber
m_grabbers[m_hover_id].render(true);
}
}
void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const
{
::glDisable(GL_DEPTH_TEST);
render_grabbers_for_picking();
}
double GLGizmoMove3D::calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const
{
double displacement = 0.0;
Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
double len_starting_vec = starting_vec.norm();
if (len_starting_vec == 0.0)
return displacement;
Vec3d starting_vec_dir = starting_vec.normalized();
Vec3d mouse_dir = mouse_ray.unit_vector();
unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id);
switch (plane_id)
{
case 0:
{
displacement = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, m_starting_box_center));
break;
}
case 1:
{
displacement = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, m_starting_box_center));
break;
}
case 2:
{
displacement = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, m_starting_box_center));
break;
}
}
return displacement;
}
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent)
: GLGizmoBase(parent)
, m_normal(0.0, 0.0, 0.0)
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{
}
@ -981,10 +1167,13 @@ bool GLGizmoFlatten::on_init()
return true;
}
void GLGizmoFlatten::on_start_dragging()
void GLGizmoFlatten::on_start_dragging(const BoundingBoxf3& box)
{
if (m_hover_id != -1)
{
m_normal = m_planes[m_hover_id].normal;
m_starting_center = box.center();
}
}
void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
@ -992,13 +1181,10 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
// the dragged_offset is a vector measuring where was the object moved
// with the gizmo being on. This is reset in set_flattening_data and
// does not work correctly when there are multiple copies.
if (!m_center) // this is the first bounding box that we see
m_center.reset(new Vec3d(box.center()));
Vec3d dragged_offset(Vec3d::Zero());
if (m_dragging)
dragged_offset = box.center() - m_starting_center;
Vec3d dragged_offset = box.center() - *m_center;
bool blending_was_enabled = ::glIsEnabled(GL_BLEND);
bool depth_test_was_enabled = ::glIsEnabled(GL_DEPTH_TEST);
::glEnable(GL_BLEND);
::glEnable(GL_DEPTH_TEST);
@ -1020,20 +1206,15 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
}
}
if (!blending_was_enabled)
::glDisable(GL_BLEND);
if (!depth_test_was_enabled)
::glDisable(GL_DEPTH_TEST);
::glDisable(GL_BLEND);
}
void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
{
::glDisable(GL_DEPTH_TEST);
::glEnable(GL_DEPTH_TEST);
for (unsigned int i = 0; i < m_planes.size(); ++i)
{
// FIXME: the color assignement will fail if the planes count is greater than 254
// use the other color components in that case !!
::glColor3f(1.0f, 1.0f, picking_color_component(i));
for (const Vec2d& offset : m_instances_positions) {
::glPushMatrix();
@ -1049,7 +1230,6 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
{
m_center.release(); // object is not being dragged (this would not be called otherwise) - we must forget about the bounding box position...
m_model_object = model_object;
// ...and save the updated positions of the object instances:
@ -1211,9 +1391,9 @@ void GLGizmoFlatten::update_planes()
polygon = transform(polygon, m);
}
// We'll sort the planes by area and only keep the 255 largest ones (because of the picking pass limitations):
// We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations):
std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; });
m_planes.resize(std::min((int)m_planes.size(), 255));
m_planes.resize(std::min((int)m_planes.size(), 254));
// Planes are finished - let's save what we calculated it from:
m_source_data.bounding_boxes.clear();
@ -1251,11 +1431,9 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
}
Vec3d GLGizmoFlatten::get_flattening_normal() const {
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(-m_model_object->instances.front()->rotation, Vec3d::UnitZ()));
Vec3d normal = m * m_normal;
Vec3d normal = m_model_object->instances.front()->world_matrix(true).matrix().block(0, 0, 3, 3).inverse() * m_normal;
m_normal = Vec3d::Zero();
return normal;
return normal.normalized();
}
} // namespace GUI

View File

@ -57,6 +57,7 @@ protected:
// textures are assumed to be square and all with the same size in pixels, no internal check is done
GLTexture m_textures[Num_States];
int m_hover_id;
bool m_dragging;
float m_base_color[3];
float m_drag_color[3];
float m_highlight_color[3];
@ -82,10 +83,11 @@ public:
void set_highlight_color(const float* color);
void start_dragging();
void start_dragging(const BoundingBoxf3& box);
void stop_dragging();
bool is_dragging() const { return m_dragging; }
void update(const Linef3& mouse_ray);
void refresh() { on_refresh(); }
void render(const BoundingBoxf3& box) const { on_render(box); }
void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); }
@ -94,10 +96,9 @@ protected:
virtual bool on_init() = 0;
virtual void on_set_state() {}
virtual void on_set_hover_id() {}
virtual void on_start_dragging() {}
virtual void on_start_dragging(const BoundingBoxf3& box) {}
virtual void on_stop_dragging() {}
virtual void on_update(const Linef3& mouse_ray) = 0;
virtual void on_refresh() {}
virtual void on_render(const BoundingBoxf3& box) const = 0;
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
@ -136,7 +137,6 @@ private:
mutable Vec3d m_center;
mutable float m_radius;
mutable bool m_keep_initial_values;
public:
GLGizmoRotate(GLCanvas3D& parent, Axis axis);
@ -146,9 +146,8 @@ public:
protected:
virtual bool on_init();
virtual void on_set_state() { m_keep_initial_values = (m_state == On) ? false : true; }
virtual void on_start_dragging(const BoundingBoxf3& box);
virtual void on_update(const Linef3& mouse_ray);
virtual void on_refresh() { m_keep_initial_values = false; }
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
@ -167,56 +166,52 @@ private:
class GLGizmoRotate3D : public GLGizmoBase
{
GLGizmoRotate m_x;
GLGizmoRotate m_y;
GLGizmoRotate m_z;
std::vector<GLGizmoRotate> m_gizmos;
public:
explicit GLGizmoRotate3D(GLCanvas3D& parent);
double get_angle_x() const { return m_x.get_angle(); }
void set_angle_x(double angle) { m_x.set_angle(angle); }
double get_angle_x() const { return m_gizmos[X].get_angle(); }
void set_angle_x(double angle) { m_gizmos[X].set_angle(angle); }
double get_angle_y() const { return m_y.get_angle(); }
void set_angle_y(double angle) { m_y.set_angle(angle); }
double get_angle_y() const { return m_gizmos[Y].get_angle(); }
void set_angle_y(double angle) { m_gizmos[Y].set_angle(angle); }
double get_angle_z() const { return m_z.get_angle(); }
void set_angle_z(double angle) { m_z.set_angle(angle); }
double get_angle_z() const { return m_gizmos[Z].get_angle(); }
void set_angle_z(double angle) { m_gizmos[Z].set_angle(angle); }
protected:
virtual bool on_init();
virtual void on_set_state()
{
m_x.set_state(m_state);
m_y.set_state(m_state);
m_z.set_state(m_state);
for (GLGizmoRotate& g : m_gizmos)
{
g.set_state(m_state);
}
}
virtual void on_set_hover_id()
{
m_x.set_hover_id(m_hover_id == 0 ? 0 : -1);
m_y.set_hover_id(m_hover_id == 1 ? 0 : -1);
m_z.set_hover_id(m_hover_id == 2 ? 0 : -1);
for (unsigned int i = 0; i < 3; ++i)
{
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
}
}
virtual void on_start_dragging();
virtual void on_start_dragging(const BoundingBoxf3& box);
virtual void on_stop_dragging();
virtual void on_update(const Linef3& mouse_ray)
{
m_x.update(mouse_ray);
m_y.update(mouse_ray);
m_z.update(mouse_ray);
}
virtual void on_refresh()
{
m_x.refresh();
m_y.refresh();
m_z.refresh();
for (GLGizmoRotate& g : m_gizmos)
{
g.update(mouse_ray);
}
}
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const
{
m_x.render_for_picking(box);
m_y.render_for_picking(box);
m_z.render_for_picking(box);
for (const GLGizmoRotate& g : m_gizmos)
{
g.render_for_picking(box);
}
}
};
@ -230,7 +225,8 @@ class GLGizmoScale3D : public GLGizmoBase
Vec3d m_starting_scale;
Vec3d m_starting_drag_position;
Vec3d m_starting_center;
bool m_show_starting_box;
BoundingBoxf3 m_starting_box;
public:
explicit GLGizmoScale3D(GLCanvas3D& parent);
@ -248,13 +244,14 @@ public:
protected:
virtual bool on_init();
virtual void on_start_dragging();
virtual void on_start_dragging(const BoundingBoxf3& box);
virtual void on_stop_dragging() { m_show_starting_box = false; }
virtual void on_update(const Linef3& mouse_ray);
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
private:
void render_box() const;
void render_box(const BoundingBoxf3& box) const;
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;
void do_scale_x(const Linef3& mouse_ray);
@ -265,6 +262,31 @@ private:
double calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const;
};
class GLGizmoMove3D : public GLGizmoBase
{
static const double Offset;
Vec3d m_position;
Vec3d m_starting_drag_position;
Vec3d m_starting_box_center;
public:
explicit GLGizmoMove3D(GLCanvas3D& parent);
const Vec3d& get_position() const { return m_position; }
void set_position(const Vec3d& position) { m_position = position; }
protected:
virtual bool on_init();
virtual void on_start_dragging(const BoundingBoxf3& box);
virtual void on_update(const Linef3& mouse_ray);
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
private:
double calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const;
};
class GLGizmoFlatten : public GLGizmoBase
{
// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.
@ -289,7 +311,7 @@ private:
std::vector<PlaneData> m_planes;
std::vector<Vec2d> m_instances_positions;
mutable std::unique_ptr<Vec3d> m_center = nullptr;
Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
void update_planes();
@ -303,11 +325,12 @@ public:
protected:
virtual bool on_init();
virtual void on_start_dragging();
virtual void on_start_dragging(const BoundingBoxf3& box);
virtual void on_update(const Linef3& mouse_ray) {}
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
virtual void on_set_state() {
virtual void on_set_state()
{
if (m_state == On && is_plane_update_necessary())
update_planes();
}

View File

@ -196,6 +196,8 @@ void set_main_frame(wxFrame *main_frame)
g_wxMainFrame = main_frame;
}
wxFrame* get_main_frame() { return g_wxMainFrame; }
void set_tab_panel(wxNotebook *tab_panel)
{
g_wxTabPanel = tab_panel;
@ -859,10 +861,6 @@ wxWindow* get_right_panel(){
return g_right_panel;
}
wxFrame* get_main_frame() {
return g_wxMainFrame;
}
wxNotebook * get_tab_panel() {
return g_wxTabPanel;
}

View File

@ -1533,13 +1533,20 @@ void update_settings_value()
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
if (m_selected_object_id < 0 || m_objects->size() <= m_selected_object_id) {
og->set_value("scale_x", 0);
og->set_value("position_x", 0);
og->set_value("position_y", 0);
og->set_value("position_z", 0);
og->set_value("scale_x", 0);
og->set_value("scale_y", 0);
og->set_value("scale_z", 0);
og->set_value("rotation_x", 0);
og->set_value("rotation_y", 0);
og->set_value("rotation_z", 0);
og->disable();
return;
}
g_is_percent_scale = boost::any_cast<wxString>(og->get_value("scale_unit")) == _("%");
update_position_values();
update_scale_values();
update_rotation_values();
og->enable();
@ -1715,50 +1722,94 @@ void update_extruder_in_config(const wxString& selection)
}
void update_scale_values()
{
update_scale_values((*m_objects)[m_selected_object_id]->instance_bounding_box(0).size(),
(*m_objects)[m_selected_object_id]->instances[0]->scaling_factor);
}
void update_scale_values(const Vec3d& size, float scaling_factor)
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
auto instance = (*m_objects)[m_selected_object_id]->instances.front();
auto size = (*m_objects)[m_selected_object_id]->instance_bounding_box(0).size();
if (g_is_percent_scale) {
auto scale = scaling_factor * 100;
auto scale = instance->scaling_factor * 100.0;
og->set_value("scale_x", int(scale));
og->set_value("scale_y", int(scale));
og->set_value("scale_z", int(scale));
}
else {
og->set_value("scale_x", int(size(0) + 0.5));
og->set_value("scale_y", int(size(1) + 0.5));
og->set_value("scale_z", int(size(2) + 0.5));
og->set_value("scale_x", int(instance->scaling_factor * size(0) + 0.5));
og->set_value("scale_y", int(instance->scaling_factor * size(1) + 0.5));
og->set_value("scale_z", int(instance->scaling_factor * size(2) + 0.5));
}
}
void update_position_values()
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
auto instance = (*m_objects)[m_selected_object_id]->instances.front();
og->set_value("position_x", int(instance->offset(0)));
og->set_value("position_y", int(instance->offset(1)));
og->set_value("position_z", 0);
}
void update_position_values(const Vec3d& position)
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
og->set_value("position_x", int(position(0)));
og->set_value("position_y", int(position(1)));
og->set_value("position_z", int(position(2)));
}
void update_scale_values(double scaling_factor)
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
// this is temporary
// to be able to update the values as size
// we need to store somewhere the original size
// or have it passed as parameter
if (!g_is_percent_scale)
og->set_value("scale_unit", _("%"));
auto scale = scaling_factor * 100.0;
og->set_value("scale_x", int(scale));
og->set_value("scale_y", int(scale));
og->set_value("scale_z", int(scale));
}
void update_rotation_values()
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
auto instance = (*m_objects)[m_selected_object_id]->instances.front();
og->set_value("rotation_x", 0);
og->set_value("rotation_y", 0);
auto rotation_z = (*m_objects)[m_selected_object_id]->instances[0]->rotation;
auto deg = int(Geometry::rad2deg(rotation_z));
// if (deg > 180) deg -= 360;
og->set_value("rotation_z", deg);
og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation)));
}
void update_rotation_value(const double angle, const std::string& axis)
void update_rotation_value(double angle, Axis axis)
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
int deg = int(Geometry::rad2deg(angle));
// if (deg>180) deg -= 360;
og->set_value("rotation_"+axis, deg);
std::string axis_str;
switch (axis)
{
case X:
{
axis_str = "rotation_x";
break;
}
case Y:
{
axis_str = "rotation_y";
break;
}
case Z:
{
axis_str = "rotation_z";
break;
}
}
og->set_value(axis_str, int(Geometry::rad2deg(angle)));
}
void set_uniform_scaling(const bool uniform_scale)

View File

@ -112,13 +112,16 @@ void update_settings_value();
void set_extruder_column_hidden(bool hide);
// update extruder in current config
void update_extruder_in_config(const wxString& selection);
// update position values displacements or "gizmos"
void update_position_values();
void update_position_values(const Vec3d& position);
// update scale values after scale unit changing or "gizmos"
void update_scale_values();
void update_scale_values(const Vec3d& size, float scale);
void update_scale_values(double scaling_factor);
// update rotation values object selection changing
void update_rotation_values();
// update rotation value after "gizmos"
void update_rotation_value(const double angle, const std::string& axis);
void update_rotation_value(double angle, Axis axis);
void set_uniform_scaling(const bool uniform_scale);
void on_begin_drag(wxDataViewEvent &event);

View File

@ -1,286 +0,0 @@
#include "PngExportDialog.hpp"
namespace Slic3r {
PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id,
const wxString& title, const wxPoint& pos,
const wxSize& size, long style ) :
wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
auto top_layout = new wxBoxSizer(wxHORIZONTAL);
// /////////////////////////////////////////////////////////////////////////
// Labels
// /////////////////////////////////////////////////////////////////////////
auto labels_layout = new wxGridSizer(6, 1, 0, 0);
// Input File picker label
auto filepick_text = new wxStaticText( this, wxID_ANY,
_("Target zip file"),
wxDefaultPosition,
wxDefaultSize, 0 );
filepick_text->Wrap( -1 );
labels_layout->Add( filepick_text, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
// Config file label
auto confpick_text = new wxStaticText( this, wxID_ANY,
_("Config file (optional)"),
wxDefaultPosition,
wxDefaultSize, 0 );
confpick_text->Wrap( -1 );
labels_layout->Add( confpick_text, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
confpick_text->Disable();
// Resolution layout
auto resotext = new wxStaticText( this, wxID_ANY,
_("Resolution (w, h) [px]"),
wxDefaultPosition, wxDefaultSize, 0 );
resotext->Wrap( -1 );
labels_layout->Add( resotext, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
// Bed size label
auto bed_size_text = new wxStaticText( this, wxID_ANY,
_("Bed size (w, h) [mm]"),
wxDefaultPosition,
wxDefaultSize, 0 );
bed_size_text->Wrap( -1 );
labels_layout->Add( bed_size_text, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
// Correction label
auto corr_text = new wxStaticText( this, wxID_ANY, _("Scale (x, y, z)"),
wxDefaultPosition, wxDefaultSize, 0 );
corr_text->Wrap( -1 );
labels_layout->Add( corr_text, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
// Exp time label
auto exp_text = new wxStaticText( this, wxID_ANY,
_("Exposure time [s]"),
wxDefaultPosition, wxDefaultSize, 0 );
exp_text->Wrap( -1 );
labels_layout->Add( exp_text, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
top_layout->Add( labels_layout, 0, wxEXPAND, 5 );
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// Body
// /////////////////////////////////////////////////////////////////////////
auto body_layout = new wxBoxSizer( wxVERTICAL );
// Input file picker
auto fpicklayout = new wxBoxSizer(wxHORIZONTAL);
filepick_ctl_ = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString,
_("Select a file"), wxT("*.zip"),
wxDefaultPosition, wxDefaultSize,
wxFLP_USE_TEXTCTRL | wxFLP_SAVE,
wxDefaultValidator,
wxT("filepick_ctl") );
fpicklayout->Add( filepick_ctl_, 1, wxALL | wxALIGN_CENTER, 5);
body_layout->Add( fpicklayout, 1, wxEXPAND, 5 );
auto ctlpicklayout = new wxBoxSizer(wxHORIZONTAL);
confpick_ctl_ = new wxFilePickerCtrl(
this, wxID_ANY, wxEmptyString, _("Select a file"),
wxT("*.json"), wxDefaultPosition, wxDefaultSize,
wxFLP_USE_TEXTCTRL | wxFLP_DEFAULT_STYLE, wxDefaultValidator,
wxT("filepick_ctl") );
confpick_ctl_->Disable();
ctlpicklayout->Add( confpick_ctl_, 1, wxALL | wxALIGN_CENTER, 5);
body_layout->Add( ctlpicklayout, 1, wxEXPAND, 5 );
// Resolution controls /////////////////////////////////////////////////////
auto res_spins_layout = new wxBoxSizer( wxHORIZONTAL );
spin_reso_width_ = new wxSpinCtrl( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 10000, 1440 );
res_spins_layout->Add( spin_reso_width_, 1, wxALIGN_CENTER|wxALL, 5 );
spin_reso_height_ = new wxSpinCtrl( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 10000, 2560 );
res_spins_layout->Add( spin_reso_height_, 1, wxALIGN_CENTER|wxALL, 5 );
reso_lock_btn_ = new wxToggleButton( this, wxID_ANY, _("Lock"),
wxDefaultPosition, wxDefaultSize, 0 );
reso_lock_btn_->SetValue(true);
res_spins_layout->Add( reso_lock_btn_, 0, wxALIGN_CENTER|wxALL, 5 );
body_layout->Add( res_spins_layout, 1, wxEXPAND, 5 );
// Bed size controls ///////////////////////////////////////////////////////
auto bed_spins_layout = new wxBoxSizer( wxHORIZONTAL );
bed_width_spin_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 1e6, 68.0 );
bed_spins_layout->Add( bed_width_spin_, 1, wxALIGN_CENTER|wxALL, 5 );
bed_height_spin_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 1e6, 120.0 );
bed_spins_layout->Add( bed_height_spin_, 1, wxALIGN_CENTER|wxALL, 5 );
bedsize_lock_btn_ = new wxToggleButton( this, wxID_ANY, _("Lock"),
wxDefaultPosition,
wxDefaultSize, 0 );
bedsize_lock_btn_->SetValue(true);
bed_spins_layout->Add( bedsize_lock_btn_, 0, wxALIGN_CENTER|wxALL, 5 );
body_layout->Add( bed_spins_layout, 1, wxEXPAND, 5 );
// Scale correction controls ///////////////////////////////////////////////
auto corr_layout = new wxBoxSizer( wxHORIZONTAL );
corr_spin_x_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 100, 1, 0.01 );
corr_spin_x_->SetDigits(3);
corr_spin_x_->SetMaxSize(wxSize(100, -1));
corr_layout->Add( corr_spin_x_, 0, wxALIGN_CENTER|wxALL, 5 );
corr_spin_y_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 100, 1, 0.01 );
corr_spin_y_->SetDigits(3);
corr_spin_y_->SetMaxSize(wxSize(100, -1));
corr_layout->Add( corr_spin_y_, 0, wxALIGN_CENTER|wxALL, 5 );
corr_spin_z_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 100, 1, 0.01 );
corr_spin_z_->SetDigits(3);
corr_spin_z_->SetMaxSize(wxSize(100, -1));
corr_layout->Add( corr_spin_z_, 0, wxALIGN_CENTER|wxALL, 5 );
corr_layout->Add( bedsize_lock_btn_->GetSize().GetWidth(), 0, 1, wxEXPAND, 5 );
body_layout->Add( corr_layout, 1, wxEXPAND, 5 );
// Exposure time controls /////////////////////////////////////////////////
auto exp_layout = new wxBoxSizer( wxHORIZONTAL );
exptime_spin_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, 0, 100, 1, 0.01 );
exptime_spin_->SetDigits(3);
exptime_spin_->SetMaxSize(wxSize(100, -1));
auto first_txt = new wxStaticText( this, wxID_ANY,
_("First exp. time"),
wxDefaultPosition,
wxDefaultSize, wxALIGN_RIGHT );
exptime_first_spin_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString,
wxDefaultPosition,
wxDefaultSize, wxSP_ARROW_KEYS,
0, 100, 1, 0.01 );
exptime_first_spin_->SetDigits(3);
exptime_first_spin_->SetMaxSize(wxSize(100, -1));
exp_layout->Add( exptime_spin_, 1, wxALIGN_CENTER|wxALL, 5 );
exp_layout->Add( first_txt, 1, wxALIGN_CENTER|wxALL, 5);
exp_layout->Add( exptime_first_spin_, 1, wxALIGN_CENTER|wxALL, 5 );
export_btn_ = new wxButton( this, wxID_ANY, _("Export"), wxDefaultPosition,
wxDefaultSize, 0, wxDefaultValidator,
wxT("export_btn") );
exp_layout->Add( export_btn_, 0, wxALIGN_CENTER|wxALL, 5 );
body_layout->Add( exp_layout, 1, wxEXPAND, 5 );
top_layout->Add( body_layout, 0, wxEXPAND, 5 );
// /////////////////////////////////////////////////////////////////////////
// Finalize
// /////////////////////////////////////////////////////////////////////////
this->SetSizer(top_layout);
this->Layout();
this->Fit();
this->SetMinSize(this->GetSize());
this->Centre( wxBOTH );
// Connect Events
filepick_ctl_->Connect(
wxEVT_COMMAND_FILEPICKER_CHANGED,
wxFileDirPickerEventHandler( PngExportDialog::onFileChanged ),
NULL, this );
spin_reso_width_->Connect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalResoSpin ),
NULL, this );
spin_reso_height_->Connect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalResoSpin ),
NULL, this );
reso_lock_btn_->Connect(
wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler( PngExportDialog::ResoLock ),
NULL, this );
bed_width_spin_->Connect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalBedSpin ),
NULL, this );
bed_height_spin_->Connect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalBedSpin ),
NULL, this );
bedsize_lock_btn_->Connect(
wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler( PngExportDialog::BedsizeLock ),
NULL, this );
export_btn_->Connect(
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( PngExportDialog::Close ), NULL, this );
}
PngExportDialog::~PngExportDialog()
{
// Disconnect Events
filepick_ctl_->Disconnect(
wxEVT_COMMAND_FILEPICKER_CHANGED,
wxFileDirPickerEventHandler( PngExportDialog::onFileChanged ),
NULL, this );
spin_reso_width_->Disconnect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalResoSpin ),
NULL, this );
spin_reso_height_->Disconnect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalResoSpin ),
NULL, this );
reso_lock_btn_->Disconnect(
wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler( PngExportDialog::ResoLock ),
NULL, this );
bed_width_spin_->Disconnect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalBedSpin ),
NULL, this );
bed_height_spin_->Disconnect(
wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( PngExportDialog::EvalBedSpin ),
NULL, this );
bedsize_lock_btn_->Disconnect(
wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler( PngExportDialog::BedsizeLock ),
NULL, this );
export_btn_->Disconnect(
wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( PngExportDialog::Close ), NULL, this );
}
}

View File

@ -1,67 +0,0 @@
#ifndef PNG_EXPORT_DIALOG_HPP
#define PNG_EXPORT_DIALOG_HPP
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include <wx/string.h>
#include <wx/stattext.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/filepicker.h>
#include <wx/spinctrl.h>
#include <wx/tglbtn.h>
#include <wx/button.h>
#include <wx/dialog.h>
#include "GUI.hpp"
namespace Slic3r {
///////////////////////////////////////////////////////////////////////////////
/// Class PngExportDialog
///////////////////////////////////////////////////////////////////////////////
class PngExportDialog : public wxDialog
{
private:
protected:
wxFilePickerCtrl* filepick_ctl_;
wxFilePickerCtrl* confpick_ctl_;
wxSpinCtrl* spin_reso_width_;
wxSpinCtrl* spin_reso_height_;
wxToggleButton* reso_lock_btn_;
wxSpinCtrlDouble* bed_width_spin_;
wxSpinCtrlDouble* bed_height_spin_;
wxToggleButton* bedsize_lock_btn_;
wxSpinCtrlDouble* exptime_spin_;
wxSpinCtrlDouble* exptime_first_spin_;
wxSpinCtrlDouble* corr_spin_x_;
wxSpinCtrlDouble* corr_spin_y_;
wxSpinCtrlDouble* corr_spin_z_;
wxButton* export_btn_;
// Virtual event handlers, overide them in your derived class
virtual void onFileChanged( wxFileDirPickerEvent& event ) { event.Skip(); }
virtual void EvalResoSpin( wxCommandEvent& event ) { event.Skip(); }
virtual void ResoLock( wxCommandEvent& event ) { event.Skip(); }
virtual void EvalBedSpin( wxCommandEvent& event ) { event.Skip(); }
virtual void BedsizeLock( wxCommandEvent& event ) { event.Skip(); }
virtual void Close( wxCommandEvent& /*event*/ ) { EndModal(wxID_OK); }
public:
PngExportDialog( wxWindow* parent, wxWindowID id = wxID_ANY,
const wxString& title = L("Slice to zipped PNG files"),
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxSize( 452,170 ),
long style = wxDEFAULT_DIALOG_STYLE );
~PngExportDialog();
};
}
#endif //PNG_EXPORT_DIALOG_HPP

View File

@ -0,0 +1,151 @@
#include "ProgressStatusBar.hpp"
#include <wx/timer.h>
#include <wx/gauge.h>
#include <wx/button.h>
#include <wx/statusbr.h>
#include <wx/frame.h>
#include "GUI.hpp"
#include <iostream>
namespace Slic3r {
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id):
self(new wxStatusBar(parent ? parent : GUI::get_main_frame(),
id == -1? wxID_ANY : id)),
timer_(new wxTimer(self)),
prog_ (new wxGauge(self,
wxGA_HORIZONTAL,
100,
wxDefaultPosition,
wxDefaultSize)),
cancelbutton_(new wxButton(self,
-1,
"Cancel",
wxDefaultPosition,
wxDefaultSize))
{
prog_->Hide();
cancelbutton_->Hide();
self->SetFieldsCount(3);
int w[] = {-1, 150, 155};
self->SetStatusWidths(3, w);
self->Bind(wxEVT_TIMER, [this](const wxTimerEvent&) {
if (prog_->IsShown()) timer_->Stop();
if(is_busy()) prog_->Pulse();
});
self->Bind(wxEVT_SIZE, [this](wxSizeEvent& event){
wxRect rect;
self->GetFieldRect(1, rect);
auto offset = 0;
cancelbutton_->Move(rect.GetX() + offset, rect.GetY() + offset);
cancelbutton_->SetSize(rect.GetWidth() - offset, rect.GetHeight());
self->GetFieldRect(2, rect);
prog_->Move(rect.GetX() + offset, rect.GetY() + offset);
prog_->SetSize(rect.GetWidth() - offset, rect.GetHeight());
event.Skip();
});
cancelbutton_->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) {
if(cancel_cb_) cancel_cb_();
cancelbutton_->Hide();
});
}
ProgressStatusBar::~ProgressStatusBar() {
if(timer_->IsRunning()) timer_->Stop();
}
int ProgressStatusBar::get_progress() const
{
return prog_->GetValue();
}
void ProgressStatusBar::set_progress(int val)
{
if(!prog_->IsShown()) show_progress(true);
if(val == prog_->GetRange()) {
prog_->SetValue(0);
show_progress(false);
} else {
prog_->SetValue(val);
}
}
int ProgressStatusBar::get_range() const
{
return prog_->GetRange();
}
void ProgressStatusBar::set_range(int val)
{
if(val != prog_->GetRange()) {
prog_->SetRange(val);
}
}
void ProgressStatusBar::show_progress(bool show)
{
prog_->Show(show);
prog_->Pulse();
}
void ProgressStatusBar::start_busy(int rate)
{
busy_ = true;
show_progress(true);
if (!timer_->IsRunning()) {
timer_->Start(rate);
}
}
void ProgressStatusBar::stop_busy()
{
timer_->Stop();
show_progress(false);
prog_->SetValue(0);
busy_ = false;
}
void ProgressStatusBar::set_cancel_callback(ProgressStatusBar::CancelFn ccb) {
cancel_cb_ = ccb;
if(ccb) cancelbutton_->Show();
else cancelbutton_->Hide();
}
void ProgressStatusBar::run(int rate)
{
if(!timer_->IsRunning()) {
timer_->Start(rate);
}
}
void ProgressStatusBar::embed(wxFrame *frame)
{
wxFrame* mf = frame? frame : GUI::get_main_frame();
mf->SetStatusBar(self);
}
void ProgressStatusBar::set_status_text(const std::string& txt)
{
self->SetStatusText(txt);
}
void ProgressStatusBar::show_cancel_button()
{
cancelbutton_->Show();
}
void ProgressStatusBar::hide_cancel_button()
{
cancelbutton_->Hide();
}
}

View File

@ -0,0 +1,64 @@
#ifndef PROGRESSSTATUSBAR_HPP
#define PROGRESSSTATUSBAR_HPP
#include <memory>
#include <functional>
class wxTimer;
class wxGauge;
class wxButton;
class wxTimerEvent;
class wxStatusBar;
class wxWindow;
class wxFrame;
namespace Slic3r {
/**
* @brief The ProgressStatusBar class is the widgets occupying the lower area
* of the Slicer main window. It consists of a message area to the left and a
* progress indication area to the right with an optional cancel button.
*/
class ProgressStatusBar {
wxStatusBar *self; // we cheat! It should be the base class but: perl!
wxTimer *timer_;
wxGauge *prog_;
wxButton *cancelbutton_;
public:
/// Cancel callback function type
using CancelFn = std::function<void()>;
ProgressStatusBar(wxWindow *parent = nullptr, int id = -1);
~ProgressStatusBar();
int get_progress() const;
void set_progress(int);
int get_range() const;
void set_range(int = 100);
void show_progress(bool);
void start_busy(int = 100);
void stop_busy();
inline bool is_busy() const { return busy_; }
void set_cancel_callback(CancelFn = CancelFn());
inline void remove_cancel_callback() { set_cancel_callback(); }
void run(int rate);
void embed(wxFrame *frame = nullptr);
void set_status_text(const std::string& txt);
// Temporary methods to satisfy Perl side
void show_cancel_button();
void hide_cancel_button();
private:
bool busy_ = false;
CancelFn cancel_cb_;
};
namespace GUI {
using Slic3r::ProgressStatusBar;
}
}
#endif // PROGRESSSTATUSBAR_HPP

View File

@ -10,9 +10,9 @@ namespace Slic3r {
/**
* @brief Generic progress indication interface.
*/
class IProgressIndicator {
class ProgressIndicator {
public:
using CancelFn = std::function<void(void)>; // Cancel functio signature.
using CancelFn = std::function<void(void)>; // Cancel function signature.
private:
float state_ = .0f, max_ = 1.f, step_;
@ -20,7 +20,7 @@ private:
public:
inline virtual ~IProgressIndicator() {}
inline virtual ~ProgressIndicator() {}
/// Get the maximum of the progress range.
float max() const { return max_; }
@ -28,14 +28,14 @@ public:
/// Get the current progress state
float state() const { return state_; }
/// Set the maximum of hte progress range
/// Set the maximum of the progress range
virtual void max(float maxval) { max_ = maxval; }
/// Set the current state of the progress.
virtual void state(float val) { state_ = val; }
/**
* @brief Number of states int the progress. Can be used insted of giving a
* @brief Number of states int the progress. Can be used instead of giving a
* maximum value.
*/
virtual void states(unsigned statenum) {
@ -45,14 +45,14 @@ public:
/// Message shown on the next status update.
virtual void message(const string&) = 0;
/// Title of the operaton.
/// Title of the operation.
virtual void title(const string&) = 0;
/// Formatted message for the next status update. Works just like sprinf.
/// Formatted message for the next status update. Works just like sprintf.
virtual void message_fmt(const string& fmt, ...);
/// Set up a cancel callback for the operation if feasible.
inline void on_cancel(CancelFn func) { cancelfunc_ = func; }
virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
/**
* Explicitly shut down the progress indicator and call the associated
@ -60,7 +60,7 @@ public:
*/
virtual void cancel() { cancelfunc_(); }
/// Convinience function to call message and status update in one function.
/// Convenience function to call message and status update in one function.
void update(float st, const string& msg) {
message(msg); state(st);
}

View File

@ -197,7 +197,7 @@ std::string Duet::get_upload_url(const std::string &filename) const
{
return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%")
% get_base_url()
% filename
% Http::url_encode(filename)
% timestamp_str()).str();
}
@ -230,7 +230,7 @@ std::string Duet::timestamp_str() const
auto tm = *std::localtime(&t);
char buffer[BUFFER_SIZE];
std::strftime(buffer, BUFFER_SIZE, "time=%Y-%d-%mT%H:%M:%S", &tm);
std::strftime(buffer, BUFFER_SIZE, "time=%Y-%m-%dT%H:%M:%S", &tm);
return std::string(buffer);
}
@ -248,9 +248,10 @@ wxString Duet::format_error(const std::string &body, const std::string &error, u
bool Duet::start_print(wxString &msg, const std::string &filename) const
{
bool res = false;
auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"")
% get_base_url()
% filename).str();
% Http::url_encode(filename)).str();
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
@ -275,5 +276,4 @@ int Duet::get_err_code_from_body(const std::string &body) const
return root.get<int>("err", 0);
}
}

View File

@ -421,6 +421,21 @@ bool Http::ca_file_supported()
return res;
}
std::string Http::url_encode(const std::string &str)
{
::CURL *curl = ::curl_easy_init();
if (curl == nullptr) {
return str;
}
char *ce = ::curl_easy_escape(curl, str.c_str(), str.length());
std::string encoded = std::string(ce);
::curl_free(ce);
::curl_easy_cleanup(curl);
return encoded;
}
std::ostream& operator<<(std::ostream &os, const Http::Progress &progress)
{
os << "Http::Progress("

View File

@ -98,6 +98,9 @@ public:
// Tells whether current backend supports seting up a CA file using ca_file()
static bool ca_file_supported();
// converts the given string to an url_encoded_string
static std::string url_encode(const std::string &str);
private:
Http(const std::string &url);

View File

@ -5,6 +5,7 @@
#include "slic3r/AppController.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include "slic3r/GUI/ProgressStatusBar.hpp"
%}
%name{Slic3r::PrintController} class PrintController {
@ -22,7 +23,7 @@
PrintController *print_ctl();
void set_model(Model *model);
void set_print(Print *print);
void set_global_progress_indicator(unsigned gauge_id, unsigned statusbar_id);
void set_global_progress_indicator(ProgressStatusBar *prs);
void arrange_model();
};

View File

@ -147,10 +147,7 @@ void select_current_object(int idx)
void remove_obj()
%code%{ Slic3r::GUI::remove(); %};
void update_rotation_value(double angle, const char *axis)
%code%{ Slic3r::GUI::update_rotation_value(angle, axis); %};
std::string fold_utf8_to_ascii(const char *src)
%code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %};

View File

@ -56,9 +56,9 @@
int volume_idx() const;
int instance_idx() const;
Clone<Vec3d> origin() const
%code%{ RETVAL = THIS->get_origin(); %};
%code%{ RETVAL = THIS->get_offset(); %};
void translate(double x, double y, double z)
%code%{ THIS->set_origin(THIS->get_origin() + Vec3d(x, y, z)); %};
%code%{ THIS->set_offset(THIS->get_offset() + Vec3d(x, y, z)); %};
Clone<BoundingBoxf3> bounding_box() const
%code%{ RETVAL = THIS->bounding_box; %};
@ -337,6 +337,14 @@ set_drag_by(canvas, value)
CODE:
_3DScene::set_drag_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), value);
std::string
get_select_by(canvas)
SV *canvas;
CODE:
RETVAL = _3DScene::get_select_by((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
OUTPUT:
RETVAL
bool
is_layers_editing_enabled(canvas)
SV *canvas;
@ -643,6 +651,13 @@ register_on_gizmo_rotate_callback(canvas, callback)
CODE:
_3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
void
register_on_gizmo_flatten_callback(canvas, callback)
SV *canvas;
SV *callback;
CODE:
_3DScene::register_on_gizmo_flatten_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
void
register_on_update_geometry_info_callback(canvas, callback)
SV *canvas;
@ -720,6 +735,13 @@ register_action_layersediting_callback(canvas, callback)
CODE:
_3DScene::register_action_layersediting_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
void
register_action_selectbyparts_callback(canvas, callback)
SV *canvas;
SV *callback;
CODE:
_3DScene::register_action_selectbyparts_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
void
reset_legend_texture()
CODE:
@ -736,6 +758,15 @@ load_model_object(canvas, model_object, obj_idx, instance_idxs)
OUTPUT:
RETVAL
int
get_first_volume_id(canvas, obj_idx)
SV *canvas;
int obj_idx;
CODE:
RETVAL = _3DScene::get_first_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), obj_idx);
OUTPUT:
RETVAL
std::vector<int>
load_model(canvas, model, obj_idx)
SV *canvas;

View File

@ -0,0 +1,48 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "slic3r/GUI/ProgressStatusBar.hpp"
%}
%name{Slic3r::GUI::ProgressStatusBar} class ProgressStatusBar {
ProgressStatusBar();
~ProgressStatusBar();
int GetProgress() const
%code%{ RETVAL=THIS->get_progress(); %};
void SetProgress(int val)
%code%{ THIS->set_progress(val); %};
void SetRange(int val = 100)
%code%{ THIS->set_range(val); %};
void ShowProgress(bool show)
%code%{ THIS->show_progress(show); %};
void StartBusy(int val = 100)
%code%{ THIS->start_busy(val); %};
void StopBusy()
%code%{ THIS->stop_busy(); %};
bool IsBusy() const
%code%{ RETVAL=THIS->is_busy(); %};
void Run(int rate)
%code%{ THIS->run(rate); %};
void Embed()
%code%{ THIS->embed(); %};
void SetStatusText(std::string txt)
%code%{ THIS->set_status_text(txt); %};
void ShowCancelButton()
%code%{ THIS->show_cancel_button(); %};
void HideCancelButton()
%code%{ THIS->hide_cancel_button(); %};
};

View File

@ -233,8 +233,10 @@ PresetCollection* O_OBJECT_SLIC3R
Ref<PresetCollection> O_OBJECT_SLIC3R_T
PresetBundle* O_OBJECT_SLIC3R
Ref<PresetBundle> O_OBJECT_SLIC3R_T
TabIface* O_OBJECT_SLIC3R
Ref<TabIface> O_OBJECT_SLIC3R_T
TabIface* O_OBJECT_SLIC3R
Ref<TabIface> O_OBJECT_SLIC3R_T
ProgressStatusBar* O_OBJECT_SLIC3R
Ref<ProgressStatusBar> O_OBJECT_SLIC3R_T
PresetUpdater* O_OBJECT_SLIC3R
Ref<PresetUpdater> O_OBJECT_SLIC3R_T

View File

@ -213,6 +213,7 @@
%typemap{PresetHints*};
%typemap{Ref<PresetHints>}{simple};
%typemap{TabIface*};
%typemap{ProgressStatusBar*};
%typemap{PrintRegionPtrs*};
%typemap{PrintObjectPtrs*};