mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Implemented support enforcers / blockers.
Reduced amount of full support interfaces similar to S3D.
This commit is contained in:
		
							parent
							
								
									6a1f15823f
								
							
						
					
					
						commit
						2a81408e8b
					
				
					 20 changed files with 461 additions and 163 deletions
				
			
		|  | @ -238,7 +238,7 @@ sub _update { | |||
|             my @expolygons = (); | ||||
|             foreach my $volume (@{$self->{model_object}->volumes}) { | ||||
|                 next if !$volume->mesh; | ||||
|                 next if $volume->modifier; | ||||
|                 next if !$volume->model_part; | ||||
|                 my $expp = $volume->mesh->slice([ $z_cut ])->[0]; | ||||
|                 push @expolygons, @$expp; | ||||
|             } | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ use base 'Wx::Panel'; | |||
| use constant ICON_OBJECT        => 0; | ||||
| use constant ICON_SOLIDMESH     => 1; | ||||
| use constant ICON_MODIFIERMESH  => 2; | ||||
| use constant ICON_SUPPORT_ENFORCER => 3; | ||||
| use constant ICON_SUPPORT_BLOCKER => 4; | ||||
| 
 | ||||
| sub new { | ||||
|     my ($class, $parent, %params) = @_; | ||||
|  | @ -35,7 +37,7 @@ sub new { | |||
|         y               => 0, | ||||
|         z               => 0, | ||||
|     }; | ||||
|      | ||||
| 
 | ||||
|     # create TreeCtrl | ||||
|     my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],  | ||||
|         wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT | ||||
|  | @ -46,6 +48,8 @@ sub new { | |||
|         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG));     # ICON_OBJECT | ||||
|         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG));   # ICON_SOLIDMESH | ||||
|         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG));    # ICON_MODIFIERMESH | ||||
|         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_enforcer.png"), wxBITMAP_TYPE_PNG));    # ICON_SUPPORT_ENFORCER | ||||
|         $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_blocker.png"), wxBITMAP_TYPE_PNG));    # ICON_SUPPORT_BLOCKER | ||||
|          | ||||
|         my $rootId = $tree->AddRoot("Object", ICON_OBJECT); | ||||
|         $tree->SetPlData($rootId, { type => 'object' }); | ||||
|  | @ -89,7 +93,14 @@ sub new { | |||
|     $self->{btn_move_down}->SetFont($Slic3r::GUI::small_font); | ||||
|      | ||||
|     # part settings panel | ||||
|     $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; }); | ||||
|     $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { | ||||
|         my ($key, $value) = @_; | ||||
|         wxTheApp->CallAfter(sub {  | ||||
|             $self->set_part_type($value) if ($key eq "part_type"); | ||||
|             $self->{part_settings_changed} = 1; | ||||
|             $self->_update_canvas;  | ||||
|         }); | ||||
|     }); | ||||
|     my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL); | ||||
|     $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); | ||||
| 
 | ||||
|  | @ -225,8 +236,11 @@ sub reload_tree { | |||
|     my $selectedId = $rootId; | ||||
|     foreach my $volume_id (0..$#{$object->volumes}) { | ||||
|         my $volume = $object->volumes->[$volume_id]; | ||||
|          | ||||
|         my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH; | ||||
|         my $icon =  | ||||
|             $volume->modifier ? ICON_MODIFIERMESH :  | ||||
|             $volume->support_enforcer ? ICON_SUPPORT_ENFORCER : | ||||
|             $volume->support_blocker ? ICON_SUPPORT_BLOCKER : | ||||
|             ICON_SOLIDMESH; | ||||
|         my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon); | ||||
|         if ($volume_id == $selected_volume_idx) { | ||||
|             $selectedId = $itemId; | ||||
|  | @ -288,6 +302,8 @@ sub selection_changed { | |||
|      | ||||
|     if (my $itemData = $self->get_selection) { | ||||
|         my ($config, @opt_keys); | ||||
|         my $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_OBJECT; | ||||
|         my $support = 0; | ||||
|         if ($itemData->{type} eq 'volume') { | ||||
|             # select volume in 3D preview | ||||
|             if ($self->{canvas}) { | ||||
|  | @ -301,16 +317,24 @@ sub selection_changed { | |||
|             # attach volume config to settings panel | ||||
|             my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; | ||||
|     | ||||
|             if ($volume->modifier) { | ||||
|             if (! $volume->model_part) { | ||||
|                 $self->{optgroup_movers}->enable; | ||||
|                 if ($volume->support_enforcer || $volume->support_blocker) { | ||||
|                     $support = 1; | ||||
|                     $type = $volume->support_enforcer ?  | ||||
|                         Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER : | ||||
|                         Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER; | ||||
|                 } else { | ||||
|                     $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER; | ||||
|                 } | ||||
|             } else { | ||||
|                 $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART; | ||||
|                 $self->{optgroup_movers}->disable; | ||||
|             } | ||||
|             $config = $volume->config; | ||||
|             $self->{staticbox}->SetLabel('Part Settings'); | ||||
|              | ||||
|             # get default values | ||||
|             @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys}; | ||||
|             @opt_keys = $support ? () : @{Slic3r::Config::PrintRegion->new->get_keys}; | ||||
|         } elsif ($itemData->{type} eq 'object') { | ||||
|             # select nothing in 3D preview | ||||
|              | ||||
|  | @ -323,33 +347,54 @@ sub selection_changed { | |||
|         # get default values | ||||
|         my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys); | ||||
| 
 | ||||
|        # decide which settings will be shown by default | ||||
|         # decide which settings will be shown by default | ||||
|         if ($itemData->{type} eq 'object') { | ||||
|             $config->set_ifndef('wipe_into_objects', 0); | ||||
|             $config->set_ifndef('wipe_into_infill', 0); | ||||
|         } | ||||
| 
 | ||||
|         # append default extruder | ||||
|         push @opt_keys, 'extruder'; | ||||
|         $default_config->set('extruder', 0); | ||||
|         $config->set_ifndef('extruder', 0); | ||||
|         if (! $support) { | ||||
|             push @opt_keys, 'extruder'; | ||||
|             $default_config->set('extruder', 0); | ||||
|             $config->set_ifndef('extruder', 0); | ||||
|         } | ||||
|         $self->{settings_panel}->set_type($type); | ||||
|         $self->{settings_panel}->set_default_config($default_config); | ||||
|         $self->{settings_panel}->set_config($config); | ||||
|         $self->{settings_panel}->set_opt_keys(\@opt_keys); | ||||
| 
 | ||||
|         # disable minus icon to remove the settings | ||||
|         if ($itemData->{type} eq 'object') { | ||||
|             $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]); | ||||
| 	} else { | ||||
|             $self->{settings_panel}->set_fixed_options([qw(extruder)]); | ||||
|         } | ||||
| 
 | ||||
|         my $fixed_options =  | ||||
|             ($itemData->{type} eq 'object') ? [qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)] : | ||||
|             $support ? [] : [qw(extruder)]; | ||||
|         $self->{settings_panel}->set_fixed_options($fixed_options); | ||||
|         $self->{settings_panel}->enable; | ||||
|     } | ||||
|      | ||||
|     Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas}; | ||||
| } | ||||
| 
 | ||||
| sub set_part_type | ||||
| { | ||||
|     my ($self, $part_type) = @_; | ||||
|     if (my $itemData = $self->get_selection) { | ||||
|         if ($itemData->{type} eq 'volume') { | ||||
|             my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; | ||||
|             if ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER || | ||||
|                 $part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART) {             | ||||
|                 $volume->set_modifier($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER); | ||||
|             } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER) { | ||||
|                 $volume->set_support_enforcer; | ||||
|             } elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER) { | ||||
|                 $volume->set_support_blocker; | ||||
|             } | ||||
|             # We want the icon of the selected item to be changed as well. | ||||
|             $self->reload_tree($itemData->{volume_id}); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub on_btn_load { | ||||
|     my ($self, $is_modifier) = @_; | ||||
|      | ||||
|  |  | |||
|  | @ -7,15 +7,20 @@ use warnings; | |||
| use utf8; | ||||
| 
 | ||||
| use List::Util qw(first); | ||||
| use Wx qw(:misc :sizer :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG | ||||
|     wxTheApp); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_LEFT_DOWN EVT_MENU); | ||||
| use Wx qw(:misc :sizer :button :combobox wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxTheApp); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_LEFT_DOWN EVT_MENU); | ||||
| use base 'Wx::ScrolledWindow'; | ||||
| 
 | ||||
| use constant ICON_MATERIAL      => 0; | ||||
| use constant ICON_SOLIDMESH     => 1; | ||||
| use constant ICON_MODIFIERMESH  => 2; | ||||
| 
 | ||||
| use constant TYPE_OBJECT        => -1; | ||||
| use constant TYPE_PART          => 0; | ||||
| use constant TYPE_MODIFIER      => 1; | ||||
| use constant TYPE_SUPPORT_ENFORCER => 2; | ||||
| use constant TYPE_SUPPORT_BLOCKER => 3; | ||||
| 
 | ||||
| my %icons = ( | ||||
|     'Advanced'              => 'wand.png', | ||||
|     'Extruders'             => 'funnel.png', | ||||
|  | @ -36,13 +41,14 @@ sub new { | |||
|     $self->{config} = Slic3r::Config->new; | ||||
|     # On change callback. | ||||
|     $self->{on_change} = $params{on_change}; | ||||
|     $self->{type} = TYPE_OBJECT; | ||||
|     $self->{fixed_options} = {}; | ||||
|      | ||||
|     $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); | ||||
|      | ||||
|     $self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0); | ||||
|      | ||||
| 
 | ||||
|     # option selector | ||||
|     { | ||||
|         # create the button | ||||
|  | @ -110,6 +116,16 @@ sub set_opt_keys { | |||
|     $self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ]; | ||||
| } | ||||
| 
 | ||||
| sub set_type { | ||||
|     my ($self, $type) = @_; | ||||
|     $self->{type} = $type; | ||||
|     if ($type == TYPE_SUPPORT_ENFORCER || $type == TYPE_SUPPORT_BLOCKER) { | ||||
|         $self->{btn_add}->Hide; | ||||
|     } else { | ||||
|         $self->{btn_add}->Show; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub set_fixed_options { | ||||
|     my ($self, $opt_keys) = @_; | ||||
|     $self->{fixed_options} = { map {$_ => 1} @$opt_keys }; | ||||
|  | @ -121,12 +137,28 @@ sub update_optgroup { | |||
|      | ||||
|     $self->{options_sizer}->Clear(1); | ||||
|     return if !defined $self->{config}; | ||||
|      | ||||
| 
 | ||||
|     if ($self->{type} != TYPE_OBJECT) { | ||||
|         my $label = Wx::StaticText->new($self, -1, "Type:"), | ||||
|         my $selection = [ "Part", "Modifier", "Support Enforcer", "Support Blocker" ]; | ||||
|         my $field = Wx::ComboBox->new($self, -1, $selection->[$self->{type}], wxDefaultPosition, Wx::Size->new(160, -1), $selection, wxCB_READONLY); | ||||
|         my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         $sizer->Add($label, 1, wxEXPAND | wxALL, 5); | ||||
|         $sizer->Add($field, 0, wxALL, 5); | ||||
|         EVT_COMBOBOX($self, $field, sub { | ||||
|             my $idx = $field->GetSelection;  # get index of selected value | ||||
|             $self->{on_change}->("part_type", $idx) if $self->{on_change}; | ||||
|         }); | ||||
|         $self->{options_sizer}->Add($sizer, 0, wxEXPAND | wxBOTTOM, 0); | ||||
|     } | ||||
| 
 | ||||
|     my %categories = (); | ||||
|     foreach my $opt_key (@{$self->{config}->get_keys}) { | ||||
|         my $category = $Slic3r::Config::Options->{$opt_key}{category}; | ||||
|         $categories{$category} ||= []; | ||||
|         push @{$categories{$category}}, $opt_key; | ||||
|     if ($self->{type} != TYPE_SUPPORT_ENFORCER && $self->{type} != TYPE_SUPPORT_BLOCKER) { | ||||
|         foreach my $opt_key (@{$self->{config}->get_keys}) { | ||||
|             my $category = $Slic3r::Config::Options->{$opt_key}{category}; | ||||
|             $categories{$category} ||= []; | ||||
|             push @{$categories{$category}}, $opt_key; | ||||
|         } | ||||
|     } | ||||
|     foreach my $category (sort keys %categories) { | ||||
|         my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								resources/icons/support_blocker.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icons/support_blocker.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 656 B | 
							
								
								
									
										
											BIN
										
									
								
								resources/icons/support_enforcer.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icons/support_enforcer.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 509 B | 
|  | @ -1227,6 +1227,8 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo | |||
| 
 | ||||
| Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const | ||||
| { | ||||
| 	assert(std::abs(2 * offset) < m_resolution); | ||||
| 
 | ||||
| 	typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType; | ||||
| 	// 0) Prepare a binary grid.
 | ||||
| 	size_t cell_rows = m_rows + 2; | ||||
|  |  | |||
|  | @ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume"; | |||
| 
 | ||||
| const char* NAME_KEY = "name"; | ||||
| const char* MODIFIER_KEY = "modifier"; | ||||
| const char* VOLUME_TYPE_KEY = "volume_type"; | ||||
| 
 | ||||
| const unsigned int VALID_OBJECT_TYPES_COUNT = 1; | ||||
| const char* VALID_OBJECT_TYPES[] = | ||||
|  | @ -1501,7 +1502,9 @@ namespace Slic3r { | |||
|                 if (metadata.key == NAME_KEY) | ||||
|                     volume->name = metadata.value; | ||||
|                 else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1")) | ||||
|                     volume->modifier = true; | ||||
|                     volume->set_type(ModelVolume::PARAMETER_MODIFIER); | ||||
|                 else if (metadata.key == VOLUME_TYPE_KEY) | ||||
|                     volume->set_type(ModelVolume::type_from_string(metadata.value)); | ||||
|                 else | ||||
|                     volume->config.set_deserialize(metadata.key, metadata.value); | ||||
|             } | ||||
|  | @ -2017,9 +2020,12 @@ namespace Slic3r { | |||
|                             if (!volume->name.empty()) | ||||
|                                 stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n"; | ||||
| 
 | ||||
|                             // stores volume's modifier field
 | ||||
|                             if (volume->modifier) | ||||
|                             // stores volume's modifier field (legacy, to support old slicers)
 | ||||
|                             if (volume->is_modifier()) | ||||
|                                 stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n"; | ||||
|                             // stores volume's type (overrides the modifier field above)
 | ||||
|                             stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<  | ||||
|                                 VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n"; | ||||
| 
 | ||||
|                             // stores volume's config data
 | ||||
|                             for (const std::string& key : volume->config.keys()) | ||||
|  |  | |||
|  | @ -458,9 +458,14 @@ void AMFParserContext::endElement(const char * /* name */) | |||
| 					p = end + 1; | ||||
|                 } | ||||
|                 m_object->layer_height_profile_valid = true; | ||||
|             } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) { | ||||
|                 // Is this volume a modifier volume?
 | ||||
|                 m_volume->modifier = atoi(m_value[1].c_str()) == 1; | ||||
|             } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) { | ||||
|                 if (strcmp(opt_key, "modifier") == 0) { | ||||
|                     // Is this volume a modifier volume?
 | ||||
|                     // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
 | ||||
|                     m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); | ||||
|                 } else if (strcmp(opt_key, "volume_type") == 0) { | ||||
|                     m_volume->set_type(ModelVolume::type_from_string(m_value[1])); | ||||
|                 } | ||||
|             } | ||||
|         } else if (m_path.size() == 3) { | ||||
|             if (m_path[1] == NODE_TYPE_MATERIAL) { | ||||
|  | @ -781,8 +786,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c | |||
|                 stream << "        <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n"; | ||||
|             if (!volume->name.empty()) | ||||
|                 stream << "        <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n"; | ||||
|             if (volume->modifier) | ||||
|             if (volume->is_modifier()) | ||||
|                 stream << "        <metadata type=\"slic3r.modifier\">1</metadata>\n"; | ||||
|             stream << "        <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n"; | ||||
|             for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) { | ||||
|                 stream << "        <triangle>\n"; | ||||
|                 for (int j = 0; j < 3; ++j) | ||||
|  |  | |||
|  | @ -609,7 +609,7 @@ const BoundingBoxf3& ModelObject::bounding_box() const | |||
|     if (! m_bounding_box_valid) { | ||||
|         BoundingBoxf3 raw_bbox; | ||||
|         for (const ModelVolume *v : this->volumes) | ||||
|             if (! v->modifier) | ||||
|             if (v->is_model_part()) | ||||
|                 raw_bbox.merge(v->mesh.bounding_box()); | ||||
|         BoundingBoxf3 bb; | ||||
|         for (const ModelInstance *i : this->instances) | ||||
|  | @ -640,7 +640,7 @@ TriangleMesh ModelObject::raw_mesh() const | |||
| { | ||||
|     TriangleMesh mesh; | ||||
|     for (const ModelVolume *v : this->volumes) | ||||
|         if (! v->modifier) | ||||
|         if (v->is_model_part()) | ||||
|             mesh.merge(v->mesh); | ||||
|     return mesh; | ||||
| } | ||||
|  | @ -651,7 +651,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const | |||
| { | ||||
|     BoundingBoxf3 bb; | ||||
|     for (const ModelVolume *v : this->volumes) | ||||
|         if (! v->modifier) { | ||||
|         if (v->is_model_part()) { | ||||
|             if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); | ||||
|             bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true)); | ||||
|         } | ||||
|  | @ -663,7 +663,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ | |||
| { | ||||
|     BoundingBoxf3 bb; | ||||
|     for (ModelVolume *v : this->volumes) | ||||
|         if (! v->modifier) | ||||
|         if (v->is_model_part()) | ||||
|             bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate)); | ||||
|     return bb; | ||||
| } | ||||
|  | @ -674,7 +674,7 @@ void ModelObject::center_around_origin() | |||
|     // center this object around the origin
 | ||||
| 	BoundingBoxf3 bb; | ||||
| 	for (ModelVolume *v : this->volumes) | ||||
|         if (! v->modifier) | ||||
|         if (v->is_model_part()) | ||||
| 			bb.merge(v->mesh.bounding_box()); | ||||
|      | ||||
|     // first align to origin on XYZ
 | ||||
|  | @ -778,7 +778,7 @@ size_t ModelObject::facets_count() const | |||
| { | ||||
|     size_t num = 0; | ||||
|     for (const ModelVolume *v : this->volumes) | ||||
|         if (! v->modifier) | ||||
|         if (v->is_model_part()) | ||||
|             num += v->mesh.stl.stats.number_of_facets; | ||||
|     return num; | ||||
| } | ||||
|  | @ -786,7 +786,7 @@ size_t ModelObject::facets_count() const | |||
| bool ModelObject::needed_repair() const | ||||
| { | ||||
|     for (const ModelVolume *v : this->volumes) | ||||
|         if (! v->modifier && v->mesh.needed_repair()) | ||||
|         if (v->is_model_part() && v->mesh.needed_repair()) | ||||
|             return true; | ||||
|     return false; | ||||
| } | ||||
|  | @ -802,7 +802,7 @@ void ModelObject::cut(coordf_t z, Model* model) const | |||
|     lower->input_file = ""; | ||||
|      | ||||
|     for (ModelVolume *volume : this->volumes) { | ||||
|         if (volume->modifier) { | ||||
|         if (! volume->is_model_part()) { | ||||
|             // don't cut modifiers
 | ||||
|             upper->add_volume(*volume); | ||||
|             lower->add_volume(*volume); | ||||
|  | @ -854,7 +854,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) | |||
|         ModelVolume* new_volume = new_object->add_volume(*mesh); | ||||
|         new_volume->name        = volume->name; | ||||
|         new_volume->config      = volume->config; | ||||
|         new_volume->modifier    = volume->modifier; | ||||
|         new_volume->set_type(volume->type()); | ||||
|         new_volume->material_id(volume->material_id()); | ||||
|          | ||||
|         new_objects->push_back(new_object); | ||||
|  | @ -868,7 +868,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_ | |||
| { | ||||
|     for (const ModelVolume* vol : this->volumes) | ||||
|     { | ||||
|         if (!vol->modifier) | ||||
|         if (vol->is_model_part()) | ||||
|         { | ||||
|             for (ModelInstance* inst : this->instances) | ||||
|             { | ||||
|  | @ -972,6 +972,37 @@ const TriangleMesh& ModelVolume::get_convex_hull() const | |||
|     return m_convex_hull; | ||||
| } | ||||
| 
 | ||||
| ModelVolume::Type ModelVolume::type_from_string(const std::string &s) | ||||
| { | ||||
|     // Legacy support
 | ||||
|     if (s == "0") | ||||
|         return MODEL_PART; | ||||
|     if (s == "1") | ||||
|         return PARAMETER_MODIFIER; | ||||
|     // New type (supporting the support enforcers & blockers)
 | ||||
|     if (s == "ModelPart") | ||||
|         return MODEL_PART; | ||||
|     if (s == "ParameterModifier") | ||||
|         return PARAMETER_MODIFIER; | ||||
|     if (s == "SupportEnforcer") | ||||
|         return SUPPORT_ENFORCER; | ||||
|     if (s == "SupportBlocker") | ||||
|         return SUPPORT_BLOCKER; | ||||
| } | ||||
| 
 | ||||
| std::string ModelVolume::type_to_string(const Type t) | ||||
| { | ||||
|     switch (t) { | ||||
|     case MODEL_PART:         return "ModelPart"; | ||||
|     case PARAMETER_MODIFIER: return "ParameterModifier"; | ||||
|     case SUPPORT_ENFORCER:   return "SupportEnforcer"; | ||||
|     case SUPPORT_BLOCKER:    return "SupportBlocker"; | ||||
|     default: | ||||
|         assert(false); | ||||
|         return "ModelPart"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Split this volume, append the result to the object owning this volume.
 | ||||
| // Return the number of volumes created from this one.
 | ||||
| // This is useful to assign different materials to different volumes of an object.
 | ||||
|  |  | |||
|  | @ -165,15 +165,27 @@ public: | |||
|     // Configuration parameters specific to an object model geometry or a modifier volume, 
 | ||||
|     // overriding the global Slic3r settings and the ModelObject settings.
 | ||||
|     DynamicPrintConfig config; | ||||
|     // Is it an object to be printed, or a modifier volume?
 | ||||
|     bool modifier; | ||||
|      | ||||
| 
 | ||||
|     enum Type { | ||||
|         MODEL_TYPE_INVALID = -1, | ||||
|         MODEL_PART = 0, | ||||
|         PARAMETER_MODIFIER, | ||||
|         SUPPORT_ENFORCER, | ||||
|         SUPPORT_BLOCKER, | ||||
|     }; | ||||
| 
 | ||||
|     // A parent object owning this modifier volume.
 | ||||
|     ModelObject* get_object() const { return this->object; }; | ||||
|     ModelObject*        get_object() const { return this->object; }; | ||||
|     Type                type() const { return m_type; } | ||||
|     void                set_type(const Type t) { m_type = t; } | ||||
|     bool                is_model_part()         const { return m_type == MODEL_PART; } | ||||
|     bool                is_modifier()           const { return m_type == PARAMETER_MODIFIER; } | ||||
|     bool                is_support_enforcer()   const { return m_type == SUPPORT_ENFORCER; } | ||||
|     bool                is_support_blocker()    const { return m_type == SUPPORT_BLOCKER; } | ||||
|     t_model_material_id material_id() const { return this->_material_id; } | ||||
|     void material_id(t_model_material_id material_id); | ||||
|     ModelMaterial* material() const; | ||||
|     void set_material(t_model_material_id material_id, const ModelMaterial &material); | ||||
|     void                material_id(t_model_material_id material_id); | ||||
|     ModelMaterial*      material() const; | ||||
|     void                set_material(t_model_material_id material_id, const ModelMaterial &material); | ||||
|     // Split this volume, append the result to the object owning this volume.
 | ||||
|     // Return the number of volumes created from this one.
 | ||||
|     // This is useful to assign different materials to different volumes of an object.
 | ||||
|  | @ -184,24 +196,30 @@ public: | |||
|     void calculate_convex_hull(); | ||||
|     const TriangleMesh& get_convex_hull() const; | ||||
| 
 | ||||
|     // Helpers for loading / storing into AMF / 3MF files.
 | ||||
|     static Type         type_from_string(const std::string &s); | ||||
|     static std::string  type_to_string(const Type t); | ||||
| 
 | ||||
| private: | ||||
|     // Parent object owning this ModelVolume.
 | ||||
|     ModelObject* object; | ||||
|     t_model_material_id _material_id; | ||||
|     ModelObject*            object; | ||||
|     // Is it an object to be printed, or a modifier volume?
 | ||||
|     Type                    m_type; | ||||
|     t_model_material_id     _material_id; | ||||
|      | ||||
|     ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object) | ||||
|     ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object) | ||||
|     { | ||||
|         if (mesh.stl.stats.number_of_facets > 1) | ||||
|             calculate_convex_hull(); | ||||
|     } | ||||
|     ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {} | ||||
|     ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} | ||||
|     ModelVolume(ModelObject *object, const ModelVolume &other) : | ||||
|         name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object) | ||||
|         name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) | ||||
|     { | ||||
|         this->material_id(other.material_id()); | ||||
|     } | ||||
|     ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : | ||||
|         name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object) | ||||
|         name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) | ||||
|     { | ||||
|         this->material_id(other.material_id()); | ||||
|         if (mesh.stl.stats.number_of_facets > 1) | ||||
|  |  | |||
|  | @ -362,9 +362,12 @@ void Print::add_model_object(ModelObject* model_object, int idx) | |||
|     // Invalidate all print steps.
 | ||||
|     this->invalidate_all_steps(); | ||||
| 
 | ||||
|     for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { | ||||
|     size_t volume_id = 0; | ||||
|     for (const ModelVolume *volume : model_object->volumes) { | ||||
|         if (! volume->is_model_part() && ! volume->is_modifier()) | ||||
|             continue; | ||||
|         // Get the config applied to this volume.
 | ||||
|         PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]); | ||||
|         PrintRegionConfig config = this->_region_config_from_model_volume(*volume); | ||||
|         // Find an existing print region with the same config.
 | ||||
|         size_t region_id = size_t(-1); | ||||
|         for (size_t i = 0; i < this->regions.size(); ++ i) | ||||
|  | @ -379,6 +382,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) | |||
|         } | ||||
|         // Assign volume to a region.
 | ||||
|         object->add_region_volume(region_id, volume_id); | ||||
|         ++ volume_id; | ||||
|     } | ||||
| 
 | ||||
|     // Apply config to print object.
 | ||||
|  | @ -853,7 +857,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const | |||
|     for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { | ||||
|         ModelVolume *volume = model_object->volumes[volume_id]; | ||||
|         //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
 | ||||
|         if (! volume->material_id().empty() && ! volume->config.has("extruder")) | ||||
|         if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder")) | ||||
|             volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -214,6 +214,10 @@ public: | |||
| 
 | ||||
|     bool is_printable() const { return !this->_shifted_copies.empty(); } | ||||
| 
 | ||||
|     // Helpers to slice support enforcer / blocker meshes by the support generator.
 | ||||
|     std::vector<ExPolygons>     slice_support_enforcers() const; | ||||
|     std::vector<ExPolygons>     slice_support_blockers() const; | ||||
| 
 | ||||
| private: | ||||
|     Print* _print; | ||||
|     ModelObject* _model_object; | ||||
|  | @ -225,6 +229,7 @@ private: | |||
|     ~PrintObject() {} | ||||
| 
 | ||||
|     std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier); | ||||
|     std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const; | ||||
| }; | ||||
| 
 | ||||
| typedef std::vector<PrintObject*> PrintObjectPtrs; | ||||
|  |  | |||
|  | @ -1734,7 +1734,7 @@ PrintConfigDef::PrintConfigDef() | |||
|                    "for the first object layer."); | ||||
|     def->sidetext = L("mm"); | ||||
|     def->cli = "support-material-contact-distance=f"; | ||||
|     def->min = 0; | ||||
| //    def->min = 0;
 | ||||
|     def->enum_values.push_back("0"); | ||||
|     def->enum_values.push_back("0.2"); | ||||
| 	def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str()); | ||||
|  |  | |||
|  | @ -1320,29 +1320,62 @@ end: | |||
| 
 | ||||
| std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier) | ||||
| { | ||||
|     std::vector<ExPolygons> layers; | ||||
|     std::vector<const ModelVolume*> volumes; | ||||
|     if (region_id < this->region_volumes.size()) { | ||||
|         std::vector<int> &volumes = this->region_volumes[region_id]; | ||||
|         if (! volumes.empty()) { | ||||
|             // Compose mesh.
 | ||||
|             //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
 | ||||
|             TriangleMesh mesh; | ||||
|             for (int volume_id : volumes) { | ||||
|                 ModelVolume *volume = this->model_object()->volumes[volume_id]; | ||||
|                 if (volume->modifier == modifier) | ||||
|                     mesh.merge(volume->mesh); | ||||
|             } | ||||
|             if (mesh.stl.stats.number_of_facets > 0) { | ||||
|                 // transform mesh
 | ||||
|                 // we ignore the per-instance transformations currently and only 
 | ||||
|                 // consider the first one
 | ||||
|                 this->model_object()->instances.front()->transform_mesh(&mesh, true); | ||||
|                 // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
 | ||||
|                 mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z)); | ||||
|                 // perform actual slicing
 | ||||
|                 TriangleMeshSlicer mslicer(&mesh); | ||||
|                 mslicer.slice(z, &layers); | ||||
|             } | ||||
|         for (int volume_id : this->region_volumes[region_id]) { | ||||
|             const ModelVolume *volume = this->model_object()->volumes[volume_id]; | ||||
|             if (modifier ? volume->is_modifier() : volume->is_model_part()) | ||||
|                 volumes.emplace_back(volume); | ||||
|         } | ||||
|     } | ||||
|     return this->_slice_volumes(z, volumes); | ||||
| } | ||||
| 
 | ||||
| std::vector<ExPolygons> PrintObject::slice_support_enforcers() const | ||||
| { | ||||
|     std::vector<const ModelVolume*> volumes; | ||||
|     for (const ModelVolume *volume : this->model_object()->volumes) | ||||
|         if (volume->is_support_enforcer()) | ||||
|             volumes.emplace_back(volume); | ||||
|     std::vector<float> zs; | ||||
|     zs.reserve(this->layers.size()); | ||||
|     for (const Layer *l : this->layers) | ||||
|         zs.emplace_back(l->slice_z); | ||||
|     return this->_slice_volumes(zs, volumes); | ||||
| } | ||||
| 
 | ||||
| std::vector<ExPolygons> PrintObject::slice_support_blockers() const | ||||
| { | ||||
|     std::vector<const ModelVolume*> volumes; | ||||
|     for (const ModelVolume *volume : this->model_object()->volumes) | ||||
|         if (volume->is_support_blocker()) | ||||
|             volumes.emplace_back(volume); | ||||
|     std::vector<float> zs; | ||||
|     zs.reserve(this->layers.size()); | ||||
|     for (const Layer *l : this->layers) | ||||
|         zs.emplace_back(l->slice_z); | ||||
|     return this->_slice_volumes(zs, volumes); | ||||
| } | ||||
| 
 | ||||
| std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const | ||||
| { | ||||
|     std::vector<ExPolygons> layers; | ||||
|     if (! volumes.empty()) { | ||||
|         // Compose mesh.
 | ||||
|         //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
 | ||||
|         TriangleMesh mesh; | ||||
|         for (const ModelVolume *v : volumes) | ||||
|             mesh.merge(v->mesh); | ||||
|         if (mesh.stl.stats.number_of_facets > 0) { | ||||
|             // transform mesh
 | ||||
|             // we ignore the per-instance transformations currently and only 
 | ||||
|             // consider the first one
 | ||||
|             this->model_object()->instances.front()->transform_mesh(&mesh, true); | ||||
|             // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
 | ||||
|             mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z)); | ||||
|             // perform actual slicing
 | ||||
|             TriangleMeshSlicer mslicer(&mesh); | ||||
|             mslicer.slice(z, &layers); | ||||
|         } | ||||
|     } | ||||
|     return layers; | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const | |||
| 
 | ||||
| coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const | ||||
| { | ||||
|     return this->nozzle_dmr_avg(print_config) * this->config.bridge_flow_ratio.value; | ||||
|     return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive( | |||
|     // 1) Initialize the SlicingAdaptive class with the object meshes.
 | ||||
|     SlicingAdaptive as; | ||||
|     as.set_slicing_parameters(slicing_params); | ||||
|     for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it) | ||||
|         if (! (*it)->modifier) | ||||
|             as.add_mesh(&(*it)->mesh); | ||||
|     for (const ModelVolume *volume : volumes) | ||||
|         if (volume->is_model_part()) | ||||
|             as.add_mesh(&volume->mesh); | ||||
|     as.prepare(); | ||||
| 
 | ||||
|     // 2) Generate layers using the algorithm of @platsch 
 | ||||
|  |  | |||
|  | @ -283,7 +283,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) | |||
|         object, bottom_contacts, top_contacts, layer_storage); | ||||
| 
 | ||||
| //    this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
 | ||||
|     this->trim_support_layers_by_object(object, top_contacts, 0., 0., m_gap_xy); | ||||
|     this->trim_support_layers_by_object(object, top_contacts,  | ||||
|         m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,  | ||||
|         m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); | ||||
| 
 | ||||
| #ifdef SLIC3R_DEBUG | ||||
|     for (const MyLayer *layer : top_contacts) | ||||
|  | @ -428,29 +430,17 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t | |||
| { | ||||
|     // 1) Count the new polygons first.
 | ||||
|     size_t n_polygons_new = 0; | ||||
|     for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) { | ||||
|         const LayerRegion       ®ion = *(*it_region); | ||||
|         const SurfaceCollection &slices = region.slices; | ||||
|         for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) { | ||||
|             const Surface &surface = *it; | ||||
|     for (const LayerRegion *region : layer.regions) | ||||
|         for (const Surface &surface : region->slices.surfaces) | ||||
|             if (surface.surface_type == surface_type) | ||||
|                 n_polygons_new += surface.expolygon.holes.size() + 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 2) Collect the new polygons.
 | ||||
|     Polygons out; | ||||
|     out.reserve(n_polygons_new); | ||||
|     for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) { | ||||
|         const LayerRegion       ®ion = *(*it_region); | ||||
|         const SurfaceCollection &slices = region.slices; | ||||
|         for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) { | ||||
|             const Surface &surface = *it; | ||||
|     for (const LayerRegion *region : layer.regions) | ||||
|         for (const Surface &surface : region->slices.surfaces) | ||||
|             if (surface.surface_type == surface_type) | ||||
|                 polygons_append(out, surface.expolygon); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
|  | @ -460,8 +450,8 @@ Polygons collect_slices_outer(const Layer &layer) | |||
| { | ||||
|     Polygons out; | ||||
|     out.reserve(out.size() + layer.slices.expolygons.size()); | ||||
|     for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it) | ||||
|         out.push_back(it->contour); | ||||
|     for (const ExPolygon &expoly : layer.slices.expolygons) | ||||
|         out.emplace_back(expoly.contour); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
|  | @ -469,8 +459,11 @@ class SupportGridPattern | |||
| { | ||||
| public: | ||||
|     SupportGridPattern( | ||||
|         // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
 | ||||
|         const Polygons &support_polygons,  | ||||
|         const Polygons &trimming_polygons,  | ||||
|         // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
 | ||||
|         const Polygons &trimming_polygons, | ||||
|         // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing()
 | ||||
|         coordf_t        support_spacing,  | ||||
|         coordf_t        support_angle) : | ||||
|         m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons), | ||||
|  | @ -493,7 +486,8 @@ public: | |||
|         m_grid.set_bbox(bbox); | ||||
|         m_grid.create(*m_support_polygons, grid_resolution); | ||||
|         m_grid.calculate_sdf(); | ||||
|         // Extract a bounding contour from the grid, trim by the object.
 | ||||
|         // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
 | ||||
|         // polygons if ever these polygons get split into parts by the trimming polygons.
 | ||||
|         m_island_samples = island_samples(*m_support_polygons); | ||||
|     } | ||||
| 
 | ||||
|  | @ -504,19 +498,19 @@ public: | |||
|     Polygons extract_support(const coord_t offset_in_grid) | ||||
|     { | ||||
|         // Generate islands, so each island may be tested for overlap with m_island_samples.
 | ||||
|         assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); | ||||
|         ExPolygons islands = diff_ex( | ||||
|             m_grid.contours_simplified(offset_in_grid), | ||||
|             *m_trimming_polygons, false); | ||||
| 
 | ||||
|         // Extract polygons, which contain some of the m_island_samples.
 | ||||
|         Polygons out; | ||||
|         std::vector<std::pair<Point,bool>> samples_inside; | ||||
| 
 | ||||
|         for (ExPolygon &island : islands) { | ||||
|             BoundingBox bbox = get_extents(island.contour); | ||||
|             // Samples are sorted lexicographically.
 | ||||
|             auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), bbox.min - Point(1, 1)); | ||||
|             auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), bbox.max + Point(1, 1)); | ||||
|             samples_inside.clear(); | ||||
|             std::vector<std::pair<Point,bool>> samples_inside; | ||||
|             for (auto it = it_lower; it != it_upper; ++ it) | ||||
|                 if (bbox.contains(*it)) | ||||
|                     samples_inside.push_back(std::make_pair(*it, false)); | ||||
|  | @ -577,8 +571,10 @@ public: | |||
| private: | ||||
|     SupportGridPattern& operator=(const SupportGridPattern &rhs); | ||||
| 
 | ||||
| #if 0 | ||||
|     // Get some internal point of an expolygon, to be used as a representative
 | ||||
|     // sample to test, whether this island is inside another island.
 | ||||
|     //FIXME this was quick, but not sufficiently robust.
 | ||||
|     static Point island_sample(const ExPolygon &expoly) | ||||
|     { | ||||
|         // Find the lowest point lexicographically.
 | ||||
|  | @ -599,7 +595,10 @@ private: | |||
|         double coef = 20. / sqrt(l2); | ||||
|         return Point(p2.x + coef * v.x, p2.y + coef * v.y); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     // Sample one internal point per expolygon.
 | ||||
|     // FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust.
 | ||||
|     static Points island_samples(const ExPolygons &expolygons) | ||||
|     { | ||||
|         Points pts; | ||||
|  | @ -637,6 +636,8 @@ private: | |||
|     coordf_t                m_support_spacing; | ||||
| 
 | ||||
|     Slic3r::EdgeGrid::Grid  m_grid; | ||||
|     // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
 | ||||
|     // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
 | ||||
|     Points                  m_island_samples; | ||||
| }; | ||||
| 
 | ||||
|  | @ -804,6 +805,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|     ++ iRun;  | ||||
| #endif /* SLIC3R_DEBUG */ | ||||
| 
 | ||||
|     // Slice support enforcers / support blockers.
 | ||||
|     std::vector<ExPolygons> enforcers = object.slice_support_enforcers(); | ||||
|     std::vector<ExPolygons> blockers  = object.slice_support_blockers(); | ||||
| 
 | ||||
|     // Output layers, sorted by top Z.
 | ||||
|     MyLayersPtr contact_out; | ||||
| 
 | ||||
|  | @ -841,10 +846,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|     // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
 | ||||
|     // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
 | ||||
|     size_t num_layers = this->has_support() ? object.layer_count() : 1; | ||||
|     // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, 
 | ||||
|     // and the other for the overhangs extruded with a normal flow.
 | ||||
|     contact_out.assign(num_layers * 2, nullptr); | ||||
|     tbb::spin_mutex layer_storage_mutex; | ||||
|     tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers), | ||||
|         [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) { | ||||
|         [this, &object, &buildplate_covered, &enforcers, &blockers, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) { | ||||
|             for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id)  | ||||
|             { | ||||
|                 const Layer &layer = *object.layers[layer_id]; | ||||
|  | @ -855,6 +862,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                 Polygons contact_polygons; | ||||
|                 Polygons slices_margin_cached; | ||||
|                 float    slices_margin_cached_offset = -1.; | ||||
|                 Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons); | ||||
|                 // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
 | ||||
|                 float    no_interface_offset = 0.f; | ||||
|                 if (layer_id == 0) { | ||||
|                     // This is the first object layer, so the object is being printed on a raft and
 | ||||
|                     // we're here just to get the object footprint for the raft.
 | ||||
|  | @ -869,6 +879,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                         // Extrusion width accounts for the roundings of the extrudates.
 | ||||
|                         // It is the maximum widh of the extrudate.
 | ||||
|                         float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); | ||||
|                         no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw); | ||||
|                         float lower_layer_offset =  | ||||
|                             (layer_id < this->m_object_config->support_material_enforce_layers.value) ?  | ||||
|                                 // Enforce a full possible support, ignore the overhang angle.
 | ||||
|  | @ -881,7 +892,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                         // Overhang polygons for this layer and region.
 | ||||
|                         Polygons diff_polygons; | ||||
|                         Polygons layerm_polygons = to_polygons(layerm->slices); | ||||
|                         Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons); | ||||
|                         if (lower_layer_offset == 0.f) { | ||||
|                             // Support everything.
 | ||||
|                             diff_polygons = diff(layerm_polygons, lower_layer_polygons); | ||||
|  | @ -893,26 +903,58 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                         } else { | ||||
|                             // Get the regions needing a suport, collapse very tiny spots.
 | ||||
|                             //FIXME cache the lower layer offset if this layer has multiple regions.
 | ||||
| #if 1 | ||||
|                             diff_polygons = offset2( | ||||
|                                 diff(layerm_polygons, | ||||
|                                      offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)),  | ||||
|                                 -0.1f*fw, +0.1f*fw); | ||||
|                                      offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),  | ||||
|                                 //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
 | ||||
|                                 // no support at all for not so steep overhangs.
 | ||||
|                                 - 0.1f * fw, 0.1f * fw); | ||||
| #else | ||||
|                             diff_polygons =  | ||||
|                                 diff(layerm_polygons, | ||||
|                                      offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
| #endif | ||||
|                             if (! buildplate_covered.empty()) { | ||||
|                                 // Don't support overhangs above the top surfaces.
 | ||||
|                                 // This step is done before the contact surface is calculated by growing the overhang region.
 | ||||
|                                 diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]); | ||||
|                             } | ||||
|                             if (diff_polygons.empty()) | ||||
|                                 continue; | ||||
|                             // Offset the support regions back to a full overhang, restrict them to the full overhang.
 | ||||
|                             diff_polygons = diff( | ||||
|                                 intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),  | ||||
|                                 lower_layer_polygons); | ||||
|                             if (! diff_polygons.empty()) { | ||||
| 	                            // Offset the support regions back to a full overhang, restrict them to the full overhang.
 | ||||
| 	                            // This is done to increase size of the supporting columns below, as they are calculated by 
 | ||||
| 	                            // propagating these contact surfaces downwards.
 | ||||
| 	                            diff_polygons = diff( | ||||
| 	                                intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),  | ||||
| 	                                lower_layer_polygons); | ||||
| 							} | ||||
| 
 | ||||
|                             if (! enforcers.empty()) { | ||||
|                                 // Apply the "support enforcers".
 | ||||
|                                 //FIXME add the "enforcers" to the sparse support regions only.
 | ||||
|                                 const ExPolygons &enforcer = enforcers[layer_id - 1]; | ||||
|                                 if (! enforcer.empty()) { | ||||
|                                     // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
 | ||||
|                                     Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)), | ||||
|                                             offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|                                     if (! new_contacts.empty()) { | ||||
|                                         if (diff_polygons.empty()) | ||||
|                                             diff_polygons = std::move(new_contacts); | ||||
|                                         else | ||||
|                                             diff_polygons = union_(diff_polygons, new_contacts); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         // Apply the "support blockers".
 | ||||
|                         if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) { | ||||
|                             // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
 | ||||
|                             diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id])); | ||||
|                         } | ||||
|                         if (diff_polygons.empty()) | ||||
|                             continue; | ||||
| 
 | ||||
|                         #ifdef SLIC3R_DEBUG                         | ||||
|                         #ifdef SLIC3R_DEBUG | ||||
|                         { | ||||
|                             ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg",  | ||||
|                                 iRun, layer_id,  | ||||
|  | @ -939,7 +981,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                             union_ex(diff_polygons, false)); | ||||
|                         #endif /* SLIC3R_DEBUG */ | ||||
| 
 | ||||
|                         if (this->has_contact_loops()) | ||||
|                         //FIXME the overhang_polygons are used to construct the support towers as well.
 | ||||
|                         //if (this->has_contact_loops())
 | ||||
|                             // Store the exact contour of the overhang for the contact loops.
 | ||||
|                             polygons_append(overhang_polygons, diff_polygons); | ||||
| 
 | ||||
|                         // Let's define the required contact area by using a max gap of half the upper 
 | ||||
|  | @ -948,12 +992,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                         // on the other side of the object (if it's very thin).
 | ||||
|                         { | ||||
|                             //FIMXE 1) Make the offset configurable, 2) Make the Z span configurable.
 | ||||
|                             //FIXME one should trim with the layer span colliding with the support layer, this layer
 | ||||
|                             // may be lower than lower_layer, so the support area needed may need to be actually bigger!
 | ||||
|                             // For the same reason, the non-bridging support area may be smaller than the bridging support area!
 | ||||
|                             float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy)));  | ||||
|                             if (slices_margin_cached_offset != slices_margin_offset) { | ||||
|                                 slices_margin_cached_offset = slices_margin_offset; | ||||
|                                 slices_margin_cached = (slices_margin_offset == 0.f) ?  | ||||
|                                     to_polygons(lower_layer.slices.expolygons) : | ||||
|                                     offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS); | ||||
|                                     lower_layer_polygons : | ||||
|                                     offset2(to_polygons(lower_layer.slices.expolygons), - scale_(- no_interface_offset * 0.5f), slices_margin_offset + scale_(- no_interface_offset * 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS); | ||||
|                                 if (! buildplate_covered.empty()) { | ||||
|                                     // Trim the inflated contact surfaces by the top surfaces as well.
 | ||||
|                                     polygons_append(slices_margin_cached, buildplate_covered[layer_id]); | ||||
|  | @ -1028,7 +1075,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                                     // Align the layer with the 1st layer height.
 | ||||
|                                     bridging_print_z = m_slicing_params.first_print_layer_height; | ||||
|                                 } | ||||
|                                 if (bridging_print_z != new_layer.print_z) { | ||||
|                                 if (bridging_print_z < new_layer.print_z - EPSILON) { | ||||
|                                     // Allocate the new layer.
 | ||||
|                                     bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact); | ||||
|                                     bridging_layer->idx_object_layer_above = layer_id; | ||||
|  | @ -1051,20 +1098,38 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|                         contact_polygons,  | ||||
|                         // Trimming polygons, to trim the stretched support islands.
 | ||||
|                         slices_margin_cached, | ||||
|                         // How much to offset the extracted contour outside of the grid.
 | ||||
|                         // Grid resolution.
 | ||||
|                         m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), | ||||
|                         Geometry::deg2rad(m_object_config->support_material_angle.value)); | ||||
|                     // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
 | ||||
|                     new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5); | ||||
|                     // 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells.
 | ||||
|                     // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
 | ||||
|                     new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3)); | ||||
|                     // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
 | ||||
|                     if (layer_id == 0) { | ||||
|                     // if (no_interface_offset == 0.f) {
 | ||||
|                         new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5); | ||||
|                     } else  { | ||||
|                         //Polygons dense_interface_polygons = diff(overhang_polygons, offset(lower_layer_polygons, scale_(no_interface_offset * 0.7f)));
 | ||||
|                         Polygons dense_interface_polygons = diff(overhang_polygons,  | ||||
|                             offset2(lower_layer_polygons, scale_(- no_interface_offset * 0.5f), scale_(no_interface_offset * (0.7f + 0.5f)), SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|                         if (! dense_interface_polygons.empty()) { | ||||
|                             SupportGridPattern support_grid_pattern( | ||||
|                                 // Support islands, to be stretched into a grid.
 | ||||
|                                 dense_interface_polygons,  | ||||
|                                 // Trimming polygons, to trim the stretched support islands.
 | ||||
|                                 slices_margin_cached, | ||||
|                                 // Grid resolution.
 | ||||
|                                 m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), | ||||
|                                 Geometry::deg2rad(m_object_config->support_material_angle.value));                         | ||||
|                             new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5); | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
 | ||||
|                     // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
 | ||||
|                      | ||||
|                     // Store the overhang polygons.
 | ||||
|                     // The overhang polygons are used in the path generator for planning of the contact loops.
 | ||||
|                     // if (this->has_contact_loops())
 | ||||
|                     // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug.
 | ||||
|                     new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons)); | ||||
|                     contact_out[layer_id * 2] = &new_layer; | ||||
|                     if (bridging_layer != nullptr) { | ||||
|  | @ -1085,11 +1150,36 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ | |||
|     // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
 | ||||
|     // the top contact layer is merged into the bottom contact layer.
 | ||||
|     { | ||||
|         int k = 0; | ||||
|         for (int i = 0; i < int(contact_out.size()); ++ k) { | ||||
| 		int i = 0; | ||||
| 		int k = 0; | ||||
| 		{ | ||||
| 			// Find the span of layers, which are to be printed at the first layer height.
 | ||||
| 			int j = 0; | ||||
| 			for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j); | ||||
| 			if (j > 0) { | ||||
| 				// Merge the contact_out layers (0) to (j - 1) into the contact_out[0].
 | ||||
| 				MyLayer &dst = *contact_out.front(); | ||||
| 				for (int u = 1; u < j; ++ u) { | ||||
| 					MyLayer &src = *contact_out[u]; | ||||
| 					// The union_() does not support move semantic yet, but maybe one day it will.
 | ||||
| 					dst.polygons = union_(dst.polygons, std::move(src.polygons)); | ||||
| 					*dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons)); | ||||
| 					*dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons)); | ||||
| 					// Source polygon is no more needed, it will not be refrenced. Release its data.
 | ||||
| 					src.reset(); | ||||
| 				} | ||||
| 				// Snap the first layer to the 1st layer height.
 | ||||
| 				dst.print_z  = m_slicing_params.first_print_layer_height; | ||||
| 				dst.height   = m_slicing_params.first_print_layer_height; | ||||
| 				dst.bottom_z = 0; | ||||
| 				++ k; | ||||
| 			} | ||||
| 			i = j; | ||||
| 		} | ||||
|         for (; i < int(contact_out.size()); ++ k) { | ||||
|             // Find the span of layers closer than m_support_layer_height_min.
 | ||||
|             int j = i + 1; | ||||
|             coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min - EPSILON; | ||||
|             coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON; | ||||
|             for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ; | ||||
|             if (i + 1 < j) { | ||||
|                 // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
 | ||||
|  | @ -1148,7 +1238,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|             const Layer &layer = *object.get_layer(layer_id); | ||||
|             // Collect projections of all contact areas above or at the same level as this top surface.
 | ||||
|             for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) { | ||||
|                 auto *l = top_contacts[contact_idx]; | ||||
|                 Polygons polygons_new; | ||||
|                 // Contact surfaces are expanded away from the object, trimmed by the object.
 | ||||
|                 // Use a slight positive offset to overlap the touching regions.
 | ||||
|  | @ -1156,7 +1245,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|                 // Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
 | ||||
|                 polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON)); | ||||
| #else | ||||
|                 // Consume the contact_polygons. The contact polygons are already expanded into a grid form.
 | ||||
|                 // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller
 | ||||
|                 // than the grid cells.
 | ||||
|                 polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons)); | ||||
| #endif | ||||
|                 // These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
 | ||||
|  | @ -1168,9 +1258,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|                 continue; | ||||
|             Polygons projection_raw = union_(projection); | ||||
| 
 | ||||
|             // Top surfaces of this layer, to be used to stop the surface volume from growing down.
 | ||||
|             tbb::task_group task_group; | ||||
|             if (! m_object_config->support_material_buildplate_only) | ||||
|                 // Find the bottom contact layers above the top surfaces of this layer.
 | ||||
|                 task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] { | ||||
|                     Polygons top = collect_region_slices_by_type(layer, stTop); | ||||
|         #ifdef SLIC3R_DEBUG | ||||
|  | @ -1206,24 +1296,26 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|                                 // Place a bridge flow interface layer over the top surface.
 | ||||
|                                 //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
 | ||||
|                                 // According to Jindrich the bottom surfaces work well.
 | ||||
|                                 //FIXME test the bridging flow instead?
 | ||||
|                                 m_support_material_interface_flow.nozzle_diameter; | ||||
|                             layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z : | ||||
|                                 layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; | ||||
|                             layer_new.bottom_z = layer.print_z; | ||||
|                             layer_new.idx_object_layer_below = layer_id; | ||||
|                             layer_new.bridging = ! m_slicing_params.soluble_interface; | ||||
|                             //FIXME how much to inflate the top surface?
 | ||||
|                             //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
 | ||||
|                             //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
 | ||||
|                             layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); | ||||
|                             if (! m_slicing_params.soluble_interface) { | ||||
|                                 // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
 | ||||
|                                 // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
 | ||||
|                                 for (size_t top_idx = size_t(std::max<int>(0, contact_idx));  | ||||
|                                     top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min;  | ||||
|                                     top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON;  | ||||
|                                     ++ top_idx) { | ||||
|                                     if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min) { | ||||
|                                     if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) { | ||||
|                                         // A top layer has been found, which is close to the new bottom layer.
 | ||||
|                                         coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z; | ||||
|                                         assert(std::abs(diff) <= this->m_support_layer_height_min); | ||||
|                                         assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON); | ||||
|                                         if (diff > 0.) { | ||||
|                                             // The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
 | ||||
|                                             assert(diff < layer_new.height + EPSILON); | ||||
|  | @ -1247,10 +1339,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|                                 union_ex(layer_new.polygons, false)); | ||||
|                 #endif /* SLIC3R_DEBUG */ | ||||
|                             // Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
 | ||||
|                             //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
 | ||||
|                             touching = offset(touching, float(SCALED_EPSILON)); | ||||
|                             for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) { | ||||
|                                 const Layer &layer_above = *object.layers[layer_id_above]; | ||||
|                                 if (layer_above.print_z > layer_new.print_z + EPSILON) | ||||
|                                 if (layer_above.print_z > layer_new.print_z - EPSILON) | ||||
|                                     break;  | ||||
|                                 if (! layer_support_areas[layer_id_above].empty()) { | ||||
| #ifdef SLIC3R_DEBUG | ||||
|  | @ -1303,7 +1396,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|                     projection,  | ||||
|                     // Trimming polygons, to trim the stretched support islands.
 | ||||
|                     trimming, | ||||
|                     // How much to offset the extracted contour outside of the grid.
 | ||||
|                     // Grid spacing.
 | ||||
|                     m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), | ||||
|                     Geometry::deg2rad(m_object_config->support_material_angle.value)); | ||||
|                 tbb::task_group task_group_inner; | ||||
|  | @ -1341,7 +1434,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta | |||
|             task_group.wait(); | ||||
|         } | ||||
|         std::reverse(bottom_contacts.begin(), bottom_contacts.end()); | ||||
|         trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy); | ||||
| //        trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
 | ||||
|         trim_support_layers_by_object(object, bottom_contacts,  | ||||
|             m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,  | ||||
|             m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); | ||||
| 
 | ||||
|     } // ! top_contacts.empty()
 | ||||
| 
 | ||||
|     return bottom_contacts; | ||||
|  | @ -1658,9 +1755,6 @@ void PrintObjectSupportMaterial::generate_base_layers( | |||
|                 assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z); | ||||
| 
 | ||||
|                 // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
 | ||||
|                 idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,  | ||||
|                     [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); | ||||
| 
 | ||||
|                 // New polygons for layer_intermediate.
 | ||||
|                 Polygons polygons_new; | ||||
| 
 | ||||
|  | @ -1679,12 +1773,10 @@ void PrintObjectSupportMaterial::generate_base_layers( | |||
|                 // 3) base.print_z > top.print_z  && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here.
 | ||||
|                 // 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen.
 | ||||
|                 // 5) base.print_z <= top.print_z  && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top.
 | ||||
|                 int idx_top_contact_overlapping = idx_top_contact_above; | ||||
|                 while (idx_top_contact_overlapping >= 0 &&  | ||||
|                        top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON) | ||||
|                     -- idx_top_contact_overlapping;  | ||||
|                 idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,  | ||||
|                     [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); | ||||
|                 // Collect all the top_contact layer intersecting with this layer.
 | ||||
|                 for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { | ||||
|                 for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { | ||||
|                     MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping]; | ||||
|                     if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON) | ||||
|                         break; | ||||
|  | @ -1764,7 +1856,10 @@ void PrintObjectSupportMaterial::generate_base_layers( | |||
|     ++ iRun; | ||||
| #endif /* SLIC3R_DEBUG */ | ||||
| 
 | ||||
|     trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy); | ||||
| //    trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
 | ||||
|     this->trim_support_layers_by_object(object, intermediate_layers,  | ||||
|         m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,  | ||||
|         m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); | ||||
| } | ||||
| 
 | ||||
| void PrintObjectSupportMaterial::trim_support_layers_by_object( | ||||
|  | @ -1809,7 +1904,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( | |||
|                     const Layer &object_layer = *object.layers[i]; | ||||
|                     if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) | ||||
|                         break; | ||||
|                     polygons_append(polygons_trimming, (Polygons)object_layer.slices); | ||||
|                     polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); | ||||
|                 } | ||||
|                 if (! this->m_slicing_params.soluble_interface) { | ||||
|                     // Collect all bottom surfaces, which will be extruded with a bridging flow.
 | ||||
|  | @ -1958,11 +2053,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int | |||
|                     coordf_t top_z    = intermediate_layers[std::min<int>(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z; | ||||
|                     coordf_t bottom_z = intermediate_layers[std::max<int>(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z; | ||||
|                     // Move idx_top_contact_first up until above the current print_z.
 | ||||
|                     idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); | ||||
|                     idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); //  - EPSILON
 | ||||
|                     // Collect the top contact areas above this intermediate layer, below top_z.
 | ||||
|                     Polygons polygons_top_contact_projected; | ||||
|                     for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) { | ||||
|                         const MyLayer &top_contact_layer = *top_contacts[idx_top_contact]; | ||||
|                         //FIXME maybe this adds one interface layer in excess?
 | ||||
|                         if (top_contact_layer.bottom_z - EPSILON > top_z) | ||||
|                             break; | ||||
|                         polygons_append(polygons_top_contact_projected, top_contact_layer.polygons); | ||||
|  | @ -2517,7 +2613,7 @@ void modulate_extrusion_by_overlapping_layers( | |||
|                 (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); | ||||
|         } | ||||
|     private: | ||||
|         ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&); | ||||
|         ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {} | ||||
|         const std::vector<ExtrusionPathFragment> &m_path_fragments; | ||||
|     }; | ||||
|     const coord_t search_radius = 7; | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ class PrintConfig; | |||
| class PrintObjectConfig; | ||||
| 
 | ||||
| // how much we extend support around the actual contact area
 | ||||
| //FIXME this should be dependent on the nozzle diameter!
 | ||||
| #define SUPPORT_MATERIAL_MARGIN 1.5	 | ||||
| 
 | ||||
| // This class manages raft and supports for a single PrintObject.
 | ||||
|  |  | |||
|  | @ -622,7 +622,7 @@ std::vector<int> GLVolumeCollection::load_object( | |||
|         const ModelVolume *model_volume = model_object->volumes[volume_idx]; | ||||
| 
 | ||||
|         int extruder_id = -1; | ||||
|         if (!model_volume->modifier) | ||||
|         if (model_volume->is_model_part()) | ||||
|         { | ||||
|             extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0; | ||||
|             if (extruder_id == 0) | ||||
|  | @ -635,7 +635,16 @@ std::vector<int> GLVolumeCollection::load_object( | |||
|             volumes_idx.push_back(int(this->volumes.size())); | ||||
|             float color[4]; | ||||
|             memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); | ||||
|             color[3] = model_volume->modifier ? 0.5f : 1.f; | ||||
|             if (model_volume->is_support_blocker()) { | ||||
|                 color[0] = 1.0f; | ||||
|                 color[1] = 0.2f; | ||||
|                 color[2] = 0.2f; | ||||
|             } else if (model_volume->is_support_enforcer()) { | ||||
|                 color[0] = 0.2f; | ||||
|                 color[1] = 0.2f; | ||||
|                 color[2] = 1.0f; | ||||
|             } | ||||
|             color[3] = model_volume->is_model_part() ? 1.f : 0.5f; | ||||
|             this->volumes.emplace_back(new GLVolume(color)); | ||||
|             GLVolume &v = *this->volumes.back(); | ||||
|             if (use_VBOs) | ||||
|  | @ -658,15 +667,15 @@ std::vector<int> GLVolumeCollection::load_object( | |||
|             else if (drag_by == "instance") | ||||
|                 v.drag_group_id = obj_idx * 1000 + instance_idx; | ||||
| 
 | ||||
|             if (!model_volume->modifier) | ||||
|             if (model_volume->is_model_part()) | ||||
|             { | ||||
|                 v.set_convex_hull(model_volume->get_convex_hull()); | ||||
|                 v.layer_height_texture = layer_height_texture; | ||||
|                 if (extruder_id != -1) | ||||
|                     v.extruder_id = extruder_id; | ||||
|             } | ||||
|             v.is_modifier = model_volume->modifier; | ||||
|             v.shader_outside_printer_detection_enabled = !model_volume->modifier; | ||||
|             v.is_modifier = ! model_volume->is_model_part(); | ||||
|             v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); | ||||
|             v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0)); | ||||
|             v.set_angle_z(instance->rotation); | ||||
|             v.set_scale_factor(instance->scaling_factor); | ||||
|  |  | |||
|  | @ -340,9 +340,19 @@ ModelMaterial::attributes() | |||
|         %code%{ RETVAL = &THIS->mesh; %}; | ||||
|      | ||||
|     bool modifier() | ||||
|         %code%{ RETVAL = THIS->modifier; %}; | ||||
|         %code%{ RETVAL = THIS->is_modifier(); %}; | ||||
|     void set_modifier(bool modifier) | ||||
|         %code%{ THIS->modifier = modifier; %}; | ||||
|         %code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %}; | ||||
|     bool model_part() | ||||
|         %code%{ RETVAL = THIS->is_model_part(); %}; | ||||
|     bool support_enforcer() | ||||
|         %code%{ RETVAL = THIS->is_support_enforcer(); %}; | ||||
|     void set_support_enforcer() | ||||
|         %code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %}; | ||||
|     bool support_blocker() | ||||
|         %code%{ RETVAL = THIS->is_support_blocker(); %}; | ||||
|     void set_support_blocker() | ||||
|         %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %}; | ||||
| 
 | ||||
|     size_t split(unsigned int max_extruders); | ||||
|      | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv